Code structure
EnergyModelsRecedingHorizon is based on setting up optimization subproblems given implementation and optimization horizons. The horizons for the optimization subproblems are expressed through AbstractHorizons objects, see the dedicated section on horizons. These will ultimately define how many optimization subproblems will be solved and how large they will be.
General concept
For every iteration over the horizons, the optimization subproblem needs to be updated for the time-dependent fields. This is done through AbstractPath objects, whose aim is to automatically identify the model variables that need updating. Each AbstractPath object corresponds to a field in the model structure to be updated. The creation of the accessors to these fields (known as lenses) is done through AbstractReset objects, allowing for type checking of the fields to be updated. This object must always be created through ResetType constructors. The element subject to updating is wrapped around an AbstractSub object, where the original (full-problem) and new (receding horizon) instances of the element are included as fields, as well as the AbstractReset objects linked to the element. A unified constructor, Substitution, is used within the model.
As a general rule, the fields that require updating are either related to the initial conditions of the system or to the time profiles that the system is subject to. For updating the time profiles, we read the originally provided time profile for the full optimization problem. As for the initial conditions of the system, each element is assumed to be independently initializable through their own variables. Initialization of the system is therefore done at the element level, which we describe in more detail in the dedicated page on initialization.
Implemented reset types
The individual reset types correspond to fields of an element that should be replaced. The reset types have as individual fields
lensrepresenting the lens pointing towards the field in the type,varcorresponding to the parameter variable when usingParametricOptInterface, andvalis the value which is used in the resets. Its meaning differs depending on the individual reset type.
EnergyModelsRecedingHorizon implements the following standard reset types:
OperResetwithOperPath: A reset type pointing towards a field withOperationalProfile.
The fieldvalcorresponds to the operational profile of the full time structure, not the profile of the individual horizon.ElementResetwithElementPath: A reset type pointing towards a field with anAbstractElement.
The fieldvalcorresponds to the instance of the element of the original case type.TimeWeightResetwithTimeWeightPath: A reset type for a time weight of aFutureValue.
The fieldvalis updated at the beginning of each iteration according to the optimization horizon end time.InitResetwithAbstractInitDataPath: A reset type pointing towards fields of initial data.
In addition, it has theAbstractInitDataPathinstance as second field. The fieldvalis updated after each optimization iteration with the extracted value from the optimization results.
All reset types utilize an inner constructor for creating the lens in their construction. Furthermore, all reset types are constructed only through the function EMRH.ResetType.
While the core structure includes the majority of the required reset types, it can be necessary to create new reset types. In this case, you must create:
- a subtype to
AbstractPath, - a subtype to
EMRH.AbstractReset, - a new method for the function
ResetTypeusing the same arguments order, - a new method for the function
EMRH._find_update_paths, and - a new method for the function
EMRH._reset_fieldand itsPOIextension.
In general, it should not be necessary to create a new reset type as we simplified the approach for initialization data through the utilization of a parametric type.
Implemented substitution types
Substitution types unify all resets within an AbstractElement. All substitution types are parametric on the element type and have the fields
newrepresenting the new element instance that is used in the case for analysis,orgrepresenting the original element instance from the original case structure, andresets::Vector{<:AbstractReset}including allAbstractResets that are relevant for the given type.
They are constructed through the function EMRH.Substitution.
EnergyModelsRecHorizon implements the following standard reset types:
ModelSubfor resetting fields in the model type,ProductSubfor resetting fields inResources,NodeSubfor resetting fields inNode,LinkSubfor resetting fields inLink, andFutureValueSubfor resetting fields inFutureValue.
The extension for EnergyModelsGeography adds the following additional reset types:
AreaSubfor resetting fields inAreas andTransmissionSubfor resetting fields inTransmissioncorridors.
If your EnergyModelsBase extension introduces new AbstractElements, you must provide:
- a subtype to
EMRH.AbstractSub, - a method to the function
EMRH._ele_to_sub, and - a method for your function for extracting the
AbstractElementfrom theUpdateCase, see, e.g.,EMB.get_nodes.
Depending on the structure of your AbstractElement, it can be furthermore necessary to provide new methods to the functions EMRH.original and EMRH.updated.
Internal work flow
While the internal work flow is in general the same whether you use ParametricOptInterface or not, there are some differences in the individual function flow. The following section provides the general overview with differentiation between the individual implementations.
The
UpdateCaseis first created for all elements through the functionsEMRH._create_updatetypeandEMRH._add_elements!. These functions create the individual substitution and reset types including the relevant lenses. The functions are agnostic whether you useParametricOptInterfaceor not.POI implementation The
POIimplementation requires as additional step the initialization of theJuMPmodel through calling the functioncreate_modelto avoid constructing the model in every single iteration. To this end, the specific case and model are initialized through the functionPOIExt._init_update_case!in which the first horizon is used for the initialization of the optimization problem and the creation of the additional auxiliary variables required for the parameters.The individual elements with
AbstractInitDataare identified to avoid iterating through all elements in each iteration of the receding horizon framework. In addition, we rearrange theFutureValuesubstitution types for simplified later updating.The algorithm iterates through all horizons sequentially and solves the optimization problem.
If the system includes
FutureValuetypes, we updateFutureValues whose weight is dependent on the elapsed time using the functionEMRH._update_future_value!. This is for example the case forStorageValueCuts.The second step is different in the two implementations.
Standard implementation In the standard implementation, we first update the
UpdateCasecreated in the first step through the functionEMRH._update_update_case!and subsequently extract from theUpdateCaseboth themodelandcaserequired for creating an EMX model. Subsequently, we create theJuMPmodel through calling the functioncreate_model.POI implementation In the
POIimplementation, we only have to update the values of the created parameter variables through the functionPOIExt.update_model!.Subsequently, the model is optimized.
Once the model is solved, the operational results for the implementation horizon are extracted from the model and saved as a
DataFramethrough the functionEMRH.update_results!. Variables that are indexed with strategic periods are not extracted.The value of the reset type for initialization data is updated using the results from the optimization problem using the function
EMRH.update_init_data!.