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 d749ab0cd0a8f4e89b732a56f9b59273704efede..51137d860a0f39c8ee1066f900caeacfb8ca6a51 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/gmm/Gmm.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/gmm/Gmm.java @@ -250,12 +250,12 @@ public enum Gmm { SadighEtAl_1997.COEFFS_BC_HI, SadighEtAl_1997.CONSTRAINTS), - // /** @see Grazier_2018 */ - // GRAIZER_2018( - // Graizer_2018.class, - // Graizer_2018.NAME, - // Graizer_2018.COEFFS, - // Graizer_2018.CONSTRAINTS), + /** @see Grazier_2018 */ + GRAIZER_2018( + Graizer_2018.class, + Graizer_2018.NAME, + Graizer_2018.COEFFS, + Graizer_2018.CONSTRAINTS), /* Subduction Interface and Slab WUS 2008 2014 2018, AK 2007 */ diff --git a/src/main/java/gov/usgs/earthquake/nshmp/gmm/Graizer_2018.java b/src/main/java/gov/usgs/earthquake/nshmp/gmm/Graizer_2018.java index 481c1b371100ded629b133036c049f85683d5044..5f8e799b8a7e60028ae5cc06671da9a750964777 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/gmm/Graizer_2018.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/gmm/Graizer_2018.java @@ -29,7 +29,7 @@ import gov.usgs.earthquake.nshmp.gmm.GmmInput.Constraints; import gov.usgs.earthquake.nshmp.tree.LogicTree; /** - * TODO: + * Outstanding questions: * * * is Q_SA, for G3, defined by Eq. 8? * @@ -45,7 +45,11 @@ import gov.usgs.earthquake.nshmp.tree.LogicTree; * * Attempt to compute values in Table S1 and/or S2 using the manual regression * code blocks in Matlab (so far, the matlab code does not reproduce the tables) * - * + * * "Residuals" calculated in matlab are not documented in published paper + * (they are mentioned) + */ + +/** * Implementation of the GK17 ground motion model for shallow crustal * continental earthquakes by Grazier (2018). This model computes the average * (RotD50) horizontal components of PGA and 5% damped PSA at periods from 0.01 @@ -128,11 +132,11 @@ public final class Graizer_2018 implements GroundMotionModel { * the "shape-preserving piecewise cubic interpolation", and the resulting * values are hard-coded here. * - * The following Matlab code was used to compute sigma values at the MSRS + * The following Matlab code was used to compute sigma values at the MPRS * periods: * * <pre> - * % From Table 3 in Graizer (2018) + * % From Table 1 in Graizer (2018) * t = [0.010 0.020 0.040 0.075 0.100 0.150 0.200 0.400 0.500 0.750 1.000 2.000 3.000 4.000 5.000 8.000 10.0 ]; * sigma = [0.631 0.633 0.653 0.713 0.734 0.720 0.693 0.673 0.693 0.732 0.738 0.783 0.774 0.733 0.746 0.789 0.736]; * tau = [0.375 0.375 0.386 0.456 0.477 0.451 0.407 0.365 0.398 0.465 0.492 0.501 0.512 0.448 0.464 0.528 0.451]; @@ -155,8 +159,6 @@ public final class Graizer_2018 implements GroundMotionModel { @Override public LogicTree<GroundMotion> calc(GmmInput in) { - /* Eq. 1 */ - // SA = G1 * G2 * G3 * G4 * G5 * SA_NORM * εγ var fault_style = GmmUtils.rakeToFaultStyle_NSHMP(in.rake); @@ -164,25 +166,15 @@ public final class Graizer_2018 implements GroundMotionModel { double G1 = filterG1(in.Mw, fault_style); // M-scaling double G2 = filterG2(in.Mw, in.rRup); // R-scaling - // PGA is apparently G1 * G2 ??? + // Matlab code GK17PGA.m returns G1 * G2 double G3 = filterG3(period, in.rRup); // Apparent attenuation scaling double G4 = filterG4(period, in.Mw, in.rRup, fault_style); // Style of // faulting style - double G5 = filterG5(period, in.vs30); // Site-scaling - - /** - * <pre> - * Questions: - * 1. Is a "G6" filter implied? To add the basin/deep sediment effects to the - * Vs30 amplification? No equation is provided but factors (relative to Vs30 - * amplification) are provided in Table S2 (as a function of Vs30 and T) - * - * But then use Eq. 16 to convert Z2.5 to effective Vs30 to look up the basin effect??? - * </pre> - */ - // "G6" for basin effect? look up from table? + double G5 = filterG5(period, in.vs30, in.z2p5); // Site-scaling + /* Eq. 1 */ + // SA = G1 * G2 * G3 * G4 * G5 * SA_NORM * εγ double μ = log(G1 * G2 * G3 * G4 * G5 * sa_norm); double σ = coeffs.σ; @@ -212,6 +204,10 @@ public final class Graizer_2018 implements GroundMotionModel { /* 5% damped response spectral shape, SA_norm (Figure 3) */ private static final double spectralShape(double T, double Mw, double rRup) { + /* + * Matlab code GK17SA.m uses truncated M and R values when calculating the + * spectral shape, this is not mentioned in the paper + */ double μ_mr = m1 * rRup + m2 * Mw + m3; double I_mr = (a1 * Mw + a2) * exp(a3 * rRup); double S_mr = s1 * rRup - (s2 * Mw + s3); @@ -223,7 +219,7 @@ public final class Graizer_2018 implements GroundMotionModel { sqrt((1.0 - t_ratio) * (1.0 - t_ratio) + 4.0 * Dsp * Dsp * t_ratio); } - /* Model coefficients from Graizer & Kalkan (2016) Table 3 */ + /* Model coefficients from Graizer & Kalkan (2016) Table 3 and GK17PGA.m */ private static final double c1 = 0.14; private static final double c2 = -6.25; private static final double c3 = 0.37; @@ -240,17 +236,29 @@ public final class Graizer_2018 implements GroundMotionModel { private static final double b2 = 0.27; // inferred from matlab code private static final double b3 = 0.444; // inferred from matlab code private static final double λ = 1.36; // inferred from matlab code - private static final double c21 = 0.000854; // inferred from matlab code - private static final double c22 = 1.0513; // inferred from matlab code + private static final double c21 = 0.000854; // inferred from matlab GK17PGA.m + private static final double c22 = 1.0513; // inferred from matlab GK17PGA.m /* Filter G1 - magnitude-scaling */ private static final double filterG1(double Mw, FaultStyle fault_style) { /* - * k_scale is style-of-faulting factor from Graizer and Kalkan (2016) Eq. 3 + * Magnitude scaling filter is from previous model (Graizer and Kalkan + * (2016) Eq. 3) for larger magnitudes with new small-magnitude scaling. + * + * Paper implies k_scale is style-of-faulting factor from Graizer and Kalkan + * (2016) and does not define new coefficients c21 and c22, which must be + * inferred from matlab code GK17PGA.m (F1 term in matlab). + * + * "k_scale" as used in matlab code GK17PGA.m differs from Graizer and + * Kalkan (2016), which was dependent on fault style. */ double k_scale = (fault_style == REVERSE) ? 1.28 : (fault_style == REVERSE_OBLIQUE) ? 1.14 : 1.0; + double F_matlab = 1.0; // F = 1.0 for geomean and RotD50 + double k_scale_matlab = F_matlab / 1.12; + + /* matlab code GK17PGA.m uses M <= 5.5 and M > 5.5 */ return (Mw < 5.5) ? (c21 * exp(c22 * Mw) * k_scale) : (c1 * atan(Mw + c2) + c3) * k_scale; } @@ -262,11 +270,19 @@ public final class Graizer_2018 implements GroundMotionModel { /* Eq. 3 */ double w1 = (1 - sqrt(50 / R2)) * (1 - sqrt(50 / R2)); - double w2 = 4 * D2 * D2 * sqrt(50 / R2); - double w3 = (1 - (50 / R2)) * (1 - (50 / R2)); - double w4 = 4 * D2 * D2 * (50 / R2); + double w2 = 4 * D2 * D2 * sqrt(50. / R2); + double w3 = (1 - 50. / R2) * (1 - 50. / R2); + double w4 = 4 * D2 * D2 * 50. / R2; double w = sqrt((w1 + w2) / (w3 + w4)); + // /* + // * w from Matlab GK17PGA.m uses 1.96 instead of Mw-dependent "4 * D2 * D2" + // * and pulls -sqrt out into log-space + // */ + // double ml_w2 = 1.96 * sqrt(50. / R2); // matlab + // double w_matlab = exp(-0.5 * log((w3 + w4) / (w1 + ml_w2))); + // System.out.printf("Paper w = %.6e\nMatlab w = %.6e\n", w, w_matlab); + /* Eq. 3 */ double g1 = (1 - sqrt(rRup / R2)) * (1 - sqrt(rRup / R2)); double g2 = 4 * D2 * D2 * (rRup / R2); @@ -279,23 +295,37 @@ public final class Graizer_2018 implements GroundMotionModel { private double filterG3(double T, double rRup) { double freq = 1.0 / T; - // TODO: Is this the right formulation of QSA to use? /* Qsa from Eq. 8 (as a function of freq) */ double p = (freq < 1.0) ? 0.90 : (freq < 10.0) ? 0.91 : 0.95; - double Qsa = 120 * pow(freq, p); + double rRef = 120.0; + + double Qsa = rRef * pow(freq, p); + + /* + * Q-term (Qsa factor) in matlab code GK17SA.m has only a slight resemblance + * to Eq. 8 (and the G3 filter too, for that matter) and includes an added + * dependence on magnitude (Eq. 10?) + * + * β, with a value of 3.5 km defined in the paper does not appear in the + * matlab code + */ + // double p_matlab = (freq <= 1.0) ? 0.90 : (freq <= 10.0) ? 0.91 : 0.95; + // double rRef_matlab = (rRup <= 140.0) ? 120.0 : 120. + (140. - rRup) / + // 100.; + // double Qsa_matlab = (0.2759 + 0.1379 * Mw) * rRef * pow(freq, p); return exp(-freq * rRup / β / Qsa); } /* Filter G4 - Fault-style scaling */ private double filterG4(double T, double Mw, double rRup, FaultStyle fault_style) { - // coefficients b1, b2, b3, and λ inferred from matlab code + /* Eqs. 13 and 14 */ if ((fault_style != FaultStyle.REVERSE) || (Mw < 4.0)) { return 1.0; } double dist = (rRup <= 90.0) ? rRup : 90; - /* Eq. 12 */ + /* Eq. 12 - coefficients b1, b2, b3, and λ inferred from matlab code */ double CSoF0 = (b1 + b2 / (1 + (T / 7.0) * (T / 7.0))) * (b3 + 1 / (1 + pow(dist / 100.0, λ))); @@ -303,7 +333,7 @@ public final class Graizer_2018 implements GroundMotionModel { } /* Filter G5 - Site-response term */ - private double filterG5(double T, double vs30) { + private double filterG5(double T, double vs30, double z2p5) { /** * <pre> * Questions: @@ -312,11 +342,15 @@ public final class Graizer_2018 implements GroundMotionModel { * 2. Should Eq. 15 reproduce Table S1: VS30 site amplification factors relative * to the western hard rock of VS30 = 1100 m/s? (it doesn't seem to) * + * 3. Should basin effects (sediment thickness) be added here to this term? Sediment + * thickness effects appear be calculated based on residuals in matlab code GK17SA.m * </pre> */ double freq = 1.0 / T; /* + * Eq. 15 + * * amplification coefficient constant for vs30 below 180 m/s and above 1100 * m/s */ @@ -327,22 +361,91 @@ public final class Graizer_2018 implements GroundMotionModel { double kvs = -0.5 * log(vs / 1100.0); double fvs = vss / 120.0 - 2.0; - return 1 + kvs / sqrt((1 - (fvs / freq)) * (1 - (fvs / freq)) + 1.96 * (fvs / freq)); - } + double g5_vs30 = 1 + kvs / sqrt((1 - (fvs / freq)) * (1 - (fvs / freq)) + 1.96 * (fvs / freq)); - /* Sediment thickness (Z2.5) */ - private double sedimentThickness(double vs30, double z2p5) { - // Eq. 16 is a Z2.5 - Vs30 relation - // ln(z2p5) = 7.108 - 1.145 * ln(vs30) + /* + * Matlab code in GK17SA.m, "Reducing to hard rock", has a vague similarity + * to Eq. 15. Terms Amp_Va and Amp_Vs are calculated and, in the formulation + * for Y, Amp_vs is divided by Amp_Va and the vs30 correction (residuals) + */ + double basinAmp = basinAmplification(vs30, z2p5); - // Matlab script has this relation: - // ln(z2p5) = 7.521 - 1.233 * ln(VS30) + return g5_vs30 * basinAmp; + } - // No equations in the paper are a function of Z2.5. - // Paper refers to Table S2 for basin amplification (as a function of Vs30 - // and Vs30), which are specified as relative to the site amplification due - // Vs30 + /* Basin amplification */ + private double basinAmplification(double vs30, double z2p5) { + /* + * The paper does not define an equation for the sediment thickness term + * + * Eq. 16 is a Z2.5 - Vs30 relation: ln(z2p5) = 7.108 - 1.145 * ln(vs30) + * + * Matlab code GK17SA.m uses Z2.5 directly and has a different Z2.5 - Vs30 + * relation in a comment: ln(Z25) = 7.521 - 1.233 * ln(VS30) + * + * Paper refers to Table S2 for basin amplification (as a function of Vs30 + * and period, T), which is specified as relative to the site amplification + * due to Vs30. + */ + if (Double.isNaN(z2p5)) { + return 1.0; + } + /* + * Look up value from table of ratios (vs30_amp / basin_amp?) for vs30 and + * z2p5? Or calculate using residuals calculations (BasAmp term) in matlab + * code GK17SA.m? + * + * From matlab, 5 period-dependent coefs: ba0, ba1, ba2, ba3, ba4: + * + * - BasAmp = ba0 for 0 <= z2p5 <= 0.18 + * + * - BasAmp = ba1 * z2p5^ba2 for 0.18 < z2p5 <= 0.67 + * + * - BasAmp = 1 for 0.67 < z2p5 < 3.15 *** Does this interval make sense? + * + * - BasAmp = ba3 * z2p5^ba4 for z2p5 >= 3.15 + * + * where BasAmp = ba1 * z2p5^ba2 or BasAmp = ba3 * z2p5^ba4, depending on + * value of z2p5 + */ return 1.0; } + + // private static final double getPGA_matlab(double Mw, double rRup) { + // double F = 1.0; // F = 1.0 for geomean and RotD50 + // double F1 = 0; + // double F2 = 0; + // + // // This is Equation 2 [G1(M,kscale)], but with kscale replaced with F/1.12 + // // (instead of style-of-faulting factor from GK16) + // // c21 = 0.000854 + // // c22 = 1.0513 + // if (Mw > 5.5) { // Eq. 2 (M >= 5.5) + // F1 = log((c1 * atan(Mw + c2) + c3) * F / 1.12); + // } else { + // F1 = log((0.000854 * exp(1.0513 * Mw) * F / 1.12)); + // } + // + // // This is Eq. 4 in the paper, Ro is R2, and Do is D2 + // double Ro = c4 * Mw + c5; + // double Do = c6 * cos(c7 * (Mw + c8)) + c9; + // + // // Bottom of Eq. 3 in the paper ??? in log space without sqrt + // // Conversion from body to surface waves W + // double W = log((pow((1.0 - 50.0 / Ro), 2) + 4 * (Do * Do) * 50.0 / Ro) / + // (pow(1 - sqrt(50 / Ro), 2) + 1.96 * sqrt(50.0 / Ro))); + // + // if (rRup <= 50.0) { + // // attenuation of the order R-1 + // F2 = -0.5 * log(pow(1 - (rRup / Ro), 2) + 4.0 * Do * Do * (rRup / Ro)); + // } else { + // // attenuation of the order R-0.5 + // F2 = -0.5 * log(pow(1 - sqrt(rRup / Ro), 2) + 1.96 * sqrt(rRup / Ro)) - 0.5 + // * W; + // } + // + // return F1 + F2; + // } + }