Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java @ 8915:d9dbf0b74bc2
Refaktoring of flow depth calculation, extracting tkh part. First implementation of tkh calculation.
author | gernotbelger |
---|---|
date | Wed, 28 Feb 2018 17:27:15 +0100 |
parents | e3519c3e7a0a |
children | 5d5d482da3e9 |
comparison
equal
deleted
inserted
replaced
8914:e3519c3e7a0a | 8915:d9dbf0b74bc2 |
---|---|
7 * and comes with ABSOLUTELY NO WARRANTY! Check out the | 7 * and comes with ABSOLUTELY NO WARRANTY! Check out the |
8 * documentation coming with Dive4Elements River for details. | 8 * documentation coming with Dive4Elements River for details. |
9 */ | 9 */ |
10 package org.dive4elements.river.artifacts.sinfo.flowdepth; | 10 package org.dive4elements.river.artifacts.sinfo.flowdepth; |
11 | 11 |
12 import java.util.ArrayList; | |
13 import java.util.Calendar; | |
14 import java.util.Collection; | 12 import java.util.Collection; |
15 import java.util.Collections; | |
16 import java.util.Date; | |
17 import java.util.List; | |
18 | 13 |
19 import org.apache.commons.lang.math.DoubleRange; | 14 import org.apache.commons.lang.math.DoubleRange; |
20 import org.apache.commons.math.FunctionEvaluationException; | |
21 import org.apache.commons.math.analysis.UnivariateRealFunction; | |
22 import org.dive4elements.artifacts.ArtifactDatabase; | |
23 import org.dive4elements.artifacts.CallContext; | 15 import org.dive4elements.artifacts.CallContext; |
24 import org.dive4elements.river.artifacts.BedHeightsArtifact; | 16 import org.dive4elements.river.artifacts.BedHeightsArtifact; |
25 import org.dive4elements.river.artifacts.model.Calculation; | 17 import org.dive4elements.river.artifacts.model.Calculation; |
26 import org.dive4elements.river.artifacts.model.CalculationResult; | 18 import org.dive4elements.river.artifacts.model.CalculationResult; |
27 import org.dive4elements.river.artifacts.model.DateRange; | |
28 import org.dive4elements.river.artifacts.model.LocationProvider; | |
29 import org.dive4elements.river.artifacts.model.QKms; | |
30 import org.dive4elements.river.artifacts.model.WKms; | 19 import org.dive4elements.river.artifacts.model.WKms; |
31 import org.dive4elements.river.artifacts.resources.Resources; | 20 import org.dive4elements.river.artifacts.resources.Resources; |
32 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; | 21 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; |
22 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; | |
33 import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthAccess.DifferencesPair; | 23 import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthAccess.DifferencesPair; |
34 import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo; | 24 import org.dive4elements.river.artifacts.sinfo.tkhcalculation.DischargeValuesFinder; |
25 import org.dive4elements.river.artifacts.sinfo.tkhcalculation.TkhCalculator; | |
26 import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder; | |
27 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; | |
35 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; | 28 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; |
36 import org.dive4elements.river.artifacts.sinfo.util.WstInfo; | 29 import org.dive4elements.river.artifacts.sinfo.util.WstInfo; |
37 import org.dive4elements.river.artifacts.states.WaterlevelData; | 30 import org.dive4elements.river.artifacts.states.WaterlevelData; |
38 import org.dive4elements.river.artifacts.states.WaterlevelFetcher; | 31 import org.dive4elements.river.artifacts.states.WaterlevelFetcher; |
39 import org.dive4elements.river.model.BedHeight; | |
40 import org.dive4elements.river.model.BedHeightValue; | |
41 import org.dive4elements.river.model.Gauge; | |
42 import org.dive4elements.river.model.River; | 32 import org.dive4elements.river.model.River; |
43 import org.dive4elements.river.utils.DoubleUtil; | |
44 import org.dive4elements.river.utils.GaugeIndex; | |
45 import org.dive4elements.river.utils.RiverUtils; | 33 import org.dive4elements.river.utils.RiverUtils; |
46 | 34 |
47 class FlowDepthCalculation { | 35 class FlowDepthCalculation { |
48 | 36 |
49 // private static Logger log = Logger.getLogger(FlowDepthCalculation.class); | 37 // private static Logger log = Logger.getLogger(FlowDepthCalculation.class); |
50 | |
51 private static final int VALID_BED_MEASUREMENT_YEARS = 20; | |
52 | |
53 private static final String CSV_NOT_IN_GAUGE_RANGE = "export.waterlevel.csv.not.in.gauge.range"; | |
54 | 38 |
55 private final CallContext context; | 39 private final CallContext context; |
56 | 40 |
57 public FlowDepthCalculation(final CallContext context) { | 41 public FlowDepthCalculation(final CallContext context) { |
58 this.context = context; | 42 this.context = context; |
59 } | 43 } |
60 | 44 |
61 public CalculationResult calculate(final SINFOArtifact sinfo) { | 45 public CalculationResult calculate(final SINFOArtifact sinfo) { |
62 | 46 |
63 /* | 47 final String user = CalculationUtils.findArtifactUser(this.context, sinfo); |
64 * find the user of this artifact, sadly this is not part of the calling context, so instead we determine the | |
65 * owner oft the artifact | |
66 */ | |
67 final ArtifactDatabase database = this.context.getDatabase(); | |
68 final String user = database.findArtifactUser(sinfo.identifier()); | |
69 | 48 |
70 /* access input data */ | 49 /* access input data */ |
71 final FlowDepthAccess access = new FlowDepthAccess(sinfo); | 50 final FlowDepthAccess access = new FlowDepthAccess(sinfo); |
72 final River river = access.getRiver(); | 51 final River river = access.getRiver(); |
73 final RiverInfo riverInfo = new RiverInfo(river); | 52 final RiverInfo riverInfo = new RiverInfo(river); |
74 | 53 |
75 final Collection<DifferencesPair> diffPairs = access.getDifferencePairs(); | 54 final Collection<DifferencesPair> diffPairs = access.getDifferencePairs(); |
76 | 55 |
77 final double from = access.getFrom(); | 56 final DoubleRange calcRange = access.getRange(); |
78 final double to = access.getTo(); | |
79 final DoubleRange calcRange = new DoubleRange(from, to); | |
80 | 57 |
81 final boolean useTkh = access.isUseTransportBodies(); | 58 final boolean useTkh = access.isUseTransportBodies(); |
82 | 59 |
83 /* calculate results for each diff pair */ | 60 /* calculate results for each diff pair */ |
84 final Calculation problems = new Calculation(); | 61 final Calculation problems = new Calculation(); |
85 | 62 |
86 final List<Gauge> gauges = river.determineGauges(from, to); | 63 final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange); |
87 final GaugeIndex gaugeIndex = new GaugeIndex(gauges); | |
88 | 64 |
89 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); | 65 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); |
90 | 66 |
91 final FlowDepthCalculationResults results = new FlowDepthCalculationResults(calcModeLabel, user, riverInfo, calcRange, useTkh); | 67 final FlowDepthCalculationResults results = new FlowDepthCalculationResults(calcModeLabel, user, riverInfo, calcRange, useTkh); |
92 | 68 |
93 for (final DifferencesPair diffPair : diffPairs) { | 69 for (final DifferencesPair diffPair : diffPairs) { |
94 final FlowDepthCalculationResult result = calculateResult(river, calcRange, diffPair, problems, gaugeIndex, useTkh); | 70 final FlowDepthCalculationResult result = calculateResult(calcRange, diffPair, problems, infoProvider, useTkh); |
95 if (result != null) | 71 if (result != null) |
96 results.addResult(result); | 72 results.addResult(result); |
97 } | 73 } |
98 | 74 |
99 return new CalculationResult(results, problems); | 75 return new CalculationResult(results, problems); |
100 } | 76 } |
101 | 77 |
102 /** | 78 /** |
103 * Calculates one W-MSH differences pair. | 79 * Calculates one W-MSH differences pair. |
80 * | |
81 * @param infoProvider | |
104 */ | 82 */ |
105 private FlowDepthCalculationResult calculateResult(final River river, final DoubleRange calcRange, final DifferencesPair diffPair, | 83 private FlowDepthCalculationResult calculateResult(final DoubleRange calcRange, final DifferencesPair diffPair, |
106 final Calculation problems, final GaugeIndex gaugeIndex, final boolean useTkh) { | 84 final Calculation problems, final RiverInfoProvider infoProvider, final boolean useTkh) { |
107 | 85 |
108 /* access real input data from database */ | 86 /* access real input data from database */ |
109 final String soundingId = diffPair.getSoundingId(); | 87 final String soundingId = diffPair.getSoundingId(); |
110 final String wstId = diffPair.getWstId(); | 88 final String wstId = diffPair.getWstId(); |
111 | 89 |
112 final BedHeight bedHeight = loadBedHeight(soundingId); | 90 final BedHeightsFinder bedHeight = loadBedHeight(soundingId, calcRange); |
113 if (bedHeight == null) { | 91 if (bedHeight == null) { |
114 final String message = Resources.format(this.context.getMeta(), "Failed to access sounding with id '{0}'", soundingId); | 92 final String message = Resources.format(this.context.getMeta(), "Failed to access sounding with id '{0}'", soundingId); |
115 problems.addProblem(message); | 93 problems.addProblem(message); |
116 return null; | 94 return null; |
117 } | 95 } |
124 return null; | 102 return null; |
125 } | 103 } |
126 final WKms wstKms = waterlevel.getWkms(); | 104 final WKms wstKms = waterlevel.getWkms(); |
127 | 105 |
128 final String wspLabel = wstKms.getName(); | 106 final String wspLabel = wstKms.getName(); |
129 final String soundingLabel = bedHeight.getDescription(); | 107 final String soundingLabel = bedHeight.getInfo().getDescription(); |
130 final String label = String.format("%s - %s", wspLabel, soundingLabel); | 108 final String label = String.format("%s - %s", wspLabel, soundingLabel); |
131 | 109 |
132 checkYearDifference(label, waterlevel, bedHeight, problems); | 110 checkYearDifference(label, waterlevel, bedHeight.getInfo().getYear(), problems); |
133 checkWaterlevelDiscretisation(wstKms, calcRange, problems); | 111 checkWaterlevelDiscretisation(wstKms, calcRange, problems); |
134 // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden | 112 // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden |
135 | 113 |
136 /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */ | 114 /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */ |
137 final String notinrange = Resources.getMsg(this.context.getMeta(), CSV_NOT_IN_GAUGE_RANGE, CSV_NOT_IN_GAUGE_RANGE); | 115 final RiverInfoProvider riverInfoProvider = infoProvider.forWaterlevel(waterlevel); |
138 | 116 |
139 final Gauge refGauge = waterlevel.findReferenceGauge(river); | |
140 final String refGaugeName = refGauge == null ? notinrange : refGauge.getName(); | |
141 | |
142 final BedHeightInfo sounding = BedHeightInfo.from(bedHeight); | |
143 final int wspYear = waterlevel.getYear(); | 117 final int wspYear = waterlevel.getYear(); |
144 final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, refGaugeName); | 118 final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, riverInfoProvider.getReferenceGauge()); |
145 | 119 |
146 final FlowDepthCalculationResult resultData = new FlowDepthCalculationResult(label, wstInfo, sounding); | 120 final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wstKms); |
147 | 121 |
148 boolean doCalcTkh = useTkh; | 122 final River river = riverInfoProvider.getRiver(); |
149 if (doCalcTkh && !(wstKms instanceof QKms)) { | 123 final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(useTkh, this.context, problems, label, river, calcRange, dischargeProvider, |
150 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); | 124 bedHeight); |
151 problems.addProblem(message); | 125 |
152 doCalcTkh = false; | 126 final FlowDepthCalculator calculator = new FlowDepthCalculator(riverInfoProvider, wstKms, dischargeProvider, bedHeight, tkhCalculator); |
153 } | 127 return calculator.execute(label, wstInfo, calcRange); |
154 | 128 } |
155 BedQualityD50KmValueFinder bedMeasurementsFinder = null; | 129 |
156 if (doCalcTkh) { | |
157 bedMeasurementsFinder = loadBedMeasurements(river, calcRange, sounding.getYear().intValue()); | |
158 if (bedMeasurementsFinder == null) { | |
159 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label); | |
160 problems.addProblem(message); | |
161 doCalcTkh = false; | |
162 } | |
163 } | |
164 | |
165 final String bedHeightLabel = bedHeight.getDescription(); | |
166 final String wstLabel = wstKms.getName(); | |
167 | |
168 final UnivariateRealFunction wstInterpolator = DoubleUtil.getLinearInterpolator(wstKms.allKms(), wstKms.allWs()); | |
169 UnivariateRealFunction qInterpolator = null; | |
170 DoubleRange qRange = null; | |
171 if (doCalcTkh) { | |
172 qInterpolator = DoubleUtil.getLinearInterpolator(((QKms) wstKms).allKms(), ((QKms) wstKms).allQs()); | |
173 if (qInterpolator != null) | |
174 qRange = new DoubleRange(((QKms) wstKms).allQs().min(), ((QKms) wstKms).allQs().max()); | |
175 else { | |
176 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); | |
177 problems.addProblem(message); | |
178 doCalcTkh = false; | |
179 } | |
180 } | |
181 | |
182 // FIXME: sort by station first, but in what direction? | |
183 // FIXME: using river.getKmUp()? | |
184 final List<BedHeightValue> values = bedHeight.getValues(); | |
185 | |
186 final List<BedHeightValue> sortedValues = new ArrayList<>(values); | |
187 Collections.sort(sortedValues, new BedHeightStationComparator()); | |
188 | |
189 // FIXME: wie wird ggf. interpoliert? prüfung ob werte vorhanden? | |
190 /* SoilKind lastKind = SoilKind.mobil; */ | |
191 SoilKindKmValueFinder soilKindFinder = null; | |
192 if (doCalcTkh) { | |
193 soilKindFinder = new SoilKindKmValueFinder(); | |
194 if (!soilKindFinder.loadValues(river, calcRange)) { | |
195 doCalcTkh = false; | |
196 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label); | |
197 problems.addProblem(message); | |
198 } | |
199 } | |
200 | |
201 FlowVelocityModelKmValueFinder flowVelocitiesFinder = null; | |
202 if (doCalcTkh) { | |
203 flowVelocitiesFinder = new FlowVelocityModelKmValueFinder(); | |
204 if (!flowVelocitiesFinder.loadValues(river, calcRange, qRange)) { | |
205 doCalcTkh = false; | |
206 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingVelocity", null, label); | |
207 problems.addProblem(message); | |
208 } | |
209 } | |
210 | |
211 for (final BedHeightValue bedHeightValue : sortedValues) { | |
212 | |
213 final Double station = bedHeightValue.getStation(); | |
214 if (station == null || station.isNaN()) | |
215 continue; | |
216 | |
217 final Double meanBedHeightDbl = bedHeightValue.getHeight(); | |
218 if (meanBedHeightDbl == null || meanBedHeightDbl.isNaN()) | |
219 continue; | |
220 | |
221 final double km = station; | |
222 final double meanBedHeight = meanBedHeightDbl; | |
223 | |
224 if (!calcRange.containsDouble(km)) | |
225 continue; | |
226 | |
227 try { | |
228 // FIXME: check out of range | |
229 final double wst = wstInterpolator.value(km); | |
230 | |
231 final double flowDepth = wst - meanBedHeight; | |
232 | |
233 // FIXME: piecewise constant interpolation? | |
234 // final double discharge = wstKms instanceof QKms ? ((QKms) wstKms).getQ(i) : Double.NaN; | |
235 double discharge; | |
236 if (qInterpolator != null) | |
237 discharge = qInterpolator.value(km); | |
238 else | |
239 discharge = Double.NaN; | |
240 | |
241 // Calculate tkh | |
242 double tkh = 0; | |
243 if (doCalcTkh) { | |
244 double d50 = Double.NaN; | |
245 try { | |
246 d50 = bedMeasurementsFinder.findD50(km); | |
247 } | |
248 catch (final Exception e) { | |
249 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingD50", null, label); | |
250 problems.addProblem(km, message); | |
251 // FIXME: cumulate problems to one message? | |
252 } | |
253 if (!Double.isNaN(d50)) { | |
254 if (flowVelocitiesFinder.findKmQValues(km, discharge)) { | |
255 tkh = calculateTkh(wst - meanBedHeight, flowVelocitiesFinder.getFindVmainFound(), d50, flowVelocitiesFinder.getFindTauFound()); | |
256 if (!Double.isNaN(tkh) && (tkh < 0)) | |
257 tkh = 0; | |
258 /* | |
259 * log.debug(String.format("calculateTkh km %.3f q %.0f w %.2f mbh %.2f vm %.1f tau %.1f d50(mm) %.1f tkh(cm) %.1f", | |
260 * km, discharge, wst, meanBedHeight, flowVelocitiesFinder.getFindVmainFound(), flowVelocitiesFinder.getFindTauFound(), | |
261 * d50*1000, tkh)); | |
262 */ | |
263 } else { | |
264 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); | |
265 problems.addProblem(km, message); | |
266 // FIXME: cumulate problems to one message? | |
267 } | |
268 } else | |
269 tkh = Double.NaN; | |
270 } | |
271 | |
272 // Soil kind | |
273 SoilKind kind = SoilKind.mobil; | |
274 if (doCalcTkh) { | |
275 try { | |
276 kind = soilKindFinder.findSoilKind(km); | |
277 } | |
278 catch (final Exception e) { | |
279 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label); | |
280 problems.addProblem(km, message); | |
281 // FIXME: cumulate problems to one message? | |
282 } | |
283 } | |
284 | |
285 final double flowDepthTkh; | |
286 final double tkhUp; | |
287 final double tkhDown; | |
288 switch (kind) { | |
289 case starr: | |
290 flowDepthTkh = wst - (meanBedHeight + tkh / 100); | |
291 tkhUp = tkh; | |
292 tkhDown = 0; | |
293 break; | |
294 | |
295 case mobil: | |
296 default: | |
297 flowDepthTkh = wst - (meanBedHeight + tkh / 200); | |
298 tkhUp = tkh / 2; | |
299 tkhDown = -tkh / 2; | |
300 break; | |
301 } | |
302 | |
303 // REMARK: access the location once only during calculation | |
304 final String location = LocationProvider.getLocation(river.getName(), km); | |
305 | |
306 // REMARK: access the gauge once only during calculation | |
307 final Gauge gauge = findGauge(waterlevel, refGauge, gaugeIndex, km); | |
308 | |
309 final String gaugeLabel = gauge == null ? notinrange : gauge.getName(); | |
310 | |
311 resultData.addRow(km, flowDepth, flowDepthTkh, kind, tkh, tkhUp, tkhDown, wst, discharge, wstLabel, gaugeLabel, meanBedHeight, bedHeightLabel, | |
312 location); | |
313 } | |
314 catch (final FunctionEvaluationException e) { | |
315 /* should only happen if out of range */ | |
316 e.printStackTrace(); | |
317 /* simply ignore */ | |
318 } | |
319 } | |
320 | |
321 return resultData; | |
322 } | |
323 | |
324 /** | |
325 * Sohlbeschaffenheit (D50 Korndurchmesser aus Seddb) | |
326 * Abhängig von Peiljahr | |
327 */ | |
328 private BedQualityD50KmValueFinder loadBedMeasurements(final River river, final DoubleRange kmRange, final int soundingYear) { | |
329 | |
330 /* construct valid measurement time range */ | |
331 final Calendar cal = Calendar.getInstance(); | |
332 cal.clear(); | |
333 | |
334 cal.set(soundingYear - VALID_BED_MEASUREMENT_YEARS, 0, 1); | |
335 final Date startTime = cal.getTime(); | |
336 | |
337 cal.set(soundingYear + VALID_BED_MEASUREMENT_YEARS, 11, 31); | |
338 final Date endTime = cal.getTime(); | |
339 | |
340 final BedQualityD50KmValueFinder finder = new BedQualityD50KmValueFinder(); | |
341 if (finder.loadValues(river, kmRange, new DateRange(startTime, endTime))) | |
342 return finder; | |
343 else | |
344 return null; | |
345 } | |
346 | 130 |
347 /** | 131 /** |
348 * Checks the year difference between waterlevels and sounding, and issues a warning if too big. | 132 * Checks the year difference between waterlevels and sounding, and issues a warning if too big. |
349 * | 133 * |
350 * Zeitraum Zeitliche Differenz [a] | 134 * Zeitraum Zeitliche Differenz [a] |
351 * X ≥ 1998 ± 3 | 135 * X ≥ 1998 ± 3 |
352 * 1958 ≤ X < 1998 ± 6 | 136 * 1958 ≤ X < 1998 ± 6 |
353 * 1918 ≤ X < 1958 ± 12 | 137 * 1918 ≤ X < 1958 ± 12 |
354 * X < 1918 ± 25 | 138 * X < 1918 ± 25 |
355 */ | 139 */ |
356 private void checkYearDifference(final String label, final WaterlevelData waterlevel, final BedHeight sounding, final Calculation problems) { | 140 private void checkYearDifference(final String label, final WaterlevelData waterlevel, final Integer soundingYear, final Calculation problems) { |
357 | |
358 final Integer soundingYear = sounding.getYear(); | |
359 if (soundingYear == null) | 141 if (soundingYear == null) |
360 return; | 142 return; |
361 | 143 |
362 final int wstYear = waterlevel.getYear(); | 144 final int wstYear = waterlevel.getYear(); |
363 if (wstYear < 0) | 145 if (wstYear < 0) |
386 | 168 |
387 /* >= 1998 */ | 169 /* >= 1998 */ |
388 return 3; | 170 return 3; |
389 } | 171 } |
390 | 172 |
391 private Gauge findGauge(final WaterlevelData waterlevel, final Gauge refGauge, final GaugeIndex gaugeIndex, final double km) { | |
392 | |
393 // REMARK: using same logic as in WaterlevelExporter here | |
394 | |
395 final boolean showAllGauges = waterlevel.isShowAllGauges(); | |
396 | |
397 if (showAllGauges) | |
398 return gaugeIndex.findGauge(km); | |
399 | |
400 if (refGauge.getRange().contains(km)) | |
401 return refGauge; | |
402 | |
403 return null; | |
404 } | |
405 | |
406 /* Checks if the discretisation of the waterlevel exceeds 1000m */ | 173 /* Checks if the discretisation of the waterlevel exceeds 1000m */ |
407 | 174 |
408 private void checkWaterlevelDiscretisation(final WKms wstKms, final DoubleRange calcRange, final Calculation problems) { | 175 private void checkWaterlevelDiscretisation(final WKms wstKms, final DoubleRange calcRange, final Calculation problems) { |
409 | 176 |
410 final int size = wstKms.size(); | 177 final int size = wstKms.size(); |
422 } | 189 } |
423 } | 190 } |
424 } | 191 } |
425 } | 192 } |
426 | 193 |
427 private BedHeight loadBedHeight(final String soundingId) { | 194 private BedHeightsFinder loadBedHeight(final String soundingId, final DoubleRange calcRange) { |
428 | 195 |
429 // REMARK: absolutely unbelievable.... | 196 // REMARK: absolutely unbelievable.... |
430 // The way how bed-heights (and other data too) is accessed is different for nearly every calculation-type | 197 // The way how bed-heights (and other data too) is accessed is different for nearly every calculation-type |
431 // throughout flys. | 198 // throughout flys. |
432 // The knowledge on how to parse the datacage-ids is spread through the complete code-base... | 199 // The knowledge on how to parse the datacage-ids is spread through the complete code-base... |
436 final String[] parts = soundingId.split(";"); | 203 final String[] parts = soundingId.split(";"); |
437 | 204 |
438 final BedHeightsArtifact artifact = (BedHeightsArtifact) RiverUtils.getArtifact(parts[0], this.context); | 205 final BedHeightsArtifact artifact = (BedHeightsArtifact) RiverUtils.getArtifact(parts[0], this.context); |
439 | 206 |
440 final Integer bedheightId = artifact.getDataAsInteger("height_id"); | 207 final Integer bedheightId = artifact.getDataAsInteger("height_id"); |
208 if (bedheightId == null) { | |
209 // FIXME: error message! | |
210 return null; | |
211 } | |
212 | |
441 // REMARK: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the | 213 // REMARK: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the |
442 // other type means) | 214 // other type means) |
443 // Luckily, the requirement is to only access 'single' data here. | 215 // Luckily, the requirement is to only access 'single' data here. |
444 // final String bedheightType = artifact.getDataAsString("type"); | 216 // final String bedheightType = artifact.getDataAsString("type"); |
445 | 217 |
447 // REMARK: second absolutely awful thing: BedHeight is a hibernate binding class, accessing the database via | 219 // REMARK: second absolutely awful thing: BedHeight is a hibernate binding class, accessing the database via |
448 // hibernate stuff | 220 // hibernate stuff |
449 // BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes. | 221 // BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes. |
450 // return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to); | 222 // return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to); |
451 | 223 |
452 return BedHeight.getBedHeightById(bedheightId); | 224 return BedHeightsFinder.forId(bedheightId, calcRange); |
453 } | |
454 | |
455 /** | |
456 * Calculates a transport body height | |
457 * | |
458 * @param h | |
459 * flow depth in m | |
460 * @param vm | |
461 * flow velocity in m | |
462 * @param d50 | |
463 * grain diameter D50 in m (!) | |
464 * @param tau | |
465 * shear stress in N/m^2 | |
466 * @return transport body height in cm (!) | |
467 */ | |
468 private double calculateTkh(final double h, final double vm, final double d50, final double tau) { | |
469 final double PHYS_G = 9.81; | |
470 final double PHYS_SPECGRAV_S = 2.6; | |
471 final double PHYS_VELOCCOEFF_N = 6; | |
472 final double PHYS_FORMCOEFF_ALPHA = 0.7; | |
473 final double PHYS_VISCOSITY_NUE = 1.3e-6; | |
474 final double PHYS_GRAIN_DENSITY_RHOS = 2603; | |
475 final double PHYS_WATER_DENSITY_RHO = 999.97; | |
476 | |
477 final double froude = vm / Math.sqrt(PHYS_G * h); | |
478 final double partReynolds = Math.sqrt((PHYS_SPECGRAV_S - 1) * PHYS_G * d50) / PHYS_VISCOSITY_NUE * d50; | |
479 final double critShields = 0.22 * Math.pow(partReynolds, -0.6) + 0.06 * Math.pow(10, 7.7 * Math.pow(partReynolds, -0.6)); | |
480 final double critTau = critShields * (PHYS_GRAIN_DENSITY_RHOS - PHYS_WATER_DENSITY_RHO) * PHYS_G * d50; | |
481 return 100 * h * (1 - Math.pow(froude, 2)) / (2 * PHYS_VELOCCOEFF_N * PHYS_FORMCOEFF_ALPHA) * (1 - critTau / tau); | |
482 } | 225 } |
483 } | 226 } |