Newer
Older
package org.opensha2.programs;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.Runtime.getRuntime;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static org.opensha2.util.TextUtils.NEWLINE;
import static org.opensha2.util.TextUtils.format;
import static org.opensha2.util.Parsing.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import org.opensha2.calc.CalcConfig;
import org.opensha2.calc.Site;
import org.opensha2.calc.Site.Builder;
import org.opensha2.eq.model.HazardModel;
import org.opensha2.gmm.Imt;
import org.opensha2.util.Logging;
import org.opensha2.util.Parsing;
import org.opensha2.util.Parsing.Delimiter;
import com.google.common.base.Optional;
import com.google.common.base.StandardSystemProperty;
import com.google.common.base.Stopwatch;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
* Entry point for computing probabilisitic seismic hazard at a {@link Site}
* from a {@link HazardModel}. The main method of this class outputs mean hazard
* curves for the model and {@link Imt}s specified per the calculation
* configuration. For more detailed results, consider programmatically using
* the @ calc()} method of this class.
// TODO move to config
private static final int FLUSH_LIMIT = 5;
*
* <p>Computing hazard curves requires at least 1, and at most 3, arguments.
* At a minimum, the path to a model zip file or directory must be
* specified. If only a model is supplied, model initialization and
* calculation configuration settings are drawn from the config file that
* must reside at the root of the model directory.</p>
*
* <p>Alternatively, the path to a file with calculation configuration may
* also be supplied. A configuration file, whether included with a model or
* supplied independently, is assumed to contain the sites of interest for a
* calculation. Any calculation settings in a supplied configuration file
* will override those included with a model; model initialization settings
* will be ignored and must be updated in the config file at the root of the
* model to take effect.</p>
*
* <p>For long lists of sites, it may be easier to supply a third argument:
* the path to a comma-delimited file of site data. Please refer to the
* nshmp-haz <a href="https://github.com/usgs/nshmp-haz/wiki">wiki</a> for
* comprehensive descriptions of source models, configuration files, and
* hazard calculations.</p>
*
* @param args
* @see <a href="https://github.com/usgs/nshmp-haz/wiki/Building-&-Running">
* nshmp-haz wiki</a>
* @see <a
* href="https://github.com/usgs/nshmp-haz/tree/master/etc/examples">
* example calculations</a>
/* Delegate to run which has a return value for testing. */
Optional<String> status = run(args);
if (status.isPresent()) {
static Optional<String> run(String[] args) {
if (argCount < 2 || argCount > 3) {
return Optional.of(USAGE);
Logger log = Logger.getLogger(HazardCalc.class.getName());
log.info(PROGRAM + ": initializing...");
Path modelPath = Paths.get(args[0]);
HazardModel model = HazardModel.load(modelPath);
CalcConfig config = model.config();
Path out = Paths.get(StandardSystemProperty.USER_DIR.value());
if (argCount == 3) {
Path userConfigPath = Paths.get(args[2]);
config = CalcConfig.builder()
.copy(model.config())
.extend(CalcConfig.builder(userConfigPath))
out = userConfigPath.getParent();
Iterable<Site> sites = readSites(args[1]);
log.info("");
log.info("Sites:" + sites);
// Iterable<Site> sites = config.sites();
// if (argCount > 2) {
// Path sitePath = Paths.get(args[2]);
// sites = Sites.fromCsv(sitePath);
// log.info("");
// StringBuilder sb = new StringBuilder()
// .append("Site config:")
// .append(format("resource")).append(sitePath)
// .append(format("(override) sites"))
// .append(sites);
// log.info(sb.toString());
// }
calc(model, config, sites, out, log);
log.info(PROGRAM + ": finished");
return Optional.absent();
StringBuilder sb = new StringBuilder()
.append(PROGRAM + ": error").append(NEWLINE)
.append(" Arguments: ").append(Arrays.toString(args)).append(NEWLINE)
.append(NEWLINE)
.append(Throwables.getStackTraceAsString(e)).append(NEWLINE)
.append(NEWLINE)
.append(USAGE);
return Optional.of(sb.toString());
private static Iterable<Site> readSites(String arg) throws IOException {
if (arg.startsWith("\"")) {
return Sites.fromString(arg);
}
Path sitePath = Paths.get(arg);
if (arg.toLowerCase().endsWith(".csv")) {
return Sites.fromCsv(sitePath);
}
if (arg.toLowerCase().endsWith(".geojson")) {
return Sites.fromJson(sitePath);
}
throw new IllegalArgumentException(
"Sites argument [" + arg + "] must either be a quoted string" +
"or specify a path to a *.csv or *.geojson file");
}
private static final OpenOption[] WRITE_OPTIONS = new OpenOption[] {};
private static final OpenOption[] APPEND_OPTIONS = new OpenOption[] { APPEND };
* Compute hazard curves using the supplied model, config, and site files.
private static void calc(
HazardModel model,
CalcConfig config,
Iterable<Site> sites,
Logger log) throws IOException {
ExecutorService execSvc = createExecutor();
Optional<Executor> executor = Optional.<Executor> of(execSvc);
log.info(PROGRAM + ": calculating ...");
Stopwatch batchWatch = Stopwatch.createStarted();
Stopwatch totalWatch = Stopwatch.createStarted();
List<Hazard> results = new ArrayList<>();
Hazard result = calc(model, config, site, executor);
results.add(result);
if (results.size() == FLUSH_LIMIT) {
OpenOption[] opts = firstBatch ? WRITE_OPTIONS : APPEND_OPTIONS;
firstBatch = false;
Results.writeResults(out, results, opts);
log.info(" batch: " + (count + 1) + " " + batchWatch +
// write final batch
if (!results.isEmpty()) {
OpenOption[] opts = firstBatch ? WRITE_OPTIONS : APPEND_OPTIONS;
Results.writeResults(out, results, opts);
log.info(PROGRAM + ": " + count + " complete " + totalWatch);
* Compute hazard curves at a {@code site} for a {@code model} and
* {@code config}. If an {@code executor} is supplied, it will be used to
* distribute tasks; otherwise, one will be created.
*
* <p><b>Note:</b> any model initialization settings in {@code config} will
* be ignored as the supplied model will already have been initialized.</p>
*
* @param model to use
* @param config calculation configuration
* @param site of interest
* @param executor to use ({@link Optional})
* @return a HazardResult
HazardModel model,
CalcConfig config,
Site site,
Optional<Executor> executor) {
Optional<Executor> execLocal = executor.or(Optional.of(createExecutor()));
Hazard result = Calcs.hazard(model, config, site, execLocal);
if (executor.isPresent()) ((ExecutorService) executor).shutdown();
return result;
} catch (ExecutionException | InterruptedException e) {
Throwables.propagate(e);
return null;
private static ExecutorService createExecutor() {
return newFixedThreadPool(getRuntime().availableProcessors());
private static final String PROGRAM = HazardCalc.class.getSimpleName();
private static final String USAGE_COMMAND =
"java -cp nshmp-haz.jar org.opensha2.programs.HazardCalc model sites [config]";
private static final String USAGE_URL1 = "https://github.com/usgs/nshmp-haz/wiki";
private static final String USAGE_URL2 = "https://github.com/usgs/nshmp-haz/tree/master/etc";
private static final String SITE_STRING = "\"name, lon, lat[, vs30, vsInf[, z1p0, z2p5]]\"";
private static final String USAGE = new StringBuilder()
.append(PROGRAM).append(" usage:").append(NEWLINE)
.append(" ").append(USAGE_COMMAND).append(NEWLINE)
.append(NEWLINE)
.append("Where:").append(NEWLINE)
.append(" 'model' is a model zip file or directory")
.append(NEWLINE)
.append(" 'sites' is either:")
.append(NEWLINE)
.append(" - a quoted string, e.g. ").append(SITE_STRING)
.append(NEWLINE)
.append(" (site class and basin terms are optional)")
.append(NEWLINE)
.append(" - or a *.csv file or *.geojson file of site data")
.append(NEWLINE)
.append(" 'config' (optional) supplies a calculation configuration")
.append(NEWLINE)
.append(NEWLINE)
.append("For more information, see:").append(NEWLINE)
.append(" ").append(USAGE_URL1).append(NEWLINE)
.append(" ").append(USAGE_URL2).append(NEWLINE)