Allow passing custom options to HiGHS via model.toml#1276
Conversation
It doesn't make sense to create a `DispatchRun` struct and not do anything with it.
…rent/non-optimal
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1276 +/- ##
==========================================
- Coverage 89.78% 89.50% -0.29%
==========================================
Files 57 57
Lines 8204 8267 +63
Branches 8204 8267 +63
==========================================
+ Hits 7366 7399 +33
- Misses 542 567 +25
- Partials 296 301 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adds a development-focused mechanism for passing user-specified HiGHS solver options through model.toml, gated behind please_give_me_broken_results, and wires those options into both dispatch and investment appraisal optimisations.
Changes:
- Introduces a new optional
[highs]configuration section withglobal_options,dispatch_options, andappraisal_options, plus validation/gating. - Applies configured HiGHS options when constructing the HiGHS model for dispatch and appraisal runs.
- Adds developer documentation and schema updates for the new configuration.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/simulation/optimisation.rs |
Adds TOML→HiGHS option application helper; updates solver error handling; applies dispatch options before solving. |
src/simulation/investment/appraisal/optimisation.rs |
Applies appraisal options before solving and adjusts API to access Model for parameters/time slices. |
src/simulation/investment/appraisal.rs |
Updates appraisal calls to pass Model into optimisation helper. |
src/model/parameters.rs |
Adds HighsOptions to ModelParameters, validates gating, and merges global_options into dispatch/appraisal options. |
schemas/input/model.yaml |
Documents the new [highs] config structure in the model schema. |
docs/SUMMARY.md |
Links the new developer guide page. |
docs/developer_guide/custom_highs_options.md |
Documents how to configure custom HiGHS options via model.toml. |
clippy.toml |
Allows “HiGHS” as a valid doc identifier for Clippy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fn check_highs_options(dangerous_options_enabled: bool, highs: &HighsOptions) -> Result<()> { | ||
| ensure!( | ||
| dangerous_options_enabled || highs.is_empty(), | ||
| "Cannot set custom HiGHS options without enabling {ALLOW_DANGEROUS_OPTION_NAME}" | ||
| ); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…options Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| /// Another error occurred | ||
| Other(anyhow::Error), | ||
| } | ||
|
|
| /// NOTE: If you add or change a field in this struct, you must also update the schema in | ||
| /// `schemas/input/model.yaml`. | ||
| #[derive(Debug, Deserialize, PartialEq)] | ||
| #[derive(Deserialize)] |
|
|
||
| Ok(()) | ||
| } | ||
|
|
| pub fn from_path<P: AsRef<Path>>(model_dir: P) -> Result<ModelParameters> { | ||
| let file_path = model_dir.as_ref().join(MODEL_PARAMETERS_FILE_NAME); | ||
| let model_params: ModelParameters = read_toml(&file_path)?; | ||
| let mut model_params: ModelParameters = read_toml(&file_path)?; | ||
|
|
||
| set_dangerous_model_options_flag(model_params.allow_dangerous_options); | ||
|
|
||
| model_params | ||
| .validate() | ||
| .with_context(|| input_err_msg(file_path))?; | ||
|
|
||
| // Copy global options to other tables | ||
| model_params.highs.apply_global_options(); | ||
|
|
||
| Ok(model_params) |
There was a problem hiding this comment.
Probably fine as it is, but I agree this isn't ideal. Have you considered other approaches? Wouldn't move it to validate() like it's suggesting, but possibly the deserialization suggestion if easy, or merging the global and local options on retrieval?
| As part of development, you may wish to directly set custom options for the HiGHS solver. Note that | ||
| while some of these options will not affect results of simulations (e.g. to enable console logging | ||
| for HiGHS), as we cannot guarantee this for all options, in order to use this feature, you have to | ||
| set `please_give_me_broken_results = true` in your [`model.toml` file][model.toml]. | ||
|
|
||
| You can change any of the options exposed by the HiGHS solver; for more information, see [the HiGHS | ||
| documentation][highs-opts]. | ||
|
|
||
| You can set options to be applied to all optimisations, just dispatch or just appraisal. | ||
|
|
||
| Here is an example: | ||
|
|
||
| ```toml | ||
| please_give_me_broken_results = true | ||
| milestone_years = [2020, 2030, 2040] | ||
|
|
||
| # These options are applied to all optimisations | ||
| [highs.global_options] | ||
| # These two options are required to be enabled to log to console | ||
| log_to_console = true |
tsmbland
left a comment
There was a problem hiding this comment.
Some comments perhaps worth addressing, but otherwise this looks good and potentially very useful!
| ## New features | ||
|
|
||
| <!-- TODO --> | ||
| - Users can now optionally pass [custom options][highs-options] to the HiGHS solver [#1276] |
There was a problem hiding this comment.
I think it would be more helpful here to link to the new documentation page
| pub fn from_path<P: AsRef<Path>>(model_dir: P) -> Result<ModelParameters> { | ||
| let file_path = model_dir.as_ref().join(MODEL_PARAMETERS_FILE_NAME); | ||
| let model_params: ModelParameters = read_toml(&file_path)?; | ||
| let mut model_params: ModelParameters = read_toml(&file_path)?; | ||
|
|
||
| set_dangerous_model_options_flag(model_params.allow_dangerous_options); | ||
|
|
||
| model_params | ||
| .validate() | ||
| .with_context(|| input_err_msg(file_path))?; | ||
|
|
||
| // Copy global options to other tables | ||
| model_params.highs.apply_global_options(); | ||
|
|
||
| Ok(model_params) |
There was a problem hiding this comment.
Probably fine as it is, but I agree this isn't ideal. Have you considered other approaches? Wouldn't move it to validate() like it's suggesting, but possibly the deserialization suggestion if easy, or merging the global and local options on retrieval?
| /// For a detailed description, please see the [dispatch optimisation formulation][1]. | ||
| /// | ||
| /// [1]: https://energysystemsmodellinglab.github.io/MUSE2/model/dispatch_optimisation.html | ||
| #[must_use = "Must call run() method on DispatchRun struct"] |
There was a problem hiding this comment.
Didn't know about this before, good to know that it's a thing!
| let option = $option.as_str(); | ||
| let value = $value; | ||
|
|
||
| debug!("Setting HiGHS option \"{option}\" to \"{value}\""); |
There was a problem hiding this comment.
I can see the value in logging this, but shall we make this trace instead? I use debug level to see model progress and choices like investment decisions, and it would be annoying to clog up my console/log files with potentially thousands of these messages
Description
The HiGHS solver provides many options to change its behaviour, but there is currently no mechanism for changing them within MUSE2. While we don't want ordinary users to be mucking around with things like tolerances, it's useful for development to be able to tweak them to see what happens.
Add a new optional
[highs]section to themodel.tomlfile, which lets users pass through options directly to the solver. I thought it was worth letting users change things separately for dispatch and appraisal, as these are different optimisation problems, so there are actually three subsections to[highs]:[highs.global_options],[highs.dispatch_options]and[highs.appraisal_options]. Global options are applied to both optimisation types (for if e.g. you want to enable logging for HiGHS for both). As we can't be sure that the options changed won't do something odd, it's gated behind theplease_give_me_broken_resultssetting, like we do for other potentially dangerous things.An alternative way of implementing this would be to let users create a HiGHS config file then telling HiGHS to load this, but it seemed cleaner to just use the existing file we have for configuring models and not having to worry about another input format etc.
I've also added a short page to the docs to explain how to use this feature.
Closes #420.
Type of change
Key checklist
$ cargo test$ cargo docpresent in the previous release
Further checks