changeset 5962:d861efa13272

Partial rewrite of historical discharge curves. Needs testing.
author Sascha L. Teichmann <teichmann@intevation.de>
date Fri, 10 May 2013 10:46:56 +0200
parents 55687a506296
children dbbe3085384e
files artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeData.java
diffstat 2 files changed, 135 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java	Fri May 10 10:02:27 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java	Fri May 10 10:46:56 2013 +0200
@@ -11,7 +11,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.log4j.Logger;
 
@@ -87,6 +89,11 @@
         this.officialGaugeNumber = officialGaugeNumber;
     }
 
+    protected CalculationResult error(String msg) {
+        addProblem(msg);
+        return new CalculationResult(new HistoricalDischargeData(), this);
+    }
+
     public CalculationResult calculate() {
         if (hasProblems()) {
             log.warn("Parameters not valid for calculation.");
@@ -96,43 +103,73 @@
         Gauge gauge = Gauge.getGaugeByOfficialNumber(officialGaugeNumber);
         if (gauge == null) {
             // TODO: i18n
-            addProblem("hist.discharge.gauge.not.found");
-            return null;
+            return error("hist.discharge.gauge.not.found");
         }
 
         if (log.isDebugEnabled()) {
             debug();
         }
 
-        DischargeTable refTable = fetchReferenceTable(gauge);
         List<DischargeTable> dts = fetchDischargeTables(gauge);
-
-        int numTables = dts.size();
-
-        log.debug("Take " + numTables + " into account.");
-
-        if (numTables == 0) {
-            addProblem("cannot.find.hist.q.tables");
+        if (dts.isEmpty()) {
+            return error("cannot.find.hist.q.tables");
         }
 
-        WQTimerange[] wqt = prepareTimerangeData(refTable, dts);
-        WQKms[] wqs = prepareWQData(dts);
+        DischargeTable refTable = fetchReferenceTable(dts);
 
-        log.debug("Number of calculation results: " + wqt.length);
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("Take " + dts.size() + " into account.");
+        }
+
+        ValuesCache vc = new ValuesCache();
+
+        WQTimerange [] wqt = prepareData(refTable, dts, vc);
+        WQKms []       wqs = prepareWQData(dts, vc);
+
+        if (debug) {
+            log.debug("Number of calculation results: " + wqt.length);
+        }
 
         return new CalculationResult(new HistoricalDischargeData(wqt, wqs),
             this);
     }
 
-    protected DischargeTable fetchReferenceTable(Gauge gauge) {
-        return gauge.fetchMasterDischargeTable();
+    /** The youngest discharge table of the selected set is the reference */
+    protected DischargeTable fetchReferenceTable(List<DischargeTable> dts) {
+        DischargeTable ref = null;
+        long now = System.currentTimeMillis();
+        for (DischargeTable dt: dts) {
+            if (ref == null) {
+                ref = dt;
+            }
+            else {
+                TimeInterval cti = dt.getTimeInterval();
+                TimeInterval rti = ref.getTimeInterval();
+
+                long ct = cti.getStopTime() != null
+                    ? cti.getStopTime().getTime()
+                    : now;
+                long rt = rti.getStopTime() != null
+                    ? rti.getStopTime().getTime()
+                    : now;
+
+                if (ct > rt) {
+                    ref = dt;
+                }
+
+            }
+        }
+        return ref;
     }
 
     protected List<DischargeTable> fetchDischargeTables(Gauge gauge) {
-        List<DischargeTable> relevant = new ArrayList<DischargeTable>();
+
         List<DischargeTable> all = gauge.getDischargeTables();
+        List<DischargeTable> relevant = new ArrayList<DischargeTable>(all.size());
 
-        for (DischargeTable dt : all) {
+        for (DischargeTable dt: all) {
             if (isDischargeTableRelevant(dt)) {
                 relevant.add(dt);
             }
@@ -143,62 +180,27 @@
 
     /** True if timerange of given discharge table overlaps with timerange. */
     protected boolean isDischargeTableRelevant(DischargeTable dt) {
+
         TimeInterval ti = dt.getTimeInterval();
 
         if (dt.getKind() == Gauge.MASTER_DISCHARGE_TABLE || ti == null) {
             return false;
         }
 
-        Date start = ti.getStartTime();
-        long startTime = start.getTime();
-
-        if (startTime >= timerange[0] && startTime <= timerange[1]) {
-            return true;
-        }
+        long dtStart = ti.getStartTime().getTime();
+        long dtStop  = ti.getStopTime() != null
+            ? ti.getStopTime().getTime()
+            : System.currentTimeMillis();
 
-        Date stop = ti.getStopTime();
-        long stopTime = stop != null ? stop.getTime() : -1l;
-
-        if (stopTime >= timerange[0] && stopTime <= timerange[1]) {
-            return true;
-        }
-
-        log.debug("DischargeTable not in range: " + start + " -> " + stop);
-
-        return false;
+        return !(timerange[1] < dtStart || timerange[0] > dtStop);
     }
 
-    protected WQTimerange[] prepareTimerangeData(DischargeTable refTable,
-        List<DischargeTable> dts) {
-        if (refTable == null) {
-            addProblem("cannot.find.hist.q.reftable");
-            return prepareSimpleData(dts);
-        }
-        else {
-            return prepareData(refTable, dts);
-        }
-    }
-
-    protected WQKms[] prepareWQData(List<DischargeTable> dts) {
+    protected WQKms[] prepareWQData(List<DischargeTable> dts, ValuesCache vc) {
         WQKms[] wqs = new WQKms[dts.size()];
 
-        int idx = 0;
-
-        for (DischargeTable dt : dts) {
-            double[][] values = null;
-
-            if (dt.getKind() == DischargeTables.MASTER) {
-                values = DischargeTables.loadDischargeTableValues(dt,
-                    DischargeTables.MASTER_SCALE);
-
-            }
-            else {
-                values = DischargeTables.loadDischargeTableValues(dt,
-                    DischargeTables.HISTORICAL_SCALE);
-
-            }
-
-            wqs[idx++] = prepareWQ(dt, values);
+        for (int i = 0, N = wqs.length; i < N; ++i) {
+            DischargeTable dt = dts.get(i);
+            wqs[i] = prepareWQ(dt, vc.getValues(dts.get(i)));
         }
 
         return wqs;
@@ -214,96 +216,49 @@
             String.valueOf(km), dt.getTimeInterval());
     }
 
-    /** Without reference. */
-    protected WQTimerange[] prepareSimpleData(List<DischargeTable> dts) {
-        List<WQTimerange> wqts = new ArrayList<WQTimerange>(values.length);
+    protected String name(double value) {
+        return mode == EvaluationMode.W.getMode()
+            ? "W=" + value
+            : "Q=" + value;
+    }
 
-        for (double value : values) {
-            log.debug("Prepare data for value: " + value);
+    /** With reference. */
+    protected HistoricalWQTimerange[] prepareData(
+        DischargeTable       refTable,
+        List<DischargeTable> dts,
+        ValuesCache          vc
+    ) {
+        List<HistoricalWQTimerange> wqts =
+            new ArrayList<HistoricalWQTimerange>(values.length);
 
-            String name = mode == EvaluationMode.W.getMode()
-                ? "W=" + value
-                : "Q=" + value;
+        boolean debug = log.isDebugEnabled();
 
-            WQTimerange wqt = null;
+        for (double value: values) {
+            if (debug) {
+                log.debug("Prepare data plus diff for value: " + value);
+            }
+
+            double ref = mode == EvaluationMode.W.getMode()
+                ? vc.findValueForW(refTable, value)
+                : vc.findValueForQ(refTable, value);
+
+            if (Double.isNaN(ref)) {
+                addProblem("hist.discharge.bo.value.in.ref", value);
+                continue;
+            }
+
+            String name = name(value);
+            HistoricalWQTimerange wqt = null;
 
             for (DischargeTable dt : dts) {
                 Date[] ti = prepareTimeInterval(dt);
                 Timerange t = new Timerange(ti[0], ti[1]);
                 double w;
                 double q;
+                double diff;
 
                 if (mode == EvaluationMode.W.getMode()) {
-                    w = value;
-                    q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
-
-                    if (Double.isNaN(q)) {
-                        log.warn("Cannot find Q for W: " + w);
-                        addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]);
-                        continue;
-                    }
-                }
-                else {
-                    q = value;
-                    w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
-                }
-
-                log.debug("Q=" + q + " | W=" + w);
-
-                if (wqt == null) {
-                    wqt = new WQTimerange(name);
-                }
-
-                wqt.add(w, q, t);
-            }
-
-            if (wqt != null) {
-                wqts.add(wqt);
-            }
-        }
-
-        return wqts.toArray(new WQTimerange[wqts.size()]);
-    }
-
-    /** With reference. */
-    protected HistoricalWQTimerange[] prepareData(DischargeTable refTable,
-        List<DischargeTable> dts) {
-        List<HistoricalWQTimerange> wqts = new ArrayList<HistoricalWQTimerange>(
-            values.length);
-
-        for (double value : values) {
-            log.debug("Prepare data plus diff for value: " + value);
-
-            String name = mode == EvaluationMode.W.getMode()
-                ? "W=" + value
-                : "Q=" + value;
-            HistoricalWQTimerange wqt = null;
-
-            double ref;
-            double diff;
-
-            if (refTable != null && mode == EvaluationMode.W.getMode()) {
-                ref = findValueForW(refTable, value,
-                    DischargeTables.MASTER_SCALE);
-            }
-            else if (refTable != null) {
-                ref = findValueForQ(refTable, value,
-                    DischargeTables.MASTER_SCALE);
-            }
-            else {
-                ref = Double.NaN;
-            }
-
-            for (DischargeTable dt : dts) {
-                Date[] ti = prepareTimeInterval(dt);
-
-                Timerange t = new Timerange(ti[0], ti[1]);
-                double w;
-                double q;
-
-                if (mode == EvaluationMode.W.getMode()) {
-                    w = value;
-                    q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
+                    q = vc.findValueForW(dt, w = value);
 
                     if (Double.isNaN(q)) {
                         log.warn("Cannot find Q for W: " + w);
@@ -314,8 +269,7 @@
                     diff = ref - q;
                 }
                 else {
-                    q = value;
-                    w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
+                    w = vc.findValueForQ(dt, q = value);
 
                     if (Double.isNaN(w)) {
                         log.warn("Cannot find W for Q: " + q);
@@ -325,7 +279,9 @@
                     diff = ref - w;
                 }
 
-                log.debug("Q=" + q + " | W=" + w + " | Ref = " + ref);
+                if (debug) {
+                    log.debug("Q=" + q + " | W=" + w + " | Ref = " + ref);
+                }
 
                 if (wqt == null) {
                     wqt = new HistoricalWQTimerange(name);
@@ -339,8 +295,8 @@
             }
         }
 
-        return (HistoricalWQTimerange[]) wqts
-            .toArray(new HistoricalWQTimerange[wqts.size()]);
+        return (HistoricalWQTimerange[])wqts.toArray(
+            new HistoricalWQTimerange[wqts.size()]);
     }
 
     /** Returns discharge table interval as Date[]. */
@@ -359,18 +315,39 @@
         return new Date[] { start, end };
     }
 
-    protected double findValueForW(DischargeTable dt, double w, double scale) {
-        double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
-        double[] qs = DischargeTables.getQsForW(vs, w);
-        return qs.length == 0 ? Double.NaN : qs[0];
-    }
 
-    protected double findValueForQ(DischargeTable dt, double q, double scale) {
-        double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
-        double[] ws = DischargeTables.getWsForQ(vs, q);
+    /** Helper to avoid redundant loading of discharge table values. */
+    private static final class ValuesCache {
 
-        return ws.length == 0 ? Double.NaN : ws[0];
-    }
+        private Map<Integer, double[][]> cache;
+
+        ValuesCache() {
+            cache = new HashMap<Integer, double [][]>();
+        }
+
+        double [][] getValues(DischargeTable dt) {
+            Integer id = dt.getId();
+            double [][] vs = cache.get(id);
+            if (vs == null) {
+                vs = DischargeTables.loadDischargeTableValues(
+                    dt, DischargeTables.HISTORICAL_SCALE);
+                cache.put(id, vs);
+            }
+            return vs;
+        }
+
+        private static double firstOrNaN(double [] vs) {
+            return vs.length > 0 ? vs[0] : Double.NaN;
+        }
+
+        double findValueForW(DischargeTable dt, double w) {
+            return firstOrNaN(DischargeTables.getQsForW(getValues(dt), w));
+        }
+
+        double findValueForQ(DischargeTable dt, double q) {
+            return firstOrNaN(DischargeTables.getWsForQ(getValues(dt), q));
+        }
+    } // class ValuesCache
 
     /**
      * Writes the parameters used for this calculation to log.
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeData.java	Fri May 10 10:02:27 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/HistoricalDischargeData.java	Fri May 10 10:46:56 2013 +0200
@@ -17,6 +17,8 @@
     private WQTimerange[] wqTimeranges;
 
     public HistoricalDischargeData() {
+        wqs          = new WQKms[0];
+        wqTimeranges = new WQTimerange[0];
     }
 
     public HistoricalDischargeData(WQTimerange[] wqTimeranges, WQKms[] wqs) {

http://dive4elements.wald.intevation.org