Skip to content
Snippets Groups Projects
Commit eeb9ff60 authored by Powers, Peter M.'s avatar Powers, Peter M.
Browse files

Merge branch 'model-mining' into 'main'

Model mining

See merge request !195
parents d535d4a3 4ccf0508
No related branches found
No related tags found
1 merge request!195Model mining
Pipeline #74942 passed
Showing
with 291 additions and 118 deletions
...@@ -14,6 +14,7 @@ import gov.usgs.earthquake.nshmp.geo.Location; ...@@ -14,6 +14,7 @@ import gov.usgs.earthquake.nshmp.geo.Location;
import gov.usgs.earthquake.nshmp.geo.LocationList; import gov.usgs.earthquake.nshmp.geo.LocationList;
import gov.usgs.earthquake.nshmp.geo.Locations; import gov.usgs.earthquake.nshmp.geo.Locations;
import gov.usgs.earthquake.nshmp.mfd.Mfd; import gov.usgs.earthquake.nshmp.mfd.Mfd;
import gov.usgs.earthquake.nshmp.tree.LogicTree;
/** /**
* Cluster source representation. Each cluster source wraps a * Cluster source representation. Each cluster source wraps a
...@@ -72,6 +73,13 @@ public class ClusterRuptureSet implements RuptureSet { ...@@ -72,6 +73,13 @@ public class ClusterRuptureSet implements RuptureSet {
return FAULT_CLUSTER; return FAULT_CLUSTER;
} }
@Override
public LogicTree<Mfd> mfdTree() {
System.err.println("TODO Cluster MFD tree implementation");
// only returning first cluster source
return faultRuptureSets.get(0).mfdTree;
}
/** /**
* The closest point across the traces of all fault sources that participate * The closest point across the traces of all fault sources that participate
* in this cluster, relative to the supplied site {@code Location}. * in this cluster, relative to the supplied site {@code Location}.
......
...@@ -113,6 +113,11 @@ public class FaultRuptureSet implements RuptureSet { ...@@ -113,6 +113,11 @@ public class FaultRuptureSet implements RuptureSet {
return FAULT; return FAULT;
} }
@Override
public LogicTree<Mfd> mfdTree() {
return mfdTree;
}
/** /**
* The closest point on the fault trace, relative to the supplied site * The closest point on the fault trace, relative to the supplied site
* {@code Location}. * {@code Location}.
......
...@@ -27,6 +27,7 @@ class GridRuptureSet implements RuptureSet { ...@@ -27,6 +27,7 @@ class GridRuptureSet implements RuptureSet {
private final List<Location> locations; private final List<Location> locations;
private final List<Mfd> mfds; private final List<Mfd> mfds;
private final LogicTree<List<Mfd>> mfdsTree; private final LogicTree<List<Mfd>> mfdsTree;
private final LogicTree<Mfd> mfdTree;
private final Optional<List<Map<FocalMech, Double>>> focalMechMaps; private final Optional<List<Map<FocalMech, Double>>> focalMechMaps;
private GridSourceSet gss; private GridSourceSet gss;
...@@ -44,6 +45,7 @@ class GridRuptureSet implements RuptureSet { ...@@ -44,6 +45,7 @@ class GridRuptureSet implements RuptureSet {
this.locations = builder.locations; this.locations = builder.locations;
this.mfds = builder.mfds; this.mfds = builder.mfds;
this.mfdsTree = builder.mfdsTree; this.mfdsTree = builder.mfdsTree;
this.mfdTree = builder.mfdTree;
this.focalMechMaps = builder.focalMechMaps; this.focalMechMaps = builder.focalMechMaps;
} }
...@@ -105,6 +107,11 @@ class GridRuptureSet implements RuptureSet { ...@@ -105,6 +107,11 @@ class GridRuptureSet implements RuptureSet {
return SourceType.GRID; return SourceType.GRID;
} }
@Override
public LogicTree<Mfd> mfdTree() {
return mfdTree;
}
@Override @Override
public Location location(Location site) { public Location location(Location site) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
...@@ -127,6 +134,7 @@ class GridRuptureSet implements RuptureSet { ...@@ -127,6 +134,7 @@ class GridRuptureSet implements RuptureSet {
private List<Location> locations; private List<Location> locations;
private List<Mfd> mfds; private List<Mfd> mfds;
private LogicTree<List<Mfd>> mfdsTree; private LogicTree<List<Mfd>> mfdsTree;
private LogicTree<Mfd> mfdTree;
private Optional<List<Map<FocalMech, Double>>> focalMechMaps = Optional.empty(); private Optional<List<Map<FocalMech, Double>>> focalMechMaps = Optional.empty();
...@@ -161,6 +169,7 @@ class GridRuptureSet implements RuptureSet { ...@@ -161,6 +169,7 @@ class GridRuptureSet implements RuptureSet {
this.locations = locations; this.locations = locations;
this.mfds = mfds; this.mfds = mfds;
this.mfdsTree = mfdsTree; this.mfdsTree = mfdsTree;
this.mfdTree = Trees.reduceMfdListTree(mfdsTree);
this.focalMechMaps = Optional.ofNullable(focalMechMaps); this.focalMechMaps = Optional.ofNullable(focalMechMaps);
return this; return this;
} }
...@@ -175,6 +184,7 @@ class GridRuptureSet implements RuptureSet { ...@@ -175,6 +184,7 @@ class GridRuptureSet implements RuptureSet {
checkNotNull(locations, "%s locations", label); checkNotNull(locations, "%s locations", label);
checkNotNull(mfds, "%s total MFDs", label); checkNotNull(mfds, "%s total MFDs", label);
checkNotNull(mfdsTree, "%s MFDs logic tree", label); checkNotNull(mfdsTree, "%s MFDs logic tree", label);
checkNotNull(mfdTree, "%s collapsed MFD logic tree", label);
built = true; built = true;
} }
......
...@@ -3,6 +3,7 @@ package gov.usgs.earthquake.nshmp.model; ...@@ -3,6 +3,7 @@ package gov.usgs.earthquake.nshmp.model;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static gov.usgs.earthquake.nshmp.Text.NEWLINE; import static gov.usgs.earthquake.nshmp.Text.NEWLINE;
import static java.util.stream.Collectors.toUnmodifiableMap;
import java.awt.geom.Area; import java.awt.geom.Area;
import java.io.IOException; import java.io.IOException;
...@@ -14,7 +15,9 @@ import java.util.Iterator; ...@@ -14,7 +15,9 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import com.google.common.base.Strings; import com.google.common.base.Strings;
...@@ -63,6 +66,7 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -63,6 +66,7 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
private final SiteData siteData; private final SiteData siteData;
private final Multimap<TectonicSetting, SourceTree> treesMap; private final Multimap<TectonicSetting, SourceTree> treesMap;
private final Map<Integer, SourceTree> idMap;
private HazardModel(Builder builder) { private HazardModel(Builder builder) {
this.name = builder.info.name; this.name = builder.info.name;
...@@ -73,6 +77,7 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -73,6 +77,7 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
this.mapRegionMap = Maps.immutableEnumMap(builder.mapRegionMap); this.mapRegionMap = Maps.immutableEnumMap(builder.mapRegionMap);
this.siteData = builder.siteData; this.siteData = builder.siteData;
this.treesMap = builder.treesMap; this.treesMap = builder.treesMap;
this.idMap = builder.idMap;
} }
/** /**
...@@ -149,6 +154,16 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -149,6 +154,16 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
return settings; return settings;
} }
/**
* Return an {@code Optional} describing the source tree for the supplied ID
* or an empty {@code Optional} if the ID is not present in the model.
*
* @param id to get source tree for
*/
public Optional<SourceTree> tree(int id) {
return Optional.ofNullable(idMap.get(id));
}
/** /**
* The multimap of tectonic settings to source logic trees in this model. * The multimap of tectonic settings to source logic trees in this model.
*/ */
...@@ -218,6 +233,7 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -218,6 +233,7 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
private ImmutableListMultimap.Builder<TectonicSetting, SourceTree> treesMapBuilder; private ImmutableListMultimap.Builder<TectonicSetting, SourceTree> treesMapBuilder;
private Multimap<TectonicSetting, SourceTree> treesMap; private Multimap<TectonicSetting, SourceTree> treesMap;
private Map<Integer, SourceTree> idMap;
// TODO not used but need to be // TODO not used but need to be
private List<SourceTree> trees = new ArrayList<>(); private List<SourceTree> trees = new ArrayList<>();
...@@ -281,6 +297,10 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -281,6 +297,10 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
HazardModel build() { HazardModel build() {
sourceSetMap = sourceMapBuilder.build(); sourceSetMap = sourceMapBuilder.build();
treesMap = treesMapBuilder.build(); treesMap = treesMapBuilder.build();
idMap = treesMap.values().stream()
.collect(toUnmodifiableMap(
SourceTree::id,
Function.identity()));
validateState(ID); validateState(ID);
return new HazardModel(this); return new HazardModel(this);
} }
...@@ -320,9 +340,9 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -320,9 +340,9 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
/* SYSTEM: each leaf is a SystemSourceSet */ /* SYSTEM: each leaf is a SystemSourceSet */
if (tree.type() == SourceType.FAULT_SYSTEM) { if (tree.type() == SourceType.FAULT_SYSTEM) {
for (Leaf leaf : tree.branchMap().keySet()) { for (Leaf leaf : tree.branches().keySet()) {
RuptureSet rs = leaf.ruptureSet; RuptureSet rs = leaf.ruptureSet;
double branchWeight = tree.branchWeights().get(leaf); double branchWeight = tree.leaves().get(leaf);
SystemRuptureSet srs = (SystemRuptureSet) rs; SystemRuptureSet srs = (SystemRuptureSet) rs;
SystemSourceSet sss = srs.createSourceSet(branchWeight); SystemSourceSet sss = srs.createSourceSet(branchWeight);
addSourceSet(sss); addSourceSet(sss);
...@@ -343,14 +363,14 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -343,14 +363,14 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
.weight(1.0) .weight(1.0)
.gmms(tree.gmms()); .gmms(tree.gmms());
for (Leaf leaf : tree.branchMap().keySet()) { for (Leaf leaf : tree.branches().keySet()) {
RuptureSet rs = leaf.ruptureSet; RuptureSet rs = leaf.ruptureSet;
double branchWeight = tree.branchWeights().get(leaf); double branchWeight = tree.leaves().get(leaf);
if (leaf.ruptureSet.type() == SourceType.FAULT) { if (leaf.ruptureSet.type() == SourceType.FAULT) {
double leafWeight = tree.branchWeights().get(leaf); double leafWeight = tree.leaves().get(leaf);
FaultRuptureSet frs = (FaultRuptureSet) rs; FaultRuptureSet frs = (FaultRuptureSet) rs;
/* /*
...@@ -443,8 +463,8 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -443,8 +463,8 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
.weight(1.0) .weight(1.0)
.gmms(tree.gmms()); .gmms(tree.gmms());
for (Leaf leaf : tree.branchMap().keySet()) { for (Leaf leaf : tree.branches().keySet()) {
double leafWeight = tree.branchWeights().get(leaf); double leafWeight = tree.leaves().get(leaf);
InterfaceRuptureSet irs = (InterfaceRuptureSet) leaf.ruptureSet; InterfaceRuptureSet irs = (InterfaceRuptureSet) leaf.ruptureSet;
InterfaceSource is = interfaceRuptureSetToSource(irs, leafWeight); InterfaceSource is = interfaceRuptureSetToSource(irs, leafWeight);
builder.source(is, leafWeight); builder.source(is, leafWeight);
...@@ -466,24 +486,24 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>> ...@@ -466,24 +486,24 @@ public final class HazardModel implements Iterable<SourceSet<? extends Source>>
} }
private void zoneSourceSetsFromTree(SourceTree tree) { private void zoneSourceSetsFromTree(SourceTree tree) {
for (Leaf leaf : tree.branchMap().keySet()) { for (Leaf leaf : tree.branches().keySet()) {
double leafWeight = tree.branchWeights().get(leaf); double leafWeight = tree.leaves().get(leaf);
ZoneRuptureSet zrs = (ZoneRuptureSet) leaf.ruptureSet; ZoneRuptureSet zrs = (ZoneRuptureSet) leaf.ruptureSet;
addSourceSet(zrs.sourceSet(leafWeight)); addSourceSet(zrs.sourceSet(leafWeight));
} }
} }
private void slabSourceSetsFromTree(SourceTree tree) { private void slabSourceSetsFromTree(SourceTree tree) {
for (Leaf leaf : tree.branchMap().keySet()) { for (Leaf leaf : tree.branches().keySet()) {
double leafWeight = tree.branchWeights().get(leaf); double leafWeight = tree.leaves().get(leaf);
SlabRuptureSet srs = (SlabRuptureSet) leaf.ruptureSet; SlabRuptureSet srs = (SlabRuptureSet) leaf.ruptureSet;
addSourceSet(srs.sourceSet(leafWeight)); addSourceSet(srs.sourceSet(leafWeight));
} }
} }
private void gridSourceSetsFromTree(SourceTree tree) { private void gridSourceSetsFromTree(SourceTree tree) {
for (Leaf leaf : tree.branchMap().keySet()) { for (Leaf leaf : tree.branches().keySet()) {
double leafWeight = tree.branchWeights().get(leaf); double leafWeight = tree.leaves().get(leaf);
GridRuptureSet grs = (GridRuptureSet) leaf.ruptureSet; GridRuptureSet grs = (GridRuptureSet) leaf.ruptureSet;
addSourceSet(grs.sourceSet(leafWeight)); addSourceSet(grs.sourceSet(leafWeight));
} }
......
...@@ -76,6 +76,11 @@ public class InterfaceRuptureSet implements RuptureSet { ...@@ -76,6 +76,11 @@ public class InterfaceRuptureSet implements RuptureSet {
return INTERFACE; return INTERFACE;
} }
@Override
public LogicTree<Mfd> mfdTree() {
return mfdTree;
}
/** /**
* The closest point on the fault trace, relative to the supplied site * The closest point on the fault trace, relative to the supplied site
* {@code Location}. * {@code Location}.
......
...@@ -101,8 +101,8 @@ abstract class ModelLoader { ...@@ -101,8 +101,8 @@ abstract class ModelLoader {
*/ */
public static void main(String[] args) { public static void main(String[] args) {
// Path testModel = Paths.get("../nshm-conus-2018"); Path testModel = Paths.get("../nshm-conus");
Path testModel = Paths.get("../nshm-hawaii"); // Path testModel = Paths.get("../nshm-hawaii");
HazardModel model = ModelLoader.load(testModel); HazardModel model = ModelLoader.load(testModel);
System.out.println(); System.out.println();
System.out.println(model); System.out.println(model);
......
...@@ -8,11 +8,16 @@ import java.nio.file.Path; ...@@ -8,11 +8,16 @@ import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import gov.usgs.earthquake.nshmp.mfd.Mfd;
import gov.usgs.earthquake.nshmp.tree.Branch;
import gov.usgs.earthquake.nshmp.tree.LogicTree;
/** /**
* Factory class for querying source models. * Factory class for querying source models.
* *
...@@ -25,24 +30,54 @@ public class Models { ...@@ -25,24 +30,54 @@ public class Models {
.create(); .create();
public static void main(String[] args) { public static void main(String[] args) {
HazardModel model = ModelLoader.load(Path.of("../nshm-conus-2018")); HazardModel model = ModelLoader.load(Path.of("../nshm-conus"));
// HazardModel model = ModelLoader.load(Path.of("../nshm-hawaii")); // HazardModel model = ModelLoader.load(Path.of("../nshm-hawaii"));
List<?> trees = trees(model);
String out = GSON.toJson(trees); // JSON of all tree info for a model
System.out.println(out); System.out.println(GSON.toJson(trees(model)));
System.out.println();
// Cascadia interface tree
SourceTree tree = model.tree(3199).orElseThrow();
// see what the list of nodes for each branch looks like
// we don't use this but it's good for illustrating
// internal structure of a SourceTree
tree.nodes().stream().forEach(System.out::println);
System.out.println();
// Serialized form of Object returned by tree() in this class
System.out.println(GSON.toJson(tree(model, 3199)));
System.out.println();
// 2799 is huge wasatch tree
} }
/** /**
* Returns a serializable list of source logic tree groups. * Returns an object for JSON serialization with the name and ID of all source
* logic tree groups in the supplied model organized by tectonic setting and
* source type.
* *
* @param model to extract logic tree data from * @param model to extract logic tree data from
*/ */
public static List<?> trees(HazardModel model) { public static Object trees(HazardModel model) {
return model.trees().asMap().entrySet().stream() return model.trees().asMap().entrySet().stream()
.map(Models::toSettingGroup) .map(Models::toSettingGroup)
.collect(toList()); .collect(toList());
} }
/**
* Returns an object for JSON serialization the details of a logic tree in the
* supplied model.
*
* @param model to query
* @param id of desired source tree
* @throws NoSuchElementException if the id is not present in the model
*/
public static Object tree(HazardModel model, int id) {
return model.tree(id).map(Tree::new).orElseThrow();
}
private static SettingGroup toSettingGroup(Entry<TectonicSetting, Collection<SourceTree>> entry) { private static SettingGroup toSettingGroup(Entry<TectonicSetting, Collection<SourceTree>> entry) {
return new SettingGroup( return new SettingGroup(
entry.getKey(), entry.getKey(),
...@@ -54,7 +89,7 @@ public class Models { ...@@ -54,7 +89,7 @@ public class Models {
.collect(groupingBy( .collect(groupingBy(
SourceTree::type, SourceTree::type,
mapping( mapping(
Tree::new, TreeInfo::new,
Collectors.toList()))) Collectors.toList())))
.entrySet().stream() .entrySet().stream()
.map(e -> new SourceGroup(e.getKey(), e.getValue())) .map(e -> new SourceGroup(e.getKey(), e.getValue()))
...@@ -75,22 +110,73 @@ public class Models { ...@@ -75,22 +110,73 @@ public class Models {
static final class SourceGroup { static final class SourceGroup {
final SourceType type; final SourceType type;
final List<Tree> data; final List<TreeInfo> data;
SourceGroup(SourceType type, List<Tree> data) { SourceGroup(SourceType type, List<TreeInfo> data) {
this.type = type; this.type = type;
this.data = data; this.data = data;
} }
} }
static final class TreeInfo {
final int id;
final String name;
TreeInfo(SourceTree tree) {
this.id = tree.id();
this.name = tree.name();
}
}
static final class Tree { static final class Tree {
final int id; final int id;
final String name; final String name;
final TectonicSetting setting;
final SourceType type;
final List<SourceBranch> branches;
Tree(SourceTree tree) { Tree(SourceTree tree) {
this.id = tree.id(); this.id = tree.id();
this.name = tree.name(); this.name = tree.name();
this.setting = tree.setting();
this.type = tree.type();
this.branches = tree.branches().entrySet().stream()
.map(e -> new SourceBranch(
e.getValue().toString(),
tree.leaves().get(e.getKey()),
e.getKey().ruptureSet().mfdTree()))
.collect(toList());
}
}
static final class SourceBranch {
final String name;
final String path;
final double weight;
final List<MfdBranch> mfds;
SourceBranch(String path, double weight, LogicTree<Mfd> mfds) {
this.name = mfds.name();
this.path = path;
this.weight = weight;
this.mfds = mfds.stream()
.map(MfdBranch::new)
.collect(toList());
}
}
static final class MfdBranch {
final String id;
final double weight;
final Mfd mfd;
MfdBranch(Branch<Mfd> branch) {
this.id = branch.id();
this.weight = branch.weight();
this.mfd = branch.value();
} }
} }
} }
...@@ -2,6 +2,7 @@ package gov.usgs.earthquake.nshmp.model; ...@@ -2,6 +2,7 @@ package gov.usgs.earthquake.nshmp.model;
import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.geo.Location;
import gov.usgs.earthquake.nshmp.mfd.Mfd; import gov.usgs.earthquake.nshmp.mfd.Mfd;
import gov.usgs.earthquake.nshmp.tree.LogicTree;
/** /**
* A rupture set; usually some physical or pseudo-representation of a fault and * A rupture set; usually some physical or pseudo-representation of a fault and
...@@ -23,7 +24,7 @@ public interface RuptureSet { ...@@ -23,7 +24,7 @@ public interface RuptureSet {
int id(); int id();
/** /**
* The number of {@link Rupture}s in the rupture set. * The number of {@link Rupture}s in this rupture set.
*/ */
int size(); int size();
...@@ -32,6 +33,11 @@ public interface RuptureSet { ...@@ -32,6 +33,11 @@ public interface RuptureSet {
*/ */
SourceType type(); SourceType type();
/**
* The logic tree of MFDs in this rupture set.
*/
LogicTree<Mfd> mfdTree();
/** /**
* The {@code Location} of this source relative to the supplied {@code site} * The {@code Location} of this source relative to the supplied {@code site}
* location. The details of what this method returns are implementation * location. The details of what this method returns are implementation
......
...@@ -4,19 +4,22 @@ import static com.google.common.base.Preconditions.checkArgument; ...@@ -4,19 +4,22 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static gov.usgs.earthquake.nshmp.Text.checkName; import static gov.usgs.earthquake.nshmp.Text.checkName;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toUnmodifiableMap;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.graph.EndpointPair; import com.google.common.graph.EndpointPair;
import com.google.common.graph.Graphs; import com.google.common.graph.Graphs;
import com.google.common.graph.ImmutableNetwork;
import com.google.common.graph.MutableNetwork; import com.google.common.graph.MutableNetwork;
import com.google.common.graph.Network; import com.google.common.graph.Network;
import com.google.common.graph.NetworkBuilder; import com.google.common.graph.NetworkBuilder;
...@@ -36,35 +39,15 @@ import gov.usgs.earthquake.nshmp.tree.LogicTree; ...@@ -36,35 +39,15 @@ import gov.usgs.earthquake.nshmp.tree.LogicTree;
public class SourceTree { public class SourceTree {
/* /*
* Using optionals for now, but may want base class that handles all the
* branches with subclasses for different source model types: sections vs
* polygons vs grids
*
* Developer notes: * Developer notes:
* *
* Root --> Node --> Node --> Leaf * Root --> Node --> ... --> Node --> Leaf
* *
* We use Guava's graph library to track source model logic trees. The * We use Guava's graph library to track source model logic trees. The
* heirarchy of logic trees in a source model is mapped into a Network where * heirarchy of logic trees in a source model is mapped into a Network where
* nodes are connected with unique edges that are the branches of the * nodes are connected with unique edges that are the branches of the
* component logic trees. * component logic trees. The network is a directed tree and each node has
* * only one incident edge. Leaf weights are rounded to 8 decimal places.
* We tried using a regular Graph with Branches representing the nodes, but it
* was difficult to extract the node paths between root and Leaf endpoints.
*
* We need to really deal with the Node class, it's just connective tissue and
* supports rooot/trunk and leaf.
*
* With regards to sampling, we may want to consider holding a reference to
* the relevant logic tree at each node.
*
* The internal graph (Network) is a directed tree; each node has only one
* incident edge/node.
*
* Leaf weights are rounded to 6 decimal places.
*
* TODO enforce collection immutability; currently some branches are null
* preventing this; perhaps have a EmptyNode with no path
*/ */
private final String name; private final String name;
...@@ -73,10 +56,29 @@ public class SourceTree { ...@@ -73,10 +56,29 @@ public class SourceTree {
private final SourceType type; private final SourceType type;
private final GmmSet gmms; private final GmmSet gmms;
// TODO may not need to keep Network (should also be immutable if we do)
private final Network<Node, Branch<Path>> tree; private final Network<Node, Branch<Path>> tree;
private final Map<Leaf, List<Branch<Path>>> leafBranches; private final List<List<Node>> nodes;
private final Map<Leaf, Double> leafWeights; private final Map<Leaf, List<Branch<Path>>> branches;
private final Map<Leaf, Double> leaves;
SourceTree(Builder builder) {
this.name = builder.name;
this.id = builder.id;
this.setting = builder.setting;
this.type = builder.type;
this.gmms = builder.gmms;
this.tree = ImmutableNetwork.copyOf(builder.tree);
this.nodes = builder.nodes;
this.branches = builder.branches;
this.leaves = builder.leaves;
// List<String> branchStrs = branches.keySet().stream()
// .map(this::branchString)
// .sorted()
// .collect(toList());
// branchStrs.forEach(System.out::println);
}
/** The name of this tree. */ /** The name of this tree. */
public String name() { public String name() {
...@@ -103,28 +105,24 @@ public class SourceTree { ...@@ -103,28 +105,24 @@ public class SourceTree {
return gmms; return gmms;
} }
public Map<Leaf, List<Branch<Path>>> branchMap() { /** The leaf nodes of this tree mapped to their branches. */
return leafBranches; public Map<Leaf, List<Branch<Path>>> branches() {
return branches;
} }
public Map<Leaf, Double> branchWeights() { /** The leaf nodes of this tree mapped to the total branch weight. */
return leafWeights; public Map<Leaf, Double> leaves() {
return leaves;
} }
SourceTree(Builder builder) { /**
this.name = builder.name; * The list of {@code root --> node --> ... --> node --> leaf} node mappings
this.id = builder.id; * that reflects the internal structure of this tree.
this.setting = builder.setting; */
this.type = builder.type; public List<String> nodes() {
this.gmms = builder.gmms; return nodes.stream()
this.tree = builder.tree; .map(Object::toString)
this.leafBranches = builder.leafBranches; .collect(toList());
this.leafWeights = builder.leafWeights;
// TODO clean - we do need this kind of output though (verbose logging?)
// for (Leaf leaf : leafBranches.keySet()) {
// System.out.println(leafBranches.get(leaf) + " " + leafWeights.get(leaf));
// }
} }
static Builder builder() { static Builder builder() {
...@@ -147,11 +145,12 @@ public class SourceTree { ...@@ -147,11 +145,12 @@ public class SourceTree {
private GmmSet gmms; private GmmSet gmms;
private MutableNetwork<Node, Branch<Path>> tree; private MutableNetwork<Node, Branch<Path>> tree;
private List<Leaf> leaves = new ArrayList<>(); private List<Leaf> leafList = new ArrayList<>();
/* Created on build. */ /* Created on build. */
private Map<Leaf, List<Branch<Path>>> leafBranches; private List<List<Node>> nodes;
private Map<Leaf, Double> leafWeights; private Map<Leaf, List<Branch<Path>>> branches;
private Map<Leaf, Double> leaves;
private Builder() {} private Builder() {}
...@@ -222,13 +221,14 @@ public class SourceTree { ...@@ -222,13 +221,14 @@ public class SourceTree {
*/ */
Builder addLeaf(Branch<Path> parent, RuptureSet ruptureSet) { Builder addLeaf(Branch<Path> parent, RuptureSet ruptureSet) {
checkState(this.tree != null, "Tree root node is not set"); checkState(this.tree != null, "Tree root node is not set");
checkArgument(tree.edges().contains(parent), "Missing parent branch");
EndpointPair<Node> endpoints = tree.incidentNodes(parent); EndpointPair<Node> endpoints = tree.incidentNodes(parent);
Node target = endpoints.target(); Node target = endpoints.target();
Node source = endpoints.source(); Node source = endpoints.source();
tree.removeNode(target); tree.removeNode(target);
Leaf leaf = new Leaf(target.index, ruptureSet); // transfer assigned index Leaf leaf = new Leaf(target.index, ruptureSet); // transfer assigned index
tree.addEdge(source, leaf, parent); tree.addEdge(source, leaf, parent);
leaves.add(leaf); leafList.add(leaf);
return this; return this;
} }
...@@ -240,60 +240,65 @@ public class SourceTree { ...@@ -240,60 +240,65 @@ public class SourceTree {
checkNotNull(type); checkNotNull(type);
checkNotNull(gmms); checkNotNull(gmms);
checkState(tree.nodes().size() > 0, "Empty tree"); checkState(tree.nodes().size() > 0, "Empty tree");
List<Leaf> leafList = List.copyOf(leaves); // List<Leaf> leafList = List.copyOf(leaves);
checkState(leafList.size() > 0, "Leafless tree"); checkState(leafList.size() > 0, "Leafless tree");
leafBranches = branchLists(tree, leafList); buildBranchesAndNodes();
leafWeights = leafWeights(leafBranches); // leafBranches = branchLists(tree, leaves);
// leafWeights = leafWeights(branches);
buildLeafWeights();
checkState(!built, "Single use builder"); checkState(!built, "Single use builder");
built = true; built = true;
return new SourceTree(this); return new SourceTree(this);
} }
}
/* /*
* On build: Generate lists of logic tree branches (branch paths) from the * On build: Generate lists of logic tree branches (branch paths) from the
* root of a tree to each of the supplied leaf nodes. Because the tree is a * root of a tree to each of the supplied leaf nodes. Because the tree is a
* directed graph, traversing a transposed view starting with each leaf yields * directed graph, traversing a transposed view starting with each leaf
* the required node path. * yields the required node path.
*/ */
private static Map<Leaf, List<Branch<Path>>> branchLists( private void buildBranchesAndNodes() {
Network<Node, Branch<Path>> tree,
List<Leaf> leaves) { Traverser<Node> traverser = Traverser.forTree(Graphs.transpose(tree));
Map<Leaf, List<Branch<Path>>> branchListsMap = new HashMap<>();
Traverser<Node> traverser = Traverser.forTree(Graphs.transpose(tree)); List<List<Node>> nodeLists = new ArrayList<>();
Map<Leaf, List<Branch<Path>>> branchListMap = new HashMap<>();
for (Leaf leaf : leaves) { for (Leaf leaf : leafList) {
List<Node> nodeList = Lists.newArrayList(traverser.depthFirstPostOrder(leaf)); List<Node> nodeList = Lists.newArrayList(traverser.depthFirstPostOrder(leaf));
checkState(nodeList.size() > 1); // 2 nodes minimum [root -> leaf] checkState(nodeList.size() > 1); // 2 nodes minimum [root -> leaf]
List<Branch<Path>> branchList = new ArrayList<>(); nodeLists.add(nodeList);
Node source = nodeList.get(0);
for (Node target : Iterables.skip(nodeList, 1)) { List<Branch<Path>> branchList = new ArrayList<>();
branchList.add(tree.edgeConnecting(source, target).orElseThrow()); Node source = nodeList.get(0);
source = target; for (Node target : Iterables.skip(nodeList, 1)) {
branchList.add(tree.edgeConnecting(source, target).orElseThrow());
source = target;
}
branchListsMap.put(leaf, List.copyOf(branchList));
} }
branchListMap.put(leaf, List.copyOf(branchList)); nodes = List.copyOf(nodeLists);
branches = Map.copyOf(branchListsMap);
} }
return Map.copyOf(branchListMap);
}
/* /*
* On build: Create map of leaves and their weights. Note that the values of * On build: Create map of leaves and their weights. Note that the values of
* the returned map will not sum to one when the source tree contains one or * the returned map will not sum to one when the source tree contains one or
* more LogicGroups. * more LogicGroups.
*/ */
private static Map<Leaf, Double> leafWeights(Map<Leaf, List<Branch<Path>>> branchLists) { private void buildLeafWeights() {
return branchLists.entrySet().stream() leaves = branches.entrySet().stream()
.collect(Collectors.toUnmodifiableMap( .collect(toUnmodifiableMap(
e -> e.getKey(), Entry::getKey,
e -> leafWeight(e.getValue()))); e -> leafWeight(e.getValue())));
} }
/* Compute cumulative weight of a source branch from root to leaf. */ /* Compute cumulative weight of a source branch from root to leaf. */
private static double leafWeight(List<Branch<Path>> branchList) { private static double leafWeight(List<Branch<Path>> branchList) {
double weight = branchList.stream() double weight = branchList.stream()
.mapToDouble(Branch::weight) .mapToDouble(Branch::weight)
.reduce(1, (a, b) -> a * b); .reduce(1, (a, b) -> a * b);
return Maths.round(weight, 8); return Maths.round(weight, 8);
}
} }
static class Node { static class Node {
......
...@@ -24,7 +24,9 @@ import gov.usgs.earthquake.nshmp.data.DelimitedData.Record; ...@@ -24,7 +24,9 @@ import gov.usgs.earthquake.nshmp.data.DelimitedData.Record;
import gov.usgs.earthquake.nshmp.fault.surface.DefaultGriddedSurface; import gov.usgs.earthquake.nshmp.fault.surface.DefaultGriddedSurface;
import gov.usgs.earthquake.nshmp.fault.surface.GriddedSurface; import gov.usgs.earthquake.nshmp.fault.surface.GriddedSurface;
import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.geo.Location;
import gov.usgs.earthquake.nshmp.mfd.Mfd;
import gov.usgs.earthquake.nshmp.model.SourceFeature.SystemSection; import gov.usgs.earthquake.nshmp.model.SourceFeature.SystemSection;
import gov.usgs.earthquake.nshmp.tree.LogicTree;
/** /**
* Crustal fault-system rupture set. * Crustal fault-system rupture set.
...@@ -172,6 +174,11 @@ public class SystemRuptureSet implements RuptureSet { ...@@ -172,6 +174,11 @@ public class SystemRuptureSet implements RuptureSet {
return FAULT_SYSTEM; return FAULT_SYSTEM;
} }
@Override
public LogicTree<Mfd> mfdTree() {
throw new UnsupportedOperationException();
}
/** /**
* The closest point on the fault trace, relative to the supplied site * The closest point on the fault trace, relative to the supplied site
* {@code Location}. * {@code Location}.
......
...@@ -203,6 +203,18 @@ class Trees { ...@@ -203,6 +203,18 @@ class Trees {
return Mfds.combine(scaledMfdList(tree)); return Mfds.combine(scaledMfdList(tree));
} }
static LogicTree<Mfd> reduceMfdListTree(LogicTree<List<Mfd>> mfdsTree) {
// TODO what is metadata for combined, e.g. grid, mfd
// is a value sum of recomputed a; we really want a
// to be the value from the rate tree
LogicTree.Builder<Mfd> mfdTree = LogicTree.builder(mfdsTree.name());
mfdsTree.forEach(branch -> mfdTree.addBranch(
branch.id(),
Mfds.combine(branch.value()),
branch.weight()));
return mfdTree.build();
}
/* LogicTree<MFD> --> List<MFD * branchWeight> */ /* LogicTree<MFD> --> List<MFD * branchWeight> */
static List<Mfd> scaledMfdList(LogicTree<Mfd> tree) { static List<Mfd> scaledMfdList(LogicTree<Mfd> tree) {
return tree.stream() return tree.stream()
......
...@@ -33,6 +33,7 @@ class ZoneRuptureSet implements RuptureSet { ...@@ -33,6 +33,7 @@ class ZoneRuptureSet implements RuptureSet {
private final List<Location> locations; private final List<Location> locations;
private final List<Mfd> mfds; private final List<Mfd> mfds;
private final LogicTree<List<Mfd>> mfdsTree; private final LogicTree<List<Mfd>> mfdsTree;
private final LogicTree<Mfd> mfdTree;
private GridSourceSet gss; private GridSourceSet gss;
...@@ -49,6 +50,7 @@ class ZoneRuptureSet implements RuptureSet { ...@@ -49,6 +50,7 @@ class ZoneRuptureSet implements RuptureSet {
this.data = builder.data; this.data = builder.data;
this.locations = builder.locations; this.locations = builder.locations;
this.mfdsTree = builder.mfdsTree; this.mfdsTree = builder.mfdsTree;
this.mfdTree = builder.mfdTree;
this.mfds = builder.mfds; this.mfds = builder.mfds;
} }
...@@ -100,6 +102,11 @@ class ZoneRuptureSet implements RuptureSet { ...@@ -100,6 +102,11 @@ class ZoneRuptureSet implements RuptureSet {
return SourceType.ZONE; return SourceType.ZONE;
} }
@Override
public LogicTree<Mfd> mfdTree() {
throw new UnsupportedOperationException();
}
@Override @Override
public Location location(Location site) { public Location location(Location site) {
// TODO is this called a lot and should be precomputed?? // TODO is this called a lot and should be precomputed??
...@@ -123,6 +130,7 @@ class ZoneRuptureSet implements RuptureSet { ...@@ -123,6 +130,7 @@ class ZoneRuptureSet implements RuptureSet {
private List<Location> locations; private List<Location> locations;
private LogicTree<List<Mfd>> mfdsTree; private LogicTree<List<Mfd>> mfdsTree;
private LogicTree<Mfd> mfdTree;
private List<Mfd> mfds; private List<Mfd> mfds;
private LogicTree<double[]> ratesTree; private LogicTree<double[]> ratesTree;
...@@ -168,6 +176,7 @@ class ZoneRuptureSet implements RuptureSet { ...@@ -168,6 +176,7 @@ class ZoneRuptureSet implements RuptureSet {
initLocationAndRateLists(); initLocationAndRateLists();
mfds = createTotalMfds(); mfds = createTotalMfds();
mfdsTree = createSingleMfdsTree(); mfdsTree = createSingleMfdsTree();
mfdTree = Trees.reduceMfdListTree(mfdsTree);
} }
private void initLocationAndRateLists() { private void initLocationAndRateLists() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment