changeset 8581:073ea4bcea58

(issue1755) Interpolate BedQuality Results This adds an interpolation function to each various bedQuality result class. Imho this is ok as the interpolation function can be seen as part of the result. The interpolation function is initalized on first use and can be accessed through get.*Interpol functions.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 16 Mar 2015 15:36:38 +0100
parents d9f038b8e2ce
children 17a3030bbda2
files artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiameterResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedParametersResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadDiameterResult.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java artifacts/src/main/java/org/dive4elements/river/utils/DoubleUtil.java
diffstat 5 files changed, 225 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiameterResult.java	Mon Mar 16 14:37:30 2015 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiameterResult.java	Mon Mar 16 15:36:38 2015 +0100
@@ -8,8 +8,13 @@
 
 package org.dive4elements.river.artifacts.model.minfo;
 
+import org.dive4elements.river.utils.DoubleUtil;
+
 import gnu.trove.TDoubleArrayList;
 
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
 
 public class BedDiameterResult
 extends BedQualityDiameterResult
@@ -17,6 +22,11 @@
     protected TDoubleArrayList diameterCap;
     protected TDoubleArrayList diameterSub;
 
+    protected PolynomialSplineFunction interpolSub;
+    protected PolynomialSplineFunction interpolCap;
+    protected boolean nonInterpolSub;
+    protected boolean nonInterpolCap;
+
     public BedDiameterResult (
         String type,
         TDoubleArrayList diameterCap,
@@ -26,6 +36,10 @@
         super(type, km);
         this.diameterCap = diameterCap;
         this.diameterSub = diameterSub;
+        interpolSub = null;
+        nonInterpolSub = false;
+        interpolCap = null;
+        nonInterpolCap = false;
     }
 
     public double getDiameterCap(int ndx) {
@@ -49,6 +63,25 @@
         return Double.NaN;
     }
 
+    public double getDiameterCapInterpol(double km) {
+        if (nonInterpolCap) {
+            return Double.NaN;
+        }
+        if (interpolCap == null) {
+            interpolCap = DoubleUtil.getLinearInterpolator(kms, diameterCap);
+            if (interpolCap == null) {
+                nonInterpolCap = true;
+                return Double.NaN;
+            }
+        }
+        try {
+            return interpolCap.value(km);
+        } catch (ArgumentOutsideDomainException e) {
+            /* This is expected for many results. */
+            return Double.NaN;
+        }
+    }
+
     public double getDiameterSub(double km) {
         if (kms.indexOf(km) >= 0) {
             return diameterSub.get(kms.indexOf(km));
@@ -56,6 +89,25 @@
         return Double.NaN;
     }
 
+    public double getDiameterSubInterpol(double km) {
+        if (nonInterpolSub) {
+            return Double.NaN;
+        }
+        if (interpolSub == null) {
+            interpolSub = DoubleUtil.getLinearInterpolator(kms, diameterSub);
+            if (interpolSub == null) {
+                nonInterpolSub = true;
+                return Double.NaN;
+            }
+        }
+        try {
+            return interpolSub.value(km);
+        } catch (ArgumentOutsideDomainException e) {
+            /* This is expected for many results. */
+            return Double.NaN;
+        }
+    }
+
     public double[][] getDiameterCapData() {
         return new double[][] {
             kms.toNativeArray(),
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedParametersResult.java	Mon Mar 16 14:37:30 2015 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedParametersResult.java	Mon Mar 16 15:36:38 2015 +0100
@@ -9,6 +9,9 @@
 package org.dive4elements.river.artifacts.model.minfo;
 
 import gnu.trove.TDoubleArrayList;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.dive4elements.river.utils.DoubleUtil;
+import org.apache.commons.math.ArgumentOutsideDomainException;
 
 import java.io.Serializable;
 
@@ -22,6 +25,16 @@
     protected TDoubleArrayList loadDensitySub;
     protected TDoubleArrayList kms;
 
+    protected PolynomialSplineFunction interpolPoroSub;
+    protected PolynomialSplineFunction interpolPoroCap;
+    protected PolynomialSplineFunction interpolDensSub;
+    protected PolynomialSplineFunction interpolDensCap;
+
+    protected boolean nonInterpolPoroSub;
+    protected boolean nonInterpolPoroCap;
+    protected boolean nonInterpolDensSub;
+    protected boolean nonInterpolDensCap;
+
     public BedParametersResult() {
 
     }
@@ -38,6 +51,16 @@
         this.porositySub = porositySub;
         this.loadDensityCap = densityCap;
         this.loadDensitySub = densitySub;
+
+        PolynomialSplineFunction interpolPoroSub = null;
+        PolynomialSplineFunction interpolPoroCap = null;
+        PolynomialSplineFunction interpolDensSub = null;
+        PolynomialSplineFunction interpolDensCap = null;
+
+        nonInterpolPoroSub = false;
+        nonInterpolPoroCap = false;
+        nonInterpolDensSub = false;
+        nonInterpolDensCap = false;
     }
 
     public double getPorosityCap(int ndx) {
@@ -111,4 +134,81 @@
             loadDensitySub.toNativeArray()
         };
     }
+
+    public double getPorositySubInterpol(double km) {
+        if (nonInterpolPoroSub) {
+            return Double.NaN;
+        }
+        if (interpolPoroSub == null) {
+            interpolPoroSub = DoubleUtil.getLinearInterpolator(kms, porositySub);
+            if (interpolPoroSub == null) {
+                nonInterpolPoroSub = true;
+                return Double.NaN;
+            }
+        }
+        try {
+            return interpolPoroSub.value(km);
+        } catch (ArgumentOutsideDomainException e) {
+            /* This is expected for many results. */
+            return Double.NaN;
+        }
+    }
+
+    public double getPorosityCapInterpol(double km) {
+        if (nonInterpolPoroCap) {
+            return Double.NaN;
+        }
+        if (interpolPoroCap == null) {
+            interpolPoroCap = DoubleUtil.getLinearInterpolator(kms, porosityCap);
+            if (interpolPoroCap == null) {
+                nonInterpolPoroCap = true;
+                return Double.NaN;
+            }
+        }
+        try {
+            return interpolPoroCap.value(km);
+        } catch (ArgumentOutsideDomainException e) {
+            /* This is expected for many results. */
+            return Double.NaN;
+        }
+    }
+
+    public double getDensitySubInterpol(double km) {
+        if (nonInterpolDensSub) {
+            return Double.NaN;
+        }
+        if (interpolDensSub == null) {
+            interpolDensSub = DoubleUtil.getLinearInterpolator(kms, loadDensitySub);
+            if (interpolDensSub == null) {
+                nonInterpolDensSub = true;
+                return Double.NaN;
+            }
+        }
+        try {
+            return interpolDensSub.value(km);
+        } catch (ArgumentOutsideDomainException e) {
+            /* This is expected for many results. */
+            return Double.NaN;
+        }
+    }
+
+    public double getDensityCapInterpol(double km) {
+        if (nonInterpolDensCap) {
+            return Double.NaN;
+        }
+        if (interpolDensCap == null) {
+            interpolDensCap = DoubleUtil.getLinearInterpolator(kms, loadDensityCap);
+            if (interpolDensCap == null) {
+                nonInterpolDensCap = true;
+                return Double.NaN;
+            }
+        }
+        try {
+            return interpolDensCap.value(km);
+        } catch (ArgumentOutsideDomainException e) {
+            /* This is expected for many results. */
+            return Double.NaN;
+        }
+    }
+
 }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadDiameterResult.java	Mon Mar 16 14:37:30 2015 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedloadDiameterResult.java	Mon Mar 16 15:36:38 2015 +0100
@@ -9,13 +9,22 @@
 package org.dive4elements.river.artifacts.model.minfo;
 
 import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.utils.DoubleUtil;
+
 import gnu.trove.TDoubleArrayList;
 
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
 
 public class BedloadDiameterResult
 extends BedQualityDiameterResult
 {
     protected TDoubleArrayList diameter;
+    protected PolynomialSplineFunction interpol;
+
+    /** Set to true if this result can't be interpolated.*/
+    protected boolean nonInterpolResult;
 
     public BedloadDiameterResult(
         String type,
@@ -25,6 +34,8 @@
     ) {
         super (type, km);
         this.diameter = diameter;
+        interpol = null;
+        nonInterpolResult = false;
     }
 
     public double getDiameter(int ndx) {
@@ -41,6 +52,25 @@
         return Double.NaN;
     }
 
+    public double getDiameterInterpol(double km) {
+        if (nonInterpolResult) {
+            return Double.NaN;
+        }
+        if (interpol == null) {
+            interpol = DoubleUtil.getLinearInterpolator(kms, diameter);
+            if (interpol == null) {
+                nonInterpolResult = true;
+                return Double.NaN;
+            }
+        }
+        try {
+            return interpol.value(km);
+        } catch (ArgumentOutsideDomainException e) {
+            /* This is expected for many results. */
+            return Double.NaN;
+        }
+    }
+
     public double[][] getDiameterData() {
         return new double[][] {
             kms.toNativeArray(),
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java	Mon Mar 16 14:37:30 2015 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java	Mon Mar 16 15:36:38 2015 +0100
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+/* Copyright (C) 2011, 2012, 2013, 2015 by Bundesanstalt für Gewässerkunde
  * Software engineering by Intevation GmbH
  *
  * This file is Free Software under the GNU AGPL (>=v3)
@@ -29,6 +29,9 @@
 import org.dive4elements.river.exports.AbstractExporter;
 import org.dive4elements.river.utils.Formatter;
 
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.D4EArtifact;
+
 
 public class BedQualityExporter
 extends AbstractExporter
@@ -59,43 +62,16 @@
         results = new BedQualityResult[0];
     }
 
-    /** Populate param kms with kms from param beds and loads, return it. */
-    private TDoubleArrayList populateKmList(BedDiameterResult[] beds,
-        BedloadDiameterResult[] loads,
-        TDoubleArrayList kms) {
-        for (int j = 0; j < beds.length; j++) {
-            TDoubleArrayList bkms = beds[j].getKms();
-            for (int k = 0, K = bkms.size(); k < K; k++) {
-                double km = bkms.get(k);
-                if (!kms.contains(km)) { // XXX: O(N^2)
-                    kms.add(km);
-                }
-            }
-        }
-        for (int j = 0; j < loads.length; j++) {
-            TDoubleArrayList lkms = loads[j].getKms();
-            for (int k = 0, L = lkms.size(); k < L; k++) {
-                double km = lkms.get(k);
-                if (!kms.contains(km)) { // XXX: O(N^2)
-                    kms.add(km);
-                }
-            }
-        }
-        return kms;
-    }
-
     /** Create double[] containing the data for rows in csv. */
     private List<double[]> createDataRows() {
-        // Calculate how many columns and rows we need.
-        TDoubleArrayList kms = new TDoubleArrayList();
+
+        double[] kms = new RangeAccess((D4EArtifact) master).getKmSteps();
 
         int cols = 1;
         for (BedQualityResult result : results) {
             BedDiameterResult[]     beds  = result.getBedResults();
             BedloadDiameterResult[] loads = result.getBedloadResults();
 
-            kms = populateKmList(beds, loads, kms);
-
             cols += beds.length * 2;
             if (beds.length > 0) {
                 cols += 4;
@@ -103,22 +79,21 @@
             cols += loads.length;
         }
 
-        kms.sort();
-
-        List<double[]> rows = new ArrayList<double[]>(kms.size());
-        for (int i = 0, K = kms.size(); i < K; i++) {
+        List<double[]> rows = new ArrayList<double[]>(kms.length);
+        for (double km: kms) {
             double[] row = new double[cols];
-            double km = kms.get(i);
             row[0] = km;
             for (int j = 0; j < results.length; j++) {
+
                 BedloadDiameterResult[] loads = results[j].getBedloadResults();
 
                 for(int k = 0; k < loads.length; k++) {
                     // k + 1: shift km column.
                     // j* loads.length: shift periods.
                     row[(k + 1) + (j * loads.length)] =
-                        loads[k].getDiameter(km);
+                        loads[k].getDiameterInterpol(km);
                 }
+
                 BedDiameterResult[] beds = results[j].getBedResults();
                 if (beds.length == 0) {
                     continue;
@@ -128,17 +103,18 @@
                     // j * beds.length * 2: shift periods.
                     // loads.length * results.length: shift bed load columns.
                     int ndx = (k * 2 + 1) + (j * beds.length * 2) + (loads.length * results.length);
-                    row[ndx] = beds[k].getDiameterCap(km);
-                    row[ndx + 1] = beds[k].getDiameterSub(km);
+                    row[ndx] = beds[k].getDiameterCapInterpol(km);
+                    row[ndx + 1] = beds[k].getDiameterSubInterpol(km);
                 }
+
                 BedParametersResult[] params = results[j].getParameters();
                 for(int k = 0; k < params.length; k++) {
                     // loads.length + (beds.lenght * 2) * (j + 1): shift bed and bedload columns.
                     int ndx = 1 + (loads.length + (beds.length * 2) * (j + 1));
-                    row[ndx] = params[k].getLoadDensityCap(km);
-                    row[ndx + 1] = params[k].getLoadDensitySub(km);
-                    row[ndx + 2] = params[k].getPorosityCap(km);
-                    row[ndx + 3] = params[k].getPorositySub(km);
+                    row[ndx] = params[k].getDensityCapInterpol(km);
+                    row[ndx + 1] = params[k].getDensitySubInterpol(km);
+                    row[ndx + 2] = params[k].getPorosityCapInterpol(km);
+                    row[ndx + 3] = params[k].getPorositySubInterpol(km);
                 }
             }
             rows.add(row);
@@ -149,7 +125,6 @@
 
     @Override
     protected void writeCSVData(CSVWriter writer) throws IOException {
-        // TODO Auto-generated method stub
         writeCSVHeader(writer);
 
         NumberFormat nf = Formatter.getFormatter(context, 1, 3);
@@ -177,7 +152,6 @@
 
     @Override
     protected void addData(Object data) {
-        // TODO Auto-generated method stub
         log.debug("addData()");
         if (!(data instanceof CalculationResult)) {
             log.warn("Invalid data type.");
--- a/artifacts/src/main/java/org/dive4elements/river/utils/DoubleUtil.java	Mon Mar 16 14:37:30 2015 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/DoubleUtil.java	Mon Mar 16 15:36:38 2015 +0100
@@ -10,6 +10,13 @@
 
 import org.dive4elements.river.artifacts.math.Linear;
 
+import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+
+import org.apache.commons.math.exception.DimensionMismatchException;
+import org.apache.commons.math.exception.NumberIsTooSmallException;
+import org.apache.commons.math.exception.NonMonotonousSequenceException;
+
 import gnu.trove.TDoubleArrayList;
 
 import java.util.Arrays;
@@ -305,5 +312,23 @@
             }
         }
     }
+
+    /** Convieniance function for results to get an interpolator.
+     * This is basically a static wrapper to for LinearInterpolator.interpolate
+     * with error handling. Returns null on error.*/
+    public static PolynomialSplineFunction getLinearInterpolator(TDoubleArrayList x, TDoubleArrayList y) {
+        LinearInterpolator lpol = new LinearInterpolator();
+        try {
+            return lpol.interpolate(x.toNativeArray(), y.toNativeArray());
+        } catch (DimensionMismatchException e) {
+            log.error("KMs and Result values have different sizes. Failed to interpolate: " +
+                    e.getMessage());
+        } catch (NonMonotonousSequenceException e) {
+            log.error("KMs are not monotonous. Failed to interpolate: " + e.getMessage());
+        } catch (NumberIsTooSmallException e) {
+            log.error("Result is to small. Failed to interpolate: " + e.getMessage());
+        }
+        return null;
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org