Mixed Integer Linear Programming (MILP) example model¶
This example is based on the Urban scale example model, but with an override to introduce binary and integer variables.
This override is applied from the scenarios.yaml
file:
overrides:
milp:
config:
init.name: "Urban-scale example model with MILP"
solve.solver_options: { mipgap: 0.05 }
techs:
chp:
cap_method: integer
integer_dispatch: true
purchased_units_max: 4
flow_cap_max:
_REPLACE_: null # (1)!
flow_cap_per_unit:
data: 300
index: electricity
dims: carriers
flow_out_min_relative:
data: 0.2
index: electricity
dims: carriers
cost_flow_cap.data: 700
cost_purchase:
data: 40000
index: monetary
dims: costs
boiler:
cap_method: integer
purchased_units_max: 1 # i.e., binary variable.
cost_flow_cap.data: 35
cost_purchase:
data: 2000
index: monetary
dims: costs
tech_groups:
heat_pipes:
force_async_flow: true
- The
_REPLACE_
syntax can be used with Calliope YAML files when you want the override to completely replace the sub-dictionary rather than update parts of it.
Note
MILP functionality can be easily applied, but convergence is slower as a result of integer/binary variables. It is recommended to use a commercial solver (e.g. Gurobi, CPLEX) if you wish to utilise these variables outside this example model.
Model definition¶
We will only discuss the components of the model definition that differ from the urban scale example model. Refer to that tutorial page for more information on this model.
Purchased units¶
The capacity of a technology is usually a continuous decision variable, which can be within the range of 0 and flow_cap_max
(the maximum capacity of a technology).
In this model, we introduce a unit limit on the CHP instead:
techs:
chp:
cap_method: integer
integer_dispatch: true
purchased_units_max: 4
flow_cap_max:
_REPLACE_: null # (1)!
flow_cap_per_unit:
data: 300
index: electricity
dims: carriers
flow_out_min_relative:
data: 0.2
index: electricity
dims: carriers
cost_flow_cap.data: 700
cost_purchase:
data: 40000
index: monetary
dims: costs
A unit maximum allows a discrete, integer number of CHP to be purchased, each having a capacity of flow_cap_per_unit
.
flow_cap_max
and flow_cap_min
cannot be defined alongside flow_cap_per_unit
.
Instead, purchased_units_max
and purchased_units_min
are available.
A useful feature unlocked by introducing this is the ability to set a minimum operating capacity which is only enforced when the technology is operating.
In the LP model, flow_out_min_relative
would force the technology to operate at least at that proportion of its maximum capacity at each time step.
In this model, the newly introduced flow_out_min_relative
of 0.2 will ensure that the output of the CHP is 20% of its maximum capacity in any time step in which it has a non-zero output.
Purchase cost¶
The boiler also has a unit limit. However, we limit it to purchased_units_max
to 1 and use it alongside a continuous flow_cap
:
techs:
boiler:
cap_method: integer
purchased_units_max: 1 # i.e., binary variable.
cost_flow_cap.data: 35
cost_purchase:
data: 2000
index: monetary
dims: costs
By introducing this, the boiler is now associated with a binary decision variable.
It is 1 if the boiler has a non-zero flow_cap
(i.e. the optimisation results in investment in a boiler) and 0 if the capacity is 0.
The purchase cost is applied to the binary result, providing a fixed cost on purchase of the technology, irrespective of the technology size. In physical terms, this may be associated with the cost of pipework, land purchase, etc. The purchase cost is also imposed on the CHP, which is applied to the number of integer CHP units in which the solver chooses to invest.
Asynchronous flow in/out¶
The heat pipes which distribute thermal energy in the network may be prone to dissipating heat in an "unphysical" way. I.e. given that they have distribution losses associated with them, in any given timestep a link could produce and consume energy simultaneously. It would therefore lose energy to the atmosphere, but have a net energy transmission of zero.
This allows e.g. a CHP facility to overproduce heat to produce more cheap electricity, and have some way of dumping that heat.
The async_flow_switch
binary variable (triggered by the force_async_flow
parameter) ensures this phenomenon is avoided:
Now, only one of flow_out
and flow_in
can be non-zero in a given timestep.
This constraint can also be applied to storage technologies, to similarly control charge/discharge.