Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid modifying LCC with Obj Func Incentives #345

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (into **ObjectivePenalties**) 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.
Expand Down
41 changes: 12 additions & 29 deletions src/core/reopt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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[:ObjectivePenalties] = 0.0

if !isempty(p.techs.all)
add_tech_size_constraints(m, p)
Expand Down Expand Up @@ -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) +
Expand All @@ -470,36 +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[:ObjectivePenalties] += 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[:ObjectivePenalties] += -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[:ObjectivePenalties] += 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])

# if !(isempty(p.s.storage.types.elec)) && p.s.settings.add_soc_incentive # Keep SOC high
# @objective(m, Min, m[:Costs] -
# 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
# Set model objective
@objective(m, Min, m[:Costs] + m[:ObjectivePenalties] )

for b in p.s.storage.types.elec
if p.s.storage.attr[b].model_degradation
Expand Down
6 changes: 2 additions & 4 deletions src/results/financial.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)])

Expand Down
2 changes: 1 addition & 1 deletion test/test_with_xpress.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1479,7 +1479,7 @@ end
@test results["Site"]["total_renewable_energy_fraction_bau"] ≈ 0.132118 atol=1e-3 # 0.1354 atol=1e-3
# CO2 emissions - totals ≈ from grid, from fuelburn, ER, $/tCO2 breakeven
@test results["Site"]["lifecycle_emissions_reduction_CO2_fraction"] ≈ 0.8 atol=1e-3 # 0.8
@test results["Financial"]["breakeven_cost_of_emissions_reduction_per_tonne_CO2"] ≈ 373.9 atol=1e-1
@test results["Financial"]["breakeven_cost_of_emissions_reduction_per_tonne_CO2"] ≈ 374.0 atol=1e-1
@test results["Site"]["annual_emissions_tonnes_CO2"] ≈ 14.2 atol=1
@test results["Site"]["annual_emissions_tonnes_CO2_bau"] ≈ 70.99 atol=1
@test results["Site"]["annual_emissions_from_fuelburn_tonnes_CO2"] ≈ 0.0 atol=1 # 0.0
Expand Down
Loading