Skip to content

Commit b22e60d

Browse files
authored
Merge pull request #10080 from gem/damages2
Honoring `total_losses` in damage calculations too
2 parents 0b12d33 + 708a3ca commit b22e60d

File tree

12 files changed

+297
-684
lines changed

12 files changed

+297
-684
lines changed

openquake/calculators/event_based_damage.py

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ def damage_from_gmfs(gmfslices, oqparam, dstore, monitor):
7979
return event_based_damage(df, oqparam, dstore, monitor)
8080

8181

82-
def _gen_d4(asset_df, gmf_df, crmodel, dparam):
83-
# yields (aids, d4) triples
82+
def _gen_d3(asset_df, gmf_df, crmodel, dparam):
83+
# yields (aids, d3) triples
8484
oq = crmodel.oqparam
8585
sec_sims = oq.secondary_simulations.items()
8686
for prob_field, num_sims in sec_sims:
@@ -124,11 +124,18 @@ def _gen_d4(asset_df, gmf_df, crmodel, dparam):
124124
d4[lti, a, :, d] *= dprobs
125125

126126
df = crmodel.tmap_df[crmodel.tmap_df.taxi == assets[0]['taxonomy']]
127+
if 'losses' in crmodel.get_consequences():
128+
loss_types = oq.total_loss_types
129+
else:
130+
loss_types = {lt: i for i, lt in enumerate(oq.loss_types)}
127131
csq = crmodel.compute_csq(
128-
assets, d4[:, :, :, :D], df, oq.loss_types, oq.time_event)
132+
assets, d4[:, :, :, :D], df, loss_types, oq.time_event)
133+
d3 = numpy.zeros((A, E, dparam.Dc), F32)
134+
for li, lt in enumerate(oq.loss_types):
135+
d3[:] += d4[li]
129136
for name, values in csq.items():
130-
d4[:, :, :, dparam.csqidx[name]] = values
131-
yield aids, d4 # d4 has shape (L, A, E, Dc)
137+
d3[:, :, dparam.csqidx[name]] = values
138+
yield aids, d3 # d3 has shape (A, E, Dc)
132139

133140

134141
def event_based_damage(df, oq, dstore, monitor):
@@ -176,28 +183,30 @@ def event_based_damage(df, oq, dstore, monitor):
176183
else:
177184
rng = None
178185
dparam = Dparam(eids, aggids, rlzs, csqidx, D, Dc, rng)
179-
for aids, d4 in _gen_d4(asset_df, gmf_df, crmodel, dparam):
180-
for lti, d3 in enumerate(d4):
181-
if R == 1:
182-
dmgcsq[aids, 0] += d3.sum(axis=1)
183-
else:
184-
for e, rlz in enumerate(dparam.rlzs):
185-
dmgcsq[aids, rlz] += d3[:, e]
186-
tot = d3.sum(axis=0) # sum on the assets
187-
for e, eid in enumerate(eids):
188-
dddict[eid, oq.K] += tot[e]
189-
if oq.K:
190-
for kids in dparam.aggids:
191-
for a, aid in enumerate(aids):
192-
dddict[eid, kids[aid]] += d3[a, e]
193-
194-
return _dframe(dddict, csqidx, oq.loss_types), dmgcsq
186+
for aids, d3 in _gen_d3(asset_df, gmf_df, crmodel, dparam):
187+
if R == 1:
188+
dmgcsq[aids, 0] += d3.sum(axis=1)
189+
else:
190+
for e, rlz in enumerate(dparam.rlzs):
191+
dmgcsq[aids, rlz] += d3[:, e]
192+
tot = d3.sum(axis=0) # sum on the assets
193+
for e, eid in enumerate(eids):
194+
dddict[eid, oq.K] += tot[e]
195+
if oq.K:
196+
for kids in dparam.aggids:
197+
for a, aid in enumerate(aids):
198+
dddict[eid, kids[aid]] += d3[a, e]
199+
try:
200+
[lt] = oq.loss_types
201+
except ValueError:
202+
lt = oq.total_losses
203+
return _dframe(dddict, csqidx, [lt]), dmgcsq
195204

196205

197-
def _dframe(adic, csqidx, loss_types):
198-
# convert {eid, kid: dd} into a DataFrame (agg_id, event_id, loss_id)
206+
def _dframe(dddic, csqidx, loss_types):
207+
# convert {(eid, kid): dd} into a DataFrame (agg_id, event_id, loss_id)
199208
dic = general.AccumDict(accum=[])
200-
for (eid, kid), dd in sorted(adic.items()):
209+
for (eid, kid), dd in sorted(dddic.items()):
201210
for li, lt in enumerate(loss_types):
202211
dic['agg_id'].append(kid)
203212
dic['event_id'].append(eid)
@@ -301,12 +310,12 @@ def post_execute(self, dummy):
301310
D = len(self.crmodel.damage_states)
302311
# fix no_damage distribution for events with zero damage
303312
number = self.assetcol['value-number']
304-
nl = len(oq.loss_types)
313+
L = len(oq.loss_types)
305314
for r in range(self.R):
306315
ne = prc.num_events[r]
307316
self.dmgcsq[:, r, 0] = ( # no damage
308-
nl * number * ne - self.dmgcsq[:, r, 1:D].sum(axis=1))
309-
self.dmgcsq[:, r] /= ne
317+
number * ne * L - self.dmgcsq[:, r, 1:D].sum(axis=1))
318+
self.dmgcsq[:, r] /= (ne * L)
310319
assert (self.dmgcsq >= 0).all() # sanity check
311320
self.datastore['damages-rlzs'] = self.dmgcsq
312321
set_rlzs_stats(self.datastore,

openquake/calculators/multi_risk.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ def get_dmg_csq(crm, assets_by_site, gmf, time_event):
5959
for k, w in zip(df.risk_id, df.weight)]
6060
# NB: risk logic trees are not yet supported in multi_risk
6161
fracs[li] = rm.scenario_damage(loss_type, assets, peril_df, 'peril')
62-
csq = crm.compute_csq(assets, fracs, df, crm.loss_types, time_event)
62+
csq = crm.compute_csq(
63+
assets, fracs, df, crm.oqparam.total_loss_types, time_event)
6364
number = assets['value-number']
6465
for a, o in enumerate(assets['ordinal']):
6566
out[o, :, 0, :D] = number[a] * fracs[:, a]
66-
out[o, :, 0, [D]] = csq['losses'][:, a]
67+
out[o, :, 0, [D]] = csq['losses'][a]
6768
return out
6869

6970

openquake/calculators/tests/scenario_damage_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ def test_case_7(self):
161161
df = self.calc.datastore.read_df(
162162
'risk_by_event', ['event_id', 'loss_id', 'agg_id'],
163163
dict(agg_id=K))
164-
self.assertEqual(len(df), 300)
165-
self.assertEqual(len(df[df.dmg_1 > 0]), 174) # only 174/300 are nonzero
164+
self.assertEqual(len(df), 100)
165+
self.assertEqual(len(df[df.dmg_1 > 0]), 58) # only 58/100 are nonzero
166166

167167
def test_case_8(self):
168168
# case with a shakemap

openquake/calculators/views.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ def view_portfolio_loss(token, dstore):
552552
return text_table([['avg'] + avgs], ['loss'] + oq.loss_types)
553553

554554

555+
# used in the oq-risk-tests
555556
@view.add('portfolio_dmgdist')
556557
def portfolio_dmgdist(token, dstore):
557558
"""
@@ -561,10 +562,10 @@ def portfolio_dmgdist(token, dstore):
561562
dstates = ['no_damage'] + oq.limit_states
562563
D = len(dstates)
563564
arr = dstore['damages-rlzs'][:, 0, :D].sum(axis=0) # shape D
564-
tbl = numpy.zeros(len(arr), dt(['total'] + dstates))
565-
tbl['total'] = arr.sum(axis=1)
565+
tbl = numpy.zeros(1, dt(['total'] + dstates))
566+
tbl['total'] = arr.sum()
566567
for dsi, ds in enumerate(dstates):
567-
tbl[ds] = arr[:, dsi]
568+
tbl[ds] = arr[dsi]
568569
return tbl
569570

570571

openquake/commonlib/oqvalidation.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,11 @@ def check_risk(self):
13851385
if not self.investigation_time and self.hazard_calculation_id is None:
13861386
self.raise_invalid('missing investigation_time')
13871387

1388+
# check total_losses
1389+
if ('damage' in self.calculation_mode and len(self.loss_types) > 1
1390+
and not self.total_losses):
1391+
self.raise_invalid('you forgot to specify total_losses =')
1392+
13881393
def check_ebrisk(self):
13891394
# check specific to ebrisk
13901395
if self.calculation_mode == 'ebrisk':
@@ -1748,14 +1753,15 @@ def ext_loss_types(self):
17481753
@property
17491754
def total_loss_types(self):
17501755
"""
1751-
:returns: the loss types in total_losses or the single loss type
1756+
:returns: a dictionary loss_type -> index
17521757
"""
17531758
if self.total_losses:
1754-
return self.total_losses.split('+')
1759+
total = self.total_losses.split('+')
17551760
elif len(self.loss_types) == 1:
1756-
return self.loss_types
1761+
total = self.loss_types
17571762
else:
17581763
self.raise_invalid('please specify total_losses')
1764+
return {lt: li for li, lt in enumerate(self.loss_types) if lt in total}
17591765

17601766
def loss_dt(self, dtype=F64):
17611767
"""
Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
1-
#,,,,,,,,,"generated_by='OpenQuake engine 3.22.0-git083e1be7c8', start_date='2024-10-22T16:53:41', checksum=2278520494, investigation_time=None, risk_investigation_time=None"
1+
#,,,,,,,,,"generated_by='OpenQuake engine 3.22.0-git2c7171503a', start_date='2024-10-23T08:10:47', checksum=2278520494, investigation_time=None, risk_investigation_time=None"
22
loss_type,parent_id,rlz_id,no_damage,slight,moderate,extensive,complete,non_operational_value,non_operational_ratio
33
structural,A,0,9.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,1.00000E-01,1.00000E-01,1.00000E-01
44
structural,A,1,8.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,2.00000E-01,2.00000E-01,2.00000E-01
5-
liquefaction,A,0,9.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,1.00000E-01,1.00000E-01,1.00000E-01
6-
liquefaction,A,1,8.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,2.00000E-01,2.00000E-01,2.00000E-01
7-
landslide,A,0,9.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,1.00000E-01,1.00000E-01,1.00000E-01
8-
landslide,A,1,8.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,2.00000E-01,2.00000E-01,2.00000E-01
95
structural,B,0,1.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00
106
structural,B,1,1.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00
11-
liquefaction,B,0,1.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00
12-
liquefaction,B,1,1.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00
13-
landslide,B,0,1.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00
14-
landslide,B,1,1.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00
157
structural,E1,0,-2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,3.00000E+00,3.00000E+00,3.00000E+00
168
structural,E1,1,-2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,3.00000E+00,3.00000E+00,3.00000E+00
17-
liquefaction,E1,0,-2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,3.00000E+00,3.00000E+00,3.00000E+00
18-
liquefaction,E1,1,-2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,3.00000E+00,3.00000E+00,3.00000E+00
19-
landslide,E1,0,-2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,3.00000E+00,3.00000E+00,3.00000E+00
20-
landslide,E1,1,-2.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,3.00000E+00,3.00000E+00,3.00000E+00
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
#,,,,,,,,"generated_by='OpenQuake engine 3.22.0-git083e1be7c8', start_date='2024-10-22T16:53:41', checksum=2278520494, investigation_time=None, risk_investigation_time=None"
1+
#,,,,,,,,"generated_by='OpenQuake engine 3.22.0-git2c7171503a', start_date='2024-10-23T08:10:47', checksum=2278520494, investigation_time=None, risk_investigation_time=None"
22
loss_type,rlz_id,no_damage,slight,moderate,extensive,complete,non_operational_value,non_operational_ratio
33
structural,0,-1.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,3.10000E+00,3.10000E+00,1.03333E+00
44
structural,1,-2.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,3.20000E+00,3.20000E+00,1.06667E+00
5-
liquefaction,0,-1.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,3.10000E+00,3.10000E+00,1.03333E+00
6-
liquefaction,1,-2.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,3.20000E+00,3.20000E+00,1.06667E+00
7-
landslide,0,-1.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,3.10000E+00,3.10000E+00,1.03333E+00
8-
landslide,1,-2.00000E-01,0.00000E+00,0.00000E+00,0.00000E+00,3.20000E+00,3.20000E+00,1.06667E+00
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#,,,,,,,"generated_by='OpenQuake engine 3.22.0-git083e1be7c8', start_date='2024-10-22T16:53:41', checksum=2138001544, investigation_time=None, risk_investigation_time=None"
1+
#,,,,,,,"generated_by='OpenQuake engine 3.22.0-git2c7171503a', start_date='2024-10-23T08:10:48', checksum=2138001544, investigation_time=None, risk_investigation_time=None"
22
loss_type,no_damage,slight,moderate,extreme,complete,losses_value,losses_ratio
33
structural,2.29684E+00,1.41803E+00,1.39786E+00,9.49061E-01,8.93820E+00,1.84217E+04,1.08299E-01
4-
liquefaction,2.29684E+00,1.41803E+00,1.39786E+00,9.49061E-01,8.93820E+00,1.84217E+04,INF
5-
landslide,2.29684E+00,1.41803E+00,1.39786E+00,9.49061E-01,8.93820E+00,1.84217E+04,INF

0 commit comments

Comments
 (0)