source-modelling 2026.5.2__tar.gz → 2026.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/PKG-INFO +1 -1
  2. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/gc2_distances.py +9 -3
  3. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/magnitude_scaling.py +113 -113
  4. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/moment.py +113 -8
  5. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling.egg-info/PKG-INFO +1 -1
  6. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/deptry.yml +0 -0
  7. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/git-extension.yml +0 -0
  8. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/numpydoc.yml +0 -0
  9. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/publish-PyPI.yml +0 -0
  10. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/pytest.yml +0 -0
  11. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/ruff.yml +0 -0
  12. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/types.yml +0 -0
  13. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.github/workflows/wiki.yml +0 -0
  14. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.gitignore +0 -0
  15. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.isort.cfg +0 -0
  16. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/.python-version +0 -0
  17. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/MANIFEST.in +0 -0
  18. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/pyproject.toml +0 -0
  19. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/setup.cfg +0 -0
  20. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/setup.py +0 -0
  21. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.CPG +0 -0
  22. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.dbf +0 -0
  23. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.prj +0 -0
  24. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.sbn +0 -0
  25. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.sbx +0 -0
  26. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.shp +0 -0
  27. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.shp.xml +0 -0
  28. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/NZ_CFM/NZ_CFM_v1_0.shx +0 -0
  29. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/__init__.py +0 -0
  30. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/ccldpy.py +0 -0
  31. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/community_fault_model.py +0 -0
  32. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/fsp.py +0 -0
  33. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/gsf.py +0 -0
  34. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/parse_utils.py +0 -0
  35. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/rupture_propagation.py +0 -0
  36. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/slip.py +0 -0
  37. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/sources.py +0 -0
  38. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/srf.py +0 -0
  39. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/srf_parser/Cargo.toml +0 -0
  40. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/srf_parser/src/lib.rs +0 -0
  41. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/stoch.py +0 -0
  42. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling/trim.py +0 -0
  43. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling.egg-info/SOURCES.txt +0 -0
  44. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling.egg-info/dependency_links.txt +0 -0
  45. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling.egg-info/not-zip-safe +0 -0
  46. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling.egg-info/requires.txt +0 -0
  47. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/source_modelling.egg-info/top_level.txt +0 -0
  48. {source_modelling-2026.5.2 → source_modelling-2026.6.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: source-modelling
3
- Version: 2026.5.2
3
+ Version: 2026.6.2
4
4
  Summary: Source modelling library
5
5
  Author: ucgmsim
6
6
  Requires-Python: >=3.11
@@ -140,7 +140,9 @@ def segment_weights(
140
140
 
141
141
 
142
142
  @njit([float64[:](float64[:], uint64[:]), float64[:](float64[:], int64[:])], cache=True)
143
- def cumulative_reduction(data: np.ndarray, indices: np.ndarray) -> np.ndarray: # pragma: no cover
143
+ def cumulative_reduction(
144
+ data: np.ndarray, indices: np.ndarray
145
+ ) -> np.ndarray: # pragma: no cover
144
146
  """Calculate sum between indices.
145
147
 
146
148
  Parameters
@@ -173,7 +175,9 @@ def cumulative_reduction(data: np.ndarray, indices: np.ndarray) -> np.ndarray: #
173
175
  [float64[:, :](float64[:, :], uint64[:]), float64[:, :](float64[:, :], int64[:])],
174
176
  cache=True,
175
177
  )
176
- def diff_reduction(points: np.ndarray, indices: np.ndarray) -> np.ndarray: # pragma: no cover
178
+ def diff_reduction(
179
+ points: np.ndarray, indices: np.ndarray
180
+ ) -> np.ndarray: # pragma: no cover
177
181
  """Calculate a diff reduction points between the given indices.
178
182
 
179
183
  Parameters
@@ -425,7 +429,9 @@ def multi_trace_rx_ry(
425
429
  b_hat = np.sum(direction_vectors, axis=0)
426
430
  b_hat_norm = np.linalg.norm(b_hat)
427
431
  if np.isclose(b_hat_norm, 0.0):
428
- raise ValueError("Invalid direction vectors calculated: likely indicates degenerate trace or invalid geometry.")
432
+ raise ValueError(
433
+ "Invalid direction vectors calculated: likely indicates degenerate trace or invalid geometry."
434
+ )
429
435
  b_hat /= b_hat_norm
430
436
 
431
437
  u_shift_origins = calculate_gc2_u_origins(
@@ -1,12 +1,14 @@
1
1
  """Magnitude scaling relationships for fault dimensions."""
2
2
 
3
- import functools
4
3
  import warnings
5
4
  from enum import Enum, StrEnum, auto
6
5
 
7
6
  import numpy as np
8
7
  import scipy as sp
9
8
 
9
+ from source_modelling import moment
10
+ from source_modelling.moment import BoldM, Mw
11
+
10
12
 
11
13
  class RakeType(Enum):
12
14
  """Enumeration of rake types."""
@@ -61,13 +63,13 @@ def rake_type(rake: float) -> RakeType:
61
63
  return RakeType.REVERSE_OBLIQUE
62
64
 
63
65
 
64
- def leonard_area_to_magnitude(area: float, rake: float, random: bool = False) -> float:
66
+ def leonard_area_to_magnitude(area: float, rake: float, random: bool = False) -> Mw:
65
67
  """Convert area to magnitude using the leonard scaling relationship [0]_.
66
68
 
67
69
  Parameters
68
70
  ----------
69
71
  area : float
70
- Area of the fault (km^2).
72
+ Area of the fault rupture (km^2).
71
73
  rake : float
72
74
  Rake of the fault (degrees).
73
75
  random : bool, optional
@@ -76,8 +78,8 @@ def leonard_area_to_magnitude(area: float, rake: float, random: bool = False) ->
76
78
 
77
79
  Returns
78
80
  -------
79
- float
80
- Moment magnitude of the fault.
81
+ Mw
82
+ Moment magnitude of the fault rupture.
81
83
 
82
84
  Notes
83
85
  -----
@@ -100,20 +102,20 @@ def leonard_area_to_magnitude(area: float, rake: float, random: bool = False) ->
100
102
  )
101
103
 
102
104
  # Leonard quotes assymetric uncertainties for the other rake types.
103
- return np.log10(area) + (
104
- sp.stats.norm(loc=4.03, scale=0.3).rvs() if random else 4.0
105
+ return Mw(
106
+ np.log10(area) + (sp.stats.norm(loc=4.03, scale=0.3).rvs() if random else 4.0)
105
107
  )
106
108
 
107
109
 
108
110
  def leonard_magnitude_to_area(
109
- magnitude: float, rake: float, random: bool = False
111
+ magnitude: Mw, rake: float, random: bool = False
110
112
  ) -> float:
111
113
  """Convert magnitude to area using the Leonard scaling relationship [0]_.
112
114
 
113
115
  Parameters
114
116
  ----------
115
- magnitude : float
116
- Moment magnitude of the fault.
117
+ magnitude : Mw
118
+ Moment magnitude of the fault rupture.
117
119
  rake : float
118
120
  Rake of the fault (degrees).
119
121
  random : bool, optional
@@ -123,7 +125,7 @@ def leonard_magnitude_to_area(
123
125
  Returns
124
126
  -------
125
127
  float
126
- Area of the fault. (km^2)
128
+ Area of the fault rupture. (km^2)
127
129
 
128
130
  Notes
129
131
  -----
@@ -150,7 +152,7 @@ def leonard_magnitude_to_area(
150
152
 
151
153
 
152
154
  def leonard_magnitude_to_length(
153
- magnitude: float,
155
+ magnitude: Mw,
154
156
  rake: float,
155
157
  random: bool = False,
156
158
  ) -> float:
@@ -158,8 +160,8 @@ def leonard_magnitude_to_length(
158
160
 
159
161
  Parameters
160
162
  ----------
161
- magnitude : float
162
- Moment magnitude of the fault.
163
+ magnitude : Mw
164
+ Moment magnitude of the fault rupture.
163
165
  rake : float
164
166
  Rake of the fault (degrees).
165
167
  random : bool, optional
@@ -169,7 +171,7 @@ def leonard_magnitude_to_length(
169
171
  Returns
170
172
  -------
171
173
  float
172
- Length of the fault. (km)
174
+ Length of the fault rupture. (km)
173
175
 
174
176
  References
175
177
  ----------
@@ -203,7 +205,7 @@ def leonard_magnitude_to_length(
203
205
 
204
206
 
205
207
  def leonard_magnitude_to_width(
206
- magnitude: float,
208
+ magnitude: Mw,
207
209
  rake: float,
208
210
  random: bool = False,
209
211
  ) -> float:
@@ -211,8 +213,8 @@ def leonard_magnitude_to_width(
211
213
 
212
214
  Parameters
213
215
  ----------
214
- magnitude : float
215
- Moment magnitude of the fault.
216
+ magnitude : Mw
217
+ Moment magnitude of the fault rupture.
216
218
  rake : float
217
219
  Rake of the fault (degrees).
218
220
  random : bool, optional
@@ -222,7 +224,7 @@ def leonard_magnitude_to_width(
222
224
  Returns
223
225
  -------
224
226
  float
225
- Width of the fault. (km)
227
+ Width of the fault rupture. (km)
226
228
 
227
229
  Warns
228
230
  -----
@@ -257,7 +259,7 @@ def leonard_magnitude_to_width(
257
259
 
258
260
 
259
261
  def leonard_magnitude_to_length_width(
260
- magnitude: float,
262
+ magnitude: Mw,
261
263
  rake: float,
262
264
  random: bool = False,
263
265
  ) -> tuple[float, float]:
@@ -265,8 +267,8 @@ def leonard_magnitude_to_length_width(
265
267
 
266
268
  Parameters
267
269
  ----------
268
- magnitude : float
269
- Moment magnitude of the fault.
270
+ magnitude : Mw
271
+ Moment magnitude of the fault rupture.
270
272
  rake : float
271
273
  Rake of the fault (degrees).
272
274
  random : bool, optional
@@ -276,7 +278,7 @@ def leonard_magnitude_to_length_width(
276
278
  Returns
277
279
  -------
278
280
  tuple[float, float]
279
- Length and width of the fault.
281
+ Length and width of the fault rupture.
280
282
 
281
283
  References
282
284
  ----------
@@ -314,21 +316,21 @@ def area_aspect_ratio_to_length_width(
314
316
  return length, width
315
317
 
316
318
 
317
- def contreras_interface_area_to_magnitude(area: float, random: bool = False) -> float:
319
+ def contreras_interface_area_to_magnitude(area: float, random: bool = False) -> BoldM:
318
320
  """Convert area to magnitude using the Contreras scaling relationship [0]_.
319
321
 
320
322
  Parameters
321
323
  ----------
322
324
  area : float
323
- Area of the fault (km^2).
325
+ Area of the fault rupture (km^2).
324
326
  random : bool, optional
325
327
  If True, sample parameters according to uncertainties in the
326
328
  paper, otherwise use the mean values. Default is False.
327
329
 
328
330
  Returns
329
331
  -------
330
- float
331
- Moment magnitude of the fault.
332
+ BoldM
333
+ Moment magnitude of the fault rupture.
332
334
 
333
335
  Warns
334
336
  -----
@@ -355,18 +357,18 @@ def contreras_interface_area_to_magnitude(area: float, random: bool = False) ->
355
357
  sigma_a = sp.stats.norm(loc=0, scale=0.73).rvs() if random else 0
356
358
  a_1 = -8.890
357
359
  a_2 = np.log(10)
358
- return 1 / a_2 * (np.log(area) - a_1 - sigma_a)
360
+ return BoldM(1 / a_2 * (np.log(area) - a_1 - sigma_a))
359
361
 
360
362
 
361
363
  def contreras_interface_magnitude_to_area(
362
- magnitude: float, random: bool = False
364
+ magnitude: BoldM, random: bool = False
363
365
  ) -> float:
364
366
  """Convert magnitude to area using the Contreras scaling relationship [0]_.
365
367
 
366
368
  Parameters
367
369
  ----------
368
- magnitude : float
369
- Moment magnitude of the fault.
370
+ magnitude : BoldM
371
+ Moment magnitude of the fault rupture.
370
372
  random : bool, optional
371
373
  If True, sample parameters according to uncertainties in the
372
374
  paper, otherwise use the mean values. Default is False.
@@ -374,7 +376,7 @@ def contreras_interface_magnitude_to_area(
374
376
  Returns
375
377
  -------
376
378
  float
377
- Area of the fault. (km^2)
379
+ Area of the fault rupture. (km^2)
378
380
 
379
381
  Warns
380
382
  -----
@@ -405,14 +407,14 @@ def contreras_interface_magnitude_to_area(
405
407
 
406
408
 
407
409
  def contreras_interface_magnitude_to_aspect_ratio(
408
- magnitude: float, random: bool = False
410
+ magnitude: BoldM, random: bool = False
409
411
  ) -> float:
410
412
  """Convert magnitude to aspect ratio (L/W) using the Contreras scaling relationship [0]_.
411
413
 
412
414
  Parameters
413
415
  ----------
414
- magnitude : float
415
- Moment magnitude of the fault.
416
+ magnitude : BoldM
417
+ Moment magnitude of the fault rupture.
416
418
  random : bool, optional
417
419
  If True, sample parameters according to uncertainties in the
418
420
  paper, otherwise use the mean values. Default is False.
@@ -449,14 +451,14 @@ def contreras_interface_magnitude_to_aspect_ratio(
449
451
 
450
452
 
451
453
  def contreras_interface_magnitude_to_length_width(
452
- magnitude: float, random: bool = False
454
+ magnitude: BoldM, random: bool = False
453
455
  ) -> tuple[float, float]:
454
456
  """Convert magnitude to length and width using the Contreras scaling relationship [0]_.
455
457
 
456
458
  Parameters
457
459
  ----------
458
- magnitude : float
459
- Moment magnitude of the fault.
460
+ magnitude : BoldM
461
+ Moment magnitude of the fault rupture.
460
462
  random : bool, optional
461
463
  If True, sample parameters according to uncertainties in the
462
464
  paper, otherwise use the mean values. Default is False.
@@ -464,7 +466,7 @@ def contreras_interface_magnitude_to_length_width(
464
466
  Returns
465
467
  -------
466
468
  tuple[float, float]
467
- Length and width of the fault.
469
+ Length and width of the fault rupture.
468
470
 
469
471
  References
470
472
  ----------
@@ -476,7 +478,7 @@ def contreras_interface_magnitude_to_length_width(
476
478
  return area_aspect_ratio_to_length_width(area, aspect_ratio)
477
479
 
478
480
 
479
- def strasser_slab_area_to_magnitude(area: float, random: bool = False) -> float:
481
+ def strasser_slab_area_to_magnitude(area: float, random: bool = False) -> BoldM:
480
482
  """Convert area to magnitude using the Strasser scaling relationship [0]_.
481
483
 
482
484
  Parameters
@@ -489,20 +491,28 @@ def strasser_slab_area_to_magnitude(area: float, random: bool = False) -> float:
489
491
 
490
492
  Returns
491
493
  -------
492
- float
493
- Moment magnitude of the fault.
494
+ BoldM
495
+ Moment magnitude of the fault rupture.
494
496
 
495
497
  Warns
496
498
  -----
497
499
  UserWarning
498
500
  If the area is not between the minimum and maximum area of the Strasser model, estimated at 130km^2 and 5212km^2.
499
501
 
502
+ Notes
503
+ -----
504
+ The moment magnitude convention is not specified in [0]_, so we assume
505
+ BoldM for consistency with [1]_, which recommends this relationship for
506
+ intraslab events.
507
+
500
508
  References
501
509
  ----------
502
510
  .. [0] Strasser, Fleur O., M. C. Arango, and Julian J. Bommer.
503
511
  "Scaling of the source dimensions of interface and intraslab
504
512
  subduction-zone earthquakes with moment magnitude." Seismological
505
513
  Research Letters 81.6 (2010): 941-950.
514
+ .. [1] Contreras, Victor, et al. "NGA-Sub source and path database."
515
+ Earthquake Spectra 38.2 (2022): 799-840.
506
516
  """
507
517
 
508
518
  # lower bound and upper bound for the area are estimated from the minimum and maximum magnitude of the Strasser model.
@@ -518,16 +528,16 @@ def strasser_slab_area_to_magnitude(area: float, random: bool = False) -> float:
518
528
  sigma_a = sp.stats.norm(loc=0, scale=0.288).rvs() if random else 0
519
529
  sigma_b = sp.stats.norm(loc=0, scale=0.093).rvs() if random else 0
520
530
 
521
- return a + sigma_a + (b + sigma_b) * np.log10(area)
531
+ return BoldM(a + sigma_a + (b + sigma_b) * np.log10(area))
522
532
 
523
533
 
524
- def strasser_slab_magnitude_to_area(magnitude: float, random: bool = False) -> float:
534
+ def strasser_slab_magnitude_to_area(magnitude: BoldM, random: bool = False) -> float:
525
535
  """Convert magnitude to area using the Strasser scaling relationship [0]_.
526
536
 
527
537
  Parameters
528
538
  ----------
529
- magnitude : float
530
- Moment magnitude of the fault.
539
+ magnitude : BoldM
540
+ Moment magnitude of the fault rupture.
531
541
  random : bool, optional
532
542
  If True, sample parameters according to uncertainties in the
533
543
  paper, otherwise use the mean values. Default is False.
@@ -543,12 +553,20 @@ def strasser_slab_magnitude_to_area(magnitude: float, random: bool = False) -> f
543
553
  If the magnitude is less than the minimum magnitude of 5.9 or
544
554
  larger than the maximum magnitude of 7.8.
545
555
 
556
+ Notes
557
+ -----
558
+ The moment magnitude convention is not specified in [0]_, so we assume
559
+ BoldM for consistency with [1]_, which recommends this relationship for
560
+ intraslab events.
561
+
546
562
  References
547
563
  ----------
548
564
  .. [0] Strasser, Fleur O., M. C. Arango, and Julian J. Bommer.
549
565
  "Scaling of the source dimensions of interface and intraslab
550
566
  subduction-zone earthquakes with moment magnitude." Seismological
551
567
  Research Letters 81.6 (2010): 941-950.
568
+ .. [1] Contreras, Victor, et al. "NGA-Sub source and path database."
569
+ Earthquake Spectra 38.2 (2022): 799-840.
552
570
  """
553
571
  if not (5.9 <= magnitude <= 7.8):
554
572
  warnings.warn(
@@ -562,13 +580,13 @@ def strasser_slab_magnitude_to_area(magnitude: float, random: bool = False) -> f
562
580
 
563
581
 
564
582
  def contreras_slab_magnitude_to_aspect_ratio(
565
- magnitude: float, random: bool = False
583
+ magnitude: BoldM, random: bool = False
566
584
  ) -> float:
567
585
  """Convert magnitude to aspect ratio (L/W) using the Contreras scaling relationship [0]_.
568
586
 
569
587
  Parameters
570
588
  ----------
571
- magnitude : float
589
+ magnitude : BoldM
572
590
  Moment magnitude of the fault.
573
591
  random : bool, optional
574
592
  If True, sample parameters according to uncertainties in the
@@ -609,13 +627,13 @@ def contreras_slab_magnitude_to_aspect_ratio(
609
627
 
610
628
 
611
629
  def contreras_slab_magnitude_to_length_width(
612
- magnitude: float, random: bool = False
630
+ magnitude: BoldM, random: bool = False
613
631
  ) -> tuple[float, float]:
614
632
  """Convert magnitude to length and width using the Contreras scaling relationship [0]_.
615
633
 
616
634
  Parameters
617
635
  ----------
618
- magnitude : float
636
+ magnitude : BoldM
619
637
  Moment magnitude of the fault.
620
638
  random : bool, optional
621
639
  If True, sample parameters according to uncertainties in the
@@ -640,7 +658,7 @@ def contreras_slab_magnitude_to_length_width(
640
658
 
641
659
  def magnitude_to_length_width(
642
660
  scaling_relation: ScalingRelation,
643
- magnitude: float,
661
+ magnitude: BoldM,
644
662
  rake: float | None = None,
645
663
  random: bool = False,
646
664
  ) -> tuple[float, float]:
@@ -650,8 +668,8 @@ def magnitude_to_length_width(
650
668
  ----------
651
669
  scaling_relation : ScalingRelation
652
670
  Scaling relation to use.
653
- magnitude : float
654
- Moment magnitude of the fault.
671
+ magnitude : BoldM
672
+ Moment magnitude of the fault rupture.
655
673
  rake : float, optional
656
674
  Rake of the fault (degrees). Required for Leonard scaling.
657
675
  random : bool, optional
@@ -663,29 +681,24 @@ def magnitude_to_length_width(
663
681
  tuple[float, float]
664
682
  Length and width of the fault estimated by the scaling relation.
665
683
  """
666
-
667
- if scaling_relation == ScalingRelation.LEONARD2014 and rake is None:
668
- raise ValueError("Rake must be specified for Leonard scaling.")
669
- scaling_relations_map = {
670
- ScalingRelation.LEONARD2014: functools.partial(
671
- leonard_magnitude_to_length_width,
672
- # Rake is checked earlier so ty warning is not required.
673
- rake=rake, # ty: ignore[invalid-argument-type]
674
- random=random,
675
- ),
676
- ScalingRelation.CONTRERAS_INTERFACE2017: functools.partial(
677
- contreras_interface_magnitude_to_length_width, random=random
678
- ),
679
- ScalingRelation.CONTRERAS_SLAB2020: functools.partial(
680
- contreras_slab_magnitude_to_length_width, random=random
681
- ),
682
- }
683
- return scaling_relations_map[scaling_relation](magnitude)
684
+ match scaling_relation:
685
+ case ScalingRelation.LEONARD2014 if rake is not None:
686
+ return leonard_magnitude_to_length_width(
687
+ moment.boldm_to_mw(magnitude), rake=rake, random=random
688
+ )
689
+ case ScalingRelation.LEONARD2014:
690
+ raise ValueError("Rake must be specified for Leonard scaling.")
691
+ case ScalingRelation.CONTRERAS_INTERFACE2017:
692
+ return contreras_interface_magnitude_to_length_width(
693
+ magnitude, random=random
694
+ )
695
+ case ScalingRelation.CONTRERAS_SLAB2020:
696
+ return contreras_slab_magnitude_to_length_width(magnitude, random=random)
684
697
 
685
698
 
686
699
  def magnitude_to_area(
687
700
  scaling_relation: ScalingRelation,
688
- magnitude: float,
701
+ magnitude: BoldM,
689
702
  rake: float | None = None,
690
703
  random: bool = False,
691
704
  ) -> float:
@@ -695,8 +708,8 @@ def magnitude_to_area(
695
708
  ----------
696
709
  scaling_relation : ScalingRelation
697
710
  Scaling relation to use.
698
- magnitude : float
699
- Moment magnitude of the fault.
711
+ magnitude : BoldM
712
+ Moment magnitude of the fault rupture.
700
713
  rake : float, optional
701
714
  Rake of the fault (degrees). Required for Leonard scaling.
702
715
  random : bool, optional
@@ -710,26 +723,20 @@ def magnitude_to_area(
710
723
 
711
724
  Returns
712
725
  -------
713
- tuple[float, float]
726
+ float
714
727
  Area of the fault estimated by the scaling relation.
715
728
  """
716
- if scaling_relation == ScalingRelation.LEONARD2014 and rake is None:
717
- raise ValueError("Rake must be specified for Leonard scaling.")
718
- scaling_relations_map = {
719
- ScalingRelation.LEONARD2014: functools.partial(
720
- leonard_magnitude_to_area,
721
- # Rake is checked earlier so ty warning is not required.
722
- rake=rake, # ty: ignore[invalid-argument-type]
723
- random=random,
724
- ),
725
- ScalingRelation.CONTRERAS_INTERFACE2017: functools.partial(
726
- contreras_interface_magnitude_to_area, random=random
727
- ),
728
- ScalingRelation.CONTRERAS_SLAB2020: functools.partial(
729
- strasser_slab_magnitude_to_area, random=random
730
- ),
731
- }
732
- return scaling_relations_map[scaling_relation](magnitude)
729
+ match scaling_relation:
730
+ case ScalingRelation.LEONARD2014 if rake is not None:
731
+ return leonard_magnitude_to_area(
732
+ moment.boldm_to_mw(magnitude), rake=rake, random=random
733
+ )
734
+ case ScalingRelation.LEONARD2014:
735
+ raise ValueError("Rake must be specified for Leonard scaling.")
736
+ case ScalingRelation.CONTRERAS_INTERFACE2017:
737
+ return contreras_interface_magnitude_to_area(magnitude, random=random)
738
+ case ScalingRelation.CONTRERAS_SLAB2020:
739
+ return strasser_slab_magnitude_to_area(magnitude, random=random)
733
740
 
734
741
 
735
742
  def area_to_magnitude(
@@ -737,7 +744,7 @@ def area_to_magnitude(
737
744
  area: float,
738
745
  rake: float | None = None,
739
746
  random: bool = False,
740
- ) -> float:
747
+ ) -> BoldM:
741
748
  """Convert area to magnitude using a scaling relationship.
742
749
 
743
750
  Parameters
@@ -754,24 +761,17 @@ def area_to_magnitude(
754
761
 
755
762
  Returns
756
763
  -------
757
- float
758
- Moment magnitude of the fault estimated by the scaling relation.
764
+ BoldM
765
+ Moment magnitude of the fault rupture estimated by the scaling relation.
759
766
  """
760
- if scaling_relation == ScalingRelation.LEONARD2014 and rake is None:
761
- raise ValueError("Rake must be specified for Leonard scaling.")
762
-
763
- scaling_relations_map = {
764
- ScalingRelation.LEONARD2014: functools.partial(
765
- leonard_area_to_magnitude,
766
- # Rake is checked earlier so ty warning is not required.
767
- rake=rake, # ty: ignore[invalid-argument-type]
768
- random=random,
769
- ),
770
- ScalingRelation.CONTRERAS_INTERFACE2017: functools.partial(
771
- contreras_interface_area_to_magnitude, random=random
772
- ),
773
- ScalingRelation.CONTRERAS_SLAB2020: functools.partial(
774
- strasser_slab_area_to_magnitude, random=random
775
- ),
776
- }
777
- return scaling_relations_map[scaling_relation](area)
767
+
768
+ match scaling_relation:
769
+ case ScalingRelation.LEONARD2014 if rake is not None:
770
+ mag = leonard_area_to_magnitude(area, rake=rake, random=random)
771
+ return moment.mw_to_boldm(mag)
772
+ case ScalingRelation.LEONARD2014:
773
+ raise ValueError("Rake must be specified for Leonard scaling.")
774
+ case ScalingRelation.CONTRERAS_INTERFACE2017:
775
+ return contreras_interface_area_to_magnitude(area, random=random)
776
+ case ScalingRelation.CONTRERAS_SLAB2020:
777
+ return strasser_slab_area_to_magnitude(area, random=random)
@@ -1,6 +1,7 @@
1
1
  """Utility functions for working with moment rate and moment."""
2
2
 
3
3
  import itertools
4
+ import typing
4
5
 
5
6
  import numpy as np
6
7
  import numpy.typing as npt
@@ -12,6 +13,17 @@ from scipy.sparse import csr_array
12
13
  from source_modelling import rupture_propagation, sources
13
14
  from source_modelling.sources import Fault, Plane
14
15
 
16
+ BoldM = typing.NewType("BoldM", float)
17
+ Mw = typing.NewType("Mw", float)
18
+
19
+
20
+ # Moment magnitude scale coefficients for seismic moment in Nm, from
21
+ # equations 4 and 7 of Hanks and Kanamori (1979). See the [Hanks1979] reference
22
+ # in the `moment_to_magnitude` docstring for the full citation.
23
+ EQUATION_4_COEFFICIENT = 6.0667 # `Mw` convention
24
+ EQUATION_7_COEFFICIENT = 6.0333 # `BoldM` convention
25
+ BOLDM_TO_MW = EQUATION_7_COEFFICIENT - EQUATION_4_COEFFICIENT
26
+
15
27
 
16
28
  def find_connected_faults(
17
29
  faults: dict[str, Fault | Plane],
@@ -120,7 +132,15 @@ def moment_rate_over_time_from_slip(
120
132
  return moment_rate_df
121
133
 
122
134
 
123
- def moment_to_magnitude(moment: float) -> float:
135
+ @typing.overload
136
+ def moment_to_magnitude(
137
+ moment: float, bold_m: typing.Literal[True]
138
+ ) -> BoldM: ... # numpydoc ignore=GL08
139
+ @typing.overload
140
+ def moment_to_magnitude(
141
+ moment: float, bold_m: typing.Literal[False] = False
142
+ ) -> Mw: ... # numpydoc ignore=GL08
143
+ def moment_to_magnitude(moment: float, bold_m: bool = True) -> BoldM | Mw:
124
144
  """Convert moment to magnitude.
125
145
 
126
146
  NOTE: the qcore mag_scaling module does not have this expression.
@@ -129,29 +149,114 @@ def moment_to_magnitude(moment: float) -> float:
129
149
  ----------
130
150
  moment : float
131
151
  The moment of the rupture in Nm.
152
+ bold_m : bool, optional
153
+ Set whether Equation 4 or 7 from [Hanks1979]_ is used for the conversion.
154
+ If True, use Equation 7 (`BoldM` convention).
155
+ If False, use Equation 4 (`Mw` convention).
132
156
 
133
157
  Returns
134
158
  -------
135
- float
136
- Rupture magnitude
159
+ BoldM | Mw
160
+ Rupture moment magnitude in the convention specified by `bold_m`.
161
+
162
+ Raises
163
+ ------
164
+ ValueError
165
+ If the provided moment corresponds to a physically implausible
166
+ magnitude. Likely this is because the user supplied moment is in dyne-cm
167
+ instead of Nm.
168
+
169
+ References
170
+ ----------
171
+ .. [Hanks1979] Hanks, T. C., and H. Kanamori (1979),
172
+ "A moment magnitude scale",
173
+ J. Geophys. Res., 84(B5), 2348-2350,
174
+ doi:10.1029/JB084iB05p02348.
137
175
  """
138
- return 2 / 3 * np.log10(moment) - 6.03333
139
176
 
177
+ if bold_m:
178
+ mag = BoldM(2 / 3 * np.log10(moment) - EQUATION_7_COEFFICIENT)
179
+ else:
180
+ mag = Mw(2 / 3 * np.log10(moment) - EQUATION_4_COEFFICIENT)
140
181
 
141
- def magnitude_to_moment(magnitude: float) -> float:
182
+ if mag > 10.0:
183
+ raise ValueError(
184
+ "Magnitude for moment is unreasonably large, did you provide moment in dyne-cm instead of Nm?"
185
+ )
186
+
187
+ return mag
188
+
189
+
190
+ @typing.overload
191
+ def magnitude_to_moment(
192
+ magnitude: BoldM, bold_m: typing.Literal[True]
193
+ ) -> float: ... # numpydoc ignore=GL08
194
+ @typing.overload
195
+ def magnitude_to_moment(
196
+ magnitude: Mw, bold_m: typing.Literal[False] = False
197
+ ) -> float: ... # numpydoc ignore=GL08
198
+ def magnitude_to_moment(magnitude: BoldM | Mw, bold_m: bool = True) -> float:
142
199
  """Convert magnitude to moment.
143
200
 
144
201
  Parameters
145
202
  ----------
146
- magnitude : float
147
- The magnitude of the rupture.
203
+ magnitude : BoldM | Mw
204
+ The magnitude of the rupture, in the convention indicated by `bold_m`.
205
+ bold_m : bool, optional
206
+ Set whether Equation 4 or 7 from [Hanks1979]_ is used for the conversion.
207
+ If True, use Equation 7 (`BoldM` convention).
208
+ If False, use Equation 4 (`Mw` convention).
148
209
 
149
210
  Returns
150
211
  -------
151
212
  float
152
213
  Rupture moment in Nm.
153
214
  """
154
- return 10 ** ((magnitude + 6.03333) * 3 / 2)
215
+
216
+ if bold_m:
217
+ return 10 ** ((magnitude + EQUATION_7_COEFFICIENT) * 3 / 2)
218
+ else:
219
+ return 10 ** ((magnitude + EQUATION_4_COEFFICIENT) * 3 / 2)
220
+
221
+
222
+ def boldm_to_mw(magnitude: BoldM) -> Mw:
223
+ """Convert a BoldM convention magnitude to an Mw convention magnitude.
224
+
225
+ Parameters
226
+ ----------
227
+ magnitude : BoldM
228
+ A magnitude in the BoldM convention.
229
+
230
+ Returns
231
+ -------
232
+ Mw
233
+ A magnitude in the Mw convention.
234
+
235
+ See Also
236
+ --------
237
+ moment_to_magnitude : Introduces the different magnitude conventions.
238
+ """
239
+ return Mw(magnitude + BOLDM_TO_MW)
240
+
241
+
242
+ def mw_to_boldm(magnitude: Mw) -> BoldM:
243
+ """Convert a Mw convention magnitude to a BoldM convention magnitude.
244
+
245
+ Parameters
246
+ ----------
247
+ magnitude : Mw
248
+ A magnitude in the Mw convention.
249
+
250
+ Returns
251
+ -------
252
+ BoldM
253
+ A magnitude in the BoldM convention.
254
+
255
+ See Also
256
+ --------
257
+ moment_to_magnitude : Introduces the different magnitude conventions.
258
+ """
259
+ return BoldM(magnitude - BOLDM_TO_MW)
155
260
 
156
261
 
157
262
  def moment_over_time_from_moment_rate(moment_rate_df: pd.DataFrame) -> pd.DataFrame:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: source-modelling
3
- Version: 2026.5.2
3
+ Version: 2026.6.2
4
4
  Summary: Source modelling library
5
5
  Author: ucgmsim
6
6
  Requires-Python: >=3.11