diff --git a/powersimdata/input/change_table.py b/powersimdata/input/change_table.py index a52650be5..69f8e9fda 100644 --- a/powersimdata/input/change_table.py +++ b/powersimdata/input/change_table.py @@ -400,12 +400,12 @@ def _add_line(self, key, info): else: start = line['from_bus_id'] end = line['to_bus_id'] - if start not in self.grid['bus'].index: + if start not in self.grid.bus.index: print("No bus with the following id for line #%d: %d" % (i + 1, start)) self.ct.pop(key) return - elif end not in self.grid['bus'].index: + elif end not in self.grid.bus.index: print("No bus with the following id for line #%d: %d" % (i + 1, end)) self.ct.pop(key) @@ -419,17 +419,17 @@ def _add_line(self, key, info): self.ct.pop(key) return elif key == 'new_branch' and \ - self.grid['bus'].interconnect[start] != \ - self.grid['bus'].interconnect[end]: + self.grid.bus.interconnect[start] != \ + self.grid.bus.interconnect[end]: print("Buses of line #%d must be in same interconnect" % (i + 1)) self.ct.pop(key) return - elif haversine((self.grid['bus'].lat[start], - self.grid['bus'].lon[start]), - (self.grid['bus'].lat[end], - self.grid['bus'].lon[end])) == 0: + elif haversine((self.grid.bus.lat[start], + self.grid.bus.lon[start]), + (self.grid.bus.lat[end], + self.grid.bus.lon[end])) == 0: print("Distance between buses of line #%d is 0" % (i + 1)) self.ct.pop(key) return diff --git a/powersimdata/input/grid.py b/powersimdata/input/grid.py index 1beed0c8f..673f44aa0 100644 --- a/powersimdata/input/grid.py +++ b/powersimdata/input/grid.py @@ -1,19 +1,14 @@ import os -import warnings from powersimdata.input.usa_tamu_model import TAMU from powersimdata.input.scenario_grid import FromREISE, FromREISEjl -from powersimdata.input.grid_fields \ - import AbstractGridField, Branch, Bus, DCLine, GenCost, Plant, Storage, Sub class Grid(object): """Grid - """ def __init__(self, interconnect, source='usa_tamu', engine='REISE'): """Constructor - :param list interconnect: interconnect name(s). :param str source: model used to build the network. :param str engine: engine used to run scenario, if using ScenarioGrid. @@ -39,96 +34,28 @@ def __init__(self, interconnect, source='usa_tamu', engine='REISE'): else: raise ValueError('%s not implemented' % source) - # Specific grid info self.data_loc = data.data_loc self.interconnect = data.interconnect - - self.fields = {} - self.transform = {} - - # Input data as grid fields - self.fields['bus'] = Bus(data.bus) - self.fields['branch'] = Branch(data.branch) - self.fields['dcline'] = DCLine(data.dcline) - self.fields['gencost'] = GenCost(data.gencost) - self.fields['plant'] = Plant(data.plant) - self.fields['sub'] = Sub(data.sub) - self.fields['storage'] = Storage(data.storage) - - # Conversion helpers - self.transform['bus2sub'] = data.bus2sub - self.transform['zone2id'] = data.zone2id - self.transform['id2zone'] = data.id2zone - self.transform['id2type'] = get_id2type() - self.transform['type2id'] = {value: key for key, value in - self.transform['id2type'].items()} - - # Plotting helper - self.transform['type2color'] = get_type2color() - - def __getattr__(self, prop_name): - """ - Overrides the object "." property interface to maintain backwards - compatibility, i.e. grid.plant - is the same as grid.fields["plant"], or grid.transform["bus2sub"] - - :param str prop_name: property name as string - :raises KeyError: For attempts to use key not in the dictionary - :return: property of the Grid class - """ - - # needed for deepcopy to work - if prop_name == "__deepcopy__": - return super().__deepcopy__ - if prop_name == "__len__": - return super().__len__ - if prop_name == "__getstate__": - return super().__getstate__ - if prop_name == "__setstate__": - return super().__setstate__ - - # switch between transform and grid_field attributes - if prop_name in ['bus2sub', 'zone2id', 'id2zone', 'id2type', - 'type2id', 'type2color']: - return self.transform[prop_name] - else: - try: - warnings.warn( - "Grid property access is moving to dictionary indexing, " - "i.e. grid['branch'] consistent with REISE.jl", - DeprecationWarning - ) - return self.fields[prop_name].data - except AttributeError as e: - print(e) - - def __getitem__(self, field_name): - """ - Allows indexing into the resources dictionary directly from the - object variable, i.e. grid["plant"] is the - same as grid.fields["plant"] - :param str field_name: grid field name as string - :raises KeyError: For attempts to use key not in the dictionary - :return: property of the Grid class - """ - try: - return self.fields[field_name].data - except KeyError as e: - print(e) - - def __setattr__(self, name, value): - if name in ['data_loc', 'interconnect', 'fields', 'transform']: - super().__setattr__(name, value) - elif name in self.fields: - self.fields[name].data = value - else: - super().__setattr__(name, value) + self.zone2id = data.zone2id + self.id2zone = data.id2zone + self.sub = data.sub + self.plant = data.plant + self.gencost = data.gencost + self.dcline = data.dcline + self.bus2sub = data.bus2sub + self.bus = data.bus + self.branch = data.branch + self.storage = data.storage + self.type2color = get_type2color() + self.id2type = get_id2type() + self.type2id = {value: key for key, value in self.id2type.items()} def __eq__(self, other): """Used when 'self == other' is evaluated. :param object other: other object to be compared against. :return: (*bool*). """ + def _univ_eq(ref, test): """Check for {boolean, dataframe, or column data} equality. :param object ref: one object to be tested (order does not matter). @@ -149,55 +76,65 @@ def _univ_eq(ref, test): if not isinstance(other, Grid): err_msg = 'Unable to compare Grid & %s' % type(other).__name__ raise NotImplementedError(err_msg) - assert self.fields.keys() == other.fields.keys() - assert self.transform.keys() == other.transform.keys() - # Check all AbstractGridField attributes + try: - for k, v in self.fields.items(): - if isinstance(v, GenCost): - # Comparing 'after' will fail if one Grid was linearized - self_data = self.fields[k].data['before'] - other_data = other.fields[k].data['before'] - _univ_eq(self_data, other_data) - elif isinstance(v, Storage): - self_storage_num = len(self.fields[k].data['gencost']) - other_storage_num = len(other.fields[k].data['gencost']) - if self_storage_num == 0: - assert other_storage_num == 0 - continue - # These are dicts, so we need to go one level deeper - self_keys = self.fields[k].data.keys() - other_keys = other.fields[k].data.keys() - assert self_keys == other_keys - for subkey in self_keys: - # REISE will modify gencost and some gen columns - if subkey == 'gencost': - continue - self_data = self.fields[k].data[subkey] - other_data = other.fields[k].data[subkey] + # compare gencost + # Comparing 'after' will fail if one Grid was linearized + self_data = self.gencost['before'] + other_data = other.gencost['before'] + _univ_eq(self_data, other_data) + + # compare storage + self_storage_num = len(self.storage['gencost']) + other_storage_num = len(other.storage['gencost']) + if self_storage_num == 0: + assert other_storage_num == 0 + else: + # These are dicts, so we need to go one level deeper + self_keys = self.storage.keys() + other_keys = other.storage.keys() + assert self_keys == other_keys + for subkey in self_keys: + # REISE will modify gencost and some gen columns + if subkey != 'gencost': + self_data = self.storage[subkey] + other_data = other.storage[subkey] if subkey == 'gen': excluded_cols = ['ramp_10', 'ramp_30'] self_data = self_data.drop(excluded_cols, axis=1) other_data = other_data.drop(excluded_cols, axis=1) _univ_eq(self_data, other_data) - elif isinstance(v, Bus): - # MOST changes BUS_TYPE for buses with DC Lines attached - self_df = self.fields[k].data.drop('type', axis=1) - other_df = other.fields[k].data.drop('type', axis=1) - _univ_eq(self_df, other_df) - elif isinstance(v, Plant): - # REISE does some modifications to Plant data - excluded_cols = ['status', 'Pmin', 'ramp_10', 'ramp_30'] - self_df = self.fields[k].data.drop(excluded_cols, axis=1) - other_df = other.fields[k].data.drop(excluded_cols, axis=1) - _univ_eq(self_df, other_df) - elif isinstance(v, AbstractGridField): - _univ_eq(self.fields[k].data, other.fields[k].data) - else: - _univ_eq(self.fields[k], other.fields[k]) - # Check the transform attributes - for k, v in self.transform.items(): - _univ_eq(self.transform[k], other.transform[k]) + + # compare bus + # MOST changes BUS_TYPE for buses with DC Lines attached + self_df = self.bus.drop('type', axis=1) + other_df = other.bus.drop('type', axis=1) + _univ_eq(self_df, other_df) + + # compare plant + # REISE does some modifications to Plant data + excluded_cols = ['status', 'Pmin', 'ramp_10', 'ramp_30'] + self_df = self.plant.drop(excluded_cols, axis=1) + other_df = other.plant.drop(excluded_cols, axis=1) + _univ_eq(self_df, other_df) + + # compare branch + _univ_eq(self.branch, other.branch) + + # compare dcline + _univ_eq(self.dcline, other.dcline) + + # compare sub + _univ_eq(self.sub, other.sub) + + # check grid helper function equality + _univ_eq(self.type2color, other.type2color) + _univ_eq(self.id2type, other.id2type) + _univ_eq(self.type2id, other.type2id) + _univ_eq(self.zone2id, other.zone2id) + _univ_eq(self.id2zone, other.id2zone) + _univ_eq(self.bus2sub, other.bus2sub) + except: return False return True @@ -205,7 +142,6 @@ def _univ_eq(ref, test): def get_type2color(): """Defines generator type to generator color mapping. Used for plotting. - :return: (*dict*) -- generator type to color mapping. """ type2color = { @@ -227,7 +163,6 @@ def get_type2color(): def get_id2type(): """Defines generator type to generator id mapping. - :return: (*tuple*) -- generator type to generator id mapping. """ id2type = { diff --git a/powersimdata/input/grid_fields.py b/powersimdata/input/grid_fields.py deleted file mode 100644 index d5f36d171..000000000 --- a/powersimdata/input/grid_fields.py +++ /dev/null @@ -1,228 +0,0 @@ -class FieldHierarchicalIndex: - """ - The class wraps indices from our grid fields simplifying the interface - and providing context such as the dataframe the index originated from - to aid in input validation - """ - def __init__(self, index, index_hierarchy, origin_dataframe): - """ - :param pandas.Series index: Pandas series with multi-level index - :param list index_hierarchy: list hierarchy for the hierarchical index - :param str origin_dataframe: dataframe this index originated from - """ - self._index = index - self.index_hierarchy = index_hierarchy - self.origin_dataframe = origin_dataframe - - def get_idx(self, index_tuple): - """ - returns a native single-dimensional python index from the - groupby object - :param tuple index_tuple: desired grouping - :return: (*list*) -- native single-dimensional python index - """ - return self._index.loc[index_tuple].values.tolist() - - def __getitem__(self, index_tuple): - """ - gets a list of indices for a given tuple - :param tuple index_tuple: desired grouping - :return: (*list*) -- python list of indices - """ - return self.get_idx(self, index_tuple) - - -class AbstractGridField: - """ - This define base level functionality of a grid data field - We can put general data validation and utility functions here - """ - def __init__(self, data, name, transform): - """ - :param pandas.DataFrame data: dataframe for this grid field - :param str name: name of the grid field - :param dict transform: dictionary of valid transformations on the data - """ - self.data = data - self.name = name - self.transform = transform - - -class HierarchicalGridField(AbstractGridField): - """ - This defines a grid field that can support hierarchical indexing - """ - def ct_hierarchy_iterator(self, change_table, ct_hierarchy): - """ - This function wraps validation for the static recursive Python - generator function hierarchy_generator - :param dict change_table: the particular level in change table - :param list ct_hierarchy: data column hierarchy in the change table - :raise TypeError: for inputs not of the correct type - :raise AssertionError: for hierarchy names which are not in dataframe - :return: (*types.GeneratorType*) -- python generator that returns - individual changes to be performed - """ - if type(change_table) is not dict: - raise TypeError("change_table must be a dictionary") - if type(ct_hierarchy) is not list: - raise TypeError("ct_hierarchy must be a list") - assert all(column in self.data.columns for column in ct_hierarchy), \ - 'ct_hierarchy must contain columns in dataframe' - self.ct_hierarchy_generator(change_table, ct_hierarchy) - - @staticmethod - def ct_hierarchy_generator(change_level, ct_hierarchy, index=()): - """ - This function creates a recursive Python generator function which - traverses the change table hierarchy and returns a unnested list of - changes to be implemented - :param {dict, int, float} change_level: the particular level in - change table - :param list ct_hierarchy: data column hierarchy in the change table - :param tuple index: initial starting tuple - :raises ValueError: tuple index not compatible with hierarchy depth - :raises TypeError: item not a valid change table type - :return: (*types.GeneratorType*) -- python generator that returns - individual changes to be performed - """ - if isinstance(change_level, dict): - for current_level, next_level in change_level.items(): - yield from HierarchicalGridField.ct_hierarchy_generator( - next_level, ct_hierarchy, index + (current_level,)) - elif isinstance(change_level, (int, float)): - if len(ct_hierarchy) != len(index): - raise ValueError("Generated index must of same length as " - "ct_hierarchy input") - change_info = {"ct_hierarchy": ct_hierarchy, "index": index, - "scaling_value": change_level} - yield change_info - else: - raise TypeError(f"{change_level} not a valid change table type") - - def get_hierarchical_index(self, index_hierarchy, base_index_column=None): - """ - Returns a hierarchical index dataframe where the base level column - matches the existing dataframe index. This allow sharing - hierarchical indices between complementary dataframes, such 'plant' - and 'gencost'. - :param list index_hierarchy: list of the index hierarchy to be - implemented - :param str base_index_column: base level column for index which - matches the index column of the dataframe to be scaled - :return: FieldHierarchicalIndex -- set_index method index type - """ - assert all(column in self.data.columns for column in index_hierarchy),\ - 'index_hierarchy must contain columns in dataframe' - if base_index_column is None: - base_index_column = self.data.index.name - assert (base_index_column not in index_hierarchy), \ - 'base index column must not be index hierarchy' - - hierarchical_index = self.data[index_hierarchy] \ - .reset_index() \ - .set_index(index_hierarchy) \ - .sort_index()[base_index_column] - - field_idx = FieldHierarchicalIndex(hierarchical_index, - index_hierarchy, - self.name) - return field_idx - - -class Branch(HierarchicalGridField): - """ - Class for branch data which includes data validation, dictionary of valid - data transformations, and utility functionss - """ - def __init__(self, data): - """ - :param pandas.DataFrame data: dataframe for the branch field - """ - name = 'branch' - transform = {} - super().__init__(data, name, transform) - - -class Bus(HierarchicalGridField): - """ - Class for bus data which includes data validation, dictionary of valid data - transformations, and utility functions - """ - def __init__(self, data): - """ - :param pandas.DataFrame data: dataframe for the bus field - """ - name = 'bus' - transform = {} - super().__init__(data, name, transform) - - -class DCLine(AbstractGridField): - """ - Class for dcline data which includes data validation, dictionary of valid - data transformations, and utility functions - """ - def __init__(self, data): - """ - :param pandas.DataFrame data: dataframe for the dcline field - """ - name = 'dcline' - transform = {} - super().__init__(data, name, transform) - - -class GenCost(HierarchicalGridField): - """ - Class for gencost data which includes data validation, dictionary of valid - data transformations, and utility functions - """ - def __init__(self, data): - """ - :param pandas.DataFrame data: dataframe for the gencost field - """ - name = 'gencost' - transform = {} - super().__init__(data, name, transform) - - -class Plant(HierarchicalGridField): - """ - Class for plant data which includes data validation, dictionary of valid - data transformations, and utility functions - """ - def __init__(self, data): - """ - :param pandas.DataFrame data: dataframe for the plant field - """ - name = 'plant' - transform = {} - super().__init__(data, name, transform) - - -class Storage(AbstractGridField): - """ - Class for storage data which includes data validation, dictionary of valid - data transformations, and utility functions - """ - def __init__(self, data): - """ - :param pandas.DataFrame data: dataframe for the storage field - """ - name = 'storage' - transform = {} - super().__init__(data, name, transform) - - -class Sub(HierarchicalGridField): - """ - Class for sub data which includes data validation, dictionary of valid data - transformations, and utility functions - """ - def __init__(self, data): - """ - :param pandas.DataFrame data: dataframe for the sub field - """ - name = 'sub' - transform = {} - super().__init__(data, name, transform) \ No newline at end of file diff --git a/powersimdata/input/tests/test_grid.py b/powersimdata/input/tests/test_grid.py index 63013bb58..ffb98f5d4 100644 --- a/powersimdata/input/tests/test_grid.py +++ b/powersimdata/input/tests/test_grid.py @@ -238,7 +238,7 @@ def test_grid_eq_failure_simple(base_texas, base_western): def test_grid_eq_failure_bus(base_texas): test_grid = copy.deepcopy(base_texas) - test_grid.bus.baseKV.iloc[0] *= 2 + test_grid.bus.loc[test_grid.bus.head(1).index, 'baseKV'] *= 2 assert base_texas != test_grid @@ -250,19 +250,20 @@ def test_grid_eq_success_bus_type(base_texas): def test_grid_eq_failure_branch(base_texas): test_grid = copy.deepcopy(base_texas) - test_grid.branch.rateA.iloc[0] *= 2 + test_grid.branch.loc[test_grid.branch.head(1).index, 'rateA'] *= 2 assert base_texas != test_grid def test_grid_eq_failure_dcline(base_western): test_grid = copy.deepcopy(base_western) - test_grid.dcline.Pmax.iloc[0] *= 2 + test_grid.dcline.loc[test_grid.dcline.head(1).index, 'Pmax'] *= 2 assert base_western != test_grid def test_grid_eq_failure_gencost_before(base_texas): test_grid = copy.deepcopy(base_texas) - test_grid.gencost['before'].n.iloc[0] += 1 + before = test_grid.gencost['before'] + before.loc[before.head(1).index, 'n'] += 1 assert base_texas != test_grid @@ -275,19 +276,20 @@ def test_grid_eq_success_gencost_after(base_texas): def test_grid_eq_failure_plant(base_texas): test_grid = copy.deepcopy(base_texas) - test_grid.plant.Pmax.iloc[0] *= 2 + test_grid.plant.loc[test_grid.plant.head(1).index, 'Pmax'] *= 2 assert base_texas != test_grid def test_grid_eq_success_plant_ramp30(base_texas): test_grid = copy.deepcopy(base_texas) - test_grid.plant.ramp_30.iloc[0] *= 2 + test_grid.plant.loc[test_grid.plant.head(1).index, 'ramp_30'] *= 2 assert base_texas == test_grid def test_grid_eq_failure_sub(base_texas): test_grid = copy.deepcopy(base_texas) - test_grid.sub.name.iloc[0] = test_grid.sub.name.iloc[0][::-1] + first_name = str(test_grid.sub.loc[test_grid.sub.head(1).index, 'name']) + test_grid.sub.loc[test_grid.sub.head(1).index, 'name'] = first_name[::-1] assert base_texas != test_grid @@ -308,8 +310,3 @@ def test_that_fields_are_not_modified_when_loading_another_grid(): eastern_grid = Grid(['Eastern']) assert western_plant_original_shape == western_grid.plant.shape - -def test_that_fields_can_be_modified_with_conventional_syntax(): - grid = Grid(['Texas']) - grid.plant = grid.plant.append(grid.plant.iloc[0:4]) - assert grid.plant.shape == grid['plant'].shape diff --git a/powersimdata/input/tests/test_grid_field.py b/powersimdata/input/tests/test_grid_field.py deleted file mode 100644 index c1aa8fef7..000000000 --- a/powersimdata/input/tests/test_grid_field.py +++ /dev/null @@ -1,149 +0,0 @@ -from powersimdata.input.grid_fields import HierarchicalGridField -import pytest -import pandas as pd -import unittest -from pandas.testing import assert_frame_equal - - -def test_change_table_parsing_2levels(): - input_ct = {'wind': {301: 3}, 'coal': {302: 5, 304: 6, 305: 10}} - change_table_hierarchy = ['type', 'zone_id'] - result = HierarchicalGridField.ct_hierarchy_generator( - input_ct, change_table_hierarchy) - - # Answer should be: - # [{'ct_hierarchy': - # ['type', 'zoneid'], 'index': ('wind', 301), 'scaling_value': 3}, - # {'ct_hierarchy': - # ['type', 'zoneid'], 'index': ('coal', 302), 'scaling_value': 5}, - # {'ct_hierarchy': - # ['type', 'zoneid'], 'index': ('coal', 304), 'scaling_value': 6}, - # {'ct_hierarchy': - # ['type', 'zoneid'], 'index': ('coal', 305), 'scaling_value': 10}] - - for change in result: - assert(change['ct_hierarchy'] == change_table_hierarchy) - assert (len(change['ct_hierarchy']) == len(change['index']) == 2) - index = change['index'] - assert input_ct[index[0]][index[1]] == change['scaling_value'] - - -def test_change_table_parsing_3levels(): - input_ct = {'wind': {301: {1002: 3}}, 'coal': {302: {1001: 5, 1003: 2}}} - - change_table_hierarchy = ['type', 'zone_id', 'bus_id'] - result = HierarchicalGridField.ct_hierarchy_generator( - input_ct, change_table_hierarchy) - - # Answer should be: - # [{'ct_hierarchy': - # ['type', 'zoneid','bus_id'], 'index': ('wind', 301, 1002), - # 'scaling_value': 3}, - # {'ct_hierarchy': - # ['type', 'zoneid', 'bus_id'], 'index': ('coal', 302, 1001), - # 'scaling_value': 5}, - # {'ct_hierarchy': - # ['type', 'zoneid', 'bus_id'], 'index': ('coal', 304, 1003), - # 'scaling_value': 2}, - - for change in result: - assert(change['ct_hierarchy'] == change_table_hierarchy) - assert(len(change['ct_hierarchy']) == len(change['index']) == 3) - index = change['index'] - assert input_ct[index[0]][index[1]][index[2]] == \ - change['scaling_value'] - - -def test_change_table_error(): - input_ct = {'wind': {301: 3}, 'coal': {302: [5], 304: 6, 305: 10}} - change_table_hierarchy = ['type', 'zone_id'] - with pytest.raises(TypeError): - list(HierarchicalGridField.ct_hierarchy_generator( - input_ct, change_table_hierarchy)) - - -def test_change_table_depth_mixed_type(): - input_ct = {'wind': {3}, 'coal': {302: [5], 304: 6, 305: 10}} - change_table_hierarchy = ['type', 'zone_id'] - with pytest.raises(TypeError): - list(HierarchicalGridField.ct_hierarchy_generator( - input_ct, change_table_hierarchy)) - - -def test_change_table_depth_too_shallow(): - input_ct = {'wind': 3, 'coal': {302: 5, 304: 6, 305: 10}} - change_table_hierarchy = ['type', 'zone_id'] - with pytest.raises(ValueError): - list(HierarchicalGridField.ct_hierarchy_generator( - input_ct, change_table_hierarchy)) - - -def test_change_table_depth_too_deep(): - input_ct = {'wind': {301: {301: 3}}, 'coal': {302: 5, 304: 6, 305: 10}} - change_table_hierarchy = ['type', 'zone_id'] - with pytest.raises(ValueError): - list(HierarchicalGridField.ct_hierarchy_generator( - input_ct, change_table_hierarchy)) - - -def test_ct_hierarchy_validation(): - plant = _create_grid_plant_dataframe() - input_ct = {'wind': {301: {301: 3}}, 'coal': {302: 5, 304: 6, 305: 10}} - change_table_hierarchy = ['type', 'zoneid'] - plant_field = HierarchicalGridField(plant, 'plant', {}) - with pytest.raises(AssertionError): - list(plant_field.ct_hierarchy_iterator(input_ct, - change_table_hierarchy)) - - -# For these tests, the grouping are as follows: -# -# type zone_name -# hydro Atlantic 103 -# Pacific 106 -# solar Pacific 101 -# Pacific 104 -# Pacific 105 -# wind Atlantic 102 -# Name: plant_id, dtype: int64 - - -def test_single_hierarchical_index(): - plant = _create_grid_plant_dataframe() - plant_field = HierarchicalGridField(plant, 'plant', {}) - - type_zone_index = plant_field.get_hierarchical_index(['type', 'zone_name']) - idx = type_zone_index.get_idx(('solar', 'Pacific')) - - tc = unittest.TestCase('__init__') - tc.assertEqual(idx, [101, 104, 105]) - - assert_frame_equal(plant.loc[idx], plant.loc[[101, 104, 105]]) - - -def test_multiple_hierarchical_index(): - plant = _create_grid_plant_dataframe() - plant_field = HierarchicalGridField(plant, 'plant', {}) - - type_zone_index = plant_field.get_hierarchical_index(['type', 'zone_name']) - idx = type_zone_index.get_idx((['solar', 'wind'], ['Atlantic', 'Pacific'])) - - tc = unittest.TestCase('__init__') - tc.assertEqual(idx, [101, 104, 105, 102]) - - assert_frame_equal(plant.loc[idx], plant.loc[[101, 104, 105, 102]]) - - -def _create_grid_plant_dataframe(): - mock_plant = { - 'plant_id': [101, 102, 103, 104, 105, 106], - 'bus_id': [1001, 1002, 1003, 1004, 1005, 1001], - 'type': ['solar', 'wind', 'hydro', 'solar', 'solar', 'hydro'], - 'zone_name': ['Pacific', 'Atlantic', 'Atlantic', 'Pacific', - 'Pacific', 'Pacific'], - 'GenFuelCost': [0, 0, 3.3, 4.4, 5.5, 0], - 'Pmax': [50, 200, 80, 100, 120, 220], - } - plant = pd.DataFrame(mock_plant) - plant.set_index('plant_id', inplace=True) - return plant