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

Added Fix calculation. flys-artifacts/trunk@4462 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 22 May 2012 17:09:27 +0000
parents
children c1f2e792704a
line wrap: on
line source
package de.intevation.flys.artifacts.model.fixings;

import de.intevation.flys.artifacts.FixationArtifactAccess;

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;
import de.intevation.flys.artifacts.model.FixingsOverviewFactory;
import de.intevation.flys.artifacts.model.Parameters;

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

import de.intevation.flys.utils.DoubleUtil;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.math.MathException;

import org.apache.commons.math.optimization.fitting.CurveFitter;

import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;

import org.apache.log4j.Logger;

public class FixCalculation
extends      Calculation
{
    private static Logger logger = Logger.getLogger(FixCalculation.class);

    protected String  river;
    protected double  from;
    protected double  to;
    protected double  step;
    protected boolean preprocessing;
    protected String  function;
    protected int []  events;

    public FixCalculation() {
    }

    public FixCalculation(FixationArtifactAccess 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();

        if (river == null) {
            // TODO: i18n
            addProblem("fix.missing.river");
        }

        if (from == null) {
            // TODO: i18n
            addProblem("fix.missing.from");
        }

        if (to == null) {
            // TODO: i18n
            addProblem("fix.missing.to");
        }

        if (step == null) {
            // TODO: i18n
            addProblem("fix.missing.step");
        }

        if (function == null) {
            // TODO: i18n
            addProblem("fix.missing.function");
        }

        if (events == null || events.length < 1) {
            // TODO: i18n
            addProblem("fix.missing.events");
        }

        if (!hasProblems()) {
            this.river    = river;
            this.from     = from;
            this.to       = to;
            this.step     = step;
            this.function = function;
            this.events   = events;
        }
    }

    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) {
            // TODO: i18n
            addProblem("fix.invalid.function.name");
        }

        if (hasProblems()) {
            return new CalculationResult(this);
        }

        FixingsColumnFactory fcf = FixingsColumnFactory.getInstance();

        List<FixingsColumn> dataColumns =
            new ArrayList<FixingsColumn>(events.length);

        for (int eventId: events) {
            IdFilter idFilter = new IdFilter(eventId);

            List<Fixing.Column> columns = overview.filter(null, idFilter);
            if (columns.isEmpty()) {
                // TODO: i18n
                addProblem("fix.missing.column", eventId);
                continue;
            }
            FixingsColumn dataColumn = fcf.getColumnData(columns.get(0));
            if (dataColumn == null) {
                // TODO: i18n
                addProblem("fix.cannot.load.data", eventId);
                continue;
            }
            dataColumns.add(dataColumn);
        }

        if (dataColumns.size() < 2) {
            // TODO: i18n
            addProblem("fix.too.less.data.columns");
            return new CalculationResult(this);
        }

        double [] kms = DoubleUtil.explode(from, to, step);

        double [] ws = new double[dataColumns.size()];
        double [] qs = new double[ws.length];

        String [] parameterNames = func.getParameterNames();

        Parameters results =
            new Parameters(createColumnNames(parameterNames));

        boolean invalid = false;

        for (int i = 0; i < kms.length; ++i) {
            double km = kms[i];

            for (int j = 0; j < ws.length; ++j) {
                FixingsColumn column = dataColumns.get(j);
                qs[j] = column.getQ(km);
                boolean interpolated = column.getW(km, ws, j);
                // TODO: mark as interpolated.
            }

            // TODO: Do preprocessing here!
            double [] parameters = fit(func, km, ws, qs);
            if (parameters == null) { // Problems are reported already.
                continue;
            }

            int row = results.newRow();

            results.set(row, "km", km);
            for (int j = 0; j < parameters.length; ++j) {
                if (Double.isNaN(parameters[j])) {
                    invalid = true;
                }
                else {
                    results.set(row, parameterNames[j], parameters[j]);
                }
            }
            // TODO: Calculate statistics, too!
        }

        if (invalid) {
            // TODO: i18n
            addProblem("fix.invalid.values");
            results.removeNaNs();
        }

        // TODO: Calculate Delta W/t, too.
        return new CalculationResult(results, this);
    }

    protected static String [] createColumnNames(String [] parameters) {
        String [] result = new String[parameters.length + 1];
        result[0] = "km";
        // TODO: Add statistic columns, too.
        System.arraycopy(parameters, 0, result, 1, parameters.length);
        return result;
    }

    protected double [] fit(
        Function  function,
        double    km,
        double [] ws, 
        double [] qs
    ) {
        LevenbergMarquardtOptimizer lmo = new LevenbergMarquardtOptimizer();
        CurveFitter cf = new CurveFitter(lmo);

        boolean missingWs = false;
        boolean missingQs = false;

        for (int i = 0; i < ws.length; ++i) {
            boolean ignore = false;
            if (Double.isNaN(ws[i])) {
                ignore = true;
                if (!missingWs) {
                    missingWs = true;
                    // TODO: i18n
                    addProblem(km, "fix.missing.w");
                }
            }
            if (Double.isNaN(qs[i])) {
                ignore = true;
                if (!missingQs) {
                    missingQs = true;
                    // TODO: i18n
                    addProblem(km, "fix.missing.q");
                }
            }
            if (!ignore) {
                cf.addObservedPoint(ws[i], qs[i]);
            }
        }

        try {
            return cf.fit(function, function.getInitialGuess());
        }
        catch (MathException me) {
            addProblem(km, "fix.fitting.failed");
        }

        return null;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org