From 813e01ae5a47f6b011ae5dbd37eb0de34c85b229 Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Sun, 30 Jan 2022 11:06:52 -0700 Subject: [PATCH 1/7] merged old stash for running local lib and conus --- gradle/dependencies.gradle | 2 ++ settings.gradle | 3 +++ src/main/java/gov/usgs/earthquake/nshmp/DisaggEpsilon.java | 1 + src/main/resources/application.yml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 2a81e3a09..af19505cd 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -1,6 +1,8 @@ dependencies { + // NSHMP + // implementation files('../nshmp-lib/build/libs/nshmp-lib.jar') implementation "ghsc:nshmp-lib:${nshmpLibVersion}" implementation "ghsc:nshmp-ws-utils:${nshmpWsUtilsVersion}" diff --git a/settings.gradle b/settings.gradle index 90c2faad1..d1ad3db91 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,6 +20,9 @@ git { fetch("https://code.usgs.gov/ghsc/nshmp/nshms/nshm-hawaii.git", { name "nshmp-haz-dep--nshm-hi-2021" tag "2.0.0" + // fetch("https://code.usgs.gov/ghsc/nshmp/nshms/nshm-conus.git", { + // name "nshmp-haz-dep--nshm-conus-2018" + // tag "main" }) } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/DisaggEpsilon.java b/src/main/java/gov/usgs/earthquake/nshmp/DisaggEpsilon.java index d10e9eda7..e4d039601 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/DisaggEpsilon.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/DisaggEpsilon.java @@ -146,6 +146,7 @@ public class DisaggEpsilon { 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, imtImlMaps, log); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d4d11ef0c..5ca335064 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,4 +20,6 @@ nshmp-haz: # The path to the models. # To specify the model to use: # java -jar build/libs/nshmp-haz.jar --models=<path/to/models> + # model-path: ${models:libs/nshmp-haz-dep--nshm-hi-2021} + # model-path: ${models:libs/nshmp-haz-dep--nshm-conus-2018} -- GitLab From f591f79ff9902c2701e4e1365b83f3378afe701d Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Mon, 31 Jan 2022 10:50:57 -0700 Subject: [PATCH 2/7] moved hazard to own package; simplified request-response --- .../www/{services => }/ServicesUtil.java | 17 +- .../nshmp/www/{services => }/ServletUtil.java | 12 +- .../nshmp/www/SwaggerController.java | 2 - .../www/{ => hazard}/HazardController.java | 57 ++- .../{services => hazard}/HazardService.java | 245 ++++------ .../{services => hazard}/MaxDirection.java | 2 +- .../earthquake/nshmp/www/meta/Metadata.java | 2 +- .../nshmp/www/services/HazardService2.java | 452 ------------------ .../nshmp/www/services/RateService.java | 9 +- .../www/services/SourceLogicTreesService.java | 2 + .../nshmp/www/services/SourceServices.java | 5 +- 11 files changed, 146 insertions(+), 659 deletions(-) rename src/main/java/gov/usgs/earthquake/nshmp/www/{services => }/ServicesUtil.java (88%) rename src/main/java/gov/usgs/earthquake/nshmp/www/{services => }/ServletUtil.java (94%) rename src/main/java/gov/usgs/earthquake/nshmp/www/{ => hazard}/HazardController.java (63%) rename src/main/java/gov/usgs/earthquake/nshmp/www/{services => hazard}/HazardService.java (62%) rename src/main/java/gov/usgs/earthquake/nshmp/www/{services => hazard}/MaxDirection.java (97%) delete mode 100644 src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService2.java diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServicesUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java similarity index 88% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/ServicesUtil.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java index b2da3f091..0da190978 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServicesUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java @@ -1,4 +1,4 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -11,9 +11,6 @@ import gov.usgs.earthquake.nshmp.calc.Hazard; import gov.usgs.earthquake.nshmp.calc.HazardCalcs; import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.model.HazardModel; -import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.WsUtils; - import io.micronaut.http.HttpResponse; public class ServicesUtil { @@ -35,7 +32,8 @@ public class ServicesUtil { return HttpResponse.serverError(response); } - static Hazard calcHazard( + @Deprecated + public static Hazard calcHazard( Function<HazardModel, CalcConfig> configFunction, Function<CalcConfig, Site> siteFunction) throws InterruptedException, ExecutionException { @@ -47,12 +45,12 @@ public class ServicesUtil { } @Deprecated - static class ServiceQueryData implements ServiceQuery { + public static class ServiceQueryData implements ServiceQuery { public final Double longitude; public final Double latitude; - ServiceQueryData(Double longitude, Double latitude) { + public ServiceQueryData(Double longitude, Double latitude) { this.longitude = longitude; this.latitude = latitude; } @@ -70,7 +68,7 @@ public class ServicesUtil { } @Deprecated - static class ServiceRequestData { + public static class ServiceRequestData { public final double longitude; public final double latitude; @@ -81,7 +79,7 @@ public class ServicesUtil { } } - enum Key { + public enum Key { EDITION, REGION, MODEL, @@ -114,6 +112,7 @@ public class ServicesUtil { void checkValues(); } + @Deprecated private static CompletableFuture<Hazard> calcHazard( HazardModel model, CalcConfig config, diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServletUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java similarity index 94% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/ServletUtil.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java index 81651afd7..3de64116a 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServletUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java @@ -1,4 +1,4 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www; import static java.lang.Runtime.getRuntime; @@ -27,9 +27,7 @@ import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.calc.ValueFormat; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.model.HazardModel; -import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.MetaUtil; - import io.micronaut.context.annotation.Value; import io.micronaut.context.event.ShutdownEvent; import io.micronaut.context.event.StartupEvent; @@ -46,10 +44,10 @@ public class ServletUtil { public static final Gson GSON; - static final ListeningExecutorService CALC_EXECUTOR; - static final ExecutorService TASK_EXECUTOR; + public static final ListeningExecutorService CALC_EXECUTOR; + public static final ExecutorService TASK_EXECUTOR; - static final int THREAD_COUNT; + public static final int THREAD_COUNT; @Value("${nshmp-haz.model-path}") private Path modelPath; @@ -73,7 +71,7 @@ public class ServletUtil { .create(); } - static HazardModel model() { + public static HazardModel model() { return HAZARD_MODEL; } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java index c2251caba..6cd6d6d04 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java @@ -5,8 +5,6 @@ import java.util.stream.Collectors; import com.google.common.io.Resources; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.MediaType; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/HazardController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java similarity index 63% rename from src/main/java/gov/usgs/earthquake/nshmp/www/HazardController.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java index fdc747478..54c765def 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/HazardController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java @@ -1,8 +1,7 @@ -package gov.usgs.earthquake.nshmp.www; - -import gov.usgs.earthquake.nshmp.www.services.HazardService; -import gov.usgs.earthquake.nshmp.www.services.HazardService.QueryParameters; +package gov.usgs.earthquake.nshmp.www.hazard; +import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.Controller; @@ -16,9 +15,9 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; /** - * Micronaut controller for probabilistic seismic hazard calculations. + * Micronaut controller for probabilistic seismic hazard calculations and + * services. * - * @see HazardService * @author U.S. Geological Survey */ @Tag( @@ -32,8 +31,7 @@ public class HazardController { @Operation( summary = "Hazard model and service metadata", - description = "Returns details of the installed model and service request parameters", - operationId = "hazard_doGetMetadata") + description = "Returns details of the installed model and service request parameters") @ApiResponse( description = "Hazard service metadata", responseCode = "200") @@ -51,31 +49,32 @@ public class HazardController { */ @Operation( summary = "Compute probabilisitic hazard at a site", - description = "Returns hazard curves computed from the installed model", - operationId = "hazard_doGetHazard") + description = "Returns hazard curves computed from the installed model") @ApiResponse( description = "Hazard curves", responseCode = "200") @Get(uri = "/{longitude}/{latitude}/{vs30}{?truncate,maxdir}") public HttpResponse<String> doGetHazard( - HttpRequest<?> request, - - @Schema(minimum = "-360", maximum = "360") @PathVariable double longitude, - - @Schema(minimum = "-90", maximum = "90") @PathVariable double latitude, - - @Schema(minimum = "150", maximum = "3000") @PathVariable int vs30, - - @QueryValue(defaultValue = "false") boolean truncate, - - @QueryValue(defaultValue = "false") boolean maxdir) { - - /* - * @Schema annotation parameter constraints only affect Swagger service - * index page behavior; still need to validate against model. TODO - */ - - var query = new QueryParameters(longitude, latitude, vs30, truncate, maxdir); - return HazardService.handleDoGetHazard(request, query); + HttpRequest<?> http, + @Schema( + minimum = "-360", + maximum = "360") @PathVariable double longitude, + @Schema( + minimum = "-90", + maximum = "90") @PathVariable double latitude, + @Schema( + minimum = "150", + maximum = "3000") @PathVariable int vs30, + @QueryValue( + defaultValue = "false") boolean truncate, + @QueryValue( + defaultValue = "false") boolean maxdir) { + try { + HazardService.Request request = new HazardService.Request( + http, longitude, latitude, vs30, truncate, maxdir); + return HazardService.processRequest(request); + } catch (Exception e) { + return ServicesUtil.handleError(e, HazardService.NAME, http.getUri().getPath()); + } } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java similarity index 62% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java index 89f03a87b..90cf98ef5 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java @@ -1,20 +1,24 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www.hazard; import static com.google.common.base.Preconditions.checkState; import static gov.usgs.earthquake.nshmp.calc.HazardExport.curvesBySource; +import static gov.usgs.earthquake.nshmp.data.DoubleData.checkInRange; +import static gov.usgs.earthquake.nshmp.geo.Coordinates.checkLatitude; +import static gov.usgs.earthquake.nshmp.geo.Coordinates.checkLongitude; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.function.Function; import com.google.common.base.Stopwatch; import gov.usgs.earthquake.nshmp.calc.CalcConfig; import gov.usgs.earthquake.nshmp.calc.Hazard; +import gov.usgs.earthquake.nshmp.calc.HazardCalcs; import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.data.MutableXySequence; import gov.usgs.earthquake.nshmp.data.XySequence; @@ -23,16 +27,13 @@ import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.model.SourceType; -import gov.usgs.earthquake.nshmp.www.HazardController; import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.WsUtils; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Parameter; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceQueryData; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceRequestData; import gov.usgs.earthquake.nshmp.www.services.SourceServices.SourceModel; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; @@ -46,13 +47,13 @@ import jakarta.inject.Singleton; @Singleton public final class HazardService { - private static final String NAME = "Hazard Service"; + static final String NAME = "Hazard Service"; /** HazardController.doGetUsage() handler. */ public static HttpResponse<String> handleDoGetMetadata(HttpRequest<?> request) { var url = request.getUri().getPath(); try { - var usage = new RequestMetadata(ServletUtil.model()); + var usage = new UsageMetadata(ServletUtil.model()); var response = ResponseBody.usage() .name(NAME) .url(url) @@ -67,111 +68,82 @@ public final class HazardService { } /** HazardController.doGetHazard() handler. */ - public static HttpResponse<String> handleDoGetHazard( - HttpRequest<?> request, - QueryParameters query) { - + public static HttpResponse<String> processRequest(Request request) { try { - // TODO still need to validate - // if (query.isEmpty()) { - // return handleDoGetUsage(urlHelper); - // } - // query.checkParameters(); - var data = new RequestData(query); - var response = process(request, data); - var svcResponse = ServletUtil.GSON.toJson(response); + Response response = process(request); + var body = ResponseBody.success() + .name(NAME) + .url(request.http.getUri().getPath()) + .request(request) + .response(response) + .build(); + String svcResponse = ServletUtil.GSON.toJson(body); return HttpResponse.ok(svcResponse); } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, request.getUri().getPath()); + return ServicesUtil.handleError(e, NAME, request.http.getUri().getPath()); } } - static ResponseBody<RequestData, ResponseData> process( - HttpRequest<?> request, - RequestData data) + /* + * Developer notes: + * + * Future calculation configuration options: vertical GMs + * + * NSHM Hazard Tool will not pass truncation and maxdir args/flags as the apps + * apply truncation and scaling on the client. + */ + + static Response process(Request request) throws InterruptedException, ExecutionException { - var configFunction = new ConfigFunction(); - var siteFunction = new SiteFunction(data); var stopwatch = Stopwatch.createStarted(); - var hazard = ServicesUtil.calcHazard(configFunction, siteFunction); + var hazard = calcHazard(request); return new ResultBuilder() + .request(request) .hazard(hazard) - .requestData(data) .timer(stopwatch) - .url(request) .build(); } - static class ConfigFunction implements Function<HazardModel, CalcConfig> { - @Override - public CalcConfig apply(HazardModel model) { - var configBuilder = CalcConfig.copyOf(model.config()); - return configBuilder.build(); - } - } + public static Hazard calcHazard(Request request) + throws InterruptedException, ExecutionException { - static class SiteFunction implements Function<CalcConfig, Site> { - final RequestData data; + HazardModel model = ServletUtil.model(); - private SiteFunction(RequestData data) { - this.data = data; - } + // will we be passing in options for config?? + CalcConfig config = CalcConfig.copyOf(model.config()).build(); - @Override // TODO this needs to pick up SiteData - public Site apply(CalcConfig config) { - return Site.builder() - .location(Location.create(data.longitude, data.latitude)) - .vs30(data.vs30) - .build(); - } + // TODO this needs to pick up SiteData + Site site = Site.builder() + .location(Location.create(request.longitude, request.latitude)) + .vs30(request.vs30) + .build(); + CompletableFuture<Hazard> future = futureHazard(model, config, site); + return future.get(); } - public static class QueryParameters { + private static CompletableFuture<Hazard> futureHazard( + HazardModel model, + CalcConfig config, + Site site) { - final double longitude; - final double latitude; - final int vs30; - final boolean truncate; - final boolean maxdir; - - public QueryParameters( - double longitude, - double latitude, - int vs30, - boolean truncate, - boolean maxdir) { - - this.longitude = longitude; - this.latitude = latitude; - this.vs30 = vs30; - this.truncate = truncate; - this.maxdir = maxdir; - } - - // void checkParameters() { - // checkParameter(longitude, "longitude"); - // checkParameter(latitude, "latitude"); - // checkParameter(vs30, "vs30"); - // } + return CompletableFuture.supplyAsync( + () -> HazardCalcs.hazard(model, config, site, ServletUtil.CALC_EXECUTOR), + ServletUtil.TASK_EXECUTOR); } - // private static void checkParameter(Object param, String id) { - // checkNotNull(param, "Missing parameter: %s", id); - // // TODO check range here - // } - - /* Service request and model metadata */ - static class RequestMetadata { + private static class UsageMetadata { final SourceModel model; final DoubleParameter longitude; final DoubleParameter latitude; final DoubleParameter vs30; - RequestMetadata(HazardModel model) { + UsageMetadata(HazardModel model) { this.model = new SourceModel(model); + // perhaps move out to shared factory with parameter instances + // // TODO need min max from model longitude = new DoubleParameter( "Longitude", @@ -186,27 +158,36 @@ public final class HazardService { Coordinates.LAT_RANGE.upperEndpoint()); vs30 = new DoubleParameter( - "Latitude", + "Vs30", "m/s", 150, 1500); } } - static class RequestData { + public static final class Request { + transient HttpRequest<?> http; final double longitude; final double latitude; final double vs30; final boolean truncate; final boolean maxdir; - RequestData(QueryParameters query) { - this.longitude = query.longitude; - this.latitude = query.latitude; - this.vs30 = query.vs30; - this.truncate = query.truncate; - this.maxdir = query.maxdir; + public Request( + HttpRequest<?> http, + double longitude, + double latitude, + int vs30, + boolean truncate, + boolean maxdir) { + + this.http = http; + this.longitude = checkLongitude(longitude); + this.latitude = checkLatitude(latitude); + this.vs30 = checkInRange(Site.VS30_RANGE, Site.Key.VS30, vs30); + this.truncate = truncate; + this.maxdir = maxdir; } } @@ -220,40 +201,21 @@ public final class HazardService { } } - private static String imtShortLabel(Imt imt) { - if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { - return imt.name(); - } else if (imt.isSA()) { - return imt.period() + " s"; - } - return imt.toString(); - } - - @Deprecated - static class RequestDataOld extends ServiceRequestData { - final double vs30; - - RequestDataOld(Query query, double vs30) { - super(query); - this.vs30 = vs30; - } - } - - private static final class ResponseData { + private static final class Response { final ResponseMetadata metadata; - final List<HazardResponse> hazardCurves; + final List<ImtCurves> hazardCurves; - ResponseData(ResponseMetadata metadata, List<HazardResponse> hazardCurves) { + Response(ResponseMetadata metadata, List<ImtCurves> hazardCurves) { this.metadata = metadata; this.hazardCurves = hazardCurves; } } - private static final class HazardResponse { + private static final class ImtCurves { final Parameter imt; final List<Curve> data; - HazardResponse(Imt imt, List<Curve> data) { + ImtCurves(Imt imt, List<Curve> data) { this.imt = new Parameter(imtShortLabel(imt), imt.name()); this.data = data; } @@ -273,9 +235,8 @@ public final class HazardService { private static final class ResultBuilder { - String url; Stopwatch timer; - RequestData request; + Request request; Map<Imt, Map<SourceType, MutableXySequence>> componentMaps; Map<Imt, MutableXySequence> totalMap; @@ -311,23 +272,18 @@ public final class HazardService { return this; } - ResultBuilder url(HttpRequest<?> request) { - url = request.getUri().getPath(); - return this; - } - ResultBuilder timer(Stopwatch timer) { this.timer = timer; return this; } - ResultBuilder requestData(RequestData request) { + ResultBuilder request(Request request) { this.request = request; return this; } - ResponseBody<RequestData, ResponseData> build() { - var hazards = new ArrayList<HazardResponse>(); + Response build() { + var hazards = new ArrayList<ImtCurves>(); for (Imt imt : totalMap.keySet()) { var curves = new ArrayList<Curve>(); @@ -345,18 +301,15 @@ public final class HazardService { updateCurve(request, typeMap.get(type), imt))); } - hazards.add(new HazardResponse(imt, List.copyOf(curves))); + hazards.add(new ImtCurves(imt, List.copyOf(curves))); } Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); - var response = new ResponseData(new ResponseMetadata(server), List.copyOf(hazards)); + var response = new Response( + new ResponseMetadata(server), + List.copyOf(hazards)); - return ResponseBody.<RequestData, ResponseData> success() - .name(NAME) - .url(url) - .request(request) - .response(response) - .build(); + return response; } } @@ -364,7 +317,7 @@ public final class HazardService { /* Convert to linear and possibly truncate and scale to max-direction. */ private static XySequence updateCurve( - RequestData request, + Request request, XySequence curve, Imt imt) { @@ -398,25 +351,13 @@ public final class HazardService { return limit; } - @Deprecated - public static class Query extends ServiceQueryData { - Integer vs30; - - public Query(Double longitude, Double latitude, Integer vs30) { - super(longitude, latitude); - this.vs30 = vs30; - } - - @Override - public boolean isNull() { - return super.isNull() && vs30 == null; - } - - @Override - public void checkValues() { - super.checkValues(); - WsUtils.checkValue(ServicesUtil.Key.VS30, vs30); + private static String imtShortLabel(Imt imt) { + if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { + return imt.name(); + } else if (imt.isSA()) { + return imt.period() + " s"; } + return imt.toString(); } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/MaxDirection.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/MaxDirection.java similarity index 97% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/MaxDirection.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/hazard/MaxDirection.java index e35c59355..99c40c42d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/MaxDirection.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/MaxDirection.java @@ -1,4 +1,4 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www.hazard; import static gov.usgs.earthquake.nshmp.gmm.Imt.PGA; import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P01; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java index 1120be850..ed8a2973d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java @@ -4,7 +4,7 @@ import com.google.common.base.Stopwatch; import com.google.common.base.Throwables; import gov.usgs.earthquake.nshmp.geo.Coordinates; -import gov.usgs.earthquake.nshmp.www.services.ServletUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; /** * Service metadata, parameterization, and constraint strings, in JSON format. diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService2.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService2.java deleted file mode 100644 index 63fb4261b..000000000 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService2.java +++ /dev/null @@ -1,452 +0,0 @@ -package gov.usgs.earthquake.nshmp.www.services; - -import static com.google.common.base.Preconditions.checkState; -import static gov.usgs.earthquake.nshmp.calc.HazardExport.curvesBySource; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.function.Function; - -import com.google.common.base.Stopwatch; - -import gov.usgs.earthquake.nshmp.calc.CalcConfig; -import gov.usgs.earthquake.nshmp.calc.Hazard; -import gov.usgs.earthquake.nshmp.calc.Site; -import gov.usgs.earthquake.nshmp.data.MutableXySequence; -import gov.usgs.earthquake.nshmp.data.XySequence; -import gov.usgs.earthquake.nshmp.geo.Coordinates; -import gov.usgs.earthquake.nshmp.geo.Location; -import gov.usgs.earthquake.nshmp.gmm.Imt; -import gov.usgs.earthquake.nshmp.model.HazardModel; -import gov.usgs.earthquake.nshmp.model.SourceType; -import gov.usgs.earthquake.nshmp.www.HazardController; -import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.WsUtils; -import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; -import gov.usgs.earthquake.nshmp.www.meta.Metadata; -import gov.usgs.earthquake.nshmp.www.meta.Parameter; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceQueryData; -import gov.usgs.earthquake.nshmp.www.services.SourceServices.SourceModel; - -import io.micronaut.http.HttpRequest; -import io.micronaut.http.HttpResponse; -import jakarta.inject.Singleton; - -/** - * Probabilistic seismic hazard calculation handler for - * {@link HazardController}. - * - * @author U.S. Geological Survey - */ -@Singleton -public final class HazardService2 { - - private static final String NAME = "Hazard Service"; - - /** HazardController.doGetUsage() handler. */ - public static HttpResponse<String> handleDoGetMetadata(HttpRequest<?> request) { - var url = request.getUri().getPath(); - try { - var usage = new RequestMetadata(ServletUtil.model());// SourceServices.ResponseData(); - var response = ResponseBody.usage() - .name(NAME) - .url(url) - .request(url) - .response(usage) - .build(); - var svcResponse = ServletUtil.GSON.toJson(response); - return HttpResponse.ok(svcResponse); - } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, url); - } - } - - /** HazardController.doGetHazard() handler. */ - public static HttpResponse<String> handleDoGetHazard( - HttpRequest<?> request, - RequestData args) { - - try { - // TODO still need to validate - // if (query.isEmpty()) { - // return handleDoGetUsage(urlHelper); - // } - // query.checkParameters(); - - // var data = new RequestData(query); - - ResponseBody<RequestData, ResponseData> response = process(request, args); - String svcResponse = ServletUtil.GSON.toJson(response); - return HttpResponse.ok(svcResponse); - - } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, request.getUri().getPath()); - } - } - - static ResponseBody<RequestData, ResponseData> process( - HttpRequest<?> request, - RequestData data) throws InterruptedException, ExecutionException { - - var configFunction = new ConfigFunction(); - var siteFunction = new SiteFunction(data); - var stopwatch = Stopwatch.createStarted(); - var hazard = ServicesUtil.calcHazard(configFunction, siteFunction); - - return new ResultBuilder() - .hazard(hazard) - .requestData(data) - .timer(stopwatch) - .url(request) - .build(); - } - - static class ConfigFunction implements Function<HazardModel, CalcConfig> { - @Override - public CalcConfig apply(HazardModel model) { - var configBuilder = CalcConfig.copyOf(model.config()); - return configBuilder.build(); - } - } - - static class SiteFunction implements Function<CalcConfig, Site> { - final RequestData data; - - private SiteFunction(RequestData data) { - this.data = data; - } - - @Override // TODO this needs to pick up SiteData - public Site apply(CalcConfig config) { - return Site.builder() - .location(Location.create(data.longitude, data.latitude)) - .vs30(data.vs30) - .build(); - } - } - - // public static class QueryParameters { - // - // final double longitude; - // final double latitude; - // final int vs30; - // final boolean truncate; - // final boolean maxdir; - // - // public QueryParameters( - // double longitude, - // double latitude, - // int vs30, - // boolean truncate, - // boolean maxdir) { - // - // this.longitude = longitude; - // this.latitude = latitude; - // this.vs30 = vs30; - // this.truncate = truncate; - // this.maxdir = maxdir; - // } - // - // // void checkParameters() { - // // checkParameter(longitude, "longitude"); - // // checkParameter(latitude, "latitude"); - // // checkParameter(vs30, "vs30"); - // // } - // } - - // private static void checkParameter(Object param, String id) { - // checkNotNull(param, "Missing parameter: %s", id); - // // TODO check range here - // } - - /* Service request and model metadata */ - static class RequestMetadata { - - final SourceModel model; - final DoubleParameter longitude; - final DoubleParameter latitude; - final DoubleParameter vs30; - - RequestMetadata(HazardModel model) { - this.model = new SourceModel(model); - // TODO need min max from model - longitude = new DoubleParameter( - "Longitude", - "°", - Coordinates.LON_RANGE.lowerEndpoint(), - Coordinates.LON_RANGE.upperEndpoint()); - - latitude = new DoubleParameter( - "Latitude", - "°", - Coordinates.LAT_RANGE.lowerEndpoint(), - Coordinates.LAT_RANGE.upperEndpoint()); - - vs30 = new DoubleParameter( - "Latitude", - "m/s", - 150, - 1500); - } - } - - // static class RequestData { - // - // final double longitude; - // final double latitude; - // final double vs30; - // final boolean truncate; - // final boolean maxdir; - // - // RequestData(QueryParameters query) { - // this.longitude = query.longitude; - // this.latitude = query.latitude; - // this.vs30 = query.vs30; - // this.truncate = query.truncate; - // this.maxdir = query.maxdir; - // } - // } - - private static final class ResponseMetadata { - final String xlabel = "Ground Motion (g)"; - final String ylabel = "Annual Frequency of Exceedence"; - final Object server; - - ResponseMetadata(Object server) { - this.server = server; - } - } - - private static String imtShortLabel(Imt imt) { - if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { - return imt.name(); - } else if (imt.isSA()) { - return imt.period() + " s"; - } - return imt.toString(); - } - - // @Deprecated - // static class RequestDataOld extends ServiceRequestData { - // final double vs30; - // - // RequestDataOld(Query query, double vs30) { - // super(query); - // this.vs30 = vs30; - // } - // } - - private static final class ResponseData { - final ResponseMetadata metadata; - final List<HazardResponse> hazardCurves; - - ResponseData(ResponseMetadata metadata, List<HazardResponse> hazardCurves) { - this.metadata = metadata; - this.hazardCurves = hazardCurves; - } - } - - private static final class HazardResponse { - final Parameter imt; - final List<Curve> data; - - HazardResponse(Imt imt, List<Curve> data) { - this.imt = new Parameter(imtShortLabel(imt), imt.name()); - this.data = data; - } - } - - private static final class Curve { - final String component; - final XySequence values; - - Curve(String component, XySequence values) { - this.component = component; - this.values = values; - } - } - - private static final String TOTAL_KEY = "Total"; - - private static final class ResultBuilder { - - String url; - Stopwatch timer; - RequestData request; - - Map<Imt, Map<SourceType, MutableXySequence>> componentMaps; - Map<Imt, MutableXySequence> totalMap; - - ResultBuilder hazard(Hazard hazardResult) { - // TODO necessary?? - checkState(totalMap == null, "Hazard has already been added to this builder"); - - componentMaps = new EnumMap<>(Imt.class); - totalMap = new EnumMap<>(Imt.class); - - var typeTotalMaps = curvesBySource(hazardResult); - - for (var imt : hazardResult.curves().keySet()) { - - /* Total curve for IMT. */ - XySequence.addToMap(imt, totalMap, hazardResult.curves().get(imt)); - - /* Source component curves for IMT. */ - var typeTotalMap = typeTotalMaps.get(imt); - var componentMap = componentMaps.get(imt); - - if (componentMap == null) { - componentMap = new EnumMap<>(SourceType.class); - componentMaps.put(imt, componentMap); - } - - for (var type : typeTotalMap.keySet()) { - XySequence.addToMap(type, componentMap, typeTotalMap.get(type)); - } - } - - return this; - } - - ResultBuilder url(HttpRequest<?> request) { - url = request.getUri().getPath(); - return this; - } - - ResultBuilder timer(Stopwatch timer) { - this.timer = timer; - return this; - } - - ResultBuilder requestData(RequestData request) { - this.request = request; - return this; - } - - ResponseBody<RequestData, ResponseData> build() { - var hazards = new ArrayList<HazardResponse>(); - - for (Imt imt : totalMap.keySet()) { - var curves = new ArrayList<Curve>(); - - // total curve - curves.add(new Curve( - TOTAL_KEY, - updateCurve(request, totalMap.get(imt), imt))); - - // component curves - var typeMap = componentMaps.get(imt); - for (SourceType type : typeMap.keySet()) { - curves.add(new Curve( - type.toString(), - updateCurve(request, typeMap.get(type), imt))); - } - - hazards.add(new HazardResponse(imt, List.copyOf(curves))); - } - - Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); - var response = new ResponseData(new ResponseMetadata(server), List.copyOf(hazards)); - - return ResponseBody.<RequestData, ResponseData> success() - .name(NAME) - .url(url) - .request(request) - .response(response) - .build(); - } - } - - private static final double TRUNCATION_LIMIT = 1e-4; - - /* Convert to linear and possibly truncate and scale to max-direction. */ - private static XySequence updateCurve( - RequestData request, - XySequence curve, - Imt imt) { - - /* - * If entire curve is <1e-4, this method will return a curve consisting of - * just the first point in the supplied curve. - * - * TODO We probably want to move the TRUNCATION_LIMIT out to a config. - */ - - double[] yValues = curve.yValues().toArray(); - int limit = request.truncate ? truncationLimit(yValues) : yValues.length; - yValues = Arrays.copyOf(yValues, limit); - - double scale = request.maxdir ? MaxDirection.FACTORS.get(imt) : 1.0; - double[] xValues = curve.xValues() - .limit(yValues.length) - .map(Math::exp) - .map(x -> x * scale) - .toArray(); - - return XySequence.create(xValues, yValues); - } - - private static int truncationLimit(double[] yValues) { - int limit = 1; - double y = yValues[0]; - while (y > TRUNCATION_LIMIT && limit < yValues.length) { - y = yValues[limit++]; - } - return limit; - } - - @Deprecated - public static class Query extends ServiceQueryData { - Integer vs30; - - public Query(Double longitude, Double latitude, Integer vs30) { - super(longitude, latitude); - this.vs30 = vs30; - } - - @Override - public boolean isNull() { - return super.isNull() && vs30 == null; - } - - @Override - public void checkValues() { - super.checkValues(); - WsUtils.checkValue(ServicesUtil.Key.VS30, vs30); - } - } - - public static final class RequestData { - - final double longitude; - final double latitude; - final int vs30; - final boolean truncate; - final boolean maxdir; - - public RequestData( - double longitude, - double latitude, - int vs30, - boolean truncate, - boolean maxdir) { - - this.longitude = longitude; - this.latitude = latitude; - this.vs30 = vs30; - this.truncate = truncate; - this.maxdir = maxdir; - } - - // void checkParameters() { - // checkParameter(longitude, "longitude"); - // checkParameter(latitude, "latitude"); - // checkParameter(vs30, "vs30"); - // } - } - -} diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java index 0312e7359..8c1a50c43 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java @@ -17,14 +17,15 @@ import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.RateController; import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; +import gov.usgs.earthquake.nshmp.www.ServicesUtil.Key; +import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceQueryData; +import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceRequestData; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Metadata.DefaultParameters; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.Key; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceQueryData; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceRequestData; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java index 3d0dfe0a7..4a7882cdb 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java @@ -2,6 +2,8 @@ package gov.usgs.earthquake.nshmp.www.services; import gov.usgs.earthquake.nshmp.model.Models; import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.SourceLogicTreesController; import io.micronaut.http.HttpRequest; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java index 83910b2fc..bd22e1c37 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java @@ -12,9 +12,10 @@ import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.Metadata; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; @@ -105,7 +106,7 @@ public class SourceServices { Set<Gmm> gmms; Map<NehrpSiteClass, Double> siteClasses; - SourceModel(HazardModel model) { + public SourceModel(HazardModel model) { name = model.name(); gmms = model.gmms(); siteClasses = model.siteClasses(); -- GitLab From f7b0ecd57c19d533335813a5542d94778a699a6f Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Tue, 1 Feb 2022 16:26:51 -0700 Subject: [PATCH 3/7] refactor of hazard services --- .../earthquake/nshmp/www/ServicesUtil.java | 20 -- .../earthquake/nshmp/www/ServletUtil.java | 44 +++++ .../nshmp/www/SwaggerController.java | 6 +- .../nshmp/www/hazard/HazardController.java | 48 +++-- .../nshmp/www/hazard/HazardService.java | 174 +++++++++--------- .../earthquake/nshmp/www/meta/MetaUtil.java | 2 +- .../nshmp/www/services/RateService.java | 14 +- .../www/services/SourceLogicTreesService.java | 11 +- .../nshmp/www/services/SourceServices.java | 19 +- 9 files changed, 204 insertions(+), 134 deletions(-) diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java index 0da190978..3bdcab696 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java @@ -4,34 +4,14 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Function; -import com.google.gson.GsonBuilder; - import gov.usgs.earthquake.nshmp.calc.CalcConfig; import gov.usgs.earthquake.nshmp.calc.Hazard; import gov.usgs.earthquake.nshmp.calc.HazardCalcs; import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.model.HazardModel; -import io.micronaut.http.HttpResponse; public class ServicesUtil { - public static HttpResponse<String> handleError( - Throwable e, - String name, - String url) { - var msg = e.getMessage() + " (see logs)"; - var svcResponse = ResponseBody.error() - .name(name) - .url(url) - .request(url) - .response(msg) - .build(); - var gson = new GsonBuilder().setPrettyPrinting().create(); - var response = gson.toJson(svcResponse); - e.printStackTrace(); - return HttpResponse.serverError(response); - } - @Deprecated public static Hazard calcHazard( Function<HazardModel, CalcConfig> configFunction, diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java index 3de64116a..c3221f244 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java @@ -14,6 +14,9 @@ import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.Gson; @@ -31,6 +34,7 @@ import gov.usgs.earthquake.nshmp.www.meta.MetaUtil; import io.micronaut.context.annotation.Value; import io.micronaut.context.event.ShutdownEvent; import io.micronaut.context.event.StartupEvent; +import io.micronaut.http.HttpResponse; import io.micronaut.runtime.event.annotation.EventListener; import jakarta.inject.Singleton; @@ -43,12 +47,15 @@ import jakarta.inject.Singleton; public class ServletUtil { public static final Gson GSON; + public static final Gson GSON2; public static final ListeningExecutorService CALC_EXECUTOR; public static final ExecutorService TASK_EXECUTOR; public static final int THREAD_COUNT; + private static final Logger LOGGER = LoggerFactory.getLogger(ServletUtil.class); + @Value("${nshmp-haz.model-path}") private Path modelPath; @@ -69,6 +76,17 @@ public class ServletUtil { .serializeNulls() .setPrettyPrinting() .create(); + + // removed old IMT and ValueFormat enum serialization + GSON2 = new GsonBuilder() + .registerTypeAdapter(Double.class, new WsUtils.DoubleSerializer()) + .registerTypeAdapter(Site.class, new MetaUtil.SiteSerializer()) + .registerTypeHierarchyAdapter(Path.class, new PathConverter()) + .disableHtmlEscaping() + .serializeNulls() + .setPrettyPrinting() + .create(); + } public static HazardModel model() { @@ -139,4 +157,30 @@ public class ServletUtil { } } + public static HttpResponse<String> error( + Logger logger, + Throwable e, + String name, + String url) { + var msg = e.getMessage() + " (see logs)"; + var svcResponse = ResponseBody.error() + .name(name) + .url(url) + .request(url) + .response(msg) + .build(); + var response = GSON2.toJson(svcResponse); + logger.error("Servlet error", e); + return HttpResponse.serverError(response); + } + + public static String imtShortLabel(Imt imt) { + if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { + return imt.name(); + } else if (imt.isSA()) { + return imt.period() + " s"; + } + return imt.toString(); + } + } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java index 6cd6d6d04..dc4d6fb3c 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java @@ -3,6 +3,8 @@ package gov.usgs.earthquake.nshmp.www; import java.nio.charset.StandardCharsets; import java.util.stream.Collectors; +import org.slf4j.LoggerFactory; + import com.google.common.io.Resources; import io.micronaut.http.HttpRequest; @@ -38,7 +40,9 @@ public class SwaggerController { .collect(Collectors.joining("\n")); return HttpResponse.ok(yml); } catch (Exception e) { - return ServicesUtil.handleError(e, "Swagger", request.getUri().getPath()); + return ServletUtil.error( + LoggerFactory.getLogger("Swagger"), + e, "Swagger", request.getUri().toString()); } } } 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 54c765def..4dd72d3f5 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 @@ -1,7 +1,11 @@ package gov.usgs.earthquake.nshmp.www.hazard; +import java.util.Set; + +import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; -import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; +import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.Controller; @@ -15,8 +19,8 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; /** - * Micronaut controller for probabilistic seismic hazard calculations and - * services. + * Micronaut web service controller for probabilistic seismic hazard + * calculations. * * @author U.S. Geological Survey */ @@ -36,8 +40,15 @@ public class HazardController { description = "Hazard service metadata", responseCode = "200") @Get - public HttpResponse<String> doGetMetadata(HttpRequest<?> request) { - return HazardService.handleDoGetMetadata(request); + public HttpResponse<String> doGetMetadata(HttpRequest<?> http) { + try { + return HazardService.getMetadata(http); + } catch (Exception e) { + return ServletUtil.error( + HazardService.LOG, e, + HazardService.NAME, + http.getUri().toString()); + } } /** @@ -46,6 +57,13 @@ public class HazardController { * @param vs30 Site Vs30 value in m/s [150..3000] * @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 default set of supported IMTs 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. @code{?imt=PGA,SA0p2,SA1P0}. + * */ @Operation( summary = "Compute probabilisitic hazard at a site", @@ -53,7 +71,7 @@ public class HazardController { @ApiResponse( description = "Hazard curves", responseCode = "200") - @Get(uri = "/{longitude}/{latitude}/{vs30}{?truncate,maxdir}") + @Get(uri = "/{longitude}/{latitude}/{vs30}{?truncate,maxdir,imt}") public HttpResponse<String> doGetHazard( HttpRequest<?> http, @Schema( @@ -66,15 +84,23 @@ public class HazardController { minimum = "150", maximum = "3000") @PathVariable int vs30, @QueryValue( - defaultValue = "false") boolean truncate, + defaultValue = "false") @Nullable Boolean truncate, @QueryValue( - defaultValue = "false") boolean maxdir) { + defaultValue = "false") @Nullable Boolean maxdir, + @QueryValue @Nullable Set<Imt> imt) { try { + Set<Imt> imts = HazardService.readImts(http); HazardService.Request request = new HazardService.Request( - http, longitude, latitude, vs30, truncate, maxdir); - return HazardService.processRequest(request); + http, + longitude, latitude, vs30, + truncate, maxdir, + imts); + return HazardService.getHazard(request); } catch (Exception e) { - return ServicesUtil.handleError(e, HazardService.NAME, http.getUri().getPath()); + return ServletUtil.error( + HazardService.LOG, e, + HazardService.NAME, + http.getUri().toString()); } } } 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 90cf98ef5..556dffea9 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 @@ -5,15 +5,21 @@ import static gov.usgs.earthquake.nshmp.calc.HazardExport.curvesBySource; import static gov.usgs.earthquake.nshmp.data.DoubleData.checkInRange; import static gov.usgs.earthquake.nshmp.geo.Coordinates.checkLatitude; import static gov.usgs.earthquake.nshmp.geo.Coordinates.checkLongitude; +import static java.util.stream.Collectors.toCollection; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; +import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Stopwatch; import gov.usgs.earthquake.nshmp.calc.CalcConfig; @@ -28,7 +34,6 @@ import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.model.SourceType; import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.ServicesUtil; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata; @@ -39,49 +44,49 @@ import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; /** - * Probabilistic seismic hazard calculation handler for - * {@link HazardController}. + * Hazard service. * + * @see HazardController * @author U.S. Geological Survey */ @Singleton public final class HazardService { static final String NAME = "Hazard Service"; + static final Logger LOG = LoggerFactory.getLogger(HazardService.class); /** HazardController.doGetUsage() handler. */ - public static HttpResponse<String> handleDoGetMetadata(HttpRequest<?> request) { - var url = request.getUri().getPath(); - try { - var usage = new UsageMetadata(ServletUtil.model()); - var response = ResponseBody.usage() - .name(NAME) - .url(url) - .request(url) - .response(usage) - .build(); - var svcResponse = ServletUtil.GSON.toJson(response); - return HttpResponse.ok(svcResponse); - } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, url); - } + public static HttpResponse<String> getMetadata(HttpRequest<?> request) { + var url = request.getUri().toString(); + var usage = new UsageMetadata(ServletUtil.model()); + var body = ResponseBody.usage() + .name(NAME) + .url(url) + .request(url) + .response(usage) + .build(); + var json = ServletUtil.GSON.toJson(body); + return HttpResponse.ok(json); } /** HazardController.doGetHazard() handler. */ - public static HttpResponse<String> processRequest(Request request) { - try { - Response response = process(request); - var body = ResponseBody.success() - .name(NAME) - .url(request.http.getUri().getPath()) - .request(request) - .response(response) - .build(); - String svcResponse = ServletUtil.GSON.toJson(body); - return HttpResponse.ok(svcResponse); - } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, request.http.getUri().getPath()); - } + public static HttpResponse<String> getHazard(Request request) + throws InterruptedException, ExecutionException { + var stopwatch = Stopwatch.createStarted(); + var hazard = calcHazard(request); + var response = new ResponseBuilder() + .timer(stopwatch) + .request(request) + .hazard(hazard) + .build(); + var body = ResponseBody.success() + .name(NAME) + .url(request.http.getUri().toString()) + .request(request) + .response(response) + .build(); + String json = ServletUtil.GSON.toJson(body); + return HttpResponse.ok(json); } /* @@ -93,47 +98,32 @@ public final class HazardService { * apply truncation and scaling on the client. */ - static Response process(Request request) - throws InterruptedException, ExecutionException { - - var stopwatch = Stopwatch.createStarted(); - var hazard = calcHazard(request); - - return new ResultBuilder() - .request(request) - .hazard(hazard) - .timer(stopwatch) - .build(); - } - public static Hazard calcHazard(Request request) throws InterruptedException, ExecutionException { HazardModel model = ServletUtil.model(); - // will we be passing in options for config?? - CalcConfig config = CalcConfig.copyOf(model.config()).build(); + // modify config to include service endpoint arguments + CalcConfig config = CalcConfig.copyOf(model.config()) + .imts(request.imts) + .build(); - // TODO this needs to pick up SiteData + // TODO this needs to pick up SiteData, centralize Site site = Site.builder() .location(Location.create(request.longitude, request.latitude)) .vs30(request.vs30) .build(); - CompletableFuture<Hazard> future = futureHazard(model, config, site); - return future.get(); - } - private static CompletableFuture<Hazard> futureHazard( - HazardModel model, - CalcConfig config, - Site site) { - - return CompletableFuture.supplyAsync( - () -> HazardCalcs.hazard(model, config, site, ServletUtil.CALC_EXECUTOR), + CompletableFuture<Hazard> future = CompletableFuture.supplyAsync( + () -> HazardCalcs.hazard( + model, config, site, + ServletUtil.CALC_EXECUTOR), ServletUtil.TASK_EXECUTOR); + + return future.get(); } - private static class UsageMetadata { + static class UsageMetadata { final SourceModel model; final DoubleParameter longitude; @@ -142,8 +132,6 @@ public final class HazardService { UsageMetadata(HazardModel model) { this.model = new SourceModel(model); - // perhaps move out to shared factory with parameter instances - // // TODO need min max from model longitude = new DoubleParameter( "Longitude", @@ -167,12 +155,13 @@ public final class HazardService { public static final class Request { - transient HttpRequest<?> http; + final transient HttpRequest<?> http; final double longitude; final double latitude; final double vs30; final boolean truncate; final boolean maxdir; + final Set<Imt> imts; public Request( HttpRequest<?> http, @@ -180,7 +169,8 @@ public final class HazardService { double latitude, int vs30, boolean truncate, - boolean maxdir) { + boolean maxdir, + Set<Imt> imts) { this.http = http; this.longitude = checkLongitude(longitude); @@ -188,13 +178,16 @@ public final class HazardService { this.vs30 = checkInRange(Site.VS30_RANGE, Site.Key.VS30, vs30); this.truncate = truncate; this.maxdir = maxdir; + this.imts = imts.isEmpty() + ? ServletUtil.model().config().hazard.imts + : imts; } } private static final class ResponseMetadata { + final Object server; final String xlabel = "Ground Motion (g)"; final String ylabel = "Annual Frequency of Exceedence"; - final Object server; ResponseMetadata(Object server) { this.server = server; @@ -216,7 +209,7 @@ public final class HazardService { final List<Curve> data; ImtCurves(Imt imt, List<Curve> data) { - this.imt = new Parameter(imtShortLabel(imt), imt.name()); + this.imt = new Parameter(ServletUtil.imtShortLabel(imt), imt.name()); this.data = data; } } @@ -233,27 +226,36 @@ public final class HazardService { private static final String TOTAL_KEY = "Total"; - private static final class ResultBuilder { + private static final class ResponseBuilder { Stopwatch timer; Request request; - Map<Imt, Map<SourceType, MutableXySequence>> componentMaps; Map<Imt, MutableXySequence> totalMap; - ResultBuilder hazard(Hazard hazardResult) { + ResponseBuilder timer(Stopwatch timer) { + this.timer = timer; + return this; + } + + ResponseBuilder request(Request request) { + this.request = request; + return this; + } + + ResponseBuilder hazard(Hazard hazard) { // TODO necessary?? checkState(totalMap == null, "Hazard has already been added to this builder"); componentMaps = new EnumMap<>(Imt.class); totalMap = new EnumMap<>(Imt.class); - var typeTotalMaps = curvesBySource(hazardResult); + var typeTotalMaps = curvesBySource(hazard); - for (var imt : hazardResult.curves().keySet()) { + for (var imt : hazard.curves().keySet()) { /* Total curve for IMT. */ - XySequence.addToMap(imt, totalMap, hazardResult.curves().get(imt)); + XySequence.addToMap(imt, totalMap, hazard.curves().get(imt)); /* Source component curves for IMT. */ var typeTotalMap = typeTotalMaps.get(imt); @@ -272,16 +274,6 @@ public final class HazardService { return this; } - ResultBuilder timer(Stopwatch timer) { - this.timer = timer; - return this; - } - - ResultBuilder request(Request request) { - this.request = request; - return this; - } - Response build() { var hazards = new ArrayList<ImtCurves>(); @@ -301,13 +293,13 @@ public final class HazardService { updateCurve(request, typeMap.get(type), imt))); } - hazards.add(new ImtCurves(imt, List.copyOf(curves))); + hazards.add(new ImtCurves(imt, curves)); } Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); var response = new Response( new ResponseMetadata(server), - List.copyOf(hazards)); + hazards); return response; } @@ -351,13 +343,15 @@ public final class HazardService { return limit; } - private static String imtShortLabel(Imt imt) { - if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { - return imt.name(); - } else if (imt.isSA()) { - return imt.period() + " s"; - } - return imt.toString(); + /* 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() + .map(s -> s.split(",")) + .flatMap(Arrays::stream) + .map(Imt::valueOf) + .collect(toCollection(() -> EnumSet.noneOf(Imt.class))); } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/MetaUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/MetaUtil.java index 74c7c7ab4..a1a2c0684 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/MetaUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/MetaUtil.java @@ -39,7 +39,7 @@ public final class MetaUtil { JsonObject json = new JsonObject(); json.add("location", loc); json.addProperty("vs30", site.vs30()); - json.addProperty("vsInfered", site.vsInferred()); + json.addProperty("vsInferred", site.vsInferred()); json.addProperty("z1p0", Double.isNaN(site.z1p0()) ? null : site.z1p0()); json.addProperty("z2p5", Double.isNaN(site.z2p5()) ? null : site.z2p5()); diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java index 8c1a50c43..6b042038a 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java @@ -6,6 +6,9 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.ListenableFuture; @@ -17,12 +20,11 @@ import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.RateController; import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.ServicesUtil; -import gov.usgs.earthquake.nshmp.www.ServletUtil; -import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.ServicesUtil.Key; import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceQueryData; import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceRequestData; +import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Metadata.DefaultParameters; @@ -39,6 +41,8 @@ import jakarta.inject.Singleton; @Singleton public final class RateService { + static final Logger LOG = LoggerFactory.getLogger(RateService.class); + /* * Developer notes: * @@ -63,7 +67,7 @@ public final class RateService { var json = ServletUtil.GSON.toJson(response); return HttpResponse.ok(json); } catch (Exception e) { - return ServicesUtil.handleError(e, service.name, request.getUri().getPath()); + return ServletUtil.error(LOG, e, service.name, request.getUri().getPath()); } } @@ -92,7 +96,7 @@ public final class RateService { var svcResponse = ServletUtil.GSON.toJson(response); return HttpResponse.ok(svcResponse); } catch (Exception e) { - return ServicesUtil.handleError(e, service.name, request.getUri().getPath()); + return ServletUtil.error(LOG, e, service.name, request.getUri().getPath()); } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java index 4a7882cdb..344514c8d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java @@ -1,11 +1,12 @@ package gov.usgs.earthquake.nshmp.www.services; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import gov.usgs.earthquake.nshmp.model.Models; import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.ServicesUtil; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.SourceLogicTreesController; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; @@ -18,6 +19,8 @@ import jakarta.inject.Singleton; @Singleton public class SourceLogicTreesService { + static final Logger LOG = LoggerFactory.getLogger(SourceLogicTreesService.class); + private static final String NAME = "Source Logic Trees"; /** SourceLogicTreesController.doGetMetadata() handler */ @@ -34,7 +37,7 @@ public class SourceLogicTreesService { .build(); return HttpResponse.ok(ServletUtil.GSON.toJson(response)); } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, url); + return ServletUtil.error(LOG, e, NAME, url); } } @@ -53,7 +56,7 @@ public class SourceLogicTreesService { .build(); return HttpResponse.ok(ServletUtil.GSON.toJson(response)); } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, url); + return ServletUtil.error(LOG, e, NAME, url); } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java index bd22e1c37..75cf76b28 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java @@ -1,8 +1,14 @@ package gov.usgs.earthquake.nshmp.www.services; +import static java.util.stream.Collectors.toList; + +import java.util.List; import java.util.Map; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.base.Stopwatch; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -12,10 +18,10 @@ import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.ServicesUtil; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.Metadata; +import gov.usgs.earthquake.nshmp.www.meta.Parameter; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; @@ -34,6 +40,8 @@ public class SourceServices { private static final String SERVICE_DESCRIPTION = "Utilities for querying earthquake source models"; + static final Logger LOG = LoggerFactory.getLogger(RateService.class); + public static final Gson GSON; static { @@ -57,7 +65,7 @@ public class SourceServices { var json = GSON.toJson(response); return HttpResponse.ok(json); } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, url); + return ServletUtil.error(LOG, e, NAME, url); } } @@ -105,11 +113,18 @@ public class SourceServices { String name; Set<Gmm> gmms; Map<NehrpSiteClass, Double> siteClasses; + List<Parameter> imts; public SourceModel(HazardModel model) { name = model.name(); gmms = model.gmms(); siteClasses = model.siteClasses(); + imts = model.gmms().stream() + .map(Gmm::supportedImts) + .flatMap(Set::stream) + .sorted() + .map(imt -> new Parameter(ServletUtil.imtShortLabel(imt), imt.name())) + .collect(toList()); } // public static List<SourceModel> getList() { -- GitLab From a7ca58755c4c63b0185aa0288328387bad0d633a Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Tue, 1 Feb 2022 16:27:21 -0700 Subject: [PATCH 4/7] migrated disagg services to micronaut --- .../nshmp/www/hazard/DisaggController.java | 155 +++++++++ .../nshmp/www/hazard/DisaggService.java | 320 ++++++++++++++++++ 2 files changed, 475 insertions(+) create mode 100644 src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java create mode 100644 src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java 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 new file mode 100644 index 000000000..ac2647922 --- /dev/null +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java @@ -0,0 +1,155 @@ +package gov.usgs.earthquake.nshmp.www.hazard; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.Map; +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 io.micronaut.core.annotation.Nullable; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.PathVariable; +import io.micronaut.http.annotation.QueryValue; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.inject.Inject; + +/** + * Micronaut web service controller for disaggregation of probabilistic seismic + * hazard. + * + * @author U.S. Geological Survey + */ +@Tag( + name = "Disaggregation", + description = "USGS NSHMP hazard disaggregation service") +@Controller("/disagg") +public class DisaggController { + + @Inject + private NshmpMicronautServlet servlet; + + @Operation( + summary = "Disaggregation model and service metadata", + description = "Returns details of the installed model and service request parameters") + @ApiResponse( + description = "Disaggregation service metadata", + responseCode = "200") + @Get + public HttpResponse<String> doGetMetadata(HttpRequest<?> http) { + try { + return DisaggService.getMetadata(http); + } catch (Exception e) { + return ServletUtil.error( + DisaggService.LOG, e, + DisaggService.NAME, + http.getUri().toString()); + } + } + + /** + * @param longitude Longitude in the range [-360..360]°. + * @param latitude Latitude in the range [-90..90]°. + * @param vs30 Site Vs30 value in the range [150..3000] m/s. + * @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 run disaggregations. If none are + * supplied then the default set of supported IMTs 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. @code{?imt=PGA,SA0p2,SA1P0}. + */ + @Operation( + summary = "Disaggregate hazard at a specified return period", + description = "Returns a hazard disaggregation computed from the installed model") + @ApiResponse( + description = "Disaggregation", + responseCode = "200") + @Get(uri = "rp/{longitude}/{latitude}/{vs30}/{returnPeriod}{?imt}") + public HttpResponse<String> doGetDisaggReturnPeriod( + HttpRequest<?> http, + @Schema( + minimum = "-360", + maximum = "360") @PathVariable double longitude, + @Schema( + minimum = "-90", + maximum = "90") @PathVariable double latitude, + @Schema( + minimum = "150", + maximum = "3000") @PathVariable double vs30, + @Schema( + minimum = "150", + maximum = "3000") @PathVariable double returnPeriod, + @Schema() @QueryValue @Nullable Set<Imt> imt) { + try { + Set<Imt> imts = HazardService.readImts(http); + DisaggService.RequestRp request = new DisaggService.RequestRp( + http, + longitude, latitude, vs30, + returnPeriod, + imts); + return DisaggService.getDisaggRp(request); + } catch (Exception e) { + return ServletUtil.error( + DisaggService.LOG, e, + DisaggService.NAME, + http.getUri().toString()); + } + } + + /** + * @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. + */ + @Operation( + summary = "Disaggregate hazard at specified IMLs", + description = "Returns a hazard disaggregation computed from the installed model") + @ApiResponse( + description = "Disaggregation", + responseCode = "200") + @Get(uri = "iml/{longitude}/{latitude}/{vs30}") + public HttpResponse<String> doGetDisaggIml( + HttpRequest<?> http, + @Schema( + minimum = "-360", + maximum = "360") @PathVariable double longitude, + @Schema( + minimum = "-90", + maximum = "90") @PathVariable double latitude, + @Schema( + minimum = "150", + maximum = "3000") @PathVariable double vs30) { + + /* + * 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. + */ + + try { + Map<Imt, Double> imtImlMap = http.getParameters().asMap(Imt.class, Double.class); + checkArgument(!imtImlMap.isEmpty(), "No IMLs supplied"); + DisaggService.RequestIml request = new DisaggService.RequestIml( + http, + longitude, latitude, vs30, + imtImlMap); + return DisaggService.getDisaggIml(request); + } catch (Exception e) { + return ServletUtil.error( + DisaggService.LOG, e, + DisaggService.NAME, + http.getUri().toString()); + } + } +} 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 new file mode 100644 index 000000000..cac2e3537 --- /dev/null +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java @@ -0,0 +1,320 @@ +package gov.usgs.earthquake.nshmp.www.hazard; + +import static java.util.stream.Collectors.toList; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.Range; + +import gov.usgs.earthquake.nshmp.calc.CalcConfig; +import gov.usgs.earthquake.nshmp.calc.Disaggregation; +import gov.usgs.earthquake.nshmp.calc.Hazard; +import gov.usgs.earthquake.nshmp.calc.HazardCalcs; +import gov.usgs.earthquake.nshmp.calc.Site; +import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.gmm.Imt; +import gov.usgs.earthquake.nshmp.model.HazardModel; +import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.UsageMetadata; +import gov.usgs.earthquake.nshmp.www.meta.Metadata; +import gov.usgs.earthquake.nshmp.www.meta.Parameter; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import jakarta.inject.Singleton; + +/** + * Disaggregation service. + * + * @see DisaggController + * @author U.S. Geological Survey + */ +@Singleton +public final class DisaggService { + + /* + * Developer notes: + * + * Same query structure as hazard service, but either return period and imt(s) + * OR imt=iml pairs + */ + + static final String NAME = "Disaggregation Service"; + static final Logger LOG = LoggerFactory.getLogger(DisaggService.class); + + private static Range<Double> rpRange = Range.closed(1.0, 20000.0); + private static Range<Double> imlRange = Range.closed(0.0001, 8.0); + + /** HazardController.doGetMetadata() handler. */ + public static HttpResponse<String> getMetadata(HttpRequest<?> request) { + var url = request.getUri().toString(); + var usage = new UsageMetadata(ServletUtil.model()); + var response = ResponseBody.usage() + .name(NAME) + .url(url) + .request(url) + .response(usage) + .build(); + var svcResponse = ServletUtil.GSON.toJson(response); + return HttpResponse.ok(svcResponse); + } + + /** HazardController.doGetDisaggIml() handler. */ + public static HttpResponse<String> getDisaggIml(RequestIml request) + throws InterruptedException, ExecutionException { + var stopwatch = Stopwatch.createStarted(); + var disagg = calcDisaggIml(request); + var response = new ResponseBuilder() + .timer(stopwatch) + .request(request) + .disagg(disagg) + .build(); + var body = ResponseBody.success() + .name(NAME) + .url(request.http.getUri().toString()) + .request(request) + .response(response) + .build(); + String svcResponse = ServletUtil.GSON2.toJson(body); + return HttpResponse.ok(svcResponse); + } + + /** HazardController.doGetDisaggRp() handler. */ + public static HttpResponse<String> getDisaggRp(RequestRp request) + throws InterruptedException, ExecutionException { + var stopwatch = Stopwatch.createStarted(); + var disagg = calcDisaggRp(request); + var response = new ResponseBuilder() + .timer(stopwatch) + .request(request) + .disagg(disagg) + .build(); + var body = ResponseBody.success() + .name(NAME) + .url(request.http.getUri().toString()) + .request(request) + .response(response) + .build(); + String svcResponse = ServletUtil.GSON2.toJson(body); + return HttpResponse.ok(svcResponse); + } + + /* + * Developer notes: + * + * If disaggIml, we need to do the calculation for single XySeqs if disaggRp, + * we don't know the imls so must compute hazard over the full curve + * + */ + + static Disaggregation calcDisaggIml(RequestIml request) + throws InterruptedException, ExecutionException { + + HazardModel model = ServletUtil.model(); + + // modify config to include service endpoint arguments + CalcConfig config = CalcConfig.copyOf(model.config()) + .imts(request.imls.keySet()) + .build(); + + // TODO this needs to pick up SiteData, centralize + Site site = Site.builder() + .location(Location.create(request.longitude, request.latitude)) + .vs30(request.vs30) + .build(); + + // use HazardService.calcHazard() instead? + CompletableFuture<Hazard> hazFuture = CompletableFuture.supplyAsync( + () -> HazardCalcs.hazard( + model, config, site, + ServletUtil.CALC_EXECUTOR), + ServletUtil.TASK_EXECUTOR); + + Hazard hazard = hazFuture.get(); + + CompletableFuture<Disaggregation> disaggfuture = CompletableFuture.supplyAsync( + () -> Disaggregation.atImls( + hazard, request.imls, + ServletUtil.CALC_EXECUTOR), + ServletUtil.TASK_EXECUTOR); + + Disaggregation disagg = disaggfuture.get(); + + return disagg; + } + + static Disaggregation calcDisaggRp(RequestRp request) + throws InterruptedException, ExecutionException { + + HazardModel model = ServletUtil.model(); + + // modify config to include service endpoint arguments + CalcConfig config = CalcConfig.copyOf(model.config()) + .imts(request.imts) + .build(); + + // TODO this needs to pick up SiteData, centralize + Site site = Site.builder() + .location(Location.create(request.longitude, request.latitude)) + .vs30(request.vs30) + .build(); + + CompletableFuture<Hazard> hazFuture = CompletableFuture.supplyAsync( + () -> HazardCalcs.hazard( + model, config, site, + ServletUtil.CALC_EXECUTOR), + ServletUtil.TASK_EXECUTOR); + + Hazard hazard = hazFuture.get(); + + CompletableFuture<Disaggregation> disaggfuture = CompletableFuture.supplyAsync( + () -> Disaggregation.atReturnPeriod( + hazard, request.returnPeriod, + ServletUtil.CALC_EXECUTOR), + ServletUtil.TASK_EXECUTOR); + + Disaggregation disagg = disaggfuture.get(); + + return disagg; + } + + static final class RequestIml { + + final transient HttpRequest<?> http; + final double longitude; + final double latitude; + final double vs30; + final Map<Imt, Double> imls; + + RequestIml( + HttpRequest<?> http, + double longitude, + double latitude, + double vs30, + Map<Imt, Double> imls) { + + this.http = http; + this.longitude = longitude; + this.latitude = latitude; + this.vs30 = vs30; + this.imls = imls; + } + } + + static final class RequestRp { + + final transient HttpRequest<?> http; + final double longitude; + final double latitude; + final double vs30; + final double returnPeriod; + final Set<Imt> imts; + + RequestRp( + HttpRequest<?> http, + double longitude, + double latitude, + double vs30, + double returnPeriod, + Set<Imt> imts) { + + this.http = http; + this.longitude = longitude; + this.latitude = latitude; + this.vs30 = vs30; + this.returnPeriod = returnPeriod; + this.imts = imts.isEmpty() + ? ServletUtil.model().config().hazard.imts + : imts; + } + } + + private static final class ResponseMetadata { + final Object server; + final String rlabel = "Closest Distance, rRup (km)"; + final String mlabel = "Magnitude (Mw)"; + final String εlabel = "% Contribution to Hazard"; + final Object εbins; + + ResponseMetadata(Object server, Object εbins) { + this.server = server; + this.εbins = εbins; + } + } + + private static final class Response { + final ResponseMetadata metadata; + final List<ImtDisagg> disaggs; + + Response(ResponseMetadata metadata, List<ImtDisagg> disaggs) { + this.metadata = metadata; + this.disaggs = disaggs; + } + } + + private static final class ImtDisagg { + final Parameter imt; + final Object data; + + ImtDisagg(Imt imt, Object data) { + this.imt = new Parameter( + ServletUtil.imtShortLabel(imt), + imt.name()); + this.data = data; + } + } + + private static final class ResponseBuilder { + + Stopwatch timer; + Optional<RequestRp> requestRp = Optional.empty(); + Optional<RequestIml> requestIml = Optional.empty(); + Disaggregation disagg; + + ResponseBuilder timer(Stopwatch timer) { + this.timer = timer; + return this; + } + + ResponseBuilder request(Object request) { + if (request instanceof RequestRp) { + requestRp = Optional.of((RequestRp) request); + return this; + } + requestIml = Optional.of((RequestIml) request); + return this; + } + + ResponseBuilder disagg(Disaggregation disagg) { + this.disagg = disagg; + return this; + } + + Response build() { + + Set<Imt> imts = requestRp.isPresent() + ? requestRp.orElseThrow().imts + : requestIml.orElseThrow().imls.keySet(); + + List<ImtDisagg> disaggs = imts.stream() + .map(imt -> new ImtDisagg(imt, disagg.toJson(imt))) + .collect(toList()); + + Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); + + return new Response( + new ResponseMetadata(server, disagg.εBins()), + disaggs); + } + } + +} -- GitLab From 515827a88fedd18e3b06933f86c1425a8bdb9b9b Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Tue, 1 Feb 2022 16:56:06 -0700 Subject: [PATCH 5/7] nested response classes --- .../earthquake/nshmp/www/ServletUtil.java | 18 ++ .../nshmp/www/hazard/DisaggService.java | 118 ++++++------ .../nshmp/www/hazard/HazardService.java | 182 +++++++++--------- .../earthquake/nshmp/www/meta/Metadata.java | 34 +--- .../nshmp/www/services/RateService.java | 5 +- .../nshmp/www/services/SourceServices.java | 6 +- 6 files changed, 174 insertions(+), 189 deletions(-) diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java index c3221f244..a357ab271 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java @@ -17,6 +17,7 @@ import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.Gson; @@ -183,4 +184,21 @@ public class ServletUtil { return imt.toString(); } + public static Object serverData(int threads, Stopwatch timer) { + return new Server(threads, timer); + } + + private static class Server { + + final int threads; + final String timer; + final String version; + + Server(int threads, Stopwatch timer) { + this.threads = threads; + this.timer = timer.toString(); + this.version = "TODO where to get version?"; + } + } + } 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 cac2e3537..55a955b99 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 @@ -25,8 +25,7 @@ import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; -import gov.usgs.earthquake.nshmp.www.hazard.HazardService.UsageMetadata; -import gov.usgs.earthquake.nshmp.www.meta.Metadata; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Parameter; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -57,7 +56,7 @@ public final class DisaggService { /** HazardController.doGetMetadata() handler. */ public static HttpResponse<String> getMetadata(HttpRequest<?> request) { var url = request.getUri().toString(); - var usage = new UsageMetadata(ServletUtil.model()); + var usage = new Metadata(ServletUtil.model()); var response = ResponseBody.usage() .name(NAME) .url(url) @@ -73,7 +72,7 @@ public final class DisaggService { throws InterruptedException, ExecutionException { var stopwatch = Stopwatch.createStarted(); var disagg = calcDisaggIml(request); - var response = new ResponseBuilder() + var response = new Response.Builder() .timer(stopwatch) .request(request) .disagg(disagg) @@ -93,7 +92,7 @@ public final class DisaggService { throws InterruptedException, ExecutionException { var stopwatch = Stopwatch.createStarted(); var disagg = calcDisaggRp(request); - var response = new ResponseBuilder() + var response = new Response.Builder() .timer(stopwatch) .request(request) .disagg(disagg) @@ -238,83 +237,82 @@ public final class DisaggService { } } - private static final class ResponseMetadata { - final Object server; - final String rlabel = "Closest Distance, rRup (km)"; - final String mlabel = "Magnitude (Mw)"; - final String εlabel = "% Contribution to Hazard"; - final Object εbins; - - ResponseMetadata(Object server, Object εbins) { - this.server = server; - this.εbins = εbins; - } - } - private static final class Response { - final ResponseMetadata metadata; + final Response.Metadata metadata; final List<ImtDisagg> disaggs; - Response(ResponseMetadata metadata, List<ImtDisagg> disaggs) { + Response(Response.Metadata metadata, List<ImtDisagg> disaggs) { this.metadata = metadata; this.disaggs = disaggs; } - } - private static final class ImtDisagg { - final Parameter imt; - final Object data; + private static final class Metadata { + final Object server; + final String rlabel = "Closest Distance, rRup (km)"; + final String mlabel = "Magnitude (Mw)"; + final String εlabel = "% Contribution to Hazard"; + final Object εbins; - ImtDisagg(Imt imt, Object data) { - this.imt = new Parameter( - ServletUtil.imtShortLabel(imt), - imt.name()); - this.data = data; + Metadata(Object server, Object εbins) { + this.server = server; + this.εbins = εbins; + } } - } - private static final class ResponseBuilder { + private static final class Builder { - Stopwatch timer; - Optional<RequestRp> requestRp = Optional.empty(); - Optional<RequestIml> requestIml = Optional.empty(); - Disaggregation disagg; + Stopwatch timer; + Optional<RequestRp> requestRp = Optional.empty(); + Optional<RequestIml> requestIml = Optional.empty(); + Disaggregation disagg; - ResponseBuilder timer(Stopwatch timer) { - this.timer = timer; - return this; - } + Builder timer(Stopwatch timer) { + this.timer = timer; + return this; + } - ResponseBuilder request(Object request) { - if (request instanceof RequestRp) { - requestRp = Optional.of((RequestRp) request); + Builder request(Object request) { + if (request instanceof RequestRp) { + requestRp = Optional.of((RequestRp) request); + return this; + } + requestIml = Optional.of((RequestIml) request); return this; } - requestIml = Optional.of((RequestIml) request); - return this; - } - ResponseBuilder disagg(Disaggregation disagg) { - this.disagg = disagg; - return this; - } + Builder disagg(Disaggregation disagg) { + this.disagg = disagg; + return this; + } - Response build() { + Response build() { - Set<Imt> imts = requestRp.isPresent() - ? requestRp.orElseThrow().imts - : requestIml.orElseThrow().imls.keySet(); + Set<Imt> imts = requestRp.isPresent() + ? requestRp.orElseThrow().imts + : requestIml.orElseThrow().imls.keySet(); - List<ImtDisagg> disaggs = imts.stream() - .map(imt -> new ImtDisagg(imt, disagg.toJson(imt))) - .collect(toList()); + List<ImtDisagg> disaggs = imts.stream() + .map(imt -> new ImtDisagg(imt, disagg.toJson(imt))) + .collect(toList()); - Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); + Object server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); - return new Response( - new ResponseMetadata(server, disagg.εBins()), - disaggs); + return new Response( + new Response.Metadata(server, disagg.εBins()), + disaggs); + } } } + private static final class ImtDisagg { + final Parameter imt; + final Object data; + + ImtDisagg(Imt imt, Object data) { + this.imt = new Parameter( + ServletUtil.imtShortLabel(imt), + imt.name()); + this.data = data; + } + } } 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 556dffea9..fb51309a0 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 @@ -36,7 +36,6 @@ import gov.usgs.earthquake.nshmp.model.SourceType; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; -import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Parameter; import gov.usgs.earthquake.nshmp.www.services.SourceServices.SourceModel; import io.micronaut.http.HttpRequest; @@ -55,10 +54,12 @@ public final class HazardService { static final String NAME = "Hazard Service"; static final Logger LOG = LoggerFactory.getLogger(HazardService.class); + private static final String TOTAL_KEY = "Total"; + /** HazardController.doGetUsage() handler. */ public static HttpResponse<String> getMetadata(HttpRequest<?> request) { var url = request.getUri().toString(); - var usage = new UsageMetadata(ServletUtil.model()); + var usage = new Metadata(ServletUtil.model()); var body = ResponseBody.usage() .name(NAME) .url(url) @@ -74,7 +75,7 @@ public final class HazardService { throws InterruptedException, ExecutionException { var stopwatch = Stopwatch.createStarted(); var hazard = calcHazard(request); - var response = new ResponseBuilder() + var response = new Response.Builder() .timer(stopwatch) .request(request) .hazard(hazard) @@ -123,14 +124,14 @@ public final class HazardService { return future.get(); } - static class UsageMetadata { + static class Metadata { final SourceModel model; final DoubleParameter longitude; final DoubleParameter latitude; final DoubleParameter vs30; - UsageMetadata(HazardModel model) { + Metadata(HazardModel model) { this.model = new SourceModel(model); // TODO need min max from model longitude = new DoubleParameter( @@ -184,124 +185,124 @@ public final class HazardService { } } - private static final class ResponseMetadata { - final Object server; - final String xlabel = "Ground Motion (g)"; - final String ylabel = "Annual Frequency of Exceedence"; - - ResponseMetadata(Object server) { - this.server = server; - } - } - private static final class Response { - final ResponseMetadata metadata; + + final Metadata metadata; final List<ImtCurves> hazardCurves; - Response(ResponseMetadata metadata, List<ImtCurves> hazardCurves) { + Response(Metadata metadata, List<ImtCurves> hazardCurves) { this.metadata = metadata; this.hazardCurves = hazardCurves; } - } - - private static final class ImtCurves { - final Parameter imt; - final List<Curve> data; - - ImtCurves(Imt imt, List<Curve> data) { - this.imt = new Parameter(ServletUtil.imtShortLabel(imt), imt.name()); - this.data = data; - } - } - private static final class Curve { - final String component; - final XySequence values; + private static final class Metadata { + final Object server; + final String xlabel = "Ground Motion (g)"; + final String ylabel = "Annual Frequency of Exceedence"; - Curve(String component, XySequence values) { - this.component = component; - this.values = values; + Metadata(Object server) { + this.server = server; + } } - } - private static final String TOTAL_KEY = "Total"; + private static final class Builder { - private static final class ResponseBuilder { + Stopwatch timer; + Request request; + Map<Imt, Map<SourceType, MutableXySequence>> componentMaps; + Map<Imt, MutableXySequence> totalMap; - Stopwatch timer; - Request request; - Map<Imt, Map<SourceType, MutableXySequence>> componentMaps; - Map<Imt, MutableXySequence> totalMap; + Builder timer(Stopwatch timer) { + this.timer = timer; + return this; + } - ResponseBuilder timer(Stopwatch timer) { - this.timer = timer; - return this; - } + Builder request(Request request) { + this.request = request; + return this; + } - ResponseBuilder request(Request request) { - this.request = request; - return this; - } + Builder hazard(Hazard hazard) { + // TODO necessary?? + checkState(totalMap == null, "Hazard has already been added to this builder"); - ResponseBuilder hazard(Hazard hazard) { - // TODO necessary?? - checkState(totalMap == null, "Hazard has already been added to this builder"); + componentMaps = new EnumMap<>(Imt.class); + totalMap = new EnumMap<>(Imt.class); - componentMaps = new EnumMap<>(Imt.class); - totalMap = new EnumMap<>(Imt.class); + var typeTotalMaps = curvesBySource(hazard); - var typeTotalMaps = curvesBySource(hazard); + for (var imt : hazard.curves().keySet()) { - for (var imt : hazard.curves().keySet()) { + /* Total curve for IMT. */ + XySequence.addToMap(imt, totalMap, hazard.curves().get(imt)); - /* Total curve for IMT. */ - XySequence.addToMap(imt, totalMap, hazard.curves().get(imt)); + /* Source component curves for IMT. */ + var typeTotalMap = typeTotalMaps.get(imt); + var componentMap = componentMaps.get(imt); - /* Source component curves for IMT. */ - var typeTotalMap = typeTotalMaps.get(imt); - var componentMap = componentMaps.get(imt); + if (componentMap == null) { + componentMap = new EnumMap<>(SourceType.class); + componentMaps.put(imt, componentMap); + } - if (componentMap == null) { - componentMap = new EnumMap<>(SourceType.class); - componentMaps.put(imt, componentMap); + for (var type : typeTotalMap.keySet()) { + XySequence.addToMap(type, componentMap, typeTotalMap.get(type)); + } } - for (var type : typeTotalMap.keySet()) { - XySequence.addToMap(type, componentMap, typeTotalMap.get(type)); - } + return this; } - return this; - } - - Response build() { - var hazards = new ArrayList<ImtCurves>(); - - for (Imt imt : totalMap.keySet()) { - var curves = new ArrayList<Curve>(); + Response build() { + var hazards = new ArrayList<ImtCurves>(); - // total curve - curves.add(new Curve( - TOTAL_KEY, - updateCurve(request, totalMap.get(imt), imt))); + for (Imt imt : totalMap.keySet()) { + var curves = new ArrayList<Curve>(); - // component curves - var typeMap = componentMaps.get(imt); - for (SourceType type : typeMap.keySet()) { + // total curve curves.add(new Curve( - type.toString(), - updateCurve(request, typeMap.get(type), imt))); + TOTAL_KEY, + updateCurve(request, totalMap.get(imt), imt))); + + // component curves + var typeMap = componentMaps.get(imt); + for (SourceType type : typeMap.keySet()) { + curves.add(new Curve( + type.toString(), + updateCurve(request, typeMap.get(type), imt))); + } + + hazards.add(new ImtCurves(imt, curves)); } - hazards.add(new ImtCurves(imt, curves)); + Object server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); + var response = new Response( + new Response.Metadata(server), + hazards); + + return response; } + } + + } + + private static final class ImtCurves { + final Parameter imt; + final List<Curve> data; + + ImtCurves(Imt imt, List<Curve> data) { + this.imt = new Parameter(ServletUtil.imtShortLabel(imt), imt.name()); + this.data = data; + } + } - Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); - var response = new Response( - new ResponseMetadata(server), - hazards); + private static final class Curve { + final String component; + final XySequence values; - return response; + Curve(String component, XySequence values) { + this.component = component; + this.values = values; } } @@ -353,5 +354,4 @@ public final class HazardService { .map(Imt::valueOf) .collect(toCollection(() -> EnumSet.noneOf(Imt.class))); } - } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java index ed8a2973d..84227bdce 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java @@ -28,43 +28,11 @@ public final class Metadata { this.status = Status.USAGE.toString(); this.description = description; this.syntax = syntax; - this.server = serverData(1, Stopwatch.createStarted()); + this.server = ServletUtil.serverData(1, Stopwatch.createStarted()); this.parameters = parameters; } } - public static Object serverData(int threads, Stopwatch timer) { - return new Server(threads, timer); - } - - private static class Server { - - final int threads; - final String timer; - final String version; - - Server(int threads, Stopwatch timer) { - this.threads = threads; - this.timer = timer.toString(); - this.version = "TODO where to get version?"; - } - - // static Component NSHMP_HAZ_COMPONENT = new Component( - // NSHMP_HAZ_URL, - // Versions.NSHMP_HAZ_VERSION); - // - // static final class Component { - // - // final String url; - // final String version; - // - // Component(String url, String version) { - // this.url = url; - // this.version = version; - // } - // } - } - public static class DefaultParameters { // final EnumParameter<Edition> edition; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java index 6b042038a..05f2389c6 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java @@ -26,7 +26,6 @@ import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceRequestData; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; -import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Metadata.DefaultParameters; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -276,7 +275,7 @@ public final class RateService { final List<Sequence> data; ResponseData(ResponseMetadata metadata, EqRate rates, Stopwatch timer) { - server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); + server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); this.metadata = metadata; this.data = buildSequence(rates); } @@ -335,7 +334,7 @@ public final class RateService { private Usage(Service service, DefaultParameters parameters) { description = service.description; this.syntax = service.syntax; - server = Metadata.serverData(1, Stopwatch.createStarted()); + server = ServletUtil.serverData(1, Stopwatch.createStarted()); this.parameters = parameters; } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java index 75cf76b28..f55efc1d7 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java @@ -20,7 +20,6 @@ import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; -import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Parameter; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -80,7 +79,9 @@ public class SourceServices { public ResponseData() { this.description = "Installed source model listing"; - this.server = Metadata.serverData(ServletUtil.THREAD_COUNT, Stopwatch.createStarted()); + this.server = ServletUtil.serverData( + ServletUtil.THREAD_COUNT, + Stopwatch.createStarted()); // this.parameters = new Parameters(); } } @@ -122,6 +123,7 @@ public class SourceServices { imts = model.gmms().stream() .map(Gmm::supportedImts) .flatMap(Set::stream) + .distinct() .sorted() .map(imt -> new Parameter(ServletUtil.imtShortLabel(imt), imt.name())) .collect(toList()); -- GitLab From cdef4f7e1f1adae52a0c0c6e857969acda47bd45 Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Tue, 1 Feb 2022 17:32:07 -0700 Subject: [PATCH 6/7] formatting and docs --- .../nshmp/www/hazard/DisaggController.java | 12 ++++++------ .../nshmp/www/hazard/HazardController.java | 12 ++++++------ .../earthquake/nshmp/www/hazard/HazardService.java | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) 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 ac2647922..a440b55a5 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 @@ -60,12 +60,12 @@ public class DisaggController { * @param vs30 Site Vs30 value in the range [150..3000] m/s. * @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 run disaggregations. If none are - * supplied then the default set of supported IMTs 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. @code{?imt=PGA,SA0p2,SA1P0}. + * @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. */ @Operation( summary = "Disaggregate hazard at a specified return period", 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 4dd72d3f5..1452b2b67 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 @@ -57,12 +57,12 @@ public class HazardController { * @param vs30 Site Vs30 value in m/s [150..3000] * @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 default set of supported IMTs 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. @code{?imt=PGA,SA0p2,SA1P0}. + * @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. * */ @Operation( 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 fb51309a0..f300a1384 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 @@ -66,7 +66,7 @@ public final class HazardService { .request(url) .response(usage) .build(); - var json = ServletUtil.GSON.toJson(body); + var json = ServletUtil.GSON2.toJson(body); return HttpResponse.ok(json); } @@ -86,7 +86,7 @@ public final class HazardService { .request(request) .response(response) .build(); - String json = ServletUtil.GSON.toJson(body); + String json = ServletUtil.GSON2.toJson(body); return HttpResponse.ok(json); } -- GitLab From f6a2da6f4b80692e71e5e227f77ada443b6cccff Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Tue, 1 Feb 2022 18:03:02 -0700 Subject: [PATCH 7/7] spotless formatting --- settings.gradle | 6 +++--- .../java/gov/usgs/earthquake/nshmp/www/ServletUtil.java | 1 + .../usgs/earthquake/nshmp/www/hazard/DisaggController.java | 1 + .../gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java | 1 + .../usgs/earthquake/nshmp/www/hazard/HazardController.java | 1 + .../gov/usgs/earthquake/nshmp/www/hazard/HazardService.java | 1 + .../gov/usgs/earthquake/nshmp/www/services/RateService.java | 1 + .../nshmp/www/services/SourceLogicTreesService.java | 1 + .../usgs/earthquake/nshmp/www/services/SourceServices.java | 1 + 9 files changed, 11 insertions(+), 3 deletions(-) diff --git a/settings.gradle b/settings.gradle index d1ad3db91..0daffad9b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,9 +20,9 @@ git { fetch("https://code.usgs.gov/ghsc/nshmp/nshms/nshm-hawaii.git", { name "nshmp-haz-dep--nshm-hi-2021" tag "2.0.0" - // fetch("https://code.usgs.gov/ghsc/nshmp/nshms/nshm-conus.git", { - // name "nshmp-haz-dep--nshm-conus-2018" - // tag "main" + // fetch("https://code.usgs.gov/ghsc/nshmp/nshms/nshm-conus.git", { + // name "nshmp-haz-dep--nshm-conus-2018" + // tag "main" }) } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java index a357ab271..3e3b7425d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java @@ -32,6 +32,7 @@ import gov.usgs.earthquake.nshmp.calc.ValueFormat; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.meta.MetaUtil; + import io.micronaut.context.annotation.Value; import io.micronaut.context.event.ShutdownEvent; import io.micronaut.context.event.StartupEvent; 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 a440b55a5..680d946ad 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 @@ -8,6 +8,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 io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; 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 55a955b99..ac428621e 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 @@ -27,6 +27,7 @@ import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.hazard.HazardService.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Parameter; + import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; 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 1452b2b67..0fe36de8a 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 io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; 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 f300a1384..f668b016e 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 @@ -38,6 +38,7 @@ import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Parameter; import gov.usgs.earthquake.nshmp.www.services.SourceServices.SourceModel; + import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java index 05f2389c6..d4b81df81 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java @@ -27,6 +27,7 @@ import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata.DefaultParameters; + import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java index 344514c8d..01e2d4c21 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java @@ -7,6 +7,7 @@ import gov.usgs.earthquake.nshmp.model.Models; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.SourceLogicTreesController; + import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java index f55efc1d7..b473fd92f 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java @@ -21,6 +21,7 @@ import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.Parameter; + import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; -- GitLab