diff --git a/src/org/opensha2/calc/CalcConfig.java b/src/org/opensha2/calc/CalcConfig.java index c141fe59f94057581da26e7c2163378557905aff..f686df1225d7a48002c7f1522111fc57d6683d2a 100644 --- a/src/org/opensha2/calc/CalcConfig.java +++ b/src/org/opensha2/calc/CalcConfig.java @@ -32,48 +32,28 @@ import com.google.gson.GsonBuilder; /** * Calculation configuration. - * - * All config fields are immutable and all methods return immutable objects. - * * @author Peter Powers */ public final class CalcConfig { - /** - * Returns models of the intensity measure levels for each {@code Imt} - * adressed by this calculation. The {@code Map} returned by this method is - * an immutable {@code EnumMap}. - * - * @see Maps#immutableEnumMap(Map) - */ - - /** - * Returns models of the intensity measure levels for each {@code Imt} - * adressed by this calculation. The x-values in each sequence are in - * natural log space. The {@code Map} returned by this method is an - * immutable {@code EnumMap}. - * - * @see Maps#immutableEnumMap(Map) - */ - - // TODO revisit privatization, comments, and immutability - static final String FILE_NAME = "config.json"; - final Path resource; + private final Path resource; - final ExceedanceModel exceedanceModel; - final double truncationLevel; - final Set<Imt> imts; + private final ExceedanceModel exceedanceModel; + private final double truncationLevel; + private final Set<Imt> imts; private final double[] defaultImls; private final Map<Imt, double[]> customImls; - final DeaggData deagg; + private final boolean optimizeGrids; + private final boolean gmmUncertainty; + + private final DeaggData deagg; + private final SiteSet sites; - - final boolean optimizeGrids; - final Map<Imt, XySequence> modelCurves; - final Map<Imt, XySequence> logModelCurves; + private final Map<Imt, XySequence> modelCurves; + private final Map<Imt, XySequence> logModelCurves; private static final Gson GSON = new GsonBuilder() .registerTypeAdapter(Site.class, new Site.Deserializer()) @@ -87,9 +67,10 @@ public final class CalcConfig { Set<Imt> imts, double[] defaultImls, Map<Imt, double[]> customImls, + boolean optimizeGrids, + boolean gmmUncertainty, DeaggData deagg, SiteSet sites, - boolean optimizeGrids, Map<Imt, XySequence> modelCurves, Map<Imt, XySequence> logModelCurves) { @@ -99,9 +80,10 @@ public final class CalcConfig { this.imts = imts; this.defaultImls = defaultImls; this.customImls = customImls; + this.optimizeGrids = optimizeGrids; + this.gmmUncertainty = gmmUncertainty; this.deagg = deagg; this.sites = sites; - this.optimizeGrids = optimizeGrids; this.modelCurves = modelCurves; this.logModelCurves = logModelCurves; } @@ -113,6 +95,8 @@ public final class CalcConfig { IMTS, DEFAULT_IMLS, CUSTOM_IMLS, + GMM_UNCERTAINTY, + OPTIMIZE_GRIDS, DEAGG, SITES; @@ -145,6 +129,8 @@ public final class CalcConfig { .append(format(Key.IMTS)).append(Parsing.enumsToString(imts, Imt.class)) .append(format(Key.DEFAULT_IMLS)).append(Arrays.toString(defaultImls)) .append(customImlStr) + .append(format(Key.OPTIMIZE_GRIDS)).append(optimizeGrids) + .append(format(Key.GMM_UNCERTAINTY)).append(gmmUncertainty) .append(format("Deaggregation R")) .append("min=").append(deagg.rMin).append(", ") .append("max=").append(deagg.rMax).append(", ") @@ -162,30 +148,83 @@ public final class CalcConfig { } /** - * Return an unmodifiable iterator over the {@code Site}s specified by this - * configuration. + * The probability distribution model to use when computing hazard curves. */ - public Iterable<Site> sites() { - return sites; + public ExceedanceModel exceedanceModel() { + return exceedanceModel; // TODO probabilitModel } /** - * Return the unmodifiable {@code Set} of IMTs for which calculations should - * be performed. + * The number of standard deviations at which to truncate a distribution. + * This field is ignored if a model does not implement truncation. + */ + public double truncationLevel() { + return truncationLevel; + } + + /** + * The unmodifiable {@code Set} of IMTs for which calculations should be + * performed. */ public Set<Imt> imts() { return imts; } /** - * Return an empty linear (i.e. not log) curve for the requested {@code Imt} - * . + * Whether to optimize grid source sets, or not. + */ + public boolean optimizeGrids() { + return optimizeGrids; + } + + /** + * Whether to consider additional ground motion model uncertainty, or not. + */ + public boolean gmmUncertainty() { + return gmmUncertainty; + } + + /** + * Deaggregation configuration data. + */ + public DeaggData deagg() { + return deagg; + } + + /** + * An unmodifiable iterator over the {@code Site}s at which hazard should be + * calculated. + */ + public Iterable<Site> sites() { + return sites; + } + + /** + * An empty linear curve for the requested {@code Imt}. * @param imt to get curve for */ public XySequence modelCurve(Imt imt) { return modelCurves.get(imt); } + /** + * An immutable map of model curves where x-values are in linear space. + */ + public Map<Imt, XySequence> modelCurves() { + return modelCurves; + } + + /** + * An immutable map of model curves where x-values are in natural-log space. + */ + public Map<Imt, XySequence> logModelCurves() { + return logModelCurves; + } + + /** + * Deaggregation configuration data container. + */ + @SuppressWarnings("javadoc") public static final class DeaggData { public final double rMin; @@ -213,7 +252,6 @@ public final class CalcConfig { εMax = 3.0; Δε = 0.5; } - } /** @@ -246,16 +284,22 @@ public final class CalcConfig { private static final String ID = "CalcConfig.Builder"; private boolean built = false; + // TODO should resource be Optional; if created with defaults + // there will be no path private Path resource; private ExceedanceModel exceedanceModel; private Double truncationLevel; private Set<Imt> imts; private double[] defaultImls; private Map<Imt, double[]> customImls; + private Boolean optimizeGrids; + private Boolean gmmUncertainty; private DeaggData deagg; private SiteSet sites; - private Boolean optimizeGrids; + /** + * Initialize a new builder with a copy of that supplied. + */ public Builder copy(CalcConfig config) { checkNotNull(config); this.resource = config.resource; @@ -264,12 +308,16 @@ public final class CalcConfig { this.imts = config.imts; this.defaultImls = config.defaultImls; this.customImls = config.customImls; + this.optimizeGrids = config.optimizeGrids; + this.gmmUncertainty = config.gmmUncertainty; this.deagg = config.deagg; this.sites = config.sites; - this.optimizeGrids = config.optimizeGrids; return this; } + /** + * Initialize a new builder with defaults. + */ public Builder withDefaults() { this.exceedanceModel = ExceedanceModel.TRUNCATION_UPPER_ONLY; this.truncationLevel = 3.0; @@ -279,12 +327,17 @@ public final class CalcConfig { 0.0380, 0.0570, 0.0854, 0.128, 0.192, 0.288, 0.432, 0.649, 0.973, 1.46, 2.19, 3.28, 4.92, 7.38 }; this.customImls = Maps.newHashMap(); + this.optimizeGrids = true; + this.gmmUncertainty = false; this.deagg = new DeaggData(); this.sites = new SiteSet(Lists.newArrayList(Site.builder().build())); - this.optimizeGrids = true; return this; } + /** + * Extend {@code this} builder to match {@code that} builder. Fields in + * that builder take precedence unless they are not set. + */ public Builder extend(final Builder that) { checkNotNull(that); if (that.resource != null) this.resource = that.resource; @@ -293,12 +346,16 @@ public final class CalcConfig { if (that.imts != null) this.imts = that.imts; if (that.defaultImls != null) this.defaultImls = that.defaultImls; if (that.customImls != null) this.customImls = that.customImls; + if (that.optimizeGrids != null) this.optimizeGrids = that.optimizeGrids; + if (that.gmmUncertainty != null) this.gmmUncertainty = that.gmmUncertainty; if (that.deagg != null) this.deagg = that.deagg; if (that.sites != null) this.sites = that.sites; - if (that.optimizeGrids != null) this.optimizeGrids = that.optimizeGrids; return this; } + /** + * Set the IMTs for which results should be calculated. + */ public Builder imts(Set<Imt> imts) { this.imts = checkNotNull(imts); return this; @@ -338,19 +395,32 @@ public final class CalcConfig { checkNotNull(imts, MSSG, buildId, Key.IMTS); checkNotNull(defaultImls, MSSG, buildId, Key.DEFAULT_IMLS); checkNotNull(customImls, MSSG, buildId, Key.CUSTOM_IMLS); + checkNotNull(optimizeGrids, MSSG, buildId, Key.OPTIMIZE_GRIDS); + checkNotNull(gmmUncertainty, MSSG, buildId, Key.GMM_UNCERTAINTY); checkNotNull(deagg, MSSG, buildId, Key.DEAGG); checkNotNull(sites, MSSG, buildId, Key.SITES); built = true; } + /** + * Build a new calculation configuration. + */ public CalcConfig build() { validateState(ID); Set<Imt> finalImts = Sets.immutableEnumSet(imts); Map<Imt, XySequence> curves = createCurveMap(); Map<Imt, XySequence> logCurves = createLogCurveMap(); return new CalcConfig( - resource, exceedanceModel, truncationLevel, finalImts, - defaultImls, customImls, deagg, sites, optimizeGrids, + resource, + exceedanceModel, + truncationLevel, + finalImts, + defaultImls, + customImls, + optimizeGrids, + gmmUncertainty, + deagg, + sites, curves, logCurves); } diff --git a/src/org/opensha2/calc/Calcs.java b/src/org/opensha2/calc/Calcs.java index 06f804d27dfdc9074074b81fdd632a511557647e..0c923317c8d77b9855d1a0313f68e475397e80ce 100644 --- a/src/org/opensha2/calc/Calcs.java +++ b/src/org/opensha2/calc/Calcs.java @@ -156,7 +156,7 @@ public class Calcs { case GRID: GridSourceSet gss = (GridSourceSet) sourceSet; - if (config.optimizeGrids && gss.sourceType() != FIXED_STRIKE) { + if (config.optimizeGrids() && gss.sourceType() != FIXED_STRIKE) { gridTables.add(transform(immediateFuture(gss), GridSourceSet.toTableFunction(site.location), ex)); break; @@ -210,7 +210,7 @@ public class Calcs { switch (sourceSet.type()) { case GRID: GridSourceSet gss = (GridSourceSet) sourceSet; - if (config.optimizeGrids && gss.sourceType() != FIXED_STRIKE) { + if (config.optimizeGrids() && gss.sourceType() != FIXED_STRIKE) { sourceSet = GridSourceSet.toTableFunction(site.location).apply(gss); log(log, MSSG_GRID_INIT, sourceSet.name(), duration(swSource)); } diff --git a/src/org/opensha2/calc/Deaggregation.java b/src/org/opensha2/calc/Deaggregation.java index 45f748cf8ba7d016218bf9b3484c6a0a54c483e6..f938110fd35efb00a2308ecd44aa6f745c6fc89b 100644 --- a/src/org/opensha2/calc/Deaggregation.java +++ b/src/org/opensha2/calc/Deaggregation.java @@ -221,8 +221,8 @@ public final class Deaggregation { .dataModel( Dataset.builder(hazard.config).build()) .probabilityModel( - hazard.config.exceedanceModel, - hazard.config.truncationLevel); + hazard.config.exceedanceModel(), + hazard.config.truncationLevel()); } /* Reusable builder */ @@ -908,7 +908,7 @@ public final class Deaggregation { * @see CalcConfig */ static Builder builder(CalcConfig config) { - DeaggData d = config.deagg; + DeaggData d = config.deagg(); return builder( d.rMin, d.rMax, d.Δr, d.mMin, d.mMax, d.Δm, diff --git a/src/org/opensha2/calc/Hazard.java b/src/org/opensha2/calc/Hazard.java index 8a71da65f2e364fe955ab2106d625b4124c3cceb..0c15e841d03c815b962e1660dd6fedb3a43452ac 100644 --- a/src/org/opensha2/calc/Hazard.java +++ b/src/org/opensha2/calc/Hazard.java @@ -72,7 +72,7 @@ public final class Hazard { sb.append(curveSet.hazardGroundMotionsList.get(0).inputs.size()); break; case GRID: - if (ss instanceof GridSourceSet.Table && config.optimizeGrids) { + if (ss instanceof GridSourceSet.Table && config.optimizeGrids()) { GridSourceSet.Table gsst = (GridSourceSet.Table) curveSet.sourceSet; sb.append(gsst.parentCount()); sb.append(" (").append(curveSet.hazardGroundMotionsList.size()); @@ -143,7 +143,7 @@ public final class Hazard { private Builder(CalcConfig config) { this.config = checkNotNull(config); totalCurves = new EnumMap<>(Imt.class); - for (Entry<Imt, XySequence> entry : config.logModelCurves.entrySet()) { + for (Entry<Imt, XySequence> entry : config.logModelCurves().entrySet()) { totalCurves.put(entry.getKey(), emptyCopyOf(entry.getValue())); } curveMapBuilder = ImmutableSetMultimap.builder(); diff --git a/src/org/opensha2/calc/Results.java b/src/org/opensha2/calc/Results.java index ccac36d068e52999a72e6161947631b662919c8e..cb40d4e4f0384cb67085139bf69a36c1da29832f 100644 --- a/src/org/opensha2/calc/Results.java +++ b/src/org/opensha2/calc/Results.java @@ -78,7 +78,7 @@ public class Results { headings.add("lat"); Iterable<? extends Object> header = Iterables.concat( headings, - demo.config.modelCurves.get(imt).xValues()); + demo.config.modelCurves().get(imt).xValues()); lineList.add(Parsing.join(header, Delimiter.COMMA)); } lineMap.put(imt, lineList); diff --git a/src/org/opensha2/calc/Transforms.java b/src/org/opensha2/calc/Transforms.java index 0066557ed25afbc4b72d3602235f70b89026a325..7fb4ac0682b1392c114a8c3e7055eb829d7180ec 100644 --- a/src/org/opensha2/calc/Transforms.java +++ b/src/org/opensha2/calc/Transforms.java @@ -15,6 +15,7 @@ import org.opensha2.eq.model.ClusterSource; import org.opensha2.eq.model.ClusterSourceSet; import org.opensha2.eq.model.Distance; import org.opensha2.eq.model.FaultSource; +import org.opensha2.eq.model.GmmSet; import org.opensha2.eq.model.HazardModel; import org.opensha2.eq.model.Rupture; import org.opensha2.eq.model.Source; @@ -155,9 +156,9 @@ final class Transforms { private final double truncationLevel; GroundMotionsToCurves(CalcConfig config) { - this.modelCurves = config.logModelCurves; - this.exceedanceModel = config.exceedanceModel; - this.truncationLevel = config.truncationLevel; + this.modelCurves = config.logModelCurves(); + this.exceedanceModel = config.exceedanceModel(); + this.truncationLevel = config.truncationLevel(); } @Override public HazardCurves apply(GroundMotions groundMotions) { @@ -172,15 +173,17 @@ final class Transforms { XySequence utilCurve = XySequence.copyOf(modelCurve); XySequence gmmCurve = XySequence.copyOf(modelCurve); - Map<Gmm, List<Double>> gmmMeans = groundMotions.means.get(imt); - Map<Gmm, List<Double>> gmmSigmas = groundMotions.sigmas.get(imt); + // Map<Gmm, List<Double>> gmmMeans = + // groundMotions.means.get(imt); + // Map<Gmm, List<Double>> gmmSigmas = + // groundMotions.sigmas.get(imt); - for (Gmm gmm : gmmMeans.keySet()) { + for (Gmm gmm : groundMotions.means.get(imt).keySet()) { gmmCurve.clear(); - List<Double> means = gmmMeans.get(gmm); - List<Double> sigmas = gmmSigmas.get(gmm); + List<Double> means = groundMotions.means.get(imt).get(gmm); + List<Double> sigmas = groundMotions.sigmas.get(imt).get(gmm); for (int i = 0; i < means.size(); i++) { exceedanceModel.exceedance( @@ -199,6 +202,91 @@ final class Transforms { } } + static final class GroundMotionsToCurvesWithUncertainty implements + Function<GroundMotions, HazardCurves> { + + private final GmmSet gmmSet; + private final Map<Imt, XySequence> modelCurves; + private final ExceedanceModel exceedanceModel; + private final double truncationLevel; + + GroundMotionsToCurvesWithUncertainty(GmmSet gmmSet, CalcConfig config) { + this.gmmSet = gmmSet; + this.modelCurves = config.logModelCurves(); + this.exceedanceModel = config.exceedanceModel(); + this.truncationLevel = config.truncationLevel(); + } + + @Override public HazardCurves apply(GroundMotions groundMotions) { + + HazardCurves.Builder curveBuilder = HazardCurves.builder(groundMotions); + + // initialize uncertainty for each input + InputList inputs = groundMotions.inputs; + double[] uncertainties = new double[inputs.size()]; + double[] rates = new double[inputs.size()]; + for (int i = 0; i < inputs.size(); i++) { + HazardInput input = inputs.get(i); + rates[i] = input.rate; + uncertainties[i] = gmmSet.epiValue(input.Mw, input.rJB); + } + + for (Entry<Imt, XySequence> entry : modelCurves.entrySet()) { + + XySequence modelCurve = entry.getValue(); + Imt imt = entry.getKey(); + + XySequence utilCurve = XySequence.copyOf(modelCurve); + XySequence gmmCurve = XySequence.copyOf(modelCurve); + + for (Gmm gmm : groundMotions.means.get(imt).keySet()) { + + gmmCurve.clear(); + + List<Double> means = groundMotions.means.get(imt).get(gmm); + List<Double> sigmas = groundMotions.sigmas.get(imt).get(gmm); + + for (int i = 0; i < means.size(); i++) { + double mean = means.get(i); + double epi = uncertainties[i]; + double[] epiMeans = new double[] { mean - epi, mean, mean + epi }; + exceedanceCurve( + epiMeans, + sigmas.get(i), + imt, + utilCurve); + utilCurve.multiply(groundMotions.inputs.get(i).rate); + gmmCurve.add(utilCurve); + } + curveBuilder.addCurve(imt, gmm, gmmCurve); + } + } + return curveBuilder.build(); + } + + /* Construct an exceedance curve considering uncertain ground motions. */ + private XySequence exceedanceCurve( + final double[] means, + final double sigma, + final Imt imt, + final XySequence curve) { + + XySequence utilCurve = XySequence.emptyCopyOf(curve); + double[] weights = gmmSet.epiWeights(); + for (int i = 0; i < means.length; i++) { + exceedanceModel.exceedance( + means[i], + sigma, + truncationLevel, + imt, + utilCurve); + utilCurve.multiply(weights[i]); + curve.add(utilCurve); + } + return curve; + } + } + /* * Source --> HazardCurves * @@ -216,12 +304,16 @@ final class Transforms { CalcConfig config, Site site) { - Set<Gmm> gmms = sources.groundMotionModels().gmms(); - Map<Imt, Map<Gmm, GroundMotionModel>> gmmTable = instances(config.imts, gmms); + GmmSet gmmSet = sources.groundMotionModels(); + Map<Imt, Map<Gmm, GroundMotionModel>> gmmTable = instances( + config.imts(), + gmmSet.gmms()); this.sourceToInputs = new SourceToInputs(site); this.inputsToGroundMotions = new InputsToGroundMotions(gmmTable); - this.groundMotionsToCurves = new GroundMotionsToCurves(config); + this.groundMotionsToCurves = config.gmmUncertainty() && gmmSet.epiUncertainty() ? + new GroundMotionsToCurvesWithUncertainty(gmmSet, config) : + new GroundMotionsToCurves(config); } @Override public HazardCurves apply(Source source) { @@ -246,7 +338,7 @@ final class Transforms { CalcConfig config) { this.sources = sources; - this.modelCurves = config.logModelCurves; + this.modelCurves = config.logModelCurves(); } @Override public HazardCurveSet apply(List<HazardCurves> curvesList) { @@ -283,12 +375,16 @@ final class Transforms { CalcConfig config, Site site) { - Set<Gmm> gmms = sources.groundMotionModels().gmms(); - Map<Imt, Map<Gmm, GroundMotionModel>> gmmTable = instances(config.imts, gmms); + GmmSet gmmSet = sources.groundMotionModels(); + Map<Imt, Map<Gmm, GroundMotionModel>> gmmTable = instances( + config.imts(), + gmmSet.gmms()); this.sourcesToInputs = SystemSourceSet.toInputsFunction(site); this.inputsToGroundMotions = new InputsToGroundMotions(gmmTable); - this.groundMotionsToCurves = new GroundMotionsToCurves(config); + this.groundMotionsToCurves = config.gmmUncertainty() && gmmSet.epiUncertainty() ? + new GroundMotionsToCurvesWithUncertainty(gmmSet, config) : + new GroundMotionsToCurves(config); this.curveConsolidator = new CurveConsolidator(sources, config); } @@ -373,9 +469,9 @@ final class Transforms { private final double truncationLevel; ClusterGroundMotionsToCurves(CalcConfig config) { - this.logModelCurves = config.logModelCurves; - this.exceedanceModel = config.exceedanceModel; - this.truncationLevel = config.truncationLevel; + this.logModelCurves = config.logModelCurves(); + this.exceedanceModel = config.exceedanceModel(); + this.truncationLevel = config.truncationLevel(); } @Override public ClusterCurves apply(ClusterGroundMotions clusterGroundMotions) { @@ -447,7 +543,7 @@ final class Transforms { Site site) { Set<Gmm> gmms = sources.groundMotionModels().gmms(); - Map<Imt, Map<Gmm, GroundMotionModel>> gmmTable = instances(config.imts, gmms); + Map<Imt, Map<Gmm, GroundMotionModel>> gmmTable = instances(config.imts(), gmms); this.sourceToInputs = new ClusterSourceToInputs(site); this.inputsToGroundMotions = new ClusterInputsToGroundMotions(gmmTable); @@ -477,7 +573,7 @@ final class Transforms { CalcConfig config) { this.sources = sources; - this.modelCurves = config.logModelCurves; + this.modelCurves = config.logModelCurves(); } @Override public HazardCurveSet apply(List<ClusterCurves> curvesList) { diff --git a/src/org/opensha2/eq/model/GmmSet.java b/src/org/opensha2/eq/model/GmmSet.java index fe68049525152c981800e8b52ffea697414fe760..37204d56dc29a08c408d3a932ad53521c059bd57 100644 --- a/src/org/opensha2/eq/model/GmmSet.java +++ b/src/org/opensha2/eq/model/GmmSet.java @@ -6,10 +6,12 @@ import static com.google.common.base.Preconditions.checkState; import static org.opensha2.data.Data.checkInRange; import static org.opensha2.data.Data.checkWeightSum; +import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.Set; +import org.opensha2.data.Data; import org.opensha2.gmm.Gmm; import org.opensha2.gmm.GroundMotionModel; @@ -47,7 +49,7 @@ public final class GmmSet { private final UncertType uncertainty; private final double epiValue; private final double[][] epiValues; - private final double[] epiWeights; + private final double[] epiWeights; // TODO DataArray (vs. Table, Volume) GmmSet(Map<Gmm, Double> weightMapLo, double maxDistLo, Map<Gmm, Double> weightMapHi, double maxDistHi, double[] epiValues, double[] epiWeights) { @@ -128,16 +130,20 @@ public final class GmmSet { }; } + public boolean epiUncertainty() { + return uncertainty != UncertType.NONE; + } + /* - * Returns the epistemic uncertainty for the supplied magnitude (M) and - * distance (D) that + * Returns the epistemic uncertainty for the supplied magnitude and distance + * in natural log units of ground motion. */ - private double getUncertainty(double M, double D) { + public double epiValue(double m, double r) { switch (uncertainty) { case MULTI: - int mi = (M < 6) ? 0 : (M < 7) ? 1 : 2; - int di = (D < 10) ? 0 : (D < 30) ? 1 : 2; - return epiValues[di][mi]; + int mi = (m < 6) ? 0 : (m < 7) ? 1 : 2; + int ri = (r < 10) ? 0 : (r < 30) ? 1 : 2; + return epiValues[ri][mi]; case SINGLE: return epiValue; default: @@ -145,7 +151,16 @@ public final class GmmSet { } } - static enum UncertType { + /* + * Returns the 3 weights used for low, median, and high branches of the epistemic + * uncertainty distribution. + * TODO needs to be immutable, This is a good case for DataArray + */ + public double[] epiWeights() { + return epiWeights; + } + + private static enum UncertType { NONE, SINGLE, MULTI; @@ -201,6 +216,7 @@ public final class GmmSet { "Values must contain 1 or 9 values"); checkArgument(checkNotNull(weights, "Weights are null").length == 3, "Weights must contain 3 values"); + checkArgument(Data.sum(weights) == 1.0, "%s uncertainty weights must sum to 1"); uncValues = values; uncWeights = weights; return this; @@ -227,6 +243,7 @@ public final class GmmSet { if (uncValues != null) checkNotNull(uncWeights, "%s uncertainty weights not set", id); if (uncWeights != null) checkNotNull(uncValues, "%s uncertainty values not set", id); + built = true; }