Newer
Older
package org.opensha2.calc;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.padEnd;
import static com.google.common.base.Strings.repeat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.opensha2.data.XySequence.create;
import static org.opensha2.data.XySequence.immutableCopyOf;
import static org.opensha2.util.Parsing.enumsToString;
import static org.opensha2.util.TextUtils.NEWLINE;
// import static org.opensha2.util.TextUtils.*;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import org.opensha2.eq.model.SourceType;
import org.opensha2.gmm.Gmm;
import org.opensha2.gmm.Imt;
import org.opensha2.util.TextUtils;
import com.google.common.base.Optional;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
public final class CalcConfig {
private static final String ID = CalcConfig.class.getSimpleName();
private static final String STATE_ERROR = "%s %s not set";
private static final String DEFAULT_OUT = "curves";
* The resource from which {@code this} was derived. If this configuration
* was built using {@link Builder#extend(Builder)}, this field will reflect
* the field of the extending resource. If this configuration was built only
* from {@link Builder#withDefaults()}, this field will be empty.
public final Optional<Path> resource;
/** Hazard curve calculation settings */
public final Curve curve;
/** Performance and optimization settings. */
public final Performance performance;
/** Output configuration. */
public final Output output;
/** Deaggregation configuration. */
public final Deagg deagg;
private CalcConfig(
Optional<Path> resource,
Curve curve,
Performance performance,
Output output,
Deagg deagg) {
this.resource = resource;
this.curve = curve;
this.performance = performance;
this.output = output;
this.deagg = deagg;
}
* Hazard curve calculation configuration.
public final static class Curve {
static final String ID = CalcConfig.ID + "." + Curve.class.getSimpleName();
/**
* The probability distribution model to use when computing hazard
* curves.
*
* <p><b>Default:</b> {@link ExceedanceModel#TRUNCATION_UPPER_ONLY}
*/
public final ExceedanceModel exceedanceModel;
// TODO refactor to probabilityModel
/**
* The number of standard deviations (σ) at which to truncate a
* distribution. This field is ignored if an {@link ExceedanceModel}
* does not implement truncation.
*
* <p><b>Default:</b> {@code 3.0}
*/
public final double truncationLevel;
/**
* The {@code Set} of IMTs for which calculations should be performed.
*
* <p><b>Default:</b> [{@link Imt#PGA}, {@link Imt#SA0P2},
* {@link Imt#SA1P0}]
*/
public final Set<Imt> imts;
/**
* Whether to consider additional ground motion model uncertainty, or
* not. Currently this is only applicable when using the PEER NGA-West
* or NGA-West2 {@link Gmm}s with USGS hazard models.
*
* <p><b>Default:</b> {@code false}
*/
public final boolean gmmUncertainty;
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* The value format for hazard curves.
*
* <p><b>Default:</b> {@link CurveValue#ANNUAL_RATE}
*/
public final CurveValue valueType;
private final double[] defaultImls;
private final Map<Imt, double[]> customImls;
private final Map<Imt, XySequence> modelCurves;
private final Map<Imt, XySequence> logModelCurves;
private Curve(
ExceedanceModel exceedanceModel,
double truncationLevel,
Set<Imt> imts,
boolean gmmUncertainty,
CurveValue valueType,
double[] defaultImls,
Map<Imt, double[]> customImls,
Map<Imt, XySequence> modelCurves,
Map<Imt, XySequence> logModelCurves) {
this.exceedanceModel = exceedanceModel;
this.truncationLevel = truncationLevel;
this.imts = imts;
this.gmmUncertainty = gmmUncertainty;
this.valueType = valueType;
this.defaultImls = defaultImls;
this.customImls = customImls;
this.modelCurves = modelCurves;
this.logModelCurves = logModelCurves;
}
/**
* 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;
}
private StringBuilder asString() {
StringBuilder imlSb = new StringBuilder();
if (!customImls.isEmpty()) {
for (Entry<Imt, double[]> entry : customImls.entrySet()) {
String imtStr = "imls (" + entry.getKey().name() + ")";
imlSb.append(formatEntry(imtStr))
.append(wrap(Arrays.toString(entry.getValue()), false));
}
return new StringBuilder()
.append(formatGroup("Curve"))
.append(formatEntry(Key.EXCEEDANCE_MODEL, exceedanceModel))
.append(formatEntry(Key.TRUNCATION_LEVEL, truncationLevel))
.append(formatEntry(Key.IMTS, enumsToString(imts, Imt.class)))
.append(formatEntry(Key.GMM_UNCERTAINTY, gmmUncertainty))
.append(formatEntry(Key.VALUE_TYPE, valueType))
.append(formatEntry(Key.DEFAULT_IMLS, wrap(Arrays.toString(defaultImls), false)))
.append(imlSb);
private static final class Builder {
ExceedanceModel exceedanceModel;
Double truncationLevel;
Set<Imt> imts;
Boolean gmmUncertainty;
CurveValue valueType;
double[] defaultImls;
Map<Imt, double[]> customImls;
Curve build() {
return new Curve(
exceedanceModel,
truncationLevel,
Sets.immutableEnumSet(imts),
gmmUncertainty,
valueType,
defaultImls,
customImls,
createCurveMap(),
createLogCurveMap());
}
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
void copy(Curve that) {
this.exceedanceModel = that.exceedanceModel;
this.truncationLevel = that.truncationLevel;
this.imts = that.imts;
this.gmmUncertainty = that.gmmUncertainty;
this.valueType = that.valueType;
this.defaultImls = that.defaultImls;
this.customImls = that.customImls;
}
void extend(Builder that) {
if (that.exceedanceModel != null) this.exceedanceModel = that.exceedanceModel;
if (that.truncationLevel != null) this.truncationLevel = that.truncationLevel;
if (that.imts != null) this.imts = that.imts;
if (that.gmmUncertainty != null) this.gmmUncertainty = that.gmmUncertainty;
if (that.valueType != null) this.valueType = that.valueType;
if (that.defaultImls != null) this.defaultImls = that.defaultImls;
if (that.customImls != null) this.customImls = that.customImls;
}
static Builder defaults() {
Builder b = new Builder();
b.exceedanceModel = ExceedanceModel.TRUNCATION_UPPER_ONLY;
b.truncationLevel = 3.0;
b.imts = EnumSet.of(Imt.PGA, Imt.SA0P2, Imt.SA1P0);
b.gmmUncertainty = false;
b.valueType = CurveValue.ANNUAL_RATE;
// Slightly modified version of NSHM 5Hz curve, size = 20
b.defaultImls = new double[] { 0.0025, 0.0045, 0.0075, 0.0113, 0.0169, 0.0253,
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 };
b.customImls = Maps.newHashMap();
return b;
}
void validate() {
checkNotNull(exceedanceModel, STATE_ERROR, Curve.ID, Key.EXCEEDANCE_MODEL);
checkNotNull(truncationLevel, STATE_ERROR, Curve.ID, Key.TRUNCATION_LEVEL);
checkNotNull(imts, STATE_ERROR, Curve.ID, Key.IMTS);
checkNotNull(defaultImls, STATE_ERROR, Curve.ID, Key.DEFAULT_IMLS);
checkNotNull(customImls, STATE_ERROR, Curve.ID, Key.CUSTOM_IMLS);
}
Map<Imt, XySequence> createLogCurveMap() {
Map<Imt, XySequence> curveMap = Maps.newEnumMap(Imt.class);
for (Imt imt : imts) {
double[] imls = imlsForImt(imt);
imls = Arrays.copyOf(imls, imls.length);
Data.ln(imls);
curveMap.put(imt, immutableCopyOf(create(imls, null)));
}
return Maps.immutableEnumMap(curveMap);
}
Map<Imt, XySequence> createCurveMap() {
Map<Imt, XySequence> curveMap = Maps.newEnumMap(Imt.class);
for (Imt imt : imts) {
double[] imls = imlsForImt(imt);
imls = Arrays.copyOf(imls, imls.length);
curveMap.put(imt, immutableCopyOf(create(imls, null)));
}
return Maps.immutableEnumMap(curveMap);
}
double[] imlsForImt(Imt imt) {
return customImls.containsKey(imt) ? customImls.get(imt) : defaultImls;
}
}

Powers, Peter M.
committed
}
* Performance and optimization settings.
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
public static final class Performance {
static final String ID = CalcConfig.ID + "." + Performance.class.getSimpleName();
/**
* Whether to optimize grid source sets, or not.
*
* <p><b>Default:</b> {@code true}
*/
public final boolean optimizeGrids;
/**
* Whether to collapse/combine magnitude-frequency distributions, or
* not. Doing so prevents uncertainty analysis as logic-tree branches
* are obscured.
*
* <p><b>Default:</b> {@code true}
*/
public final boolean collapseMfds;
/**
* The partition or batch size to use when distributing
* {@link SourceType#SYSTEM} calculations.
*
* <p><b>Default:</b> {@code 1000}
*/
public final int systemPartition;
/**
* The number of threads to use when distributing calculations.
*
* <p><b>Default:</b> {@link ThreadCount#ALL}
*/
public final ThreadCount threadCount;
private Performance(
boolean optimizeGrids,
boolean collapseMfds,
int systemPartition,
ThreadCount threadCount) {
this.optimizeGrids = optimizeGrids;
this.collapseMfds = collapseMfds;
this.systemPartition = systemPartition;
this.threadCount = threadCount;
}
private StringBuilder asString() {
return new StringBuilder()
.append(formatGroup("Performance"))
.append(formatEntry(Key.OPTIMIZE_GRIDS, optimizeGrids))
.append(formatEntry(Key.COLLAPSE_MFDS, collapseMfds))
.append(formatEntry(Key.SYSTEM_PARTITION, systemPartition))
.append(formatEntry(Key.THREAD_COUNT, threadCount));
}
private static final class Builder {
Boolean optimizeGrids;
Boolean collapseMfds;
Integer systemPartition;
ThreadCount threadCount;
Performance build() {
return new Performance(
optimizeGrids,
collapseMfds,
systemPartition,
threadCount);
}
void copy(Performance that) {
this.optimizeGrids = that.optimizeGrids;
this.collapseMfds = that.collapseMfds;
this.systemPartition = that.systemPartition;
this.threadCount = that.threadCount;
}
void extend(Builder that) {
if (that.optimizeGrids != null) this.optimizeGrids = that.optimizeGrids;
if (that.collapseMfds != null) this.collapseMfds = that.collapseMfds;
if (that.systemPartition != null) this.systemPartition = that.systemPartition;
if (that.threadCount != null) this.threadCount = that.threadCount;
}
static Builder defaults() {
Builder b = new Builder();
b.optimizeGrids = true;
b.collapseMfds = true;
b.systemPartition = 1000;
b.threadCount = ThreadCount.ALL;
return b;
}
void validate() {
checkNotNull(optimizeGrids, STATE_ERROR, Performance.ID, Key.OPTIMIZE_GRIDS);
checkNotNull(collapseMfds, STATE_ERROR, Performance.ID, Key.COLLAPSE_MFDS);
checkNotNull(systemPartition, STATE_ERROR, Performance.ID, Key.SYSTEM_PARTITION);
checkNotNull(threadCount, STATE_ERROR, Performance.ID, Key.THREAD_COUNT);
}
}
}
/**
* Hazard curve and file output settings.
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
public static final class Output {
static final String ID = CalcConfig.ID + "." + Output.class.getSimpleName();
/**
* The directory to write any results to.
*
* <p><b>Default:</b> {@code "curves"}
*/
public final Path directory;
/**
* The different {@linkplain CurveType types} of curves to save. Note
* that {@link CurveType#TOTAL} will <i>always</i> be included in this
* set, regardless of any user settings.
*
* <p><b>Default:</b> [{@link CurveType#TOTAL}]
*/
public final Set<CurveType> curveTypes;
/**
* The number of results (one per {@code Site}) to store before writing
* to file(s). A larger number requires more memory.
*
* <p><b>Default:</b> {@code 20}
*/
public final int flushLimit;
private Output(
Path directory,
Set<CurveType> curveTypes,
int flushLimit) {
this.directory = directory;
curveTypes.add(CurveType.TOTAL);
this.curveTypes = Sets.immutableEnumSet(curveTypes);
this.flushLimit = flushLimit;
}
private StringBuilder asString() {
return new StringBuilder()
.append(formatGroup("Output"))
.append(formatEntry(Key.DIRECTORY, directory.toAbsolutePath().normalize()))
.append(formatEntry(Key.CURVE_TYPES, enumsToString(curveTypes, CurveType.class)))
.append(formatEntry(Key.FLUSH_LIMIT, flushLimit));
}
private static final class Builder {
Path directory;
Set<CurveType> curveTypes;
Integer flushLimit;
Output build() {
return new Output(
directory,
curveTypes,
flushLimit);
}
void copy(Output that) {
this.directory = that.directory;
this.curveTypes = that.curveTypes;
this.flushLimit = that.flushLimit;
}
void extend(Builder that) {
if (that.directory != null) this.directory = that.directory;
if (that.curveTypes != null) this.curveTypes = that.curveTypes;
if (that.flushLimit != null) this.flushLimit = that.flushLimit;
}
static Builder defaults() {
Builder b = new Builder();
b.directory = Paths.get(DEFAULT_OUT);
b.curveTypes = EnumSet.of(CurveType.TOTAL);
b.flushLimit = 20;
return b;
}
void validate() {
checkNotNull(directory, STATE_ERROR, Performance.ID, Key.DIRECTORY);
checkNotNull(curveTypes, STATE_ERROR, Performance.ID, Key.CURVE_TYPES);
checkNotNull(flushLimit, STATE_ERROR, Performance.ID, Key.FLUSH_LIMIT);
}
}
}
/**
* Deaggregation configuration data container.
*/
public static final class Deagg {
static final String ID = CalcConfig.ID + "." + Deagg.class.getSimpleName();
/** Minimum distance. Lower edge of smallest distance bin. */
/** Maximum distance. Upper edge of largest distance bin. */
/** Distance bin width. */
/** Minimum magnitude. Lower edge of smallest magnitude bin. */
/** Maximum magnitude. Upper edge of largest magnitude bin. */
/** Magnitude bin width. */
/** Minimum epsilon. Lower edge of smallest epsilon bin. */
/** Maximum epsilon. Upper edge of largest epsilon bin. */
/** Epsilon bin width. */
rMin = 0.0;
rMax = 100.0;
Δr = 10.0;
mMin = 5.0;
mMax = 7.0;
Δm = 0.1;
εMin = -3;
εMax = 3.0;
Δε = 0.5;
}
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
private StringBuilder asString() {
return new StringBuilder()
.append(formatGroup("Deaggregation"))
.append(formatEntry("R"))
.append("min=").append(rMin).append(", ")
.append("max=").append(rMax).append(", ")
.append("Δ=").append(Δr)
.append(formatEntry("M"))
.append("min=").append(mMin).append(", ")
.append("max=").append(mMax).append(", ")
.append("Δ=").append(Δm)
.append(formatEntry("ε"))
.append("min=").append(εMin).append(", ")
.append("max=").append(εMax).append(", ")
.append("Δ=").append(Δε);
}
}
private enum Key {
RESOURCE,
/* curve */
EXCEEDANCE_MODEL,
TRUNCATION_LEVEL,
IMTS,
GMM_UNCERTAINTY,
VALUE_TYPE,
DEFAULT_IMLS,
CUSTOM_IMLS,
/* performance */
OPTIMIZE_GRIDS,
COLLAPSE_MFDS,
SYSTEM_PARTITION,
THREAD_COUNT,
/* output */
DIRECTORY,
CURVE_TYPES,
FLUSH_LIMIT,
/* deagg */
DEAGG;
private String label;
private Key() {
this.label = UPPER_UNDERSCORE.to(LOWER_CAMEL, name());
}
@Override
public String toString() {
return label;
}
}
@Override
public String toString() {
return new StringBuilder("Calc Configuration: ")
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
.append(resource.isPresent()
? resource.get().toAbsolutePath().normalize()
: "(from defaults)")
.append(curve.asString())
.append(performance.asString())
.append(output.asString())
.append(deagg.asString())
.toString();
}
// public static <E extends Enum<E>> String format(E id) {
// return format(id.toString());
// }
private static final int GROUP_INDENT_SIZE = 8;
private static final int KEY_INDENT_SIZE = 10;
private static final int VALUE_INDENT_SIZE = 28;
private static final int MAX_COL = 100;
private static final int VALUE_WIDTH = MAX_COL - VALUE_INDENT_SIZE;
private static final String S = " ";
private static final String GROUP_INDENT = repeat(S, GROUP_INDENT_SIZE);
private static final String KEY_INDENT = repeat(S, KEY_INDENT_SIZE);
private static final String VALUE_INDENT = repeat(S, VALUE_INDENT_SIZE);
private static String formatGroup(String group) {
return TextUtils.NEWLINE + GROUP_INDENT + group;
}
private static String formatEntry(String key) {
return NEWLINE + padEnd(KEY_INDENT + '.' + key + ':', VALUE_INDENT_SIZE, ' ');
}
private static <E extends Enum<E>> String formatEntry(E key, Object value) {
return NEWLINE + padEnd(KEY_INDENT + '.' + key + ':', VALUE_INDENT_SIZE, ' ') + value;
}
/* wrap a commma-delimited string */
private static String wrap(String s, boolean pad) {
if (s.length() <= VALUE_WIDTH) return pad ? NEWLINE + VALUE_INDENT + s : s;
StringBuilder sb = new StringBuilder();
int lastCommaIndex = s.substring(0, VALUE_WIDTH).lastIndexOf(',') + 1;
if (pad) sb.append(NEWLINE).append(VALUE_INDENT);
sb.append(s.substring(0, lastCommaIndex));
sb.append(wrap(s.substring(lastCommaIndex).trim(), true));
return sb.toString();
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(Path.class, new JsonDeserializer<Path>() {
@Override
public Path deserialize(
JsonElement json,
Type type,
JsonDeserializationContext context) throws JsonParseException {
return Paths.get(json.getAsString());
}
})
.create();
public static void main(String[] args) throws IOException {
// CalcConfig cc =
// Builder.fromFile(Paths.get("etc/examples/5-complex-model/config-sites.json")).build();
// System.out.println(cc);
CalcConfig cc = Builder
.withDefaults()
// .extend(Builder.fromFile(Paths.get("etc/examples/6-enhanced-output/config.json")))
.extend(Builder.fromFile(Paths.get("etc/examples/2-custom-config/config.json")))
.build();
System.out.println(cc);
}
* A builder of configuration instances.
*/
public static final class Builder {
private boolean built = false;
private Curve.Builder curve;
private Performance.Builder performance;
private Output.Builder output;
private Deagg deagg;
private Builder() {
curve = new Curve.Builder();
performance = new Performance.Builder();
output = new Output.Builder();
}
* Initialize a new builder with values copied from the supplied config.
public static Builder copyOf(CalcConfig config) {
Builder b = new Builder();
if (config.resource.isPresent()) {
b.resource = config.resource.get();
}
b.curve.copy(config.curve);
b.performance.copy(config.performance);
b.output.copy(config.output);
b.deagg = config.deagg;
return b;
* Create a new builder from the resource at the specified path. This
* will only set those fields that are explicitely defined.
*
* @param path to configuration file or resource
* @throws IOException
public static Builder fromFile(Path path) throws IOException {
checkNotNull(path);
// TODO test with zip files
Path configPath = Files.isDirectory(path) ? path.resolve(FILE_NAME) : path;
Reader reader = Files.newBufferedReader(configPath, UTF_8);
Builder b = GSON.fromJson(reader, Builder.class);
reader.close();
b.resource = configPath;
return b;
}
/**
* Initialize a new builder with all fields initialized to default
* values.
*/
public static Builder withDefaults() {
Builder b = new Builder();
b.curve = Curve.Builder.defaults();
b.performance = Performance.Builder.defaults();
b.output = Output.Builder.defaults();
b.deagg = new Deagg();
return b;
/**
* 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);
this.resource = that.resource;
this.curve.extend(that.curve);
this.performance.extend(that.performance);
this.output.extend(that.output);
if (that.deagg != null) this.deagg = that.deagg;
return this;
}
/**
* Set the IMTs for which results should be calculated.
*/
this.curve.imts = checkNotNull(imts);
private void validateState() {
checkState(!built, "This %s instance as already been used", ID + ".Builder");
curve.validate();
performance.validate();
output.validate();
checkNotNull(deagg, STATE_ERROR, Deagg.ID, "deagg");
/**
* Build a new calculation configuration.
*/
Optional.fromNullable(resource),
curve.build(),
performance.build(),
output.build(),
deagg);