package org.opensha2.eq.fault.surface;
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.checkInterfaceWidth;
import static org.opensha2.eq.fault.Faults.checkDip;
import static org.opensha2.eq.fault.Faults.checkStrike;
import static org.opensha2.eq.fault.Faults.checkTrace;
import org.opensha2.eq.fault.Faults;
import org.opensha2.geo.LocationGrid;
import org.opensha2.geo.LocationList;
import org.opensha2.util.Maths;
import com.google.common.collect.Range;
/**
* Add comments here
*
* @author Peter Powers
*/
class GriddedFaultSurface {
private final LocationGrid grid;
// TODO not sure these are really needed once surface is constructed
private final double strikeSpacing;
private final double dipSpacing;
private GriddedFaultSurface(LocationGrid grid,
double strikeSpacing,
double dipSpacing) {
this.grid = grid;
this.strikeSpacing = strikeSpacing;
this.dipSpacing = dipSpacing;
}
// public static GriddedFaultSurface create()
/*
* Document that width, whether computed from top-bottom or assigned, is
* always then applied in the dip direction, either supplied or computed from
* trace.
*/
public Builder builder() {
return new Builder();
}
/*
* TODO document builder which will almost certainly be part of a public API
*
* TODO doc trace assumed to be at depth=0?
*
* TODO do trace depths all need to be the same; condidtion used to be imposed
* in assertValidState
*
* TODO surface is initialized with a dip direction in radians; this may be
* normal to Faults.strike(trace), but may not be; in any event, we do not
* want to recompute it internally.
*
* TODO single-use builder
*
* TODO right-hand-rule
*
* TODO should surface only be a single row if width < dipSpacing/2
*/
public static class Builder {
private static final Range<Double> SPACING_RANGE = Range.closed(0.01, 20.0);
private static final String ID = "GriddedFaultSurface.Builder";
private boolean built = false;
// required
private LocationList trace;
private Double dipRad;
private Double depth;
// conditional (either but not both)
private Double width;
private Double lowerDepth;
// optional - dipDir may not necessarily be normal to strike
private Double dipDirRad;
// optional with defualts
private double strikeSpacing = 1.0;
private double dipSpacing = 1.0;
private Builder() {}
public Builder trace(LocationList trace) {
this.trace = checkTrace(trace);
return this;
}
public Builder dip(double dip) {
this.dipRad = checkDip(dip) * Maths.TO_RAD;
return this;
}
public Builder dipDir(double dipDir) {
this.dipDirRad = checkStrike(dipDir) * Maths.TO_RAD;
return this;
}
public Builder depth(double depth) {
this.depth = checkCrustalDepth(depth);
return this;
}
public Builder lowerDepth(double lowerDepth) {
checkState(width == null, "Either lower depth or width may be set, but not both");
this.lowerDepth = checkCrustalDepth(lowerDepth);
return this;
}
public Builder width(double width) {
checkState(lowerDepth == null, "Either width or lower depth may be set, but not both");
// we don't know what the surface may be used to represent
// so we validate against the largest (interface) values
this.width = checkInterfaceWidth(width);
return this;
}
public Builder spacing(double spacing) {
return spacing(spacing, spacing);
}
public Builder spacing(double strike, double dip) {
this.strikeSpacing = checkInRange(SPACING_RANGE, "Strike Spacing", strike);
this.dipSpacing = checkInRange(SPACING_RANGE, "Dip Spacing", dip);
return this;
}
private void validateState(String id) {
checkState(!built, "This %s instance as already been used", id);
checkState(trace != null, "%s trace not set", id);
checkState(dipRad != null, "%s dip not set", id);
checkState(depth != null, "%s depth not set", id);
checkState((width != null) ^ (lowerDepth != null), "%s width or lowerDepth not set",
id);
if (lowerDepth != null && lowerDepth <= depth) {
throw new IllegalStateException("Lower depth is above upper depth");
}
built = true;
}
public DefaultGriddedSurface build() {
validateState(ID);
if (dipDirRad == null) {
dipDirRad = Faults.dipDirectionRad(trace);
}
if (width == null) {
width = (lowerDepth - depth) / Math.sin(dipRad);
}
return null;
// new DefaultGriddedSurface(trace, dipRad, dipDirRad, depth, width,
// strikeSpacing, dipSpacing);
}
}
}