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

feat: test for Grid equality #152

Merged
merged 2 commits into from
Apr 23, 2020
Merged

feat: test for Grid equality #152

merged 2 commits into from
Apr 23, 2020

Conversation

danielolsen
Copy link
Contributor

@danielolsen danielolsen commented Apr 22, 2020

Purpose

Be able to compare whether two Grid objects are 'equal' in pythonic syntax: x == y. This feature will be useful for our own development (e.g. #150), to ensure that any refactoring does not change how Grid objects are instantiated. It may come in handy for some other analysis functions as well, although I don't have an example off the top of my head.

What is the code doing

In grid.py we add the __eq__ magic method to Grid objects. There is an internal _univ_eq function which will raise an AssertionError if the two inputs are not: a) logically equal (e.g. ints or dicts), b) DataFrames with equal indexes and values, or c) DataFrames with identical indexes and the same columns and data, but with column order transposed. This last condition is relevant for our differently instantiated Grids, which load all the right data in the Plant DataFrame but transpose some columns.

Then, we loop through all elements in fields and transform, ensuring that the values in them are equal (or close enough to equal, given the data modifications we expect).

In mat_reader.py: we correct the transposition of two column names for the bus table we read from the MAT file.

Verification that this works as expected

python
>>> from powersimdata.scenario.scenario import Scenario
>>> from powersimdata.input.grid import Grid
>>> western = Grid(['Western'])
Reading bus.csv
Reading plant.csv
Reading gencost.csv
Reading branch.csv
Reading dcline.csv
Reading sub.csv
Reading bus2sub.csv
Reading zone.csv
>>> western2 = Grid(['Western'])
Reading bus.csv
Reading plant.csv
Reading gencost.csv
Reading branch.csv
Reading dcline.csv
Reading sub.csv
Reading bus2sub.csv
Reading zone.csv
>>> western is western2
False
>>> western == western2
True
>>> eastern = Grid(['Eastern'])
Reading bus.csv
Reading plant.csv
Reading gencost.csv
Reading branch.csv
Reading dcline.csv
Reading sub.csv
Reading bus2sub.csv
Reading zone.csv
>>> western == eastern
False
>>> grid_419 = Scenario('419').state.get_grid()
SCENARIO: test | WesternBase24

--> State
analyze
--> Loading grid
Loading bus
Loading plant
Loading heat_rate_curve
Loading gencost_before
Loading gencost_after
Loading branch
Loading dcline
Loading sub
Loading bus2sub
>>> western == grid_419
True
>>> grid_430 = Scenario('430').state.get_grid()
SCENARIO: test | Eastern2030Independent_OB2_Mesh200

--> State
analyze
--> Loading grid
Loading bus
Loading plant
Loading heat_rate_curve
Loading gencost_before
Loading gencost_after
Loading branch
Loading dcline
Loading sub
Loading bus2sub
>>> western == grid_430
False

Time to review

Approximately one hour, maybe less.

def __eq__(self, other):
"""Used when 'self == other' is evaluated.
:param object other: other object to be compared against.
:return: *bool*.
Copy link
Collaborator

@rouille rouille Apr 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:return: (bool). Missing parenthesis

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -208,7 +208,7 @@ def column_name_provider():
'name', 'interconnect_sub_id', 'lat', 'lon', 'interconnect']
col_name_bus = [
'bus_id', 'type', 'Pd', 'Qd', 'Gs', 'Bs', 'zone_id', 'Vm', 'Va',
'loss_zone', 'baseKV', 'Vmax', 'Vmin', 'lam_P', 'lam_Q', 'mu_Vmax',
'baseKV', 'loss_zone', 'Vmax', 'Vmin', 'lam_P', 'lam_Q', 'mu_Vmax',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mislabelled these 2 column names in the bus data frame? Good catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't matter for any of our purposes, but this popped up when I was trying to compare bus dataframes between a USA TAMU model and a MATReader model.

:return: *bool*.
"""
def _univ_eq(ref, test):
"""Check for {boolean, dataframe, or column data} equality."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:param ref: and :param test: are missing

Copy link
Collaborator

@rouille rouille Apr 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the exception raised are not listed in the docstring. This will have the benefit to help the user understand what is tested and where.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


if not isinstance(other, Grid):
other_type = type(other).__name__
raise NotImplementedError(f'Unable to compare Grid & {other_type}')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

f-string won't work on server

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@rouille
Copy link
Collaborator

rouille commented Apr 22, 2020

It seems like Storage is the only object that is not fully tested in your tests. Scenario 419 allows to test that TAMU == MATREader with empty storage keys.

Should we create a new scenario with Storage to make sure that the data in the gen, gencost and StorageData data frames, list and other objects of the dictionary are correctly filled out by MATReader. I guess we want to do TAMU + storage == MATReader

@danielolsen
Copy link
Contributor Author

@rouille you are right, we were not adequately accounting for storage. I tried comparing scenarios 90 and 159 from the server, which are identical besides the presence of storage in 159, and it was succeeding at first because I was looking to genfuel for the number of storage devices, when I should have been looking at gencost. The fix I just pushed takes care of this.

Documenting that the addition of storage causes a False evaluation:

>>> from powersimdata.scenario.scenario import Scenario
>>> grid_159 = Scenario('159').state.get_grid()
SCENARIO: base | WACA_Anchor_AllMatchCA_2030_SpurUpgrade_Storage1325

--> State
analyze
--> Loading grid
Loading bus
Loading plant
Loading heat_rate_curve
Loading gencost_before
Loading gencost_after
Loading branch
Loading sub
Loading bus2sub
>>> grid_90 = Scenario('90').state.get_grid()
SCENARIO: base | WACA_Anchor_AllMatchCA_2030_SpurUpgrade_1

--> State
analyze
--> Loading grid
Loading bus
Loading plant
Loading heat_rate_curve
Loading gencost_before
Loading gencost_after
Loading branch
Loading sub
Loading bus2sub
>>> grid_90 == grid_159
False
>>> (grid_90.gencost['before'] == grid_159.gencost['before']).all().all()
True
>>> (grid_90.bus == grid_159.bus).all().all()
True
>>> (grid_90.branch == grid_159.branch).all().all()
True
>>> (grid_90.plant == grid_159.plant).all().all()
True
>>> (grid_90.dcline == grid_159.dcline).all().all()
True
>>> (grid_90.sub == grid_159.sub).all().all()
True
>>> len(grid_90.storage['gencost']) == len(grid_159.storage['gencost'])
False

I'm not immediately picturing how we test that TAMU + storage == MATReader. I don't think we can get_grid() in a Create state.

@rouille
Copy link
Collaborator

rouille commented Apr 22, 2020

Thanks for the additional test and fix. The test is even better. I was not sure that we had two exact scenarios but for storage on the server.

Copy link
Collaborator

@rouille rouille left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing these tests. It looks great.

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

Successfully merging this pull request may close these issues.

2 participants