diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4d05b91..b084c2d4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ Classify the change according to the following categories: ### Deprecated ### Removed +## Develop 2024-01-16 +### Fixed +- In `reopt.jl`, group objective function incentives and avoid directly modifying m[:Costs]. Previously, some of these were incorrectly included in the reported **Financial.lcc** + ## v0.39.1 ### Changed - Changed testing suite from using Xpress to using HiGHS, an open-source solver. This has led to a reduction in the number of tests due to incompatibility with indicator constraints. diff --git a/src/core/reopt.jl b/src/core/reopt.jl index 17d2b1082..7850eadcb 100644 --- a/src/core/reopt.jl +++ b/src/core/reopt.jl @@ -247,6 +247,7 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) m[:GHPOMCosts] = 0.0 m[:AvoidedCapexByGHP] = 0.0 m[:ResidualGHXCapCost] = 0.0 + m[:ObjectiveIncentives] = 0.0 if !isempty(p.techs.all) add_tech_size_constraints(m, p) @@ -441,12 +442,7 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) m[:TotalElecBill] * (1 - p.s.financial.offtaker_tax_rate_fraction) - # Subtract Incentives, which are taxable - m[:TotalProductionIncentive] * (1 - p.s.financial.owner_tax_rate_fraction) + - - # Comfort limit violation costs - #TODO: add this to objective like SOC incentive below and - #don't then subtract out when setting lcc in results/financial.jl - m[:dvComfortLimitViolationCost] + + m[:TotalProductionIncentive] * (1 - p.s.financial.owner_tax_rate_fraction) + # Additional annual costs, tax deductible for owner (only applies when `off_grid_flag` is true) p.s.financial.offgrid_other_annual_costs * p.pwf_om * (1 - p.s.financial.owner_tax_rate_fraction) + @@ -470,28 +466,23 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) add_to_expression!(Costs, m[:Lifecycle_Emissions_Cost_Health]) end - @expression(m, Objective, - m[:Costs] - ) - + ## Modify objective with incentives that are not part of the LCC + # 1. Comfort limit violation costs + m[:ObjectiveIncentives] += m[:dvComfortLimitViolationCost] + # 2. Incentive to keep SOC high if !(isempty(p.s.storage.types.elec)) && p.s.settings.add_soc_incentive - # Incentive to keep SOC high - add_to_expression!( - Objective, - - sum( + m[:ObjectiveIncentives] += -1 * sum( m[:dvStoredEnergy][b, ts] for b in p.s.storage.types.elec, ts in p.time_steps ) / (8760. / p.hours_per_time_step) - ) end + # 3. Incentive to minimize unserved load in each outage, not just the max over outage start times if !isempty(p.s.electric_utility.outage_durations) - # Incentive to minimize unserved load in each outage, not just the max over outage start times - add_to_expression!( - Objective, - sum(sum(0.0001 * m[:dvUnservedLoad][s, tz, ts] for ts in 1:p.s.electric_utility.outage_durations[s]) for s in p.s.electric_utility.scenarios, tz in p.s.electric_utility.outage_start_time_steps) - ) + m[:ObjectiveIncentives] += sum(sum(0.0001 * m[:dvUnservedLoad][s, tz, ts] for ts in 1:p.s.electric_utility.outage_durations[s]) + for s in p.s.electric_utility.scenarios, tz in p.s.electric_utility.outage_start_time_steps) end - @objective(m, Min, m[:Objective]) + # Set model objective + @objective(m, Min, m[:Costs] + m[:ObjectiveIncentives] ) # if !(isempty(p.s.storage.types.elec)) && p.s.settings.add_soc_incentive # Keep SOC high # @objective(m, Min, m[:Costs] - diff --git a/src/results/financial.jl b/src/results/financial.jl index ef5f5e7c5..6d17db100 100644 --- a/src/results/financial.jl +++ b/src/results/financial.jl @@ -38,9 +38,6 @@ calculated in combine_results function if BAU scenario is run: """ function add_financial_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _n="") r = Dict{String, Float64}() - if !(Symbol("dvComfortLimitViolationCost"*_n) in keys(m.obj_dict)) - m[Symbol("dvComfortLimitViolationCost"*_n)] = 0.0 - end if !(Symbol("TotalProductionIncentive"*_n) in keys(m.obj_dict)) # not currently included in multi-node modeling b/c these constraints require binary vars. m[Symbol("TotalProductionIncentive"*_n)] = 0.0 end @@ -54,7 +51,8 @@ function add_financial_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _ m[Symbol("GHPCapCosts"*_n)] = 0.0 end - r["lcc"] = value(m[Symbol("Costs"*_n)]) + 0.0001 * value(m[Symbol("MinChargeAdder"*_n)]) - value(m[Symbol("dvComfortLimitViolationCost"*_n)]) + r["lcc"] = value(m[Symbol("Costs"*_n)]) + 0.0001 * value(m[Symbol("MinChargeAdder"*_n)]) + r["lifecycle_om_costs_before_tax"] = value(m[Symbol("TotalPerUnitSizeOMCosts"*_n)] + m[Symbol("TotalPerUnitProdOMCosts"*_n)] + m[Symbol("TotalPerUnitHourOMCosts"*_n)] + m[Symbol("GHPOMCosts"*_n)])