Skip to content
Snippets Groups Projects
HazardCalc.java 9.65 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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;
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    
    import static org.opensha2.util.Parsing.*;
    
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    import java.io.IOException;
    
    import java.nio.file.OpenOption;
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.ArrayList;
    import java.util.Arrays;
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    import java.util.List;
    
    import java.util.concurrent.ExecutionException;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    
    import java.util.logging.Logger;
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    
    import org.opensha2.calc.CalcConfig;
    
    import org.opensha2.calc.Calcs;
    
    import org.opensha2.calc.Hazard;
    
    import org.opensha2.calc.Results;
    
    import org.opensha2.calc.Site;
    
    import org.opensha2.calc.Site.Builder;
    
    import org.opensha2.calc.Sites;
    
    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;
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    
    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;
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
     * 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.
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
     * 
    
     * @author Peter Powers
     */
    
    public class HazardCalc {
    
    	private static final int FLUSH_LIMIT = 5;
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	/**
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	 * Entry point for a hazard calculation.
    
    	 * 
    	 * <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>
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	 * @see <a
    	 *      href="https://github.com/usgs/nshmp-haz/tree/master/etc/examples">
    	 *      example calculations</a>
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	 */
    
    	public static void main(String[] args) {
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    		/* Delegate to run which has a return value for testing. */
    
    
    		Optional<String> status = run(args);
    		if (status.isPresent()) {
    
    			System.err.print(status);
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    			System.exit(1);
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    		System.exit(0);
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	}
    
    	static Optional<String> run(String[] args) {
    
    		int argCount = args.length;
    
    
    		if (argCount < 2 || argCount > 3) {
    			return Optional.of(USAGE);
    
    		Logging.init();
    
    		Logger log = Logger.getLogger(HazardCalc.class.getName());
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    
    			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))
    
    					.build();
    
    				out = userConfigPath.getParent();
    
    			}
    			log.info(config.toString());
    
    
    			
    			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();
    
    
    		} catch (Exception e) {
    
    			StringBuilder sb = new StringBuilder()
    
    				.append(NEWLINE)
    
    				.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());
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    		}
    	}
    
    	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 };
    
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	/*
    
    	 * Compute hazard curves using the supplied model, config, and site files.
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	 */
    
    	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();
    
    		int count = 0;
    
    
    		List<Hazard> results = new ArrayList<>();
    
    		boolean firstBatch = true;
    
    		for (Site site : sites) {
    
    			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 +
    
    					"  total: " + totalWatch);
    
    				results.clear();
    
    				batchWatch.reset().start();
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    			}
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    		}
    
    		// write final batch
    		if (!results.isEmpty()) {
    			OpenOption[] opts = firstBatch ? WRITE_OPTIONS : APPEND_OPTIONS;
    
    			Results.writeResults(out, results, opts);
    
    		log.info(PROGRAM + ": " + count + " complete " + totalWatch);
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    
    		execSvc.shutdown();
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	/**
    
    	 * 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
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	 */
    
    	public static Hazard calc(
    
    			HazardModel model,
    			CalcConfig config,
    			Site site,
    			Optional<Executor> executor) {
    
    
    		Optional<Executor> execLocal = executor.or(Optional.of(createExecutor()));
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    
    
    			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());
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    	}
    
    	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)
    
    Powers, Peter M.'s avatar
    Powers, Peter M. committed
    		.toString();