view artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java @ 9415:9744ce3c3853

Rework of fixanalysis computation and dWt and WQ facets. Got rid of strange remapping and bitshifting code by explicitely saving the column information and using it in the facets. The facets also put the valid station range into their xml-metadata
author gernotbelger
date Thu, 16 Aug 2018 16:27:53 +0200
parents ddcd52d239cd
children 2b83d3a96703
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.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
    public CalculationResult innerCalculate(final FixingsOverview overview, final Function func) {
        final ColumnCache cc = new ColumnCache();

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

        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(func, 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