Mercurial > dive4elements > river
diff artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java @ 9202:b4402594213b
More work on calculations and output for S-Info flood duration workflow (chart types 1 and 2)
author | mschaefer |
---|---|
date | Mon, 02 Jul 2018 07:33:53 +0200 |
parents | 1614cb14308f |
children | 3dae6b78e1da |
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java Sun Jul 01 15:29:40 2018 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java Mon Jul 02 07:33:53 2018 +0200 @@ -12,23 +12,33 @@ 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.artifacts.sinfo.util.RiverInfo; 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 @@ -50,60 +60,207 @@ } /** - * Calculate the result rows - * - * @return a result collection with one result + * Calculate the infrastructures flood duration result rows */ - public FloodDurationCalculationResults execute(final Calculation problems, final String label, final String calcModeLabel, - final DoubleRange calcRange, final RiversideChoiceKey riverside, final String user) { + public FloodDurationCalculationResult execute(final Calculation problems, final String label, final DoubleRange calcRange, + final RiversideChoiceKey riverside, final WINFOArtifact winfo) { - final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), - calcRange.getMaximumDouble(), problems); + // 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)); - } - final AttributeKey bankKey = riverside.getAttributeKey(); - for (final InfrastructureValue infrastructure : InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), - calcRange.getMaximumDouble(), bankKey)) { - calculateResultRow(infrastructure, wqFinder, durFinders); + if (firstGauge == null) + firstGauge = gauge; } - final FloodDurationCalculationResult result = new FloodDurationCalculationResult(label, this.rows); + // 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); - final RiverInfo riverInfo = new RiverInfo(this.riverInfoProvider.getRiver()); + // 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 + addRangeStations(allStations, winfo); + addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble()); + addInfrastructures(allStations, secondBank, infras); + final double[] stationsSorted = sortStations(allStations.keySet()); - final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange); - results.addResult(result, problems); - return results; + // 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); + + // 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(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); } /** - * Calculate the result row for one infrastructure + * Adds to a stations map all stations corresponding to the active range and step */ - private void calculateResultRow(final InfrastructureValue infrastructure, final WQBaseTableFinder wqFinder, - final Map<Gauge, GaugeDurationValuesFinder> durFinders) { + 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 + */ + 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) { + // Labels like Q=123 or W=123 + for (int i = 0; i <= wqkmsArray.length - 1; 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 + */ + private ResultRow createRow(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 location = this.riverInfoProvider.getLocation(station); + row.putValue(SInfoResultType.location, location); - final Gauge gauge = this.riverInfoProvider.getGauge(infrastructure.getStation(), true); + if (wqkmsArray.length >= 1) { + assert (wqkmsArray[0].getKm(kmIndex) == station.doubleValue()); + row.putValue(SInfoResultType.waterlevel1, wqkmsArray[0].getW(kmIndex)); + row.putValue(SInfoResultType.discharge1, wqkmsArray[0].getQ(kmIndex)); + row.putValue(SInfoResultType.mainValue1Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[0].getQ(kmIndex)))); + if (wqkmsArray.length >= 2) { + assert (wqkmsArray[1].getKm(kmIndex) == station.doubleValue()); + row.putValue(SInfoResultType.waterlevel2, wqkmsArray[1].getW(kmIndex)); + row.putValue(SInfoResultType.discharge2, wqkmsArray[1].getQ(kmIndex)); + row.putValue(SInfoResultType.mainValue2Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[1].getQ(kmIndex)))); + if (wqkmsArray.length >= 3) { + assert (wqkmsArray[2].getKm(kmIndex) == station.doubleValue()); + row.putValue(SInfoResultType.waterlevel3, wqkmsArray[2].getW(kmIndex)); + row.putValue(SInfoResultType.discharge3, wqkmsArray[2].getQ(kmIndex)); + row.putValue(SInfoResultType.mainValue3Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[2].getQ(kmIndex)))); + } + } + } + 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; - // REMARK: access the location once only during calculation - final String location = this.riverInfoProvider.getLocation(infrastructure.getStation()); - row.putValue(GeneralResultType.station, infrastructure.getStation()); + final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q)); row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n - row.putValue(SInfoResultType.floodDuration, 365 - durFinders.get(gauge).getDuration(q)); + row.putValue(SInfoResultType.floodDuration, dur); row.putValue(SInfoResultType.floodDischarge, qOut); row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); - - // TODO Berechne W, Überflutungsdauer, Q und Bezeichnung von bis zu drei WSPL - // row.putValue(SInfoResultType.customMultiRowColWaterlevel, rowWsps); + } - row.putValue(SInfoResultType.gaugeLabel, gauge.getName()); - row.putValue(SInfoResultType.location, location); - - this.rows.add(row); + /** + * Translates underflow duration into overflow duration + */ + private double underflowDaysToOverflowDays(final double underflowDays) { + return 365 - underflowDays; } } \ No newline at end of file