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

Switching to tx_util piecewise validator #278

Merged
merged 2 commits into from
May 5, 2022
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
136 changes: 19 additions & 117 deletions egret/model_library/unit_commitment/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -1007,13 +1007,6 @@ def _check_curve(m, g, curve, curve_type):
curve_t = curve
_t = None

tx_utils.validate_and_clean_cost_curve(curve_t,
curve_type=curve_type,
p_min=value(m.MinimumPowerOutput[g, t]),
p_max=value(m.MaximumPowerOutput[g, t]),
gen_name=g,
t=_t)

# if no curve_type+'_type' is specified, we assume piecewise (for backwards
# compatibility with no 'fuel_curve_type')
if curve_type + '_type' in curve and \
Expand All @@ -1040,24 +1033,12 @@ def validate_cost_rule(m, g):
if cost is not None and fuel is not None:
logger.warning("WARNING: ignoring provided p_cost and using fuel cost data from p_fuel for generator {}".format(g))

## look at p_cost through time
if fuel is None:
_check_curve(m, g, cost, 'cost_curve')
else:
if fuel_cost is None:
raise Exception("Found fuel_curve but not fuel_cost for generator {}".format(g))
_check_curve(m, g, fuel, 'fuel_curve')
for i, t in enumerate(m.TimePeriods):
if fuel_cost is dict:
if fuel_cost['data_type'] != 'time_series':
raise Exception("fuel_cost must be either numeric or time_series")
fuel_cost_t = fuel_cost['values'][i]
else:
fuel_cost_t = fuel_cost
if fuel_cost_t < 0:
raise Exception("fuel_cost must be non-negative, found negative fuel_cost for generator {}".format(g))
if fuel_cost_t == fuel_cost:
break
return True

model.ValidateGeneratorCost = BuildCheck(model.ThermalGenerators, rule=validate_cost_rule)
Expand Down Expand Up @@ -1095,103 +1076,23 @@ def validate_cost_rule(m, g):
_minimum_production_cost = {}
_minimum_fuel_consumption = {}

def _piecewise_adjustment_helper(m, g, p_min, p_max, curve, curve_type, t):

def _eliminate_piecewise_duplicates(input_func):
if len(input_func) <= 1:
return input_func
new = [input_func[0]]
for (o1, c1), (o2, c2) in zip(input_func, input_func[1:]):
if not math.isclose(o1,o2) and not math.isclose(c1,c2):
new.append((o2,c2))
return new

def _much_less_than(v1, v2):
return v1 < v2 and not math.isclose(v1,v2)

def _piecewise_adjustment_helper(m, p_min, p_max, input_func):

minimum_val = 0.
new_points = []
new_vals = []

input_func = _eliminate_piecewise_duplicates(input_func)
cleaned_values = tx_utils.validate_and_clean_cost_curve(curve,
curve_type=curve_type,
p_min=p_min,
p_max=p_max,
gen_name=g,
t=t)

set_p_min = False
p_min, p_min_cost = cleaned_values[0]
new_points = [0.,]
new_vals = [0.,]
for pnt, val in cleaned_values[1:]:
new_points.append(pnt-p_min)
new_vals.append(val-p_min_cost)

# NOTE: this implicitly inserts a (0.,0.)
# into every cost array
prior_output, prior_cost = 0., 0.

for output, cost in input_func:
## catch this case
if math.isclose(output, p_min) and math.isclose(output, p_max):
new_points.append(0.)
new_vals.append(0.)
minimum_val = cost
break

## output < p_min
elif _much_less_than(output, p_min):
pass

## p_min == output
elif math.isclose(output, p_min):
assert set_p_min is False
new_points.append(0.)
new_vals.append(0.)
minimum_val = cost
set_p_min = True

## p_min < output
elif _much_less_than(p_min, output) and _much_less_than(output, p_max):
if not set_p_min:
new_points.append(0.)
new_vals.append(0.)

price = ((cost-prior_cost)/(output-prior_output))
minimum_val = (p_min - prior_output) * price + prior_cost

new_points.append( output - p_min )
new_vals.append( (output - p_min) * price )

set_p_min = True
else:
new_points.append( output - p_min )
new_vals.append( cost - minimum_val )

elif math.isclose(output, p_max) or _much_less_than(p_max, output):
if not set_p_min:
new_points.append(0.)
new_vals.append(0.)

price = ((cost-prior_cost)/(output-prior_output))
minimum_val = (p_min - prior_output) * price + prior_cost

new_points.append( p_max - p_min )

if math.isclose(output, p_max):
new_vals.append( cost - minimum_val )
else:
new_vals.append( (p_max - p_min) * price )
set_p_min = True

else:
new_points.append( p_max - p_min )
if math.isclose(output, p_max):
new_vals.append( cost - minimum_val )
else:
price = ((cost-prior_cost)/(output-prior_output))
new_vals.append( (p_max - prior_output) * price + prior_cost - minimum_val )

break

else:
raise Exception("Unexpected case in _piecewise_adjustment_helper, "
"p_min={}, p_max={}, output={}".format(p_min, p_max, output))

prior_output, prior_cost = output, cost

return new_points, new_vals, minimum_val
return new_points, new_vals, p_min_cost

def _polynomial_to_piecewise_helper(m, p_min, p_max, input_func):
segment_max = value(m.NumGeneratorCostCurvePieces)
Expand Down Expand Up @@ -1227,12 +1128,13 @@ def _polynomial_to_piecewise_helper(m, p_min, p_max, input_func):

return new_points, new_vals, minimum_val

def _piecewise_helper(m, p_min, p_max, curve, curve_type):
def _piecewise_helper(m, g, p_min, p_max, curve, curve_type, t):
if curve_type not in curve or \
curve[curve_type] == 'piecewise':
return _piecewise_adjustment_helper(m, p_min, p_max, curve['values'])
return _piecewise_adjustment_helper(m, g, p_min, p_max, curve, curve['data_type'], t)
else:
assert curve[curve_type] == 'polynomial'
tx_utils.validate_and_clean_cost_curve(curve, curve['data_type'], p_min, p_max, g, t)
return _polynomial_to_piecewise_helper(m, p_min, p_max, curve['values'])


Expand Down Expand Up @@ -1289,7 +1191,7 @@ def power_generation_piecewise_points_rule(m, g):
m.PowerGenerationPiecewiseCostValues[g,t] = curve['cost_values']
continue

points, values, minimum_val = _piecewise_helper(m, p_min, p_max, fuel_curve, 'fuel_curve_type')
points, values, minimum_val = _piecewise_helper(m, g, p_min, p_max, fuel_curve, 'fuel_curve_type',t)

curve = { 'points' : points }

Expand Down Expand Up @@ -1346,7 +1248,7 @@ def power_generation_piecewise_points_rule(m, g):
values = [0., 0.]
min_production = nlc
else:
points, values, minimum_val = _piecewise_helper(m, p_min, p_max, cost_curve, 'cost_curve_type')
points, values, minimum_val = _piecewise_helper(m, g, p_min, p_max, cost_curve, 'cost_curve_type', t)
min_production = minimum_val + nlc

curve = {'points':points, 'cost_values':values, 'min_production':min_production}
Expand Down
Loading