Merge branch 'nomopyomo'

This commit is contained in:
Tom Brown 2019-12-13 18:07:06 +01:00
commit 105aeba215
7 changed files with 355 additions and 218 deletions

View File

@ -219,7 +219,7 @@ rule solve_network:
memory="logs/" + config['run'] + "/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}_memory.log"
benchmark: "benchmarks/solve_network/{network}_s{simpl}_{clusters}_lv{lv}_{opts}_{sector_opts}"
threads: 4
resources: mem=120000 #memory in MB; 40 GB enough for 45+B+I; 100 GB based on RESI usage for 128
resources: mem=50000 #memory in MB; 40 GB enough for 45+B+I; 100 GB based on RESI usage for 128
# group: "solve" # with group, threads is ignored https://bitbucket.org/snakemake/snakemake/issues/971/group-job-description-does-not-contain
script: "scripts/solve_network.py"

View File

@ -2,7 +2,7 @@ logging_level: INFO
results_dir: 'results/'
summary_dir: results
run: '191108-h2_pipeline_network'
run: '191212-ccs'
scenario:
sectors: [E] # ,E+EV,E+BEV,E+BEV+V2G] # [ E+EV, E+BEV, E+BEV+V2G ]
@ -10,7 +10,7 @@ scenario:
lv: [1.0]#, 1.125, 1.25, 1.5, 2.0]# or opt
clusters: [50] #[90, 128, 181] #[45, 64, 90, 128, 181, 256] #, 362] # (2**np.r_[5.5:9:.5]).astype(int) minimum is 37
opts: [''] #for pypsa-eur
sector_opts: [Co2L0-3H-T-H-B-I,Co2L0-5H-T-H-B-I]#,Co2L0p1-3H-T-H-B-I,Co2L0p25-3H-T-H-B-I,Co2L0p5-3H-T-H-B-I]#[Co2L0-3H-T-H-B-I-onwind0-solar3,Co2L0-3H-T-H-B-I-onwind0p125-solar3,Co2L0-3H-T-H-B-I-onwind0p25-solar3,Co2L0-3H-T-H-B-I-onwind0p50-solar3,Co2L0-3H-T-H-B-I-solar3]#,Co2L0-3H-T-H-B-I-onwind0p25-solar3]#,Co2L0p05-3H-T-H-B-I,Co2L0p10-3H-T-H-B-I,Co2L0p20-3H-T-H-B-I,Co2L0p30-3H-T-H-B-I,Co2L0p50-3H-T-H-B-I]#[Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0-3H-T-H,Co2L0p20-3H-T-H] #Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p20-3H-T-HCo2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p30-3H-T-H,Co2L0p50-3H-T-H] #Co2L-3H,Co2L-3H-T,, LC-FL, LC-T, Ep-T, Co2L-T]
sector_opts: [Co2L0-3H-T-H-B-I,Co2L0p2-3H-T-H-B-I,Co2L0p5-3H-T-H-B-I]#,Co2L0p1-3H-T-H-B-I,Co2L0p25-3H-T-H-B-I,Co2L0p5-3H-T-H-B-I]#[Co2L0-3H-T-H-B-I-onwind0-solar3,Co2L0-3H-T-H-B-I-onwind0p125-solar3,Co2L0-3H-T-H-B-I-onwind0p25-solar3,Co2L0-3H-T-H-B-I-onwind0p50-solar3,Co2L0-3H-T-H-B-I-solar3]#,Co2L0-3H-T-H-B-I-onwind0p25-solar3]#,Co2L0p05-3H-T-H-B-I,Co2L0p10-3H-T-H-B-I,Co2L0p20-3H-T-H-B-I,Co2L0p30-3H-T-H-B-I,Co2L0p50-3H-T-H-B-I]#[Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0-3H-T-H,Co2L0p20-3H-T-H] #Co2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p20-3H-T-HCo2L-3H-T-H,Co2L0p10-3H-T-H,Co2L0p30-3H-T-H,Co2L0p50-3H-T-H] #Co2L-3H,Co2L-3H-T,, LC-FL, LC-T, Ep-T, Co2L-T]
# Co2L will give default (5%); Co2L0p25 will give 25% CO2 emissions; Co2Lm0p05 will give 5% negative emissions
@ -73,11 +73,6 @@ sector:
'tes_tau' : 3.
'boilers' : True
'chp' : True
'chp_parameters':
'eta_elec' : 0.468 #electrical efficiency with no heat output
'c_v' : 0.15 #loss of fuel for each addition of heat
'c_m' : 0.75 #backpressure ratio
'p_nom_ratio' : 1. #ratio of max heat output to max electrical output
'solar_thermal' : True
'solar_cf_correction': 0.788457 # = >>> 1/1.2683
'marginal_cost_storage' : 0. #1e-4

View File

@ -73,15 +73,15 @@ CCGT,2030,efficiency,0.5,per unit,DIW DataDoc http://hdl.handle.net/10419/80348
biomass,2030,efficiency,0.468,per unit,DIW DataDoc http://hdl.handle.net/10419/80348
geothermal,2030,efficiency,0.239,per unit,DIW DataDoc http://hdl.handle.net/10419/80348
nuclear,2030,efficiency,0.337,per unit,DIW DataDoc http://hdl.handle.net/10419/80348
gas,2030,CO2 intensity,0.187,tCO2/MWth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
gas,2030,CO2 intensity,0.187,tCO2/MWhth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
coal,2030,efficiency,0.464,per unit,DIW DataDoc http://hdl.handle.net/10419/80348 PC (Advanced/SuperC)
lignite,2030,efficiency,0.447,per unit,DIW DataDoc http://hdl.handle.net/10419/80348
oil,2030,efficiency,0.393,per unit,DIW DataDoc http://hdl.handle.net/10419/80348 CT
coal,2030,CO2 intensity,0.354,tCO2/MWth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
lignite,2030,CO2 intensity,0.4,tCO2/MWth,German sources
oil,2030,CO2 intensity,0.248,tCO2/MWth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
geothermal,2030,CO2 intensity,0.026,tCO2/MWth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
solid biomass,2030,CO2 intensity,0.3,tCO2/MWth,TODO
coal,2030,CO2 intensity,0.354,tCO2/MWhth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
lignite,2030,CO2 intensity,0.4,tCO2/MWhth,German sources
oil,2030,CO2 intensity,0.248,tCO2/MWhth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
geothermal,2030,CO2 intensity,0.026,tCO2/MWhth,https://www.eia.gov/environment/emissions/co2_vol_mass.php
solid biomass,2030,CO2 intensity,0.3,tCO2/MWhth,TODO
electrolysis,2030,investment,350,EUR/kWel,Palzer Thesis
electrolysis,2030,FOM,4,%/year,NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; budischak2013
electrolysis,2030,lifetime,18,years,NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; budischak2013
@ -109,6 +109,14 @@ SMR,2030,investment,540.56,EUR/kWCH4,https://www.gov.uk/government/publications/
SMR,2030,lifetime,25,years,TODO
SMR,2030,FOM,5.4,%/year,https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030
SMR,2030,efficiency,0.74,per unit,https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030
SMR CCS,2030,investment,1032,EUR/kWCH4,https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030; GBP 466 exchange 1.16; CCS costed at 300 EUR/tCO2/a
SMR CCS,2030,lifetime,25,years,TODO
SMR CCS,2030,FOM,5.4,%/year,https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030
SMR CCS,2030,efficiency,0.67,per unit,https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030; CCS uses 10% of gas
industry CCS,2030,investment,300,EUR/tCO2/a,Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
industry CCS,2030,FOM,2,%/year,Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
industry CCS,2030,lifetime,25,years,Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
industry CCS,2030,efficiency,0.9,per unit,Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
Fischer-Tropsch,2030,investment,677.6,EUR/kWH2,Fasihi doi:10.3390/su9020306 (60 kEUR/bpd = 847 EUR/kWL (1b = 1.7 MWh) 847*0.8 = 677.6)
Fischer-Tropsch,2030,lifetime,30,years,doi:10.3390/su9020306
Fischer-Tropsch,2030,FOM,3,%/year,doi:10.3390/su9020306
@ -118,7 +126,7 @@ DAC,2030,lifetime,30,years,Fasihi
DAC,2030,FOM,4,%/year,Fasihi
battery inverter,2030,investment,411,USD/kWel,budischak2013
battery inverter,2030,lifetime,20,years,budischak2013
battery inverter,2030,efficiency,0.81,per unit,budischak2013; Lund and Kempton (2008) http://dx.doi.org/10.1016/j.enpol.2008.06.007
battery inverter,2030,efficiency,0.81,per unit,budischak2013; Lund and Kempton (2008) https://doi.org/10.1016/j.enpol.2008.06.007
battery inverter,2030,FOM,3,%/year,budischak2013
battery storage,2030,investment,192,USD/kWh,budischak2013
battery storage,2030,lifetime,15,years,budischak2013
@ -175,9 +183,38 @@ decentral CHP,2030,lifetime,25,years,HP
decentral CHP,2030,investment,1400,EUR/kWel,HP
decentral CHP,2030,FOM,3,%/year,HP
decentral CHP,2030,discount rate,0.04,per unit,Palzer thesis
central CHP,2030,lifetime,25,years,HP
central CHP,2030,investment,650,EUR/kWel,HP
central CHP,2030,FOM,3,%/year,HP
central gas CHP,2030,lifetime,30,years,DEA
central gas CHP,2030,investment,1300,EUR/kWel,DEA
central gas CHP,2030,FOM,3,%/year,DEA
central gas CHP,2030,efficiency,0.45,per unit,DEA (condensation mode)
central gas CHP,2030,c_b,0.7,per unit,DEA (backpressure ratio)
central gas CHP,2030,c_v,0.17,per unit,DEA (loss of fuel for additional heat)
central gas CHP,2030,p_nom_ratio,1.,per unit,
central gas CHP,2030,VOM,0.82,EUR/MWh,DEA
central gas CHP CCS,2030,lifetime,30,years,DEA
central gas CHP CCS,2030,investment,1900,EUR/kWel,DEA + DIW extra for CCS on gas plant
central gas CHP CCS,2030,FOM,3,%/year,DEA
central gas CHP CCS,2030,efficiency,0.405,per unit,DEA (condensation mode + efficiency loss due to capture)
central gas CHP CCS,2030,c_b,0.7,per unit,DEA (backpressure ratio)
central gas CHP CCS,2030,c_v,0.17,per unit,DEA (loss of fuel for additional heat)
central gas CHP CCS,2030,p_nom_ratio,1.,per unit,
central gas CHP CCS,2030,VOM,0.82,EUR/MWh,DEA
central solid biomass CHP,2030,lifetime,40,years,DEA for wood pellets CHP
central solid biomass CHP,2030,investment,1990,EUR/kWel,DEA for wood pellets CHP
central solid biomass CHP,2030,FOM,3,%/year,DEA for wood pellets CHP
central solid biomass CHP,2030,efficiency,0.52,per unit,DEA for wood pellets CHP (condensation mode)
central solid biomass CHP,2030,c_b,1.01,per unit,DEA for wood pellets CHP (backpressure ratio)
central solid biomass CHP,2030,c_v,0.15,per unit,DEA for wood pellets CHP (loss of fuel for additional heat)
central solid biomass CHP,2030,p_nom_ratio,1.,per unit,
central solid biomass CHP,2030,VOM,2.2,EUR/MWh,DEA for wood pellets CHP
central solid biomass CHP CCS,2030,lifetime,40,years,DEA for wood pellets CHP
central solid biomass CHP CCS,2030,investment,2590,EUR/kWel,DEA for wood pellets CHP + DIW extra for CCS on gas plant
central solid biomass CHP CCS,2030,FOM,3,%/year,DEA for wood pellets CHP
central solid biomass CHP CCS,2030,efficiency,0.468,per unit,DEA for wood pellets CHP (condensation mode + efficiency loss due to capture)
central solid biomass CHP CCS,2030,c_b,1.01,per unit,DEA for wood pellets CHP (backpressure ratio)
central solid biomass CHP CCS,2030,c_v,0.15,per unit,DEA for wood pellets CHP (loss of fuel for additional heat)
central solid biomass CHP CCS,2030,p_nom_ratio,1.,per unit,
central solid biomass CHP CCS,2030,VOM,2.2,EUR/MWh,DEA for wood pellets CHP
micro CHP,2030,lifetime,20,years,DEA for PEMFC with methane (for unit consuming 2kW CH4)
micro CHP,2030,investment,4500,EUR/kWCH4,DEA for PEMFC with methane (for unit consuming 2kW CH4)
micro CHP,2030,FOM,6,%/year,DEA for PEMFC with methane (for unit consuming 2kW CH4)

1 technology year parameter value unit source
73 biomass 2030 efficiency 0.468 per unit DIW DataDoc http://hdl.handle.net/10419/80348
74 geothermal 2030 efficiency 0.239 per unit DIW DataDoc http://hdl.handle.net/10419/80348
75 nuclear 2030 efficiency 0.337 per unit DIW DataDoc http://hdl.handle.net/10419/80348
76 gas 2030 CO2 intensity 0.187 tCO2/MWth tCO2/MWhth https://www.eia.gov/environment/emissions/co2_vol_mass.php
77 coal 2030 efficiency 0.464 per unit DIW DataDoc http://hdl.handle.net/10419/80348 PC (Advanced/SuperC)
78 lignite 2030 efficiency 0.447 per unit DIW DataDoc http://hdl.handle.net/10419/80348
79 oil 2030 efficiency 0.393 per unit DIW DataDoc http://hdl.handle.net/10419/80348 CT
80 coal 2030 CO2 intensity 0.354 tCO2/MWth tCO2/MWhth https://www.eia.gov/environment/emissions/co2_vol_mass.php
81 lignite 2030 CO2 intensity 0.4 tCO2/MWth tCO2/MWhth German sources
82 oil 2030 CO2 intensity 0.248 tCO2/MWth tCO2/MWhth https://www.eia.gov/environment/emissions/co2_vol_mass.php
83 geothermal 2030 CO2 intensity 0.026 tCO2/MWth tCO2/MWhth https://www.eia.gov/environment/emissions/co2_vol_mass.php
84 solid biomass 2030 CO2 intensity 0.3 tCO2/MWth tCO2/MWhth TODO
85 electrolysis 2030 investment 350 EUR/kWel Palzer Thesis
86 electrolysis 2030 FOM 4 %/year NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; budischak2013
87 electrolysis 2030 lifetime 18 years NREL http://www.nrel.gov/docs/fy09osti/45873.pdf; budischak2013
109 SMR 2030 lifetime 25 years TODO
110 SMR 2030 FOM 5.4 %/year https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030
111 SMR 2030 efficiency 0.74 per unit https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030
112 SMR CCS 2030 investment 1032 EUR/kWCH4 https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030; GBP 466 exchange 1.16; CCS costed at 300 EUR/tCO2/a
113 SMR CCS 2030 lifetime 25 years TODO
114 SMR CCS 2030 FOM 5.4 %/year https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030
115 SMR CCS 2030 efficiency 0.67 per unit https://www.gov.uk/government/publications/hydrogen-supply-chain-evidence-base; slide 42 assumption for 2030; CCS uses 10% of gas
116 industry CCS 2030 investment 300 EUR/tCO2/a Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
117 industry CCS 2030 FOM 2 %/year Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
118 industry CCS 2030 lifetime 25 years Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
119 industry CCS 2030 efficiency 0.9 per unit Saygin et al 2013 https://doi.org/10.1016/j.ijggc.2013.05.032
120 Fischer-Tropsch 2030 investment 677.6 EUR/kWH2 Fasihi doi:10.3390/su9020306 (60 kEUR/bpd = 847 EUR/kWL (1b = 1.7 MWh) 847*0.8 = 677.6)
121 Fischer-Tropsch 2030 lifetime 30 years doi:10.3390/su9020306
122 Fischer-Tropsch 2030 FOM 3 %/year doi:10.3390/su9020306
126 DAC 2030 FOM 4 %/year Fasihi
127 battery inverter 2030 investment 411 USD/kWel budischak2013
128 battery inverter 2030 lifetime 20 years budischak2013
129 battery inverter 2030 efficiency 0.81 per unit budischak2013; Lund and Kempton (2008) http://dx.doi.org/10.1016/j.enpol.2008.06.007 budischak2013; Lund and Kempton (2008) https://doi.org/10.1016/j.enpol.2008.06.007
130 battery inverter 2030 FOM 3 %/year budischak2013
131 battery storage 2030 investment 192 USD/kWh budischak2013
132 battery storage 2030 lifetime 15 years budischak2013
183 decentral CHP 2030 investment 1400 EUR/kWel HP
184 decentral CHP 2030 FOM 3 %/year HP
185 decentral CHP 2030 discount rate 0.04 per unit Palzer thesis
186 central CHP central gas CHP 2030 lifetime 25 30 years HP DEA
187 central CHP central gas CHP 2030 investment 650 1300 EUR/kWel HP DEA
188 central CHP central gas CHP 2030 FOM 3 %/year HP DEA
189 central gas CHP 2030 efficiency 0.45 per unit DEA (condensation mode)
190 central gas CHP 2030 c_b 0.7 per unit DEA (backpressure ratio)
191 central gas CHP 2030 c_v 0.17 per unit DEA (loss of fuel for additional heat)
192 central gas CHP 2030 p_nom_ratio 1. per unit
193 central gas CHP 2030 VOM 0.82 EUR/MWh DEA
194 central gas CHP CCS 2030 lifetime 30 years DEA
195 central gas CHP CCS 2030 investment 1900 EUR/kWel DEA + DIW extra for CCS on gas plant
196 central gas CHP CCS 2030 FOM 3 %/year DEA
197 central gas CHP CCS 2030 efficiency 0.405 per unit DEA (condensation mode + efficiency loss due to capture)
198 central gas CHP CCS 2030 c_b 0.7 per unit DEA (backpressure ratio)
199 central gas CHP CCS 2030 c_v 0.17 per unit DEA (loss of fuel for additional heat)
200 central gas CHP CCS 2030 p_nom_ratio 1. per unit
201 central gas CHP CCS 2030 VOM 0.82 EUR/MWh DEA
202 central solid biomass CHP 2030 lifetime 40 years DEA for wood pellets CHP
203 central solid biomass CHP 2030 investment 1990 EUR/kWel DEA for wood pellets CHP
204 central solid biomass CHP 2030 FOM 3 %/year DEA for wood pellets CHP
205 central solid biomass CHP 2030 efficiency 0.52 per unit DEA for wood pellets CHP (condensation mode)
206 central solid biomass CHP 2030 c_b 1.01 per unit DEA for wood pellets CHP (backpressure ratio)
207 central solid biomass CHP 2030 c_v 0.15 per unit DEA for wood pellets CHP (loss of fuel for additional heat)
208 central solid biomass CHP 2030 p_nom_ratio 1. per unit
209 central solid biomass CHP 2030 VOM 2.2 EUR/MWh DEA for wood pellets CHP
210 central solid biomass CHP CCS 2030 lifetime 40 years DEA for wood pellets CHP
211 central solid biomass CHP CCS 2030 investment 2590 EUR/kWel DEA for wood pellets CHP + DIW extra for CCS on gas plant
212 central solid biomass CHP CCS 2030 FOM 3 %/year DEA for wood pellets CHP
213 central solid biomass CHP CCS 2030 efficiency 0.468 per unit DEA for wood pellets CHP (condensation mode + efficiency loss due to capture)
214 central solid biomass CHP CCS 2030 c_b 1.01 per unit DEA for wood pellets CHP (backpressure ratio)
215 central solid biomass CHP CCS 2030 c_v 0.15 per unit DEA for wood pellets CHP (loss of fuel for additional heat)
216 central solid biomass CHP CCS 2030 p_nom_ratio 1. per unit
217 central solid biomass CHP CCS 2030 VOM 2.2 EUR/MWh DEA for wood pellets CHP
218 micro CHP 2030 lifetime 20 years DEA for PEMFC with methane (for unit consuming 2kW CH4)
219 micro CHP 2030 investment 4500 EUR/kWCH4 DEA for PEMFC with methane (for unit consuming 2kW CH4)
220 micro CHP 2030 FOM 6 %/year DEA for PEMFC with methane (for unit consuming 2kW CH4)

View File

@ -36,6 +36,8 @@ override_component_attrs["Link"].loc["efficiency2"] = ["static or series","per u
override_component_attrs["Link"].loc["efficiency3"] = ["static or series","per unit",1.,"3rd bus efficiency","Input (optional)"]
override_component_attrs["Link"].loc["p2"] = ["series","MW",0.,"2nd bus output","Output"]
override_component_attrs["Link"].loc["p3"] = ["series","MW",0.,"3rd bus output","Output"]
override_component_attrs["StorageUnit"].loc["p_dispatch"] = ["series","MW",0.,"Storage discharging.","Output"]
override_component_attrs["StorageUnit"].loc["p_store"] = ["series","MW",0.,"Storage charging.","Output"]
@ -77,7 +79,7 @@ def calculate_nodal_cfs(n,label,nodal_cfs):
cf_c = p_c/capacities_c
index = pd.MultiIndex.from_tuples([(c.list_name,) + t for t in cf_c.index.to_list()])
nodal_cfs = nodal_cfs.reindex(nodal_cfs.index|index)
nodal_cfs = nodal_cfs.reindex(index|nodal_cfs.index)
nodal_cfs.loc[index,label] = cf_c.values
return nodal_cfs
@ -102,7 +104,7 @@ def calculate_cfs(n,label,cfs):
cf_c = p_c/capacities_c
cfs = cfs.reindex(cfs.index|pd.MultiIndex.from_product([[c.list_name],cf_c.index]))
cfs = cfs.reindex(pd.MultiIndex.from_product([[c.list_name],cf_c.index])|cfs.index)
cfs.loc[idx[c.list_name,list(cf_c.index)],label] = cf_c.values
@ -116,7 +118,7 @@ def calculate_nodal_costs(n,label,nodal_costs):
for c in n.iterate_components(n.branch_components|n.controllable_one_port_components^{"Load"}):
capital_costs = (c.df.capital_cost*c.df[opt_name.get(c.name,"p") + "_nom_opt"]).groupby((c.df.location,c.df.carrier)).sum()
index = pd.MultiIndex.from_tuples([(c.list_name,"capital") + t for t in capital_costs.index.to_list()])
nodal_costs = nodal_costs.reindex(nodal_costs.index|index)
nodal_costs = nodal_costs.reindex(index|nodal_costs.index)
nodal_costs.loc[index,label] = capital_costs.values
if c.name == "Link":
@ -137,7 +139,7 @@ def calculate_nodal_costs(n,label,nodal_costs):
marginal_costs = (p*c.df.marginal_cost).groupby((c.df.location,c.df.carrier)).sum()
index = pd.MultiIndex.from_tuples([(c.list_name,"marginal") + t for t in marginal_costs.index.to_list()])
nodal_costs = nodal_costs.reindex(nodal_costs.index|index)
nodal_costs = nodal_costs.reindex(index|nodal_costs.index)
nodal_costs.loc[index,label] = marginal_costs.values
return nodal_costs
@ -149,7 +151,7 @@ def calculate_costs(n,label,costs):
capital_costs = c.df.capital_cost*c.df[opt_name.get(c.name,"p") + "_nom_opt"]
capital_costs_grouped = capital_costs.groupby(c.df.carrier).sum()
costs = costs.reindex(costs.index|pd.MultiIndex.from_product([[c.list_name],["capital"],capital_costs_grouped.index]))
costs = costs.reindex(pd.MultiIndex.from_product([[c.list_name],["capital"],capital_costs_grouped.index])|costs.index)
costs.loc[idx[c.list_name,"capital",list(capital_costs_grouped.index)],label] = capital_costs_grouped.values
@ -173,7 +175,7 @@ def calculate_costs(n,label,costs):
marginal_costs_grouped = marginal_costs.groupby(c.df.carrier).sum()
costs = costs.reindex(costs.index|pd.MultiIndex.from_product([[c.list_name],["marginal"],marginal_costs_grouped.index]))
costs = costs.reindex(pd.MultiIndex.from_product([[c.list_name],["marginal"],marginal_costs_grouped.index])|costs.index)
costs.loc[idx[c.list_name,"marginal",list(marginal_costs_grouped.index)],label] = marginal_costs_grouped.values
@ -198,7 +200,7 @@ def calculate_nodal_capacities(n,label,nodal_capacities):
for c in n.iterate_components(n.branch_components|n.controllable_one_port_components^{"Load"}):
nodal_capacities_c = c.df[opt_name.get(c.name,"p") + "_nom_opt"].groupby((c.df.location,c.df.carrier)).sum()
index = pd.MultiIndex.from_tuples([(c.list_name,) + t for t in nodal_capacities_c.index.to_list()])
nodal_capacities = nodal_capacities.reindex(nodal_capacities.index|index)
nodal_capacities = nodal_capacities.reindex(index|nodal_capacities.index)
nodal_capacities.loc[index,label] = nodal_capacities_c.values
return nodal_capacities
@ -211,7 +213,7 @@ def calculate_capacities(n,label,capacities):
for c in n.iterate_components(n.branch_components|n.controllable_one_port_components^{"Load"}):
capacities_grouped = c.df[opt_name.get(c.name,"p") + "_nom_opt"].groupby(c.df.carrier).sum()
capacities = capacities.reindex(capacities.index|pd.MultiIndex.from_product([[c.list_name],capacities_grouped.index]))
capacities = capacities.reindex(pd.MultiIndex.from_product([[c.list_name],capacities_grouped.index])|capacities.index)
capacities.loc[idx[c.list_name,list(capacities_grouped.index)],label] = capacities_grouped.values
@ -238,7 +240,7 @@ def calculate_energy(n,label,energy):
for port in [col[3:] for col in c.df.columns if col[:3] == "bus"]:
c_energies -= c.pnl["p"+port].multiply(n.snapshot_weightings,axis=0).sum().groupby(c.df.carrier).sum()
energy = energy.reindex(energy.index|pd.MultiIndex.from_product([[c.list_name],c_energies.index]))
energy = energy.reindex(pd.MultiIndex.from_product([[c.list_name],c_energies.index])|energy.index)
energy.loc[idx[c.list_name,list(c_energies.index)],label] = c_energies.values
@ -246,17 +248,13 @@ def calculate_energy(n,label,energy):
def calculate_supply(n,label,supply):
"""calculate the max dispatch of each component at the buses where the loads are attached"""
"""calculate the max dispatch of each component at the buses aggregated by carrier"""
load_types = n.loads.carrier.value_counts().index
bus_carriers = n.buses.carrier.unique()
for i in load_types:
buses = n.loads.bus[n.loads.carrier == i].values
bus_map = pd.Series(False,index=n.buses.index)
bus_map.loc[buses] = True
for i in bus_carriers:
bus_map = (n.buses.carrier == i)
bus_map.at[""] = False
for c in n.iterate_components(n.one_port_components):
@ -267,15 +265,15 @@ def calculate_supply(n,label,supply):
s = c.pnl.p[items].max().multiply(c.df.loc[items,'sign']).groupby(c.df.loc[items,'carrier']).sum()
supply = supply.reindex(supply.index|pd.MultiIndex.from_product([[i],[c.list_name],s.index]))
supply = supply.reindex(pd.MultiIndex.from_product([[i],[c.list_name],s.index])|supply.index)
supply.loc[idx[i,c.list_name,list(s.index)],label] = s.values
for c in n.iterate_components(n.branch_components):
for end in ["0","1"]:
for end in [col[3:] for col in c.df.columns if col[:3] == "bus"]:
items = c.df.index[c.df["bus" + end].map(bus_map)]
items = c.df.index[c.df["bus" + end].map(bus_map,na_action=False)]
if len(items) == 0:
continue
@ -283,23 +281,20 @@ def calculate_supply(n,label,supply):
#lots of sign compensation for direction and to do maximums
s = (-1)**(1-int(end))*((-1)**int(end)*c.pnl["p"+end][items]).max().groupby(c.df.loc[items,'carrier']).sum()
supply = supply.reindex(supply.index|pd.MultiIndex.from_product([[i],[c.list_name],s.index]))
supply.loc[idx[i,c.list_name,list(s.index)],label] = s.values
supply = supply.reindex(pd.MultiIndex.from_product([[i],[c.list_name],s.index+end])|supply.index)
supply.loc[idx[i,c.list_name,list(s.index+end)],label] = s.values
return supply
def calculate_supply_energy(n,label,supply_energy):
"""calculate the total dispatch of each component at the buses where the loads are attached"""
"""calculate the total energy supply/consuption of each component at the buses aggregated by carrier"""
load_types = n.loads.carrier.value_counts().index
for i in load_types:
bus_carriers = n.buses.carrier.unique()
buses = n.loads.bus[n.loads.carrier == i].values
bus_map = pd.Series(False,index=n.buses.index)
bus_map.loc[buses] = True
for i in bus_carriers:
bus_map = (n.buses.carrier == i)
bus_map.at[""] = False
for c in n.iterate_components(n.one_port_components):
@ -308,31 +303,31 @@ def calculate_supply_energy(n,label,supply_energy):
if len(items) == 0:
continue
s = c.pnl.p[items].sum().multiply(c.df.loc[items,'sign']).groupby(c.df.loc[items,'carrier']).sum()
s = c.pnl.p[items].multiply(n.snapshot_weightings,axis=0).sum().multiply(c.df.loc[items,'sign']).groupby(c.df.loc[items,'carrier']).sum()
supply_energy = supply_energy.reindex(supply_energy.index|pd.MultiIndex.from_product([[i],[c.list_name],s.index]))
supply_energy = supply_energy.reindex(pd.MultiIndex.from_product([[i],[c.list_name],s.index])|supply_energy.index)
supply_energy.loc[idx[i,c.list_name,list(s.index)],label] = s.values
for c in n.iterate_components(n.branch_components):
for end in ["0","1"]:
for end in [col[3:] for col in c.df.columns if col[:3] == "bus"]:
items = c.df.index[c.df["bus" + end].map(bus_map)]
items = c.df.index[c.df["bus" + str(end)].map(bus_map,na_action=False)]
if len(items) == 0:
continue
s = (-1)*c.pnl["p"+end][items].sum().groupby(c.df.loc[items,'carrier']).sum()
s = (-1)*c.pnl["p"+end][items].multiply(n.snapshot_weightings,axis=0).sum().groupby(c.df.loc[items,'carrier']).sum()
supply_energy = supply_energy.reindex(supply_energy.index|pd.MultiIndex.from_product([[i],[c.list_name],s.index]))
supply_energy.loc[idx[i,c.list_name,list(s.index)],label] = s.values
supply_energy = supply_energy.reindex(pd.MultiIndex.from_product([[i],[c.list_name],s.index+end])|supply_energy.index)
supply_energy.loc[idx[i,c.list_name,list(s.index+end)],label] = s.values
return supply_energy
def calculate_metrics(n,label,metrics):
metrics = metrics.reindex(metrics.index|pd.Index(["line_volume","line_volume_limit","line_volume_AC","line_volume_DC","line_volume_shadow","co2_shadow"]))
metrics = metrics.reindex(pd.Index(["line_volume","line_volume_limit","line_volume_AC","line_volume_DC","line_volume_shadow","co2_shadow"])|metrics.index)
metrics.at["line_volume_DC",label] = (n.links.length*n.links.p_nom_opt)[n.links.carrier == "DC"].sum()
metrics.at["line_volume_AC",label] = (n.lines.length*n.lines.s_nom_opt).sum()
@ -402,7 +397,7 @@ def calculate_weighted_prices(n,label,weighted_prices):
if names.empty:
continue
load += n.links_t.p0[names].groupby(n.links.loc[names,"bus0"],axis=1).sum(axis=1)
load += n.links_t.p0[names].groupby(n.links.loc[names,"bus0"],axis=1).sum()
#Add H2 Store when charging
#if carrier == "H2":

View File

@ -32,6 +32,9 @@ override_component_attrs["Link"].loc["efficiency2"] = ["static or series","per u
override_component_attrs["Link"].loc["efficiency3"] = ["static or series","per unit",1.,"3rd bus efficiency","Input (optional)"]
override_component_attrs["Link"].loc["p2"] = ["series","MW",0.,"2nd bus output","Output"]
override_component_attrs["Link"].loc["p3"] = ["series","MW",0.,"3rd bus output","Output"]
override_component_attrs["StorageUnit"].loc["p_dispatch"] = ["series","MW",0.,"Storage discharging.","Output"]
override_component_attrs["StorageUnit"].loc["p_store"] = ["series","MW",0.,"Storage charging.","Output"]
def rename_techs_tyndp(tech):

View File

@ -623,16 +623,27 @@ def add_storage(network):
if options['SMR']:
network.madd("Link",
nodes + " SMR",
nodes + " SMR CCS",
bus0=["EU gas"]*len(nodes),
bus1=nodes+" H2",
bus2="co2 atmosphere",
bus3="co2 stored",
p_nom_extendable=True,
carrier="SMR",
efficiency=costs.at["SMR","efficiency"],
carrier="SMR CCS",
efficiency=costs.at["SMR CCS","efficiency"],
efficiency2=costs.at['gas','CO2 intensity']*(1-options["ccs_fraction"]),
efficiency3=costs.at['gas','CO2 intensity']*options["ccs_fraction"],
capital_cost=costs.at["SMR CCS","fixed"])
network.madd("Link",
nodes + " SMR",
bus0=["EU gas"]*len(nodes),
bus1=nodes+" H2",
bus2="co2 atmosphere",
p_nom_extendable=True,
carrier="SMR",
efficiency=costs.at["SMR","efficiency"],
efficiency2=costs.at['gas','CO2 intensity'],
capital_cost=costs.at["SMR","fixed"])
@ -765,7 +776,7 @@ def add_heat(network):
network.madd("Load",
nodes[name],
suffix=" " + name + "heat",
suffix=" " + name + " heat",
bus=nodes[name] + " " + name + " heat",
carrier=name + " heat",
p_set=heat_load)
@ -868,47 +879,72 @@ def add_heat(network):
if options["chp"]:
if name == "urban central":
#additional bus, to which we can also connect biomass
network.madd("Bus",
nodes[name] + " urban central CHP",
carrier="urban central CHP")
#add gas CHP; biomass CHP is added in biomass section
network.madd("Link",
nodes[name] + " urban central gas CHP electric",
bus0="EU gas",
bus1=nodes[name],
bus2="co2 atmosphere",
carrier="urban central gas CHP electric",
p_nom_extendable=True,
capital_cost=costs.at['central gas CHP','fixed']*costs.at['central gas CHP','efficiency'],
marginal_cost=costs.at['central gas CHP','VOM'],
efficiency=costs.at['central gas CHP','efficiency'],
efficiency2=costs.at['gas','CO2 intensity'],
c_b=costs.at['central gas CHP','c_b'],
c_v=costs.at['central gas CHP','c_v'],
p_nom_ratio=costs.at['central gas CHP','p_nom_ratio'])
network.madd("Link",
nodes[name] + " gas to urban central CHP",
nodes[name] + " urban central gas CHP heat",
bus0="EU gas",
bus1=nodes[name] + " urban central CHP",
bus1=nodes[name] + " urban central heat",
bus2="co2 atmosphere",
carrier="urban central gas CHP heat",
p_nom_extendable=True,
marginal_cost=costs.at['central gas CHP','VOM'],
efficiency=costs.at['central gas CHP','efficiency']/costs.at['central gas CHP','c_v'],
efficiency2=costs.at['gas','CO2 intensity'])
network.madd("Link",
nodes[name] + " urban central gas CHP CCS electric",
bus0="EU gas",
bus1=nodes[name],
bus2="co2 atmosphere",
bus3="co2 stored",
carrier="urban central gas CHP CCS electric",
p_nom_extendable=True,
capital_cost=costs.at['central gas CHP CCS','fixed']*costs.at['central gas CHP CCS','efficiency'],
marginal_cost=costs.at['central gas CHP CCS','VOM'],
efficiency=costs.at['central gas CHP CCS','efficiency'],
efficiency2=costs.at['gas','CO2 intensity']*(1-options["ccs_fraction"]),
efficiency3=costs.at['gas','CO2 intensity']*options["ccs_fraction"],
carrier="gas to central CHP",
p_nom_extendable=True)
c_b=costs.at['central gas CHP CCS','c_b'],
c_v=costs.at['central gas CHP CCS','c_v'],
p_nom_ratio=costs.at['central gas CHP CCS','p_nom_ratio'])
network.madd("Link",
nodes[name] + " urban central CHP electric",
bus0=nodes[name] + " urban central CHP",
bus1=nodes[name],
carrier="urban central CHP electric",
p_nom_extendable=True,
capital_cost=costs.at['central CHP','fixed']*options['chp_parameters']['eta_elec'],
efficiency=options['chp_parameters']['eta_elec'])
network.madd("Link",
nodes[name] + " urban central CHP heat",
bus0=nodes[name] + " urban central CHP",
nodes[name] + " urban central gas CHP CCS heat",
bus0="EU gas",
bus1=nodes[name] + " urban central heat",
carrier="urban central CHP heat",
bus2="co2 atmosphere",
bus3="co2 stored",
carrier="urban central gas CHP CCS heat",
p_nom_extendable=True,
efficiency=options['chp_parameters']['eta_elec']/options['chp_parameters']['c_v'])
marginal_cost=costs.at['central gas CHP CCS','VOM'],
efficiency=costs.at['central gas CHP CCS','efficiency']/costs.at['central gas CHP CCS','c_v'],
efficiency2=costs.at['gas','CO2 intensity']*(1-options["ccs_fraction"]),
efficiency3=costs.at['gas','CO2 intensity']*options["ccs_fraction"])
else:
network.madd("Link",
nodes[name] + " " + name + " micro CHP",
nodes[name] + " " + name + " micro gas CHP",
p_nom_extendable=True,
bus0=["EU gas"]*len(nodes[name]),
bus0="EU gas",
bus1=nodes[name],
bus2=nodes[name] + " " + name + " heat",
bus3="co2 atmosphere",
carrier=name + " micro CHP",
carrier=name + " micro gas CHP",
efficiency=costs.at['micro CHP','efficiency'],
efficiency2=costs.at['micro CHP','efficiency-heat'],
efficiency3=costs.at['gas','CO2 intensity'],
@ -1028,20 +1064,61 @@ def add_biomass(network):
#AC buses with district heating
urban_central = n.buses.index[n.buses.carrier == "urban central heat"]
if not urban_central.empty:
if not urban_central.empty and options["chp"]:
urban_central = urban_central.str[:-len(" urban central heat")]
#with BECCS
network.madd("Link",
urban_central + " solid biomass to urban central CHP",
urban_central + " urban central solid biomass CHP electric",
bus0="EU solid biomass",
bus1=urban_central + " urban central CHP",
bus1=urban_central,
carrier="urban central solid biomass CHP electric",
p_nom_extendable=True,
capital_cost=costs.at['central solid biomass CHP','fixed']*costs.at['central solid biomass CHP','efficiency'],
marginal_cost=costs.at['central solid biomass CHP','VOM'],
efficiency=costs.at['central solid biomass CHP','efficiency'],
c_b=costs.at['central solid biomass CHP','c_b'],
c_v=costs.at['central solid biomass CHP','c_v'],
p_nom_ratio=costs.at['central solid biomass CHP','p_nom_ratio'])
network.madd("Link",
urban_central + " urban central solid biomass CHP heat",
bus0="EU solid biomass",
bus1=urban_central + " urban central heat",
carrier="urban central solid biomass CHP heat",
p_nom_extendable=True,
marginal_cost=costs.at['central solid biomass CHP','VOM'],
efficiency=costs.at['central solid biomass CHP','efficiency']/costs.at['central solid biomass CHP','c_v'])
network.madd("Link",
urban_central + " urban central solid biomass CHP CCS electric",
bus0="EU solid biomass",
bus1=urban_central,
bus2="co2 atmosphere",
bus3="co2 stored",
carrier="urban central solid biomass CHP CCS electric",
p_nom_extendable=True,
capital_cost=costs.at['central solid biomass CHP CCS','fixed']*costs.at['central solid biomass CHP CCS','efficiency'],
marginal_cost=costs.at['central solid biomass CHP CCS','VOM'],
efficiency=costs.at['central solid biomass CHP CCS','efficiency'],
efficiency2=-costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"],
efficiency3=costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"],
carrier="solid biomass to urban central CHP",
p_nom_extendable=True)
c_b=costs.at['central solid biomass CHP','c_b'],
c_v=costs.at['central solid biomass CHP','c_v'],
p_nom_ratio=costs.at['central solid biomass CHP','p_nom_ratio'])
network.madd("Link",
urban_central + " urban central solid biomass CHP CCS heat",
bus0="EU solid biomass",
bus1=urban_central + " urban central heat",
bus2="co2 atmosphere",
bus3="co2 stored",
carrier="urban central solid biomass CHP CCS heat",
p_nom_extendable=True,
marginal_cost=costs.at['central solid biomass CHP CCS','VOM'],
efficiency=costs.at['central solid biomass CHP CCS','efficiency']/costs.at['central solid biomass CHP CCS','c_v'],
efficiency2=-costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"],
efficiency3=costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"])
def add_industry(network):
@ -1057,43 +1134,70 @@ def add_industry(network):
solid_biomass_by_country = industrial_demand["solid biomass"].groupby(pop_layout.ct).sum()
countries = solid_biomass_by_country.index
network.madd("Bus",
["solid biomass for industry"],
carrier="solid biomass for industry")
network.madd("Load",
["solid biomass for industry"],
bus="EU solid biomass",
bus="solid biomass for industry",
carrier="solid biomass for industry",
p_set=solid_biomass_by_country.sum()/8760.)
#Net transfer of CO2 from atmosphere to stored
network.madd("Load",
["solid biomass for industry co2 from atmosphere"],
bus="co2 atmosphere",
carrier="solid biomass for industry co2 from atmosphere",
p_set=solid_biomass_by_country.sum()*costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"]/8760.)
network.madd("Link",
["solid biomass for industry"],
bus0="EU solid biomass",
bus1="solid biomass for industry",
carrier="solid biomass for industry",
p_nom_extendable=True,
efficiency=1.)
network.madd("Load",
["solid biomass for industry co2 to stored"],
bus="co2 stored",
carrier="solid biomass for industry co2 to stored",
p_set=-solid_biomass_by_country.sum()*costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"]/8760.)
network.madd("Link",
["solid biomass for industry CCS"],
bus0="EU solid biomass",
bus1="solid biomass for industry",
bus2="co2 atmosphere",
bus3="co2 stored",
carrier="solid biomass for industry CCS",
p_nom_extendable=True,
capital_cost=costs.at["industry CCS","fixed"]*costs.at['solid biomass','CO2 intensity']*8760, #8760 converts EUR/(tCO2/a) to EUR/(tCO2/h)
efficiency=0.9,
efficiency2=-costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"],
efficiency3=costs.at['solid biomass','CO2 intensity']*options["ccs_fraction"])
network.madd("Bus",
["gas for industry"],
carrier="gas for industry")
network.madd("Load",
["gas for industry"],
bus="EU gas",
bus="gas for industry",
carrier="gas for industry",
p_set=industrial_demand.loc[nodes,"methane"].sum()/8760.)
network.madd("Load",
["gas for industry co2 to atmosphere"],
bus="co2 atmosphere",
carrier="gas for industry co2 to atmosphere",
p_set=-industrial_demand.loc[nodes,"methane"].sum()*costs.at['gas','CO2 intensity']*(1-options["ccs_fraction"])/8760.)
network.madd("Link",
["gas for industry"],
bus0="EU gas",
bus1="gas for industry",
bus2="co2 atmosphere",
carrier="gas for industry",
p_nom_extendable=True,
efficiency=1.,
efficiency2=costs.at['gas','CO2 intensity'])
network.madd("Load",
["gas for industry co2 to stored"],
bus="co2 stored",
carrier="gas for industry co2 to stored",
p_set=-industrial_demand.loc[nodes,"methane"].sum()*costs.at['gas','CO2 intensity']*options["ccs_fraction"]/8760.)
network.madd("Link",
["gas for industry CCS"],
bus0="EU gas",
bus1="gas for industry",
bus2="co2 atmosphere",
bus3="co2 stored",
carrier="gas for industry CCS",
p_nom_extendable=True,
capital_cost=costs.at["industry CCS","fixed"]*costs.at['gas','CO2 intensity']*8760, #8760 converts EUR/(tCO2/a) to EUR/(tCO2/h)
efficiency=0.9,
efficiency2=costs.at['gas','CO2 intensity']*(1-options["ccs_fraction"]),
efficiency3=costs.at['gas','CO2 intensity']*options["ccs_fraction"])
network.madd("Load",
@ -1184,17 +1288,37 @@ def add_industry(network):
carrier="industry new electricity",
p_set = (industrial_demand.loc[nodes,"electricity"]-industrial_demand.loc[nodes,"current electricity"])/8760.)
network.madd("Load",
["process emissions to atmosphere"],
bus="co2 atmosphere",
carrier="process emissions to atmosphere",
p_set = -industrial_demand.loc[nodes,"process emission"].sum()*(1-options["ccs_fraction"])/8760.)
network.madd("Bus",
["process emissions"],
carrier="process emissions")
#this should be process emissions fossil+feedstock
#then need load on atmosphere for feedstock emissions that are currently going to atmosphere via Link Fischer-Tropsch demand
network.madd("Load",
["process emissions to stored"],
bus="co2 stored",
carrier="process emissions to stored",
p_set = -industrial_demand.loc[nodes,"process emission"].sum()*options["ccs_fraction"]/8760.)
["process emissions"],
bus="process emissions",
carrier="process emissions",
p_set = -industrial_demand.loc[nodes,"process emission"].sum()/8760.)
network.madd("Link",
["process emissions"],
bus0="process emissions",
bus1="co2 atmosphere",
carrier="process emissions",
p_nom_extendable=True,
efficiency=1.)
#assume enough local waste heat for CCS
network.madd("Link",
["process emissions CCS"],
bus0="process emissions",
bus1="co2 atmosphere",
bus2="co2 stored",
carrier="process emissions CCS",
p_nom_extendable=True,
capital_cost=costs.at["industry CCS","fixed"]*8760, #8760 converts EUR/(tCO2/a) to EUR/(tCO2/h)
efficiency=(1-options["ccs_fraction"]),
efficiency2=options["ccs_fraction"])

View File

@ -1,7 +1,15 @@
import os
os.system("conda config --add channels http://conda.anaconda.org/gurobi")
os.system("conda install -y gurobi=8.1.0")
import sys
sys.path = ["/home/vres/data/tom/lib/pypsa"] + sys.path
sys.path = ["pypsa"] + sys.path
import numpy as np
import pandas as pd
@ -12,6 +20,8 @@ import os
import pypsa
from pypsa.linopt import get_var, linexpr, define_constraints
from pypsa.descriptors import free_output_series_dataframes
# Suppress logging of the slack bus choices
@ -103,80 +113,74 @@ def add_opts_constraints(n, opts=None):
ext_gens_i = n.generators.index[n.generators.carrier.isin(conv_techs) & n.generators.p_nom_extendable]
n.model.safe_peakdemand = pypsa.opt.Constraint(expr=sum(n.model.generator_p_nom[gen] for gen in ext_gens_i) >= peakdemand - exist_conv_caps)
def add_lv_constraint(n):
line_volume = getattr(n, 'line_volume_limit', None)
if line_volume is not None and not np.isinf(line_volume):
n.model.line_volume_constraint = pypsa.opt.Constraint(
expr=((sum(n.model.passive_branch_s_nom["Line",line]*n.lines.at[line,"length"]
for line in n.lines.index[n.lines.s_nom_extendable]) +
sum(n.model.link_p_nom[link]*n.links.at[link,"length"]
for link in n.links.index[(n.links.carrier=='DC') &
n.links.p_nom_extendable]))
<= line_volume)
)
def add_eps_storage_constraint(n):
if not hasattr(n, 'epsilon'):
n.epsilon = 1e-5
fix_sus_i = n.storage_units.index[~ n.storage_units.p_nom_extendable]
n.model.objective.expr += sum(n.epsilon * n.model.state_of_charge[su, n.snapshots[0]] for su in fix_sus_i)
def add_battery_constraints(network):
def add_battery_constraints(n):
nodes = list(network.buses.index[network.buses.carrier == "battery"])
nodes = n.buses.index[n.buses.carrier == "battery"]
def battery(model, node):
return model.link_p_nom[node + " charger"] == model.link_p_nom[node + " discharger"]*network.links.at[node + " charger","efficiency"]
link_p_nom = get_var(n, "Link", "p_nom")
network.model.battery = pypsa.opt.Constraint(nodes, rule=battery)
lhs = linexpr((1,link_p_nom[nodes + " charger"]),
(-n.links.loc[nodes + " discharger", "efficiency"].values,
link_p_nom[nodes + " discharger"].values))
define_constraints(n, lhs, "=", 0, 'Link', 'charger_ratio')
def add_chp_constraints(n):
def add_chp_constraints(network):
electric = n.links.index[n.links.index.str.contains("urban central") & n.links.index.str.contains("CHP") & n.links.index.str.contains("electric")]
heat = n.links.index[n.links.index.str.contains("urban central") & n.links.index.str.contains("CHP") & n.links.index.str.contains("heat")]
options = snakemake.config["sector"]
if not electric.empty:
if hasattr(network.links.index,"str") and network.links.index.str.contains("CHP").any():
link_p_nom = get_var(n, "Link", "p_nom")
#AC buses with district heating
urban_central = n.buses.index[n.buses.carrier == "urban central heat"]
if not urban_central.empty:
urban_central = urban_central.str[:-len(" urban central heat")]
#ratio of output heat to electricity set by p_nom_ratio
lhs = linexpr((n.links.loc[electric,"efficiency"]
*n.links.loc[electric,'p_nom_ratio'],
link_p_nom[electric]),
(-n.links.loc[heat,"efficiency"].values,
link_p_nom[heat].values))
define_constraints(n, lhs, "=", 0, 'chplink', 'fix_p_nom_ratio')
link_p = get_var(n, "Link", "p")
def chp_nom(model,node):
return network.links.at[node + " urban central CHP electric","efficiency"]*options['chp_parameters']['p_nom_ratio']*model.link_p_nom[node + " urban central CHP electric"] == network.links.at[node + " urban central CHP heat","efficiency"]*options['chp_parameters']['p_nom_ratio']*model.link_p_nom[node + " urban central CHP heat"]
#backpressure
lhs = linexpr((n.links.loc[electric,'c_b'].values
*n.links.loc[heat,"efficiency"],
link_p[heat]),
(-n.links.loc[electric,"efficiency"].values,
link_p[electric].values))
define_constraints(n, lhs, "<=", 0, 'chplink', 'backpressure')
network.model.chp_nom = pypsa.opt.Constraint(urban_central,rule=chp_nom)
#top_iso_fuel_line
lhs = linexpr((1,link_p[heat]),
(1,link_p[electric].values),
(-1,link_p_nom[electric].values))
define_constraints(n, lhs, "<=", 0, 'chplink', 'top_iso_fuel_line')
def backpressure(model,node,snapshot):
return options['chp_parameters']['c_m']*network.links.at[node + " urban central CHP heat","efficiency"]*model.link_p[node + " urban central CHP heat",snapshot] <= network.links.at[node + " urban central CHP electric","efficiency"]*model.link_p[node + " urban central CHP electric",snapshot]
network.model.backpressure = pypsa.opt.Constraint(urban_central,list(network.snapshots),rule=backpressure)
def top_iso_fuel_line(model,node,snapshot):
return model.link_p[node + " urban central CHP heat",snapshot] + model.link_p[node + " urban central CHP electric",snapshot] <= model.link_p_nom[node + " urban central CHP electric"]
network.model.top_iso_fuel_line = pypsa.opt.Constraint(urban_central,list(network.snapshots),rule=top_iso_fuel_line)
def extra_functionality(n, snapshots):
#add_opts_constraints(n, opts)
#add_eps_storage_constraint(n)
add_chp_constraints(n)
add_battery_constraints(n)
def fix_branches(n, lines_s_nom=None, links_p_nom=None):
if lines_s_nom is not None and len(lines_s_nom) > 0:
for l, s_nom in lines_s_nom.iteritems():
n.model.passive_branch_s_nom["Line", l].fix(s_nom)
if isinstance(n.opt, pypsa.opf.PersistentSolver):
n.opt.update_var(n.model.passive_branch_s_nom)
n.lines.loc[lines_s_nom.index,"s_nom"] = lines_s_nom.values
n.lines.loc[lines_s_nom.index,"s_nom_extendable"] = False
if links_p_nom is not None and len(links_p_nom) > 0:
for l, p_nom in links_p_nom.iteritems():
n.model.link_p_nom[l].fix(p_nom)
if isinstance(n.opt, pypsa.opf.PersistentSolver):
n.opt.update_var(n.model.link_p_nom)
n.links.loc[links_p_nom.index,"p_nom"] = links_p_nom.values
n.links.loc[links_p_nom.index,"p_nom_extendable"] = False
def solve_network(n, config=None, solver_log=None, opts=None):
if config is None:
@ -191,16 +195,6 @@ def solve_network(n, config=None, solver_log=None, opts=None):
def run_lopf(n, allow_warning_status=False, fix_zero_lines=False, fix_ext_lines=False):
free_output_series_dataframes(n)
if not hasattr(n, 'opt') or not isinstance(n.opt, pypsa.opf.PersistentSolver):
pypsa.opf.network_lopf_build_model(n, formulation=solve_opts['formulation'])
add_opts_constraints(n, opts)
add_lv_constraint(n)
#add_eps_storage_constraint(n)
add_battery_constraints(n)
add_chp_constraints(n)
pypsa.opf.network_lopf_prepare_solver(n, solver_name=solver_name)
if fix_zero_lines:
fix_lines_b = (n.lines.s_nom_opt == 0.) & n.lines.s_nom_extendable
fix_links_b = (n.links.carrier=='DC') & (n.links.p_nom_opt == 0.) & n.links.p_nom_extendable
@ -212,19 +206,19 @@ def solve_network(n, config=None, solver_log=None, opts=None):
fix_branches(n,
lines_s_nom=n.lines.loc[n.lines.s_nom_extendable, 's_nom_opt'],
links_p_nom=n.links.loc[(n.links.carrier=='DC') & n.links.p_nom_extendable, 'p_nom_opt'])
if not fix_ext_lines and hasattr(n.model, 'line_volume_constraint'):
def extra_postprocessing(n, snapshots, duals):
index = list(n.model.line_volume_constraint.keys())
cdata = pd.Series(list(n.model.line_volume_constraint.values()),
index=index)
n.line_volume_limit_dual = -cdata.map(duals).sum()
print("line volume limit dual:",n.line_volume_limit_dual)
if "line_volume_constraint" in n.global_constraints.index:
n.global_constraints.drop("line_volume_constraint",inplace=True)
else:
extra_postprocessing = None
if "line_volume_constraint" not in n.global_constraints.index:
line_volume = getattr(n, 'line_volume_limit', None)
if line_volume is not None and not np.isinf(line_volume):
n.add("GlobalConstraint",
"line_volume_constraint",
type="transmission_volume_expansion_limit",
carrier_attribute="AC,DC",
sense="<=",
constant=line_volume)
# Firing up solve will increase memory consumption tremendously, so
# make sure we freed everything we can
@ -237,21 +231,25 @@ def solve_network(n, config=None, solver_log=None, opts=None):
#sys.exit()
status, termination_condition = \
pypsa.opf.network_lopf_solve(n,
status, termination_condition = n.lopf(pyomo=False,
solver_name=solver_name,
solver_logfile=solver_log,
solver_options=solver_options,
formulation=solve_opts['formulation'],
extra_postprocessing=extra_postprocessing
extra_functionality=extra_functionality,
formulation=solve_opts['formulation'])
#extra_postprocessing=extra_postprocessing
#keep_files=True
#free_memory={'pypsa'}
)
assert status == "ok" or allow_warning_status and status == 'warning', \
("network_lopf did abort with status={} "
"and termination_condition={}"
.format(status, termination_condition))
if not fix_ext_lines and "line_volume_constraint" in n.global_constraints.index:
n.line_volume_limit_dual = n.global_constraints.at["line_volume_constraint","mu"]
print("line volume limit dual:",n.line_volume_limit_dual)
return status, termination_condition
lines_ext_b = n.lines.s_nom_extendable
@ -286,21 +284,6 @@ def solve_network(n, config=None, solver_log=None, opts=None):
)
logger.debug("lines.num_parallel={}".format(n.lines.loc[lines_ext_typed_b, 'num_parallel']))
if isinstance(n.opt, pypsa.opf.PersistentSolver):
n.calculate_dependent_values()
assert solve_opts['formulation'] == 'kirchhoff', \
"Updating persistent solvers has only been implemented for the kirchhoff formulation for now"
n.opt.remove_constraint(n.model.cycle_constraints)
del n.model.cycle_constraints_index
del n.model.cycle_constraints_index_0
del n.model.cycle_constraints_index_1
del n.model.cycle_constraints
pypsa.opf.define_passive_branch_flows_with_kirchhoff(n, n.snapshots, skip_vars=True)
n.opt.add_constraint(n.model.cycle_constraints)
iteration = 1
lines['s_nom_opt'] = lines['s_nom'] * n.lines['num_parallel'].where(n.lines.type != '', 1.)