view flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixCalculation.java @ 4187:21f4e4b79121

Refactor GaugeDischargeCurveFacet to be able to set a facet name For adding another output of the GaugeDischargeCurveArtifact it is necessary to provide to facet instances with different names. Therefore the GaugeDischargeCurveFacet is extended to set the facet name in the constructor.
author Björn Ricks <bjoern.ricks@intevation.de>
date Fri, 19 Oct 2012 13:25:49 +0200
parents 3cda41b5eb23
children 345f3bba6f15
line wrap: on
line source
package de.intevation.flys.artifacts.model.fixings;

import de.intevation.artifacts.common.utils.StringUtils;

import de.intevation.flys.artifacts.access.FixAccess;

import de.intevation.flys.artifacts.math.fitting.Function;
import de.intevation.flys.artifacts.math.fitting.FunctionFactory;

import de.intevation.flys.artifacts.model.Calculation;
import de.intevation.flys.artifacts.model.CalculationResult;
import de.intevation.flys.artifacts.model.FixingsColumn;
import de.intevation.flys.artifacts.model.FixingsColumnFactory;

import de.intevation.flys.artifacts.model.FixingsOverview.Fixing.Filter;

import de.intevation.flys.artifacts.model.FixingsOverview.Fixing;
import de.intevation.flys.artifacts.model.FixingsOverview.IdsFilter;

import de.intevation.flys.artifacts.model.FixingsOverview;
import de.intevation.flys.artifacts.model.FixingsOverviewFactory;
import de.intevation.flys.artifacts.model.Parameters;

import de.intevation.flys.utils.DoubleUtil;
import de.intevation.flys.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;

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();
    }

    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;
    }

    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