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

Degrees vs Radians inconsistency... #213

Open
carldlaird opened this issue Mar 4, 2021 · 2 comments
Open

Degrees vs Radians inconsistency... #213

carldlaird opened this issue Mar 4, 2021 · 2 comments

Comments

@carldlaird
Copy link
Collaborator

It looks like we read in "degrees" for voltage angles from the matpower based input files. However, our Pyomo models are in radians. When they put the data back into the model data object (following a solve), it looks like they are not being converted back to degrees.

@bknueven
Copy link
Collaborator

bknueven commented Mar 5, 2021

My suggestion (putting it here for posterity) is to add this to (and perhaps refactor/move) the scale/unscale capability. It probably should be its own module, at minimum.

## attributes which are scaled for power flow models
ancillary_service_stack = [
'reserve_requirement',
'spinning_reserve_requirement',
'non_spinning_reserve_requirement',
'regulation_up_requirement',
'regulation_down_requirement',
'flexible_ramp_up_requirement',
'flexible_ramp_down_requirement',
'supplemental_reserve_requirement',
'reserve_shortfall',
'spinning_reserve_shortfall',
'non_spinning_reserve_shortfall',
'regulation_up_shortfall',
'regulation_down_shortfall',
'flexible_ramp_up_shortfall',
'flexible_ramp_down_shortfall',
'supplemental_shortfall',
'reserve_price',
'spinning_reserve_price',
'non_spinning_reserve_price',
'regulation_up_price',
'regulation_down_price',
'flexible_ramp_up_price',
'flexible_ramp_down_price',
'supplemental_price',
]
## TODO?: break apart by data that needed to be scaled down (capacity limits, power),
## vs. scaled up (costs, prices, etc)
scaled_attributes = {
('element_type','generator', None): [
'p_min',
'p_max',
'p_min_agc',
'p_max_agc',
'q_min',
'q_max',
'startup_capacity',
'shutdown_capacity',
'ramp_up_60min',
'ramp_down_60min',
'initial_p_output',
'initial_q_output',
'pc1',
'pc2',
'qc1_min',
'qc1_max',
'qc2_min',
'qc2_max',
'ramp_agc',
'ramp_10',
'ramp_30',
'ramp_q',
'pg',
'qg',
'rg',
'headroom',
'reg_up_supplied',
'reg_down_supplied',
'spin_supplied',
'flex_up_supplied',
'flex_down_supplied',
'non_spinning_supplied',
'supplemental_supplied',
'p_cost',
'p_fuel',
'q_cost',
'agc_marginal_cost',
'spinning_cost',
'non_spinning_cost',
'supplemental_cost',
'spinning_capacity',
'non_spinning_capacity',
'supplemental_spinning_capacity',
'supplemental_non_spinning_capacity',
],
('element_type','storage', None): [
'energy_capacity',
'max_discharge_rate',
'min_discharge_rate',
'max_charge_rate',
'min_charge_rate',
'ramp_up_input_60min',
'ramp_down_input_60min',
'ramp_up_output_60min',
'ramp_down_output_60min',
'p_discharge',
'p_charge',
'charge_cost',
'discharge_cost',
],
('element_type','load', None) : [
'p_load',
'q_load',
'p_load_shed',
'q_load_shed',
],
('element_type','branch', None) : [
'rating_long_term',
'rating_short_term',
'rating_emergency',
'pf',
'qf',
'pt',
'qt',
'violation_penalty',
'pf_violation',
],
('element_type','dc_branch', None) : [
'rating_long_term',
'rating_short_term',
'rating_emergency',
'pf',
'qf',
'pt',
'qt',
'violation_penalty',
'pf_violation',
],
('element_type', 'shunt', None) : [
'bs',
'gs',
'bs_min',
'bs_max',
'gs_min',
'gs_max',
],
('element_type', 'area', None) : [
] + \
ancillary_service_stack,
('element_type', 'zone', None) : [
] + \
ancillary_service_stack,
('element_type', 'interface', None) : [
'minimum_limit',
'maximum_limit',
'pf',
'qf',
'pt',
'qt',
'violation_penalty',
'pf_violation',
],
('element_type', 'bus', None) : [
'p_balance_violation',
'q_balance_violation',
'lmp',
'q_lmp',
'qlmp',
'pl',
'ql',
],
('element_type', 'security_constraint', 'pg') : [ 'lower_bound',
'upper_bound',
'violation_penalty',
'pf',
'pf_violation',
],
('element_type', 'contingency', None) : [
'violation_penalty',
'pf',
'pf_violation',
],
('system_attributes', None, None ) : [
'load_mismatch_cost',
'q_load_mismatch_cost',
'reserve_shortfall_cost',
] + \
ancillary_service_stack,
}
def scale_ModelData_to_pu(model_data, inplace=False):
return _convert_modeldata_pu(model_data, _divide_by_baseMVA, inplace)
def unscale_ModelData_to_pu(model_data, inplace=False):
return _convert_modeldata_pu(model_data, _multiply_by_baseMVA, inplace)
def _multiply_by_baseMVA(element, attr_name, attr, baseMVA, attributes):
_scale_by_baseMVA(_mul, _div, element, attr_name, attr, baseMVA, attributes)
def _divide_by_baseMVA(element, attr_name, attr, baseMVA, attributes):
_scale_by_baseMVA(_div, _mul, element, attr_name, attr, baseMVA, attributes)
def _mul(a,b):
return a*b
def _div(a,b):
return a/b
def _get_op(normal_op, inverse_op, attr_name):
if ('cost' in attr_name) or ('price' in attr_name) or ('lmp' in attr_name) or ('penalty' in attr_name):
return inverse_op
return normal_op
def _scale_by_baseMVA(normal_op, inverse_op, element, attr_name, attr, baseMVA, attributes):
if attr is None:
return
if isinstance(attr, dict):
if 'data_type' in attr and attr['data_type'] == 'time_series':
op = _get_op(normal_op, inverse_op, attr_name)
values_list = attr['values']
for time, value in enumerate(values_list):
if isinstance(value, dict):
_scale_by_baseMVA(normal_op, inverse_op, element, attr_name, value, baseMVA, attributes)
else:
values_list[time] = op( value , baseMVA )
elif 'data_type' in attr and attr['data_type'] == 'cost_curve':
if attr['cost_curve_type'] == 'polynomial':
values_dict = attr['values']
new_values = { int(power): coeff*(inverse_op(1.,baseMVA)**int(power)) \
for (power, coeff) in values_dict.items() }
attr['values'] = new_values
elif attr['cost_curve_type'] == 'piecewise':
values_list_of_tuples = attr['values']
new_values = [ ( normal_op(point,baseMVA), cost) \
for (point, cost) in values_list_of_tuples ]
attr['values'] = new_values
elif 'data_type' in attr and attr['data_type'] == 'fuel_curve':
values_list_of_tuples = attr['values']
new_values = [ ( normal_op(point,baseMVA), fuel) \
for (point, fuel) in values_list_of_tuples ]
attr['values'] = new_values
else: # recurse deeper
for k,v in attr.items():
_scale_by_baseMVA(normal_op, inverse_op, attr, k, v, baseMVA, attributes)
elif attr_name in attributes:
op = _get_op(normal_op, inverse_op, attr_name)
element[attr_name] = op( attr , baseMVA )
else:
return
## NOTE: ideally this would be done in the definitions of
## these constraints. Futher, it is not obvious that
## the baseMVA provides the best scaling
## NOTE: specifying inplace returns None
def _convert_modeldata_pu(model_data, transform_func, inplace):
if inplace:
md = model_data
else:
md = model_data.clone()
baseMVA = float(md.data['system']['baseMVA'])
for (attr_type, element_type, element_subtype), attributes in scaled_attributes.items():
if attr_type == 'system_attributes':
system_dict = md.data['system']
assert element_type is None
assert element_subtype is None
for name, sys_attr in system_dict.items():
transform_func(system_dict, name, sys_attr, baseMVA, attributes)
elif attr_type == 'element_type':
if element_type not in md.data['elements']:
continue
element_dict = md.data['elements'][element_type]
if element_subtype is None:
for name, element in element_dict.items():
for attr_name, attr in element.items():
transform_func(element, attr_name, attr, baseMVA, attributes)
else: ## allow for different actions depending on the subtype
for name, element in element_dict.items():
element_subtype_key = element_type+'_type'
if element_subtype == element[element_subtype_key]:
for attr_name, attr in element.items():
transform_func(element, attr_name, attr, baseMVA, attributes)
if inplace:
return
else:
return md

@jeanpaulwatson
Copy link
Collaborator

This is definitely a broad issue. For example, when we write incumbent-finders or fixing schemes that are problem-specific, e.g., for unit commitment, we're working typically working directly on Egret models. I'm with Ben in that the we need a general scale/unscale module, to include angles, the stuff Ben mentions above, and anything per-unit related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants