comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/FlowVelocityModelKmValueFinder.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 artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowVelocityModelKmValueFinder.java@89f3c5462a16
children 45f1ad66560e
comparison
equal deleted inserted replaced
8914:e3519c3e7a0a 8915:d9dbf0b74bc2
1 /* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
2 * Software engineering by
3 * Björnsen Beratende Ingenieure GmbH
4 * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
5 *
6 * This file is Free Software under the GNU AGPL (>=v3)
7 * and comes with ABSOLUTELY NO WARRANTY! Check out the
8 * documentation coming with Dive4Elements River for details.
9 */
10
11 package org.dive4elements.river.artifacts.sinfo.tkhcalculation;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import org.apache.commons.lang.math.DoubleRange;
17 import org.apache.log4j.Logger;
18 import org.dive4elements.river.artifacts.math.Linear;
19 import org.dive4elements.river.artifacts.math.Utils;
20 import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowVelocityKmModelValues;
21 import org.dive4elements.river.backend.SessionHolder;
22 import org.dive4elements.river.model.River;
23 import org.hibernate.SQLQuery;
24 import org.hibernate.Session;
25 import org.hibernate.type.StandardBasicTypes;
26
27 import gnu.trove.TDoubleArrayList;
28
29 /**
30 * Searchable sorted km array with parallel FlowVelocityKmModelValues array and linear interpolation for km and the
31 * model values between the array elements.<br />
32 * {@link loadValues} loads all the model values for a given km range of a river.<br />
33 * {@link findKmQValues} then searches a km in the values table or the nearest including km interval, resp.
34 * The v and tau values for a given discharge are either found directly or also interpolated linearly.<br />
35 *
36 * (Created based on a copy of FlowVelocityMeasurementFactory.)
37 *
38 * @author Matthias Schäfer
39 */
40 final class FlowVelocityModelKmValueFinder {
41 /***** FIELDS *****/
42
43 /**
44 * Private log to use here.
45 */
46 private static Logger log = Logger.getLogger(FlowVelocityModelKmValueFinder.class);
47
48 /**
49 * Query for a range of stations of a river with all their q, main-v and tau values.<br />
50 * (Might be several 10000 rows if many stations and large q range)
51 */
52 private static final String SQL_SELECT_ALL = //
53 "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau"
54 + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)"
55 + " INNER JOIN flow_velocity_model_values fvmv ON fvm.id = fvmv.flow_velocity_model_id"
56 + " WHERE (dz.river_id = :river_id) AND (fvmv.station BETWEEN :kmfrom - 0.0001 AND :kmto + 0.0001)"
57 /* + " WHERE (dz.river_id = :river_id) AND (fvmv.q BETWEEN :qmin AND :qmax)" */
58 + " ORDER BY fvmv.station ASC, fvmv.q ASC";
59
60 /**
61 * Query for a river's max km below a limit with all its q, main-v and tau values.
62 */
63 private static final String SQL_SELECT_KMLOWER = //
64 "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" + " FROM flow_velocity_model_values fvmv"
65 + " INNER JOIN (SELECT MAX(fvmvi.station) AS kmmax"
66 + " FROM(discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)"
67 + " INNER JOIN flow_velocity_model_values fvmvi ON fvm.id = fvmvi.flow_velocity_model_id"
68 + " WHERE (dz.river_id = :river_id) AND (fvmvi.station < :kmfrom - 0.0001)) finf ON fvmv.station = finf.kmmax"
69 + " ORDER BY fvmv.q ASC";
70
71 /**
72 * Query for a river's min km above a limit with all its q, main-v and tau values.
73 */
74 private static final String SQL_SELECT_KMUPPER = //
75 "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" + " FROM flow_velocity_model_values fvmv"
76 + " INNER JOIN (SELECT MIN(fvmvi.station) AS kmmin"
77 + " FROM(discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)"
78 + " INNER JOIN flow_velocity_model_values fvmvi ON fvm.id = fvmvi.flow_velocity_model_id"
79 + " WHERE (dz.river_id = :river_id) AND (fvmvi.station > :kmto + 0.0001)) fsup ON fvmv.station = fsup.kmmin"
80 + " ORDER BY fvmv.q ASC";
81
82 // /**
83 // * Query to select all km-q-v-tau of a river that are the q maxima below a q limit
84 // */
85 // private static final String SQL_SELECT_QLOWER =
86 // "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau"
87 // + " FROM flow_velocity_model_values fvmv"
88 // + " INNER JOIN (SELECT fv2.station, MAX(fv2.q) AS q"
89 // + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)"
90 // + " INNER JOIN flow_velocity_model_values fv2 ON fvm.id = fv2.flow_velocity_model_id"
91 // + " WHERE (dz.river_id = :river_id) AND (fv2.q < :qlim) GROUP BY fv2.station) qx"
92 // + " ON (fvmv.station=qx.station) AND (fvmv.q=qx.q)"
93 // + " ORDER BY fvmv.station ASC";
94 //
95 // /**
96 // * Query to select all km-q-v-tau of a river that are the q minima above a q limit
97 // */
98 // private static final String SQL_SELECT_QUPPER =
99 // "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau"
100 // + " FROM flow_velocity_model_values fvmv"
101 // + " INNER JOIN (SELECT fv2.station, MIN(fv2.q) AS q"
102 // + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)"
103 // + " INNER JOIN flow_velocity_model_values fv2 ON fvm.id = fv2.flow_velocity_model_id"
104 // + " WHERE (dz.river_id = :river_id) AND (fv2.q > :qlim) GROUP BY fv2.station) qx"
105 // + " ON (fvmv.station=qx.station) AND (fvmv.q=qx.q)"
106 // + " ORDER BY fvmv.station ASC";
107
108 /**
109 * Kms of the loaded river range
110 */
111 private final TDoubleArrayList kms = new TDoubleArrayList();
112
113 /**
114 * For each km in kms a list of q-v-tau-tupels
115 */
116 private final List<FlowVelocityKmModelValues> values = new ArrayList<>();
117
118 /**
119 * Searched km of the last findKmValue
120 */
121 private double findKm;
122
123 /**
124 * kms and values index of the interval start found by the last findKmValue
125 */
126 private int leftIndexFound = -1;
127
128 /**
129 * kms and values index of the interval end found by the last findKmValue
130 */
131 private int rightIndexFound = -1;
132
133 /**
134 * Q of the last findKmQValues
135 */
136 private double findQ;
137
138 /***** METHODS *****/
139
140 /**
141 * Discharge of the last {@link findKmQValue}
142 */
143 public double getFindQ() {
144 return this.findQ;
145 }
146
147 /**
148 * Velocity of the last {@link findKmQValues}
149 */
150 public double getFindVmainFound() {
151 if (this.leftIndexFound < 0)
152 return Double.NaN;
153 else if (this.leftIndexFound == this.rightIndexFound)
154 return getLeftValues().getVmainFound();
155 else
156 return Linear.linear(this.findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getVmainFound(),
157 getRightValues().getVmainFound());
158 }
159
160 /**
161 * Shear stress tau of the last {@link findKmQValues}
162 */
163 public double getFindTauFound() {
164 if (this.leftIndexFound < 0)
165 return Double.NaN;
166 else if (this.leftIndexFound == this.rightIndexFound)
167 return getLeftValues().getTauFound();
168 else
169 return Linear.linear(this.findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getTauFound(), getRightValues().getTauFound());
170 }
171
172 /**
173 * Whether the discharge has been interpolated in the last {@link findKmQValues}
174 */
175 public boolean getFindIsQInterpolated() {
176 return (getLeftValues() != null) && (getLeftValues().getIsInterpolated() || getRightValues().getIsInterpolated());
177 }
178
179 /**
180 * Static constructor: queries a range of a river's kms with all their q-v-tau values.
181 *
182 * @return Whether the load has been successful the new instance, <code>null</code> otherwise.
183 */
184 public static FlowVelocityModelKmValueFinder loadValues(final River river, final DoubleRange kmRange, final DoubleRange qRange) {
185 // DB session
186 log.debug(String.format("loadValues km %.3f - %.3f / q %.1f - %.1f", kmRange.getMinimumDouble(), kmRange.getMaximumDouble(), qRange.getMinimumDouble(),
187 qRange.getMaximumDouble()));
188
189 final FlowVelocityModelKmValueFinder instance = new FlowVelocityModelKmValueFinder();
190
191 final TDoubleArrayList kms = instance.kms;
192 final List<FlowVelocityKmModelValues> values = instance.values;
193
194 final boolean isDemoValuesCorrection = river.getName().equalsIgnoreCase("beispielfluss");
195 final Session session = SessionHolder.HOLDER.get();
196
197 // Select km infimum
198 SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_KMLOWER).addScalar("station", StandardBasicTypes.DOUBLE).addScalar("q", StandardBasicTypes.DOUBLE)
199 .addScalar("vmain", StandardBasicTypes.DOUBLE).addScalar("tau", StandardBasicTypes.DOUBLE);
200 sqlQuery.setParameter("river_id", river.getId());
201 sqlQuery.setParameter("kmfrom", kmRange.getMinimumDouble());
202 instance.addKms(sqlQuery.list(), isDemoValuesCorrection);
203
204 // Select km range
205 sqlQuery = session.createSQLQuery(SQL_SELECT_ALL).addScalar("station", StandardBasicTypes.DOUBLE).addScalar("q", StandardBasicTypes.DOUBLE)
206 .addScalar("vmain", StandardBasicTypes.DOUBLE).addScalar("tau", StandardBasicTypes.DOUBLE);
207 sqlQuery.setParameter("river_id", river.getId());
208 sqlQuery.setParameter("kmfrom", kmRange.getMinimumDouble());
209 sqlQuery.setParameter("kmto", kmRange.getMaximumDouble());
210 // sqlQuery.setParameter("qmin", qRange.getMinimumDouble());
211 // sqlQuery.setParameter("qmax", qRange.getMaximumDouble());
212
213 int kmcount = kms.size();
214 final int rowcount = instance.addKms(sqlQuery.list(), isDemoValuesCorrection);
215 kmcount = kms.size() - kmcount;
216
217 // Select km supremum
218 sqlQuery = session.createSQLQuery(SQL_SELECT_KMUPPER).addScalar("station", StandardBasicTypes.DOUBLE).addScalar("q", StandardBasicTypes.DOUBLE)
219 .addScalar("vmain", StandardBasicTypes.DOUBLE).addScalar("tau", StandardBasicTypes.DOUBLE);
220 sqlQuery.setParameter("river_id", river.getId());
221 sqlQuery.setParameter("kmto", kmRange.getMaximumDouble());
222 final int supcnt = instance.addKms(sqlQuery.list(), isDemoValuesCorrection);
223
224 // Add copy of last km for search of max km value
225 if ((supcnt == 0) && (values.size() >= 1)) {
226 kms.add(kms.getQuick(kms.size()) + 0.0001);
227 values.add(new FlowVelocityKmModelValues(kms.getQuick(kms.size() - 1), values.get(values.size() - 1)));
228 }
229
230 // log.debug
231 if (values.size() - 1 >= 0) {
232 log.debug(String.format("loadValues %d: km %.3f - %d values", 0, values.get(0).getKm(), values.get(0).size()));
233
234 if (values.size() - 1 >= 1) {
235 log.debug(String.format("loadValues %d: km %.3f - %d values", 1, values.get(1).getKm(), values.get(1).size()));
236
237 if (values.size() - 1 >= 2)
238 log.debug("loadValues ...");
239
240 if (values.size() - 2 >= 3)
241 log.debug(String.format("loadValues %d: km %.3f - %d values", values.size() - 2, values.get(values.size() - 2).getKm(),
242 values.get(values.size() - 2).size()));
243
244 if (values.size() - 1 >= 3)
245 log.debug(String.format("loadValues %d: km %.3f - %d values", values.size() - 1, values.get(values.size() - 1).getKm(),
246 values.get(values.size() - 1).size()));
247 }
248 }
249
250 log.debug(String.format("loadValues %d kms, %d values loaded", kmcount, rowcount));
251
252 if (kms.size() == 0)
253 return null;
254
255 return instance;
256 }
257
258 /**
259 * Adds the km-q-v-tau values of a query result row to the last km of the list, or a new one resp.
260 *
261 * @return Number of rows
262 */
263 private int addKms(final List<Object[]> rows, final boolean isDemoValuesCorrection) {
264 for (final Object[] row : rows) {
265 if ((this.kms.size() == 0) || !Utils.epsilonEquals(this.kms.get(this.kms.size() - 1), (double) row[0], 0.0001)) {
266 this.kms.add((double) row[0]);
267 this.values.add(new FlowVelocityKmModelValues(this.kms.get(this.kms.size() - 1)));
268 }
269 if (isDemoValuesCorrection)
270 // "Verfremdung" der v-Werte etwas korrigieren (Originalwerte wurden mit Zufallswert zwischen 10 und 20 multipliziert)
271 this.values.get(this.values.size() - 1).addValues((double) row[1], ((double) row[2]) / 10, (double) row[3]);
272 else
273 this.values.get(this.values.size() - 1).addValues((double) row[1], (double) row[2], (double) row[3]);
274 }
275 return rows.size();
276 }
277
278 /**
279 * Searches a km and finds or interpolates the velocity and shear stress values for a discharge<br />
280 * The values may be got via {@link getVmainFound} etc.
281 *
282 * @return Whether values have been found
283 */
284 public boolean findKmQValues(final double km, final double q) {
285 this.findQ = q;
286 if (!searchKm(km))
287 return false;
288 if (this.leftIndexFound == this.rightIndexFound) {
289 // Exact km match
290 final double qfound = getLeftValues().findQ(q);
291 log.debug(String.format("findKmQValues km %.3f q %.0f = %.0f (%d)", km, q, qfound, this.leftIndexFound));
292 return !Double.isNaN(qfound);
293 } else {
294 final double[] qfound = { getLeftValues().findQ(q), getRightValues().findQ(q) };
295 log.debug(String.format("findKmQValues km %.3f q %.0f = %.0f (%d, %.3f) - %.0f (%d, %.3f)", km, q, qfound[0], this.leftIndexFound,
296 getLeftValues().getKm(), qfound[1], this.rightIndexFound, getRightValues().getKm()));
297 return !Double.isNaN(qfound[0]) && !Double.isNaN(qfound[1]);
298 }
299 }
300
301 /**
302 * Searches a km
303 *
304 * @return Whether the km was within the supported range
305 */
306 private boolean searchKm(final double km) {
307 this.findKm = km;
308 this.leftIndexFound = -1;
309 this.rightIndexFound = -1;
310 if ((this.kms == null) || (this.kms.size() == 0))
311 return false;
312 int i = this.kms.binarySearch(km);
313 if (i >= 0) {
314 // Exact km match
315 this.leftIndexFound = i;
316 this.rightIndexFound = i;
317 return true;
318 } else {
319 // Out of range or within km interval
320 if (i < 0)
321 i = -i - 1;
322 if ((i <= 0) || (i >= this.kms.size()))
323 return false;
324 this.leftIndexFound = i - 1;
325 this.rightIndexFound = i;
326 return true;
327 }
328 }
329
330 private FlowVelocityKmModelValues getLeftValues() {
331 return this.values.get(this.leftIndexFound);
332 }
333
334 private FlowVelocityKmModelValues getRightValues() {
335 return this.values.get(this.rightIndexFound);
336 }
337 }

http://dive4elements.wald.intevation.org