-
Notifications
You must be signed in to change notification settings - Fork 47
refactor: add mwmiles calculations to design.transmission module #291
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,7 @@ | ||
| __all__ = ["tests"] | ||
| __all__ = [ | ||
| "generation", | ||
| "mimic_grid", | ||
| "scenario_info", | ||
| "tests", | ||
| "transmission", | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| __all__ = ["clean_capacity_scaling"] |
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| __all__ = ["test_clean_capacity_scaling"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| __all__ = ["mwmiles", "statelines", "tests", "upgrade"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| from powersimdata.utility.distance import haversine | ||
| from powersimdata.input.grid import Grid | ||
|
|
||
|
|
||
| def calculate_mw_miles(scenario, exclude_branches=None): | ||
| """Given a Scenario object, calculate the number of upgraded lines and | ||
| transformers, and the total upgrade quantity (in MW and MW-miles). | ||
| Currently only supports change tables that specify branches' id, not | ||
| zone name. Currently lumps Transformer and TransformerWinding upgrades | ||
| together. | ||
|
|
||
| :param powersimdata.scenario.scenario.Scenario scenario: scenario instance. | ||
| :param list/tuple/set/None exclude_branches: branches to exclude. | ||
| :return: (*dict*) -- Upgrades to the branches. | ||
| """ | ||
|
|
||
| original_grid = Grid(scenario.info["interconnect"].split("_")) | ||
| ct = scenario.state.get_ct() | ||
| upgrades = _calculate_mw_miles(original_grid, ct, exclude_branches) | ||
| return upgrades | ||
|
|
||
|
|
||
| def _calculate_mw_miles(original_grid, ct, exclude_branches=None): | ||
| """Given a base grid and a change table, calculate the number of upgraded | ||
| lines and transformers, and the total upgrade quantity (in MW & MW-miles). | ||
| This function is separate from calculate_mw_miles() for testing purposes. | ||
| Currently only supports change_tables that specify branches, not zone_name. | ||
| Currently lumps Transformer and TransformerWinding upgrades together. | ||
|
|
||
| :param powersimdata.input.grid.Grid original_grid: grid instance. | ||
| :param dict ct: change table instance. | ||
| :param list/tuple/set/None exclude_branches: branches to exclude. | ||
| :raises ValueError: if not all values in exclude_branches are in the grid. | ||
| :raises TypeError: if exclude_branches gets the wrong type. | ||
| :return: (*dict*) -- Upgrades to the branches. | ||
| """ | ||
|
|
||
| upgrade_categories = ("mw_miles", "transformer_mw", "num_lines", "num_transformers") | ||
| upgrades = {u: 0 for u in upgrade_categories} | ||
|
|
||
| if "branch" not in ct or "branch_id" not in ct["branch"]: | ||
| return upgrades | ||
|
|
||
| if exclude_branches is None: | ||
| exclude_branches = {} | ||
| elif isinstance(exclude_branches, (list, set, tuple)): | ||
| good_branch_indices = original_grid.branch.index | ||
| if not all([e in good_branch_indices for e in exclude_branches]): | ||
| raise ValueError("not all branches are present in grid!") | ||
| exclude_branches = set(exclude_branches) | ||
| else: | ||
| raise TypeError("exclude_branches must be None, list, tuple, or set") | ||
|
|
||
| base_branch = original_grid.branch | ||
| upgraded_branches = ct["branch"]["branch_id"] | ||
| for b, v in upgraded_branches.items(): | ||
| if b in exclude_branches: | ||
| continue | ||
| # 'upgraded' capacity is v-1 because a scale of 1 = an upgrade of 0 | ||
| upgraded_capacity = base_branch.loc[b, "rateA"] * (v - 1) | ||
| device_type = base_branch.loc[b, "branch_device_type"] | ||
| if device_type == "Line": | ||
| from_coords = ( | ||
| base_branch.loc[b, "from_lat"], | ||
| base_branch.loc[b, "from_lon"], | ||
| ) | ||
| to_coords = (base_branch.loc[b, "to_lat"], base_branch.loc[b, "to_lon"]) | ||
| addtl_mw_miles = upgraded_capacity * haversine(from_coords, to_coords) | ||
| upgrades["mw_miles"] += addtl_mw_miles | ||
| upgrades["num_lines"] += 1 | ||
| elif device_type == "Transformer": | ||
| upgrades["transformer_mw"] += upgraded_capacity | ||
| upgrades["num_transformers"] += 1 | ||
| elif device_type == "TransformerWinding": | ||
| upgrades["transformer_mw"] += upgraded_capacity | ||
| upgrades["num_transformers"] += 1 | ||
| else: | ||
| raise Exception("Unknown branch: " + str(b)) | ||
|
|
||
| return upgrades |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| from powersimdata.network.usa_tamu.constants.zones import id2state | ||
|
|
||
|
|
||
| def classify_interstate_intrastate(scenario): | ||
| """Classifies branches in a change_table as interstate or intrastate. | ||
|
|
||
| :param powersimdata.scenario.scenario.Scenario scenario: scenario instance. | ||
| :return: (*dict*) -- keys are *'interstate'*, *'intrastate'*. Values are | ||
| list of branch ids. | ||
| """ | ||
|
|
||
| ct = scenario.state.get_ct() | ||
| grid = scenario.state.get_grid() | ||
| upgraded_branches = _classify_interstate_intrastate(ct, grid) | ||
| return upgraded_branches | ||
|
|
||
|
|
||
| def _classify_interstate_intrastate(ct, grid): | ||
| """Classifies branches in a change_table as interstate or intrastate. | ||
| This function is separate from classify_interstate_intrastate() for testing | ||
| purposes. | ||
|
|
||
| :param dict ct: change_table dictionary. | ||
| :param powersimdata.input.grid.Grid grid: Grid instance. | ||
| :return: (*dict*) -- keys are *'interstate'*, *'intrastate'*. Values are | ||
| list of branch ids. | ||
| """ | ||
|
|
||
| branch = grid.branch | ||
| upgraded_branches = {"interstate": [], "intrastate": []} | ||
|
|
||
| if "branch" not in ct or "branch_id" not in ct["branch"]: | ||
| return upgraded_branches | ||
|
|
||
| all_upgraded_branches = ct["branch"]["branch_id"].keys() | ||
| for b in all_upgraded_branches: | ||
| # Alternatively: bus.loc[branch.loc[b, 'from_bus_id'], 'from_zone_id'] | ||
| try: | ||
| from_zone = branch.loc[b, "from_zone_id"] | ||
| to_zone = branch.loc[b, "to_zone_id"] | ||
| except KeyError: | ||
| raise ValueError(f"ct entry not found in branch: {b}") | ||
| try: | ||
| from_state = id2state[from_zone] | ||
| except KeyError: | ||
| raise ValueError(f"zone not found in id2state: {from_zone}") | ||
| try: | ||
| to_state = id2state[to_zone] | ||
| except KeyError: | ||
| raise ValueError(f"zone not found in id2state: {to_zone}") | ||
| if from_state == to_state: | ||
| upgraded_branches["intrastate"].append(b) | ||
| else: | ||
| upgraded_branches["interstate"].append(b) | ||
|
|
||
| return upgraded_branches | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| __all__ = ["test_mwmiles", "test_statelines", "test_upgrade"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| import unittest | ||
|
|
||
| from powersimdata.tests.mock_grid import MockGrid | ||
| from powersimdata.design.transmission.mwmiles import _calculate_mw_miles | ||
|
|
||
| # branch 11 from Seattle to San Francisco (~679 miles) | ||
| # branch 12 from Seattle to Spokane (~229 miles) | ||
| # branch 13-15 are transformers (0 miles) | ||
| mock_branch = { | ||
| "branch_id": [11, 12, 13, 14, 15], | ||
| "rateA": [10, 20, 30, 40, 50], | ||
| "from_lat": [47.61, 47.61, 47.61, 47.61, 47.61], | ||
| "from_lon": [-122.33, -122.33, -122.33, -122.33, -122.33], | ||
| "to_lat": [37.78, 47.66, 47.61, 47.61, 47.61], | ||
| "to_lon": [-122.42, -117.43, -122.33, -122.33, -122.33], | ||
| "branch_device_type": 2 * ["Line"] + 3 * ["Transformer"], | ||
| } | ||
|
|
||
| expected_keys = {"mw_miles", "transformer_mw", "num_lines", "num_transformers"} | ||
|
|
||
|
|
||
| class TestCalculateMWMiles(unittest.TestCase): | ||
| def setUp(self): | ||
| self.grid = MockGrid(grid_attrs={"branch": mock_branch}) | ||
|
|
||
| def _check_expected_values(self, mw_miles, expected_mw_miles): | ||
| """Check for proper structure and that values match expected. | ||
|
|
||
| :param dict mw_miles: dict of upgrade metrics. | ||
| :param dict expected_mw_miles: dict of expected upgrade metrics. | ||
| """ | ||
| self.assertIsInstance(mw_miles, dict, "dict not returned") | ||
| self.assertEqual( | ||
| expected_keys, mw_miles.keys(), msg="Dict keys not as expected" | ||
| ) | ||
| for v in mw_miles.values(): | ||
| self.assertIsInstance(v, (float, int)) | ||
| for k in expected_mw_miles.keys(): | ||
| err_msg = "Did not get expected value for " + str(k) | ||
| self.assertAlmostEqual(mw_miles[k], expected_mw_miles[k], msg=err_msg) | ||
|
|
||
| def test_calculate_mw_miles_no_scale(self): | ||
| mock_ct = {"branch": {"branch_id": {}}} | ||
| expected_mw_miles = {k: 0 for k in expected_keys} | ||
| mw_miles = _calculate_mw_miles(self.grid, mock_ct) | ||
| self._check_expected_values(mw_miles, expected_mw_miles) | ||
|
|
||
| def test_calculate_mw_miles_one_line_scaled(self): | ||
| mock_ct = {"branch": {"branch_id": {11: 2}}} | ||
| expected_mw_miles = { | ||
| "mw_miles": 6792.03551523, | ||
| "transformer_mw": 0, | ||
| "num_lines": 1, | ||
| "num_transformers": 0, | ||
| } | ||
| mw_miles = _calculate_mw_miles(self.grid, mock_ct) | ||
| self._check_expected_values(mw_miles, expected_mw_miles) | ||
|
|
||
| def test_calculate_mw_miles_one_transformer_scaled(self): | ||
| mock_ct = {"branch": {"branch_id": {13: 2.5}}} | ||
| expected_mw_miles = { | ||
| "mw_miles": 0, | ||
| "transformer_mw": 45, | ||
| "num_lines": 0, | ||
| "num_transformers": 1, | ||
| } | ||
| mw_miles = _calculate_mw_miles(self.grid, mock_ct) | ||
| self._check_expected_values(mw_miles, expected_mw_miles) | ||
|
|
||
| def test_calculate_mw_miles_many_scaled(self): | ||
| mock_ct = {"branch": {"branch_id": {11: 2, 12: 3, 13: 1.5, 14: 1.2, 15: 3}}} | ||
| expected_mw_miles = { | ||
| "mw_miles": 15917.06341095, | ||
| "transformer_mw": 123, | ||
| "num_lines": 2, | ||
| "num_transformers": 3, | ||
| } | ||
| mw_miles = _calculate_mw_miles(self.grid, mock_ct) | ||
| self._check_expected_values(mw_miles, expected_mw_miles) | ||
|
|
||
| def test_calculate_mw_miles_many_scaled_one_branch_excluded(self): | ||
| mock_ct = {"branch": {"branch_id": {11: 2, 12: 3, 13: 1.5, 14: 1.2, 15: 3}}} | ||
| expected_mw_miles = { | ||
| "mw_miles": 9125.027895725, | ||
| "transformer_mw": 123, | ||
| "num_lines": 1, | ||
| "num_transformers": 3, | ||
| } | ||
| mw_miles = _calculate_mw_miles(self.grid, mock_ct, exclude_branches={11}) | ||
| self._check_expected_values(mw_miles, expected_mw_miles) | ||
|
|
||
| def test_calculate_mw_miles_many_scaled_two_branches_excluded(self): | ||
| mock_ct = {"branch": {"branch_id": {11: 2, 12: 3, 13: 1.5, 14: 1.2, 15: 3}}} | ||
| expected_mw_miles = { | ||
| "mw_miles": 9125.027895725, | ||
| "transformer_mw": 108, | ||
| "num_lines": 1, | ||
| "num_transformers": 2, | ||
| } | ||
| mw_miles = _calculate_mw_miles(self.grid, mock_ct, exclude_branches=[11, 13]) | ||
| self._check_expected_values(mw_miles, expected_mw_miles) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.