-
Notifications
You must be signed in to change notification settings - Fork 2
Group commissioned divided assets under parent assets #1121
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
base: main
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1121 +/- ##
==========================================
+ Coverage 87.54% 87.57% +0.02%
==========================================
Files 55 55
Lines 7460 7500 +40
Branches 7460 7500 +40
==========================================
+ Hits 6531 6568 +37
- Misses 631 633 +2
- Partials 298 299 +1 ☔ 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 introduces “parent” assets for commissioned divided assets to enable grouping in downstream optimisation (per #1043/#1100), while continuing to store only the child assets in the active AssetPool. It also introduces controlled interior mutability for Asset.capacity so parent capacity can be updated as child units are decommissioned.
Changes:
- Add
AssetState::Parentand storeparent: Option<AssetRef>on commissioned child assets; derivegroup_idfrom the parent. - Make
Asset.capacitymutable viaCell<AssetCapacity>and decrement parent unit count when a child asset is decommissioned. - Add
Asset::total_capacity()and update call sites; add Clippy configuration to ignore interior mutability forAssetRef.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
src/asset.rs |
Adds parent/child relationships for divided assets, switches capacity storage to Cell, updates commissioning/decommissioning and tests. |
src/simulation/investment.rs |
Switches capacity logging to use Asset::total_capacity(). |
src/output.rs |
Outputs asset capacity via Asset::total_capacity(). |
clippy.toml |
Configures Clippy to ignore interior mutability warnings for AssetRef. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for child in children.clone() { | ||
| let mut count = 0; | ||
| let mut total_child_capacity = Capacity(0.0); | ||
| asset.clone().into_for_each_child(&mut 0, |parent, child| { |
Copilot
AI
Feb 10, 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 test won’t compile because &mut 0 attempts to take a mutable reference to an integer literal. Introduce a let mut next_group_id = 0u32; variable and pass &mut next_group_id instead.
| asset.clone().into_for_each_child(&mut 0, |parent, child| { | |
| let mut next_group_id = 0u32; | |
| asset.clone().into_for_each_child(&mut next_group_id, |parent, child| { |
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.
...and yet it compiles 😆. You wouldn't get away with this in C/C++ though...
|
|
||
| let asset = AssetRef::from(asset); | ||
| let mut count = 0; | ||
| asset.clone().into_for_each_child(&mut 0, |parent, child| { |
Copilot
AI
Feb 10, 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.
Same issue as above: &mut 0 is an invalid mutable reference to a literal and will fail to compile. Use a mutable local u32 for next_group_id.
| // Create a child of size `unit_size` | ||
| let child = AssetRef::from(Asset { | ||
| capacity: Cell::new(AssetCapacity::Discrete(1, unit_size)), | ||
| ..Asset::clone(&self) | ||
| }); | ||
|
|
||
| // Turn this asset into a parent | ||
| let agent_id = self.agent_id().unwrap().clone(); | ||
| self.make_mut().state = AssetState::Parent { | ||
| agent_id, | ||
| group_id: AssetGroupID(*next_group_id), | ||
| }; | ||
| *next_group_id += 1; | ||
|
|
||
| // Run `f` over each child | ||
| for child in iter::repeat_n(child, n_units as usize) { | ||
| f(Some(&self), child); | ||
| } |
Copilot
AI
Feb 10, 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.
iter::repeat_n(child, n_units) repeats the same AssetRef (shared Rc) and relies on downstream code calling make_mut() to force clones. If the closure ever stores/uses children without mutating them, all “children” will alias the same underlying asset. Consider constructing a fresh AssetRef inside the loop (or cloning the underlying Asset) so each child is distinct by construction and doesn’t depend on Rc::make_mut semantics.
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 did this on purpose. It would be fine if a caller stored the identical child assets anyway though, I think!
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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 4 out of 4 changed files in this pull request and generated 2 comments.
💡 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 4 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Hash an asset according to its state: | ||
| /// - Commissioned assets are hashed based on their ID alone | ||
| /// - Selected assets are hashed based on `process_id`, `region_id`, `commission_year` and `agent_id` | ||
| /// - Selected assets are hashed based on `process_id`, `region_id`, `commission_year` and | ||
| /// `agent_id` | ||
| /// - Candidate assets are hashed based on `process_id`, `region_id` and `commission_year` | ||
| /// - Parent assets are hashed based on `agent_id` and `group_id` | ||
| /// - Future and Decommissioned assets cannot currently be hashed |
Copilot
AI
Feb 10, 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 Hash impl doc comment doesn’t match the implementation: Candidate, Selected, and Parent all hash process.id, region_id, commission_year, agent_id(), and group_id(), but the bullets describe different field sets (e.g. "Parent assets are hashed based on agent_id and group_id"). Please update the comment to reflect the current hashing logic.
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.
Sort of, except that they don't all have these fields, in which case the result is None. I don't think it needs amending.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
039dc1a to
5b9736a
Compare
Aurashk
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.
As we discussed, I feel uneasy already about how many distinct forms an Asset can take through the AssetState, I would prefer a separate ParentAsset struct even if it repeats a lot of the Asset struct (which could be simplified later on). It looks like you can adapt AssetPool with a separate field Vec[ParentAsset] and handle commissions with a dedicated function. That being said I'm approving this and will leave up to your judgement whether that would fit well into the wider refactoring for divisible assets.
Description
Currently divisible assets are broken into small individual assets when commissioned, which is messy and bad for performance. As a step on the road to replacing these many sub-assets with a single super-asset representing all of them, I've added parent assets to divided assets (commissioned ones only). This will allow us to use parent assets for variables in the dispatch optimisation (#1043). We still store the individual child assets in the
AssetPool, not the parents.As parent assets need to have their capacity updated as children are decommissioned, I've made the
capacityfield mutable by wrapping it in aCell(this is called "interior mutability" in Rust-speak). As this is potentially dangerous, however, there's only one private method which allows for changing the capacity of an immutableAssetand it's only used for parent assets.In the longer run, we will be able to get rid of child assets altogether and just have parents. There are a few other issues we need to figure out first, though (e.g. #1042).
Closes #1100.
Type of change
Key checklist
$ cargo test$ cargo docpresent in the previous release
Further checks