4#define DOCTEST_CONFIG_IMPLEMENT
6#include "doctest_utils.h"
11 return helios::runDoctestWithValidation(argc, argv);
14DOCTEST_TEST_CASE(
"RadiationModel 90 Degree Common-Edge Squares") {
15 float error_threshold = 0.005;
18 uint Ndiffuse_1 = 100000;
19 uint Ndirect_1 = 5000;
22 float sigma = 5.6703744E-8;
24 float shortwave_exact_0 = 0.7f * Qs;
25 float shortwave_exact_1 = 0.3f * 0.2f * Qs;
26 float longwave_exact_0 = 0.f;
27 float longwave_exact_1 = sigma * powf(300.f, 4) * 0.2f;
40 float shortwave_rho = 0.3f;
44 radiationmodel_1.disableMessages();
47 radiationmodel_1.addRadiationBand(
"LW");
48 radiationmodel_1.setDirectRayCount(
"LW", Ndiffuse_1);
49 radiationmodel_1.setDiffuseRayCount(
"LW", Ndiffuse_1);
50 radiationmodel_1.setScatteringDepth(
"LW", 0);
53 uint SunSource_1 = radiationmodel_1.addCollimatedRadiationSource(
make_vec3(0, 0, 1));
54 radiationmodel_1.addRadiationBand(
"SW");
55 radiationmodel_1.disableEmission(
"SW");
56 radiationmodel_1.setDirectRayCount(
"SW", Ndirect_1);
57 radiationmodel_1.setDiffuseRayCount(
"SW", Ndirect_1);
58 radiationmodel_1.setScatteringDepth(
"SW", 1);
59 radiationmodel_1.setSourceFlux(SunSource_1,
"SW", Qs);
61 radiationmodel_1.updateGeometry();
63 float longwave_model_0 = 0.f;
64 float longwave_model_1 = 0.f;
65 float shortwave_model_0 = 0.f;
66 float shortwave_model_1 = 0.f;
69 for (
int r = 0; r < Nensemble; r++) {
70 std::vector<std::string> bands{
"LW",
"SW"};
71 radiationmodel_1.runBand(bands);
75 longwave_model_0 +=
R / float(Nensemble);
78 longwave_model_1 +=
R / float(Nensemble);
82 shortwave_model_0 +=
R / float(Nensemble);
85 shortwave_model_1 +=
R / float(Nensemble);
88 float shortwave_error_0 = fabsf(shortwave_model_0 - shortwave_exact_0) / fabsf(shortwave_exact_0);
89 float shortwave_error_1 = fabsf(shortwave_model_1 - shortwave_exact_1) / fabsf(shortwave_exact_1);
90 float longwave_error_1 = fabsf(longwave_model_1 - longwave_exact_1) / fabsf(longwave_exact_1);
92 DOCTEST_CHECK(shortwave_error_0 <= error_threshold);
93 DOCTEST_CHECK(shortwave_error_1 <= error_threshold);
95 DOCTEST_CHECK(longwave_model_0 == longwave_exact_0);
96 DOCTEST_CHECK(longwave_error_1 <= error_threshold);
99DOCTEST_TEST_CASE(
"RadiationModel Black Parallel Rectangles") {
100 float error_threshold = 0.005;
103 uint Ndiffuse_2 = 50000;
115 2.0f / float(
M_PI * X * Y) * (logf(std::sqrt((1.f + X2) * (1.f + Y2) / (1.f + X2 + Y2))) + X * std::sqrt(1.f + Y2) * atanf(X / std::sqrt(1.f + Y2)) + Y * std::sqrt(1.f + X2) * atanf(Y / std::sqrt(1.f + X2)) - X * atanf(X) - Y * atanf(Y));
117 float shortwave_exact_0 = (1.f - F12);
118 float shortwave_exact_1 = (1.f - F12);
129 radiationmodel_2.disableMessages();
132 radiationmodel_2.addRadiationBand(
"SW");
133 radiationmodel_2.disableEmission(
"SW");
134 radiationmodel_2.setDiffuseRayCount(
"SW", Ndiffuse_2);
135 radiationmodel_2.setDiffuseRadiationFlux(
"SW", 1.f);
136 radiationmodel_2.setScatteringDepth(
"SW", 0);
138 radiationmodel_2.updateGeometry();
140 float shortwave_model_0 = 0.f;
141 float shortwave_model_1 = 0.f;
144 for (
int r = 0; r < Nensemble; r++) {
145 radiationmodel_2.runBand(
"SW");
148 shortwave_model_0 +=
R / float(Nensemble);
150 shortwave_model_1 +=
R / float(Nensemble);
153 float shortwave_error_0 = fabsf(shortwave_model_0 - shortwave_exact_0) / fabsf(shortwave_exact_0);
154 float shortwave_error_1 = fabsf(shortwave_model_1 - shortwave_exact_1) / fabsf(shortwave_exact_1);
156 DOCTEST_CHECK(shortwave_error_0 <= error_threshold);
157 DOCTEST_CHECK(shortwave_error_1 <= error_threshold);
160DOCTEST_TEST_CASE(
"RadiationModel Gray Parallel Rectangles") {
161 float error_threshold = 0.005;
163 float sigma = 5.6703744E-8;
165 uint Ndiffuse_3 = 100000;
168 float longwave_rho = 0.4;
184 2.0f / float(
M_PI * X * Y) * (logf(std::sqrt((1.f + X2) * (1.f + Y2) / (1.f + X2 + Y2))) + X * std::sqrt(1.f + Y2) * atanf(X / std::sqrt(1.f + Y2)) + Y * std::sqrt(1.f + X2) * atanf(Y / std::sqrt(1.f + X2)) - X * atanf(X) - Y * atanf(Y));
186 float longwave_exact_0 = (eps * (1.f / eps - 1.f) * F12 * sigma * (powf(T1, 4) - F12 * powf(T0, 4)) + sigma * (powf(T0, 4) - F12 * powf(T1, 4))) / (1.f / eps - (1.f / eps - 1.f) * F12 * eps * (1 / eps - 1) * F12) - eps * sigma * powf(T0, 4);
187 float longwave_exact_1 = fabsf(eps * ((1 / eps - 1) * F12 * (longwave_exact_0 + eps * sigma * powf(T0, 4)) + sigma * (powf(T1, 4) - F12 * powf(T0, 4))) - eps * sigma * powf(T1, 4));
188 longwave_exact_0 = fabsf(longwave_exact_0);
207 radiationmodel_3.disableMessages();
210 radiationmodel_3.addRadiationBand(
"LW");
211 radiationmodel_3.setDirectRayCount(
"LW", Ndiffuse_3);
212 radiationmodel_3.setDiffuseRayCount(
"LW", Ndiffuse_3);
213 radiationmodel_3.setDiffuseRadiationFlux(
"LW", 0.f);
214 radiationmodel_3.setScatteringDepth(
"LW", Nscatter_3);
216 radiationmodel_3.updateGeometry();
218 float longwave_model_0 = 0.f;
219 float longwave_model_1 = 0.f;
222 for (
int r = 0; r < Nensemble; r++) {
223 radiationmodel_3.runBand(
"LW");
226 longwave_model_0 +=
R / float(Nensemble);
228 longwave_model_1 +=
R / float(Nensemble);
231 float longwave_error_0 = fabsf(longwave_exact_0 - longwave_model_0) / fabsf(longwave_exact_0);
232 float longwave_error_1 = fabsf(longwave_exact_1 - longwave_model_1) / fabsf(longwave_exact_1);
234 DOCTEST_CHECK(longwave_error_0 <= error_threshold);
235 DOCTEST_CHECK(longwave_error_1 <= error_threshold);
238DOCTEST_TEST_CASE(
"RadiationModel Sphere Source") {
239 float error_threshold = 0.005;
242 uint Ndirect_4 = 10000;
252 float F12 = 0.25f / float(
M_PI) * atanf(sqrtf(1.f / (D1 * D1 + D2 * D2 + D1 * D1 * D2 * D2)));
254 float shortwave_exact_0 = 4.0f * float(
M_PI) * r * r * F12 / (l1 * l2);
260 radiationmodel_4.disableMessages();
262 uint Source_4 = radiationmodel_4.addSphereRadiationSource(
make_vec3(0, 0, d), r);
265 radiationmodel_4.addRadiationBand(
"SW");
266 radiationmodel_4.disableEmission(
"SW");
267 radiationmodel_4.setDirectRayCount(
"SW", Ndirect_4);
268 radiationmodel_4.setSourceFlux(Source_4,
"SW", 1.f);
269 radiationmodel_4.setScatteringDepth(
"SW", 0);
271 radiationmodel_4.updateGeometry();
273 float shortwave_model_0 = 0.f;
276 for (
int i = 0; i < Nensemble; i++) {
277 radiationmodel_4.runBand(
"SW");
280 shortwave_model_0 +=
R / float(Nensemble);
283 float shortwave_error_0 = fabsf(shortwave_exact_0 - shortwave_model_0) / fabsf(shortwave_exact_0);
285 DOCTEST_CHECK(shortwave_error_0 <= error_threshold);
288DOCTEST_TEST_CASE(
"RadiationModel 90 Degree Common-Edge Sub-Triangles") {
289 float error_threshold = 0.005;
291 float sigma = 5.6703744E-8;
295 uint Ndiffuse_5 = 100000;
296 uint Ndirect_5 = 5000;
298 float shortwave_exact_0 = 0.7f * Qs;
299 float shortwave_exact_1 = 0.3f * 0.2f * Qs;
300 float longwave_exact_0 = 0.f;
301 float longwave_exact_1 = sigma * powf(300.f, 4) * 0.2f;
316 float shortwave_rho = 0.3f;
327 radiationmodel_5.disableMessages();
330 radiationmodel_5.addRadiationBand(
"LW");
331 radiationmodel_5.setDirectRayCount(
"LW", Ndiffuse_5);
332 radiationmodel_5.setDiffuseRayCount(
"LW", Ndiffuse_5);
333 radiationmodel_5.setScatteringDepth(
"LW", 0);
336 uint SunSource_5 = radiationmodel_5.addCollimatedRadiationSource(
make_vec3(0, 0, 1));
337 radiationmodel_5.addRadiationBand(
"SW");
338 radiationmodel_5.disableEmission(
"SW");
339 radiationmodel_5.setDirectRayCount(
"SW", Ndirect_5);
340 radiationmodel_5.setDiffuseRayCount(
"SW", Ndirect_5);
341 radiationmodel_5.setScatteringDepth(
"SW", 1);
342 radiationmodel_5.setSourceFlux(SunSource_5,
"SW", Qs);
344 radiationmodel_5.updateGeometry();
346 float longwave_model_0 = 0.f;
347 float longwave_model_1 = 0.f;
348 float shortwave_model_0 = 0.f;
349 float shortwave_model_1 = 0.f;
352 for (
int i = 0; i < Nensemble; i++) {
353 std::vector<std::string> bands{
"SW",
"LW"};
354 radiationmodel_5.runBand(bands);
358 longwave_model_0 += 0.5f *
R / float(Nensemble);
360 longwave_model_0 += 0.5f *
R / float(Nensemble);
363 longwave_model_1 += 0.5f *
R / float(Nensemble);
365 longwave_model_1 += 0.5f *
R / float(Nensemble);
369 shortwave_model_0 += 0.5f *
R / float(Nensemble);
371 shortwave_model_0 += 0.5f *
R / float(Nensemble);
374 shortwave_model_1 += 0.5f *
R / float(Nensemble);
376 shortwave_model_1 += 0.5f *
R / float(Nensemble);
379 float shortwave_error_0 = fabsf(shortwave_model_0 - shortwave_exact_0) / fabsf(shortwave_exact_0);
380 float shortwave_error_1 = fabsf(shortwave_model_1 - shortwave_exact_1) / fabsf(shortwave_exact_1);
381 float longwave_error_1 = fabsf(longwave_model_1 - longwave_exact_1) / fabsf(longwave_exact_1);
383 DOCTEST_CHECK(shortwave_error_0 <= error_threshold);
384 DOCTEST_CHECK(shortwave_error_1 <= error_threshold);
386 DOCTEST_CHECK(longwave_model_0 == longwave_exact_0);
387 DOCTEST_CHECK(longwave_error_1 <= error_threshold);
390DOCTEST_TEST_CASE(
"RadiationModel Parallel Disks Texture Masked Patches") {
391 float error_threshold = 0.005;
393 float sigma = 5.6703744E-8;
395 uint Ndirect_6 = 1000;
396 uint Ndiffuse_6 = 500000;
398 float shortwave_rho = 0.3;
404 float A1 =
M_PI * r1 * r1;
405 float A2 =
M_PI * r2 * r2;
410 float X = 1.f + (1.f + R2 * R2) / (R1 * R1);
411 float F12 = 0.5f * (X - std::sqrt(X * X - 4.f * powf(R2 / R1, 2)));
413 float shortwave_exact_0 = (A1 - A2) / A1 * (1.f - shortwave_rho);
414 float shortwave_exact_1 = (A1 - A2) / A1 * F12 * A1 / A2 * shortwave_rho;
415 float longwave_exact_0 = sigma * powf(300.f, 4) * F12;
416 float longwave_exact_1 = sigma * powf(300.f, 4) * F12 * A1 / A2;
435 radiationmodel_6.disableMessages();
437 uint SunSource_6 = radiationmodel_6.addCollimatedRadiationSource(
make_vec3(0, 0, 1));
440 radiationmodel_6.addRadiationBand(
"SW");
441 radiationmodel_6.disableEmission(
"SW");
442 radiationmodel_6.setDirectRayCount(
"SW", Ndirect_6);
443 radiationmodel_6.setDiffuseRayCount(
"SW", Ndiffuse_6);
444 radiationmodel_6.setSourceFlux(SunSource_6,
"SW", 1.f);
445 radiationmodel_6.setDiffuseRadiationFlux(
"SW", 0);
446 radiationmodel_6.setScatteringDepth(
"SW", 1);
449 radiationmodel_6.addRadiationBand(
"LW");
450 radiationmodel_6.setDiffuseRayCount(
"LW", Ndiffuse_6);
451 radiationmodel_6.setDiffuseRadiationFlux(
"LW", 0.f);
452 radiationmodel_6.setScatteringDepth(
"LW", 0);
454 radiationmodel_6.updateGeometry();
456 float shortwave_model_0 = 0;
457 float shortwave_model_1 = 0;
458 float longwave_model_0 = 0;
459 float longwave_model_1 = 0;
462 for (
uint i = 0; i < Nensemble; i++) {
463 radiationmodel_6.runBand(
"SW");
464 radiationmodel_6.runBand(
"LW");
467 shortwave_model_0 +=
R / float(Nensemble);
470 shortwave_model_1 +=
R / float(Nensemble);
473 longwave_model_0 +=
R / float(Nensemble);
476 longwave_model_1 +=
R / float(Nensemble);
479 float shortwave_error_0 = fabsf(shortwave_exact_0 - shortwave_model_0) / fabsf(shortwave_exact_0);
480 float shortwave_error_1 = fabsf(shortwave_exact_1 - shortwave_model_1) / fabsf(shortwave_exact_1);
481 float longwave_error_0 = fabsf(longwave_exact_0 - longwave_model_0) / fabsf(longwave_exact_0);
482 float longwave_error_1 = fabsf(longwave_exact_1 - longwave_model_1) / fabsf(longwave_exact_1);
484 DOCTEST_CHECK(shortwave_error_0 <= error_threshold);
485 DOCTEST_CHECK(shortwave_error_1 <= error_threshold);
486 DOCTEST_CHECK(longwave_error_0 <= error_threshold);
487 DOCTEST_CHECK(longwave_error_1 <= error_threshold);
490DOCTEST_TEST_CASE(
"RadiationModel Second Law Equilibrium Test") {
491 float error_threshold = 0.005;
492 float sigma = 5.6703744E-8;
494 uint Ndiffuse_7 = 50000;
503 uint objID_7 = context_7.
addBoxObject(
make_vec3(0, 0, 0),
make_vec3(10, 10, 10),
make_int3(5, 5, 5), RGB::black,
true);
514 radiationmodel_7.disableMessages();
517 radiationmodel_7.addRadiationBand(
"LW");
518 radiationmodel_7.setDiffuseRayCount(
"LW", Ndiffuse_7);
519 radiationmodel_7.setDiffuseRadiationFlux(
"LW", 0);
520 radiationmodel_7.setScatteringDepth(
"LW", 5);
522 radiationmodel_7.updateGeometry();
524 radiationmodel_7.runBand(
"LW");
527 float flux_err = 0.f;
528 for (
int p = 0; p < UUIDt.size(); p++) {
531 flux_err += fabsf(
R - eps1_7 * sigma * powf(300, 4)) / (eps1_7 * sigma * powf(300, 4)) /
float(UUIDt.size());
534 DOCTEST_CHECK(flux_err <= error_threshold);
537 for (
uint p: UUIDt) {
539 if (context_7.
randu() < 0.5f) {
548 radiationmodel_7.updateGeometry();
549 radiationmodel_7.runBand(
"LW");
552 for (
int p = 0; p < UUIDt.size(); p++) {
557 flux_err += fabsf(
R - emissivity * sigma * powf(300, 4)) / (emissivity * sigma * powf(300, 4)) /
float(UUIDt.size());
560 DOCTEST_CHECK(flux_err <= error_threshold);
563DOCTEST_TEST_CASE(
"RadiationModel Texture Mapping") {
564 float error_threshold = 0.005;
570 uint source = radiation.addCollimatedRadiationSource(
make_vec3(0, 0, 1));
572 radiation.addRadiationBand(
"SW");
574 radiation.setDirectRayCount(
"SW", 10000);
575 radiation.disableEmission(
"SW");
576 radiation.disableMessages();
578 radiation.setSourceFlux(source,
"SW", 1.f);
590 radiation.updateGeometry();
592 radiation.runBand(
"SW");
598 DOCTEST_CHECK(fabs(F0 - (1.f - 0.25f *
M_PI)) <= error_threshold);
599 DOCTEST_CHECK(fabsf(F1 - 1.f) <= error_threshold);
607 radiation.updateGeometry();
609 radiation.runBand(
"SW");
615 for (
uint p: UUIDs1) {
626 bool test_8b_pass =
true;
627 for (
uint p = 0; p < UUIDs1.size(); p++) {
630 if (fabs(
R - 1.f) > error_threshold) {
631 test_8b_pass =
false;
635 DOCTEST_CHECK(fabs(F0 - (1.f - 0.25f *
M_PI)) <= error_threshold);
636 DOCTEST_CHECK(fabsf(F1 - 1.f) <= error_threshold);
637 DOCTEST_CHECK(test_8b_pass);
644 radiation.updateGeometry();
646 radiation.runBand(
"SW");
651 DOCTEST_CHECK(fabsf(F0) <= error_threshold);
652 DOCTEST_CHECK(fabsf(F1 - 1.f) <= error_threshold);
659 radiation.updateGeometry();
661 radiation.runBand(
"SW");
666 DOCTEST_CHECK(fabs(F0 - (1.f - 0.25f *
M_PI)) <= error_threshold);
667 DOCTEST_CHECK(fabsf(F1 - 1.f) <= error_threshold);
672 UUID1 = context_8.
addTriangle(p1 +
make_vec3(-0.5f * sz.x, -0.5f * sz.y, 0), p1 +
make_vec3(0.5f * sz.x, 0.5f * sz.y, 0.f), p1 +
make_vec3(-0.5f * sz.x, 0.5f * sz.y, 0.f),
"lib/images/disk_texture.png",
make_vec2(0, 0),
make_vec2(1, 1),
675 radiation.updateGeometry();
677 radiation.runBand(
"SW");
682 DOCTEST_CHECK(fabs(F0 - 0.5 - 0.5 * (1.f - 0.25f *
M_PI)) <= error_threshold);
683 DOCTEST_CHECK(fabsf(F1 - 1.f) <= error_threshold);
690 uint UUID2 = context_8.
addTriangle(p1 +
make_vec3(-0.5f * sz.x, -0.5f * sz.y, 0), p1 +
make_vec3(0.5f * sz.x, -0.5f * sz.y, 0), p1 +
make_vec3(0.5f * sz.x, 0.5f * sz.y, 0),
"lib/images/disk_texture.png",
make_vec2(0, 0),
make_vec2(1, 0),
693 radiation.updateGeometry();
695 radiation.runBand(
"SW");
702 DOCTEST_CHECK(fabsf(F0) <= error_threshold);
703 DOCTEST_CHECK(fabsf(F1 - 1.f) <= error_threshold);
704 DOCTEST_CHECK(fabsf(F2 - 1.f) <= error_threshold);
713 UUID1 = context_8.
addTriangle(p0 +
make_vec3(-0.5f * sz.x, -0.5f * sz.y, 0), p0 +
make_vec3(0.5f * sz.x, 0.5f * sz.y, 0), p0 +
make_vec3(-0.5f * sz.x, 0.5f * sz.y, 0),
"lib/images/disk_texture.png",
make_vec2(0, 0),
make_vec2(1, 1),
715 UUID2 = context_8.
addTriangle(p0 +
make_vec3(-0.5f * sz.x, -0.5f * sz.y, 0), p0 +
make_vec3(0.5f * sz.x, -0.5f * sz.y, 0), p0 +
make_vec3(0.5f * sz.x, 0.5f * sz.y, 0),
"lib/images/disk_texture.png",
make_vec2(0, 0),
make_vec2(1, 0),
718 radiation.updateGeometry();
720 radiation.runBand(
"SW");
726 DOCTEST_CHECK(fabsf(F1) <= error_threshold);
727 DOCTEST_CHECK(fabsf(F2) <= error_threshold);
728 DOCTEST_CHECK(fabsf(F0 - 1.f) <= error_threshold);
731DOCTEST_TEST_CASE(
"RadiationModel Homogeneous Canopy of Patches") {
732 float error_threshold = 0.005;
733 float sigma = 5.6703744E-8;
735 uint Ndirect_9 = 1000;
736 uint Ndiffuse_9 = 5000;
742 float w_leaf_9 = 0.075;
744 int Nleaves = (int) lroundf(LAI_9 * D_9 * D_9 / w_leaf_9 / w_leaf_9);
748 std::vector<uint> UUIDs_leaf, UUIDs_inc;
750 for (
int i = 0; i < Nleaves; i++) {
751 vec3 position((-0.5f + context_9.
randu()) * D_9, (-0.5f + context_9.
randu()) * D_9, 0.5f * w_leaf_9 + context_9.
randu() * h_9);
755 if (fabsf(position.x) <= 0.5 * D_inc_9 && fabsf(position.y) <= 0.5 * D_inc_9) {
756 UUIDs_inc.push_back(UUID);
764 radiation_9.disableMessages();
766 radiation_9.addRadiationBand(
"direct");
767 radiation_9.disableEmission(
"direct");
768 radiation_9.setDirectRayCount(
"direct", Ndirect_9);
769 float theta_s = 0.2 *
M_PI;
771 radiation_9.setSourceFlux(ID,
"direct", 1.f / cosf(theta_s));
773 radiation_9.addRadiationBand(
"diffuse");
774 radiation_9.disableEmission(
"diffuse");
775 radiation_9.setDiffuseRayCount(
"diffuse", Ndiffuse_9);
776 radiation_9.setDiffuseRadiationFlux(
"diffuse", 1.f);
778 radiation_9.updateGeometry();
780 radiation_9.runBand(
"direct");
781 radiation_9.runBand(
"diffuse");
783 float intercepted_leaf_direct = 0.f;
784 float intercepted_leaf_diffuse = 0.f;
785 for (
uint i: UUIDs_inc) {
789 intercepted_leaf_direct += flux * area / D_inc_9 / D_inc_9;
791 intercepted_leaf_diffuse += flux * area / D_inc_9 / D_inc_9;
794 float intercepted_ground_direct = 0.f;
795 float intercepted_ground_diffuse = 0.f;
796 for (
uint i: UUIDs_ground) {
803 if (fabsf(position.
x) <= 0.5 * D_inc_9 && fabsf(position.
y) <= 0.5 * D_inc_9) {
804 intercepted_ground_direct += flux_dir * area / D_inc_9 / D_inc_9;
805 intercepted_ground_diffuse += flux_diff * area / D_inc_9 / D_inc_9;
809 intercepted_ground_direct = 1.f - intercepted_ground_direct;
810 intercepted_ground_diffuse = 1.f - intercepted_ground_diffuse;
813 float dtheta = 0.5f * float(
M_PI) / float(N);
815 float intercepted_theoretical_diffuse = 0.f;
816 for (
int i = 0; i < N; i++) {
817 float theta = (float(i) + 0.5f) * dtheta;
818 intercepted_theoretical_diffuse += 2.f * (1.f - expf(-0.5f * LAI_9 / cosf(theta))) * cosf(theta) * sinf(theta) * dtheta;
821 float intercepted_theoretical_direct = 1.f - expf(-0.5f * LAI_9 / cosf(theta_s));
823 DOCTEST_CHECK(fabsf(intercepted_ground_direct - intercepted_theoretical_direct) <= 2.f * error_threshold);
824 DOCTEST_CHECK(fabsf(intercepted_leaf_direct - intercepted_theoretical_direct) <= 2.f * error_threshold);
825 DOCTEST_CHECK(fabsf(intercepted_ground_diffuse - intercepted_theoretical_diffuse) <= 2.f * error_threshold);
826 DOCTEST_CHECK(fabsf(intercepted_leaf_diffuse - intercepted_theoretical_diffuse) <= 2.f * error_threshold);
829DOCTEST_TEST_CASE(
"RadiationModel Gas-filled Furnace") {
830 float error_threshold = 0.005;
831 float sigma = 5.6703744E-8;
833 float Rref_10 = 33000.f;
834 uint Ndiffuse_10 = 10000;
840 float Tw_10 = 1273.f;
841 float Tm_10 = 1773.f;
843 float kappa_10 = 0.1f;
844 float eps_m_10 = 1.f;
845 float w_patch_10 = 0.01;
847 int Npatches_10 = (int) lroundf(2.f * kappa_10 * w_10 * h_10 * d_10 / w_patch_10 / w_patch_10);
851 std::vector<uint> UUIDs_box = context_10.
addBox(
make_vec3(0, 0, 0),
make_vec3(d_10, w_10, h_10),
make_int3(round(d_10 / w_patch_10), round(w_10 / w_patch_10), round(h_10 / w_patch_10)), RGB::green,
true);
856 std::vector<uint> UUIDs_patches;
858 for (
int i = 0; i < Npatches_10; i++) {
859 float x = -0.5f * d_10 + 0.5f * w_patch_10 + (d_10 - 2 * w_patch_10) * context_10.
randu();
860 float y = -0.5f * w_10 + 0.5f * w_patch_10 + (w_10 - 2 * w_patch_10) * context_10.
randu();
861 float z = -0.5f * h_10 + 0.5f * w_patch_10 + (h_10 - 2 * w_patch_10) * context_10.
randu();
863 float theta = acosf(1.f - context_10.
randu());
864 float phi = 2.f * float(
M_PI) * context_10.
randu();
870 context_10.
setPrimitiveData(UUIDs_patches,
"reflectivity_LW", 1.f - eps_m_10);
873 radiation_10.disableMessages();
875 radiation_10.addRadiationBand(
"LW");
876 radiation_10.setDiffuseRayCount(
"LW", Ndiffuse_10);
877 radiation_10.setScatteringDepth(
"LW", 0);
879 radiation_10.updateGeometry();
880 radiation_10.runBand(
"LW");
884 for (
uint i: UUIDs_box) {
889 R_wall += flux * area;
891 R_wall = R_wall / A_wall - sigma * powf(Tw_10, 4);
893 DOCTEST_CHECK(fabsf(R_wall - Rref_10) / Rref_10 <= error_threshold);
896DOCTEST_TEST_CASE(
"RadiationModel Purely Scattering Medium Between Infinite Plates") {
897 float error_threshold = 0.005;
898 float sigma = 5.6703744E-8;
904 float Tw1_11 = 300.f;
905 float Tw2_11 = 400.f;
907 float epsw1_11 = 0.8f;
908 float epsw2_11 = 0.5f;
910 float omega_11 = 1.f;
911 float tauL_11 = 0.1f;
913 float Psi2_exact = 0.427;
915 float w_patch_11 = 0.05;
917 float beta = tauL_11 / h_11;
919 int Nleaves_11 = (int) lroundf(2.f * beta * W_11 * W_11 * h_11 / w_patch_11 / w_patch_11);
938 std::vector<uint> UUIDs_patches_11;
940 for (
int i = 0; i < Nleaves_11; i++) {
941 float x = -0.5f * W_11 + 0.5f * w_patch_11 + (W_11 - w_patch_11) * context_11.
randu();
942 float y = -0.5f * W_11 + 0.5f * w_patch_11 + (W_11 - w_patch_11) * context_11.
randu();
943 float z = -0.5f * h_11 + 0.5f * w_patch_11 + (h_11 - w_patch_11) * context_11.
randu();
945 float theta = acosf(1.f - context_11.
randu());
946 float phi = 2.f * float(
M_PI) * context_11.
randu();
951 context_11.
setPrimitiveData(UUIDs_patches_11,
"emissivity_LW", 1.f - omega_11);
955 radiation_11.disableMessages();
957 radiation_11.addRadiationBand(
"LW");
958 radiation_11.setDiffuseRayCount(
"LW", 10000);
959 radiation_11.setScatteringDepth(
"LW", 4);
961 radiation_11.updateGeometry();
962 radiation_11.runBand(
"LW");
966 for (
int i = 0; i < UUIDs_1.size(); i++) {
969 if (fabsf(position.
x) < 0.5 * w_11 && fabsf(position.
y) < 0.5 * w_11) {
974 R_wall2 += flux * area;
979 R_wall2 = (R_wall2 / A_wall2 - epsw2_11 * sigma * pow(Tw2_11, 4)) / (sigma * (pow(Tw1_11, 4) - pow(Tw2_11, 4)));
981 DOCTEST_CHECK(fabsf(R_wall2 - Psi2_exact) <= 10.f * error_threshold);
984DOCTEST_TEST_CASE(
"RadiationModel Homogeneous Canopy with Periodic Boundaries") {
985 float error_threshold = 0.005;
987 uint Ndirect_12 = 1000;
988 uint Ndiffuse_12 = 5000;
993 float w_leaf_12 = 0.05;
995 int Nleaves_12 = round(LAI_12 * D_12 * D_12 / w_leaf_12 / w_leaf_12);
999 std::vector<uint> UUIDs_leaf_12;
1001 for (
int i = 0; i < Nleaves_12; i++) {
1002 vec3 position((-0.5 + context_12.
randu()) * D_12, (-0.5 + context_12.
randu()) * D_12, 0.5 * w_leaf_12 + context_12.
randu() * h_12);
1006 UUIDs_leaf_12.push_back(UUID);
1013 radiation_12.disableMessages();
1015 radiation_12.addRadiationBand(
"direct");
1016 radiation_12.disableEmission(
"direct");
1017 radiation_12.setDirectRayCount(
"direct", Ndirect_12);
1018 float theta_s = 0.2 *
M_PI;
1020 radiation_12.setSourceFlux(ID,
"direct", 1.f / cos(theta_s));
1022 radiation_12.addRadiationBand(
"diffuse");
1023 radiation_12.disableEmission(
"diffuse");
1024 radiation_12.setDiffuseRayCount(
"diffuse", Ndiffuse_12);
1025 radiation_12.setDiffuseRadiationFlux(
"diffuse", 1.f);
1027 radiation_12.enforcePeriodicBoundary(
"xy");
1029 radiation_12.updateGeometry();
1031 radiation_12.runBand(
"direct");
1032 radiation_12.runBand(
"diffuse");
1034 float intercepted_leaf_direct_12 = 0.f;
1035 float intercepted_leaf_diffuse_12 = 0.f;
1036 for (
int i = 0; i < UUIDs_leaf_12.size(); i++) {
1039 context_12.
getPrimitiveData(UUIDs_leaf_12.at(i),
"radiation_flux_direct", flux);
1040 intercepted_leaf_direct_12 += flux * area / D_12 / D_12;
1041 context_12.
getPrimitiveData(UUIDs_leaf_12.at(i),
"radiation_flux_diffuse", flux);
1042 intercepted_leaf_diffuse_12 += flux * area / D_12 / D_12;
1045 float intercepted_ground_direct_12 = 0.f;
1046 float intercepted_ground_diffuse_12 = 0.f;
1047 for (
int i = 0; i < UUIDs_ground_12.size(); i++) {
1050 context_12.
getPrimitiveData(UUIDs_ground_12.at(i),
"radiation_flux_direct", flux_dir);
1052 context_12.
getPrimitiveData(UUIDs_ground_12.at(i),
"radiation_flux_diffuse", flux_diff);
1054 intercepted_ground_direct_12 += flux_dir * area / D_12 / D_12;
1055 intercepted_ground_diffuse_12 += flux_diff * area / D_12 / D_12;
1058 intercepted_ground_direct_12 = 1.f - intercepted_ground_direct_12;
1059 intercepted_ground_diffuse_12 = 1.f - intercepted_ground_diffuse_12;
1062 float dtheta = 0.5 *
M_PI / float(N);
1064 float intercepted_theoretical_diffuse_12 = 0.f;
1065 for (
int i = 0; i < N; i++) {
1066 float theta = (i + 0.5f) * dtheta;
1067 intercepted_theoretical_diffuse_12 += 2.f * (1.f - exp(-0.5 * LAI_12 / cos(theta))) * cos(theta) * sin(theta) * dtheta;
1070 float intercepted_theoretical_direct_12 = 1.f - exp(-0.5 * LAI_12 / cos(theta_s));
1072 DOCTEST_CHECK(fabsf(intercepted_ground_direct_12 - intercepted_theoretical_direct_12) <= 2.f * error_threshold);
1073 DOCTEST_CHECK(fabsf(intercepted_leaf_direct_12 - intercepted_theoretical_direct_12) <= 2.f * error_threshold);
1074 DOCTEST_CHECK(fabsf(intercepted_ground_diffuse_12 - intercepted_theoretical_diffuse_12) <= 2.f * error_threshold);
1075 DOCTEST_CHECK(fabsf(intercepted_leaf_diffuse_12 - intercepted_theoretical_diffuse_12) <= 2.f * error_threshold);
1078DOCTEST_TEST_CASE(
"RadiationModel Texture-masked Tile Objects with Periodic Boundaries") {
1079 float error_threshold = 0.005;
1081 uint Ndirect_13 = 1000;
1082 uint Ndiffuse_13 = 5000;
1087 float w_leaf_13 = 0.05;
1095 for (
uint p = 0; p < UUIDs_ptype.size(); p++) {
1099 int Nleaves_13 = round(LAI_13 * D_13 * D_13 / A_leaf);
1101 std::vector<uint> UUIDs_leaf_13;
1103 for (
int i = 0; i < Nleaves_13; i++) {
1104 vec3 position((-0.5 + context_13.
randu()) * D_13, (-0.5 + context_13.
randu()) * D_13, 0.5 * w_leaf_13 + context_13.
randu() * h_13);
1114 UUIDs_leaf_13.insert(UUIDs_leaf_13.end(), UUIDs.begin(), UUIDs.end());
1123 radiation_13.disableMessages();
1125 radiation_13.addRadiationBand(
"direct");
1126 radiation_13.disableEmission(
"direct");
1127 radiation_13.setDirectRayCount(
"direct", Ndirect_13);
1128 float theta_s = 0.2 *
M_PI;
1130 radiation_13.setSourceFlux(ID,
"direct", 1.f / cos(theta_s));
1132 radiation_13.addRadiationBand(
"diffuse");
1133 radiation_13.disableEmission(
"diffuse");
1134 radiation_13.setDiffuseRayCount(
"diffuse", Ndiffuse_13);
1135 radiation_13.setDiffuseRadiationFlux(
"diffuse", 1.f);
1137 radiation_13.enforcePeriodicBoundary(
"xy");
1139 radiation_13.updateGeometry();
1141 radiation_13.runBand(
"direct");
1142 radiation_13.runBand(
"diffuse");
1144 float intercepted_leaf_direct_13 = 0.f;
1145 float intercepted_leaf_diffuse_13 = 0.f;
1146 for (
int i = 0; i < UUIDs_leaf_13.size(); i++) {
1149 context_13.
getPrimitiveData(UUIDs_leaf_13.at(i),
"radiation_flux_direct", flux);
1150 intercepted_leaf_direct_13 += flux * area / D_13 / D_13;
1151 context_13.
getPrimitiveData(UUIDs_leaf_13.at(i),
"radiation_flux_diffuse", flux);
1152 intercepted_leaf_diffuse_13 += flux * area / D_13 / D_13;
1155 float intercepted_ground_direct_13 = 0.f;
1156 float intercepted_ground_diffuse_13 = 0.f;
1157 for (
int i = 0; i < UUIDs_ground_13.size(); i++) {
1160 context_13.
getPrimitiveData(UUIDs_ground_13.at(i),
"radiation_flux_direct", flux_dir);
1162 context_13.
getPrimitiveData(UUIDs_ground_13.at(i),
"radiation_flux_diffuse", flux_diff);
1164 intercepted_ground_direct_13 += flux_dir * area / D_13 / D_13;
1165 intercepted_ground_diffuse_13 += flux_diff * area / D_13 / D_13;
1168 intercepted_ground_direct_13 = 1.f - intercepted_ground_direct_13;
1169 intercepted_ground_diffuse_13 = 1.f - intercepted_ground_diffuse_13;
1172 float dtheta = 0.5 *
M_PI / float(N);
1174 float intercepted_theoretical_diffuse_13 = 0.f;
1175 for (
int i = 0; i < N; i++) {
1176 float theta = (i + 0.5f) * dtheta;
1177 intercepted_theoretical_diffuse_13 += 2.f * (1.f - exp(-0.5 * LAI_13 / cos(theta))) * cos(theta) * sin(theta) * dtheta;
1180 float intercepted_theoretical_direct_13 = 1.f - exp(-0.5 * LAI_13 / cos(theta_s));
1182 DOCTEST_CHECK(fabsf(intercepted_ground_direct_13 - intercepted_theoretical_direct_13) <= 2.f * error_threshold);
1183 DOCTEST_CHECK(fabsf(intercepted_leaf_direct_13 - intercepted_theoretical_direct_13) <= 2.f * error_threshold);
1184 DOCTEST_CHECK(fabsf(intercepted_ground_diffuse_13 - intercepted_theoretical_diffuse_13) <= 2.f * error_threshold);
1185 DOCTEST_CHECK(fabsf(intercepted_leaf_diffuse_13 - intercepted_theoretical_diffuse_13) <= 4.f * error_threshold);
1188DOCTEST_TEST_CASE(
"RadiationModel Anisotropic Diffuse Radiation Horizontal Patch") {
1189 float error_threshold = 0.005;
1191 uint Ndiffuse_14 = 50000;
1195 std::vector<float> K_14;
1196 K_14.push_back(0.f);
1197 K_14.push_back(0.25f);
1198 K_14.push_back(1.f);
1200 std::vector<float> thetas_14;
1201 thetas_14.push_back(0.f);
1202 thetas_14.push_back(0.25 *
M_PI);
1208 radiation_14.disableMessages();
1210 radiation_14.addRadiationBand(
"diffuse");
1211 radiation_14.disableEmission(
"diffuse");
1212 radiation_14.setDiffuseRayCount(
"diffuse", Ndiffuse_14);
1213 radiation_14.setDiffuseRadiationFlux(
"diffuse", 1.f);
1215 radiation_14.updateGeometry();
1217 for (
int t = 0; t < thetas_14.size(); t++) {
1218 for (
int k = 0; k < K_14.size(); k++) {
1219 radiation_14.setDiffuseRadiationExtinctionCoeff(
"diffuse", K_14.at(k),
make_SphericalCoord(0.5 *
M_PI - thetas_14.at(t), 0.f));
1220 radiation_14.runBand(
"diffuse");
1225 DOCTEST_CHECK(fabsf(Rdiff - 1.f) <= 2.f * error_threshold);
1230DOCTEST_TEST_CASE(
"RadiationModel Disk Radiation Source Above Circular Element") {
1231 float error_threshold = 0.005;
1233 uint Ndirect_15 = 10000;
1241 radiation_15.disableMessages();
1247 radiation_15.addRadiationBand(
"light");
1248 radiation_15.disableEmission(
"light");
1249 radiation_15.setSourceFlux(ID_15,
"light", 1.f);
1250 radiation_15.setDirectRayCount(
"light", Ndirect_15);
1252 radiation_15.updateGeometry();
1253 radiation_15.runBand(
"light");
1258 float R1_15 = r1_15 / a_15;
1259 float R2_15 = r2_15 / a_15;
1260 float X_15 = 1.f + (1.f + R2_15 * R2_15) / (R1_15 * R1_15);
1261 float F12_exact_15 = 0.5f * (X_15 - sqrtf(X_15 * X_15 - 4.f * powf(R2_15 / R1_15, 2)));
1263 DOCTEST_CHECK(fabs(F12_15 - F12_exact_15 * r1_15 * r1_15 / r2_15 / r2_15) <= 2.f * error_threshold);
1266DOCTEST_TEST_CASE(
"RadiationModel Rectangular Radiation Source Above Patch") {
1267 float error_threshold = 0.01;
1269 uint Ndirect_16 = 50000;
1277 radiation_16.disableMessages();
1283 radiation_16.addRadiationBand(
"light");
1284 radiation_16.disableEmission(
"light");
1285 radiation_16.setSourceFlux(ID_16,
"light", 1.f);
1286 radiation_16.setDirectRayCount(
"light", Ndirect_16);
1288 radiation_16.updateGeometry();
1289 radiation_16.runBand(
"light");
1294 float X_16 = a_16 / c_16;
1295 float Y_16 = b_16 / c_16;
1296 float X2_16 = X_16 * X_16;
1297 float Y2_16 = Y_16 * Y_16;
1299 float F12_exact_16 = 2.0f / float(
M_PI * X_16 * Y_16) *
1300 (logf(std::sqrt((1.f + X2_16) * (1.f + Y2_16) / (1.f + X2_16 + Y2_16))) + X_16 * std::sqrt(1.f + Y2_16) * atanf(X_16 / std::sqrt(1.f + Y2_16)) + Y_16 * std::sqrt(1.f + X2_16) * atanf(Y_16 / std::sqrt(1.f + X2_16)) -
1301 X_16 * atanf(X_16) - Y_16 * atanf(Y_16));
1303 DOCTEST_CHECK(fabs(F12_16 - F12_exact_16) <= error_threshold);
1306DOCTEST_TEST_CASE(
"RadiationModel ROMC Camera Test Verification") {
1308 float sunzenithd = 30;
1309 float reflectivityleaf = 0.02;
1310 float transmissivityleaf = 0.01;
1311 std::string bandname =
"RED";
1313 float viewazimuth = 0;
1314 float heightscene = 30.f;
1315 float rangescene = 100.f;
1316 std::vector<float> viewangles = {-75, 0, 36};
1317 float sunazimuth = 0;
1318 std::vector<float> referencevalues = {66.f, 225.f, 274.f};
1321 std::vector<std::vector<float>> CSpositions = {{-24.8302, 11.6110, 15.6210}, {-38.3380, -9.06342, 17.6094}, {-5.26569, 18.9618, 17.2535}, {-27.4794, -32.0266, 15.9146},
1322 {33.5709, -6.31039, 14.5332}, {11.9126, 8.32062, 12.1220}, {32.4756, -26.9023, 16.3684}};
1324 for (
int w = -1; w < 2; w++) {
1326 for (
auto &CSposition: CSpositions) {
1327 vec3 transpos = movew +
make_vec3(CSposition.at(0), CSposition.at(1), CSposition.at(2));
1329 std::vector<uint> iCUUIDsn = cameracalibration.readROMCCanopy();
1332 context_17.
setPrimitiveData(iCUUIDsn,
"reflectivity_spectrum",
"leaf_reflectivity");
1333 context_17.
setPrimitiveData(iCUUIDsn,
"transmissivity_spectrum",
"leaf_transmissivity");
1338 std::vector<helios::vec2> leafspectrarho(2200);
1339 std::vector<helios::vec2> leafspectratau(2200);
1340 std::vector<helios::vec2> sourceintensity(2200);
1341 for (
int i = 0; i < leafspectrarho.size(); i++) {
1342 leafspectrarho.at(i).x = float(301 + i);
1343 leafspectrarho.at(i).y = reflectivityleaf;
1344 leafspectratau.at(i).x = float(301 + i);
1345 leafspectratau.at(i).y = transmissivityleaf;
1346 sourceintensity.at(i).x = float(301 + i);
1347 sourceintensity.at(i).y = 1;
1349 context_17.
setGlobalData(
"leaf_reflectivity", leafspectrarho);
1350 context_17.
setGlobalData(
"leaf_transmissivity", leafspectratau);
1351 context_17.
setGlobalData(
"camera_response", sourceintensity);
1352 context_17.
setGlobalData(
"source_intensity", sourceintensity);
1356 std::vector<std::string> cameralabels;
1358 radiation_17.disableMessages();
1359 for (
float viewangle: viewangles) {
1362 vec3 camera_position = 100000 * camerarotation + camera_lookat;
1367 cameraproperties.
HFOV = 0.02864786f * 2.f;
1370 std::string cameralabel =
"ROMC" + std::to_string(viewangle);
1371 radiation_17.addRadiationCamera(cameralabel, {bandname}, camera_position, camera_lookat, cameraproperties, 60);
1372 cameralabels.push_back(cameralabel);
1375 radiation_17.setSourceSpectrum(0,
"source_intensity");
1376 radiation_17.addRadiationBand(bandname, 500, 502);
1377 radiation_17.setDiffuseRayCount(bandname, 20);
1378 radiation_17.disableEmission(bandname);
1379 radiation_17.setSourceFlux(0, bandname, 5);
1380 radiation_17.setScatteringDepth(bandname, 1);
1381 radiation_17.setDiffuseRadiationFlux(bandname, 0);
1382 radiation_17.setDiffuseRadiationExtinctionCoeff(bandname, 0.f,
make_vec3(-0.5, 0.5, 1));
1384 for (
const auto &cameralabel: cameralabels) {
1385 radiation_17.setCameraSpectralResponse(cameralabel, bandname,
"camera_response");
1387 radiation_17.updateGeometry();
1388 radiation_17.runBand(bandname);
1391 std::vector<float> camera_data;
1392 std::vector<uint> camera_UUID;
1394 for (
int i = 0; i < cameralabels.size(); i++) {
1395 std::string global_data_label =
"camera_" + cameralabels.at(i) +
"_" + bandname;
1396 std::string global_UUID =
"camera_" + cameralabels.at(i) +
"_pixel_UUID";
1397 context_17.
getGlobalData(global_data_label.c_str(), camera_data);
1399 float camera_all_data = 0;
1400 for (
int v = 0; v < camera_data.size(); v++) {
1401 uint iUUID = camera_UUID.at(v) - 1;
1403 camera_all_data += camera_data.at(v);
1406 cameravalue = std::abs(referencevalues.at(i) - camera_all_data);
1407 DOCTEST_CHECK(cameravalue <= 1.5f);
1411DOCTEST_TEST_CASE(
"RadiationModel Spectral Integration and Interpolation Tests") {
1415 radiation.disableMessages();
1419 std::vector<helios::vec2> test_spectrum;
1420 test_spectrum.push_back(
make_vec2(400, 0.1f));
1421 test_spectrum.push_back(
make_vec2(500, 0.5f));
1422 test_spectrum.push_back(
make_vec2(600, 0.3f));
1423 test_spectrum.push_back(
make_vec2(700, 0.2f));
1426 float full_integral = radiation.integrateSpectrum(test_spectrum);
1428 float expected_integral = (0.1f + 0.5f) * 100.0f * 0.5f + (0.5f + 0.3f) * 100.0f * 0.5f + (0.3f + 0.2f) * 100.0f * 0.5f;
1429 DOCTEST_CHECK(std::abs(full_integral - expected_integral) < 1e-5f);
1433 float partial_integral = radiation.integrateSpectrum(test_spectrum, 450, 650);
1435 DOCTEST_CHECK(std::abs(partial_integral - full_integral) < 1e-5f);
1440 std::vector<helios::vec2> source_spectrum;
1441 source_spectrum.push_back(
make_vec2(400, 1.0f));
1442 source_spectrum.push_back(
make_vec2(500, 2.0f));
1443 source_spectrum.push_back(
make_vec2(600, 1.5f));
1444 source_spectrum.push_back(
make_vec2(700, 0.5f));
1446 std::vector<helios::vec2> surface_spectrum;
1447 surface_spectrum.push_back(
make_vec2(400, 0.2f));
1448 surface_spectrum.push_back(
make_vec2(500, 0.6f));
1449 surface_spectrum.push_back(
make_vec2(600, 0.4f));
1450 surface_spectrum.push_back(
make_vec2(700, 0.1f));
1453 radiation.setSourceSpectrum(source_ID, source_spectrum);
1455 float integrated_product = radiation.integrateSpectrum(source_ID, surface_spectrum, 400, 700);
1458 DOCTEST_CHECK(integrated_product > 0.0f);
1459 DOCTEST_CHECK(integrated_product <= 1.0f);
1464 std::vector<helios::vec2> surface_spectrum;
1465 surface_spectrum.push_back(
make_vec2(400, 0.3f));
1466 surface_spectrum.push_back(
make_vec2(500, 0.7f));
1467 surface_spectrum.push_back(
make_vec2(600, 0.5f));
1468 surface_spectrum.push_back(
make_vec2(700, 0.2f));
1470 std::vector<helios::vec2> camera_response;
1471 camera_response.push_back(
make_vec2(400, 0.1f));
1472 camera_response.push_back(
make_vec2(500, 0.8f));
1473 camera_response.push_back(
make_vec2(600, 0.9f));
1474 camera_response.push_back(
make_vec2(700, 0.3f));
1476 float camera_integrated = radiation.integrateSpectrum(surface_spectrum, camera_response);
1477 DOCTEST_CHECK(camera_integrated >= 0.0f);
1478 DOCTEST_CHECK(camera_integrated <= 1.0f);
1482DOCTEST_TEST_CASE(
"RadiationModel Spectral Radiative Properties Setting and Validation") {
1486 radiation.disableMessages();
1494 std::vector<helios::vec2> leaf_reflectivity;
1495 leaf_reflectivity.push_back(
make_vec2(400, 0.05f));
1496 leaf_reflectivity.push_back(
make_vec2(500, 0.10f));
1497 leaf_reflectivity.push_back(
make_vec2(600, 0.08f));
1498 leaf_reflectivity.push_back(
make_vec2(700, 0.45f));
1499 leaf_reflectivity.push_back(
make_vec2(800, 0.50f));
1501 std::vector<helios::vec2> leaf_transmissivity;
1502 leaf_transmissivity.push_back(
make_vec2(400, 0.02f));
1503 leaf_transmissivity.push_back(
make_vec2(500, 0.05f));
1504 leaf_transmissivity.push_back(
make_vec2(600, 0.04f));
1505 leaf_transmissivity.push_back(
make_vec2(700, 0.40f));
1506 leaf_transmissivity.push_back(
make_vec2(800, 0.45f));
1508 context.
setGlobalData(
"test_leaf_reflectivity", leaf_reflectivity);
1509 context.
setGlobalData(
"test_leaf_transmissivity", leaf_transmissivity);
1512 context.
setPrimitiveData(patch_UUID,
"reflectivity_spectrum",
"test_leaf_reflectivity");
1513 context.
setPrimitiveData(patch_UUID,
"transmissivity_spectrum",
"test_leaf_transmissivity");
1516 std::string refl_spectrum_label;
1517 context.
getPrimitiveData(patch_UUID,
"reflectivity_spectrum", refl_spectrum_label);
1518 DOCTEST_CHECK(refl_spectrum_label ==
"test_leaf_reflectivity");
1520 std::string trans_spectrum_label;
1521 context.
getPrimitiveData(patch_UUID,
"transmissivity_spectrum", trans_spectrum_label);
1522 DOCTEST_CHECK(trans_spectrum_label ==
"test_leaf_transmissivity");
1525 std::vector<helios::vec2> retrieved_refl;
1526 context.
getGlobalData(
"test_leaf_reflectivity", retrieved_refl);
1527 DOCTEST_CHECK(retrieved_refl.size() == leaf_reflectivity.size());
1529 for (
size_t i = 0; i < retrieved_refl.size(); i++) {
1530 DOCTEST_CHECK(std::abs(retrieved_refl[i].x - leaf_reflectivity[i].x) < 1e-5f);
1531 DOCTEST_CHECK(std::abs(retrieved_refl[i].y - leaf_reflectivity[i].y) < 1e-5f);
1537 radiation.addRadiationBand(
"VIS", 400, 700);
1538 radiation.addRadiationBand(
"NIR", 700, 900);
1541 std::vector<helios::vec2> solar_spectrum;
1542 solar_spectrum.push_back(
make_vec2(400, 1.5f));
1543 solar_spectrum.push_back(
make_vec2(500, 2.0f));
1544 solar_spectrum.push_back(
make_vec2(600, 1.8f));
1545 solar_spectrum.push_back(
make_vec2(700, 1.2f));
1546 solar_spectrum.push_back(
make_vec2(800, 1.0f));
1547 solar_spectrum.push_back(
make_vec2(900, 0.8f));
1550 radiation.setSourceSpectrum(sun_source, solar_spectrum);
1552 radiation.setScatteringDepth(
"VIS", 0);
1553 radiation.setScatteringDepth(
"NIR", 0);
1554 radiation.disableEmission(
"VIS");
1555 radiation.disableEmission(
"NIR");
1558 radiation.updateGeometry();
1566 DOCTEST_CHECK(has_refl_spectrum);
1567 DOCTEST_CHECK(has_trans_spectrum);
1572 std::vector<helios::vec2> rgb_red_response;
1573 rgb_red_response.push_back(
make_vec2(400, 0.0f));
1574 rgb_red_response.push_back(
make_vec2(500, 0.1f));
1575 rgb_red_response.push_back(
make_vec2(600, 0.6f));
1576 rgb_red_response.push_back(
make_vec2(700, 0.9f));
1577 rgb_red_response.push_back(
make_vec2(800, 0.1f));
1579 context.
setGlobalData(
"rgb_red_response", rgb_red_response);
1583 camera_properties.
HFOV = 45.0f *
M_PI / 180.0f;
1585 radiation.addRadiationCamera(
"test_camera", {
"VIS"},
make_vec3(0, 0, 5),
make_vec3(0, 0, 0), camera_properties, 1);
1587 radiation.setCameraSpectralResponse(
"test_camera",
"VIS",
"rgb_red_response");
1591 radiation.updateGeometry();
1595 DOCTEST_CHECK(
true);
1599DOCTEST_TEST_CASE(
"RadiationModel Spectral Edge Cases and Error Handling") {
1603 radiation.disableMessages();
1607 std::vector<helios::vec2> empty_spectrum;
1610 bool caught_error =
false;
1612 float integral = radiation.integrateSpectrum(empty_spectrum);
1614 caught_error =
true;
1616 DOCTEST_CHECK(caught_error);
1621 std::vector<helios::vec2> single_point;
1622 single_point.push_back(
make_vec2(550, 0.5f));
1624 bool caught_error =
false;
1626 float integral = radiation.integrateSpectrum(single_point);
1628 caught_error =
true;
1630 DOCTEST_CHECK(caught_error);
1635 std::vector<helios::vec2> test_spectrum;
1636 test_spectrum.push_back(
make_vec2(400, 0.2f));
1637 test_spectrum.push_back(
make_vec2(600, 0.8f));
1638 test_spectrum.push_back(
make_vec2(800, 0.3f));
1640 bool caught_error =
false;
1643 float integral = radiation.integrateSpectrum(test_spectrum, 700, 500);
1645 caught_error =
true;
1647 DOCTEST_CHECK(caught_error);
1649 caught_error =
false;
1652 float integral = radiation.integrateSpectrum(test_spectrum, 600, 600);
1654 caught_error =
true;
1656 DOCTEST_CHECK(caught_error);
1661 std::vector<helios::vec2> non_monotonic;
1662 non_monotonic.push_back(
make_vec2(500, 0.3f));
1663 non_monotonic.push_back(
make_vec2(400, 0.5f));
1664 non_monotonic.push_back(
make_vec2(600, 0.2f));
1668 bool function_completed =
true;
1670 context.
setGlobalData(
"non_monotonic_spectrum", non_monotonic);
1672 context.
setPrimitiveData(patch,
"reflectivity_spectrum",
"non_monotonic_spectrum");
1674 radiation.addRadiationBand(
"test", 400, 700);
1675 radiation.updateGeometry();
1677 function_completed =
false;
1680 DOCTEST_CHECK(function_completed);
1685 std::vector<helios::vec2> limited_spectrum;
1686 limited_spectrum.push_back(
make_vec2(500, 0.3f));
1687 limited_spectrum.push_back(
make_vec2(600, 0.7f));
1690 float extended_integral = radiation.integrateSpectrum(limited_spectrum, 400, 800);
1691 float limited_integral = radiation.integrateSpectrum(limited_spectrum, 500, 600);
1694 DOCTEST_CHECK(extended_integral == 0.0f);
1695 DOCTEST_CHECK(limited_integral > 0.0f);
1699DOCTEST_TEST_CASE(
"RadiationModel Spectral Caching and Performance Validation") {
1703 radiation.disableMessages();
1708 std::vector<helios::vec2> common_spectrum;
1709 common_spectrum.push_back(
make_vec2(400, 0.1f));
1710 common_spectrum.push_back(
make_vec2(500, 0.5f));
1711 common_spectrum.push_back(
make_vec2(600, 0.3f));
1712 common_spectrum.push_back(
make_vec2(700, 0.2f));
1714 context.
setGlobalData(
"common_leaf_spectrum", common_spectrum);
1717 std::vector<uint> patch_UUIDs;
1718 for (
int i = 0; i < 10; i++) {
1720 context.
setPrimitiveData(patch,
"reflectivity_spectrum",
"common_leaf_spectrum");
1721 context.
setPrimitiveData(patch,
"transmissivity_spectrum",
"common_leaf_spectrum");
1722 patch_UUIDs.push_back(patch);
1726 radiation.addRadiationBand(
"test_band", 400, 700);
1728 radiation.setSourceSpectrum(source, common_spectrum);
1730 radiation.disableEmission(
"test_band");
1731 radiation.setScatteringDepth(
"test_band", 0);
1734 auto start_time = std::chrono::high_resolution_clock::now();
1735 radiation.updateGeometry();
1736 auto end_time = std::chrono::high_resolution_clock::now();
1738 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
1741 DOCTEST_CHECK(duration.count() < 10000000);
1744 for (
uint patch_UUID: patch_UUIDs) {
1747 DOCTEST_CHECK(has_spectrum);
1752DOCTEST_TEST_CASE(
"RadiationModel Spectral Library Integration") {
1756 radiation.disableMessages();
1764 bool library_available =
false;
1766 context.
setPrimitiveData(patch,
"reflectivity_spectrum",
"leaf_reflectivity");
1769 library_available =
false;
1772 if (library_available) {
1774 radiation.addRadiationBand(
"test", 400, 800);
1775 radiation.updateGeometry();
1777 std::string spectrum_label;
1779 DOCTEST_CHECK(spectrum_label ==
"leaf_reflectivity");
1782 DOCTEST_CHECK(
true);
1787DOCTEST_TEST_CASE(
"RadiationModel Multi-Spectrum Primitive Assignment") {
1791 radiation.disableMessages();
1794 std::vector<helios::vec2> red_spectrum;
1795 red_spectrum.push_back(
make_vec2(400, 0.1f));
1796 red_spectrum.push_back(
make_vec2(500, 0.1f));
1797 red_spectrum.push_back(
make_vec2(600, 0.8f));
1798 red_spectrum.push_back(
make_vec2(700, 0.9f));
1800 std::vector<helios::vec2> green_spectrum;
1801 green_spectrum.push_back(
make_vec2(400, 0.1f));
1802 green_spectrum.push_back(
make_vec2(500, 0.8f));
1803 green_spectrum.push_back(
make_vec2(600, 0.9f));
1804 green_spectrum.push_back(
make_vec2(700, 0.1f));
1806 std::vector<helios::vec2> blue_spectrum;
1807 blue_spectrum.push_back(
make_vec2(400, 0.9f));
1808 blue_spectrum.push_back(
make_vec2(500, 0.8f));
1809 blue_spectrum.push_back(
make_vec2(600, 0.1f));
1810 blue_spectrum.push_back(
make_vec2(700, 0.1f));
1818 std::vector<uint> red_patches, green_patches, blue_patches;
1821 for (
int i = 0; i < 5; i++) {
1824 red_patches.push_back(patch);
1828 for (
int i = 0; i < 5; i++) {
1830 context.
setPrimitiveData(patch,
"reflectivity_spectrum",
"green_spectrum");
1831 green_patches.push_back(patch);
1835 for (
int i = 0; i < 5; i++) {
1838 blue_patches.push_back(patch);
1842 radiation.addRadiationBand(
"R", 600, 700);
1843 radiation.addRadiationBand(
"G", 500, 600);
1844 radiation.addRadiationBand(
"B", 400, 500);
1847 radiation.setDiffuseRayCount(
"R", 10000);
1848 radiation.setDiffuseRayCount(
"G", 10000);
1849 radiation.setDiffuseRayCount(
"B", 10000);
1853 std::vector<helios::vec2> uniform_spectrum;
1854 uniform_spectrum.push_back(
make_vec2(300, 1.0f));
1855 uniform_spectrum.push_back(
make_vec2(800, 1.0f));
1856 radiation.setSourceSpectrum(source, uniform_spectrum);
1857 radiation.setSourceFlux(source,
"R", 1000.0f);
1858 radiation.setSourceFlux(source,
"G", 1000.0f);
1859 radiation.setSourceFlux(source,
"B", 1000.0f);
1860 radiation.setDirectRayCount(
"R", 1000);
1861 radiation.setDirectRayCount(
"G", 1000);
1862 radiation.setDirectRayCount(
"B", 1000);
1866 std::vector<helios::vec2> camera_spectrum;
1867 camera_spectrum.push_back(
make_vec2(400, 0.3f));
1868 camera_spectrum.push_back(
make_vec2(500, 0.9f));
1869 camera_spectrum.push_back(
make_vec2(600, 0.8f));
1870 camera_spectrum.push_back(
make_vec2(700, 0.2f));
1874 std::vector<helios::vec2> camera_spectrum2;
1875 camera_spectrum2.push_back(
make_vec2(400, 0.2f));
1876 camera_spectrum2.push_back(
make_vec2(500, 0.3f));
1877 camera_spectrum2.push_back(
make_vec2(600, 0.8f));
1878 camera_spectrum2.push_back(
make_vec2(700, 0.9f));
1879 context.
setGlobalData(
"camera2_spectrum", camera_spectrum2);
1881 std::vector<std::string> band_labels = {
"R",
"G",
"B"};
1884 camera_props.
HFOV = 2.0f;
1886 radiation.addRadiationCamera(
"camera1", band_labels,
make_vec3(0, 0, 5),
make_vec3(0, 0, 0), camera_props, 100);
1887 radiation.setCameraSpectralResponse(
"camera1",
"R",
"camera1_spectrum");
1888 radiation.setCameraSpectralResponse(
"camera1",
"G",
"camera1_spectrum");
1889 radiation.setCameraSpectralResponse(
"camera1",
"B",
"camera1_spectrum");
1891 radiation.addRadiationCamera(
"camera2", band_labels,
make_vec3(5, 0, 5),
make_vec3(0, 0, 0), camera_props, 100);
1892 radiation.setCameraSpectralResponse(
"camera2",
"R",
"camera2_spectrum");
1893 radiation.setCameraSpectralResponse(
"camera2",
"G",
"camera2_spectrum");
1894 radiation.setCameraSpectralResponse(
"camera2",
"B",
"camera2_spectrum");
1896 radiation.disableEmission(
"R");
1897 radiation.disableEmission(
"G");
1898 radiation.disableEmission(
"B");
1899 radiation.setScatteringDepth(
"R", 1);
1900 radiation.setScatteringDepth(
"G", 1);
1901 radiation.setScatteringDepth(
"B", 1);
1904 radiation.updateGeometry();
1907 radiation.runBand(
"R");
1908 radiation.runBand(
"G");
1909 radiation.runBand(
"B");
1913 float red_patch_R_flux = 0, red_patch_G_flux = 0, red_patch_B_flux = 0;
1914 for (
uint patch: red_patches) {
1915 float flux_R, flux_G, flux_B;
1919 red_patch_R_flux += flux_R;
1920 red_patch_G_flux += flux_G;
1921 red_patch_B_flux += flux_B;
1923 red_patch_R_flux /= red_patches.size();
1924 red_patch_G_flux /= red_patches.size();
1925 red_patch_B_flux /= red_patches.size();
1928 float green_patch_R_flux = 0, green_patch_G_flux = 0, green_patch_B_flux = 0;
1929 for (
uint patch: green_patches) {
1930 float flux_R, flux_G, flux_B;
1934 green_patch_R_flux += flux_R;
1935 green_patch_G_flux += flux_G;
1936 green_patch_B_flux += flux_B;
1938 green_patch_R_flux /= green_patches.size();
1939 green_patch_G_flux /= green_patches.size();
1940 green_patch_B_flux /= green_patches.size();
1943 float blue_patch_R_flux = 0, blue_patch_G_flux = 0, blue_patch_B_flux = 0;
1944 for (
uint patch: blue_patches) {
1945 float flux_R, flux_G, flux_B;
1949 blue_patch_R_flux += flux_R;
1950 blue_patch_G_flux += flux_G;
1951 blue_patch_B_flux += flux_B;
1953 blue_patch_R_flux /= blue_patches.size();
1954 blue_patch_G_flux /= blue_patches.size();
1955 blue_patch_B_flux /= blue_patches.size();
1959 DOCTEST_CHECK(red_patch_R_flux < red_patch_G_flux);
1960 DOCTEST_CHECK(red_patch_R_flux < red_patch_B_flux);
1963 DOCTEST_CHECK(green_patch_G_flux < green_patch_R_flux);
1964 DOCTEST_CHECK(green_patch_G_flux < green_patch_B_flux);
1967 DOCTEST_CHECK(blue_patch_B_flux < blue_patch_R_flux);
1968 DOCTEST_CHECK(blue_patch_B_flux < blue_patch_G_flux);
1971 for (
uint i = 1; i < red_patches.size(); i++) {
1972 float flux_R_0, flux_R_i;
1975 DOCTEST_CHECK(std::abs(flux_R_0 - flux_R_i) / flux_R_0 < 0.15f);
1979DOCTEST_TEST_CASE(
"RadiationModel Band-Specific Camera Spectral Response") {
1983 radiation.disableMessages();
1987 std::vector<helios::vec2> red_spectrum;
1988 red_spectrum.push_back(
make_vec2(400, 0.1f));
1989 red_spectrum.push_back(
make_vec2(500, 0.1f));
1990 red_spectrum.push_back(
make_vec2(600, 0.8f));
1991 red_spectrum.push_back(
make_vec2(700, 0.9f));
1995 std::vector<helios::vec2> green_spectrum;
1996 green_spectrum.push_back(
make_vec2(400, 0.1f));
1997 green_spectrum.push_back(
make_vec2(500, 0.8f));
1998 green_spectrum.push_back(
make_vec2(600, 0.9f));
1999 green_spectrum.push_back(
make_vec2(700, 0.1f));
2003 std::vector<helios::vec2> blue_spectrum;
2004 blue_spectrum.push_back(
make_vec2(400, 0.9f));
2005 blue_spectrum.push_back(
make_vec2(500, 0.8f));
2006 blue_spectrum.push_back(
make_vec2(600, 0.1f));
2007 blue_spectrum.push_back(
make_vec2(700, 0.1f));
2011 std::vector<uint> red_patches, green_patches, blue_patches, white_patches;
2014 for (
int i = 0; i < 2; i++) {
2017 red_patches.push_back(patch);
2021 for (
int i = 0; i < 2; i++) {
2023 context.
setPrimitiveData(patch,
"reflectivity_spectrum",
"green_spectrum");
2024 green_patches.push_back(patch);
2028 for (
int i = 0; i < 2; i++) {
2031 blue_patches.push_back(patch);
2035 for (
int i = 0; i < 2; i++) {
2037 context.
setPrimitiveData(patch,
"reflectivity_spectrum",
"white_spectrum");
2038 white_patches.push_back(patch);
2042 radiation.addRadiationBand(
"R", 600, 700);
2043 radiation.addRadiationBand(
"G", 500, 600);
2044 radiation.addRadiationBand(
"B", 400, 500);
2047 radiation.setDiffuseRayCount(
"R", 10000);
2048 radiation.setDiffuseRayCount(
"G", 10000);
2049 radiation.setDiffuseRayCount(
"B", 10000);
2053 std::vector<helios::vec2> uniform_spectrum;
2054 uniform_spectrum.push_back(
make_vec2(350, 1.0f));
2055 uniform_spectrum.push_back(
make_vec2(800, 1.0f));
2056 radiation.setSourceSpectrum(source, uniform_spectrum);
2057 radiation.setSourceFlux(source,
"R", 1000.0f);
2058 radiation.setSourceFlux(source,
"G", 1000.0f);
2059 radiation.setSourceFlux(source,
"B", 1000.0f);
2063 std::vector<std::string> band_labels = {
"R",
"G",
"B"};
2066 camera_props.
HFOV = 2.0f;
2069 std::vector<helios::vec2> cam1_R_spectrum;
2070 cam1_R_spectrum.push_back(
make_vec2(600, 1.0f));
2071 cam1_R_spectrum.push_back(
make_vec2(700, 1.0f));
2074 std::vector<helios::vec2> cam1_G_spectrum;
2075 cam1_G_spectrum.push_back(
make_vec2(500, 0.05f));
2076 cam1_G_spectrum.push_back(
make_vec2(600, 0.05f));
2079 std::vector<helios::vec2> cam1_B_spectrum;
2080 cam1_B_spectrum.push_back(
make_vec2(400, 0.05f));
2081 cam1_B_spectrum.push_back(
make_vec2(500, 0.05f));
2084 radiation.addRadiationCamera(
"camera1", band_labels,
make_vec3(0, 0, 5),
make_vec3(0, 0, 0), camera_props, 100);
2085 radiation.setCameraSpectralResponse(
"camera1",
"R",
"cam1_R_spectrum");
2086 radiation.setCameraSpectralResponse(
"camera1",
"G",
"cam1_G_spectrum");
2087 radiation.setCameraSpectralResponse(
"camera1",
"B",
"cam1_B_spectrum");
2090 std::vector<helios::vec2> cam2_R_spectrum;
2091 cam2_R_spectrum.push_back(
make_vec2(600, 0.05f));
2092 cam2_R_spectrum.push_back(
make_vec2(700, 0.05f));
2095 std::vector<helios::vec2> cam2_G_spectrum;
2096 cam2_G_spectrum.push_back(
make_vec2(500, 0.3f));
2097 cam2_G_spectrum.push_back(
make_vec2(600, 0.3f));
2100 std::vector<helios::vec2> cam2_B_spectrum;
2101 cam2_B_spectrum.push_back(
make_vec2(400, 1.0f));
2102 cam2_B_spectrum.push_back(
make_vec2(500, 1.0f));
2105 radiation.addRadiationCamera(
"camera2", band_labels,
make_vec3(5, 0, 5),
make_vec3(0, 0, 0), camera_props, 100);
2106 radiation.setCameraSpectralResponse(
"camera2",
"R",
"cam2_R_spectrum");
2107 radiation.setCameraSpectralResponse(
"camera2",
"G",
"cam2_G_spectrum");
2108 radiation.setCameraSpectralResponse(
"camera2",
"B",
"cam2_B_spectrum");
2110 radiation.disableEmission(
"R");
2111 radiation.disableEmission(
"G");
2112 radiation.disableEmission(
"B");
2113 radiation.setScatteringDepth(
"R", 1);
2114 radiation.setScatteringDepth(
"G", 1);
2115 radiation.setScatteringDepth(
"B", 1);
2119 DOCTEST_CHECK_NOTHROW(radiation.updateGeometry());
2122 radiation.runBand(
"R");
2123 radiation.runBand(
"G");
2124 radiation.runBand(
"B");
2127 uint red_patch = red_patches[0];
2128 float red_flux_R, red_flux_G, red_flux_B;
2133 uint green_patch = green_patches[0];
2134 float green_flux_R, green_flux_G, green_flux_B;
2139 uint blue_patch = blue_patches[0];
2140 float blue_flux_R, blue_flux_G, blue_flux_B;
2174DOCTEST_TEST_CASE(
"CameraCalibration Basic Functionality") {
2179 std::vector<uint> calibrite_UUIDs = calibration.addCalibriteColorboard(
make_vec3(0, 0.5, 0.001), 0.05);
2180 DOCTEST_CHECK(calibrite_UUIDs.size() == 24);
2183 std::vector<uint> all_colorboard_UUIDs = calibration.getColorBoardUUIDs();
2184 DOCTEST_CHECK(all_colorboard_UUIDs.size() == 24);
2187 std::vector<uint> all_UUIDs = context.
getAllUUIDs();
2188 DOCTEST_CHECK(all_UUIDs.size() >= 24);
2191 int patches_with_reflectivity = 0;
2192 for (
uint UUID: calibrite_UUIDs) {
2194 patches_with_reflectivity++;
2197 DOCTEST_CHECK(patches_with_reflectivity == 24);
2201 std::vector<uint> spyder_UUIDs = calibration2.addSpyderCHECKRColorboard(
make_vec3(0.5, 0.5, 0.001), 0.05);
2202 DOCTEST_CHECK(spyder_UUIDs.size() == 24);
2205 patches_with_reflectivity = 0;
2206 for (
uint UUID: spyder_UUIDs) {
2208 patches_with_reflectivity++;
2211 DOCTEST_CHECK(patches_with_reflectivity == 24);
2214 std::vector<helios::vec2> test_spectrum;
2215 test_spectrum.push_back(
make_vec2(400.0f, 0.1f));
2216 test_spectrum.push_back(
make_vec2(500.0f, 0.5f));
2217 test_spectrum.push_back(
make_vec2(600.0f, 0.8f));
2218 test_spectrum.push_back(
make_vec2(700.0f, 0.3f));
2221 bool write_success = calibration.writeSpectralXMLfile(
"/tmp/test_spectrum.xml",
"Test spectrum",
"test_label", &test_spectrum);
2222 DOCTEST_CHECK(write_success ==
true);
2225DOCTEST_TEST_CASE(
"CameraCalibration DGK Integration") {
2236 std::vector<uint> colorboard_UUIDs = calibration.getColorBoardUUIDs();
2238 DOCTEST_CHECK(colorboard_UUIDs.size() == 0);
2241 std::vector<uint> test_patches;
2242 for (
int i = 0; i < 18; i++) {
2244 test_patches.push_back(patch);
2250 std::vector<uint> all_UUIDs = context.
getAllUUIDs();
2251 DOCTEST_CHECK(all_UUIDs.size() >= 18);
2254 int dgk_labeled_patches = 0;
2255 for (
uint UUID: test_patches) {
2257 dgk_labeled_patches++;
2260 DOCTEST_CHECK(dgk_labeled_patches == 18);
2266DOCTEST_TEST_CASE(
"RadiationModel CCM Export and Import") {
2271 std::vector<std::string> band_labels = {
"red",
"green",
"blue"};
2272 std::string camera_label =
"test_camera";
2278 camera_properties.
HFOV = 45.0f;
2283 radiationmodel.addRadiationCamera(camera_label, band_labels,
make_vec3(0, 0, 1),
make_vec3(0, 0, 0), camera_properties, 1);
2286 size_t pixel_count = resolution.
x * resolution.
y;
2287 std::vector<float> red_data(pixel_count, 0.8f);
2288 std::vector<float> green_data(pixel_count, 0.6f);
2289 std::vector<float> blue_data(pixel_count, 0.4f);
2292 radiationmodel.setCameraPixelData(camera_label,
"red", red_data);
2293 radiationmodel.setCameraPixelData(camera_label,
"green", green_data);
2294 radiationmodel.setCameraPixelData(camera_label,
"blue", blue_data);
2299 std::vector<std::vector<float>> test_matrix = {{1.2f, -0.1f, 0.05f}, {-0.08f, 1.15f, 0.02f}, {0.03f, -0.12f, 1.18f}};
2301 std::string ccm_file_path =
"/tmp/test_ccm_3x3.xml";
2304 radiationmodel.exportColorCorrectionMatrixXML(ccm_file_path, camera_label, test_matrix,
"/path/to/test_image.jpg",
"DGK", 15.5f);
2307 std::ifstream test_file(ccm_file_path);
2308 DOCTEST_CHECK(test_file.good());
2312 std::string loaded_camera_label;
2313 std::vector<std::vector<float>> loaded_matrix = radiationmodel.loadColorCorrectionMatrixXML(ccm_file_path, loaded_camera_label);
2316 DOCTEST_CHECK(loaded_camera_label == camera_label);
2317 DOCTEST_CHECK(loaded_matrix.size() == 3);
2318 DOCTEST_CHECK(loaded_matrix[0].size() == 3);
2321 for (
size_t i = 0; i < 3; i++) {
2322 for (
size_t j = 0; j < 3; j++) {
2323 DOCTEST_CHECK(std::abs(loaded_matrix[i][j] - test_matrix[i][j]) < 1e-5f);
2328 std::remove(ccm_file_path.c_str());
2334 std::vector<std::vector<float>> test_matrix_4x3 = {{1.1f, -0.05f, 0.02f, 0.01f}, {-0.04f, 1.08f, 0.01f, -0.005f}, {0.02f, -0.06f, 1.12f, 0.008f}};
2336 std::string ccm_file_path =
"/tmp/test_ccm_4x3.xml";
2339 radiationmodel.exportColorCorrectionMatrixXML(ccm_file_path, camera_label, test_matrix_4x3,
"/path/to/test_image.jpg",
"Calibrite", 12.3f);
2342 std::string loaded_camera_label;
2343 std::vector<std::vector<float>> loaded_matrix = radiationmodel.loadColorCorrectionMatrixXML(ccm_file_path, loaded_camera_label);
2345 DOCTEST_CHECK(loaded_camera_label == camera_label);
2346 DOCTEST_CHECK(loaded_matrix.size() == 3);
2347 DOCTEST_CHECK(loaded_matrix[0].size() == 4);
2350 for (
size_t i = 0; i < 3; i++) {
2351 for (
size_t j = 0; j < 4; j++) {
2352 DOCTEST_CHECK(std::abs(loaded_matrix[i][j] - test_matrix_4x3[i][j]) < 1e-5f);
2357 std::remove(ccm_file_path.c_str());
2363 std::vector<std::vector<float>> test_matrix = {{1.1f, -0.05f, 0.02f}, {-0.03f, 1.08f, 0.01f}, {0.01f, -0.04f, 1.12f}};
2365 std::string ccm_file_path =
"/tmp/test_apply_ccm_3x3.xml";
2366 radiationmodel.exportColorCorrectionMatrixXML(ccm_file_path, camera_label, test_matrix,
"/path/to/test.jpg",
"DGK", 10.0f);
2369 std::vector<float> initial_red = radiationmodel.getCameraPixelData(camera_label,
"red");
2370 std::vector<float> initial_green = radiationmodel.getCameraPixelData(camera_label,
"green");
2371 std::vector<float> initial_blue = radiationmodel.getCameraPixelData(camera_label,
"blue");
2374 radiationmodel.applyCameraColorCorrectionMatrix(camera_label,
"red",
"green",
"blue", ccm_file_path);
2377 std::vector<float> corrected_red = radiationmodel.getCameraPixelData(camera_label,
"red");
2378 std::vector<float> corrected_green = radiationmodel.getCameraPixelData(camera_label,
"green");
2379 std::vector<float> corrected_blue = radiationmodel.getCameraPixelData(camera_label,
"blue");
2383 float expected_red = test_matrix[0][0] * initial_red[0] + test_matrix[0][1] * initial_green[0] + test_matrix[0][2] * initial_blue[0];
2384 float expected_green = test_matrix[1][0] * initial_red[0] + test_matrix[1][1] * initial_green[0] + test_matrix[1][2] * initial_blue[0];
2385 float expected_blue = test_matrix[2][0] * initial_red[0] + test_matrix[2][1] * initial_green[0] + test_matrix[2][2] * initial_blue[0];
2387 DOCTEST_CHECK(std::abs(corrected_red[0] - expected_red) < 1e-5f);
2388 DOCTEST_CHECK(std::abs(corrected_green[0] - expected_green) < 1e-5f);
2389 DOCTEST_CHECK(std::abs(corrected_blue[0] - expected_blue) < 1e-5f);
2392 std::remove(ccm_file_path.c_str());
2398 std::vector<std::vector<float>> test_matrix = {{1.05f, -0.02f, 0.01f, 0.005f}, {-0.01f, 1.03f, 0.005f, -0.002f}, {0.005f, -0.015f, 1.08f, 0.003f}};
2400 std::string ccm_file_path =
"/tmp/test_apply_ccm_4x3.xml";
2401 radiationmodel.exportColorCorrectionMatrixXML(ccm_file_path, camera_label, test_matrix,
"/path/to/test.jpg",
"SpyderCHECKR", 8.5f);
2404 std::fill(red_data.begin(), red_data.end(), 0.7f);
2405 std::fill(green_data.begin(), green_data.end(), 0.5f);
2406 std::fill(blue_data.begin(), blue_data.end(), 0.3f);
2408 radiationmodel.setCameraPixelData(camera_label,
"red", red_data);
2409 radiationmodel.setCameraPixelData(camera_label,
"green", green_data);
2410 radiationmodel.setCameraPixelData(camera_label,
"blue", blue_data);
2413 radiationmodel.applyCameraColorCorrectionMatrix(camera_label,
"red",
"green",
"blue", ccm_file_path);
2416 std::vector<float> corrected_red = radiationmodel.getCameraPixelData(camera_label,
"red");
2417 std::vector<float> corrected_green = radiationmodel.getCameraPixelData(camera_label,
"green");
2418 std::vector<float> corrected_blue = radiationmodel.getCameraPixelData(camera_label,
"blue");
2421 float expected_red = test_matrix[0][0] * 0.7f + test_matrix[0][1] * 0.5f + test_matrix[0][2] * 0.3f + test_matrix[0][3];
2422 float expected_green = test_matrix[1][0] * 0.7f + test_matrix[1][1] * 0.5f + test_matrix[1][2] * 0.3f + test_matrix[1][3];
2423 float expected_blue = test_matrix[2][0] * 0.7f + test_matrix[2][1] * 0.5f + test_matrix[2][2] * 0.3f + test_matrix[2][3];
2425 DOCTEST_CHECK(std::abs(corrected_red[0] - expected_red) < 1e-5f);
2426 DOCTEST_CHECK(std::abs(corrected_green[0] - expected_green) < 1e-5f);
2427 DOCTEST_CHECK(std::abs(corrected_blue[0] - expected_blue) < 1e-5f);
2430 std::remove(ccm_file_path.c_str());
2434DOCTEST_TEST_CASE(
"RadiationModel CCM Error Handling") {
2440 std::string camera_label;
2441 bool exception_thrown =
false;
2443 std::vector<std::vector<float>> matrix = radiationmodel.loadColorCorrectionMatrixXML(
"/nonexistent/path.xml", camera_label);
2444 }
catch (
const std::runtime_error &e) {
2445 exception_thrown =
true;
2446 std::string error_msg(e.what());
2447 DOCTEST_CHECK(error_msg.find(
"Failed to open file for reading") != std::string::npos);
2449 DOCTEST_CHECK(exception_thrown);
2454 std::string malformed_ccm_path =
"/tmp/malformed_ccm.xml";
2455 std::ofstream malformed_file(malformed_ccm_path);
2456 malformed_file <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2457 malformed_file <<
"<helios>\n";
2458 malformed_file <<
" <InvalidTag>\n";
2459 malformed_file <<
" <row>1.0 0.0 0.0</row>\n";
2460 malformed_file <<
" </InvalidTag>\n";
2461 malformed_file <<
"</helios>\n";
2462 malformed_file.close();
2464 std::string camera_label;
2465 bool exception_thrown =
false;
2467 std::vector<std::vector<float>> matrix = radiationmodel.loadColorCorrectionMatrixXML(malformed_ccm_path, camera_label);
2468 }
catch (
const std::runtime_error &e) {
2469 exception_thrown =
true;
2470 std::string error_msg(e.what());
2471 DOCTEST_CHECK(error_msg.find(
"No matrix data found") != std::string::npos);
2473 DOCTEST_CHECK(exception_thrown);
2475 std::remove(malformed_ccm_path.c_str());
2480 std::string ccm_file_path =
"/tmp/test_error_ccm.xml";
2481 std::vector<std::vector<float>> identity_matrix = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}};
2483 radiationmodel.exportColorCorrectionMatrixXML(ccm_file_path,
"test_camera", identity_matrix,
"/test.jpg",
"DGK", 5.0f);
2485 bool exception_thrown =
false;
2487 radiationmodel.applyCameraColorCorrectionMatrix(
"nonexistent_camera",
"red",
"green",
"blue", ccm_file_path);
2488 }
catch (
const std::runtime_error &e) {
2489 exception_thrown =
true;
2490 std::string error_msg(e.what());
2491 DOCTEST_CHECK(error_msg.find(
"Camera 'nonexistent_camera' does not exist") != std::string::npos);
2493 DOCTEST_CHECK(exception_thrown);
2495 std::remove(ccm_file_path.c_str());