Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java @ 9215:0fc9c82e744e
work on collison, flood_duration
author | gernotbelger |
---|---|
date | Tue, 03 Jul 2018 17:00:48 +0200 |
parents | 3dae6b78e1da |
children | 0dcd1cd41915 |
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.flood_duration; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.math.DoubleRange; import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.artifacts.WINFOArtifact; import org.dive4elements.river.artifacts.access.ComputationRangeAccess; import org.dive4elements.river.artifacts.common.GeneralResultType; import org.dive4elements.river.artifacts.common.ResultRow; import org.dive4elements.river.artifacts.model.Calculation; import org.dive4elements.river.artifacts.model.Calculation.Problem; import org.dive4elements.river.artifacts.model.CalculationResult; import org.dive4elements.river.artifacts.model.WQKms; import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder; import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder; import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder; import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey; import org.dive4elements.river.exports.WaterlevelDescriptionBuilder; import org.dive4elements.river.model.Attribute.AttributeKey; import org.dive4elements.river.model.Gauge; import org.dive4elements.river.model.MainValueType.MainValueTypeKey; import org.dive4elements.river.model.sinfo.InfrastructureValue; import gnu.trove.TDoubleArrayList; /** * Calculation of the result rows of the flood duration of the infrastructures in a river km range * and selected main value durations * * @author Matthias Schäfer */ final class FloodDurationCalculator { private final Collection<ResultRow> rows = new ArrayList<>(); private final RiverInfoProvider riverInfoProvider; private final CallContext context; public FloodDurationCalculator(final CallContext context, final RiverInfoProvider riverInfoProvider) { this.context = context; this.riverInfoProvider = riverInfoProvider; } /** * Calculate the infrastructures flood duration result rows */ public FloodDurationCalculationResult execute(final Calculation problems, final String label, final DoubleRange calcRange, final RiversideChoiceKey riverside, final WINFOArtifact winfo) { // Find all gauges of the calc range, and create the duration finders final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>(); Gauge firstGauge = null; for (final Gauge gauge : this.riverInfoProvider.getRiver().determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) { durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems)); if (firstGauge == null) firstGauge = gauge; } // Find all infrastructures within the calc range final AttributeKey bankKey = riverside.getAttributeKey(); final List<InfrastructureValue> infras = InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble(), bankKey); // Merge all stations (range/step, borders of gauge ranges, infrastructures) final Map<Double, InfrastructureValue> allStations = new HashMap<>(); final Map<Double, InfrastructureValue> secondBank = new HashMap<>(); // any second infrastructure in case of both-banks-option // FIXME: check, do we really need all stations? compare with tkh... addRangeStations(allStations, winfo); addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble()); addInfrastructures(allStations, secondBank, infras); final double[] stationsSorted = sortStations(allStations.keySet()); // Calculate W and Q for all stations and the selected discharge states final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems); // Determine discharge state labels of the main values final String[] mainValueLabels = findMainValueLabels(wqkmsArray, winfo.getQs(), firstGauge, problems); // Create a finder for Q in the {river}.wst km-w-q table final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble(), problems); final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context); // Calculate the durations and create the result rows for (int i = 0; i <= stationsSorted.length - 1; i++) { final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true); final ResultRow row = createRow(descBuilder, stationsSorted[i], gauge, wqkmsArray, durFinders.get(gauge), i); if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null)) calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders); this.rows.add(row); if (secondBank.containsKey(stationsSorted[i])) { final ResultRow row2 = ResultRow.create(row); calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders); this.rows.add(row2); } } return new FloodDurationCalculationResult(label, mainValueLabels, this.rows); } /** * Adds to a stations map all stations corresponding to the active range and step */ private void addRangeStations(final Map<Double, InfrastructureValue> allStations, final WINFOArtifact winfo) { for (final double station : new ComputationRangeAccess(winfo).getKms()) allStations.put(Double.valueOf(station), null); } /** * Adds to a stations map all range limits of the gauges within the calc range */ private void addGaugeLimits(final Map<Double, InfrastructureValue> allStations, final Set<Gauge> gauges, final double fromKm, final double toKm) { for (final Gauge gauge : gauges) { final Double kmA = Double.valueOf(gauge.getRange().getA().doubleValue()); final Double kmB = Double.valueOf(gauge.getRange().getB().doubleValue()); if (kmA > fromKm - 0.0001) allStations.put(kmA, null); if (kmB < toKm + 0.0001) allStations.put(kmB, null); } } /** * Adds to a stations map all (first) infrastructures of a station, and the second, if any, to another map */ private void addInfrastructures(final Map<Double, InfrastructureValue> allStations, final Map<Double, InfrastructureValue> secondBank, final List<InfrastructureValue> infrastructures) { for (final InfrastructureValue infrastructure : infrastructures) { final Double station = infrastructure.getStation(); if (!allStations.containsKey(station) || !(allStations.get(station) instanceof InfrastructureValue)) allStations.put(station, infrastructure); else secondBank.put(station, infrastructure); } } /** * Returns a double array with a sorted stations set */ private double[] sortStations(final Set<Double> stations) { final TDoubleArrayList sorted = new TDoubleArrayList(); for (final Double station : stations) sorted.add(station.doubleValue()); sorted.sort(); return sorted.toNativeArray(); } /** * Calculates an array of w-q-longitudinal sections for all artifact W/Q options */ private WQKms[] calculateWaterlevels(final WINFOArtifact winfo, final double[] stations, final Calculation problems) { // REMARK aus TkhCalculation - move to WinfoArtifactWrapper? // TODO das ist ziemlich langsam - durch den WQBaseTableFinder ersetzen? (vorher W-Optionen in Q umrechnen) // (So funktioniert computeWaterlevelData wohl: // Es sucht die Spalte(n) zum Bezugspegel-Q in der W-Q-Tabelle ({river}.wst in Wst etc.) // und interpoliert für diese horizontale Tabellenposition jeweils die vertikale Tabellenposition der station; // das ergibt das W einer station für einen Abflusszustand; // bei Vorgabe eines Pegel-W wird vorher anhand der W-Q-Tabelle des Pegels ({gauge}.at in DischargeTable) das Q // interpoliert; // bei Vorgabe eines W auf freier Strecke wird wohl vorher noch die .wst-Interpolation eingesetzt. final CalculationResult waterlevelData = winfo.computeWaterlevelData(stations); /* copy all problems */ final Calculation winfoProblems = waterlevelData.getReport(); final List<Problem> problems2 = winfoProblems.getProblems(); if (problems2 != null) { for (final Problem problem : problems2) { problems.addProblem(problem); } } return (WQKms[]) waterlevelData.getData(); } /** * Determines the discharge state labels for the selected Q or W values */ // FIXME: use WaterlevelDescriptionBuilder instead! private String[] findMainValueLabels(final WQKms[] wqkmsArray, final double[] qs, final Gauge gauge, final Calculation problems) { final String[] mainValueLabels = new String[wqkmsArray.length]; if (wqkmsArray.length >= 1) { // FIXME // WaterlevelDescriptionBuilder builder = new WaterlevelDescriptionBuilder(artifact, context); // Labels like Q=123 or W=123 for (int i = 0; i <= wqkmsArray.length - 1; i++) { // FIXME // String label = builder.getDesc(wqkmsArray[i]); mainValueLabels[i] = wqkmsArray[i].getName(); } // Replace labels for named main Q values final GaugeMainValueFinder zoneFinder = GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, gauge, problems); if ((zoneFinder != null) && (qs != null)) { for (int i = 0; i <= qs.length - 1; i++) mainValueLabels[i] = zoneFinder.findExactZoneName(qs[i], mainValueLabels[i]); } } return mainValueLabels; } /** * Create a result row for a station and its gauge, and add w-q-values as selected * * @param descBuilder */ private ResultRow createRow(final WaterlevelDescriptionBuilder descBuilder, final Double station, final Gauge gauge, final WQKms[] wqkmsArray, final GaugeDurationValuesFinder durationFinder, final int kmIndex) { final ResultRow row = ResultRow.create(); row.putValue(GeneralResultType.station, station); row.putValue(SInfoResultType.infrastructuretype, null); // is replaced later for an infrastructure row.putValue(SInfoResultType.floodDuration, Double.NaN); // is replaced later for an infrastructure // row.putValue(SInfoResultType.gaugeLabel, gauge.getName()); final String gaugeLabel = this.riverInfoProvider.findGauge(station); row.putValue(SInfoResultType.gaugeLabel, gaugeLabel); final String location = this.riverInfoProvider.getLocation(station); row.putValue(SInfoResultType.location, location); final List<DurationWaterlevel> waterlevels = new ArrayList<>(wqkmsArray.length); for (final WQKms wqKm : wqkmsArray) { assert (wqKm.getKm(kmIndex) == station.doubleValue()); final int overflowDays = (int) Math.round(underflowDaysToOverflowDays(durationFinder.getDuration(wqKm.getQ(kmIndex)))); final String waterlevelLabel = descBuilder.getDesc(wqKm); final DurationWaterlevel dw = new DurationWaterlevel(wqKm.getW(kmIndex), overflowDays, wqKm.getQ(kmIndex), waterlevelLabel); waterlevels.add(dw); } row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels); return row; } /** * Calculate the result row fields for one infrastructure */ private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure, final WQBaseTableFinder wqFinder, final Map<Gauge, GaugeDurationValuesFinder> durFinders) { final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight()); final double qOut = Double.isInfinite(q) ? Double.NaN : q; final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q)); row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n row.putValue(SInfoResultType.floodDuration, dur); row.putValue(SInfoResultType.floodDischarge, qOut); row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); } /** * Translates underflow duration into overflow duration */ private double underflowDaysToOverflowDays(final double underflowDays) { return 365 - underflowDays; } }