Skip to content

Commit

Permalink
Merge pull request #1141 from NREL-Sienna/kdrh/psy4_docs_renaming
Browse files Browse the repository at this point in the history
Kdrh/psy4 docs renaming
  • Loading branch information
jd-lara authored Jul 2, 2024
2 parents 40661d2 + c40ec43 commit 13b8464
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 107 deletions.
2 changes: 1 addition & 1 deletion src/descriptors/power_system_inputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@
"unit": "$/MW",
"name": "fixed_cost",
"description": "Fixed Cost of Generation",
"default_value": null
"default_value": 0.0
},
{
"unit": "$/start",
Expand Down
2 changes: 1 addition & 1 deletion src/parsers/power_models_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ function make_thermal_gen(
cost = QuadraticFunctionData(get.(Ref(coeffs), quadratic_degrees, 0)...)
fixed = (d["ncost"] >= 1) ? last(d["cost"]) : 0.0
end
cost = CostCurve(InputOutputCurve((cost)))
cost = CostCurve(InputOutputCurve((cost)), UnitSystem.DEVICE_BASE)
startup = d["startup"]
shutdn = d["shutdown"]
else
Expand Down
251 changes: 155 additions & 96 deletions src/parsers/power_system_table_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -807,99 +807,166 @@ function make_generator(data::PowerSystemTableData, gen, cost_colnames, bus, gen
return generator
end

function calculate_variable_cost(
data::PowerSystemTableData,
function make_cost(
::Type{T},
data,
gen,
cost_colnames::_HeatRateColumns,
base_power,
)
fuel_cost = gen.fuel_price / 1000.0
) where {T <: ThermalGen}
fuel_price = gen.fuel_price / 1000.0

vom = isnothing(gen.variable_cost) ? 0.0 : gen.variable_cost
cost_pairs = get_cost_pairs(gen, cost_colnames)
var_cost, fixed = create_pwinc_cost(cost_pairs)

if fuel_cost > 0.0
var_cost =
[(getfield(gen, hr), getfield(gen, mw)) for (hr, mw) in cost_colnames.columns]
var_cost = unique([
(tryparse(Float64, string(c[1])), tryparse(Float64, string(c[2]))) for
c in var_cost if !in(nothing, c)
])
if isempty(var_cost)
@warn "Unable to calculate variable cost for $(gen.name)" var_cost maxlog = 5
end
else
var_cost = [(0.0, 0.0)]
end
startup_cost, shutdown_cost = calculate_uc_cost(data, gen, fuel_price)

if length(var_cost) > 1
var_cost[2:end] = [
(
(
var_cost[i][1] * fuel_cost * (var_cost[i][2] - var_cost[i - 1][2]) +
var_cost[i][2] * vom
),
var_cost[i][2],
) .* gen.active_power_limits_max .* base_power for i in 2:length(var_cost)
]
var_cost[1] =
((var_cost[1][1] * fuel_cost + vom) * var_cost[1][2], var_cost[1][2]) .*
gen.active_power_limits_max .* base_power

fixed = max(
0.0,
var_cost[1][1] -
(var_cost[2][1] / (var_cost[2][2] - var_cost[1][2]) * var_cost[1][2]),
)
var_cost[1] = (var_cost[1][1] - fixed, var_cost[1][2])
op_cost = ThermalGenerationCost(
FuelCurve(var_cost, UnitSystem.NATURAL_UNITS, fuel_price),
fixed * fuel_price,
startup_cost,
shutdown_cost,
)
return op_cost
end

for i in 2:length(var_cost)
var_cost[i] = (var_cost[i - 1][1] + var_cost[i][1], var_cost[i][2])
function make_cost(
::Type{T},
data,
gen,
cost_colnames::_CostPointColumns,
) where {T <: ThermalGen}
fuel_price = gen.fuel_price / 1000.0
cost_pairs = get_cost_pairs(gen, cost_colnames)
var_cost = create_pwl_cost(cost_pairs)
startup_cost, shutdown_cost = calculate_uc_cost(data, gen, fuel_price)

op_cost = ThermalGenerationCost(
CostCurve(var_cost, UnitSystem.NATURAL_UNITS),
gen.fixed_cost,
startup_cost,
shutdown_cost,
)
return op_cost
end

function make_cost(
::Type{T},
data,
gen,
cost_colnames::_HeatRateColumns,
) where {T <: HydroGen}
fuel_price = gen.fuel_price / 1000.0
cost_pairs = get_cost_pairs(gen, cost_colnames)
var_cost, fixed = create_pwinc_cost(cost_pairs)
op_cost = HydroGenerationCost(
FuelCurve(var_cost, UnitSystem.NATURAL_UNITS, fuel_price),
fixed * fuel_price)
return op_cost
end

function make_cost(
::Type{T},
data,
gen,
cost_colnames::_CostPointColumns,
) where {T <: HydroGen}
cost_pairs = get_cost_pairs(gen, cost_colnames)
var_cost = create_pwl_cost(cost_pairs)
op_cost = HydroGenerationCost(
CostCurve(var_cost, UnitSystem.NATURAL_UNITS),
gen.fixed_cost)
return op_cost
end

function make_cost(
::Type{T},
data,
gen,
cost_colnames::_HeatRateColumns,
) where {T <: RenewableGen}
@warn "Heat rate parsing not valid for RenewableGen replacing with zero cost"
var_cost = CostCurve(;
value_curve = LinearCurve(0.0),
power_units = UnitSystem.NATURAL_UNITS,
vom_cost = if isnothing(gen.variable_cost)
LinearCurve(0.0)
else
LinearCurve(gen.variable_cost)
end,
)
op_cost = RenewableGenerationCost(var_cost)
return op_cost
end

function make_cost(
::Type{T},
data,
gen,
cost_colnames::_CostPointColumns,
) where {T <: RenewableGen}
cost_pairs = get_cost_pairs(gen, cost_colnames)
var_cost = CostCurve(;
value_curve = cost_pairs,
power_units = UnitSystem.NATURAL_UNITS,
vom_cost = isnothing(gen.variable_cost) ? 0.0 : gen.variable_cost,
)
op_cost = RenewableGenerationCost(var_cost)
return op_cost
end

function get_cost_pairs(gen::NamedTuple, cost_colnames)
base_power = gen.base_mva * gen.active_power_limits_max
vals = []
for (c, pt) in cost_colnames.columns
x = getfield(gen, pt)
y = getfield(gen, c)

if !in(nothing, [x, y])
push!(vals,
(x = tryparse(Float64, string(x)) * base_power,
y = tryparse(Float64, string(y))))
end
var_cost = PiecewiseLinearData([(x, y) for (y, x) in var_cost])
end

last_increasing_point = findfirst(x -> x < 0.0, [diff(getfield.(vals, :x))..., -Inf])
return vals[1:last_increasing_point]
end

elseif length(var_cost) == 1
function create_pwl_cost(
cost_pairs,
)
if length(cost_pairs) > 1
var_cost = PiecewisePointCurve(PiecewiseLinearData(cost_pairs))
elseif length(cost_pairs) == 1
# if there is only one point, use it to determine the constant $/MW cost
var_cost = LinearFunctionData(var_cost[1][1] * fuel_cost + vom)
fixed = 0.0
var_cost = LinearCurve(cost_pairs[1].y / cost_pairs[1].x)
else
@warn "$(gen.name) has no costs defined, using 0.0" cost_pairs maxlog = 5
var_cost = LinearCurve(0.0)
end
return var_cost, fixed, fuel_cost

return var_cost
end

function calculate_variable_cost(
data::PowerSystemTableData,
gen,
cost_colnames::_CostPointColumns,
base_power,
function create_pwinc_cost(
cost_pairs,
)
vom = isnothing(gen.variable_cost) ? 0.0 : gen.variable_cost

var_cost = [(getfield(gen, c), getfield(gen, mw)) for (c, mw) in cost_colnames.columns]
var_cost = unique([
(tryparse(Float64, string(c[1])), tryparse(Float64, string(c[2]))) for
c in var_cost if !in(nothing, c)
])

var_cost = [
(
(var_cost[i][1] + vom),
(var_cost[i][2] .* gen.active_power_limits_max .* base_power),
) for i in 1:length(var_cost)
]

if length(var_cost) > 1
fixed = max(
0.0,
var_cost[1][1] -
(var_cost[2][1] + vom / (var_cost[2][2] - var_cost[1][2]) * var_cost[1][2]),
if length(cost_pairs) > 1
x_points = getfield.(cost_pairs, :x)
y_points = getfield.(cost_pairs, :y)
var_cost = PiecewiseIncrementalCurve(
first(y_points) * first(x_points),
x_points,
y_points[2:end],
)
var_cost = [(var_cost[i][1] - fixed, var_cost[i][2]) for i in 1:length(var_cost)]
var_cost = PiecewiseLinearData([(x, y) for (y, x) in var_cost])
elseif length(var_cost) == 1
var_cost = LinearFunctionData(var_cost[1][1])
fixed = 0.0
elseif length(cost_pairs) == 1
# if there is only one point, use it to determine the constant $/MW cost
var_cost = LinearCurve(cost_pairs[1].y)
else
@warn "Unable to calculate variable cost for $(gen.name)" cost_pairs maxlog = 5
end

return var_cost, fixed, 0.0
return var_cost, 0.0
end

function calculate_uc_cost(data, gen, fuel_cost)
Expand Down Expand Up @@ -989,7 +1056,12 @@ function make_reactive_params(
return reactive_power, reactive_power_limits
end

function make_thermal_generator(data::PowerSystemTableData, gen, cost_colnames, bus)
function make_thermal_generator(
data::PowerSystemTableData,
gen,
cost_colnames::Union{_CostPointColumns, _HeatRateColumns},
bus,
)
@debug "Making ThermaStandard" _group = IS.LOG_GROUP_PARSING gen.name
active_power_limits =
(min = gen.active_power_limits_min, max = gen.active_power_limits_max)
Expand All @@ -1001,15 +1073,8 @@ function make_thermal_generator(data::PowerSystemTableData, gen, cost_colnames,
fuel = parse_enum_mapping(ThermalFuels, gen.fuel)

base_power = gen.base_mva
var_cost, fixed, fuel_cost =
calculate_variable_cost(data, gen, cost_colnames, base_power)
startup_cost, shutdown_cost = calculate_uc_cost(data, gen, fuel_cost)
op_cost = ThermalGenerationCost(
CostCurve(InputOutputCurve(var_cost)),
fixed,
startup_cost,
shutdown_cost,
)

op_cost = make_cost(ThermalStandard, data, gen, cost_colnames)

gen_must_run = isnothing(gen.must_run) ? false : gen.must_run
if !isa(gen_must_run, Bool)
Expand Down Expand Up @@ -1047,7 +1112,7 @@ function make_thermal_generator_multistart(
@debug "Making ThermalMultiStart" _group = IS.LOG_GROUP_PARSING gen.name
base_power = get_base_power(thermal_gen)
var_cost, fixed, fuel_cost =
calculate_variable_cost(data, gen, cost_colnames, base_power)
create_pwl_cost(data, gen, cost_colnames, base_power)
if var_cost isa LinearFunctionData
no_load_cost = 0.0
else
Expand Down Expand Up @@ -1155,10 +1220,7 @@ function make_hydro_generator(
end
storage = (head = head_dict[gen.name], tail = get(tail_dict, gen.name, nothing))

var_cost, fixed, fuel_cost =
calculate_variable_cost(data, gen, cost_colnames, base_power)
operation_cost =
HydroGenerationCost(CostCurve(InputOutputCurve(var_cost)), fixed)
operation_cost = make_cost(HydroGen, data, gen, cost_colnames)

if gen_type == HydroEnergyReservoir
@debug "Creating $(gen.name) as HydroEnergyReservoir" _group =
Expand Down Expand Up @@ -1278,10 +1340,7 @@ function make_renewable_generator(
(reactive_power, reactive_power_limits) = make_reactive_params(gen)
rating = calculate_rating(active_power_limits, reactive_power_limits)
base_power = gen.base_mva
var_cost, fixed, fuel_cost =
calculate_variable_cost(data, gen, cost_colnames, base_power)
@assert fixed == 0 "RenewableGenerationCost cannot have a fixed cost, got $fixed with variable cost $varcost"
operation_cost = RenewableGenerationCost(CostCurve(InputOutputCurve(var_cost)))
operation_cost = make_cost(RenewableGen, data, gen, cost_colnames)

if gen_type == RenewableDispatch
@debug "Creating $(gen.name) as RenewableDispatch" _group = IS.LOG_GROUP_PARSING
Expand Down
2 changes: 0 additions & 2 deletions test/test_cost_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,6 @@ end
generators = collect(get_components(ThermalStandard, sys))
generator = get_component(ThermalStandard, sys, "322_CT_6")

@test_throws ArgumentError get_fuel_cost(generator) # Can't get the fuel cost of a CostCurve

op_cost = get_operation_cost(generator)
value_curve = get_value_curve(get_variable(op_cost))
set_variable!(op_cost, FuelCurve(value_curve, 0.0))
Expand Down
21 changes: 14 additions & 7 deletions test/test_power_system_table_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,25 @@ end
get_variable(mpgen_cost))),
)
if length(mp_points) == 4
cdm_points = get_points(
get_function_data(
get_value_curve(
get_variable(get_operation_cost(cdmgen))),
),
cdm_op_cost = get_operation_cost(cdmgen)
@test get_fixed(cdm_op_cost) == 0.0
fuel_curve = get_variable(cdm_op_cost)
fuel_cost = get_fuel_cost(fuel_curve)
mp_fixed = get_fixed(mpgen_cost)
io_curve = InputOutputCurve(get_value_curve(fuel_curve))
cdm_points = get_points(io_curve)
@test all(
isapprox.(
[p.y * fuel_cost for p in cdm_points],
[p.y + mp_fixed for p in mp_points],
atol = 0.1),
)
@test all(
isapprox.(
[p.y for p in cdm_points], [p.y for p in mp_points],
[p.x for p in cdm_points],
[p.x * get_base_power(mpgen) for p in mp_points],
atol = 0.1),
)
#@test PSY.compare_values(cdmgen.operation_cost, mpgen.operation_cost, compare_uuids = false)
end
end

Expand Down

0 comments on commit 13b8464

Please sign in to comment.