diff --git a/src/main/java/gov/usgs/earthquake/nshmp/gmm/ParkerEtAl_2020.java b/src/main/java/gov/usgs/earthquake/nshmp/gmm/ParkerEtAl_2020.java new file mode 100644 index 0000000000000000000000000000000000000000..85de2d625d02ff1bd9edaec84125076b96675cc7 --- /dev/null +++ b/src/main/java/gov/usgs/earthquake/nshmp/gmm/ParkerEtAl_2020.java @@ -0,0 +1,274 @@ +package gov.usgs.earthquake.nshmp.gmm; + +import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.MW; +import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.RRUP; +import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.VS30; +import static gov.usgs.earthquake.nshmp.gmm.GmmInput.Field.ZHYP; +import static java.lang.Math.exp; +import static java.lang.Math.log; +import static java.lang.Math.min; +import static java.lang.Math.pow; + +import java.util.Map; + +import com.google.common.collect.Range; + +import gov.usgs.earthquake.nshmp.Maths; +import gov.usgs.earthquake.nshmp.gmm.GmmInput.Constraints; + +/** + * Implementation of the PEER NGA-Subduction GMM, Parker et al., 2020 * + * <p><b>Reference:</b> Parker, G.A, Stewart, J.P., Boore, D.M., Atkinson, G.M., + * Hassani, B. (2020) NGA-Subduction global ground-motion models with regional + * adjustment factors. Report no. 2020/03. Berkeley, CA: PEER, 131 p. + * + * <p><b>Component:</b> TODO + * + * @author U.S. Geological Survey + */ + +public abstract class ParkerEtAl_2020 implements GroundMotionModel { + + static final String NAME = "Parker et al. (2020) NGA-Subduction"; + + static final Constraints CONSTRAINTS_INTERFACE = Constraints.builder() + .set(MW, Range.closed(4.5, 9.5)) + .set(RRUP, Range.closed(20.0, 1000.0)) + .set(ZHYP, Range.closed(0.0, 40.0)) + .set(VS30, Range.closed(150.0, 2000.0)) + .build(); + static final Constraints CONSTRAINTS_INTRASLAB = Constraints.builder() + .set(MW, Range.closed(4.5, 8.5)) + .set(RRUP, Range.closed(35.0, 1000.0)) + .set(ZHYP, Range.closed(0.0, 200.0)) + .set(VS30, Range.closed(150.0, 2000.0)) + .build(); + + static final CoefficientContainer COEFFS = new CoefficientContainer("TBD.csv"); + + private static final double V1 = 270.0; // m/s + private static final double VREF = 760.0; // m/s + private static final double DB1 = 20.0; // km + private static final double DB2 = 67.0; // km + + private static final class Coefficients { + final Imt imt; + final double c0, c1, b4, a0, c4, c5, c6, d, m, db, v2, s1, s2, f4, f5, e1, e2, e3; + + Coefficients(Imt imt, CoefficientContainer cc) { + this.imt = imt; + Map<String, Double> coeffs = cc.get(imt); + c0 = 0.0; + c1 = 0.0; + b4 = 0.0; + a0 = 0.0; + c4 = 0.0; + c5 = 0.0; + c6 = 0.0; + d = 0.0; + m = 0.0; + db = 0.0; + v2 = 0.0; + s1 = 0.0; // s1 = s2 except for Japan and Taiwan + s2 = 0.0; + f4 = 0.0; + f5 = 0.0; + e1 = 0.0; + e2 = 0.0; + e3 = 0.0; + } + } + + private final Coefficients coeffs; + private final Coefficients coeffsPGA; + + ParkerEtAl_2020(final Imt imt) { + coeffs = new Coefficients(imt, COEFFS); + coeffsPGA = new Coefficients(imt, COEFFS); + } + + @Override + public final ScalarGroundMotion calc(final GmmInput in) { + /* + * Equation 4.1 μ_lny = c0 + Fp + Fm + Fd + Fs + */ + + double pgaRef = + exp(calcMean(coeffsPGA, isSlab(), false, getMc(), 0.0, in.Mw, in.rRup, in.zHyp, in.vs30, + in.z2p5)); + + double μ = calcMean(coeffs, isSlab(), useBasinTerm(), getMc(), pgaRef, in.Mw, in.rRup, in.zHyp, + in.vs30, in.z2p5); + + double σ = 0.0; + return DefaultScalarGroundMotion.create(μ, σ); + } + + abstract boolean isSlab(); + + // Note: mc, corner magnitude, is a regional saturation magnitude + abstract double getMc(); + + abstract boolean useBasinTerm(); + + /* Equation 4.1 */ + private static final double calcMean( + final Coefficients c, + final boolean slab, + final boolean useBasinTerm, + final double mc, + final double pgaRef, + final double Mw, + final double rRup, + final double zHyp, + final double vs30, + final double z2p5) { + + /* Equation 4.7: Fs = Flin + Fnl + Fb */ + double siteTerm = getSiteTermLinear(c, vs30) + getSiteTermNonLinear(c, pgaRef, vs30) + + getSiteBasinTerm(c, useBasinTerm, z2p5); + + double h = nearSourceSaturationTerm(Mw, mc, slab); + + double μ = c.c0 + + getPathTerm(c, rRup, Mw, h) + + getMagnitudeTerm(c, mc, Mw) + + getDepthTerm(c, slab, zHyp) + + siteTerm; + + return 0.0; + } + + /* Equations 4.2 and 4.3 */ + private static final double getPathTerm(final Coefficients c, double rRup, double Mw, double h) { + double R = Maths.hypot(rRup, h); + return c.c1 * log(R) + c.b4 * Mw * log(R) + c.a0 * R; + } + + private static final double nearSourceSaturationTerm(double Mw, double mc, boolean slab) { + if (slab) { + /* Equation 4.4b */ + return (Mw <= mc) + ? pow(10, (1.050 / (mc - 4.0)) * (Mw - mc) + 1.544) + : 35; + } else { + /* Equation 4.4a */ + return pow(10.0, -0.82 + 0.252 * Mw); + } + } + + /* Equation 4.5 */ + private static final double getMagnitudeTerm(final Coefficients c, double mc, double Mw) { + double dM = Mw - mc; + return (Mw <= mc) + ? c.c4 * dM + c.c5 * dM * dM + : c.c6 * dM; + } + + /* Equation 4.6 */ + private static final double getDepthTerm(final Coefficients c, boolean slab, double zHyp) { + if (!slab) { + return 0.0; + } + // Zhyp (see section 4.3.3) can be replaced with mean depth (Zhyp_bar) + // that depends on Ztor, fault width, and dip angle + double dTerm; + if (zHyp < DB1) { // "less than" + dTerm = c.m * (DB1 - DB2) + c.d; + } else if (zHyp > DB2) { + dTerm = c.d; + } else { // "db1 <= zHyp" possible typo? (written as "db1 < xHyp") + dTerm = c.m * (zHyp - DB2) + c.d; + } + return dTerm; + } + + /* Equation 4.8 */ + private static final double getSiteTermLinear(final Coefficients c, double vs30) { + double Flin; + if (vs30 <= V1) { + Flin = c.s1 * log(vs30 / V1) + c.s2 * log(V1 / VREF); + } else if (vs30 > c.v2) { + Flin = c.s2 * log(c.v2 / VREF); + } else { + Flin = c.s2 * log(vs30 / VREF); + } + return Flin; + } + + /* Equation 4.9 */ + private static final double getSiteTermNonLinear(final Coefficients c, double pgaRef, + double vs30) { + double f2 = c.f4 * (exp(c.f5 * (min(vs30, 760) - 200)) - exp(c.f5 * (760 - 200))); + return 0.0 + f2 * log((pgaRef + 0.05) / 0.05); + } + + /* Equations 4.11 - 4.13 */ + private static final double getSiteBasinTerm(final Coefficients c, boolean basin, double z2p5) { + if (!basin) { + return 0.0; + } else { + // TODO: implement for Cascadia - this term is 0 except in Japan and + // Cascadia + return 0.0; + } + } + + /* + * Global interface model + */ + static class Interface extends ParkerEtAl_2020 { + static final String NAME = ParkerEtAl_2020.NAME + " : Interface"; + + Interface(Imt imt) { + super(imt); + } + + @Override + final boolean isSlab() { + return false; + } + + @Override + final boolean useBasinTerm() { + return false; + } + + @Override + final double getMc() { + return 7.9; + } + + } + + /* + * Global intraslab model + */ + static class Slab extends ParkerEtAl_2020 { + static final String NAME = ParkerEtAl_2020.NAME + " : Slab"; + + Slab(Imt imt) { + super(imt); + } + + @Override + final boolean isSlab() { + return true; + } + + @Override + final boolean useBasinTerm() { + return false; + } + + @Override + final double getMc() { + return 7.6; + } + } + + public static void main(String[] args) { + + } + +}