view flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/fixings/FixCalculation.java @ 3603:d2ae58f6730d

FixA: Use the column cache to establish a consistent index scheme. flys-artifacts/trunk@5229 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 22 Aug 2012 10:51:29 +0000
parents b5eec54bd4a2
children 2382b26079ac
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<QW []>  outliers;

        public FitResult() {
        }

        public FitResult(
            Parameters      parameters,
            KMIndex<QWD []> referenced,
            KMIndex<QW []>  outliers
        ) {
            this.parameters = parameters;
            this.referenced = referenced;
            this.outliers   = outliers;
        }

        public Parameters getParameters() {
            return parameters;
        }

        public KMIndex<QWD []> getReferenced() {
            return referenced;
        }

        public KMIndex<QW []> 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);
                    }
                }
                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<QW []>  outliers   = new KMIndex<QW []>();
        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