comparison 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
comparison
equal deleted inserted replaced
9201:491e1a434457 9202:b4402594213b
10 package org.dive4elements.river.artifacts.sinfo.flood_duration; 10 package org.dive4elements.river.artifacts.sinfo.flood_duration;
11 11
12 import java.util.ArrayList; 12 import java.util.ArrayList;
13 import java.util.Collection; 13 import java.util.Collection;
14 import java.util.HashMap; 14 import java.util.HashMap;
15 import java.util.List;
15 import java.util.Map; 16 import java.util.Map;
17 import java.util.Set;
16 18
17 import org.apache.commons.lang.math.DoubleRange; 19 import org.apache.commons.lang.math.DoubleRange;
18 import org.dive4elements.artifacts.CallContext; 20 import org.dive4elements.artifacts.CallContext;
21 import org.dive4elements.river.artifacts.WINFOArtifact;
22 import org.dive4elements.river.artifacts.access.ComputationRangeAccess;
19 import org.dive4elements.river.artifacts.common.GeneralResultType; 23 import org.dive4elements.river.artifacts.common.GeneralResultType;
20 import org.dive4elements.river.artifacts.common.ResultRow; 24 import org.dive4elements.river.artifacts.common.ResultRow;
21 import org.dive4elements.river.artifacts.model.Calculation; 25 import org.dive4elements.river.artifacts.model.Calculation;
26 import org.dive4elements.river.artifacts.model.Calculation.Problem;
27 import org.dive4elements.river.artifacts.model.CalculationResult;
28 import org.dive4elements.river.artifacts.model.WQKms;
22 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder; 29 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder;
30 import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder;
23 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; 31 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
24 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; 32 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
25 import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder; 33 import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder;
26 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey; 34 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey;
27 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
28 import org.dive4elements.river.model.Attribute.AttributeKey; 35 import org.dive4elements.river.model.Attribute.AttributeKey;
29 import org.dive4elements.river.model.Gauge; 36 import org.dive4elements.river.model.Gauge;
37 import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
30 import org.dive4elements.river.model.sinfo.InfrastructureValue; 38 import org.dive4elements.river.model.sinfo.InfrastructureValue;
39
40 import gnu.trove.TDoubleArrayList;
31 41
32 /** 42 /**
33 * Calculation of the result rows of the flood duration of the infrastructures in a river km range 43 * Calculation of the result rows of the flood duration of the infrastructures in a river km range
34 * and selected main value durations 44 * and selected main value durations
35 * 45 *
48 this.context = context; 58 this.context = context;
49 this.riverInfoProvider = riverInfoProvider; 59 this.riverInfoProvider = riverInfoProvider;
50 } 60 }
51 61
52 /** 62 /**
53 * Calculate the result rows 63 * Calculate the infrastructures flood duration result rows
54 * 64 */
55 * @return a result collection with one result 65 public FloodDurationCalculationResult execute(final Calculation problems, final String label, final DoubleRange calcRange,
56 */ 66 final RiversideChoiceKey riverside, final WINFOArtifact winfo) {
57 public FloodDurationCalculationResults execute(final Calculation problems, final String label, final String calcModeLabel, 67
58 final DoubleRange calcRange, final RiversideChoiceKey riverside, final String user) { 68 // Find all gauges of the calc range, and create the duration finders
59 69 final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>();
70 Gauge firstGauge = null;
71 for (final Gauge gauge : this.riverInfoProvider.getRiver().determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) {
72 durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems));
73 if (firstGauge == null)
74 firstGauge = gauge;
75 }
76
77 // Find all infrastructures within the calc range
78 final AttributeKey bankKey = riverside.getAttributeKey();
79 final List<InfrastructureValue> infras = InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
80 calcRange.getMaximumDouble(), bankKey);
81
82 // Merge all stations (range/step, borders of gauge ranges, infrastructures)
83 final Map<Double, InfrastructureValue> allStations = new HashMap<>();
84 final Map<Double, InfrastructureValue> secondBank = new HashMap<>(); // any second infrastructure in case of both-banks-option
85 addRangeStations(allStations, winfo);
86 addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble());
87 addInfrastructures(allStations, secondBank, infras);
88 final double[] stationsSorted = sortStations(allStations.keySet());
89
90 // Calculate W and Q for all stations and the selected discharge states
91 final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems);
92
93 // Determine discharge state labels of the main values
94 final String[] mainValueLabels = findMainValueLabels(wqkmsArray, winfo.getQs(), firstGauge, problems);
95
96 // Create a finder for Q in the {river}.wst km-w-q table
60 final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), 97 final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
61 calcRange.getMaximumDouble(), problems); 98 calcRange.getMaximumDouble(), problems);
62 final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>(); 99
63 for (final Gauge gauge : this.riverInfoProvider.getRiver().determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) { 100 // Calculate the durations and create the result rows
64 durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems)); 101 for (int i = 0; i <= stationsSorted.length - 1; i++) {
65 } 102 final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true);
66 final AttributeKey bankKey = riverside.getAttributeKey(); 103 final ResultRow row = createRow(stationsSorted[i], gauge, wqkmsArray, durFinders.get(gauge), i);
67 for (final InfrastructureValue infrastructure : InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), 104 if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
68 calcRange.getMaximumDouble(), bankKey)) { 105 calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders);
69 calculateResultRow(infrastructure, wqFinder, durFinders); 106 this.rows.add(row);
70 } 107 if (secondBank.containsKey(stationsSorted[i])) {
71 108 final ResultRow row2 = ResultRow.create(row);
72 final FloodDurationCalculationResult result = new FloodDurationCalculationResult(label, this.rows); 109 calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders);
73 110 this.rows.add(row2);
74 final RiverInfo riverInfo = new RiverInfo(this.riverInfoProvider.getRiver()); 111 }
75 112 }
76 final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange); 113
77 results.addResult(result, problems); 114 return new FloodDurationCalculationResult(label, mainValueLabels, this.rows);
78 return results; 115 }
79 } 116
80 117 /**
81 /** 118 * Adds to a stations map all stations corresponding to the active range and step
82 * Calculate the result row for one infrastructure 119 */
83 */ 120 private void addRangeStations(final Map<Double, InfrastructureValue> allStations, final WINFOArtifact winfo) {
84 private void calculateResultRow(final InfrastructureValue infrastructure, final WQBaseTableFinder wqFinder, 121 for (final double station : new ComputationRangeAccess(winfo).getKms())
85 final Map<Gauge, GaugeDurationValuesFinder> durFinders) { 122 allStations.put(Double.valueOf(station), null);
123 }
124
125 /**
126 * Adds to a stations map all range limits of the gauges within the calc range
127 */
128 private void addGaugeLimits(final Map<Double, InfrastructureValue> allStations, final Set<Gauge> gauges, final double fromKm, final double toKm) {
129 for (final Gauge gauge : gauges) {
130 final Double kmA = Double.valueOf(gauge.getRange().getA().doubleValue());
131 final Double kmB = Double.valueOf(gauge.getRange().getB().doubleValue());
132 if (kmA > fromKm - 0.0001)
133 allStations.put(kmA, null);
134 if (kmB < toKm + 0.0001)
135 allStations.put(kmB, null);
136 }
137 }
138
139 /**
140 * Adds to a stations map all (first) infrastructures of a station, and the second, if any, to another map
141 */
142 private void addInfrastructures(final Map<Double, InfrastructureValue> allStations, final Map<Double, InfrastructureValue> secondBank,
143 final List<InfrastructureValue> infrastructures) {
144 for (final InfrastructureValue infrastructure : infrastructures) {
145 final Double station = infrastructure.getStation();
146 if (!allStations.containsKey(station) || !(allStations.get(station) instanceof InfrastructureValue))
147 allStations.put(station, infrastructure);
148 else
149 secondBank.put(station, infrastructure);
150 }
151 }
152
153 /**
154 * Returns a double array with a sorted stations set
155 */
156 private double[] sortStations(final Set<Double> stations) {
157 final TDoubleArrayList sorted = new TDoubleArrayList();
158 for (final Double station : stations)
159 sorted.add(station.doubleValue());
160 sorted.sort();
161 return sorted.toNativeArray();
162 }
163
164 /**
165 * Calculates an array of w-q-longitudinal sections for all artifact W/Q options
166 */
167 private WQKms[] calculateWaterlevels(final WINFOArtifact winfo, final double[] stations, final Calculation problems) {
168 // REMARK aus TkhCalculation - move to WinfoArtifactWrapper?
169 // TODO das ist ziemlich langsam - durch den WQBaseTableFinder ersetzen? (vorher W-Optionen in Q umrechnen)
170 // (So funktioniert computeWaterlevelData wohl:
171 // Es sucht die Spalte(n) zum Bezugspegel-Q in der W-Q-Tabelle ({river}.wst in Wst etc.)
172 // und interpoliert für diese horizontale Tabellenposition jeweils die vertikale Tabellenposition der station;
173 // das ergibt das W einer station für einen Abflusszustand;
174 // bei Vorgabe eines Pegel-W wird vorher anhand der W-Q-Tabelle des Pegels ({gauge}.at in DischargeTable) das Q
175 // interpoliert;
176 // bei Vorgabe eines W auf freier Strecke wird wohl vorher noch die .wst-Interpolation eingesetzt.
177 final CalculationResult waterlevelData = winfo.computeWaterlevelData(stations);
178
179 /* copy all problems */
180 final Calculation winfoProblems = waterlevelData.getReport();
181 final List<Problem> problems2 = winfoProblems.getProblems();
182 if (problems2 != null) {
183 for (final Problem problem : problems2) {
184 problems.addProblem(problem);
185 }
186 }
187 return (WQKms[]) waterlevelData.getData();
188 }
189
190 /**
191 * Determines the discharge state labels for the selected Q or W values
192 */
193 private String[] findMainValueLabels(final WQKms[] wqkmsArray, final double[] qs, final Gauge gauge, final Calculation problems) {
194 final String[] mainValueLabels = new String[wqkmsArray.length];
195 if (wqkmsArray.length >= 1) {
196 // Labels like Q=123 or W=123
197 for (int i = 0; i <= wqkmsArray.length - 1; i++)
198 mainValueLabels[i] = wqkmsArray[i].getName();
199 // Replace labels for named main Q values
200 final GaugeMainValueFinder zoneFinder = GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, gauge, problems);
201 if ((zoneFinder != null) && (qs != null)) {
202 for (int i = 0; i <= qs.length - 1; i++)
203 mainValueLabels[i] = zoneFinder.findExactZoneName(qs[i], mainValueLabels[i]);
204 }
205 }
206 return mainValueLabels;
207 }
208
209 /**
210 * Create a result row for a station and its gauge, and add w-q-values as selected
211 */
212 private ResultRow createRow(final Double station, final Gauge gauge, final WQKms[] wqkmsArray,
213 final GaugeDurationValuesFinder durationFinder, final int kmIndex) {
86 214
87 final ResultRow row = ResultRow.create(); 215 final ResultRow row = ResultRow.create();
88 216 row.putValue(GeneralResultType.station, station);
89 final Gauge gauge = this.riverInfoProvider.getGauge(infrastructure.getStation(), true); 217 row.putValue(SInfoResultType.infrastructuretype, null); // is replaced later for an infrastructure
218 row.putValue(SInfoResultType.floodDuration, Double.NaN); // is replaced later for an infrastructure
219 row.putValue(SInfoResultType.gaugeLabel, gauge.getName());
220 final String location = this.riverInfoProvider.getLocation(station);
221 row.putValue(SInfoResultType.location, location);
222
223 if (wqkmsArray.length >= 1) {
224 assert (wqkmsArray[0].getKm(kmIndex) == station.doubleValue());
225 row.putValue(SInfoResultType.waterlevel1, wqkmsArray[0].getW(kmIndex));
226 row.putValue(SInfoResultType.discharge1, wqkmsArray[0].getQ(kmIndex));
227 row.putValue(SInfoResultType.mainValue1Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[0].getQ(kmIndex))));
228 if (wqkmsArray.length >= 2) {
229 assert (wqkmsArray[1].getKm(kmIndex) == station.doubleValue());
230 row.putValue(SInfoResultType.waterlevel2, wqkmsArray[1].getW(kmIndex));
231 row.putValue(SInfoResultType.discharge2, wqkmsArray[1].getQ(kmIndex));
232 row.putValue(SInfoResultType.mainValue2Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[1].getQ(kmIndex))));
233 if (wqkmsArray.length >= 3) {
234 assert (wqkmsArray[2].getKm(kmIndex) == station.doubleValue());
235 row.putValue(SInfoResultType.waterlevel3, wqkmsArray[2].getW(kmIndex));
236 row.putValue(SInfoResultType.discharge3, wqkmsArray[2].getQ(kmIndex));
237 row.putValue(SInfoResultType.mainValue3Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[2].getQ(kmIndex))));
238 }
239 }
240 }
241 return row;
242 }
243
244 /**
245 * Calculate the result row fields for one infrastructure
246 */
247 private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure,
248 final WQBaseTableFinder wqFinder, final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
249
90 final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight()); 250 final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight());
91 final double qOut = Double.isInfinite(q) ? Double.NaN : q; 251 final double qOut = Double.isInfinite(q) ? Double.NaN : q;
92 // REMARK: access the location once only during calculation 252 final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q));
93 final String location = this.riverInfoProvider.getLocation(infrastructure.getStation());
94 row.putValue(GeneralResultType.station, infrastructure.getStation());
95 row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n 253 row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n
96 row.putValue(SInfoResultType.floodDuration, 365 - durFinders.get(gauge).getDuration(q)); 254 row.putValue(SInfoResultType.floodDuration, dur);
97 row.putValue(SInfoResultType.floodDischarge, qOut); 255 row.putValue(SInfoResultType.floodDischarge, qOut);
98 row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); 256 row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
99 row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); 257 row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName());
100 258 }
101 // TODO Berechne W, Überflutungsdauer, Q und Bezeichnung von bis zu drei WSPL 259
102 // row.putValue(SInfoResultType.customMultiRowColWaterlevel, rowWsps); 260 /**
103 261 * Translates underflow duration into overflow duration
104 row.putValue(SInfoResultType.gaugeLabel, gauge.getName()); 262 */
105 row.putValue(SInfoResultType.location, location); 263 private double underflowDaysToOverflowDays(final double underflowDays) {
106 264 return 365 - underflowDays;
107 this.rows.add(row);
108 } 265 }
109 } 266 }

http://dive4elements.wald.intevation.org