Skip to content

Conversation

@luseverin
Copy link
Collaborator

@luseverin luseverin commented Mar 22, 2024

Changes proposed in this PR:

  • Add a test in 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.
  • Change previous AttributeError: 'list' has no attribute 'calc_mdr' to a more explanatory error message

This PR fixes #669 (reopened)

PR Author Checklist

PR Reviewer Checklist

@luseverin luseverin added enhancement accepting pull request Contribute by raising a pull request to resolve this issue! labels Mar 22, 2024
@luseverin luseverin removed the accepting pull request Contribute by raising a pull request to resolve this issue! label Mar 22, 2024
Copy link
Member

@chahank chahank 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 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

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
Copy link
Member

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.

Copy link
Collaborator Author

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):
Copy link
Member

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.

Copy link
Collaborator Author

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.

Comment on lines 155 to 158
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.")
Copy link
Member

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?

Copy link
Collaborator Author

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.

@luseverin
Copy link
Collaborator Author

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.

Comment on lines 145 to 152
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
Copy link
Member

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.

Suggested change
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

Copy link
Collaborator Author

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!

Comment on lines 155 to 163
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.")
Copy link
Member

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

Copy link
Collaborator Author

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.


impf_col = self.exposures.get_impf_column(self.hazard.haz_type)

# check for compability of impact function id between impact function set and exposure
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
# 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

Copy link
Collaborator Author

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!

Comment on lines 137 to 144
"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.")
Copy link
Collaborator

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:

Suggested change
"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.")

Copy link
Collaborator Author

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.

Comment on lines 134 to 135
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)):
Copy link
Collaborator

Choose a reason for hiding this comment

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

or maybe -

Suggested change
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

Copy link
Collaborator Author

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!

@emanuel-schmid
Copy link
Collaborator

Good catch!

@chahank
Copy link
Member

chahank commented Apr 8, 2024

The compatibility test with petals is failing, but I think it is unrelated to this PR. @emanuel-schmid @luseverin are we ready to merge?

@luseverin
Copy link
Collaborator Author

It also does not seem to me that the tests failing are related to the PR. So ready to merge from my side.

@emanuel-schmid emanuel-schmid merged commit 7f74ea4 into develop Apr 9, 2024
@emanuel-schmid
Copy link
Collaborator

Thanks a lot!

@emanuel-schmid emanuel-schmid deleted the feature/improve_impact_calc_error_messages2 branch April 9, 2024 06:19
gdeskos pushed a commit to gdeskos/climada_python that referenced this pull request Sep 16, 2024
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants