Skip to content

Objective

The objective is to minimise total system costs (i.e. the sum of all costs) subject to meeting all constraints of the optimisation problem (e.g. energy balance, dispatch limits, etc.) for the selected snapshots.

The objective function is composed of investment costs and multiple forms of operational costs, such as marginal costs, quadratic marginal costs, marginal storage costs, spillage costs, start-up, shut-down and stand-by costs.

Which cost components are included in the objective function depends on the data provided for the network components. For instance, if no extendable components are present ({p,s,e}_nom_extendable=False), the investment cost terms are skipped. Or, if no components are marked as committable (committable=False), the start-up, shut-down and stand-by costs are skipped. All cost coefficients default to zero, so that the objective function is empty if no costs are defined.

Investment Costs

For extendable components ({p,s,e}_nom_extendable=True), the investment costs of expanding their capacity are given by

\[\sum_{n,s} c_{n,s} G_{n,s} + \sum_{n,s} c_{n,s} H_{n,s} + \sum_{n,s} c_{n,s} E_{n,s} + \sum_{l} c_{l} F_l + \sum_{m} c_{m} R_m + \sum_{l} c_{l} P_l\]

where \(c_{*}\) are the capital costs per unit of nominal capacity for generators (\(G_{n,s} \in \mathbb{R}\)), storage units (\(H_{n,s} \in \mathbb{R}\)), stores (\(E_{n,s} \in \mathbb{R}\)), links (\(F_l \in \mathbb{R}\)), processes (\(R_m \in \mathbb{R}\)), and lines and transformers (\(P_l \in \mathbb{R}\)). The subscript \(n\) labels the bus, \(s\) labels the particular generator/storage type at the bus, \(l\) labels the branch, and \(m\) labels the process. The decision variables \(G_{n,s}\), \(H_{n,s}\), \(F_l\), \(R_m\), and \(P_l\) are the nominal power capacities, whereas \(E_{n,s}\) is the nominal energy capacity of the store. The capital costs are given in currency/MW for generators, links, processes, lines and transformers, and in currency/MWh for stores.

To minimise long-run annual system costs (currency/a), capital costs for components should be set to annualised investment costs (i.e. currency/MW/a and currency/MWh/a ), marginal costs for dispatch in currency/MWh, and the weightings (h/a) are chosen such that \(\sum_t w_t^o = 8760\) hours per annum.

If no extendable components are present, only the dispatch of the components is optimised as in a short-run market model.

Specifying Capital Costs

PyPSA supports two approaches for specifying investment costs:

Provide capital_cost directly as annualized cost per unit capacity (currency/MW or currency/MWh) per model period:

n.add("Generator", "wind",
      bus="bus",
      p_nom_extendable=True,
      capital_cost=50000)  # Already periodized: €/MW

Provide overnight_cost (upfront investment cost), discount_rate, and lifetime. PyPSA automatically calculates the periodized cost:

n.add("Generator", "wind",
      bus="bus",
      p_nom_extendable=True,
      overnight_cost=1200000,  # Upfront cost: €/MW
      discount_rate=0.07,      # 7% discount rate
      lifetime=25,             # 25 years
      fom_cost=12000)          # Fixed O&M: €/MW/a

The effective periodized cost per MW installation used in optimization is calculated as:

\[c = c_{\text{overnight}} \cdot \text{annuity}(r, n) \cdot N_{\text{years}} + c_{\text{fom}}\]

where \(N_{\text{years}}\) refers to the model time span in units of years, derived from n.snapshot_weightings["objective"]. The annuity factor converts overnight cost to annual payments:

\[\text{annuity}(r, n) = \frac{r}{1 - (1 + r)^{-n}}\]

or

\[1/n /text{if} r=0\]

Fixed Operation & Maintenance Costs

Fixed O&M costs (fom_cost) represent periodized costs that are incurred regardless of dispatch, such as maintenance, insurance, and land lease. They are added to the annualized investment cost (capital_cost / overnight_cost):

# Wind turbine with 2% of overnight cost as annual FOM
n.generators.loc["wind", "fom_cost"] = 0.02 * n.generators.loc["wind", "overnight_cost"]
Mapping of symbols to attributes
Symbol Attribute Type
\(G_{n,s}\) n.generators.p_nom_opt Decision variable
\(H_{n,s}\) n.storage_units.p_nom_opt Decision variable
\(E_{n,s}\) n.stores.p_nom_opt Decision variable
\(F_l\) n.links.p_nom_opt Decision variable
\(R_m\) n.processes.p_nom_opt Decision variable
\(P_l\) n.lines.s_nom_opt Decision variable
\(c_{n,s}\) n.{generators,storage_units,stores}.capital_cost Parameter
\(c_{l}\) n.{links,lines,transformers}.capital_cost Parameter
\(c_{m}\) n.processes.capital_cost Parameter
\(c_{\text{overnight}}\) n.{generators,...}.overnight_cost Parameter
\(r\) n.{generators,...}.discount_rate Parameter
\(n\) n.{generators,...}.lifetime Parameter
\(c_{\text{fom}}\) n.{generators,...}.fom_cost Parameter

Marginal Costs

The marginal costs of dispatch are given by

\[+ \sum_{t} w_t^o \left( \sum_{n,s} o_{n,s,t} g_{n,s,t} + \sum_{n,s} o_{n,s,t} h_{n,s,t}^- + \sum_{n,s} o_{n,s,t} h_{n,s,t} + \sum_{l} o_{l,t} f_{l,t} + \sum_{m} o_{m,t} r_{m,t} \right)\]

where \(o_{*}\) are the marginal costs per unit of power for generator (\(g_{n,s,t} \in \mathbb{R}\)), storage unit (\(h_{n,s,t}^- \in \mathbb{R}\)), store (\(h_{n,s,t} \in \mathbb{R}\)), link (\(f_{l,t} \in \mathbb{R}\)) and process (\(r_{m,t} \in \mathbb{R}\)) dispatch. Note here the difference between storage unit dispatch \(h_{n,s,t}^-\) (where costs are only incurred for discharging) and store dispatch \(h_{n,s,t}\) (where marginal costs incur a cost for discharging and a revenue for charging). The subscript \(t\) indicates the snapshot.

Mapping of symbols to attributes
Symbol Attribute Type
\(g_{n,s,t}\) n.generators_t.p Decision variable
\(h_{n,s,t}^-\) n.storage_units_t.p_dispatch Decision variable
\(h_{n,s,t}\) n.stores_t.p Decision variable
\(f_{l,t}\) n.links_t.p Decision variable
\(r_{m,t}\) n.processes_t.p Decision variable
\(o_{n,s,t}\) n.{generators,storage_units,stores}_t.marginal_cost Parameter
\(o_{l,t}\) n.links_t.marginal_cost Parameter
\(o_{m,t}\) n.processes_t.marginal_cost Parameter
\(w_t^o\) n.snapshots.weightings.objective Parameter

Quadratic Marginal Costs

Quadratic marginal costs can be included, which turn the problem into a quadratic program (QP):

\[+ \sum_{t} w_t^o \left( \sum_{n,s} qmc_{n,s,t} g_{n,s,t}^2 + \sum_{n,s} qmc_{n,s,t} {h_{n,s,t}^-}^2 + \sum_{n,s} qmc_{n,s,t} h_{n,s,t}^2 + \sum_{l} qmc_{l,t} f_{l,t}^2 + \sum_{m} qmc_{m,t} r_{m,t}^2 \right)\]

where \(qmc_{*}\) are the quadratic marginal costs per unit of dispatch for generators (\(g_{n,s,t}\)), storage units (\(h_{n,s,t}^-\)), stores (\(h_{n,s,t}\)), links (\(f_{l,t}\)) and processes (\(r_{m,t}\)), weighted by the objective snapshot weightings \(w_t^o\).

Mapping of symbols to attributes
Symbol Attribute Type
\(g_{n,s,t}\) n.generators_t.p Decision variable
\(h_{n,s,t}^-\) n.storage_units_t.p_dispatch Decision variable
\(h_{n,s,t}\) n.stores_t.p Decision variable
\(f_{l,t}\) n.links_t.p Decision variable
\(r_{m,t}\) n.processes_t.p Decision variable
\(qmc_{n,s,t}\) n.{generators,storage_units,stores}_t.marginal_cost_quadratic Parameter
\(qmc_{l,t}\) n.links_t.marginal_cost_quadratic Parameter
\(qmc_{m,t}\) n.processes_t.marginal_cost_quadratic Parameter
\(w_t^o\) n.snapshots.weightings.objective Parameter

Marginal Storage Costs

Marginal storage costs can also be applied to storage units and stores, which represent the cost of holding energy in storage. The objective function includes these costs as follows:

\[+ \sum_{t} w_t^o \left( \sum_{n,s} mcs_{n,s,t} soc_{n,s,t} + \sum_{n,s} mcs_{n,s,t} e_{n,s,t} \right)\]

where \(mcs_{*}\) are the marginal storage costs per unit of energy for storage units (\(soc_{n,s,t} \in \mathbb{R}\)) and stores (\(e_{n,s,t} \in \mathbb{R}\)), weighted by the objective snapshot weightings \(w_t^o\).

Mapping of symbols to attributes
Symbol Attribute Type
\(soc_{n,s,t}\) n.storage_units_t.state_of_charge Decision variable
\(e_{n,s,t}\) n.stores_t.e Decision variable
\(mcs_{n,s,t}\) n.storage_units_t.marginal_cost_storage Parameter
\(mcs_{n,s,t}\) n.stores_t.marginal_cost_storage Parameter
\(w_t^o\) n.snapshots.weightings.objective Parameter

Spillage Costs

Spillage costs, e.g. sending water in a hydro reservoir over a spillway without generating electricity, can be given for storage units by

\[ + \sum_{n,s,t} w_t^o sc_{n,s,t} \textrm{spillage}_{n,s,t} \]

where \(sc_{n,s,t}\) are the spillage costs per unit of spillage (\(\textrm{spillage}_{n,s,t} \in \mathbb{R}\)), weighted by the objective snapshot weightings \(w_t^o\).

Mapping of symbols to attributes
Symbol Attribute Type
\(\textrm{spillage}_{n,s,t}\) n.storage_units_t.spillage Decision variable
\(sc_{n,s,t}\) n.storage_units_t.spill_cost Parameter
\(w_t^o\) n.snapshots.weightings.objective Parameter

Start-Up, Shut-Down, Stand-By Costs

For generators, links and processes with unit commitment (committable=True), stand-by costs \(sbc_{*,t}\), start-up costs \(suc_{*,t}\) and shut-down costs \(sdc_{*,t}\) are given by

\[+ \sum_{n,s,t} w_t^o sbc_{n,s,t} u_{n,s,t} + \sum_{n,s} suc_{n,s} su_{n,s,t} + \sum_{n,s} sdc_{n,s} sd_{n,s,t}\]
\[+ \sum_{l,t} w_t^o sbc_{l,t} u_{l,t} + \sum_{l,t} suc_{l,t} su_{l,t} + \sum_{l,t} sdc_{l,t} sd_{l,t}\]
\[+ \sum_{m,t} w_t^o sbc_{m,t} u_{m,t} + \sum_{m,t} suc_{m,t} su_{m,t} + \sum_{m,t} sdc_{m,t} sd_{m,t}\]

where \(sbc_{*,t}\), \(suc_{*,t}\), and \(sdc_{*,t}\) are the stand-by, start-up, and shut-down costs linked to the status (\(u_{*,t} \in \mathbb{B}\)), start-up (\(su_{*,t} \in \mathbb{B}\)), and shut-down (\(sd_{*,t} \in \mathbb{B}\)) unit commitment variables. Only the stand-by costs are weighted by the objective snapshot weightings \(w_t^o\). Components with unit commitment constraints turn the problem into a mixed-integer linear program (MILP).

Mapping of symbols to attributes
Symbol Attribute Type
\(u_{n,s,t}\) n.generators_t.status Decision variable
\(su_{n,s,t}\) n.generators_t.start_up Decision variable
\(sd_{n,s,t}\) n.generators_t.shut_down Decision variable
\(sbc_{n,s,t}\) n.generators_t.stand_by_cost Parameter
\(suc_{n,s}\) n.generators.start_up_cost Parameter
\(sdc_{n,s}\) n.generators.shut_down_cost Parameter
\(w_t^o\) n.snapshots.weightings.objective Parameter
Symbol Attribute Type
\(u_{l,t}\) n.links_t.status Decision variable
\(su_{l,t}\) n.links_t.start_up Decision variable
\(sd_{l,t}\) n.links_t.shut_down Decision variable
\(sbc_{l,t}\) n.links_t.stand_by_cost Parameter
\(suc_{l,t}\) n.links.start_up_cost Parameter
\(sdc_{l,t}\) n.links.shut_down_cost Parameter
\(w_t^o\) n.snapshots.weightings.objective Parameter
Symbol Attribute Type
\(u_{m,t}\) n.processes_t.status Decision variable
\(su_{m,t}\) n.processes_t.start_up Decision variable
\(sd_{m,t}\) n.processes_t.shut_down Decision variable
\(sbc_{m,t}\) n.processes_t.stand_by_cost Parameter
\(suc_{m,t}\) n.processes.start_up_cost Parameter
\(sdc_{m,t}\) n.processes.shut_down_cost Parameter
\(w_t^o\) n.snapshots.weightings.objective Parameter

Some decision variables do not show up in the objective function, such as the power flow on lines and transformers (\(p_{l,t} \in \mathbb{R}\)) and the storage unit charging (\(h_{n,s,t}^+ \in \mathbb{R}\)). They are only used to enforce constraints, e.g. the power flow on lines and transformers.

The objective function is defined in the function define_objective().