package org.opensha2.gmm; import static java.lang.Math.exp; import static java.lang.Math.log; import static java.lang.Math.max; import static java.lang.Math.pow; import static java.lang.Math.sqrt; import static org.opensha2.gmm.FaultStyle.NORMAL; import static org.opensha2.gmm.FaultStyle.REVERSE; import static org.opensha2.gmm.GmmInput.Field.DIP; import static org.opensha2.gmm.GmmInput.Field.MW; import static org.opensha2.gmm.GmmInput.Field.RAKE; import static org.opensha2.gmm.GmmInput.Field.RJB; import static org.opensha2.gmm.GmmInput.Field.RRUP; import static org.opensha2.gmm.GmmInput.Field.VS30; import static org.opensha2.gmm.GmmInput.Field.Z2P5; import static org.opensha2.gmm.GmmInput.Field.ZTOP; import static org.opensha2.gmm.Imt.PGA; import static org.opensha2.gmm.Imt.SA0P01; import static org.opensha2.gmm.Imt.SA0P25; import org.opensha2.eq.fault.Faults; import org.opensha2.gmm.GmmInput.Constraints; import com.google.common.collect.Range; import java.util.EnumSet; import java.util.Map; import java.util.Set; /** * Implementation of the Campbell & Bozorgnia (2008) next generation attenuation * for active crustal regions relationship developed as part of <a * href="http://peer.berkeley.edu/ngawest/">NGA West I</a>. * * <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> NGA Ground Motion Model for the Geometric Mean * Horizontal Component of PGA, PGV, PGD and 5% Damped Linear Elastic Response * Spectra for Periods Ranging from 0.01 to 10 s: Earthquake Spectra, v. 24, n. * 1, pp. 139-171. * * <p><b>doi:</b> <a href="http://dx.doi.org/10.1193/1.2857546"> * 10.1193/1.2857546</a> * * <p><b>Component:</b> GMRotI50 (geometric mean) * * @author Peter Powers * @see Gmm#CB_08 */ public final class CampbellBozorgnia_2008 implements GroundMotionModel { static final String NAME = "Campbell & Bozorgnia (2008)"; static final Constraints CONSTRAINTS = Constraints.builder() // TODO there are rake dependent M restrictions .set(MW, Range.closed(4.0, 8.5)) .set(RJB, Range.closed(0.0, 300.0)) .set(RRUP, Range.closed(0.0, 300.0)) // TODO actually is 15-90 .set(DIP, Faults.DIP_RANGE) .set(ZTOP, Range.closed(0.0, 15.0)) .set(RAKE, Faults.RAKE_RANGE) .set(VS30, Range.closedOpen(150.0, 1500.0)) .set(Z2P5, Range.closed(0.0, 10.0)) .build(); static final CoefficientContainer COEFFS = new CoefficientContainer("CB08.csv"); private static final Set<Imt> SHORT_PERIODS = EnumSet.range(SA0P01, SA0P25); private static final double S_lnAF = 0.3; private static final double N = 1.18; private static final double C = 1.88; private static final double S_lnAFsq = S_lnAF * S_lnAF; private static final class Coefficients { final Imt imt; final double c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, k1, k2, k3, s_lny, t_lny, ρ; // unused // double s_c; Coefficients(Imt imt, CoefficientContainer cc) { this.imt = imt; Map<String, Double> coeffs = cc.get(imt); c0 = coeffs.get("c0"); c1 = coeffs.get("c1"); c2 = coeffs.get("c2"); c3 = coeffs.get("c3"); c4 = coeffs.get("c4"); c5 = coeffs.get("c5"); c6 = coeffs.get("c6"); c7 = coeffs.get("c7"); c8 = coeffs.get("c8"); c9 = coeffs.get("c9"); c10 = coeffs.get("c10"); c11 = coeffs.get("c11"); c12 = coeffs.get("c12"); k1 = coeffs.get("k1"); k2 = coeffs.get("k2"); k3 = coeffs.get("k3"); s_lny = coeffs.get("s_lny"); t_lny = coeffs.get("t_lny"); ρ = coeffs.get("rho"); } } private final Coefficients coeffs; private final Coefficients coeffsPGA; CampbellBozorgnia_2008(final Imt imt) { coeffs = new Coefficients(imt, COEFFS); coeffsPGA = new Coefficients(PGA, COEFFS); } @Override public final ScalarGroundMotion calc(final GmmInput in) { return calc(coeffs, coeffsPGA, in); } private static final ScalarGroundMotion calc(final Coefficients c, final Coefficients cPGA, final GmmInput in) { FaultStyle style = GmmUtils.rakeToFaultStyle_NSHMP(in.rake); double vs30 = in.vs30; double pgaRock = (vs30 < c.k1) ? exp(calcMean(cPGA, style, 1100.0, 0.0, in)) : 0.0; double μ = calcMean(c, style, vs30, pgaRock, in); // prevent SA<PGA for short periods if (SHORT_PERIODS.contains(c.imt)) { double pgaMean = calcMean(cPGA, style, vs30, pgaRock, in); μ = max(μ, pgaMean); } double σ = calcStdDev(c, cPGA, vs30, pgaRock); return DefaultScalarGroundMotion.create(μ, σ); } // Mean ground motion model -- we use supplied vs30 rather than one from // input to impose 1100 when computing rock reference private static final double calcMean(final Coefficients c, final FaultStyle style, final double vs30, final double pga_rock, final GmmInput in) { double Mw = in.Mw; double rJB = in.rJB; double rRup = in.rRup; double zTop = in.zTop; double fmag, fdis, fflt, fhng, fsite, fsed; // Magnitude Term fmag = c.c0 + c.c1 * Mw; if (Mw > 5.5) { fmag += c.c2 * (Mw - 5.5); } if (Mw > 6.5) { fmag += c.c3 * (Mw - 6.5); } // Source to Site Term fdis = (c.c4 + c.c5 * Mw) * log(sqrt(rRup * rRup + c.c6 * c.c6)); // Fault-style Term fflt = (style == REVERSE) ? c.c7 * ((zTop < 1.0) ? zTop : 1.0) : (style == NORMAL) ? c.c8 : 0.0; // Hanging-wall Term double fhngr, fhngm, fhngz, fhngd; // .....distance if (rJB == 0.0) { fhngr = 1.0; } else if (zTop < 1 && rJB > 0.0) { double rJBsq1 = sqrt(rJB * rJB + 1.0); fhngr = (max(rRup, rJBsq1) - rJB) / max(rRup, rJBsq1); } else { fhngr = (rRup - rJB) / rRup; } // .....magnitude if (Mw <= 6.0) { fhngm = 0.0; } else if (Mw < 6.5) { fhngm = 2.0 * (Mw - 6.0); } else { fhngm = 1.0; } // .....rupture depth fhngz = (zTop >= 20.0) ? 0.0 : (20.0 - zTop) / 20.0; // .....rupture dip fhngd = (in.dip <= 70.0) ? 1.0 : (90.0 - in.dip) / 20.0; // .....total fhng = c.c9 * fhngr * fhngm * fhngz * fhngd; // Site Term - linear and non-linear double vsk1 = vs30 / c.k1; if (vs30 < c.k1) { fsite = c.c10 * log(vsk1) + c.k2 * (log(pga_rock + C * pow(vsk1, N)) - log(pga_rock + C)); } else if (vs30 < 1100.0) { fsite = (c.c10 + c.k2 * N) * Math.log(vsk1); } else { fsite = (c.c10 + c.k2 * N) * Math.log(1100.0 / c.k1); } // update z2p5 if not supplied double z2p5 = in.z2p5; if (Double.isNaN(z2p5)) { z2p5 = (vs30 <= 2500) ? 2.0 : 0.0; } // Shallow Sediment and Basin Term if (z2p5 < 1.0) { fsed = c.c11 * (z2p5 - 1.0); } else if (z2p5 <= 3.0) { fsed = 0.0; } else { fsed = c.c12 * c.k3 * exp(-0.75) * (1.0 - exp(-0.25 * (z2p5 - 3.0))); } // Total Model return fmag + fdis + fflt + fhng + fsite + fsed; } // Aleatory uncertainty model private static final double calcStdDev(final Coefficients c, final Coefficients cPGA, final double vs30, final double pgaRock) { // Inter-event Term double tau = c.t_lny; // Intra-event Term double sigma = c.s_lny; if (vs30 < c.k1) { double s_lnYb = sqrt(c.s_lny * c.s_lny - S_lnAFsq); double s_lnAb = sqrt(cPGA.s_lny * cPGA.s_lny - S_lnAFsq); double alpha = c.k2 * pgaRock * ((1.0 / (pgaRock + C * pow(vs30 / c.k1, N))) - 1 / (pgaRock + C)); sigma = sqrt(s_lnYb * s_lnYb + S_lnAF * S_lnAF + alpha * alpha * s_lnAb * s_lnAb + 2.0 * alpha * c.ρ * s_lnYb * s_lnAb); } // Total Model return sqrt(tau * tau + sigma * sigma); } }