package org.opensha2.eq.model;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.StandardSystemProperty.LINE_SEPARATOR;
import static org.opensha2.internal.Parsing.addAttribute;
import static org.opensha2.internal.Parsing.addElement;
import static org.opensha2.internal.Parsing.toDoubleArray;
import static org.opensha2.internal.SourceAttribute.COUNT;
import static org.opensha2.internal.SourceAttribute.CUTOFF;
import static org.opensha2.internal.SourceAttribute.DELTAS;
import static org.opensha2.internal.SourceAttribute.MO_BALANCE;
import static org.opensha2.internal.SourceAttribute.SIGMA;
import static org.opensha2.internal.SourceAttribute.WEIGHTS;
import static org.opensha2.internal.SourceElement.ALEATORY;
import static org.opensha2.internal.SourceElement.EPISTEMIC;
import static org.opensha2.internal.SourceElement.MAG_UNCERTAINTY;
import org.opensha2.eq.Earthquakes;
import org.w3c.dom.Element;
import java.util.Arrays;
import java.util.Map;
/**
* Wrapper class for magnitude uncertainty data. Uncertainty flags are
* initialized based on input data, however, due to quirky nshmp rules, they may
* be overriden at some point and should always be checked prior to calculation
* regardless of any uncertainty values present.
*/
public class MagUncertainty {
boolean hasEpistemic;
int epiCount;
double[] epiDeltas;
double[] epiWeights;
double epiCutoff;
boolean hasAleatory;
int aleaCount;
double aleaSigma;
boolean moBalance;
double aleaCutoff;
MagUncertainty() {}
/**
* Factory magnitude uncertainty container constructor.
*
* @param epiDeltas epistemic change to magnitude (M +/- delta)
* @param epiWeights weight for each change; must be same length as
* {@code epiDeltas}
* @param epiCutoff minimum magnitude for which epistemic uncertainty applies,
* below which it is disabled
* @param aleaSigma standard deviation of aleatory Gaussian uncertainty
* @param aleaCount number of aleatory uncertainty magnitude bins across a
* normal distribution
* @param moBalance whether to preserve moment across aleatory uncertainty
* bins
* @param aleaCutoff minimum magnitude for which aleatory uncertainty applies,
* below which it is disabled
* @return a magnitude uncertainty container
*/
public static MagUncertainty create(double[] epiDeltas, double[] epiWeights, double epiCutoff,
double aleaSigma, int aleaCount, boolean moBalance, double aleaCutoff) {
MagUncertainty mu = new MagUncertainty();
checkArgument(epiDeltas.length > 0);
checkArgument(epiWeights.length > 0);
checkArgument(epiDeltas.length == epiWeights.length);
mu.epiDeltas = epiDeltas;
mu.epiWeights = epiWeights;
mu.epiCount = mu.epiDeltas.length;
mu.hasEpistemic = mu.epiCount > 1;
mu.epiCutoff = Earthquakes.checkMagnitude(epiCutoff);
checkArgument(aleaSigma >= 0);
checkArgument(aleaCount < 40);
mu.aleaSigma = aleaSigma;
mu.aleaCount = aleaCount;
mu.moBalance = moBalance;
mu.hasAleatory = mu.aleaCount > 1 && mu.aleaSigma != 0.0;
mu.aleaCutoff = Earthquakes.checkMagnitude(aleaCutoff);
return mu;
}
/* Package-private constructor using XML attribute strings */
static MagUncertainty create(Map<String, String> epiAtts, Map<String, String> aleaAtts) {
MagUncertainty mu = new MagUncertainty();
// epistemic
if (epiAtts != null) {
mu.epiDeltas = toDoubleArray(epiAtts.get(DELTAS.toString()));
mu.epiWeights = toDoubleArray(epiAtts.get(WEIGHTS.toString()));
mu.epiCutoff = Double.valueOf(epiAtts.get(CUTOFF.toString()));
checkArgument(mu.epiDeltas.length == mu.epiWeights.length,
"Epistemic deltas and mags are different lengths [%s, %s]", mu.epiDeltas.length,
mu.epiWeights.length);
mu.epiCount = mu.epiDeltas.length;
mu.hasEpistemic = mu.epiCount > 1;
}
// aleatory
if (aleaAtts != null) {
mu.aleaSigma = Double.valueOf(aleaAtts.get(SIGMA.toString()));
mu.aleaCount = Integer.valueOf(aleaAtts.get(COUNT.toString()));
mu.aleaCutoff = Double.valueOf(aleaAtts.get(CUTOFF.toString()));
checkArgument(mu.aleaCount % 2 == 1,
"Aleatory bins [%s] should be odd so they center on mean magnitude", mu.aleaCount);
mu.moBalance = Boolean.valueOf(aleaAtts.get(MO_BALANCE.toString()));
// two ways to kill aleatory
mu.hasAleatory = mu.aleaCount > 1 && mu.aleaSigma != 0.0;
}
return mu;
}
private static final String LF = LINE_SEPARATOR.value();
@Override
public String toString() {
return new StringBuilder()
.append(" MFD Data...").append(LF)
.append(" Epistemic unc: ").append(hasEpistemic).append(LF)
.append(" deltas: ").append(Arrays.toString(epiDeltas)).append(LF)
.append(" weights: ").append(Arrays.toString(epiWeights)).append(LF)
.append(" cutoff: ").append(epiCutoff).append(LF)
.append(" Aleatory unc: ").append(hasAleatory).append(LF)
.append(" sigma: ").append(aleaSigma).append(LF)
.append(" count: ").append(aleaCount).append(LF)
.append(" Mo balance: ").append(moBalance).append(LF)
.append(" cutoff: ").append(aleaCutoff).append(LF).toString();
}
/**
* Appends the XML form of this magnitude uncertainty data to the supplied
* {@code Element}.
* @param node to append to
* @return a reference to the newly created {@code Element}
*/
public Element appendTo(Element node) {
if (!hasAleatory && !hasEpistemic) {
return null;
}
Element e = addElement(MAG_UNCERTAINTY, node);
if (hasEpistemic) {
Element eEpistemic = addElement(EPISTEMIC, e);
addAttribute(DELTAS, epiDeltas, eEpistemic);
addAttribute(WEIGHTS, epiWeights, eEpistemic);
addAttribute(CUTOFF, epiCutoff, eEpistemic);
}
if (hasAleatory) {
Element eAleatory = addElement(ALEATORY, e);
addAttribute(SIGMA, aleaSigma, eAleatory);
addAttribute(COUNT, aleaCount, eAleatory);
addAttribute(MO_BALANCE, moBalance, eAleatory);
addAttribute(CUTOFF, aleaCutoff, eAleatory);
}
return e;
}
}