-
Notifications
You must be signed in to change notification settings - Fork 149
Improve impact calc error messages when there is a mismatch of impf ids between exposures and impfset #863
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
Improve impact calc error messages when there is a mismatch of impf ids between exposures and impfset #863
Conversation
…pfset in ImpactCalc.impact()
…pfset and exposures in ImpactCalc.impact()
…set and exposures in ImpactCalc.impact()
chahank
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the quick fix! I have some very small style requests.
Also, please do not forget to complete the changelog and to add yourself to the author's list
climada/engine/impact_calc.py
Outdated
| impf_col = self.exposures.get_impf_column(self.hazard.haz_type) | ||
|
|
||
| # check for compability of impact function id between impact function set and exposure | ||
| if any(if_id not in self.impfset.get_ids(haz_type=self.hazard.haz_type) for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use the similar variable names as in the rest of the file: if -> impf.
Besides, a variable name if is rather confusing as this is also a python operator. Note that we had a longer discussion on that 2 years ago when we decided to avoid the variable name if.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the notice, I changed if_id to impf_id.
| except Exception as e: | ||
| self.assertEqual(str(e), "Impact calculation not possible. No impact " | ||
| "functions found for hazard type TC in impf_set.") | ||
| def test_error_handling_mismatch_impf_ids(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If being pedantic, I would improve slightly the test by having two impact functions in the set, one that is in the exposures, and one that isn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so now the test considers the case of having two impact functions in the exposures (impf ids 1 and 2), and two impact functions in the impfset (impf ids 1 and 3), so that an error is raised as there is no impact function with id 2 in the impfset. Let me know in case you meant something else.
| self.assertEqual(str(e), "At least one impact function associated to the exposures has no match in the impact function set.\n" | ||
| "The impact functions in the impact function set for hazard type \"TC\" have ids [2]. " | ||
| "The column \"impf_TC\" in the exposures contains the ids [1].\n" | ||
| "Please make sure that all exposure points are associated with an impact function that is included in the impact function set.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The linter should complain that these lines are too long. Could you please shorten them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I might have missed this. I think it is good now.
…est_error_handling_mismatch_impf_ids
|
Thanks for the review! I think I have adressed all your comments. I also modified the author's list and changelog. Let me know if there's anything else. |
| impf_exp = ImpactFunc() | ||
| impf_exp.id = 1 | ||
| impf_exp.intensity = np.array([0, 20]) | ||
| impf_exp.paa = np.array([0, 1]) | ||
| impf_exp.mdd = np.array([0, 0.5]) | ||
| impf_exp.haz_type = 'TC' | ||
| impf_noexp = deepcopy(impf_exp) | ||
| impf_noexp.id = 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should work too right? Ideally, one does not first initiate an object and then modify its attributes. This is mostly a Matlab-style remnant in older CLIMADA code.
| impf_exp = ImpactFunc() | |
| impf_exp.id = 1 | |
| impf_exp.intensity = np.array([0, 20]) | |
| impf_exp.paa = np.array([0, 1]) | |
| impf_exp.mdd = np.array([0, 0.5]) | |
| impf_exp.haz_type = 'TC' | |
| impf_noexp = deepcopy(impf_exp) | |
| impf_noexp.id = 3 | |
| impf_exp = ImpactFunc(haz_type='TC', id=1) | |
| impf_noexp = deepcopy(impf_exp) | |
| impf_noexp.id = 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's indeed better, thanks for the suggestion!
| try: | ||
| ImpactCalc(exp, impfset, haz).impact() | ||
| except Exception as e: | ||
| self.assertEqual(str(e), "At least one impact function associated to" | ||
| " the exposures has no match in the impact function set.\n" | ||
| "The impact functions in the impact function set for hazard type \"TC\" " | ||
| "have ids [1, 3]. The column \"impf_TC\" in the exposures contains the ids" | ||
| " [1, 2].\nPlease make sure that all exposure points are associated with an " | ||
| "impact function that is included in the impact function set.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I did not see that first. For the unit test, please use self.assertRaises() : https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that makes sense to use this instead. I modified the testing function.
climada/engine/impact_calc.py
Outdated
|
|
||
| impf_col = self.exposures.get_impf_column(self.hazard.haz_type) | ||
|
|
||
| # check for compability of impact function id between impact function set and exposure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| # check for compability of impact function id between impact function set and exposure | |
| # check for compatibility of impact function id between impact function set and exposure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing out the typo!
climada/engine/impact_calc.py
Outdated
| "At least one impact function associated to the exposures has no match " | ||
| "in the impact function set.\nThe impact functions in the impact function" | ||
| f" set for hazard type \"{self.hazard.haz_type}\" have ids " | ||
| f"{self.impfset.get_ids(haz_type=self.hazard.haz_type)}. The column " | ||
| f"\"{impf_col}\" in the exposures contains the ids " | ||
| f"{np.unique(self.exposures.gdf[impf_col].values).astype(int).tolist()}.\n" | ||
| "Please make sure that all exposure points are associated with an impact " | ||
| "function that is included in the impact function set.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while we're at it why not just list the felonies:
| "At least one impact function associated to the exposures has no match " | |
| "in the impact function set.\nThe impact functions in the impact function" | |
| f" set for hazard type \"{self.hazard.haz_type}\" have ids " | |
| f"{self.impfset.get_ids(haz_type=self.hazard.haz_type)}. The column " | |
| f"\"{impf_col}\" in the exposures contains the ids " | |
| f"{np.unique(self.exposures.gdf[impf_col].values).astype(int).tolist()}.\n" | |
| "Please make sure that all exposure points are associated with an impact " | |
| "function that is included in the impact function set.") | |
| unknown_impact_functions = list(self.exposures.gdf[ | |
| ~self.exposures.gdf[impf_col].isin(known_impact_functions) | |
| ][impf_col].drop_duplicates().astype(int)) | |
| raise ValueError( | |
| f"The associated impact function(s) {', '.join(unknown_impact_functions)}" | |
| " have no match in impact function set for hazard type '{self.hazard.haz_type}'\n" | |
| "Please make sure that all exposure points are associated with an impact " | |
| "function that is included in the impact function set.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, that's even better this way! I have included this modification in my last commits.
climada/engine/impact_calc.py
Outdated
| if any(impf_id not in self.impfset.get_ids(haz_type=self.hazard.haz_type) for | ||
| impf_id in np.unique(self.exposures.gdf[impf_col].values)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or maybe -
| if any(impf_id not in self.impfset.get_ids(haz_type=self.hazard.haz_type) for | |
| impf_id in np.unique(self.exposures.gdf[impf_col].values)): | |
| known_impact_functions = self.impfset.get_ids(haz_type=self.hazard.haz_type) | |
| if not all(self.exposures.gdf[impf_col].isin(known_impact_functions)): |
which is possibly easier to read
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that is more readable, I also included this. Thanks!
|
Good catch! |
|
The compatibility test with petals is failing, but I think it is unrelated to this PR. @emanuel-schmid @luseverin are we ready to merge? |
|
It also does not seem to me that the tests failing are related to the PR. So ready to merge from my side. |
|
Thanks a lot! |
…ds between exposures and impfset (CLIMADA-project#863) * Add error handling for missmatching impf ids between exposures and impfset in ImpactCalc.impact() * Clarify error message for handling of mismatching impf ids between impfset and exposures in ImpactCalc.impact() * Add unit test for error handling of mismatchin impf ids between impf set and exposures in ImpactCalc.impact() * Make code compliant with PEP8 * Change ambiguous variable names * Add impact function in the impf set but not in the exposure for the test_error_handling_mismatch_impf_ids * Add name to authors's list * Add changes to changelog * Use assertRaises instead of assertEqual to test the error handling * Refine error message so that it provides users with the ids of missing impfs * Initiate dummy impf in testing function directly setting its attributes * Correct for pylint warnings for too long lines --------- Co-authored-by: luseverin <luca.severino@usys.ethz.ch>
Changes proposed in this PR:
ImpactCalc.impact(exposures, impfset, hazard)to handle AttributeError arising when an impact function id contained in the exposures has no matching impact function in the impact function set.AttributeError: 'list' has no attribute 'calc_mdr'to a more explanatory error messageThis PR fixes #669 (reopened)
PR Author Checklist
develop)PR Reviewer Checklist