diff --git a/gradle.properties b/gradle.properties index cb06f8f26b77edd164e23c43d752185c93c3bfb1..59ab9e79e40dc7b657035a2ccb3f120e43d82450 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ micronautRxVersion = 2.1.1 micronautPluginVersion = 3.1.1 nodePluginVersion = 3.0.1 nodeVersion = 16.3.0 -nshmpLibVersion = 0.9.1 +nshmpLibVersion = 0.9.3 nshmpWsUtilsVersion = 0.1.7 shadowVersion = 7.1.2 spotbugsVersion = 4.7.0 diff --git a/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java b/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java index dd59717afd96208dc9e6e6dfb2734cc2617f9607..de59f18b22b4d55af6e5b48e9a2daea542165798 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java @@ -3,6 +3,9 @@ package gov.usgs.earthquake.nshmp; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static gov.usgs.earthquake.nshmp.Text.NEWLINE; +import static gov.usgs.earthquake.nshmp.calc.DataType.DISAGG_DATA; +import static gov.usgs.earthquake.nshmp.calc.DataType.GMM; +import static gov.usgs.earthquake.nshmp.calc.DataType.SOURCE; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @@ -166,16 +169,11 @@ public class DisaggCalc { */ Path out; if (siteColumns.size() == allColumns.size()) { + checkArgument( modelImts.containsAll(config.hazard.imts), "Config specifies IMTs not supported by model"); - - // List<Imt> imts = config.imts; - - // Path out = calc(model, config, sites, imtImlMaps, log); - double returnPeriod = config.disagg.returnPeriod; - out = calcRp(model, config, sites, returnPeriod, log); } else { @@ -189,19 +187,9 @@ public class DisaggCalc { sites.size() == imls.size(), "Sites and spectra lists different sizes"); log.info("Spectra: " + imls.size()); // 1:1 with sites - out = calcIml(model, config, sites, imls, log); - } - - // List<Map<Imt, Double>> imtImlMaps = readSpectra(siteFile, imts, - // colsToSkip); - // log.info("Spectra: " + imtImlMaps.size()); - - // checkArgument(sites.size() == imtImlMaps.size(), "Sites and spectra - // lists different sizes"); - // Spectra should be checked against IMTs supported by model GMMs - // Path out = calc(model, config, sites, imls, log); + } log.info(PROGRAM + ": finished"); @@ -307,16 +295,13 @@ public class DisaggCalc { Site site = sites.get(i); Hazard hazard = HazardCalcs.hazard(model, config, site, exec); + handler.write(hazard); Map<Imt, Double> imls = imlsForReturnPeriod(hazard, returnPeriod); - Disaggregation disagg = Disaggregation.atImls(hazard, imls, exec); - // needs to handle disagg same way as iml - // handler.write(hazard, Optional.of(disagg)); - handler.write(hazard, Optional.empty()); - Response response = new Response.Builder() + .config(config) .site(site) .returnPeriod(returnPeriod) .imls(imls) @@ -346,13 +331,13 @@ public class DisaggCalc { } /* Hazard curves are already in log-x space. */ - static final Interpolator IML_INTERPOLATER = Interpolator.builder() + private static final Interpolator IML_INTERPOLATER = Interpolator.builder() .logy() .decreasingY() .build(); - // this should be in a factory - private static Map<Imt, Double> imlsForReturnPeriod( + /** Compute the return period intercepts from a hazard result. */ + public static Map<Imt, Double> imlsForReturnPeriod( Hazard hazard, double returnPeriod) { @@ -360,7 +345,6 @@ public class DisaggCalc { Map<Imt, Double> imls = new EnumMap<>(Imt.class); for (Entry<Imt, XySequence> entry : hazard.curves().entrySet()) { double iml = IML_INTERPOLATER.findX(entry.getValue(), rate); - // remove exp below by transforming disagg-epsilon to log earlier imls.put(entry.getKey(), Math.exp(iml)); } return imls; @@ -408,6 +392,7 @@ public class DisaggCalc { Disaggregation disagg = Disaggregation.atImls(hazard, siteImls, exec); Response response = new Response.Builder() + .config(config) .site(site) .imls(siteImls) .disagg(disagg) @@ -470,10 +455,11 @@ public class DisaggCalc { static final class Builder { - Disaggregation disagg; Site site; + Disaggregation disagg; Double returnPeriod; // optional Map<Imt, Double> imls; + CalcConfig config; Builder imls(Map<Imt, Double> imls) { this.imls = imls; @@ -495,10 +481,20 @@ public class DisaggCalc { return this; } + Builder config(CalcConfig config) { + this.config = config; + return this; + } + Response build() { + // default toJson(imt, false, false, false) List<ImtDisagg> disaggs = imls.keySet().stream() - .map(imt -> new ImtDisagg(imt, disagg.toJson(imt))) + .map(imt -> new ImtDisagg(imt, disagg.toJson( + imt, + config.output.dataTypes.contains(GMM), + config.output.dataTypes.contains(SOURCE), + config.output.dataTypes.contains(DISAGG_DATA)))) .collect(toList()); return new Response( diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java index 27ab4200b2d48cab86285d437dffc3bb24170e31..a525b1b8c1124939e10f9383e5f43409f8d99297 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java @@ -5,9 +5,12 @@ import static com.google.common.base.Preconditions.checkArgument; import java.util.Map; import java.util.Set; +import gov.usgs.earthquake.nshmp.calc.DataType; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.hazard.DisaggService.DisaggDataType; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.HazardImt; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; @@ -63,11 +66,9 @@ public class DisaggController { * @param returnPeriod The return period of the target ground motion, or * intensity measure level (IML), in the range [1..20000] years. * @param imt Optional IMTs at which to compute hazard. If none are supplied, - * then the supported set for the installed model is used. Note that a - * model may not support all the values listed below (see - * disagreggation metadata). Responses for numerous IMT's are quite - * large, on the order of MB. Multiple IMTs may be comma delimited, - * e.g. ?imt=PGA,SA0p2,SA1P0. + * then the supported set for the installed model is used. Responses + * for numerous IMT's are quite large, on the order of MB. + * @param out The data types to output */ @Operation( summary = "Disaggregate hazard at a specified return period", @@ -92,14 +93,17 @@ public class DisaggController { @Schema( minimum = "150", maximum = "3000") @PathVariable double returnPeriod, - @Schema() @QueryValue @Nullable Set<Imt> imt) { + @QueryValue @Nullable Set<HazardImt> imt, + @QueryValue @Nullable Set<DisaggDataType> out) { try { Set<Imt> imts = HazardService.readImts(http); + Set<DataType> dataTypes = HazardService.readDataTypes(http); DisaggService.RequestRp request = new DisaggService.RequestRp( http, longitude, latitude, vs30, returnPeriod, - imts); + imts, + dataTypes); return DisaggService.getDisaggRp(request); } catch (Exception e) { return ServletUtil.error( @@ -113,6 +117,7 @@ public class DisaggController { * @param longitude Longitude in the range [-360..360]°. * @param latitude Latitude in decimal degrees [-90..90]°. * @param vs30 Site Vs30 value in the range [150..3000] m/s. + * @param out The data types to output */ @Operation( summary = "Disaggregate hazard at specified IMLs", @@ -133,23 +138,26 @@ public class DisaggController { maximum = "90") @PathVariable double latitude, @Schema( minimum = "150", - maximum = "3000") @PathVariable double vs30) { + maximum = "3000") @PathVariable double vs30, + @QueryValue @Nullable Set<DisaggDataType> out) { /* * Developer notes: * * It is awkward to support IMT=#; numerous unique keys that may or may not * be present yields a clunky swagger interface. The disagg-iml endpoint - * requires one or more IMT=# query arguments. Documented in example. + * requires one or more IMT=# query arguments. Document in example. */ try { Map<Imt, Double> imtImlMap = http.getParameters().asMap(Imt.class, Double.class); checkArgument(!imtImlMap.isEmpty(), "No IMLs supplied"); + Set<DataType> dataTypes = HazardService.readDataTypes(http); DisaggService.RequestIml request = new DisaggService.RequestIml( http, longitude, latitude, vs30, - imtImlMap); + imtImlMap, + dataTypes); return DisaggService.getDisaggIml(request); } catch (Exception e) { return ServletUtil.error( diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java index 842194aaa68257ee447de8a9611b85dcbc2c475d..04c4b44afe320a329c758bf04ad8d9a9eba7ce32 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java @@ -1,5 +1,8 @@ package gov.usgs.earthquake.nshmp.www.hazard; +import static gov.usgs.earthquake.nshmp.calc.DataType.DISAGG_DATA; +import static gov.usgs.earthquake.nshmp.calc.DataType.GMM; +import static gov.usgs.earthquake.nshmp.calc.DataType.SOURCE; import static java.util.stream.Collectors.toList; import java.util.List; @@ -15,7 +18,9 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Stopwatch; import com.google.common.collect.Range; +import gov.usgs.earthquake.nshmp.DisaggCalc; import gov.usgs.earthquake.nshmp.calc.CalcConfig; +import gov.usgs.earthquake.nshmp.calc.DataType; import gov.usgs.earthquake.nshmp.calc.Disaggregation; import gov.usgs.earthquake.nshmp.calc.Hazard; import gov.usgs.earthquake.nshmp.calc.HazardCalcs; @@ -54,8 +59,15 @@ public final class DisaggService { private static Range<Double> rpRange = Range.closed(1.0, 20000.0); private static Range<Double> imlRange = Range.closed(0.0001, 8.0); + /* For Swagger selctions */ + enum DisaggDataType { + GMM, + SOURCE, + DISAGG_DATA; + } + /** HazardController.doGetMetadata() handler. */ - public static HttpResponse<String> getMetadata(HttpRequest<?> request) { + static HttpResponse<String> getMetadata(HttpRequest<?> request) { var url = request.getUri().toString(); var usage = new Metadata(ServletUtil.model()); var response = ResponseBody.usage() @@ -69,7 +81,7 @@ public final class DisaggService { } /** HazardController.doGetDisaggIml() handler. */ - public static HttpResponse<String> getDisaggIml(RequestIml request) + static HttpResponse<String> getDisaggIml(RequestIml request) throws InterruptedException, ExecutionException { var stopwatch = Stopwatch.createStarted(); var disagg = calcDisaggIml(request); @@ -89,7 +101,7 @@ public final class DisaggService { } /** HazardController.doGetDisaggRp() handler. */ - public static HttpResponse<String> getDisaggRp(RequestRp request) + static HttpResponse<String> getDisaggRp(RequestRp request) throws InterruptedException, ExecutionException { var stopwatch = Stopwatch.createStarted(); var disagg = calcDisaggRp(request); @@ -116,7 +128,7 @@ public final class DisaggService { * */ - static Disaggregation calcDisaggIml(RequestIml request) + private static Disaggregation calcDisaggIml(RequestIml request) throws InterruptedException, ExecutionException { HazardModel model = ServletUtil.model(); @@ -124,6 +136,7 @@ public final class DisaggService { // modify config to include service endpoint arguments CalcConfig config = CalcConfig.copyOf(model.config()) .imts(request.imls.keySet()) + // .dataTypes(request.dataTypes) .build(); // TODO this needs to pick up SiteData, centralize @@ -152,7 +165,7 @@ public final class DisaggService { return disagg; } - static Disaggregation calcDisaggRp(RequestRp request) + private static Disaggregation calcDisaggRp(RequestRp request) throws InterruptedException, ExecutionException { HazardModel model = ServletUtil.model(); @@ -160,6 +173,7 @@ public final class DisaggService { // modify config to include service endpoint arguments CalcConfig config = CalcConfig.copyOf(model.config()) .imts(request.imts) + // .dataTypes(request.dataTypes) .build(); // TODO this needs to pick up SiteData, centralize @@ -176,10 +190,13 @@ public final class DisaggService { ServletUtil.TASK_EXECUTOR); Hazard hazard = hazFuture.get(); + Map<Imt, Double> imls = DisaggCalc.imlsForReturnPeriod( + hazard, + request.returnPeriod); CompletableFuture<Disaggregation> disaggfuture = CompletableFuture.supplyAsync( - () -> Disaggregation.atReturnPeriod( - hazard, request.returnPeriod, + () -> Disaggregation.atImls( + hazard, imls, ServletUtil.CALC_EXECUTOR), ServletUtil.TASK_EXECUTOR); @@ -195,19 +212,22 @@ public final class DisaggService { final double latitude; final double vs30; final Map<Imt, Double> imls; + final Set<DataType> dataTypes; RequestIml( HttpRequest<?> http, double longitude, double latitude, double vs30, - Map<Imt, Double> imls) { + Map<Imt, Double> imls, + Set<DataType> dataTypes) { this.http = http; this.longitude = longitude; this.latitude = latitude; this.vs30 = vs30; this.imls = imls; + this.dataTypes = dataTypes; } } @@ -219,6 +239,7 @@ public final class DisaggService { final double vs30; final double returnPeriod; final Set<Imt> imts; + final Set<DataType> dataTypes; RequestRp( HttpRequest<?> http, @@ -226,7 +247,8 @@ public final class DisaggService { double latitude, double vs30, double returnPeriod, - Set<Imt> imts) { + Set<Imt> imts, + Set<DataType> dataTypes) { this.http = http; this.longitude = longitude; @@ -236,6 +258,7 @@ public final class DisaggService { this.imts = imts.isEmpty() ? ServletUtil.model().config().hazard.imts : imts; + this.dataTypes = dataTypes; } } @@ -267,6 +290,7 @@ public final class DisaggService { Optional<RequestRp> requestRp = Optional.empty(); Optional<RequestIml> requestIml = Optional.empty(); Disaggregation disagg; + CalcConfig config; Builder timer(Stopwatch timer) { this.timer = timer; @@ -293,8 +317,16 @@ public final class DisaggService { ? requestRp.orElseThrow().imts : requestIml.orElseThrow().imls.keySet(); + Set<DataType> dataTypes = requestRp.isPresent() + ? requestRp.orElseThrow().dataTypes + : requestIml.orElseThrow().dataTypes; + List<ImtDisagg> disaggs = imts.stream() - .map(imt -> new ImtDisagg(imt, disagg.toJson(imt))) + .map(imt -> new ImtDisagg(imt, disagg.toJson( + imt, + dataTypes.contains(GMM), + dataTypes.contains(SOURCE), + dataTypes.contains(DISAGG_DATA)))) .collect(toList()); Object server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java index 1646f6ae279182588d70025a44a000858b094a4c..03bc22142d9da072cdd6991f2269d389f1981095 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java @@ -5,6 +5,7 @@ import java.util.Set; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.HazardImt; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; @@ -60,11 +61,8 @@ public class HazardController { * @param truncate Truncate curves at return periods below ~10,000 years * @param maxdir Apply max-direction scaling * @param imt Optional IMTs at which to compute hazard. If none are supplied, - * then the supported set for the installed model is used. Note that a - * model may not support all the values listed below (see - * disagreggation metadata). Responses for numerous IMT's are quite - * large, on the order of MB. Multiple IMTs may be comma delimited, - * e.g. ?imt=PGA,SA0p2,SA1P0. + * then the supported set for the installed model is used. Responses + * for numerous IMT's are quite large, on the order of MB. * */ @Operation( @@ -91,7 +89,7 @@ public class HazardController { defaultValue = "false") @Nullable Boolean truncate, @QueryValue( defaultValue = "false") @Nullable Boolean maxdir, - @QueryValue @Nullable Set<Imt> imt) { + @QueryValue @Nullable Set<HazardImt> imt) { try { Set<Imt> imts = HazardService.readImts(http); HazardService.Request request = new HazardService.Request( diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java index f668b016e76468c696e0759fa3cee55077c2451e..c5c278ee2d53ed744cb999c07d06413dd0c52897 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java @@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Stopwatch; import gov.usgs.earthquake.nshmp.calc.CalcConfig; +import gov.usgs.earthquake.nshmp.calc.DataType; import gov.usgs.earthquake.nshmp.calc.Hazard; import gov.usgs.earthquake.nshmp.calc.HazardCalcs; import gov.usgs.earthquake.nshmp.calc.Site; @@ -57,6 +58,33 @@ public final class HazardService { private static final String TOTAL_KEY = "Total"; + /* For Swagger selections; mprs + pgv */ + enum HazardImt { + PGA, + PGV, + SA0P01, + SA0P02, + SA0P03, + SA0P05, + SA0P075, + SA0P1, + SA0P15, + SA0P2, + SA0P25, + SA0P3, + SA0P4, + SA0P5, + SA0P75, + SA1P0, + SA1P5, + SA2P0, + SA3P0, + SA4P0, + SA5P0, + SA7P5, + SA10P0; + } + /** HazardController.doGetUsage() handler. */ public static HttpResponse<String> getMetadata(HttpRequest<?> request) { var url = request.getUri().toString(); @@ -155,7 +183,7 @@ public final class HazardService { } } - public static final class Request { + static final class Request { final transient HttpRequest<?> http; final double longitude; @@ -347,12 +375,19 @@ public final class HazardService { /* Read the 'imt' query values; can be comma-delimited. */ static Set<Imt> readImts(HttpRequest<?> http) { - return http.getParameters() - .getAll("imt")// TODO where are key strings? - .stream() + return http.getParameters().getAll("imt").stream() .map(s -> s.split(",")) .flatMap(Arrays::stream) .map(Imt::valueOf) .collect(toCollection(() -> EnumSet.noneOf(Imt.class))); } + + /* Read the 'imt' query values; can be comma-delimited. */ + static Set<DataType> readDataTypes(HttpRequest<?> http) { + return http.getParameters().getAll("out").stream() + .map(s -> s.split(",")) + .flatMap(Arrays::stream) + .map(DataType::valueOf) + .collect(toCollection(() -> EnumSet.noneOf(DataType.class))); + } }