fix: amortize installation impacts over a unit's lifetime output - #61
Merged
Merged
Conversation
An installed unit's capacity was treated as available in every year of its operation window, so one unit could deliver its whole lifetime production each year. Installation-dependent flows (construction, end-of-life) were therefore spread over too much output, under-counting them by the number of operating years a vintage was dispatched. Production and operation-dependent flows are now taken at the vintage's current lifecycle stage tau = t - v, and the capacity bound compares unit counts: var_operation[p, v, t] <= var_installation[p, v]. Postprocessing capacity outputs report annual capacity, so they stay comparable with production; get_installation returns unit counts, which are a lifetime quantity. Docstrings, docs and changelog state the convention explicitly.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
langeluca
added a commit
to langeluca/optimex
that referenced
this pull request
Aug 1, 2026
…n-fix Integrates RWTH-LTT#61, which fixes the lifetime installation-impact accounting for multi-year processes. Previously, production and operation-dependent flows were summed over the complete operation window in every calendar year. As a result, one installed unit could incorrectly deliver its entire lifetime production each year. This understated the required number of installed units and therefore undercounted construction and other installation-dependent impacts by up to the number of operating years. Resolves merge conflicts in CHANGES.md and lca_processor.py while retaining both the upstream background-activity lookup improvements and the fork's economic CAPEX/OPEX extensions. Migrates the ethylene case study to the new capacity semantics: existing_capacity now represents process units. The assumed annual Steam Cracker capacity remains 0.5e9 kg ethylene per vintage and is converted to 25e9 process units for a 50-year lifetime. Validation: - 78 tests passed and 3 were skipped - HiGHS was used locally because GLPK was unavailable - Notebook JSON and the brownfield conversion identity were statically verified
Resolve conflicts against the v0.6.0 performance refactor (#64): - optimizer.py: main moved the flow and production rates into cached helpers (`flow_rate`, `production_rate`) that summed the tensor over the whole operation window. Both now take the lifecycle stage `tau` and return the per-tau entry, i.e. the flow/output per operating unit and year; the cache key carries `tau`. Call sites pass `t - v`. - optimizer.py: `OperationCapacity` keeps the PR's unit-count form and is indexed by ACTIVE_VINTAGE_TIME only, so `get_production_value` is now unused and removed. - tests/test_single_route_lca_comparison.py: main rewrote the file with LF endings and added `test_background_inventory_handles_squeezed_solver_result`, so the whole file conflicted; rebuilt as main's version plus the PR's restated single-year test and new multi-period test. - CHANGES.md: PR entries moved to [Unreleased] above the released 0.6.0.
TimoDiepers
force-pushed
the
fix/lifetime-installation-semantics
branch
from
August 20, 2026 16:58
8a9a505 to
48e8d51
Compare
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
An installed unit's capacity was treated as available in every year of its operation window, so one unit could deliver its entire lifetime production each year. Installation-dependent flows (construction, end-of-life) were spread over too much output and under-counted by the number of operating years a vintage was dispatched — 16x for a 16-year process, and so on. Only single-year operation windows were correct.
Fix
The unit convention is now explicit: one installed unit delivers the production of its production exchange over its whole lifetime, so the per-process-time entry is the output of one running unit per year.
tau = t - vinstead of summed over the operation window.OperationCapacitycompares unit counts:var_operation[p, v, t] <= var_installation[p, v].get_production_capacity,plot_capacity_balance,plot_utilization_heatmap) report annual capacity, comparable with production.get_installationreturns unit counts, a lifetime quantity.Impact on results
Optimization results change for any process with a multi-year operation window; construction-heavy technologies are affected most. Models with a single-year window are unaffected.
Note for existing setups:
existing_capacityand deployment limits are in process units, so values calibrated as an annual capacity now need multiplying by the number of operating years.Tests
Test expectations restated in unit counts.
test_single_route_lca_comparison.pygains a multi-period demand case that pins optimex tobc.LCAexactly when every unit is fully used, plus a case documenting that a single demand year legitimately strands capacity. Full suite passes.