# -*- coding: utf-8 -*-
"""
@author: Vaclav Miklas

Nazev: KGJ
===============================================================================
UVOD

Tento skript obsahuje, vzhledem k neucelne materialove bilanci, pouze funkci
EnerBilance. Pokud je cely skript importovan, volani funkce je nasledujici:
KGJ.EnerBilance(argumenty)
–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
POKYNY K FUNKCI EnerBilance

Argumenty:
vysledkyMBF ... 14-clenne pole vysledku funkce fermentace.MatBilance

arg ... pro mod c.1: pozadovany pocet kogeneracnich jednotek
        pro mod c.2 a 3: maximalni pocet kogeneracnich jednotek
        
name ... string s nazvem a cestou k databazi KGJ; je nutne zadavat v uvozovkach,
         pricemz plati, ze pokud neni zadan zadan, uvazuje se KGJ.xlsx (musi 
         byt ve slozce se skriptem)
          
mod ... argument na poslednim miste, ve tvaru mod=hodnota; 
        
        hodnota 1 nalezne kogeneracni jednotku/jednotky o definovanem poctu, 
        odpovidajici danemu objemovemu toku bioplynu - bude-li pripadat v uvahu 
        vice KGJ, bude zvolena slabsi z nich z duvodu nizsich porizovacich
        nakladu a (typicky) rezimu vyssi elektricke ucinnosti;
        
        hodnota 2 znamena, ze se nalezne takova konfigurace o maximalnim
        poctu kogeneracnich jednotek, pri kterem bude vyroba elektricke energie
        maximalni;
        
        hodnota 3 provede totez co predchozi, avsak ucelovou funkci je vykon
        tepelny.



Priklad volani:
    KGJ.EnerBilance(vysledkyMBF, arg, name, mod = X)

Pozn. Vysledky jsou ve forme 5-clenneho pole. Na prvnim miste je elektricky
      vykon v kW, na druhem miste tepelny vykon v kW, na tretim miste pocet
      KGJ, na ctvrtem miste string nesouci nazev pouzite KGJ, na poslednim
      miste pak podruzne pole obsahujici ucinnosti [%] v poradi: elektricka,
      tepelna, celkova.
    
===============================================================================
"""
import numpy as np
import pandas as pd

def EnerBilance (vysledkyMBF, arg, *args, **kwargs):
    # DEKLARACE PROMENNYCH:
    if arg < 1:
        arg = 1
        print ('Neplatná hodnota druhého argumentu. Argument položen rovno 1.')
    LHV = 22 # MJ/Nm3
    rhoBP = 1.2 # kg/Nm3
    mod = kwargs.get('mod')
    regres_koef = np.zeros((10,3))
    ucinnosti = []
    nazvy = ('MGM100','MGM200','MGM350','MGM430','MGM500')
    
    # NACTENI SOUBORU S REGRESNIMI KOEFICIENTY A PRACOVNIMI ROZSAHY:
    if len(args) == 1:
        file = args[0]
    else:
        file = 'KGJ.xlsx'
    df = pd.read_excel(file)
    
    # STANOVENI PRUTOKU BIOPLYNU A PRIKONU V PALIVU:
    V_BP = vysledkyMBF[3]/rhoBP
    FuelInp = V_BP*LHV/3.6    
    
    # DEFINICE REGRESNI FUNKCE KORESPONDUJICI S "curve_fit_KGJ.py":
    def func(x, a, b, c):
        return a*x**2 + b*x + c
    
    # DEFINICE FUNKCE, KTERA ZJEDNODUSI OSETRENI CHYBOVYCH STAVU:
    def error():
        E = 0
        Q = 0
        pocet = 0
        nazev = 'N/A'  
        return E, Q, pocet, nazev
    
    # KALKULACE DLE MODU:
    
    # Prvni mod pro dany pocet KGJ nalezne modely pripadajici v uvahu a vybere
    # nejslabsi z nich (mensi porizovaci naklady, vyssi elektricka ucinnost).

    if not mod in (2,3):
        if not mod:
            print ('Nebyl zvolen mód kalkulace KGJ. Výpočet proběhne pro mód 1.')
        # Pole pro zaznamenani indexu KGJ, jejichz pracovni rozsah je vhodny
        # pro vstupni parametry.
        KGJ_index = []
        # Argument je v tomto pripade fixni pocet KGJ:
        pocet = arg
        # Prutok bioplynu do jedne KGJ:
        V_BP_jed = V_BP/pocet
        
        # Cyklus, ktery postupne hodnoti rozsahy z nacteneho souboru KGJ.xlsx
        # a pokud prutok bioplynu je v mezich moznosti dane KGJ, je zaznamenan
        # jeji index. Velicina 'low' zaroven detekuje, zda byla alespon jednou
        # prekrocena spodni mez, tedy zda neni prutok pri dane konfiguraci
        # prilis nizky:
        low = True       
        for i in range (len(nazvy)):
            if df.iat[2*i,3] < V_BP_jed:
                low = False
                if V_BP_jed < df.iat[2*i,4]:
                    KGJ_index.append(i)
        
        # KGJ_index muze byt prazdny ze dvou duvodu: a) prutok je prilis nizky,
        # b) prutok je prilis vysoky. Ktery z techto stavu je aktualni, to
        # rekne velicina low. Nasledne je vypsana chybova hlaska a je vyuzita
        # funkce 'error' pro prirazeni nulovych vykonu, nuloveho poctu
        # a nazvu N/A:
        if not KGJ_index and low == True:
            print ('Průtok bioplynu do jedné KGJ je příliš nízký. '+
                   'Upravte prosím zadání (průtok bioplynu či počet KGJ).')
            E, Q, pocet, nazev = error()
        elif not KGJ_index and low == False:
            print ('Průtok bioplynu do jedné KGJ je příliš vysoký. '+
                   'Upravte prosím zadání (průtok bioplynu či počet KGJ).')
            E, Q, pocet, nazev = error()
        # Pokud pole indexu prazdne neni, pak je vse v poradku:
        else:
            # Index vybrane kogeneracni jednotky bude minimem vyse 
            # uvedeneho pole (odpovida nejslabsi z KGJ).
            KGJ_vyb = min(KGJ_index)
            # Nazev vybrane KGJ:
            nazev = nazvy[KGJ_vyb]
            
            # Nacteni regresnich koeficientu pro vybranou KGJ:
            regres_koef[0] = df.values[2*KGJ_vyb,range(3)]
            regres_koef[1] = df.values[2*KGJ_vyb+1,range(3)]
            # Kalkulace elektrickeho a tepelneho vykonu:
            E = pocet*func(V_BP_jed,*regres_koef[0])
            Q = pocet*func(V_BP_jed,*regres_koef[1])
    
    # Druhy mod maximalizuje vyrobu elektricke energie. Argument predstavuje
    # maximalni pocet KGJ. Skutecny pocet a zvoleny model (voli se pouze
    # jednotky stejneho typu) je predmetem optimalizace:
    elif mod == 2:
        # Pokud je prutok bioplynu mensi nez minimum MGM100, pak je opet
        # vypsana chybova hlaska a prirazeny chybove hodnoty promennych:
        if df.iat[0,3] > V_BP:
            print ('Průtok bioplynu do jedné KGJ je příliš nízký. '+
                   'Upravte prosím zadání (průtok bioplynu či počet KGJ).')
            E, Q, pocet, nazev = error()
        # Pokud je naopak jednotkovy prutok odpovidajici maximalnimu poctu KGJ 
        # vetsi nez maximum MGM500, je proveden analogicky ukon:
        elif df.iat[2*(len(nazvy)-1),4] < V_BP/arg:
            print ('Průtok bioplynu do jedné KGJ je příliš vysoký. '+
                   'Upravte prosím zadání (průtok bioplynu či počet KGJ).')
            E, Q, pocet, nazev = error()
        else:    
            # Vytvoreni pole, do ktereho budou zapisovany vysledky elektrickych
            # vykonu. Radky predstavuji jednotlive pocty KGJ, sloupce pak 
            # modely KGJ:
            E_pole = np.zeros((arg,len(nazvy)))
            # Pro i kogeneracnich jednotek...
            for i in range (arg):
                # ...je vzdy napocitan prutok bioplynu do jedne KGJ:
                V_BP_jed = V_BP/(i+1)
                # Nasledne jsou vzdy vzaty do uvahy vsechny modely KGJ:
                for j in range (len(nazvy)):
                    # Pokud spada jednotkovy prutok bioplynu do pracovniho
                    # rozsahu...
                    if df.iat[2*j,3] < V_BP_jed < df.iat[2*j,4]:
                        # ...a zaroven doposud nebyly nacteny regresni 
                        # koeficienty dane KGJ, pak dojde k jejich nacteni:
                        if regres_koef[2*j][0] == 0:
                            regres_koef[2*j] = df.values[2*j,range(3)]
                        # Nakonec je pro kazdou vhodnou kombinaci pocet-model
                        # spocitan elektricky vykon:
                        E_pole[i][j] = (i+1)*func(V_BP_jed,*regres_koef[2*j])
            # Po ukonceni celeho slozeneho cyklu je nalezen index maxima pole
            # elektrickych vykonu:                    
            ind = np.unravel_index(np.argmax(E_pole, axis=None), E_pole.shape)
            # Zaznamenani vysledneho poctu KGJ:
            pocet = ind[0]+1
            
            # Z vyse uvedeneho pole je vycten elektricky vykon optimalni KGJ:
            E = E_pole[ind]
            # Nacteni regresnich koeficientu pro vypocet tepelneho vykonu 
            # optimalni KGJ:
            regres_koef[2*ind[1]+1] = df.values[2*ind[1]+1,range(3)]
            # Dale je vypocten tepelny vykon KGJ:
            Q = pocet*func(V_BP/pocet,*regres_koef[2*ind[1]+1])
            # Nakonec je zaznamenan nazev KGJ:
            nazev = nazvy[ind[1]]
    
    # Treti mod maximalizuje vyrobu tepelne energie. Procedura je zcela
    # analogicka k modu 2, proto jsou vynechany komentare.
    elif mod == 3:              
        if df.iat[0,3] > V_BP:
            print ('Průtok bioplynu do jedné KGJ je příliš nízký. '+
                   'Upravte prosím zadání (průtok bioplynu či počet KGJ).')
            E, Q, pocet, nazev = error()
        elif df.iat[2*(len(nazvy)-1),4] < V_BP/arg:
            print ('Průtok bioplynu do jedné KGJ je příliš vysoký. '+
                   'Upravte prosím zadání (průtok bioplynu či počet KGJ).')
            E, Q, pocet, nazev = error()
        else: 
            Q_pole = np.zeros((arg,len(nazvy)))
            for i in range (arg):
                V_BP_jed = V_BP/(i+1)
                for j in range (len(nazvy)):
                    if df.iat[2*j,3] < V_BP_jed < df.iat[2*j,4]:
                        if regres_koef[2*j+1][0] == 0:
                            regres_koef[2*j+1] = df.values[2*j+1,range(3)]
                        Q_pole[i][j] = (i+1)*func(V_BP_jed,*regres_koef[2*j+1])
            ind = np.unravel_index(np.argmax(Q_pole, axis=None), Q_pole.shape)
            pocet = ind[0]+1
            
            
            regres_koef[2*ind[1]] = df.values[2*ind[1],range(3)]
            E = pocet*func(V_BP/pocet,*regres_koef[2*ind[1]])        
            Q = Q_pole[ind]
            nazev = nazvy[ind[1]]  

    # Nakonec jsou vypocteny: elektricka ucinnost, tepelna ucinnost, celkova
    # ucinnost.
    ucinnosti.append(E/FuelInp*100)
    ucinnosti.append(Q/FuelInp*100)
    ucinnosti.append(ucinnosti[0]+ucinnosti[1])
    # Vystupem funkce jsou pak: elektricky vykon, tepelny vykon, pocet KGJ,
    # nazev pouziteho modelu a pole ucinnosti stanovenych vyse.
    return E, Q, pocet, nazev, ucinnosti