fix Nyears scaling

This commit is contained in:
Fabian 2023-03-10 14:12:22 +01:00
parent 2baf8f5ba4
commit 4f0ddf2e95
8 changed files with 94 additions and 50 deletions

View File

@ -83,7 +83,7 @@ def build_transport_demand(traffic_fn, airtemp_fn, nodes, nodal_transport_data):
) )
transport = ( transport = (
(transport_shape.multiply(energy_totals_transport) * 1e6 * Nyears) (transport_shape.multiply(energy_totals_transport) * 1e6 * nyears)
.divide(efficiency_gain * ice_correction) .divide(efficiency_gain * ice_correction)
.multiply(1 + dd_EV) .multiply(1 + dd_EV)
) )
@ -181,7 +181,7 @@ if __name__ == "__main__":
snapshots = pd.date_range(freq="h", **snakemake.config["snapshots"], tz="UTC") snapshots = pd.date_range(freq="h", **snakemake.config["snapshots"], tz="UTC")
Nyears = 1 nyears = len(snapshots) / 8760
nodal_transport_data = build_nodal_transport_data( nodal_transport_data = build_nodal_transport_data(
snakemake.input.transport_data, pop_layout snakemake.input.transport_data, pop_layout

View File

@ -699,7 +699,7 @@ if __name__ == "__main__":
for planning_horizon in snakemake.config["scenario"]["planning_horizons"] for planning_horizon in snakemake.config["scenario"]["planning_horizons"]
} }
Nyears = 1 Nyears = len(pd.date_range(freq="h", **snakemake.config["snapshots"])) / 8760
costs_db = prepare_costs( costs_db = prepare_costs(
snakemake.input.costs, snakemake.input.costs,

View File

@ -23,7 +23,7 @@ from make_summary import assign_carriers
from plot_summary import preferred_order, rename_techs from plot_summary import preferred_order, rename_techs
from pypsa.plot import add_legend_circles, add_legend_lines, add_legend_patches from pypsa.plot import add_legend_circles, add_legend_lines, add_legend_patches
plt.style.use(["ggplot", "matplotlibrc"]) # plt.style.use(["ggplot", "matplotlibrc"])
def rename_techs_tyndp(tech): def rename_techs_tyndp(tech):
@ -394,8 +394,7 @@ def plot_h2_map(network, regions):
link_widths=link_widths_retro, link_widths=link_widths_retro,
branch_components=["Link"], branch_components=["Link"],
ax=ax, ax=ax,
color_geomap=False, **map_opts,
boundaries=map_opts["boundaries"],
) )
regions.plot( regions.plot(
@ -922,11 +921,11 @@ if __name__ == "__main__":
snakemake = mock_snakemake( snakemake = mock_snakemake(
"plot_network", "plot_network",
simpl="", simpl="",
clusters="181",
ll="vopt",
opts="", opts="",
sector_opts="Co2L0-730H-T-H-B-I-A-solar+p3-linemaxext10", clusters="5",
planning_horizons="2050", ll="v1.5",
sector_opts="CO2L0-1H-T-H-B-I-A-solar+p3-dist1",
planning_horizons="2030",
) )
logging.basicConfig(level=snakemake.config["logging"]["level"]) logging.basicConfig(level=snakemake.config["logging"]["level"])
@ -938,6 +937,9 @@ if __name__ == "__main__":
map_opts = snakemake.config["plotting"]["map"] map_opts = snakemake.config["plotting"]["map"]
if map_opts["boundaries"] is None:
map_opts["boundaries"] = regions.total_bounds[[0, 2, 1, 3]] + [-1, 1, -1, 1]
plot_map( plot_map(
n, n,
components=["generators", "links", "stores", "storage_units"], components=["generators", "links", "stores", "storage_units"],

View File

@ -676,7 +676,7 @@ def add_dac(n, costs):
) )
def add_co2limit(n, Nyears=1.0, limit=0.0): def add_co2limit(n, nyears=1.0, limit=0.0):
logger.info(f"Adding CO2 budget limit as per unit of 1990 levels of {limit}") logger.info(f"Adding CO2 budget limit as per unit of 1990 levels of {limit}")
countries = snakemake.config["countries"] countries = snakemake.config["countries"]
@ -688,7 +688,7 @@ def add_co2limit(n, Nyears=1.0, limit=0.0):
co2_limit = co2_totals.loc[countries, sectors].sum().sum() co2_limit = co2_totals.loc[countries, sectors].sum().sum()
co2_limit *= limit * Nyears co2_limit *= limit * nyears
n.add( n.add(
"GlobalConstraint", "GlobalConstraint",
@ -732,7 +732,7 @@ def cycling_shift(df, steps=1):
return df return df
def prepare_costs(cost_file, config, Nyears): def prepare_costs(cost_file, config, nyears):
# set all asset costs and other parameters # set all asset costs and other parameters
costs = pd.read_csv(cost_file, index_col=[0, 1]).sort_index() costs = pd.read_csv(cost_file, index_col=[0, 1]).sort_index()
@ -750,7 +750,7 @@ def prepare_costs(cost_file, config, Nyears):
return annuity(v["lifetime"], v["discount rate"]) + v["FOM"] / 100 return annuity(v["lifetime"], v["discount rate"]) + v["FOM"] / 100
costs["fixed"] = [ costs["fixed"] = [
annuity_factor(v) * v["investment"] * Nyears for i, v in costs.iterrows() annuity_factor(v) * v["investment"] * nyears for i, v in costs.iterrows()
] ]
return costs return costs
@ -1557,8 +1557,7 @@ def add_land_transport(n, costs):
co2 = ( co2 = (
ice_share ice_share
/ ice_efficiency / ice_efficiency
* transport[nodes].sum().sum() * transport[nodes].sum(axis=1)
/ 8760
* costs.at["oil", "CO2 intensity"] * costs.at["oil", "CO2 intensity"]
) )
@ -2333,11 +2332,13 @@ def add_industry(n, costs):
logger.info("Add industrial demand") logger.info("Add industrial demand")
nodes = pop_layout.index nodes = pop_layout.index
nsnapshots = n.snapshot_weightings.generators.sum()
nyears = nsnapshots / 8760
# 1e6 to convert TWh to MWh # 1e6 to convert TWh to MWh
industrial_demand = ( industrial_demand = (
pd.read_csv(snakemake.input.industrial_demand, index_col=0) * 1e6 pd.read_csv(snakemake.input.industrial_demand, index_col=0) * 1e6
) ) * nyears
n.madd( n.madd(
"Bus", "Bus",
@ -2352,10 +2353,10 @@ def add_industry(n, costs):
industrial_demand.loc[spatial.biomass.locations, "solid biomass"].rename( industrial_demand.loc[spatial.biomass.locations, "solid biomass"].rename(
index=lambda x: x + " solid biomass for industry" index=lambda x: x + " solid biomass for industry"
) )
/ 8760 / nsnapshots
) )
else: else:
p_set = industrial_demand["solid biomass"].sum() / 8760 p_set = industrial_demand["solid biomass"].sum() / nsnapshots
n.madd( n.madd(
"Load", "Load",
@ -2402,7 +2403,7 @@ def add_industry(n, costs):
unit="MWh_LHV", unit="MWh_LHV",
) )
gas_demand = industrial_demand.loc[nodes, "methane"] / 8760.0 gas_demand = industrial_demand.loc[nodes, "methane"] / nsnapshots
if options["gas_network"]: if options["gas_network"]:
spatial_gas_demand = gas_demand.rename(index=lambda x: x + " gas for industry") spatial_gas_demand = gas_demand.rename(index=lambda x: x + " gas for industry")
@ -2454,7 +2455,7 @@ def add_industry(n, costs):
suffix=" H2 for industry", suffix=" H2 for industry",
bus=nodes + " H2", bus=nodes + " H2",
carrier="H2 for industry", carrier="H2 for industry",
p_set=industrial_demand.loc[nodes, "hydrogen"] / 8760, p_set=industrial_demand.loc[nodes, "hydrogen"] / nsnapshots,
) )
shipping_hydrogen_share = get(options["shipping_hydrogen_share"], investment_year) shipping_hydrogen_share = get(options["shipping_hydrogen_share"], investment_year)
@ -2470,11 +2471,11 @@ def add_industry(n, costs):
domestic_navigation = pop_weighted_energy_totals.loc[ domestic_navigation = pop_weighted_energy_totals.loc[
nodes, "total domestic navigation" nodes, "total domestic navigation"
].squeeze() ].squeeze()
international_navigation = pd.read_csv( international_navigation = (
snakemake.input.shipping_demand, index_col=0 pd.read_csv(snakemake.input.shipping_demand, index_col=0).squeeze() * nyears
).squeeze() )
all_navigation = domestic_navigation + international_navigation all_navigation = domestic_navigation + international_navigation
p_set = all_navigation * 1e6 / 8760 p_set = all_navigation * 1e6 / nsnapshots
if shipping_hydrogen_share: if shipping_hydrogen_share:
oil_efficiency = options.get( oil_efficiency = options.get(
@ -2681,7 +2682,7 @@ def add_industry(n, costs):
) )
demand_factor = options.get("HVC_demand_factor", 1) demand_factor = options.get("HVC_demand_factor", 1)
p_set = demand_factor * industrial_demand.loc[nodes, "naphtha"].sum() / 8760 p_set = demand_factor * industrial_demand.loc[nodes, "naphtha"].sum() / nsnapshots
if demand_factor != 1: if demand_factor != 1:
logger.warning(f"Changing HVC demand by {demand_factor*100-100:+.2f}%.") logger.warning(f"Changing HVC demand by {demand_factor*100-100:+.2f}%.")
@ -2699,7 +2700,7 @@ def add_industry(n, costs):
demand_factor demand_factor
* pop_weighted_energy_totals.loc[nodes, all_aviation].sum(axis=1).sum() * pop_weighted_energy_totals.loc[nodes, all_aviation].sum(axis=1).sum()
* 1e6 * 1e6
/ 8760 / nsnapshots
) )
if demand_factor != 1: if demand_factor != 1:
logger.warning(f"Changing aviation demand by {demand_factor*100-100:+.2f}%.") logger.warning(f"Changing aviation demand by {demand_factor*100-100:+.2f}%.")
@ -2718,7 +2719,8 @@ def add_industry(n, costs):
co2_release = ["naphtha for industry", "kerosene for aviation"] co2_release = ["naphtha for industry", "kerosene for aviation"]
co2 = ( co2 = (
n.loads.loc[co2_release, "p_set"].sum() * costs.at["oil", "CO2 intensity"] n.loads.loc[co2_release, "p_set"].sum() * costs.at["oil", "CO2 intensity"]
- industrial_demand.loc[nodes, "process emission from feedstock"].sum() / 8760 - industrial_demand.loc[nodes, "process emission from feedstock"].sum()
/ nsnapshots
) )
n.add( n.add(
@ -2741,12 +2743,13 @@ def add_industry(n, costs):
for node in nodes for node in nodes
], ],
carrier="low-temperature heat for industry", carrier="low-temperature heat for industry",
p_set=industrial_demand.loc[nodes, "low-temperature heat"] / 8760, p_set=industrial_demand.loc[nodes, "low-temperature heat"] / nsnapshots,
) )
# remove today's industrial electricity demand by scaling down total electricity demand # remove today's industrial electricity demand by scaling down total electricity demand
for ct in n.buses.country.dropna().unique(): for ct in n.buses.country.dropna().unique():
# TODO map onto n.bus.country # TODO map onto n.bus.country
loads_i = n.loads.index[ loads_i = n.loads.index[
(n.loads.index.str[:2] == ct) & (n.loads.carrier == "electricity") (n.loads.index.str[:2] == ct) & (n.loads.carrier == "electricity")
] ]
@ -2765,7 +2768,7 @@ def add_industry(n, costs):
suffix=" industry electricity", suffix=" industry electricity",
bus=nodes, bus=nodes,
carrier="industry electricity", carrier="industry electricity",
p_set=industrial_demand.loc[nodes, "electricity"] / 8760, p_set=industrial_demand.loc[nodes, "electricity"] / nsnapshots,
) )
n.madd( n.madd(
@ -2782,10 +2785,10 @@ def add_industry(n, costs):
-industrial_demand.loc[nodes, sel] -industrial_demand.loc[nodes, sel]
.sum(axis=1) .sum(axis=1)
.rename(index=lambda x: x + " process emissions") .rename(index=lambda x: x + " process emissions")
/ 8760 / nsnapshots
) )
else: else:
p_set = -industrial_demand.loc[nodes, sel].sum(axis=1).sum() / 8760 p_set = -industrial_demand.loc[nodes, sel].sum(axis=1).sum() / nsnapshots
# this should be process emissions fossil+feedstock # 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 # then need load on atmosphere for feedstock emissions that are currently going to atmosphere via Link Fischer-Tropsch demand
@ -2829,10 +2832,10 @@ def add_industry(n, costs):
industrial_demand.loc[spatial.ammonia.locations, "ammonia"].rename( industrial_demand.loc[spatial.ammonia.locations, "ammonia"].rename(
index=lambda x: x + " NH3" index=lambda x: x + " NH3"
) )
/ 8760 / nsnapshots
) )
else: else:
p_set = industrial_demand["ammonia"].sum() / 8760 p_set = industrial_demand["ammonia"].sum() / nsnapshots
n.madd( n.madd(
"Load", "Load",
@ -2884,6 +2887,7 @@ def add_agriculture(n, costs):
logger.info("Add agriculture, forestry and fishing sector.") logger.info("Add agriculture, forestry and fishing sector.")
nodes = pop_layout.index nodes = pop_layout.index
nsnapshots = n.snapshot_weightings.generators.sum()
# electricity # electricity
@ -2895,7 +2899,7 @@ def add_agriculture(n, costs):
carrier="agriculture electricity", carrier="agriculture electricity",
p_set=pop_weighted_energy_totals.loc[nodes, "total agriculture electricity"] p_set=pop_weighted_energy_totals.loc[nodes, "total agriculture electricity"]
* 1e6 * 1e6
/ 8760, / nsnapshots,
) )
# heat # heat
@ -2908,7 +2912,7 @@ def add_agriculture(n, costs):
carrier="agriculture heat", carrier="agriculture heat",
p_set=pop_weighted_energy_totals.loc[nodes, "total agriculture heat"] p_set=pop_weighted_energy_totals.loc[nodes, "total agriculture heat"]
* 1e6 * 1e6
/ 8760, / nsnapshots,
) )
# machinery # machinery
@ -2944,7 +2948,7 @@ def add_agriculture(n, costs):
/ efficiency_gain / efficiency_gain
* machinery_nodal_energy * machinery_nodal_energy
* 1e6 * 1e6
/ 8760, / nsnapshots,
) )
if oil_share > 0: if oil_share > 0:
@ -2953,14 +2957,14 @@ def add_agriculture(n, costs):
["agriculture machinery oil"], ["agriculture machinery oil"],
bus=spatial.oil.nodes, bus=spatial.oil.nodes,
carrier="agriculture machinery oil", carrier="agriculture machinery oil",
p_set=oil_share * machinery_nodal_energy.sum() * 1e6 / 8760, p_set=oil_share * machinery_nodal_energy.sum() * 1e6 / nsnapshots,
) )
co2 = ( co2 = (
oil_share oil_share
* machinery_nodal_energy.sum() * machinery_nodal_energy.sum()
* 1e6 * 1e6
/ 8760 / nsnapshots
* costs.at["oil", "CO2 intensity"] * costs.at["oil", "CO2 intensity"]
) )
@ -3229,19 +3233,19 @@ def set_temporal_aggregation(n, opts, solver_name):
return n return n
# %%
if __name__ == "__main__": if __name__ == "__main__":
if "snakemake" not in globals(): if "snakemake" not in globals():
from _helpers import mock_snakemake from _helpers import mock_snakemake
snakemake = mock_snakemake( snakemake = mock_snakemake(
"prepare_sector_network", "prepare_sector_network",
configfiles="test/config.overnight.yaml",
simpl="", simpl="",
opts="", opts="",
clusters="37", clusters="5",
ll="v1.5", ll="v1.5",
sector_opts="cb40ex0-365H-T-H-B-I-A-solar+p3-dist1", sector_opts="CO2L0-24H-T-H-B-I-A-solar+p3-dist1",
planning_horizons="2020", planning_horizons="2030",
) )
logging.basicConfig(level=snakemake.config["logging"]["level"]) logging.basicConfig(level=snakemake.config["logging"]["level"])
@ -3258,16 +3262,17 @@ if __name__ == "__main__":
n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides) n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides)
pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout, index_col=0) pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout, index_col=0)
Nyears = n.snapshot_weightings.generators.sum() / 8760 nsnapshots = n.snapshot_weightings.objective.sum()
nyears = nsnapshots / 8760
costs = prepare_costs( costs = prepare_costs(
snakemake.input.costs, snakemake.input.costs,
snakemake.config["costs"], snakemake.config["costs"],
Nyears, nyears,
) )
pop_weighted_energy_totals = pd.read_csv( pop_weighted_energy_totals = (
snakemake.input.pop_weighted_energy_totals, index_col=0 pd.read_csv(snakemake.input.pop_weighted_energy_totals, index_col=0) * nyears
) )
patch_electricity_network(n) patch_electricity_network(n)
@ -3369,7 +3374,7 @@ if __name__ == "__main__":
limit = float(limit.replace("p", ".").replace("m", "-")) limit = float(limit.replace("p", ".").replace("m", "-"))
break break
logger.info(f"Add CO2 limit from {limit_type}") logger.info(f"Add CO2 limit from {limit_type}")
add_co2limit(n, Nyears, limit) add_co2limit(n, nyears, limit)
for o in opts: for o in opts:
if not o[:10] == "linemaxext": if not o[:10] == "linemaxext":

View File

@ -31,7 +31,7 @@ def add_land_use_constraint(n, config):
_add_land_use_constraint(n, config) _add_land_use_constraint(n, config)
def _add_land_use_constraint(n): def _add_land_use_constraint(n, config):
# warning: this will miss existing offwind which is not classed AC-DC and has carrier 'offwind' # warning: this will miss existing offwind which is not classed AC-DC and has carrier 'offwind'
for carrier in ["solar", "onwind", "offwind-ac", "offwind-dc"]: for carrier in ["solar", "onwind", "offwind-ac", "offwind-dc"]:

View File

@ -58,3 +58,15 @@ solving:
name: glpk name: glpk
options: glpk-default options: glpk-default
mem: 4000 mem: 4000
plotting:
map:
boundaries:
eu_node_location:
x: -5.5
y: 46.
costs_max: 1000
costs_threshold: 0.0000001
energy_max:
energy_min:
energy_threshold: 0.000001

View File

@ -16,7 +16,7 @@ scenario:
clusters: clusters:
- 5 - 5
sector_opts: sector_opts:
- CO2L0-24H-T-H-B-I-A-solar+p3-dist1 - CO2L0-1H-T-H-B-I-A-solar+p3-dist1
planning_horizons: planning_horizons:
- 2030 - 2030
@ -59,3 +59,15 @@ solving:
name: glpk name: glpk
options: glpk-default options: glpk-default
mem: 4000 mem: 4000
plotting:
map:
boundaries:
eu_node_location:
x: -5.5
y: 46.
costs_max: 1000
costs_threshold: 0.0000001
energy_max:
energy_min:
energy_threshold: 0.000001

View File

@ -65,3 +65,16 @@ solving:
solver: solver:
name: glpk name: glpk
options: "glpk-default" options: "glpk-default"
plotting:
map:
boundaries:
eu_node_location:
x: -5.5
y: 46.
costs_max: 1000
costs_threshold: 0.0000001
energy_max:
energy_min:
energy_threshold: 0.000001