def test_init_full_load_hours(minimal_test_esM):
    import FINE as fn
    import pandas as pd

    # load minimal test system
    esM = minimal_test_esM

    # add a component with yearly minimal load hours
    esM.add(
        fn.Conversion(
            esM=esM,
            name="Electrolyzers_minFLH",
            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,
            yearlyFullLoadHoursMin=5000,
        )
    )

    full_load_hours_min = esM.getComponent(
        "Electrolyzers_minFLH"
    ).yearlyFullLoadHoursMin
    full_load_hours_max = esM.getComponent(
        "Electrolyzers_minFLH"
    ).yearlyFullLoadHoursMax

    assert isinstance(full_load_hours_min, pd.Series)
    assert full_load_hours_max is None
def test_conversion_factors_as_series():
    """
    Input as pandas.Series for one location.
    """

    esM = create_core_esm()

    esM.add(
        fn.Conversion(
            esM=esM,
            name="Electrolyzers_VarConvFac",
            physicalUnit=r"kW$_{el}$",
            commodityConversionFactors=pd.Series(
                [0.7, -1], index=["hydrogen", "electricity"]
            ),  # Here we add a Series of time invariant conversion factors.
            hasCapacityVariable=True,
            investPerCapacity=1000,  # euro/kW
            opexPerCapacity=500 * 0.025,
            interestRate=0.08,
            capacityMax=1000,
            economicLifetime=10,
            locationalEligibility=pd.Series([1], ["ElectrolyzerLocation"]),
        )
    )

    # optimize
    esM.optimize(timeSeriesAggregation=False, solver="glpk")
Exemple #3
0
def test_QPinvest():

    numberOfTimeSteps = 4
    hoursPerTimeStep = 2190

# Create an energy system model instance 
    esM = fn.EnergySystemModel(locations={'location1'}, 
                                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)

# time step length [h]
    timeStepLength = numberOfTimeSteps * hoursPerTimeStep


### Buy electricity at the electricity market
    costs = pd.Series([0.5,0.4,0.2,0.5], index=[0,1,2,3])
    esM.add(fn.Source(esM=esM, name='Electricity market', commodity='electricity', 
                        hasCapacityVariable=False, commodityCostTimeSeries = costs, 
                        )) # euro/kWh

### Electrolyzers
    esM.add(fn.Conversion(esM=esM, name='Electrolyzer', 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, QPcostScale=0.1, 
                            capacityMin=0, capacityMax=10))

### Industry site
    demand = pd.Series([10000., 10000., 10000., 10000.], index = [0,1,2,3])
    esM.add(fn.Sink(esM=esM, name='Industry site', commodity='hydrogen', hasCapacityVariable=False,
                    operationRateFix = demand,
                    ))

### Optimize (just executed if gurobi is installed)

    flag=True
    try:
        esM.optimize(timeSeriesAggregation=False, solver = 'gurobi')
    except:
        flag=False

    if flag:
        invest = round(esM.getOptimizationSummary('ConversionModel').loc['Electrolyzer']. \
            loc['invest']['location1'].values.astype(float)[0],3)
        assert invest == 3148.179
def test_linkedQuantityID(minimal_test_esM):
    """ """
    esM = minimal_test_esM

    # get components
    electrolyzer = esM.getComponent("Electrolyzers")
    market = esM.getComponent("Electricity market")

    # create dummy component
    esM.add(
        fn.Conversion(
            esM=esM,
            name="Dummy",
            physicalUnit=r"kW$_{el}$",
            commodityConversionFactors={"electricity": -1, "electricity": 1},
            hasCapacityVariable=True,
            capacityPerPlantUnit=1.0,
            opexPerCapacity=1.0,
            linkedQuantityID="test",
        )
    )

    # make electrolyzer sizing discrete
    electrolyzer.capacityPerPlantUnit = 1
    electrolyzer.linkedQuantityID = "test"
    electrolyzer.opexPerCapacity = pd.Series(1, index=esM.locations)

    # optimize
    esM.optimize(timeSeriesAggregation=False, solver="glpk")

    assert (
        esM.getOptimizationSummary("ConversionModel")
        .loc["Electrolyzers"]
        .loc["opexCap"]["ElectrolyzerLocation"]
        .values.astype(int)[0]
        == esM.getOptimizationSummary("ConversionModel")
        .loc["Dummy"]
        .loc["opexCap"]["ElectrolyzerLocation"]
        .values.astype(int)[0]
    )
def test_conversionPartLoad():

    # Set up energy system model instance
    locations = {'GlassProductionSite'}
    commodities = {'electricity', 'heat', 'hydrogen', 'O2', 'CO2','rawMaterial'}
    commodityUnitDict = {'electricity': r'kW$_{el}$', 'heat':r'kW$_{heat}$}', 'hydrogen':r'kW$_{H2}$',
                        'O2': r'kg$_{O_{2}}$/h', 'CO2': r'kg$_{CO_{2}}$/h', 'rawMaterial': r'kg$_{R}}$/h'}
    numberOfTimeSteps=80
    hoursPerTimeStep=0.25

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


    ### Sources ###

    # Electricity from grid
    esM.add(fn.Source(esM=esM, name='ElectricityGrid', commodity='electricity', hasCapacityVariable=False,
                    commodityCost=0.070))
    # Oxygen source from trailer
    esM.add(fn.Source(esM=esM, name='oxygenSource', commodity='O2', hasCapacityVariable=False,
                    commodityCost=0.070))
    # Raw material for glass furnace
    esM.add(fn.Source(esM=esM, name='rawMaterialSource', commodity='rawMaterial', hasCapacityVariable=False,
                    commodityCost=0.20))

    ### Conversion ###

    # PEM Electrolyzer
    # Get partLoadData from EC Campus Mainz
    Utilization = [0.0208023774145616,0.0222882615156017,0.0222882615156017,0.0222882615156017,0.025260029717682,
                    0.025260029717682,0.0267459138187221,0.0282317979197622,0.0297176820208024,0.0312035661218424,
                    0.0326894502228826,0.0341753343239227,0.0356612184249628,0.0371471025260029,0.038632986627043,
                    0.0416047548291233,0.0430906389301634,0.0445765230312035,0.0475482912332838,0.0430906389301634,
                    0.0490341753343239,0.050520059435364,0.0520059435364041,0.0549777117384844,0.0579494799405646,
                    0.0609212481426448,0.0638930163447251,0.0683506686478455,0.0713224368499256,0.075780089153046,
                    0.0787518573551263,0.0832095096582466,0.087667161961367,0.0921248142644873,0.0980683506686478,
                    0.104011887072808,0.108469539375928,0.114413075780089,0.120356612184249,0.12778603268945,
                    0.13521545319465,0.142644873699851,0.151560178306092,0.157503714710252,0.166419019316493,
                    0.173848439821693,0.181277860326894,0.190193164933135,0.196136701337295,0.202080237741456,
                    0.209509658246656,0.216939078751857,0.224368499257057,0.233283803863298,0.240713224368499,
                    0.246656760772659,0.25408618127786,0.26151560178306,0.270430906389301,0.276374442793462,
                    0.283803863298662,0.292719167904903,0.298662704309063,0.306092124814264,0.313521545319465,
                    0.319465081723625,0.328380386329866,0.335809806835066,0.343239227340267,0.349182763744427,
                    0.356612184249628,0.364041604754829,0.371471025260029,0.38038632986627,0.389301634472511,
                    0.398216939078751,0.407132243684992,0.416047548291233,0.423476968796433,0.430906389301634,
                    0.439821693907875,0.448736998514115,0.457652303120356,0.465081723625557,0.472511144130757,
                    0.481426448736998,0.490341753343239,0.497771173848439,0.50668647845468,0.514115898959881,
                    0.523031203566121,0.531946508172362,0.540861812778603,0.551263001485884,0.560178306092124,
                    0.567607726597325,0.576523031203566,0.585438335809806,0.595839524517087,0.604754829123328,
                    0.613670133729569,0.619613670133729,0.62852897473997,0.638930163447251,0.646359583952451,
                    0.656760772659732,0.664190193164933,0.673105497771173,0.684992570579494,0.692421991084695,
                    0.699851411589896,0.710252600297176,0.717682020802377,0.726597325408618,0.735512630014858,
                    0.744427934621099,0.75334323922734,0.762258543833581,0.769687964338781,0.780089153046062,
                    0.786032689450223,0.793462109955423,0.802377414561664,0.811292719167904,0.820208023774145,
                    0.829123328380386,0.838038632986627,0.846953937592867,0.858841010401188,0.867756315007429,
                    0.87667161961367,0.88707280832095,0.895988112927191,0.904903417533432,0.913818722139673,
                    0.924219910846954,0.933135215453194,0.942050520059435,0.947994056463595,0.958395245170876,
                    0.965824665676077,0.974739970282318,0.985141158989598,0.994056463595839]
    Efficiency = [0.03449362655834178,0.05655553999159559,0.04920156884717763,0.07616612971004356,0.09332539571368476,
                0.115387309146939,0.13254657515058135,0.1497058411542229,0.1693164308726712,0.1864756968763127,
                0.2036349628799551,0.2256968763132085,0.2404048186020449,0.25511276089088053,0.27472335060932884,
                0.2918826166129703,0.30904188261661275,0.33110379604986695,0.34581173833870255,0.3212985011906424,
                0.3629710043423449,0.38013027034598645,0.3923868889200161,0.4095461549236585,0.42425409721249496,
                0.4365107157865246,0.44876733436055427,0.45857262921977887,0.4683779240790026,0.4830858663678382,
                0.4953424849418686,0.5075991035158983,0.517404398375122,0.5247583695195407,0.5321123406639585,
                0.5419176355231823,0.5517229303824059,0.5639795489564365,0.5713335201008543,0.5811388149600779,
                0.5884927861044958,0.5933954335341085,0.5982980809637204,0.6007494046785262,0.6007494046785262,
                0.6007494046785262,0.5982980809637204,0.5982980809637204,0.5958467572489144,0.5958467572489144,
                0.5958467572489144,0.5933954335341085,0.5933954335341085,0.5933954335341085,0.5909441098193018,
                0.5909441098193018,0.5909441098193018,0.5884927861044958,0.5884927861044958,0.5884927861044958,
                0.5884927861044958,0.5884927861044958,0.5884927861044958,0.5860414623896899,0.5860414623896899,
                0.5860414623896899,0.583590138674884,0.583590138674884,0.583590138674884,0.5811388149600779,
                0.5811388149600779,0.5786874912452721,0.5762361675304661,0.5762361675304661,0.5737848438156602,
                0.5737848438156602,0.5713335201008543,0.5713335201008543,0.5688821963860483,0.5688821963860483,
                0.5664308726712425,0.5664308726712425,0.5639795489564365,0.5639795489564365,0.5615282252416306,
                0.5590769015268245,0.5590769015268245,0.5590769015268245,0.5566255778120178,0.5566255778120178,
                0.5541742540972119,0.5541742540972119,0.5517229303824059,0.5517229303824059,0.5517229303824059,
                0.5492716066676,0.5492716066676,0.5492716066676,0.5492716066676,0.5468202829527942,
                0.5443689592379882,0.5443689592379882,0.5443689592379882,0.5443689592379882,0.5419176355231823,
                0.5394663118083762,0.5394663118083762,0.5370149880935703,0.5370149880935703,0.5345636643787645,
                0.5345636643787645,0.5321123406639585,0.5321123406639585,0.5296610169491526,0.5296610169491526,
                0.5272096932343466,0.5272096932343466,0.5272096932343466,0.5247583695195399,0.5247583695195399,
                0.522307045804734,0.522307045804734,0.522307045804734,0.522307045804734,0.519855722089928,
                0.519855722089928,0.519855722089928,0.517404398375122,0.5149530746603161,0.5125017509455102,
                0.5125017509455102,0.5100504272307043,0.5100504272307043,0.5075991035158983,0.5051477798010924,
                0.5051477798010924,0.5051477798010924,0.5026964560862865,0.5026964560862865,0.5026964560862865,
                0.5026964560862865,0.5002451323714806,0.5002451323714806,0.49779380865667455]
    d = {'x':Utilization, 'y':Efficiency}
    partLoadData = pd.DataFrame(d)
    partLoadData
    nSegments = 4
    esM.add(fn.ConversionPartLoad(esM=esM, name='PEMEC', physicalUnit=r'kW$_{el}$',
                            commodityConversionFactors={'electricity':-1, 'hydrogen':1},
                            commodityConversionFactorsPartLoad={'electricity':-1, 'hydrogen': partLoadData},
                            nSegments=nSegments,
                            hasCapacityVariable=True, 
                            bigM=99999,
                            investPerCapacity=900, opexPerCapacity=900*0.01, interestRate=0.08,
                            economicLifetime=10))

    # Glass production - Hydrogen gas furnace
    capacityFix = 4985
    annualLoss = 0.03 #After one year a glass melting furnace needs 3% more energy to maintain process quality due to increasing thermal losses
    operationRateFix = pd.DataFrame(np.linspace(capacityFix*(1-annualLoss/2),capacityFix*(1+annualLoss/2),num=numberOfTimeSteps),columns=['GlassProductionSite'])
    operationRateFix.mean()
    operationRateFix = operationRateFix/operationRateFix.mean()
    esM.add(fn.Conversion(esM=esM, name='hydrogenGasFurnace', physicalUnit= r'kW$_{H2}$',
                        commodityConversionFactors={'hydrogen':-1, 'electricity':-0.020,'O2': -0.137, # stochiometric combustion: -0.238; lambda: -0.274
                        'rawMaterial': -0.209, 'heat':0.070, 'CO2':0.020}, # 'CO2':0.040 for H2 with german grid; 'CO2':0.021 for 100% renewable electricity
                        hasCapacityVariable=True, capacityFix = capacityFix,
                        operationRateFix=operationRateFix,
                        investPerCapacity=1103.5, opexPerCapacity=652, interestRate=0.08,
                        economicLifetime=10))


    ### Sinks ###

    # Heat output
    esM.add(fn.Sink(esM=esM, name='Heat output', commodity='heat', hasCapacityVariable=False))
    # CO2 output
    esM.add(fn.Sink(esM=esM, name='CO2 output', commodity='CO2', hasCapacityVariable=False))
    # O2 output
    # We include this sink to enable feasibility of the optimization problem in case the electrolyzer produces slightly more oxygen than required in the hydrogen furnace (round-off error < 1%) - we only that for stochiometric combustion -> for lambda > 1 that shouldn't be necessary
    esM.add(fn.Sink(esM=esM, name='O2 output', commodity='O2', hasCapacityVariable=False))

    ### Optimization ###
    # Input parameters
    timeSeriesAggregation=False
    solver='glpk'
    # Code
    esM.optimize(timeSeriesAggregation=timeSeriesAggregation, solver=solver)


    ### Test ###
    # Overall TAC
    srcSnkSummary = esM.getOptimizationSummary("SourceSinkModel", outputLevel=1)
    convSummary = esM.getOptimizationSummary("ConversionModel", outputLevel=1)
    convPartloadSummary = esM.getOptimizationSummary("ConversionPartLoadModel", outputLevel=1)
    TAC=(srcSnkSummary.loc[('ElectricityGrid', 'TAC', '[1 Euro/a]'),'GlassProductionSite']+ 
        srcSnkSummary.loc[('oxygenSource', 'TAC', '[1 Euro/a]'),'GlassProductionSite']+ 
        srcSnkSummary.loc[('rawMaterialSource', 'TAC', '[1 Euro/a]'),'GlassProductionSite']+ 
        convSummary.loc[('hydrogenGasFurnace', 'TAC', '[1 Euro/a]'),'GlassProductionSite']+ 
        convPartloadSummary.loc[('PEMEC', 'TAC', '[1 Euro/a]'),'GlassProductionSite'])
    np.testing.assert_allclose(TAC, 14016197.2088, rtol = 0.005) # relative toerlance < 0.5%
    # Electricity TAC
    np.testing.assert_allclose(srcSnkSummary.loc[('ElectricityGrid', 'TAC', '[1 Euro/a]'),'GlassProductionSite'], 6.23855e+06, rtol = 0.01) # relative toerlance < 1%
    # PEMEC summary
    np.testing.assert_allclose(convPartloadSummary.loc[('PEMEC', 'TAC', '[1 Euro/a]'),'GlassProductionSite'], 1.46349e+06, rtol = 0.002) # relative toerlance < 0.2%
    np.testing.assert_allclose(convPartloadSummary.loc[('PEMEC', 'capacity', '[kW$_{el}$]'),'GlassProductionSite'], 10225.1729065, rtol = 0.002) # relative toerlance < 0.2%
    np.testing.assert_allclose(convPartloadSummary.loc[('PEMEC', 'capexCap', '[1 Euro/a]'),'GlassProductionSite'], 1371467.06108539, rtol = 0.002) # relative toerlance < 0.2%
    np.testing.assert_allclose(convPartloadSummary.loc[('PEMEC', 'invest', '[1 Euro]'),'GlassProductionSite'], 9202655.61585, rtol = 0.002) # relative toerlance < 0.2%
    np.testing.assert_allclose(convPartloadSummary.loc[('PEMEC', 'opexCap', '[1 Euro/a]'),'GlassProductionSite'], 92026.5561585, rtol = 0.002) # relative toerlance < 0.2%
    np.testing.assert_allclose(convPartloadSummary.loc[('PEMEC', 'operation', '[kW$_{el}$*h/a]'),'GlassProductionSite'], 8.82488e+07, rtol = 0.01) # relative toerlance < 1%
    # PEM operation results 
    opVarOptPartLoad = esM.componentModelingDict["ConversionPartLoadModel"].operationVariablesOptimum.loc[('PEMEC', 'GlassProductionSite')]
    opVarOptConstLoad = [2480.73776181,2481.6941601,2482.65055839,2483.60695668,2484.56335497,2485.51975325,2486.47615154,
                        2487.43254983,2488.38894812,2489.34534641,2490.3017447,2491.25814299,2492.21454128,2493.17093957,2494.12733785,
                        2495.08373614,2496.04013443,2496.99653272,2497.95293101,2498.9093293,2499.86572759,2500.82212588,
                        2501.77852417,2502.73492245,2503.69132074,2504.64771903,2505.60411732,2506.56051561,2507.5169139,
                        2508.47331219,2509.42971048,2510.38610877,2511.34250706,2512.29890534,2513.25530363,2514.21170192,
                        2515.16810021,2516.1244985,2517.08089679,2518.03729508,2518.99369337,2519.95009166,2520.90648994,
                        2521.86288823,2522.81928652,2523.77568481,2524.7320831,2525.68848139,2526.64487968,2527.60127797,
                        2528.55767626,2529.51407455,2530.47047283,2531.42687112,2532.38326941,2533.3396677,2534.29606599,
                        2535.25246428,2536.20886257,2537.16526086,2538.12165915,2539.07805743,2540.03445572,2540.99085401,
                        2541.9472523,2542.90365059,2543.86004888,2544.81644717,2545.77284546,2546.72924375,2547.68564204,
                        2548.64204032,2549.59843861,2550.5548369,2551.51123519,2552.46763348,2553.42403177,2554.38043006,
                        2555.33682835,2556.29322664]
    np.testing.assert_allclose(opVarOptPartLoad, opVarOptConstLoad,rtol=0.01)
Exemple #6
0
def test_watersupply():

    # read in original results
    results = pd.read_csv(os.path.join(
        os.path.dirname(__file__), '_testInputFiles',
        'waterSupplySystem_totalTransmission.csv'),
                          index_col=[0, 1, 2],
                          header=None,
                          squeeze=True)

    # get parameters
    locations = [
        'House 1', 'House 2', 'House 3', 'House 4', 'House 5', 'House 6',
        'Node 1', 'Node 2', 'Node 3', 'Node 4', 'Water treatment', 'Water tank'
    ]
    commodityUnitDict = {'clean water': 'U', 'river water': 'U'}
    commodities = {'clean water', 'river water'}

    esM = fn.EnergySystemModel(locations=set(locations),
                               commodities=commodities,
                               numberOfTimeSteps=8760,
                               commodityUnitsDict=commodityUnitDict,
                               hoursPerTimeStep=1,
                               costUnit='1e3 Euro',
                               lengthUnit='m')

    starttime = time.time()

    # Source
    riverFlow = pd.DataFrame(np.zeros((8760, 12)), columns=locations)
    np.random.seed(42)
    riverFlow.loc[:, 'Water treatment'] = np.random.uniform(
        0, 4, (8760)) + 8 * np.sin(np.pi * np.arange(8760) / 8760)
    esM.add(
        fn.Source(esM=esM,
                  name='River',
                  commodity='river water',
                  hasCapacityVariable=False,
                  operationRateMax=riverFlow,
                  opexPerOperation=0.05))

    # Conversion
    eligibility = pd.Series([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
                            index=locations)
    esM.add(
        fn.Conversion(esM=esM,
                      name='Water treatment plant',
                      physicalUnit='U',
                      commodityConversionFactors={
                          'river water': -1,
                          'clean water': 1
                      },
                      hasCapacityVariable=True,
                      locationalEligibility=eligibility,
                      investPerCapacity=7,
                      opexPerCapacity=0.02 * 7,
                      interestRate=0.08,
                      economicLifetime=20))

    # Storage
    eligibility = pd.Series([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                            index=locations)
    esM.add(
        fn.Storage(esM=esM,
                   name='Water tank',
                   commodity='clean water',
                   hasCapacityVariable=True,
                   chargeRate=1 / 24,
                   dischargeRate=1 / 24,
                   locationalEligibility=eligibility,
                   investPerCapacity=0.10,
                   opexPerCapacity=0.02 * 0.1,
                   interestRate=0.08,
                   economicLifetime=20))

    # Transmission
    distances = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0],
                          [0, 0, 0, 0, 38, 40, 0, 105, 0, 0, 0, 0],
                          [0, 0, 38, 40, 0, 0, 105, 0, 100, 0, 0, 0],
                          [38, 40, 0, 0, 0, 0, 0, 100, 0, 30, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 20, 50],
                          [0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0]])

    distances = pd.DataFrame(distances, index=locations, columns=locations)

    # Old water pipes
    capacityFix = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0],
                            [1, 1, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 4],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0]])

    capacityFix = pd.DataFrame(capacityFix, index=locations, columns=locations)

    isBuiltFix = capacityFix.copy()
    isBuiltFix[isBuiltFix > 0] = 1

    esM.add(
        fn.Transmission(esM=esM,
                        name='Old water pipes',
                        commodity='clean water',
                        losses=0.1e-2,
                        distances=distances,
                        hasCapacityVariable=True,
                        hasIsBuiltBinaryVariable=True,
                        bigM=100,
                        capacityFix=capacityFix,
                        isBuiltFix=isBuiltFix))

    # New water pipes
    incidence = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
                          [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
                          [0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0],
                          [0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0],
                          [1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1],
                          [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
                          [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]])

    eligibility = pd.DataFrame(incidence, index=locations, columns=locations)

    esM.add(
        fn.Transmission(esM=esM,
                        name='New water pipes',
                        commodity='clean water',
                        losses=0.05e-2,
                        distances=distances,
                        hasCapacityVariable=True,
                        hasIsBuiltBinaryVariable=True,
                        bigM=100,
                        locationalEligibility=eligibility,
                        investPerCapacity=0.1,
                        investIfBuilt=0.5,
                        interestRate=0.08,
                        economicLifetime=50))

    # Sink
    winterHours = np.append(range(8520, 8760), range(1920))
    springHours, summerHours, autumnHours = np.arange(1920, 4128), np.arange(
        4128, 6384), np.arange(6384, 8520)

    demand = pd.DataFrame(np.zeros((8760, 12)), columns=list(locations))
    np.random.seed(42)
    demand[locations[0:6]] = np.random.uniform(0, 1, (8760, 6))

    demand.loc[winterHours[(winterHours % 24 < 5) |
                           (winterHours % 24 >= 23)]] = 0
    demand.loc[springHours[(springHours % 24 < 4)]] = 0
    demand.loc[summerHours[(summerHours % 24 < 5) |
                           (summerHours % 24 >= 23)]] = 0
    demand.loc[autumnHours[(autumnHours % 24 < 6) |
                           (autumnHours % 24 >= 23)]] = 0
    esM.add(
        fn.Sink(esM=esM,
                name='Water demand',
                commodity='clean water',
                hasCapacityVariable=False,
                operationRateFix=demand))

    # # Optimize the system
    esM.cluster(numberOfTypicalPeriods=7)
    esM.optimize(
        timeSeriesAggregation=True,
        solver='glpk',
        optimizationSpecs='LogToConsole=1 OptimalityTol=1e-6 crossover=1')

    # # Selected results output
    esM.getOptimizationSummary("SourceSinkModel", outputLevel=2)

    # ### Storage
    esM.getOptimizationSummary("StorageModel", outputLevel=2)

    # ### Transmission
    esM.getOptimizationSummary("TransmissionModel", outputLevel=2)
    esM.componentModelingDict[
        "TransmissionModel"].operationVariablesOptimum.sum(axis=1)

    #
    testresults = esM.componentModelingDict[
        "TransmissionModel"].operationVariablesOptimum.sum(axis=1)

    print('Optimization took ' + str(time.time() - starttime))

    # test if here solved fits with original results
    np.testing.assert_array_almost_equal(testresults.values,
                                         results.values,
                                         decimal=2)
# # 4. Add conversion components to the energy system model

# %% [markdown]
# ### Boiler

# %%
esM.add(
    fn.Conversion(
        esM=esM,
        name="Boiler",
        physicalUnit="kW_th",
        commodityConversionFactors={
            "methane": -1.1,
            "heat": 1
        },
        hasIsBuiltBinaryVariable=True,
        hasCapacityVariable=True,
        interestRate=0.04,
        economicLifetime=20,
        investIfBuilt=2800,
        investPerCapacity=100,
        opexIfBuilt=24,
        bigM=200,
    ))

# %% [markdown]
# # 5. Add commodity storages to the energy system model

# %% [markdown]
# ### Thermal Storage
Exemple #8
0
#
# These are the components which can transfer one commodity into another one.

# %% [markdown]
# ## 4.1 Biomas to biogas

# %% [markdown]
# ### Bioslurry to Biogas

# %%
esM.add(
    fn.Conversion(
        esM=esM,
        name="bioslurry-biogas",
        physicalUnit=r"GW$_{CH4}$",
        commodityConversionFactors={
            "bioslurry": -1,
            "biogas": 1
        },
        hasCapacityVariable=False,
    ))

# %% [markdown]
# ### Biowaste to Biogas

# %%
esM.add(
    fn.Conversion(
        esM=esM,
        name="biowaste-biogas",
        physicalUnit=r"GW$_{CH4}$",
        commodityConversionFactors={
def test_CO2Limit():
    # 0) Preprocess energy system model
    locations = {"Region1", "Region2"}
    commodityUnitDict = {
        "electricity": r"MW$_{el}$",
        "methane": r"MW$_{CH_{4},LHV}$",
        "CO2": r"Mio. kg$_{CO_2}$/h",
    }
    commodities = {"electricity", "methane", "CO2"}
    ndays = 30
    nhours = 24 * ndays

    ## Define Electricity Demand
    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,
    ]
    demand = pd.DataFrame(
        [[(u + 0.1 * np.random.rand()) * 40, (u + 0.1 * np.random.rand()) * 60]
         for day in range(ndays) for u in dailyProfileSimple],
        index=range(nhours),
        columns=["Region1", "Region2"],
    ).round(2)

    # 1) Define CO2-Limit with balanceLimitConstraint (sink are defined negative)
    CO2_limit = pd.Series(index=["CO2 limit"])
    CO2_limit.loc["CO2 limit"] = -1 * demand.sum().sum(
    ) * 0.6 * 201 * 1e-6 / 0.6

    # 2) Initialize EnergySystemModel with two Regions
    esM = fn.EnergySystemModel(
        locations=locations,
        commodities=commodities,
        numberOfTimeSteps=nhours,
        commodityUnitsDict=commodityUnitDict,
        hoursPerTimeStep=1,
        costUnit="1e6 Euro",
        lengthUnit="km",
        verboseLogLevel=2,
        balanceLimit=CO2_limit,
        lowerBound=True,
    )

    # 3) Components are added: 'Electricity demand', 'Methane purchase', 'cctg', 'CO2 to environment',
    # 'Fuel Cell', 'Batteries'

    # Define Electricity demand and added to Energysystem
    esM.add(
        fn.Sink(
            esM=esM,
            name="Electricity demand",
            commodity="electricity",
            hasCapacityVariable=False,
            operationRateFix=demand,
        ))
    # Define Methane purchase and added to Energysystem
    esM.add(
        fn.Source(
            esM=esM,
            name="Methane purchase",
            commodity="methane",
            hasCapacityVariable=False,
        ))
    # Define ccgt and added to Energysystem
    esM.add(
        fn.Conversion(
            esM=esM,
            name="ccgt",
            physicalUnit=r"MW$_{el}$",
            commodityConversionFactors={
                "electricity": 1,
                "methane": -1 / 0.6,
                "CO2": 201 * 1e-6 / 0.6,
            },
            hasCapacityVariable=True,
            investPerCapacity=0.65,
            opexPerCapacity=0.021,
            interestRate=0.08,
            economicLifetime=33,
        ))
    # Define CO2 to environment and added to Energysystem
    esM.add(
        fn.Sink(
            esM=esM,
            name="CO2 to environment",
            commodity="CO2",
            hasCapacityVariable=False,
            balanceLimitID="CO2 limit",
        ))
    ## Wind turbines
    # Define Fuel Cell and added to Energysystem
    opexPerOperation = 150 / 1e6
    interestRate, economicLifetime = 0.08, 20
    esM.add(
        fn.Source(
            esM=esM,
            name="Fuel Cell",
            commodity="electricity",
            hasCapacityVariable=False,
            opexPerOperation=opexPerOperation,
            interestRate=interestRate,
            economicLifetime=economicLifetime,
        ))

    # Define Batteries and added to Energysystem
    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="Batteries",
            commodity="electricity",
            hasCapacityVariable=True,
            chargeEfficiency=chargeEfficiency,
            cyclicLifetime=cyclicLifetime,
            dischargeEfficiency=dischargeEfficiency,
            selfDischarge=selfDischarge,
            chargeRate=chargeRate,
            dischargeRate=dischargeRate,
            investPerCapacity=investPerCapacity,
            opexPerCapacity=opexPerCapacity,
            interestRate=interestRate,
            economicLifetime=economicLifetime,
        ))

    # 4) Optimize model
    esM.optimize(timeSeriesAggregation=False, solver="glpk")
    co2_to_environment = 0

    # 5) The CO2_limit is compared to the outcome of the model
    # (sinks are defined negative)
    for i, loc in enumerate(esM.locations):
        # Get operation of Renewables for loc
        co2_to_environment += (
            esM.componentModelingDict["SourceSinkModel"].
            operationVariablesOptimum.loc["CO2 to environment", loc].sum())
    tolerance = 0.001
    ## Compare modeled co2 emissions to limit set in constraint.
    assert co2_to_environment * (1 -
                                 tolerance) < -1 * CO2_limit.loc["CO2 limit"]
    assert co2_to_environment * (1 +
                                 tolerance) > -1 * CO2_limit.loc["CO2 limit"]
def test_balanceLimitConstraint():
    # 0) Preprocess energy system model
    locations = {"Region1", "Region2"}
    commodityUnitDict = {"electricity": r"MW$_{el}$", "heat": r"MW$_{th}$"}
    commodities = {"electricity", "heat"}
    ndays = 30
    nhours = 24 * ndays

    # Electricity Demand
    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,
    ]
    demand = pd.DataFrame(
        [[(u + 0.1 * np.random.rand()) * 40, (u + 0.1 * np.random.rand()) * 60]
         for day in range(ndays) for u in dailyProfileSimple],
        index=range(nhours),
        columns=["Region1", "Region2"],
    ).round(2)
    heat_demand = pd.DataFrame(
        [[(u + 0.1 * np.random.rand()) * 10, (u + 0.1 * np.random.rand()) * 20]
         for day in range(ndays) for u in dailyProfileSimple],
        index=range(nhours),
        columns=["Region1", "Region2"],
    ).round(2)
    # 1) Define balanceLimit constraint in relation to demand in regions
    balanceLimit = pd.DataFrame(columns=["Region1", "Region2"],
                                index=["el", "heat"])
    perNetAutarky = 0.75
    perNetAutarky_h = 1
    balanceLimit.loc["el"] = (1 - perNetAutarky) * demand.sum()
    balanceLimit.loc["heat"] = (1 - perNetAutarky_h) * heat_demand.sum()

    # 2) Initialize esM with two regions
    esM = fn.EnergySystemModel(
        locations=locations,
        commodities=commodities,
        numberOfTimeSteps=nhours,
        commodityUnitsDict=commodityUnitDict,
        hoursPerTimeStep=1,
        costUnit="1e6 Euro",
        lengthUnit="km",
        verboseLogLevel=2,
        balanceLimit=balanceLimit,
    )

    # 3) Components are added: 'Electricity demand', 'Heat demand', 'Electricity purchase', 'Heat purchase',
    # 'Heat pump', 'Wind turbines', 'PV', 'Batteries', 'AC cables', 'Heat pipes'

    # Define Electricity demand and added to Energysystem
    esM.add(
        fn.Sink(
            esM=esM,
            name="Electricity demand",
            commodity="electricity",
            hasCapacityVariable=False,
            operationRateFix=demand,
        ))
    # Define Heat demand and added to Energysystem
    esM.add(
        fn.Sink(
            esM=esM,
            name="Heat demand",
            commodity="heat",
            hasCapacityVariable=False,
            operationRateFix=heat_demand,
        ))

    # Define Cheap purchase 'Electricity purchase' and 'Heat purchase', which incentives to purchase,
    # but is limited because of balanceLimit
    # added to Energysystem
    esM.add(
        fn.Source(
            esM=esM,
            name="Electricity purchase",
            commodity="electricity",
            hasCapacityVariable=False,
            commodityCost=0.001,
            balanceLimitID="el",
        ))
    esM.add(
        fn.Source(
            esM=esM,
            name="Heat purchase",
            commodity="heat",
            hasCapacityVariable=False,
            commodityCost=0.001,
            balanceLimitID="heat",
        ))
    # Define heatpump and added to Energysystem
    esM.add(
        fn.Conversion(
            esM=esM,
            name="heatpump",
            physicalUnit=r"MW$_{el}$",
            commodityConversionFactors={
                "electricity": -1,
                "heat": 2.5,
            },
            hasCapacityVariable=True,
            capacityMax=1e6,
            investPerCapacity=0.95,
            opexPerCapacity=0.95 * 0.01,
            interestRate=0.08,
            economicLifetime=33,
        ))
    # Define Wind turbines and added to Energysystem
    operationRateMax = pd.DataFrame(
        [[np.random.beta(a=2, b=7.5),
          np.random.beta(a=2, b=9)] for t in range(nhours)],
        index=range(nhours),
        columns=["Region1", "Region2"],
    ).round(6)
    capacityMax = pd.Series([400, 200], index=["Region1", "Region2"])
    investPerCapacity, opexPerCapacity = 1200, 1200 * 0.02
    interestRate, economicLifetime = 0.08, 20
    esM.add(
        fn.Source(
            esM=esM,
            name="Wind turbines",
            commodity="electricity",
            hasCapacityVariable=True,
            operationRateMax=operationRateMax,
            capacityMax=capacityMax,
            investPerCapacity=investPerCapacity,
            opexPerCapacity=opexPerCapacity,
            interestRate=interestRate,
            economicLifetime=economicLifetime,
        ))

    # Define PV and added to Energysystem
    operationRateMax = pd.DataFrame(
        [[u, u] for day in range(ndays) for u in dailyProfileSimple],
        index=range(nhours),
        columns=["Region1", "Region2"],
    )
    capacityMax = pd.Series([100, 100], index=["Region1", "Region2"])
    investPerCapacity, opexPerCapacity = 800, 800 * 0.02
    interestRate, economicLifetime = 0.08, 25
    esM.add(
        fn.Source(
            esM=esM,
            name="PV",
            commodity="electricity",
            hasCapacityVariable=True,
            operationRateMax=operationRateMax,
            capacityMax=capacityMax,
            investPerCapacity=investPerCapacity,
            opexPerCapacity=opexPerCapacity,
            interestRate=interestRate,
            economicLifetime=economicLifetime,
        ))

    # Define Batteries and added to Energysystem
    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="Batteries",
            commodity="electricity",
            hasCapacityVariable=True,
            chargeEfficiency=chargeEfficiency,
            cyclicLifetime=cyclicLifetime,
            dischargeEfficiency=dischargeEfficiency,
            selfDischarge=selfDischarge,
            chargeRate=chargeRate,
            dischargeRate=dischargeRate,
            investPerCapacity=investPerCapacity,
            opexPerCapacity=opexPerCapacity,
            interestRate=interestRate,
            economicLifetime=economicLifetime,
        ))

    # Transmission Components
    # Define AC Cables and added to Energysystem
    capacityFix = pd.DataFrame([[0, 30], [30, 0]],
                               columns=["Region1", "Region2"],
                               index=["Region1", "Region2"])
    distances = pd.DataFrame(
        [[0, 400], [400, 0]],
        columns=["Region1", "Region2"],
        index=["Region1", "Region2"],
    )
    losses = 0.0001
    esM.add(
        fn.Transmission(
            esM=esM,
            name="AC cables",
            commodity="electricity",
            hasCapacityVariable=True,
            capacityFix=capacityFix,
            distances=distances,
            losses=losses,
            balanceLimitID="el",
        ))

    # Define Heat pipes and added to Energysystem
    capacityFix = pd.DataFrame([[0, 30], [30, 0]],
                               columns=["Region1", "Region2"],
                               index=["Region1", "Region2"])
    distances = pd.DataFrame(
        [[0, 400], [400, 0]],
        columns=["Region1", "Region2"],
        index=["Region1", "Region2"],
    )
    losses = 0.0001
    esM.add(
        fn.Transmission(
            esM=esM,
            name="Heat pipes",
            commodity="heat",
            hasCapacityVariable=True,
            capacityFix=capacityFix,
            distances=distances,
            losses=losses,
            balanceLimitID="heat",
        ))

    # 4) Optimize model
    esM.optimize(timeSeriesAggregation=False, solver="glpk")

    # 5) The balanceLimit is compared to the outcome of the model
    #   purchase + exchange_in - exchange_out <= balanceLimit
    for i, loc in enumerate(esM.locations):
        # Get Electricity Purchase for location
        el_purchase = (esM.componentModelingDict["SourceSinkModel"].
                       operationVariablesOptimum.loc["Electricity purchase",
                                                     loc].sum())
        heat_purchase = (esM.componentModelingDict["SourceSinkModel"].
                         operationVariablesOptimum.loc["Heat purchase",
                                                       loc].sum())
        # Get Exchange going into region and out of region
        cables = esM.componentModelingDict[
            "TransmissionModel"].operationVariablesOptimum.loc["AC cables"]
        pipes = esM.componentModelingDict[
            "TransmissionModel"].operationVariablesOptimum.loc["Heat pipes"]
        for j, loc_ in enumerate(esM.locations):
            if loc != loc_:
                exch_in = (cables.loc[loc_, loc] *
                           (1 - losses * distances.loc[loc_, loc])).T.sum()
                exch_in_h = (pipes.loc[loc_, loc] *
                             (1 - losses * distances.loc[loc_, loc])).T.sum()
                exch_out = cables.loc[loc, loc_].T.sum()
                exch_out_h = pipes.loc[loc, loc_].T.sum()

        tolerance = 0.001

        ## Compare modelled autarky to limit set in constraint.
        assert (el_purchase + exch_in - exch_out - tolerance <
                balanceLimit.loc["el", loc])
        assert (heat_purchase + exch_in_h - exch_out_h - tolerance <
                balanceLimit.loc["heat", loc])
Exemple #11
0
def getModel():

    # 1. Create an energy system model instance
    locations = {
        "cluster_0",
        "cluster_1",
        "cluster_2",
        "cluster_3",
        "cluster_4",
        "cluster_5",
        "cluster_6",
        "cluster_7",
    }
    commodityUnitDict = {
        "electricity": "GW$_{el}$",
        "methane": "GW$_{CH_{4},LHV}$",
        "biogas": "GW$_{biogas,LHV}$",
        "CO2": "Mio. t$_{CO_2}$/h",
        "hydrogen": "GW$_{H_{2},LHV}$",
    }
    commodities = {"electricity", "hydrogen", "methane", "biogas", "CO2"}

    esM = fn.EnergySystemModel(
        locations=locations,
        commodities=commodities,
        numberOfTimeSteps=8760,
        commodityUnitsDict=commodityUnitDict,
        hoursPerTimeStep=1,
        costUnit="1e9 Euro",
        lengthUnit="km",
        verboseLogLevel=0,
    )

    CO2_reductionTarget = 1

    # 2. Add commodity sources to the energy system model

    ### Wind onshore
    esM.add(
        fn.Source(
            esM=esM,
            name="Wind (onshore)",
            commodity="electricity",
            hasCapacityVariable=True,
            operationRateMax=data["Wind (onshore), operationRateMax"],
            capacityMax=data["Wind (onshore), capacityMax"],
            investPerCapacity=1.1,
            opexPerCapacity=1.1 * 0.02,
            interestRate=0.08,
            economicLifetime=20,
        ))

    ### PV
    esM.add(
        fn.Source(
            esM=esM,
            name="PV",
            commodity="electricity",
            hasCapacityVariable=True,
            operationRateMax=data["PV, operationRateMax"],
            capacityMax=data["PV, capacityMax"],
            investPerCapacity=0.65,
            opexPerCapacity=0.65 * 0.02,
            interestRate=0.08,
            economicLifetime=25,
        ))

    # 3. Add conversion components to the energy system model

    ### New combined cycly gas turbines for hydrogen
    esM.add(
        fn.Conversion(
            esM=esM,
            name="New CCGT plants (hydrogen)",
            physicalUnit=r"GW$_{el}$",
            commodityConversionFactors={
                "electricity": 1,
                "hydrogen": -1 / 0.6
            },
            hasCapacityVariable=True,
            investPerCapacity=0.7,
            opexPerCapacity=0.021,
            interestRate=0.08,
            economicLifetime=33,
        ))

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

    # 4. Add commodity storages to the energy system model

    ### Lithium ion batteries
    esM.add(
        fn.Storage(
            esM=esM,
            name="Li-ion batteries",
            commodity="electricity",
            hasCapacityVariable=True,
            chargeEfficiency=0.95,
            cyclicLifetime=10000,
            dischargeEfficiency=0.95,
            selfDischarge=1 - (1 - 0.03)**(1 / (30 * 24)),
            chargeRate=1,
            dischargeRate=1,
            doPreciseTsaModeling=False,
            investPerCapacity=0.151,
            opexPerCapacity=0.002,
            interestRate=0.08,
            economicLifetime=22,
        ))

    ### Hydrogen filled salt caverns
    esM.add(
        fn.Storage(
            esM=esM,
            name="Salt caverns (hydrogen)",
            commodity="hydrogen",
            hasCapacityVariable=True,
            capacityVariableDomain="continuous",
            capacityPerPlantUnit=133,
            chargeRate=1 / 470.37,
            dischargeRate=1 / 470.37,
            sharedPotentialID="Existing salt caverns",
            stateOfChargeMin=0.33,
            stateOfChargeMax=1,
            capacityMax=data["Salt caverns (hydrogen), capacityMax"],
            investPerCapacity=0.00011,
            opexPerCapacity=0.00057,
            interestRate=0.08,
            economicLifetime=30,
        ))

    # 5. Add commodity transmission components to the energy system model

    ### AC cables
    esM.add(
        fn.LinearOptimalPowerFlow(
            esM=esM,
            name="AC cables",
            commodity="electricity",
            hasCapacityVariable=True,
            capacityFix=data["AC cables, capacityFix"],
            reactances=data["AC cables, reactances"],
        ))

    ### DC cables
    esM.add(
        fn.Transmission(
            esM=esM,
            name="DC cables",
            commodity="electricity",
            losses=data["DC cables, losses"],
            distances=data["DC cables, distances"],
            hasCapacityVariable=True,
            capacityFix=data["DC cables, capacityFix"],
        ))

    ### Hydrogen pipelines
    esM.add(
        fn.Transmission(
            esM=esM,
            name="Pipelines (hydrogen)",
            commodity="hydrogen",
            distances=data["Pipelines, distances"],
            hasCapacityVariable=True,
            hasIsBuiltBinaryVariable=False,
            bigM=300,
            locationalEligibility=data["Pipelines, eligibility"],
            capacityMax=data["Pipelines, eligibility"] * 15,
            sharedPotentialID="pipelines",
            investPerCapacity=0.000177,
            investIfBuilt=0.00033,
            interestRate=0.08,
            economicLifetime=40,
        ))

    # 6. Add commodity sinks to the energy system model

    ### Electricity demand
    esM.add(
        fn.Sink(
            esM=esM,
            name="Electricity demand",
            commodity="electricity",
            hasCapacityVariable=False,
            operationRateFix=data["Electricity demand, operationRateFix"],
        ))

    ## 7.2. Hydrogen sinks
    FCEV_penetration = 0.5
    esM.add(
        fn.Sink(
            esM=esM,
            name="Hydrogen demand",
            commodity="hydrogen",
            hasCapacityVariable=False,
            operationRateFix=data["Hydrogen demand, operationRateFix"] *
            FCEV_penetration,
        ))

    return esM
def create_simple_esm():
    """
    To observe the effects of variable conversion factors, we create a simple test
    esm. It consists of a source, a conversion and a sink. The sink has a fixed
    demand. The conversion rate of the electrolyzer changes in every period. We use
    a pandas.DataFrame for the electricity conversion factors and a pandas.Series for
    the hydrogen conversion factors to test the different inputs.
    """
    numberOfTimeSteps = 4
    hoursPerTimeStep = 2190
    locs = ["ElectrolyzerLocation"]
    # Create an energy system model instance
    esM = fn.EnergySystemModel(
        locations={"ElectrolyzerLocation"},
        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,
    )
    # Source
    esM.add(
        fn.Source(
            esM=esM,
            name="Electricity market",
            commodity="electricity",
            hasCapacityVariable=False,
        ))
    # Sink
    demand = pd.Series(np.array([1.0, 1.0, 1.0, 1.0])) * hoursPerTimeStep
    esM.add(
        fn.Sink(
            esM=esM,
            name="Industry site",
            commodity="hydrogen",
            hasCapacityVariable=False,
            operationRateFix=demand,
        ))
    cfs = {}
    # Use Dataframe for conversion rate timeseries
    cfs["electricity"] = pd.DataFrame([np.array([-0.1, -1, -10, -100])],
                                      index=locs).T
    # Use Series for conversion rate timeseries
    cfs["hydrogen"] = pd.Series(np.array([0.7, 0.7, 0.7, 0.7]))
    esM.add(
        fn.Conversion(
            esM=esM,
            name="Electrolyzers_VarConvFac",
            physicalUnit=r"kW$_{el}$",
            commodityConversionFactors={
                "electricity": cfs["electricity"],
                "hydrogen": cfs["hydrogen"],
            },
            hasCapacityVariable=True,
            investPerCapacity=1000,  # euro/kW
            opexPerCapacity=500 * 0.025,
            interestRate=0.08,
            capacityMax=1000,
            economicLifetime=10,
            locationalEligibility=pd.Series([1], ["ElectrolyzerLocation"]),
        ))
    return esM
def test_variable_conversion_factor_with_tsa(minimal_test_esM):
    """
    Same as `test_variable_conversion_factor_no_tsa` but with time series aggregation
    using 3 typical periods. Now the optimal solution is composed of only three different
    periods.
    """

    # Get the minimal test system from conftest
    esM = copy.deepcopy(minimal_test_esM)

    # Create time-variable conversion rates for the two locations as pandas.DataFrame.
    locs = ["ElectrolyzerLocation", "IndustryLocation"]
    cfs = {}
    cfs["electricity"] = pd.DataFrame(
        [np.array([-0.1, -1, -1, -10]),
         np.array([-0.1, -1, -1, -10])],
        index=locs).T
    cfs["hydrogen"] = pd.DataFrame(
        [np.array([0.7, 0.7, 0.7, 0.7]),
         np.array([0.7, 0.7, 0.7, 0.7])],
        index=locs).T

    # Add a new component with variable conversion rate to the EnergySystemModel.
    esM.add(
        fn.Conversion(
            esM=esM,
            name="Electrolyzers_VarConvFac",
            physicalUnit=r"kW$_{el}$",
            commodityConversionFactors={
                "electricity": cfs["electricity"],
                "hydrogen": cfs["hydrogen"],
            },
            hasCapacityVariable=True,
            investPerCapacity=1000,  # euro/kW
            opexPerCapacity=500 * 0.025,
            interestRate=0.08,
            economicLifetime=10,
        ))

    esM.aggregateTemporally(numberOfTypicalPeriods=3,
                            numberOfTimeStepsPerPeriod=1)
    esM.optimize(timeSeriesAggregation=True, solver="glpk")

    # Get optimal electrolyzer operations
    op_test_const = []
    op_test_var = []
    for t in range(0, 4):
        op_test_const.append(esM.componentModelingDict["ConversionModel"].
                             operationVariablesOptimum.xs("Electrolyzers").loc[
                                 "ElectrolyzerLocation", t])
        op_test_var.append(
            esM.componentModelingDict["ConversionModel"].
            operationVariablesOptimum.xs("Electrolyzers_VarConvFac").loc[
                "ElectrolyzerLocation", t])

    # Assert the optimal operation
    # We are asserting up to a precision of one decimal to account for precision gaps
    # of the solver.
    assertion_values_const = [0.0, 9385714.2, 0.0, 9385714.2]
    assertion_values_var = [18771428.5, 18771428.5, 18771428.5, 0.0]
    for t in range(0, 4):
        np.testing.assert_almost_equal(op_test_const[t],
                                       assertion_values_const[t],
                                       decimal=1)
        np.testing.assert_almost_equal(op_test_var[t],
                                       assertion_values_var[t],
                                       decimal=1)
def test_variable_conversion_factor_no_tsa(minimal_test_esM):
    """
    We add an additional electrolyzer component with variable conversion rates.
    It has a very high efficiency in time-step 1, where it is now choosen to operate
    in favour of the electrolyzer with constant efficiency.
    Efficiency in the last time-step is very low for the new electolyzer, therefore
    it is not operated in this time-step.
    """

    # Get the minimal test system from conftest
    esM = copy.deepcopy(minimal_test_esM)

    # Create time-variable conversion rates for the two locations as pandas.DataFrame.
    locs = ["ElectrolyzerLocation", "IndustryLocation"]
    cfs = {}
    cfs["electricity"] = pd.DataFrame(
        [np.array([-0.1, -1, -1, -10]),
         np.array([-0.1, -1, -1, -10])],
        index=locs).T
    cfs["hydrogen"] = pd.DataFrame(
        [np.array([0.7, 0.7, 0.7, 0.7]),
         np.array([0.7, 0.7, 0.7, 0.7])],
        index=locs).T

    # Add a new component with variable conversion rate to the EnergySystemModel.
    esM.add(
        fn.Conversion(
            esM=esM,
            name="Electrolyzers_VarConvFac",
            physicalUnit=r"kW$_{el}$",
            commodityConversionFactors={
                "electricity": cfs["electricity"],
                "hydrogen": cfs["hydrogen"],
            },
            hasCapacityVariable=True,
            investPerCapacity=1000,  # euro/kW
            opexPerCapacity=500 * 0.025,
            interestRate=0.08,
            economicLifetime=10,
        ))

    # Optimize the esM without TSA.
    esM.optimize(timeSeriesAggregation=False, solver="glpk")

    # Get optimal electrolyzer operations
    op_test_const = []
    op_test_var = []
    for t in range(0, 4):
        op_test_const.append(esM.componentModelingDict["ConversionModel"].
                             operationVariablesOptimum.xs("Electrolyzers").loc[
                                 "ElectrolyzerLocation", t])
        op_test_var.append(
            esM.componentModelingDict["ConversionModel"].
            operationVariablesOptimum.xs("Electrolyzers_VarConvFac").loc[
                "ElectrolyzerLocation", t])

    # Assert the optimal operation
    # We are asserting up to a precision of one decimal to account for precision gaps
    # of the solver.
    assertion_values_const = [0.0, 18771428.5, 0.0, 18771428.5]
    assertion_values_var = [18771428.5, 18771428.5, 0.0, 0.0]
    for t in range(0, 4):
        np.testing.assert_almost_equal(op_test_const[t],
                                       assertion_values_const[t],
                                       decimal=1)
        np.testing.assert_almost_equal(op_test_var[t],
                                       assertion_values_var[t],
                                       decimal=1)
Exemple #15
0
# %% [markdown]
# # 4. Add conversion components to the energy system model

# %% [markdown]
# ### New combined cycly gas turbines for hydrogen

# %%
esM.add(
    fn.Conversion(
        esM=esM,
        name="New CCGT plants (hydrogen)",
        physicalUnit=r"GW$_{el}$",
        commodityConversionFactors={
            "electricity": 1,
            "hydrogen": -1 / 0.6
        },
        hasCapacityVariable=True,
        investPerCapacity=0.7,
        opexPerCapacity=0.021,
        interestRate=0.08,
        economicLifetime=33,
    ))

# %% [markdown]
# ### Electrolyzers

# %%
esM.add(
    fn.Conversion(
        esM=esM,
        name="Electroylzers",
Exemple #16
0
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()
Exemple #17
0
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'].values.sum() < 183

    assert results['ESM_2030'].getOptimizationSummary('SourceSinkModel').loc[
        'CO2 to environment'].loc['operation'].values.sum() == 0
Exemple #18
0
# %% [markdown]
# # 4. Add conversion components to the energy system model

# %% [markdown]
# ### Combined cycle gas turbine plants

# %%
esM.add(
    fn.Conversion(
        esM=esM,
        name="CCGT plants (methane)",
        physicalUnit=r"GW$_{el}$",
        commodityConversionFactors={
            "electricity": 1,
            "methane": -1 / 0.6,
            "CO2": 201 * 1e-6 / 0.6,
        },
        hasCapacityVariable=True,
        investPerCapacity=0.65,
        opexPerCapacity=0.021,
        interestRate=0.08,
        economicLifetime=33,
    ))

# %% [markdown]
# ### New combined cycle gas turbine plants for biogas

# %%
esM.add(
    fn.Conversion(
        esM=esM,
Exemple #19
0
def test_industrialSite():
    # # Model an energy system

    # Input parameters
    locations = {'industry_0'}
    commodityUnitDict = {
        'electricity': r'MW$_{el}$',
        'hydrogen': r'MW$_{H_{2},LHV}$',
        'heat': r'MW$_{heat}$}'
    }
    commodities = {'electricity', 'hydrogen', 'heat'}
    numberOfTimeSteps, hoursPerTimeStep = 24 * 6, 1 / 6  #8760, 1 # 52560, 1/6
    costUnit, lengthUnit = '1e3 Euro', 'km'

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

    # ## Add source component
    data = pd.read_excel(
        os.path.join(os.path.dirname(__file__), '_testInputFiles',
                     'generationTimeSeries_e825103.xlsx'))

    data = data.iloc[0:numberOfTimeSteps]

    operationRateFix = pd.DataFrame(
        data['e825103_2017_2.3MW_faults9'],
        index=range(numberOfTimeSteps))  # Dataset with least missing data
    operationRateFix.columns = ['industry_0']

    # Input parameters
    name, commodity = 'Wind turbines', 'electricity'
    hasCapacityVariable = True
    capacityMax = pd.Series([10],
                            index=['industry_0'])  # 10 MW_el = 0.01 GW_el
    investPerCapacity, opexPerCapacity = 0, 30  # 30 €/kW = 30 1e6€/GW = 30 1e3€/MW
    interestRate, economicLifetime = 0.08, 20

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

    # ## Add conversion components

    esM.add(
        fn.Conversion(
            esM=esM,
            name='PEMEC',
            physicalUnit=r'MW$_{el}$',
            commodityConversionFactors={
                'electricity': -1,
                'hydrogen': 0.67
            },
            hasCapacityVariable=True,
            investPerCapacity=2300,
            opexPerCapacity=12.5,
            interestRate=0.08,  # for 2018 CAPEX
            economicLifetime=5))

    func = lambda x: 0.5 * (x - 2)**3 + (x - 2)**2 + 0.0001

    esM.add(
        fn.ConversionPartLoad(
            esM=esM,
            name='AEC',
            physicalUnit=r'MW$_{el}$',
            commodityConversionFactors={
                'electricity': -1,
                'hydrogen': 0.64
            },
            commodityConversionFactorsPartLoad={
                'electricity': -1,
                'hydrogen': func
            },
            # commodityConversionFactorsPartLoad=pwl,
            nSegments=2,
            hasCapacityVariable=True,
            bigM=99,
            investPerCapacity=1300,
            opexPerCapacity=18,
            interestRate=0.08,  # for 2018 CAPEX
            economicLifetime=9))

    # ## Add storage components

    esM.add(
        fn.Storage(esM=esM,
                   name='Hydrogen tank (gaseous)',
                   commodity='hydrogen',
                   hasCapacityVariable=True,
                   capacityVariableDomain='continuous',
                   capacityPerPlantUnit=1,
                   chargeRate=1,
                   dischargeRate=1,
                   sharedPotentialID=None,
                   stateOfChargeMin=0.06,
                   stateOfChargeMax=1,
                   investPerCapacity=0.004,
                   opexPerCapacity=0.004 * 0.02,
                   interestRate=0.08,
                   economicLifetime=20))

    ### Industrial hydrogen demand
    operationRateFix = pd.DataFrame(
        2 * np.ones(numberOfTimeSteps) * (hoursPerTimeStep),
        columns=['industry_0'])  # constant hydrogen demand of 2 MW_GH2:
    esM.add(
        fn.Sink(esM=esM,
                name='Hydrogen demand',
                commodity='hydrogen',
                hasCapacityVariable=False,
                operationRateFix=operationRateFix))

    # Heat output
    esM.add(
        fn.Sink(esM=esM,
                name='Heat output',
                commodity='heat',
                hasCapacityVariable=False))

    # Input parameters
    timeSeriesAggregation = False
    solver = 'glpk'

    # Optimize
    esM.optimize(timeSeriesAggregation=timeSeriesAggregation, solver=solver)
Exemple #20
0
def test_QPinvest():

    numberOfTimeSteps = 4
    hoursPerTimeStep = 2190

    # Create an energy system model instance
    esM = fn.EnergySystemModel(
        locations={"location1"},
        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,
    )

    # time step length [h]
    timeStepLength = numberOfTimeSteps * hoursPerTimeStep

    ### Buy electricity at the electricity market
    costs = pd.Series([0.5, 0.4, 0.2, 0.5], index=[0, 1, 2, 3])
    esM.add(
        fn.Source(
            esM=esM,
            name="Electricity market",
            commodity="electricity",
            hasCapacityVariable=False,
            commodityCostTimeSeries=costs,
        )
    )  # euro/kWh

    ### Electrolyzers
    esM.add(
        fn.Conversion(
            esM=esM,
            name="Electrolyzer",
            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,
            QPcostScale=0.1,
            capacityMin=0,
            capacityMax=10,
        )
    )

    ### Industry site
    demand = pd.Series([10000.0, 10000.0, 10000.0, 10000.0], index=[0, 1, 2, 3])
    esM.add(
        fn.Sink(
            esM=esM,
            name="Industry site",
            commodity="hydrogen",
            hasCapacityVariable=False,
            operationRateFix=demand,
        )
    )

    ### Optimize (just executed if gurobi is installed)

    flag = True
    try:
        esM.optimize(timeSeriesAggregation=False, solver="gurobi")
    except:
        flag = False

    if flag:
        invest = round(
            esM.getOptimizationSummary("ConversionModel")
            .loc["Electrolyzer"]
            .loc["invest"]["location1"]
            .values.astype(float)[0],
            3,
        )
        assert invest == 3148.179
def test_hydrogenSinkDriver():

    # 0) Preprocess energy system model
    locations = {"Region1"}
    commodityUnitDict = {
        "electricity": r"MW$_{el}$",
        "hydrogen": r"MW$_{LHV_H2}$"
    }
    commodities = {"electricity", "hydrogen"}
    ndays = 20
    nhours = 24 * ndays

    # Wind turbines
    dailyProfile = [
        0.21773151164616183,
        0.034941022753796035,
        0.06456056257136962,
        0.18781756363388397,
        0.07389116186240981,
        0.1696331916932108,
        0.4464499926416005,
        0.1706764273756967,
        0.15694190971549513,
        0.2882743403035651,
        0.32127549679527717,
        0.14116461052786136,
        0.5461613758054059,
        0.21958149674207716,
        0.25715084860896853,
        0.21525778612323568,
        0.15916222011475448,
        0.11014921269063864,
        0.2532504131880449,
        0.3154483508270503,
        0.08412368254727028,
        0.06337996942065917,
        0.12431489082721527,
        0.17319120880651773,
    ]
    operationRateMax = pd.DataFrame(
        [u for day in range(ndays) for u in dailyProfile],
        index=range(nhours),
        columns=["Region1"],
    ).round(6)
    capacityMaxWind = pd.Series([400], index=["Region1"])
    # Define min production
    minProduction = (operationRateMax["Region1"].sum() *
                     capacityMaxWind.loc["Region1"] * 0.6)
    investPerCapacity, opexPerCapacity = 1200, 1200 * 0.02
    interestRate, economicLifetime = 0.08, 20

    # 1) specify balanceLimit

    balanceLimit = pd.DataFrame(index=["hydrogenDriver"], columns=["Region1"])
    # Define negative balanceLimit as driver. Because sink is contributing negatively.
    balanceLimit.loc["hydrogenDriver", "Region1"] = -1 * minProduction

    # 2) Initialize esM with one region with use of parameters balanceLimit and lowerBound

    # Use upperBound in a mathematical sense. abs(Sink Operation) >= abs(balanceLimit), both negative because Sink.
    esM = fn.EnergySystemModel(
        locations=locations,
        commodities=commodities,
        numberOfTimeSteps=nhours,
        commodityUnitsDict=commodityUnitDict,
        hoursPerTimeStep=1,
        costUnit="1e6 Euro",
        lengthUnit="km",
        verboseLogLevel=2,
        balanceLimit=balanceLimit,
        lowerBound=False,
    )

    # 3) Components are added: 'Wind turbines', 'Electrolyzer', 'Batteries' and 'Hydrogen Annual Production'
    # 'Hydrogen Annual Production' is included in the balanceLimit analysis

    # Define Hydrogen Annual Production and added to Energysystem
    esM.add(
        fn.Sink(
            esM=esM,
            name="Hydrogen Annual Production",
            balanceLimitID="hydrogenDriver",
            commodity="hydrogen",
            hasCapacityVariable=False,
        ))

    # Define Wind turbines and added to Energysystem
    esM.add(
        fn.Source(
            esM=esM,
            name="Wind turbines",
            commodity="electricity",
            hasCapacityVariable=True,
            operationRateMax=operationRateMax,
            capacityMax=capacityMaxWind,
            investPerCapacity=investPerCapacity,
            opexPerCapacity=opexPerCapacity,
            interestRate=interestRate,
            economicLifetime=economicLifetime,
        ))

    # Define techno-economic parameters of Batteries and added to Energysystem
    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="Batteries",
            commodity="electricity",
            hasCapacityVariable=True,
            chargeEfficiency=chargeEfficiency,
            cyclicLifetime=cyclicLifetime,
            dischargeEfficiency=dischargeEfficiency,
            selfDischarge=selfDischarge,
            chargeRate=chargeRate,
            dischargeRate=dischargeRate,
            investPerCapacity=investPerCapacity,
            opexPerCapacity=opexPerCapacity,
            interestRate=interestRate,
            economicLifetime=economicLifetime,
        ))

    # Define techno-economic parameters Electrolyzers and added to Energysystem
    invest_per_capacity_electrolysis = 0.5  # unit 1e9 EUR/GW
    opex_electrolysis = 0.025  # in %/100 of capex
    interestRate_electrolysis = 0.08  # in %/100
    economicLifetime_electrolysis = 10  # in years
    esM.add(
        fn.Conversion(
            esM=esM,
            name="Electrolyzers",
            physicalUnit=r"MW$_{el}$",
            commodityConversionFactors={
                "electricity": -1,
                "hydrogen": 0.7
            },
            hasCapacityVariable=True,
            investPerCapacity=invest_per_capacity_electrolysis,
            opexPerCapacity=invest_per_capacity_electrolysis *
            opex_electrolysis,
            interestRate=interestRate_electrolysis,
            economicLifetime=economicLifetime_electrolysis,
        ))
    # 4) Optimize model
    esM.optimize(timeSeriesAggregation=False, solver="glpk")

    # 5) The balanceLimit is compared to the outcome of the model
    # Hydrogen Annual Production >= hydrogenDriver (as balanceLimitID)
    for i, loc in enumerate(esM.locations):
        # Get operation of Renewables for loc
        operation_hydrogen = (
            esM.componentModelingDict["SourceSinkModel"].
            operationVariablesOptimum.loc["Hydrogen Annual Production",
                                          loc].sum())
        tolerance = 0.001
        ## Compare modelled lowerLimit to limit set in constraint.
        test_min_production = (ndays * sum(dailyProfile) *
                               capacityMaxWind.loc["Region1"] * 0.6)
        # Assert that the min. production requirement was indeed fulfilled (must be).
        assert operation_hydrogen > test_min_production * (1 - tolerance)
        # Assert that produced volume does also not exceed the min. requirements (should be
        # the case for linear models and TAC as cost function but could change in the future).
        assert operation_hydrogen < test_min_production * (1 + tolerance)
Exemple #22
0
    ))

# %% [markdown]
# # Conversion

# %%
eligibility = pd.Series([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], index=locations)
esM.add(
    fn.Conversion(
        esM=esM,
        name="Water treatment plant",
        physicalUnit="U",
        commodityConversionFactors={
            "river water": -1,
            "clean water": 1
        },
        hasCapacityVariable=True,
        locationalEligibility=eligibility,
        investPerCapacity=7,
        opexPerCapacity=0.02 * 7,
        interestRate=0.08,
        economicLifetime=20,
    ))

# %% [markdown]
# # Storage

# %%
eligibility = pd.Series([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], index=locations)
esM.add(
    fn.Storage(
Exemple #23
0
def test_minimumPartLoad():
    # read in original results
    results = [4.0, 4.0, 0.0, 0.0, 4.0]

    # 2. Create an energy system model instance
    locations = {"example_region"}
    commodityUnitDict = {"electricity": r"GW$_{el}$", "methane": r"GW$_{CH_{4},LHV}$"}
    commodities = {"electricity", "methane"}

    esM = fn.EnergySystemModel(
        locations=locations,
        commodities=commodities,
        numberOfTimeSteps=5,
        commodityUnitsDict=commodityUnitDict,
        hoursPerTimeStep=1,
        costUnit="1e9 Euro",
        lengthUnit="km",
        verboseLogLevel=0,
    )

    data_cost = {"example_region": [10, 10, 10, 10, 10]}
    data_cost_df = pd.DataFrame(data=data_cost)

    esM.add(
        fn.Source(
            esM=esM,
            name="Natural gas purchase",
            commodity="methane",
            hasCapacityVariable=False,
            commodityCostTimeSeries=data_cost_df,
        )
    )

    # 4. Add conversion components to the energy system model

    ### Combined cycle gas turbine plants

    esM.add(
        fn.Conversion(
            esM=esM,
            name="unrestricted",
            physicalUnit=r"GW$_{el}$",
            commodityConversionFactors={"electricity": 1, "methane": -1 / 0.625},
            hasCapacityVariable=True,
            investPerCapacity=0.65,
            opexPerCapacity=0.021,
            interestRate=0.08,
            economicLifetime=33,
        )
    )

    data_cap = pd.Series(index=["example_region"], data=10)

    esM.add(
        fn.Conversion(
            esM=esM,
            name="restricted",
            physicalUnit=r"GW$_{el}$",
            commodityConversionFactors={"electricity": 1, "methane": -1 / 0.625},
            capacityFix=data_cap,
            partLoadMin=0.4,
            bigM=10000,
            investPerCapacity=0.5,
            opexPerCapacity=0.015,
            interestRate=0.08,
            economicLifetime=33,
        )
    )

    data_demand = {"example_region": [5, 5, 1, 1, 4]}
    data_demand_df = pd.DataFrame(data=data_demand)
    esM.add(
        fn.Sink(
            esM=esM,
            name="Electricity demand",
            commodity="electricity",
            hasCapacityVariable=False,
            operationRateFix=data_demand_df,
        )
    )
    esM.optimize(timeSeriesAggregation=False, solver="glpk")

    print("restricted dispatch:\n")
    print(
        esM.componentModelingDict["ConversionModel"].operationVariablesOptimum.xs(
            "restricted"
        )
    )
    print("unrestricted dispatch:\n")
    print(
        esM.componentModelingDict["ConversionModel"].operationVariablesOptimum.xs(
            "unrestricted"
        )
    )
    # test if here solved fits with original results

    # test if here solved fits with original results
    testresults = esM.componentModelingDict[
        "ConversionModel"
    ].operationVariablesOptimum.xs("restricted")
    np.testing.assert_array_almost_equal(testresults.values[0], results, decimal=2)
Exemple #24
0
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)
Exemple #25
0
def getModel():
    numberOfTimeSteps = 4
    hoursPerTimeStep = 2190

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

    # time step length [h]
    timeStepLength = numberOfTimeSteps * hoursPerTimeStep

    ### Buy electricity at the electricity market
    costs = pd.DataFrame(
        [
            np.array([
                0.05,
                0.0,
                0.1,
                0.051,
            ]),
            np.array([
                0.0,
                0.0,
                0.0,
                0.0,
            ]),
        ],
        index=["ElectrolyzerLocation", "IndustryLocation"],
    ).T
    revenues = pd.DataFrame(
        [
            np.array([
                0.0,
                0.01,
                0.0,
                0.0,
            ]),
            np.array([
                0.0,
                0.0,
                0.0,
                0.0,
            ]),
        ],
        index=["ElectrolyzerLocation", "IndustryLocation"],
    ).T
    maxpurchase = (pd.DataFrame(
        [
            np.array([
                1e6,
                1e6,
                1e6,
                1e6,
            ]),
            np.array([
                0.0,
                0.0,
                0.0,
                0.0,
            ]),
        ],
        index=["ElectrolyzerLocation", "IndustryLocation"],
    ).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,
        ))

    ### Hydrogen pipelines
    esM.add(
        fn.Transmission(
            esM=esM,
            name="Pipelines",
            commodity="hydrogen",
            hasCapacityVariable=True,
            investPerCapacity=0.177,
            interestRate=0.08,
            economicLifetime=40,
        ))

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

    return esM
Exemple #26
0
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,
            0.1,
            0.051,
        ])],
        index=["OneLocation"],
    ).T
    revenues = pd.DataFrame(
        [np.array([
            0.0,
            0.01,
            0.0,
            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_commodityCostTimeSeries():
    cwd = os.getcwd()
    data = getData()

    # read in original results
    results = pd.Series.from_csv(
        os.path.join(os.path.dirname(__file__), '..', 'examples',
                     'Multi-regional Energy System Workflow',
                     'totalBiogasPurchase.csv'))

    # 2. Create an energy system model instance
    locations = {
        'cluster_0', 'cluster_1', 'cluster_2', 'cluster_3', 'cluster_4',
        'cluster_5', 'cluster_6', 'cluster_7'
    }
    commodityUnitDict = {
        'electricity': r'GW$_{el}$',
        'methane': r'GW$_{CH_{4},LHV}$',
        'biogas': r'GW$_{biogas,LHV}$',
        'CO2': r'Mio. t$_{CO_2}$/h',
        'hydrogen': r'GW$_{H_{2},LHV}$'
    }
    commodities = {'electricity', 'hydrogen', 'methane', 'biogas', 'CO2'}
    numberOfTimeSteps = 8760
    hoursPerTimeStep = 1

    esM = fn.EnergySystemModel(locations=locations,
                               commodities=commodities,
                               numberOfTimeSteps=8760,
                               commodityUnitsDict=commodityUnitDict,
                               hoursPerTimeStep=1,
                               costUnit='1e9 Euro',
                               lengthUnit='km',
                               verboseLogLevel=0)

    CO2_reductionTarget = 1

    # 3. Add commodity sources to the energy system model
    ## 3.1. Electricity sources
    ### Wind onshore

    esM.add(
        fn.Source(esM=esM,
                  name='Wind (onshore)',
                  commodity='electricity',
                  hasCapacityVariable=True,
                  operationRateMax=data['Wind (onshore), operationRateMax'],
                  capacityMax=data['Wind (onshore), capacityMax'],
                  investPerCapacity=1.1,
                  opexPerCapacity=1.1 * 0.02,
                  interestRate=0.08,
                  economicLifetime=20))

    data['Wind (onshore), operationRateMax'].sum()

    ### Wind offshore

    esM.add(
        fn.Source(esM=esM,
                  name='Wind (offshore)',
                  commodity='electricity',
                  hasCapacityVariable=True,
                  operationRateMax=data['Wind (offshore), operationRateMax'],
                  capacityMax=data['Wind (offshore), capacityMax'],
                  investPerCapacity=2.3,
                  opexPerCapacity=2.3 * 0.02,
                  interestRate=0.08,
                  economicLifetime=20))

    data['Wind (offshore), operationRateMax'].sum()

    ### PV

    esM.add(
        fn.Source(esM=esM,
                  name='PV',
                  commodity='electricity',
                  hasCapacityVariable=True,
                  operationRateMax=data['PV, operationRateMax'],
                  capacityMax=data['PV, capacityMax'],
                  investPerCapacity=0.65,
                  opexPerCapacity=0.65 * 0.02,
                  interestRate=0.08,
                  economicLifetime=25))

    data['PV, operationRateMax'].sum()

    ### Exisisting run-of-river hydroelectricity plants

    esM.add(
        fn.Source(
            esM=esM,
            name='Existing run-of-river plants',
            commodity='electricity',
            hasCapacityVariable=True,
            operationRateFix=data[
                'Existing run-of-river plants, operationRateFix'],
            tsaWeight=0.01,
            capacityFix=data['Existing run-of-river plants, capacityFix'],
            investPerCapacity=0,
            opexPerCapacity=0.208))

    ## 3.2. Methane (natural gas and biogas)
    ### Natural gas
    esM.add(
        fn.Source(esM=esM,
                  name='Natural gas purchase',
                  commodity='methane',
                  hasCapacityVariable=False,
                  commodityCostTimeSeries=data[
                      'Natural Gas, commodityCostTimeSeries']))

    ### Biogas
    esM.add(
        fn.Source(
            esM=esM,
            name='Biogas purchase',
            commodity='biogas',
            operationRateMax=data['Biogas, operationRateMax'],
            hasCapacityVariable=False,
            commodityCostTimeSeries=data['Biogas, commodityCostTimeSeries']))

    ## 3.3 CO2
    ### CO2

    esM.add(
        fn.Source(esM=esM,
                  name='CO2 from enviroment',
                  commodity='CO2',
                  hasCapacityVariable=False,
                  commodityLimitID='CO2 limit',
                  yearlyLimit=366 * (1 - CO2_reductionTarget)))

    # 4. Add conversion components to the energy system model

    ### Combined cycle gas turbine plants

    esM.add(
        fn.Conversion(esM=esM,
                      name='CCGT plants (methane)',
                      physicalUnit=r'GW$_{el}$',
                      commodityConversionFactors={
                          'electricity': 1,
                          'methane': -1 / 0.625,
                          'CO2': 201 * 1e-6 / 0.625
                      },
                      hasCapacityVariable=True,
                      investPerCapacity=0.65,
                      opexPerCapacity=0.021,
                      interestRate=0.08,
                      economicLifetime=33))

    ### New combined cycle gas turbine plants for biogas

    esM.add(
        fn.Conversion(esM=esM,
                      name='New CCGT plants (biogas)',
                      physicalUnit=r'GW$_{el}$',
                      commodityConversionFactors={
                          'electricity': 1,
                          'biogas': -1 / 0.635
                      },
                      hasCapacityVariable=True,
                      investPerCapacity=0.7,
                      opexPerCapacity=0.021,
                      interestRate=0.08,
                      economicLifetime=33))

    ### New combined cycly gas turbines for hydrogen

    esM.add(
        fn.Conversion(esM=esM,
                      name='New CCGT plants (hydrogen)',
                      physicalUnit=r'GW$_{el}$',
                      commodityConversionFactors={
                          'electricity': 1,
                          'hydrogen': -1 / 0.6
                      },
                      hasCapacityVariable=True,
                      investPerCapacity=0.7,
                      opexPerCapacity=0.021,
                      interestRate=0.08,
                      economicLifetime=33))

    ### Electrolyzers

    esM.add(
        fn.Conversion(esM=esM,
                      name='Electroylzers',
                      physicalUnit=r'GW$_{el}$',
                      commodityConversionFactors={
                          'electricity': -1,
                          'hydrogen': 0.7
                      },
                      hasCapacityVariable=True,
                      investPerCapacity=0.5,
                      opexPerCapacity=0.5 * 0.025,
                      interestRate=0.08,
                      economicLifetime=10))

    ### rSOC

    capexRSOC = 1.5

    esM.add(
        fn.Conversion(esM=esM,
                      name='rSOEC',
                      physicalUnit=r'GW$_{el}$',
                      linkedConversionCapacityID='rSOC',
                      commodityConversionFactors={
                          'electricity': -1,
                          'hydrogen': 0.6
                      },
                      hasCapacityVariable=True,
                      investPerCapacity=capexRSOC / 2,
                      opexPerCapacity=capexRSOC * 0.02 / 2,
                      interestRate=0.08,
                      economicLifetime=10))

    esM.add(
        fn.Conversion(esM=esM,
                      name='rSOFC',
                      physicalUnit=r'GW$_{el}$',
                      linkedConversionCapacityID='rSOC',
                      commodityConversionFactors={
                          'electricity': 1,
                          'hydrogen': -1 / 0.6
                      },
                      hasCapacityVariable=True,
                      investPerCapacity=capexRSOC / 2,
                      opexPerCapacity=capexRSOC * 0.02 / 2,
                      interestRate=0.08,
                      economicLifetime=10))

    # 5. Add commodity storages to the energy system model
    ## 5.1. Electricity storage
    ### Lithium ion batteries

    esM.add(
        fn.Storage(esM=esM,
                   name='Li-ion batteries',
                   commodity='electricity',
                   hasCapacityVariable=True,
                   chargeEfficiency=0.95,
                   cyclicLifetime=10000,
                   dischargeEfficiency=0.95,
                   selfDischarge=1 - (1 - 0.03)**(1 / (30 * 24)),
                   chargeRate=1,
                   dischargeRate=1,
                   doPreciseTsaModeling=False,
                   investPerCapacity=0.151,
                   opexPerCapacity=0.002,
                   interestRate=0.08,
                   economicLifetime=22))

    ## 5.2. Hydrogen storage
    ### Hydrogen filled salt caverns

    esM.add(
        fn.Storage(esM=esM,
                   name='Salt caverns (hydrogen)',
                   commodity='hydrogen',
                   hasCapacityVariable=True,
                   capacityVariableDomain='continuous',
                   capacityPerPlantUnit=133,
                   chargeRate=1 / 470.37,
                   dischargeRate=1 / 470.37,
                   sharedPotentialID='Existing salt caverns',
                   stateOfChargeMin=0.33,
                   stateOfChargeMax=1,
                   capacityMax=data['Salt caverns (hydrogen), capacityMax'],
                   investPerCapacity=0.00011,
                   opexPerCapacity=0.00057,
                   interestRate=0.08,
                   economicLifetime=30))

    ## 5.3. Methane storage
    ### Methane filled salt caverns

    esM.add(
        fn.Storage(esM=esM,
                   name='Salt caverns (biogas)',
                   commodity='biogas',
                   hasCapacityVariable=True,
                   capacityVariableDomain='continuous',
                   capacityPerPlantUnit=443,
                   chargeRate=1 / 470.37,
                   dischargeRate=1 / 470.37,
                   sharedPotentialID='Existing salt caverns',
                   stateOfChargeMin=0.33,
                   stateOfChargeMax=1,
                   capacityMax=data['Salt caverns (methane), capacityMax'],
                   investPerCapacity=0.00004,
                   opexPerCapacity=0.00001,
                   interestRate=0.08,
                   economicLifetime=30))

    ## 5.4 Pumped hydro storage
    ### Pumped hydro storage

    esM.add(
        fn.Storage(esM=esM,
                   name='Pumped hydro storage',
                   commodity='electricity',
                   chargeEfficiency=0.88,
                   dischargeEfficiency=0.88,
                   hasCapacityVariable=True,
                   selfDischarge=1 - (1 - 0.00375)**(1 / (30 * 24)),
                   chargeRate=0.16,
                   dischargeRate=0.12,
                   capacityFix=data['Pumped hydro storage, capacityFix'],
                   investPerCapacity=0,
                   opexPerCapacity=0.000153))

    # 6. Add commodity transmission components to the energy system model
    ## 6.1. Electricity transmission
    ### AC cables

    esM.add(
        fn.LinearOptimalPowerFlow(esM=esM,
                                  name='AC cables',
                                  commodity='electricity',
                                  hasCapacityVariable=True,
                                  capacityFix=data['AC cables, capacityFix'],
                                  reactances=data['AC cables, reactances']))

    ### DC cables

    esM.add(
        fn.Transmission(esM=esM,
                        name='DC cables',
                        commodity='electricity',
                        losses=data['DC cables, losses'],
                        distances=data['DC cables, distances'],
                        hasCapacityVariable=True,
                        capacityFix=data['DC cables, capacityFix']))

    ## 6.2 Methane transmission
    ### Methane pipeline

    esM.add(
        fn.Transmission(esM=esM,
                        name='Pipelines (biogas)',
                        commodity='biogas',
                        distances=data['Pipelines, distances'],
                        hasCapacityVariable=True,
                        hasIsBuiltBinaryVariable=False,
                        bigM=300,
                        locationalEligibility=data['Pipelines, eligibility'],
                        capacityMax=data['Pipelines, eligibility'] * 15,
                        sharedPotentialID='pipelines',
                        investPerCapacity=0.000037,
                        investIfBuilt=0.000314,
                        interestRate=0.08,
                        economicLifetime=40))

    ## 6.3 Hydrogen transmission
    ### Hydrogen pipelines

    esM.add(
        fn.Transmission(esM=esM,
                        name='Pipelines (hydrogen)',
                        commodity='hydrogen',
                        distances=data['Pipelines, distances'],
                        hasCapacityVariable=True,
                        hasIsBuiltBinaryVariable=False,
                        bigM=300,
                        locationalEligibility=data['Pipelines, eligibility'],
                        capacityMax=data['Pipelines, eligibility'] * 15,
                        sharedPotentialID='pipelines',
                        investPerCapacity=0.000177,
                        investIfBuilt=0.00033,
                        interestRate=0.08,
                        economicLifetime=40))

    # 7. Add commodity sinks to the energy system model
    ## 7.1. Electricity sinks
    ### Electricity demand

    esM.add(
        fn.Sink(esM=esM,
                name='Electricity demand',
                commodity='electricity',
                hasCapacityVariable=False,
                operationRateFix=data['Electricity demand, operationRateFix']))

    ## 7.2. Hydrogen sinks
    ### Fuel cell electric vehicle (FCEV) demand

    FCEV_penetration = 0.5
    esM.add(
        fn.Sink(esM=esM,
                name='Hydrogen demand',
                commodity='hydrogen',
                hasCapacityVariable=False,
                operationRateFix=data['Hydrogen demand, operationRateFix'] *
                FCEV_penetration))

    ## 7.3. CO2 sinks
    ### CO2 exiting the system's boundary

    esM.add(
        fn.Sink(esM=esM,
                name='CO2 to enviroment',
                commodity='CO2',
                hasCapacityVariable=False,
                commodityLimitID='CO2 limit',
                yearlyLimit=366 * (1 - CO2_reductionTarget)))

    # 8. Optimize energy system model

    esM.cluster(numberOfTypicalPeriods=3)

    esM.optimize(timeSeriesAggregation=True, solver='glpk')

    # test if here solved fits with original results
    testresults = esM.componentModelingDict[
        "SourceSinkModel"].operationVariablesOptimum.xs('Biogas purchase').sum(
            axis=1)
    np.testing.assert_array_almost_equal(testresults.values,
                                         results.values,
                                         decimal=2)
    "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

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

# %% [markdown]
# ## Add storage components
#
# Storage components can store commodities across time steps.
#
# The self discharge of a storage technology is described in FINE in percent per hour. If the literature value is given in percent per month, e.g. 3%/month, the self discharge per hours is obtained using the equation (1-$\text{selfDischarge}_\text{hour})^{30*24\text{h}} = 1-\text{selfDischarge}_\text{month}$.

# %%
Exemple #29
0
def minimal_test_esM():
    """Returns minimal instance of esM"""

    numberOfTimeSteps = 4
    hoursPerTimeStep = 2190

    # Create an energy system model instance
    esM = fn.EnergySystemModel(
        locations={'ElectrolyzerLocation', 'IndustryLocation'},
        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)

    # time step length [h]
    timeStepLength = numberOfTimeSteps * hoursPerTimeStep

    ### Buy electricity at the electricity market
    costs = pd.DataFrame(
        [np.array([
            0.05,
            0.,
            0.1,
            0.051,
        ]), np.array([
            0.,
            0.,
            0.,
            0.,
        ])],
        index=['ElectrolyzerLocation', 'IndustryLocation']).T
    revenues = pd.DataFrame(
        [np.array([
            0.,
            0.01,
            0.,
            0.,
        ]), np.array([
            0.,
            0.,
            0.,
            0.,
        ])],
        index=['ElectrolyzerLocation', 'IndustryLocation']).T
    maxpurchase = pd.DataFrame(
        [np.array([
            1e6,
            1e6,
            1e6,
            1e6,
        ]), np.array([
            0.,
            0.,
            0.,
            0.,
        ])],
        index=['ElectrolyzerLocation', 'IndustryLocation'
               ]).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))

    ### Hydrogen pipelines
    esM.add(
        fn.Transmission(esM=esM,
                        name='Pipelines',
                        commodity='hydrogen',
                        hasCapacityVariable=True,
                        investPerCapacity=0.177,
                        interestRate=0.08,
                        economicLifetime=40))

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

    return esM
Exemple #30
0
def test_minimumPartLoad():
    # read in original results
    results = [4., 4., 0., 0., 4.]

    # 2. Create an energy system model instance
    locations = {'example_region'}
    commodityUnitDict = {
        'electricity': r'GW$_{el}$',
        'methane': r'GW$_{CH_{4},LHV}$'
    }
    commodities = {'electricity', 'methane'}

    esM = fn.EnergySystemModel(locations=locations,
                               commodities=commodities,
                               numberOfTimeSteps=5,
                               commodityUnitsDict=commodityUnitDict,
                               hoursPerTimeStep=1,
                               costUnit='1e9 Euro',
                               lengthUnit='km',
                               verboseLogLevel=0)

    data_cost = {'example_region': [10, 10, 10, 10, 10]}
    data_cost_df = pd.DataFrame(data=data_cost)

    esM.add(
        fn.Source(esM=esM,
                  name='Natural gas purchase',
                  commodity='methane',
                  hasCapacityVariable=False,
                  commodityCostTimeSeries=data_cost_df))

    # 4. Add conversion components to the energy system model

    ### Combined cycle gas turbine plants

    esM.add(
        fn.Conversion(esM=esM,
                      name='unrestricted',
                      physicalUnit=r'GW$_{el}$',
                      commodityConversionFactors={
                          'electricity': 1,
                          'methane': -1 / 0.625
                      },
                      hasCapacityVariable=True,
                      investPerCapacity=0.65,
                      opexPerCapacity=0.021,
                      interestRate=0.08,
                      economicLifetime=33))

    data_cap = pd.Series(index=['example_region'], data=10)

    esM.add(
        fn.Conversion(esM=esM,
                      name='restricted',
                      physicalUnit=r'GW$_{el}$',
                      commodityConversionFactors={
                          'electricity': 1,
                          'methane': -1 / 0.625
                      },
                      capacityFix=data_cap,
                      partLoadMin=0.4,
                      bigM=10000,
                      investPerCapacity=0.5,
                      opexPerCapacity=0.015,
                      interestRate=0.08,
                      economicLifetime=33))

    data_demand = {'example_region': [5, 5, 1, 1, 4]}
    data_demand_df = pd.DataFrame(data=data_demand)
    esM.add(
        fn.Sink(esM=esM,
                name='Electricity demand',
                commodity='electricity',
                hasCapacityVariable=False,
                operationRateFix=data_demand_df))
    esM.optimize(timeSeriesAggregation=False, solver='glpk')

    print('restricted dispatch:\n')
    print(esM.componentModelingDict['ConversionModel'].
          operationVariablesOptimum.xs('restricted'))
    print('unrestricted dispatch:\n')
    print(esM.componentModelingDict['ConversionModel'].
          operationVariablesOptimum.xs('unrestricted'))
    # test if here solved fits with original results

    # test if here solved fits with original results
    testresults = esM.componentModelingDict[
        "ConversionModel"].operationVariablesOptimum.xs('restricted')
    np.testing.assert_array_almost_equal(testresults.values[0],
                                         results,
                                         decimal=2)