# ############################################################################ #
#                                                                              #
#         AUSWERTUNG DER BASECHAIN-RESULTATDATEI <REGION NAME>_OUT.TXT         #
#                                                                              #
# ############################################################################ #
#                                                                              #
#                                  COPYRIGHT:                                  #
#                                                                              #
# INSTITUT FUER BAU UND UMWELT (IBU), HOCHSCHULE FUER TECHNIK RAPPERSWIL (HSR) #
#                                                                              #
#                                 ENTWICKLUNG:                                 #
#                                                                              #
#   PROF. DR. JUERG SPEERLI (KONZEPT)   &    DANIEL EHRBAR (IMPLEMENTIERUNG)   #
#                                                                              #
# ############################################################################ #
#                                                                              #
#                               VORAUSSETZUNGEN:                               #
#                                                                              #
#                     PYTHON 2.X (64-BIT) INKL. MATPLOTLIB                     #
#                                                                              #
# ############################################################################ #


#!/usr/bin/env python

# Bibliotheken importieren
import os                           ## os = operating system
import matplotlib.pyplot as plt     ## Bibliothek fuer Matlab-aehnliches Plotten
import matplotlib.ticker as mtick   ## zur Modifizierung der Achsen-Teilstriche
from matplotlib.ticker import AutoMinorLocator
import time                         ## bietet Pause-Funktion fuer Skript an
from datetime import datetime       ## zur Bestimmung der Skript-Laufzeit

# Start-Zeitpunkt des Skriptaufrufs speichern
startTime = datetime.now()

# Konsolen-Nachricht
print("\nProcessing data...")



# ==============================================================================
# TEIL A - DATEN EINLESEN UND ABSPEICHERN
# ==============================================================================

# Schleife ueber alle Dateien im aktuellen Verzeichnis (cwd = current working
# directory) um Standard-Resultatdatei zu suchen
for files in os.listdir(os.getcwd()):

    # falls Datei mit charakteristischer Endung der Standard-Resultatdatei:
    if files.endswith("_out.dat"):

        # Datei als 'outputFile' oeffnen (nur lesender Zugriff, "r" = read only)
        outputFile = open(files, "r")

        # ----------------------------------------------------------------------
        # Querprofile, Distanzen und Dammoberkanten ermitteln (1. Schleife)
        # ----------------------------------------------------------------------

        # Initialisierung der Liste mit Querprofilen, Distanzen und
        # Dammoberkanten
        crossSections = []
        distances = []
        dam_elevations_left = []
        dam_elevations_right = []

        # Schleife ueber alle Zeilen in Output-Datei
        for line in outputFile:

            # Zeilen mit "Start" oder "cross_section" ueberspringen
            # N.B.: betrifft Zeilen 1 und 2
            if line.startswith("Start") or line.startswith("cross_section"):
                continue ## wechselt zur naechsten Zeile

            # Abbruch bei erster Leerzeile
            elif line in ["\n"]:
                break ## bricht Schleife ab

            # Querprofile, Distanzen und Dammoberkanten zwischen Zeile 3
            # und erstem Zeilenumbruch in Listeeinfuegen
            else:

                # Zeile (Linie) nach einem oder mehreren Leerschlaegen
                # auftrennen
                line = line.split()

                # Daten in Listen abspeichern
                crossSections.append( line[0] )
                distances.append( float(line[1]) )
                dam_elevations_left.append( float(line[3]) )
                dam_elevations_right.append( float(line[4]) )

        # Pointer auf Anfang (erstes byte) der Output-Datei zuruecksetzen
        outputFile.seek(0)

        # Konsolen-Nachricht bezueglich Anzahl gefundener Querprofile
        print("\n  ...%d cross sections found\n" % len(crossSections))


        # ----------------------------------------------------------------------
        # Zeitschritte ermitteln (2. Schleife)
        # ----------------------------------------------------------------------

        # Initialisierung der Liste mit Zeitschritten
        timeSteps = []

        # Schleife ueber alle Zeilen in Output-Datei
        for line in outputFile:

            # Startzeitpunkt der Simulation (Initialzustand)
            if line.startswith("Start"):
                timeSteps.append( 0.0 ) ## Annahme, ist zeitlich nicht referenziert!

            # Zeitschritte der Zeilen mit "time_[s]" am Anfang in Liste
            # einfuegen (als Gleitkommazahl)
            if line.startswith("time_[s]:"):
                timeSteps.append( float(line.split()[1]) )

            # Zeilen ohne Zeitschritt-Information ueberspringen
            else:
                continue ## wechselt zur naechsten Zeile, d.h. naechste Schleife

        # Pointer auf Anfang (erstes byte) der Output-Datei zuruecksetzen
        outputFile.seek(0)

        # Konsolen-Nachricht bezueglich Anzahl Zeitschritte
        print("  ...%d time steps found\n" % len(timeSteps))


        # ----------------------------------------------------------------------
        # Daten einlesen und speichern (3. Schleife)
        # ----------------------------------------------------------------------

        # Konsolen-Nachricht
        print("  ...reading result data\n")

        # Dimensionen der Matrizen
        ## len() = Laenge der Liste = Anzahl Eintraege
        m = len(crossSections)
        n = len(timeSteps)

        # Matrizen fuer Variablen der Querprofile
        # Adressierung: name[ID des Zeitschritts][ID des Querprofils]
        talweg                  = [[[] for x in range(m)] for x in range(n)]
        mean_bottom_elevation   = [[[] for x in range(m)] for x in range(n)]
        water_surface_elevation = [[[] for x in range(m)] for x in range(n)]
        energy_line             = [[[] for x in range(m)] for x in range(n)]
        froude_number           = [[[] for x in range(m)] for x in range(n)]
        bed_shear_stress        = [[[] for x in range(m)] for x in range(n)]

        # Schaltvariable zur Unterscheidung zwischen Querprofil-Daten und
        # Kanten-Daten
        crossSectionData = True ## Bool'sche Variable: 'True' oder 'False'

        # Zaehler fuer Positionen innerhalb der Matrizen
        TS_ID = 0   ## Zeitschritt-ID (TS = Time Step)
        CS_ID = 0   ## Querprofil-ID (CS = Cross Section)

        # Zeitschritt (Initialisiert mit 0.0 fuer Initialzustand)
        timestep = 0.0

        # Schleife ueber alle Zeilen in Output-Datei
        for line in outputFile:

            # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
            # Querprofil-Daten einlesen und abspeichern
            # ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

            # Querprofil-Daten
            if crossSectionData == True:

                # Ueberschrifts-Zeile
                if ( line.startswith("Start") or
                     line.startswith("cross_section") ):
                    continue

                # neuer Datensatz nach Leerzeile
                elif line in ['\n', '\r\n']:
                    # Zeitschritt-Zaehler um Eins (1) erhoehen
                    TS_ID += 1
                    # Querprofil-Zaehler auf Null (0) zuruecksetzen
                    CS_ID = 0
                    # Wechsel zu Kanten-Daten
                    crossSectionData = False

                # Querprofil-Daten einlesen und speichern
                else:

                    # Zeile (Linie) nach einem oder mehreren Leerschlaegen
                    # auftrennen
                    line = line.split()

                    # Initialzustand: Froude-Zahl und Sohlschubspannung sind
                    # nicht definiert
                    if len(line) == 12:
                        froude = "undef"            ## Froude-Zahl
                        tau = "undef"               ## Sohlschubspannung

                    # wahrend Simulation: Froude-Zahl und Sohlschubspannung
                    # sind definiert (d.h. mehr als 12 Spalten...)
                    else: ## len(line) != 12
                        if ( line[13] == "-" ):
                        	line[13] = "0"
                        froude = float(line[13])
                        tau = float(line[14])

                    # momentaner Zustand des Querprofils in entsprechender
                    talweg[TS_ID][CS_ID] = float(line[2])
                    mean_bottom_elevation[TS_ID][CS_ID] = float(line[5])
                    water_surface_elevation[TS_ID][CS_ID] = float(line[6])
                    energy_line[TS_ID][CS_ID] = float(line[7])
                    froude_number[TS_ID][CS_ID] = froude
                    bed_shear_stress[TS_ID][CS_ID] = tau

                    # Querprofil-Zaehler um 1 erhoehen
                    CS_ID += 1

            # Kanten-Daten
            else: ## crossSectionData == False

                # nach Leerzeile folgen wieder Querprofil-Daten
                if line in ['\n', '\r\n']:

                    # Wechsel zu Querprofil-Daten
                    crossSectionData = True

                # bei Kanten-Daten keine Operation ausfuehren
                else:
                    pass ## 'null operation'

        # Output-Datei schliessen (alle Daten sind eingelesen)
        outputFile.close()

        # Schleife fuer Suche nach Standard-Resultatdatei abbrechen
        break ## bricht Schleife komplett ab (auch ausstehende Durchgaenge)

    # falls Datei nicht die Standard-Resultatdatei ist:
    else:

        # weitersuchen!
        pass ## 'null operation'



# ==============================================================================
# TEIL B - DATEN DARSTELLEN
# ==============================================================================

# Groesse der Abbildungen definieren
plt.rc('figure', figsize=[32,18])       ## Format (16:9, Masseinheit: inches)
plt.rc('savefig', dpi=300)              ## Aufloesung d. gespeicherten Abbildung
plt.rc('savefig', bbox='tight')         ## Abbildungen randlos speichern
plt.rc('lines', linewidth=4)            ## Linienbreite der Abbildungen (in pt)

# Schriftart(en) definieren
plt.rc('font', family='Arial')          ## Schriftart
plt.rc('text', usetex=False)            ## Math-Mode (TeX) (de-)aktivieren
plt.rc('mathtext', default='regular')   ## Schriftart fuer Math-Mode (TeX)

# Schriftartgroessen (in pt) definieren
plt.rc('font', size=15)                 ## Beschriftungen ('annotations')
plt.rc('legend', fontsize=30)           ## Legende
plt.rc('axes', labelsize=30)            ## Achsentitel
plt.rc('xtick', labelsize=30)           ## Teilstrichbeschriftungen x-Achse
plt.rc('ytick', labelsize=30)           ## Teilstrichbeschriftungen y-Achse

# Formatierung der Teilstriche (Masse in pt)
plt.rc('axes', linewidth=1)             ## Breite der x- und y-Achsen
plt.rc('xtick', direction='out')        ## Richtung x-Teilstriche
plt.rc('ytick', direction='out')        ## Richtung y-Teilstriche
plt.rc('xtick.major', pad=10)           ## Abstand x-Teilstrich - Beschriftung
plt.rc('ytick.major', pad=10)           ## Abstand y-Teilstrich - Beschriftung
plt.rc('xtick.major', size=15)          ## Laenge der x-Haupt-Teilstriche
plt.rc('ytick.major', size=15)          ## Laenge der y-Haupt-Teilstriche
plt.rc('xtick.minor', size=10)          ## Laenge der x-Unter-Teilstriche
plt.rc('ytick.minor', size=10)          ## Laenge der y-Unter-Teilstriche

# Formatierung des Gitters
plt.rc('grid', linewidth=1.0)           ## Linienbreite (in pt)
plt.rc('grid', linestyle='--')          ## Linientyp (gestrichelt)


# ------------------------------------------------------------------------------
# Laengenprofil
# ------------------------------------------------------------------------------

# Konsolen-Nachricht
print("  ...creating longitudinal profile\n")

# Dammoberkanten links und rechts
plt.plot(distances, dam_elevations_left, color='black', linestyle='-',
    label=r'left levee')
plt.plot(distances, dam_elevations_right, color='black', linestyle='-.',
    label=r'right levee')

# Lage der Energielinie am Ende der Simulation
plt.plot(distances, energy_line[-1], color='red', linestyle=':',
    label=r'energy head')

# Lage des Wasserspiegels am Ende der Simulation
plt.plot(distances, water_surface_elevation[-1], color='blue',
    linestyle='-', label=r'water level')

# Sohlenlage im Endzustand
#plt.plot(distances, mean_bottom_elevation[-1], color='green', linestyle='-.',
#    label=r'mittlere Sohlenlage')

# Talweg im Endzustand
plt.plot(distances, talweg[-1], color='green', linestyle='-',
    label=r'thalweg')

# Legende und Gitter anzeigen
plt.legend(); plt.grid()

# x-Achse auf minimalen und maximalen Wert beschraenken
plt.xlim(min(distances), max(distances))

# Achsenbeschriftungen mit Umlauten in Tex-Schreibweise (zwischen $-Zeichen
# eingeschlossen)
plt.xlabel(r'Distance [m]', fontweight='bold')
plt.ylabel(r'Elevation [m a.s.l.]', fontweight='bold')

# automatische Zwischen-Teilstriche setzen
## N.B.: gca() = get current axes (Zeiger resp. Pointer auf aktuelle Achsen)
plt.gca().xaxis.set_minor_locator(AutoMinorLocator())
plt.gca().yaxis.set_minor_locator(AutoMinorLocator())

# Abbildung speichern
plt.savefig('longitudinal_profile.png')

# Abbildung schliessen
plt.close()


# ------------------------------------------------------------------------------
# Froude-Diagramm
# ------------------------------------------------------------------------------

# Konsolen-Nachricht
print("  ...creating Froude diagram\n")

# rote Linie bei Froude = 1
plt.plot([0, max(distances)], [1.0, 1.0], color='red', linestyle='-')

# Froude-Zahl im Endzustand
plt.plot(distances, froude_number[-1], color='orange', linestyle='-')

# x-Achse auf minimalen und maximalen Wert beschraenken
plt.xlim(min(distances), max(distances))

# y-Achse zwingend bei (0) Null beginnen
plt.ylim(ymin=0.0)

# Achsenbeschriftungen
plt.xlabel(r'Distance [m]', fontweight='bold')
plt.ylabel(r'Froude number [-]', fontweight='bold')

# automatische Zwischen-Teilstriche setzen
## N.B.: gca() = get current axes (Zeiger resp. Pointer auf aktuelle Achsen)
plt.gca().xaxis.set_minor_locator(AutoMinorLocator())
plt.gca().yaxis.set_minor_locator(AutoMinorLocator())

# Gitter anzeigen
plt.grid()

# Abbildung speichern
plt.savefig('Froude_diagram.png')

# Abbildung schliessen
plt.close()


# ------------------------------------------------------------------------------

# Laufzeit des Skripts ermitteln
runTime = datetime.now() - startTime

# Konsolen-Nachricht
print("processing terminated! (runTime: %.0f seconds)" % runTime.seconds)

# Konsole fuer weitere 5 Sekunden offen halten
time.sleep(5)