1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.numerics.integrate;
39
40 import static ffx.numerics.integrate.Integrate1DNumeric.IntegrationSide.LEFT;
41 import static ffx.numerics.integrate.Integrate1DNumeric.IntegrationSide.RIGHT;
42 import static java.lang.String.format;
43 import static org.junit.Assert.assertEquals;
44 import static org.junit.Assert.assertTrue;
45
46 import ffx.numerics.integrate.Integrate1DNumeric.IntegrationSide;
47 import ffx.numerics.integrate.Integrate1DNumeric.IntegrationType;
48 import java.util.Arrays;
49 import java.util.List;
50 import java.util.logging.Logger;
51 import java.util.stream.Collectors;
52
53 import ffx.utilities.FFXTest;
54 import org.junit.Test;
55
56
57
58
59
60
61
62
63 public class Integrate1DTest extends FFXTest {
64
65 private static final int NUM_INTEGRATION_TYPES = 8;
66 private static final Logger logger = Logger.getLogger(Integrate1DTest.class.getName());
67
68
69
70
71
72
73
74
75 private static void assertToUlp(double trueVal, double ulpMult, double... values) {
76 double ulp = Math.ulp(trueVal) * ulpMult;
77 for (double val : values) {
78 assertEquals(trueVal, val, ulp);
79 }
80 }
81
82
83
84
85
86
87
88
89
90 private static void assertToUlp(
91 String message, double trueVal, double ulpMult, double... values) {
92 double ulp = Math.ulp(trueVal) * ulpMult;
93 for (double val : values) {
94 assertEquals(message, trueVal, val, ulp);
95 }
96 }
97
98
99
100
101
102 @Test
103 public void castToDoubleSetTest() {
104 logger.info(" Testing casting of a function to a DoublesDataSet");
105
106 double[] points = Integrate1DNumeric.generateXPoints(0.0, 1.0, 11, false);
107
108 double[] zeroOrder = {1.0};
109 double[] firstOrder = {2.0, 1.0};
110 double[] secondOrder = {1.5, -4.0, 1.0};
111 double[] thirdOrder = {2.0, -1.0, -3.0, 1.0};
112 double[] fourthOrder = {1, -4.0, -6.0, 4.0, 1.0};
113 double[] fifthOrder = {
114 2.0, 10.0, -18.0, 8.0, -5.0, 1.0
115 };
116
117
118 double[][] coeffs = {zeroOrder, firstOrder, secondOrder, thirdOrder, fourthOrder, fifthOrder};
119 List<FunctionDataCurve> polynomials =
120 Arrays.stream(coeffs)
121 .map(
122 (double[] coeff) -> {
123 return new PolynomialCurve(points, false, coeff);
124 })
125 .collect(Collectors.toList());
126
127 for (FunctionDataCurve pn : polynomials) {
128 pn.assertXIntegrity();
129 DataSet dpn = new DoublesDataSet(pn);
130 double[] ypts = pn.getAllFxPoints();
131 DataSet abInitio = new DoublesDataSet(points, ypts, false);
132
133 for (int j = 0; j < NUM_INTEGRATION_TYPES; j++) {
134 IntegrationResult rpn = new IntegrationResult(pn, j);
135 IntegrationResult rdpn = new IntegrationResult(dpn, j);
136 IntegrationResult raipn = new IntegrationResult(abInitio, j);
137
138 double pnVal = rpn.getValue();
139 double dpnVal = rdpn.getValue();
140 double aipnVal = raipn.getValue();
141
142 String message =
143 format(
144 " Casted function:\n%s\nfunction "
145 + "result %9.3g, casted result %9.3g, from points %9.3g, "
146 + "difference to casted %9.3g, difference to from-points %9.3g"
147 + "\nintegration method %s",
148 pn.toString(),
149 pnVal,
150 dpnVal,
151 aipnVal,
152 (pnVal - dpnVal),
153 (pnVal - aipnVal),
154 rpn.toString());
155 assertToUlp(message, pnVal, 10.0, dpnVal, aipnVal);
156 }
157 }
158 logger.info(" Polynomial casting tests without halved sides complete.");
159
160 double[] points2 = Integrate1DNumeric.generateXPoints(0.0, 1.0, 12, true);
161 polynomials =
162 Arrays.stream(coeffs)
163 .map(
164 (double[] coeff) -> {
165 return new PolynomialCurve(points2, true, coeff);
166 })
167 .collect(Collectors.toList());
168
169 for (FunctionDataCurve pn : polynomials) {
170 pn.assertXIntegrity();
171 DataSet dpn = new DoublesDataSet(pn);
172 double[] ypts = pn.getAllFxPoints();
173 DataSet abInitio = new DoublesDataSet(points2, ypts, true);
174
175 for (int j = 0; j < NUM_INTEGRATION_TYPES; j++) {
176 IntegrationResult rpn = new IntegrationResult(pn, j);
177 IntegrationResult rdpn = new IntegrationResult(dpn, j);
178 IntegrationResult raipn = new IntegrationResult(abInitio, j);
179
180 double pnVal = rpn.getValue();
181 double dpnVal = rdpn.getValue();
182 double aipnVal = raipn.getValue();
183
184 String message =
185 format(
186 " Casted function:\n%s\nfunction "
187 + "result %9.3g, casted result %9.3g, from points %9.3g, "
188 + "difference to casted %9.3g, difference to from-points %9.3g"
189 + "\nintegration method %s",
190 pn.toString(),
191 pnVal,
192 dpnVal,
193 aipnVal,
194 (pnVal - dpnVal),
195 (pnVal - aipnVal),
196 rpn.toString());
197 assertToUlp(message, pnVal, 10.0, dpnVal, aipnVal);
198 }
199 }
200 logger.info(" Polynomial casting tests with halved sides complete.");
201
202 double[] sinePoints = Integrate1DNumeric.generateXPoints(0, 4.0, 101, false);
203 FunctionDataCurve sinWave = new SinWave(sinePoints, false, 20, 30);
204 sinWave.assertXIntegrity();
205 DataSet dsw = new DoublesDataSet(sinWave);
206 double[] ypts = sinWave.getAllFxPoints();
207 DataSet abInitio = new DoublesDataSet(sinePoints, ypts, false);
208
209 for (int j = 0; j < NUM_INTEGRATION_TYPES; j++) {
210 IntegrationResult rsw = new IntegrationResult(sinWave, j);
211 IntegrationResult rdsw = new IntegrationResult(dsw, j);
212 IntegrationResult raisw = new IntegrationResult(abInitio, j);
213
214 double swVal = rsw.getValue();
215 double dswVal = rdsw.getValue();
216 double aiswVal = raisw.getValue();
217
218 String message =
219 format(
220 " Casted function:\n%s\nfunction "
221 + "result %9.3g, casted result %9.3g, from points %9.3g, "
222 + "difference to casted %9.3g, difference to from-points %9.3g"
223 + "\nintegration method %s",
224 sinWave.toString(),
225 swVal,
226 dswVal,
227 aiswVal,
228 (swVal - dswVal),
229 (swVal - aiswVal),
230 rsw.toString());
231 assertToUlp(message, swVal, 10.0, dswVal, aiswVal);
232 }
233 logger.info(" Sine-wave casting tests with halved sides complete.");
234
235 double[] sinePoints2 = Integrate1DNumeric.generateXPoints(0, 4.0, 102, true);
236 sinWave = new SinWave(sinePoints2, true, 20, 30);
237 sinWave.assertXIntegrity();
238 dsw = new DoublesDataSet(sinWave);
239 ypts = sinWave.getAllFxPoints();
240 abInitio = new DoublesDataSet(sinePoints2, ypts, true);
241
242 for (int j = 0; j < NUM_INTEGRATION_TYPES; j++) {
243 IntegrationResult rsw = new IntegrationResult(sinWave, j);
244 IntegrationResult rdsw = new IntegrationResult(dsw, j);
245 IntegrationResult raisw = new IntegrationResult(abInitio, j);
246
247 double swVal = rsw.getValue();
248 double dswVal = rdsw.getValue();
249 double aiswVal = raisw.getValue();
250
251 String message =
252 format(
253 " Casted function:\n%s\nfunction "
254 + "result %9.3g, casted result %9.3g, from points %9.3g, "
255 + "difference to casted %9.3g, difference to from-points %9.3g"
256 + "\nintegration method %s",
257 sinWave.toString(),
258 swVal,
259 dswVal,
260 aiswVal,
261 (swVal - dswVal),
262 (swVal - aiswVal),
263 rsw.toString());
264 assertToUlp(message, swVal, 10.0, dswVal, aiswVal);
265 }
266 logger.info(" Sine-wave casting tests with halved sides complete.");
267 logger.info(" Casting tests complete.");
268 }
269
270
271
272
273
274
275
276 @Test
277 public void parallelTest() {
278 double[] pts = Integrate1DNumeric.generateXPoints(0, 2.0, 92, false);
279 FunctionDataCurve tcurve = new SinWave(pts, false, 60, 60);
280 tcurve.assertXIntegrity();
281 for (int i = 0; i < NUM_INTEGRATION_TYPES; i++) {
282 IntegrationResult seqResult = new IntegrationResult(tcurve, i, false);
283 IntegrationResult parResult = new IntegrationResult(tcurve, i, true);
284 double seqVal = seqResult.getValue();
285 double parVal = parResult.getValue();
286 String message =
287 format(
288 "Parallel value %9.3g did not match sequential value %9.3g, error %9.3g",
289 parVal, seqVal, (parVal - seqVal));
290 assertToUlp(message, seqVal, 80.0, parVal);
291 }
292 }
293
294
295
296
297
298 @Test
299 public void polynomialGrinderTest() {
300 logger.info(" Testing integration methods on polynomials with large coefficients.");
301 double[] points = Integrate1DNumeric.generateXPoints(0.0, 2.0, 5, false);
302
303 double[] zeroOrder = {-2.0};
304 double[] firstOrder = {6.0, -14.0};
305 double[] secondOrder = {4.5, -5.0, 3.0};
306 double[] thirdOrder = {-3.0, 5.0, -2.0, 12.0};
307 double[] fourthOrder = {12, -4.5, 8.0, -5.0, 4.0};
308 double[] fifthOrder = {-4.0, 10.0, -18.0, 8.0, -5.0, 8.0};
309
310 double[][] coeffs = {zeroOrder, firstOrder, secondOrder, thirdOrder, fourthOrder, fifthOrder};
311
312 List<FunctionDataCurve> polynomials =
313 Arrays.stream(coeffs)
314 .map(
315 (double[] coeff) -> {
316 return new PolynomialCurve(points, false, coeff);
317 })
318 .collect(Collectors.toList());
319
320 for (int i = 0; i < polynomials.size(); i++) {
321 FunctionDataCurve pn = polynomials.get(i);
322 pn.assertXIntegrity();
323
324
325 double analytical = pn.analyticalIntegral();
326
327 double rLeft = Integrate1DNumeric.rectangular(pn, LEFT);
328 double rRight = Integrate1DNumeric.rectangular(pn, RIGHT);
329
330 double tLeft = Integrate1DNumeric.trapezoidal(pn, LEFT);
331 double tRight = Integrate1DNumeric.trapezoidal(pn, RIGHT);
332
333 double sLeft = Integrate1DNumeric.simpsons(pn, LEFT);
334 double sRight = Integrate1DNumeric.simpsons(pn, RIGHT);
335
336 double bLeft = Integrate1DNumeric.booles(pn, LEFT);
337 double bRight = Integrate1DNumeric.booles(pn, RIGHT);
338
339 StringBuilder sb = new StringBuilder();
340
341 sb.append(format(" Integrals for polynomial of degree %d\n", i));
342 sb.append(format(" %-18s %9.3g\n", "Analytical", analytical));
343 sb.append(" Numerical integration errors:\n");
344 sb.append(
345 format(
346 " %-18s %9.3g %-18s %9.3g\n",
347 "Left rectangular", rLeft - analytical, "Right rectangular", rRight - analytical));
348 sb.append(
349 format(
350 " %-18s %9.3g %-18s %9.3g\n",
351 "Left trapezoidal", tLeft - analytical, "Right trapezoidal", tRight - analytical));
352 sb.append(
353 format(
354 " %-18s %9.3g %-18s %9.3g\n",
355 "Left Simpson's", sLeft - analytical, "Right Simpson's", sRight - analytical));
356 sb.append(
357 format(
358 " %-18s %9.3g %-18s %9.3g\n",
359 "Left Boole's", bLeft - analytical, "Right Boole's", bRight - analytical));
360 sb.append("\n");
361
362 logger.info(sb.toString());
363
364 if (i == 0) {
365 assertToUlp(analytical, 500.0, rLeft, rRight);
366 }
367 if (i <= 1) {
368 assertToUlp(analytical, 500.0, tLeft, tRight);
369 }
370 if (i <= 2) {
371 assertToUlp(analytical, 500.0, sLeft, sRight);
372 }
373 if (i <= 4) {
374 assertToUlp(analytical, 500.0, bLeft, bRight);
375 }
376 }
377 }
378
379
380
381
382
383
384
385 @Test
386 public void polynomialTest() {
387 logger.info(" Testing integration methods on polynomials with known integrals.");
388
389
390
391
392
393
394
395
396
397
398
399
400 double[] points = Integrate1DNumeric.generateXPoints(0.0, 1.0, 9, false);
401
402
403
404
405
406 double[] zeroOrder = {1.0};
407 double[] firstOrder = {2.0, 1.0};
408 double[] secondOrder = {1.5, -4.0, 1.0};
409 double[] thirdOrder = {2.0, -1.0, -3.0, 1.0};
410 double[] fourthOrder = {1, -4.0, -6.0, 4.0, 1.0};
411 double[] fifthOrder = {
412 2.0, 10.0, -18.0, 8.0, -5.0, 1.0
413 };
414
415
416
417
418
419 double[] trueVals = {1.0, 2.5, (-1.0 / 6.0), 0.75, -1.8, (13.0 / 6.0)};
420
421
422 double[][] coeffs = {zeroOrder, firstOrder, secondOrder, thirdOrder, fourthOrder, fifthOrder};
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 List<FunctionDataCurve> polynomials =
467 Arrays.stream(coeffs)
468 .map(
469 (double[] coeff) -> {
470 return new PolynomialCurve(points, false, coeff);
471 })
472 .collect(Collectors.toList());
473
474 for (int i = 0; i < polynomials.size(); i++) {
475 FunctionDataCurve pn = polynomials.get(i);
476 pn.assertXIntegrity();
477
478
479 double trueVal = trueVals[i];
480 double analytical = pn.analyticalIntegral();
481
482 double rLeft = Integrate1DNumeric.rectangular(pn, LEFT);
483 double rRight = Integrate1DNumeric.rectangular(pn, RIGHT);
484
485 double tLeft = Integrate1DNumeric.trapezoidal(pn, LEFT);
486 double tRight = Integrate1DNumeric.trapezoidal(pn, RIGHT);
487
488 double sLeft = Integrate1DNumeric.simpsons(pn, LEFT);
489 double sRight = Integrate1DNumeric.simpsons(pn, RIGHT);
490
491 double bLeft = Integrate1DNumeric.booles(pn, LEFT);
492 double bRight = Integrate1DNumeric.booles(pn, RIGHT);
493
494 StringBuilder sb = new StringBuilder();
495
496 sb.append(format(" Integrals for polynomial of degree %d\n", i));
497 sb.append(format(" %-18s %9.3g %-18s %9.3g\n", "Exact", trueVal, "Analytical", analytical));
498 sb.append(" Numerical integration errors:\n");
499 sb.append(
500 format(
501 " %-18s %9.3g %-18s %9.3g\n",
502 "Left rectangular", rLeft - trueVal, "Right rectangular", rRight - trueVal));
503 sb.append(
504 format(
505 " %-18s %9.3g %-18s %9.3g\n",
506 "Left trapezoidal", tLeft - trueVal, "Right trapezoidal", tRight - trueVal));
507 sb.append(
508 format(
509 " %-18s %9.3g %-18s %9.3g\n",
510 "Left Simpson's", sLeft - trueVal, "Right Simpson's", sRight - trueVal));
511 sb.append(
512 format(
513 " %-18s %9.3g %-18s %9.3g\n",
514 "Left Boole's", bLeft - trueVal, "Right Boole's", bRight - trueVal));
515 sb.append("\n");
516 logger.info(sb.toString());
517
518 assertToUlp(trueVal, 10.0, analytical);
519 if (i == 0) {
520 assertToUlp(trueVal, 500.0, rLeft, rRight);
521 }
522 if (i <= 1) {
523 assertToUlp(trueVal, 500.0, tLeft, tRight);
524 }
525 if (i <= 2) {
526 assertToUlp(trueVal, 500.0, sLeft, sRight);
527 }
528 if (i <= 4) {
529 assertToUlp(trueVal, 500.0, bLeft, bRight);
530 }
531 }
532 }
533
534
535 @Test
536 public void testByBinIntegral() {
537 double[] pts = Integrate1DNumeric.generateXPoints(0, 1.0, 18, false);
538 FunctionDataCurve curve = new SinWave(pts, 1, 20.0);
539 curve.assertXIntegrity();
540
541 logger.info(" Testing by-bin integration with a sin wave (sum vs. overall result).");
542 for (int i = 0; i < NUM_INTEGRATION_TYPES; i++) {
543 IntegrationResult knownResult = new IntegrationResult(curve, i, false);
544 double[] sequentialPts =
545 Integrate1DNumeric.integrateByBins(curve, knownResult.getSide(), knownResult.getType());
546 double seqResult = Arrays.stream(sequentialPts).sum();
547
548 double knownVal = knownResult.getValue();
549 assertToUlp(
550 format(" Mismatch for result %s\n ", knownResult, knownVal, seqResult),
551 knownVal,
552 200.0,
553 seqResult);
554 }
555 logger.info(" Testing match to analytical integrals.\n");
556 double[] leftPts = Integrate1DNumeric.integrateByBins(curve, LEFT, IntegrationType.BOOLE);
557 double[] rightPts = Integrate1DNumeric.integrateByBins(curve, RIGHT, IntegrationType.BOOLE);
558 double maxDelta = 0.075;
559 for (int i = 0; i < pts.length; i++) {
560 double leftAnalytic = curve.analyticalIntegral(pts[0], pts[i]);
561 if (i > 0) {
562 leftAnalytic -= curve.analyticalIntegral(pts[0], pts[i - 1]);
563 }
564 double rightAnalytic = curve.analyticalIntegral(pts[i], pts[pts.length - 1]);
565 if (i < (pts.length - 1)) {
566 rightAnalytic -= curve.analyticalIntegral(pts[i + 1], pts[pts.length - 1]);
567 }
568
569 assertEquals(
570 format(
571 " Contribution of bin %d to left-hand integral of %s does not match analytic solution",
572 i, curve),
573 leftAnalytic,
574 leftPts[i],
575 maxDelta);
576 assertEquals(
577 format(
578 " Contribution of bin %d to right-hand integral of %s does not match analytic solution",
579 i, curve),
580 rightAnalytic,
581 rightPts[i],
582 maxDelta);
583 }
584
585 logger.info(" Testing by-bin integration with a higher-frequency wave.");
586 curve = new CosineWave(pts, 1, 120.0);
587 curve.assertXIntegrity();
588 for (int i = 0; i < NUM_INTEGRATION_TYPES; i++) {
589 IntegrationResult knownResult = new IntegrationResult(curve, i, false);
590 double[] sequentialPts =
591 Integrate1DNumeric.integrateByBins(curve, knownResult.getSide(), knownResult.getType());
592 double seqResult = Arrays.stream(sequentialPts).sum();
593
594 double knownVal = knownResult.getValue();
595 assertToUlp(
596 format(" Mismatch for result %s\n ", knownResult, knownVal, seqResult),
597 knownVal,
598 200.0,
599 seqResult);
600 }
601 logger.info(" Testing match to analytical integrals.\n");
602 leftPts = Integrate1DNumeric.integrateByBins(curve, LEFT, IntegrationType.BOOLE);
603 rightPts = Integrate1DNumeric.integrateByBins(curve, RIGHT, IntegrationType.BOOLE);
604 maxDelta = 0.075;
605 for (int i = 0; i < pts.length; i++) {
606 double leftAnalytic = curve.analyticalIntegral(pts[0], pts[i]);
607 if (i > 0) {
608 leftAnalytic -= curve.analyticalIntegral(pts[0], pts[i - 1]);
609 }
610 double rightAnalytic = curve.analyticalIntegral(pts[i], pts[pts.length - 1]);
611 if (i < (pts.length - 1)) {
612 rightAnalytic -= curve.analyticalIntegral(pts[i + 1], pts[pts.length - 1]);
613 }
614
615 assertEquals(
616 format(
617 " Contribution of bin %d to left-hand integral of %s does not match analytic solution",
618 i, curve),
619 leftAnalytic,
620 leftPts[i],
621 maxDelta);
622 assertEquals(
623 format(
624 " Contribution of bin %d to right-hand integral of %s does not match analytic solution",
625 i, curve),
626 rightAnalytic,
627 rightPts[i],
628 maxDelta);
629 }
630
631 logger.info(
632 " Testing by-bin integration with a higher-frequency wave with half-width ending bins.");
633 pts = Integrate1DNumeric.generateXPoints(0, 1.0, 19, true);
634 curve = new CosineWave(pts, true, 1, 120.0);
635 for (int i = 0; i < NUM_INTEGRATION_TYPES; i++) {
636 IntegrationResult knownResult = new IntegrationResult(curve, i, false);
637 double[] sequentialPts =
638 Integrate1DNumeric.integrateByBins(curve, knownResult.getSide(), knownResult.getType());
639 double seqResult = Arrays.stream(sequentialPts).sum();
640
641 double knownVal = knownResult.getValue();
642 assertToUlp(
643 format(
644 " Mismatch for result %s\n Points %s\n ",
645 knownResult, Arrays.toString(sequentialPts), knownVal, seqResult),
646 knownVal,
647 200.0,
648 seqResult);
649 }
650 logger.info(" Testing match to analytical integrals.\n");
651 leftPts = Integrate1DNumeric.integrateByBins(curve, LEFT, IntegrationType.BOOLE);
652 rightPts = Integrate1DNumeric.integrateByBins(curve, RIGHT, IntegrationType.BOOLE);
653 maxDelta = 0.1;
654 for (int i = 0; i < pts.length; i++) {
655 double leftAnalytic = curve.analyticalIntegral(pts[0], pts[i]);
656 if (i > 0) {
657 leftAnalytic -= curve.analyticalIntegral(pts[0], pts[i - 1]);
658 }
659 double rightAnalytic = curve.analyticalIntegral(pts[i], pts[pts.length - 1]);
660 if (i < (pts.length - 1)) {
661 rightAnalytic -= curve.analyticalIntegral(pts[i + 1], pts[pts.length - 1]);
662 }
663
664 assertEquals(
665 format(
666 " Contribution of bin %d to left-hand integral of %s does not match analytic solution",
667 i, curve),
668 leftAnalytic,
669 leftPts[i],
670 maxDelta);
671 assertEquals(
672 format(
673 " Contribution of bin %d to right-hand integral of %s does not match analytic solution",
674 i, curve),
675 rightAnalytic,
676 rightPts[i],
677 maxDelta);
678 }
679
680 logger.info(" Testing by-bin integration with a polynomial function.");
681 pts = Integrate1DNumeric.generateXPoints(0.0, 2.0, 5, false);
682 double[] fifthOrder = {-4.0, 10.0, -18.0, 8.0, -5.0, 8.0};
683 curve = new PolynomialCurve(pts, false, fifthOrder);
684 curve.assertXIntegrity();
685
686 for (int i = 0; i < NUM_INTEGRATION_TYPES; i++) {
687 IntegrationResult knownResult = new IntegrationResult(curve, i, false);
688 double[] sequentialPts =
689 Integrate1DNumeric.integrateByBins(curve, knownResult.getSide(), knownResult.getType());
690 double seqResult = Arrays.stream(sequentialPts).sum();
691
692 double knownVal = knownResult.getValue();
693 assertToUlp(
694 format(" Mismatch for result %s\n ", knownResult, knownVal, seqResult),
695 knownVal,
696 200.0,
697 seqResult);
698 }
699
700
701
702 logger.info(" Completed testing of by-bin integration\n");
703 }
704
705 @Test
706 public void testSinCosine() {
707 double[] points = Integrate1DNumeric.generateXPoints(0, 1, 201, false);
708 boolean verb = false;
709
710 logger.info("\n Sin wave test without half-width bins");
711 sinTest(points, false, false, verb);
712 logger.info("\n Cosine wave test without half-width bins");
713 sinTest(points, false, true, verb);
714
715 points = Integrate1DNumeric.generateXPoints(0, 1, 202, true);
716 logger.info("\n Sin wave test with half-width bins");
717 sinTest(points, true, false, verb);
718 logger.info("\n Cosine wave test with half-width bins");
719 sinTest(points, true, true, verb);
720 logger.info("\n");
721 }
722
723
724
725
726
727
728
729
730
731 private void sinTest(double[] points, boolean halvedEnds, boolean cosine, boolean verbose) {
732 int failRleft = 0;
733 int failRright = 0;
734 int failTleft = 0;
735 int failTright = 0;
736 int failSleft = 0;
737 int failSright = 0;
738 int failBleft = 0;
739 int failBright = 0;
740 double maxDelta = 0.05;
741
742 for (int j = 1; j <= 500; j++) {
743 FunctionDataCurve wave =
744 cosine ? new CosineWave(points, halvedEnds, j, j) : new SinWave(points, halvedEnds, j, j);
745 wave.assertXIntegrity();
746
747
748 double analytical = wave.analyticalIntegral();
749
750 double rLeft = Integrate1DNumeric.rectangular(wave, LEFT);
751 double erLeft = analytical - rLeft;
752 double rRight = Integrate1DNumeric.rectangular(wave, RIGHT);
753 double erRight = analytical - rRight;
754
755 double tLeft = Integrate1DNumeric.trapezoidal(wave, LEFT);
756 double etLeft = analytical - tLeft;
757 double tRight = Integrate1DNumeric.trapezoidal(wave, RIGHT);
758 double etRight = analytical - tRight;
759
760 double sLeft = Integrate1DNumeric.simpsons(wave, LEFT);
761 double esLeft = analytical - sLeft;
762 double sRight = Integrate1DNumeric.simpsons(wave, RIGHT);
763 double esRight = analytical - sRight;
764
765 double bLeft = Integrate1DNumeric.booles(wave, LEFT);
766 double ebLeft = analytical - bLeft;
767 double bRight = Integrate1DNumeric.booles(wave, RIGHT);
768 double ebRight = analytical - bRight;
769 if (verbose) {
770 StringBuilder sb = new StringBuilder();
771
772 sb.append(format(" Integrals for sine wave j*sin(j) of j %d\n", j));
773 sb.append(format(" %-18s %9.3g\n", "Analytical", analytical));
774 sb.append(" Numerical integration errors:\n");
775 sb.append(
776 format(
777 " %-18s %9.3g %-18s %9.3g\n",
778 "Left rectangular", erLeft, "Right rectangular", erRight));
779 sb.append(
780 format(
781 " %-18s %9.3g %-18s %9.3g\n",
782 "Left trapezoidal", etLeft, "Right trapezoidal", etRight));
783 sb.append(
784 format(
785 " %-18s %9.3g %-18s %9.3g\n",
786 "Left Simpson's", esLeft, "Right Simpson's", esRight));
787 sb.append(
788 format(
789 " %-18s %9.3g %-18s %9.3g\n", "Left Boole's", ebLeft, "Right Boole's", ebRight));
790 sb.append("\n");
791
792 logger.info(sb.toString());
793 }
794
795 if (failRleft == 0 && Math.abs(erLeft) > maxDelta) {
796 failRleft = j;
797 }
798 if (failRright == 0 && Math.abs(erRight) > maxDelta) {
799 failRright = j;
800 }
801
802 if (failTleft == 0 && Math.abs(etLeft) > maxDelta) {
803 failTleft = j;
804 }
805 if (failTright == 0 && Math.abs(etRight) > maxDelta) {
806 failTright = j;
807 }
808
809 if (failSleft == 0 && Math.abs(esLeft) > maxDelta) {
810 failSleft = j;
811 }
812 if (failSright == 0 && Math.abs(esRight) > maxDelta) {
813 failSright = j;
814 }
815
816 if (failBleft == 0 && Math.abs(ebLeft) > maxDelta) {
817 failBleft = j;
818 }
819 if (failBright == 0 && Math.abs(ebRight) > maxDelta) {
820 failBright = j;
821 }
822 }
823
824 StringBuilder sb1 =
825 new StringBuilder(format(" Error exceeded delta %8.3f at iterations:\n", maxDelta));
826 sb1.append(
827 format(
828 " %-18s %9d %-18s %9d\n",
829 "Left rectangular", failRleft, "Right rectangular", failRright));
830 sb1.append(
831 format(
832 " %-18s %9d %-18s %9d\n",
833 "Left trapezoidal", failTleft, "Right trapezoidal", failTright));
834 sb1.append(
835 format(
836 " %-18s %9d %-18s %9d\n", "Left Simpson's", failSleft, "Right Simpson's", failSright));
837 sb1.append(
838 format(" %-18s %9d %-18s %9d\n", "Left Boole's", failBleft, "Right Boole's", failBright));
839 logger.info(sb1.toString());
840
841 StringBuilder sb = new StringBuilder(" integration failed for ");
842 if (halvedEnds) {
843 if (cosine) {
844 sb.append("cosine wave, with half-width end bins, of frequency ");
845 } else {
846 sb.append("sine wave, with half-width end bins, of frequency ");
847 }
848 String midMessage = sb.toString();
849 assertTrue(format("Rectangular%s%d", midMessage, 12), failRleft > 12 && failRright > 12);
850 assertTrue(format("Trapezoidal%s%d", midMessage, 100), failTleft > 100 && failTright > 100);
851 assertTrue(format("Simpson's%s%d", midMessage, 150), failSleft > 150 && failSright > 150);
852 assertTrue(format("Boole's%s%d", midMessage, 150), failBleft > 150 && failBright > 150);
853
854 } else {
855 if (cosine) {
856 sb.append("cosine wave of frequency ");
857 } else {
858 sb.append("sine wave of frequency ");
859 }
860 String midMessage = sb.toString();
861 assertTrue(format("Rectangular%s%d", midMessage, 12), failRleft > 12 && failRright > 12);
862 assertTrue(format("Trapezoidal%s%d", midMessage, 100), failTleft > 100 && failTright > 100);
863 assertTrue(format("Simpson's%s%d", midMessage, 250), failSleft > 250 && failSright > 250);
864 assertTrue(format("Boole's%s%d", midMessage, 240), failBleft > 240 && failBright > 240);
865 }
866 }
867
868
869
870
871
872
873 private class IntegrationResult {
874 private final IntegrationType type;
875 private final IntegrationSide side;
876 private final boolean halvedSides;
877 private final DataSet data;
878 private final double integratedValue;
879 private final boolean parallel;
880
881
882
883
884
885
886
887
888
889
890
891
892
893 public IntegrationResult(DataSet data, int index) {
894 this(data, index, false);
895 }
896
897 public IntegrationResult(DataSet data, int index, boolean parallel) {
898 switch ((index / 2) % 4) {
899 case 0:
900 type = IntegrationType.RECTANGULAR;
901 break;
902 case 1:
903 type = IntegrationType.TRAPEZOIDAL;
904 break;
905 case 2:
906 type = IntegrationType.SIMPSONS;
907 break;
908 case 3:
909 type = IntegrationType.BOOLE;
910 break;
911 default:
912 throw new IllegalArgumentException(
913 " How did ((index / 2) % 4) ever come out to not be 0-3?");
914 }
915 side = (index % 2 == 0) ? LEFT : RIGHT;
916 halvedSides = data.halfWidthEnds();
917 this.data = data;
918 this.parallel = parallel;
919
920 if (parallel) {
921 switch (type) {
922 case RECTANGULAR:
923 integratedValue = Integrate1DNumeric.rectangularParallel(data, side);
924 break;
925 case TRAPEZOIDAL:
926 integratedValue = Integrate1DNumeric.trapezoidalParallel(data, side);
927 break;
928 case SIMPSONS:
929 integratedValue = Integrate1DNumeric.simpsonsParallel(data, side);
930 break;
931 case BOOLE:
932 integratedValue = Integrate1DNumeric.boolesParallel(data, side);
933 break;
934 default:
935 throw new IllegalArgumentException(
936 " How did this end up with an integration type not rectangular, trapezoidal, Simpson's, or Boole's?");
937 }
938
939 } else {
940 switch (type) {
941 case RECTANGULAR:
942 integratedValue = Integrate1DNumeric.rectangular(data, side);
943 break;
944 case TRAPEZOIDAL:
945 integratedValue = Integrate1DNumeric.trapezoidal(data, side);
946 break;
947 case SIMPSONS:
948 integratedValue = Integrate1DNumeric.simpsons(data, side);
949 break;
950 case BOOLE:
951 integratedValue = Integrate1DNumeric.booles(data, side);
952 break;
953 default:
954 throw new IllegalArgumentException(
955 " How did this end up with an integration type not rectangular, trapezoidal, Simpson's, or Boole's?");
956 }
957 }
958 }
959
960 public DataSet getDataSet() {
961 return data;
962 }
963
964 public boolean getHalvedSides() {
965 return halvedSides;
966 }
967
968 public IntegrationSide getSide() {
969 return side;
970 }
971
972 public IntegrationType getType() {
973 return type;
974 }
975
976 public double getValue() {
977 return integratedValue;
978 }
979
980 public boolean isParallel() {
981 return parallel;
982 }
983
984 @Override
985 public String toString() {
986 StringBuilder sb = new StringBuilder(type.toString().toLowerCase());
987 sb.append(", ").append(side.toString().toLowerCase());
988 sb.append("-side integral");
989 if (halvedSides) {
990 sb.append(" with half-width ending bins");
991 }
992 sb.append(".");
993 return sb.toString();
994 }
995 }
996 }