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() {

http://dive4elements.wald.intevation.org