-
Notifications
You must be signed in to change notification settings - Fork 81
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
Initial refactor of cost functions #1071
Conversation
It was never fully supported, we can add it back at some point if we intend to fully support it.
Do we have methods to unpack the points of PieceWisePointCurves? Or any other InputOutput curve? We should have a method that wraps:
Also, are we expecting users to use the
instead of:
|
@rodrigomha I'm still ironing out the public interface (have taken a few stabs at this but I keep getting the domain-specific terminology wrong) but yes there should ultimately be a way to get all the x-coordinates and a way to get all the y-coordinates. In fact these functions exist at the underlying FunctionData level, I just need to figure out how best to wrap them. Definitely planning a more friendly |
This is good, we can just make a wrapper of Regarding the presentation, we should discuss if using |
There is an open issue for the printing. We should merge this to continue with the implementation in PSI. |
Tracking issue is #1092, I'd love some substantive review of the added source files before I merge this. |
|
||
PowerSystems.jl provides an extensive type hierarchy to explicitly express relationships between power production and cost. This lets the modeler represent cost functions as linear, quadratic, or piecewise input-output curves, potentially piecewise marginal heat rates, average heat rates, and more, as best fits the input data. | ||
|
||
To represent a cost for a particular [`Component`](@ref), the modeler first chooses one of the variable cost representations in the table below. Then, they wrap this [`ProductionVariableCost`](@ref) in either a [`CostCurve`](@ref) to indicate a cost in currency or in a [`FuelCurve`](@ref) to indicate a cost per unit of fuel plus a fuel cost. Finally, the user creates a domain-specific [`OperationalCost`](@ref) that contains this variable cost as well as other costs that may exist in that domain, such as a fixed cost that is always incurred when the unit is on. For instance, we may have `RenewableGenerationCost(CostCurve(TODO), 0.0)` to represent the cost of a renewable unit that produces at TODO, or `ThermalGenerationCost(; variable = FuelCurve(TODO), fixed = TODO, start_up = TODO, shut_down = TODO)` to represent the cost of a thermal unit that produces at TODO. Below, we give the options for `ProductionVariableCost`s. Information on what domain-specific cost must be provided for a given component type can be found in that component type's documentation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the TODOs get completed?
op_cost["shut_down"], | ||
) | ||
|
||
# TODO implement remaining _convert_op_cost methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to happen?
@@ -1,3 +1,24 @@ | |||
# MarketBidCost has two variable costs, here we mean the incremental one | |||
get_generation_variable_cost(cost::MarketBidCost) = get_incremental_offer_curves(cost) | |||
# get_generation_variable_cost(cost::OperationalCost) = get_variable_cost(cost) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to stay?
"Variable Cost TimeSeriesKey" | ||
incremental_offer_curves::Union{ | ||
Nothing, | ||
IS.TimeSeriesKey, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this become with the new time series features (variable number of key/value pairs)?
get_generation_variable_cost(cost::MarketBidCost) = get_incremental_offer_curves(cost) | ||
# get_generation_variable_cost(cost::OperationalCost) = get_variable_cost(cost) | ||
|
||
function _validate_time_series_variable_cost( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we are consistent, but we have often followed a convention where checker functions that throw start with check or _check. validate or _validate functions usually return true/false. I'm sure that isn't documented, but we should do it. It helps the reader to know when an exception can get thrown.
power_units::UnitSystem = UnitSystem.NATURAL_UNITS | ||
end | ||
|
||
CostCurve(value_curve) = CostCurve(; value_curve) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it painful to implement all of these positional argument constructors? You could require that all cost structs be constructed with kwargs. It might help with consistency.
@@ -0,0 +1,34 @@ | |||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it help users to make cost_functions a submodule? Maybe it helps searchability.
cost = PolynomialFunctionData(Dict((i, c / sys_mbase^i) for (i, c) in coeffs)) | ||
coeffs = Dict((i, c / sys_mbase^i) for (i, c) in coeffs) | ||
quadratic_degrees = [2, 1, 0] | ||
(keys(coeffs) <= Set(quadratic_degrees)) || throw( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this idiomatic Julia? It also seems more natural to me to say if error-condition throw
or error-condition && throw
. Asserts use this form.
@@ -1267,7 +1280,8 @@ function make_renewable_generator( | |||
base_power = gen.base_mva | |||
var_cost, fixed, fuel_cost = | |||
calculate_variable_cost(data, gen, cost_colnames, base_power) | |||
operation_cost = TwoPartCost(var_cost, fixed) | |||
@assert fixed == 0 "RenewableGenerationCost cannot have a fixed cost, got $fixed with variable cost $varcost" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@assert fixed == 0 "RenewableGenerationCost cannot have a fixed cost, got $fixed with variable cost $varcost" | |
@assert fixed == 0 "RenewableGenerationCost cannot have a fixed cost, got $fixed with variable cost $var_cost" |
for T in subtypes(PSY.OperationalCost) | ||
isabstracttype(T) || (@test T(nothing) isa IS.InfrastructureSystemsType) | ||
end | ||
# TODO add concrete subtypes of ProductionVariableCost? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to get done?
@GabrielKS this is a first pass at a rewrite of all the costs associated with different unit types and the market costs.