-
Notifications
You must be signed in to change notification settings - Fork 1
Use integer variables in appraisal for divisible assets #1075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1075 +/- ##
==========================================
+ Coverage 82.18% 82.26% +0.07%
==========================================
Files 53 53
Lines 7383 7416 +33
Branches 7383 7416 +33
==========================================
+ Hits 6068 6101 +33
Misses 1025 1025
Partials 290 290 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR modifies the appraisal optimization to use integer variables for divisible assets, ensuring that appraised capacities are valid multiples of the unit size. The change prevents unrealistic capacity recommendations (e.g., half a wind turbine) during initial appraisal of new assets.
Changes:
- Modified optimization to use unit count as the decision variable for divisible assets, with proper conversion between unit counts and total capacity
- Updated capacity constraints to handle unit-based variables, including division of capacity limits by unit size
- Adjusted activity constraints to account for unit-based capacity representation
- Added helper methods to Asset for converting between capacity and unit counts
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/simulation/investment/appraisal/optimisation.rs | Modified variable creation to use unit count for divisible assets and convert results back to total capacity |
| src/simulation/investment/appraisal/constraints.rs | Updated capacity and activity constraints to handle unit-based capacity variables |
| src/simulation/investment.rs | Added rounding of capacity limits to nearest unit size multiple for divisible assets |
| src/asset.rs | Added helper methods for capacity-to-units conversion and rounding capacity to unit size |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Sanity check: capacity_limit should be a whole number of units (i.e pre-adjusted | ||
| // capacity limit was a multiple of unit size) | ||
| assert!(approx_eq!(f64, capacity_limit, capacity_limit.round())); |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assertion can fail for commissioned assets with divisible processes whose capacity is not an exact multiple of the unit size. The code in src/input/asset.rs allows such assets to be loaded (with a warning), but when these assets are appraised, this assertion will panic. For commissioned assets, the capacity should be used as-is without requiring it to be a perfect multiple of unit_size, or the capacity should be rounded to the nearest unit count before the assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope. Assets in the input will get split into units by divide_asset prior to reaching this point
alexdewar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've suggested a couple of small stylistic things, but otherwise LGTM 😄
| // For divisible assets, round up to the nearest multiple of the process unit size | ||
| if asset.is_divisible() { | ||
| max_capacity = asset.round_capacity_to_unit_size(max_capacity); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This threw me when I first read it because I assumed it was rounding the capacity of the asset and didn't notice there was an extra argument.
I'm wondering if it might be clearer to have round_capacity_to_unit_size be a standalone function in this file instead. Also, it could handle the case of non-divisible assets by just returning the capacity argument rather than panicking. I think this might be more readable.
This is obvs a bit subjective, so up to you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to get rid of this in #1079 (if we agree to go down that route), so I'll leave it for now
Co-authored-by: Alex Dewar <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub fn capacity_to_units(&self, capacity: Capacity) -> u32 { | ||
| let unit_size = self.unit_size().expect("Asset must be divisible"); | ||
| #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] | ||
| { | ||
| (capacity / unit_size).value().ceil() as u32 | ||
| } | ||
| } |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new method capacity_to_units() lacks test coverage. Consider adding unit tests to verify correct behavior for edge cases like exact multiples, rounding up, and values smaller than unit_size.
| pub fn round_capacity_to_unit_size(&self, capacity: Capacity) -> Capacity { | ||
| let unit_size = self.unit_size().expect("Asset must be divisible"); | ||
| let n_units = self.capacity_to_units(capacity); | ||
| Capacity(unit_size.value() * n_units as f64) | ||
| } |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new method round_capacity_to_unit_size() lacks test coverage. Consider adding unit tests to verify correct rounding behavior for various capacity and unit_size combinations.
| 2,WNDFRM,GBR,A0_ELC,,2020,,3.964844 | ||
| 3,GASCGT,GBR,A0_ELC,,2020,,2.43 |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of decommission years (2040) for assets 2 and 3 is not explained in the PR description. If this change is intentional and related to the integer variable modifications, it should be documented; otherwise, it may be an unintended side effect.
| 2,WNDFRM,GBR,A0_ELC,,2020,,3.964844 | |
| 3,GASCGT,GBR,A0_ELC,,2020,,2.43 | |
| 2,WNDFRM,GBR,A0_ELC,,2020,2040,3.964844 | |
| 3,GASCGT,GBR,A0_ELC,,2020,2040,2.43 |
| 8,RELCHP,GBR,A0_RES,,2030,,255.83840587648046 | ||
| 9,GASCGT,GBR,A0_ELC,,2030,2040,3.1192651014219064 |
Copilot
AI
Jan 15, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assets 8 and 9 show updated capacities and different asset types/IDs compared to the original data. The change from RGASBR to RELCHP for asset 8 and the significant capacity changes (1000.0 to 255.83840587648046) should be verified as intentional outcomes of the integer optimization.
Description
This PR modifies the appraisal optimisation to use integer variables for divisible assets, to ensure that capacities are valid multiples of the unit size.
This was a bit fiddly as there was lots of conversion between capacities in absolute terms and capacities as unit counts, but I think it's all correct. I also had to round capacity limits up to the nearest multiple of the unit size to ensure that sufficient capacity can be installed.
Hopefully once we've got a better way of representing divisible assets we'll be able to tidy this up a bit. I didn't want to polish this too much until then, hence the lack of tests etc.
Fixes #1044 I guess this is enough to close the issue. There are some more points in there, but my main issue was that we were appraising unrealistic capacities (e.g. half a wind turbine), which this does fix. Note, this only affects initial appraisal for commissioning new assets - when it comes to reappraisal assets will have already been split into individual units which are appraised individually.
Type of change
Key checklist
$ cargo test$ cargo docpresent in the previous release
Further checks