From f69ec97a18eb29bd815145e87edea92f828fff21 Mon Sep 17 00:00:00 2001 From: Parisra <102019906+Parisra@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:45:31 +0200 Subject: [PATCH] Add solar_potential constraint The constraint ensures the combined installed capacity of solar and solar-hsat does not exceed the total solar capacity of the node --- scripts/solve_network.py | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/scripts/solve_network.py b/scripts/solve_network.py index dbde8813..47a9c1e7 100644 --- a/scripts/solve_network.py +++ b/scripts/solve_network.py @@ -43,6 +43,7 @@ from _helpers import ( set_scenario_config, update_config_from_wildcards, ) +from functools import reduce from pypsa.descriptors import get_activity_mask from pypsa.descriptors import get_switchable_as_dense as get_as_dense @@ -198,6 +199,61 @@ def _add_land_use_constraint_m(n, planning_horizons, config): n.generators.p_nom_max.clip(lower=0, inplace=True) +def add_solar_potential_constraints(n, config): + """ + Add constraint to make sure the sum capacity of all solar technologies (fixed, tracking, ets. ) is below the region potential. + Example: + ES1 0: total solar potential is 10 GW, meaning: + solar potential : 10 GW + solar-hsat potential : 8 GW (solar with single axis tracking is assumed to have higher land use) + The constraint ensures that: + solar_p_nom + solar_hsat_p_nom * 1.13 <= 10 GW + """ + land_use_factors= { + 'solar-hsat' : config['renewable']['solar']['capacity_per_sqkm']/config['renewable']['solar-hsat']['capacity_per_sqkm'] , + } + gen_index = n.generators[n.generators.p_nom_extendable].index + + filters = [("solar", True), ("thermal", False), ("rooftop", False)] ## filter all utility solar generation except solar thermal + solar = reduce(lambda gen_index, f: gen_index[gen_index.str.contains(f[0]) == f[1]], filters, gen_index) + solar_today = n.generators[(n.generators.carrier=='solar') & (n.generators.p_nom_extendable)].index + solar_hsat = n.generators[(n.generators.carrier=='solar-hsat') ].index + land_use = pd.DataFrame(1, index=solar, columns=['land_use_factor']) + for key in land_use_factors.keys(): + land_use = land_use.apply(lambda x: (x*land_use_factors[key]) if key in x.name else x, axis=1) + + rename = {"Generator-ext": "Generator"} + if "m" in snakemake.wildcards.clusters: + location = ( + pd.Series([' '.join(i.split(' ')[:2]) for i in n.generators.index], index=n.generators.index) + ) + ggrouper= pd.Series(n.generators.loc[solar].index.rename('bus').map(location), index=n.generators.loc[solar].index,).to_xarray() + rhs = (n.generators.loc[solar_today,"p_nom_max"] + .groupby(n.generators.loc[solar_today].index.rename('bus').map(location)).sum() - + n.generators.loc[solar_hsat,"p_nom_opt"] + .groupby(n.generators.loc[solar_hsat].index.rename('bus').map(location)).sum() * land_use_factors['solar-hsat'] ).clip(lower=0) + + else : + location = ( + n.buses.location + if "location" in n.buses.columns + else pd.Series(n.buses.index, index=n.buses.index) + ) + ggrouper= (n.generators.loc[solar].bus) + rhs = (n.generators.loc[solar_today,"p_nom_max"] + .groupby(n.generators.loc[solar_today].bus.map(location)).sum() - + n.generators.loc[solar_hsat,"p_nom_opt"] + .groupby(n.generators.loc[solar_hsat].bus.map(location)).sum() * land_use_factors['solar-hsat'] ).clip(lower=0) + + lhs = ( + (n.model["Generator-p_nom"].rename(rename).loc[solar] + *land_use.squeeze().values) + .groupby(ggrouper) + .sum() + ) + + print('adding solar rooftop constraints...') + n.model.add_constraints(lhs <= rhs, name="solar_potential") def add_co2_sequestration_limit(n, limit=200): """