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.
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.
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.
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.
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.
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.
The linter should complain that these lines are too long. Could you please shorten them?
There was a problem hiding this comment.
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.
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.
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.
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.
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.
| # 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.
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.
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.
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.
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.
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 <[email protected]>
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