comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java @ 1190:f514894ec2fd

merged flys-artifacts/2.5
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:17 +0200
parents 1ea7eb72aaa6
children e5f7f25a511c
comparison
equal deleted inserted replaced
917:b48c36076e17 1190:f514894ec2fd
1 package de.intevation.flys.artifacts.model;
2
3 import java.io.Serializable;
4
5 import de.intevation.flys.artifacts.math.Linear;
6 import de.intevation.flys.artifacts.math.Function;
7
8 import java.util.Arrays;
9 import java.util.ArrayList;
10 import java.util.List;
11 import java.util.Collections;
12
13 import org.apache.log4j.Logger;
14
15 import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
16
17 import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
18
19 import org.apache.commons.math.ArgumentOutsideDomainException;
20
21 import org.apache.commons.math.exception.MathIllegalArgumentException;
22
23 public class WstValueTable
24 implements Serializable
25 {
26 private static Logger log = Logger.getLogger(WstValueTable.class);
27
28 public static final int DEFAULT_Q_STEPS = 500;
29
30 public static final class Column
31 implements Serializable
32 {
33 protected String name;
34
35 protected QRangeTree qRangeTree;
36
37 public Column() {
38 }
39
40 public Column(String name) {
41 this.name = name;
42 }
43
44 public String getName() {
45 return name;
46 }
47
48 public void setName(String name) {
49 this.name = name;
50 }
51
52 public QRangeTree getQRangeTree() {
53 return qRangeTree;
54 }
55
56 public void setQRangeTree(QRangeTree qRangeTree) {
57 this.qRangeTree = qRangeTree;
58 }
59 } // class Column
60
61 public static final class QPosition {
62
63 protected int index;
64 protected double weight;
65
66 public QPosition() {
67 }
68
69 public QPosition(int index, double weight) {
70 this.index = index;
71 this.weight = weight;
72 }
73
74 public QPosition set(int index, double weight) {
75 this.index = index;
76 this.weight = weight;
77 return this;
78 }
79
80 } // class Position
81
82 public static final class Row
83 implements Serializable, Comparable<Row>
84 {
85 double km;
86 double [] ws;
87
88 public Row() {
89 }
90
91 public Row(double km) {
92 this.km = km;
93 }
94
95 public Row(double km, double [] ws) {
96 this(km);
97 this.ws = ws;
98 }
99
100 public int compareTo(Row other) {
101 double d = km - other.km;
102 if (d < -0.0001) return -1;
103 if (d > 0.0001) return +1;
104 return 0;
105 }
106
107 public void interpolateW(
108 Row other,
109 double km,
110 double [] iqs,
111 double [] ows,
112 WstValueTable table,
113 Calculation errors
114 ) {
115 double kmWeight = Linear.factor(km, this.km, other.km);
116
117 QPosition qPosition = new QPosition();
118
119 for (int i = 0; i < iqs.length; ++i) {
120 if (table.getQPosition(km, iqs[i], qPosition) != null) {
121 double wt = getW(qPosition);
122 double wo = other.getW(qPosition);
123 if (Double.isNaN(wt) || Double.isNaN(wo)) {
124 if (errors != null) {
125 // TODO: I18N
126 errors.addProblem(
127 km, "cannot find w for q = " + iqs[i]);
128 }
129 ows[i] = Double.NaN;
130 }
131 else {
132 ows[i] = Linear.weight(kmWeight, wt, wo);
133 }
134 }
135 else {
136 if (errors != null) {
137 // TODO: I18N
138 errors.addProblem(km, "cannot find q = " + iqs[i]);
139 }
140 ows[i] = Double.NaN;
141 }
142 }
143 }
144
145 public double [][] interpolateWQ(
146 Row other,
147 double km,
148 int steps,
149 WstValueTable table,
150 Calculation errors
151 ) {
152 int W = Math.min(ws.length, other.ws.length);
153
154 if (W < 1) {
155 if (errors != null) {
156 // TODO: I18N
157 errors.addProblem("no ws found");
158 }
159 return new double[2][0];
160 }
161
162 double factor = Linear.factor(km, this.km, other.km);
163
164 double [] splineQ = new double[W];
165 double [] splineW = new double[W];
166
167 double minQ = Double.MAX_VALUE;
168 double maxQ = -Double.MAX_VALUE;
169
170 for (int i = 0; i < W; ++i) {
171 double wws = Linear.weight(factor, ws[i], other.ws[i]);
172 double wqs = Linear.weight(
173 factor,
174 table.getQIndex(i, km),
175 table.getQIndex(i, other.km));
176
177 if (Double.isNaN(wws) || Double.isNaN(wqs)) {
178 if (errors != null) {
179 // TODO: I18N
180 errors.addProblem(km, "cannot find w or q");
181 }
182 }
183 else {
184 if (wqs < minQ) minQ = wqs;
185 if (wqs > maxQ) maxQ = wqs;
186 }
187
188 splineW[i] = wws;
189 splineQ[i] = wqs;
190 }
191
192 double stepWidth = (maxQ - minQ)/steps;
193
194 SplineInterpolator interpolator = new SplineInterpolator();
195 PolynomialSplineFunction spline;
196
197 try {
198 spline = interpolator.interpolate(splineQ, splineW);
199 }
200 catch (MathIllegalArgumentException miae) {
201 if (errors != null) {
202 // TODO: I18N
203 errors.addProblem(km, "spline creation failed");
204 }
205 log.error("spline creation failed");
206 return new double[2][0];
207 }
208
209 double [] outWs = new double[steps];
210 double [] outQs = new double[steps];
211
212 Arrays.fill(outWs, Double.NaN);
213 Arrays.fill(outQs, Double.NaN);
214
215 try {
216 double q = minQ;
217 for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
218 outWs[i] = spline.value(outQs[i] = q);
219 }
220 }
221 catch (ArgumentOutsideDomainException aode) {
222 if (errors != null) {
223 // TODO: I18N
224 errors.addProblem(km, "spline interpolation failed");
225 }
226 log.error("spline interpolation failed", aode);
227 }
228
229 return new double [][] { outWs, outQs };
230 }
231
232 public double [][] interpolateWQ(
233 int steps,
234 WstValueTable table,
235 Calculation errors
236 ) {
237 int W = ws.length;
238
239 if (W < 1) {
240 if (errors != null) {
241 // TODO: I18N
242 errors.addProblem(km, "no ws found");
243 }
244 return new double[2][0];
245 }
246
247 double [] splineQ = new double[W];
248
249 double minQ = Double.MAX_VALUE;
250 double maxQ = -Double.MAX_VALUE;
251
252 for (int i = 0; i < W; ++i) {
253 double sq = table.getQIndex(i, km);
254 if (Double.isNaN(sq)) {
255 if (errors != null) {
256 // TODO: I18N
257 errors.addProblem(
258 km, "no q found in " + (i+1) + " column");
259 }
260 }
261 else {
262 if (sq < minQ) minQ = sq;
263 if (sq > maxQ) maxQ = sq;
264 }
265 splineQ[i] = sq;
266 }
267
268 double stepWidth = (maxQ - minQ)/steps;
269
270 SplineInterpolator interpolator = new SplineInterpolator();
271
272 PolynomialSplineFunction spline;
273
274 try {
275 spline = interpolator.interpolate(splineQ, ws);
276 }
277 catch (MathIllegalArgumentException miae) {
278 if (errors != null) {
279 // TODO: I18N
280 errors.addProblem(km, "spline creation failed");
281 }
282 log.error("spline creation failed");
283 return new double[2][0];
284 }
285
286 double [] outWs = new double[steps];
287 double [] outQs = new double[steps];
288
289 Arrays.fill(outWs, Double.NaN);
290 Arrays.fill(outQs, Double.NaN);
291
292 try {
293 double q = minQ;
294 for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
295 outWs[i] = spline.value(outQs[i] = q);
296 }
297 }
298 catch (ArgumentOutsideDomainException aode) {
299 if (errors != null) {
300 // TODO: I18N
301 errors.addProblem(km, "spline interpolation failed");
302 }
303 log.error("spline interpolation failed.", aode);
304 }
305
306 return new double [][] { outWs, outQs };
307 }
308
309
310 public double getW(QPosition qPosition) {
311 int index = qPosition.index;
312 double weight = qPosition.weight;
313
314 return weight == 1.0
315 ? ws[index]
316 : Linear.weight(weight, ws[index-1], ws[index]);
317 }
318
319 public double getW(
320 Row other,
321 double km,
322 QPosition qPosition
323 ) {
324 double kmWeight = Linear.factor(km, this.km, other.km);
325
326 int index = qPosition.index;
327 double weight = qPosition.weight;
328
329 double tw, ow;
330
331 if (weight == 1.0) {
332 tw = ws[index];
333 ow = other.ws[index];
334 }
335 else {
336 tw = Linear.weight(weight, ws[index-1], ws[index]);
337 ow = Linear.weight(weight, other.ws[index-1], other.ws[index]);
338 }
339
340 return Linear.weight(kmWeight, tw, ow);
341 }
342 } // class Row
343
344 protected List<Row> rows;
345
346 protected Column [] columns;
347
348 public WstValueTable() {
349 rows = new ArrayList<Row>();
350 }
351
352 public WstValueTable(Column [] columns) {
353 this();
354 this.columns = columns;
355 }
356
357 public WstValueTable(Column [] columns, List<Row> rows) {
358 this.columns = columns;
359 this.rows = rows;
360 }
361
362 public void sortRows() {
363 Collections.sort(rows);
364 }
365
366 public double [] interpolateW(double km, double [] qs, double [] ws) {
367 return interpolateW(km, qs, ws, null);
368 }
369
370
371 /**
372 * @param ws (output parameter), gets returned.
373 * @return output parameter ws.
374 */
375 public double [] interpolateW(
376 double km,
377 double [] qs,
378 double [] ws,
379 Calculation errors
380 ) {
381 int rowIndex = Collections.binarySearch(rows, new Row(km));
382
383 QPosition qPosition = new QPosition();
384
385 if (rowIndex >= 0) { // direct row match
386 Row row = rows.get(rowIndex);
387 for (int i = 0; i < qs.length; ++i) {
388 if (getQPosition(km, qs[i], qPosition) == null) {
389 if (errors != null) {
390 // TODO: I18N
391 errors.addProblem(km, "cannot find q = " + qs[i]);
392 }
393 ws[i] = Double.NaN;
394 }
395 else {
396 if (Double.isNaN(ws[i] = row.getW(qPosition))
397 && errors != null) {
398 // TODO: I18N
399 errors.addProblem(
400 km, "cannot find w for q = " + qs[i]);
401 }
402 }
403 }
404 }
405 else { // needs bilinear interpolation
406 rowIndex = -rowIndex -1;
407
408 if (rowIndex < 1 || rowIndex >= rows.size()) {
409 // do not extrapolate
410 Arrays.fill(ws, Double.NaN);
411 if (errors != null) {
412 // TODO: I18N
413 errors.addProblem(km, "km not found");
414 }
415 }
416 else {
417 Row r1 = rows.get(rowIndex-1);
418 Row r2 = rows.get(rowIndex);
419 r1.interpolateW(r2, km, qs, ws, this, errors);
420 }
421 }
422
423 return ws;
424 }
425
426 public double [][] interpolateWQ(double km) {
427 return interpolateWQ(km, null);
428 }
429
430 public double [][] interpolateWQ(double km, Calculation errors) {
431 return interpolateWQ(km, DEFAULT_Q_STEPS, errors);
432 }
433
434 public double [][] interpolateWQ(double km, int steps, Calculation errors) {
435
436 int rowIndex = Collections.binarySearch(rows, new Row(km));
437
438 if (rowIndex >= 0) { // direct row match
439 Row row = rows.get(rowIndex);
440 return row.interpolateWQ(steps, this, errors);
441 }
442
443 rowIndex = -rowIndex -1;
444
445 if (rowIndex < 1 || rowIndex >= rows.size()) {
446 // do not extrapolate
447 if (errors != null) {
448 // TODO: I18N
449 errors.addProblem(km, "km not found");
450 }
451 return new double[2][0];
452 }
453
454 Row r1 = rows.get(rowIndex-1);
455 Row r2 = rows.get(rowIndex);
456
457 return r1.interpolateWQ(r2, km, steps, this, errors);
458 }
459
460 public boolean interpolate(
461 double km,
462 double [] out,
463 QPosition qPosition,
464 Function qFunction
465 ) {
466 int R1 = rows.size()-1;
467
468 out[1] = qFunction.value(getQ(qPosition, km));
469
470 if (Double.isNaN(out[1])) {
471 return false;
472 }
473
474 QPosition nPosition = new QPosition();
475 if (getQPosition(km, out[1], nPosition) == null) {
476 return false;
477 }
478
479 int rowIndex = Collections.binarySearch(rows, new Row(km));
480
481 if (rowIndex >= 0) {
482 // direct row match
483 out[0] = rows.get(rowIndex).getW(nPosition);
484 return !Double.isNaN(out[0]);
485 }
486
487 rowIndex = -rowIndex -1;
488
489 if (rowIndex < 1 || rowIndex >= R1) {
490 // do not extrapolate
491 return false;
492 }
493
494 Row r1 = rows.get(rowIndex-1);
495 Row r2 = rows.get(rowIndex);
496 out[0] = r1.getW(r2, km, nPosition);
497
498 return !Double.isNaN(out[0]);
499 }
500
501
502 /**
503 * Look up interpolation of a Q at given positions.
504 *
505 * @param q the non-interpolated Q value.
506 * @param referenceKm the reference km (e.g. gauge position).
507 * @param kms positions for which to interpolate.
508 * @param ws (output) resulting interpolated ws.
509 * @param qs (output) resulting interpolated qs.
510 * @param errors calculation object to store errors.
511 */
512 public QPosition interpolate(
513 double q,
514 double referenceKm,
515 double [] kms,
516 double [] ws,
517 double [] qs,
518 Calculation errors
519 ) {
520 return interpolate(
521 q, referenceKm, kms, ws, qs, 0, kms.length, errors);
522 }
523
524 public QPosition interpolate(
525 double q,
526 double referenceKm,
527 double [] kms,
528 double [] ws,
529 double [] qs,
530 int startIndex,
531 int length,
532 Calculation errors
533 ) {
534 QPosition qPosition = getQPosition(referenceKm, q);
535
536 if (qPosition == null) {
537 // we cannot locate q at km
538 Arrays.fill(ws, Double.NaN);
539 Arrays.fill(qs, Double.NaN);
540 if (errors != null) {
541 errors.addProblem(referenceKm, "cannot find q");
542 }
543 return null;
544 }
545
546 Row kmKey = new Row();
547
548 int R1 = rows.size()-1;
549
550 for (int i = startIndex, end = startIndex+length; i < end; ++i) {
551
552 if (Double.isNaN(qs[i] = getQ(qPosition, kms[i]))) {
553 if (errors != null) {
554 errors.addProblem(kms[i], "cannot find q");
555 }
556 ws[i] = Double.NaN;
557 continue;
558 }
559
560 kmKey.km = kms[i];
561 int rowIndex = Collections.binarySearch(rows, kmKey);
562
563 if (rowIndex >= 0) {
564 // direct row match
565 if (Double.isNaN(ws[i] = rows.get(rowIndex).getW(qPosition))
566 && errors != null) {
567 errors.addProblem(kms[i], "cannot find w");
568 }
569 continue;
570 }
571
572 rowIndex = -rowIndex -1;
573
574 if (rowIndex < 1 || rowIndex >= R1) {
575 // do not extrapolate
576 if (errors != null) {
577 errors.addProblem(kms[i], "cannot find km");
578 }
579 ws[i] = Double.NaN;
580 continue;
581 }
582 Row r1 = rows.get(rowIndex-1);
583 Row r2 = rows.get(rowIndex);
584
585 if (Double.isNaN(ws[i] = r1.getW(r2, kms[i], qPosition))
586 && errors != null) {
587 errors.addProblem(kms[i], "cannot find w");
588 }
589 }
590
591 return qPosition;
592 }
593
594 public QPosition getQPosition(double km, double q) {
595 return getQPosition(km, q, new QPosition());
596 }
597
598 public QPosition getQPosition(double km, double q, QPosition qPosition) {
599
600 if (columns.length == 0) {
601 return null;
602 }
603
604 double qLast = columns[0].getQRangeTree().findQ(km);
605
606 if (Math.abs(qLast - q) < 0.00001) {
607 return qPosition.set(0, 1d);
608 }
609
610 for (int i = 1; i < columns.length; ++i) {
611 double qCurrent = columns[i].getQRangeTree().findQ(km);
612 if (Math.abs(qCurrent - q) < 0.00001) {
613 return qPosition.set(i, 1d);
614 }
615
616 double qMin, qMax;
617 if (qLast < qCurrent) { qMin = qLast; qMax = qCurrent; }
618 else { qMin = qCurrent; qMax = qLast; }
619
620 if (q > qMin && q < qMax) {
621 double weight = Linear.factor(q, qLast, qCurrent);
622 return qPosition.set(i, weight);
623 }
624 qLast = qCurrent;
625 }
626
627 return null;
628 }
629
630 public double getQIndex(int index, double km) {
631 return columns[index].getQRangeTree().findQ(km);
632 }
633
634 public double getQ(QPosition qPosition, double km) {
635 int index = qPosition.index;
636 double weight = qPosition.weight;
637
638 if (weight == 1d) {
639 return columns[index].getQRangeTree().findQ(km);
640 }
641 double q1 = columns[index-1].getQRangeTree().findQ(km);
642 double q2 = columns[index ].getQRangeTree().findQ(km);
643 return Linear.weight(weight, q1, q2);
644 }
645
646 }
647 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org