restructure to links without temperature correction

This commit is contained in:
lisazeyen 2024-03-01 15:19:00 +01:00
parent b70aa59dce
commit 548a99f273

View File

@ -24,6 +24,7 @@ from _helpers import (
) )
from add_electricity import calculate_annuity, sanitize_carriers, sanitize_locations from add_electricity import calculate_annuity, sanitize_carriers, sanitize_locations
from build_energy_totals import build_co2_totals, build_eea_co2, build_eurostat_co2 from build_energy_totals import build_co2_totals, build_eea_co2, build_eurostat_co2
from build_transport_demand import transport_degree_factor
from networkx.algorithms import complement from networkx.algorithms import complement
from networkx.algorithms.connectivity.edge_augmentation import k_edge_augmentation from networkx.algorithms.connectivity.edge_augmentation import k_edge_augmentation
from prepare_network import maybe_adjust_costs_and_potentials from prepare_network import maybe_adjust_costs_and_potentials
@ -1502,41 +1503,18 @@ def add_storage_and_grids(n, costs):
) )
def add_land_transport(n, costs): def check_land_transport_shares(shares):
# TODO options? # Sums up the shares, ignoring None values
total_share = sum(filter(None, shares))
logger.info("Add land transport")
transport = pd.read_csv(
snakemake.input.transport_demand, index_col=0, parse_dates=True
)
number_cars = pd.read_csv(snakemake.input.transport_data, index_col=0)[
"number cars"
]
avail_profile = pd.read_csv(
snakemake.input.avail_profile, index_col=0, parse_dates=True
)
dsm_profile = pd.read_csv(
snakemake.input.dsm_profile, index_col=0, parse_dates=True
)
fuel_cell_share = get(options["land_transport_fuel_cell_share"], investment_year)
electric_share = get(options["land_transport_electric_share"], investment_year)
ice_share = get(options["land_transport_ice_share"], investment_year)
total_share = fuel_cell_share + electric_share + ice_share
if total_share != 1: if total_share != 1:
logger.warning( logger.warning(
f"Total land transport shares sum up to {total_share:.2%}, corresponding to increased or decreased demand assumptions." f"Total land transport shares sum up to {total_share:.2%},"
"corresponding to increased or decreased demand assumptions."
) )
logger.info(f"FCEV share: {fuel_cell_share*100}%") def add_EVs(n, nodes, avail_profile, dsm_profile, p_set, electric_share,
logger.info(f"EV share: {electric_share*100}%") number_cars, temperature):
logger.info(f"ICEV share: {ice_share*100}%")
nodes = pop_layout.index
if electric_share > 0:
n.add("Carrier", "Li ion") n.add("Carrier", "Li ion")
n.madd( n.madd(
@ -1548,25 +1526,33 @@ def add_land_transport(n, costs):
unit="MWh_el", unit="MWh_el",
) )
p_set = ( # temperature correction for EVs
electric_share dd_EV = transport_degree_factor(
* ( temperature,
transport[nodes] options["transport_heating_deadband_lower"],
+ cycling_shift(transport[nodes], 1) options["transport_heating_deadband_upper"],
+ cycling_shift(transport[nodes], 2) options["EV_lower_degree_factor"],
) options["EV_upper_degree_factor"],
/ 3
) )
profile = p_set/p_set.max()
efficiency = costs.at['Battery electric (passenger cars)', 'efficiency']
n.madd( n.madd(
"Load", "Link",
nodes, nodes,
suffix=" land transport EV", suffix=" land transport EV",
bus=nodes + " EV battery", bus0=nodes + " EV battery",
bus1=nodes + " land transport",
carrier="land transport EV", carrier="land transport EV",
p_set=p_set, efficiency=efficiency,
p_min_pu=profile,
p_max_pu=profile,
p_nom=electric_share*p_set.max()/efficiency,
p_nom_extendable=False,
) )
p_nom = number_cars * options.get("bev_charge_rate", 0.011) * electric_share p_nom = number_cars * options.get("bev_charge_rate", 0.011) * electric_share
n.madd( n.madd(
@ -1579,13 +1565,9 @@ def add_land_transport(n, costs):
carrier="BEV charger", carrier="BEV charger",
p_max_pu=avail_profile[nodes], p_max_pu=avail_profile[nodes],
efficiency=options.get("bev_charge_efficiency", 0.9), efficiency=options.get("bev_charge_efficiency", 0.9),
# These were set non-zero to find LU infeasibility when availability = 0.25
# p_nom_extendable=True,
# p_nom_min=p_nom,
# capital_cost=1e6, #i.e. so high it only gets built where necessary
) )
if electric_share > 0 and options["v2g"]: if options["v2g"]:
n.madd( n.madd(
"Link", "Link",
nodes, nodes,
@ -1598,7 +1580,7 @@ def add_land_transport(n, costs):
efficiency=options.get("bev_charge_efficiency", 0.9), efficiency=options.get("bev_charge_efficiency", 0.9),
) )
if electric_share > 0 and options["bev_dsm"]: if options["bev_dsm"]:
e_nom = ( e_nom = (
number_cars number_cars
* options.get("bev_energy", 0.05) * options.get("bev_energy", 0.05)
@ -1618,61 +1600,120 @@ def add_land_transport(n, costs):
e_min_pu=dsm_profile[nodes], e_min_pu=dsm_profile[nodes],
) )
if fuel_cell_share > 0: def add_fuel_cell_cars(n, nodes, p_set, fuel_cell_share, temperature):
efficiency = options["transport_fuel_cell_efficiency"]
profile = p_set / p_set.max()
p_nom = fuel_cell_share * p_set / efficiency
n.madd( n.madd(
"Load", "Link",
nodes, nodes,
suffix=" land transport fuel cell", suffix=" land transport fuel cell",
bus=nodes + " H2", bus0=spatial.h2.nodes,
bus1=nodes + " land transport",
carrier="land transport fuel cell", carrier="land transport fuel cell",
p_set=fuel_cell_share efficiency=efficiency,
/ options["transport_fuel_cell_efficiency"] p_nom_extendable=False,
* transport[nodes], p_nom=p_nom,
p_min_pu=profile,
p_max_pu=profile,
) )
if ice_share > 0:
def add_ice_cars(n, nodes, p_set, ice_share, temperature):
add_carrier_buses(n, "oil") add_carrier_buses(n, "oil")
ice_efficiency = options["transport_internal_combustion_efficiency"] ice_efficiency = options["transport_internal_combustion_efficiency"]
p_set_land_transport_oil = (
ice_share
/ ice_efficiency
* transport[nodes].rename(columns=lambda x: x + " land transport oil")
)
if not options["regional_oil_demand"]: if not options["regional_oil_demand"]:
p_set_land_transport_oil = p_set_land_transport_oil.sum(axis=1).to_frame( p_nom = ice_share * p_set.sum(axis=1).max() / ice_efficiency
name="EU land transport oil" else:
) p_nom = ice_share * p_set.max() / ice_efficiency
n.madd(
"Bus",
spatial.oil.land_transport,
location=spatial.oil.demand_locations,
carrier="land transport oil",
unit="land transport",
)
n.madd(
"Load",
spatial.oil.land_transport,
bus=spatial.oil.land_transport,
carrier="land transport oil",
p_set=p_set_land_transport_oil,
)
n.madd( n.madd(
"Link", "Link",
spatial.oil.land_transport, nodes + " land transport ICE",
bus0=spatial.oil.nodes, bus0=spatial.oil.nodes,
bus1=spatial.oil.land_transport, bus1=nodes + " land transport",
bus2="co2 atmosphere", bus2=["co2 atmosphere"],
carrier="land transport oil", carrier="land transport oil",
efficiency=ice_efficiency,
efficiency2=costs.at["oil", "CO2 intensity"], efficiency2=costs.at["oil", "CO2 intensity"],
p_nom_extendable=True, p_nom_extendable=True,
# p_nom=p_nom,
capital_cost=1e4,
) )
def add_land_transport(n, costs):
# TODO options?
logger.info("Add land transport")
# read in transport demand in units kinetic energy
transport = pd.read_csv(
snakemake.input.transport_demand, index_col=0, parse_dates=True
)
number_cars = pd.read_csv(snakemake.input.transport_data, index_col=0)[
"number cars"
]
avail_profile = pd.read_csv(
snakemake.input.avail_profile, index_col=0, parse_dates=True
)
dsm_profile = pd.read_csv(
snakemake.input.dsm_profile, index_col=0, parse_dates=True
)
# exogenous share of passenger car type
engine_types = ["fuel_cell", "electric", "ice"]
shares = pd.Series()
for engine in engine_types:
shares[engine] = get(options[f"land_transport_{engine}_share"],
investment_year)
logger.info(f"{engine} share: {shares[engine]*100}%")
check_land_transport_shares(shares)
nodes = spatial.nodes
# Add load for transport demand
n.add("Carrier", "land transport demand")
n.madd("Bus",
nodes,
location=nodes,
suffix=" land transport",
carrier="land transport demand",
unit="MWh_kinetic")
p_set = transport[nodes]
# add demand
n.madd(
"Load",
nodes,
suffix=" land transport",
bus=nodes + " land transport",
carrier="land transport demand",
p_set=p_set,
)
# temperature for correction factor for heating/cooling
temperature = xr.open_dataarray(snakemake.input.temp_air_total).to_pandas()
if shares["electric"] > 0:
add_EVs(n, nodes, avail_profile, dsm_profile, p_set, shares["electric"],
number_cars, temperature)
if shares["fuel_cell"] > 0:
add_fuel_cell_cars(n, nodes, p_set, shares["fuel_cell"], temperature)
if shares["ice"] > 0:
add_ice_cars(n, nodes, p_set, shares["ice"], temperature)
def build_heat_demand(n): def build_heat_demand(n):
heat_demand_shape = ( heat_demand_shape = (
@ -3565,19 +3606,19 @@ def lossy_bidirectional_links(n, carrier, efficiencies={}):
-compression_per_1000km * n.links.loc[carrier_i, "length_original"] / 1e3 -compression_per_1000km * n.links.loc[carrier_i, "length_original"] / 1e3
) )
#%%
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", # configfiles="test/config.overnight.yaml",
simpl="", simpl="",
opts="", opts="",
clusters="37", clusters="37",
ll="v1.0", ll="v1.0",
sector_opts="CO2L0-24H-T-H-B-I-A-dist1", sector_opts="730H-T-H-B-I-A-dist1",
planning_horizons="2030", planning_horizons="2030",
) )