spine-tools / SpineOpt.jl

A highly adaptable modelling framework for multi-energy systems
https://www.tools-for-energy-system-modelling.org/
GNU Lesser General Public License v3.0
53 stars 13 forks source link

Multi-stage and multi-instance models #901

Open jkiviluo opened 6 months ago

jkiviluo commented 6 months ago

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:

tarskul commented 6 months ago

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.

jkiviluo commented 6 months ago

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.