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

fix: Adding support for quadratic functions using the table data parser #1178

Merged
merged 8 commits into from
Aug 28, 2024
3 changes: 3 additions & 0 deletions src/PowerSystems.jl
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,9 @@ export get_compression_settings
export CompressionSettings
export CompressionTypes

# Parsing functions
export create_poly_cost

#export make_time_series
export get_bus_numbers
export get_name
Expand Down
15 changes: 15 additions & 0 deletions src/descriptors/power_system_inputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,21 @@
"description": "Heat required to startup from cold",
"default_value": null
},
{
"name": "heat_rate_a0",
"description": "Heat rate constant term",
"default_value": null
},
{
"name": "heat_rate_a1",
"description": "Heat rate proportional term",
"default_value": null
},
{
"name": "heat_rate_a2",
"description": "Heat rate quadratic term",
"default_value": null
},
{
"name": "heat_rate_avg_0",
"description": "Heat rate Average 0 TODO",
Expand Down
51 changes: 49 additions & 2 deletions src/parsers/power_system_table_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,16 @@
) where {T <: ThermalGen}
fuel_price = gen.fuel_price / 1000.0

cost_pairs = get_cost_pairs(gen, cost_colnames)
var_cost, fixed = create_pwinc_cost(cost_pairs)
# We check if there is any Quadratic or Linear Data defined. If not we fall back to create PiecewiseIncrementalCurve
quadratic_fields = (gen.heat_rate_a0, gen.heat_rate_a1, gen.heat_rate_a2)

if any(field -> field != nothing, quadratic_fields)
pesap marked this conversation as resolved.
Show resolved Hide resolved
var_cost, fixed =

Check warning on line 830 in src/parsers/power_system_table_data.jl

View check run for this annotation

Codecov / codecov/patch

src/parsers/power_system_table_data.jl#L830

Added line #L830 was not covered by tests
create_poly_cost(gen, ["heat_rate_a0", "heat_rate_a1", "heat_rate_a2"])
else
cost_pairs = get_cost_pairs(gen, cost_colnames)
var_cost, fixed = create_pwinc_cost(cost_pairs)
end

startup_cost, shutdown_cost = calculate_uc_cost(data, gen, fuel_price)

Expand Down Expand Up @@ -956,6 +964,45 @@
return var_cost
end

"""
create_poly_cost(gen, cost_colnames)

Return a Polynomial function cost based on the coeffiecients provided on gen.

Three supported cases,
1. If three values are passed then we have data looking like: `a2 * x^2 + a1 * x + a0`,
2. If `a1` and `a0` are passed then we have data looking like: `a1 * x + a0`,
3. If only `a1` is passed then we have data looking like: `a1 * x`.
"""
function create_poly_cost(
gen, cost_colnames,
)
fixed_cost = 0.0
parse_maybe_nothing(x) = isnothing(x) ? nothing : tryparse(Float64, x)
a2 = parse_maybe_nothing(getfield(gen, Symbol("heat_rate_a2")))
a1 = parse_maybe_nothing(getfield(gen, Symbol("heat_rate_a1")))
a0 = parse_maybe_nothing(getfield(gen, Symbol("heat_rate_a0")))

if !isnothing(a2) && (isnothing(a1) || isnothing(a0))
throw(
DataFormatError(
"All coefficients must be passed if quadratic term is passed.",
),
)
end

if !any(isnothing.([a2, a1, a0]))
@debug "QuadraticCurve created for $(gen.name)"
return QuadraticCurve(a2, a1, a0), fixed_cost
end
if all(isnothing.([a2, a0])) && !isnothing(a1)
@debug "LinearCurve created for $(gen.name)"
return LinearCurve(a1), fixed_cost
end
@debug "LinearCurve created for $(gen.name)"
return LinearCurve(a1, a0), fixed_cost
end

function create_pwinc_cost(
cost_pairs,
)
Expand Down
82 changes: 82 additions & 0 deletions test/test_power_system_table_data.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,85 @@ end
g = get_components(ThermalStandard, sys)
@test get_variable.(get_operation_cost.(g)) == get_variable.(get_operation_cost.(g))
end

@testset "Test create_poly_cost function" begin
cost_colnames = ["heat_rate_a0", "heat_rate_a1", "heat_rate_a2"]

# Coefficients for a CC using natural gas
a2 = -0.000531607
a1 = 0.060554675
a0 = 8.951100118

# First test that return quadratic if all coefficients are provided.
# We convert the coefficients to string to mimic parsing from csv
example_generator = (
name = "test-gen",
heat_rate_a0 = string(a0),
heat_rate_a1 = string(a1),
heat_rate_a2 = string(a2),
)
cost_curve, fixed_cost = create_poly_cost(example_generator, cost_colnames)
@assert cost_curve isa QuadraticCurve
@assert isapprox(get_quadratic_term(cost_curve), a2, atol = 0.01)
@assert isapprox(get_proportional_term(cost_curve), a1, atol = 0.01)
@assert isapprox(get_constant_term(cost_curve), a0, atol = 0.01)

# Test return linear with both proportional and constant term
example_generator = (
name = "test-gen",
heat_rate_a0 = string(a0),
heat_rate_a1 = string(a1),
heat_rate_a2 = nothing,
)
cost_curve, fixed_cost = create_poly_cost(example_generator, cost_colnames)
@assert cost_curve isa LinearCurve
@assert isapprox(get_proportional_term(cost_curve), a1, atol = 0.01)
@assert isapprox(get_constant_term(cost_curve), a0, atol = 0.01)

# Test return linear with just proportional term
example_generator = (
name = "test-gen",
heat_rate_a0 = nothing,
heat_rate_a1 = string(a1),
heat_rate_a2 = nothing,
)
cost_curve, fixed_cost = create_poly_cost(example_generator, cost_colnames)
@assert cost_curve isa LinearCurve
@assert isapprox(get_proportional_term(cost_curve), a1, atol = 0.01)

# Test raises error if a2 is passed but other coefficients are nothing
example_generator = (
name = "test-gen",
heat_rate_a0 = nothing,
heat_rate_a1 = nothing,
heat_rate_a2 = string(a2),
)
@test_throws IS.DataFormatError create_poly_cost(example_generator, cost_colnames)
example_generator = (
name = "test-gen",
heat_rate_a0 = nothing,
heat_rate_a1 = string(a1),
heat_rate_a2 = string(a2),
)
@test_throws IS.DataFormatError create_poly_cost(example_generator, cost_colnames)
example_generator = (
name = "test-gen",
heat_rate_a0 = string(a0),
heat_rate_a1 = nothing,
heat_rate_a2 = string(a2),
)
@test_throws IS.DataFormatError create_poly_cost(example_generator, cost_colnames)

# Test that it works with zero proportional and constant term
example_generator = (
name = "test-gen",
heat_rate_a0 = string(0.0),
heat_rate_a1 = string(0.0),
heat_rate_a2 = string(a2),
)
cost_curve, fixed_cost = create_poly_cost(example_generator, cost_colnames)
@assert cost_curve isa QuadraticCurve
@assert isapprox(get_quadratic_term(cost_curve), a2, atol = 0.01)
@assert isapprox(get_proportional_term(cost_curve), 0.0, atol = 0.01)
@assert isapprox(get_constant_term(cost_curve), 0.0, atol = 0.01)
end
Loading