Implementation of future values
As outlined on the page implementation of future values, we implemented a new AbstractElement for representing the future value in dynamic constraints. So far, two subtype are implemented through StorageValueCuts and TypeFutureValue. It is however possible to provide new subtypes representing different implementations for calculating or constraining a future value.
Function flow for incorporating future values
As mentioned, FutureValue is implemented as subtype of AbstractElement. This implies that the package must provide methods for a variety of individual functions from EnergyModelsBase.
The new methods for variable creation are
EMB.variables_capacity, not incorporating any new variable,EMB.variables_flow, not incorporating any new variable,EMB.variables_opex, incorporating the new variable $\texttt{future\_value}(v) ~ \forall v \in V$,EMB.variables_element, not incorporating any new variable, andEMB.variables_emission, not incorporating any new variable.
This implies that we currently only include a single additional variable for the future value, $\texttt{future\_value}(v)$. This variable is created for all subtypes of FutureValue.
The individual constraints are added through the methods
EMB.constraints_elements, calling the subfunctionEMRH.create_future_value,EMB.constraints_couple, calling the subfunctionEMRH.create_future_value_couple,EMB.emissions_operational, returning aJuMPexpression of 0 with the required indices asFutureValues do not result in emissions, andEMB.objective_operational, calling the subfunctionEMRH.get_future_value_expression.
There are two things to highlight in this design:
- the default method for
EMRH.create_future_valuedoes not create any constraints and EMRH.create_future_value_coupleis only declared forStorageValueCuts.
Point 1 implies that the variable $\texttt{future\_value}(v)$ does not have any internal constraints by default. It is instead constrained for the implementation of StorageValueCuts through the function EMRH.create_future_value_couple. The thought process behind this approach is that StorageValueCuts are constrained through Storage nodes. It is hence a constraints_couple constraint, even if the function has only a single input, 𝒱::Vector{<:FutureValue}.
We furthermore decided to split the overall contribution to the cost function in the current design into additional subfunctions through EMRH.get_future_value_expression for the individual supertypes. While this is not strictly necessary, it is one approach for differentiating between subtypes of FutureValue.
Within the concept of EnergyModelsRecedingHorizon, we added as well an additional function EMRH._update_future_value! for updating parameters of the FutureValue before each optimization run.
We do not include default methods for the subfunctions. These subfunctions are EMRH.create_future_value_couple and EMRH.get_future_value_expression. As a consequence, you must declare them for your new type.
The philosophy here is that it is preferable that the developer of new methods receives a method error instead of no error when he implements a wrong method.
Requirements for new future values
It is possible to create new FutureValues. This requires a combination of new types and new metods. The following steps must be conducted to incorporate a new type:
Create a new type as composite type:
struct NewFutureValue <: FutureValue id::Any endIt can include as many fields as desired, but it 1. must include the field
idand 2. cannot include a dictionary in which aNodeis a key (you may useElementValueinstead if necessary).Create a new method for the function
EMRH.create_future_value_couplefor yourNewFutureValue. In general, you should have the correspondingNodeas field of yourNewFutureValue. If this is not the case, e.g., through assigning the same future value for all instances of a given variable of a type, it is necessary to provide new methods forEMB.constraints_couplewhich also includes a potentially empty method forFutureValue.Create a new method for the function
EMRH.get_future_value_expressionwhich returns aJuMPexpression indexed over the strategic periods which corresponds to the contribution to the cost function.Create a new method for the function
EMRH._update_future_value!. This function is used for updating potential fields that are dependent on the elapsed time before the optimization problem is solved. If the future value for yourNewFutureValueis independent of the elapsed time, you can provide an empty method.
If your NewFutureValue requires additional variables, you can create a new method for EMB.variables_element. These variables will then be accessible in all potential subsequent functions. If you have internal constraints, that is constraints only accessing variables indexed over Vector{<:NewFutureValue>}, you must create a new method for EMRH.create_future_value and add these constraints in this function.