import FINE as fn
import numpy as np
import pandas as pd

np.random.seed(42)        # Sets a "seed" to produce the same random input data in each model run

def test_exceededLifetime():
    # load a minimal test system
    """Returns minimal instance of esM"""

    numberOfTimeSteps = 4
    hoursPerTimeStep = 2190

    # Create an energy system model instance 
    esM = fn.EnergySystemModel(locations={'OneLocation'}, 
                                commodities={'electricity', 'hydrogen'}, 
                                numberOfTimeSteps=numberOfTimeSteps,
                                commodityUnitsDict={'electricity': r'kW$_{el}$', 'hydrogen': r'kW$_{H_{2},LHV}$'},
                                hoursPerTimeStep=hoursPerTimeStep, costUnit='1 Euro', 
                                lengthUnit='km', 
                                verboseLogLevel=2)

    ### Buy electricity at the electricity market
    costs = pd.DataFrame([np.array([ 0.05, 0., 0.1, 0.051,])],
                            index = ['OneLocation']).T
    revenues = pd.DataFrame([np.array([ 0., 0.01, 0., 0.,])],
                            index = ['OneLocation']).T
    maxpurchase = pd.DataFrame([np.array([1e6, 1e6, 1e6, 1e6,])],
                            index = ['OneLocation']).T * hoursPerTimeStep
    esM.add(fn.Source(esM=esM, name='Electricity market', commodity='electricity', 
                        hasCapacityVariable=False, operationRateMax = maxpurchase,
                        commodityCostTimeSeries = costs,  
                        commodityRevenueTimeSeries = revenues,  
                        )) # eur/kWh

    ### Electrolyzers
    esM.add(fn.Conversion(esM=esM, name='Electrolyzers', physicalUnit=r'kW$_{el}$',
                          commodityConversionFactors={'electricity':-1, 'hydrogen':0.7},
                          hasCapacityVariable=True, 
                          investPerCapacity=500, # euro/kW
                          opexPerCapacity=500*0.025, 
                          interestRate=0.08,
                          economicLifetime=10))

    ### Hydrogen filled somewhere
    esM.add(fn.Storage(esM=esM, name='Pressure tank', commodity='hydrogen',
                       hasCapacityVariable=True, capacityVariableDomain='continuous',
                       stateOfChargeMin=0.33, 
                       investPerCapacity=0.5, # eur/kWh
                       interestRate=0.08,
                       economicLifetime=30))

    ### Industry site
    demand = pd.DataFrame([np.array([6e3, 6e3, 6e3, 6e3,])],
                    index = ['OneLocation']).T * hoursPerTimeStep
    esM.add(fn.Sink(esM=esM, name='Industry site', commodity='hydrogen', hasCapacityVariable=False,
                    operationRateFix = demand,
                    ))

    # Set the technical lifetime of the electrolyzers to 7 years.
    setattr(esM.componentModelingDict['ConversionModel'].componentsDict['Electrolyzers'], 'technicalLifetime', pd.Series([7], index=['OneLocation']))

    results = fn.optimizeSimpleMyopic(esM, startYear=2020, endYear=2030, nbOfRepresentedYears=5, timeSeriesAggregation=False, 
                                        solver='glpk', saveResults=False, trackESMs=True)

    # Check if electrolyzers which are installed in 2020 are not included in the system of 2030 due to the exceeded lifetime
    assert 'Electrolyzers_stock_2020' not in results['ESM_2030'].componentNames.keys()


def test_CO2ReductionTargets():
    locations = {'regionN', 'regionS'}
    commodityUnitDict = {'electricity': r'GW$_{el}$', 'naturalGas': r'GW$_{CH_{4},LHV}$',
                        'CO2': r'Mio. t$_{CO_2}$/h'}
    commodities = {'electricity', 'naturalGas', 'CO2'}
    numberOfTimeSteps, hoursPerTimeStep = 8760, 1
    costUnit, lengthUnit = '1e6 Euro', 'km'
    CO2_reductionTarget = 0.8

    esM = fn.EnergySystemModel(locations=locations, commodities=commodities,
        numberOfTimeSteps=numberOfTimeSteps, commodityUnitsDict=commodityUnitDict,
        hoursPerTimeStep=hoursPerTimeStep, costUnit=costUnit, lengthUnit=lengthUnit, verboseLogLevel=0)


    # Add Source Components
    ## Wind turbines
    name, commodity ='Wind turbines', 'electricity'
    hasCapacityVariable = True
    operationRateMax = pd.DataFrame([[np.random.beta(a=2,b=7.5),np.random.beta(a=2,b=9)]
                                    for t in range(8760)],
                                    index=range(8760), columns=['regionN', 'regionS']).round(6)
    capacityMax = pd.Series([400, 200], index=['regionN', 'regionS'])
    investPerCapacity, opexPerCapacity = 1200, 1200*0.02
    interestRate, economicLifetime = 0.08, 20

    esM.add(fn.Source(esM=esM, name=name, commodity=commodity, hasCapacityVariable=hasCapacityVariable,
        operationRateMax=operationRateMax, capacityMax=capacityMax, investPerCapacity=investPerCapacity,
        opexPerCapacity=opexPerCapacity, interestRate=interestRate, economicLifetime=economicLifetime))

    ## PV
    name, commodity ='PV', 'electricity'
    hasCapacityVariable = True
    dailyProfileSimple = [0,0,0,0,0,0,0,0.05,0.15,0.2,0.4,0.8,0.7,0.4,0.2,0.15,0.05,0,0,0,0,0,0,0]
    operationRateMax = pd.DataFrame([[u,u] for day in range(365) for u in dailyProfileSimple],
                                    index=range(8760), columns=['regionN', 'regionS'])
    capacityMax = pd.Series([100, 100], index=['regionN', 'regionS'])
    investPerCapacity, opexPerCapacity = 800, 800*0.02
    interestRate, economicLifetime = 0.08, 25

    esM.add(fn.Source(esM=esM, name=name, commodity=commodity, hasCapacityVariable=hasCapacityVariable,
        operationRateMax=operationRateMax, capacityMax=capacityMax, investPerCapacity=investPerCapacity,
        opexPerCapacity=opexPerCapacity, interestRate=interestRate, economicLifetime=economicLifetime))

    # Natural Gas
    name, commodity ='Natural gas import', 'naturalGas'
    hasCapacityVariable = False
    commodityCost = 0.03

    esM.add(fn.Source(esM=esM, name=name, commodity=commodity, hasCapacityVariable=hasCapacityVariable,
        commodityCost=commodityCost))

    # Add Conversion components
    # Gas power plants
    name, physicalUnit = 'Gas power plants', r'GW$_{el}$'
    commodityConversionFactors = {'electricity':1, 'naturalGas':-1/0.63, 'CO2':201*1e-6/0.63}
    hasCapacityVariable=True
    investPerCapacity, opexPerCapacity = 650, 650*0.03
    interestRate, economicLifetime = 0.08, 30

    esM.add(fn.Conversion(esM=esM, name=name, physicalUnit=physicalUnit,
        commodityConversionFactors=commodityConversionFactors, hasCapacityVariable=hasCapacityVariable,
        investPerCapacity=investPerCapacity, opexPerCapacity=opexPerCapacity, 
        interestRate=interestRate, economicLifetime=economicLifetime))

    # Storage Components
    ## Batteries
    name, commodity = 'Batteries', 'electricity'
    hasCapacityVariable=True
    chargeEfficiency, dischargeEfficiency, selfDischarge = 0.95, 0.95, 1-(1-0.03)**(1/(30*24))
    chargeRate, dischargeRate = 1, 1
    investPerCapacity, opexPerCapacity = 150, 150*0.01
    interestRate, economicLifetime, cyclicLifetime = 0.08, 22, 10000

    esM.add(fn.Storage(esM=esM, name=name, commodity=commodity, hasCapacityVariable=hasCapacityVariable,
        chargeEfficiency=chargeEfficiency, cyclicLifetime=cyclicLifetime,
        dischargeEfficiency=dischargeEfficiency, selfDischarge=selfDischarge, chargeRate=chargeRate,
        dischargeRate=dischargeRate, investPerCapacity=investPerCapacity,
        opexPerCapacity=opexPerCapacity, interestRate=interestRate, economicLifetime=economicLifetime))

    # Transmission Components
    ## AC cables
    name, commodity ='AC cables', 'electricity'
    hasCapacityVariable = True
    capacityFix = pd.DataFrame([[0, 30], [30, 0]], columns=['regionN', 'regionS'],
        index=['regionN', 'regionS'])
    distances = pd.DataFrame([[0, 400], [400, 0]], columns=['regionN', 'regionS'],
        index=['regionN', 'regionS'])
    losses=0.0001

    esM.add(fn.Transmission(esM=esM, name=name, commodity=commodity,
        hasCapacityVariable=hasCapacityVariable, capacityFix=capacityFix,
        distances=distances, losses=losses))

    # Sink Components
    ## Electricity Demand
    name, commodity ='Electricity demand', 'electricity',
    hasCapacityVariable = False
    dailyProfileSimple = [0.6,0.6,0.6,0.6,0.6,0.7,0.9,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.9,0.8]
    operationRateFix = pd.DataFrame([[(u+0.1*np.random.rand())*25,(u+0.1*np.random.rand())*40]
                                    for day in range(365) for u in dailyProfileSimple],
                                    index=range(8760), columns=['regionN', 'regionS']).round(2)

    esM.add(fn.Sink(esM=esM, name=name, commodity=commodity, hasCapacityVariable=hasCapacityVariable,
        operationRateFix=operationRateFix))

    # CO2 to environment
    name, commodity ='CO2 to environment', 'CO2',
    hasCapacityVariable=False
    commodityLimitID, yearlyLimit ='CO2 limit', 366*(1-CO2_reductionTarget)

    if yearlyLimit > 0:
        esM.add(fn.Sink(esM=esM, name=name, commodity=commodity,
            hasCapacityVariable=hasCapacityVariable, commodityLimitID=commodityLimitID,
            yearlyLimit=yearlyLimit))

    # Optimize the system with simple myopic approach
    results = fn.optimizeSimpleMyopic(esM, startYear=2020, nbOfSteps=2, nbOfRepresentedYears=5, CO2Reference=366, CO2ReductionTargets=[25,50,100], 
                                        saveResults=False, trackESMs=True, numberOfTypicalPeriods=3, solver='glpk')

    assert results['ESM_2025'].getOptimizationSummary('SourceSinkModel').loc['CO2 to environment'].loc["operation", "[Mio. t$_{CO_2}$/h*h/a]"].sum() < 183

    assert results['ESM_2030'].getOptimizationSummary('SourceSinkModel').loc['CO2 to environment'].loc["operation", "[Mio. t$_{CO_2}$/h*h/a]"].sum() == 0
