Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 9 additions & 3 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
[Michele Simionato]
* Fixed a bug affecting previous versions of the engine: with few sites
only the ruptures of the first atomic group of a task were stored,
so the disaggregation missed the ruptures of the other atomic groups
* Fixed a bug affecting previous versions of the engine: with
site_labels the atomic groups (cluster or mutex sources) were
computed only for the sites of the last label. This affects the
USA model near New Madrid and Memphis
disagg_by_src the rates of multiple atomic groups were all attributed
to the source of the first group in mean_rates_by_src, and the
ruptures of the other groups were not stored; with site_labels the
atomic groups (cluster or mutex sources) were computed only for the
sites of the last label. This affects the USA model near New Madrid
and Memphis

[Anirudh Rao]
* Added support for generating regular UTM site grids from exposure,
Expand Down
57 changes: 42 additions & 15 deletions openquake/calculators/classical.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def geometry_key(srcs, bvals):
# e.g. logictree/case_18_bis: the sources of 'first' have two keys, one
# for setLowerSeismDepthAbsolute 10.0 and one for 20.0, with 9 sets each
# (3 abGR x 3 maxMagGR), i.e. two groups of 9 variants for make_results:
# (('bs6', '10.0'),) and (('bs6', '20.0'),)
# (('bs6', '10.0'),) and (('bs6', '20.0'),)
return tuple((bset.id, repr(value)) for bset, value in bvals
if bset.uncertainty_type not in GEOMETRY_PRESERVING and
any(get_bset_value(bset, value, src)[0] for src in srcs))
Expand Down Expand Up @@ -363,8 +363,33 @@ def _make_results(pairs, sites, remove_zeros, as_rmap):
return dics


def _atomic_results(grps, sites, cmaker, remove_zeros, as_rmap,
merge_atomic):
# yield the results of the atomic groups collapsed in a single
# task (see get_allargs and bysrc_results); they contribute to the
# same RateMap in the master, with the gids of all the
# realizations; the uncertainties are not applied, which is fine
# because the sources of an atomic group are nonparametric (the
# mutex ones must be, since mutually exclusive ruptures are
# modelled with nonparametric sources)
if merge_atomic:
# with many sites there are no rup_data nor mean_rates_by_src
yield baseclassical(grps, sites, cmaker, remove_zeros,
as_rmap=as_rmap)
else:
# one result per group, since the rates are attributed to the
# source of each result (see 'basename' in RmapMaker.make) and
# the rup_data of different groups cannot be concatenated
# (different shapes of probs_occur); the groups are
# independent, so the sum of the results is the same;
# tested in classical/case_27_other and disagg/case_18
for grp in grps:
yield baseclassical([grp], sites, cmaker, remove_zeros,
as_rmap=as_rmap)


def bysrc_results(grps, sites, cmaker, gid_dic, full_lt, remove_zeros,
as_rmap=True):
as_rmap=True, merge_atomic=True):
"""
Yield the results of a classical task as RateMaps: the CSM is built without
applying the uncertainties, so cmakers_groups_gkeys applies them one
Expand All @@ -379,17 +404,12 @@ def bysrc_results(grps, sites, cmaker, gid_dic, full_lt, remove_zeros,
:param remove_zeros: if True the sites with zero rates are removed
:param as_rmap: if False the rates are returned as arrays of rates,
stored right away by the master, see baseclassical
:param merge_atomic: if True the atomic groups of the task are
returned in a single result, otherwise in a result per group
"""
if len(grps) > 1:
# the atomic groups collapsed in a single task (see get_allargs)
# contribute to the same RateMap in the master, so they must be
# returned in a single result, with the gids of all the
# realizations; the uncertainties are not applied, which is fine
# because the sources of an atomic group are nonparametric (the
# mutex ones must be, since mutually exclusive ruptures are
# modelled with nonparametric sources) and no uncertainty can be
# applied to a nonparametric source (the calculation would fail)
yield baseclassical(grps, sites, cmaker, remove_zeros, as_rmap=as_rmap)
yield from _atomic_results(grps, sites, cmaker, remove_zeros,
as_rmap, merge_atomic)
return
grp = grps[0]
if grp.atomic:
Expand Down Expand Up @@ -496,8 +516,13 @@ def classical_bysrc(grp_keys, tilegetter, cmaker, dstore, monitor):
"""
grps, sites, gid_dic, full_lt = read_task_input(
grp_keys, tilegetter, cmaker, dstore, monitor)
yield from bysrc_results(grps, sites, cmaker, gid_dic, full_lt,
remove_zeros=False)
# the RateMap in the master is the one of the first group (see
# get_rmap), also for the results of the other atomic groups
rmap_id = int(grp_keys[0].split('-')[0])
for dic in bysrc_results(grps, sites, cmaker, gid_dic, full_lt,
remove_zeros=False, merge_atomic=False):
dic['rmap_id'] = rmap_id
yield dic


def classical(grp_keys, tilegetter, cmaker, dstore, monitor):
Expand Down Expand Up @@ -751,8 +776,10 @@ def agg_dicts(self, acc, dic):
chunkno = dic.get('chunkno') # None with the current impl.
_store(rmap, self.num_chunks, self.datastore, chunkno)
else:
# aggregating rates is ultra-fast compared to storing
self.rmap[grp_id] += rmap
# aggregating rates is ultra-fast compared to storing; NB: the
# results of the atomic groups go in the RateMap of the first
# group of the task, see classical_bysrc
self.rmap[dic.pop('rmap_id', grp_id)] += rmap
return acc

def create_rup(self):
Expand Down
20 changes: 19 additions & 1 deletion openquake/calculators/tests/classical_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,9 @@ def test_case_27(self): # Nankai mutex model
# make sure probs_occur are stored as expected
probs_occur = self.calc.datastore['rup/probs_occur'][:]
tot_probs_occur = sum(len(po) for po in probs_occur)
self.assertEqual(tot_probs_occur, 4) # 2 x 2
# 2 atomic groups x 2 ruptures x 2 probabilities; there were 4
# when the ruptures of the second atomic group were not stored
self.assertEqual(tot_probs_occur, 8)

# check mean_rates_by_src
[fname] = export(('mean_rates_by_src', 'csv'), self.calc.datastore)
Expand All @@ -419,6 +421,22 @@ def test_case_27(self): # Nankai mutex model
fname = general.gettemp(text_table(rel, ext='org'))
self.assertEqualFiles('expected/rel_source.org', fname)

def test_case_27_other(self):
# the second atomic group has sources named other:03, other:04;
# with disagg_by_src the rates of each atomic group must be
# attributed to its own source, while they were all attributed to
# the first group of the task, i.e. case=0 and other=9.39E-3
self.run_calc(case_27.__file__, 'job_other.ini')
mrs = self.calc.datastore['mean_rates_by_src']
rates = dict(zip(mrs.src_id, mrs.array[0, 0, 0])) # site 0, PGV
aac([rates['case'], rates['other']], [5.1798E-3, 4.2115E-3],
rtol=1E-4)
# the total is the same as in case_27, where both groups are 'case'
aac(rates['case'] + rates['other'], 9.3913E-3, rtol=1E-4)
# the ruptures of both atomic groups are stored, see test_case_27
probs_occur = self.calc.datastore['rup/probs_occur'][:]
self.assertEqual(sum(len(po) for po in probs_occur), 8)

def test_case_27_labels(self):
# two close sites with different labels and the same gsim: the
# atomic groups must be computed for both, while they were
Expand Down
16 changes: 15 additions & 1 deletion openquake/calculators/tests/disagg_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from openquake.qa_tests_data.disagg import (
case_1, case_2, case_3, case_4, case_5, case_6, case_7, case_8, case_9,
case_10, case_11, case_12, case_13, case_14, case_15, case_16, case_17,
case_master)
case_18, case_master)

aae = numpy.testing.assert_almost_equal
ae = numpy.testing.assert_equal
Expand Down Expand Up @@ -287,6 +287,20 @@ def test_case_17(self):
# it depends on the maxMagGRRelative uncertainty on the source 1
aae(dstore.read_df("mce").MCE, [0.46, 1.6310, 1.51], 4)

def test_case_18(self):
# Nankai mutex model with two atomic groups in the same task (see
# concurrent_tasks in job.ini): the ruptures of both groups must
# be stored, while only the ones of the first group were stored
# (4 probs_occur instead of 8), so the disaggregation missed the
# large Nankai ruptures of the second group (Mag sum .19 vs .41)
self.run_calc(case_18.__file__, 'job.ini')
probs_occur = self.calc.datastore['rup/probs_occur'][:]
self.assertEqual(sum(len(po) for po in probs_occur), 8)
fnames = export(('disagg-rlzs', 'csv'), self.calc.datastore)
for fname in fnames:
self.assertEqualFiles(
'expected_output/' + strip_calc_id(fname), fname)

def test_case_master(self):
# this tests exercise the case of a complex logic tree
self.run_calc(case_master.__file__, 'job.ini')
Expand Down
1 change: 1 addition & 0 deletions openquake/qa_tests_data/classical/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
|case\_25|Test negative depths|
|case\_26|Test YoungsCoppersmith1985MFD|
|case\_27|Nankai mutex model, including disaggregation|
|case\_27\_other|Nankai mutex model with disagg\_by\_src and atomic groups with different source names|
|case\_27\_labels|Nankai mutex model with site\_labels|
|case\_28|Test hypoDepthDist (and down-dip fractions for hypo placement) with a simple fault source|
|case\_29|Non-parametric source with one rupture represente by 2 kite surfaces|
Expand Down
41 changes: 41 additions & 0 deletions openquake/qa_tests_data/classical/case_27/job_other.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[general]

description = Nankai mutex model with atomic groups with different source names
calculation_mode = classical
random_seed = 1066

[geometry]

sites = 135.68 35.68

[logic_tree]

# end branch enumeration
number_of_logic_tree_samples = 0

[erf]

# km
rupture_mesh_spacing = 1.
width_of_mfd_bin = 1.
area_source_discretization =

[site_params]

reference_vs30_type = measured
reference_vs30_value = 400.0
reference_depth_to_2pt5km_per_sec = 1.
reference_depth_to_1pt0km_per_sec = 100.

[calculation]

source_model_logic_tree_file = source_model_logic_tree_other.xml
gsim_logic_tree_file = gsim_logic_tree.xml
# years
investigation_time = 50.
intensity_measure_types_and_levels = {"PGV": [20]}
# km
maximum_distance = 200.
truncation_level = 99.
disagg_by_src = true
poes = .02
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<nrml xmlns:gml="http://www.opengis.net/gml"
xmlns="http://openquake.org/xmlns/nrml/0.4">
<logicTree logicTreeID="lt1">
<logicTreeBranchingLevel branchingLevelID="bl1">
<logicTreeBranchSet uncertaintyType="sourceModel"
branchSetID="bs1">
<logicTreeBranch branchID="b1">
<uncertaintyModel>source_model_other.xml</uncertaintyModel>
<uncertaintyWeight>1.0</uncertaintyWeight>
</logicTreeBranch>
</logicTreeBranchSet>
</logicTreeBranchingLevel>
</logicTree>
</nrml>
Binary file not shown.
115 changes: 115 additions & 0 deletions openquake/qa_tests_data/classical/case_27/source_model_other.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<nrml
xmlns="http://openquake.org/xmlns/nrml/0.5"
xmlns:gml="http://www.opengis.net/gml"
>
<sourceModel
investigation_time="50.0"
name="test"
>
<sourceGroup
name="Nankai"
rup_interdep="indep"
src_interdep="mutex"
srcs_weights="0.625 0.375"
tectonicRegion="Subduction Interface"
>
<nonParametricSeismicSource id="case:01" name="Nankai Trough - CASE 1"/>
<nonParametricSeismicSource id="case:02" name="Nankai Trough - CASE 2"/>
</sourceGroup>
<sourceGroup
name="point source"
tectonicRegion="Subduction Interface">
<pointSource
id="gs_PSE_CPCF_2_100"
name="PSE_CPCF"
>
<pointGeometry>
<gml:Point>
<gml:pos>
135.705 35.898
</gml:pos>
</gml:Point>
<upperSeismoDepth>
64.995
</upperSeismoDepth>
<lowerSeismoDepth>
65.00500000000001
</lowerSeismoDepth>
</pointGeometry>
<magScaleRel>
PointMSR
</magScaleRel>
<ruptAspectRatio>
1
</ruptAspectRatio>
<arbitraryMFD>
<magnitudes>
5.0 5.1 5.199999999999999 5.299999999999999 5.399999999999999 5.499999999999998 5.599999999999998 5.6999999999999975 5.799999999999997 5.899999999999997 5.9999999999999964 6.099999999999996 6.199999999999996 6.299999999999995 6.399999999999995 6.499999999999995 6.599999999999994 6.699999999999994 6.799999999999994 6.899999999999993 6.999999999999993 7.0999999999999925 7.199999999999992 7.299999999999992 7.3999999999999915 7.499999999999991
</magnitudes>
<occurRates>
0.0017404297316440736 0.0014146743971195982 0.0011498905204148596 0.0009346661052410158 0.0007597251327641438 0.0006175277718075182 0.0005019454175039715 0.00040799655279595776 0.0003316320486023118 0.00026956064924197487 0.00021910712166088434 0.00017809695479484682 0.00014476263969314965 0.00011766749114306046 9.56437275615485e-05 7.774214044171201e-05 6.319118414293873e-05 5.136372282392555e-05 4.175000133508116e-05 3.393567513504588e-05 2.758395233639655e-05 2.242107821543914e-05 1.8224536578811047e-05 1.48134594742065e-05 1.2040831910595241e-05 9.7871556169343e-06
</occurRates>
</arbitraryMFD>
<nodalPlaneDist>
<nodalPlane dip="41.0" probability="1.0" rake="90" strike="232.0"/>
</nodalPlaneDist>
<hypoDepthDist>
<hypoDepth depth="65.0" probability="1.0"/>
</hypoDepthDist>
</pointSource>
</sourceGroup>
<sourceGroup
name="Nankai2"
rup_interdep="indep"
src_interdep="mutex"
srcs_weights="0.725 0.275"
tectonicRegion="Subduction Interface"
>
<nonParametricSeismicSource id="other:03" name="Nankai CASE 3"/>
<nonParametricSeismicSource id="other:04" name="Nankai CASE 4"/>
</sourceGroup>
<sourceGroup
name="point source"
tectonicRegion="Subduction Interface">
<pointSource
id="gs_PSE_2_100"
name="PSE"
>
<pointGeometry>
<gml:Point>
<gml:pos>
135.805 35.898
</gml:pos>
</gml:Point>
<upperSeismoDepth>
60.995
</upperSeismoDepth>
<lowerSeismoDepth>
61.5
</lowerSeismoDepth>
</pointGeometry>
<magScaleRel>
PointMSR
</magScaleRel>
<ruptAspectRatio>
1
</ruptAspectRatio>
<arbitraryMFD>
<magnitudes>
5.0 5.1 5.199999999999999 5.299999999999999 5.399999999999999 5.499999999999998 5.599999999999998 5.6999999999999975 5.799999999999997 5.899999999999997 5.9999999999999964 6.099999999999996 6.199999999999996 6.299999999999995 6.399999999999995 6.499999999999995 6.599999999999994 6.699999999999994 6.799999999999994 6.899999999999993 6.999999999999993 7.0999999999999925 7.199999999999992 7.299999999999992 7.3999999999999915 7.499999999999991
</magnitudes>
<occurRates>
0.0017404297316440736 0.0014146743971195982 0.0011498905204148596 0.0009346661052410158 0.0007597251327641438 0.0006175277718075182 0.0005019454175039715 0.00040799655279595776 0.0003316320486023118 0.00026956064924197487 0.00021910712166088434 0.00017809695479484682 0.00014476263969314965 0.00011766749114306046 9.56437275615485e-05 7.774214044171201e-05 6.319118414293873e-05 5.136372282392555e-05 4.175000133508116e-05 3.393567513504588e-05 2.758395233639655e-05 2.242107821543914e-05 1.8224536578811047e-05 1.48134594742065e-05 1.2040831910595241e-05 9.7871556169343e-06
</occurRates>
</arbitraryMFD>
<nodalPlaneDist>
<nodalPlane dip="41.0" probability="1.0" rake="90" strike="132.0"/>
</nodalPlaneDist>
<hypoDepthDist>
<hypoDepth depth="61" probability="1.0"/>
</hypoDepthDist>
</pointSource>
</sourceGroup>
</sourceModel>
</nrml>
1 change: 1 addition & 0 deletions openquake/qa_tests_data/disagg/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
| case_11 | |
| case_12 | Source fragments and alternatives of fragments |
| case_16 | Check the NZ22 K20 GMM has required attributes |
| case_18 | Nankai mutex model with two atomic groups in the same task |


Empty file.
22 changes: 22 additions & 0 deletions openquake/qa_tests_data/disagg/case_18/expected_output/Dist-0.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#,,,,"generated_by='OpenQuake engine 3.27.0-gitc1eb1c6f35', start_date='2026-10-18T02:42:48', checksum=2496305347, investigation_time=50.0, mag_bin_edges=[5.0, 5.1000000000000005, 5.2, 5.300000000000001, 5.4, 5.5, 5.6000000000000005, 5.7, 5.800000000000001, 5.9, 6.0, 6.1000000000000005, 6.2, 6.300000000000001, 6.4, 6.5, 6.6000000000000005, 6.7, 6.800000000000001, 6.9, 7.0, 7.1000000000000005, 7.2, 7.300000000000001, 7.4, 7.5, 7.6000000000000005, 7.7, 7.800000000000001, 7.9, 8.0, 8.1, 8.200000000000001, 8.3, 8.4, 8.5, 8.6, 8.700000000000001, 8.8, 8.9, 9.0, 9.1, 9.200000000000001], dist_bin_edges=[0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0, 200.0], lon_bin_edges=[133.46570921391037, 135.68, 137.89429078608964], lat_bin_edges=[33.88136, 35.68, 37.47864], eps_bin_edges=[-3.0, 3.0], tectonic_region_types=['Subduction Interface'], lon=135.68, lat=35.68, weights=[1.0], rlz_ids=[0]"
imt,iml,poe,dist,rlz0
PGV,2.00000E+01,2.00000E-02,5.00000E+00,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,2.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,3.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,4.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,5.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,6.50000E+01,4.80213E-03
PGV,2.00000E+01,2.00000E-02,7.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,8.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,9.50000E+01,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.05000E+02,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.15000E+02,2.72122E-01
PGV,2.00000E+01,2.00000E-02,1.25000E+02,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.35000E+02,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.45000E+02,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.55000E+02,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.65000E+02,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.75000E+02,1.19650E-01
PGV,2.00000E+01,2.00000E-02,1.85000E+02,0.00000E+00
PGV,2.00000E+01,2.00000E-02,1.95000E+02,0.00000E+00
Loading