From 8bb21f91d6ff43492301f870d4d62f8e0290e50d Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 09:49:23 +1200 Subject: [PATCH 1/8] implement random rupture sampling in the NSHM2022 ruptures --- workflow/scripts/nshm2022_to_realisation.py | 28 +++++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 2415cd85..69e07061 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -64,12 +64,20 @@ app = typer.Typer() +class SamplingStrategy(StrEnum): + """Rupture propagation strategy to employ.""" + + maximising = "maximising" + random = "random" + + def default_magnitude_estimation( faults: dict[str, Fault], # NOTE: this must be in quotes because the runtime class DisjointSet is # not generic, just the stub implementation. components: "DisjointSet[str]", avg_rake: float, + strategy: SamplingStrategy, ) -> dict[str, magnitude_scaling.BoldM]: """Estimate the magnitudes for a set of faults based on their areas and average rake. @@ -91,8 +99,12 @@ def default_magnitude_estimation( estimated magnitudes (in the `BoldM` convention) for each fault. """ total_area = sum(fault.area() for fault in faults.values()) + estimated_magnitude = magnitude_scaling.area_to_magnitude( - magnitude_scaling.ScalingRelation.LEONARD2014, total_area, avg_rake + magnitude_scaling.ScalingRelation.LEONARD2014, + total_area, + avg_rake, + random=strategy == SamplingStrategy.RANDOM, ) estimated_moment = moment.magnitude_to_moment(estimated_magnitude, bold_m=True) roots = {components[fault_name] for fault_name in faults} @@ -157,13 +169,6 @@ def find_fault_and_hypocentre( raise ValueError("Hypocentre not on any fault.") -class SamplingStrategy(StrEnum): - """Rupture propagation strategy to employ.""" - - maximising = "maximising" - random = "random" - - @cli.from_docstring(app) @log_call() def generate_realisation( @@ -185,6 +190,9 @@ def generate_realisation( SamplingStrategy, typer.Option(), ] = SamplingStrategy.random, + magnitude_strategy: Annotated[ + SamplingStrategy, typer.Option() + ] = SamplingStrategy.random, jump_cutoff: Annotated[ float, typer.Option(min=0), @@ -247,6 +255,10 @@ def generate_realisation( The strategy to use when sampling rupture propagation. "maximising" will choose the maximally likely rupture propagation tree. "random" will choose a random rupture propagation tree. + magnitude_strategy : SamplingStrategy, optional + The strategy to use when sampling total rupture magnitude. "maximising" + will choose the median rupture magnitude. "random" will choose a random + rupture propagation tree. jump_cutoff : float, optional The maximum jump distance between faults in km. shypo : float, optional From fc968a4e46f25f4eed2b9ceeb6294ebf2ca4b74c Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 09:50:20 +1200 Subject: [PATCH 2/8] fix random flag --- workflow/scripts/nshm2022_to_realisation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 69e07061..7ea15c56 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -104,7 +104,7 @@ def default_magnitude_estimation( magnitude_scaling.ScalingRelation.LEONARD2014, total_area, avg_rake, - random=strategy == SamplingStrategy.RANDOM, + random=strategy == SamplingStrategy.random, ) estimated_moment = moment.magnitude_to_moment(estimated_magnitude, bold_m=True) roots = {components[fault_name] for fault_name in faults} From d1de05d6ca0388b2f1f645b0c9bc4981373f07fa Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 09:53:21 +1200 Subject: [PATCH 3/8] fix seed reproducibility --- workflow/scripts/nshm2022_to_realisation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 7ea15c56..725d6643 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -34,6 +34,7 @@ See the output of `nshm2022-to-realisation --help`. """ +import random from enum import StrEnum from pathlib import Path from typing import Annotated @@ -320,6 +321,7 @@ def generate_realisation( faults_info = db.get_rupture_fault_info(rupture_id) seeds = Seeds.read_from_realisation_or_random(realisation_ffp) np.random.seed(seed=seeds.nshm_to_realisation_seed) + random.seed(seeds.nshm_to_realisation_seed) source_config = SourceConfig(faults) rakes = { From 3f36faa346137e8e753d6639cca59eceb2da6240 Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 16:29:27 +1200 Subject: [PATCH 4/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- workflow/scripts/nshm2022_to_realisation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 725d6643..2891bac6 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -66,7 +66,7 @@ class SamplingStrategy(StrEnum): - """Rupture propagation strategy to employ.""" + """Sampling strategy to employ.""" maximising = "maximising" random = "random" From 05aeae110860ded20974b5776ef493cbbe9cf86a Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 16:29:59 +1200 Subject: [PATCH 5/8] wire-in strategy argument --- workflow/scripts/nshm2022_to_realisation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 725d6643..5846ba95 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -331,7 +331,9 @@ def generate_realisation( components = moment.find_connected_faults( faults, separation_distance, dip_delta, min_connected_depth ) - magnitudes = default_magnitude_estimation(faults, components, float(avg_rake)) + magnitudes = default_magnitude_estimation( + faults, components, float(avg_rake), strategy + ) if lat_hypo is not None and lon_hypo is not None: initial_fault, hypocentre = find_fault_and_hypocentre( faults, lat_hypo, lon_hypo From e44c4afd8f18908688993490b702baacaffefdd5 Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 16:30:52 +1200 Subject: [PATCH 6/8] use the appropriate magnitude strategy --- workflow/scripts/nshm2022_to_realisation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 245a857d..15af74a8 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -332,7 +332,7 @@ def generate_realisation( faults, separation_distance, dip_delta, min_connected_depth ) magnitudes = default_magnitude_estimation( - faults, components, float(avg_rake), strategy + faults, components, float(avg_rake), magnitude_strategy ) if lat_hypo is not None and lon_hypo is not None: initial_fault, hypocentre = find_fault_and_hypocentre( From e9328adc148d2df11a8a7487923674be77e5740e Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 16:32:51 +1200 Subject: [PATCH 7/8] document strategy argument --- workflow/scripts/nshm2022_to_realisation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 15af74a8..356bd21e 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -92,6 +92,10 @@ def default_magnitude_estimation( and share a common rupture propagation path. avg_rake : float The average rake angle of the rupture. + strategy : SamplingStrategy + Sampling strategy for magnitude estimation. If maximising choose the + mean leonard scaling relation magnitude, else sample from the magnitude + distribution for the total rupture area. Returns ------- From 8e290c7b310ad0bef29ddbef186dee7cbdf6d6e8 Mon Sep 17 00:00:00 2001 From: Jake Faulkner Date: Fri, 28 Aug 2026 16:35:31 +1200 Subject: [PATCH 8/8] update magnitude sampling documentation --- workflow/scripts/nshm2022_to_realisation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workflow/scripts/nshm2022_to_realisation.py b/workflow/scripts/nshm2022_to_realisation.py index 356bd21e..345f04d8 100755 --- a/workflow/scripts/nshm2022_to_realisation.py +++ b/workflow/scripts/nshm2022_to_realisation.py @@ -262,8 +262,8 @@ def generate_realisation( choose a random rupture propagation tree. magnitude_strategy : SamplingStrategy, optional The strategy to use when sampling total rupture magnitude. "maximising" - will choose the median rupture magnitude. "random" will choose a random - rupture propagation tree. + will choose the median rupture magnitude. Otherwise, sample from the magnitude + distribution for the total rupture area. jump_cutoff : float, optional The maximum jump distance between faults in km. shypo : float, optional