From d6da017a34d9379010e1f5381942e8589ad60fac Mon Sep 17 00:00:00 2001
From: Peter Powers <pmpowers@usgs.gov>
Date: Tue, 20 Feb 2024 11:45:18 -0700
Subject: [PATCH] updated Imt and tests

---
 .../gov/usgs/earthquake/nshmp/gmm/Imt.java    | 73 ++++++++-----------
 .../usgs/earthquake/nshmp/gmm/ImtTest.java    | 59 ++++++++++++---
 .../earthquake/nshmp/gmm/InstanceTest.java    | 43 +++++++++++
 3 files changed, 122 insertions(+), 53 deletions(-)
 create mode 100644 src/test/java/gov/usgs/earthquake/nshmp/gmm/InstanceTest.java

diff --git a/src/main/java/gov/usgs/earthquake/nshmp/gmm/Imt.java b/src/main/java/gov/usgs/earthquake/nshmp/gmm/Imt.java
index 093dd6bc..0e4ee4c7 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/gmm/Imt.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/gmm/Imt.java
@@ -2,14 +2,10 @@ package gov.usgs.earthquake.nshmp.gmm;
 
 import static com.google.common.math.DoubleMath.fuzzyEquals;
 
-import java.text.DecimalFormat;
-import java.util.Collection;
 import java.util.EnumSet;
-import java.util.List;
+import java.util.OptionalDouble;
 import java.util.Set;
 
-import com.google.common.collect.Lists;
-
 /**
  * Intesity measure type (Imt) identifiers. {@code SA0P1} stands for spectal
  * acceleration of 0.1 seconds.
@@ -73,7 +69,16 @@ public enum Imt {
   SA7P5,
   SA10P0;
 
-  private static final DecimalFormat SA_FORMAT = new DecimalFormat("0.00#");
+  private final OptionalDouble period;
+
+  private Imt() {
+    if (ordinal() < 10) {
+      period = OptionalDouble.empty();
+    } else {
+      String valStr = name().substring(2).replace("P", ".");
+      period = OptionalDouble.of(Double.parseDouble(valStr));
+    }
+  }
 
   @Override
   public String toString() {
@@ -99,7 +104,7 @@ public enum Imt {
       case DS595:
         return "Significant Duration 5-95%";
       default:
-        return SA_FORMAT.format(period()) + " Second Spectral Acceleration";
+        return period() + " Second Spectral Acceleration";
     }
   }
 
@@ -134,36 +139,21 @@ public enum Imt {
   }
 
   /**
-   * Returns the corresponding period or frequency for this {@code Imt} if it
-   * represents a spectral acceleration.
+   * Returns the corresponding period for this {@code Imt} if it represents a
+   * spectral acceleration and throws an {@code UnsupportedOperationException}
+   * otherwise. Rather than trying to catch the exception, users should call
+   * {@link #isSA()} to check validity of this {@code Imt} before calling this
+   * method.
    *
    * @return the period for this {@code Imt} if it represents a spectral
-   *         acceleration; null otherwise
-   */
-  public Double period() {
-    if (ordinal() < 10) {
-      return null;
-    }
-    String valStr = name().substring(2).replace("P", ".");
-    return Double.parseDouble(valStr);
-  }
-
-  /**
-   * Returns the {@code List} of periods for the supplied {@code Imt}s. The
-   * result will be sorted according to the iteration order of the supplied
-   * {@code Collection}. Any non spectral acceleration {@code Imt}s will have
-   * null values in the returned {@code List}.
-   *
-   * @param imts to list periods for
-   * @return a {@code List} of spectral periods
-   * @see #saImts()
+   *         acceleration
+   * @throws UnsupportedOperationException if this {@code Imt} is not a spectral
+   *         acceleration
    */
-  public static List<Double> periods(Collection<Imt> imts) {
-    List<Double> periodList = Lists.newArrayListWithCapacity(imts.size());
-    for (Imt imt : imts) {
-      periodList.add(imt.period());
-    }
-    return periodList;
+  public double period() {
+    return period.orElseThrow(
+        () -> new UnsupportedOperationException(
+            name() + " is not a SA IMT."));
   }
 
   /**
@@ -177,9 +167,8 @@ public enum Imt {
    */
   public static Imt fromPeriod(double period) {
     for (Imt imt : Imt.values()) {
-      if (imt.name().startsWith("SA")) {
-        double saPeriod = imt.period();
-        if (fuzzyEquals(saPeriod, period, 0.000001)) {
+      if (imt.isSA()) {
+        if (fuzzyEquals(imt.period(), period, 0.000001)) {
           return imt;
         }
       }
@@ -200,16 +189,17 @@ public enum Imt {
     if (this == PGA) {
       return 100;
     }
-    if (this.isSA()) {
+    if (isSA()) {
       return 1.0 / period();
     }
     throw new UnsupportedOperationException("frequency() supports PGA and SA IMTs only");
   }
 
   /**
-   * Returns true if this Imt is some flavor of spectral acceleration.
+   * Returns true if this {@code Imt} represents a spectral acceleration.
    *
-   * @return {@code true} if this is a spectral period, {@code false} otherwise
+   * @return {@code true} if this is a spectral acceleration {@code Imt},
+   *         {@code false} otherwise
    */
   public boolean isSA() {
     return ordinal() > 9;
@@ -221,7 +211,7 @@ public enum Imt {
    * @return the set of spectral acceleration IMTs
    */
   public static Set<Imt> saImts() {
-    return EnumSet.complementOf(EnumSet.range(PGA, AI));
+    return EnumSet.range(SA0P01, SA10P0);
   }
 
   /**
@@ -248,5 +238,4 @@ public enum Imt {
         SA1P0, SA1P5, SA2P0, SA3P0, SA4P0, SA5P0, SA7P5,
         SA10P0);
   }
-
 }
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/gmm/ImtTest.java b/src/test/java/gov/usgs/earthquake/nshmp/gmm/ImtTest.java
index f1aecc99..e3feb1c2 100644
--- a/src/test/java/gov/usgs/earthquake/nshmp/gmm/ImtTest.java
+++ b/src/test/java/gov/usgs/earthquake/nshmp/gmm/ImtTest.java
@@ -10,16 +10,33 @@ import static gov.usgs.earthquake.nshmp.gmm.Imt.PGA;
 import static gov.usgs.earthquake.nshmp.gmm.Imt.PGD;
 import static gov.usgs.earthquake.nshmp.gmm.Imt.PGV;
 import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P01;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P02;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P03;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P05;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P075;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P1;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P15;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P2;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P25;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P3;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P4;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P5;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P75;
 import static gov.usgs.earthquake.nshmp.gmm.Imt.SA10P0;
 import static gov.usgs.earthquake.nshmp.gmm.Imt.SA1P0;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA1P5;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA2P0;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA3P0;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA4P0;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA5P0;
+import static gov.usgs.earthquake.nshmp.gmm.Imt.SA7P5;
 import static gov.usgs.earthquake.nshmp.gmm.Imt.SI;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.EnumSet;
-import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import org.junit.jupiter.api.Test;
 
@@ -43,7 +60,7 @@ class ImtTest {
     assertEquals(CAV.toString(), "Cumulative Absolute Velocity");
     assertEquals(DS575.toString(), "Significant Duration 5-75%");
     assertEquals(DS595.toString(), "Significant Duration 5-95%");
-    assertEquals(SA1P0.toString(), "1.00 Second Spectral Acceleration");
+    assertEquals(SA1P0.toString(), "1.0 Second Spectral Acceleration");
   }
 
   @Test
@@ -62,17 +79,27 @@ class ImtTest {
   }
 
   @Test
-  void testPeriods() {
-    Set<Imt> imts = EnumSet.range(SA0P01, SA10P0);
-    List<Double> expected = imts.stream()
-        .map(Imt::period)
-        .collect(Collectors.toList());
-    assertEquals(expected, Imt.periods(imts));
+  void testPeriod() {
+    assertEquals(0.2, SA0P2.period());
+    assertThrows(UnsupportedOperationException.class, () -> {
+      PGV.period();
+    });
+  }
 
+  @Test
+  void testFromPeriod() {
+    assertEquals(SA0P2, Imt.fromPeriod(0.2));
     assertThrows(IllegalArgumentException.class, () -> {
       Imt.fromPeriod(0.1234);
     });
+  }
 
+  @Test
+  void testFrequency() {
+    assertEquals(100, PGA.frequency());
+    assertEquals(5.0, SA0P2.frequency());
+    assertEquals(3.3333, SA0P3.frequency(), 0.0001);
+    assertEquals(0.1, SA10P0.frequency());
     assertThrows(UnsupportedOperationException.class, () -> {
       PGV.frequency();
     });
@@ -80,9 +107,19 @@ class ImtTest {
 
   @Test
   void testSaImts() {
-    // underlying method is complement of non-SA Imts
-    // which are listed first in the enum
     assertEquals(Imt.saImts(), EnumSet.range(SA0P01, SA10P0));
+    assertFalse(Imt.saImts().contains(PGV));
+  }
+
+  @Test
+  void testMprs() {
+    Set<Imt> mprsImts = EnumSet.of(
+        PGA,
+        SA0P01, SA0P02, SA0P03, SA0P05, SA0P075,
+        SA0P1, SA0P15, SA0P2, SA0P25, SA0P3, SA0P4, SA0P5, SA0P75,
+        SA1P0, SA1P5, SA2P0, SA3P0, SA4P0, SA5P0, SA7P5,
+        SA10P0);
+    assertEquals(mprsImts, Imt.mprsImts());
   }
 
 }
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/gmm/InstanceTest.java b/src/test/java/gov/usgs/earthquake/nshmp/gmm/InstanceTest.java
new file mode 100644
index 00000000..c5ce869d
--- /dev/null
+++ b/src/test/java/gov/usgs/earthquake/nshmp/gmm/InstanceTest.java
@@ -0,0 +1,43 @@
+package gov.usgs.earthquake.nshmp.gmm;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class InstanceTest {
+
+  /*
+   * Intantiates all supported IMTs for all GMMs and runs a test calculation.
+   */
+
+  private static GmmInput GMM_INPUT = GmmInput.builder().withDefaults().build();
+
+  @ParameterizedTest
+  @MethodSource("gmmImtPairs")
+  public void testInstances(Gmm gmm, Imt imt) {
+    assertDoesNotThrow(() -> {
+      GroundMotionModel instance = gmm.instance(imt);
+      instance.calc(GMM_INPUT);
+    });
+  }
+
+  private static Stream<Arguments> gmmImtPairs() {
+    List<Arguments> argsList = new ArrayList<>();
+    Set<Gmm> gmms = EnumSet.allOf(Gmm.class);
+    for (Gmm gmm : gmms) {
+      Set<Imt> imts = gmm.supportedImts();
+      for (Imt imt : imts) {
+        argsList.add(Arguments.of(gmm, imt));
+      }
+    }
+    return argsList.stream();
+  }
+}
-- 
GitLab