view artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java @ 9479:2b83d3a96703

i18n TODO "benutzerdefiniert" = "custom" fixed
author gernotbelger
date Mon, 10 Sep 2018 15:31:55 +0200
parents 9744ce3c3853
children
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.artifacts.model.fixings;

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

import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
import org.apache.log4j.Logger;
import org.dive4elements.artifacts.CallMeta;
import org.dive4elements.river.artifacts.access.FixAnalysisAccess;
import org.dive4elements.river.artifacts.math.fitting.Function;
import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.model.DateRange;
import org.dive4elements.river.artifacts.model.Parameters;
import org.dive4elements.river.artifacts.model.Range;
import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.AndFilter;
import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.DateRangeFilter;
import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.IdsFilter;
import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.KmFilter;
import org.dive4elements.river.artifacts.model.fixings.FixingsOverview.SectorFilter;
import org.dive4elements.river.utils.DateAverager;
import org.dive4elements.river.utils.KMIndex;

import gnu.trove.TIntIntHashMap;

public class FixAnalysisCalculation extends FixCalculation {

    private static final long serialVersionUID = 1L;

    private static Logger log = Logger.getLogger(FixAnalysisCalculation.class);

    private DateRange referencePeriod;

    private DateRange[] analysisPeriods;

    public FixAnalysisCalculation() {
    }

    public FixAnalysisCalculation(final FixAnalysisAccess access) {

        super(access);

        final DateRange referencePeriod = access.getReferencePeriod();
        final DateRange[] analysisPeriods = access.getAnalysisPeriods();

        if (referencePeriod == null) {
            addProblem("fix.missing.reference.period");
        }

        if (analysisPeriods == null || analysisPeriods.length < 1) {
            addProblem("fix.missing.analysis.periods");
        }

        if (!hasProblems()) {
            this.referencePeriod = referencePeriod;
            this.analysisPeriods = analysisPeriods;
        }
    }

    @Override
    protected CalculationResult innerCalculate(final FixingsOverview overview, final Function function, final CallMeta meta) {
        final ColumnCache cc = new ColumnCache();

        final FitResult fitResult = doFitting(overview, cc, function);

        if (fitResult == null)
            return new CalculationResult(this);

        final Parameters parameters = fitResult.getParameters();

        final AnalysisPeriodEventResults eventResults = new AnalysisPeriodEventResults();
        final KMIndex<AnalysisPeriod[]> kmResults = new KMIndex<>(parameters.size());

        calculateAnalysisPeriods(function, parameters, overview, cc, eventResults, kmResults);
        eventResults.sortAll();
        kmResults.sort();

        final FixAnalysisResult far = new FixAnalysisResult(parameters, fitResult.getResultColumns(), kmResults, eventResults);
        return new CalculationResult(far, this);
    }

    @Override
    protected FixingColumnFilter createFilter() {
        final FixingColumnFilter ids = super.createFilter();
        final DateRangeFilter rdf = new DateRangeFilter(this.referencePeriod.getFrom(), this.referencePeriod.getTo());
        return new AndFilter().add(rdf).add(ids);
    }

    private void calculateAnalysisPeriods(final Function function, final Parameters parameters, final FixingsOverview overview, final ColumnCache cc,
            final AnalysisPeriodEventResults eventResults, final KMIndex<AnalysisPeriod[]> kmResults) {

        final Range range = new Range(this.from, this.to);

        final int kmIndex = parameters.columnIndex("km");
        final int maxQIndex = parameters.columnIndex("max_q");

        final double[] ws = new double[1];

        final int[] parameterIndices = parameters.columnIndices(function.getParameterNames());

        final double[] parameterValues = new double[parameterIndices.length];

        final DateAverager dateAverager = new DateAverager();

        final IdsFilter idsFilter = new IdsFilter(this.events);

        final TIntIntHashMap[] col2indices = new TIntIntHashMap[this.analysisPeriods.length];

        final DateRangeFilter[] drfs = new DateRangeFilter[this.analysisPeriods.length];

        final boolean debug = log.isDebugEnabled();

        for (int i = 0; i < this.analysisPeriods.length; ++i) {
            col2indices[i] = new TIntIntHashMap();
            drfs[i] = new DateRangeFilter(this.analysisPeriods[i].getFrom(), this.analysisPeriods[i].getTo());

            if (debug) {
                log.debug("Analysis period " + (i + 1) + " date range: " + this.analysisPeriods[i].getFrom() + " - " + this.analysisPeriods[i].getTo());
            }
        }

        for (int row = 0, R = parameters.size(); row < R; ++row) {
            final double km = parameters.get(row, kmIndex);
            parameters.get(row, parameterIndices, parameterValues);

            // This is the parameterized function for a given km.
            final org.dive4elements.river.artifacts.math.Function instance = function.instantiate(parameterValues);

            final KmFilter kmFilter = new KmFilter(km);

            final List<AnalysisPeriod> periodResults = new ArrayList<>(this.analysisPeriods.length);

            for (int ap = 0; ap < this.analysisPeriods.length; ++ap) {
                final DateRange analysisPeriod = this.analysisPeriods[ap];

                final DateRangeFilter drf = drfs[ap];

                final QWD[] qSectorAverages = new QWD[4];
                final double[] qSectorStdDevs = new double[4];

                final ArrayList<QWD> allQWDs = new ArrayList<>();

                // for all Q sectors.
                for (int qSector = this.qSectorStart; qSector <= this.qSectorEnd; ++qSector) {
                    final FixingColumnFilter filter = new AndFilter().add(kmFilter).add(new SectorFilter(qSector)).add(drf).add(idsFilter);

                    final List<FixingColumn> metas = overview.filter(range, filter);

                    if (metas.isEmpty()) {
                        // No fixings for km and analysis period
                        continue;
                    }

                    double sumQ = 0.0;
                    double sumW = 0.0;

                    final StandardDeviation stdDev = new StandardDeviation();

                    final List<QWD> qwds = new ArrayList<>(metas.size());

                    dateAverager.clear();

                    for (final FixingColumn meta : metas) {
                        if (meta.findQSector(km) != qSector) {
                            // Ignore not matching sectors.
                            continue;
                        }

                        final FixingColumnWithData column = cc.getColumn(meta);
                        if (column == null)
                            continue;

                        final double q = column.getQ(km);
                        final boolean interpolated = !column.getW(km, ws, 0);
                        final double w = ws[0];

                        if (Double.isNaN(w) || Double.isNaN(q))
                            continue;

                        final double fw = instance.value(q);
                        if (Double.isNaN(fw))
                            continue;

                        final double dw = (w - fw) * 100.0;

                        stdDev.increment(dw);

                        final Date date = column.getDate();

                        final QWD qwd = new QWD(q, w, date, interpolated, dw, false);
                        qwds.add(qwd);
                        eventResults.addQWD(ap, km, column, qwd);

                        sumW += w;
                        sumQ += q;

                        dateAverager.add(date);
                    }

                    // Calulate average per Q sector.
                    final int N = qwds.size();
                    if (N > 0) {
                        allQWDs.addAll(qwds);
                        final double avgW = sumW / N;
                        final double avgQ = sumQ / N;

                        final double avgFw = instance.value(avgQ);
                        if (!Double.isNaN(avgFw)) {
                            final double avgDw = (avgW - avgFw) * 100.0;
                            final Date avgDate = dateAverager.getAverage();

                            final QWD avgQWD = new QWD(avgQ, avgW, avgDate, true, avgDw, false);

                            qSectorAverages[qSector] = avgQWD;
                        }
                        qSectorStdDevs[qSector] = stdDev.getResult();
                    } else {
                        qSectorStdDevs[qSector] = Double.NaN;
                    }
                } // for all Q sectors

                /* calculate max q for this period */
                double maxQ = -Double.MAX_VALUE;
                for (final QWD qwd : allQWDs) {
                    if (qwd.getQ() > maxQ)
                        maxQ = qwd.getQ();
                }
                for (final QWD qwd : qSectorAverages) {
                    if (qwd != null && qwd.getQ() > maxQ)
                        maxQ = qwd.getQ();
                }

                final AnalysisPeriod periodResult = new AnalysisPeriod(analysisPeriod, qSectorAverages, qSectorStdDevs, maxQ);
                periodResults.add(periodResult);
            }

            double maxQ = -Double.MAX_VALUE;
            for (final AnalysisPeriod ap : periodResults) {
                final double q = ap.getMaxQ();
                if (q > maxQ) {
                    maxQ = q;
                }
            }

            final double oldMaxQ = parameters.get(row, maxQIndex);
            if (oldMaxQ < maxQ) {
                parameters.set(row, maxQIndex, maxQ);
            }

            final AnalysisPeriod[] rap = new AnalysisPeriod[periodResults.size()];
            periodResults.toArray(rap);
            kmResults.add(km, rap);
        }
    }

}

http://dive4elements.wald.intevation.org