From b45fa486af11b14924c2e7a3e385f0ed6e1e5221 Mon Sep 17 00:00:00 2001 From: Justin Barno Date: Wed, 14 Aug 2019 16:04:41 -0700 Subject: [PATCH] CCT 1.0.5: Bugfixes and performance improvements from feedback on users of REST clients. --- README.md | 6 +- calibration-gui/pom.xml | 2 +- .../gui/plotting/CodaWaveformPlot.java | 19 +- .../calibration-application/pom.xml | 2 +- .../calibration-integration/pom.xml | 2 +- calibration-service/calibration-model/pom.xml | 2 +- .../calibration-repository/pom.xml | 2 +- .../calibration-service-api/pom.xml | 2 +- .../calibration-service-impl/pom.xml | 2 +- .../service/impl/Joint1DPathCorrection.java | 7 +- .../processing/CalibrationCurveFitter.java | 58 ++---- calibration-service/pom.xml | 2 +- calibration-standalone/pom.xml | 2 +- common-gui/pom.xml | 2 +- .../gui/converters/sac/SacExporter.java | 16 +- .../common/gui/converters/sac/SacLoader.java | 8 +- common-service/common-application/pom.xml | 2 +- .../WaveformsCollectionJsonController.java | 34 +-- common-service/common-model/pom.xml | 2 +- .../coda/common/model/domain/Station.java | 5 + common-service/common-repository/pom.xml | 2 +- common-service/common-service-api/pom.xml | 2 +- common-service/common-service-impl/pom.xml | 2 +- common-service/pom.xml | 2 +- envelope-gui/pom.xml | 2 +- .../WaveformLoadingController.java | 193 ++++++++++-------- envelope-service/envelope-application/pom.xml | 2 +- .../web/EnvelopeJsonController.java | 25 ++- envelope-service/envelope-model/pom.xml | 2 +- envelope-service/envelope-repository/pom.xml | 2 +- envelope-service/envelope-service-api/pom.xml | 2 +- .../service/api/EnvelopeCreationService.java | 8 +- .../envelope/service/api/WaveformStacker.java | 23 +++ .../envelope-service-impl/pom.xml | 2 +- .../impl/EnvelopeCreationServiceImpl.java | 132 +++++++----- .../service/impl/EnvelopeStacker.java | 90 ++++++++ .../impl/EnvelopeCreationServiceImplTest.java | 173 ++++++++++++++++ envelope-service/pom.xml | 2 +- envelope-standalone/pom.xml | 2 +- .../data/client/EnvelopeLocalClient.java | 8 +- .../measure-mws/Mw-From-FDSN-waveforms.ipynb | 55 ++--- externals/pom.xml | 2 +- .../llnl/gnem/core/util/Geometry/EModel.java | 17 +- .../core/util/MathFunctions/MathFunction.java | 14 +- .../java/llnl/gnem/core/util/SeriesMath.java | 31 +-- .../main/java/llnl/gnem/core/util/TimeT.java | 20 +- mapping/pom.xml | 2 +- pom.xml | 2 +- 48 files changed, 647 insertions(+), 349 deletions(-) create mode 100644 envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/WaveformStacker.java create mode 100644 envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java create mode 100644 envelope-service/envelope-service-impl/src/test/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImplTest.java diff --git a/README.md b/README.md index 8020df36..2b855069 100644 --- a/README.md +++ b/README.md @@ -42,18 +42,18 @@ We don't presently deploy versioned artifacts into a public repository like the #### **As a single runnable JAR** ```shell -java -jar coda-calibration/calibration-standalone/target/calibration-standalone-1.0.4-runnable.jar +java -jar coda-calibration/calibration-standalone/target/calibration-standalone-1.0.5-runnable.jar ``` #### **GUI alone** ```shell -java -jar coda-calibration/calibration-gui/target/calibration-gui-1.0.4-runnable.jar +java -jar coda-calibration/calibration-gui/target/calibration-gui-1.0.5-runnable.jar ``` #### **Calibration REST service alone** ```shell -java -jar coda-calibration/calibration-service/application/target/application-1.0.4-runnable.jar +java -jar coda-calibration/calibration-service/application/target/application-1.0.5-runnable.jar ``` #### A note about HTTPS diff --git a/calibration-gui/pom.xml b/calibration-gui/pom.xml index 590270ec..bc74794c 100644 --- a/calibration-gui/pom.xml +++ b/calibration-gui/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 calibration-gui diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java index 4b496a91..a3a30cb5 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -150,7 +150,7 @@ public void setWaveform(Waveform waveform, SyntheticCoda synth) { // waveform so we need to check that for (WaveformPick pick : picks) { double pickTime; - //"'Bad' pick, plot it at begin time + //"'Bad' pick, plot it at begin time if (pick.getPickTimeSecFromOrigin() < 0) { pickTime = new TimeT(waveform.getBeginTime()).getEpochTime(); } else { @@ -271,17 +271,20 @@ private void plotSynthetic(Waveform waveform, SyntheticCoda synth, final TimeT b if (startTime.lt(endTime)) { interpolatedSeries.cut(startTime, endTime); synthSeriesBeforeEndMarker.cut(startTime, endTime); - synthSeriesRemaining.cutBefore(endTime); TimeSeries diffSeis = interpolatedSeries.subtract(synthSeriesBeforeEndMarker); int synthStartTimeShift = (int) (startTime.subtractD(beginTime) + 0.5); - int remainingStartTimeShift = (int) (endTime.subtractD(beginTime) + 0.5); double median = diffSeis.getMedian(); subplot.DeletePlotObject(legendRef); subplot.AddPlotObject(createLegend(labelText + "Shift: " + dfmt4.format(median))); subplot.AddPlotObject(createLine(synthStartTimeShift, median, synthSeriesBeforeEndMarker, Color.GREEN), PLOT_ORDERING.MODEL_FIT.getZOrder()); - subplot.AddPlotObject(createLine(remainingStartTimeShift, median, synthSeriesRemaining, Color.GREEN, 3, PenStyle.DASH), PLOT_ORDERING.MODEL_FIT.getZOrder()); + + if (endTime.lt(synthSeriesRemaining.getEndtime())) { + synthSeriesRemaining.cutBefore(endTime); + int remainingStartTimeShift = (int) (endTime.subtractD(beginTime) + 0.5); + subplot.AddPlotObject(createLine(remainingStartTimeShift, median, synthSeriesRemaining, Color.GREEN, 3, PenStyle.DASH), PLOT_ORDERING.MODEL_FIT.getZOrder()); + } repaint(); } } @@ -336,7 +339,7 @@ private PlotObject createRectangle(int timeStart, int timeEnd, int heightMin, in /* * (non-Javadoc) - * + * * @see * llnl.gnem.core.gui.waveform.WaveformPlot#handlePickMovedState(java.lang. * Object)T diff --git a/calibration-service/calibration-application/pom.xml b/calibration-service/calibration-application/pom.xml index 2d524d6d..5b7cbf57 100644 --- a/calibration-service/calibration-application/pom.xml +++ b/calibration-service/calibration-application/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/calibration-service/calibration-integration/pom.xml b/calibration-service/calibration-integration/pom.xml index 486fb261..91c47cc0 100644 --- a/calibration-service/calibration-integration/pom.xml +++ b/calibration-service/calibration-integration/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/calibration-service/calibration-model/pom.xml b/calibration-service/calibration-model/pom.xml index f26c0701..c3b28761 100644 --- a/calibration-service/calibration-model/pom.xml +++ b/calibration-service/calibration-model/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/calibration-service/calibration-repository/pom.xml b/calibration-service/calibration-repository/pom.xml index 530c276c..394535ce 100644 --- a/calibration-service/calibration-repository/pom.xml +++ b/calibration-service/calibration-repository/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/calibration-service/calibration-service-api/pom.xml b/calibration-service/calibration-service-api/pom.xml index 31775d3f..c1cff6a2 100644 --- a/calibration-service/calibration-service-api/pom.xml +++ b/calibration-service/calibration-service-api/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/calibration-service/calibration-service-impl/pom.xml b/calibration-service/calibration-service-impl/pom.xml index 24ae9c97..6cc6bae5 100644 --- a/calibration-service/calibration-service-impl/pom.xml +++ b/calibration-service/calibration-service-impl/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java index 121635f8..d1be3b10 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java @@ -24,7 +24,6 @@ import java.util.Set; import java.util.stream.IntStream; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.math3.analysis.MultivariateFunction; import org.apache.commons.math3.exception.TooManyEvaluationsException; import org.apache.commons.math3.optim.ConvergenceChecker; @@ -37,7 +36,7 @@ import org.apache.commons.math3.optim.nonlinear.scalar.ObjectiveFunction; import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.CMAESOptimizer; import org.apache.commons.math3.random.MersenneTwister; -import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; +import org.eclipse.collections.impl.list.mutable.primitive.DoubleArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -348,7 +347,7 @@ public double costFunction(Map> evidStaD Event evid = evidEntry.getKey(); Map stationMapData = evidEntry.getValue(); - List dataVec = new ArrayList<>(optimizationParams.length); + DoubleArrayList dataVec = new DoubleArrayList(stationMapData.size()); for (Entry entry : stationMapData.entrySet()) { double del = distanceMap.get(evid).get(entry.getKey()); double site = optimizationParams[stationIdxMap.get(entry.getKey())]; @@ -367,7 +366,7 @@ public double costFunction(Map> evidStaD if (dataVec.size() > 1) { double huberDel = .5d; - double median = new DescriptiveStatistics(ArrayUtils.toPrimitive(dataVec.toArray(new Double[0]))).getPercentile(50d); + double median = dataVec.median(); for (Entry entry1 : stationMapData.entrySet()) { double diff = Math.abs(localDataMap.get(evid).get(entry1.getKey()) - median); cost = cost + (Math.pow(huberDel, 2.0) + (Math.sqrt(1d + Math.pow(diff / huberDel, 2.0)) - 1d)); diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java index 8a1052bb..4465ce51 100755 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -159,6 +160,14 @@ public EnvelopeFit fitCodaCMAES(final float[] segment) { return fit; } + private PointValuePair bestByFunction(MultivariateFunction prediction, Function mapper) { + return IntStream.range(0, 1 + ITER_COUNT) + .parallel() + .mapToObj(mapper::apply) + .reduce((left, right) -> left.getValue() < right.getValue() ? left : right) + .orElseGet(() -> new PointValuePair(new double[4], Double.MAX_VALUE)); + } + public double[] gridSearchCodaVApacheCMAES(List> velocityDistancePairs) { return gridSearchCodaVApacheCMAES(velocityDistancePairs, YVV_MIN, YVV_MAX, 0, V_DIST_MAX); } @@ -192,23 +201,14 @@ public double[] gridSearchCodaVApacheCMAES(final List> vel return sum; }; - PointValuePair bestResult = optimizeCMAES( + Function mapper = (i) -> optimizeCMAES( prediction, new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minP1, maxP1), ThreadLocalRandom.current().nextDouble(minP2, maxP2), ThreadLocalRandom.current().nextDouble(minP3, maxP3) }), new CMAESOptimizer.Sigma(new double[] { 1, 75, 100 }), new SimpleBounds(new double[] { minP1, minP2, minP3 }, new double[] { maxP1, maxP2, maxP3 })); - for (int i = 1; i < ITER_COUNT; i++) { - PointValuePair result = optimizeCMAES( - prediction, - new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minP1, maxP1), ThreadLocalRandom.current().nextDouble(minP2, maxP2), - ThreadLocalRandom.current().nextDouble(minP3, maxP3) }), - new CMAESOptimizer.Sigma(new double[] { 1, 75, 100 }), - new SimpleBounds(new double[] { minP1, minP2, minP3 }, new double[] { maxP1, maxP2, maxP3 })); - if (result.getValue() < bestResult.getValue()) { - bestResult = result; - } - } + + PointValuePair bestResult = bestByFunction(prediction, mapper); double[] curve = new double[4]; curve[0] = bestResult.getPoint()[0] / V0_REG; @@ -256,25 +256,15 @@ public double[] gridSearchCodaBApacheCMAES(final List> bet return sum; }; - PointValuePair bestResult = optimizeCMAES( + Function mapper = (i) -> optimizeCMAES( prediction, new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minP1, maxP1), ThreadLocalRandom.current().nextDouble(minP2, maxP2), ThreadLocalRandom.current().nextDouble(minP3, maxP3) }), new CMAESOptimizer.Sigma(new double[] { .05, 0.5, 750 }), 50, new SimpleBounds(new double[] { minP1, minP2, minP3 }, new double[] { maxP1, maxP2, maxP3 })); - for (int i = 1; i < ITER_COUNT; i++) { - PointValuePair result = optimizeCMAES( - prediction, - new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minP1, maxP1), ThreadLocalRandom.current().nextDouble(minP2, maxP2), - ThreadLocalRandom.current().nextDouble(minP3, maxP3) }), - new CMAESOptimizer.Sigma(new double[] { .05, 0.5, 750 }), - 50, - new SimpleBounds(new double[] { minP1, minP2, minP3 }, new double[] { maxP1, maxP2, maxP3 })); - if (result.getValue() < bestResult.getValue()) { - bestResult = result; - } - } + + PointValuePair bestResult = bestByFunction(prediction, mapper); double[] curve = new double[4]; curve[0] = bestResult.getPoint()[0] / B0_REG; @@ -338,27 +328,15 @@ public double[] gridSearchCodaGApacheCMAES(final List> gam ConvergenceChecker convergenceChecker = new SimplePointChecker<>(0.005, 0.005, 100000); - PointValuePair bestResult = optimizeCMAES( + Function mapper = (i) -> optimizeCMAES( prediction, new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minP1, maxP1), minP2, minP3 }), new CMAESOptimizer.Sigma(new double[] { 1, 50, 50 }), convergenceChecker, 50, new SimpleBounds(new double[] { minP1, minP2, minP3 }, new double[] { maxP1, maxP2, maxP3 })); - for (int i = 1; i < ITER_COUNT; i++) { - - PointValuePair result = optimizeCMAES( - prediction, - new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minP1, maxP1), ThreadLocalRandom.current().nextDouble(minP2, maxP2), - ThreadLocalRandom.current().nextDouble(minP3, maxP3) }), - new CMAESOptimizer.Sigma(new double[] { 1, 50, 50 }), - convergenceChecker, - 50, - new SimpleBounds(new double[] { minP1, minP2, minP3 }, new double[] { maxP1, maxP2, maxP3 })); - if (result.getValue() < bestResult.getValue()) { - bestResult = result; - } - } + + PointValuePair bestResult = bestByFunction(prediction, mapper); double[] curve = new double[4]; curve[0] = bestResult.getPoint()[0] / G0_REG; diff --git a/calibration-service/pom.xml b/calibration-service/pom.xml index 4486245b..cae26073 100644 --- a/calibration-service/pom.xml +++ b/calibration-service/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/calibration-standalone/pom.xml b/calibration-standalone/pom.xml index 5e7cedf7..8cea825a 100644 --- a/calibration-standalone/pom.xml +++ b/calibration-standalone/pom.xml @@ -6,7 +6,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/common-gui/pom.xml b/common-gui/pom.xml index 667c47c7..a8e2a5f4 100644 --- a/common-gui/pom.xml +++ b/common-gui/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 gov.llnl.gnem.apps.coda.common diff --git a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporter.java b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporter.java index d0efda1d..9a293a83 100644 --- a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporter.java +++ b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporter.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -42,8 +42,6 @@ @Component public class SacExporter { private static final String SEP = "_"; - private final NumberFormat dfmt4 = NumberFormatFactory.fourDecimalOneLeadingZero(); - private static final Logger log = LoggerFactory.getLogger(SacExporter.class); public Result writeWaveformToDirectory(File exportDirectory, Waveform w) { @@ -104,24 +102,23 @@ public Result writeWaveformToDirectory(File exportDirectory, Waveform w) } os.flush(); - return new Result(true, filename); + return new Result<>(true, filename); } } catch (IOException e) { log.error(e.getMessage(), e); - return new Result(false, e.toString()).setErrors(Collections.singletonList(e)); + return new Result<>(false, e.toString()).setErrors(Collections.singletonList(e)); } } String message = "Waveform is missing required information or is malformed; waveform: " + w; log.error(message); - return new Result(false, message); + return new Result<>(false, message); } private boolean waveformFullySpecified(Waveform w) { return w != null && w.getLowFrequency() != null && w.getHighFrequency() != null - && present(w.getSegmentType()) && w.getSampleRate() != null && w.getBeginTime() != null && w.getEndTime() != null @@ -136,6 +133,7 @@ && present(w.getStream().getChannelName()) public String getFileName(Waveform w) { if (waveformFullySpecified(w)) { + NumberFormat dfmt4 = NumberFormatFactory.fourDecimalOneLeadingZero(); Stream stream = w.getStream(); Event ev = w.getEvent(); diff --git a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java index 7e794b59..8b0efa4a 100644 --- a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java +++ b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -209,7 +209,7 @@ public Result convertSacFileToWaveform(File file) { double[] segment = new double[rawVals.length]; try { IntStream.range(0, rawVals.length).forEach(index -> { - segment[index] = Double.valueOf(rawVals[index]); + segment[index] = rawVals[index]; if (!Double.isFinite(segment[index])) { throw new LightweightIllegalStateException("Invalid data in segment for file: " + fileName); } diff --git a/common-service/common-application/pom.xml b/common-service/common-application/pom.xml index e0f4d910..c342d9f6 100644 --- a/common-service/common-application/pom.xml +++ b/common-service/common-application/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/common-service/common-application/src/main/java/gov/llnl/gnem/apps/coda/common/application/web/WaveformsCollectionJsonController.java b/common-service/common-application/src/main/java/gov/llnl/gnem/apps/coda/common/application/web/WaveformsCollectionJsonController.java index b7125e82..519b4f4b 100644 --- a/common-service/common-application/src/main/java/gov/llnl/gnem/apps/coda/common/application/web/WaveformsCollectionJsonController.java +++ b/common-service/common-application/src/main/java/gov/llnl/gnem/apps/coda/common/application/web/WaveformsCollectionJsonController.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -14,8 +14,10 @@ */ package gov.llnl.gnem.apps.coda.common.application.web; +import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Set; import javax.validation.Valid; @@ -41,7 +43,7 @@ public class WaveformsCollectionJsonController { /** - * + * * @param waveformService */ @Autowired @@ -50,7 +52,7 @@ public WaveformsCollectionJsonController(WaveformService waveformService) { } /** - * + * * @param waveform * @param result * @return ResponseEntity @@ -106,7 +108,7 @@ public ResponseEntity setActiveFlagByStationName(@PathVariable Boolean active } /** - * + * * @param ids * @return ResponseEntity */ @@ -121,7 +123,7 @@ public ResponseEntity getBatchMetadata(@PathVariable("ids") List ids) { } /** - * + * * @param ids * @return ResponseEntity */ @@ -136,37 +138,37 @@ public ResponseEntity getBatch(@PathVariable("ids") Collection ids) { } /** - * + * * @param waveforms * @param result * @return ResponseEntity */ @PostMapping(value = "/batch/{sessionId}", name = "createBatch") - public ResponseEntity createBatch(@PathVariable Long sessionId, @Valid @RequestBody List waveforms, BindingResult result) { + public ResponseEntity createBatch(@PathVariable Long sessionId, @Valid @RequestBody Set waveforms, BindingResult result) { if (result.hasErrors()) { return ResponseEntity.status(HttpStatus.CONFLICT).body(result); } - getWaveformService().update(sessionId, waveforms); + getWaveformService().update(sessionId, new ArrayList<>(waveforms)); return ResponseEntity.ok().build(); } /** - * + * * @param waveforms * @param result * @return ResponseEntity */ @PutMapping(value = "/batch/{sessionId}", name = "updateBatch") - public ResponseEntity updateBatch(@PathVariable Long sessionId, @Valid @RequestBody List waveforms, BindingResult result) { + public ResponseEntity updateBatch(@PathVariable Long sessionId, @Valid @RequestBody Set waveforms, BindingResult result) { if (result.hasErrors()) { return ResponseEntity.status(HttpStatus.CONFLICT).body(result); } - getWaveformService().update(sessionId, waveforms); + getWaveformService().update(sessionId, new ArrayList<>(waveforms)); return ResponseEntity.ok().build(); } /** - * + * * @param ids * @return ResponseEntity */ @@ -177,12 +179,12 @@ public ResponseEntity deleteBatch(@PathVariable("ids") Collection ids) } /** - * + * */ private WaveformService waveformService; /** - * + * * @return WaveformService */ public WaveformService getWaveformService() { diff --git a/common-service/common-model/pom.xml b/common-service/common-model/pom.xml index 2716ffa2..a3f4c3a4 100644 --- a/common-service/common-model/pom.xml +++ b/common-service/common-model/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/domain/Station.java b/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/domain/Station.java index cc066cde..3b585baf 100644 --- a/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/domain/Station.java +++ b/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/domain/Station.java @@ -20,6 +20,8 @@ import javax.persistence.Embeddable; import javax.validation.constraints.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.format.annotation.NumberFormat; import com.fasterxml.jackson.annotation.JsonIdentityInfo; @@ -29,6 +31,7 @@ @JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class) public class Station implements Serializable { + private static final Logger log = LoggerFactory.getLogger(Station.class); private static final long serialVersionUID = 1L; @Column(name = "network_name") @@ -107,9 +110,11 @@ public boolean equals(Object obj) { Station other = (Station) obj; if (networkName == null) { if (other.networkName != null) { + log.trace("Mismatched network names {} and {}", networkName, other.networkName); return false; } } else if (!networkName.equalsIgnoreCase(other.networkName)) { + log.trace("Mismatched network names {} and {}", networkName, other.networkName); return false; } if (stationName == null) { diff --git a/common-service/common-repository/pom.xml b/common-service/common-repository/pom.xml index 4a388f6a..f357fb15 100644 --- a/common-service/common-repository/pom.xml +++ b/common-service/common-repository/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/common-service/common-service-api/pom.xml b/common-service/common-service-api/pom.xml index 28fe6ff6..7789385b 100644 --- a/common-service/common-service-api/pom.xml +++ b/common-service/common-service-api/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/common-service/common-service-impl/pom.xml b/common-service/common-service-impl/pom.xml index fce7ce87..4a4dc903 100644 --- a/common-service/common-service-impl/pom.xml +++ b/common-service/common-service-impl/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/common-service/pom.xml b/common-service/pom.xml index d5d9a68a..9203f8f1 100644 --- a/common-service/pom.xml +++ b/common-service/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 gov.llnl.gnem.apps.coda.common diff --git a/envelope-gui/pom.xml b/envelope-gui/pom.xml index c03074fc..0dd9da2f 100644 --- a/envelope-gui/pom.xml +++ b/envelope-gui/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 envelope-gui diff --git a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java index d1ad6975..81c19f51 100644 --- a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java +++ b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -20,10 +20,14 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Optional; import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -40,7 +44,11 @@ import gov.llnl.gnem.apps.coda.common.gui.converters.api.StackInfo; import gov.llnl.gnem.apps.coda.common.gui.converters.sac.SacExporter; import gov.llnl.gnem.apps.coda.common.gui.converters.sac.SacLoader; +import gov.llnl.gnem.apps.coda.common.gui.util.ProgressEventProgressListener; +import gov.llnl.gnem.apps.coda.common.gui.util.ProgressMonitor; import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; +import gov.llnl.gnem.apps.coda.common.model.messaging.Progress; +import gov.llnl.gnem.apps.coda.common.model.messaging.ProgressEvent; import gov.llnl.gnem.apps.coda.common.model.messaging.Result; import gov.llnl.gnem.apps.coda.envelope.gui.data.api.EnvelopeClient; import llnl.gnem.core.io.SAC.SACHeader; @@ -60,6 +68,12 @@ public class WaveformLoadingController extends AbstractSeismogramSaveLoadControl private CodaFilenameParser filenameParser; + private ProgressMonitor progressMonitor; + + private ProgressEvent progressEvent; + + private Progress progress; + @Autowired public WaveformLoadingController(List fileConverters, EnvelopeClient client, EnvelopeParamsController params, EventBus bus, SacExporter sacExporter, SacLoader sacLoader, CodaFilenameParser filenameParser) { @@ -67,12 +81,16 @@ public WaveformLoadingController(List fileConverters, E this.sacLoader = sacLoader; this.filenameParser = filenameParser; this.loadClient = (id, waveforms) -> client.postEnvelopes(id, waveforms).doOnNext(w -> { - this.sacExporter.writeWaveformToDirectory(getExportPath(w).toFile(), w); + CompletableFuture.runAsync(() -> this.sacExporter.writeWaveformToDirectory(getExportPath(w).toFile(), w)); }); this.setCompletionCallback(() -> { stackEnvelopes(createEnvelopeMapping(getSacFiles(getExportPath()))); }); this.setMaxBatching(50); + + progress = new Progress(-1l, 0l); + progressEvent = new ProgressEvent(idCounter.getAndIncrement(), progress); + progressMonitor = new ProgressMonitor("Saving Event-Sta-Freq pairs", new ProgressEventProgressListener(bus, progressEvent)); } private List getSacFiles(Path path) { @@ -91,7 +109,7 @@ private List getSacFiles(Path path) { public void loadFiles(List inputFiles) { try { Files.createDirectories(getExportPath()); - super.loadFiles(inputFiles); + super.loadFiles(inputFiles, this.getCompletionCallback(), progressMonitor); } catch (IOException ex) { // TODO: bus.post(new DisplayableExceptionEvent("Unable to create directory for envelopes.", ex)); log.error(ex.getMessage(), ex); @@ -99,7 +117,7 @@ public void loadFiles(List inputFiles) { } public TreeMap> createEnvelopeMapping(List files) { - TreeMap> evidStaFreqMap = new TreeMap>(); + TreeMap> evidStaFreqMap = new TreeMap<>(); for (int ii = 0; ii < files.size(); ii++) { try { File file = files.get(ii); @@ -142,100 +160,113 @@ private boolean isValidStationName(SACHeader header) { } public void stackEnvelopes(TreeMap> evidStaFreqMap) { + final AtomicLong count = new AtomicLong(0); + progress.setTotal((long) evidStaFreqMap.size()); + progress.setCurrent(count.get()); + progressEvent.setProgress(progress); + bus.post(progressEvent); + evidStaFreqMap.entrySet().parallelStream().forEach(entry -> { if (entry.getValue().size() > 1) { List files = entry.getValue(); - TimeSeries stackedSeries = null; - Waveform stackedWaveform = null; - int nseismograms = 0; + Map> waveformsByFreqAndSta = new HashMap<>(); for (int i = 0; i < files.size(); i++) { - Result result = sacLoader.convertSacFileToWaveform(files.get(i)); - if (result.isSuccess() && result.getResultPayload().isPresent()) { - Waveform rawWaveform = result.getResultPayload().get(); - if (rawWaveform != null && rawWaveform.getSegment() != null && rawWaveform.getSegment().length > 0) { - float[] fData = new float[rawWaveform.getSegment().length]; - for (int j = 0; j < fData.length; ++j) { - fData[j] = (float) rawWaveform.getSegment()[j]; - } - TimeSeries seis = new TimeSeries(fData, rawWaveform.getSampleRate(), new TimeT(rawWaveform.getBeginTime())); - if (stackedSeries == null) { - stackedSeries = seis; - stackedWaveform = rawWaveform; - nseismograms = 1; + Result res = filenameParser.parse(files.get(i).getName().toUpperCase(Locale.ENGLISH)); + if (res != null && res.isSuccess() && res.getResultPayload().isPresent()) { + StackInfo stackInfo = res.getResultPayload().get(); + Result result = sacLoader.convertSacFileToWaveform(files.get(i)); + + if (result.isSuccess() && result.getResultPayload().isPresent()) { + Waveform rawWaveform = result.getResultPayload().get(); + rawWaveform.setLowFrequency(stackInfo.getLowFrequency()); + rawWaveform.setHighFrequency(stackInfo.getHighFrequency()); + if (rawWaveform != null + && rawWaveform.getSegment() != null + && rawWaveform.getSegment().length > 0 + && rawWaveform.getStream() != null + && rawWaveform.getStream().getStation() != null) { + waveformsByFreqAndSta.computeIfAbsent(entry.getKey() + " " + rawWaveform.getStream().getStation().hashCode(), k -> new ArrayList<>()).add(rawWaveform); } else { - try { - if (seis.getTime().gt(stackedSeries.getTime())) { - stackedSeries.cutBefore(seis.getTime()); - } else { - seis.cutBefore(stackedSeries.getTime()); - } - - if (seis.getEndtime().lt(stackedSeries.getEndtime())) { - stackedSeries.cutAfter(seis.getEndtime()); - } else { - seis.cutAfter(stackedSeries.getEndtime()); - } - - if (stackedSeries.AddSeismogram(seis)) { - nseismograms++; - } else { - log.warn("{}. Unabled to stack series {} and {}; sample rate or timing mismatch.", entry.getKey(), stackedSeries, seis); - } - } catch (IllegalArgumentException e) { - log.warn("{}. Unabled to stack series {} and {}; {}", entry.getKey(), stackedSeries, seis, e.getMessage()); - } - } - if (stackedWaveform != null && (stackedWaveform.getLowFrequency() == null || stackedWaveform.getHighFrequency() == null)) { - Result res = filenameParser.parse(files.get(i).getName().toUpperCase(Locale.ENGLISH)); - if (res.isSuccess() && res.getResultPayload().isPresent()) { - StackInfo info = res.getResultPayload().get(); - stackedWaveform.setLowFrequency(info.getLowFrequency()); - stackedWaveform.setHighFrequency(info.getHighFrequency()); - } + log.warn("No data or bad station specification for waveform {}.", rawWaveform); } + } else { + log.warn("Unable to read envelope file {}. {}", files.get(i), result.getErrors()); } } else { - log.warn("Unabled to read envelope file {}. {}", files.get(i), result.getErrors()); + log.warn("Unable to parse envelope filename for frequency band {}. {}", files.get(i), res.getErrors()); } - - // TODO: Implement array processing - // TODO: Profile to improve perf - // TODO: Progress bars for stacking - // TODO: Export envelopes and stacks to separate dirs - // TODO: Create folder hierarchy for output (Year->Month->Evid->Sta) - - // 1. For each element envelope, find record begin and end of each - // trace ( b and e may have changed because of time shifting) - // 2. Keep the largest begin and smallest end times of all elements - // 3. Form an average envelope by summing each trace and then - // dividing by the total number of envelopes used - // 4. TODO ? Decimate the averaged envelopes using interp d 0.5 - // where d is the inverse of the number of samples per second TODO - // == didn't we already do this? - // 5. Change station header location lat and lon to the reference - // (array center?) and re-compute hypocentral distance - // 6. Store the envelope for coda amplitude measurement } - if (nseismograms > 1) { - stackedSeries.MultiplyScalar(1 / ((double) nseismograms)); - float[] stackedData = stackedSeries.getData(); - double[] data = new double[stackedData.length]; - for (int i = 0; i < data.length; ++i) { - data[i] = stackedData[i]; - } - stackedWaveform.setSegment(data); - stackedWaveform.getStream().setChannelName("STACK"); + List stackedWaveforms = waveformsByFreqAndSta.entrySet().stream().map(e -> stackEnvelopes(e.getValue())).collect(Collectors.toList()); + // TODO: Export envelopes and stacks to separate dirs + for (Waveform stackedWaveform : stackedWaveforms) { File stackFolder = getExportPath(stackedWaveform).toFile(); sacExporter.writeWaveformToDirectory(stackFolder, stackedWaveform); - } else { - log.warn("No valid seismograms available to stack for inputs: {}", files); } + } + long currentCount = count.getAndIncrement(); + if (currentCount % this.getMaxBatching() == 0) { + progress.setCurrent(currentCount); + progressEvent.setProgress(progress); + bus.post(progressEvent); } }); + + progress.setCurrent(progress.getTotal()); + progressEvent.setProgress(progress); + bus.post(progressEvent); + } + + private Waveform stackEnvelopes(List waves) { + // FIXME: Duplicate of the one in service. + // Need a common-utils because this pulls in stuff from Externals for TimeSeries etc so I can't cheat and slam it into the common model. + Waveform base = null; + if (waves != null && waves.size() > 1) { + try { + base = waves.get(0); + TimeSeries seis = convertToTimeSeries(base); + + // int nseismograms = 0; + for (int i = 1; i < waves.size(); i++) { + TimeSeries seis2 = convertToTimeSeries(waves.get(i)); + seis = seis.add(seis2); + } + seis.MultiplyScalar(1d / waves.size()); + + double[] data = new double[seis.getData().length]; + for (int j = 0; j < data.length; ++j) { + data[j] = seis.getData()[j]; + } + base.setSegment(data); + if (base.getSegment() == null || base.getSegment().length == 0) { + return null; + } + + base.setSampleRate(seis.getSamprate()); + base.setBeginTime(seis.getTime().getDate()); + base.setEndTime(seis.getEndtime().getDate()); + if (base.getStream() != null) { + base.getStream().setChannelName("STACK"); + } + } catch (Exception e) { + log.info(e.getMessage(), e); + } + } else { + log.info("Waveform with only one channel found for list {}, skipping stacking", waves); + } + return base; + } + + private TimeSeries convertToTimeSeries(Waveform base) { + float[] fData = new float[base.getSegment().length]; + for (int j = 0; j < fData.length; ++j) { + fData[j] = (float) base.getSegment()[j]; + } + TimeSeries seis = new TimeSeries(fData, base.getSampleRate(), new TimeT(base.getBeginTime())); + return seis; } public Path getExportPath() { diff --git a/envelope-service/envelope-application/pom.xml b/envelope-service/envelope-application/pom.xml index 17426746..1997d774 100644 --- a/envelope-service/envelope-application/pom.xml +++ b/envelope-service/envelope-application/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/envelope-service/envelope-application/src/main/java/gov/llnl/gnem/apps/coda/envelope/application/web/EnvelopeJsonController.java b/envelope-service/envelope-application/src/main/java/gov/llnl/gnem/apps/coda/envelope/application/web/EnvelopeJsonController.java index a55359da..f8be0077 100644 --- a/envelope-service/envelope-application/src/main/java/gov/llnl/gnem/apps/coda/envelope/application/web/EnvelopeJsonController.java +++ b/envelope-service/envelope-application/src/main/java/gov/llnl/gnem/apps/coda/envelope/application/web/EnvelopeJsonController.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -52,7 +52,24 @@ public ResponseEntity createBatch(@PathVariable Long sessionId, @Valid @Reque return ResponseEntity.badRequest().body(bindResult); } - Result> result = service.createEnvelopes(sessionId, job.getData(), job.getJobConfig()); + Result> result = service.createEnvelopes(sessionId, job.getData(), job.getJobConfig(), false); + + BodyBuilder response; + if (result.isSuccess()) { + response = ResponseEntity.ok(); + } else { + response = ResponseEntity.badRequest(); + } + return response.body(result); + } + + @PostMapping(value = "/batch-stacks-only/{sessionId}", name = "createBatchStacks") + public ResponseEntity createBatchStacks(@PathVariable Long sessionId, @Valid @RequestBody EnvelopeJob job, BindingResult bindResult) { + if (bindResult.hasErrors()) { + return ResponseEntity.badRequest().body(bindResult); + } + + Result> result = service.createEnvelopes(sessionId, job.getData(), job.getJobConfig(), true); BodyBuilder response; if (result.isSuccess()) { diff --git a/envelope-service/envelope-model/pom.xml b/envelope-service/envelope-model/pom.xml index 5f01b047..fa70beab 100644 --- a/envelope-service/envelope-model/pom.xml +++ b/envelope-service/envelope-model/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/envelope-service/envelope-repository/pom.xml b/envelope-service/envelope-repository/pom.xml index f6d232bf..3b906548 100644 --- a/envelope-service/envelope-repository/pom.xml +++ b/envelope-service/envelope-repository/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/envelope-service/envelope-service-api/pom.xml b/envelope-service/envelope-service-api/pom.xml index 6fc6b03a..12810d14 100644 --- a/envelope-service/envelope-service-api/pom.xml +++ b/envelope-service/envelope-service-api/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/EnvelopeCreationService.java b/envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/EnvelopeCreationService.java index 947f281c..3000f18c 100644 --- a/envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/EnvelopeCreationService.java +++ b/envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/EnvelopeCreationService.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -23,6 +23,6 @@ public interface EnvelopeCreationService { - public Result> createEnvelopes(Long sessionId, Collection waveforms, EnvelopeJobConfiguration envConf); + public Result> createEnvelopes(Long sessionId, Collection waveforms, EnvelopeJobConfiguration envConf, boolean shouldReturnStacks); } diff --git a/envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/WaveformStacker.java b/envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/WaveformStacker.java new file mode 100644 index 00000000..5a353362 --- /dev/null +++ b/envelope-service/envelope-service-api/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/api/WaveformStacker.java @@ -0,0 +1,23 @@ +/* +* Copyright (c) 2019, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.envelope.service.api; + +import java.util.List; + +import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; + +public interface WaveformStacker { + public Waveform stackEnvelopes(List waves); +} diff --git a/envelope-service/envelope-service-impl/pom.xml b/envelope-service/envelope-service-impl/pom.xml index 86243aef..abd813e2 100644 --- a/envelope-service/envelope-service-impl/pom.xml +++ b/envelope-service/envelope-service-impl/pom.xml @@ -6,7 +6,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java index a98b1032..223e52c8 100644 --- a/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java +++ b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java @@ -16,8 +16,10 @@ import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -31,9 +33,11 @@ import gov.llnl.gnem.apps.coda.common.model.util.LightweightIllegalStateException; import gov.llnl.gnem.apps.coda.common.service.util.WaveformToTimeSeriesConverter; import gov.llnl.gnem.apps.coda.common.service.util.WaveformUtils; +import gov.llnl.gnem.apps.coda.envelope.model.domain.EnvelopeBandParameters; import gov.llnl.gnem.apps.coda.envelope.model.domain.EnvelopeJobConfiguration; import gov.llnl.gnem.apps.coda.envelope.service.api.EnvelopeCreationService; import gov.llnl.gnem.apps.coda.envelope.service.api.EnvelopeParamsService; +import gov.llnl.gnem.apps.coda.envelope.service.api.WaveformStacker; import llnl.gnem.core.util.Passband; import llnl.gnem.core.waveform.seismogram.TimeSeries; @@ -46,14 +50,17 @@ public class EnvelopeCreationServiceImpl implements EnvelopeCreationService { private EnvelopeParamsService params; + private WaveformStacker stacker; + @Autowired - public EnvelopeCreationServiceImpl(WaveformToTimeSeriesConverter converter, EnvelopeParamsService params) { + public EnvelopeCreationServiceImpl(WaveformToTimeSeriesConverter converter, EnvelopeParamsService params, WaveformStacker stacker) { this.converter = converter; this.params = params; + this.stacker = stacker; } @Override - public Result> createEnvelopes(Long sessionId, Collection waveforms, EnvelopeJobConfiguration envConf) { + public Result> createEnvelopes(Long sessionId, Collection waveforms, EnvelopeJobConfiguration envConf, boolean shouldReturnStacks) { if (waveforms == null || waveforms.isEmpty()) { // TODO: Propagate warning to the status API return new Result<>(false, Collections.singletonList(new LightweightIllegalStateException("No waveforms provided; unable to compute envelopes.")), Collections.emptyList()); @@ -70,62 +77,85 @@ public Result> createEnvelopes(Long sessionId, Collection results = generateEnvelopesForBands(waveforms.stream().filter(Objects::nonNull).collect(Collectors.toList()), envConf); - // CompletableFuture.runAsync(() -> waveformSvc.saveAll(results)); + Set distinctWaveforms = new HashSet<>(waveforms); + List results = generateEnvelopesForBands(distinctWaveforms.stream().filter(Objects::nonNull).collect(Collectors.toList()), envConf); + if (shouldReturnStacks) { + results = results.stream() + .filter(w -> w.getEvent() != null && w.getLowFrequency() != null && w.getStream() != null && w.getStream().getStation() != null) + .collect(Collectors.groupingBy(w -> w.getEvent().getEventId() + w.getLowFrequency() + w.getStream().getStation().hashCode())) + .values() + .stream() + .map(waves -> { + return stacker.stackEnvelopes(waves); + }) + .collect(Collectors.toList()); + } return new Result<>(true, results); } private List generateEnvelopesForBands(List rawWaveforms, EnvelopeJobConfiguration envConf) { return rawWaveforms.parallelStream().map(wave -> { - return envConf.getFrequencyBandConfiguration().parallelStream().map(bandConfig -> { - try { - Waveform seisWave = new Waveform().mergeNonNullOrEmptyFields(wave); - - Double sampRate = seisWave.getSampleRate(); - if (sampRate != null && bandConfig.getHighFrequency() > (sampRate / 2.0)) { - log.info("Asked to generate a frequency band {}-{} above nyquist for waveform {}.", bandConfig.getLowFrequency(), bandConfig.getHighFrequency(), seisWave); - return null; - } - - TimeSeries seis = converter.convert(wave); - //Note these mutate the series - seis.RemoveMean(); - seis.removeTrend(); - seis.Taper(1); - - seis.filter(4, Passband.BAND_PASS, bandConfig.getLowFrequency(), bandConfig.getHighFrequency(), true); - - seis.Envelope(); - seis.Log10(); - - int smoothing = bandConfig.getSmoothing(); - seis.interpolate(bandConfig.getInterpolation()); - - //Convert it to samples - smoothing = (int) (smoothing * seis.getSamprate()); - seis.Smooth(smoothing); - - // final cut to eliminate smoothing edge effects - double trimlength = 2 * smoothing / seis.getSamprate(); - seis.cut(seis.getTime().add(trimlength), seis.getEndtime().add(-1 * trimlength)); - - seisWave.setSampleRate(seis.getSamprate()); - seisWave.setSegment(WaveformUtils.floatsToDoubles(seis.getData())); - seisWave.setLowFrequency(bandConfig.getLowFrequency()); - seisWave.setHighFrequency(bandConfig.getHighFrequency()); - seisWave.setBeginTime(seis.getTime().getDate()); - seisWave.setEndTime(seis.getEndtime().getDate()); - if (seisWave.getStream() != null) { - seisWave.getStream().setChannelName("STACK"); - } - - return seisWave; - } catch (Exception e) { - log.info(e.getMessage(), e); - return null; - } + return createEnvelopeForBand(wave, bandConfig); }).filter(Objects::nonNull); }).flatMap(Function.identity()).collect(Collectors.toList()); } + + private Waveform createEnvelopeForBand(Waveform wave, EnvelopeBandParameters bandConfig) { + try { + Waveform seisWave = new Waveform().mergeNonNullOrEmptyFields(wave); + + Double sampRate = seisWave.getSampleRate(); + if (sampRate != null && bandConfig.getHighFrequency() > (sampRate / 2.0)) { + log.info("Asked to generate a frequency band {}-{} outside nyquist for waveform {}.", bandConfig.getLowFrequency(), bandConfig.getHighFrequency(), seisWave); + return null; + } + + TimeSeries seis = converter.convert(wave); + + //Note these mutate the series + double maxNeededRate = sampRate; + if (maxNeededRate > bandConfig.getHighFrequency() * 2.0) { + maxNeededRate = (int) (bandConfig.getHighFrequency() * 2.0 + 0.5); + } + if (maxNeededRate < 1.0) { + maxNeededRate = 1.0; + } + if (maxNeededRate < bandConfig.getInterpolation()) { + maxNeededRate = bandConfig.getInterpolation(); + } + seis.interpolate(maxNeededRate); + + seis.RemoveMean(); + seis.removeTrend(); + seis.Taper(1); + + seis.filter(4, Passband.BAND_PASS, bandConfig.getLowFrequency(), bandConfig.getHighFrequency(), true); + + seis.Envelope(); + seis.Log10(); + + int smoothing = bandConfig.getSmoothing(); + + //Convert it to samples + smoothing = (int) (smoothing * seis.getSamprate()); + seis.Smooth(smoothing); + + // final cut to eliminate smoothing edge effects + double trimlength = 2 * smoothing / seis.getSamprate(); + seis.cut(seis.getTime().add(trimlength), seis.getEndtime().add(-1 * trimlength)); + + seisWave.setSampleRate(seis.getSamprate()); + seisWave.setSegment(WaveformUtils.floatsToDoubles(seis.getData())); + seisWave.setLowFrequency(bandConfig.getLowFrequency()); + seisWave.setHighFrequency(bandConfig.getHighFrequency()); + seisWave.setBeginTime(seis.getTime().getDate()); + seisWave.setEndTime(seis.getEndtime().getDate()); + + return seisWave; + } catch (Exception e) { + log.info(e.getMessage(), e); + return null; + } + } } diff --git a/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java new file mode 100644 index 00000000..438c977c --- /dev/null +++ b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java @@ -0,0 +1,90 @@ +/* +* Copyright (c) 2019, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.envelope.service.impl; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; +import gov.llnl.gnem.apps.coda.common.service.util.WaveformToTimeSeriesConverter; +import gov.llnl.gnem.apps.coda.common.service.util.WaveformUtils; +import gov.llnl.gnem.apps.coda.envelope.service.api.WaveformStacker; +import llnl.gnem.core.waveform.seismogram.TimeSeries; + +@Component +public class EnvelopeStacker implements WaveformStacker { + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private WaveformToTimeSeriesConverter converter; + + @Autowired + public EnvelopeStacker(WaveformToTimeSeriesConverter converter) { + this.converter = converter; + } + + @Override + public Waveform stackEnvelopes(List waves) { + // TODO: Implement array processing + // TODO: Profile to improve perf + // TODO: Progress notification for stacking + + //TODO: Thoughts on array stacks + // 1. Compare all the elements in the array to look for outliers in measurements (bad channels). + // Needs some kind of hypothesis testing probably to try and figure out 'bad' in this context (majority rules?). + // 2. For each element envelope, find record begin and end of each + // trace ( b and e may have changed because of time shifting) + // 3. Keep the largest begin and smallest end times of all elements + // 4. Form an average envelope by summing each trace and then + // dividing by the total number of envelopes used + // 5. Change station header location lat and lon to the reference + // (array element closest to centroid of hull maybe?) and re-compute hypocentral distance + // 6. Store the envelope for coda amplitude measurement + + Waveform base = null; + if (waves != null && waves.size() > 1) { + try { + base = waves.get(0); + TimeSeries seis = converter.convert(base); + for (int i = 1; i < waves.size(); i++) { + TimeSeries seis2 = converter.convert(waves.get(i)); + seis = seis.add(seis2); + } + seis.MultiplyScalar(1d / (waves.size())); + base.setSegment(WaveformUtils.floatsToDoubles(seis.getData())); + + if (base.getSegment() == null || base.getSegment().length == 0) { + return null; + } + + base.setSampleRate(seis.getSamprate()); + base.setBeginTime(seis.getTime().getDate()); + base.setEndTime(seis.getEndtime().getDate()); + if (base.getStream() != null) { + base.getStream().setChannelName("STACK"); + } + } catch (Exception e) { + log.info(e.getMessage(), e); + } + } else { + log.info("Waveform with only one channel found for list {}, skipping stacking", waves); + } + return base; + } +} diff --git a/envelope-service/envelope-service-impl/src/test/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImplTest.java b/envelope-service/envelope-service-impl/src/test/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImplTest.java new file mode 100644 index 00000000..710ff4b1 --- /dev/null +++ b/envelope-service/envelope-service-impl/src/test/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImplTest.java @@ -0,0 +1,173 @@ +/* +* Copyright (c) 2019, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.envelope.service.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gov.llnl.gnem.apps.coda.common.model.domain.Event; +import gov.llnl.gnem.apps.coda.common.model.domain.Station; +import gov.llnl.gnem.apps.coda.common.model.domain.Stream; +import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; +import gov.llnl.gnem.apps.coda.common.model.messaging.Result; +import gov.llnl.gnem.apps.coda.common.service.util.WaveformToTimeSeriesConverter; +import gov.llnl.gnem.apps.coda.envelope.model.domain.Default14BandEnvelopeJobConfiguration; +import gov.llnl.gnem.apps.coda.envelope.service.api.EnvelopeParamsService; + +@ExtendWith(MockitoExtension.class) +public class EnvelopeCreationServiceImplTest { + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + @Mock + private EnvelopeParamsService params; + + private EnvelopeCreationServiceImpl envelopeCreationService; + + @BeforeAll + protected static void setUpBeforeClass() throws Exception { + } + + @AfterAll + protected static void tearDownAfterClass() throws Exception { + } + + @BeforeEach + protected void setUp() throws Exception { + WaveformToTimeSeriesConverter converter = new WaveformToTimeSeriesConverter(); + envelopeCreationService = new EnvelopeCreationServiceImpl(converter, params, new EnvelopeStacker(converter)); + } + + @AfterEach + protected void tearDown() throws Exception { + } + + @Test + public void testCreateEnvelopesDefault14Bands() throws Exception { + Mockito.when(params.getConfiguration()).thenReturn(Default14BandEnvelopeJobConfiguration.getConfiguration()); + List waveforms = new ArrayList<>(); + + waveforms = generateWaveforms(); + + Result> stacks = envelopeCreationService.createEnvelopes(1l, waveforms, null, false); + assertTrue(stacks.isSuccess()); + assertTrue(stacks.getResultPayload().isPresent()); + long eventCount = waveforms.stream().map(w -> w.getEvent().getEventId()).distinct().count(); + long bandCount = params.getConfiguration().getFrequencyBandConfiguration().size(); + long channelCount = waveforms.stream().map(w -> w.getStream().getChannelName()).distinct().count(); + assertEquals(bandCount * eventCount * channelCount, stacks.getResultPayload().get().stream().distinct().count()); + } + + @Test + public void testCreateStacksDefault14Bands() throws Exception { + Mockito.when(params.getConfiguration()).thenReturn(Default14BandEnvelopeJobConfiguration.getConfiguration()); + List waveforms = new ArrayList<>(); + + waveforms = generateWaveforms(); + + Result> stacks = envelopeCreationService.createEnvelopes(1l, waveforms, null, true); + assertTrue(stacks.isSuccess()); + assertTrue(stacks.getResultPayload().isPresent()); + + long eventCount = waveforms.stream().map(w -> w.getEvent().getEventId()).distinct().count(); + long bandCount = params.getConfiguration().getFrequencyBandConfiguration().size(); + assertEquals(bandCount * eventCount, stacks.getResultPayload().get().stream().distinct().count()); + } + + private List generateWaveforms() { + Date startTime = Date.from(Instant.now()); + Date endTime = Date.from(startTime.toInstant().plusSeconds(1l)); + + Event event = new Event(); + event.setEventId("1234"); + event.setLatitude(1.0); + event.setLongitude(1.0); + + Event event2 = new Event(); + event2.setEventId("2345"); + event2.setLatitude(1.0); + event2.setLongitude(1.0); + + Station station = new Station(); + station.setLatitude(1.0); + station.setLongitude(1.0); + station.setStationName("TEST"); + station.setNetworkName(null); + + Stream s1 = new Stream(); + s1.setChannelName("BHE"); + s1.setStation(station); + + Stream s2 = new Stream(); + s2.setChannelName("BHN"); + s2.setStation(station); + + double[] data = new double[400]; + Arrays.fill(data, 0, 200, 1000.0); + Arrays.fill(data, 2, 400, 2000.0); + Waveform w1 = new Waveform(); + w1.setEvent(event); + w1.setStream(s1); + w1.setSegment(data); + w1.setBeginTime(startTime); + w1.setEndTime(endTime); + w1.setSampleRate(16d); + + Arrays.fill(data, 0, 200, 1000.0); + Arrays.fill(data, 2, 400, 2000.0); + Waveform w2 = new Waveform(); + w2.setEvent(event); + w2.setStream(s2); + w2.setSegment(data); + w2.setBeginTime(startTime); + w2.setEndTime(endTime); + w2.setSampleRate(16d); + + Waveform w3 = Waveform.mergeNonNullOrEmptyFields(w1, new Waveform()); + w3.setEvent(event2); + Waveform w4 = Waveform.mergeNonNullOrEmptyFields(w2, new Waveform()); + w4.setEvent(event2); + + List waveforms = new ArrayList<>(2); + waveforms.add(w1); + waveforms.add(w2); + + //Toss some duplicates in just to see + waveforms.add(w1); + waveforms.add(w2); + + waveforms.add(w3); + waveforms.add(w4); + return waveforms; + } +} diff --git a/envelope-service/pom.xml b/envelope-service/pom.xml index b104396e..9a15b512 100644 --- a/envelope-service/pom.xml +++ b/envelope-service/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 gov.llnl.gnem.apps.coda.envelope diff --git a/envelope-standalone/pom.xml b/envelope-standalone/pom.xml index cb5a6bcb..00ccc054 100644 --- a/envelope-standalone/pom.xml +++ b/envelope-standalone/pom.xml @@ -6,7 +6,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 4.0.0 diff --git a/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java index a42fb584..47c76b5f 100644 --- a/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java +++ b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -69,7 +69,7 @@ public Flux postEnvelopes(Long sessionId, List segments) { @Override public Flux postEnvelopes(Long sessionId, List segments, EnvelopeJobConfiguration conf) { - return Flux.fromIterable(service.createEnvelopes(sessionId, segments, conf).getResultPayload().orElseGet(() -> new ArrayList())); + return Flux.fromIterable(service.createEnvelopes(sessionId, segments, conf, false).getResultPayload().orElseGet(() -> new ArrayList<>())); } } diff --git a/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb b/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb index 3ab6eb2e..0e2477c9 100644 --- a/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb +++ b/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -21,7 +21,6 @@ "import json\n", "\n", "from obspy import read\n", - "from obspy.signal.trigger import recursive_sta_lta_py\n", "import matplotlib.pyplot as plt" ] }, @@ -34,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -95,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -114,7 +113,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": { "scrolled": true }, @@ -138,7 +137,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": { "scrolled": true }, @@ -193,7 +192,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -211,7 +210,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -227,7 +226,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -256,7 +255,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -283,7 +282,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": { "scrolled": false }, @@ -297,41 +296,11 @@ } ], "source": [ - "r = requests.post('https://127.0.0.1:53922/api/v1/envelopes/create/batch/10000', json=waveData, verify=False)\n", + "r = requests.post('https://127.0.0.1:53922/api/v1/envelopes/create/batch-stacks-only/10000', json=waveData, verify=False)\n", "print(r.status_code)\n", "envData = json.loads(r.text)[\"resultPayload\"]" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can look at the envelopes to see if anything looks odd." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "for env in envData:\n", - " plt.plot(env[\"segment\"])" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -341,7 +310,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": { "scrolled": true }, diff --git a/externals/pom.xml b/externals/pom.xml index a3b89c2d..eadbf070 100644 --- a/externals/pom.xml +++ b/externals/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 externals diff --git a/externals/src/main/java/llnl/gnem/core/util/Geometry/EModel.java b/externals/src/main/java/llnl/gnem/core/util/Geometry/EModel.java index ed740b93..487c6102 100755 --- a/externals/src/main/java/llnl/gnem/core/util/Geometry/EModel.java +++ b/externals/src/main/java/llnl/gnem/core/util/Geometry/EModel.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.List; import org.apache.commons.math3.linear.Array2DRowRealMatrix; import org.apache.commons.math3.linear.RealMatrix; @@ -117,7 +118,7 @@ public static double getAngleInRadians(double v1x, double v1y, double v2x, doubl * The Vector of event-station azimuths * @return The azgap value */ - public static double getAzGapFromAzVector(ArrayList az) { + public static double getAzGapFromAzVector(List az) { Collections.sort(az); double maxGap = 0.0; double dif; @@ -147,12 +148,12 @@ public static double getAzGapFromAzVector(ArrayList az) { * A Vector of station longitudes * @return The azgap value */ - public static double getAzgap(double evla, double evlo, ArrayList stla, ArrayList stlo) { + public static double getAzgap(double evla, double evlo, List stla, List stlo) { int nsta = stla.size(); if (nsta < 1 || nsta != stlo.size()) { return -1.0; } - ArrayList az = new ArrayList<>(); + List az = new ArrayList<>(); //Calculate event-station azimuths for all stations... for (int j = 0; j < nsta; ++j) { @@ -733,9 +734,9 @@ public static ArrayList track(double startLat, double startLon, double e /* * function to compute a great circle arc on a sphere - * + * * after the fortran routine by Dave Harris to do the same. - * + * */ public static void getGreatCircleArc(GeographicCoordinate coord1, GeographicCoordinate coord2, double az, double dist, double[] lats, double[] lons, int npts) { diff --git a/externals/src/main/java/llnl/gnem/core/util/MathFunctions/MathFunction.java b/externals/src/main/java/llnl/gnem/core/util/MathFunctions/MathFunction.java index db8b7c4c..bc7c4cc3 100755 --- a/externals/src/main/java/llnl/gnem/core/util/MathFunctions/MathFunction.java +++ b/externals/src/main/java/llnl/gnem/core/util/MathFunctions/MathFunction.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -15,7 +15,7 @@ package llnl.gnem.core.util.MathFunctions; import java.security.SecureRandom; -import java.util.ArrayList; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -293,7 +293,7 @@ public static double randomBetween(double low, double high) { * @return cos(theta) * */ - public Double getCosineTheta(ArrayList vectorA, ArrayList vectorB) { + public Double getCosineTheta(List vectorA, List vectorB) { Double AdotB = dotprod(vectorA, vectorB); Double normA = L2norm(vectorA); Double normB = L2norm(vectorB); @@ -302,7 +302,7 @@ public Double getCosineTheta(ArrayList vectorA, ArrayList vector return costheta; } - public Double dotprod(ArrayList vectorA, ArrayList vectorB) { + public Double dotprod(List vectorA, List vectorB) { Double result = 0.; if (vectorA.size() == vectorB.size()) { @@ -324,7 +324,7 @@ public Double dotprod(ArrayList vectorA, ArrayList vectorB) { * @param ndimensionalvector * @return */ - public static double L2norm(ArrayList ndimensionalvector) { + public static double L2norm(List ndimensionalvector) { double sumofsquares = 0; for (Double element : ndimensionalvector) { double squarevalue = element * element; diff --git a/externals/src/main/java/llnl/gnem/core/util/SeriesMath.java b/externals/src/main/java/llnl/gnem/core/util/SeriesMath.java index 800b1d6a..e18cc9dd 100755 --- a/externals/src/main/java/llnl/gnem/core/util/SeriesMath.java +++ b/externals/src/main/java/llnl/gnem/core/util/SeriesMath.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -1029,7 +1029,7 @@ public static float[] envelope(float[] data) { public static int[] find(long[] array, String condition, long value) { int[] indexes; Integer temp; - Vector indexV = new Vector(); + Vector indexV = new Vector<>(); if (condition.equals("==")) { for (int i = 0; i < array.length; i++) { @@ -1582,26 +1582,7 @@ public static double getMedian(List data) { return median; } - ArrayList tmp = new ArrayList<>(data); - Collections.sort(tmp); - int nhalf = Nsamps / 2; - if (2 * nhalf == Nsamps) { - median = 0.5 * (tmp.get(nhalf) + tmp.get(nhalf - 1)); - } else { - median = tmp.get(nhalf); - } - return median; - - } - - public static double getMedian(ArrayList data) { - int Nsamps = data.size(); - double median = 0.0; - if (Nsamps < 1) { - return median; - } - - ArrayList tmp = new ArrayList<>(data); + List tmp = new ArrayList<>(data); Collections.sort(tmp); int nhalf = Nsamps / 2; if (2 * nhalf == Nsamps) { @@ -2580,7 +2561,7 @@ public static void removeGlitches(float[] data, double threshhold) { /** * Check whether the data series has flat segments - where every element of * the segment is identical - * + * * @param minsegmentlength * the shortest number of datapoints that must be identical * before it qualifies as "flat" diff --git a/externals/src/main/java/llnl/gnem/core/util/TimeT.java b/externals/src/main/java/llnl/gnem/core/util/TimeT.java index 75b11527..bb5d612e 100755 --- a/externals/src/main/java/llnl/gnem/core/util/TimeT.java +++ b/externals/src/main/java/llnl/gnem/core/util/TimeT.java @@ -2,11 +2,11 @@ * Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. -* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. -* +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* * Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: * http://www.apache.org/licenses/LICENSE-2.0 -* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the license. * * This work was performed under the auspices of the U.S. Department of Energy @@ -56,6 +56,7 @@ public strictfp class TimeT implements Comparable, Serializable { private static final String[] MONTH_ABBREVIATION = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; private final long milliseconds; private final int microseconds; + private static final TimeZone tz = TimeZone.getTimeZone("GMT"); public static final double MAX_EPOCH_TIME = 9999999999.999; /** @@ -93,7 +94,7 @@ public TimeT(int year, int month, int day, int hour, int minute, double second) // Get the fractional part of the seconds int tmp = (int) Math.rint((second - (int) second) * 1000000); microseconds = tmp % 1000; - GregorianCalendar d = new GregorianCalendar(TimeZone.getTimeZone("GMT")); + GregorianCalendar d = new GregorianCalendar(tz); d.set(year, month - 1, day, hour, minute, (int) second); d.set(Calendar.MILLISECOND, tmp / 1000); milliseconds = d.getTime().getTime(); @@ -141,7 +142,7 @@ public TimeT(int year, int month, int day, int hour, int minute, int sec, int ms * Description of the Parameter */ public TimeT(int year, int jday, int hour, int min, int sec, int msec) { - GregorianCalendar d = new GregorianCalendar(TimeZone.getTimeZone("GMT")); + GregorianCalendar d = new GregorianCalendar(tz); d.clear(); d.set(Calendar.MILLISECOND, msec); d.set(Calendar.SECOND, sec); @@ -230,7 +231,6 @@ public TimeT(String stringSpecifier) { } public TimeT(Date date) { - TimeZone tz = TimeZone.getTimeZone("GMT"); GregorianCalendar d = new GregorianCalendar(tz); d.setTime(date); @@ -573,7 +573,6 @@ public void print(PrintStream ps) { } public Date getDate() { - TimeZone tz = TimeZone.getTimeZone("Etc/UTC"); GregorianCalendar d = new GregorianCalendar(tz); long tmp = milliseconds; tmp -= d.get(Calendar.DST_OFFSET); @@ -582,7 +581,7 @@ public Date getDate() { } private GregorianCalendar getCalendar() { - GregorianCalendar d = new GregorianCalendar(TimeZone.getTimeZone("GMT")); + GregorianCalendar d = new GregorianCalendar(tz); Date date = new Date(milliseconds); d.setTime(date); return d; @@ -605,7 +604,6 @@ private GregorianCalendar getCalendar() { * @see SimpleDateFormat for format rules. */ public String toString(String format) { - TimeZone tz = TimeZone.getTimeZone("GMT"); GregorianCalendar d = new GregorianCalendar(tz); Date date = new Date(milliseconds + (int) Math.rint(microseconds / 1000.0)); d.setTime(date); @@ -678,7 +676,7 @@ public static long toMilliseconds(double seconds) { public static Date getDateFrom(String timeString, String formatString) throws ParseException { SimpleDateFormat df = new SimpleDateFormat(formatString); - df.setTimeZone(TimeZone.getTimeZone("GMT")); + df.setTimeZone(tz); return df.parse(timeString); } @@ -748,7 +746,7 @@ public static int getMaxDaysOfMonth(int year, int month) { * @return True if the year is a leap year. */ public static boolean isLeapYear(int year) { - GregorianCalendar d = new GregorianCalendar(TimeZone.getTimeZone("GMT")); + GregorianCalendar d = new GregorianCalendar(tz); return d.isLeapYear(year); } diff --git a/mapping/pom.xml b/mapping/pom.xml index 07ef9efe..ae0a590b 100644 --- a/mapping/pom.xml +++ b/mapping/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 gov.llnl.gnem.apps.coda.common diff --git a/pom.xml b/pom.xml index 81910b55..8fe65d16 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.4-SNAPSHOT + 1.0.5 coda-calibration pom