add_electricity: revise code and make it leaner

This commit is contained in:
Fabian 2022-01-11 09:38:34 +01:00
parent 3905cd8823
commit 66f2d36f0d

View File

@ -190,12 +190,13 @@ def load_powerplants(ppl_fn):
.replace({'carrier': carrier_dict}))
def attach_load(n, regions, load, nuts3_shapes, cntries = [], scaling = 1.):
def attach_load(n, regions, load, nuts3_shapes, countries, scaling=1.):
substation_lv_i = n.buses.index[n.buses['substation_lv']]
regions = (gpd.read_file(regions).set_index('name')
.reindex(substation_lv_i))
opsd_load = (pd.read_csv(load, index_col=0, parse_dates=True)
.filter(items=cntries))
.filter(items=countries))
logger.info(f"Load data scaled with scalling factor {scaling}.")
opsd_load *= scaling
@ -229,6 +230,9 @@ def attach_load(n, regions, load, nuts3_shapes, cntries = [], scaling = 1.):
def update_transmission_costs(n, costs, length_factor=1.0, simple_hvdc_costs=False):
# TODO: line length factor of lines is applied to lines and links.
# Separate the function to distinguish.
n.lines['capital_cost'] = (n.lines['length'] * length_factor *
costs.at['HVAC overhead', 'capital_cost'])
@ -253,9 +257,9 @@ def update_transmission_costs(n, costs, length_factor=1.0, simple_hvdc_costs=Fal
n.links.loc[dc_b, 'capital_cost'] = costs
def attach_wind_and_solar(n, costs, input_profiles,
technologies = ['onwind', 'offwind-ac', 'offwind-dc', 'solar'],
line_length_factor = 1.):
def attach_wind_and_solar(n, costs, input_profiles, technologies, line_length_factor=1):
# TODO: rename tech -> carrier, technologies -> carriers
for tech in technologies:
if tech == 'hydro': continue
@ -292,8 +296,7 @@ def attach_wind_and_solar(n, costs, input_profiles,
p_max_pu=ds['profile'].transpose('time', 'bus').to_pandas())
def attach_conventional_generators(n, costs, ppl, carriers=['nuclear', 'oil', 'OCGT', 'CCGT',
'coal', 'lignite', 'geothermal', 'biomass']):
def attach_conventional_generators(n, costs, ppl, carriers):
_add_missing_carriers_from_costs(n, costs, carriers)
@ -314,9 +317,7 @@ def attach_conventional_generators(n, costs, ppl, carriers=['nuclear', 'oil', 'O
logger.warning(f'Capital costs for conventional generators put to 0 EUR/MW.')
def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities,
config_hydro = {'carriers': {'ror', 'PHS', 'hydro'}}):
carriers = config_hydro.get('carriers', ['ror', 'PHS', 'hydro'])
def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities, carriers, **config):
_add_missing_carriers_from_costs(n, costs, carriers)
@ -361,7 +362,8 @@ def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities,
if 'PHS' in carriers and not phs.empty:
# fill missing max hours to config value and
# assume no natural inflow due to lack of data
phs = phs.replace({'max_hours': {0: config_hydro['PHS_max_hours']}})
max_hours = config.get('PHS_max_hours', 6)
phs = phs.replace({'max_hours': {0: max_hours}})
n.madd('StorageUnit', phs.index,
carrier='PHS',
bus=phs['bus'],
@ -373,7 +375,10 @@ def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities,
cyclic_state_of_charge=True)
if 'hydro' in carriers and not hydro.empty:
hydro_max_hours = config_hydro.get('hydro_max_hours')
hydro_max_hours = config.get('hydro_max_hours')
assert hydro_max_hours is not None, "No path for hydro capacities given."
hydro_stats = pd.read_csv(hydro_capacities,
comment="#", na_values='-', index_col=0)
e_target = hydro_stats["E_store[TWh]"].clip(lower=0.2) * 1e6
@ -402,8 +407,7 @@ def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities,
bus=hydro['bus'],
p_nom=hydro['p_nom'],
max_hours=hydro_max_hours,
capital_cost=(costs.at['hydro', 'capital_cost']
if config_hydro.get('hydro_capital_cost') else 0.),
capital_cost=costs.at['hydro', 'capital_cost'],
marginal_cost=costs.at['hydro', 'marginal_cost'],
p_max_pu=1., # dispatch
p_min_pu=0., # store
@ -413,8 +417,7 @@ def attach_hydro(n, costs, ppl, profile_hydro, hydro_capacities,
inflow=inflow_t.loc[:, hydro.index])
def attach_extendable_generators(n, costs, ppl, elec_opts = {'extendable_carriers': {'Generator': []}}):
carriers = pd.Index(elec_opts['extendable_carriers']['Generator'])
def attach_extendable_generators(n, costs, ppl, carriers):
_add_missing_carriers_from_costs(n, costs, carriers)
@ -462,7 +465,7 @@ def attach_extendable_generators(n, costs, ppl, elec_opts = {'extendable_carrier
def attach_OPSD_renewables(n, techs=[]):
def attach_OPSD_renewables(n, techs):
available = ['DE', 'FR', 'PL', 'CH', 'DK', 'CZ', 'SE', 'GB']
tech_map = {'Onshore': 'onwind', 'Offshore': 'offwind', 'Solar': 'solar'}
@ -494,7 +497,7 @@ def attach_OPSD_renewables(n, techs=[]):
def estimate_renewable_capacities(n, tech_map={}):
def estimate_renewable_capacities(n, tech_map):
if len(tech_map) == 0: return
@ -526,16 +529,15 @@ def estimate_renewable_capacities(n, tech_map={}):
n.generators.loc[tech_i, 'p_nom_min'] = n.generators.loc[tech_i, 'p_nom']
def add_nice_carrier_names(n, config):
def add_nice_carrier_names(n, nice_names, tech_colors):
carrier_i = n.carriers.index
nice_names = (pd.Series(config['plotting']['nice_names'])
nice_names = (pd.Series(nice_names)
.reindex(carrier_i).fillna(carrier_i.to_series().str.title()))
n.carriers['nice_name'] = nice_names
colors = pd.Series(config['plotting']['tech_colors']).reindex(carrier_i)
colors = pd.Series(tech_colors).reindex(carrier_i)
if colors.isna().any():
missing_i = list(colors.index[colors.isna()])
logger.warning(f'tech_colors for carriers {missing_i} not defined '
'in config.')
logger.warning(f'tech_colors for carriers {missing_i} not defined.')
n.carriers['color'] = colors
@ -545,35 +547,42 @@ if __name__ == "__main__":
snakemake = mock_snakemake('add_electricity')
configure_logging(snakemake)
n = pypsa.Network(snakemake.input.base_network)
config = snakemake.config
paths = snakemake.input
n = pypsa.Network(paths.base_network)
Nyears = n.snapshot_weightings.objective.sum() / 8760.
costs = load_costs(tech_costs = snakemake.input.tech_costs, config = snakemake.config['costs'],
elec_config = snakemake.config['electricity'], Nyears = Nyears)
ppl = load_powerplants(snakemake.input.powerplants)
costs = load_costs(paths.tech_costs, config['costs'], config['electricity'], Nyears=Nyears)
ppl = load_powerplants(paths.powerplants)
attach_load(n, regions = snakemake.input.regions, load = snakemake.input.load,
nuts3_shapes = snakemake.input.nuts3_shapes,
cntries = snakemake.config['countries'],
scaling = snakemake.config.get('load', {}).get('scaling_factor', 1.0))
attach_load(n, paths.regions, paths.load, paths.nuts3_shapes, config['countries'],
scaling=config['load']['scaling_factor'])
update_transmission_costs(n, costs)
update_transmission_costs(n, costs, config['lines']['length_factor'])
attach_conventional_generators(n, costs, ppl, carriers = snakemake.config['electricity']['conventional_carriers'])
attach_wind_and_solar(n, costs, snakemake.input, technologies = snakemake.config['renewable'],
line_length_factor = snakemake.config['lines']['length_factor'])
carriers = config['electricity']['conventional_carriers']
attach_conventional_generators(n, costs, ppl, carriers)
if 'hydro' in snakemake.config['renewable']:
attach_hydro(n, costs, ppl, snakemake.input.profile_hydro, snakemake.input.hydro_capacities,
config_hydro = snakemake.config['renewable']['hydro'])
carriers = config['renewable']
attach_wind_and_solar(n, costs, paths, carriers, config['lines']['length_factor'])
attach_extendable_generators(n, costs, ppl, elec_opts = snakemake.config['electricity'])
if 'hydro' in config['renewable']:
carriers = config['renewable']['hydro'].pop('carriers', [])
attach_hydro(n, costs, ppl, paths.profile_hydro, paths.hydro_capacities,
carriers, **config['renewable']['hydro'])
carriers = config['electricity']['extendable_carriers']['Generator']
attach_extendable_generators(n, costs, ppl, carriers)
tech_map = config['electricity'].get('estimate_renewable_capacities_from_capacity_stats', {})
estimate_renewable_capacities(n, tech_map)
techs = config['electricity'].get('renewable_capacities_from_OPSD', [])
attach_OPSD_renewables(n, techs)
estimate_renewable_capacities(n, tech_map = (snakemake.config['electricity']
.get('estimate_renewable_capacities_from_capacity_stats', {})))
attach_OPSD_renewables(n, techs = snakemake.config['electricity'].get('renewable_capacities_from_OPSD', []))
update_p_nom_max(n)
add_nice_carrier_names(n, config = snakemake.config)
plot_config = config['plotting']
add_nice_carrier_names(n, plot_config['nice_names'], plot_config['tech_colors'])
n.export_to_netcdf(snakemake.output[0])