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
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:
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:
or
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
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):
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:
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
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
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().