SpineOpt.jl
SpineOpt.jl copied to clipboard
Multi-stage and multi-instance models
In Mopo Task 4.6 the aim is to enable solve sequences (e.g. multi-stage investment models; storage value combined with dispatch; unit commitment and realisation of reserves) and other cases where multiple instances of SpineOpt need to be populated (e.g. resource adequacy, decomposition).
@DillonJ, @trobob and @manuelma need resource adequacy for another project, but it could be done in a way that helps to build the machinery for all of these use cases. There is further discussion about this in e-mail, since ER has been thinking a lot about how to do the resource adequacy part, but I'll summarize the approach from the generic perspective - we need specific issues for the things that will support specific implementations (e.g. resource adequacy will need to be able to generate random samples of outages).
Also, the approach presented here is influenced by the work already done for FlexTool where nested models work quite nicely: https://irena-flextool.github.io/flextool/how_to/#how-to-use-nested-rolling-window-solves-investments-and-long-term-storage
What new parameters are needed and how to organize them
Currently SpineOpt has a model
class that contains the parameters that specify how the model behaves at the high level. If we have multiple models in a sequence, we need to be able to chain model instances. One way to do this is to split the model
to model
and solve
classes. Then model
class entity could define the sequence of solves (e.g. through an array parameter solves
). Another option would be to allow self-referencing: model
object could have a parameter contain_models
. It can be more messy, because it does not allow for a clear distinction what parameters belong to all solves and what are solve specific. So, probably best to have a new class: solve
. For nested models, where information needs to be passed between upper and lower level solves, there is still a need for parameter like contains_solve in the solve
class, so that the nested structure can be established between solves.
Many new parameters will be specific to information passed between models or to the way the specific parts of the model need to be instantiated. For example: how to inform which storage's value to pass between solves or how to define units/connections to be included in randomized outages. These should have their own issues (and then link to this one), so that this generic issue does not come too messy.
A wrapper to orchestrate model solves
A wrapper function (similar to current decomposition approach) that orchestrates the solving of model instances. It passes information between models when needed (e.g. storage value to a lower level model or actual storage content from lower level to upper level.
Update the model in the loop
As the model instances can be very similar between solves (especially in resource adequacy), it can be efficient to just update the model instead of building new model instances. There should also be user controlled parallelisation when it is helpful (it could be efficient to run as many scenarios in parallel as there are cores).
Hot-starts could also be used between upper and lower models: water value model probably solves the same problem as the dispatch but with less temporal detail – it could be a good basis. However, that could come later (and solve in a separate issue).
Result processing
What information is taken from the multiple solves - user needs to be able to limit this in a nice way. Probably depends on the particular type of model, so best to tackle in separate issues. However, maybe there are couple of broad categories:
- Nested models: what to keep from the upper level model
- Rolling models: what to keep from the horizon that is not realized
- Resource adequacy: Nearly the same model solved many times, what to keep and how to summarize for the user (e.g. highlight worst cases)
FYI: We are also thinking about adding adequacy (to account for low voltage flexibility) to SpineOpt in another project. Though I'm not yet sure what we will exactly need in that project.
Remark: Despite the name solve
corresponding to the same use in Flextool, solve
may be a bit unclear that it is some kind of model
. You could argue that a solve of a model could be using different solvers or time steps but that the model structure stays the same. An alternative name could be submodel
as it is a smaller/reduced model as part of a bigger model? Though, it is just a name and we should not dwell too long on it.
In ines the term is actually solve_pattern
which is better than just solve
(I just forgot when writing that). Sometimes one solve_pattern
defines multiple solves (e.g. rolling window). I wouldn't personally confuse those with a 'solver', since that has a clearly different meaning, but I suppose that can vary between people. Choosing the solver should actually be one of the options of a solve_pattern
.
submodel
could also be fine, although it can be confused to be a section of the physical model (e.g. gas system or Ireland). I guess I think solve_pattern
is more accurate, although it is more verbose and underscore would be nice to avoid.