osier.DispatchModel#

class osier.DispatchModel(technology_list, net_demand, time_delta=None, solver='cbc', lower_bound=0.0, oversupply=0.0, undersupply=0.0, verbosity=50, penalty=1e-10, power_units=MW, curtailment=True, allow_blackout=False, **kwargs)[source]#

Bases: object

The DispatchModel class creates and solves a basic dispatch model from the perspective of a “grid operator.” The model uses pyomo to create and solve a linear programming model. The mathematical formulation for this problem is:

Minimize

\[\text{C}_{\text{total}} = \sum_t^T \sum_u^U \left[c^{\text{fuel}}_{u,t} + c^{\text{om,var}}_{u,t}\right]x_{u,t}\]

Such that,

1. The generation meets demand within a user-specified tolerance (undersupply and oversupply)

\[ \begin{align}\begin{aligned}\sum_u^Ux_{u,t} &\geq \left(1-\text{undersupply}\right)\text{D}_t \ \forall \ t \in T\\\sum_u^Ux_{u,t} &\leq \left(1+\text{oversupply}\right)\text{D}_t \ \forall \ t \in T\end{aligned}\end{align} \]

2. A technology’s generation (\(x_u\)) does not exceed its capacity to generate at any time, \(t\).

\[x_{u,t} \leq \textbf{CAP}_{u}\Delta t \ \forall \ u,t \in U,T\]
  1. Technologies may not exceed their ramp up rate,

\[\frac{x_{r,t} - x_{r,t-1}}{\Delta t} = \Delta P_{r,t} \leq (\text{ramp up})\textbf{CAP}_u\Delta t \ \forall \ r,t \in R \subset U, T\]

or ramp down rate,

\[\frac{x_{r,t} - x_{r,t-1}}{\Delta t} = \Delta P_{r,t} \leq -(\text{ramp down})\textbf{CAP}_u\Delta t \ \forall \ r,t \in R \subset U, T .\]
Parameters:
  • technology_list (list of osier.Technology) – The list of Technology objects to dispatch – i.e. decide how much energy each technology should produce.

  • net_demand (list, numpy.ndarray, unyt.array.unyt_array, pandas.DataFrame) – The remaining energy demand to be fulfilled by the technologies in technology_list. The values of an object passed as net_demand are used to create a supply constraint. See oversupply and undersupply. If a pandas.DataFrame is passed, osier will try inferring a time_delta from the dataframe index. Otherwise, the time_delta must be passed or the default is used.

  • time_delta (str, unyt.unyt_quantity, float, int) –

    Specifies the amount of time between two time slices. The default is one hour. Can be overridden by specifying a unit with the value. For example:

    >>> time_delta = "5 minutes"
    >>> from unyt import days
    >>> time_delta = 30*days
    

    would both work.

  • power_units (str, unyt.unit_object) – Specifies the units for the power demand. The default is MW. Can be overridden by specifying a unit with the value.

  • solver (str) – Indicates which solver to use. May require separate installation. Accepts: [‘cplex’, ‘cbc’, ‘appsi_highs’]. Other solvers will be added in the future.

  • lower_bound (float) – The minimum amount of energy each technology can produce per time period. Default is 0.0.

  • oversupply (float) – The amount of allowed oversupply as a percentage of demand. Default is 0.0 (no oversupply allowed).

  • undersupply (float) – The amount of allowed undersupply as a percentage of demand. Default is 0.0 (no undersupply allowed).

  • verbosity (Optional, int) – Sets the logging level for the simulation. Accepts logging.LEVEL or integer where LEVEL is {10:DEBUG, 20:INFO, 30:WARNING, 40:ERROR, 50:CRITICAL}.

  • curtailment (boolean) – Indicates if the model should enable a curtailment option.

  • allow_blackout (boolean) – If True, a “reliability” technology is added to the model that will fulfill the mismatch in supply and demand. This reliability technology has a variable cost of 1e4 $/MWh. The value must be higher than the variable cost of any other technology to prevent a pathological preference for blackouts. Default is False.

model#

The pyomo model class that converts python code into a set of linear equations.

Type:

pyomo.environ.ConcreteModel

objective#

The result of the model’s objective function. Only instantiated after DispatchModel.solve() is called.

Type:

float

n_timesteps#

The number of timesteps in the model.

Type:

int

upper_bound#

The upper bound for all decision variables. Chosen to be equal to the maximum capacity of all technologies in tech_set.

Type:

float

storage_upper_bound#

The upper bound for storage decision variables.

Type:

float

penalty#

The penalty applied to the objective function to eliminate simultaneous charging and discharging. Users may need to tune this parameter. Default is 1e-10.

Type:

float

model_initialized#

Indicates whether DispatchModel.model has been populated with equations yet. This is set to True after DispatchModel._write_model_equations() has been called.

Type:

bool

indices#

The list of tuples representing the product of the tech_set and time_set attributes.

Type:

list of tuples

tech_set#

A list of the unique technology names in the simulation.

Type:

list of str

capacity_dict#

A dictionary of {name : capacity} pairs.

Type:

dict

efficiency_dict#

A dictionary of {name : efficiency} pairs.

Type:

dict

time_set#

The result of range(self.n_timesteps).

Type:

iterable

cost_params#

The set of cost parameters for each technology. Corresponds to a list of values. Size is equal to the product of the number of timesteps and the number of technologies in the model.

Type:

list

ramp_up_params#

The set of ramp_up parameters. Only initialized if there is a ramping technology.

Type:

list

ramp_down_params#

The set of ramp_down parameters. Only initialized if there is a ramping technology.

Type:

list

ramping_techs#

The subset of tech_set containing ramping technologies.

Type:

list

Notes

1. Technically, solver will accept any solver that pyomo can use. We only list two solvers because those are the only solvers in the osier test suite.

2. The default value for time_delta in __init__ is None. This is replaced by a setter method in Dispatch.

3. This formulation uses a penalty parameter to prevent unphysical behavior because it preserves the problem’s linearity. Some formulations may use a binary variable to prevent simultaneous charging and discharing. However, this changes the problem to a mixed-integer linear program which requires a more sophisticated solver such as gurobi.

4. _write_model_equations() may be called before solve() if users wish to add their own constraints or parameters to the problem.

Methods

solve([solver])

Executes the model solve.