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