package org.opensha2.eq.model;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.opensha2.internal.TextUtils.NEWLINE;
import static org.opensha2.internal.TextUtils.validateName;
import org.opensha2.calc.CalcConfig;
import org.opensha2.gmm.GroundMotionModel;
import org.opensha2.util.Named;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Set;
/**
* A {@code HazardModel} is the top-level wrapper for earthquake {@link Source}
* definitions and attendant {@link GroundMotionModel}s used in probabilisitic
* seismic hazard analyses (PSHAs) and related calculations. A
* {@code HazardModel} contains of a number of {@link SourceSet}s that define
* logical groupings of sources by {@link SourceType}. Each {@code SourceSet}
* carries with it references to the {@code GroundMotionModel}s and associated
* weights to be used when evaluating hazard.
*
* @author Peter Powers
* @see Source
* @see SourceSet
* @see SourceType
*/
public final class HazardModel implements Iterable<SourceSet<? extends Source>>, Named {
// high priority
// TODO grid sources; map against all matching defaultMfd types
// TODO Is current approach in SYstemSourceSet for precomputing distances
// correct? Double check that the individual sections are scaled by aseis
// not after once built into ruptures.
// TODO UCERF3 xml (indexedFaultSource) needs to have aftershock correction
// either imposed in file (better) of be imlemented in Parser
// or Loader (worse)
// TODO are any M=6.0 finite source representation cutoffs being used in
// 2014 fortran
// low priority
// TODO test low rate shortcut in FaultSource
// "if (rate < 1e-14) continue; // shortcut low rates"
// TODO need to revisit the application of uncertainty when minM < 6.5
// e.g. 809a Pine Valley graben in orwa.c.in
private final String name;
private final SetMultimap<SourceType, SourceSet<? extends Source>> sourceSetMap;
private final CalcConfig config;
private HazardModel(String name, CalcConfig config,
SetMultimap<SourceType, SourceSet<? extends Source>> sourceSetMap) {
this.name = name;
this.config = config;
this.sourceSetMap = sourceSetMap;
}
/**
* Load a {@code HazardModel} from a directory or Zip file specified by the
* supplied {@code path}.
*
* <p>For more information on a HazardModel directory and file structure, see
* the <a
* href="https://github.com/usgs/nshmp-haz/wiki/Earthquake-Source-Models"
* target="_top">nshmp-haz wiki</a>.
*
* <p><b>Notes:</b> HazardModel loading is not thread safe. Also, there are a
* wide variety of exceptions that may be encountered when loading a model. In
* most cases, the exception will be logged and the JVM will exit.
*
* @param path to {@code HazardModel} directory or Zip file
* @return a newly instantiated {@code HazardModel}
*/
public static HazardModel load(Path path) {
return Loader.load(path);
}
/**
* The number of {@code SourceSet}s in this {@code HazardModel}.
*/
public int size() {
return sourceSetMap.size();
}
@Override
public Iterator<SourceSet<? extends Source>> iterator() {
return sourceSetMap.values().iterator();
}
@Override
public String name() {
return name;
}
/**
* The set of {@code SourceType}s included in this model.
*/
public Set<SourceType> types() {
return sourceSetMap.keySet();
}
/**
* Return the default calculation configuration. This may be overridden.
*/
public CalcConfig config() {
return config;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("HazardModel: ");
sb.append(name).append(NEWLINE);
for (SourceSet<? extends Source> sourceSet : this) {
String typeStr = " " + sourceSet.type() + " Source Set";
sb.append(Strings.padEnd(typeStr, 24, ' '));
sb.append(sourceSet);
sb.append(NEWLINE);
}
return sb.toString();
}
/**
* Returns a new {@link Builder}.
*/
static Builder builder() {
return new Builder();
}
static class Builder {
static final String ID = "HazardModel.Builder";
boolean built = false;
// ImmutableSetMultimap.Builder preserves value addition order
private ImmutableSetMultimap.Builder<SourceType, SourceSet<? extends Source>> sourceMapBuilder;
private SetMultimap<SourceType, SourceSet<? extends Source>> sourceSetMap;
private CalcConfig config;
private String name;
private Builder() {
sourceMapBuilder = ImmutableSetMultimap.builder();
}
Builder config(CalcConfig config) {
this.config = checkNotNull(config);
return this;
}
Builder name(String name) {
this.name = validateName(name);
return this;
}
Builder sourceSet(SourceSet<? extends Source> source) {
sourceMapBuilder.put(source.type(), source);
return this;
}
private void validateState(String mssgID) {
checkState(!built, "This %s instance as already been used", mssgID);
checkState(name != null, "%s name not set", mssgID);
checkState(sourceSetMap.size() > 0, "%s has no source sets", mssgID);
checkState(config != null, "%s config not set", mssgID);
built = true;
}
HazardModel build() {
sourceSetMap = sourceMapBuilder.build();
validateState(ID);
return new HazardModel(name, config, sourceSetMap);
}
}
}