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

Refactor GenerationStandard #118

Merged
merged 2 commits into from
Apr 24, 2023
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
67 changes: 49 additions & 18 deletions src/types/modifications/GenerationStandard.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,31 @@ This encompasses RPSs, CESs, and technology carveouts.
To assign the credit (the portion of generation that can contribute) to generators, the [Crediting](@ref) type is used.

* `name` - Name of the policy
* `targets` - The yearly targets for the Generation Standard
* `gen_filters` - Filters on which generation qualifies to fulfill the GS. Sometimes qualifying generators may be outside of the GS load region if they supply power to it.
* `crediting` - the crediting structure and related fields
* `load_bus_filters` - Filters on which buses fall into the GS load region. The GS will be applied to the load from these buses.
* `gs_type` - The original type the GS (RPS, CES, etc)
* `load_targets` - OrderedDict containing key-value pairs where each key is the name of a requirement (not currently used), and each value is an OrderedDict with the following keys:
* `filters` - Filters on which buses fall into the GS load region. The GS will be applied to the load from these buses.
* `targets` - The yearly percent targets of the qualifying load that must be covered by the qualifying generation.
"""
struct GenerationStandard{T} <: Policy
name::Symbol
targets::OrderedDict
crediting::Crediting
gen_filters::OrderedDict
load_bus_filters::OrderedDict
gen_filters::OrderedDict{Symbol, Any}
load_targets::OrderedDict{Symbol, Any}

function GenerationStandard{T}(name, crediting, gen_filters::OrderedDict{Symbol, Any}, load_targets::OrderedDict{Symbol, Any}) where T
for (k,v) in load_targets
@assert haskey(v, :targets)
@assert haskey(v, :filters)
end
return new{T}(Symbol(name), Crediting(crediting), gen_filters, load_targets)
end

end


function GenerationStandard(;name, targets, crediting::OrderedDict, gen_filters, load_bus_filters)
c = Crediting(crediting)
return GenerationStandard(Symbol(name), targets, c, gen_filters, load_bus_filters)
function GenerationStandard(;name, crediting::OrderedDict, gen_filters, load_targets)
return GenerationStandard(name, crediting, gen_filters, load_targets)
sallyrobson marked this conversation as resolved.
Show resolved Hide resolved
end
export GenerationStandard

Expand Down Expand Up @@ -69,8 +75,6 @@ function E4ST.modify_model!(pol::GenerationStandard, config, data, model)
gen_idxs = get_row_idxs(gen, parse_comparisons(pol.gen_filters))

bus = get_table(data, :bus)
bus_idxs = get_row_idxs(bus, parse_comparisons(pol.load_bus_filters))

years = Symbol.(get_years(data))

# create expression for qualifying load, only created if it hasn't already been defined in the model
Expand All @@ -84,24 +88,51 @@ function E4ST.modify_model!(pol::GenerationStandard, config, data, model)
# create yearly constraint that qualifying generation meeting qualifying load
# takes the form sum(gs_egen * credit) <= gs_value * sum(gs_load)

cons_name = "cons_$(pol.name)"
target_years = collect(keys(pol.targets))
cons_name = Symbol("cons_$(pol.name)")
tgt_load_name = Symbol("tgt_load_$(pol.name)")
hour_weights = get_hour_weights(data)

pl_gs_bus = model[:pl_gs_bus]

# Construct expression for target load
tgt_load_expr = @expression(model,
[yr_idx in 1:nyear],
AffExpr(0.0)
)

for (k,d) in pol.load_targets
targets = d[:targets]
filters = d[:filters]
bus_idxs = get_row_idxs(bus, parse_comparisons(d[:filters]))
for yr_idx in 1:nyear
year = years[yr_idx]
haskey(targets, year) || continue
curr_tgt_load_expr = tgt_load_expr[yr_idx]
for bus_idx in bus_idxs, hr_idx in 1:nhour
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there an advantage to using a for loop like this instead of just sum() over the buses and hours? just curious

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's a minor performance thing - when calling sum, JuMP constructs a new AffExpr object, then adds it to the expression. Now that you mention it, this is still creating an expression, since the multiplication is happening outside the add_to_expression! call. I will update that.

add_to_expression!(curr_tgt_load_expr,
pl_gs_bus[bus_idx, yr_idx, hr_idx],
targets[year] * hour_weights[hr_idx]
)
end
end
end


model[tgt_load_name] = tgt_load_expr


@info "Creating a generation constraint for the Generation Standard $(pol.name)."
model[Symbol(cons_name)] = @constraint(model,
model[cons_name] = @constraint(model,
[
yr_idx in 1:nyear;
haskey(pol.targets, years[yr_idx])
tgt_load_expr[yr_idx] != 0
],
sum(
get_egen_gen(data, model, gen_idx, yr_idx, hr_idx) *
get_table_num(data, :gen, pol.name, gen_idx, yr_idx, hr_idx)
for gen_idx=gen_idxs, hr_idx=1:nhour
) >= (
pol.targets[years[yr_idx]] *
sum(model[:pl_gs_bus][bus_idx, yr_idx, hr_idx]*hour_weights[hr_idx]
for bus_idx=bus_idxs, hr_idx=1:nhour)
tgt_load_expr[yr_idx]
)
)
end
Expand Down
2 changes: 1 addition & 1 deletion src/types/policies/CES.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ No default crediting is specified although the standard crediting will be [`Cred
"""
const CES = GenerationStandard{:CES}

CES(;name, targets, crediting, gen_filters, load_bus_filters) = CES(name, targets, Crediting(crediting), gen_filters, load_bus_filters)
CES(;name, crediting, gen_filters, load_targets) = CES(name, crediting, gen_filters, load_targets)

export CES

Expand Down
5 changes: 1 addition & 4 deletions src/types/policies/RPS.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ mod_rank for RPS will be 1.0 because that is the rank of GenerationStandards
"""
const RPS = GenerationStandard{:RPS}

function RPS(;name, targets, crediting=StandardRPSCrediting(), gen_filters, load_bus_filters)
return RPS(name, targets, Crediting(crediting), gen_filters, load_bus_filters)
end

RPS(;name, crediting=StandardRPSCrediting(), gen_filters, load_targets) = RPS(name, crediting, gen_filters, load_targets)

export RPS

Expand Down
12 changes: 7 additions & 5 deletions test/config/config_3bus_ces.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ line_loss_type: pflow # Need to test that this type of line loss works with gen
mods:
example_ces:
type: "CES"
targets:
y2035: 0.9
y2040: 0.95
crediting:
type: "CreditByBenchmark"
benchmark: 0.5
gen_filters:
country: "archenland"
load_bus_filters:
state: "anvard"
load_targets:
anvard_ces:
targets:
y2035: 0.9
y2040: 0.95
filters:
state: "anvard"

26 changes: 15 additions & 11 deletions test/config/config_3bus_rps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@ base_out_path: "../out/3bus_rps"
mods:
example_rps:
type: "RPS"
targets:
y2035: 0.9
y2040: 0.95
crediting:
type: "StandardRPSCrediting"
gen_filters:
country: "archenland"
load_bus_filters:
state: "stormness"
load_targets:
stormness_rps:
filters:
state: stormness
targets:
y2035: 0.9
y2040: 0.95
example_rps_gentype:
type: "RPS"
targets:
y2030: 0.7
y2035: 0.8
y2040: 0.9
crediting:
type: "CreditByGentype"
credits:
Expand All @@ -25,8 +23,14 @@ mods:
oswind: 1.0
gen_filters:
country: "narnia"
load_bus_filters:
country: "narnia"
load_targets:
narnia_rps:
targets:
y2030: 0.7
y2035: 0.8
y2040: 0.9
filters:
country: "narnia"
storage:
type: Storage
file: "../data/3bus/storage.csv"
Expand Down
11 changes: 7 additions & 4 deletions test/testpoltypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,14 @@
gen_total_qual_2035 = aggregate_result(total, data, :gen, :egen, [:emis_co2 => 0, :country => "archenland"], 2)
elserv_total_qual_2035 = aggregate_result(total, data, :bus, :el_gs, :state => "stormness", 2)

@test gen_total_qual_2035 / elserv_total_qual_2035 >= rps_mod.targets[:y2035]
targets = first(values(rps_mod.load_targets))[:targets]

@test gen_total_qual_2035 / elserv_total_qual_2035 >= targets[:y2035]

gen_total_qual_2040 = aggregate_result(total, data, :gen, :egen, [:emis_co2 => 0, :country => "archenland"], 3)
elserv_total_qual_2040 = aggregate_result(total, data, :bus, :el_gs, :state => "stormness", 3)

@test gen_total_qual_2040 / elserv_total_qual_2040 >= rps_mod.targets[:y2040]
@test gen_total_qual_2040 / elserv_total_qual_2040 >= targets[:y2040]

gen_ref = get_table(data_ref, :gen)
gen_total_ref = aggregate_result(total, data_ref, :gen, :egen, :emis_co2 => 0)
Expand Down Expand Up @@ -351,20 +353,21 @@

## Check that CES correctly impacts results
ces_mod = config[:mods][:example_ces]
targets = first(values(ces_mod.load_targets))[:targets]

gen_total_qual_2035 = aggregate_result(total, data, :gen, :egen, [:emis_co2 => <(0.5), :country => "archenland"], 2)
gen_total_qual_2035_ref = aggregate_result(total, data_ref, :gen, :egen, [:emis_co2 => <(0.5), :country => "archenland"], 2)
elserv_total_qual_2035 = aggregate_result(total, data, :bus, :el_gs, :state => "anvard", 2)

@test gen_total_qual_2035 > gen_total_qual_2035_ref
@test gen_total_qual_2035 / elserv_total_qual_2035 >= ces_mod.targets[:y2035] - 0.001 #would use approx but need the > in case partial credit gen is used
@test gen_total_qual_2035 / elserv_total_qual_2035 >= targets[:y2035] - 0.001 #would use approx but need the > in case partial credit gen is used

gen_total_qual_2040 = aggregate_result(total, data, :gen, :egen, [:emis_co2 => <(0.5), :country => "archenland"], 3)
gen_total_qual_2040_ref = aggregate_result(total, data_ref, :gen, :egen, [:emis_co2 => <(0.5), :country => "archenland"], 3)
elserv_total_qual_2040 = aggregate_result(total, data, :bus, :el_gs, :state => "anvard", 3)

@test gen_total_qual_2040 > gen_total_qual_2040_ref
@test gen_total_qual_2040 / elserv_total_qual_2040 >= ces_mod.targets[:y2040] - 0.001 #would use approx but need the > in case partial credit gen is used
@test gen_total_qual_2040 / elserv_total_qual_2040 >= targets[:y2040] - 0.001 #would use approx but need the > in case partial credit gen is used

end

Expand Down