Skip to content

Rule solve_network

Workflow Diagram

See the complete workflow in the repository.

digraph snakemake_dag { graph [bgcolor=white, margin=0, size="3,3" ]; node [fontname=sans, fontsize=10, penwidth=2, shape=box, style=rounded ]; edge [color=grey, penwidth=2 ]; 0 [color="0.64 0.6 0.85", fillcolor=gray, label=solve_network, style=filled]; 1 [color="0.33 0.6 0.85", label=prepare_network]; 1 -> 0; }

Script Documentation

Solves linear optimal power flow for a network iteratively while updating reactances.

Relevant Settings

.. code:: yaml

solving:
    tmpdir:
    options:
        formulation:
        clip_p_max_pu:
        load_shedding:
        noisy_costs:
        nhours:
        min_iterations:
        max_iterations:
        skip_iterations:
        track_iterations:
    solver:
        name:

.. seealso:: Documentation of the configuration file config.yaml at :ref:electricity_cf, :ref:solving_cf, :ref:plotting_cf

Inputs

  • networks/elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc: confer :ref:prepare

Outputs

  • results/networks/elec_s{simpl}_{clusters}_ec_l{ll}_{opts}.nc: Solved PyPSA network including optimisation results

    .. image:: /img/results.png :width: 40 %

Description

Total annual system costs are minimised with PyPSA. The full formulation of the linear optimal power flow (plus investment planning) is provided in the documentation of PyPSA <https://pypsa.readthedocs.io/en/latest/optimal_power_flow.html#linear-optimal-power-flow>_. The optimization is based on the :func:network.optimize function. Additionally, some extra constraints specified in :mod:prepare_network and :mod:solve_network are added.

Solving the network in multiple iterations is motivated through the dependence of transmission line capacities and impedances on values of corresponding flows. As lines are expanded their electrical parameters change, which renders the optimisation bilinear even if the power flow equations are linearized. To retain the computational advantage of continuous linear programming, a sequential linear programming technique is used, where in between iterations the line impedances are updated. Details (and errors introduced through this heuristic) are discussed in the paper

  • Fabian Neumann and Tom Brown. Heuristics for Transmission Expansion Planning in Low-Carbon Energy System Models <https://arxiv.org/abs/1907.10548>), 16th International Conference on the European Energy Market, 2019. arXiv:1907.10548 <https://arxiv.org/abs/1907.10548>.

.. warning:: Capital costs of existing network components are not included in the objective function, since for the optimisation problem they are just a constant term (no influence on optimal result).

Therefore, these capital costs are not included in ``network.objective``!

If you want to calculate the full total annual system costs add these to the objective value.

.. tip:: The rule :mod:solve_all_networks runs for all scenario s in the configuration file the rule :mod:solve_network.

get_load_shedding_capacity(n, safety_margin=1.2)

Calculate required load shedding p_nom per bus based on the maximum aggregated load observed in any snapshot.

Parameters

n : pypsa.Network The PyPSA network safety_margin : float, default 1.2 Safety factor to apply to the maximum load

Returns

pd.Series Required p_nom per bus for load shedding.

add_CCL_constraints(n, config)

Add CCL (country & carrier limit) constraint to the network.

Add minimum and maximum levels of generator nominal capacity per carrier for individual countries. Opts and path for agg_p_nom_minmax.csv must be defined in config.yaml. Default file is available at data/agg_p_nom_minmax.csv. Parameter include_existing in config.yaml decides whether existing capacities are considered in the CCL constraints. Default is false.

Parameters

n : pypsa.Network config : dict

Example

scenario: opts: [CCL-Co2L-24H] electricity: agg_p_nom_limits: file: data/agg_p_nom_minmax.csv include_existing: false

add_EQ_constraints(n, o, scaling=0.1)

Add equity constraints to the network.

Currently this is only implemented for the electricity sector only.

Opts must be specified in the config.yaml.

Parameters

n : pypsa.Network o : str

Example

scenario: opts: [Co2L-EQ0.7-24h]

Require each country or node to on average produce a minimal share of its total electricity consumption itself. Example: EQ0.7c demands each country to produce on average at least 70% of its consumption; EQ0.7 demands each node to produce on average at least 70% of its consumption.

add_BAU_constraints(n, config)

Add a per-carrier minimal overall capacity.

BAU_mincapacities and opts must be adjusted in the config.yaml.

Parameters

n : pypsa.Network config : dict

Example

scenario: opts: [Co2L-BAU-24h] electricity: BAU_mincapacities: solar: 0 onwind: 0 OCGT: 100000 offwind-ac: 0 offwind-dc: 0 Which sets minimum expansion across all nodes e.g. in Europe to 100GW. OCGT bus 1 + OCGT bus 2 + ... > 100000

add_SAFE_constraints(n, config)

Add a capacity reserve margin of a certain fraction above the peak demand. Renewable generators and storage do not contribute. Ignores network.

Parameters

n : pypsa.Network
config : dict

Example

config.yaml requires to specify opts:

scenario: opts: [Co2L-SAFE-24h] electricity: SAFE_reservemargin: 0.1 Which sets a reserve margin of 10% above the peak demand.

add_operational_reserve_margin_constraint(n, sns, config)

Build reserve margin constraints based on the formulation as suggested in GenX https://energy.mit.edu/wp-content/uploads/2017/10/Enhanced-Decision-Support-for-a-Changing-Electricity-Landscape.pdf It implies that the reserve margin also accounts for optimal dispatch of distributed energy resources (DERs) and demand response which is a novel feature of GenX.

add_operational_reserve_margin(n, sns, config)

Parameters

n : pypsa.Network
sns: pd.DatetimeIndex
config : dict

Example:

config.yaml requires to specify operational_reserve: operational_reserve: # like https://genxproject.github.io/GenX/dev/core/#Reserves activate: true epsilon_load: 0.02 # percentage of load at each snapshot epsilon_vres: 0.02 # percentage of VRES at each snapshot contingency: 400000 # MW

add_battery_constraints(n)

Add constraint ensuring that charger = discharger, i.e. 1 * charger_size - efficiency * discharger_size = 0

add_RES_constraints(n, res_share, config)

The constraint ensures that a predefined share of power is generated by renewable sources

Parameters

n : pypsa.Network
res_share: float
config : dict

hydrogen_temporal_constraint(n, n_ref, time_period)

Applies temporal constraints for hydrogen production based on renewable energy sources (RES) and electrolysis within a specified time period. The function ensures that the hydrogen production adheres to policy configurations such as temporal matching and allowed excess. Parameters:


n : pypsa.Network The PyPSA network object containing the current state of the energy system model. n_ref : pypsa.Network A reference PyPSA network object used for additionality constraints (if enabled). time_period : str The time period for grouping constraints. Can be one of "hour", "month", or "year". Returns:


None Adds constraints directly to the PyPSA network model. Raises:


KeyError If required configuration keys are missing in snakemake.config. ValueError If an unsupported time_period is provided.

Ensures that the two links simulating a bidirectional_link are extended the same amount.

extra_functionality(n, snapshots)

Collects supplementary constraints which will be passed to pypsa.linopf.network_lopf.

If you want to enforce additional custom constraints, this is a good location to add them. The arguments opts and snakemake.config are expected to be attached to the network.