diff flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java @ 5831:bd047b71ab37

Repaired internal references
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 12:06:39 +0200
parents flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixCalculation.java@6e6753b9757f
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java	Thu Apr 25 12:06:39 2013 +0200
@@ -0,0 +1,425 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import org.dive4elements.artifacts.common.utils.StringUtils;
+
+import org.dive4elements.river.artifacts.access.FixAccess;
+
+import org.dive4elements.river.artifacts.math.fitting.Function;
+import org.dive4elements.river.artifacts.math.fitting.FunctionFactory;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.FixingsColumn;
+import org.dive4elements.river.artifacts.model.FixingsColumnFactory;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing.Filter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview.Fixing;
+import org.dive4elements.river.artifacts.model.FixingsOverview.IdsFilter;
+
+import org.dive4elements.river.artifacts.model.FixingsOverview;
+import org.dive4elements.river.artifacts.model.FixingsOverviewFactory;
+import org.dive4elements.river.artifacts.model.Parameters;
+
+import org.dive4elements.river.utils.DoubleUtil;
+import org.dive4elements.river.utils.KMIndex;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+/** Calculation base class for fix. */
+public abstract class FixCalculation
+extends               Calculation
+{
+    private static Logger log = Logger.getLogger(FixCalculation.class);
+
+    public static final double EPSILON = 1e-4;
+
+    public static final String [] STANDARD_COLUMNS = {
+        "km", "chi_sqr", "max_q", "std-dev"
+    };
+
+    protected static class FitResult {
+
+        protected Parameters      parameters;
+        protected KMIndex<QWD []> referenced;
+        protected KMIndex<QWI []> outliers;
+
+        public FitResult() {
+        }
+
+        public FitResult(
+            Parameters      parameters,
+            KMIndex<QWD []> referenced,
+            KMIndex<QWI []> outliers
+        ) {
+            this.parameters = parameters;
+            this.referenced = referenced;
+            this.outliers   = outliers;
+        }
+
+        public Parameters getParameters() {
+            return parameters;
+        }
+
+        public KMIndex<QWD []> getReferenced() {
+            return referenced;
+        }
+
+        public KMIndex<QWI []> getOutliers() {
+            return outliers;
+        }
+    } // class FitResult
+
+    /** Helper class to bundle the meta information of a column
+     *  and the real data.
+     */
+    protected static class Column {
+
+        protected Fixing.Column meta;
+        protected FixingsColumn data;
+        protected int           index;
+
+        public Column() {
+        }
+
+        public Column(Fixing.Column meta, FixingsColumn data, int index) {
+            this.meta  = meta;
+            this.data  = data;
+            this.index = index;
+        }
+
+        public Date getDate() {
+            return meta.getStartTime();
+        }
+
+        public String getDescription() {
+            return meta.getDescription();
+        }
+
+        public int getIndex() {
+            return index;
+        }
+
+        public boolean getQW(
+            double    km,
+            double [] qs,
+            double [] ws,
+            int       index
+        ) {
+            qs[index] = data.getQ(km);
+            return data.getW(km, ws, index);
+        }
+
+        public boolean getQW(double km, double [] wq) {
+            data.getW(km, wq, 0);
+            if (Double.isNaN(wq[0])) return false;
+            wq[1] = data.getQ(km);
+            return !Double.isNaN(wq[1]);
+        }
+    } // class Column
+
+    /**
+     * Helper class to find the data belonging to meta info more quickly.
+     */
+    protected static class ColumnCache {
+
+        protected Map<Integer, Column> columns;
+
+        public ColumnCache() {
+            columns = new HashMap<Integer, Column>();
+        }
+
+        public Column getColumn(Fixing.Column meta) {
+            Integer key = meta.getId();
+            Column column = columns.get(key);
+            if (column == null) {
+                FixingsColumn data = FixingsColumnFactory
+                    .getInstance()
+                    .getColumnData(meta);
+                if (data != null) {
+                    column = new Column(meta, data, columns.size());
+                    columns.put(key, column);
+                }
+            }
+            return column;
+        }
+    } // class ColumnCache
+
+
+    protected String  river;
+    protected double  from;
+    protected double  to;
+    protected double  step;
+    protected boolean preprocessing;
+    protected String  function;
+    protected int []  events;
+    protected int     qSectorStart;
+    protected int     qSectorEnd;
+
+    public FixCalculation() {
+    }
+
+    public FixCalculation(FixAccess access) {
+        String  river         = access.getRiver();
+        Double  from          = access.getFrom();
+        Double  to            = access.getTo();
+        Double  step          = access.getStep();
+        String  function      = access.getFunction();
+        int []  events        = access.getEvents();
+        Integer qSectorStart  = access.getQSectorStart();
+        Integer qSectorEnd    = access.getQSectorEnd();
+        Boolean preprocessing = access.getPreprocessing();
+
+        if (river == null) {
+            addProblem("fix.missing.river");
+        }
+
+        if (from == null) {
+            addProblem("fix.missing.from");
+        }
+
+        if (to == null) {
+            addProblem("fix.missing.to");
+        }
+
+        if (step == null) {
+            addProblem("fix.missing.step");
+        }
+
+        if (function == null) {
+            addProblem("fix.missing.function");
+        }
+
+        if (events == null || events.length < 1) {
+            addProblem("fix.missing.events");
+        }
+
+        if (qSectorStart == null) {
+            addProblem("fix.missing.qstart.sector");
+        }
+
+        if (qSectorEnd == null) {
+            addProblem("fix.missing.qend.sector");
+        }
+
+        if (preprocessing == null) {
+            addProblem("fix.missing.preprocessing");
+        }
+
+        if (!hasProblems()) {
+            this.river         = river;
+            this.from          = from;
+            this.to            = to;
+            this.step          = step;
+            this.function      = function;
+            this.events        = events;
+            this.qSectorStart  = qSectorStart;
+            this.qSectorEnd    = qSectorEnd;
+            this.preprocessing = preprocessing;
+        }
+    }
+
+    protected static String toString(
+        String [] parameterNames,
+        double [] values
+    ) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < parameterNames.length; ++i) {
+            if (i > 0) sb.append(", ");
+            sb.append(parameterNames[i]).append(": ").append(values[i]);
+        }
+        return sb.toString();
+    }
+
+
+    /** Create filter to accept only the chosen events. 
+     *  This factored out out to be overwritten. 
+     */
+    protected Filter createFilter() {
+        return new IdsFilter(events);
+    }
+
+    protected List<Column> getEventColumns(
+        FixingsOverview overview,
+        ColumnCache     cc
+    ) {
+        FixingsColumnFactory fcf = FixingsColumnFactory.getInstance();
+
+        Filter filter = createFilter();
+
+        List<Fixing.Column> metas = overview.filter(null, filter);
+
+        List<Column> columns = new ArrayList<Column>(metas.size());
+
+        for (Fixing.Column meta: metas) {
+
+            Column data = cc.getColumn(meta);
+            if (data == null) {
+                addProblem("fix.cannot.load.data");
+            }
+            else {
+                columns.add(data);
+            }
+        }
+
+        return columns;
+    }
+
+    // Fit a function to the given points from fixation.
+    protected FitResult doFitting(
+        FixingsOverview overview,
+        ColumnCache     cc,
+        Function        func
+    ) {
+        boolean debug = log.isDebugEnabled();
+
+        final List<Column> eventColumns = getEventColumns(overview, cc);
+
+        if (eventColumns.size() < 2) {
+            addProblem("fix.too.less.data.columns");
+            return null;
+        }
+
+        final double  [] qs = new double[eventColumns.size()];
+        final double  [] ws = new double[qs.length];
+        final boolean [] interpolated = new boolean[ws.length];
+
+        Fitting.QWDFactory qwdFactory = new Fitting.QWDFactory() {
+            @Override
+            public QWD create(double q, double w) {
+                // Check all the event columns for close match
+                // and take the description and the date from meta.
+                for (int i = 0; i < qs.length; ++i) {
+                    if (Math.abs(qs[i]-q) < EPSILON
+                    &&  Math.abs(ws[i]-w) < EPSILON) {
+                        Column column = eventColumns.get(i);
+                        return new QWD(
+                            qs[i], ws[i],
+                            column.getDescription(),
+                            column.getDate(),
+                            interpolated[i],
+                            0d,
+                            column.getIndex());
+                    }
+                }
+                log.warn("cannot find column for (" + q + ", " + w + ")");
+                return new QWD(q, w);
+            }
+        };
+
+        Fitting fitting = new Fitting(func, qwdFactory, preprocessing);
+
+        String [] parameterNames = func.getParameterNames();
+
+        Parameters results =
+            new Parameters(
+                StringUtils.join(STANDARD_COLUMNS, parameterNames));
+
+        boolean invalid = false;
+
+        double [] kms = DoubleUtil.explode(from, to, step / 1000.0);
+
+        if (debug) {
+            log.debug("number of kms: " + kms.length);
+        }
+
+        KMIndex<QWI []> outliers   = new KMIndex<QWI []>();
+        KMIndex<QWD []> referenced = new KMIndex<QWD []>(kms.length);
+
+        int kmIndex             = results.columnIndex("km");
+        int chiSqrIndex         = results.columnIndex("chi_sqr");
+        int maxQIndex           = results.columnIndex("max_q");
+        int stdDevIndex         = results.columnIndex("std-dev");
+        int [] parameterIndices = results.columnIndices(parameterNames);
+
+        int numFailed = 0;
+
+        for (int i = 0; i < kms.length; ++i) {
+            double km = kms[i];
+
+            // Fill Qs and Ws from event columns.
+            for (int j = 0; j < ws.length; ++j) {
+                interpolated[j] = !eventColumns.get(j).getQW(km, qs, ws, j);
+            }
+
+            fitting.reset();
+
+            if (!fitting.fit(qs, ws)) {
+                ++numFailed;
+                addProblem(km, "fix.fitting.failed");
+                continue;
+            }
+
+            referenced.add(km, fitting.referencedToArray());
+
+            if (fitting.hasOutliers()) {
+                outliers.add(km, fitting.outliersToArray());
+            }
+
+            int row = results.newRow();
+            double [] values = fitting.getParameters();
+
+            results.set(row, kmIndex, km);
+            results.set(row, chiSqrIndex, fitting.getChiSquare());
+            results.set(row, stdDevIndex, fitting.getStandardDeviation());
+            results.set(row, maxQIndex,   fitting.getMaxQ());
+            invalid |= results.set(row, parameterIndices, values);
+
+            if (debug) {
+                log.debug("km: "+km+" " + toString(parameterNames, values));
+            }
+        }
+
+        if (debug) {
+            log.debug("success: " + (kms.length - numFailed));
+            log.debug("failed: " + numFailed);
+        }
+
+        if (invalid) {
+            addProblem("fix.invalid.values");
+            results.removeNaNs();
+        }
+
+        outliers.sort();
+        referenced.sort();
+
+        return new FitResult(
+            results,
+            referenced,
+            outliers);
+    }
+
+    public CalculationResult calculate() {
+        FixingsOverview overview =
+            FixingsOverviewFactory.getOverview(river);
+
+        if (overview == null) {
+            addProblem("fix.no.overview.available");
+        }
+
+        Function func = FunctionFactory.getInstance()
+            .getFunction(function);
+
+        if (func == null) {
+            addProblem("fix.invalid.function.name");
+        }
+
+        if (hasProblems()) {
+            return new CalculationResult(this);
+        }
+
+        return innerCalculate(overview, func);
+    }
+
+    protected abstract CalculationResult innerCalculate(
+        FixingsOverview overview,
+        Function        function
+    );
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org