package org.opensha2.eq.model; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static org.opensha2.data.Data.checkInRange; import static org.opensha2.eq.Earthquakes.checkCrustalDepth; import static org.opensha2.eq.Earthquakes.checkCrustalWidth; import static org.opensha2.eq.fault.Faults.checkDip; import static org.opensha2.eq.fault.Faults.checkRake; import static org.opensha2.eq.fault.Faults.checkTrace; import static org.opensha2.eq.model.SourceType.FAULT; import static org.opensha2.internal.TextUtils.validateName; import org.opensha2.data.XySequence; import org.opensha2.eq.fault.surface.DefaultGriddedSurface; import org.opensha2.eq.fault.surface.GriddedSurface; import org.opensha2.eq.fault.surface.RuptureFloating; import org.opensha2.eq.fault.surface.RuptureScaling; import org.opensha2.geo.Location; import org.opensha2.geo.LocationList; import org.opensha2.geo.Locations; import org.opensha2.mfd.IncrementalMfd; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Range; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Fault source representation. This class wraps a model of a fault geometry and * a list of magnitude frequency distributions that characterize how the fault * might rupture (e.g. as one, single geometry-filling event, or as multiple * smaller events) during earthquakes. Smaller events are modeled as 'floating' * ruptures; they occur in multiple locations on the fault surface with * appropriately scaled rates. * * <p>A {@code FaultSource} cannot be created directly; it may only be created * by a private parser. * * @author Peter Powers */ public class FaultSource implements Source { final String name; final int id; final LocationList trace; final double dip; final double width; final double rake; final List<IncrementalMfd> mfds; final double spacing; final RuptureScaling rupScaling; final RuptureFloating rupFloating; final boolean rupVariability; final GriddedSurface surface; private final List<List<Rupture>> ruptureLists; // 1:1 with Mfds // package privacy for subduction subclass FaultSource( String name, int id, LocationList trace, double dip, double width, GriddedSurface surface, double rake, List<IncrementalMfd> mfds, double spacing, RuptureScaling rupScaling, RuptureFloating rupFloating, boolean rupVariability) { this.name = name; this.id = id; this.trace = trace; this.dip = dip; this.width = width; this.surface = surface; this.rake = rake; this.mfds = mfds; this.spacing = spacing; this.rupScaling = rupScaling; this.rupFloating = rupFloating; this.rupVariability = rupVariability; ruptureLists = initRuptureLists(); checkState(Iterables.size(Iterables.concat(ruptureLists)) > 0, "FaultSource has no ruptures"); } @Override public String name() { return name; } @Override public int size() { return Iterables.size(this); } @Override public int id() { return id; } @Override public SourceType type() { return FAULT; } /** * The closest point on the fault trace, relative to the supplied site * {@code Location}. */ @Override public Location location(Location site) { return Locations.closestPoint(site, trace); } @Override public List<XySequence> mfds() { /* * TODO this should just pass a reference to the immutable list or map (once * tracking logic-tree branches). Requires MFD implementation update */ ImmutableList.Builder<XySequence> xyMfds = ImmutableList.builder(); for (IncrementalMfd mfd : mfds) { xyMfds.add(XySequence.create(mfd.xValues(), mfd.yValues())); } return xyMfds.build(); } @Override public Iterator<Rupture> iterator() { return Iterables.concat(ruptureLists).iterator(); } @Override public String toString() { Map<Object, Object> data = ImmutableMap.builder() .put("name", name) .put("dip", dip) .put("width", width) .put("rake", rake) .put("mfds", mfds.size()) .put("top", trace.first().depth()) .build(); return getClass().getSimpleName() + " " + data; } private List<List<Rupture>> initRuptureLists() { ImmutableList.Builder<List<Rupture>> rupListsBuilder = ImmutableList.builder(); for (IncrementalMfd mfd : mfds) { List<Rupture> rupList = createRuptureList(mfd); checkState(rupList.size() > 0, "Rupture list is empty"); rupListsBuilder.add(rupList); } return rupListsBuilder.build(); } private List<Rupture> createRuptureList(IncrementalMfd mfd) { ImmutableList.Builder<Rupture> rupListbuilder = ImmutableList.builder(); for (int i = 0; i < mfd.getNum(); ++i) { double mag = mfd.getX(i); double rate = mfd.getY(i); // TODO do we really want to do this?? // TODO low rate shortcut should be derived from config // and applied when building input lists if (rate < 1e-14) { continue; // shortcut low rates } // TODO we want to get the 'floats' attribute out of MFDs // the only reason it is there is to allow SINGLE to flip-flop // it should just be a SourceProperty if (mfd.floats()) { // AbstractGriddedSurface surf = (AbstractGriddedSurface) // surface; // List<Rupture> floaters = rupFloating.createFloatingRuptures( surface, rupScaling, mag, rate, rake, rupVariability); rupListbuilder.addAll(floaters); } else { Rupture rup = Rupture.create(mag, rate, rake, surface); rupListbuilder.add(rup); } } return rupListbuilder.build(); } /* Single use builder */ static class Builder { private static final String ID = "FaultSource.Builder"; private boolean built = false; private static final Range<Double> SURFACE_GRID_SPACING_RANGE = Range.closed(0.01, 20.0); // required String name; Integer id; LocationList trace; Double dip; Double width; Double depth; Double rake; ImmutableList.Builder<IncrementalMfd> mfdsBuilder = ImmutableList.builder(); List<IncrementalMfd> mfds; Double spacing; RuptureScaling rupScaling; RuptureFloating rupFloating; Boolean rupVariability; Builder name(String name) { this.name = validateName(name); return this; } Builder id(int id) { this.id = id; return this; } Builder trace(LocationList trace) { this.trace = checkTrace(trace); return this; } Builder dip(double dip) { this.dip = checkDip(dip); return this; } Builder width(double width) { this.width = checkCrustalWidth(width); return this; } Builder depth(double depth) { this.depth = checkCrustalDepth(depth); return this; } Builder rake(double rake) { this.rake = checkRake(rake); return this; } Builder mfd(IncrementalMfd mfd) { this.mfdsBuilder.add(checkNotNull(mfd, "MFD is null")); return this; } Builder mfds(List<IncrementalMfd> mfds) { checkNotNull(mfds, "MFD list is null"); checkArgument(mfds.size() > 0, "MFD list is empty"); this.mfdsBuilder.addAll(mfds); return this; } Builder surfaceSpacing(double spacing) { this.spacing = checkInRange(SURFACE_GRID_SPACING_RANGE, "Floater Offset", spacing); return this; } Builder ruptureScaling(RuptureScaling rupScaling) { this.rupScaling = checkNotNull(rupScaling, "Rup-Scaling Relation is null"); return this; } Builder ruptureFloating(RuptureFloating rupFloating) { this.rupFloating = checkNotNull(rupFloating, "Rup-Floating Model is null"); return this; } Builder ruptureVariability(boolean rupVariability) { this.rupVariability = rupVariability; return this; } void validateState(String buildId) { checkState(!built, "This %s instance as already been used", buildId); checkState(name != null, "%s name not set", buildId); checkState(id != null, "%s id not set", buildId); checkState(trace != null, "%s trace not set", buildId); checkState(dip != null, "%s dip not set", buildId); checkState(width != null, "%s width not set", buildId); checkState(depth != null, "%s depth not set", buildId); checkState(rake != null, "%s rake not set", buildId); checkState(mfds.size() > 0, "%s has no MFDs", buildId); checkState(spacing != null, "%s surface grid spacing not set", buildId); checkState(rupScaling != null, "%s rupture-scaling relation not set", buildId); checkState(rupFloating != null, "%s rupture-floating model not set", buildId); checkState(rupVariability != null, "%s rupture-area variability flag not set", buildId); built = true; } FaultSource buildFaultSource() { mfds = mfdsBuilder.build(); validateState(ID); // create surface DefaultGriddedSurface surface = DefaultGriddedSurface.builder().trace(trace) .depth(depth).dip(dip).width(width).spacing(spacing).build(); return new FaultSource(name, id, trace, dip, width, surface, rake, ImmutableList.copyOf(mfds), spacing, rupScaling, rupFloating, rupVariability); } } }