diff artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java @ 8946:5d5d482da3e9

Implementing SINFO - FlowDepthMinMax calculation
author gernotbelger
date Tue, 13 Mar 2018 18:49:33 +0100
parents
children a4f1ac81f26d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java	Tue Mar 13 18:49:33 2018 +0100
@@ -0,0 +1,201 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.sinfo.flowdepthminmax;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.lang.math.DoubleRange;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
+import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
+import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthUtils;
+import org.dive4elements.river.artifacts.sinfo.flowdepthminmax.FlowDepthMinMaxAccess.MinMaxIdPair;
+import org.dive4elements.river.artifacts.sinfo.tkhcalculation.DischargeValuesFinder;
+import org.dive4elements.river.artifacts.sinfo.tkhcalculation.WaterlevelValuesFinder;
+import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder;
+import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo;
+import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
+import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
+import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
+import org.dive4elements.river.artifacts.states.WaterlevelData;
+import org.dive4elements.river.artifacts.states.WaterlevelFetcher;
+import org.dive4elements.river.model.River;
+
+/**
+ * @author Gernot Belger
+ */
+final class FlowDepthMinMaxCalculation {
+
+    private final CallContext context;
+
+    public FlowDepthMinMaxCalculation(final CallContext context) {
+        this.context = context;
+    }
+
+    public CalculationResult calculate(final SINFOArtifact sinfo) {
+
+        final String user = CalculationUtils.findArtifactUser(this.context, sinfo);
+
+        /* access input data */
+        final FlowDepthMinMaxAccess access = new FlowDepthMinMaxAccess(sinfo);
+        final River river = access.getRiver();
+        final RiverInfo riverInfo = new RiverInfo(river);
+
+        final Collection<MinMaxIdPair> minMaxPairs = access.getMinMaxPairs();
+
+        final DoubleRange calcRange = access.getRange();
+
+        /* calculate results for each diff pair */
+        final Calculation problems = new Calculation();
+
+        final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);
+
+        final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());
+
+        final FlowDepthMinMaxCalculationResults results = new FlowDepthMinMaxCalculationResults(calcModeLabel, user, riverInfo, calcRange);
+
+        for (final MinMaxIdPair minMaxPair : minMaxPairs) {
+            final FlowDepthMinMaxCalculationResult result = calculateResult(calcRange, minMaxPair, problems, infoProvider);
+            if (result != null)
+                results.addResult(result);
+        }
+
+        return new CalculationResult(results, problems);
+    }
+
+    /**
+     * Calculates one W-MSH differences pair.
+     *
+     * @param infoProvider
+     */
+    private FlowDepthMinMaxCalculationResult calculateResult(final DoubleRange calcRange, final MinMaxIdPair minMaxPair, final Calculation problems,
+            final RiverInfoProvider infoProvider) {
+
+        /* access real input data from database */
+        final String wstId = minMaxPair.getWstId();
+        final String minSoundingId = minMaxPair.getMinSoundingId();
+        final String maxSoundingId = minMaxPair.getMinSoundingId();
+
+        final BedHeightsFinder minBedHeight = minSoundingId == null ? null : BedHeightsFinder.forId(this.context, minSoundingId, calcRange, problems);
+        final BedHeightsFinder maxBedHeight = maxSoundingId == null ? null : BedHeightsFinder.forId(this.context, maxSoundingId, calcRange, problems);
+        if (minBedHeight == null && maxBedHeight == null)
+            return null;
+
+        /* REMARK: fetch ALL wst kms, because we want to determine the original reference gauge */
+        final WaterlevelData waterlevel = new WaterlevelFetcher().findWaterlevel(this.context, wstId, Double.NaN, Double.NaN, problems);
+        if (waterlevel == null)
+            return null;
+
+        final String label = createLabel(waterlevel, minBedHeight, maxBedHeight);
+
+        final WKms wstKms = waterlevel.getWkms();
+
+        final int soundingYear = checkSoundingYear(minBedHeight, maxBedHeight, problems);
+        FlowDepthUtils.checkYearDifference(label, waterlevel, soundingYear, problems);
+        // FIXME
+        // checkWaterlevelDiscretisation(wstKms, calcRange, problems);
+        // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden
+
+        /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */
+        final RiverInfoProvider riverInfoProvider = infoProvider.forWaterlevel(waterlevel);
+
+        final int wspYear = waterlevel.getYear();
+        final WstInfo wstInfo = new WstInfo(waterlevel.getName(), wspYear, riverInfoProvider.getReferenceGauge());
+
+        final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(wstKms);
+        final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wstKms);
+
+        final String waterlevelLabel = waterlevel.getName();
+        final String soundingLabel = buildSoundingLabel(minBedHeight, maxBedHeight);
+
+        /* real calculation loop */
+        final Collection<FlowDepthMinMaxRow> rows = new ArrayList<>();
+
+        // FIXME: determine what is the spatial discretisation that we will use...
+        final double[] allKms = wstKms.allKms().toNativeArray();
+        for (final double station : allKms) {
+            if (calcRange.containsDouble(station)) {
+
+                final double wst = waterlevelProvider.getWaterlevel(station);
+                final double discharge = dischargeProvider.getDischarge(station);
+
+                final double minBedHeightValue = minBedHeight == null ? Double.NaN : minBedHeight.getMeanBedHeight(station);
+                final double maxBedHeightValue = maxBedHeight == null ? Double.NaN : maxBedHeight.getMeanBedHeight(station);
+
+                final double minFlowDepth = wst - minBedHeightValue;
+                final double maxFlowDepth = wst - maxBedHeightValue;
+
+                // FIXME: unclear what is meant here...
+                final double meanBedHeight = Double.NaN;
+
+                // REMARK: access the location once only during calculation
+                final String location = riverInfoProvider.getLocation(station);
+
+                // REMARK: access the gauge once only during calculation
+                final String gaugeLabel = riverInfoProvider.findGauge(station);
+
+                rows.add(new FlowDepthMinMaxRow(station, minFlowDepth, maxFlowDepth, wst, discharge, waterlevelLabel, gaugeLabel, meanBedHeight, soundingLabel,
+                        location));
+            }
+        }
+
+        final BedHeightInfo minBedHeightInfo = minBedHeight == null ? null : minBedHeight.getInfo();
+        final BedHeightInfo maxBedHeightInfo = maxBedHeight == null ? null : maxBedHeight.getInfo();
+        return new FlowDepthMinMaxCalculationResult(label, wstInfo, minBedHeightInfo, maxBedHeightInfo, rows);
+    }
+
+    private String buildSoundingLabel(final BedHeightsFinder minBedHeight, final BedHeightsFinder maxBedHeight) {
+
+        if (minBedHeight == null)
+            return maxBedHeight.getInfo().getDescription();
+
+        if (maxBedHeight == null)
+            return minBedHeight.getInfo().getDescription();
+
+        return String.format("%s / %s", minBedHeight.getInfo().getDescription(), maxBedHeight.getInfo().getDescription());
+    }
+
+    private String createLabel(final WaterlevelData waterlevel, final BedHeightsFinder minBedHeight, final BedHeightsFinder maxBedHeight) {
+
+        final StringBuilder buffer = new StringBuilder(waterlevel.getName());
+
+        if (minBedHeight != null)
+            buffer.append(" - "). //
+            append(minBedHeight.getInfo().getDescription());
+
+        if (maxBedHeight != null)
+            buffer.append(" - "). //
+            append(maxBedHeight.getInfo().getDescription());
+
+        return buffer.toString();
+    }
+
+    private int checkSoundingYear(final BedHeightsFinder minBedHeight, final BedHeightsFinder maxBedHeight, final Calculation problems) {
+
+        if (maxBedHeight == null)
+            return minBedHeight.getInfo().getYear();
+
+        if (minBedHeight == null)
+            return maxBedHeight.getInfo().getYear();
+
+        final int minYear = minBedHeight.getInfo().getYear();
+        final int maxYear = minBedHeight.getInfo().getYear();
+
+        if (minYear != maxYear)
+            problems.addProblem("sinfo.flowdepthminmaxcalculation.soundingyear.different");
+
+        return minYear;
+    }
+}
\ No newline at end of file

http://dive4elements.wald.intevation.org