diff --git a/debian/changelog b/debian/changelog
index 5021c74df6fe..8a541c1682be 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,5 @@
[Michele Simionato]
+ * Added support for consequence=losses for liquefaction and landslides
* Added a check for missing secondary perils
* Added loss types liquefaction and landslide
* Removed support for XML consequences, after 3 years of deprecation
diff --git a/openquake/calculators/event_based_damage.py b/openquake/calculators/event_based_damage.py
index c847c82bb39f..e3d26d5f29ab 100644
--- a/openquake/calculators/event_based_damage.py
+++ b/openquake/calculators/event_based_damage.py
@@ -18,6 +18,7 @@
import os.path
import logging
+from dataclasses import dataclass
import numpy
import pandas
@@ -36,6 +37,19 @@
U32 = numpy.uint32
F32 = numpy.float32
+@dataclass
+class Dparam:
+ """
+ Parameters for a damage calculation
+ """
+ eids: U32
+ aggids: U16
+ rlzs: U32
+ ci: dict
+ D: int
+ Dc: int
+ rng: scientific.MultiEventRNG
+
def zero_dmgcsq(A, R, crmodel):
"""
@@ -66,43 +80,39 @@ def damage_from_gmfs(gmfslices, oqparam, dstore, monitor):
return event_based_damage(df, oqparam, dstore, monitor)
-def _update(asset_df, gmf_df, aggids, allrlzs, sec_sims,
- crmodel, ci, R, Dc, dmgcsq, dddict):
+def _gen_d4(asset_df, gmf_df, crmodel, dparam):
+ # yields (aids, d4) triples
oq = crmodel.oqparam
- loss_types = oq.loss_types
- eids = gmf_df.eid.to_numpy()
- if R > 1:
- rlzs = allrlzs[eids]
- if sec_sims or not oq.float_dmg_dist:
- rng = scientific.MultiEventRNG(
- oq.master_seed, numpy.unique(eids))
+ sec_sims = oq.secondary_simulations.items()
for prob_field, num_sims in sec_sims:
probs = gmf_df[prob_field].to_numpy() # LiqProb
if not oq.float_dmg_dist:
- dprobs = rng.boolean_dist(probs, num_sims).mean(axis=1)
+ dprobs = dparam.rng.boolean_dist(probs, num_sims).mean(axis=1)
+ E = len(dparam.eids)
+ L = len(oq.loss_types)
for taxo, adf in asset_df.groupby('taxonomy'):
out = crmodel.get_output(adf, gmf_df)
aids = adf.index.to_numpy()
+ A = len(aids)
assets = adf.to_records()
if oq.float_dmg_dist:
number = assets['value-number']
else:
number = U32(assets['value-number'])
- for lti, lt in enumerate(loss_types):
+ d4 = numpy.zeros((L, A, E, dparam.Dc), F32)
+ D = dparam.D
+ for lti, lt in enumerate(oq.loss_types):
fractions = out[lt]
- Asid, E, D = fractions.shape
- assert len(eids) == E
- d3 = numpy.zeros((Asid, E, Dc), F32)
if oq.float_dmg_dist:
- d3[:, :, :D] = fractions
- for a in range(Asid):
- d3[a] *= number[a]
+ d4[lti, :, :, :D] = fractions
+ for a in range(A):
+ d4[lti, a] *= number[a]
else:
# this is a performance distaster; for instance
# the Messina test in oq-risk-tests becomes 12x
# slower even if it has only 25_736 assets
- d3[:, :, :D] = rng.discrete_dmg_dist(
- eids, fractions, number)
+ d4[lti, :, :, :D] = dparam.rng.discrete_dmg_dist(
+ dparam.eids, fractions, number)
# secondary perils and consequences
for a, asset in enumerate(assets):
@@ -110,27 +120,16 @@ def _update(asset_df, gmf_df, aggids, allrlzs, sec_sims,
for d in range(1, D):
# doing the mean on the secondary simulations
if oq.float_dmg_dist:
- d3[a, :, d] *= probs
+ d4[lti, a, :, d] *= probs
else:
- d3[a, :, d] *= dprobs
+ d4[lti, a, :, d] *= dprobs
csq = crmodel.compute_csq(
- asset, d3[a, :, :D] / number[a], lt,
+ asset, d4[lti, a, :, :D] / number[a], lt,
oq.time_event)
for name, values in csq.items():
- d3[a, :, ci[name]] = values
- if R == 1:
- dmgcsq[aids, 0, lti] += d3.sum(axis=1)
- else:
- for e, rlz in enumerate(rlzs):
- dmgcsq[aids, rlz, lti] += d3[:, e]
- tot = d3.sum(axis=0) # sum on the assets
- for e, eid in enumerate(eids):
- dddict[eid, oq.K][lti] += tot[e]
- if oq.K:
- for kids in aggids:
- for a, aid in enumerate(aids):
- dddict[eid, kids[aid]][lti] += d3[a, e]
+ d4[lti, a, :, dparam.ci[name]] = values
+ yield aids, d4 # d4 has shape (L, A, E, Dc)
def event_based_damage(df, oq, dstore, monitor):
@@ -144,27 +143,21 @@ def event_based_damage(df, oq, dstore, monitor):
mon_risk = monitor('computing risk', measuremem=False)
with monitor('reading gmf_data'):
if oq.parentdir:
- dstore = datastore.read(
- oq.hdf5path, parentdir=oq.parentdir)
+ dstore = datastore.read(oq.hdf5path, parentdir=oq.parentdir)
else:
dstore.open('r')
assetcol = dstore['assetcol']
- if oq.K:
- # TODO: move this in the controller!
- aggids, _ = assetcol.build_aggids(
- oq.aggregate_by, oq.max_aggregations)
- else:
- aggids = numpy.zeros(len(assetcol), U16)
crmodel = monitor.read('crmodel')
- sec_sims = oq.secondary_simulations.items()
+ aggids = monitor.read('aggids')
dmg_csq = crmodel.get_dmg_csq()
ci = {dc: i + 1 for i, dc in enumerate(dmg_csq)}
dmgcsq = zero_dmgcsq(len(assetcol), oq.R, crmodel)
_A, R, L, Dc = dmgcsq.shape
+ D = Dc - len(crmodel.get_consequences())
if R > 1:
allrlzs = dstore['events']['rlz_id']
else:
- allrlzs = [0]
+ allrlzs = U32([0])
assert len(oq.loss_types) == L
with mon_risk:
dddict = general.AccumDict(accum=numpy.zeros((L, Dc), F32)) # eid, kid
@@ -173,8 +166,33 @@ def event_based_damage(df, oq, dstore, monitor):
gmf_df = df[df.sid == sid]
if len(gmf_df) == 0:
continue
- _update(asset_df, gmf_df, aggids, allrlzs, sec_sims,
- crmodel, ci, R, Dc, dmgcsq, dddict)
+ oq = crmodel.oqparam
+ eids = gmf_df.eid.to_numpy()
+ if R > 1:
+ rlzs = allrlzs[eids]
+ else:
+ rlzs = allrlzs
+ if oq.secondary_simulations or not oq.float_dmg_dist:
+ rng = scientific.MultiEventRNG(
+ oq.master_seed, numpy.unique(eids))
+ else:
+ rng = None
+ dparam = Dparam(eids, aggids, rlzs, ci, D, Dc, rng)
+ for aids, d4 in _gen_d4(asset_df, gmf_df, crmodel, dparam):
+ for lti, d3 in enumerate(d4):
+ if R == 1:
+ dmgcsq[aids, 0, lti] += d3.sum(axis=1)
+ else:
+ for e, rlz in enumerate(dparam.rlzs):
+ dmgcsq[aids, rlz, lti] += d3[:, e]
+ tot = d3.sum(axis=0) # sum on the assets
+ for e, eid in enumerate(eids):
+ dddict[eid, oq.K][lti] += tot[e]
+ if oq.K:
+ for kids in dparam.aggids:
+ for a, aid in enumerate(aids):
+ dddict[eid, kids[aid]][lti] += d3[a, e]
+
return _dframe(dddict, ci, oq.loss_types), dmgcsq
@@ -226,8 +244,14 @@ def execute(self):
if oq.investigation_time: # event based
self.builder = get_loss_builder(self.datastore, oq) # check
self.dmgcsq = zero_dmgcsq(len(self.assetcol), self.R, self.crmodel)
- smap = calc.starmap_from_gmfs(damage_from_gmfs, oq, self.datastore,
- self._monitor)
+ if oq.K:
+ aggids, _ = self.assetcol.build_aggids(
+ oq.aggregate_by, oq.max_aggregations)
+ else:
+ aggids = 0
+ smap = calc.starmap_from_gmfs(damage_from_gmfs, oq,
+ self.datastore, self._monitor)
+ smap.monitor.save('aggids', aggids)
smap.monitor.save('assets', self.assetcol.to_dframe('id'))
smap.monitor.save('crmodel', self.crmodel)
return smap.reduce(self.combine)
diff --git a/openquake/calculators/event_based_risk.py b/openquake/calculators/event_based_risk.py
index b978e6dfb196..917dcaf54ef8 100644
--- a/openquake/calculators/event_based_risk.py
+++ b/openquake/calculators/event_based_risk.py
@@ -247,7 +247,7 @@ def gen_outputs(df, crmodel, rng, monitor):
yield out
-def check_tot_loss_unit_consistency(units, total_losses, loss_types):
+def _tot_loss_unit_consistency(units, total_losses, loss_types):
total_losses_units = set()
for separate_lt in total_losses.split('+'):
assert separate_lt in loss_types
@@ -279,10 +279,12 @@ def set_oqparam(oq, assetcol, dstore):
partial(insurance_losses, policy_df=policy_df))
ideduc = assetcol['ideductible'].any()
- if oq.total_losses:
- units = dstore['exposure'].cost_calculator.get_units(oq.loss_types)
- check_tot_loss_unit_consistency(
- units.split(), oq.total_losses, oq.loss_types)
+ cc = dstore['exposure'].cost_calculator
+ if oq.total_losses and cc.cost_types:
+ # cc.cost_types is empty in scenario_damage/case_21 (consequences)
+ units = cc.get_units(oq.total_loss_types)
+ _tot_loss_unit_consistency(
+ units.split(), oq.total_losses, oq.total_loss_types)
sec_losses.append(
partial(total_losses, kind=oq.total_losses, ideduc=ideduc))
elif ideduc:
diff --git a/openquake/calculators/tests/scenario_damage_test.py b/openquake/calculators/tests/scenario_damage_test.py
index dfb8379d476c..0be8252c566b 100644
--- a/openquake/calculators/tests/scenario_damage_test.py
+++ b/openquake/calculators/tests/scenario_damage_test.py
@@ -24,7 +24,7 @@
from openquake.qa_tests_data.scenario_damage import (
case_1, case_1c, case_2, case_3, case_4, case_4b, case_5, case_5a,
case_6, case_7, case_8, case_9, case_10, case_11, case_12, case_13,
- case_14, case_16, case_17, case_18, case_19, case_20, case_21)
+ case_14, case_16, case_17, case_18, case_19, case_20, case_21, case_22)
from openquake.calculators.tests import CalculatorTestCase, strip_calc_id
from openquake.calculators.extract import extract
from openquake.calculators.export import export
@@ -298,12 +298,18 @@ def test_case_20(self):
self.assertEqualFiles('expected/aggrisk.csv', fname)
def test_case_21(self):
- # infrastructure risk for structural and liquefaction loss types
+ # infrastructure risk for structural, liquefaction and landslides
out = self.run_calc(case_21.__file__, 'job.ini', exports='csv')
[agg_csv, aggparent_csv] = out[('aggrisk', 'csv')]
self.assertEqualFiles('expected/aggrisk.csv', agg_csv)
self.assertEqualFiles('expected/aggrisk-parent.csv', aggparent_csv)
+ def test_case_22(self):
+ # losses with liquefaction and landslides
+ out = self.run_calc(case_22.__file__, 'job.ini', exports='csv')
+ [agg_csv] = out[('aggrisk', 'csv')]
+ self.assertEqualFiles('expected/aggrisk.csv', agg_csv)
+
def losses(aid, alt):
E = len(alt.event_id.unique())
diff --git a/openquake/commonlib/oqvalidation.py b/openquake/commonlib/oqvalidation.py
index 6611ef54200a..3afa63badcca 100644
--- a/openquake/commonlib/oqvalidation.py
+++ b/openquake/commonlib/oqvalidation.py
@@ -1745,6 +1745,18 @@ def ext_loss_types(self):
etypes = self.loss_types + itypes
return etypes
+ @property
+ def total_loss_types(self):
+ """
+ :returns: the loss types in total_losses or the single loss type
+ """
+ if self.total_losses:
+ return self.total_losses.split('+')
+ elif len(self.loss_types) == 1:
+ return self.loss_types
+ else:
+ self.raise_invalid('please specify total_losses')
+
def loss_dt(self, dtype=F64):
"""
:returns: a composite dtype based on the loss types including occupants
diff --git a/openquake/hazardlib/valid.py b/openquake/hazardlib/valid.py
index b0086fb120dc..585b630196d3 100644
--- a/openquake/hazardlib/valid.py
+++ b/openquake/hazardlib/valid.py
@@ -980,20 +980,11 @@ def dictionary(value):
raise ValueError('%r is not a valid Python dictionary' % value)
for key, val in dic.items():
- try:
- has_logscale = (val[0] == 'logscale')
- except IndexError: # no val[0]
- continue
- if has_logscale:
- dic[key] = list(logscale(*val[1:]))
-
- try:
- has_linscale = (val[0] == 'linscale')
- except IndexError: # no val[0]
- continue
- if has_linscale:
- dic[key] = list(linscale(*val[1:]))
-
+ if isinstance(val, tuple):
+ if val[0] == 'logscale':
+ dic[key] = list(logscale(*val[1:]))
+ elif val[0] == 'linscale':
+ dic[key] = list(linscale(*val[1:]))
return dic
diff --git a/openquake/qa_tests_data/scenario_damage/case_21/job.ini b/openquake/qa_tests_data/scenario_damage/case_21/job.ini
index bc8975a7d1c9..764c6b2ed2b1 100644
--- a/openquake/qa_tests_data/scenario_damage/case_21/job.ini
+++ b/openquake/qa_tests_data/scenario_damage/case_21/job.ini
@@ -30,6 +30,7 @@ liquefaction_fragility_file = fragility_model_liquefaction.xml
landslide_fragility_file = fragility_model_landslide.xml
[consequence]
+total_losses = structural
consequence_file = {'taxonomy': 'consequence_multiple_loss_types.csv'}
[risk_calculation]
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/__init__.py b/openquake/qa_tests_data/scenario_damage/case_22/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/consequences.csv b/openquake/qa_tests_data/scenario_damage/case_22/consequences.csv
new file mode 100644
index 000000000000..c4a55750cfe9
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/consequences.csv
@@ -0,0 +1,7 @@
+taxonomy,consequence,loss_type,slight,moderate,extreme,complete
+Concrete,losses,structural,0.04,0.31,0.6,1
+Wood,losses,structural,0.04,0.31,0.6,1
+Concrete,losses,liquefaction,0,0,0,1
+Wood,losses,liquefaction,0,0,0,1
+Concrete,losses,landslide,0,0,0,1
+Wood,losses,landslide,0,0,0,1
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/expected/aggrisk.csv b/openquake/qa_tests_data/scenario_damage/case_22/expected/aggrisk.csv
new file mode 100644
index 000000000000..87b4af7d46fd
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/expected/aggrisk.csv
@@ -0,0 +1,5 @@
+#,,,,,,,"generated_by='OpenQuake engine 3.22.0-gitff750fe4b4', start_date='2024-10-21T11:16:00', checksum=2624933730, investigation_time=None, risk_investigation_time=None"
+loss_type,no_damage,slight,moderate,extreme,complete,losses_value,losses_ratio
+structural,1.06701E+01,1.41803E+00,1.39786E+00,9.49061E-01,5.64993E-01,1.84217E+04,1.08299E-01
+liquefaction,1.26268E+01,0.00000E+00,0.00000E+00,0.00000E+00,2.37321E+00,0.00000E+00,nan
+landslide,9.00000E+00,0.00000E+00,0.00000E+00,0.00000E+00,6.00000E+00,0.00000E+00,nan
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/exposure_model.csv b/openquake/qa_tests_data/scenario_damage/case_22/exposure_model.csv
new file mode 100644
index 000000000000..279ebef04460
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/exposure_model.csv
@@ -0,0 +1,16 @@
+id,lon,lat,number,structural,night,taxonomy,NAME_1
+a1,83.31382,29.46117,1,11340,5,Wood1,a
+a2,83.31382,29.23617,1,11340,5,Wood1,a
+a3,83.53882,29.08617,1,11340,5,Wood1,a
+a4,80.68882,28.93617,1,11340,5,Wood1,a
+a5,83.53882,29.01117,1,11340,5,Wood1,a
+a6,81.13882,28.78617,1,11340,5,Wood1,a
+a7,83.98882,28.48617,1,11340,5,Wood1,a
+a8,83.23882,29.38617,1,11340,5,Concrete1,a
+a9,83.01382,29.08617,1,11340,5,Concrete1,a
+a10,83.31382,28.71117,1,11340,5,Concrete1,a
+a11,86.91382,27.73617,1,11340,5,Concrete1,a
+a12,83.16382,29.31117,1,11340,5,Concrete1,a
+a13,80.61382,28.93617,1,11340,5,Concrete1,a
+a14,83.91382,29.01117,1,11340,5,Concrete1,a
+a15,82.03882,30.28617,1,11340,5,Concrete1,a
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/exposure_model.xml b/openquake/qa_tests_data/scenario_damage/case_22/exposure_model.xml
new file mode 100644
index 000000000000..70ef9443dc3d
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/exposure_model.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+ night
+
+
+ NAME_1
+
+
+ exposure_model.csv
+
+
+
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/fault_rupture.xml b/openquake/qa_tests_data/scenario_damage/case_22/fault_rupture.xml
new file mode 100644
index 000000000000..b65de560282e
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/fault_rupture.xml
@@ -0,0 +1,19 @@
+
+
+
+ 7.0
+ 90
+
+
+
+
+ 85.0 27.3
+ 83.8 27.8
+
+
+ 30.0
+ 20.0
+ 50.0
+
+
+
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_landslide.xml b/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_landslide.xml
new file mode 100644
index 000000000000..0d5702a8ed3d
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_landslide.xml
@@ -0,0 +1,21 @@
+
+
+
+ Road Network
+ slight moderate extreme complete
+
+ 0 0.3 0.6 1
+ 0 0 0 1
+ 0 0 0 1
+ 0 0 0 1
+ 0 0 0 1
+
+
+ 0 0.3 0.6 1
+ 0 1 1 1
+ 0 1 1 1
+ 0 1 1 1
+ 0 1 1 1
+
+
+
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_liquefaction.xml b/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_liquefaction.xml
new file mode 100644
index 000000000000..4c14181d25fa
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_liquefaction.xml
@@ -0,0 +1,21 @@
+
+
+
+ Road Network
+ slight moderate extreme complete
+
+ 0 0.3 0.6 1
+ 0 0 0 1
+ 0 0 0 1
+ 0 0 0 1
+ 0 0 0 1
+
+
+ 0 0.3 0.6 1
+ 0 1 1 1
+ 0 1 1 1
+ 0 1 1 1
+ 0 1 1 1
+
+
+
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_structural.xml b/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_structural.xml
new file mode 100644
index 000000000000..890d1a1ed3e5
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/fragility_model_structural.xml
@@ -0,0 +1,47 @@
+
+
+
+
+ Fragility model for Nepal (discrete)
+
+
+ slight moderate extreme complete
+
+
+
+ 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4
+
+
+ 0.0 0.788 0.97 0.994 0.998 1.0 1.0 1.0
+
+
+ 0.0 0.6 0.909 0.976 0.992 0.997 0.999 1.0
+
+
+ 0.0 0.341 0.75 0.904 0.96 0.982 0.992 0.996
+
+
+ 0.0 0.109 0.441 0.686 0.825 0.9 0.942 0.965
+
+
+
+
+
+ 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4
+
+
+ 0.0 0.5 0.861 0.957 0.985 0.994 0.997 0.999
+
+
+ 0.0 0.204 0.6 0.813 0.909 0.954 0.976 0.986
+
+
+ 0.0 0.041 0.255 0.49 0.664 0.78 0.855 0.903
+
+
+ 0.0 0.007 0.088 0.236 0.394 0.532 0.642 0.728
+
+
+
+
+
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/job.ini b/openquake/qa_tests_data/scenario_damage/case_22/job.ini
new file mode 100644
index 000000000000..b1cee9bce20a
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/job.ini
@@ -0,0 +1,36 @@
+[general]
+description = Scenario Damage and Consequences Demo (Nepal)
+calculation_mode = scenario_damage
+
+[exposure]
+exposure_file = exposure_model.xml
+taxonomy_mapping_csv = mapping_multiple_loss_types.csv
+
+[rupture]
+rupture_model_file = fault_rupture.xml
+rupture_mesh_spacing = 15
+
+[Secondary perils]
+secondary_perils = NewmarkDisplacement, AllstadtEtAl2022Liquefaction
+
+[site_params]
+site_model_file = site_model.csv
+
+[calculation]
+intensity_measure_types = PGA, PGV
+truncation_level = 3.0
+maximum_distance = 200
+gsim = ChiouYoungs2008
+number_of_ground_motion_fields = 10
+
+[fragility]
+structural_fragility_file = fragility_model_structural.xml
+liquefaction_fragility_file = fragility_model_liquefaction.xml
+landslide_fragility_file = fragility_model_landslide.xml
+
+[consequence]
+total_losses = structural
+consequence_file = {'taxonomy': 'consequences.csv'}
+
+[export]
+export_dir = /tmp/
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/mapping_multiple_loss_types.csv b/openquake/qa_tests_data/scenario_damage/case_22/mapping_multiple_loss_types.csv
new file mode 100644
index 000000000000..6871342b83a2
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/mapping_multiple_loss_types.csv
@@ -0,0 +1,7 @@
+taxonomy,conversion,weight,loss_type
+Wood1,Wood,1,structural
+Concrete1,Concrete,1,structural
+Wood1,Wood,1,liquefaction
+Concrete1,Concrete,1,liquefaction
+Wood1,Wood,1,landslide
+Concrete1,Concrete,1,landslide
diff --git a/openquake/qa_tests_data/scenario_damage/case_22/site_model.csv b/openquake/qa_tests_data/scenario_damage/case_22/site_model.csv
new file mode 100644
index 000000000000..105cfdaadfe1
--- /dev/null
+++ b/openquake/qa_tests_data/scenario_damage/case_22/site_model.csv
@@ -0,0 +1,16 @@
+site_id,lon,lat,vs30,z1pt0,z2pt5,gwd,precip,dc,dr,dw,slope,cohesion_mid,friction_mid,saturation,dry_density
+0,83.31382,29.46117,183,513.4083567,3.093638299,0.946059358,1796,187,1.80517065,1.80517065,20,20000,35,2,1500
+1,83.31382,29.23617,221,502.7851667,2.493037557,2.853437095,1833,183,5.991748144,5.991748144,20,20000,35,2,1500
+2,83.53882,29.08617,211,506.1375293,2.628660223,0.076295109,1775,195,2.63670763,2.63670763,20,20000,35,2,1500
+3,80.68882,28.93617,183,513.4083567,3.093638299,0.946059358,1796,187,1.80517065,1.80517065,20,20000,35,2,1500
+4,83.53882,29.01117,221,502.7851667,2.493037557,2.853437095,1833,183,5.991748144,5.991748144,20,20000,35,2,1500
+5,81.13882,28.78617,211,506.1375293,2.628660223,0.076295109,1775,195,2.63670763,2.63670763,20,20000,35,2,1500
+6,83.98882,28.48617,183,513.4083567,3.093638299,0.946059358,1796,187,1.80517065,1.80517065,20,20000,35,2,1500
+7,83.23882,29.38617,221,502.7851667,2.493037557,2.853437095,1833,183,5.991748144,5.991748144,20,20000,35,2,1500
+8,83.01382,29.08617,211,506.1375293,2.628660223,0.076295109,1775,195,2.63670763,2.63670763,20,20000,35,2,1500
+9,83.31382,28.71117,183,513.4083567,3.093638299,0.946059358,1796,187,1.80517065,1.80517065,20,20000,35,2,1500
+10,86.91382,27.73617,221,502.7851667,2.493037557,2.853437095,1833,183,5.991748144,5.991748144,20,20000,35,2,1500
+11,83.16382,29.31117,211,506.1375293,2.628660223,0.076295109,1775,195,2.63670763,2.63670763,20,20000,35,2,1500
+12,80.61382,28.93617,183,513.4083567,3.093638299,0.946059358,1796,187,1.80517065,1.80517065,20,20000,35,2,1500
+13,83.91382,29.01117,221,502.7851667,2.493037557,2.853437095,1833,183,5.991748144,5.991748144,20,20000,35,2,1500
+14,82.03882,30.28617,211,506.1375293,2.628660223,0.076295109,1775,195,2.63670763,2.63670763,20,20000,35,2,1500
diff --git a/openquake/risklib/scientific.py b/openquake/risklib/scientific.py
index a1ed615873de..6565859698b0 100644
--- a/openquake/risklib/scientific.py
+++ b/openquake/risklib/scientific.py
@@ -1722,7 +1722,11 @@ def consequence(consequence, coeffs, asset, dmgdist, loss_type, time_event):
if consequence not in KNOWN_CONSEQUENCES:
raise NotImplementedError(consequence)
if consequence.startswith(('loss', 'losses')):
- return dmgdist @ coeffs * asset['value-' + loss_type]
+ try:
+ value = asset['value-' + loss_type]
+ except ValueError: # landslide, liquefaction
+ return 0
+ return dmgdist @ coeffs * value
elif consequence in ['collapsed', 'non_operational']:
return dmgdist @ coeffs * asset['value-number']
elif consequence in ['injured', 'fatalities']:
@@ -1754,7 +1758,10 @@ def get_agg_value(consequence, agg_values, agg_id, xltype, time_event):
xltype = xltype[:-4]
if '+' in xltype: # total loss type
return sum(aval[lt] for lt in xltype.split('+'))
- return aval[xltype]
+ try:
+ return aval[xltype]
+ except ValueError: # liquefaction, landslide
+ return 0
else:
raise NotImplementedError(consequence)