view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculation.java @ 8951:322b0e6298ea

Work on SINFO FlowDepth-Development
author gernotbelger
date Fri, 16 Mar 2018 18:08:38 +0100
parents
children c40db8e8dcae
line wrap: on
line source
/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
 * Software engineering by
 *  Björnsen Beratende Ingenieure GmbH
 *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
 *
 * 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.sinfo.flowdepthdev;

import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.lang.math.DoubleRange;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.model.WKms;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
import org.dive4elements.river.artifacts.sinfo.common.SInfoResultRow;
import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthUtils;
import org.dive4elements.river.artifacts.sinfo.flowdepth.WstSoundingIdPair;
import org.dive4elements.river.artifacts.sinfo.tkhcalculation.WaterlevelValuesFinder;
import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder;
import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo;
import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
import org.dive4elements.river.artifacts.states.WaterlevelData;
import org.dive4elements.river.artifacts.states.WaterlevelFetcher;
import org.dive4elements.river.model.River;

/**
 * @author Gernot Belger
 */
final class FlowDepthDevelopmentCalculation {

    private final CallContext context;

    public FlowDepthDevelopmentCalculation(final CallContext context) {
        this.context = context;
    }

    public CalculationResult calculate(final SINFOArtifact sinfo) {

        final String user = CalculationUtils.findArtifactUser(this.context, sinfo);

        /* access input data */
        final FlowDepthDevelopmentAccess access = new FlowDepthDevelopmentAccess(sinfo);
        final River river = access.getRiver();
        final RiverInfo riverInfo = new RiverInfo(river);

        final WstSoundingIdPair currentPair = access.getCurrentPair();
        final WstSoundingIdPair histPair = access.getHistoricalPair();

        final DoubleRange calcRange = access.getRange();

        /* calculate results for each diff pair */
        final Calculation problems = new Calculation();

        final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);

        final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());

        final FlowDepthDevelopmentCalculationResults results = new FlowDepthDevelopmentCalculationResults(calcModeLabel, user, riverInfo, calcRange);

        final FlowDepthDevelopmentCalculationResult result = calculateResult(calcRange, currentPair, histPair, problems, infoProvider);
        if (result != null)
            results.addResult(result);

        return new CalculationResult(results, problems);
    }

    private FlowDepthDevelopmentCalculationResult calculateResult(final DoubleRange calcRange, final WstSoundingIdPair currentPair,
            final WstSoundingIdPair histPair, final Calculation problems, final RiverInfoProvider infoProvider) {

        /* access real input data from database */
        final WaterlevelData currentWaterlevel = loadWaterlevel(currentPair, problems);
        if (currentWaterlevel == null)
            return null;

        final WaterlevelData historicalWaterlevel = loadWaterlevel(histPair, problems);
        if (historicalWaterlevel == null)
            return null;

        final BedHeightsFinder currentSounding = loadBedHeight(currentPair, calcRange, problems);
        if (currentSounding == null)
            return null;

        final BedHeightsFinder historicalSounding = loadBedHeight(histPair, calcRange, problems);
        if (historicalSounding == null)
            return null;

        // FIXME: check current/hist wst have same discharge...

        final BedHeightInfo currentSoundingInfo = currentSounding.getInfo();
        final BedHeightInfo historicalSoundingInfo = historicalSounding.getInfo();

        // final String label = createLabel(waterlevel, minBedHeight, maxBedHeight);

        // final WKms wstKms = waterlevel.getWkms();
        final int currentWstYear = currentWaterlevel.getYear();
        final int historicalWstYear = historicalWaterlevel.getYear();
        final int currentSoundingYear = currentSoundingInfo.getYear();
        final int historicalSoundingYear = historicalSoundingInfo.getYear();

        // FIXME: distinguish error messages
        FlowDepthUtils.checkYearDifference("", currentWstYear, currentSoundingYear, problems);
        FlowDepthUtils.checkYearDifference("", historicalWstYear, historicalSoundingYear, problems);

        // checkWaterlevelDiscretisation(wstKms, calcRange, problems);
        // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden

        /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */
        final RiverInfoProvider currentRiverInfoProvider = infoProvider.forWaterlevel(currentWaterlevel);
        final RiverInfoProvider histRiverInfoProvider = infoProvider.forWaterlevel(historicalWaterlevel);

        final WstInfo currentWstInfo = new WstInfo(currentWaterlevel.getName(), currentWstYear, currentRiverInfoProvider.getReferenceGauge());
        final WstInfo historicalWstInfo = new WstInfo(historicalWaterlevel.getName(), historicalWstYear, histRiverInfoProvider.getReferenceGauge());

        final WKms currentWkms = currentWaterlevel.getWkms();
        final WaterlevelValuesFinder currentWstProvider = WaterlevelValuesFinder.fromKms(currentWkms);
        // final DischargeValuesFinder currentDischargeProvider = DischargeValuesFinder.fromKms(currentWkms);

        final WKms historicalWkms = historicalWaterlevel.getWkms();
        final WaterlevelValuesFinder historicalWstProvider = WaterlevelValuesFinder.fromKms(historicalWkms);
        // final DischargeValuesFinder historicalDischargeProvider = DischargeValuesFinder.fromKms(historicalWkms);

        final int currentMeanYear = (currentWstYear + currentSoundingYear) / 2;
        final int historcialMeanYear = (historicalWstYear + historicalSoundingYear) / 2;

        // final String waterlevelLabel = waterlevel.getName();
        // final String soundingLabel = buildSoundingLabel(minBedHeight, maxBedHeight);

        final double diffYear = currentMeanYear - historcialMeanYear;

        /* real calculation loop */
        final Collection<SInfoResultRow> rows = new ArrayList<>();

        // FIXME: determine what is the spatial discretisation that we will use...
        final double[] allKms = currentWkms.allKms().toNativeArray();
        for (final double station : allKms) {
            if (calcRange.containsDouble(station)) {

                final double currentWst = currentWstProvider.getWaterlevel(station);
                // final double currentDischarge = currentDischargeProvider.getDischarge(station);
                final double currentBedHeight = currentSounding.getMeanBedHeight(station);

                final double historicalWst = historicalWstProvider.getWaterlevel(station);
                // final double historicalDischarge = historicalDischargeProvider.getDischarge(station);
                final double historicalBedHeight = historicalSounding.getMeanBedHeight(station);

                final double diffWst = (currentWst - historicalWst) * 100;
                final double diffBedHeight = (currentBedHeight - historicalBedHeight) * 100;

                final double flowDepthDevelopment = diffWst - diffBedHeight;

                final double flowDepthDevelopmentPerYear = flowDepthDevelopment / diffYear;

                final double currentFlowDepth = currentWst - currentBedHeight;
                final double historicalFlowDepth = historicalWst - historicalBedHeight;

                // REMARK: access the location once only during calculation
                final String location = currentRiverInfoProvider.getLocation(station);

                final SInfoResultRow row = SInfoResultRow.create().//
                        putValue(SInfoResultType.station, station). //
                        putValue(SInfoResultType.flowdepthDevelopment, flowDepthDevelopment). //
                        putValue(SInfoResultType.flowdepthDevelopmentPerYear, flowDepthDevelopmentPerYear). //
                        putValue(SInfoResultType.waterlevelDifference, diffWst). //
                        putValue(SInfoResultType.bedHeightDifference, diffBedHeight). //
                        putValue(SInfoResultType.flowdepthCurrent, currentFlowDepth). //
                        putValue(SInfoResultType.flowdepthHistorical, historicalFlowDepth). //
                        putValue(SInfoResultType.location, location);
                rows.add(row);
            }
        }

        return new FlowDepthDevelopmentCalculationResult("", currentWstInfo, historicalWstInfo, currentSoundingInfo, historicalSoundingInfo,
                rows);
    }

    /* REMARK: fetch ALL wst kms, because we need to determine the original reference gauge */
    private WaterlevelData loadWaterlevel(final WstSoundingIdPair pair, final Calculation problems) {
        final String wstId = pair.getWstId();
        return new WaterlevelFetcher().findWaterlevel(this.context, wstId, Double.NaN, Double.NaN, problems);
    }

    private BedHeightsFinder loadBedHeight(final WstSoundingIdPair pair, final DoubleRange calcRange, final Calculation problems) {
        final String soundingId = pair.getSoundingId();
        return BedHeightsFinder.forId(this.context, soundingId, calcRange, problems);
    }
}

http://dive4elements.wald.intevation.org