diff --git a/README.md b/README.md
index 7ebecfc8f0dba69d54c8d3b252726d614a653c31..db3033806408b6a47ccdcaef562add15a9aac907 100644
--- a/README.md
+++ b/README.md
@@ -3,4 +3,4 @@ nshmp-haz
 
 USGS National Seismic Hazard Model Program Codes
 
-Information relevant to obtaining, building, and running the code may be found in the [wiki](https://github.com/usgs/nshmp-sha/wiki/).
+Information relevant to obtaining, building, and running the code may be found in the [wiki](https://github.com/usgs/nshmp-haz/wiki/).
diff --git a/src/org/opensha2/calc/Deaggregation.java b/src/org/opensha2/calc/Deaggregation.java
index dd5d8bfb2156e1840c68e243a1527654f1d12227..a3e7c0463598c6b4abf91db54673c8256f8828ad 100644
--- a/src/org/opensha2/calc/Deaggregation.java
+++ b/src/org/opensha2/calc/Deaggregation.java
@@ -462,8 +462,8 @@ public final class Deaggregation {
 		final String component;
 		final List<RmBin> data;
 		// final double sum;
-		final List<SourceTypeContribution> primarySourceSets;
-		final List<SourceContributionTmp> primarySources;
+		final List<Contributor> sources;
+		final List<SummaryElement> summary;
 
 		Exporter(Dataset data, String component) {
 			this.component = component;
@@ -492,16 +492,60 @@ public final class Deaggregation {
 			}
 			this.data = rmBins;
 
-			this.primarySourceSets = ImmutableList.of(
-				new SourceTypeContribution("California B-Faults CH", 28.5, -1, 5.0, 7.4, 0.4),
-				new SourceTypeContribution("California B-Faults GR", 22.0, -1, 6.2, 6.7, 0.15),
-				new SourceTypeContribution("CA Crustal Gridded", 15.0, -1, 7.0, 6.7, -0.2));
-
-			this.primarySources = ImmutableList.of(
-				new SourceContributionTmp("Puente Hills", 5.2, 521, 3.2, 7.6, 0.5, 160.1),
-				new SourceContributionTmp("Elysian Park", 4.0, 431, 5.6, 6.8, 0.7, 340.0),
-				new SourceContributionTmp("San Andreas (Mojave)", 1.2, 44, 32.1, 8.2, 1.5, 22.3));
-
+			this.sources = ImmutableList.of(
+				new Contributor(
+					"California B-Faults CH", Contributor.Type.MULTI, 28.5, -1,
+					5.0, 7.4, 0.4,
+					null, null, null),
+				new Contributor(
+					"California B-Faults GR", Contributor.Type.MULTI, 22.0, -1,
+					6.2, 6.7, 0.15,
+					null, null, null),
+				new Contributor(
+					"CA Crustal Gridded", Contributor.Type.MULTI, 15.0, -1,
+					7.0, 6.7, -0.2,
+					null, null, null),
+				new Contributor(
+					"Puente Hills", Contributor.Type.SINGLE, 5.2, 521,
+					3.2, 7.6, 0.5,
+					160.1, 33.5, -118.5),
+				new Contributor(
+					"Elysian Park", Contributor.Type.SINGLE, 4.0, 431,
+					5.6, 6.8, 0.7,
+					340.0, 33.6, -118.4),
+				new Contributor(
+					"San Andreas (Mojave)", Contributor.Type.SINGLE, 1.2, 44,
+					32.1, 8.2, 1.5,
+					22.3, 34.0, -117.5),
+				new Contributor(
+					"Grid Source ", Contributor.Type.GRID, 7.4, null,
+					22.5, 6.2, -1.2,
+					345.0, 33.7, -118.6));
+
+			this.summary = ImmutableList.of(
+				element("Deaggregation targets", true, ImmutableList.of(
+					item("Return period", 2475, "yrs"),
+					item("Exceedance rate", 4.0404e-4, "yrs⁻¹"),
+					item("Exceedance IML", 0.6085, "g"))),
+				element("Recovered targets", false, ImmutableList.of(
+					item("Return period", 2521, "yrs"),
+					item("Exceedance rate", 3.9315e-4, "yrs⁻¹"),
+					item("Exceedance IML", 0.6085, "g"))),
+				element("Mean", true, ImmutableList.of(
+					item("r", 11.2, "km"),
+					item("m", 6.98, null),
+					item("ε₀", 0.34, null))),
+				element("Mode (largest r-m bin)", true, ImmutableList.of(
+					item("r", 9.4, "km"),
+					item("m", 6.78, null),
+					item("ε₀", 0.79, null),
+					item("Contribution", 27.3, "%"))),
+				element("Mode (largest ε₀ bin)", true, ImmutableList.of(
+					item("r", 9.4, "km"),
+					item("m", 6.78, null),
+					item("ε interval", 0.5, "σ"), // report middle of bin
+					item("Contribution", 15.2, "%")))
+				);
 		}
 
 		/* Distance-magnitude bin container. */
@@ -838,8 +882,12 @@ public final class Deaggregation {
 
 	}
 
-	/* Serialization helpers */
+	/* Serialization helpers. */
 
+	/**
+	 * Returns a list of objects that define the ε bins used in this
+	 * deaggregation when serialized to JSON.
+	 */
 	public List<?> εBins() {
 		ImmutableList.Builder<εBin> bins = ImmutableList.builder();
 		List<Double> εs = model.rmε.levels();
@@ -851,6 +899,7 @@ public final class Deaggregation {
 		return bins.build();
 	}
 
+	@SuppressWarnings("unused")
 	private static class εBin {
 		final int id;
 		final Double min;
@@ -863,44 +912,95 @@ public final class Deaggregation {
 		}
 	}
 
-	static class SourceTypeContribution {
-		String name;
-		double contribution;
-		int id;
-		double rBar;
-		double mBar;
-		double εBar;
+	/* Primitive object fields may be null. */
+	@SuppressWarnings("unused")
+	private static class Contributor {
+
+		final String name;
+		final Type type;
+		final double contribution;
+		final Integer id;
+		final double r;
+		final double m;
+		final double ε;
+		final Double azimuth;
+		final Double latitude;
+		final Double longitude;
+
+		Contributor(
+				String name,
+				Type type,
+				double contribution,
+				Integer id,
+				double r,
+				double m,
+				double ε,
+				Double azimuth,
+				Double latitude,
+				Double longitude) {
 
-		SourceTypeContribution(String name, double contribution, int id, double rBar, double mBar,
-				double εBar) {
 			this.name = name;
+			this.type = type;
 			this.contribution = contribution;
 			this.id = id;
-			this.mBar = mBar;
-			this.rBar = rBar;
-			this.εBar = εBar;
+			this.m = m;
+			this.r = r;
+			this.ε = ε;
+			this.azimuth = azimuth;
+			this.latitude = latitude;
+			this.longitude = longitude;
+		}
+
+		private static enum Type {
+			SINGLE,
+			MULTI,
+			GRID;
 		}
+
 	}
 
-	static class SourceContributionTmp {
-		String name;
-		double contribution;
-		int id;
-		double r;
-		double m;
-		double ε;
-		double azimuth;
+	private static SummaryElement element(
+			String name,
+			boolean display,
+			List<SummaryElement.Item> items) {
+		return new SummaryElement(name, display, items);
+	}
+
+	private static SummaryElement.Item item(
+			String name,
+			double value,
+			String units) {
+		return new SummaryElement.Item(name, value, units);
+	}
+
+	@SuppressWarnings("unused")
+	private static class SummaryElement {
+
+		final String name;
+		final boolean display;
+		final List<Item> data;
+
+		SummaryElement(
+				String name,
+				boolean display,
+				List<Item> data) {
 
-		SourceContributionTmp(String name, double contribution, int id, double r, double m,
-				double ε,
-				double azimuth) {
 			this.name = name;
-			this.contribution = contribution;
-			this.id = id;
-			this.m = m;
-			this.r = r;
-			this.ε = ε;
-			this.azimuth = azimuth;
+			this.display = display;
+			this.data = data;
+		}
+
+		static class Item {
+
+			final String name;
+			final double value;
+			final String units;
+
+			Item(String name, double value, String units) {
+				this.name = name;
+				this.value = value;
+				this.units = units;
+			}
 		}
 	}
 
diff --git a/src/org/opensha2/calc/GridCalc.java b/src/org/opensha2/calc/GridCalc.java
index f6df7ed0fda51d92a6ae307c9436a61dd18025d2..43fc537b9dad60b68019a54e599e2226b0bec2c0 100644
--- a/src/org/opensha2/calc/GridCalc.java
+++ b/src/org/opensha2/calc/GridCalc.java
@@ -25,6 +25,7 @@ import org.opensha2.geo.Locations;
 import org.opensha2.gmm.Gmm;
 import org.opensha2.gmm.GmmInput;
 import org.opensha2.gmm.GmmInput.Constraints;
+import org.opensha2.gmm.GmmInput.Field;
 import org.opensha2.gmm.GroundMotionModel;
 import org.opensha2.gmm.Idriss_2014;
 import org.opensha2.gmm.Imt;
@@ -309,7 +310,7 @@ public class GridCalc {
 	private static boolean isMultiMech(Set<Gmm> gmms) {
 		for (Gmm gmm : gmms) {
 			Constraints c = gmm.constraints();
-			if (c.dip.isPresent() || c.rake.isPresent()) return true;
+			if (c.get(Field.DIP).isPresent() || c.get(Field.RAKE).isPresent()) return true;
 		}
 		return false;
 	}
diff --git a/src/org/opensha2/calc/Site.java b/src/org/opensha2/calc/Site.java
index c0dfe7b45d919af2ac982f83ca13fdb5ef5407c8..06c0b679f462d5547e4ceee84c9f17414d09a57c 100644
--- a/src/org/opensha2/calc/Site.java
+++ b/src/org/opensha2/calc/Site.java
@@ -53,9 +53,14 @@ public class Site implements Named {
 	/** The name used for {@code Site}s with no supplied name. */
 	public static final String NO_NAME = "Unnamed Site";
 
-	private static final Range<Double> VS30_RANGE = Range.closed(MIN_VS_30, MAX_VS_30);
-	private static final Range<Double> Z2P5_RANGE = Range.closed(MIN_Z2P5, MAX_Z2P5);
-	private static final Range<Double> Z1P0_RANGE = Range.closed(MIN_Z1P0, MAX_Z1P0);
+	/** {@link #MIN_VS_30} and {@link #MAX_VS_30} as a closed {@code Range}. */
+	public static final Range<Double> VS30_RANGE = Range.closed(MIN_VS_30, MAX_VS_30);
+	
+	/** {@link #MIN_Z2P5} and {@link #MAX_Z2P5} as a closed {@code Range}. */
+	public static final Range<Double> Z2P5_RANGE = Range.closed(MIN_Z2P5, MAX_Z2P5);
+	
+	/** {@link #MIN_Z1P0} and {@link #MAX_Z1P0} as a closed {@code Range}. */
+	public static final Range<Double> Z1P0_RANGE = Range.closed(MIN_Z1P0, MAX_Z1P0);
 
 	enum Key {
 		NAME,
diff --git a/src/org/opensha2/eq/Magnitudes.java b/src/org/opensha2/eq/Magnitudes.java
index d9bfbede53b0b3578a380afd51f910f1e0d5bbba..a7e29453d09eda756a603c9047b6901426b1dd10 100644
--- a/src/org/opensha2/eq/Magnitudes.java
+++ b/src/org/opensha2/eq/Magnitudes.java
@@ -38,7 +38,8 @@ public final class Magnitudes {
 
 	// above is same as 3e11 dyn/cm^2
 
-	private static final Range<Double> magRange = Range.closed(MIN_MAG, MAX_MAG);
+	/** The minimum and maximum supported magnitude as a range.  */
+	public static final Range<Double> MAG_RANGE = Range.closed(MIN_MAG, MAX_MAG);
 	
 	// equivalent for dyn·cm conversion is 16.05
 	private static final double SCALE_N_M = 9.05;
@@ -54,7 +55,7 @@ public final class Magnitudes {
 	 * @see Data#checkInRange(Range, String, double)
 	 */
 	public static double checkMagnitude(double mag) {
-		return checkInRange(magRange, "Magnitude", mag);
+		return checkInRange(MAG_RANGE, "Magnitude", mag);
 	}
 
 	// TODO refactor N_m out of signature
diff --git a/src/org/opensha2/eq/model/Distance.java b/src/org/opensha2/eq/model/Distance.java
index e8a68244057f180396e556f510967799936cbd54..9df7bd244f56930594d710ec4d6701bf9c142ebf 100644
--- a/src/org/opensha2/eq/model/Distance.java
+++ b/src/org/opensha2/eq/model/Distance.java
@@ -26,6 +26,15 @@ import org.opensha2.geo.Regions;
  */
 public final class Distance {
 
+	/**
+	 * Maximum supported distance for PSHA calculations. Currently set to 1000 km, the maximum
+	 * known to be supported by across all implementated ground motion models.
+	 * 
+	 * TODO: ground motion models should be polled for this number
+	 */
+	@Deprecated
+	public static final double MAX = 1000.0;
+	
 	@SuppressWarnings("javadoc")
 	public enum Type {
 		R_JB,
diff --git a/src/org/opensha2/gmm/AbrahamsonEtAl_2014.java b/src/org/opensha2/gmm/AbrahamsonEtAl_2014.java
index 3515fb0a248eb14ccaed1730bc3d8e289854a702..efad92f70d9a3ad87c23395523d9b874ba2d43e2 100644
--- a/src/org/opensha2/gmm/AbrahamsonEtAl_2014.java
+++ b/src/org/opensha2/gmm/AbrahamsonEtAl_2014.java
@@ -44,7 +44,7 @@ public final class AbrahamsonEtAl_2014 implements GroundMotionModel {
 
 	static final String NAME = "Abrahamson, Silva & Kamai (2014)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(3.0, 8.5))
 			.setDistances(300.0)
 			.set(DIP,Faults.DIP_RANGE)
diff --git a/src/org/opensha2/gmm/AtkinsonBoore_2003.java b/src/org/opensha2/gmm/AtkinsonBoore_2003.java
index 50918e2f7973181328d9787e623dc224ebde2670..1a4c18bbffdfeec15d0e4c1fe4e13acd54a6717a 100644
--- a/src/org/opensha2/gmm/AtkinsonBoore_2003.java
+++ b/src/org/opensha2/gmm/AtkinsonBoore_2003.java
@@ -56,7 +56,7 @@ public abstract class AtkinsonBoore_2003 implements GroundMotionModel {
 	static final String NAME = "Atkinson & Boore (2003)";
 
 	// TODO will probably want to have constraints per-implementation
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(5.0, 9.5))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(ZTOP, Faults.SLAB_DEPTH_RANGE)
diff --git a/src/org/opensha2/gmm/AtkinsonBoore_2006.java b/src/org/opensha2/gmm/AtkinsonBoore_2006.java
index fcf812b772ac1ea11c12700fa12689f01aeaf30b..2e603af386d68423bd359fcfa4f586075214fb35 100644
--- a/src/org/opensha2/gmm/AtkinsonBoore_2006.java
+++ b/src/org/opensha2/gmm/AtkinsonBoore_2006.java
@@ -75,7 +75,7 @@ public abstract class AtkinsonBoore_2006 implements GroundMotionModel, ConvertsM
 
 	static final String NAME = "Atkinson & Boore (2006)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/AtkinsonBoore_2006p.java b/src/org/opensha2/gmm/AtkinsonBoore_2006p.java
index 8888ec8bf2d85d20653619808c412513f1196c6e..cc2fda7c71ea343abea64c341b33a29adcf3846f 100644
--- a/src/org/opensha2/gmm/AtkinsonBoore_2006p.java
+++ b/src/org/opensha2/gmm/AtkinsonBoore_2006p.java
@@ -48,7 +48,7 @@ public final class AtkinsonBoore_2006p implements GroundMotionModel {
 
 	static final String NAME = "Atkinson & Boore (2006): Prime";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/AtkinsonMacias_2009.java b/src/org/opensha2/gmm/AtkinsonMacias_2009.java
index 03d6af0924d166f8c33857c57fa33271e6523fa8..67f84545a14abbbeda5637aa2df751de5e5087dd 100644
--- a/src/org/opensha2/gmm/AtkinsonMacias_2009.java
+++ b/src/org/opensha2/gmm/AtkinsonMacias_2009.java
@@ -44,7 +44,7 @@ public final class AtkinsonMacias_2009 implements GroundMotionModel {
 
 	static final String NAME = "Atkinson & Macias (2009): Interface";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(5.0, 9.5))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(150.0, 1500.0))
diff --git a/src/org/opensha2/gmm/Atkinson_2008p.java b/src/org/opensha2/gmm/Atkinson_2008p.java
index 460cfbcbcf5fd0b215e2328d5aaf7ed3c6cb4138..f7b2a94df3bed77d86bd298d68f7bf1eab846707 100644
--- a/src/org/opensha2/gmm/Atkinson_2008p.java
+++ b/src/org/opensha2/gmm/Atkinson_2008p.java
@@ -48,7 +48,7 @@ public final class Atkinson_2008p implements GroundMotionModel {
 
 	static final String NAME = "Atkinson (2008) Prime";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RJB, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/BcHydro_2012.java b/src/org/opensha2/gmm/BcHydro_2012.java
index 032cc39bbf923a9f84e8b4df309dd9aea5e987e8..ed38121487e2b8c34ae698b16e4d32f0e7536358 100644
--- a/src/org/opensha2/gmm/BcHydro_2012.java
+++ b/src/org/opensha2/gmm/BcHydro_2012.java
@@ -53,7 +53,7 @@ public abstract class BcHydro_2012 implements GroundMotionModel {
 
 	// TODO will probably want to have constraints per-implementation (e.g. slab
 	// vs interface depth limits)
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(5.0, 9.5))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(ZTOP, Faults.SLAB_DEPTH_RANGE)
diff --git a/src/org/opensha2/gmm/BooreAtkinson_2008.java b/src/org/opensha2/gmm/BooreAtkinson_2008.java
index 82af70d2b2498efc52b79e14a9d0744756dcdf7f..7386aefbe8247f6b4313c3daa2781b14551b0321 100644
--- a/src/org/opensha2/gmm/BooreAtkinson_2008.java
+++ b/src/org/opensha2/gmm/BooreAtkinson_2008.java
@@ -50,7 +50,7 @@ public final class BooreAtkinson_2008 implements GroundMotionModel {
 
 	static final String NAME = "Boore & Atkinson (2008)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(5.0, 8.0))
 			.set(RJB, Range.closed(0.0, 200.0))
 			.set(DIP,Faults.DIP_RANGE)
diff --git a/src/org/opensha2/gmm/BooreEtAl_2014.java b/src/org/opensha2/gmm/BooreEtAl_2014.java
index abbad49d6e1ab57f74c64c9ebe76a030c13ee9d3..5029c1d3ff1327668eae123abaf2e1ce88cdfac7 100644
--- a/src/org/opensha2/gmm/BooreEtAl_2014.java
+++ b/src/org/opensha2/gmm/BooreEtAl_2014.java
@@ -52,7 +52,7 @@ public final class BooreEtAl_2014 implements GroundMotionModel {
 
 	static final String NAME = "Boore, Stewart, Seyhan & Atkinson (2014)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		// TODO normal faults technically only applicable to M7
 		.set(MAG, Range.closed(3.0, 8.5))
 		.set(RJB, Range.closed(0.0, 400.0))
diff --git a/src/org/opensha2/gmm/CampbellBozorgnia_2008.java b/src/org/opensha2/gmm/CampbellBozorgnia_2008.java
index cc5787c09791b7ea5068d72b54036e35199262a7..58f1191934aeeb3224f5f1505b8fa0351f6d3541 100644
--- a/src/org/opensha2/gmm/CampbellBozorgnia_2008.java
+++ b/src/org/opensha2/gmm/CampbellBozorgnia_2008.java
@@ -54,7 +54,7 @@ public final class CampbellBozorgnia_2008 implements GroundMotionModel {
 
 	static final String NAME = "Campbell & Bozorgnia (2008)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		// TODO there are rake dependent M restrictions
 		.set(MAG, Range.closed(4.0, 8.5))
 		.set(RJB, Range.closed(0.0, 300.0))
diff --git a/src/org/opensha2/gmm/CampbellBozorgnia_2014.java b/src/org/opensha2/gmm/CampbellBozorgnia_2014.java
index 98191196770201a0d1393cb2d6509888b28cf56f..70456679839e5e098089fcc72dd847ee1136d1f4 100644
--- a/src/org/opensha2/gmm/CampbellBozorgnia_2014.java
+++ b/src/org/opensha2/gmm/CampbellBozorgnia_2014.java
@@ -57,7 +57,7 @@ public final class CampbellBozorgnia_2014 implements GroundMotionModel {
 
 	static final CoefficientContainer COEFFS = new CoefficientContainer("CB14.csv");
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		// TODO there are rake dependent M restrictions
 		.set(MAG, Range.closed(3.3, 8.5))
 		.setDistances(300.0)
diff --git a/src/org/opensha2/gmm/Campbell_2003.java b/src/org/opensha2/gmm/Campbell_2003.java
index 6735d47222e58f2ee6c53781045bea3b56c5516c..7f23692cf3c99be9e0d5c5548e9ce98609ca329e 100644
--- a/src/org/opensha2/gmm/Campbell_2003.java
+++ b/src/org/opensha2/gmm/Campbell_2003.java
@@ -57,7 +57,7 @@ public class Campbell_2003 implements GroundMotionModel, ConvertsMag {
 
 	static final String NAME = "Campbell (2003)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/ChiouYoungs_2008.java b/src/org/opensha2/gmm/ChiouYoungs_2008.java
index 88f4407d0c076731e78d2118f9dfd19bcfcabf9c..9ef709b4978674c39494d49f4cda3c34fda95678 100644
--- a/src/org/opensha2/gmm/ChiouYoungs_2008.java
+++ b/src/org/opensha2/gmm/ChiouYoungs_2008.java
@@ -52,7 +52,7 @@ public final class ChiouYoungs_2008 implements GroundMotionModel {
 
 	static final String NAME = "Chiou & Youngs (2008)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(4.0, 8.5))
 			.setDistances(200.0)
 			.set(DIP, Faults.DIP_RANGE)
diff --git a/src/org/opensha2/gmm/ChiouYoungs_2014.java b/src/org/opensha2/gmm/ChiouYoungs_2014.java
index 3e5647d482def17f1415e08e831e895e72065446..a065e01fa123037c54a34b47d58d3a93679971f0 100644
--- a/src/org/opensha2/gmm/ChiouYoungs_2014.java
+++ b/src/org/opensha2/gmm/ChiouYoungs_2014.java
@@ -57,7 +57,7 @@ public final class ChiouYoungs_2014 implements GroundMotionModel {
 
 	static final String NAME = "Chiou & Youngs (2014)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(3.5, 8.5))
 		.setDistances(300.0)
 		.set(DIP, Faults.DIP_RANGE)
diff --git a/src/org/opensha2/gmm/FrankelEtAl_1996.java b/src/org/opensha2/gmm/FrankelEtAl_1996.java
index 7a99047845d24a76f2a541ec96a376289f1a146a..e4b16b12e8b191fd360fac68b7bed7c8ea71e03d 100644
--- a/src/org/opensha2/gmm/FrankelEtAl_1996.java
+++ b/src/org/opensha2/gmm/FrankelEtAl_1996.java
@@ -42,7 +42,7 @@ public class FrankelEtAl_1996 implements GroundMotionModel, ConvertsMag {
 
 	static final String NAME = "Frankel et al. (1996)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/Gmm.java b/src/org/opensha2/gmm/Gmm.java
index 636bc03043816f283dd121dc886da88653cb4b90..268f53f84a13b2cc179166fc724d779e0c3ca014 100644
--- a/src/org/opensha2/gmm/Gmm.java
+++ b/src/org/opensha2/gmm/Gmm.java
@@ -6,6 +6,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import java.lang.reflect.Constructor;
 import java.util.Collection;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -15,6 +16,7 @@ import org.opensha2.gmm.CeusMb.*;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.UncheckedExecutionException;
@@ -541,4 +543,108 @@ public enum Gmm {
 		return constraints;
 	}
 
+	@SuppressWarnings("javadoc")
+	public enum Group {
+
+		WUS_14_ACTIVE_CRUST(
+				"2014 Active Crust (WUS)",
+				ImmutableList.of(
+					ASK_14,
+					BSSA_14,
+					CB_14,
+					CY_14)),
+
+		CEUS_14_STABLE_CRUST(
+				"2014 Stable Crust (CEUS)",
+				ImmutableList.of(
+					ATKINSON_08_PRIME,
+					AB_06_PRIME,
+					CAMPBELL_03,
+					FRANKEL_96,
+					PEZESHK_11,
+					SILVA_02,
+					SOMERVILLE_01,
+					TP_05,
+					TORO_97_MW)),
+
+		WUS_14_INTERFACE(
+				"2014 Subduction Interface (WUS)",
+				ImmutableList.of(
+					AB_03_GLOB_INTER,
+					AM_09_INTER,
+					BCHYDRO_12_INTER,
+					ZHAO_06_INTER)),
+
+		WUS_14_SLAB(
+				"2014 WUS Subduction Intraslab (WUS)",
+				ImmutableList.of(
+					AB_03_CASC_SLAB_LOW_SAT,
+					AB_03_GLOB_SLAB_LOW_SAT,
+					BCHYDRO_12_SLAB,
+					ZHAO_06_SLAB)),
+
+		WUS_08_ACTIVE_CRUST(
+				"2008 Active Crust (WUS)",
+				ImmutableList.of(
+					BA_08,
+					CB_08,
+					CY_08)),
+
+		CEUS_08_STABLE_CRUST(
+				"2008 Stable Crust (CEUS)",
+				ImmutableList.of(
+					AB_06_140BAR,
+					AB_06_200BAR,
+					CAMPBELL_03,
+					FRANKEL_96,
+					SILVA_02,
+					SOMERVILLE_01,
+					TP_05,
+					TORO_97_MW)),
+
+		WUS_08_INTERFACE(
+				"2008 Subduction Interface (WUS)",
+				ImmutableList.of(
+					AB_03_GLOB_INTER,
+					YOUNGS_97_INTER,
+					ZHAO_06_INTER)),
+
+		WUS_08_SLAB(
+				"2008 Subduction Intraslab (WUS)",
+				ImmutableList.of(
+					AB_03_CASC_SLAB,
+					AB_03_GLOB_SLAB,
+					YOUNGS_97_SLAB)),
+
+		OTHER(
+				"Others",
+				ImmutableList.of(
+					AB_03_CASC_INTER,
+					MCVERRY_00_CRUSTAL,
+					MCVERRY_00_INTERFACE,
+					MCVERRY_00_SLAB,
+					MCVERRY_00_VOLCANIC,
+					SADIGH_97));
+
+		private final String name;
+		private final List<Gmm> gmms;
+
+		private Group(String name, List<Gmm> gmms) {
+			this.name = name;
+			this.gmms = gmms;
+		}
+		
+		@Override public String toString() {
+			return name;
+		}
+
+		/**
+		 * Return an immutable list of the Gmms in this group, sorted
+		 * alphabetically by their display name.
+		 */
+		public List<Gmm> gmms() {
+			return gmms;
+		}
+	}
+
 }
diff --git a/src/org/opensha2/gmm/GmmInput.java b/src/org/opensha2/gmm/GmmInput.java
index c0974aa514c0b55a1fef3a7ff112196690f03069..e127a38bc515072ce1a5b2810a13de4b6c80229d 100644
--- a/src/org/opensha2/gmm/GmmInput.java
+++ b/src/org/opensha2/gmm/GmmInput.java
@@ -26,6 +26,8 @@ import java.util.Map.Entry;
 import java.util.Set;
 
 import org.opensha2.calc.Site;
+import org.opensha2.eq.Magnitudes;
+import org.opensha2.eq.fault.Faults;
 import org.opensha2.eq.model.Distance;
 import org.opensha2.eq.model.Rupture;
 
@@ -173,11 +175,33 @@ public class GmmInput {
 
 		/**
 		 * Return a {@code Builder} prepopulated with default values. Builder
-		 * has the following presets: <ul><li>Mw: 6.5</li><li>rJB: 10.0
-		 * (km)</li><li>rRup: 10.3 (km)</li><li>rX: 10.0 (km)</li> <li>dip:
-		 * 90Ëš</li><li>width: 14.0 (km)</li><li>zTop: 0.5 (km)</li><li>zHyp: 7.5
-		 * (km)</li> <li>rake: 0Ëš</li><li>vs30: 760 (m/s)</li><li>vsInf:
-		 * true</li><li>z2p5: NaN</li><li>z1p0: NaN</li></ul>
+		 * has the following presets:
+		 * 
+		 * <ul><li>Mw: 6.5</li>
+		 * 
+		 * <li>rJB: 10.0 (km)</li>
+		 * 
+		 * <li>rRup: 10.3 (km)</li>
+		 * 
+		 * <li>rX: 10.0 (km)</li>
+		 * 
+		 * <li>dip: 90Ëš</li>
+		 * 
+		 * <li>width: 14.0 (km)</li>
+		 * 
+		 * <li>zTop: 0.5(km)</li>
+		 * 
+		 * <li>zHyp: 7.5 (km)</li>
+		 * 
+		 * <li>rake: 0Ëš</li>
+		 * 
+		 * <li>vs30: 760 (m/s)</li>
+		 * 
+		 * <li>vsInf: true</li>
+		 * 
+		 * <li>z2p5: NaN</li>
+		 * 
+		 * <li>z1p0: NaN</li></ul>
 		 */
 		public Builder withDefaults() {
 			Mw = MAG.defaultValue;
@@ -326,90 +350,95 @@ public class GmmInput {
 		MAG(
 				"Magnitude",
 				"The moment magnitude of an earthquake",
-				null,
+				Optional.<String> absent(),
 				6.5),
 
 		RJB(
 				"Joyner-Boore Distance",
 				"The shortest distance from a site to the surface projection of a rupture, in kilometers",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				10.0),
 
 		RRUP(
 				"Rupture Distance",
 				"The shortest distance from a site to a rupture, in kilometers",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				10.3),
 
 		RX(
 				"Distance X",
 				"The shortest distance from a site to the extended trace a fault, in kilometers",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				10.0),
 
 		DIP(
 				"Dip",
 				"The dip of a rupture surface, in degrees",
-				ANGLE_UNIT,
+				Optional.of(ANGLE_UNIT),
 				90.0),
 
 		WIDTH(
 				"Width",
 				"The width of a rupture surface, in kilometers",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				14.0),
 
 		ZTOP(
 				"Depth",
 				"The depth to the top of a rupture surface, in kilometers and positive-down",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				0.5),
 
 		ZHYP(
 				"Hypocentral Depth",
 				"The depth to the hypocenter on a rupture surface, in kilometers and positive-down",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				7.5),
 
 		RAKE(
 				"Rake",
 				"The rake (or sense of slip) of a rupture surface, in degrees",
-				ANGLE_UNIT,
+				Optional.of(ANGLE_UNIT),
 				0.0),
 
 		VS30(
 				"Vs30",
 				"The average shear-wave velocity down to 30 meters, in kilometers per second",
-				"km/s",
+				Optional.of("km/s"),
 				760.0),
 
 		VSINF(
 				"Vs30 Inferred",
 				"Whether Vs30 was measured or inferred",
-				null,
+				Optional.<String> absent(),
 				1.0),
 
 		Z1P0(
 				"Depth to Vs=1.0 km/s",
 				"Depth to a shear-wave velocity of 1.0 kilometers per second, in kilometers",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				NaN),
 
 		Z2P5(
 				"Depth to Vs=2.5 km/s",
 				"Depth to a shear-wave velocity of 2.5 kilometers per second, in kilometers",
-				DISTANCE_UNIT,
+				Optional.of(DISTANCE_UNIT),
 				NaN);
 
 		public final String label;
 		public final String info;
-		public final String unit;
+		public final Optional<String> units;
 		public final double defaultValue;
 
-		private Field(String label, String info, String unit, double defaultValue) {
+		private Field(
+				String label,
+				String info,
+				Optional<String> units,
+				double defaultValue) {
+			
 			this.label = label;
 			this.info = info;
-			this.unit = unit;
+			this.units = units;
 			this.defaultValue = defaultValue;
 		}
 
@@ -448,14 +477,6 @@ public class GmmInput {
 		return keyValueMap;
 	}
 
-	/**
-	 * Return a builder of {@code GmmInput} constraints. This builder sets all
-	 * fields to {@code Optional.absent()} by default.
-	 */
-	static Constraints.Builder constraintsBuilder() {
-		return new Constraints.Builder();
-	}
-
 	/**
 	 * The constraints associated with each {@code GmmInput} field. All methods
 	 * return an {@link Optional} whose {@link Optional#isPresent()} method will
@@ -467,23 +488,28 @@ public class GmmInput {
 		// TODO would moving to RangeSet be a satisfactory way
 		// to handle discrete value sets (using Range.singleton)
 
-		public final Optional<Range<Double>> mag;
-		public final Optional<Range<Double>> rJB;
-		public final Optional<Range<Double>> rRup;
-		public final Optional<Range<Double>> rX;
-		public final Optional<Range<Double>> dip;
-		public final Optional<Range<Double>> width;
-		public final Optional<Range<Double>> zTop;
-		public final Optional<Range<Double>> zHyp;
-		public final Optional<Range<Double>> rake;
-		public final Optional<Range<Double>> vs30;
-		public final Optional<Range<Boolean>> vsInf;
-		public final Optional<Range<Double>> z1p0;
-		public final Optional<Range<Double>> z2p5;
+		// TODO do we need these as type specific fields?
+
+		// TODO clean
+		// public final Optional<Range<Double>> mag;
+		// public final Optional<Range<Double>> rJB;
+		// public final Optional<Range<Double>> rRup;
+		// public final Optional<Range<Double>> rX;
+		// public final Optional<Range<Double>> dip;
+		// public final Optional<Range<Double>> width;
+		// public final Optional<Range<Double>> zTop;
+		// public final Optional<Range<Double>> zHyp;
+		// public final Optional<Range<Double>> rake;
+		// public final Optional<Range<Double>> vs30;
+		// public final Optional<Range<Boolean>> vsInf;
+		// public final Optional<Range<Double>> z1p0;
+		// public final Optional<Range<Double>> z2p5;
 
 		// for internal use only
 		private Map<Field, Optional<?>> constraintMap;
 
+		private Optional<Range<Double>> mag;
+		
 		private Constraints(
 				Optional<Range<Double>> mag,
 				Optional<Range<Double>> rJB,
@@ -499,49 +525,84 @@ public class GmmInput {
 				Optional<Range<Double>> z1p0,
 				Optional<Range<Double>> z2p5) {
 
+			
 			constraintMap = new EnumMap<>(Field.class);
 
-			this.mag = mag;
+			 this.mag = mag;
 			constraintMap.put(MAG, mag);
 
-			this.rJB = rJB;
+			// this.rJB = rJB;
 			constraintMap.put(RJB, rJB);
 
-			this.rRup = rRup;
+			// this.rRup = rRup;
 			constraintMap.put(RRUP, rRup);
 
-			this.rX = rX;
+			// this.rX = rX;
 			constraintMap.put(RX, rX);
 
-			this.dip = dip;
+			// this.dip = dip;
 			constraintMap.put(DIP, dip);
 
-			this.width = width;
+			// this.width = width;
 			constraintMap.put(WIDTH, width);
 
-			this.zTop = zTop;
+			// this.zTop = zTop;
 			constraintMap.put(ZTOP, zTop);
 
-			this.zHyp = zHyp;
+			// this.zHyp = zHyp;
 			constraintMap.put(ZHYP, zHyp);
 
-			this.rake = rake;
+			// this.rake = rake;
 			constraintMap.put(RAKE, rake);
 
-			this.vs30 = vs30;
+			// this.vs30 = vs30;
 			constraintMap.put(VS30, vs30);
 
-			this.vsInf = vsInf;
+			// this.vsInf = vsInf;
 			constraintMap.put(VSINF, vsInf);
 
-			this.z1p0 = z1p0;
+			// this.z1p0 = z1p0;
 			constraintMap.put(Z1P0, z1p0);
 
-			this.z2p5 = z2p5;
+			// this.z2p5 = z2p5;
 			constraintMap.put(Z2P5, z2p5);
 
 		}
 
+		public Optional<?> get(Field field) {
+			return constraintMap.get(field);
+		}
+
+//		@SuppressWarnings("unchecked")
+//		public Optional<Boolean> getBoolean(Field field) {
+//			checkArgument(field.clazz.equals(Boolean.class));
+//			// safe covariant cast
+//			return (Optional<Boolean>) constraintMap.get(field);
+//		}
+//
+//		@SuppressWarnings("unchecked")
+//		public Optional<Double> getDouble(Field field) {
+//			checkArgument(field.clazz.equals(Double.class));
+//			// safe covariant cast
+//			return (Optional<Double>) constraintMap.get(field);
+//		}
+
+		/**
+		 * Return a builder of {@code GmmInput} constraints. This builder sets
+		 * all fields to {@code Optional.absent()}.
+		 */
+		static Builder builder() {
+			return new Builder();
+		}
+
+		/**
+		 * 
+		 * @return
+		 */
+		public static Constraints defaults() {
+			return builder().withDefaults().build();
+		}
+
 		@Override public String toString() {
 			StringBuilder sb = new StringBuilder("Constraints: ").append(NEWLINE);
 			sb.append(HEADER).append(NEWLINE);
@@ -668,9 +729,45 @@ public class GmmInput {
 			 * Set all distance metrics [rJB, rRup, rX] to the range [0, r].
 			 */
 			Builder setDistances(double r) {
-				rJB = Optional.of(Range.closed(0.0, r));
-				rRup = Optional.of(Range.closed(0.0, r));
-				rX = Optional.of(Range.closed(0.0, r));
+				set(RJB, Range.closed(0.0, r));
+				set(RRUP, Range.closed(0.0, r));
+				set(RX, Range.closed(0.0, r));
+				return this;
+			}
+
+			Builder withDefaults() {
+
+				/*
+				 * TODO this should really be executed by polling all
+				 * implemented GMMs and unioning results; are also going to need
+				 * an intersection method to support returning the constraints
+				 * supported by subsets of gmms
+				 */
+
+				set(MAG, Magnitudes.MAG_RANGE);
+				set(RJB, Range.closed(0.0, Distance.MAX));
+				set(RRUP, Range.closed(0.0, Distance.MAX));
+				set(RX, Range.closed(0.0, Distance.MAX));
+
+				Range<Double> depthRange = Faults.CRUSTAL_DEPTH_RANGE
+					.intersection(Faults.INTERFACE_DEPTH_RANGE)
+					.intersection(Faults.SLAB_DEPTH_RANGE);
+				set(ZTOP, depthRange);
+				set(ZHYP, depthRange);
+
+				set(DIP, Faults.DIP_RANGE);
+
+				Range<Double> widthRange = Faults.CRUSTAL_WIDTH_RANGE
+					.intersection(Faults.INTERFACE_WIDTH_RANGE);
+				set(WIDTH, widthRange);
+
+				set(RAKE, Faults.RAKE_RANGE);
+
+				set(VS30, Site.VS30_RANGE);
+				set(VSINF);
+				set(Z1P0, Site.Z1P0_RANGE);
+				set(Z2P5, Site.Z2P5_RANGE);
+
 				return this;
 			}
 
diff --git a/src/org/opensha2/gmm/Idriss_2014.java b/src/org/opensha2/gmm/Idriss_2014.java
index 761bb0cdcbe275f623c7ea4c01025e0410801014..cbff527ce40f30267187a718fbcc3611890c400d 100644
--- a/src/org/opensha2/gmm/Idriss_2014.java
+++ b/src/org/opensha2/gmm/Idriss_2014.java
@@ -47,7 +47,7 @@ public final class Idriss_2014 implements GroundMotionModel {
 
 	static final String NAME = "Idriss (2014)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(5.0, 8.5))
 			.setDistances(150.0)
 			.set(DIP, Faults.DIP_RANGE)
diff --git a/src/org/opensha2/gmm/McVerryEtAl_2000.java b/src/org/opensha2/gmm/McVerryEtAl_2000.java
index 35eaff5f1a4c87ce6639d8a7fbc51dfbb37f6cb9..2e4043afcdc930ad4379ab1656319f5e42af7f27 100644
--- a/src/org/opensha2/gmm/McVerryEtAl_2000.java
+++ b/src/org/opensha2/gmm/McVerryEtAl_2000.java
@@ -78,7 +78,7 @@ public abstract class McVerryEtAl_2000 implements GroundMotionModel {
 	// TODO will probably want to have constraints per-implementation
 	// (e.g. zHyp only used by subduction)
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RRUP, Range.closed(0.0, 200.0))
 		.set(ZHYP, Range.closed(0.0, 20.0))
diff --git a/src/org/opensha2/gmm/PezeshkEtAl_2011.java b/src/org/opensha2/gmm/PezeshkEtAl_2011.java
index bc33891ca576dd5ae70f18cd66fa57dedfdf69ee..c2684b68ebdcbc126c285615c43e8b8edb3601a3 100644
--- a/src/org/opensha2/gmm/PezeshkEtAl_2011.java
+++ b/src/org/opensha2/gmm/PezeshkEtAl_2011.java
@@ -43,7 +43,7 @@ public final class PezeshkEtAl_2011 implements GroundMotionModel {
 
 	static final String NAME = "Pezeshk et al. (2011)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(4.0, 8.0))
 			.set(RRUP, Range.closed(0.0, 1000.0))
 			.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/SadighEtAl_1997.java b/src/org/opensha2/gmm/SadighEtAl_1997.java
index 39ed9ba83c18e62e1a3362b794f7fccee72e9710..4f592229e72981cfbffd7ea6bde82d758e8e142d 100644
--- a/src/org/opensha2/gmm/SadighEtAl_1997.java
+++ b/src/org/opensha2/gmm/SadighEtAl_1997.java
@@ -54,7 +54,7 @@ public class SadighEtAl_1997 implements GroundMotionModel {
 
 	static final String NAME = "Sadigh et al. (1997)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(5.0, 8.0))
 		.set(RRUP, Range.closed(0.0, 100.0))
 		.set(RAKE, Faults.RAKE_RANGE)
diff --git a/src/org/opensha2/gmm/SilvaEtAl_2002.java b/src/org/opensha2/gmm/SilvaEtAl_2002.java
index e84f5d29976e93a6c712368609690a3e9c6178af..09b84c778c083da2e70743bd6cf0ac8071654657 100644
--- a/src/org/opensha2/gmm/SilvaEtAl_2002.java
+++ b/src/org/opensha2/gmm/SilvaEtAl_2002.java
@@ -53,7 +53,7 @@ public class SilvaEtAl_2002 implements GroundMotionModel, ConvertsMag {
 
 	static final String NAME = "Silva et al. (2002)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RJB, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/SomervilleEtAl_2001.java b/src/org/opensha2/gmm/SomervilleEtAl_2001.java
index 4c0cd580bfc3045b9aacc802ebc5f7220025e47f..f4e57e6320f9dc27c2ebad382a9a3b82320514ef 100644
--- a/src/org/opensha2/gmm/SomervilleEtAl_2001.java
+++ b/src/org/opensha2/gmm/SomervilleEtAl_2001.java
@@ -45,7 +45,7 @@ public final class SomervilleEtAl_2001 implements GroundMotionModel {
 
 	static final String NAME = "Somerville et al. (2001)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(4.0, 8.0))
 			.set(RJB, Range.closed(0.0, 1000.0))
 			.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/TavakoliPezeshk_2005.java b/src/org/opensha2/gmm/TavakoliPezeshk_2005.java
index e3025554683471e7da1d0bf8abda02d45782a45c..b17474c18cd48d78c0371caf4d1599b5eb712cb7 100644
--- a/src/org/opensha2/gmm/TavakoliPezeshk_2005.java
+++ b/src/org/opensha2/gmm/TavakoliPezeshk_2005.java
@@ -70,7 +70,7 @@ public class TavakoliPezeshk_2005 implements GroundMotionModel, ConvertsMag {
 
 	static final String NAME = "Tavakoli & Pezeshk (2005)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RRUP, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/ToroEtAl_1997.java b/src/org/opensha2/gmm/ToroEtAl_1997.java
index 5cec83e9d580650e151ab317071ab071cd281abf..5d630abb2d67d26fc6063318dc8e25c2bdf3b2de 100644
--- a/src/org/opensha2/gmm/ToroEtAl_1997.java
+++ b/src/org/opensha2/gmm/ToroEtAl_1997.java
@@ -71,7 +71,7 @@ public abstract class ToroEtAl_1997 implements GroundMotionModel {
 
 	static final String NAME = "Toro et al. (1997)";
 
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 		.set(MAG, Range.closed(4.0, 8.0))
 		.set(RJB, Range.closed(0.0, 1000.0))
 		.set(VS30, Range.closed(760.0, 2000.0))
diff --git a/src/org/opensha2/gmm/YoungsEtAl_1997.java b/src/org/opensha2/gmm/YoungsEtAl_1997.java
index a346e79ccde2c390fbad97a3efbf75bc53657f10..f784b244b1e63716dee78baeacba467f58114e71 100644
--- a/src/org/opensha2/gmm/YoungsEtAl_1997.java
+++ b/src/org/opensha2/gmm/YoungsEtAl_1997.java
@@ -54,7 +54,7 @@ public abstract class YoungsEtAl_1997 implements GroundMotionModel {
 	static final String NAME = "Youngs et al. (1997)";
 
 	// TODO will probably want to have constraints per-implementation
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(5.0, 9.5))
 			.set(RRUP, Range.closed(0.0, 1000.0))
 			.set(ZTOP, Faults.SLAB_DEPTH_RANGE)
diff --git a/src/org/opensha2/gmm/ZhaoEtAl_2006.java b/src/org/opensha2/gmm/ZhaoEtAl_2006.java
index 0ca584646682532b4a3c1a544e255446ef81df51..06f3f149e51fd156b5cb74ceddc218c90a83ab4b 100644
--- a/src/org/opensha2/gmm/ZhaoEtAl_2006.java
+++ b/src/org/opensha2/gmm/ZhaoEtAl_2006.java
@@ -56,7 +56,7 @@ public abstract class ZhaoEtAl_2006 implements GroundMotionModel {
 	static final String NAME = "Zhao et al. (2006)";
 
 	// TODO will probably want to have constraints per-implementation
-	static final Constraints CONSTRAINTS = GmmInput.constraintsBuilder()
+	static final Constraints CONSTRAINTS = Constraints.builder()
 			.set(MAG, Range.closed(5.0, 9.5))
 			.set(RRUP, Range.closed(0.0, 1000.0))
 			.set(ZTOP, Faults.SLAB_DEPTH_RANGE)
diff --git a/test/org/opensha2/eq/model/peer/PeerTest.java b/test/org/opensha2/eq/model/peer/PeerTest.java
index e291e3985a652781b4ef0f770fa847087f2c3990..80198cd633dd241a18d7b4c43cfd2765bf7411ec 100644
--- a/test/org/opensha2/eq/model/peer/PeerTest.java
+++ b/test/org/opensha2/eq/model/peer/PeerTest.java
@@ -82,6 +82,13 @@ public class PeerTest {
 	public static final String S2_C4B_F = "Set2-Case4b-fast";
 	public static final String S2_C5A = "Set2-Case5a";
 	public static final String S2_C5B = "Set2-Case5b";
+	
+	public static final String S3_C1A = "Set3-Case1a";
+	public static final String S3_C1B = "Set3-Case1a";
+	public static final String S3_C2 = "Set3-Case2";
+	public static final String S3_C3 = "Set3-Case3";
+	public static final String S3_C4 = "Set3-Case4";
+
 
 	// private static final Path PEER_DIR = Paths.get("etc", "peer");
 	private static final Path PEER_DIR = Paths.get("..", "nshmp-model-dev", "models", "PEER");