From 9fa1943a2fd6a74bebdeb21a78511ab854998bd0 Mon Sep 17 00:00:00 2001
From: Jason Altekruse <jaltekruse@usgs.gov>
Date: Mon, 3 Feb 2025 14:37:01 -0700
Subject: [PATCH] implementation of Spudich et al. (1999) for PRVI 2003

---
 .../gov/usgs/earthquake/nshmp/gmm/Gmm.java    |   7 +
 .../nshmp/gmm/SpudichEtAl_1999_PRVI.java      | 121 ++++++++++++++++++
 src/main/resources/gmm/coeffs/SEA99-PRVI.csv  |   8 ++
 .../usgs/earthquake/nshmp/gmm/SEA99_test.java |  47 +++++++
 src/test/resources/gmm/sea99-inputs.csv       |   7 +
 src/test/resources/gmm/sea99-results.csv      |  24 ++++
 6 files changed, 214 insertions(+)
 create mode 100644 src/main/java/gov/usgs/earthquake/nshmp/gmm/SpudichEtAl_1999_PRVI.java
 create mode 100644 src/main/resources/gmm/coeffs/SEA99-PRVI.csv
 create mode 100644 src/test/java/gov/usgs/earthquake/nshmp/gmm/SEA99_test.java
 create mode 100644 src/test/resources/gmm/sea99-inputs.csv
 create mode 100644 src/test/resources/gmm/sea99-results.csv

diff --git a/src/main/java/gov/usgs/earthquake/nshmp/gmm/Gmm.java b/src/main/java/gov/usgs/earthquake/nshmp/gmm/Gmm.java
index 9b23bb1c..f21baae8 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/gmm/Gmm.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/gmm/Gmm.java
@@ -1563,6 +1563,13 @@ public enum Gmm {
       UsgsPrviBackbone2025.COEFFS_SIGMA_SUBDUCTION,
       UsgsPrviBackbone2025.SlabTotal.CONSTRAINTS),
 
+  /* PRVI 2003 */
+  SEA99_PRVI(
+      SpudichEtAl_1999_PRVI.class,
+      SpudichEtAl_1999_PRVI.NAME,
+      SpudichEtAl_1999_PRVI.COEFFS,
+      SpudichEtAl_1999_PRVI.CONSTRAINTS),
+
   /* Combined: must be declared after any dependent models above. */
 
   /** 2021 Hawaii weight-averaged GMM for deep earthquakes. */
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/gmm/SpudichEtAl_1999_PRVI.java b/src/main/java/gov/usgs/earthquake/nshmp/gmm/SpudichEtAl_1999_PRVI.java
new file mode 100644
index 00000000..c9b5dbfa
--- /dev/null
+++ b/src/main/java/gov/usgs/earthquake/nshmp/gmm/SpudichEtAl_1999_PRVI.java
@@ -0,0 +1,121 @@
+package gov.usgs.earthquake.nshmp.gmm;
+
+import static gov.usgs.earthquake.nshmp.Maths.TWO_PI;
+import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.MW;
+import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.RAKE;
+import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.RJB;
+import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.VS30;
+import static gov.usgs.earthquake.nshmp.gmm.GmmUtils.BASE_10_TO_E;
+import static java.lang.Math.log10;
+import static java.lang.Math.sqrt;
+
+import java.util.Map;
+
+import com.google.common.collect.Range;
+
+import gov.usgs.earthquake.nshmp.Faults;
+import gov.usgs.earthquake.nshmp.gmm.GmmInput.Constraints;
+import gov.usgs.earthquake.nshmp.tree.LogicTree;
+
+/**
+ * PRVI-specific implementation of the Spudich et al. (1999) ground motion model
+ * for extensional tectonic regimes. This implementation was created to match a
+ * fortran implementation rather than adhere to the published model. For
+ * example, an errata published in 2005 corrects a sigma term, however this was
+ * not available for the 2003 PRVI model.
+ *
+ * Additional periods, not needed for PRVI 2003, are included to test
+ * implementation against Spudich et al. (1999) Table 3.
+ *
+ * <p><b>Note:</b> Direct instantiation of {@code GroundMotionModel}s is
+ * prohibited. Use {@link Gmm#instance(Imt)} to retrieve an instance for a
+ * desired {@link Imt}.
+ *
+ * <p><b>Reference:</b> Spudich, P., Joyner, W.B., Lindh, A.G., Boore, D.M.,
+ * Margaris, B.M., and Fletcher, J.B., 1999, SEA99: A revised ground motion
+ * prediction relation for use in extensional tectonic regimes, Bull. Seis. Soc.
+ * Am, v. 5, p. 1156-1170.
+ *
+ * <p><b>Component:</b> random horizontal
+ *
+ * @author U.S. Geological Survey
+ * @see Gmm#SEA99
+ */
+public class SpudichEtAl_1999_PRVI implements GroundMotionModel {
+
+  static final String NAME = "Spudich et al. (1999) : PRVI";
+
+  static final Constraints CONSTRAINTS = Constraints.builder()
+      .set(MW, Range.closed(4.0, 8.0)) // paper specifies M 5.0-707
+      .set(RJB, Range.closed(0.0, 80.0)) // paper specifies Rjb 0-100 km
+      .set(RAKE, Faults.RAKE_RANGE)
+      .set(VS30, Range.singleton(760.0))
+      .build();
+
+  static final CoefficientContainer COEFFS = new CoefficientContainer("SEA99-PRVI.csv");
+
+  private static final class Coefficients {
+
+    final Imt imt;
+    final double b1, b2, b3, b5, h, σ;
+
+    Coefficients(Imt imt, CoefficientContainer cc) {
+      this.imt = imt;
+      Map<String, Double> coeffs = cc.get(imt);
+      b1 = coeffs.get("b1");
+      b2 = coeffs.get("b2");
+      b3 = coeffs.get("b3");
+      b5 = coeffs.get("b5");
+      h = coeffs.get("h");
+      σ = coeffs.get("sigma");
+    }
+  }
+
+  private final Coefficients coeffs;
+
+  SpudichEtAl_1999_PRVI(Imt imt) {
+    coeffs = new Coefficients(imt, COEFFS);
+  }
+
+  boolean interfaceZone() {
+    return false;
+  }
+
+  @Override
+  public Imt imt() {
+    return coeffs.imt;
+  }
+
+  @Override
+  public LogicTree<GroundMotion> calc(GmmInput in) {
+    double μ = calcMean(coeffs, in);
+    return GroundMotions.createTree(μ, coeffs.σ);
+  }
+
+  private final double calcMean(Coefficients c, GmmInput in) {
+    double mFac = in.Mw - 6.0;
+    double r = sqrt(in.rJB * in.rJB + c.h * c.h);
+
+    double μlog10 = c.b1 + c.b2 * mFac + c.b3 * mFac * mFac + c.b5 * log10(r);
+
+    // double μlog10orig = μlog10;
+
+    /* convert from PSV in cm/sec to PSA in g - from hazgridXnga.samoa.f */
+    if (c.imt != Imt.PGA) {
+      μlog10 = μlog10 + log10(TWO_PI / (980.0 * c.imt.period()));
+    }
+
+    // if (c.imt == Imt.PGA && in.rJB == 0.0 && in.Mw == 5.5) {
+    // System.out.printf("Imt,Mw,Rjb,μlog10o,μlog10,μo,μ,σ\n");
+    // }
+    // double μo = μlog10orig * BASE_10_TO_E;
+    // double μ = μlog10 * BASE_10_TO_E;
+    // System.out.printf("%s,%.1f,%.1f,%.6e,%.6e,%.6e,%.6e,%.4f\n",
+    // c.imt.name(), in.Mw, in.rJB,
+    // μlog10orig, μlog10, exp(μo), exp(μ), c.σ);
+
+    // convert log10 to ln
+    return μlog10 * BASE_10_TO_E;
+  }
+
+}
diff --git a/src/main/resources/gmm/coeffs/SEA99-PRVI.csv b/src/main/resources/gmm/coeffs/SEA99-PRVI.csv
new file mode 100644
index 00000000..e3db4aad
--- /dev/null
+++ b/src/main/resources/gmm/coeffs/SEA99-PRVI.csv
@@ -0,0 +1,8 @@
+T,   b1,    b2,    b3,     b4,  b5,     b6,    h,    sigma
+PGA, 0.299, 0.229,  0.000, 0.0, -1.052, 0.112, 7.27, 0.224
+0.1, 2.144, 0.327, -0.098, 0.0, -1.250, 0.064, 9.99, 0.295
+0.2, 2.224, 0.309, -0.090, 0.0, -1.047, 0.088, 8.63, 0.262
+0.3, 2.263, 0.334, -0.070, 0.0, -1.020, 0.121, 7.72, 0.263
+0.5, 2.292, 0.384, -0.039, 0.0, -1.038, 0.166, 6.70, 0.275
+1.0, 2.276, 0.450, -0.014, 0.0, -1.083, 0.210, 6.01, 0.301
+2.0, 2.168, 0.471, -0.037, 0.0, -1.049, 0.197, 6.71, 0.341
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/gmm/SEA99_test.java b/src/test/java/gov/usgs/earthquake/nshmp/gmm/SEA99_test.java
new file mode 100644
index 00000000..a5c35892
--- /dev/null
+++ b/src/test/java/gov/usgs/earthquake/nshmp/gmm/SEA99_test.java
@@ -0,0 +1,47 @@
+package gov.usgs.earthquake.nshmp.gmm;
+
+import static gov.usgs.earthquake.nshmp.gmm.Gmm.SEA99_PRVI;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.PGA;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P1;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P5;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA2P0;
+
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+
+public class SEA99_test implements ArgumentsProvider {
+
+  private static String GMM_INPUTS = "/gmm/sea99-inputs.csv";
+  private static String GMM_RESULTS = "/gmm/sea99-results.csv";
+  // Validated results against Spudich et al. (1999) Table 3
+
+  @ParameterizedTest(name = "({index}) {0} {2} {1}")
+  @ArgumentsSource(SEA99_test.class)
+  void test(int index, Gmm gmm, Imt imt, double exMedian, double exSigma, String inputs) {
+    GmmTest.test(index, gmm, imt, exMedian, exSigma, inputs);
+  }
+
+  @Override
+  public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+    return GmmTest.loadResults(GMM_RESULTS, GMM_INPUTS);
+  }
+
+  /* Result generation sets */
+  private static Set<Gmm> gmms = EnumSet.of(SEA99_PRVI);
+
+  // private static Set<Imt> imts = SpudichEtAl_1999_PRVI.COEFFS.imts();
+  private static Set<Imt> imts = EnumSet.of(PGA, SA0P1, SA0P5, SA2P0);
+
+  public static void main(String[] args) throws IOException {
+    GmmTest.generateResults(gmms, imts, GMM_INPUTS, GMM_RESULTS);
+    System.out.println("Done generating results");
+  }
+}
diff --git a/src/test/resources/gmm/sea99-inputs.csv b/src/test/resources/gmm/sea99-inputs.csv
new file mode 100644
index 00000000..3853b913
--- /dev/null
+++ b/src/test/resources/gmm/sea99-inputs.csv
@@ -0,0 +1,7 @@
+#Mw,rjB,rRup,rX,dip,width,zTor,zHyp,rake,vs30,z1p0,z2p5,zSed
+5.5,0.0,0,0,90,1,1,1,1,760,NaN,NaN,NaN
+5.5,70.0,0,0,90,1,1,1,1,760,NaN,NaN,NaN
+6.5,0.0,0,0,90,1,1,1,1,760,NaN,NaN,NaN
+6.5,70.0,0,0,90,1,1,1,1,760,NaN,NaN,NaN
+7.5,0.0,0,0,90,1,1,1,1,760,NaN,NaN,NaN
+7.5,70.0,0,0,90,1,1,1,1,760,NaN,NaN,NaN
diff --git a/src/test/resources/gmm/sea99-results.csv b/src/test/resources/gmm/sea99-results.csv
new file mode 100644
index 00000000..64c63bc0
--- /dev/null
+++ b/src/test/resources/gmm/sea99-results.csv
@@ -0,0 +1,24 @@
+0-SEA99_PRVI-PGA,0.1897430161,0.2240000000
+1-SEA99_PRVI-PGA,0.0174183111,0.2240000000
+2-SEA99_PRVI-PGA,0.3214887646,0.2240000000
+3-SEA99_PRVI-PGA,0.0295125030,0.2240000000
+4-SEA99_PRVI-PGA,0.5447105663,0.2240000000
+5-SEA99_PRVI-PGA,0.0500041494,0.2240000000
+0-SEA99_PRVI-SA0P1,0.3262097462,0.2950000000
+1-SEA99_PRVI-SA0P1,0.0282558510,0.2950000000
+2-SEA99_PRVI-SA0P1,0.6926230371,0.2950000000
+3-SEA99_PRVI-SA0P1,0.0599940792,0.2950000000
+4-SEA99_PRVI-SA0P1,0.9364766051,0.2950000000
+5-SEA99_PRVI-SA0P1,0.0811163484,0.2950000000
+0-SEA99_PRVI-SA0P5,0.2191634575,0.2750000000
+1-SEA99_PRVI-SA0P5,0.0190970592,0.2750000000
+2-SEA99_PRVI-SA0P5,0.5306010965,0.2750000000
+3-SEA99_PRVI-SA0P5,0.0462345351,0.2750000000
+4-SEA99_PRVI-SA0P5,1.0734161944,0.2750000000
+5-SEA99_PRVI-SA0P5,0.0935333512,0.2750000000
+0-SEA99_PRVI-SA2P0,0.0364705131,0.3410000000
+1-SEA99_PRVI-SA2P0,0.0031015763,0.3410000000
+2-SEA99_PRVI-SA2P0,0.1078802324,0.3410000000
+3-SEA99_PRVI-SA2P0,0.0091745013,0.3410000000
+4-SEA99_PRVI-SA2P0,0.2691174589,0.3410000000
+5-SEA99_PRVI-SA2P0,0.0228866625,0.3410000000
-- 
GitLab