Skip to content

Change LCOX to use same optimisation as NPV#1319

Draft
alexdewar wants to merge 6 commits into
mainfrom
new-lcox-optimisation
Draft

Change LCOX to use same optimisation as NPV#1319
alexdewar wants to merge 6 commits into
mainfrom
new-lcox-optimisation

Conversation

@alexdewar
Copy link
Copy Markdown
Member

@alexdewar alexdewar commented May 28, 2026

Description

The following models are currently failing to run to completion:

Examples:
circularity: FAILED
missing_commodity: succeeded
muse1_default: FAILED
simple: succeeded
two_outputs: succeeded
two_regions: FAILED

Patched examples:
simple_divisible: succeeded
simple_full: succeeded
simple_full_average: succeeded
simple_ironing_out: succeeded
simple_marginal: FAILED
simple_marginal_average: FAILED
simple_npv: succeeded

Closes #1199.

Type of change

  • Bug fix (non-breaking change to fix an issue)
  • New feature (non-breaking change to add functionality)
  • Refactoring (non-breaking, non-functional change to improve maintainability)
  • Optimization (non-breaking change to speed up the code)
  • Breaking change (whatever its nature)
  • Documentation (improve or add documentation)

Key checklist

  • All tests pass: $ cargo test
  • The documentation builds and looks OK: $ cargo doc
  • Update release notes for the latest release if this PR adds a new feature or fixes a bug
    present in the previous release

Further checks

  • Code is commented, particularly in hard-to-understand areas
  • Tests added that prove fix is effective or that feature works

Copilot AI review requested due to automatic review settings May 28, 2026 14:25
@alexdewar alexdewar marked this pull request as draft May 28, 2026 14:29
Copy link
Copy Markdown
Contributor

Copilot AI left a 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 refactors the LCOX investment appraisal so that it uses the same optimisation formulation as the NPV appraisal (maximise activity surplus with fixed capacity equal to the supplied max_capacity), instead of the previous LCOX-specific formulation that minimised annualised cost with capacity as a decision variable and a VoLL-priced unmet-demand term. LCOX-specific per-time-slice costs are now computed separately and used only for the final LCOX metric calculation. This addresses issue #1199 where the old LCOX approach overinvested capacity and relied on VoLL to clear demand.

Changes:

  • Removed the capacity decision variable, the unmet-demand objective penalty, and the LCOX-specific Sense::Minimise path from the appraisal optimisation; both LCOX and NPV now call a single shared perform_optimisation that maximises activity surplus.
  • Restructured ObjectiveCoefficients to hold a shared activity_coefficients map plus an optional lcox_costs map; calculate_coefficients_for_assets now always builds the activity coefficients via a single helper and fills lcox_costs only when the objective is LCOX.
  • Propagated removal of capacity/unmet-demand coefficients through callers (appraisal.rs, output.rs, fixture.rs), so AppraisalOutput.capacity is now simply the supplied max_capacity and the LCOX metric uses annual_fixed_cost(asset) directly.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/simulation/investment/appraisal/optimisation.rs Drops capacity variable, hard-codes Sense::Maximise, and remaps solution columns to activity + unmet-demand only.
src/simulation/investment/appraisal/constraints.rs Removes add_capacity_constraint and candidate-specific activity constraints; activity bounds are now derived from max_capacity.total_capacity().
src/simulation/investment/appraisal/coefficients.rs Unifies coefficient calculation; adds lcox_costs and removes capacity_coefficient/unmet_demand_coefficient.
src/simulation/investment/appraisal.rs calculate_lcox/calculate_npv now share the same optimisation call and use max_capacity directly for metrics.
src/output.rs Removes capacity_coefficient column from appraisal debug output.
src/fixture.rs Updates test fixture to the new ObjectiveCoefficients shape.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 13 to 24
/// Map storing cost coefficients for an asset.
///
/// These coefficients are calculated according to the agent's `ObjectiveType` and are used by
/// the investment appraisal routines. The map contains the per-capacity and per-activity cost
/// coefficients used in the appraisal optimisation, together with the unmet-demand penalty.
#[derive(Clone)]
pub struct ObjectiveCoefficients {
/// Cost per unit of capacity
pub capacity_coefficient: MoneyPerCapacity,
/// Cost per unit of activity in each time slice
pub activity_coefficients: IndexMap<TimeSliceID, MoneyPerActivity>,
/// Unmet demand coefficient
pub unmet_demand_coefficient: MoneyPerFlow,
/// Costs for LCOX
pub lcox_costs: IndexMap<TimeSliceID, MoneyPerActivity>,
}
Comment on lines +55 to +56
/// activity coefficients so that assets with near-zero net value still appear in dispatch. Capacity
/// costs and unmet-demand penalties are set to zero.
use crate::agent::ObjectiveType;
use crate::asset::AssetRef;
use crate::model::Model;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Change the way LCOX appraisal tool works

2 participants