Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java @ 5962:d861efa13272
Partial rewrite of historical discharge curves. Needs testing.
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Fri, 10 May 2013 10:46:56 +0200 |
parents | 203c934dd669 |
children | af13ceeba52a |
comparison
equal
deleted
inserted
replaced
5961:55687a506296 | 5962:d861efa13272 |
---|---|
9 package org.dive4elements.river.artifacts.model; | 9 package org.dive4elements.river.artifacts.model; |
10 | 10 |
11 import java.util.ArrayList; | 11 import java.util.ArrayList; |
12 import java.util.Arrays; | 12 import java.util.Arrays; |
13 import java.util.Date; | 13 import java.util.Date; |
14 import java.util.HashMap; | |
14 import java.util.List; | 15 import java.util.List; |
16 import java.util.Map; | |
15 | 17 |
16 import org.apache.log4j.Logger; | 18 import org.apache.log4j.Logger; |
17 | 19 |
18 import org.dive4elements.river.model.DischargeTable; | 20 import org.dive4elements.river.model.DischargeTable; |
19 import org.dive4elements.river.model.Gauge; | 21 import org.dive4elements.river.model.Gauge; |
85 this.timerange = timerange; | 87 this.timerange = timerange; |
86 this.values = values; | 88 this.values = values; |
87 this.officialGaugeNumber = officialGaugeNumber; | 89 this.officialGaugeNumber = officialGaugeNumber; |
88 } | 90 } |
89 | 91 |
92 protected CalculationResult error(String msg) { | |
93 addProblem(msg); | |
94 return new CalculationResult(new HistoricalDischargeData(), this); | |
95 } | |
96 | |
90 public CalculationResult calculate() { | 97 public CalculationResult calculate() { |
91 if (hasProblems()) { | 98 if (hasProblems()) { |
92 log.warn("Parameters not valid for calculation."); | 99 log.warn("Parameters not valid for calculation."); |
93 return null; | 100 return null; |
94 } | 101 } |
95 | 102 |
96 Gauge gauge = Gauge.getGaugeByOfficialNumber(officialGaugeNumber); | 103 Gauge gauge = Gauge.getGaugeByOfficialNumber(officialGaugeNumber); |
97 if (gauge == null) { | 104 if (gauge == null) { |
98 // TODO: i18n | 105 // TODO: i18n |
99 addProblem("hist.discharge.gauge.not.found"); | 106 return error("hist.discharge.gauge.not.found"); |
100 return null; | |
101 } | 107 } |
102 | 108 |
103 if (log.isDebugEnabled()) { | 109 if (log.isDebugEnabled()) { |
104 debug(); | 110 debug(); |
105 } | 111 } |
106 | 112 |
107 DischargeTable refTable = fetchReferenceTable(gauge); | |
108 List<DischargeTable> dts = fetchDischargeTables(gauge); | 113 List<DischargeTable> dts = fetchDischargeTables(gauge); |
109 | 114 if (dts.isEmpty()) { |
110 int numTables = dts.size(); | 115 return error("cannot.find.hist.q.tables"); |
111 | 116 } |
112 log.debug("Take " + numTables + " into account."); | 117 |
113 | 118 DischargeTable refTable = fetchReferenceTable(dts); |
114 if (numTables == 0) { | 119 |
115 addProblem("cannot.find.hist.q.tables"); | 120 boolean debug = log.isDebugEnabled(); |
116 } | 121 |
117 | 122 if (debug) { |
118 WQTimerange[] wqt = prepareTimerangeData(refTable, dts); | 123 log.debug("Take " + dts.size() + " into account."); |
119 WQKms[] wqs = prepareWQData(dts); | 124 } |
120 | 125 |
121 log.debug("Number of calculation results: " + wqt.length); | 126 ValuesCache vc = new ValuesCache(); |
127 | |
128 WQTimerange [] wqt = prepareData(refTable, dts, vc); | |
129 WQKms [] wqs = prepareWQData(dts, vc); | |
130 | |
131 if (debug) { | |
132 log.debug("Number of calculation results: " + wqt.length); | |
133 } | |
122 | 134 |
123 return new CalculationResult(new HistoricalDischargeData(wqt, wqs), | 135 return new CalculationResult(new HistoricalDischargeData(wqt, wqs), |
124 this); | 136 this); |
125 } | 137 } |
126 | 138 |
127 protected DischargeTable fetchReferenceTable(Gauge gauge) { | 139 /** The youngest discharge table of the selected set is the reference */ |
128 return gauge.fetchMasterDischargeTable(); | 140 protected DischargeTable fetchReferenceTable(List<DischargeTable> dts) { |
141 DischargeTable ref = null; | |
142 long now = System.currentTimeMillis(); | |
143 for (DischargeTable dt: dts) { | |
144 if (ref == null) { | |
145 ref = dt; | |
146 } | |
147 else { | |
148 TimeInterval cti = dt.getTimeInterval(); | |
149 TimeInterval rti = ref.getTimeInterval(); | |
150 | |
151 long ct = cti.getStopTime() != null | |
152 ? cti.getStopTime().getTime() | |
153 : now; | |
154 long rt = rti.getStopTime() != null | |
155 ? rti.getStopTime().getTime() | |
156 : now; | |
157 | |
158 if (ct > rt) { | |
159 ref = dt; | |
160 } | |
161 | |
162 } | |
163 } | |
164 return ref; | |
129 } | 165 } |
130 | 166 |
131 protected List<DischargeTable> fetchDischargeTables(Gauge gauge) { | 167 protected List<DischargeTable> fetchDischargeTables(Gauge gauge) { |
132 List<DischargeTable> relevant = new ArrayList<DischargeTable>(); | 168 |
133 List<DischargeTable> all = gauge.getDischargeTables(); | 169 List<DischargeTable> all = gauge.getDischargeTables(); |
134 | 170 List<DischargeTable> relevant = new ArrayList<DischargeTable>(all.size()); |
135 for (DischargeTable dt : all) { | 171 |
172 for (DischargeTable dt: all) { | |
136 if (isDischargeTableRelevant(dt)) { | 173 if (isDischargeTableRelevant(dt)) { |
137 relevant.add(dt); | 174 relevant.add(dt); |
138 } | 175 } |
139 } | 176 } |
140 | 177 |
141 return relevant; | 178 return relevant; |
142 } | 179 } |
143 | 180 |
144 /** True if timerange of given discharge table overlaps with timerange. */ | 181 /** True if timerange of given discharge table overlaps with timerange. */ |
145 protected boolean isDischargeTableRelevant(DischargeTable dt) { | 182 protected boolean isDischargeTableRelevant(DischargeTable dt) { |
183 | |
146 TimeInterval ti = dt.getTimeInterval(); | 184 TimeInterval ti = dt.getTimeInterval(); |
147 | 185 |
148 if (dt.getKind() == Gauge.MASTER_DISCHARGE_TABLE || ti == null) { | 186 if (dt.getKind() == Gauge.MASTER_DISCHARGE_TABLE || ti == null) { |
149 return false; | 187 return false; |
150 } | 188 } |
151 | 189 |
152 Date start = ti.getStartTime(); | 190 long dtStart = ti.getStartTime().getTime(); |
153 long startTime = start.getTime(); | 191 long dtStop = ti.getStopTime() != null |
154 | 192 ? ti.getStopTime().getTime() |
155 if (startTime >= timerange[0] && startTime <= timerange[1]) { | 193 : System.currentTimeMillis(); |
156 return true; | 194 |
157 } | 195 return !(timerange[1] < dtStart || timerange[0] > dtStop); |
158 | 196 } |
159 Date stop = ti.getStopTime(); | 197 |
160 long stopTime = stop != null ? stop.getTime() : -1l; | 198 protected WQKms[] prepareWQData(List<DischargeTable> dts, ValuesCache vc) { |
161 | |
162 if (stopTime >= timerange[0] && stopTime <= timerange[1]) { | |
163 return true; | |
164 } | |
165 | |
166 log.debug("DischargeTable not in range: " + start + " -> " + stop); | |
167 | |
168 return false; | |
169 } | |
170 | |
171 protected WQTimerange[] prepareTimerangeData(DischargeTable refTable, | |
172 List<DischargeTable> dts) { | |
173 if (refTable == null) { | |
174 addProblem("cannot.find.hist.q.reftable"); | |
175 return prepareSimpleData(dts); | |
176 } | |
177 else { | |
178 return prepareData(refTable, dts); | |
179 } | |
180 } | |
181 | |
182 protected WQKms[] prepareWQData(List<DischargeTable> dts) { | |
183 WQKms[] wqs = new WQKms[dts.size()]; | 199 WQKms[] wqs = new WQKms[dts.size()]; |
184 | 200 |
185 int idx = 0; | 201 for (int i = 0, N = wqs.length; i < N; ++i) { |
186 | 202 DischargeTable dt = dts.get(i); |
187 for (DischargeTable dt : dts) { | 203 wqs[i] = prepareWQ(dt, vc.getValues(dts.get(i))); |
188 double[][] values = null; | |
189 | |
190 if (dt.getKind() == DischargeTables.MASTER) { | |
191 values = DischargeTables.loadDischargeTableValues(dt, | |
192 DischargeTables.MASTER_SCALE); | |
193 | |
194 } | |
195 else { | |
196 values = DischargeTables.loadDischargeTableValues(dt, | |
197 DischargeTables.HISTORICAL_SCALE); | |
198 | |
199 } | |
200 | |
201 wqs[idx++] = prepareWQ(dt, values); | |
202 } | 204 } |
203 | 205 |
204 return wqs; | 206 return wqs; |
205 } | 207 } |
206 | 208 |
212 | 214 |
213 return new HistoricalWQKms(kms, values[0], values[1], | 215 return new HistoricalWQKms(kms, values[0], values[1], |
214 String.valueOf(km), dt.getTimeInterval()); | 216 String.valueOf(km), dt.getTimeInterval()); |
215 } | 217 } |
216 | 218 |
217 /** Without reference. */ | 219 protected String name(double value) { |
218 protected WQTimerange[] prepareSimpleData(List<DischargeTable> dts) { | 220 return mode == EvaluationMode.W.getMode() |
219 List<WQTimerange> wqts = new ArrayList<WQTimerange>(values.length); | 221 ? "W=" + value |
220 | 222 : "Q=" + value; |
221 for (double value : values) { | 223 } |
222 log.debug("Prepare data for value: " + value); | 224 |
223 | 225 /** With reference. */ |
224 String name = mode == EvaluationMode.W.getMode() | 226 protected HistoricalWQTimerange[] prepareData( |
225 ? "W=" + value | 227 DischargeTable refTable, |
226 : "Q=" + value; | 228 List<DischargeTable> dts, |
227 | 229 ValuesCache vc |
228 WQTimerange wqt = null; | 230 ) { |
231 List<HistoricalWQTimerange> wqts = | |
232 new ArrayList<HistoricalWQTimerange>(values.length); | |
233 | |
234 boolean debug = log.isDebugEnabled(); | |
235 | |
236 for (double value: values) { | |
237 if (debug) { | |
238 log.debug("Prepare data plus diff for value: " + value); | |
239 } | |
240 | |
241 double ref = mode == EvaluationMode.W.getMode() | |
242 ? vc.findValueForW(refTable, value) | |
243 : vc.findValueForQ(refTable, value); | |
244 | |
245 if (Double.isNaN(ref)) { | |
246 addProblem("hist.discharge.bo.value.in.ref", value); | |
247 continue; | |
248 } | |
249 | |
250 String name = name(value); | |
251 HistoricalWQTimerange wqt = null; | |
229 | 252 |
230 for (DischargeTable dt : dts) { | 253 for (DischargeTable dt : dts) { |
231 Date[] ti = prepareTimeInterval(dt); | 254 Date[] ti = prepareTimeInterval(dt); |
232 Timerange t = new Timerange(ti[0], ti[1]); | 255 Timerange t = new Timerange(ti[0], ti[1]); |
233 double w; | 256 double w; |
234 double q; | 257 double q; |
258 double diff; | |
235 | 259 |
236 if (mode == EvaluationMode.W.getMode()) { | 260 if (mode == EvaluationMode.W.getMode()) { |
237 w = value; | 261 q = vc.findValueForW(dt, w = value); |
238 q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE); | |
239 | 262 |
240 if (Double.isNaN(q)) { | 263 if (Double.isNaN(q)) { |
241 log.warn("Cannot find Q for W: " + w); | 264 log.warn("Cannot find Q for W: " + w); |
242 addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]); | 265 addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]); |
243 continue; | 266 continue; |
244 } | 267 } |
268 | |
269 diff = ref - q; | |
245 } | 270 } |
246 else { | 271 else { |
247 q = value; | 272 w = vc.findValueForQ(dt, q = value); |
248 w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE); | |
249 } | |
250 | |
251 log.debug("Q=" + q + " | W=" + w); | |
252 | |
253 if (wqt == null) { | |
254 wqt = new WQTimerange(name); | |
255 } | |
256 | |
257 wqt.add(w, q, t); | |
258 } | |
259 | |
260 if (wqt != null) { | |
261 wqts.add(wqt); | |
262 } | |
263 } | |
264 | |
265 return wqts.toArray(new WQTimerange[wqts.size()]); | |
266 } | |
267 | |
268 /** With reference. */ | |
269 protected HistoricalWQTimerange[] prepareData(DischargeTable refTable, | |
270 List<DischargeTable> dts) { | |
271 List<HistoricalWQTimerange> wqts = new ArrayList<HistoricalWQTimerange>( | |
272 values.length); | |
273 | |
274 for (double value : values) { | |
275 log.debug("Prepare data plus diff for value: " + value); | |
276 | |
277 String name = mode == EvaluationMode.W.getMode() | |
278 ? "W=" + value | |
279 : "Q=" + value; | |
280 HistoricalWQTimerange wqt = null; | |
281 | |
282 double ref; | |
283 double diff; | |
284 | |
285 if (refTable != null && mode == EvaluationMode.W.getMode()) { | |
286 ref = findValueForW(refTable, value, | |
287 DischargeTables.MASTER_SCALE); | |
288 } | |
289 else if (refTable != null) { | |
290 ref = findValueForQ(refTable, value, | |
291 DischargeTables.MASTER_SCALE); | |
292 } | |
293 else { | |
294 ref = Double.NaN; | |
295 } | |
296 | |
297 for (DischargeTable dt : dts) { | |
298 Date[] ti = prepareTimeInterval(dt); | |
299 | |
300 Timerange t = new Timerange(ti[0], ti[1]); | |
301 double w; | |
302 double q; | |
303 | |
304 if (mode == EvaluationMode.W.getMode()) { | |
305 w = value; | |
306 q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE); | |
307 | |
308 if (Double.isNaN(q)) { | |
309 log.warn("Cannot find Q for W: " + w); | |
310 addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]); | |
311 continue; | |
312 } | |
313 | |
314 diff = ref - q; | |
315 } | |
316 else { | |
317 q = value; | |
318 w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE); | |
319 | 273 |
320 if (Double.isNaN(w)) { | 274 if (Double.isNaN(w)) { |
321 log.warn("Cannot find W for Q: " + q); | 275 log.warn("Cannot find W for Q: " + q); |
322 addProblem("cannot.find.hist.w.for.q", q, ti[0], ti[1]); | 276 addProblem("cannot.find.hist.w.for.q", q, ti[0], ti[1]); |
323 continue; | 277 continue; |
324 } | 278 } |
325 diff = ref - w; | 279 diff = ref - w; |
326 } | 280 } |
327 | 281 |
328 log.debug("Q=" + q + " | W=" + w + " | Ref = " + ref); | 282 if (debug) { |
283 log.debug("Q=" + q + " | W=" + w + " | Ref = " + ref); | |
284 } | |
329 | 285 |
330 if (wqt == null) { | 286 if (wqt == null) { |
331 wqt = new HistoricalWQTimerange(name); | 287 wqt = new HistoricalWQTimerange(name); |
332 } | 288 } |
333 | 289 |
337 if (wqt != null) { | 293 if (wqt != null) { |
338 wqts.add(wqt); | 294 wqts.add(wqt); |
339 } | 295 } |
340 } | 296 } |
341 | 297 |
342 return (HistoricalWQTimerange[]) wqts | 298 return (HistoricalWQTimerange[])wqts.toArray( |
343 .toArray(new HistoricalWQTimerange[wqts.size()]); | 299 new HistoricalWQTimerange[wqts.size()]); |
344 } | 300 } |
345 | 301 |
346 /** Returns discharge table interval as Date[]. */ | 302 /** Returns discharge table interval as Date[]. */ |
347 protected Date[] prepareTimeInterval(DischargeTable dt) { | 303 protected Date[] prepareTimeInterval(DischargeTable dt) { |
348 TimeInterval ti = dt.getTimeInterval(); | 304 TimeInterval ti = dt.getTimeInterval(); |
357 } | 313 } |
358 | 314 |
359 return new Date[] { start, end }; | 315 return new Date[] { start, end }; |
360 } | 316 } |
361 | 317 |
362 protected double findValueForW(DischargeTable dt, double w, double scale) { | 318 |
363 double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale); | 319 /** Helper to avoid redundant loading of discharge table values. */ |
364 double[] qs = DischargeTables.getQsForW(vs, w); | 320 private static final class ValuesCache { |
365 return qs.length == 0 ? Double.NaN : qs[0]; | 321 |
366 } | 322 private Map<Integer, double[][]> cache; |
367 | 323 |
368 protected double findValueForQ(DischargeTable dt, double q, double scale) { | 324 ValuesCache() { |
369 double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale); | 325 cache = new HashMap<Integer, double [][]>(); |
370 double[] ws = DischargeTables.getWsForQ(vs, q); | 326 } |
371 | 327 |
372 return ws.length == 0 ? Double.NaN : ws[0]; | 328 double [][] getValues(DischargeTable dt) { |
373 } | 329 Integer id = dt.getId(); |
330 double [][] vs = cache.get(id); | |
331 if (vs == null) { | |
332 vs = DischargeTables.loadDischargeTableValues( | |
333 dt, DischargeTables.HISTORICAL_SCALE); | |
334 cache.put(id, vs); | |
335 } | |
336 return vs; | |
337 } | |
338 | |
339 private static double firstOrNaN(double [] vs) { | |
340 return vs.length > 0 ? vs[0] : Double.NaN; | |
341 } | |
342 | |
343 double findValueForW(DischargeTable dt, double w) { | |
344 return firstOrNaN(DischargeTables.getQsForW(getValues(dt), w)); | |
345 } | |
346 | |
347 double findValueForQ(DischargeTable dt, double q) { | |
348 return firstOrNaN(DischargeTables.getWsForQ(getValues(dt), q)); | |
349 } | |
350 } // class ValuesCache | |
374 | 351 |
375 /** | 352 /** |
376 * Writes the parameters used for this calculation to log. | 353 * Writes the parameters used for this calculation to log. |
377 */ | 354 */ |
378 public void debug() { | 355 public void debug() { |