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 write capactiy value #585

Merged
132 changes: 87 additions & 45 deletions src/write_outputs/capacity_reserve_margin/write_capacity_value.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@
dfGen = inputs["dfGen"]
G = inputs["G"] # Number of resources (generators, storage, DR, and DERs)
T = inputs["T"] # Number of time steps (hours)
SEG = inputs["SEG"] # Number of lines
L = inputs["L"] # Number of lines
THERM_ALL = inputs["THERM_ALL"]
VRE = inputs["VRE"]
HYDRO_RES = inputs["HYDRO_RES"]
STOR_ALL = inputs["STOR_ALL"]
FLEX = inputs["FLEX"]
MUST_RUN = inputs["MUST_RUN"]
VRE_STOR = inputs["VRE_STOR"]
if setup["ParameterScale"] == 1
existingplant_position = findall(x -> x >= 1, (value.(EP[:eTotalCap])) * ModelScalingFactor)
else
existingplant_position = findall(x -> x >= 1, (value.(EP[:eTotalCap])))
end
THERM_ALL_EX = intersect(THERM_ALL, existingplant_position)
VRE_EX = intersect(VRE, existingplant_position)
HYDRO_RES_EX = intersect(HYDRO_RES, existingplant_position)
STOR_ALL_EX = intersect(STOR_ALL, existingplant_position)
FLEX_EX = intersect(FLEX, existingplant_position)
MUST_RUN_EX = intersect(MUST_RUN, existingplant_position)

scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1
eTotalCap = value.(EP[:eTotalCap])

Check warning on line 14 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L13-L14

Added lines #L13 - L14 were not covered by tests

minimum_plant_size = 1 # MW
large_plants = findall(>=(minimum_plant_size), eTotalCap * scale_factor)

Check warning on line 17 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L16-L17

Added lines #L16 - L17 were not covered by tests

THERM_ALL_EX = intersect(THERM_ALL, large_plants)
VRE_EX = intersect(VRE, large_plants)
HYDRO_RES_EX = intersect(HYDRO_RES, large_plants)
STOR_ALL_EX = intersect(STOR_ALL, large_plants)
FLEX_EX = intersect(FLEX, large_plants)
MUST_RUN_EX = intersect(MUST_RUN, large_plants)

Check warning on line 24 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L19-L24

Added lines #L19 - L24 were not covered by tests
# Will only be activated if grid connection capacity exists (because may build standalone storage/VRE, which will only be telling by grid connection capacity)
VRE_STOR_EX = intersect(VRE_STOR, existingplant_position)
VRE_STOR_EX = intersect(VRE_STOR, large_plants)

Check warning on line 26 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L26

Added line #L26 was not covered by tests
if !isempty(VRE_STOR_EX)
DC_DISCHARGE = inputs["VS_STOR_DC_DISCHARGE"]
DC_CHARGE = inputs["VS_STOR_DC_CHARGE"]
Expand All @@ -34,48 +34,90 @@
AC_CHARGE_EX = intersect(inputs["VS_STOR_AC_CHARGE"], VRE_STOR_EX)
dfVRE_STOR = inputs["dfVRE_STOR"]
end

totalcap = repeat((value.(EP[:eTotalCap])), 1, T)

crm_derate(i, y::Vector{Int}) = dfGen[y, Symbol("CapRes_$i")]'
max_power(t::Vector{Int}, y::Vector{Int}) = inputs["pP_Max"][y, t]'
total_cap(y::Vector{Int}) = eTotalCap[y]'

Check warning on line 40 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L38-L40

Added lines #L38 - L40 were not covered by tests

dfCapValue = DataFrame()
for i in 1:inputs["NCapacityReserveMargin"]
temp_dfCapValue = DataFrame(Resource = inputs["RESOURCES"], Zone = dfGen[!, :Zone], Reserve = fill(Symbol("CapRes_$i"), G))
temp_capvalue = zeros(G, T)
temp_riskyhour = zeros(G, T)
temp_cap_derate = zeros(G, T)
if setup["ParameterScale"] == 1
riskyhour_position = findall(x -> x >= 1, ((dual.(EP[:cCapacityResMargin][i, :])) ./ inputs["omega"] * ModelScalingFactor))
else
riskyhour_position = findall(x -> x >= 1, ((dual.(EP[:cCapacityResMargin][i, :])) ./ inputs["omega"]))
end
temp_riskyhour[:, riskyhour_position] = ones(Int, G, length(riskyhour_position))
temp_cap_derate[existingplant_position, :] = repeat(dfGen[existingplant_position, Symbol("CapRes_$i")], 1, T)
capvalue = zeros(T, G)

Check warning on line 44 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L44

Added line #L44 was not covered by tests

minimum_crm_price = 1 # $/MW
riskyhour = findall(>=(minimum_crm_price), capacity_reserve_margin_price(EP, inputs, setup, i))

Check warning on line 47 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L46-L47

Added lines #L46 - L47 were not covered by tests

power(y::Vector{Int}) = value.(EP[:vP][y, riskyhour])'

Check warning on line 49 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L49

Added line #L49 was not covered by tests

capvalue[riskyhour, THERM_ALL_EX] = crm_derate(i, THERM_ALL_EX)

Check warning on line 51 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L51

Added line #L51 was not covered by tests

capvalue[riskyhour, VRE_EX] = crm_derate(i, VRE_EX) .* max_power(riskyhour, VRE_EX)

Check warning on line 53 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L53

Added line #L53 was not covered by tests

capvalue[riskyhour, MUST_RUN_EX] = crm_derate(i, MUST_RUN_EX) .* max_power(riskyhour, MUST_RUN_EX)

Check warning on line 55 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L55

Added line #L55 was not covered by tests

capvalue[riskyhour, HYDRO_RES_EX] = crm_derate(i, HYDRO_RES_EX) .* power(HYDRO_RES_EX) ./ total_cap(HYDRO_RES_EX)

Check warning on line 57 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L57

Added line #L57 was not covered by tests

temp_capvalue[THERM_ALL_EX, :] = temp_cap_derate[THERM_ALL_EX, :] .* temp_riskyhour[THERM_ALL_EX, :]
temp_capvalue[VRE_EX, :] = temp_cap_derate[VRE_EX, :] .* (inputs["pP_Max"][VRE_EX, :]) .* temp_riskyhour[VRE_EX, :]
temp_capvalue[MUST_RUN_EX, :] = temp_cap_derate[MUST_RUN_EX, :] .* (inputs["pP_Max"][MUST_RUN_EX, :]) .* temp_riskyhour[MUST_RUN_EX, :]
temp_capvalue[HYDRO_RES_EX, :] = temp_cap_derate[HYDRO_RES_EX, :] .* (value.(EP[:vP][HYDRO_RES_EX, :])) .* temp_riskyhour[HYDRO_RES_EX, :] ./ totalcap[HYDRO_RES_EX, :]
if !isempty(STOR_ALL_EX)
temp_capvalue[STOR_ALL_EX, :] = temp_cap_derate[STOR_ALL_EX, :] .* ((value.(EP[:vP][STOR_ALL_EX, :]) - value.(EP[:vCHARGE][STOR_ALL_EX, :]).data + value.(EP[:vCAPRES_discharge][STOR_ALL_EX, :]).data - value.(EP[:vCAPRES_charge][STOR_ALL_EX, :]).data)) .* temp_riskyhour[STOR_ALL_EX, :] ./ totalcap[STOR_ALL_EX, :]
charge = value.(EP[:vCHARGE][STOR_ALL_EX, riskyhour].data)'
capres_discharge = value.(EP[:vCAPRES_discharge][STOR_ALL_EX, riskyhour].data)'
capres_charge = value.(EP[:vCAPRES_charge][STOR_ALL_EX, riskyhour].data)'

Check warning on line 62 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L60-L62

Added lines #L60 - L62 were not covered by tests

capvalue[riskyhour, STOR_ALL_EX] = crm_derate(i, STOR_ALL_EX) .* (power(STOR_ALL_EX) - charge + capres_discharge - capres_charge) ./ total_cap(STOR_ALL_EX)

Check warning on line 64 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L64

Added line #L64 was not covered by tests
end

if !isempty(FLEX_EX)
temp_capvalue[FLEX_EX, :] = temp_cap_derate[FLEX_EX, :] .* ((value.(EP[:vCHARGE_FLEX][FLEX_EX, :]).data - value.(EP[:vP][FLEX_EX, :]))) .* temp_riskyhour[FLEX_EX, :] ./ totalcap[FLEX_EX, :]
charge = value.(EP[:vCHARGE_FLEX][FLEX_EX, riskyhour].data)'
capvalue[riskyhour, FLEX_EX] = crm_derate(i, FLEX_EX) .* (charge - power(FLEX_EX)) ./ total_cap(FLEX_EX)

Check warning on line 69 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L68-L69

Added lines #L68 - L69 were not covered by tests
end
if !isempty(VRE_STOR_EX)
temp_capvalue_dc_discharge = zeros(G, T)
temp_capvalue_dc_discharge[DC_DISCHARGE, :] = value.(EP[:vCAPRES_DC_DISCHARGE][DC_DISCHARGE, :].data) .* dfVRE_STOR[(dfVRE_STOR.STOR_DC_DISCHARGE.!=0), :EtaInverter]
temp_capvalue_dc_charge = zeros(G, T)
temp_capvalue_dc_charge[DC_CHARGE, :] = value.(EP[:vCAPRES_DC_CHARGE][DC_CHARGE, :].data) ./ dfVRE_STOR[(dfVRE_STOR.STOR_DC_CHARGE.!=0), :EtaInverter]
temp_capvalue[VRE_STOR_EX, :] = temp_cap_derate[VRE_STOR_EX, :] .* (value.(EP[:vP][VRE_STOR_EX, :])) .* temp_riskyhour[VRE_STOR_EX, :] ./ totalcap[VRE_STOR_EX, :]
temp_capvalue[VRE_STOR_STOR_EX, :] .-= temp_cap_derate[VRE_STOR_STOR_EX, :] .* (value.(EP[:vCHARGE_VRE_STOR][VRE_STOR_STOR_EX, :].data)) .* temp_riskyhour[VRE_STOR_STOR_EX, :] ./ totalcap[VRE_STOR_STOR_EX, :]
temp_capvalue[DC_DISCHARGE_EX, :] .+= temp_cap_derate[DC_DISCHARGE_EX, :] .* temp_capvalue_dc_discharge[DC_DISCHARGE_EX, :] .* temp_riskyhour[DC_DISCHARGE_EX, :] ./ totalcap[DC_DISCHARGE_EX, :]
temp_capvalue[AC_DISCHARGE_EX, :] .+= temp_cap_derate[AC_DISCHARGE_EX, :] .* (value.(EP[:vCAPRES_AC_DISCHARGE][AC_DISCHARGE_EX, :]).data) .* temp_riskyhour[AC_DISCHARGE_EX, :] ./ totalcap[AC_DISCHARGE_EX, :]
temp_capvalue[DC_CHARGE_EX, :] .-= temp_cap_derate[DC_CHARGE_EX, :] .* temp_capvalue_dc_charge[DC_CHARGE_EX, :] .* temp_riskyhour[DC_CHARGE_EX, :] ./ totalcap[DC_CHARGE_EX, :]
temp_capvalue[AC_CHARGE_EX, :] .-= temp_cap_derate[AC_CHARGE_EX, :] .* (value.(EP[:vCAPRES_AC_CHARGE][AC_CHARGE_EX, :]).data) .* temp_riskyhour[AC_CHARGE_EX, :] ./ totalcap[AC_CHARGE_EX, :]
capres_dc_discharge = value.(EP[:vCAPRES_DC_DISCHARGE][DC_DISCHARGE, riskyhour].data)'
discharge_eff = dfVRE_STOR[dfVRE_STOR.STOR_DC_DISCHARGE .!= 0, :EtaInverter]'
capvalue_dc_discharge = zeros(T, G)
capvalue_dc_discharge[riskyhour, DC_DISCHARGE] = capres_dc_discharge .* discharge_eff

Check warning on line 75 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L72-L75

Added lines #L72 - L75 were not covered by tests

capres_dc_charge = value.(EP[:vCAPRES_DC_CHARGE][DC_CHARGE, riskyhour].data)'
charge_eff = dfVRE_STOR[dfVRE_STOR.STOR_DC_CHARGE .!= 0, :EtaInverter]'
capvalue_dc_charge = zeros(T, G)
capvalue_dc_charge[riskyhour, DC_CHARGE] = capres_dc_charge ./ charge_eff

Check warning on line 80 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L77-L80

Added lines #L77 - L80 were not covered by tests

capvalue[riskyhour, VRE_STOR_EX] = crm_derate(i, VRE_STOR_EX) .* power(VRE_STOR_EX) ./ total_cap(VRE_STOR_EX)

Check warning on line 82 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L82

Added line #L82 was not covered by tests

charge_vre_stor = value.(EP[:vCHARGE_VRE_STOR][VRE_STOR_STOR_EX, riskyhour].data)'
capvalue[riskyhour, VRE_STOR_STOR_EX] -= crm_derate(i, VRE_STOR_STOR_EX) .* charge_vre_stor ./ total_cap(VRE_STOR_STOR_EX)

Check warning on line 85 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L84-L85

Added lines #L84 - L85 were not covered by tests

capvalue[riskyhour, DC_DISCHARGE_EX] += crm_derate(i, DC_DISCHARGE_EX) .* capvalue_dc_discharge[riskyhour, DC_DISCHARGE_EX] ./ total_cap(DC_DISCHARGE_EX)
capres_ac_discharge = value.(EP[:vCAPRES_AC_DISCHARGE][AC_DISCHARGE_EX, riskyhour].data)'
capvalue[riskyhour, AC_DISCHARGE_EX] += crm_derate(i, AC_DISCHARGE_EX) .* capres_ac_discharge ./ total_cap(AC_DISCHARGE_EX)

Check warning on line 89 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L87-L89

Added lines #L87 - L89 were not covered by tests

capvalue[riskyhour, DC_CHARGE_EX] -= crm_derate(i, DC_CHARGE_EX) .* capvalue_dc_charge[riskyhour, DC_CHARGE_EX] ./ total_cap(DC_CHARGE_EX)
capres_ac_charge = value.(EP[:vCAPRES_AC_CHARGE][AC_CHARGE_EX, riskyhour].data)'
capvalue[riskyhour, AC_CHARGE_EX] -= crm_derate(i, AC_CHARGE_EX) .* capres_ac_charge ./ total_cap(AC_CHARGE_EX)

Check warning on line 93 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L91-L93

Added lines #L91 - L93 were not covered by tests
end
temp_dfCapValue = hcat(temp_dfCapValue, DataFrame(temp_capvalue, :auto))
capvalue = collect(transpose(capvalue))
temp_dfCapValue = DataFrame(Resource = inputs["RESOURCES"], Zone = dfGen.Zone, Reserve = fill(Symbol("CapRes_$i"), G))
temp_dfCapValue = hcat(temp_dfCapValue, DataFrame(capvalue, :auto))

Check warning on line 97 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L95-L97

Added lines #L95 - L97 were not covered by tests
auxNew_Names = [Symbol("Resource"); Symbol("Zone"); Symbol("Reserve"); [Symbol("t$t") for t in 1:T]]
rename!(temp_dfCapValue, auxNew_Names)
append!(dfCapValue, temp_dfCapValue)
end
CSV.write(joinpath(path, "CapacityValue.csv"), dfCapValue)
write_simple_csv(joinpath(path, "CapacityValue.csv"), dfCapValue)

Check warning on line 102 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L102

Added line #L102 was not covered by tests
end

@doc raw"""
capacity_reserve_margin_price(EP::Model,
inputs::Dict,
setup::Dict,
capres_zone::Int)::Vector{Float64}

Marginal electricity price for each model zone and time step.
This is equal to the dual variable of the power balance constraint.
When solving a linear program (i.e. linearized unit commitment or economic dispatch)
this output is always available; when solving a mixed integer linear program, this can
be calculated only if `WriteShadowPrices` is activated.

Returns a vector, with units of $/MW
"""
function capacity_reserve_margin_price(EP::Model, inputs::Dict, setup::Dict, capres_zone::Int)::Vector{Float64}
ω = inputs["omega"]
scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1
return dual.(EP[:cCapacityResMargin][capres_zone, :]) ./ ω * scale_factor

Check warning on line 122 in src/write_outputs/capacity_reserve_margin/write_capacity_value.jl

View check run for this annotation

Codecov / codecov/patch

src/write_outputs/capacity_reserve_margin/write_capacity_value.jl#L119-L122

Added lines #L119 - L122 were not covered by tests
end