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

Feature/reassign centr bydflt #527

Merged
merged 18 commits into from
Aug 12, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e130f3d
cosmetics
emanuel-schmid Jul 20, 2022
542aabf
impact_calc.ImpactCalc.minimal_exp_gdf: change default to assign_cent…
emanuel-schmid Jul 20, 2022
3f5e3ca
Merge branch 'develop' into feature/reassign_centr_bydflt
emanuel-schmid Aug 4, 2022
d7834e2
impact_calc: introduce optonal reassign_centroids argument
emanuel-schmid Aug 4, 2022
50e6469
test_impact_calc: fix failing tests (due to reassignment)
emanuel-schmid Aug 4, 2022
7fda806
Merge branch 'develop' into feature/reassign_centr_bydflt
emanuel-schmid Aug 5, 2022
bfc94ae
forecast.Forecast.calc: simply pass the force_reassign parameter to t…
emanuel-schmid Aug 5, 2022
5df35b7
Apply suggestions from code review
emanuel-schmid Aug 5, 2022
bad2513
rename `reassign_centroids` arguments to `assign_centroids`
emanuel-schmid Aug 5, 2022
9218ba0
calibration_opt: skip centroids assignment in calib_instance
emanuel-schmid Aug 5, 2022
648c030
fix typo
emanuel-schmid Aug 5, 2022
caf8dfd
PEP8 cosmetics
emanuel-schmid Aug 5, 2022
f0c609d
forecast.calc: there is no implicit centroid assignment in impact.cal…
emanuel-schmid Aug 5, 2022
8ed6bbe
forecastforecast.calc: there is no implicit centroid assignment in im…
emanuel-schmid Aug 5, 2022
4834f79
minimize centroid assignment in uncertainty, cost_benefit and measures
emanuel-schmid Aug 5, 2022
d764833
measures.test: explicit centroid assignment before _cutoff
emanuel-schmid Aug 8, 2022
b7962ff
unsequa.calc_cost_benefit: assign centroids manually, w/o overwriting
emanuel-schmid Aug 9, 2022
8c5ebe0
docstring consolidation
emanuel-schmid Aug 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion climada/engine/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def calc(self, force_reassign=False):
if force_reassign:
self.exposure.assign_centroids(haz_i)
self._impact[ind_i].calc(
self.exposure, self.vulnerability, haz_i, save_mat=True
self.exposure, self.vulnerability, haz_i, save_mat=True, reassign_centroids=False
peanutfun marked this conversation as resolved.
Show resolved Hide resolved
)

def plot_imp_map(
Expand Down
8 changes: 4 additions & 4 deletions climada/engine/impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def __init__(self,



def calc(self, exposures, impact_funcs, hazard, save_mat=False):
def calc(self, exposures, impact_funcs, hazard, save_mat=False, reassign_centroids=True):
peanutfun marked this conversation as resolved.
Show resolved Hide resolved
"""This function is deprecated, use ImpactCalc.impact
and ImpactCalc.insured_impact instead.
"""
Expand All @@ -198,11 +198,11 @@ def calc(self, exposures, impact_funcs, hazard, save_mat=False):
" for insured impacts instead. For non-insured impacts "
"please use ImpactCalc().impact()"
)
self.__dict__ = impcalc.insured_impact(save_mat).__dict__
self.__dict__ = impcalc.insured_impact(save_mat, reassign_centroids).__dict__
else:
LOGGER.warning("The use of Impact().calc() is deprecated. "
"Use ImpactCalc().impact() instead.")
self.__dict__ = impcalc.impact(save_mat).__dict__
self.__dict__ = impcalc.impact(save_mat, reassign_centroids).__dict__

#TODO: new name
@classmethod
Expand Down Expand Up @@ -1029,7 +1029,7 @@ def video_direct_impact(exp, impf_set, haz_list, file_name='',
imp_arr = np.zeros(len(exp.gdf))
for i_time, _ in enumerate(haz_list):
imp_tmp = Impact()
imp_tmp.calc(exp, impf_set, haz_list[i_time])
imp_tmp.calc(exp, impf_set, haz_list[i_time], reassign_centroids=False)
imp_arr = np.maximum(imp_arr, imp_tmp.eai_exp)
# remove not impacted exposures
save_exp = imp_arr > imp_thresh
Expand Down
54 changes: 38 additions & 16 deletions climada/engine/impact_calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def __init__(self,
self.exposures = exposures
self.impfset = impfset
self.hazard = hazard
self._orig_exp_idx = np.arange(self.exposures.gdf.shape[0]) #exposures index to use for matrix reconstruction
# exposures index to use for matrix reconstruction
self._orig_exp_idx = np.arange(self.exposures.gdf.shape[0])

@property
def n_exp_pnt(self):
Expand Down Expand Up @@ -101,13 +102,20 @@ def cover(self):
if 'cover' in self.exposures.gdf.columns:
return self.exposures.gdf['cover'].to_numpy()

def impact(self, save_mat=True):
def impact(self, save_mat=True, reassign_centroids=True):
"""Compute the impact of a hazard on exposures.

Parameters
----------
save_mat : bool
save_mat : bool, optional
if true, save the total impact matrix (events x exposures)
Default: True
reassign_centroids : bool, optional
indicates whether centroids are re-assigned to the self.exposures object
or kept from previous impact calculation with a hazard of the same hazard type.
Centroids assignment is an expensive operation, set this to true if you know that
the centroids are the same between two impact calculations.
emanuel-schmid marked this conversation as resolved.
Show resolved Hide resolved
Default: True

Examples
--------
Expand All @@ -126,7 +134,7 @@ def impact(self, save_mat=True):
the column is added to the exposures geodataframe.
"""
impf_col = self.exposures.get_impf_column(self.hazard.haz_type)
exp_gdf = self.minimal_exp_gdf(impf_col)
exp_gdf = self.minimal_exp_gdf(impf_col, reassign_centroids)
if exp_gdf.size == 0:
return self._return_empty(save_mat)
LOGGER.info('Calculating impact for %s assets (>0) and %s events.',
Expand All @@ -136,7 +144,7 @@ def impact(self, save_mat=True):

#TODO: make a better impact matrix generator for insured impacts when
# the impact matrix is already present
def insured_impact(self, save_mat=False):
def insured_impact(self, save_mat=False, reassign_centroids=True):
"""Compute the impact of a hazard on exposures with a deductible and/or
cover.

Expand All @@ -147,6 +155,12 @@ def insured_impact(self, save_mat=False):
----------
save_mat : bool
if true, save the total impact matrix (events x exposures)
reassign_centroids : bool, optional
indicates whether centroids are re-assigned to the self.exposures object
or kept from previous impact calculation with a hazard of the same hazard type.
Centroids assignment is an expensive operation, set this to true if you know that
the centroids are the same between two impact calculations.
emanuel-schmid marked this conversation as resolved.
Show resolved Hide resolved
Default: True

Examples
--------
Expand All @@ -169,7 +183,7 @@ def insured_impact(self, save_mat=False):
"Please set exposures.gdf.cover"
"and/or exposures.gdf.deductible")
impf_col = self.exposures.get_impf_column(self.hazard.haz_type)
exp_gdf = self.minimal_exp_gdf(impf_col)
exp_gdf = self.minimal_exp_gdf(impf_col, reassign_centroids)
if exp_gdf.size == 0:
return self._return_empty(save_mat)
LOGGER.info('Calculating impact for %s assets (>0) and %s events.',
Expand Down Expand Up @@ -238,18 +252,22 @@ def _return_empty(self, save_mat):
return Impact.from_eih(self.exposures, self.impfset, self.hazard,
at_event, eai_exp, aai_agg, imp_mat)

def minimal_exp_gdf(self, impf_col):
def minimal_exp_gdf(self, impf_col, reassign_centroids):
"""Get minimal exposures geodataframe for impact computation

Parameters
----------
exposures : climada.entity.Exposures
hazard : climada.Hazard
impf_col: str
name of the impact function column in exposures.gdf

"""
self.exposures.assign_centroids(self.hazard, overwrite=False)
impf_col : str
Name of the impact function column in exposures.gdf
reassign_centroids : bool
Indicates whether centroids are re-assigned to the self.exposures object
or kept from previous impact calculation with a hazard of the same hazard type.
Centroids assignment is an expensive operation, set this to true if you know that
the centroids are the same between two impact calculations.
emanuel-schmid marked this conversation as resolved.
Show resolved Hide resolved
"""
self.exposures.assign_centroids(self.hazard, overwrite=reassign_centroids)
mask = (
(self.exposures.gdf.value.values != 0)
& (self.exposures.gdf[self.hazard.centr_exp_col].values >= 0)
Expand All @@ -260,7 +278,8 @@ def minimal_exp_gdf(self, impf_col):
)
if exp_gdf.size == 0:
LOGGER.warning("No exposures with value >0 in the vicinity of the hazard.")
self._orig_exp_idx = mask.nonzero()[0] #update index of kept exposures points in exp_gdf within the full exposures
self._orig_exp_idx = mask.nonzero()[0] # update index of kept exposures points in exp_gdf
# within the full exposures
return exp_gdf

def imp_mat_gen(self, exp_gdf, impf_col):
Expand Down Expand Up @@ -381,10 +400,11 @@ def impact_matrix(self, exp_values, cent_idx, impf):
scipy.sparse.csr_matrix
Impact per event (rows) per exposure point (columns)
"""
n_exp_pnt = len(cent_idx) #implicitly checks in matrix assignement whether len(cent_idx) == len(exp_values)
n_exp_pnt = len(cent_idx) # implicitly checks in matrix assignement whether
# len(cent_idx) == len(exp_values)
mdr = self.hazard.get_mdr(cent_idx, impf)
fract = self.hazard.get_fraction(cent_idx)
exp_values_csr = sparse.csr_matrix( #vector 1 x exp_size
exp_values_csr = sparse.csr_matrix( # vector 1 x exp_size
(exp_values, np.arange(n_exp_pnt), [0, n_exp_pnt]),
shape=(1, n_exp_pnt))
return fract.multiply(mdr).multiply(exp_values_csr)
Expand All @@ -393,7 +413,9 @@ def stitch_impact_matrix(self, imp_mat_gen):
"""
Make an impact matrix from an impact sub-matrix generator
"""
data, row, col = np.hstack([ #rows=events index, cols=exposure point index within self.exposures
# rows: events index
# cols: exposure point index within self.exposures
data, row, col = np.hstack([
(mat.data, mat.nonzero()[0], self._orig_exp_idx[idx][mat.nonzero()[1]])
for mat, idx in imp_mat_gen
])
Expand Down
2 changes: 1 addition & 1 deletion climada/engine/test/test_cost_benefit.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ def test_impact(self):
hazard = Hazard.from_mat(HAZ_TEST_MAT)
impact = Impact()
ent.exposures.assign_centroids(hazard)
impact.calc(ent.exposures, ent.impact_funcs, hazard)
impact.calc(ent.exposures, ent.impact_funcs, hazard, reassign_centroids=False)
return impact

def test_risk_aai_agg_pass(self):
Expand Down
6 changes: 3 additions & 3 deletions climada/engine/test/test_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def test_excel_io(self):

imp_write = Impact()
ent.exposures.assign_centroids(hazard)
imp_write.calc(ent.exposures, ent.impact_funcs, hazard)
imp_write.calc(ent.exposures, ent.impact_funcs, hazard, reassign_centroids=False)
file_name = DATA_FOLDER.joinpath('test.xlsx')
imp_write.write_excel(file_name)

Expand Down Expand Up @@ -351,7 +351,7 @@ def test_local_exceedance_imp_pass(self):
# Assign centroids to exposures
ent.exposures.assign_centroids(hazard)
# Compute the impact over the whole exposures
impact.calc(ent.exposures, ent.impact_funcs, hazard, save_mat=True)
impact.calc(ent.exposures, ent.impact_funcs, hazard, save_mat=True, reassign_centroids=False)
# Compute the impact per return period over the whole exposures
impact_rp = impact.local_exceedance_imp(return_periods=(10, 40))

Expand Down Expand Up @@ -561,7 +561,7 @@ def test_select_event_identity_pass(self):
ent.exposures.assign_centroids(hazard)

# Compute the impact over the whole exposures
imp.calc(ent.exposures, ent.impact_funcs, hazard, save_mat=True)
imp.calc(ent.exposures, ent.impact_funcs, hazard, save_mat=True, reassign_centroids=False)

sel_imp = imp.select(event_ids=imp.event_id,
event_names=imp.event_name,
Expand Down
14 changes: 7 additions & 7 deletions climada/engine/test/test_impact_calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def test_calc_impact_TC_pass(self):
HAZf = deepcopy(HAZ)
HAZf.fraction *= 0.6
icalc = ImpactCalc(ENT.exposures, ENT.impact_funcs, HAZf)
impact = icalc.impact()
impact = icalc.impact(reassign_centroids=False)
self.assertEqual(icalc.n_events, len(impact.at_event))
self.assertEqual(0, impact.at_event[0])
self.assertEqual(0, impact.at_event[7225])
Expand All @@ -152,7 +152,7 @@ def test_calc_impact_RF_pass(self):
exp = Exposures.from_hdf5(get_test_file('test_exposure_US_flood_random_locations'))
impf_set = ImpactFuncSet.from_excel(Path(__file__).parent / 'data' / 'flood_imp_func_set.xls')
icalc = ImpactCalc(exp, impf_set, haz)
impact = icalc.impact()
impact = icalc.impact(reassign_centroids=False)
aai_agg = 161436.05112960344
eai_exp = np.array([
1.61159701e+05, 1.33742847e+02, 0.00000000e+00, 4.21352988e-01,
Expand Down Expand Up @@ -181,17 +181,17 @@ def test_calc_impact_RF_pass(self):
check_impact(self, impact, haz, exp, aai_agg, eai_exp, at_event, imp_mat_array)

def test_empty_impact(self):
"""Check that empty impact is returned if no centroids matching the exposures"""
"""Check that empty impact is returned if no centroids match the exposures"""
exp = ENT.exposures.copy()
exp.gdf['centr_TC'] = -1
icalc = ImpactCalc(exp, ENT.impact_funcs, HAZ)
impact = icalc.impact()
impact = icalc.impact(reassign_centroids=False)
aai_agg = 0.0
eai_exp = np.zeros(len(exp.gdf))
at_event = np.zeros(HAZ.size)
check_impact(self, impact, HAZ, exp, aai_agg, eai_exp, at_event, None)

impact = icalc.impact(save_mat=True)
impact = icalc.impact(save_mat=True, reassign_centroids=False)
imp_mat_array = sparse.csr_matrix((HAZ.size, len(exp.gdf))).toarray()
check_impact(self, impact, HAZ, exp, aai_agg, eai_exp, at_event, imp_mat_array)

Expand All @@ -204,7 +204,7 @@ def test_single_event_impact(self):
eai_exp = np.zeros(len(ENT.exposures.gdf))
at_event = np.array([0])
check_impact(self, impact, haz, ENT.exposures, aai_agg, eai_exp, at_event, None)
impact = icalc.impact(save_mat=True)
impact = icalc.impact(save_mat=True, reassign_centroids=False)
imp_mat_array = sparse.csr_matrix((haz.size, len(ENT.exposures.gdf))).toarray()
check_impact(self, impact, haz, ENT.exposures, aai_agg, eai_exp, at_event, imp_mat_array)

Expand Down Expand Up @@ -299,7 +299,7 @@ def test_calc_insured_impact_fail(self):
def test_minimal_exp_gdf(self):
"""Test obtain minimal exposures gdf"""
icalc = ImpactCalc(ENT.exposures, ENT.impact_funcs, HAZ)
exp_min_gdf = icalc.minimal_exp_gdf('impf_TC')
exp_min_gdf = icalc.minimal_exp_gdf('impf_TC', False)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe make it explicit here as well?

Suggested change
exp_min_gdf = icalc.minimal_exp_gdf('impf_TC', False)
exp_min_gdf = icalc.minimal_exp_gdf('impf_TC', reassign_centroids=False)

self.assertSetEqual(
set(exp_min_gdf.columns), set(['value', 'impf_TC', 'centr_TC'])
)
Expand Down
2 changes: 1 addition & 1 deletion climada/entity/exposures/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ def assign_centroids(self, hazard, distance='euclidean',
if overwrite:
LOGGER.info('Existing centroids will be overwritten for %s', haz_type)
else:
return None
return

LOGGER.info('Matching %s exposures with %s centroids.',
str(self.gdf.shape[0]), str(hazard.centroids.size))
Expand Down
3 changes: 2 additions & 1 deletion climada/test/test_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

Test Calibration class.
"""
import re
emanuel-schmid marked this conversation as resolved.
Show resolved Hide resolved
import unittest
from pathlib import Path
import pandas as pd
Expand Down Expand Up @@ -69,7 +70,7 @@ def test_calib_instance(self):
yearly_impact=True)
# calc Impact as comparison
impact = Impact()
impact.calc(ent.exposures, ent.impact_funcs, hazard)
impact.calc(ent.exposures, ent.impact_funcs, hazard, reassign_centroids=False)
IYS = impact.calc_impact_year_set(all_years=True)

# do the tests
Expand Down
4 changes: 2 additions & 2 deletions climada/util/lines_polys_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def calc_geom_impact(

# compute point impact
impact_pnt = Impact()
impact_pnt.calc(exp_pnt, impf_set, haz, save_mat=True)
impact_pnt.calc(exp_pnt, impf_set, haz, save_mat=True, reassign_centroids=False)

# re-aggregate impact to original exposure geometry
impact_agg = impact_pnt_agg(impact_pnt, exp_pnt.gdf, agg_met)
Expand Down Expand Up @@ -295,7 +295,7 @@ def calc_grid_impact(

# compute point impact
impact_pnt = Impact()
impact_pnt.calc(exp_pnt, impf_set, haz, save_mat=True)
impact_pnt.calc(exp_pnt, impf_set, haz, save_mat=True, reassign_centroids=False)

# re-aggregate impact to original exposure geometry
impact_agg = impact_pnt_agg(impact_pnt, exp_pnt.gdf, agg_met)
Expand Down