Skip to content
Merged
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
53 changes: 29 additions & 24 deletions src/types/policies/EmissionCap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ Note: The banking formulation in this modification requires that years[n] - year
* `price_resp_alws`: Bool that turns on price responsive allowances, defaults to false.
* `step_prices`: Length-k vector of prices for each step.
* `step_adders`: Length-k vector of allowance quantities added to (positive) or withdrawn from (negative) the base target at each price step, which defines the cumulative supply available at step k. When representing a price ceiling, include a backstop step with Inf allowances.
* `rate`: The rate step prices increase by each year. Defaults to 5%. Note that prices esacalate relative to first model year.
* `rate`: The rate step prices increase by each year. Defaults to 5%. Prices escalate relative to the first year that has a target.

### Table Column Added:
### Table Column Added:
* `(:gen, :<name>_prc)` - the allowance price of the policy converted to DollarsPerMWhGenerated
* `(:branch, :<name>_prc)` - the allowance price of the policy converted to DollarsPerMWhGenerated
* `(:dc_line, :<name>_prc)` - the allowance price of the policy converted to DollarsPerMWhGenerated
Expand Down Expand Up @@ -182,7 +182,7 @@ function E4ST.modify_model!(pol::EmissionCap, config, data, model)
for gen_idx in gen_idxs
)
)

# add emissions from imports to expression if pol.cap_imports == true
if pol.cap_imports == true
setup_imports!(pol, config, data, model, :branch)
Expand All @@ -195,7 +195,8 @@ function E4ST.modify_model!(pol::EmissionCap, config, data, model)
years = Symbol.(get_years(data))
cap_years = collect(keys(pol.targets))
filter!(in(years), cap_years)

offset_adjust = pol.offset_under_cap == false ? pol.offset : 0.0 # only loosen the cap if offsets are outside it

# add price responsive allowances to the model
if pol.price_resp_alws
add_price_responsive_allowances(pol, config, data, model)
Expand All @@ -209,37 +210,38 @@ function E4ST.modify_model!(pol::EmissionCap, config, data, model)
if pol.banking
# Cumulative constraint: sum of emissions from the first cap year through yr_idx
# must be ≤ sum of targets over those years + initial_bank

model[cap_cons_name] = @constraint(model,
[yr_idx in 1:nyr; years[yr_idx] in cap_years],
sum(
model[emis_expr_name][y_idx, hr_idx]
for y_idx in 1:nyr, hr_idx in 1:nhr
if years[y_idx] in cap_years && years[y_idx] <= years[yr_idx]
) <= (
pol.price_resp_alws ? # if pol.price_resp_alws is true, RHS is equal to sum of alllowances at each step
sum(
alw[y_idx, s]
for y_idx in 1:nyr, s in 1:nsteps
if years[y_idx] in cap_years && years[y_idx] <= years[yr_idx]
) + pol.initial_bank :
sum(pol.targets[y] for y in cap_years if y <= years[yr_idx]) + pol.initial_bank
)
(
pol.price_resp_alws ?
sum(
alw[y_idx, s]
for y_idx in 1:nyr, s in 1:nsteps
if years[y_idx] in cap_years && years[y_idx] <= years[yr_idx]
) :
sum(pol.targets[y] for y in cap_years if y <= years[yr_idx])
) + pol.initial_bank
) / (1 - offset_adjust)
)
else

model[cap_cons_name] = @constraint(model,
[yr_idx in 1:nyr; years[yr_idx] in cap_years],
sum(model[emis_expr_name][yr_idx, hr_idx] for hr_idx in 1:nhr) <=
sum(model[emis_expr_name][yr_idx, hr_idx] for hr_idx in 1:nhr) <=
(pol.price_resp_alws ?
sum(alw[yr_idx, s] for s in 1:nsteps) :
pol.targets[years[yr_idx]]
) / (1 - pol.offset)
) / (1 - offset_adjust)

)
end


end


Expand Down Expand Up @@ -274,6 +276,7 @@ function E4ST.modify_results!(pol::EmissionCap, config, data)

alw_prc = ByYear(zeros(nyr))
for y_idx in 1:nyr
years[y_idx] in cap_years || continue # no compliance obligation, and thus no allowance price, outside cap years
alw_prc[y_idx] = sum(
lambda_scaled[t_idx]
for t_idx in 1:nyr
Expand Down Expand Up @@ -531,9 +534,11 @@ function setup_allowance_price_resp_alws(pol, config, data)
target_years = [String(y) for y in sym_years if haskey(pol.targets, y)]
target_idxs = [i for i in 1:nyr if years[i] in target_years]
target_nyr = length(target_years)

# Escalate price steps at 5%/yr
prices = [[p * (1 + pol.rate)^(yr_idx - 1) for yr_idx in 1:nyr] for p in pol.step_prices]

# Escalate price steps at 5%/yr, relative to the first year that has a target
# (not the first model year), so the configured step_prices land unescalated in that year.
first_target_idx = first(target_idxs)
prices = [[p * (1 + pol.rate)^(yr_idx - first_target_idx) for yr_idx in 1:nyr] for p in pol.step_prices]

# cumulative allowance quantity at each step boundary = target + adder
targets = [get(pol.targets, y, 0.0) for y in Symbol.(target_years)]
Expand Down Expand Up @@ -564,7 +569,7 @@ function setup_allowance_price_resp_alws(pol, config, data)
push!(price_resp_alws, (
s,
years[yr_idx],
prices[s][i],
prices[s][yr_idx],
step_alw[s][i],
step_widths[s][i],
))
Expand Down Expand Up @@ -599,7 +604,7 @@ function add_price_responsive_allowances(pol, config, data, model)
step_widths[s, yr_idx] = row.step_alw_q
end

# Variables: allowances purchased per year per step
# Variables: allowances purchased per year per step, in tons
alw_name = cols.alw_name
model[alw_name] = @variable(model, # initialize a variable of size year x steps with lower bound 0
[yr_idx in 1:nyr, s in 1:nsteps],
Expand All @@ -611,9 +616,9 @@ function add_price_responsive_allowances(pol, config, data, model)
w = step_widths[s, yr_idx]
isinf(w) || set_upper_bound(alw[yr_idx, s], w)
end
# Per-year cost expression
alw_cost_name = cols.alw_cost

# Per-year cost expression
alw_cost_name = cols.alw_cost
model[alw_cost_name] = @expression(model, # cost expression is allowances at step k multiplied by price at step s
[yr_idx in 1:nyr],
sum(prices[s, yr_idx] * alw[yr_idx, s] for s in 1:nsteps)
Expand Down
Loading