package org.opensha2.eq.model;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.StandardSystemProperty.LINE_SEPARATOR;
import static org.opensha2.eq.model.SourceType.CLUSTER;
import org.opensha2.data.XySequence;
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 java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Cluster source representation. Each cluster source wraps a
* {@code FaultSourceSet} containing one or more fault representations that
* rupture as independent events but with a similar rate. For example, at New
* Madrid, each ClusterSourceSet has 5 ClusterSources, one for each position
* variant of the model. For each position variant there is one FaultSourceSet
* containing the FaultSources in the cluster, each of which may have one, or
* more, magnitude or other variants represented by its internal list of
* {@code IncrementalMfd}s.
*
* <p>Cluster source hazard is calculated from the joint probabilities of ground
* motions from the wrapped faults, which is handled internally by a separate
* calculator and {@link ClusterSource#iterator()} therefore throws an
* {@code UnsupportedOperationException}.
*
* <p>Unlike other {@code Source}s whose weights are carried exclusively with
* their associated {@link IncrementalMfd}, {@code ClusterSource}s carry an
* additional {@link #weight()} value.
*
* <p>A {@code ClusterSource} cannot be created directly; it may only be created
* by a private parser.
*
* @author Peter Powers
*/
public class ClusterSource implements Source {
final double rate; // from the default mfd xml
final FaultSourceSet faults;
ClusterSource(double rate, FaultSourceSet faults) {
this.rate = rate;
this.faults = faults;
}
@Override
public String name() {
return faults.name();
}
@Override
public int size() {
return faults.size();
}
@Override
public int id() {
return faults.id();
}
@Override
public SourceType type() {
return CLUSTER;
}
/**
* The closest point across the traces of all fault sources that participate
* in this cluster, relative to the supplied site {@code Location}.
*/
@Override
public Location location(Location site) {
LocationList.Builder locs = LocationList.builder();
for (FaultSource fault : faults) {
locs.add(fault.location(site));
}
return Locations.closestPoint(site, locs.build());
}
@Override
public List<XySequence> mfds() {
ImmutableList.Builder<XySequence> xyMfds = ImmutableList.builder();
for (FaultSource fault : faults) {
for (XySequence mfd : fault.mfds()) {
mfd.multiply(rate);
xyMfds.add(mfd);
}
}
return xyMfds.build();
}
/**
* {@code (1 / return period)} of this source in years.
* @return the cluster rate
*/
public double rate() {
return rate;
}
/**
* The weight applicable to this {@code ClusterSource}.
*/
public double weight() {
return faults.weight();
}
/**
* The {@code FaultSourceSet} of all {@code FaultSource}s that participate in
* this cluster.
*/
public FaultSourceSet faults() {
return faults;
}
/**
* Overriden to throw an {@code UnsupportedOperationException}. Cluster
* sources are handled differently than other source types.
*/
@Override
public Iterator<Rupture> iterator() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
Map<Object, Object> data = ImmutableMap.builder()
.put("name", name())
.put("rate", rate())
.put("weight", weight())
.build();
StringBuilder sb = new StringBuilder()
.append(getClass().getSimpleName())
.append(" ")
.append(data)
.append(LINE_SEPARATOR.value());
for (FaultSource fs : faults) {
sb.append(" ")
.append(fs.toString())
.append(LINE_SEPARATOR.value());
}
return sb.toString();
}
/* Single use builder */
static class Builder {
static final String ID = "ClusterSource.Builder";
boolean built = false;
Double rate;
FaultSourceSet faults;
Builder rate(double rate) {
// TODO what sort of value checking should be done for rate (<1 ??)
this.rate = rate;
return this;
}
Builder faults(FaultSourceSet faults) {
checkState(checkNotNull(faults, "Fault source set is null").size() > 0,
"Fault source set is empty");
this.faults = faults;
return this;
}
void validateState(String source) {
checkState(!built, "This %s instance as already been used", source);
checkState(rate != null, "%s rate not set", source);
checkState(faults != null, "%s has no fault sources", source);
built = true;
}
ClusterSource buildClusterSource() {
validateState(ID);
return new ClusterSource(rate, faults);
}
}
}