Repository navigation
Maximize Sizing Below a Specified Payback Period - #640
Draft
Bill-Becker wants to merge 9 commits into
Draft
Bill-Becker wants to merge 9 commits into
Bill-Becker wants to merge 9 commits into
Conversation
…puts and Techs.maximize_size field
…imizing incentive
…_before_tax_model in Financial results
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical BAU workflow and sizing-maximization issues remain, and multinode support does not enforce the requested constraint.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 3
Open (4)
What changed in this PR
Adds maximum simple-payback constraints, technology sizing incentives, financial reporting, and related tests.
Changes:
- Adds payback configuration and eligible-technology tracking.
- Enforces payback limits and sizing incentives.
- Updates multinode handling, reporting, and tests.
| File | Summary |
|---|---|
test/runtests.jl |
Adds payback sizing tests. |
src/results/financial.jl |
Reports payback-related financial values. |
src/core/types.jl |
Adds maximization metadata. |
src/core/techs.jl |
Identifies eligible technologies. |
src/core/reopt.jl |
Implements payback constraints and incentives; BAU handling, capex selection, and maximization logic require changes. |
src/core/reopt_multinode.jl |
Multinode models currently ignore the payback constraint and incentive. |
src/core/financial.jl |
Adds payback inputs; BAU fields must be cleared and inputs documented. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| min_initial_capital_costs_before_incentives::Union{Nothing,Real} = nothing, | ||
| max_initial_capital_costs_before_incentives::Union{Nothing,Real} = nothing, | ||
| max_simple_payback_years::Union{Nothing,Real} = nothing, # find max size/production of maximize_size techs while keeping simple payback <= this value | ||
| bau_year_one_operating_cost::Union{Nothing,Real} = nothing, # BAU results["Financial"]["year_one_operating_cost_before_tax_model"] (from a prior BAUInputs solve), required if max_simple_payback_years is set |
Comment on lines
+531
to
+533
| if !isnothing(p.s.financial.max_simple_payback_years) | ||
| if isnothing(p.s.financial.bau_year_one_operating_cost) | ||
| throw(@error("financial.bau_year_one_operating_cost must be set (from a prior BAU run's value(m[:Year1OperatingCost]), see results[\"Financial\"][\"year_one_operating_cost_before_tax_model\"]) to constrain max_simple_payback_years.")) |
Comment on lines
+551
to
+555
| m[:MaximizeProductionIncentive] = @expression(m, | ||
| -1.0e4 * ( | ||
| sum(m[:dvSize][t] for t in p.techs.maximize_size; init=0.0) + | ||
| sum(m[:dvStoragePower][b] + m[:dvStorageEnergy][b] for b in storage_to_maximize; init=0.0) | ||
| ) |
Contributor
There was a problem hiding this comment.
Fixed in 93ff1e9 with a lexicographic maximize-then-minimize solve.
Comment on lines
+129
to
+130
| ex_name = "MaximizeProductionIncentive"*_n | ||
| m[Symbol(ex_name)] = 0 |
Contributor
There was a problem hiding this comment.
Fixed in 93ff1e9 by rejecting financial.max_simple_payback_years in multinode solves.
Co-authored-by: Bill-Becker <42586683+Bill-Becker@users.noreply.github.com>
…ost minimization to size duration/energy
This branch has not been deployed
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.


This pull request introduces a new feature to constrain technology sizing based on a maximum simple payback period, along with supporting changes throughout the codebase. The main changes include extending the financial model to support a
max_simple_payback_yearsconstraint, updating technology selection logic, adding a lexicographic (maximize-size, then minimize-lifecycle-cost) solve, and updating results reporting and testing.Financial model and tech selection enhancements:
max_simple_payback_yearsandbau_year_one_operating_costfields to theFinancialstruct, enabling users to specify a maximum allowed simple payback period for new technologies.bau_year_one_operating_costcomes from a prior BAU solve'sresults["Financial"]["year_one_operating_cost_before_tax_model"]and is required whenevermax_simple_payback_yearsis set. (src/core/financial.jl)Techsstruct and related logic to include amaximize_sizefield, which tracks technologies eligible for maximization under the payback constraint (all techs exceptExistingBoiler/ExistingChiller). (src/core/types.jl,src/core/techs.jl)BAUScenarionow clears bothmax_simple_payback_yearsandbau_year_one_operating_cost, so the BAU reference solve is never itself payback-constrained. (src/core/bau_scenario.jl)Model building (
build_reopt!):Year1OperatingCostexpression (elec bill, CHP standby charges, fixed/variable/per-hour O&M, GHP and storage O&M, and fuel costs) the same way regardless of whethermax_simple_payback_yearsis set, so a BAU solve'svalue(m[:Year1OperatingCost])is directly comparable to the optimal model's.max_simple_payback_yearsis set, addsPaybackSavings(BAU year-one operating cost minus the model's year-one operating cost) and constrainsInitialCapexNoIncentives <= max_simple_payback_years * PaybackSavings, with a small absolute tolerance so floating-point noise can't turn a satisfiable case (e.g. all new techs at size 0) into a false infeasibility.MaximizeSizeObjectiveexpression overdvSizefortechs.maximize_sizeplusdvStoragePower/dvStorageEnergyfor all storage when the payback constraint is active. It uses sizes (not production) so it behaves correctly for both intermittent (PV, Wind) and dispatchable techs.build_reopt!no longer adds any size-maximizing term to theCostsobjective expression — the previous-1e4 * sizepenalty-style incentive insideCostswas removed in favor of the lexicographic solve below.MaximizeProductionIncentiveis retained as0.0so existing results/objective plumbing is unchanged.Lexicographic solve (
optimize_reopt!):optimize_reopt!, used byrun_reopt(m, p), replaces the singleoptimize!call:max_simple_payback_yearsis set and there is anything to maximize, maximizeMaximizeSizeObjectivesubject to the payback constraint, then pin it withMaximizeSizeObjective >= max_size - tol.Costs + ObjectivePenaltiesobjective while holding that maximum size.opt_time.run_reopt(m, ps)now throws an explicit error if any node setsmax_simple_payback_years, since the feature is not supported for multinode solves. (src/core/reopt_multinode.jl)Results:
Financialresults reportyear_one_operating_cost_before_tax_model(the model's undiscounted year-one operating cost, used to seedbau_year_one_operating_cost) and retainmaximize_tech_size_incentive_value(now always0.0). (src/results/financial.jl)Testing:
max_simple_payback_yearsthreshold behavior covering unconstrained, loose, and strict payback scenarios, thatBAUScenarioclears the payback inputs, and a high-lifecycle-cost case (1-year analysis, expensive PV) where the LCC-optimal size is 0 but the lexicographic solve still sizes PV up to a ~9-year payback. (test/runtests.jl)