Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8a2b682
Store remaining_units count for investments
tsmbland Aug 12, 2026
ebeee58
Remove AssetCapacity::Continuous
tsmbland Aug 12, 2026
d1eb51c
Fix sign error
tsmbland Aug 12, 2026
415b23b
Update comments
tsmbland Aug 12, 2026
3adc3ba
Remove candidate assets whose investment limit cannot fund one comple…
tsmbland Aug 12, 2026
b3818ea
Fix test compile issues
tsmbland Aug 12, 2026
02832fd
Fix partial_cmp
tsmbland Aug 12, 2026
1f32d9d
Fix most other tests
tsmbland Aug 12, 2026
ec47667
AssetCapacity::single
tsmbland Aug 12, 2026
b1abd33
Fix final unit test
tsmbland Aug 12, 2026
57722ba
Remove "divisible" terminology
tsmbland Aug 12, 2026
22aa0db
Add tests for AssetCapacity::new
tsmbland Aug 12, 2026
0b23a81
Small cleanups
tsmbland Aug 12, 2026
b9733e7
Fix old comment
tsmbland Aug 12, 2026
13655af
More small tidy-ups
tsmbland Aug 12, 2026
42931f7
Update schemas
tsmbland Aug 12, 2026
ba86143
Update docs
tsmbland Aug 12, 2026
6e8d98c
Fixes from review
tsmbland Aug 13, 2026
744277b
Restore check for candidate_asset_capacity
tsmbland Aug 13, 2026
0085357
Extra check for assets file
tsmbland Aug 13, 2026
c2df880
Update results
tsmbland Aug 13, 2026
602ac95
Renamed simple_divisible model
tsmbland Aug 14, 2026
8e11b91
Merge branch 'main' into remove_nondivisible_assets
tsmbland Aug 19, 2026
3040d0f
Better docstring for update_assets
tsmbland Aug 19, 2026
45eb8c1
Merge branch 'main' into remove_nondivisible_assets
tsmbland Aug 19, 2026
7b3cf98
Link to mothballing section
tsmbland Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/developer_guide/architecture_quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ differences in floating-point values in the output files.
If the model is a [patched example], then you need to pass the `--patch` flag, e.g.:

```sh
just regenerate_test_data --patch simple_divisible
just regenerate_test_data --patch simple_unit_size
```

[`log`]: https://docs.rs/log
Expand Down
71 changes: 27 additions & 44 deletions docs/model/investment.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,59 +103,42 @@ The annualised fixed cost (AFC) per unit of capacity differs between the two cat

If \\( d = 0 \\), then \\( \mathrm{CRF} = 1/L \\).

## Asset Capacity
## Asset Capacities

A process is either **divisible** or **non-divisible**:
Every asset consists of one or more equal-capacity units. A single-unit asset is retained or
mothballed as a whole, while the units of a multi-unit asset can be retained or mothballed
Comment thread
dc2917 marked this conversation as resolved.
independently (see [Mothballing and Decommissioning](#mothballing-and-decommissioning)).

- A **divisible** process has a fixed `unit_size` (defined in [`processes.csv`][processes-csv]).
Assets of this type consist of one or more discrete units, each of size `unit_size`. When
commissioned, a divisible asset is split into individual units, each of which is appraised and
retained or mothballed independently.
- A **non-divisible** process has no `unit_size`. Assets of this type are treated as a single
entity: capacities may take any value within their allowable range, but cannot be split into
independently appraised or mothballed units.
- For assets defined in `assets.csv`, an explicitly supplied `num_units` determines the unit size.
Otherwise, a process `unit_size` determines the unit size. If neither is supplied, the asset
consists of one unit with its full capacity.
- Assets invested in _by MUSE_ will use the process `unit_size`, if defined, or will use a capacity
based on demand at the time of investment (see "trial capacity" below).

### Existing assets

- **Non-divisible**: the asset is appraised as a whole at its full installed capacity.
- **Divisible**: each individual unit is appraised separately, one at a time. This allows partial
retention — for example, some units of a multi-unit plant may be retained while others are
mothballed.
Existing assets (i.e assets that have already been commissioned, whether via `assets.csv` or by
MUSE) are appraised one unit at a time to decide how many units to retain. This allows partial
retention — for example, some units of a multi-unit plant may be retained while others are
mothballed.
Comment on lines +120 to +123

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many to retain, and how many to installed, as it affects the remaining_units (see my comment above), right? I'm a bit confused about this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For existing assets (i.e. already installed), this is about how many to retain. For candidate assets (below section), this is about how many to install

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your comment makes it very clear, but I think that same explanation should be in the code. If remaining_units means different things depending on the case, that should be clarified.


### Candidate assets

Before a candidate asset can be appraised, it is assigned a trial capacity which defines how much
capacity can be installed in a single investment round (subject to further
[demand-limiting capacity](#demand-limiting-capacity-dlc) and
[investment constraints](#investment-constraints), described below)
Before a candidate asset for new investment can be appraised, it is assigned a trial capacity which
defines how much capacity can be installed in a single investment round.

- **Divisible**: the trial capacity is set to one unit (one `unit_size`), representing a single
unit being considered for investment.
- **Non-divisible**: the trial capacity is based on the capacity that would satisfy the
total remaining demand if the asset operated at its maximum annual rate:

\\[
\mathrm{TrialCapacity} = \frac{\sum_t \mathrm{Demand}_t}{\mathrm{MaxAnnualSupplyPerCapacity}}
\times \mathrm{CapacityLimitFactor}
\\]

`capacity_limit_factor` (set in [`model.toml`][model-toml], must be > 0 and <= 1) controls the
size of investment increments relative to total demand. Lower values produce smaller investment
increments (requiring more investment rounds), while higher values produce larger increments.

### Demand-limiting capacity (DLC)

In each investment round, a candidate's trial capacity is further capped by the
*demand-limiting capacity*, which is the minimum capacity required to satisfy the remaining demand
across all time-slice selections:
If a process has a defined `unit_size`, the trial capacity is set to one unit. Otherwise, it
calculated based on the capacity that would satisfy the total remaining demand if the asset operated
at its maximum annual rate:

\\[
\mathrm{DLC} = \max_{\mathrm{selection}} \frac{\sum_{t \in \mathrm{selection}} \mathrm{Demand}\_t}
{\mathrm{MaxSupplyPerCapacity}_{\mathrm{selection}}}
\mathrm{TrialCapacity} = \frac{\sum_t \mathrm{Demand}_t}{\mathrm{MaxAnnualSupplyPerCapacity}}
\\times \mathrm{CapacityLimitFactor}
\\]

Selections where the asset has zero maximum supply are excluded. The cap prevents over-investment
(i.e. building more capacity than needed to meet remaining demand).
`capacity_limit_factor` (set in [`model.toml`][model-toml], must be > 0 and <= 1) controls the
size of investment increments relative to total demand. Lower values produce smaller investment
increments (requiring more investment rounds), while higher values produce larger increments.

### Investment constraints

Expand Down Expand Up @@ -325,9 +308,10 @@ terminates with an error.

## Mothballing and Decommissioning

After investment is complete for a given MSY, any previously commissioned assets that were not
selected for retention are *mothballed*: their mothball year is recorded and they are removed from
the active asset pool. They remain available for potential re-selection in future MSYs.
After investment is complete for a given MSY, any previously commissioned assets (or individual
units making up the asset) that were not selected for retention are *mothballed*: their mothball
year is recorded and they are removed from the active asset pool. They remain available for
potential re-selection in future MSYs.
Comment on lines +311 to +314

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that MSY is milestone year, which does not feel much longer than MSY and it is way clearer...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair!


A mothballed asset that remains unused for `mothball_years` consecutive years (as defined in
[`model.toml`][model-toml]) is *decommissioned* — permanently removed from the asset pool and
Expand Down Expand Up @@ -495,6 +479,5 @@ assets shaping the demand seen by those upstream.
[framework-overview]: index.html#framework-overview
[prices]: ./prices.md
[model-toml]: ../file_formats/input_files.md#model-parameters-modeltoml
[processes-csv]: ../file_formats/input_files.md#processescsv
[process-investment-constraints-csv]: ../file_formats/input_files.md#process_investment_constraintscsv
[dispatch-optimisation]: ./dispatch_optimisation.md
9 changes: 6 additions & 3 deletions schemas/input/assets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ fields:
type: integer
description: The number of units comprising the asset
notes: |
Optional. For processes with a `unit_size`, if omitted it is calculated by rounding the
capacity up to the nearest whole unit. For processes without a `unit_size`, if provided it
determines the unit size; otherwise the asset is represented as one unit. Must be >0.
Optional. If provided, the asset is split into `num_units` units of size

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as in previous PR :)

`capacity / num_units` (taking precedence over the process's `unit_size`). If omitted and the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`capacity / num_units` (taking precedence over the process's `unit_size`). If omitted and the
`capacity / num_units` (taking precedence over the process' `unit_size`). If omitted and the

Couldn't help myself 😅

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think because I'm referring to one particular process (i.e. the process the asset belongs to), this is correct? Not an expert though!

process has a `unit_size`, the asset is divided into `ceil(capacity / unit_size)` units (note
that rounding up the number of units can result in a total capacity greater than the specified
Comment thread
dc2917 marked this conversation as resolved.
capacity). If both values are omitted, the asset is represented as one unit with the full
asset capacity. Must be >0 if provided.
- name: commission_year
type: integer
description: The year in which to commission this asset
Expand Down
16 changes: 5 additions & 11 deletions schemas/input/processes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,9 @@ fields:
notes: Must be >0. Optional (defaults to 1.0).
- name: unit_size
type: number
description:
Capacity of the units in which an asset for this process will be divided into when
commissioned, if any.
description: Default capacity of the individual units making up assets of this process
notes:
If present, must be >0. Optional (defaults to None). Assets with a defined unit size are
divided into n = ceil(C / U) equal units, where C is overall capacity and U is unit_size
(i.e. rounding up the number of units, which may result in a total capacity greater than C, if
C is not an exact multiple of U).

Note that divisible assets are appraised one unit at a time, so if this number is much smaller
than the typical size of an asset, this will potentially lead to many rounds of appraisal,
with an adverse effect on performance.
Optional (defaults to None). If present, must be >0. If no unit size is specified, the unit
size used for investments is calculated based on demand at the time of investment and
`capacity_limit_factor`. For a user-provided asset, `num_units` in `assets.csv` can instead
determine the unit size.
6 changes: 1 addition & 5 deletions schemas/output/asset_capacities.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ fields:
- name: capacity
type: number
description: The total capacity of the asset in this year
notes: |
For divisible assets, this is the total capacity across all units.
- name: num_units
type: integer
description: The number of active units making up the asset in this year
notes: |
For non-divisible assets, this is always blank.
description: The number of units making up the asset in this year
Loading
Loading