package org.opensha2.gmm;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.Math.log;
import static java.lang.Math.log10;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.opensha2.gmm.GmmInput.Field.MW;
import static org.opensha2.gmm.GmmInput.Field.RRUP;
import static org.opensha2.gmm.GmmInput.Field.VS30;
import static org.opensha2.gmm.GmmUtils.BASE_10_TO_E;
import static org.opensha2.gmm.Imt.PGA;
import static org.opensha2.gmm.MagConverter.NONE;
import static org.opensha2.gmm.SiteClass.SOFT_ROCK;
import org.opensha2.gmm.GmmInput.Constraints;
import com.google.common.collect.Range;
import java.util.Map;
/**
* Abstract implementation of the ground motion model for stable continental
* regions by Atkinson & Boore (2006). This implementation matches that used in
* the 2008 USGS NSHMP. In addition to have two stress-drop scaling variants,
* this model also comes in magnitude converting (mb to Mw) flavors to support
* the 2008 central and eastern US model.
*
* <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>Implementation note:</b> this uses a reduced set of frequencies that
* correspond most closely to defined {@code Imt}s.
*
* <p><b>Implementation note:</b> Mean values are clamped per
* {@link GmmUtils#ceusMeanClip(Imt, double)}.
*
* <p><b>Reference:</b> Atkinson, G.M., and Boore, D.M., 2006, Earthquake
* ground-motion prediction equations for eastern North America: Bulletin of the
* Seismological Society of America, v. 96, p. 2181–2205.
*
* <p><b>doi:</b> <a href="http://dx.doi.org/10.1785/0120050245">
* 10.1785/0120050245</a>
*
* <p><b>Component:</b> horizontal (not clear from publication)
*
* @author Peter Powers
* @see Gmm#AB_06_140BAR
* @see Gmm#AB_06_140BAR_AB
* @see Gmm#AB_06_140BAR_J
* @see Gmm#AB_06_200BAR
* @see Gmm#AB_06_200BAR_AB
* @see Gmm#AB_06_200BAR_J
*/
public abstract class AtkinsonBoore_2006 implements GroundMotionModel, ConvertsMag {
// This model was developed using data from hard rock sites and simulations.
// CoefficientsOld are provided for hard rock and soft rock sites. Use the
// hard
// rock coefficients for vs30 >= 2000. Use soft rock coeffs for vs30 <= 760
// and lower. It is not clear from Atkinson and Boore (2006) how vs30
// between 760 and 2000 should be handled. The published algorithms will
// keep returning the vs760 values and this is likely incorrect. For now,
// we only allow vs760 and vs200 in keeping with other CEUS GMMs.
// This implementation includes an abstract method scaleFactor(), the value
// of which can be obtained via the static utility method
// scaleFactorCalc(stress). A stress value of 140bars will return a scale
// factor of 0.
// notes from original implementation and fortran:
//
// rounded .3968 to 0.4 s for one element of abper. SH june 30 2008
static final String NAME = "Atkinson & Boore (2006)";
static final Constraints CONSTRAINTS = Constraints.builder()
.set(MW, Range.closed(4.0, 8.0))
.set(RRUP, Range.closed(0.0, 1000.0))
.set(VS30, Range.closed(760.0, 2000.0))
.build();
static final CoefficientContainer COEFFS_A = new CoefficientContainer("AB06A.csv");
static final CoefficientContainer COEFFS_BC = new CoefficientContainer("AB06BC.csv");
private static final double GFAC = 6.8875526; // ln (980)
private static final double TFAC = -0.5108256; // ln(0.6)
private static final double vref = 760.0;
private static final double v1 = 180.0;
private static final double v2 = 300.0;
private static final double fac70 = 1.8450980; // log10(70)
private static final double fac140 = 2.1461280; // log10(140)
private static final double facv1 = -0.5108256; // ln(v1/v2)
private static final double facv2 = -0.9295360; // ln(v2/vref)
private static final double SIGMA = 0.3 * BASE_10_TO_E;
// private static final double STRESSFAC = 0.5146; // ln(200/140)/ln(2)
// private static final double SFAC = 2.302585; // ln(10)
private static final class Coefficients {
final Imt imt;
final double c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
bln, b1, b2,
del,
m1, mh;
Coefficients(Imt imt, CoefficientContainer cc) {
this.imt = imt;
Map<String, Double> coeffs = cc.get(imt);
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");
bln = coeffs.get("bln");
b1 = coeffs.get("b1");
b2 = coeffs.get("b2");
del = coeffs.get("del");
m1 = coeffs.get("m1");
mh = coeffs.get("mh");
}
}
private final Coefficients coeffsA;
private final Coefficients coeffsA_PGA;
private final Coefficients coeffsBC;
private final Coefficients coeffsBC_PGA;
AtkinsonBoore_2006(final Imt imt) {
coeffsA = new Coefficients(imt, COEFFS_A);
coeffsA_PGA = new Coefficients(PGA, COEFFS_A);
coeffsBC = new Coefficients(imt, COEFFS_BC);
coeffsBC_PGA = new Coefficients(PGA, COEFFS_BC);
}
@Override
public final ScalarGroundMotion calc(final GmmInput in) {
// this call will only allow vs30 = 760 | 2000
SiteClass siteClass = GmmUtils.ceusSiteClass(in.vs30);
Coefficients coeffs = siteClass == SOFT_ROCK ? coeffsBC : coeffsA;
Coefficients coeffsPGA = siteClass == SOFT_ROCK ? coeffsBC_PGA : coeffsA_PGA;
double μ = calcMean(coeffs, coeffsPGA, converter(), scaleFactor(), in);
return DefaultScalarGroundMotion.create(μ, SIGMA);
}
@Override
public MagConverter converter() {
return NONE;
}
private static final double DEFAULT_STRESS = 140.0;
private static final double MIN_STRESS = 35.0;
private static final double MAX_STRESS = 560.0;
abstract double scaleFactor();
private static final double scaleFactorCalc(final double stress) {
checkArgument(stress >= MIN_STRESS && stress <= MAX_STRESS,
"Supplied stress value [%s] is out of range [%s, %s]", stress,
MIN_STRESS, MAX_STRESS);
return log(stress / DEFAULT_STRESS) / log(2);
}
// SF2 variable of AB06 needs to be provided by subclasses via
private static final double calcMean(final Coefficients c, final Coefficients cPGA,
final MagConverter converter, final double stressScale, final GmmInput in) {
double Mw = converter.convert(in.Mw);
double rRup = in.rRup;
double vs30 = in.vs30;
double gndmp = c.c1 + c.c2 * Mw + c.c3 * Mw * Mw;
// set up stress factor
double sf2 = 0.0;
if (stressScale != 0.0) {
double diff = max(Mw - c.m1, 0.0);
sf2 = stressScale *
min(c.del + 0.05, 0.05 + c.del * diff / (c.mh - c.m1));
}
// per NSHMP implementation rRup floored to 2km
rRup = max(rRup, 2.0);
// pga calculations
double rfac = log10(rRup);
double f0 = max(1.0 - rfac, 0.0);
double f1 = min(rfac, fac70);
double f2 = max(rfac - fac140, 0.0);
double gnd, S = 0.0;
if (vs30 <= 760.0) {
// compute pga on rock
double gndPGA = cPGA.c1 + cPGA.c2 * Mw + cPGA.c3 * Mw * Mw;
gnd = gndPGA + (cPGA.c4 + cPGA.c5 * Mw) * f1 +
(cPGA.c6 + cPGA.c7 * Mw) * f2 + (cPGA.c8 + cPGA.c9 * Mw) * f0 +
cPGA.c10 * rRup + sf2;
double bnl;
if (vs30 <= v1) {
bnl = c.b1;
} else if (vs30 <= v2) {
bnl = (c.b1 - c.b2) * Math.log(vs30 / v2) / facv1 +
c.b1;
} else if (vs30 <= vref) {
bnl = c.b2 * Math.log(vs30 / vref) / facv2;
} else {
bnl = 0.0;
}
double pga_bc = Math.pow(10, gnd);
if (pga_bc <= 60.0) {
S = c.bln * log(vs30 / vref) + bnl * TFAC;
} else {
S = c.bln * log(vs30 / vref) + bnl * log(pga_bc / 100.0);
}
S = log10(Math.exp(S));
}
gnd = gndmp + (c.c4 + c.c5 * Mw) * f1 +
(c.c6 + c.c7 * Mw) * f2 + (c.c8 + c.c9 * Mw) *
f0 +
c.c10 * rRup + sf2 + S;
gnd *= BASE_10_TO_E;
if (c.imt != Imt.PGV) {
gnd -= GFAC;
}
return GmmUtils.ceusMeanClip(c.imt, gnd);
}
// these are subclassed for mag conversion variants and therefore not final
static class StressDrop_140bar extends AtkinsonBoore_2006 {
static final String NAME = AtkinsonBoore_2006.NAME + ": 140 bar";
private static final double STRESS = 140.0;
private static final double SF2;
static {
SF2 = scaleFactorCalc(STRESS);
}
StressDrop_140bar(Imt imt) {
super(imt);
}
@Override
double scaleFactor() {
return SF2;
}
}
static class StressDrop_200bar extends AtkinsonBoore_2006 {
static final String NAME = AtkinsonBoore_2006.NAME + ": 200 bar";
private static final double STRESS = 200;
private static final double SF2;
static {
SF2 = scaleFactorCalc(STRESS);
}
StressDrop_200bar(Imt imt) {
super(imt);
}
@Override
double scaleFactor() {
return SF2;
}
}
}