def test(CostExpression): from testfixtures import compare (coefficients_sympify, var_names_sympify) = coef_via_sympify(CostExpression) (coefficients_pyomo, var_names_pyomo) = coef_via_pyomo(CostExpression) compare(var_names_sympify, var_names_pyomo) # I have to use sorted because keys come out as randomly orderd tuples # and compare got hung up on inconsequential differences their ordering. compare(sorted(coefficients_sympify.keys()), sorted(coefficients_pyomo.keys())) # I rolled my own compare tool for cost_coefficients because compare # insists on numeric equality, and the sympify's round-trip of # binary->text->binary results in slight rounding errors. from switch_model.utilities import approx_equal for vid in coefficients_pyomo.keys(): assert(approx_equal(coefficients_sympify[vid], coefficients_pyomo[vid], tolerance=.000001))
def test_approx_equal(self): assert not utilities.approx_equal(1, 2) assert not utilities.approx_equal(1, 1.02) assert utilities.approx_equal(1, 1.01) assert utilities.approx_equal(1, 1)
def load_inputs(mod, switch_data, inputs_dir): """ Import data to support modeling fuel use under partial loading conditions with piecewise linear incremental heat rates. These files are formatted differently than most to match the standard format of incremental heat rates. This format is peculiar because it formats data records that describes a fuel use curve in two disticnt ways. The first record is the first point on the curve, but all subsequent records are slopes and x-domain for each line segment. For a given generation technology or project, the relevant data should be formatted like so: power_start_mw power_end_mw ihr fuel_use_rate min_load . . value min_load mid_load1 value . mid_load1 max_load value . The first row provides the first point on the input/output curve. Literal dots should be included to indicate blanks. The column fuel_use_rate is in units of MMBTU/h. Subsequent rows provide the domain and slope of each line segement. The column ihr indicates incremental heat rate in MMBTU/MWh. Any number of line segments will be accepted. All text should be replaced with actual numerical values. I chose this format to a) be relatively consistent with standard data that is easiest to find, b) make it difficult to misinterpret the meaning of the data, and c) allow all of the standard data to be included in a single file. The following files are optional. If no representative data is provided for a generation technology, it will default to a single line segment with an intercept of 0 and a slope equal to the full load heat22 rate. If no specific data is provided for a project, it will default to its generation technology. gen_inc_heat_rates.tab project, power_start_mw, power_end_mw, incremental_heat_rate_mbtu_per_mwhr, fuel_use_rate_mmbtu_per_h """ path = os.path.join(inputs_dir, 'gen_inc_heat_rates.tab') if os.path.isfile(path): (fuel_rate_segments, min_load, full_hr) = _parse_inc_heat_rate_file(path, id_column="GENERATION_PROJECT") # Check implied minimum loading level for consistency with # gen_min_load_fraction if gen_min_load_fraction was provided. If # gen_min_load_fraction wasn't provided, set it to implied minimum # loading level. for g in min_load: if 'gen_min_load_fraction' not in switch_data.data(): switch_data.data()['gen_min_load_fraction'] = {} dp_dict = switch_data.data(name='gen_min_load_fraction') if g in dp_dict: min_load_dat = dp_dict[g] if not approx_equal(min_load[g], min_load_dat): raise ValueError( ("gen_min_load_fraction is inconsistant with " + "incremental heat rate data for project " + "{}.").format(g)) else: dp_dict[g] = min_load[g] # Same thing, but for full load heat rate. for g in full_hr: if 'gen_full_load_heat_rate' not in switch_data.data(): switch_data.data()['gen_full_load_heat_rate'] = {} dp_dict = switch_data.data(name='gen_full_load_heat_rate') if g in dp_dict: full_hr_dat = dp_dict[g] if abs((full_hr[g] - full_hr_dat) / full_hr_dat) > 0.01: raise ValueError( ("gen_full_load_heat_rate is inconsistant with " + "incremental heat rate data for project " + "{}.").format(g)) else: dp_dict[g] = full_hr[g] # Copy parsed data into the data portal. switch_data.data()['FUEL_USE_SEGMENTS_FOR_GEN'] = fuel_rate_segments
def load_inputs(mod, switch_data, inputs_dir): """ Import data to support modeling fuel use under partial loading conditions with piecewise linear incremental heat rates. These files are formatted differently than most to match the standard format of incremental heat rates. This format is peculiar because it formats data records that describes a fuel use curve in two disticnt ways. The first record is the first point on the curve, but all subsequent records are slopes and x-domain for each line segment. For a given generation technology or project, the relevant data should be formatted like so: power_start_mw power_end_mw ihr fuel_use_rate min_load . . value min_load mid_load1 value . mid_load1 max_load value . The first row provides the first point on the input/output curve. Literal dots should be included to indicate blanks. The column fuel_use_rate is in units of MMBTU/h. Subsequent rows provide the domain and slope of each line segement. The column ihr indicates incremental heat rate in MMBTU/MWh. Any number of line segments will be accepted. All text should be replaced with actual numerical values. I chose this format to a) be relatively consistent with standard data that is easiest to find, b) make it difficult to misinterpret the meaning of the data, and c) allow all of the standard data to be included in a single file. The following files are optional. If no representative data is provided for a generation technology, it will default to a single line segment with an intercept of 0 and a slope equal to the full load heat22 rate. If no specific data is provided for a project, it will default to its generation technology. gen_inc_heat_rates.tab project, power_start_mw, power_end_mw, incremental_heat_rate_mbtu_per_mwhr, fuel_use_rate_mmbtu_per_h """ path = os.path.join(inputs_dir, 'gen_inc_heat_rates.tab') if os.path.isfile(path): (fuel_rate_segments, min_load, full_hr) = _parse_inc_heat_rate_file( path, id_column="project") # Check implied minimum loading level for consistency with # gen_min_load_fraction if gen_min_load_fraction was provided. If # gen_min_load_fraction wasn't provided, set it to implied minimum # loading level. for g in min_load: if 'gen_min_load_fraction' not in switch_data.data(): switch_data.data()['gen_min_load_fraction'] = {} dp_dict = switch_data.data(name='gen_min_load_fraction') if g in dp_dict: min_load_dat = dp_dict[g] if not approx_equal(min_load[g], min_load_dat): raise ValueError(( "gen_min_load_fraction is inconsistant with " + "incremental heat rate data for project " + "{}.").format(g)) else: dp_dict[g] = min_load[g] # Same thing, but for full load heat rate. for g in full_hr: if 'gen_full_load_heat_rate' not in switch_data.data(): switch_data.data()['gen_full_load_heat_rate'] = {} dp_dict = switch_data.data(name='gen_full_load_heat_rate') if g in dp_dict: full_hr_dat = dp_dict[g] if abs((full_hr[g] - full_hr_dat) / full_hr_dat) > 0.01: raise ValueError(( "gen_full_load_heat_rate is inconsistant with " + "incremental heat rate data for project " + "{}.").format(g)) else: dp_dict[g] = full_hr[g] # Copy parsed data into the data portal. switch_data.data()['FUEL_USE_SEGMENTS_FOR_GEN'] = fuel_rate_segments