1.3.49
 
Loading...
Searching...
No Matches
RadiationModel.cpp
Go to the documentation of this file.
1
16#include "RadiationModel.h"
17#include <cmath>
18#include <ctime>
19#include <fstream>
20#include <iomanip>
21#include <sstream>
22#include <unordered_set>
23
24using namespace helios;
25
27
28 context = context_a;
29
30 // Asset directory registration removed - now using HELIOS_BUILD resolution
31
32 // All default values set here
33
34 message_flag = true;
35
36 directRayCount_default = 100;
37 diffuseRayCount_default = 1000;
38
39 diffuseFlux_default = -1.f;
40
41 minScatterEnergy_default = 0.1;
42 scatteringDepth_default = 0;
43
44 rho_default = 0.f;
45 tau_default = 0.f;
46 eps_default = 1.f;
47
48 kappa_default = 1.f;
49 sigmas_default = 0.f;
50
51 temperature_default = 300;
52
53 periodic_flag = make_vec2(0, 0);
54
55 spectral_library_files.push_back("plugins/radiation/spectral_data/camera_spectral_library.xml");
56 spectral_library_files.push_back("plugins/radiation/spectral_data/light_spectral_library.xml");
57 spectral_library_files.push_back("plugins/radiation/spectral_data/soil_surface_spectral_library.xml");
58 spectral_library_files.push_back("plugins/radiation/spectral_data/leaf_surface_spectral_library.xml");
59 spectral_library_files.push_back("plugins/radiation/spectral_data/bark_surface_spectral_library.xml");
60 spectral_library_files.push_back("plugins/radiation/spectral_data/fruit_surface_spectral_library.xml");
61 spectral_library_files.push_back("plugins/radiation/spectral_data/solar_spectrum_ASTMG173.xml");
62 spectral_library_files.push_back("plugins/radiation/spectral_data/color_board/Calibrite_ColorChecker_Classic_colorboard.xml");
63 spectral_library_files.push_back("plugins/radiation/spectral_data/color_board/DGK_DKK_colorboard.xml");
64
65 initializeOptiX();
66}
67
69 RT_CHECK_ERROR(rtContextDestroy(OptiX_Context));
70}
71
73 message_flag = false;
74}
75
77 message_flag = true;
78}
79
81
82 if (strcmp(label, "reflectivity") == 0 || strcmp(label, "transmissivity") == 0) {
83 output_prim_data.emplace_back(label);
84 } else {
85 std::cout << "WARNING (RadiationModel::optionalOutputPrimitiveData): unknown output primitive data " << label << std::endl;
86 }
87}
88
89void RadiationModel::setDirectRayCount(const std::string &label, size_t N) {
90 if (!doesBandExist(label)) {
91 helios_runtime_error("ERROR (RadiationModel::setDirectRayCount): Cannot set ray count for band '" + label + "' because it is not a valid band.");
92 }
93 radiation_bands.at(label).directRayCount = N;
94}
95
96void RadiationModel::setDiffuseRayCount(const std::string &label, size_t N) {
97 if (!doesBandExist(label)) {
98 helios_runtime_error("ERROR (RadiationModel::setDiffuseRayCount): Cannot set ray count for band '" + label + "' because it is not a valid band.");
99 }
100 radiation_bands.at(label).diffuseRayCount = N;
101}
102
103void RadiationModel::setDiffuseRadiationFlux(const std::string &label, float flux) {
104 if (!doesBandExist(label)) {
105 helios_runtime_error("ERROR (RadiationModel::setDiffuseRadiationFlux): Cannot set flux value for band '" + label + "' because it is not a valid band.");
106 }
107 radiation_bands.at(label).diffuseFlux = flux;
108}
109
110void RadiationModel::setDiffuseRadiationExtinctionCoeff(const std::string &label, float K, const SphericalCoord &peak_dir) {
112}
113
114void RadiationModel::setDiffuseRadiationExtinctionCoeff(const std::string &label, float K, const vec3 &peak_dir) {
115 if (!doesBandExist(label)) {
116 helios_runtime_error("ERROR (RadiationModel::setDiffuseRadiationExtinctionCoeff): Cannot set diffuse extinction value for band '" + label + "' because it is not a valid band.");
117 }
118
119 vec3 dir = peak_dir;
120 dir.normalize();
121
122 int N = 100;
123 float norm = 0.f;
124 for (int j = 0; j < N; j++) {
125 for (int i = 0; i < N; i++) {
126 float theta = 0.5f * M_PI / float(N) * (0.5f + float(i));
127 float phi = 2.f * M_PI / float(N) * (0.5f + float(j));
128 vec3 n = sphere2cart(make_SphericalCoord(0.5f * M_PI - theta, phi));
129
130 float psi = acos_safe(n * dir);
131 float fd;
132 if (psi < M_PI / 180.f) {
133 fd = powf(M_PI / 180.f, -K);
134 } else {
135 fd = powf(psi, -K);
136 }
137
138 norm += fd * cosf(theta) * sinf(theta) * M_PI / float(N * N);
139 // note: the multipication factors are dtheta*dphi/pi = (0.5*pi/N)*(2*pi/N)/pi = pi/N^2
140 }
141 }
142
143 radiation_bands.at(label).diffuseExtinction = K;
144 radiation_bands.at(label).diffusePeakDir = dir;
145 radiation_bands.at(label).diffuseDistNorm = 1.f / norm;
146}
147
148void RadiationModel::setDiffuseSpectrumIntegral(float spectrum_integral) {
149
150 for (auto &band: radiation_bands) {
151 setDiffuseSpectrumIntegral(band.first, spectrum_integral);
152 }
153}
154
155void RadiationModel::setDiffuseSpectrumIntegral(float spectrum_integral, float wavelength1, float wavelength2) {
156
157 for (auto &band: radiation_bands) {
158 setDiffuseSpectrumIntegral(band.first, spectrum_integral, wavelength1, wavelength2);
159 }
160}
161
162void RadiationModel::setDiffuseSpectrumIntegral(const std::string &band_label, float spectrum_integral) {
163
164 if (spectrum_integral < 0) {
165 helios_runtime_error("ERROR (RadiationModel::setDiffuseSpectrumIntegral): Source integral must be non-negative.");
166 } else if (!doesBandExist(band_label)) {
167 helios_runtime_error("ERROR (RadiationModel::setDiffuseSpectrumIntegral): Cannot set integral for band '" + band_label + "' because it is not a valid band.");
168 } else if (radiation_bands.at(band_label).diffuse_spectrum.empty()) {
169 std::cerr << "WARNING (RadiationModel::setDiffuseSpectrumIntegral): Diffuse spectral distribution has not been set for radiation band '" + band_label + "'. Cannot set its integral." << std::endl;
170 return;
171 }
172
173 float current_integral = integrateSpectrum(radiation_bands.at(band_label).diffuse_spectrum);
174
175 for (vec2 &wavelength: radiation_bands.at(band_label).diffuse_spectrum) {
176 wavelength.y *= spectrum_integral / current_integral;
177 }
178
179 radiativepropertiesneedupdate = true;
180}
181
182void RadiationModel::setDiffuseSpectrumIntegral(const std::string &band_label, float spectrum_integral, float wavelength1, float wavelength2) {
183
184 if (spectrum_integral < 0) {
185 helios_runtime_error("ERROR (RadiationModel::setDiffuseSpectrumIntegral): Source integral must be non-negative.");
186 } else if (!doesBandExist(band_label)) {
187 helios_runtime_error("ERROR (RadiationModel::setDiffuseSpectrumIntegral): Cannot set integral for band '" + band_label + "' because it is not a valid band.");
188 }
189
190 float current_integral = integrateSpectrum(radiation_bands.at(band_label).diffuse_spectrum, wavelength1, wavelength2);
191
192 for (vec2 &wavelength: radiation_bands.at(band_label).diffuse_spectrum) {
193 wavelength.y *= spectrum_integral / current_integral;
194 }
195
196 radiativepropertiesneedupdate = true;
197}
198
199void RadiationModel::addRadiationBand(const std::string &label) {
200
201 if (radiation_bands.find(label) != radiation_bands.end()) {
202 std::cerr << "WARNING (RadiationModel::addRadiationBand): Radiation band " << label << " has already been added. Skipping this call to addRadiationBand()." << std::endl;
203 return;
204 }
205
206 RadiationBand band(label, directRayCount_default, diffuseRayCount_default, diffuseFlux_default, scatteringDepth_default, minScatterEnergy_default);
207
208 radiation_bands.emplace(label, band);
209
210 // Initialize all radiation source fluxes
211 for (auto &source: radiation_sources) {
212 source.source_fluxes[label] = -1.f;
213 }
214
215 radiativepropertiesneedupdate = true;
216}
217
218void RadiationModel::addRadiationBand(const std::string &label, float wavelength1, float wavelength2) {
219
220 if (radiation_bands.find(label) != radiation_bands.end()) {
221 std::cerr << "WARNING (RadiationModel::addRadiationBand): Radiation band " << label << " has already been added. Skipping this call to addRadiationBand()." << std::endl;
222 return;
223 } else if (wavelength1 > wavelength2) {
224 helios_runtime_error("ERROR (RadiationModel::addRadiationBand): The upper wavelength bound for a band must be greater than the lower bound.");
225 } else if (wavelength2 - wavelength1 < 1) {
226 helios_runtime_error("ERROR (RadiationModel::addRadiationBand): The waveband range of a radiation band must be at least 1 nm.");
227 }
228
229 RadiationBand band(label, directRayCount_default, diffuseRayCount_default, diffuseFlux_default, scatteringDepth_default, minScatterEnergy_default);
230
231 band.wavebandBounds = make_vec2(wavelength1, wavelength2);
232
233 radiation_bands.emplace(label, band);
234
235 // Initialize all radiation source fluxes
236 for (auto &source: radiation_sources) {
237 source.source_fluxes[label] = -1.f;
238 }
239
240 radiativepropertiesneedupdate = true;
241}
242
243void RadiationModel::copyRadiationBand(const std::string &old_label, const std::string &new_label) {
244
245 if (!doesBandExist(old_label)) {
246 helios_runtime_error("ERROR (RadiationModel::copyRadiationBand): Cannot copy band " + old_label + " because it does not exist.");
247 }
248
249 vec2 waveBounds = radiation_bands.at(old_label).wavebandBounds;
250
251 copyRadiationBand(old_label, new_label, waveBounds.x, waveBounds.y);
252}
253
254void RadiationModel::copyRadiationBand(const std::string &old_label, const std::string &new_label, float wavelength_min, float wavelength_max) {
255
256 if (!doesBandExist(old_label)) {
257 helios_runtime_error("ERROR (RadiationModel::copyRadiationBand): Cannot copy band " + old_label + " because it does not exist.");
258 }
259
260 RadiationBand band = radiation_bands.at(old_label);
261 band.label = new_label;
262 band.wavebandBounds = make_vec2(wavelength_min, wavelength_max);
263
264 radiation_bands.emplace(new_label, band);
265
266 // copy source fluxes
267 for (auto &source: radiation_sources) {
268 source.source_fluxes[new_label] = source.source_fluxes.at(old_label);
269 }
270
271 radiativepropertiesneedupdate = true;
272}
273
274bool RadiationModel::doesBandExist(const std::string &label) const {
275 if (radiation_bands.find(label) == radiation_bands.end()) {
276 return false;
277 } else {
278 return true;
279 }
280}
281
282void RadiationModel::disableEmission(const std::string &label) {
283
284 if (!doesBandExist(label)) {
285 helios_runtime_error("ERROR (RadiationModel::disableEmission): Cannot disable emission for band '" + label + "' because it is not a valid band.");
286 }
287
288 radiation_bands.at(label).emissionFlag = false;
289}
290
291void RadiationModel::enableEmission(const std::string &label) {
292
293 if (!doesBandExist(label)) {
294 helios_runtime_error("ERROR (RadiationModel::enableEmission): Cannot disable emission for band '" + label + "' because it is not a valid band.");
295 }
296
297 radiation_bands.at(label).emissionFlag = true;
298}
299
304
308
310
311 if (direction.magnitude() == 0) {
312 helios_runtime_error("ERROR (RadiationModel::addCollimatedRadiationSource): Invalid collimated source direction. Direction vector should not have length of zero.");
313 }
314
315 uint Nsources = radiation_sources.size() + 1;
316 if (Nsources > 256) {
317 helios_runtime_error("ERROR (RadiationModel::addCollimatedRadiationSource): A maximum of 256 radiation sources are allowed.");
318 }
319
320 bool warn_multiple_suns = false;
321 for (auto &source: radiation_sources) {
322 if (source.source_type == RADIATION_SOURCE_TYPE_COLLIMATED || source.source_type == RADIATION_SOURCE_TYPE_SUN_SPHERE) {
323 warn_multiple_suns = true;
324 }
325 }
326 if (warn_multiple_suns) {
327 std::cerr << "WARNING (RadiationModel::addCollimatedRadiationSource): Multiple sun sources have been added to the radiation model. This may lead to unintended behavior." << std::endl;
328 }
329
330 RadiationSource collimated_source(direction);
331
332 // initialize fluxes
333 for (const auto &band: radiation_bands) {
334 collimated_source.source_fluxes[band.first] = -1.f;
335 }
336
337 radiation_sources.emplace_back(collimated_source);
338
339 radiativepropertiesneedupdate = true;
340
341 return Nsources - 1;
342}
343
344uint RadiationModel::addSphereRadiationSource(const vec3 &position, float radius) {
345
346 if (radius <= 0) {
347 helios_runtime_error("ERROR (RadiationModel::addSphereRadiationSource): Spherical radiation source radius must be positive.");
348 }
349
350 uint Nsources = radiation_sources.size() + 1;
351 if (Nsources > 256) {
352 helios_runtime_error("ERROR (RadiationModel::addSphereRadiationSource): A maximum of 256 radiation sources are allowed.");
353 }
354
355 RadiationSource sphere_source(position, 2.f * fabsf(radius));
356
357 // initialize fluxes
358 for (const auto &band: radiation_bands) {
359 sphere_source.source_fluxes[band.first] = -1.f;
360 }
361
362 radiation_sources.emplace_back(sphere_source);
363
364 uint sourceID = Nsources - 1;
365
366 if (islightvisualizationenabled) {
367 buildLightModelGeometry(sourceID);
368 }
369
370 radiativepropertiesneedupdate = true;
371
372 return sourceID;
373}
374
378
382
384
385 uint Nsources = radiation_sources.size() + 1;
386 if (Nsources > 256) {
387 helios_runtime_error("ERROR (RadiationModel::addSunSphereRadiationSource): A maximum of 256 radiation sources are allowed.");
388 }
389
390 bool warn_multiple_suns = false;
391 for (auto &source: radiation_sources) {
392 if (source.source_type == RADIATION_SOURCE_TYPE_COLLIMATED || source.source_type == RADIATION_SOURCE_TYPE_SUN_SPHERE) {
393 warn_multiple_suns = true;
394 }
395 }
396 if (warn_multiple_suns) {
397 std::cerr << "WARNING (RadiationModel::addSunSphereRadiationSource): Multiple sun sources have been added to the radiation model. This may lead to unintended behavior." << std::endl;
398 }
399
400 RadiationSource sphere_source(150e9 * sun_direction / sun_direction.magnitude(), 150e9, 2.f * 695.5e6, sigma * powf(5700, 4) / 1288.437f);
401
402 // initialize fluxes
403 for (const auto &band: radiation_bands) {
404 sphere_source.source_fluxes[band.first] = -1.f;
405 }
406
407 radiation_sources.emplace_back(sphere_source);
408
409 radiativepropertiesneedupdate = true;
410
411 return Nsources - 1;
412}
413
414uint RadiationModel::addRectangleRadiationSource(const vec3 &position, const vec2 &size, const vec3 &rotation) {
415
416 if (size.x <= 0 || size.y <= 0) {
417 helios_runtime_error("ERROR (RadiationModel::addRectangleRadiationSource): Radiation source size must be positive.");
418 }
419
420 uint Nsources = radiation_sources.size() + 1;
421 if (Nsources > 256) {
422 helios_runtime_error("ERROR (RadiationModel::addRectangleRadiationSource): A maximum of 256 radiation sources are allowed.");
423 }
424
425 RadiationSource rectangle_source(position, size, rotation);
426
427 // initialize fluxes
428 for (const auto &band: radiation_bands) {
429 rectangle_source.source_fluxes[band.first] = -1.f;
430 }
431
432 radiation_sources.emplace_back(rectangle_source);
433
434 uint sourceID = Nsources - 1;
435
436 if (islightvisualizationenabled) {
437 buildLightModelGeometry(sourceID);
438 }
439
440 radiativepropertiesneedupdate = true;
441
442 return sourceID;
443}
444
445uint RadiationModel::addDiskRadiationSource(const vec3 &position, float radius, const vec3 &rotation) {
446
447 if (radius <= 0) {
448 helios_runtime_error("ERROR (RadiationModel::addDiskRadiationSource): Disk radiation source radius must be positive.");
449 }
450
451 uint Nsources = radiation_sources.size() + 1;
452 if (Nsources > 256) {
453 helios_runtime_error("ERROR (RadiationModel::addDiskRadiationSource): A maximum of 256 radiation sources are allowed.");
454 }
455
456 RadiationSource disk_source(position, radius, rotation);
457
458 // initialize fluxes
459 for (const auto &band: radiation_bands) {
460 disk_source.source_fluxes[band.first] = -1.f;
461 }
462
463 radiation_sources.emplace_back(disk_source);
464
465 uint sourceID = Nsources - 1;
466
467 if (islightvisualizationenabled) {
468 buildLightModelGeometry(sourceID);
469 }
470
471 radiativepropertiesneedupdate = true;
472
473 return sourceID;
474}
475
477
478 if (sourceID >= radiation_sources.size()) {
479 helios_runtime_error("ERROR (RadiationModel::deleteRadiationSource): Source ID out of bounds. Only " + std::to_string(radiation_sources.size() - 1) + " radiation sources have been created.");
480 }
481
482 radiation_sources.erase(radiation_sources.begin() + sourceID);
483
484 radiativepropertiesneedupdate = true;
485}
486
487void RadiationModel::setSourceSpectrumIntegral(uint source_ID, float source_integral) {
488
489 if (source_ID >= radiation_sources.size()) {
490 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrumIntegral): Source ID out of bounds. Only " + std::to_string(radiation_sources.size() - 1) + " radiation sources have been created.");
491 } else if (source_integral < 0) {
492 helios_runtime_error("ERROR (RadiationModel::setSourceIntegral): Source integral must be non-negative.");
493 }
494
495 float current_integral = integrateSpectrum(radiation_sources.at(source_ID).source_spectrum);
496
497 for (vec2 &wavelength: radiation_sources.at(source_ID).source_spectrum) {
498 wavelength.y *= source_integral / current_integral;
499 }
500}
501
502void RadiationModel::setSourceSpectrumIntegral(uint source_ID, float source_integral, float wavelength1, float wavelength2) {
503
504 if (source_ID >= radiation_sources.size()) {
505 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrumIntegral): Source ID out of bounds. Only " + std::to_string(radiation_sources.size() - 1) + " radiation sources have been created.");
506 } else if (source_integral < 0) {
507 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrumIntegral): Source integral must be non-negative.");
508 } else if (radiation_sources.at(source_ID).source_spectrum.empty()) {
509 std::cout << "WARNING (RadiationModel::setSourceSpectrumIntegral): Spectral distribution has not been set for radiation source. Cannot set its integral." << std::endl;
510 return;
511 }
512
513 RadiationSource &source = radiation_sources.at(source_ID);
514
515 float old_integral = integrateSpectrum(source.source_spectrum, wavelength1, wavelength2);
516
517 for (vec2 &wavelength: source.source_spectrum) {
518 wavelength.y *= source_integral / old_integral;
519 }
520}
521
522void RadiationModel::setSourceFlux(uint source_ID, const std::string &label, float flux) {
523
524 if (!doesBandExist(label)) {
525 helios_runtime_error("ERROR (RadiationModel::setSourceFlux): Cannot add set source flux for band '" + label + "' because it is not a valid band.");
526 } else if (source_ID >= radiation_sources.size()) {
527 helios_runtime_error("ERROR (RadiationModel::setSourceFlux): Source ID out of bounds. Only " + std::to_string(radiation_sources.size() - 1) + " radiation sources have been created.");
528 } else if (flux < 0) {
529 helios_runtime_error("ERROR (RadiationModel::setSourceFlux): Source flux must be non-negative.");
530 // }else if( !radiation_sources.at(source_ID).source_spectrum.empty() ){
531 // std::cerr << "WARNING (RadiationModel::setSourceFlux): Source spectrum has previously been set for radiation source by calling RadiationModel::setSourceSpectrum(). The source spectrum will be ignored and overridden based on this
532 // call to RadiationModel::setSourceFlux()." << std::endl;
533 }
534
535 radiation_sources.at(source_ID).source_fluxes[label] = flux * radiation_sources.at(source_ID).source_flux_scaling_factor;
536}
537
538void RadiationModel::setSourceFlux(const std::vector<uint> &source_ID, const std::string &band_label, float flux) {
539 for (auto ID: source_ID) {
540 setSourceFlux(ID, band_label, flux);
541 }
542}
543
544float RadiationModel::getSourceFlux(uint source_ID, const std::string &label) const {
545
546 if (!doesBandExist(label)) {
547 helios_runtime_error("ERROR (RadiationModel::getSourceFlux): Cannot get source flux for band '" + label + "' because it is not a valid band.");
548 } else if (source_ID >= radiation_sources.size()) {
549 helios_runtime_error("ERROR (RadiationModel::getSourceFlux): Source ID out of bounds. Only " + std::to_string(radiation_sources.size() - 1) + " radiation sources have been created.");
550 } else if (radiation_sources.at(source_ID).source_fluxes.find(label) == radiation_sources.at(source_ID).source_fluxes.end()) {
551 helios_runtime_error("ERROR (RadiationModel::getSourceFlux): Cannot get flux for source #" + std::to_string(source_ID) + " because radiative band '" + label + "' does not exist.");
552 }
553
554 const RadiationSource &source = radiation_sources.at(source_ID);
555
556 if (!source.source_spectrum.empty() && source.source_fluxes.at(label) < 0.f) { // source spectrum was specified (and not overridden by setting source flux manually)
557 vec2 wavebounds = radiation_bands.at(label).wavebandBounds;
558 if (wavebounds == make_vec2(0, 0)) {
559 wavebounds = make_vec2(source.source_spectrum.front().x, source.source_spectrum.back().x);
560 }
561 return integrateSpectrum(source.source_spectrum, wavebounds.x, wavebounds.y) * source.source_flux_scaling_factor;
562 } else if (source.source_fluxes.at(label) < 0.f) {
563 return 0;
564 }
565
566 return source.source_fluxes.at(label);
567}
568
569void RadiationModel::setSourceSpectrum(uint source_ID, const std::vector<helios::vec2> &spectrum) {
570
571 if (source_ID >= radiation_sources.size()) {
572 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrum): Cannot add radiation spectra for this source because it is not a valid radiation source ID.\n");
573 }
574
575 // validate spectrum
576 for (auto s = 0; s < spectrum.size(); s++) {
577 // check that wavelengths are monotonic
578 if (s > 0 && spectrum.at(s).x <= spectrum.at(s - 1).x) {
579 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrum): Source spectral data validation failed. Wavelengths must increase monotonically.");
580 }
581 // check that wavelength is within a reasonable range
582 if (spectrum.at(s).x < 0 || spectrum.at(s).x > 100000) {
583 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrum): Source spectral data validation failed. Wavelength value of " + std::to_string(spectrum.at(s).x) + " appears to be erroneous.");
584 }
585 // check that flux is non-negative
586 if (spectrum.at(s).y < 0) {
587 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrum): Source spectral data validation failed. Flux value at wavelength of " + std::to_string(spectrum.at(s).x) + " appears is negative.");
588 }
589 }
590
591 radiation_sources.at(source_ID).source_spectrum = spectrum;
592
593 radiativepropertiesneedupdate = true;
594}
595
596void RadiationModel::setSourceSpectrum(const std::vector<uint> &source_ID, const std::vector<helios::vec2> &spectrum) {
597 for (auto ID: source_ID) {
598 setSourceSpectrum(ID, spectrum);
599 }
600}
601
602void RadiationModel::setSourceSpectrum(uint source_ID, const std::string &spectrum_label) {
603
604 if (source_ID >= radiation_sources.size()) {
605 helios_runtime_error("ERROR (RadiationModel::setSourceSpectrum): Cannot add radiation spectra for this source because it is not a valid radiation source ID.\n");
606 }
607
608 std::vector<vec2> spectrum = loadSpectralData(spectrum_label);
609
610 radiation_sources.at(source_ID).source_spectrum = spectrum;
611 radiation_sources.at(source_ID).source_spectrum_label = spectrum_label;
612
613 radiativepropertiesneedupdate = true;
614}
615
616void RadiationModel::setSourceSpectrum(const std::vector<uint> &source_ID, const std::string &spectrum_label) {
617 for (auto ID: source_ID) {
618 setSourceSpectrum(ID, spectrum_label);
619 }
620}
621
622void RadiationModel::setDiffuseSpectrum(const std::vector<std::string> &band_labels, const std::string &spectrum_label) {
623
624 std::vector<vec2> spectrum;
625
626 // standard solar spectrum
627 if (spectrum_label == "ASTMG173") {
628 spectrum = loadSpectralData("solar_spectrum_diffuse_ASTMG173");
629 } else {
630 spectrum = loadSpectralData(spectrum_label);
631 }
632
633 for (const auto &band: band_labels) {
634 if (!doesBandExist(band)) {
635 helios_runtime_error("ERROR (RadiationModel::setDiffuseSpectrum): Cannot set diffuse spectrum for band '" + band + "' because it is not a valid band.");
636 }
637
638 radiation_bands.at(band).diffuse_spectrum = spectrum;
639 }
640
641 radiativepropertiesneedupdate = true;
642}
643
644void RadiationModel::setDiffuseSpectrum(const std::string &band_label, const std::string &spectrum_label) {
645
646 setDiffuseSpectrum(std::vector<std::string>{band_label}, spectrum_label);
647}
648
649float RadiationModel::getDiffuseFlux(const std::string &band_label) const {
650
651 if (!doesBandExist(band_label)) {
652 helios_runtime_error("ERROR (RadiationModel::getDiffuseFlux): Cannot get diffuse flux for band '" + band_label + "' because it is not a valid band.");
653 }
654
655 const std::vector<vec2> &spectrum = radiation_bands.at(band_label).diffuse_spectrum;
656
657 if (!spectrum.empty() && radiation_bands.at(band_label).diffuseFlux < 0.f) { // source spectrum was specified (and not overridden by setting flux manually)
658 vec2 wavebounds = radiation_bands.at(band_label).wavebandBounds;
659 if (wavebounds == make_vec2(0, 0)) {
660 wavebounds = make_vec2(spectrum.front().x, spectrum.back().x);
661 }
662 return integrateSpectrum(spectrum, wavebounds.x, wavebounds.y);
663 }
664 if (radiation_bands.at(band_label).diffuseFlux < 0.f) {
665 return 0;
666 }
667
668 return radiation_bands.at(band_label).diffuseFlux;
669}
670
672 islightvisualizationenabled = true;
673
674 // build the geometry of any existing sources at this point
675 for (int s = 0; s < radiation_sources.size(); s++) {
676 buildLightModelGeometry(s);
677 }
678}
679
681 islightvisualizationenabled = false;
682 for (auto &UUIDs: source_model_UUIDs) {
683 context->deletePrimitive(UUIDs.second);
684 }
685}
686
688 iscameravisualizationenabled = true;
689
690 // build the geometry of any existing cameras at this point
691 for (auto &cam: cameras) {
692 buildCameraModelGeometry(cam.first);
693 }
694}
695
697 iscameravisualizationenabled = false;
698 for (auto &UUIDs: camera_model_UUIDs) {
699 context->deletePrimitive(UUIDs.second);
700 }
701}
702
703void RadiationModel::buildLightModelGeometry(uint sourceID) {
704
705 assert(sourceID < radiation_sources.size());
706
707 RadiationSource source = radiation_sources.at(sourceID);
708 if (source.source_type == RADIATION_SOURCE_TYPE_SPHERE) {
709 source_model_UUIDs[sourceID] = context->loadOBJ("SphereLightSource.obj", true);
710 } else if (source.source_type == RADIATION_SOURCE_TYPE_SUN_SPHERE) {
711 source_model_UUIDs[sourceID] = context->loadOBJ("SphereLightSource.obj", true);
712 } else if (source.source_type == RADIATION_SOURCE_TYPE_DISK) {
713 source_model_UUIDs[sourceID] = context->loadOBJ("DiskLightSource.obj", true);
714 context->scalePrimitive(source_model_UUIDs.at(sourceID), make_vec3(source.source_width.x, source.source_width.y, 0.05f * source.source_width.x));
715 std::vector<uint> UUIDs_arrow = context->loadOBJ("Arrow.obj", true);
716 source_model_UUIDs.at(sourceID).insert(source_model_UUIDs.at(sourceID).begin(), UUIDs_arrow.begin(), UUIDs_arrow.end());
717 context->scalePrimitive(UUIDs_arrow, make_vec3(1, 1, 1) * 0.25f * source.source_width.x);
718 } else if (source.source_type == RADIATION_SOURCE_TYPE_RECTANGLE) {
719 source_model_UUIDs[sourceID] = context->loadOBJ("RectangularLightSource.obj", true);
720 context->scalePrimitive(source_model_UUIDs.at(sourceID), make_vec3(source.source_width.x, source.source_width.y, fmin(0.05f * (source.source_width.x + source.source_width.y), 0.5f * fmin(source.source_width.x, source.source_width.y))));
721 std::vector<uint> UUIDs_arrow = context->loadOBJ("Arrow.obj", true);
722 source_model_UUIDs.at(sourceID).insert(source_model_UUIDs.at(sourceID).begin(), UUIDs_arrow.begin(), UUIDs_arrow.end());
723 context->scalePrimitive(UUIDs_arrow, make_vec3(1, 1, 1) * 0.15f * (source.source_width.x + source.source_width.y));
724 } else {
725 return;
726 }
727
728 if (source.source_type == RADIATION_SOURCE_TYPE_SPHERE) {
729 context->scalePrimitive(source_model_UUIDs.at(sourceID), make_vec3(source.source_width.x, source.source_width.x, source.source_width.x));
730 context->translatePrimitive(source_model_UUIDs.at(sourceID), source.source_position);
731 } else if (source.source_type == RADIATION_SOURCE_TYPE_SUN_SPHERE) {
732 vec3 center;
733 float radius;
734 context->getDomainBoundingSphere(center, radius);
735 context->scalePrimitive(source_model_UUIDs.at(sourceID), make_vec3(1, 1, 1) * 0.1f * radius);
736 vec3 sunvec = source.source_position;
737 sunvec.normalize();
738 context->translatePrimitive(source_model_UUIDs.at(sourceID), center + sunvec * radius);
739 } else {
740 context->rotatePrimitive(source_model_UUIDs.at(sourceID), source.source_rotation.x, "x");
741 context->rotatePrimitive(source_model_UUIDs.at(sourceID), source.source_rotation.y, "y");
742 context->rotatePrimitive(source_model_UUIDs.at(sourceID), source.source_rotation.z, "z");
743 context->translatePrimitive(source_model_UUIDs.at(sourceID), source.source_position);
744 }
745
746 context->setPrimitiveData(source_model_UUIDs.at(sourceID), "twosided_flag", uint(3)); // source model does not interact with radiation field
747}
748
749void RadiationModel::buildCameraModelGeometry(const std::string &cameralabel) {
750
751 assert(cameras.find(cameralabel) != cameras.end());
752
753 RadiationCamera camera = cameras.at(cameralabel);
754
755 vec3 viewvec = camera.lookat - camera.position;
756 SphericalCoord viewsph = cart2sphere(viewvec);
757
758 camera_model_UUIDs[cameralabel] = context->loadOBJ("Camera.obj", true);
759
760 context->rotatePrimitive(camera_model_UUIDs.at(cameralabel), viewsph.elevation, "x");
761 context->rotatePrimitive(camera_model_UUIDs.at(cameralabel), -viewsph.azimuth, "z");
762
763 context->translatePrimitive(camera_model_UUIDs.at(cameralabel), camera.position);
764
765 context->setPrimitiveData(camera_model_UUIDs.at(cameralabel), "twosided_flag", uint(3)); // camera model does not interact with radiation field
766}
767
768void RadiationModel::updateLightModelPosition(uint sourceID, const helios::vec3 &delta_position) {
769
770 assert(sourceID < radiation_sources.size());
771
772 RadiationSource source = radiation_sources.at(sourceID);
773
774 if (source.source_type != RADIATION_SOURCE_TYPE_SPHERE && source.source_type != RADIATION_SOURCE_TYPE_DISK && source.source_type != RADIATION_SOURCE_TYPE_RECTANGLE) {
775 return;
776 }
777
778 context->translatePrimitive(source_model_UUIDs.at(sourceID), delta_position);
779}
780
781void RadiationModel::updateCameraModelPosition(const std::string &cameralabel) {
782
783 assert(cameras.find(cameralabel) != cameras.end());
784
785 context->deletePrimitive(camera_model_UUIDs.at(cameralabel));
786 buildCameraModelGeometry(cameralabel);
787}
788
789float RadiationModel::integrateSpectrum(uint source_ID, const std::vector<helios::vec2> &object_spectrum, float wavelength1, float wavelength2) const {
790
791 if (source_ID >= radiation_sources.size()) {
792 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Radiation spectrum was not set for source ID. Make sure to set its spectrum using setSourceSpectrum() function.");
793 } else if (object_spectrum.size() < 2) {
794 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Radiation spectrum must have at least 2 wavelengths.");
795 } else if (wavelength1 > wavelength2 || wavelength1 == wavelength2) {
796 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Lower wavelength bound must be less than the upper wavelength bound.");
797 }
798
799 std::vector<helios::vec2> source_spectrum = radiation_sources.at(source_ID).source_spectrum;
800
801 int istart = 0;
802 int iend = (int) object_spectrum.size() - 1;
803 for (auto i = 0; i < object_spectrum.size() - 1; i++) {
804
805 if (object_spectrum.at(i).x <= wavelength1 && object_spectrum.at(i + 1).x > wavelength1) {
806 istart = i;
807 }
808 if (object_spectrum.at(i).x <= wavelength2 && object_spectrum.at(i + 1).x > wavelength2) {
809 iend = i + 1;
810 break;
811 }
812 }
813
814 float E = 0;
815 float Etot = 0;
816 for (auto i = istart; i < iend; i++) {
817
818 float x0 = object_spectrum.at(i).x;
819 float Esource0 = interp1(source_spectrum, object_spectrum.at(i).x);
820 float Eobject0 = object_spectrum.at(i).y;
821
822 float x1 = object_spectrum.at(i + 1).x;
823 float Eobject1 = object_spectrum.at(i + 1).y;
824 float Esource1 = interp1(source_spectrum, object_spectrum.at(i + 1).x);
825
826 E += 0.5f * (Eobject0 * Esource0 + Eobject1 * Esource1) * (x1 - x0);
827 Etot += 0.5f * (Esource1 + Esource0) * (x1 - x0);
828 }
829
830 return E / Etot;
831}
832
833float RadiationModel::integrateSpectrum(const std::vector<helios::vec2> &object_spectrum, float wavelength1, float wavelength2) const {
834
835 if (object_spectrum.size() < 2) {
836 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Radiation spectrum must have at least 2 wavelengths.");
837 } else if (wavelength1 > wavelength2 || wavelength1 == wavelength2) {
838 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Lower wavelength bound must be less than the upper wavelength bound.");
839 }
840
841 int istart = 1;
842 int iend = (int) object_spectrum.size() - 1;
843 for (auto i = 0; i < object_spectrum.size() - 1; i++) {
844
845 if (object_spectrum.at(i).x <= wavelength1 && object_spectrum.at(i + 1).x > wavelength1) {
846 istart = i;
847 }
848 if (object_spectrum.at(i).x <= wavelength2 && object_spectrum.at(i + 1).x > wavelength2) {
849 iend = i + 1;
850 break;
851 }
852 }
853
854 float E = 0;
855 for (auto i = istart; i < iend; i++) {
856 float E0 = object_spectrum.at(i).y;
857 float x0 = object_spectrum.at(i).x;
858 float E1 = object_spectrum.at(i + 1).y;
859 float x1 = object_spectrum.at(i + 1).x;
860 E += (E0 + E1) * (x1 - x0) * 0.5f;
861 }
862
863 return E;
864}
865
866float RadiationModel::integrateSpectrum(const std::vector<helios::vec2> &object_spectrum) const {
867 float wavelength1 = object_spectrum.at(0).x;
868 float wavelength2 = object_spectrum.at(object_spectrum.size() - 1).x;
869 float E = RadiationModel::integrateSpectrum(object_spectrum, wavelength1, wavelength2);
870 return E;
871}
872
873float RadiationModel::integrateSpectrum(uint source_ID, const std::vector<helios::vec2> &object_spectrum, const std::vector<helios::vec2> &camera_spectrum) const {
874
875 if (source_ID >= radiation_sources.size()) {
876 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Radiation spectrum was not set for source ID. Make sure to set its spectrum using setSourceSpectrum() function.");
877 } else if (object_spectrum.size() < 2) {
878 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Radiation spectrum must have at least 2 wavelengths.");
879 }
880
881 std::vector<helios::vec2> source_spectrum = radiation_sources.at(source_ID).source_spectrum;
882
883 float E = 0;
884 float Etot = 0;
885 for (auto i = 1; i < object_spectrum.size(); i++) {
886
887 if (object_spectrum.at(i).x <= source_spectrum.front().x || object_spectrum.at(i).x <= camera_spectrum.front().x) {
888 continue;
889 }
890 if (object_spectrum.at(i).x > source_spectrum.back().x || object_spectrum.at(i).x > camera_spectrum.back().x) {
891 break;
892 }
893 float x1 = object_spectrum.at(i).x;
894 float Eobject1 = object_spectrum.at(i).y;
895 float Esource1 = interp1(source_spectrum, x1);
896 float Ecamera1 = interp1(camera_spectrum, x1);
897
898
899 float x0 = object_spectrum.at(i - 1).x;
900 float Eobject0 = object_spectrum.at(i - 1).y;
901 float Esource0 = interp1(source_spectrum, x0);
902 float Ecamera0 = interp1(camera_spectrum, x0);
903
904 E += 0.5f * ((Eobject1 * Esource1 * Ecamera1) + (Eobject0 * Ecamera0 * Esource0)) * (x1 - x0);
905 Etot += 0.5f * (Esource1 + Esource0) * (x1 - x0);
906 }
907
908
909 return E / Etot;
910}
911
912float RadiationModel::integrateSpectrum(const std::vector<helios::vec2> &object_spectrum, const std::vector<helios::vec2> &camera_spectrum) const {
913
914 if (object_spectrum.size() < 2) {
915 helios_runtime_error("ERROR (RadiationModel::integrateSpectrum): Radiation spectrum must have at least 2 wavelengths.");
916 }
917
918 float E = 0;
919 float Etot = 0;
920 for (auto i = 1; i < object_spectrum.size(); i++) {
921
922 if (object_spectrum.at(i).x <= camera_spectrum.front().x) {
923 continue;
924 }
925 if (object_spectrum.at(i).x > camera_spectrum.back().x) {
926 break;
927 }
928
929 float x1 = object_spectrum.at(i).x;
930 float Eobject1 = object_spectrum.at(i).y;
931 float Ecamera1 = interp1(camera_spectrum, x1);
932
933
934 float x0 = object_spectrum.at(i - 1).x;
935 float Eobject0 = object_spectrum.at(i - 1).y;
936 float Ecamera0 = interp1(camera_spectrum, x0);
937
938 E += 0.5f * ((Eobject1 * Ecamera1) + (Eobject0 * Ecamera0)) * (x1 - x0);
939 Etot += 0.5f * (Ecamera1 + Ecamera0) * (x1 - x0);
940 }
941
942 return E / Etot;
943}
944
945float RadiationModel::integrateSourceSpectrum(uint source_ID, float wavelength1, float wavelength2) const {
946
947 if (source_ID >= radiation_sources.size()) {
948 helios_runtime_error("ERROR (RadiationModel::integrateSourceSpectrum): Radiation spectrum was not set for source ID. Make sure to set its spectrum using setSourceSpectrum() function.");
949 } else if (wavelength1 > wavelength2 || wavelength1 == wavelength2) {
950 helios_runtime_error("ERROR (RadiationModel::integrateSourceSpectrum): Lower wavelength bound must be less than the upper wavelength bound.");
951 }
952
953 return integrateSpectrum(radiation_sources.at(source_ID).source_spectrum, wavelength1, wavelength2);
954}
955
956void RadiationModel::scaleSpectrum(const std::string &existing_global_data_label, const std::string &new_global_data_label, float scale_factor) const {
957
958 std::vector<helios::vec2> spectrum = loadSpectralData(existing_global_data_label);
959
960 for (helios::vec2 &s: spectrum) {
961 s.y *= scale_factor;
962 }
963
964 context->setGlobalData(new_global_data_label.c_str(), spectrum);
965}
966
967void RadiationModel::scaleSpectrum(const std::string &global_data_label, float scale_factor) const {
968
969 std::vector<vec2> spectrum = loadSpectralData(global_data_label);
970
971 for (vec2 &s: spectrum) {
972 s.y *= scale_factor;
973 }
974
975 context->setGlobalData(global_data_label.c_str(), spectrum);
976}
977
978void RadiationModel::scaleSpectrumRandomly(const std::string &existing_global_data_label, const std::string &new_global_data_label, float minimum_scale_factor, float maximum_scale_factor) const {
979
980 scaleSpectrum(existing_global_data_label, new_global_data_label, context->randu(minimum_scale_factor, maximum_scale_factor));
981}
982
983
984void RadiationModel::blendSpectra(const std::string &new_spectrum_label, const std::vector<std::string> &spectrum_labels, const std::vector<float> &weights) const {
985
986 if (spectrum_labels.size() != weights.size()) {
987 helios_runtime_error("ERROR (RadiationModel::blendSpectra): number of spectra and weights must be equal");
988 } else if (sum(weights) != 1.f) {
989 helios_runtime_error("ERROR (RadiationModel::blendSpectra): weights must sum to 1");
990 }
991
992 std::vector<vec2> new_spectrum;
993 uint spectrum_size = 0;
994
995 std::vector<std::vector<vec2>> spectrum(spectrum_labels.size());
996
997 uint lambda_start = 0;
998 uint lambda_end = 0;
999 for (uint i = 0; i < spectrum_labels.size(); i++) {
1000
1001 spectrum.at(i) = loadSpectralData(spectrum_labels.at(i));
1002
1003 if (i == 0) {
1004 lambda_start = spectrum.at(i).front().x;
1005 lambda_end = spectrum.at(i).back().x;
1006 } else {
1007 if (spectrum.at(i).front().x > lambda_start) {
1008 lambda_start = spectrum.at(i).front().x;
1009 }
1010 if (spectrum.at(i).back().x < lambda_end) {
1011 lambda_end = spectrum.at(i).back().x;
1012 }
1013 }
1014 }
1015
1016 spectrum_size = lambda_end - lambda_start + 1;
1017 new_spectrum.resize(spectrum_size);
1018 for (uint j = 0; j < spectrum_size; j++) {
1019 new_spectrum.at(j) = make_vec2(lambda_start + j, 0);
1020 }
1021
1022 // trim front
1023 for (uint i = 0; i < spectrum_labels.size(); i++) {
1024 for (uint j = 0; j < spectrum.at(i).size(); j++) {
1025
1026 if (spectrum.at(i).at(j).x >= lambda_start) {
1027 if (j > 0) {
1028 spectrum.at(i).erase(spectrum.at(i).begin(), spectrum.at(i).begin() + j);
1029 }
1030 break;
1031 }
1032 }
1033 }
1034
1035 // trim back
1036 for (uint i = 0; i < spectrum_labels.size(); i++) {
1037 for (int j = spectrum.at(i).size() - 1; j <= 0; j--) {
1038
1039 if (spectrum.at(i).at(j).x <= lambda_end) {
1040 if (j < spectrum.at(i).size() - 1) {
1041 spectrum.at(i).erase(spectrum.at(i).begin() + j + 1, spectrum.at(i).end());
1042 }
1043 break;
1044 }
1045 }
1046 }
1047
1048 for (uint i = 0; i < spectrum_labels.size(); i++) {
1049 for (uint j = 0; j < spectrum_size; j++) {
1050 assert(new_spectrum.at(j).x == spectrum.at(i).at(j).x);
1051 new_spectrum.at(j).y += weights.at(i) * spectrum.at(i).at(j).y;
1052 }
1053 }
1054
1055 context->setGlobalData(new_spectrum_label.c_str(), new_spectrum);
1056}
1057
1058void RadiationModel::blendSpectraRandomly(const std::string &new_spectrum_label, const std::vector<std::string> &spectrum_labels) const {
1059
1060 std::vector<float> weights;
1061 weights.resize(spectrum_labels.size());
1062 for (uint i = 0; i < spectrum_labels.size(); i++) {
1063 weights.at(i) = context->randu();
1064 }
1065 float sum_weights = sum(weights);
1066 for (uint i = 0; i < spectrum_labels.size(); i++) {
1067 weights.at(i) /= sum_weights;
1068 }
1069
1070 blendSpectra(new_spectrum_label, spectrum_labels, weights);
1071}
1072
1073void RadiationModel::setSourcePosition(uint source_ID, const vec3 &position) {
1074
1075 if (source_ID >= radiation_sources.size()) {
1076 helios_runtime_error("ERROR (RadiationModel::setSourcePosition): Source ID out of bounds. Only " + std::to_string(radiation_sources.size() - 1) + " radiation sources.");
1077 }
1078
1079 vec3 old_position = radiation_sources.at(source_ID).source_position;
1080
1081 if (radiation_sources.at(source_ID).source_type == RADIATION_SOURCE_TYPE_COLLIMATED) {
1082 radiation_sources.at(source_ID).source_position = position / position.magnitude();
1083 } else {
1084 radiation_sources.at(source_ID).source_position = position * radiation_sources.at(source_ID).source_position_scaling_factor;
1085 }
1086
1087 if (islightvisualizationenabled) {
1088 updateLightModelPosition(source_ID, radiation_sources.at(source_ID).source_position - old_position);
1089 }
1090}
1091
1093 setSourcePosition(source_ID, sphere2cart(position));
1094}
1095
1097 if (source_ID >= radiation_sources.size()) {
1098 helios_runtime_error("ERROR (RadiationModel::getSourcePosition): Source ID does not exist.");
1099 }
1100 return radiation_sources.at(source_ID).source_position;
1101}
1102
1103void RadiationModel::setScatteringDepth(const std::string &label, uint depth) {
1104
1105 if (!doesBandExist(label)) {
1106 helios_runtime_error("ERROR (RadiationModel::setScatteringDepth): Cannot set scattering depth for band '" + label + "' because it is not a valid band.");
1107 }
1108 radiation_bands.at(label).scatteringDepth = depth;
1109}
1110
1111void RadiationModel::setMinScatterEnergy(const std::string &label, uint energy) {
1112
1113 if (!doesBandExist(label)) {
1114 helios_runtime_error("ERROR (setMinScatterEnergy): Cannot set minimum scattering energy for band '" + label + "' because it is not a valid band.");
1115 }
1116 radiation_bands.at(label).minScatterEnergy = energy;
1117}
1118
1119void RadiationModel::enforcePeriodicBoundary(const std::string &boundary) {
1120
1121 if (boundary == "x") {
1122
1123 periodic_flag.x = 1;
1124
1125 } else if (boundary == "y") {
1126
1127 periodic_flag.y = 1;
1128
1129 } else if (boundary == "xy") {
1130
1131 periodic_flag.x = 1;
1132 periodic_flag.y = 1;
1133
1134 } else {
1135
1136 std::cout << "WARNING (RadiationModel::enforcePeriodicBoundary()): unknown boundary of '" << boundary
1137 << "'. Possible choices are "
1138 "x"
1139 ", "
1140 "y"
1141 ", or "
1142 "xy"
1143 "."
1144 << std::endl;
1145 }
1146}
1147
1148void RadiationModel::initializeOptiX() {
1149
1150 /* Context */
1151 RT_CHECK_ERROR(rtContextCreate(&OptiX_Context));
1152 RT_CHECK_ERROR(rtContextSetPrintEnabled(OptiX_Context, 1));
1153
1154 RT_CHECK_ERROR(rtContextSetRayTypeCount(OptiX_Context, 4));
1155 // ray types are:
1156 // 0: direct_ray_type
1157 // 1: diffuse_ray_type
1158 // 2: camera_ray_type
1159 // 3: pixel_label_ray_type
1160
1161 RT_CHECK_ERROR(rtContextSetEntryPointCount(OptiX_Context, 4));
1162 // ray entery points are
1163 // 0: direct_raygen
1164 // 1: diffuse_raygen
1165 // 2: camera_raygen
1166 // 3: pixel_label_raygen
1167
1168 /* Ray Types */
1169 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "direct_ray_type", &direct_ray_type_RTvariable));
1170 RT_CHECK_ERROR(rtVariableSet1ui(direct_ray_type_RTvariable, RAYTYPE_DIRECT));
1171 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "diffuse_ray_type", &diffuse_ray_type_RTvariable));
1172 RT_CHECK_ERROR(rtVariableSet1ui(diffuse_ray_type_RTvariable, RAYTYPE_DIFFUSE));
1173 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_ray_type", &camera_ray_type_RTvariable));
1174 RT_CHECK_ERROR(rtVariableSet1ui(camera_ray_type_RTvariable, RAYTYPE_CAMERA));
1175 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "pixel_label_ray_type", &pixel_label_ray_type_RTvariable));
1176 RT_CHECK_ERROR(rtVariableSet1ui(pixel_label_ray_type_RTvariable, RAYTYPE_PIXEL_LABEL));
1177
1178 /* Ray Generation Program */
1179
1180 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayGeneration.cu.ptx").string().c_str(), "direct_raygen", &direct_raygen));
1181 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_DIRECT, direct_raygen));
1182 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayGeneration.cu.ptx").string().c_str(), "diffuse_raygen", &diffuse_raygen));
1183 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_DIFFUSE, diffuse_raygen));
1184 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayGeneration.cu.ptx").string().c_str(), "camera_raygen", &camera_raygen));
1185 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_CAMERA, camera_raygen));
1186 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayGeneration.cu.ptx").string().c_str(), "pixel_label_raygen", &pixel_label_raygen));
1187 RT_CHECK_ERROR(rtContextSetRayGenerationProgram(OptiX_Context, RAYTYPE_PIXEL_LABEL, pixel_label_raygen));
1188
1189 /* Declare Buffers and Variables */
1190
1191 // primitive reflectivity buffer
1192 addBuffer("rho", rho_RTbuffer, rho_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1193 // primitive transmissivity buffer
1194 addBuffer("tau", tau_RTbuffer, tau_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1195
1196 // primitive reflectivity buffer
1197 addBuffer("rho_cam", rho_cam_RTbuffer, rho_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1198 // primitive transmissivity buffer
1199 addBuffer("tau_cam", tau_cam_RTbuffer, tau_cam_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1200
1201 // specular reflection exponent buffer
1202 addBuffer("specular_exponent", specular_exponent_RTbuffer, specular_exponent_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1203
1204 // specular reflection scale coefficient buffer
1205 addBuffer("specular_scale", specular_scale_RTbuffer, specular_scale_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1206
1207 // number of external radiation sources
1208 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "specular_reflection_enabled", &specular_reflection_enabled_RTvariable));
1209 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, 0));
1210
1211 // primitive transformation matrix buffer
1212 addBuffer("transform_matrix", transform_matrix_RTbuffer, transform_matrix_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 2);
1213
1214 // primitive type buffer
1215 addBuffer("primitive_type", primitive_type_RTbuffer, primitive_type_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1216
1217 // primitive solid fraction buffer
1218 addBuffer("primitive_solid_fraction", primitive_solid_fraction_RTbuffer, primitive_solid_fraction_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1219
1220 // primitive UUID buffers
1221 addBuffer("patch_UUID", patch_UUID_RTbuffer, patch_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1222 addBuffer("triangle_UUID", triangle_UUID_RTbuffer, triangle_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1223 addBuffer("disk_UUID", disk_UUID_RTbuffer, disk_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1224 addBuffer("tile_UUID", tile_UUID_RTbuffer, tile_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1225 addBuffer("voxel_UUID", voxel_UUID_RTbuffer, voxel_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1226
1227 // Object ID Buffer
1228 addBuffer("objectID", objectID_RTbuffer, objectID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1229
1230 // Primitive ID Buffer
1231 addBuffer("primitiveID", primitiveID_RTbuffer, primitiveID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1232
1233 // primitive two-sided flag buffer
1234 addBuffer("twosided_flag", twosided_flag_RTbuffer, twosided_flag_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_BYTE, 1);
1235
1236 // patch buffers
1237 addBuffer("patch_vertices", patch_vertices_RTbuffer, patch_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
1238
1239 // triangle buffers
1240 addBuffer("triangle_vertices", triangle_vertices_RTbuffer, triangle_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
1241
1242 // disk buffers
1243 addBuffer("disk_centers", disk_centers_RTbuffer, disk_centers_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
1244 addBuffer("disk_radii", disk_radii_RTbuffer, disk_radii_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1245 addBuffer("disk_normals", disk_normals_RTbuffer, disk_normals_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
1246
1247 // tile buffers
1248 addBuffer("tile_vertices", tile_vertices_RTbuffer, tile_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
1249
1250 // voxel buffers
1251 addBuffer("voxel_vertices", voxel_vertices_RTbuffer, voxel_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
1252
1253 // object buffers
1254 addBuffer("object_subdivisions", object_subdivisions_RTbuffer, object_subdivisions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT2, 1);
1255
1256 // radiation energy rate data buffers
1257 // - in - //
1258 addBuffer("radiation_in", radiation_in_RTbuffer, radiation_in_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1259 // - out,top - //
1260 addBuffer("radiation_out_top", radiation_out_top_RTbuffer, radiation_out_top_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1261 // - out,bottom - //
1262 addBuffer("radiation_out_bottom", radiation_out_bottom_RTbuffer, radiation_out_bottom_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1263 // - camera - //
1264 addBuffer("radiation_in_camera", radiation_in_camera_RTbuffer, radiation_in_camera_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1265 addBuffer("camera_pixel_label", camera_pixel_label_RTbuffer, camera_pixel_label_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_UNSIGNED_INT, 1);
1266 addBuffer("camera_pixel_depth", camera_pixel_depth_RTbuffer, camera_pixel_depth_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1267
1268 // primitive scattering buffers
1269 // - top - //
1270 addBuffer("scatter_buff_top", scatter_buff_top_RTbuffer, scatter_buff_top_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1271 // - bottom - //
1272 addBuffer("scatter_buff_bottom", scatter_buff_bottom_RTbuffer, scatter_buff_bottom_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1273
1274 // Energy absorbed by "sky"
1275 addBuffer("Rsky", Rsky_RTbuffer, Rsky_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1276
1277 // number of external radiation sources
1278 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nsources", &Nsources_RTvariable));
1279 RT_CHECK_ERROR(rtVariableSet1ui(Nsources_RTvariable, 0));
1280
1281 // External radiation source positions
1282 addBuffer("source_positions", source_positions_RTbuffer, source_positions_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
1283
1284 // External radiation source widths
1285 addBuffer("source_widths", source_widths_RTbuffer, source_widths_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT2, 1);
1286
1287 // External radiation source rotations
1288 addBuffer("source_rotations", source_rotations_RTbuffer, source_rotations_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
1289
1290 // External radiation source types
1291 addBuffer("source_types", source_types_RTbuffer, source_types_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1292
1293 // External radiation source fluxes
1294 addBuffer("source_fluxes", source_fluxes_RTbuffer, source_fluxes_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1295
1296 // number of radiation bands
1297 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nbands_global", &Nbands_global_RTvariable));
1298 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, 0));
1299 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nbands_launch", &Nbands_launch_RTvariable));
1300 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, 0));
1301
1302 // flag to disable launches for certain bands
1303 addBuffer("band_launch_flag", band_launch_flag_RTbuffer, band_launch_flag_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_BYTE, 1);
1304
1305 // number of Context primitives
1306 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Nprimitives", &Nprimitives_RTvariable));
1307 RT_CHECK_ERROR(rtVariableSet1ui(Nprimitives_RTvariable, 0));
1308
1309 // Flux of diffuse radiation
1310 addBuffer("diffuse_flux", diffuse_flux_RTbuffer, diffuse_flux_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1311
1312 // Diffuse distribution extinction coefficient of ambient diffuse radiation
1313 addBuffer("diffuse_extinction", diffuse_extinction_RTbuffer, diffuse_extinction_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1314
1315 // Direction of peak diffuse radiation
1316 addBuffer("diffuse_peak_dir", diffuse_peak_dir_RTbuffer, diffuse_peak_dir_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 1);
1317
1318 // Diffuse distribution normalization factor
1319 addBuffer("diffuse_dist_norm", diffuse_dist_norm_RTbuffer, diffuse_dist_norm_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT, 1);
1320
1321 // Bounding Box
1322 addBuffer("bbox_UUID", bbox_UUID_RTbuffer, bbox_UUID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, 1);
1323 addBuffer("bbox_vertices", bbox_vertices_RTbuffer, bbox_vertices_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, 2);
1324
1325 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "periodic_flag", &periodic_flag_RTvariable));
1326 RT_CHECK_ERROR(rtVariableSet2f(periodic_flag_RTvariable, 0.f, 0.f));
1327
1328 // Texture mask data
1329 RT_CHECK_ERROR(rtBufferCreate(OptiX_Context, RT_BUFFER_INPUT, &maskdata_RTbuffer));
1330 RT_CHECK_ERROR(rtBufferSetFormat(maskdata_RTbuffer, RT_FORMAT_BYTE));
1331 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "maskdata", &maskdata_RTvariable));
1332 RT_CHECK_ERROR(rtVariableSetObject(maskdata_RTvariable, maskdata_RTbuffer));
1333 std::vector<std::vector<std::vector<bool>>> dummydata;
1334 initializeBuffer3D(maskdata_RTbuffer, dummydata);
1335
1336 // Texture mask size
1337 addBuffer("masksize", masksize_RTbuffer, masksize_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT2, 1);
1338
1339 // Texture mask ID
1340 addBuffer("maskID", maskID_RTbuffer, maskID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT, 1);
1341
1342 // Texture u,v data
1343 addBuffer("uvdata", uvdata_RTbuffer, uvdata_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_FLOAT2, 2);
1344
1345 // Texture u,v ID
1346 addBuffer("uvID", uvID_RTbuffer, uvID_RTvariable, RT_BUFFER_INPUT, RT_FORMAT_INT, 1);
1347
1348 // Radiation Camera Variables
1349 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_position", &camera_position_RTvariable));
1350 RT_CHECK_ERROR(rtVariableSet3f(camera_position_RTvariable, 0.f, 0.f, 0.f));
1351 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_direction", &camera_direction_RTvariable));
1352 RT_CHECK_ERROR(rtVariableSet2f(camera_direction_RTvariable, 0.f, 0.f));
1353 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_lens_diameter", &camera_lens_diameter_RTvariable));
1354 RT_CHECK_ERROR(rtVariableSet1f(camera_lens_diameter_RTvariable, 0.f));
1355 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "FOV_aspect_ratio", &FOV_aspect_RTvariable));
1356 RT_CHECK_ERROR(rtVariableSet1f(FOV_aspect_RTvariable, 1.f));
1357 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_focal_length", &camera_focal_length_RTvariable));
1358 RT_CHECK_ERROR(rtVariableSet1f(camera_focal_length_RTvariable, 0.f));
1359 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_viewplane_length", &camera_viewplane_length_RTvariable));
1360 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, 0.f));
1361 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "Ncameras", &Ncameras_RTvariable));
1362 RT_CHECK_ERROR(rtVariableSet1ui(Ncameras_RTvariable, 0));
1363 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "camera_ID", &camera_ID_RTvariable));
1364 RT_CHECK_ERROR(rtVariableSet1ui(camera_ID_RTvariable, 0));
1365
1366 // primitive scattering buffers (cameras)
1367 addBuffer("scatter_buff_top_cam", scatter_buff_top_cam_RTbuffer, scatter_buff_top_cam_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1368 addBuffer("scatter_buff_bottom_cam", scatter_buff_bottom_cam_RTbuffer, scatter_buff_bottom_cam_RTvariable, RT_BUFFER_INPUT_OUTPUT, RT_FORMAT_FLOAT, 1);
1369
1370 /* Hit Programs */
1371 RTprogram closest_hit_direct;
1372 RTprogram closest_hit_diffuse;
1373 RTprogram closest_hit_camera;
1374 RTprogram closest_hit_pixel_label;
1375 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "closest_hit_direct", &closest_hit_direct));
1376 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "closest_hit_diffuse", &closest_hit_diffuse));
1377 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "closest_hit_camera", &closest_hit_camera));
1378 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "closest_hit_pixel_label", &closest_hit_pixel_label));
1379
1380 /* Initialize Patch Geometry */
1381
1382 RTprogram patch_intersection_program;
1383 RTprogram patch_bounding_box_program;
1384
1385 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &patch));
1386
1387 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "rectangle_bounds", &patch_bounding_box_program));
1388 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(patch, patch_bounding_box_program));
1389 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "rectangle_intersect", &patch_intersection_program));
1390 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(patch, patch_intersection_program));
1391
1392 /* Create Patch Material */
1393
1394 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &patch_material));
1395
1396 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_DIRECT, closest_hit_direct));
1397 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
1398 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_CAMERA, closest_hit_camera));
1399 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(patch_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
1400
1401 /* Initialize Triangle Geometry */
1402
1403 RTprogram triangle_intersection_program;
1404 RTprogram triangle_bounding_box_program;
1405
1406 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &triangle));
1407
1408 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "triangle_bounds", &triangle_bounding_box_program));
1409 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(triangle, triangle_bounding_box_program));
1410 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "triangle_intersect", &triangle_intersection_program));
1411 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(triangle, triangle_intersection_program));
1412
1413 /* Create Triangle Material */
1414
1415 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &triangle_material));
1416
1417 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_DIRECT, closest_hit_direct));
1418 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
1419 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_CAMERA, closest_hit_camera));
1420 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(triangle_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
1421
1422 /* Initialize Disk Geometry */
1423
1424 RTprogram disk_intersection_program;
1425 RTprogram disk_bounding_box_program;
1426
1427 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &disk));
1428
1429 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "disk_bounds", &disk_bounding_box_program));
1430 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(disk, disk_bounding_box_program));
1431 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "disk_intersect", &disk_intersection_program));
1432 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(disk, disk_intersection_program));
1433
1434 /* Create Disk Material */
1435
1436 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &disk_material));
1437
1438 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_DIRECT, closest_hit_direct));
1439 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
1440 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_CAMERA, closest_hit_camera));
1441 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(disk_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
1442
1443 /* Initialize Tile Geometry */
1444
1445 RTprogram tile_intersection_program;
1446 RTprogram tile_bounding_box_program;
1447
1448 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &tile));
1449
1450 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "tile_bounds", &tile_bounding_box_program));
1451 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(tile, tile_bounding_box_program));
1452 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "tile_intersect", &tile_intersection_program));
1453 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(tile, tile_intersection_program));
1454
1455 /* Create Tile Material */
1456
1457 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &tile_material));
1458
1459 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_DIRECT, closest_hit_direct));
1460 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
1461 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_CAMERA, closest_hit_camera));
1462 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(tile_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
1463
1464 /* Initialize Voxel Geometry */
1465
1466 RTprogram voxel_intersection_program;
1467 RTprogram voxel_bounding_box_program;
1468
1469 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &voxel));
1470
1471 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "voxel_bounds", &voxel_bounding_box_program));
1472 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(voxel, voxel_bounding_box_program));
1473 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "voxel_intersect", &voxel_intersection_program));
1474 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(voxel, voxel_intersection_program));
1475
1476 /* Create Voxel Material */
1477
1478 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &voxel_material));
1479
1480 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_DIRECT, closest_hit_direct));
1481 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
1482 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_CAMERA, closest_hit_camera));
1483 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(voxel_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
1484
1485 /* Initialize Bounding Box Geometry */
1486
1487 RTprogram bbox_intersection_program;
1488 RTprogram bbox_bounding_box_program;
1489
1490 RT_CHECK_ERROR(rtGeometryCreate(OptiX_Context, &bbox));
1491
1492 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "bbox_bounds", &bbox_bounding_box_program));
1493 RT_CHECK_ERROR(rtGeometrySetBoundingBoxProgram(bbox, bbox_bounding_box_program));
1494 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_primitiveIntersection.cu.ptx").string().c_str(), "bbox_intersect", &bbox_intersection_program));
1495 RT_CHECK_ERROR(rtGeometrySetIntersectionProgram(bbox, bbox_intersection_program));
1496
1497 /* Create Bounding Box Material */
1498
1499 RT_CHECK_ERROR(rtMaterialCreate(OptiX_Context, &bbox_material));
1500
1501 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_DIRECT, closest_hit_direct));
1502 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_DIFFUSE, closest_hit_diffuse));
1503 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_CAMERA, closest_hit_camera));
1504 RT_CHECK_ERROR(rtMaterialSetClosestHitProgram(bbox_material, RAYTYPE_PIXEL_LABEL, closest_hit_pixel_label));
1505
1506 /* Miss Program */
1507 RTprogram miss_program_direct;
1508 RTprogram miss_program_diffuse;
1509 RTprogram miss_program_camera;
1510 RTprogram miss_program_pixel_label;
1511 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "miss_direct", &miss_program_direct));
1512 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_DIRECT, miss_program_direct));
1513 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "miss_diffuse", &miss_program_diffuse));
1514 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_DIFFUSE, miss_program_diffuse));
1515 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "miss_camera", &miss_program_camera));
1516 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_CAMERA, miss_program_camera));
1517 RT_CHECK_ERROR(rtProgramCreateFromPTXFile(OptiX_Context, helios::resolvePluginAsset("radiation", "cuda_compile_ptx_generated_rayHit.cu.ptx").string().c_str(), "miss_pixel_label", &miss_program_pixel_label));
1518 RT_CHECK_ERROR(rtContextSetMissProgram(OptiX_Context, RAYTYPE_PIXEL_LABEL, miss_program_pixel_label));
1519
1520 /* Create OptiX Geometry Structures */
1521
1522 RTtransform transform;
1523
1524 RTgeometryinstance patch_instance;
1525 RTgeometryinstance triangle_instance;
1526 RTgeometryinstance disk_instance;
1527 RTgeometryinstance tile_instance;
1528 RTgeometryinstance voxel_instance;
1529 RTgeometryinstance bbox_instance;
1530
1531 /* Create top level group and associated (dummy) acceleration */
1532 RT_CHECK_ERROR(rtGroupCreate(OptiX_Context, &top_level_group));
1533 RT_CHECK_ERROR(rtGroupSetChildCount(top_level_group, 1));
1534
1535 RT_CHECK_ERROR(rtAccelerationCreate(OptiX_Context, &top_level_acceleration));
1536 RT_CHECK_ERROR(rtAccelerationSetBuilder(top_level_acceleration, "NoAccel"));
1537 RT_CHECK_ERROR(rtAccelerationSetTraverser(top_level_acceleration, "NoAccel"));
1538 RT_CHECK_ERROR(rtGroupSetAcceleration(top_level_group, top_level_acceleration));
1539
1540 /* mark acceleration as dirty */
1541 RT_CHECK_ERROR(rtAccelerationMarkDirty(top_level_acceleration));
1542
1543 /* Create transform node */
1544 RT_CHECK_ERROR(rtTransformCreate(OptiX_Context, &transform));
1545 float m[16];
1546 m[0] = 1.f;
1547 m[1] = 0;
1548 m[2] = 0;
1549 m[3] = 0;
1550 m[4] = 0.f;
1551 m[5] = 1.f;
1552 m[6] = 0;
1553 m[7] = 0;
1554 m[8] = 0.f;
1555 m[9] = 0;
1556 m[10] = 1.f;
1557 m[11] = 0;
1558 m[12] = 0.f;
1559 m[13] = 0;
1560 m[14] = 0;
1561 m[15] = 1.f;
1562 RT_CHECK_ERROR(rtTransformSetMatrix(transform, 0, m, nullptr));
1563 RT_CHECK_ERROR(rtGroupSetChild(top_level_group, 0, transform));
1564
1565 /* Create geometry group and associated acceleration*/
1566 RT_CHECK_ERROR(rtGeometryGroupCreate(OptiX_Context, &base_geometry_group));
1567 RT_CHECK_ERROR(rtGeometryGroupSetChildCount(base_geometry_group, 6));
1568 RT_CHECK_ERROR(rtTransformSetChild(transform, base_geometry_group));
1569
1570 // create acceleration object for group and specify some build hints
1571 RT_CHECK_ERROR(rtAccelerationCreate(OptiX_Context, &geometry_acceleration));
1572 RT_CHECK_ERROR(rtAccelerationSetBuilder(geometry_acceleration, "Trbvh"));
1573 RT_CHECK_ERROR(rtAccelerationSetTraverser(geometry_acceleration, "Bvh"));
1574 RT_CHECK_ERROR(rtGeometryGroupSetAcceleration(base_geometry_group, geometry_acceleration));
1575 RT_CHECK_ERROR(rtAccelerationMarkDirty(geometry_acceleration));
1576
1577 /* Create geometry instances */
1578 // patches
1579 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &patch_instance));
1580 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(patch_instance, patch));
1581 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(patch_instance, 1));
1582 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(patch_instance, 0, patch_material));
1583 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 0, patch_instance));
1584 // triangles
1585 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &triangle_instance));
1586 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(triangle_instance, triangle));
1587 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(triangle_instance, 1));
1588 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(triangle_instance, 0, triangle_material));
1589 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 1, triangle_instance));
1590 // disks
1591 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &disk_instance));
1592 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(disk_instance, disk));
1593 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(disk_instance, 1));
1594 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(disk_instance, 0, disk_material));
1595 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 2, disk_instance));
1596 // tiles
1597 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &tile_instance));
1598 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(tile_instance, tile));
1599 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(tile_instance, 1));
1600 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(tile_instance, 0, tile_material));
1601 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 3, tile_instance));
1602 // voxels
1603 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &voxel_instance));
1604 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(voxel_instance, voxel));
1605 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(voxel_instance, 1));
1606 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(voxel_instance, 0, voxel_material));
1607 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 4, voxel_instance));
1608
1609 // bounding boxes
1610 RT_CHECK_ERROR(rtGeometryInstanceCreate(OptiX_Context, &bbox_instance));
1611 RT_CHECK_ERROR(rtGeometryInstanceSetGeometry(bbox_instance, bbox));
1612 RT_CHECK_ERROR(rtGeometryInstanceSetMaterialCount(bbox_instance, 1));
1613 RT_CHECK_ERROR(rtGeometryInstanceSetMaterial(bbox_instance, 0, bbox_material));
1614 RT_CHECK_ERROR(rtGeometryGroupSetChild(base_geometry_group, 5, bbox_instance));
1615
1616 /* Set the top_object variable */
1617 // NOTE: Not sure exactly where this has to be set
1618 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "top_object", &top_object));
1619 RT_CHECK_ERROR(rtVariableSetObject(top_object, top_level_group));
1620
1621 // random number seed
1622 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "random_seed", &random_seed_RTvariable));
1623 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, std::chrono::system_clock::now().time_since_epoch().count()));
1624
1625 // launch offset
1626 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "launch_offset", &launch_offset_RTvariable));
1627 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, 0));
1628
1629 // launch primitive face flag
1630 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "launch_face", &launch_face_RTvariable));
1631 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, 0));
1632
1633 // maximum scattering depth
1634 RT_CHECK_ERROR(rtBufferCreate(OptiX_Context, RT_BUFFER_INPUT, &max_scatters_RTbuffer));
1635 RT_CHECK_ERROR(rtBufferSetFormat(max_scatters_RTbuffer, RT_FORMAT_UNSIGNED_INT));
1636 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, "max_scatters", &max_scatters_RTvariable));
1637 RT_CHECK_ERROR(rtVariableSetObject(max_scatters_RTvariable, max_scatters_RTbuffer));
1638 zeroBuffer1D(max_scatters_RTbuffer, 1);
1639
1640 // RTsize device_memory;
1641 // RT_CHECK_ERROR( rtContextGetAttribute( OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &device_memory ) );
1642
1643 // device_memory *= 1e-6;
1644 // if( device_memory < 1000 ){
1645 // printf("available device memory at end of OptiX initialization: %6.3f MB\n",device_memory);
1646 // }else{
1647 // printf("available device memory at end of OptiX initialization: %6.3f GB\n",device_memory*1e-3);
1648 // }
1649}
1650
1654
1655void RadiationModel::updateGeometry(const std::vector<uint> &UUIDs) {
1656
1657 if (message_flag) {
1658 std::cout << "Updating geometry in radiation transport model..." << std::flush;
1659 }
1660
1661 context_UUIDs = UUIDs;
1662
1663 // remove any primitive UUIDs that don't exist or have zero area
1664 for (std::size_t u = context_UUIDs.size(); u-- > 0;) {
1665 if (!context->doesPrimitiveExist(context_UUIDs.at(u))) {
1666 context_UUIDs[u] = context_UUIDs.back();
1667 context_UUIDs.pop_back();
1668 continue;
1669 }
1670 float area = context->getPrimitiveArea(context_UUIDs.at(u));
1671 if ((area == 0 || std::isnan(area)) && context->getObjectType(context->getPrimitiveParentObjectID(context_UUIDs.at(u))) != OBJECT_TYPE_TILE) {
1672 context_UUIDs[u] = context_UUIDs.back();
1673 context_UUIDs.pop_back();
1674 }
1675 }
1676
1677 //--- Populate Primitive Geometry Buffers ---//
1678
1679 size_t Nprimitives = context_UUIDs.size(); // Number of primitives
1680
1681 if (Nprimitives == 0) {
1682 std::cerr << "WARNING (RadiationModel::updateGeometry): No primitives found in context. Cannot update geometry." << std::endl;
1683 return;
1684 }
1685
1686 std::vector<uint> objID_all = context->getUniquePrimitiveParentObjectIDs(context_UUIDs, true);
1687
1688 // We need to reorder the primitive UUIDs so they appear in the proper order within the parent object
1689
1690 std::vector<uint> primitive_UUIDs_ordered;
1691 primitive_UUIDs_ordered.reserve(Nprimitives);
1692
1693 std::unordered_set<uint> context_UUIDs_set(context_UUIDs.begin(), context_UUIDs.end());
1694
1695 for (uint objID: objID_all) {
1696
1697 const std::vector<uint> &primitive_UUIDs = context->getObjectPrimitiveUUIDs(objID);
1698 for (uint p: primitive_UUIDs) {
1699 // Only add if primitive exists AND was not filtered out for zero area
1700 if (context->doesPrimitiveExist(p) && context_UUIDs_set.find(p) != context_UUIDs_set.end()) {
1701 primitive_UUIDs_ordered.push_back(p);
1702 }
1703 }
1704 }
1705
1706 context_UUIDs = primitive_UUIDs_ordered;
1707
1708 // transformation matrix buffer - size=Nobjects
1709 std::vector<std::vector<float>> m_global;
1710
1711 // primitive type buffer - size=Nobjects
1712 std::vector<uint> ptype_global;
1713
1714 // primitive solid fraction buffer - size=Nobjects
1715 std::vector<float> solid_fraction_global;
1716
1717 // primitive UUID buffers - total size of all combined is Nobjects
1718 std::vector<uint> patch_UUID;
1719 patch_UUID.reserve(Nprimitives);
1720 std::vector<uint> triangle_UUID;
1721 triangle_UUID.reserve(Nprimitives);
1722 std::vector<uint> disk_UUID;
1723 disk_UUID.reserve(Nprimitives);
1724 std::vector<uint> tile_UUID;
1725 tile_UUID.reserve(Nprimitives);
1726 std::vector<uint> voxel_UUID;
1727
1728 // twosided flag buffer - size=Nobjects
1729 std::vector<char> twosided_flag_global;
1730 twosided_flag_global.reserve(Nprimitives);
1731
1732 // primitive geometry specification buffers
1733 std::vector<std::vector<optix::float3>> patch_vertices;
1734 patch_vertices.reserve(Nprimitives);
1735 std::vector<std::vector<optix::float3>> triangle_vertices;
1736 triangle_vertices.reserve(Nprimitives);
1737 std::vector<std::vector<optix::float3>> tile_vertices;
1738 tile_vertices.reserve(Nprimitives);
1739 std::vector<std::vector<optix::float3>> voxel_vertices;
1740
1741 // number of patch subdivisions for each tile - size is same as tile_vertices
1742 std::vector<optix::int2> object_subdivisions;
1743 object_subdivisions.reserve(Nprimitives);
1744
1745 // ID of object corresponding to each primitive - size Nprimitives
1746 std::vector<uint> objectID;
1747 objectID.resize(Nprimitives);
1748
1749 std::size_t patch_count = 0;
1750 std::size_t triangle_count = 0;
1751 std::size_t disk_count = 0;
1752 std::size_t tile_count = 0;
1753 std::size_t voxel_count = 0;
1754
1755 solid_fraction_global.resize(Nprimitives);
1756
1757 primitiveID.resize(0);
1758 primitiveID.reserve(Nprimitives);
1759
1760 // Create a vector of primitive pointers 'primitives' (note: only add one pointer for compound objects)
1761 uint objID = 0;
1762 uint ID = 99999;
1763 for (std::size_t u = 0; u < Nprimitives; u++) {
1764
1765 uint p = context_UUIDs.at(u);
1766
1767 // primitve solid fraction
1768 solid_fraction_global.at(u) = context->getPrimitiveSolidFraction(p);
1769
1770 uint parentID = context->getPrimitiveParentObjectID(p);
1771
1772 if (ID != parentID || parentID == 0 || context->getObjectPointer(parentID)->getObjectType() != helios::OBJECT_TYPE_TILE) { // if this is a new object, or primitive does not belong to an object
1773 primitiveID.push_back(u);
1774 ID = parentID;
1775 objID++;
1776 } else {
1777 ID = parentID;
1778 }
1779
1780 assert(objID > 0);
1781
1782 objectID.at(u) = objID - 1;
1783 }
1784
1785 // Nobjects is the number of isolated primitives plus the number of compound objects (all primitives inside and object combined only counts as one element)
1786 size_t Nobjects = primitiveID.size();
1787
1788 m_global.resize(Nobjects);
1789 ptype_global.resize(Nobjects);
1790 twosided_flag_global.resize(Nobjects); // initialize to be two-sided
1791 for (size_t i = 0; i < Nobjects; i++) {
1792 twosided_flag_global.at(i) = 1;
1793 }
1794
1795 // Populate attributes for each primitive in the pointer vector 'primitives'
1796 for (std::size_t u = 0; u < Nobjects; u++) {
1797
1798 uint p = context_UUIDs.at(primitiveID.at(u));
1799
1800 // transformation matrix
1801 float m[16];
1802
1803 // primitive type
1804 helios::PrimitiveType type = context->getPrimitiveType(p);
1805 ptype_global.at(u) = type;
1806
1807 assert(ptype_global.at(u) >= 0 && ptype_global.at(u) <= 4);
1808
1809 // primitive twosided flag
1810 if (context->doesPrimitiveDataExist(p, "twosided_flag")) {
1811 uint flag;
1812 context->getPrimitiveData(p, "twosided_flag", flag);
1813 twosided_flag_global.at(u) = char(flag);
1814 }
1815
1816 uint parentID = context->getPrimitiveParentObjectID(p);
1817
1818 if (parentID > 0 && context->getObjectPointer(parentID)->getObjectType() == helios::OBJECT_TYPE_TILE) { // tile objects
1819
1820 ptype_global.at(u) = 3;
1821
1822 context->getObjectPointer(parentID)->getTransformationMatrix(m);
1823
1824 m_global.at(u).resize(16);
1825 for (uint i = 0; i < 16; i++) {
1826 m_global.at(u).at(i) = m[i];
1827 }
1828
1829 std::vector<vec3> vertices = context->getTileObjectPointer(parentID)->getVertices();
1830 std::vector<optix::float3> v{optix::make_float3(vertices.at(0).x, vertices.at(0).y, vertices.at(0).z), optix::make_float3(vertices.at(1).x, vertices.at(1).y, vertices.at(1).z),
1831 optix::make_float3(vertices.at(2).x, vertices.at(2).y, vertices.at(2).z), optix::make_float3(vertices.at(3).x, vertices.at(3).y, vertices.at(3).z)};
1832 tile_vertices.push_back(v);
1833
1834 helios::int2 subdiv = context->getTileObjectPointer(parentID)->getSubdivisionCount();
1835
1836 object_subdivisions.push_back(optix::make_int2(subdiv.x, subdiv.y));
1837
1838 tile_UUID.push_back(primitiveID.at(u));
1839 tile_count++;
1840
1841 } else if (type == helios::PRIMITIVE_TYPE_PATCH) { // patches
1842
1843 context->getPrimitiveTransformationMatrix(p, m);
1844
1845 m_global.at(u).resize(16);
1846 for (uint i = 0; i < 16; i++) {
1847 m_global.at(u).at(i) = m[i];
1848 }
1849
1850 std::vector<vec3> vertices = context->getPrimitiveVertices(p);
1851 std::vector<optix::float3> v{
1852 optix::make_float3(vertices.at(0).x, vertices.at(0).y, vertices.at(0).z),
1853 optix::make_float3(vertices.at(1).x, vertices.at(1).y, vertices.at(1).z),
1854 optix::make_float3(vertices.at(2).x, vertices.at(2).y, vertices.at(2).z),
1855 optix::make_float3(vertices.at(3).x, vertices.at(3).y, vertices.at(3).z),
1856 };
1857 patch_vertices.push_back(v);
1858 object_subdivisions.push_back(optix::make_int2(1, 1));
1859 patch_UUID.push_back(primitiveID.at(u));
1860 patch_count++;
1861 } else if (type == helios::PRIMITIVE_TYPE_TRIANGLE) { // triangles
1862
1863 context->getPrimitiveTransformationMatrix(p, m);
1864
1865 m_global.at(u).resize(16);
1866 for (uint i = 0; i < 16; i++) {
1867 m_global.at(u).at(i) = m[i];
1868 }
1869
1870 std::vector<vec3> vertices = context->getPrimitiveVertices(p);
1871 std::vector<optix::float3> v{optix::make_float3(vertices.at(0).x, vertices.at(0).y, vertices.at(0).z), optix::make_float3(vertices.at(1).x, vertices.at(1).y, vertices.at(1).z),
1872 optix::make_float3(vertices.at(2).x, vertices.at(2).y, vertices.at(2).z)};
1873 triangle_vertices.push_back(v);
1874 object_subdivisions.push_back(optix::make_int2(1, 1));
1875 triangle_UUID.push_back(primitiveID.at(u));
1876 triangle_count++;
1877 } else if (type == helios::PRIMITIVE_TYPE_VOXEL) { // voxels
1878
1879 context->getPrimitiveTransformationMatrix(p, m);
1880
1881 m_global.at(u).resize(16);
1882 for (uint i = 0; i < 16; i++) {
1883 m_global.at(u).at(i) = m[i];
1884 }
1885
1886 helios::vec3 center = context->getVoxelCenter(p);
1887 helios::vec3 size = context->getVoxelSize(p);
1888 std::vector<optix::float3> v{optix::make_float3(center.x - 0.5f * size.x, center.y - 0.5f * size.y, center.z - 0.5f * size.z), optix::make_float3(center.x + 0.5f * size.x, center.y + 0.5f * size.y, center.z + 0.5f * size.z)};
1889 voxel_vertices.push_back(v);
1890 object_subdivisions.push_back(optix::make_int2(1, 1));
1891 voxel_UUID.push_back(primitiveID.at(u));
1892 voxel_count++;
1893 }
1894 }
1895
1896 // Texture mask data
1897 std::vector<std::vector<std::vector<bool>>> maskdata;
1898 std::map<std::string, uint> maskname;
1899 std::vector<optix::int2> masksize;
1900 std::vector<int> maskID;
1901 std::vector<std::vector<optix::float2>> uvdata;
1902 std::vector<int> uvID;
1903 maskID.resize(Nobjects);
1904 uvID.resize(Nobjects);
1905
1906 for (size_t u = 0; u < Nobjects; u++) {
1907
1908 uint p = context_UUIDs.at(primitiveID.at(u));
1909
1910 std::string maskfile = context->getPrimitiveTextureFile(p);
1911
1912 uint parentID = context->getPrimitiveParentObjectID(p);
1913
1914 if (context->getPrimitiveType(p) == PRIMITIVE_TYPE_VOXEL || maskfile.size() == 0 || !context->primitiveTextureHasTransparencyChannel(p)) { // does not have texture transparency
1915
1916 maskID.at(u) = -1;
1917 uvID.at(u) = -1;
1918
1919 } else {
1920
1921 // texture mask data //
1922
1923 // Check if this mask has already been added
1924 if (maskname.find(maskfile) != maskname.end()) { // has already been added
1925 uint ID = maskname.at(maskfile);
1926 maskID.at(u) = ID;
1927 } else { // mask has not been added
1928
1929 uint ID = maskdata.size();
1930 maskID.at(u) = ID;
1931 maskname[maskfile] = maskdata.size();
1932 maskdata.push_back(*context->getPrimitiveTextureTransparencyData(p));
1933 auto sy = maskdata.back().size();
1934 auto sx = maskdata.back().front().size();
1935 masksize.push_back(optix::make_int2(sx, sy));
1936 }
1937
1938 // uv coordinates //
1939 std::vector<vec2> uv;
1940
1941 if (parentID == 0 || context->getObjectPointer(parentID)->getObjectType() != helios::OBJECT_TYPE_TILE) { // primitives
1942 uv = context->getPrimitiveTextureUV(p);
1943 }
1944
1945 if (!uv.empty()) { // has custom (u,v) coordinates
1946 std::vector<optix::float2> uvf2;
1947 uvf2.resize(4);
1948 // first index if uvf2 is the minimum (u,v) coordinate, second index is the size of the (u,v) rectangle in x- and y-directions.
1949
1950 for (int i = 0; i < uv.size(); i++) {
1951 uvf2.at(i) = optix::make_float2(uv.at(i).x, uv.at(i).y);
1952 }
1953 if (uv.size() == 3) {
1954 uvf2.at(3) = optix::make_float2(0, 0);
1955 }
1956 uvdata.push_back(uvf2);
1957 uvID.at(u) = uvdata.size() - 1;
1958 } else { // DOES NOT have custom (u,v) coordinates
1959 uvID.at(u) = -1;
1960 }
1961 }
1962 }
1963
1964 int2 size_max(0, 0);
1965 for (int t = 0; t < maskdata.size(); t++) {
1966 int2 sz(maskdata.at(t).front().size(), maskdata.at(t).size());
1967 if (sz.x > size_max.x) {
1968 size_max.x = sz.x;
1969 }
1970 if (sz.y > size_max.y) {
1971 size_max.y = sz.y;
1972 }
1973 }
1974
1975 for (int t = 0; t < maskdata.size(); t++) {
1976 maskdata.at(t).resize(size_max.y);
1977 for (int j = 0; j < size_max.y; j++) {
1978 maskdata.at(t).at(j).resize(size_max.x);
1979 }
1980 }
1981
1982 initializeBuffer3D(maskdata_RTbuffer, maskdata);
1983 initializeBuffer1Dint2(masksize_RTbuffer, masksize);
1984 initializeBuffer1Di(maskID_RTbuffer, maskID);
1985
1986 initializeBuffer2Dfloat2(uvdata_RTbuffer, uvdata);
1987 initializeBuffer1Di(uvID_RTbuffer, uvID);
1988
1989 // Bounding box
1990 helios::vec2 xbounds, ybounds, zbounds;
1991 context->getDomainBoundingBox(xbounds, ybounds, zbounds);
1992
1993 if (periodic_flag.x == 1 || periodic_flag.y == 1) {
1994 if (!cameras.empty()) {
1995 for (auto &camera: cameras) {
1996 vec3 camerapos = camera.second.position;
1997 if (camerapos.x < xbounds.x || camerapos.x > xbounds.y || camerapos.y < ybounds.x || camerapos.y > ybounds.y) {
1998 std::cout << "WARNING (RadiationModel::updateGeometry): camera position is outside of the domain bounding box. Disabling periodic boundary conditions." << std::endl;
1999 periodic_flag.x = 0;
2000 periodic_flag.y = 0;
2001 break;
2002 }
2003 if (camerapos.z < zbounds.x) {
2004 zbounds.x = camerapos.z;
2005 }
2006 if (camerapos.z > zbounds.y) {
2007 zbounds.y = camerapos.z;
2008 }
2009 }
2010 }
2011 }
2012
2013 xbounds.x -= 1e-5;
2014 xbounds.y += 1e-5;
2015 ybounds.x -= 1e-5;
2016 ybounds.y += 1e-5;
2017 zbounds.x -= 1e-5;
2018 zbounds.y += 1e-5;
2019
2020 std::vector<uint> bbox_UUID;
2021 int bbox_face_count = 0;
2022
2023 std::vector<std::vector<optix::float3>> bbox_vertices;
2024
2025 // primitive type
2026
2027 std::vector<optix::float3> v;
2028 v.resize(4);
2029
2030 if (periodic_flag.x == 1) {
2031
2032 // -x facing
2033 v.at(0) = optix::make_float3(xbounds.x, ybounds.x, zbounds.x);
2034 v.at(1) = optix::make_float3(xbounds.x, ybounds.y, zbounds.x);
2035 v.at(2) = optix::make_float3(xbounds.x, ybounds.y, zbounds.y);
2036 v.at(3) = optix::make_float3(xbounds.x, ybounds.x, zbounds.y);
2037 bbox_vertices.push_back(v);
2038 bbox_UUID.push_back(Nprimitives + bbox_face_count);
2039 objectID.push_back(Nobjects + bbox_face_count);
2040 ptype_global.push_back(5);
2041 bbox_face_count++;
2042
2043 // +x facing
2044 v.at(0) = optix::make_float3(xbounds.y, ybounds.x, zbounds.x);
2045 v.at(1) = optix::make_float3(xbounds.y, ybounds.y, zbounds.x);
2046 v.at(2) = optix::make_float3(xbounds.y, ybounds.y, zbounds.y);
2047 v.at(3) = optix::make_float3(xbounds.y, ybounds.x, zbounds.y);
2048 bbox_vertices.push_back(v);
2049 bbox_UUID.push_back(Nprimitives + bbox_face_count);
2050 objectID.push_back(Nobjects + bbox_face_count);
2051 ptype_global.push_back(5);
2052 bbox_face_count++;
2053 }
2054 if (periodic_flag.y == 1) {
2055
2056 // -y facing
2057 v.at(0) = optix::make_float3(xbounds.x, ybounds.x, zbounds.x);
2058 v.at(1) = optix::make_float3(xbounds.y, ybounds.x, zbounds.x);
2059 v.at(2) = optix::make_float3(xbounds.y, ybounds.x, zbounds.y);
2060 v.at(3) = optix::make_float3(xbounds.x, ybounds.x, zbounds.y);
2061 bbox_vertices.push_back(v);
2062 bbox_UUID.push_back(Nprimitives + bbox_face_count);
2063 objectID.push_back(Nobjects + bbox_face_count);
2064 ptype_global.push_back(5);
2065 bbox_face_count++;
2066
2067 // +y facing
2068 v.at(0) = optix::make_float3(xbounds.x, ybounds.y, zbounds.x);
2069 v.at(1) = optix::make_float3(xbounds.y, ybounds.y, zbounds.x);
2070 v.at(2) = optix::make_float3(xbounds.y, ybounds.y, zbounds.y);
2071 v.at(3) = optix::make_float3(xbounds.x, ybounds.y, zbounds.y);
2072 bbox_vertices.push_back(v);
2073 bbox_UUID.push_back(Nprimitives + bbox_face_count);
2074 objectID.push_back(Nobjects + bbox_face_count);
2075 ptype_global.push_back(5);
2076 bbox_face_count++;
2077 }
2078
2079 initializeBuffer2Df(transform_matrix_RTbuffer, m_global);
2080 initializeBuffer1Dui(primitive_type_RTbuffer, ptype_global);
2081 initializeBuffer1Df(primitive_solid_fraction_RTbuffer, solid_fraction_global);
2082 initializeBuffer1Dchar(twosided_flag_RTbuffer, twosided_flag_global);
2083 initializeBuffer2Dfloat3(patch_vertices_RTbuffer, patch_vertices);
2084 initializeBuffer2Dfloat3(triangle_vertices_RTbuffer, triangle_vertices);
2085 initializeBuffer2Dfloat3(tile_vertices_RTbuffer, tile_vertices);
2086 initializeBuffer2Dfloat3(voxel_vertices_RTbuffer, voxel_vertices);
2087 initializeBuffer2Dfloat3(bbox_vertices_RTbuffer, bbox_vertices);
2088
2089 initializeBuffer1Dint2(object_subdivisions_RTbuffer, object_subdivisions);
2090
2091 initializeBuffer1Dui(patch_UUID_RTbuffer, patch_UUID);
2092 initializeBuffer1Dui(triangle_UUID_RTbuffer, triangle_UUID);
2093 initializeBuffer1Dui(disk_UUID_RTbuffer, disk_UUID);
2094 initializeBuffer1Dui(tile_UUID_RTbuffer, tile_UUID);
2095 initializeBuffer1Dui(voxel_UUID_RTbuffer, voxel_UUID);
2096 initializeBuffer1Dui(bbox_UUID_RTbuffer, bbox_UUID);
2097
2098 initializeBuffer1Dui(objectID_RTbuffer, objectID);
2099 initializeBuffer1Dui(primitiveID_RTbuffer, primitiveID);
2100
2101 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(patch, patch_count));
2102 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(triangle, triangle_count));
2103 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(disk, disk_count));
2104 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(tile, tile_count));
2105 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(voxel, voxel_count));
2106 RT_CHECK_ERROR(rtGeometrySetPrimitiveCount(bbox, bbox_face_count));
2107
2108 RT_CHECK_ERROR(rtAccelerationMarkDirty(geometry_acceleration));
2109
2110 /* Set the top_object variable */
2111 // NOTE: not sure if this has to be set again or not..
2112 RT_CHECK_ERROR(rtVariableSetObject(top_object, top_level_group));
2113
2114 RTsize device_memory;
2115 RT_CHECK_ERROR(rtContextGetAttribute(OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &device_memory));
2116
2117 device_memory *= 1e-6;
2118
2119 if (device_memory < 500) {
2120 std::cout << "WARNING (RadiationModel): device memory is very low (" << device_memory << " MB)" << std::endl;
2121 }
2122
2123 /* Validate/Compile OptiX Context */
2124 RT_CHECK_ERROR(rtContextValidate(OptiX_Context));
2125 RT_CHECK_ERROR(rtContextCompile(OptiX_Context));
2126
2127 // device_memory;
2128 // RT_CHECK_ERROR( rtContextGetAttribute( OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &device_memory ) );
2129
2130 // device_memory *= 1e-6;
2131 // if( device_memory < 1000 ){
2132 // printf("available device memory at end of OptiX context compile: %6.3f MB\n",device_memory);
2133 // }else{
2134 // printf("available device memory at end of OptiX context compile: %6.3f GB\n",device_memory*1e-3);
2135 // }
2136
2137 isgeometryinitialized = true;
2138
2139 // device_memory;
2140 // RT_CHECK_ERROR( rtContextGetAttribute( OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &device_memory ) );
2141
2142 // device_memory *= 1e-6;
2143 // if( device_memory < 1000 ){
2144 // printf("available device memory before acceleration build: %6.3f MB\n",device_memory);
2145 // }else{
2146 // printf("available device memory before acceleration build: %6.3f GB\n",device_memory*1e-3);
2147 // }
2148
2149 optix::int3 launch_dim_dummy = optix::make_int3(1, 1, 1);
2150 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIRECT, launch_dim_dummy.x, launch_dim_dummy.y, launch_dim_dummy.z));
2151
2152 RT_CHECK_ERROR(rtContextGetAttribute(OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &device_memory));
2153
2154 // device_memory;
2155 // RT_CHECK_ERROR( rtContextGetAttribute( OptiX_Context, RT_CONTEXT_ATTRIBUTE_AVAILABLE_DEVICE_MEMORY, sizeof(RTsize), &device_memory ) );
2156
2157 // device_memory *= 1e-6;
2158 // if( device_memory < 1000 ){
2159 // printf("available device memory at end of acceleration build: %6.3f MB\n",device_memory);
2160 // }else{
2161 // printf("available device memory at end of acceleration build: %6.3f GB\n",device_memory*1e-3);
2162 // }
2163
2164 radiativepropertiesneedupdate = true;
2165
2166 if (message_flag) {
2167 std::cout << "done." << std::endl;
2168 }
2169}
2170
2171void RadiationModel::updateRadiativeProperties() {
2172
2173 // Possible scenarios for specifying a primitive's radiative properties
2174 // 1. If primitive data of form reflectivity_band/transmissivity_band is given, this value is used and overrides any other option.
2175 // 2. If primitive data of form reflectivity_spectrum/transmissivity_spectrum is given that references global data containing spectral reflectivity/transmissivity:
2176 // 2a. If radiation source spectrum was not given, assume source spectral intensity is constant over band and calculate using primitive spectrum
2177 // 2b. If radiation source spectrum was given, calculate using both source and primitive spectrum.
2178
2179 if (message_flag) {
2180 std::cout << "Updating radiative properties..." << std::flush;
2181 }
2182
2183 uint Nbands = radiation_bands.size(); // number of radiative bands
2184 uint Nsources = radiation_sources.size();
2185 uint Ncameras = cameras.size();
2186 size_t Nobjects = primitiveID.size();
2187 size_t Nprimitives = context_UUIDs.size();
2188
2189 scattering_iterations_needed.clear();
2190 for (auto &band: radiation_bands) {
2191 scattering_iterations_needed[band.first] = false;
2192 }
2193
2194 std::vector<std::vector<std::vector<float>>> rho, tau; // first index is the source, second index is the primitive, third index is the band
2195 std::vector<std::vector<std::vector<std::vector<float>>>> rho_cam, tau_cam; // Fourth index is the camera
2196 float eps;
2197
2198 std::string prop;
2199 std::vector<std::string> band_labels;
2200 for (auto &band: radiation_bands) {
2201 band_labels.push_back(band.first);
2202 }
2203
2204 rho.resize(Nsources);
2205 tau.resize(Nsources);
2206 for (size_t s = 0; s < Nsources; s++) {
2207 rho.at(s).resize(Nprimitives);
2208 tau.at(s).resize(Nprimitives);
2209 for (size_t p = 0; p < Nprimitives; p++) {
2210 rho.at(s).at(p).resize(Nbands);
2211 tau.at(s).at(p).resize(Nbands);
2212 }
2213 }
2214 if (Ncameras) {
2215 rho_cam.resize(Nsources);
2216 tau_cam.resize(Nsources);
2217 for (size_t s = 0; s < Nsources; s++) {
2218 rho_cam.at(s).resize(Nprimitives);
2219 tau_cam.at(s).resize(Nprimitives);
2220 for (size_t p = 0; p < Nprimitives; p++) {
2221 rho_cam.at(s).at(p).resize(Nbands);
2222 tau_cam.at(s).at(p).resize(Nbands);
2223 for (size_t b = 0; b < Nbands; b++) {
2224 rho_cam.at(s).at(p).at(b).resize(Ncameras);
2225 tau_cam.at(s).at(p).at(b).resize(Ncameras);
2226 }
2227 }
2228 }
2229 }
2230
2231 // Cache all unique camera spectral responses for all cameras and bands
2232 std::vector<std::vector<std::vector<helios::vec2>>> camera_response_unique;
2233 camera_response_unique.resize(Ncameras);
2234 if (Ncameras > 0) {
2235 uint cam = 0;
2236 for (const auto &camera: cameras) {
2237
2238 camera_response_unique.at(cam).resize(Nbands);
2239
2240 for (uint b = 0; b < Nbands; b++) {
2241
2242 if (camera.second.band_spectral_response.find(band_labels.at(b)) == camera.second.band_spectral_response.end()) {
2243 continue;
2244 }
2245
2246 std::string camera_response = camera.second.band_spectral_response.at(band_labels.at(b));
2247
2248 if (!camera_response.empty()) {
2249
2250 if (!context->doesGlobalDataExist(camera_response.c_str())) {
2251 if (camera_response != "uniform") {
2252 std::cerr << "WARNING (RadiationModel::updateRadiativeProperties): Camera spectral response \"" << camera_response << "\" does not exist. Assuming a uniform spectral response..." << std::endl;
2253 }
2254 } else if (context->getGlobalDataType(camera_response.c_str()) == helios::HELIOS_TYPE_VEC2) {
2255
2256 std::vector<helios::vec2> data = loadSpectralData(camera_response.c_str());
2257
2258 camera_response_unique.at(cam).at(b) = data;
2259
2260 } else if (context->getGlobalDataType(camera_response.c_str()) != helios::HELIOS_TYPE_VEC2 && context->getGlobalDataType(camera_response.c_str()) != helios::HELIOS_TYPE_STRING) {
2261 camera_response.clear();
2262 std::cout << "WARNING (RadiationModel::runBand): Camera spectral response \"" << camera_response << "\" is not of type HELIOS_TYPE_VEC2 or HELIOS_TYPE_STRING. Assuming a uniform spectral response..." << std::endl;
2263 }
2264 }
2265 }
2266 cam++;
2267 }
2268 }
2269
2270 // Spectral integration cache to avoid redundant computations
2271 std::unordered_map<std::string, float> spectral_integration_cache;
2272
2273#ifdef USE_OPENMP
2274 // Temporary cache for this thread group (will be merged later)
2275 std::unordered_map<std::string, float> temp_spectral_cache;
2276#endif
2277
2278 // Helper function to create cache keys for spectral integrations
2279 auto createCacheKey = [](const std::string &spectrum_label, uint source_id, uint band_id, uint camera_id, const std::string &type) -> std::string {
2280 return spectrum_label + "_" + std::to_string(source_id) + "_" + std::to_string(band_id) + "_" + std::to_string(camera_id) + "_" + type;
2281 };
2282
2283 // Helper function to get from cache (thread-safe)
2284 auto getCachedValue = [&](const std::string &cache_key, bool &found) -> float {
2285 float result = 0.0f;
2286 found = false;
2287
2288#ifdef USE_OPENMP
2289#pragma omp critical
2290 {
2291#endif
2292 // Check shared cache
2293 auto cache_it = spectral_integration_cache.find(cache_key);
2294 if (cache_it != spectral_integration_cache.end()) {
2295 found = true;
2296 result = cache_it->second;
2297 }
2298#ifdef USE_OPENMP
2299 }
2300#endif
2301 return result;
2302 };
2303
2304 // Helper function to store in cache (thread-safe)
2305 auto setCachedValue = [&](const std::string &cache_key, float value) {
2306#ifdef USE_OPENMP
2307#pragma omp critical
2308 {
2309#endif
2310 spectral_integration_cache[cache_key] = value;
2311#ifdef USE_OPENMP
2312 }
2313#endif
2314 };
2315
2316 // Helper function for cached interpolation (thread-safe)
2317 auto cachedInterp1 = [&](const std::vector<helios::vec2> &spectrum, float wavelength, const std::string &spectrum_id) -> float {
2318 // Create cache key for this specific interpolation
2319 std::string cache_key = "interp_" + spectrum_id + "_" + std::to_string(wavelength);
2320
2321 bool found = false;
2322 float cached_result = getCachedValue(cache_key, found);
2323 if (found) {
2324 return cached_result;
2325 }
2326
2327 // Perform interpolation and cache result
2328 float result = interp1(spectrum, wavelength);
2329 setCachedValue(cache_key, result);
2330 return result;
2331 };
2332
2333 // Cached version of integrateSpectrum with source spectrum
2334 auto cachedIntegrateSpectrumWithSource = [&](uint source_ID, const std::vector<helios::vec2> &object_spectrum, float wavelength1, float wavelength2, const std::string &object_spectrum_id) -> float {
2335 if (source_ID >= radiation_sources.size() || object_spectrum.size() < 2 || wavelength1 >= wavelength2) {
2336 return 0.0f; // Handle edge cases gracefully
2337 }
2338
2339 std::vector<helios::vec2> source_spectrum = radiation_sources.at(source_ID).source_spectrum;
2340 std::string source_id = "source_" + std::to_string(source_ID);
2341
2342 int istart = 0;
2343 int iend = (int) object_spectrum.size() - 1;
2344 for (auto i = 0; i < object_spectrum.size() - 1; i++) {
2345 if (object_spectrum.at(i).x <= wavelength1 && object_spectrum.at(i + 1).x > wavelength1) {
2346 istart = i;
2347 }
2348 if (object_spectrum.at(i).x <= wavelength2 && object_spectrum.at(i + 1).x > wavelength2) {
2349 iend = i + 1;
2350 break;
2351 }
2352 }
2353
2354 float E = 0;
2355 float Etot = 0;
2356 for (auto i = istart; i < iend; i++) {
2357 float x0 = object_spectrum.at(i).x;
2358 float Esource0 = cachedInterp1(source_spectrum, x0, source_id);
2359 float Eobject0 = object_spectrum.at(i).y;
2360
2361 float x1 = object_spectrum.at(i + 1).x;
2362 float Eobject1 = object_spectrum.at(i + 1).y;
2363 float Esource1 = cachedInterp1(source_spectrum, x1, source_id);
2364
2365 E += 0.5f * (Eobject0 * Esource0 + Eobject1 * Esource1) * (x1 - x0);
2366 Etot += 0.5f * (Esource1 + Esource0) * (x1 - x0);
2367 }
2368
2369 return (Etot != 0.0f) ? E / Etot : 0.0f;
2370 };
2371
2372 // Cached version of integrateSpectrum with source and camera spectra
2373 auto cachedIntegrateSpectrumWithSourceAndCamera = [&](uint source_ID, const std::vector<helios::vec2> &object_spectrum, const std::vector<helios::vec2> &camera_spectrum, uint camera_index, uint band_index,
2374 const std::string &object_spectrum_id) -> float {
2375 if (source_ID >= radiation_sources.size() || object_spectrum.size() < 2) {
2376 return 0.0f;
2377 }
2378
2379 std::vector<helios::vec2> source_spectrum = radiation_sources.at(source_ID).source_spectrum;
2380 std::string source_id = "source_" + std::to_string(source_ID);
2381 std::string camera_id = "camera_" + std::to_string(camera_index) + "_band_" + std::to_string(band_index); // Include band for unique cache key per band
2382
2383 float E = 0;
2384 float Etot = 0;
2385 for (auto i = 1; i < object_spectrum.size(); i++) {
2386 if (object_spectrum.at(i).x <= source_spectrum.front().x || object_spectrum.at(i).x <= camera_spectrum.front().x) {
2387 continue;
2388 }
2389 if (object_spectrum.at(i).x > source_spectrum.back().x || object_spectrum.at(i).x > camera_spectrum.back().x) {
2390 break;
2391 }
2392
2393 float x1 = object_spectrum.at(i).x;
2394 float Eobject1 = object_spectrum.at(i).y;
2395 float Esource1 = cachedInterp1(source_spectrum, x1, source_id);
2396 float Ecamera1 = cachedInterp1(camera_spectrum, x1, camera_id);
2397
2398 float x0 = object_spectrum.at(i - 1).x;
2399 float Eobject0 = object_spectrum.at(i - 1).y;
2400 float Esource0 = cachedInterp1(source_spectrum, x0, source_id);
2401 float Ecamera0 = cachedInterp1(camera_spectrum, x0, camera_id);
2402
2403 E += 0.5f * ((Eobject1 * Esource1 * Ecamera1) + (Eobject0 * Ecamera0 * Esource0)) * (x1 - x0);
2404 Etot += 0.5f * (Esource1 + Esource0) * (x1 - x0);
2405 }
2406
2407 return (Etot != 0.0f) ? E / Etot : 0.0f;
2408 };
2409
2410 // Cache all unique primitive reflectivity and transmissivity spectra before assigning to primitives
2411
2412 // first, figure out all of the spectra referenced by all primitives and store it in "surface_spectra" to avoid having to load it again
2413 std::map<std::string, std::vector<helios::vec2>> surface_spectra_rho;
2414 std::map<std::string, std::vector<helios::vec2>> surface_spectra_tau;
2415 for (size_t u = 0; u < Nprimitives; u++) {
2416
2417 uint UUID = context_UUIDs.at(u);
2418
2419 if (context->doesPrimitiveDataExist(UUID, "reflectivity_spectrum")) {
2420 if (context->getPrimitiveDataType("reflectivity_spectrum") == HELIOS_TYPE_STRING) {
2421 std::string spectrum_label;
2422 context->getPrimitiveData(UUID, "reflectivity_spectrum", spectrum_label);
2423
2424 // get the spectral reflectivity data and store it in surface_spectra to avoid having to load it again
2425 if (surface_spectra_rho.find(spectrum_label) == surface_spectra_rho.end()) {
2426 if (!context->doesGlobalDataExist(spectrum_label.c_str())) {
2427 if (message_flag && !spectrum_label.empty()) {
2428 std::cerr << "WARNING (RadiationModel::runBand): Primitive spectral reflectivity \"" << spectrum_label << "\" does not exist. Using default reflectivity of 0..." << std::flush;
2429 }
2430 std::vector<helios::vec2> data;
2431 surface_spectra_rho.emplace(spectrum_label, data);
2432 } else if (context->getGlobalDataType(spectrum_label.c_str()) == HELIOS_TYPE_VEC2) {
2433
2434 std::vector<helios::vec2> data = loadSpectralData(spectrum_label.c_str());
2435 surface_spectra_rho.emplace(spectrum_label, data);
2436
2437 } else if (context->getGlobalDataType(spectrum_label.c_str()) != helios::HELIOS_TYPE_VEC2 && context->getGlobalDataType(spectrum_label.c_str()) != helios::HELIOS_TYPE_STRING) {
2438 spectrum_label.clear();
2439 std::cout << "WARNING (RadiationModel::runBand): Object spectral reflectivity \"" << spectrum_label << "\" is not of type HELIOS_TYPE_VEC2 or HELIOS_TYPE_STRING. Assuming a uniform spectral distribution..." << std::flush;
2440 }
2441 }
2442 }
2443 }
2444
2445 if (context->doesPrimitiveDataExist(UUID, "transmissivity_spectrum")) {
2446 if (context->getPrimitiveDataType("transmissivity_spectrum") == HELIOS_TYPE_STRING) {
2447 std::string spectrum_label;
2448 context->getPrimitiveData(UUID, "transmissivity_spectrum", spectrum_label);
2449
2450 // get the spectral transmissivity data and store it in surface_spectra to avoid having to load it again
2451 if (surface_spectra_tau.find(spectrum_label) == surface_spectra_tau.end()) {
2452 if (!context->doesGlobalDataExist(spectrum_label.c_str())) {
2453 if (message_flag && !spectrum_label.empty()) {
2454 std::cerr << "WARNING (RadiationModel::runBand): Primitive spectral transmissivity \"" << spectrum_label << "\" does not exist. Using default transmissivity of 0..." << std::flush;
2455 }
2456 std::vector<helios::vec2> data;
2457 surface_spectra_tau.emplace(spectrum_label, data);
2458 } else if (context->getGlobalDataType(spectrum_label.c_str()) == HELIOS_TYPE_VEC2) {
2459
2460 std::vector<helios::vec2> data = loadSpectralData(spectrum_label.c_str());
2461 surface_spectra_tau.emplace(spectrum_label, data);
2462
2463 } else if (context->getGlobalDataType(spectrum_label.c_str()) != helios::HELIOS_TYPE_VEC2 && context->getGlobalDataType(spectrum_label.c_str()) != helios::HELIOS_TYPE_STRING) {
2464 spectrum_label.clear();
2465 std::cout << "WARNING (RadiationModel::runBand): Object spectral transmissivity \"" << spectrum_label << "\" is not of type HELIOS_TYPE_VEC2 or HELIOS_TYPE_STRING. Assuming a uniform spectral distribution..." << std::flush;
2466 }
2467 }
2468 }
2469 }
2470 }
2471
2472 // printf("%d (rho) %d (tau) unique surface spectra loaded\n", surface_spectra_rho.size(),surface_spectra_tau.size());
2473
2474 // second, calculate unique values of rho and tau for all sources and bands
2475 std::map<std::string, std::vector<std::vector<float>>> rho_unique;
2476 std::map<std::string, std::vector<std::vector<float>>> tau_unique;
2477
2478 std::map<std::string, std::vector<std::vector<std::vector<float>>>> rho_cam_unique;
2479 std::map<std::string, std::vector<std::vector<std::vector<float>>>> tau_cam_unique;
2480
2481 std::vector<std::vector<float>> empty;
2482 empty.resize(Nbands);
2483 for (uint b = 0; b < Nbands; b++) {
2484 empty.at(b).resize(Nsources, 0);
2485 }
2486 std::vector<std::vector<std::vector<float>>> empty_cam;
2487 if (Ncameras > 0) {
2488 empty_cam.resize(Nbands);
2489 for (uint b = 0; b < Nbands; b++) {
2490 empty_cam.at(b).resize(Nsources);
2491 for (uint s = 0; s < Nsources; s++) {
2492 empty_cam.at(b).at(s).resize(Ncameras, 0);
2493 }
2494 }
2495 }
2496
2497 // Convert maps to vectors for OpenMP indexing
2498 std::vector<std::pair<std::string, std::vector<helios::vec2>>> spectra_rho_vector(surface_spectra_rho.begin(), surface_spectra_rho.end());
2499
2500 // Pre-initialize all map entries before parallel processing to avoid race conditions
2501 for (const auto &spectrum: spectra_rho_vector) {
2502 rho_unique[spectrum.first] = empty;
2503 if (Ncameras > 0) {
2504 rho_cam_unique[spectrum.first] = empty_cam;
2505 }
2506 }
2507
2508 // Process reflectivity spectra with OpenMP parallelization
2509#ifdef USE_OPENMP
2510#pragma omp parallel for schedule(dynamic)
2511#endif
2512 for (int spectrum_idx = 0; spectrum_idx < (int) spectra_rho_vector.size(); spectrum_idx++) {
2513 const auto &spectrum = spectra_rho_vector[spectrum_idx];
2514
2515 for (uint b = 0; b < Nbands; b++) {
2516 std::string band = band_labels.at(b);
2517
2518 for (uint s = 0; s < Nsources; s++) {
2519
2520 // integrate with caching
2521 auto band_it = radiation_bands.find(band);
2522 if (band_it != radiation_bands.end() && band_it->second.wavebandBounds.x != 0 && band_it->second.wavebandBounds.y != 0 && !spectrum.second.empty()) {
2523 if (!radiation_sources.at(s).source_spectrum.empty()) {
2524 std::string cache_key = createCacheKey(spectrum.first, s, b, 0, "rho_source");
2525 bool found;
2526 float cached_result = getCachedValue(cache_key, found);
2527 if (found) {
2528 rho_unique[spectrum.first][b][s] = cached_result;
2529 } else {
2530 float result = cachedIntegrateSpectrumWithSource(s, spectrum.second, band_it->second.wavebandBounds.x, band_it->second.wavebandBounds.y, spectrum.first);
2531 setCachedValue(cache_key, result);
2532 rho_unique[spectrum.first][b][s] = result;
2533 }
2534 } else {
2535 // source spectrum not provided, assume source intensity is constant over the band
2536 std::string cache_key = createCacheKey(spectrum.first, s, b, 0, "rho_no_source");
2537 bool found;
2538 float cached_result = getCachedValue(cache_key, found);
2539 if (found) {
2540 rho_unique[spectrum.first][b][s] = cached_result;
2541 } else {
2542 float result = integrateSpectrum(spectrum.second, band_it->second.wavebandBounds.x, band_it->second.wavebandBounds.y) / (band_it->second.wavebandBounds.y - band_it->second.wavebandBounds.x);
2543 setCachedValue(cache_key, result);
2544 rho_unique[spectrum.first][b][s] = result;
2545 }
2546 }
2547 } else {
2548 rho_unique[spectrum.first][b][s] = rho_default;
2549 }
2550
2551 // cameras
2552 if (Ncameras > 0) {
2553 uint cam = 0;
2554 for (const auto &camera: cameras) {
2555
2556 if (camera_response_unique.at(cam).at(b).empty()) {
2557 rho_cam_unique[spectrum.first][b][s][cam] = rho_unique[spectrum.first][b][s];
2558 } else {
2559
2560 // integrate with caching
2561 if (!spectrum.second.empty()) {
2562 if (!radiation_sources.at(s).source_spectrum.empty()) {
2563 std::string cache_key = createCacheKey(spectrum.first, s, b, cam, "rho_cam_source");
2564 bool found;
2565 float cached_result = getCachedValue(cache_key, found);
2566 if (found) {
2567 rho_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = cached_result;
2568 } else {
2569 float result = cachedIntegrateSpectrumWithSourceAndCamera(s, spectrum.second, camera_response_unique.at(cam).at(b), cam, b, spectrum.first);
2570 setCachedValue(cache_key, result);
2571 rho_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = result;
2572 }
2573 } else {
2574 std::string cache_key = createCacheKey(spectrum.first, s, b, cam, "rho_cam_no_source");
2575 bool found;
2576 float cached_result = getCachedValue(cache_key, found);
2577 if (found) {
2578 rho_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = cached_result;
2579 } else {
2580 float result = integrateSpectrum(spectrum.second, camera_response_unique.at(cam).at(b));
2581 setCachedValue(cache_key, result);
2582 rho_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = result;
2583 }
2584 }
2585 } else {
2586 rho_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = rho_default;
2587 }
2588 }
2589
2590 cam++;
2591 }
2592 }
2593 }
2594 }
2595 }
2596
2597 // Convert tau spectra to vector for OpenMP indexing
2598 std::vector<std::pair<std::string, std::vector<helios::vec2>>> spectra_tau_vector(surface_spectra_tau.begin(), surface_spectra_tau.end());
2599
2600 // Pre-initialize all map entries before parallel processing to avoid race conditions
2601 for (const auto &spectrum: spectra_tau_vector) {
2602 tau_unique[spectrum.first] = empty;
2603 if (Ncameras > 0) {
2604 tau_cam_unique[spectrum.first] = empty_cam;
2605 }
2606 }
2607
2608 // Process transmissivity spectra with OpenMP parallelization
2609#ifdef USE_OPENMP
2610#pragma omp parallel for schedule(dynamic)
2611#endif
2612 for (int spectrum_idx = 0; spectrum_idx < (int) spectra_tau_vector.size(); spectrum_idx++) {
2613 const auto &spectrum = spectra_tau_vector[spectrum_idx];
2614
2615 for (uint b = 0; b < Nbands; b++) {
2616 std::string band = band_labels.at(b);
2617
2618 for (uint s = 0; s < Nsources; s++) {
2619
2620 // integrate with caching
2621 auto band_it = radiation_bands.find(band);
2622 if (band_it != radiation_bands.end() && band_it->second.wavebandBounds.x != 0 && band_it->second.wavebandBounds.y != 0 && !spectrum.second.empty()) {
2623 if (!radiation_sources.at(s).source_spectrum.empty()) {
2624 std::string cache_key = createCacheKey(spectrum.first, s, b, 0, "tau_source");
2625 bool found;
2626 float cached_result = getCachedValue(cache_key, found);
2627 if (found) {
2628 tau_unique[spectrum.first][b][s] = cached_result;
2629 } else {
2630 float result = cachedIntegrateSpectrumWithSource(s, spectrum.second, band_it->second.wavebandBounds.x, band_it->second.wavebandBounds.y, spectrum.first);
2631 setCachedValue(cache_key, result);
2632 tau_unique[spectrum.first][b][s] = result;
2633 }
2634 } else {
2635 std::string cache_key = createCacheKey(spectrum.first, s, b, 0, "tau_no_source");
2636 bool found;
2637 float cached_result = getCachedValue(cache_key, found);
2638 if (found) {
2639 tau_unique[spectrum.first][b][s] = cached_result;
2640 } else {
2641 float result = integrateSpectrum(spectrum.second, band_it->second.wavebandBounds.x, band_it->second.wavebandBounds.y) / (band_it->second.wavebandBounds.y - band_it->second.wavebandBounds.x);
2642 setCachedValue(cache_key, result);
2643 tau_unique[spectrum.first][b][s] = result;
2644 }
2645 }
2646 } else {
2647 tau_unique[spectrum.first][b][s] = tau_default;
2648 }
2649
2650 // cameras
2651 if (Ncameras > 0) {
2652 uint cam = 0;
2653 for (const auto &camera: cameras) {
2654
2655 if (camera_response_unique.at(cam).at(b).empty()) {
2656
2657 tau_cam_unique[spectrum.first][b][s][cam] = tau_unique[spectrum.first][b][s];
2658
2659 } else {
2660
2661 // integrate with caching
2662 if (!spectrum.second.empty()) {
2663 if (!radiation_sources.at(s).source_spectrum.empty()) {
2664 std::string cache_key = createCacheKey(spectrum.first, s, b, cam, "tau_cam_source");
2665 bool found;
2666 float cached_result = getCachedValue(cache_key, found);
2667 if (found) {
2668 tau_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = cached_result;
2669 } else {
2670 float result = cachedIntegrateSpectrumWithSourceAndCamera(s, spectrum.second, camera_response_unique.at(cam).at(b), cam, b, spectrum.first);
2671 setCachedValue(cache_key, result);
2672 tau_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = result;
2673 }
2674 } else {
2675 std::string cache_key = createCacheKey(spectrum.first, s, b, cam, "tau_cam_no_source");
2676 bool found;
2677 float cached_result = getCachedValue(cache_key, found);
2678 if (found) {
2679 tau_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = cached_result;
2680 } else {
2681 float result = integrateSpectrum(spectrum.second, camera_response_unique.at(cam).at(b));
2682 setCachedValue(cache_key, result);
2683 tau_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = result;
2684 }
2685 }
2686 } else {
2687 tau_cam_unique.at(spectrum.first).at(b).at(s).at(cam) = tau_default;
2688 }
2689 }
2690
2691 cam++;
2692 }
2693 }
2694 }
2695 }
2696 }
2697
2698 for (size_t u = 0; u < Nprimitives; u++) {
2699
2700 uint UUID = context_UUIDs.at(u);
2701
2702 helios::PrimitiveType type = context->getPrimitiveType(UUID);
2703
2704 if (type == helios::PRIMITIVE_TYPE_VOXEL) {
2705
2706 // // NOTE: This is a little confusing - for volumes of participating media, we're going to use the "rho" variable to store the absorption coefficient and use the "tau" variable to store the scattering coefficient. This is
2707 // to save on memory so we don't have to define separate arrays.
2708 //
2709 // // Absorption coefficient
2710 //
2711 // prop = "attenuation_coefficient_" + band;
2712 //
2713 // if (context->doesPrimitiveDataExist(UUID, prop.c_str())) {
2714 // context->getPrimitiveData(UUID, prop.c_str(), rho.at(u).at(b));
2715 // } else {
2716 // rho.at(u).at(b) = kappa_default;
2717 // context->setPrimitiveData(UUID, prop.c_str(), kappa_default);
2718 // }
2719 //
2720 // if (rho.at(u).at(b) < 0) {
2721 // rho.at(u).at(b) = 0.f;
2722 // if (message_flag) {
2723 // std::cout
2724 // << "WARNING (RadiationModel): absorption coefficient cannot be less than 0. Clamping to 0 for band "
2725 // << band << "." << std::endl;
2726 // }
2727 // } else if (rho.at(u).at(b) > 1.f) {
2728 // rho.at(u).at(b) = 1.f;
2729 // if (message_flag) {
2730 // std::cout
2731 // << "WARNING (RadiationModel): absorption coefficient cannot be greater than 1. Clamping to 1 for band "
2732 // << band << "." << std::endl;
2733 // }
2734 // }
2735 //
2736 // // Scattering coefficient
2737 //
2738 // prop = "scattering_coefficient_" + band;
2739 //
2740 // if (context->doesPrimitiveDataExist(UUID, prop.c_str())) {
2741 // context->getPrimitiveData(UUID, prop.c_str(), tau[u]);
2742 // } else {
2743 // tau.at(u).at(b) = sigmas_default;
2744 // context->setPrimitiveData(UUID, prop.c_str(), sigmas_default);
2745 // }
2746 //
2747 // if (tau.at(u).at(b) < 0) {
2748 // tau.at(u).at(b) = 0.f;
2749 // if (message_flag) {
2750 // std::cout
2751 // << "WARNING (RadiationModel): scattering coefficient cannot be less than 0. Clamping to 0 for band "
2752 // << band << "." << std::endl;
2753 // }
2754 // } else if (tau.at(u).at(b) > 1.f) {
2755 // tau.at(u).at(b) = 1.f;
2756 // if (message_flag) {
2757 // std::cout
2758 // << "WARNING (RadiationModel): scattering coefficient cannot be greater than 1. Clamping to 1 for band "
2759 // << band << "." << std::endl;
2760 // }
2761 // }
2762
2763 } else { // other than voxels
2764
2765 // Reflectivity
2766
2767 // check for primitive data of form "reflectivity_spectrum" that can be used to calculate reflectivity
2768 std::string spectrum_label;
2769 if (context->doesPrimitiveDataExist(UUID, "reflectivity_spectrum")) {
2770 if (context->getPrimitiveDataType("reflectivity_spectrum") == HELIOS_TYPE_STRING) {
2771 context->getPrimitiveData(UUID, "reflectivity_spectrum", spectrum_label);
2772 }
2773 }
2774
2775 uint b = 0;
2776 for (const auto &band: band_labels) {
2777
2778 // check for primitive data of form "reflectivity_bandname"
2779 prop = "reflectivity_" + band;
2780
2781 float rho_s = rho_default;
2782 if (context->doesPrimitiveDataExist(UUID, prop.c_str())) {
2783 context->getPrimitiveData(UUID, prop.c_str(), rho_s);
2784 }
2785
2786 for (uint s = 0; s < Nsources; s++) {
2787 // if reflectivity was manually set, or a spectrum was given and the global data exists
2788 if (rho_s != rho_default || spectrum_label.empty() || !context->doesGlobalDataExist(spectrum_label.c_str()) || rho_unique.find(spectrum_label) == rho_unique.end()) {
2789
2790 rho.at(s).at(u).at(b) = rho_s;
2791
2792 // cameras
2793 for (uint cam = 0; cam < Ncameras; cam++) {
2794 rho_cam.at(s).at(u).at(b).at(cam) = rho_s;
2795 }
2796
2797 // use spectrum
2798 } else {
2799
2800 rho.at(s).at(u).at(b) = rho_unique.at(spectrum_label).at(b).at(s);
2801
2802 // cameras
2803 for (uint cam = 0; cam < Ncameras; cam++) {
2804 rho_cam.at(s).at(u).at(b).at(cam) = rho_cam_unique.at(spectrum_label).at(b).at(s).at(cam);
2805 }
2806 }
2807
2808 // error checking
2809 if (rho.at(s).at(u).at(b) < 0) {
2810 rho.at(s).at(u).at(b) = 0.f;
2811 if (message_flag) {
2812 std::cout << "WARNING (RadiationModel): reflectivity cannot be less than 0. Clamping to 0 for band " << band << "." << std::flush;
2813 }
2814 } else if (rho.at(s).at(u).at(b) > 1.f) {
2815 rho.at(s).at(u).at(b) = 1.f;
2816 if (message_flag) {
2817 std::cout << "WARNING (RadiationModel): reflectivity cannot be greater than 1. Clamping to 1 for band " << band << "." << std::flush;
2818 }
2819 }
2820 if (rho.at(s).at(u).at(b) != 0) {
2821 scattering_iterations_needed.at(band) = true;
2822 }
2823 for (auto &odata: output_prim_data) {
2824 if (odata == "reflectivity") {
2825 context->setPrimitiveData(UUID, ("reflectivity_" + std::to_string(s) + "_" + band).c_str(), rho.at(s).at(u).at(b));
2826 }
2827 }
2828 }
2829 b++;
2830 }
2831
2832 // Transmissivity
2833
2834 // check for primitive data of form "transmissivity_spectrum" that can be used to calculate transmissivity
2835 spectrum_label.resize(0);
2836 if (context->doesPrimitiveDataExist(UUID, "transmissivity_spectrum")) {
2837 if (context->getPrimitiveDataType("transmissivity_spectrum") == HELIOS_TYPE_STRING) {
2838 context->getPrimitiveData(UUID, "transmissivity_spectrum", spectrum_label);
2839 }
2840 }
2841
2842 b = 0;
2843 for (const auto &band: band_labels) {
2844
2845 // check for primitive data of form "transmissivity_bandname"
2846 prop = "transmissivity_" + band;
2847
2848 float tau_s = tau_default;
2849 if (context->doesPrimitiveDataExist(UUID, prop.c_str())) {
2850 context->getPrimitiveData(UUID, prop.c_str(), tau_s);
2851 }
2852
2853 for (uint s = 0; s < Nsources; s++) {
2854 // if transmissivity was manually set, or a spectrum was given and the global data exists
2855 if (tau_s != tau_default || spectrum_label.empty() || !context->doesGlobalDataExist(spectrum_label.c_str()) || tau_unique.find(spectrum_label) == tau_unique.end()) {
2856
2857 tau.at(s).at(u).at(b) = tau_s;
2858
2859 // cameras
2860 for (uint cam = 0; cam < Ncameras; cam++) {
2861 tau_cam.at(s).at(u).at(b).at(cam) = tau_s;
2862 }
2863
2864 } else {
2865
2866 tau.at(s).at(u).at(b) = tau_unique.at(spectrum_label).at(b).at(s);
2867
2868 // cameras
2869 for (uint cam = 0; cam < Ncameras; cam++) {
2870 tau_cam.at(s).at(u).at(b).at(cam) = tau_cam_unique.at(spectrum_label).at(b).at(s).at(cam);
2871 }
2872 }
2873
2874 // error checking
2875 if (tau.at(s).at(u).at(b) < 0) {
2876 tau.at(s).at(u).at(b) = 0.f;
2877 if (message_flag) {
2878 std::cout << "WARNING (RadiationModel): transmissivity cannot be less than 0. Clamping to 0 for band " << band << "." << std::endl;
2879 }
2880 } else if (tau.at(s).at(u).at(b) > 1.f) {
2881 tau.at(s).at(u).at(b) = 1.f;
2882 if (message_flag) {
2883 std::cout << "WARNING (RadiationModel): transmissivity cannot be greater than 1. Clamping to 1 for band " << band << "." << std::endl;
2884 }
2885 }
2886 if (tau.at(s).at(u).at(b) != 0) {
2887 scattering_iterations_needed.at(band) = true;
2888 }
2889 for (auto &odata: output_prim_data) {
2890 if (odata == "transmissivity") {
2891 context->setPrimitiveData(UUID, ("transmissivity_" + std::to_string(s) + "_" + band).c_str(), tau.at(s).at(u).at(b));
2892 }
2893 }
2894 }
2895 b++;
2896 }
2897
2898 // Emissivity (only for error checking)
2899
2900 b = 0;
2901 for (const auto &band: band_labels) {
2902
2903 prop = "emissivity_" + band;
2904
2905 if (context->doesPrimitiveDataExist(UUID, prop.c_str())) {
2906 context->getPrimitiveData(UUID, prop.c_str(), eps);
2907 } else {
2908 eps = eps_default;
2909 }
2910
2911 if (eps < 0) {
2912 eps = 0.f;
2913 if (message_flag) {
2914 std::cout << "WARNING (RadiationModel): emissivity cannot be less than 0. Clamping to 0 for band " << band << "." << std::endl;
2915 }
2916 } else if (eps > 1.f) {
2917 eps = 1.f;
2918 if (message_flag) {
2919 std::cout << "WARNING (RadiationModel): emissivity cannot be greater than 1. Clamping to 1 for band " << band << "." << std::endl;
2920 }
2921 }
2922 if (eps != 1) {
2923 scattering_iterations_needed.at(band) = true;
2924 }
2925
2926 assert(doesBandExist(band));
2927
2928 for (uint s = 0; s < Nsources; s++) {
2929 if (radiation_bands.at(band).emissionFlag) { // emission enabled
2930 if (eps != 1.f && rho.at(s).at(u).at(b) == 0 && tau.at(s).at(u).at(b) == 0) {
2931 rho.at(s).at(u).at(b) = 1.f - eps;
2932 } else if (eps + tau.at(s).at(u).at(b) + rho.at(s).at(u).at(b) != 1.f && eps > 0.f) {
2933 helios_runtime_error("ERROR (RadiationModel): emissivity, transmissivity, and reflectivity must sum to 1 to ensure energy conservation. Band " + band + ", Primitive #" + std::to_string(UUID) + ": eps=" +
2934 std::to_string(eps) + ", tau=" + std::to_string(tau.at(s).at(u).at(b)) + ", rho=" + std::to_string(rho.at(s).at(u).at(b)) + ". It is also possible that you forgot to disable emission for this band.");
2935 } else if (radiation_bands.at(band).scatteringDepth == 0 && eps != 1.f) {
2936 eps = 1.f;
2937 rho.at(s).at(u).at(b) = 0.f;
2938 tau.at(s).at(u).at(b) = 0.f;
2939 }
2940 } else if (tau.at(s).at(u).at(b) + rho.at(s).at(u).at(b) > 1.f) {
2941 helios_runtime_error("ERROR (RadiationModel): transmissivity and reflectivity cannot sum to greater than 1 ensure energy conservation. Band " + band + ", Primitive #" + std::to_string(UUID) + ": eps=" + std::to_string(eps) +
2942 ", tau=" + std::to_string(tau.at(s).at(u).at(b)) + ", rho=" + std::to_string(rho.at(s).at(u).at(b)) + ". It is also possible that you forgot to disable emission for this band.");
2943 }
2944 }
2945 b++;
2946 }
2947 }
2948 }
2949
2950 initializeBuffer1Df(rho_RTbuffer, flatten(rho));
2951 initializeBuffer1Df(tau_RTbuffer, flatten(tau));
2952
2953 initializeBuffer1Df(rho_cam_RTbuffer, flatten(rho_cam));
2954 initializeBuffer1Df(tau_cam_RTbuffer, flatten(tau_cam));
2955
2956 // Specular reflection exponent
2957 std::vector<float> specular_exponent;
2958 specular_exponent.resize(Nprimitives, 0.f);
2959 std::vector<float> specular_scale;
2960 specular_scale.resize(Nprimitives, 0.f);
2961 bool specular_exponent_specified = false;
2962 bool specular_scale_specified = false;
2963 for (size_t u = 0; u < Nprimitives; u++) {
2964
2965 uint UUID = context_UUIDs.at(u);
2966
2967 if (context->doesPrimitiveDataExist(UUID, "specular_exponent") && context->getPrimitiveDataType("specular_exponent") == HELIOS_TYPE_FLOAT) {
2968 context->getPrimitiveData(UUID, "specular_exponent", specular_exponent.at(u));
2969 specular_exponent_specified = true;
2970 } else {
2971 specular_exponent.at(u) = -1.f;
2972 }
2973
2974 if (context->doesPrimitiveDataExist(UUID, "specular_scale") && context->getPrimitiveDataType("specular_scale") == HELIOS_TYPE_FLOAT) {
2975 context->getPrimitiveData(UUID, "specular_scale", specular_scale.at(u));
2976 specular_scale_specified = true;
2977 } else {
2978 specular_scale.at(u) = 0.f;
2979 }
2980 }
2981
2982 uint specular_enabled = 0;
2983 if (specular_exponent_specified) {
2984 initializeBuffer1Df(specular_exponent_RTbuffer, specular_exponent);
2985 if (specular_scale_specified) {
2986 initializeBuffer1Df(specular_scale_RTbuffer, specular_scale);
2987 specular_enabled = 2;
2988 } else {
2989 specular_enabled = 1;
2990 }
2991 }
2992 RT_CHECK_ERROR(rtVariableSet1ui(specular_reflection_enabled_RTvariable, specular_enabled));
2993
2994 radiativepropertiesneedupdate = false;
2995
2996 if (message_flag) {
2997 std::cout << "done\n";
2998 }
2999}
3000
3001std::vector<helios::vec2> RadiationModel::loadSpectralData(const std::string &global_data_label) const {
3002
3003 std::vector<helios::vec2> spectrum;
3004
3005 if (!context->doesGlobalDataExist(global_data_label.c_str())) {
3006
3007 // check if spectral data exists in any of the library files
3008 bool data_found = false;
3009 for (const auto &file: spectral_library_files) {
3010 if (Context::scanXMLForTag(file, "globaldata_vec2", global_data_label)) {
3011 context->loadXML(file.c_str(), true);
3012 data_found = true;
3013 break;
3014 }
3015 }
3016
3017 if (!data_found) {
3018 helios_runtime_error("ERROR (RadiationModel::loadSpectralData): Global data for spectrum '" + global_data_label + "' could not be found.");
3019 }
3020 }
3021
3022 if (context->getGlobalDataType(global_data_label.c_str()) != HELIOS_TYPE_VEC2) {
3023 helios_runtime_error("ERROR (RadiationModel::loadSpectralData): Global data for spectrum '" + global_data_label + "' is not of type HELIOS_TYPE_VEC2.");
3024 }
3025
3026 context->getGlobalData(global_data_label.c_str(), spectrum);
3027
3028 // validate spectrum
3029 if (spectrum.empty()) {
3030 helios_runtime_error("ERROR (RadiationModel::loadSpectralData): Global data for spectrum '" + global_data_label + "' is empty.");
3031 }
3032 for (auto s = 0; s < spectrum.size(); s++) {
3033 // check that wavelengths are monotonic
3034 if (s > 0 && spectrum.at(s).x <= spectrum.at(s - 1).x) {
3035 helios_runtime_error("ERROR (RadiationModel::loadSpectralData): Source spectral data validation failed. Wavelengths must increase monotonically.");
3036 }
3037 // check that wavelength is within a reasonable range
3038 if (spectrum.at(s).x < 0 || spectrum.at(s).x > 100000) {
3039 helios_runtime_error("ERROR (RadiationModel::loadSpectralData): Source spectral data validation failed. Wavelength value of " + std::to_string(spectrum.at(s).x) + " appears to be erroneous.");
3040 }
3041 // check that flux is non-negative
3042 if (spectrum.at(s).y < 0) {
3043 helios_runtime_error("ERROR (RadiationModel::loadSpectralData): Source spectral data validation failed. Flux value at wavelength of " + std::to_string(spectrum.at(s).x) + " appears is negative.");
3044 }
3045 }
3046
3047 return spectrum;
3048}
3049
3050void RadiationModel::runBand(const std::string &label) {
3051 std::vector<std::string> labels{label};
3052 runBand(labels);
3053}
3054
3055void RadiationModel::runBand(const std::vector<std::string> &label) {
3056
3057 //----- VERIFICATIONS -----//
3058
3059 // We need the band label strings to appear in the same order as in radiation_bands map.
3060 // this is because that was the order in which radiative properties were laid out when updateRadiativeProperties() was called
3061 std::vector<std::string> band_labels;
3062 for (auto &band: radiation_bands) {
3063 if (std::find(label.begin(), label.end(), band.first) != label.end()) {
3064 band_labels.push_back(band.first);
3065 }
3066 }
3067
3068 // Check to make sure some geometry was added to the context
3069 if (context->getPrimitiveCount() == 0) {
3070 std::cerr << "WARNING (RadiationModel::runBand): No geometry was added to the context. There is nothing to simulate...exiting." << std::endl;
3071 return;
3072 }
3073
3074 // Check to make sure geometry was built in OptiX
3075 if (!isgeometryinitialized) {
3077 }
3078
3079 // Check that all the bands passed to the runBand() method exist
3080 for (const std::string &band: band_labels) {
3081 if (!doesBandExist(band)) {
3082 helios_runtime_error("ERROR (RadiationModel::runBand): Cannot run band " + band + " because it is not a valid band. Use addRadiationBand() function to add the band.");
3083 }
3084 }
3085
3086 // if there are no radiation sources in the simulation, add at least one but with zero fluxes
3087 if (radiation_sources.empty()) {
3089 }
3090
3091 if (radiativepropertiesneedupdate) {
3092 updateRadiativeProperties();
3093 }
3094
3095 // Number of radiation bands in this launch
3096 size_t Nbands_launch = band_labels.size();
3097 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_launch_RTvariable, Nbands_launch));
3098
3099 // Number of total bands in the radiation model
3100 size_t Nbands_global = radiation_bands.size();
3101 RT_CHECK_ERROR(rtVariableSet1ui(Nbands_global_RTvariable, Nbands_global));
3102
3103 // Run all bands by default
3104 std::vector<char> band_launch_flag(Nbands_global);
3105 uint bb = 0;
3106 for (auto &band: radiation_bands) {
3107 if (std::find(band_labels.begin(), band_labels.end(), band.first) != band_labels.end()) {
3108 band_launch_flag.at(bb) = 1;
3109 }
3110 bb++;
3111 }
3112 initializeBuffer1Dchar(band_launch_flag_RTbuffer, band_launch_flag);
3113
3114 // Set the number of Context primitives
3115 size_t Nobjects = primitiveID.size();
3116 size_t Nprimitives = context_UUIDs.size();
3117 RT_CHECK_ERROR(rtVariableSet1ui(Nprimitives_RTvariable, Nprimitives));
3118
3119 // Set the random number seed
3120 RT_CHECK_ERROR(rtVariableSet1ui(random_seed_RTvariable, std::chrono::system_clock::now().time_since_epoch().count()));
3121
3122 // Number of external radiation sources
3123 uint Nsources = radiation_sources.size();
3124 RT_CHECK_ERROR(rtVariableSet1ui(Nsources_RTvariable, Nsources));
3125
3126 // Set periodic boundary condition (if applicable)
3127 RT_CHECK_ERROR(rtVariableSet2f(periodic_flag_RTvariable, periodic_flag.x, periodic_flag.y));
3128
3129 // Number of radiation cameras
3130 uint Ncameras = cameras.size();
3131 RT_CHECK_ERROR(rtVariableSet1ui(Ncameras_RTvariable, Ncameras));
3132
3133 // Set scattering depth for each band
3134 std::vector<uint> scattering_depth(Nbands_launch);
3135 bool scatteringenabled = false;
3136 for (auto b = 0; b < Nbands_launch; b++) {
3137 scattering_depth.at(b) = radiation_bands.at(band_labels.at(b)).scatteringDepth;
3138 if (scattering_depth.at(b) > 0) {
3139 scatteringenabled = true;
3140 }
3141 }
3142 initializeBuffer1Dui(max_scatters_RTbuffer, scattering_depth);
3143
3144 // Issue warning if rho>0, tau>0, or eps<1
3145 for (int b = 0; b < Nbands_launch; b++) {
3146 if (scattering_depth.at(b) == 0 && scattering_iterations_needed.at(band_labels.at(b))) {
3147 std::cout << "WARNING (RadiationModel::runBand): Surface radiative properties for band " << band_labels.at(b)
3148 << " are set to non-default values, but scattering iterations are disabled. Surface radiative properties will be ignored unless scattering depth is non-zero." << std::endl;
3149 }
3150 }
3151
3152 // Set diffuse flux for each band
3153 std::vector<float> diffuse_flux(Nbands_launch);
3154 bool diffuseenabled = false;
3155 for (auto b = 0; b < Nbands_launch; b++) {
3156 diffuse_flux.at(b) = getDiffuseFlux(band_labels.at(b));
3157 if (diffuse_flux.at(b) > 0.f) {
3158 diffuseenabled = true;
3159 }
3160 }
3161 initializeBuffer1Df(diffuse_flux_RTbuffer, diffuse_flux);
3162
3163 // Set diffuse extinction coefficient for each band
3164 std::vector<float> diffuse_extinction(Nbands_launch, 0);
3165 if (diffuseenabled) {
3166 for (auto b = 0; b < Nbands_launch; b++) {
3167 diffuse_extinction.at(b) = radiation_bands.at(band_labels.at(b)).diffuseExtinction;
3168 }
3169 }
3170 initializeBuffer1Df(diffuse_extinction_RTbuffer, diffuse_extinction);
3171
3172 // Set diffuse distribution normalization factor for each band
3173 std::vector<float> diffuse_dist_norm(Nbands_launch, 0);
3174 if (diffuseenabled) {
3175 for (auto b = 0; b < Nbands_launch; b++) {
3176 diffuse_dist_norm.at(b) = radiation_bands.at(band_labels.at(b)).diffuseDistNorm;
3177 }
3178 initializeBuffer1Df(diffuse_dist_norm_RTbuffer, diffuse_dist_norm);
3179 }
3180
3181 // Set diffuse distribution peak direction for each band
3182 std::vector<optix::float3> diffuse_peak_dir(Nbands_launch);
3183 if (diffuseenabled) {
3184 for (auto b = 0; b < Nbands_launch; b++) {
3185 helios::vec3 peak_dir = radiation_bands.at(band_labels.at(b)).diffusePeakDir;
3186 diffuse_peak_dir.at(b) = optix::make_float3(peak_dir.x, peak_dir.y, peak_dir.z);
3187 }
3188 initializeBuffer1Dfloat3(diffuse_peak_dir_RTbuffer, diffuse_peak_dir);
3189 }
3190
3191 // Determine whether emission is enabled for any band
3192 bool emissionenabled = false;
3193 for (auto b = 0; b < Nbands_launch; b++) {
3194 if (radiation_bands.at(band_labels.at(b)).emissionFlag) {
3195 emissionenabled = true;
3196 }
3197 }
3198
3199 // Figure out the maximum direct ray count for all bands in this run and use this as the launch size
3200 size_t directRayCount = 0;
3201 for (const auto &band: label) {
3202 if (radiation_bands.at(band).directRayCount > directRayCount) {
3203 directRayCount = radiation_bands.at(band).directRayCount;
3204 }
3205 }
3206
3207 // Figure out the maximum diffuse ray count for all bands in this run and use this as the launch size
3208 size_t diffuseRayCount = 0;
3209 for (const auto &band: label) {
3210 if (radiation_bands.at(band).diffuseRayCount > diffuseRayCount) {
3211 diffuseRayCount = radiation_bands.at(band).diffuseRayCount;
3212 }
3213 }
3214
3215 // Figure out the maximum diffuse ray count for all bands in this run and use this as the launch size
3216 size_t scatteringDepth = 0;
3217 for (const auto &band: label) {
3218 if (radiation_bands.at(band).scatteringDepth > scatteringDepth) {
3219 scatteringDepth = radiation_bands.at(band).scatteringDepth;
3220 }
3221 }
3222
3223 // Zero buffers
3224 zeroBuffer1D(radiation_in_RTbuffer, Nbands_launch * Nprimitives);
3225 zeroBuffer1D(scatter_buff_top_RTbuffer, Nbands_launch * Nprimitives);
3226 zeroBuffer1D(scatter_buff_bottom_RTbuffer, Nbands_launch * Nprimitives);
3227 zeroBuffer1D(Rsky_RTbuffer, Nbands_launch * Nprimitives);
3228
3229 if (Ncameras > 0) {
3230 zeroBuffer1D(scatter_buff_top_cam_RTbuffer, Nbands_launch * Nprimitives);
3231 zeroBuffer1D(scatter_buff_bottom_cam_RTbuffer, Nbands_launch * Nprimitives);
3232 }
3233
3234 std::vector<float> TBS_top, TBS_bottom;
3235 TBS_top.resize(Nbands_launch * Nprimitives, 0);
3236 TBS_bottom = TBS_top;
3237
3238 std::map<std::string, std::vector<std::vector<float>>> radiation_in_camera;
3239
3240 size_t maxRays = 1024 * 1024 * 1024; // maximum number of total rays in a launch
3241
3242 // ***** DIRECT LAUNCH FROM ALL RADIATION SOURCES ***** //
3243
3244 optix::int3 launch_dim_dir;
3245
3246 bool rundirect = false;
3247 for (uint s = 0; s < Nsources; s++) {
3248 for (uint b = 0; b < Nbands_launch; b++) {
3249 if (getSourceFlux(s, band_labels.at(b)) > 0.f) {
3250 rundirect = true;
3251 break;
3252 }
3253 }
3254 }
3255
3256 if (Nsources > 0 && rundirect) {
3257
3258 // update radiation source buffers
3259
3260 std::vector<std::vector<float>> fluxes; // first index is the source, second index is the band (only those passed to runBand() function)
3261 fluxes.resize(Nsources);
3262 std::vector<optix::float3> positions(Nsources);
3263 std::vector<optix::float2> widths(Nsources);
3264 std::vector<optix::float3> rotations(Nsources);
3265 std::vector<uint> types(Nsources);
3266
3267 size_t s = 0;
3268 for (const auto &source: radiation_sources) {
3269
3270 fluxes.at(s).resize(Nbands_launch);
3271
3272 for (auto b = 0; b < label.size(); b++) {
3273 fluxes.at(s).at(b) = getSourceFlux(s, band_labels.at(b));
3274 }
3275
3276 positions.at(s) = optix::make_float3(source.source_position.x, source.source_position.y, source.source_position.z);
3277 widths.at(s) = optix::make_float2(source.source_width.x, source.source_width.y);
3278 rotations.at(s) = optix::make_float3(source.source_rotation.x, source.source_rotation.y, source.source_rotation.z);
3279 types.at(s) = source.source_type;
3280
3281 s++;
3282 }
3283
3284 initializeBuffer1Df(source_fluxes_RTbuffer, flatten(fluxes));
3285 initializeBuffer1Dfloat3(source_positions_RTbuffer, positions);
3286 initializeBuffer1Dfloat2(source_widths_RTbuffer, widths);
3287 initializeBuffer1Dfloat3(source_rotations_RTbuffer, rotations);
3288 initializeBuffer1Dui(source_types_RTbuffer, types);
3289
3290 // -- Ray Trace -- //
3291
3292 // Compute direct launch dimension
3293 size_t n = ceil(sqrt(double(directRayCount)));
3294
3295 size_t maxPrims = floor(float(maxRays) / float(n * n));
3296
3297 int Nlaunches = ceil(n * n * Nobjects / float(maxRays));
3298
3299 size_t prims_per_launch = fmin(Nobjects, maxPrims);
3300
3301 for (uint launch = 0; launch < Nlaunches; launch++) {
3302
3303 size_t prims_this_launch;
3304 if ((launch + 1) * prims_per_launch > Nobjects) {
3305 prims_this_launch = Nobjects - launch * prims_per_launch;
3306 } else {
3307 prims_this_launch = prims_per_launch;
3308 }
3309
3310 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, launch * prims_per_launch));
3311
3312 launch_dim_dir = optix::make_int3(round(n), round(n), prims_this_launch);
3313
3314 if (message_flag) {
3315 std::cout << "Performing primary direct radiation ray trace for bands ";
3316 for (const auto &band: label) {
3317 std::cout << band << ", ";
3318 }
3319 std::cout << " (batch " << launch + 1 << " of " << Nlaunches << ")..." << std::flush;
3320 }
3321 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIRECT, launch_dim_dir.x, launch_dim_dir.y, launch_dim_dir.z));
3322
3323 if (message_flag) {
3324 std::cout << "\r \r" << std::flush;
3325 }
3326 }
3327
3328 if (message_flag) {
3329 std::cout << "Performing primary direct radiation ray trace for bands ";
3330 for (const auto &band: label) {
3331 std::cout << band << ", ";
3332 }
3333 std::cout << "...done." << std::endl;
3334 }
3335
3336 } // end direct source launch
3337
3338 // --- Diffuse/Emission launch ---- //
3339
3340 if (emissionenabled || diffuseenabled) {
3341
3342 std::vector<float> flux_top, flux_bottom;
3343 flux_top.resize(Nbands_launch * Nprimitives, 0);
3344 flux_bottom = flux_top;
3345
3346 // If we are doing a diffuse/emission ray trace anyway and we have direct scattered energy, we get a "free" scattering trace here
3347 if (scatteringenabled && rundirect) {
3348 flux_top = getOptiXbufferData(scatter_buff_top_RTbuffer);
3349 flux_bottom = getOptiXbufferData(scatter_buff_bottom_RTbuffer);
3350 zeroBuffer1D(scatter_buff_top_RTbuffer, Nbands_launch * Nprimitives);
3351 zeroBuffer1D(scatter_buff_bottom_RTbuffer, Nbands_launch * Nprimitives);
3352 }
3353
3354 // add any emitted energy to the outgoing energy buffer
3355 if (emissionenabled) {
3356 // Update primitive outgoing emission
3357 float eps, temperature;
3358
3359 void *ptr;
3360 float *scatter_buff_top_cam_data, *scatter_buff_bottom_cam_data;
3361 if (Ncameras > 0) {
3362 // add emitted flux to camera scattered energy buffer
3363 RT_CHECK_ERROR(rtBufferMap(scatter_buff_top_cam_RTbuffer, &ptr));
3364 scatter_buff_top_cam_data = (float *) ptr;
3365 RT_CHECK_ERROR(rtBufferMap(scatter_buff_bottom_cam_RTbuffer, &ptr));
3366 scatter_buff_bottom_cam_data = (float *) ptr;
3367 }
3368
3369 for (auto b = 0; b < Nbands_launch; b++) {
3370 //\todo For emissivity and twosided_flag, this should be done in updateRadiativeProperties() to avoid having to do it on every runBand() call
3371 if (radiation_bands.at(band_labels.at(b)).emissionFlag) {
3372 std::string prop = "emissivity_" + band_labels.at(b);
3373 for (size_t u = 0; u < Nprimitives; u++) {
3374 size_t ind = u * Nbands_launch + b;
3375 uint p = context_UUIDs.at(u);
3376 if (context->doesPrimitiveDataExist(p, prop.c_str())) {
3377 context->getPrimitiveData(p, prop.c_str(), eps);
3378 } else {
3379 eps = eps_default;
3380 }
3381 if (scattering_depth.at(b) == 0 && eps != 1.f) {
3382 eps = 1.f;
3383 }
3384 if (context->doesPrimitiveDataExist(p, "temperature")) {
3385 context->getPrimitiveData(p, "temperature", temperature);
3386 if (temperature < 0) {
3387 temperature = temperature_default;
3388 }
3389 } else {
3390 temperature = temperature_default;
3391 }
3392 float out_top = sigma * eps * pow(temperature, 4);
3393 flux_top.at(ind) += out_top;
3394 if (Ncameras > 0) {
3395 scatter_buff_top_cam_data[ind] += out_top;
3396 }
3397 if (!context->doesPrimitiveDataExist(p, "twosided_flag")) { // if does not exist, assume two-sided
3398 flux_bottom.at(ind) += flux_top.at(ind);
3399 if (Ncameras > 0) {
3400 scatter_buff_bottom_cam_data[ind] += out_top;
3401 }
3402 } else {
3403 uint flag;
3404 context->getPrimitiveData(p, "twosided_flag", flag);
3405 if (flag) {
3406 flux_bottom.at(ind) += flux_top.at(ind);
3407 if (Ncameras > 0) {
3408 scatter_buff_bottom_cam_data[ind] += out_top;
3409 }
3410 }
3411 }
3412 }
3413 }
3414 }
3415 if (Ncameras > 0) {
3416 RT_CHECK_ERROR(rtBufferUnmap(scatter_buff_top_cam_RTbuffer));
3417 RT_CHECK_ERROR(rtBufferUnmap(scatter_buff_bottom_cam_RTbuffer));
3418 }
3419 }
3420
3421 initializeBuffer1Df(radiation_out_top_RTbuffer, flux_top);
3422 initializeBuffer1Df(radiation_out_bottom_RTbuffer, flux_bottom);
3423
3424 // Compute diffuse launch dimension
3425 size_t n = ceil(sqrt(double(diffuseRayCount)));
3426
3427 size_t maxPrims = floor(float(maxRays) / float(n * n));
3428
3429 int Nlaunches = ceil(n * n * Nobjects / float(maxRays));
3430
3431 size_t prims_per_launch = fmin(Nobjects, maxPrims);
3432
3433 for (uint launch = 0; launch < Nlaunches; launch++) {
3434
3435 size_t prims_this_launch;
3436 if ((launch + 1) * prims_per_launch > Nobjects) {
3437 prims_this_launch = Nobjects - launch * prims_per_launch;
3438 } else {
3439 prims_this_launch = prims_per_launch;
3440 }
3441
3442 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, launch * prims_per_launch));
3443
3444 optix::int3 launch_dim_diff = optix::make_int3(round(n), round(n), prims_this_launch);
3445 assert(launch_dim_diff.x > 0 && launch_dim_diff.y > 0);
3446
3447 if (message_flag) {
3448 std::cout << "Performing primary diffuse radiation ray trace for bands ";
3449 for (const auto &band: label) {
3450 std::cout << band << " ";
3451 }
3452 std::cout << " (batch " << launch + 1 << " of " << Nlaunches << ")..." << std::flush;
3453 }
3454
3455 // Top surface launch
3456 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, 1));
3457 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIFFUSE, launch_dim_diff.x, launch_dim_diff.y, launch_dim_diff.z));
3458
3459 // Bottom surface launch
3460 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, 0));
3461 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIFFUSE, launch_dim_diff.x, launch_dim_diff.y, launch_dim_diff.z));
3462
3463 if (message_flag) {
3464 std::cout << "\r \r" << std::flush;
3465 }
3466 }
3467
3468 if (message_flag) {
3469 std::cout << "Performing primary diffuse radiation ray trace for bands ";
3470 for (const auto &band: label) {
3471 std::cout << band << ", ";
3472 }
3473 std::cout << "...done." << std::endl;
3474 }
3475 }
3476
3477 if (scatteringenabled && (emissionenabled || diffuseenabled || rundirect)) {
3478
3479 for (auto b = 0; b < Nbands_launch; b++) {
3480 diffuse_flux.at(b) = 0.f;
3481 }
3482 initializeBuffer1Df(diffuse_flux_RTbuffer, diffuse_flux);
3483
3484 size_t n = ceil(sqrt(double(diffuseRayCount)));
3485
3486 size_t maxPrims = floor(float(maxRays) / float(n * n));
3487
3488 int Nlaunches = ceil(n * n * Nobjects / float(maxRays));
3489
3490 size_t prims_per_launch = fmin(Nobjects, maxPrims);
3491
3492 uint s;
3493 for (s = 0; s < scatteringDepth; s++) {
3494 if (message_flag) {
3495 std::cout << "Performing scattering ray trace (iteration " << s + 1 << " of " << scatteringDepth << ")..." << std::flush;
3496 }
3497
3498 int b = -1;
3499 for (uint b_global = 0; b_global < Nbands_global; b_global++) {
3500
3501 if (band_launch_flag.at(b_global) == 0) {
3502 continue;
3503 }
3504 b++;
3505
3506 uint depth = radiation_bands.at(band_labels.at(b)).scatteringDepth;
3507 if (s + 1 > depth) {
3508 if (message_flag) {
3509 std::cout << "Skipping band " << band_labels.at(b) << " for scattering launch " << s + 1 << std::flush;
3510 }
3511 band_launch_flag.at(b_global) = 0;
3512 }
3513 }
3514 initializeBuffer1Dchar(band_launch_flag_RTbuffer, band_launch_flag);
3515
3516 // TBS_top=getOptiXbufferData( scatter_buff_top_RTbuffer );
3517 // TBS_bottom=getOptiXbufferData( scatter_buff_bottom_RTbuffer );
3518 // float TBS_max = 0;
3519 // for( size_t u=0; u<Nprimitives*Nbands; u++ ){
3520 // if( TBS_top.at(u)+TBS_bottom.at(u)>TBS_max ){
3521 // TBS_max = TBS_top.at(u)+TBS_bottom.at(u);
3522 // }
3523 // }
3524
3525 copyBuffer1D(scatter_buff_top_RTbuffer, radiation_out_top_RTbuffer);
3526 zeroBuffer1D(scatter_buff_top_RTbuffer, Nbands_launch * Nprimitives);
3527 copyBuffer1D(scatter_buff_bottom_RTbuffer, radiation_out_bottom_RTbuffer);
3528 zeroBuffer1D(scatter_buff_bottom_RTbuffer, Nbands_launch * Nprimitives);
3529
3530 for (uint launch = 0; launch < Nlaunches; launch++) {
3531
3532 size_t prims_this_launch;
3533 if ((launch + 1) * prims_per_launch > Nobjects) {
3534 prims_this_launch = Nobjects - launch * prims_per_launch;
3535 } else {
3536 prims_this_launch = prims_per_launch;
3537 }
3538 optix::int3 launch_dim_diff = optix::make_int3(round(n), round(n), prims_this_launch);
3539
3540 RT_CHECK_ERROR(rtVariableSet1ui(launch_offset_RTvariable, launch * prims_per_launch));
3541
3542 // Top surface launch
3543 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, 1));
3544 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIFFUSE, launch_dim_diff.x, launch_dim_diff.y, launch_dim_diff.z));
3545
3546 // Bottom surface launch
3547 RT_CHECK_ERROR(rtVariableSet1ui(launch_face_RTvariable, 0));
3548 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_DIFFUSE, launch_dim_diff.x, launch_dim_diff.y, launch_dim_diff.z));
3549 }
3550 if (message_flag) {
3551 std::cout << "\r \r" << std::flush;
3552 }
3553 }
3554
3555 if (message_flag) {
3556 std::cout << "Performing scattering ray trace...done." << std::endl;
3557 }
3558 }
3559
3560 // **** CAMERA RAY TRACE **** //
3561 if (Ncameras > 0) {
3562
3563 if (scatteringenabled && (emissionenabled || diffuseenabled || rundirect)) {
3564 // re-set outgoing radiation buffers
3565 copyBuffer1D(scatter_buff_top_cam_RTbuffer, radiation_out_top_RTbuffer);
3566 copyBuffer1D(scatter_buff_bottom_cam_RTbuffer, radiation_out_bottom_RTbuffer);
3567
3568 // re-set diffuse radiation fluxes
3569 if (diffuseenabled) {
3570 for (auto b = 0; b < Nbands_launch; b++) {
3571 diffuse_flux.at(b) = getDiffuseFlux(band_labels.at(b));
3572 }
3573 initializeBuffer1Df(diffuse_flux_RTbuffer, diffuse_flux);
3574 }
3575
3576
3577 size_t n = ceil(sqrt(double(diffuseRayCount)));
3578
3579 uint cam = 0;
3580 for (auto &camera: cameras) {
3581
3582 // set variable values
3583 RT_CHECK_ERROR(rtVariableSet3f(camera_position_RTvariable, camera.second.position.x, camera.second.position.y, camera.second.position.z));
3584 helios::SphericalCoord dir = cart2sphere(camera.second.lookat - camera.second.position);
3585 RT_CHECK_ERROR(rtVariableSet2f(camera_direction_RTvariable, dir.zenith, dir.azimuth));
3586 RT_CHECK_ERROR(rtVariableSet1f(camera_lens_diameter_RTvariable, camera.second.lens_diameter));
3587 RT_CHECK_ERROR(rtVariableSet1f(FOV_aspect_RTvariable, camera.second.FOV_aspect_ratio));
3588 RT_CHECK_ERROR(rtVariableSet1f(camera_focal_length_RTvariable, camera.second.focal_length));
3589 RT_CHECK_ERROR(rtVariableSet1f(camera_viewplane_length_RTvariable, 0.5f / tanf(0.5f * camera.second.HFOV_degrees * M_PI / 180.f)));
3590 RT_CHECK_ERROR(rtVariableSet1ui(camera_ID_RTvariable, cam));
3591
3592 zeroBuffer1D(radiation_in_camera_RTbuffer, camera.second.resolution.x * camera.second.resolution.y * Nbands_launch);
3593
3594 optix::int3 launch_dim_camera = optix::make_int3(camera.second.antialiasing_samples, camera.second.resolution.x, camera.second.resolution.y);
3595
3596 if (message_flag) {
3597 std::cout << "Performing scattering radiation camera ray trace for camera " << camera.second.label << "..." << std::flush;
3598 }
3599 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_CAMERA, launch_dim_camera.x, launch_dim_camera.y, launch_dim_camera.z));
3600 if (message_flag) {
3601 std::cout << "done." << std::endl;
3602 }
3603
3604 std::vector<float> radiation_camera = getOptiXbufferData(radiation_in_camera_RTbuffer);
3605
3606 std::string camera_label = camera.second.label;
3607
3608 for (auto b = 0; b < Nbands_launch; b++) {
3609
3610 camera.second.pixel_data[band_labels.at(b)].resize(camera.second.resolution.x * camera.second.resolution.y);
3611
3612 std::string data_label = "camera_" + camera_label + "_" + band_labels.at(b);
3613
3614 for (auto p = 0; p < camera.second.resolution.x * camera.second.resolution.y; p++) {
3615 camera.second.pixel_data.at(band_labels.at(b)).at(p) = radiation_camera.at(p * Nbands_launch + b);
3616 }
3617
3618 context->setGlobalData(data_label.c_str(), camera.second.pixel_data.at(band_labels.at(b)));
3619 }
3620
3621 //--- Pixel Labeling Trace ---//
3622
3623 zeroBuffer1D(camera_pixel_label_RTbuffer, camera.second.resolution.x * camera.second.resolution.y);
3624 zeroBuffer1D(camera_pixel_depth_RTbuffer, camera.second.resolution.x * camera.second.resolution.y);
3625
3626 if (message_flag) {
3627 std::cout << "Performing camera pixel labeling ray trace for camera " << camera.second.label << "..." << std::flush;
3628 }
3629 RT_CHECK_ERROR(rtContextLaunch3D(OptiX_Context, RAYTYPE_PIXEL_LABEL, 1, launch_dim_camera.y, launch_dim_camera.z));
3630 if (message_flag) {
3631 std::cout << "done." << std::endl;
3632 }
3633
3634 camera.second.pixel_label_UUID = getOptiXbufferData_ui(camera_pixel_label_RTbuffer);
3635 camera.second.pixel_depth = getOptiXbufferData(camera_pixel_depth_RTbuffer);
3636
3637 // the IDs from the ray trace do not necessarily correspond to the actual primitive UUIDs, so look them up.
3638 for (uint ID = 0; ID < camera.second.pixel_label_UUID.size(); ID++) {
3639 if (camera.second.pixel_label_UUID.at(ID) > 0) {
3640 camera.second.pixel_label_UUID.at(ID) = context_UUIDs.at(camera.second.pixel_label_UUID.at(ID) - 1) + 1;
3641 }
3642 }
3643
3644 std::string data_label = "camera_" + camera_label + "_pixel_UUID";
3645
3646 context->setGlobalData(data_label.c_str(), camera.second.pixel_label_UUID);
3647
3648 data_label = "camera_" + camera_label + "_pixel_depth";
3649
3650 context->setGlobalData(data_label.c_str(), camera.second.pixel_depth);
3651
3652 cam++;
3653 }
3654 } else {
3655 // if scattering is not enabled or all sources have zero flux, we still need to zero the camera buffers
3656 for (auto &camera: cameras) {
3657 for (auto b = 0; b < Nbands_launch; b++) {
3658 camera.second.pixel_data[band_labels.at(b)].resize(camera.second.resolution.x * camera.second.resolution.y);
3659
3660 std::string data_label = "camera_" + camera.second.label + "_" + band_labels.at(b);
3661
3662 for (auto p = 0; p < camera.second.resolution.x * camera.second.resolution.y; p++) {
3663 camera.second.pixel_data.at(band_labels.at(b)).at(p) = 0.f;
3664 }
3665 context->setGlobalData(data_label.c_str(), camera.second.pixel_data.at(band_labels.at(b)));
3666 }
3667 }
3668 }
3669 }
3670
3671 // deposit any energy that is left to make sure we satisfy conservation of energy
3672 TBS_top = getOptiXbufferData(scatter_buff_top_RTbuffer);
3673 TBS_bottom = getOptiXbufferData(scatter_buff_bottom_RTbuffer);
3674
3675 // Set variables in geometric objects
3676
3677 // std::vector<float> radiation_top_cam;
3678 // radiation_top_cam=getOptiXbufferData( scatter_buff_top_cam_RTbuffer );
3679 // std::vector<float> radiation_bottom_cam;
3680 // radiation_bottom_cam=getOptiXbufferData( scatter_buff_bottom_cam_RTbuffer );
3681 // std::vector<float> radiation_flux_data = radiation_top_cam+ radiation_bottom_cam;
3682
3683 std::vector<float> radiation_flux_data;
3684 radiation_flux_data = getOptiXbufferData(radiation_in_RTbuffer);
3685
3686 std::vector<uint> UUIDs_context_all = context->getAllUUIDs();
3687
3688 for (auto b = 0; b < Nbands_launch; b++) {
3689
3690 std::string prop = "radiation_flux_" + band_labels.at(b);
3691 std::vector<float> R(Nprimitives);
3692 for (size_t u = 0; u < Nprimitives; u++) {
3693 size_t ind = u * Nbands_launch + b;
3694 R.at(u) = radiation_flux_data.at(ind) + TBS_top.at(ind) + TBS_bottom.at(ind);
3695 if (radiation_flux_data.at(ind) != radiation_flux_data.at(ind)) {
3696 std::cout << "NaN here " << ind << std::endl;
3697 }
3698 }
3699 context->setPrimitiveData(context_UUIDs, prop.c_str(), R);
3700
3701 if (UUIDs_context_all.size() != Nprimitives) {
3702 for (uint UUID: UUIDs_context_all) {
3703 if (context->doesPrimitiveExist(UUID) && !context->doesPrimitiveDataExist(UUID, prop.c_str())) {
3704 context->setPrimitiveData(UUID, prop.c_str(), 0.f);
3705 }
3706 }
3707 }
3708 }
3709}
3710
3712
3713 std::vector<float> Rsky_SW;
3714 Rsky_SW = getOptiXbufferData(Rsky_RTbuffer);
3715 float Rsky = 0.f;
3716 for (size_t i = 0; i < Rsky_SW.size(); i++) {
3717 Rsky += Rsky_SW.at(i);
3718 }
3719 return Rsky;
3720}
3721
3723
3724 std::vector<float> total_flux;
3725 total_flux.resize(context->getPrimitiveCount(), 0.f);
3726
3727 for (const auto &band: radiation_bands) {
3728
3729 std::string label = band.first;
3730
3731 for (size_t u = 0; u < context_UUIDs.size(); u++) {
3732
3733 uint p = context_UUIDs.at(u);
3734
3735 std::string str = "radiation_flux_" + label;
3736
3737 float R;
3738 context->getPrimitiveData(p, str.c_str(), R);
3739 total_flux.at(u) += R;
3740 }
3741 }
3742
3743 return total_flux;
3744}
3745
3746std::vector<float> RadiationModel::getOptiXbufferData(RTbuffer buffer) {
3747
3748 void *_data_;
3749 RT_CHECK_ERROR(rtBufferMap(buffer, &_data_));
3750 float *data_ptr = (float *) _data_;
3751
3752 RTsize size;
3753 RT_CHECK_ERROR(rtBufferGetSize1D(buffer, &size));
3754
3755 std::vector<float> data_vec;
3756 data_vec.resize(size);
3757 for (int i = 0; i < size; i++) {
3758 data_vec.at(i) = data_ptr[i];
3759 }
3760
3761 RT_CHECK_ERROR(rtBufferUnmap(buffer));
3762
3763 return data_vec;
3764}
3765
3766std::vector<double> RadiationModel::getOptiXbufferData_d(RTbuffer buffer) {
3767
3768 void *_data_;
3769 RT_CHECK_ERROR(rtBufferMap(buffer, &_data_));
3770 double *data_ptr = (double *) _data_;
3771
3772 RTsize size;
3773 RT_CHECK_ERROR(rtBufferGetSize1D(buffer, &size));
3774
3775 std::vector<double> data_vec;
3776 data_vec.resize(size);
3777 for (int i = 0; i < size; i++) {
3778 data_vec.at(i) = data_ptr[i];
3779 }
3780
3781 RT_CHECK_ERROR(rtBufferUnmap(buffer));
3782
3783 return data_vec;
3784}
3785
3786std::vector<uint> RadiationModel::getOptiXbufferData_ui(RTbuffer buffer) {
3787
3788 void *_data_;
3789 RT_CHECK_ERROR(rtBufferMap(buffer, &_data_));
3790 uint *data_ptr = (uint *) _data_;
3791
3792 RTsize size;
3793 RT_CHECK_ERROR(rtBufferGetSize1D(buffer, &size));
3794
3795 std::vector<uint> data_vec;
3796 data_vec.resize(size);
3797 for (int i = 0; i < size; i++) {
3798 data_vec.at(i) = data_ptr[i];
3799 }
3800
3801 RT_CHECK_ERROR(rtBufferUnmap(buffer));
3802
3803 return data_vec;
3804}
3805
3806void RadiationModel::addBuffer(const char *name, RTbuffer &buffer, RTvariable &variable, RTbuffertype type, RTformat format, size_t dimension) {
3807
3808 RT_CHECK_ERROR(rtBufferCreate(OptiX_Context, type, &buffer));
3809 RT_CHECK_ERROR(rtBufferSetFormat(buffer, format));
3810 RT_CHECK_ERROR(rtContextDeclareVariable(OptiX_Context, name, &variable));
3811 RT_CHECK_ERROR(rtVariableSetObject(variable, buffer));
3812 if (dimension == 1) {
3813 zeroBuffer1D(buffer, 1);
3814 } else if (dimension == 2) {
3815 zeroBuffer2D(buffer, optix::make_int2(1, 1));
3816 } else {
3817 helios_runtime_error("ERROR (RadiationModel::addBuffer): invalid buffer dimension of " + std::to_string(dimension) + ", must be 1 or 2.");
3818 }
3819}
3820
3821void RadiationModel::zeroBuffer1D(RTbuffer &buffer, size_t bsize) {
3822
3823 // get buffer format
3824 RTformat format;
3825 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
3826
3827 if (format == RT_FORMAT_USER) { // Note: for now, assume user format means it's a double
3828
3829 std::vector<double> array;
3830 array.resize(bsize);
3831 for (int i = 0; i < bsize; i++) {
3832 array.at(i) = 0.f;
3833 }
3834
3835 initializeBuffer1Dd(buffer, array);
3836
3837 } else if (format == RT_FORMAT_FLOAT) {
3838
3839 std::vector<float> array;
3840 array.resize(bsize);
3841 for (int i = 0; i < bsize; i++) {
3842 array.at(i) = 0.f;
3843 }
3844
3845 initializeBuffer1Df(buffer, array);
3846
3847 } else if (format == RT_FORMAT_FLOAT2) {
3848
3849 std::vector<optix::float2> array;
3850 array.resize(bsize);
3851 for (int i = 0; i < bsize; i++) {
3852 array.at(i) = optix::make_float2(0, 0);
3853 }
3854
3855 initializeBuffer1Dfloat2(buffer, array);
3856
3857 } else if (format == RT_FORMAT_FLOAT3) {
3858
3859 std::vector<optix::float3> array;
3860 array.resize(bsize);
3861 for (int i = 0; i < bsize; i++) {
3862 array.at(i) = optix::make_float3(0, 0, 0);
3863 }
3864
3865 initializeBuffer1Dfloat3(buffer, array);
3866
3867 } else if (format == RT_FORMAT_INT) {
3868
3869 std::vector<int> array;
3870 array.resize(bsize);
3871 for (int i = 0; i < bsize; i++) {
3872 array.at(i) = 0;
3873 }
3874
3875 initializeBuffer1Di(buffer, array);
3876
3877 } else if (format == RT_FORMAT_INT2) {
3878
3879 std::vector<optix::int2> array;
3880 array.resize(bsize);
3881 for (int i = 0; i < bsize; i++) {
3882 array.at(i) = optix::make_int2(0, 0);
3883 }
3884
3885 initializeBuffer1Dint2(buffer, array);
3886
3887 } else if (format == RT_FORMAT_INT3) {
3888
3889 std::vector<optix::int3> array;
3890 array.resize(bsize);
3891 for (int i = 0; i < bsize; i++) {
3892 array.at(i) = optix::make_int3(0, 0, 0);
3893 }
3894
3895 initializeBuffer1Dint3(buffer, array);
3896
3897 } else if (format == RT_FORMAT_UNSIGNED_INT) {
3898
3899 std::vector<uint> array;
3900 array.resize(bsize);
3901 for (int i = 0; i < bsize; i++) {
3902 array.at(i) = 0;
3903 }
3904
3905 initializeBuffer1Dui(buffer, array);
3906
3907 } else if (format == RT_FORMAT_BYTE) {
3908
3909 std::vector<char> array;
3910 array.resize(bsize);
3911 for (int i = 0; i < bsize; i++) {
3912 array.at(i) = 0;
3913 }
3914
3915 initializeBuffer1Dchar(buffer, array);
3916 } else {
3917 helios_runtime_error("ERROR (RadiationModel::zeroBuffer1D): Buffer type not supported.");
3918 }
3919}
3920
3921void RadiationModel::copyBuffer1D(RTbuffer &buffer, RTbuffer &buffer_copy) {
3922
3923 /* \todo Add support for all data types (currently only works for float and float3)*/
3924
3925 // get buffer format
3926 RTformat format;
3927 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
3928
3929 // get buffer size
3930 RTsize bsize;
3931 rtBufferGetSize1D(buffer, &bsize);
3932
3933 rtBufferSetSize1D(buffer_copy, bsize);
3934
3935 if (format == RT_FORMAT_FLOAT) {
3936
3937 void *ptr;
3938 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
3939 float *data = (float *) ptr;
3940
3941 void *ptr_copy;
3942 RT_CHECK_ERROR(rtBufferMap(buffer_copy, &ptr_copy));
3943 float *data_copy = (float *) ptr_copy;
3944
3945 for (size_t i = 0; i < bsize; i++) {
3946 data_copy[i] = data[i];
3947 }
3948
3949 } else if (format == RT_FORMAT_FLOAT3) {
3950
3951 void *ptr;
3952 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
3953 optix::float3 *data = (optix::float3 *) ptr;
3954
3955 void *ptr_copy;
3956 RT_CHECK_ERROR(rtBufferMap(buffer_copy, &ptr_copy));
3957 optix::float3 *data_copy = (optix::float3 *) ptr_copy;
3958
3959 for (size_t i = 0; i < bsize; i++) {
3960 data_copy[i] = data[i];
3961 }
3962 }
3963
3964 RT_CHECK_ERROR(rtBufferUnmap(buffer));
3965 RT_CHECK_ERROR(rtBufferUnmap(buffer_copy));
3966}
3967
3968void RadiationModel::initializeBuffer1Dd(RTbuffer &buffer, const std::vector<double> &array) {
3969
3970 size_t bsize = array.size();
3971
3972 // set buffer size
3973 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
3974
3975 // get buffer format
3976 RTformat format;
3977 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
3978
3979 if (format != RT_FORMAT_USER) {
3980 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dd): Buffer must have type double.");
3981 }
3982
3983 void *ptr;
3984 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
3985
3986 double *data = (double *) ptr;
3987
3988 for (size_t i = 0; i < bsize; i++) {
3989 data[i] = array[i];
3990 }
3991
3992 RT_CHECK_ERROR(rtBufferUnmap(buffer));
3993}
3994
3995void RadiationModel::initializeBuffer1Df(RTbuffer &buffer, const std::vector<float> &array) {
3996
3997 size_t bsize = array.size();
3998
3999 // set buffer size
4000 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4001
4002 // get buffer format
4003 RTformat format;
4004 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4005
4006 if (format != RT_FORMAT_FLOAT) {
4007 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Df): Buffer must have type float.");
4008 }
4009
4010 void *ptr;
4011 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4012
4013 float *data = (float *) ptr;
4014
4015 for (size_t i = 0; i < bsize; i++) {
4016 data[i] = array[i];
4017 }
4018
4019 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4020}
4021
4022void RadiationModel::initializeBuffer1Dfloat2(RTbuffer &buffer, const std::vector<optix::float2> &array) {
4023
4024 size_t bsize = array.size();
4025
4026 // set buffer size
4027 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4028
4029 // get buffer format
4030 RTformat format;
4031 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4032
4033 if (format != RT_FORMAT_FLOAT2) {
4034 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dfloat2): Buffer must have type float2.");
4035 }
4036
4037 void *ptr;
4038 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4039
4040 optix::float2 *data = (optix::float2 *) ptr;
4041
4042 for (size_t i = 0; i < bsize; i++) {
4043 data[i].x = array[i].x;
4044 data[i].y = array[i].y;
4045 }
4046
4047 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4048}
4049
4050void RadiationModel::initializeBuffer1Dfloat3(RTbuffer &buffer, const std::vector<optix::float3> &array) {
4051
4052 size_t bsize = array.size();
4053
4054 // set buffer size
4055 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4056
4057 // get buffer format
4058 RTformat format;
4059 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4060
4061 if (format != RT_FORMAT_FLOAT3) {
4062 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dfloat3): Buffer must have type float3.");
4063 }
4064
4065 void *ptr;
4066 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4067
4068 optix::float3 *data = (optix::float3 *) ptr;
4069
4070 for (size_t i = 0; i < bsize; i++) {
4071 data[i].x = array[i].x;
4072 data[i].y = array[i].y;
4073 data[i].z = array[i].z;
4074 }
4075
4076 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4077}
4078
4079void RadiationModel::initializeBuffer1Dfloat4(RTbuffer &buffer, const std::vector<optix::float4> &array) {
4080
4081 size_t bsize = array.size();
4082
4083 // set buffer size
4084 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4085
4086 // get buffer format
4087 RTformat format;
4088 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4089
4090 if (format != RT_FORMAT_FLOAT4) {
4091 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dfloat4): Buffer must have type float4.");
4092 }
4093
4094 void *ptr;
4095 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4096
4097 optix::float4 *data = (optix::float4 *) ptr;
4098
4099 for (size_t i = 0; i < bsize; i++) {
4100 data[i].x = array[i].x;
4101 data[i].y = array[i].y;
4102 data[i].z = array[i].z;
4103 data[i].w = array[i].w;
4104 }
4105
4106 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4107}
4108
4109void RadiationModel::initializeBuffer1Di(RTbuffer &buffer, const std::vector<int> &array) {
4110
4111 size_t bsize = array.size();
4112
4113 // set buffer size
4114 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4115
4116 // get buffer format
4117 RTformat format;
4118 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4119
4120 if (format != RT_FORMAT_INT) {
4121 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Di): Buffer must have type int.");
4122 }
4123
4124 void *ptr;
4125 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4126
4127 int *data = (int *) ptr;
4128
4129 for (size_t i = 0; i < bsize; i++) {
4130 data[i] = array[i];
4131 }
4132
4133 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4134}
4135
4136void RadiationModel::initializeBuffer1Dui(RTbuffer &buffer, const std::vector<uint> &array) {
4137
4138 size_t bsize = array.size();
4139
4140 // set buffer size
4141 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4142
4143 // get buffer format
4144 RTformat format;
4145 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4146
4147 if (format != RT_FORMAT_UNSIGNED_INT) {
4148 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dui): Buffer must have type unsigned int.");
4149 }
4150
4151 void *ptr;
4152 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4153
4154 uint *data = (uint *) ptr;
4155
4156 for (size_t i = 0; i < bsize; i++) {
4157 data[i] = array[i];
4158 }
4159
4160 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4161}
4162
4163void RadiationModel::initializeBuffer1Dint2(RTbuffer &buffer, const std::vector<optix::int2> &array) {
4164
4165 size_t bsize = array.size();
4166
4167 // set buffer size
4168 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4169
4170 // get buffer format
4171 RTformat format;
4172 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4173
4174 if (format != RT_FORMAT_INT2) {
4175 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dint2): Buffer must have type int2.");
4176 }
4177
4178 void *ptr;
4179 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4180
4181 optix::int2 *data = (optix::int2 *) ptr;
4182
4183 for (size_t i = 0; i < bsize; i++) {
4184 data[i] = array[i];
4185 }
4186
4187 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4188}
4189
4190void RadiationModel::initializeBuffer1Dint3(RTbuffer &buffer, const std::vector<optix::int3> &array) {
4191
4192 size_t bsize = array.size();
4193
4194 // set buffer size
4195 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4196
4197 // get buffer format
4198 RTformat format;
4199 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4200
4201 if (format != RT_FORMAT_INT3) {
4202 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dint3): Buffer must have type int3.");
4203 }
4204
4205 void *ptr;
4206 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4207
4208 optix::int3 *data = (optix::int3 *) ptr;
4209
4210 for (size_t i = 0; i < bsize; i++) {
4211 data[i] = array[i];
4212 }
4213
4214 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4215}
4216
4217void RadiationModel::initializeBuffer1Dchar(RTbuffer &buffer, const std::vector<char> &array) {
4218
4219 size_t bsize = array.size();
4220
4221 // set buffer size
4222 RT_CHECK_ERROR(rtBufferSetSize1D(buffer, bsize));
4223
4224 // get buffer format
4225 RTformat format;
4226 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4227
4228 if (format != RT_FORMAT_BYTE) {
4229 helios_runtime_error("ERROR (RadiationModel::initializeBuffer1Dchar): Buffer must have type char.");
4230 }
4231
4232 void *ptr;
4233 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4234
4235 char *data = (char *) ptr;
4236
4237 for (size_t i = 0; i < bsize; i++) {
4238 data[i] = array[i];
4239 }
4240
4241 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4242}
4243
4244void RadiationModel::zeroBuffer2D(RTbuffer &buffer, optix::int2 bsize) {
4245
4246 // set buffer size
4247 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4248
4249 // get buffer format
4250 RTformat format;
4251 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4252
4253 if (format == RT_FORMAT_USER) { // Note: for now we'll assume this means it's a double
4254 std::vector<std::vector<double>> array;
4255 array.resize(bsize.y);
4256 for (int j = 0; j < bsize.y; j++) {
4257 array.at(j).resize(bsize.x);
4258 for (int i = 0; i < bsize.x; i++) {
4259 array.at(j).at(i) = 0.f;
4260 }
4261 }
4262 initializeBuffer2Dd(buffer, array);
4263 } else if (format == RT_FORMAT_FLOAT) {
4264 std::vector<std::vector<float>> array;
4265 array.resize(bsize.y);
4266 for (int j = 0; j < bsize.y; j++) {
4267 array.at(j).resize(bsize.x);
4268 for (int i = 0; i < bsize.x; i++) {
4269 array.at(j).at(i) = 0.f;
4270 }
4271 }
4272 initializeBuffer2Df(buffer, array);
4273 } else if (format == RT_FORMAT_FLOAT2) {
4274 std::vector<std::vector<optix::float2>> array;
4275 array.resize(bsize.y);
4276 for (int j = 0; j < bsize.y; j++) {
4277 array.at(j).resize(bsize.x);
4278 for (int i = 0; i < bsize.x; i++) {
4279 array.at(j).at(i).x = 0.f;
4280 array.at(j).at(i).y = 0.f;
4281 }
4282 }
4283 initializeBuffer2Dfloat2(buffer, array);
4284 } else if (format == RT_FORMAT_FLOAT3) {
4285 std::vector<std::vector<optix::float3>> array;
4286 array.resize(bsize.y);
4287 for (int j = 0; j < bsize.y; j++) {
4288 array.at(j).resize(bsize.x);
4289 for (int i = 0; i < bsize.x; i++) {
4290 array.at(j).at(i).x = 0.f;
4291 array.at(j).at(i).y = 0.f;
4292 array.at(j).at(i).z = 0.f;
4293 }
4294 }
4295 initializeBuffer2Dfloat3(buffer, array);
4296 } else if (format == RT_FORMAT_FLOAT4) {
4297 std::vector<std::vector<optix::float4>> array;
4298 array.resize(bsize.y);
4299 for (int j = 0; j < bsize.y; j++) {
4300 array.at(j).resize(bsize.x);
4301 for (int i = 0; i < bsize.x; i++) {
4302 array.at(j).at(i).x = 0.f;
4303 array.at(j).at(i).y = 0.f;
4304 array.at(j).at(i).z = 0.f;
4305 array.at(j).at(i).w = 0.f;
4306 }
4307 }
4308 initializeBuffer2Dfloat4(buffer, array);
4309 } else if (format == RT_FORMAT_INT) {
4310 std::vector<std::vector<int>> array;
4311 array.resize(bsize.y);
4312 for (int j = 0; j < bsize.y; j++) {
4313 array.at(j).resize(bsize.x);
4314 for (int i = 0; i < bsize.x; i++) {
4315 array.at(j).at(i) = 0;
4316 }
4317 }
4318 initializeBuffer2Di(buffer, array);
4319 } else if (format == RT_FORMAT_UNSIGNED_INT) {
4320 std::vector<std::vector<uint>> array;
4321 array.resize(bsize.y);
4322 for (int j = 0; j < bsize.y; j++) {
4323 array.at(j).resize(bsize.x);
4324 for (int i = 0; i < bsize.x; i++) {
4325 array.at(j).at(i) = 0;
4326 }
4327 }
4328 initializeBuffer2Dui(buffer, array);
4329 } else if (format == RT_FORMAT_INT2) {
4330 std::vector<std::vector<optix::int2>> array;
4331 array.resize(bsize.y);
4332 for (int j = 0; j < bsize.y; j++) {
4333 array.at(j).resize(bsize.x);
4334 for (int i = 0; i < bsize.x; i++) {
4335 array.at(j).at(i).x = 0;
4336 array.at(j).at(i).y = 0;
4337 }
4338 }
4339 initializeBuffer2Dint2(buffer, array);
4340 } else if (format == RT_FORMAT_INT3) {
4341 std::vector<std::vector<optix::int3>> array;
4342 array.resize(bsize.y);
4343 for (int j = 0; j < bsize.y; j++) {
4344 array.at(j).resize(bsize.x);
4345 for (int i = 0; i < bsize.x; i++) {
4346 array.at(j).at(i).x = 0;
4347 array.at(j).at(i).y = 0;
4348 array.at(j).at(i).z = 0;
4349 }
4350 }
4351 initializeBuffer2Dint3(buffer, array);
4352 } else if (format == RT_FORMAT_BYTE) {
4353 std::vector<std::vector<bool>> array;
4354 array.resize(bsize.y);
4355 for (int j = 0; j < bsize.y; j++) {
4356 array.at(j).resize(bsize.x);
4357 for (int i = 0; i < bsize.x; i++) {
4358 array.at(j).at(i) = false;
4359 }
4360 }
4361 initializeBuffer2Dbool(buffer, array);
4362 } else {
4363 helios_runtime_error("ERROR (RadiationModel::zeroBuffer2D): unknown buffer format.");
4364 }
4365}
4366
4367void RadiationModel::initializeBuffer2Dd(RTbuffer &buffer, const std::vector<std::vector<double>> &array) {
4368
4369 optix::int2 bsize;
4370 bsize.y = array.size();
4371 if (bsize.y == 0) {
4372 bsize.x = 0;
4373 } else {
4374 bsize.x = array.front().size();
4375 }
4376
4377 // set buffer size
4378 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4379
4380 // get buffer formatsyn
4381 RTformat format;
4382 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4383
4384 void *ptr;
4385 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4386
4387 if (format == RT_FORMAT_USER) {
4388 double *data = (double *) ptr;
4389 for (size_t j = 0; j < bsize.y; j++) {
4390 for (size_t i = 0; i < bsize.x; i++) {
4391 data[i + j * bsize.x] = array[j][i];
4392 }
4393 }
4394 } else {
4395 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dd): Buffer does not have format 'RT_FORMAT_USER'.");
4396 }
4397
4398 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4399}
4400
4401void RadiationModel::initializeBuffer2Df(RTbuffer &buffer, const std::vector<std::vector<float>> &array) {
4402
4403 optix::int2 bsize;
4404 bsize.y = array.size();
4405 if (bsize.y == 0) {
4406 bsize.x = 0;
4407 } else {
4408 bsize.x = array.front().size();
4409 }
4410
4411 // set buffer size
4412 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4413
4414 // get buffer format
4415 RTformat format;
4416 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4417
4418 void *ptr;
4419 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4420
4421 if (format == RT_FORMAT_FLOAT) {
4422 float *data = (float *) ptr;
4423 for (size_t j = 0; j < bsize.y; j++) {
4424 for (size_t i = 0; i < bsize.x; i++) {
4425 data[i + j * bsize.x] = array[j][i];
4426 }
4427 }
4428 } else {
4429 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Df): Buffer does not have format 'RT_FORMAT_FLOAT'.");
4430 }
4431
4432 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4433}
4434
4435void RadiationModel::initializeBuffer2Dfloat2(RTbuffer &buffer, const std::vector<std::vector<optix::float2>> &array) {
4436
4437 optix::int2 bsize;
4438 bsize.y = array.size();
4439 if (bsize.y == 0) {
4440 bsize.x = 0;
4441 } else {
4442 bsize.x = array.front().size();
4443 }
4444
4445 // set buffer size
4446 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4447
4448 // get buffer format
4449 RTformat format;
4450 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4451
4452 void *ptr;
4453 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4454
4455 if (format == RT_FORMAT_FLOAT2) {
4456 optix::float2 *data = (optix::float2 *) ptr;
4457 for (size_t j = 0; j < bsize.y; j++) {
4458 for (size_t i = 0; i < bsize.x; i++) {
4459 data[i + j * bsize.x].x = array[j][i].x;
4460 data[i + j * bsize.x].y = array[j][i].y;
4461 }
4462 }
4463 } else {
4464 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dfloat2): Buffer does not have format 'RT_FORMAT_FLOAT2'.");
4465 }
4466
4467 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4468}
4469
4470void RadiationModel::initializeBuffer2Dfloat3(RTbuffer &buffer, const std::vector<std::vector<optix::float3>> &array) {
4471
4472 optix::int2 bsize;
4473 bsize.y = array.size();
4474 if (bsize.y == 0) {
4475 bsize.x = 0;
4476 } else {
4477 bsize.x = array.front().size();
4478 }
4479
4480 // set buffer size
4481 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4482
4483 // get buffer format
4484 RTformat format;
4485 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4486
4487 void *ptr;
4488 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4489
4490 if (format == RT_FORMAT_FLOAT3) {
4491 optix::float3 *data = (optix::float3 *) ptr;
4492 for (size_t j = 0; j < bsize.y; j++) {
4493 for (size_t i = 0; i < bsize.x; i++) {
4494 data[i + j * bsize.x].x = array.at(j).at(i).x;
4495 data[i + j * bsize.x].y = array.at(j).at(i).y;
4496 data[i + j * bsize.x].z = array.at(j).at(i).z;
4497 }
4498 }
4499 } else {
4500 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dfloat3): Buffer does not have format 'RT_FORMAT_FLOAT3'.");
4501 }
4502
4503 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4504}
4505
4506void RadiationModel::initializeBuffer2Dfloat4(RTbuffer &buffer, const std::vector<std::vector<optix::float4>> &array) {
4507
4508 optix::int2 bsize;
4509 bsize.y = array.size();
4510 if (bsize.y == 0) {
4511 bsize.x = 0;
4512 } else {
4513 bsize.x = array.front().size();
4514 }
4515
4516 // set buffer size
4517 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4518
4519 // get buffer format
4520 RTformat format;
4521 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4522
4523 void *ptr;
4524 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4525
4526 if (format == RT_FORMAT_FLOAT4) {
4527 optix::float4 *data = (optix::float4 *) ptr;
4528 for (size_t j = 0; j < bsize.y; j++) {
4529 for (size_t i = 0; i < bsize.x; i++) {
4530 data[i + j * bsize.x].x = array[j][i].x;
4531 data[i + j * bsize.x].y = array[j][i].y;
4532 data[i + j * bsize.x].z = array[j][i].z;
4533 data[i + j * bsize.x].w = array[j][i].w;
4534 }
4535 }
4536 } else {
4537 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dfloat4): Buffer does not have format 'RT_FORMAT_FLOAT4'.");
4538 }
4539
4540 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4541}
4542
4543void RadiationModel::initializeBuffer2Di(RTbuffer &buffer, const std::vector<std::vector<int>> &array) {
4544
4545 optix::int2 bsize;
4546 bsize.y = array.size();
4547 if (bsize.y == 0) {
4548 bsize.x = 0;
4549 } else {
4550 bsize.x = array.front().size();
4551 }
4552
4553 // set buffer size
4554 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4555
4556 // get buffer format
4557 RTformat format;
4558 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4559
4560 void *ptr;
4561 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4562
4563 if (format == RT_FORMAT_INT) {
4564 int *data = (int *) ptr;
4565 for (size_t j = 0; j < bsize.y; j++) {
4566 for (size_t i = 0; i < bsize.x; i++) {
4567 data[i + j * bsize.x] = array[j][i];
4568 }
4569 }
4570 } else {
4571 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Di): Buffer does not have format 'RT_FORMAT_INT'.");
4572 }
4573
4574 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4575}
4576
4577void RadiationModel::initializeBuffer2Dui(RTbuffer &buffer, const std::vector<std::vector<uint>> &array) {
4578
4579 optix::int2 bsize;
4580 bsize.y = array.size();
4581 if (bsize.y == 0) {
4582 bsize.x = 0;
4583 } else {
4584 bsize.x = array.front().size();
4585 }
4586
4587 // set buffer size
4588 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4589
4590 // get buffer format
4591 RTformat format;
4592 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4593
4594 void *ptr;
4595 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4596
4597 if (format == RT_FORMAT_UNSIGNED_INT) {
4598 uint *data = (uint *) ptr;
4599 for (size_t j = 0; j < bsize.y; j++) {
4600 for (size_t i = 0; i < bsize.x; i++) {
4601 data[i + j * bsize.x] = array[j][i];
4602 }
4603 }
4604 } else {
4605 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dui): Buffer does not have format 'RT_FORMAT_UNSIGNED_INT'.");
4606 }
4607
4608 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4609}
4610
4611void RadiationModel::initializeBuffer2Dint2(RTbuffer &buffer, const std::vector<std::vector<optix::int2>> &array) {
4612
4613 optix::int2 bsize;
4614 bsize.y = array.size();
4615 if (bsize.y == 0) {
4616 bsize.x = 0;
4617 } else {
4618 bsize.x = array.front().size();
4619 }
4620
4621 // set buffer size
4622 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4623
4624 // get buffer format
4625 RTformat format;
4626 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4627
4628 void *ptr;
4629 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4630
4631 if (format == RT_FORMAT_INT2) {
4632 optix::int2 *data = (optix::int2 *) ptr;
4633 for (size_t j = 0; j < bsize.y; j++) {
4634 for (size_t i = 0; i < bsize.x; i++) {
4635 data[i + j * bsize.x].x = array[j][i].x;
4636 data[i + j * bsize.x].y = array[j][i].y;
4637 }
4638 }
4639 } else {
4640 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dint2): Buffer does not have format 'RT_FORMAT_INT2'.");
4641 }
4642
4643 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4644}
4645
4646void RadiationModel::initializeBuffer2Dint3(RTbuffer &buffer, const std::vector<std::vector<optix::int3>> &array) {
4647
4648 optix::int2 bsize;
4649 bsize.y = array.size();
4650 if (bsize.y == 0) {
4651 bsize.x = 0;
4652 } else {
4653 bsize.x = array.front().size();
4654 }
4655
4656 // set buffer size
4657 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4658
4659 // get buffer format
4660 RTformat format;
4661 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4662
4663 void *ptr;
4664 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4665
4666 if (format == RT_FORMAT_INT3) {
4667 optix::int3 *data = (optix::int3 *) ptr;
4668 for (size_t j = 0; j < bsize.y; j++) {
4669 for (size_t i = 0; i < bsize.x; i++) {
4670 data[i + j * bsize.x].x = array[j][i].x;
4671 data[i + j * bsize.x].y = array[j][i].y;
4672 data[i + j * bsize.x].z = array[j][i].z;
4673 }
4674 }
4675 } else {
4676 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dint3): Buffer does not have format 'RT_FORMAT_INT3'.");
4677 }
4678
4679 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4680}
4681
4682void RadiationModel::initializeBuffer2Dbool(RTbuffer &buffer, const std::vector<std::vector<bool>> &array) {
4683
4684 optix::int2 bsize;
4685 bsize.y = array.size();
4686 if (bsize.y == 0) {
4687 bsize.x = 0;
4688 } else {
4689 bsize.x = array.front().size();
4690 }
4691
4692 // set buffer size
4693 RT_CHECK_ERROR(rtBufferSetSize2D(buffer, bsize.x, bsize.y));
4694
4695 // get buffer format
4696 RTformat format;
4697 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4698
4699 void *ptr;
4700 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4701
4702 if (format == RT_FORMAT_BYTE) {
4703 bool *data = (bool *) ptr;
4704 for (size_t j = 0; j < bsize.y; j++) {
4705 for (size_t i = 0; i < bsize.x; i++) {
4706 data[i + j * bsize.x] = array[j][i];
4707 }
4708 }
4709 } else {
4710 helios_runtime_error("ERROR (RadiationModel::initializeBuffer2Dbool): Buffer does not have format 'RT_FORMAT_BYTE'.");
4711 }
4712
4713 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4714}
4715
4716template<typename anytype>
4717void RadiationModel::initializeBuffer3D(RTbuffer &buffer, const std::vector<std::vector<std::vector<anytype>>> &array) {
4718
4719 optix::int3 bsize;
4720 bsize.z = array.size();
4721 if (bsize.z == 0) {
4722 bsize.y = 0;
4723 bsize.x = 0;
4724 } else {
4725 bsize.y = array.front().size();
4726 if (bsize.y == 0) {
4727 bsize.x = 0;
4728 } else {
4729 bsize.x = array.front().front().size();
4730 }
4731 }
4732
4733 // set buffer size
4734 RT_CHECK_ERROR(rtBufferSetSize3D(buffer, bsize.x, bsize.y, bsize.z));
4735
4736 // get buffer format
4737 RTformat format;
4738 RT_CHECK_ERROR(rtBufferGetFormat(buffer, &format));
4739
4740 // zero out buffer
4741 void *ptr;
4742 RT_CHECK_ERROR(rtBufferMap(buffer, &ptr));
4743
4744 if (format == RT_FORMAT_FLOAT) {
4745 float *data = (float *) ptr;
4746 for (size_t k = 0; k < bsize.z; k++) {
4747 for (size_t j = 0; j < bsize.y; j++) {
4748 for (size_t i = 0; i < bsize.x; i++) {
4749 data[i + j * bsize.x + k * bsize.y * bsize.x] = array[k][j][i];
4750 }
4751 }
4752 }
4753 } else if (format == RT_FORMAT_INT) {
4754 int *data = (int *) ptr;
4755 for (size_t k = 0; k < bsize.z; k++) {
4756 for (size_t j = 0; j < bsize.y; j++) {
4757 for (size_t i = 0; i < bsize.x; i++) {
4758 data[i + j * bsize.x + k * bsize.y * bsize.x] = array[k][j][i];
4759 }
4760 }
4761 }
4762 } else if (format == RT_FORMAT_UNSIGNED_INT) {
4763 uint *data = (uint *) ptr;
4764 for (size_t k = 0; k < bsize.z; k++) {
4765 for (size_t j = 0; j < bsize.y; j++) {
4766 for (size_t i = 0; i < bsize.x; i++) {
4767 data[i + j * bsize.x + k * bsize.y * bsize.x] = array[k][j][i];
4768 }
4769 }
4770 }
4771 } else if (format == RT_FORMAT_BYTE) {
4772 bool *data = (bool *) ptr;
4773 for (size_t k = 0; k < bsize.z; k++) {
4774 for (size_t j = 0; j < bsize.y; j++) {
4775 for (size_t i = 0; i < bsize.x; i++) {
4776 data[i + j * bsize.x + k * bsize.y * bsize.x] = array[k][j][i];
4777 }
4778 }
4779 }
4780 } else {
4781 std::cerr << "ERROR (RadiationModel::initializeBuffer3D): unsupported buffer format." << std::endl;
4782 }
4783
4784 RT_CHECK_ERROR(rtBufferUnmap(buffer));
4785}
4786
4788
4789 vec3 dir = view_direction;
4790 dir.normalize();
4791
4792 float Gtheta = 0;
4793 float total_area = 0;
4794 for (std::size_t u = 0; u < primitiveID.size(); u++) {
4795
4796 uint UUID = context_UUIDs.at(primitiveID.at(u));
4797
4798 vec3 normal = context->getPrimitiveNormal(UUID);
4799 float area = context->getPrimitiveArea(UUID);
4800
4801 Gtheta += fabsf(normal * dir) * area;
4802
4803 total_area += area;
4804 }
4805
4806 return Gtheta / total_area;
4807}
4808
4809void RadiationModel::exportColorCorrectionMatrixXML(const std::string &file_path, const std::string &camera_label, const std::vector<std::vector<float>> &matrix, const std::string &source_image_path, const std::string &colorboard_type,
4810 float average_delta_e) {
4811
4812 std::ofstream file(file_path);
4813 if (!file.is_open()) {
4814 helios_runtime_error("ERROR (RadiationModel::exportColorCorrectionMatrixXML): Failed to open file for writing: " + file_path);
4815 }
4816
4817 // Determine matrix type (3x3 or 4x3)
4818 std::string matrix_type = "3x3";
4819 if (matrix.size() == 4 || (matrix.size() >= 3 && matrix[0].size() == 4)) {
4820 matrix_type = "4x3";
4821 }
4822
4823 // Write XML header with informative comments
4824 file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
4825 file << "<!-- Camera Color Correction Matrix -->" << std::endl;
4826 file << "<!-- Source Image: " << source_image_path << " -->" << std::endl;
4827 file << "<!-- Camera Label: " << camera_label << " -->" << std::endl;
4828 file << "<!-- Colorboard Type: " << colorboard_type << " -->" << std::endl;
4829 if (average_delta_e >= 0.0f) {
4830 file << "<!-- Average Delta E: " << std::fixed << std::setprecision(2) << average_delta_e << " -->" << std::endl;
4831 }
4832 file << "<!-- Matrix Type: " << matrix_type << " -->" << std::endl;
4833 file << "<!-- Generated: " << getCurrentDateTime() << " -->" << std::endl;
4834
4835 // Write matrix data
4836 file << "<helios>" << std::endl;
4837 file << " <ColorCorrectionMatrix camera_label=\"" << camera_label << "\" matrix_type=\"" << matrix_type << "\">" << std::endl;
4838
4839 for (size_t i = 0; i < matrix.size(); i++) {
4840 file << " <row>";
4841 for (size_t j = 0; j < matrix[i].size(); j++) {
4842 file << std::fixed << std::setprecision(6) << matrix[i][j];
4843 if (j < matrix[i].size() - 1) {
4844 file << " ";
4845 }
4846 }
4847 file << "</row>" << std::endl;
4848 }
4849
4850 file << " </ColorCorrectionMatrix>" << std::endl;
4851 file << "</helios>" << std::endl;
4852
4853 file.close();
4854}
4855
4856std::string RadiationModel::getCurrentDateTime() {
4857 auto now = std::time(nullptr);
4858 auto tm = *std::localtime(&now);
4859 std::stringstream ss;
4860 ss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S");
4861 return ss.str();
4862}
4863
4864std::vector<std::vector<float>> RadiationModel::loadColorCorrectionMatrixXML(const std::string &file_path, std::string &camera_label_out) {
4865
4866 std::ifstream file(file_path);
4867 if (!file.is_open()) {
4868 helios_runtime_error("ERROR (RadiationModel::loadColorCorrectionMatrixXML): Failed to open file for reading: " + file_path);
4869 }
4870
4871 std::vector<std::vector<float>> matrix;
4872 std::string line;
4873 bool in_matrix = false;
4874 std::string matrix_type = "";
4875
4876 while (std::getline(file, line)) {
4877 // Remove leading/trailing whitespace
4878 line.erase(0, line.find_first_not_of(" \t"));
4879 line.erase(line.find_last_not_of(" \t") + 1);
4880
4881 // Look for ColorCorrectionMatrix opening tag
4882 if (line.find("<ColorCorrectionMatrix") != std::string::npos) {
4883 in_matrix = true;
4884
4885 // Extract camera_label attribute
4886 size_t camera_start = line.find("camera_label=\"");
4887 if (camera_start != std::string::npos) {
4888 camera_start += 14; // Length of "camera_label=\""
4889 size_t camera_end = line.find("\"", camera_start);
4890 if (camera_end != std::string::npos) {
4891 camera_label_out = line.substr(camera_start, camera_end - camera_start);
4892 }
4893 }
4894
4895 // Extract matrix_type attribute
4896 size_t type_start = line.find("matrix_type=\"");
4897 if (type_start != std::string::npos) {
4898 type_start += 13; // Length of "matrix_type=\""
4899 size_t type_end = line.find("\"", type_start);
4900 if (type_end != std::string::npos) {
4901 matrix_type = line.substr(type_start, type_end - type_start);
4902 }
4903 }
4904 continue;
4905 }
4906
4907 // Look for ColorCorrectionMatrix closing tag
4908 if (line.find("</ColorCorrectionMatrix>") != std::string::npos) {
4909 in_matrix = false;
4910 break;
4911 }
4912
4913 // Parse row data
4914 if (in_matrix && line.find("<row>") != std::string::npos && line.find("</row>") != std::string::npos) {
4915 // Extract content between <row> and </row>
4916 size_t start = line.find("<row>") + 5;
4917 size_t end = line.find("</row>");
4918 std::string row_data = line.substr(start, end - start);
4919
4920 // Parse float values from row
4921 std::vector<float> row;
4922 std::istringstream iss(row_data);
4923 float value;
4924 while (iss >> value) {
4925 row.push_back(value);
4926 }
4927
4928 if (!row.empty()) {
4929 matrix.push_back(row);
4930 }
4931 }
4932 }
4933
4934 file.close();
4935
4936 // Validate loaded matrix
4937 if (matrix.empty()) {
4938 helios_runtime_error("ERROR (RadiationModel::loadColorCorrectionMatrixXML): No matrix data found in file: " + file_path);
4939 }
4940
4941 if (matrix.size() != 3) {
4942 helios_runtime_error("ERROR (RadiationModel::loadColorCorrectionMatrixXML): Invalid matrix size. Expected 3 rows, found " + std::to_string(matrix.size()) + " rows in file: " + file_path);
4943 }
4944
4945 // Validate matrix type consistency
4946 bool is_3x3 = (matrix[0].size() == 3 && matrix[1].size() == 3 && matrix[2].size() == 3);
4947 bool is_4x3 = (matrix[0].size() == 4 && matrix[1].size() == 4 && matrix[2].size() == 4);
4948
4949 if (!is_3x3 && !is_4x3) {
4950 helios_runtime_error("ERROR (RadiationModel::loadColorCorrectionMatrixXML): Invalid matrix dimensions. All rows must have either 3 or 4 elements. File: " + file_path);
4951 }
4952
4953 // Check matrix type attribute matches actual dimensions
4954 if (!matrix_type.empty()) {
4955 if ((matrix_type == "3x3" && !is_3x3) || (matrix_type == "4x3" && !is_4x3)) {
4956 helios_runtime_error("ERROR (RadiationModel::loadColorCorrectionMatrixXML): Matrix type attribute ('" + matrix_type + "') does not match actual matrix dimensions in file: " + file_path);
4957 }
4958 }
4959
4960 return matrix;
4961}
4962
4963std::string RadiationModel::autoCalibrateCameraImage(const std::string &camera_label, const std::string &red_band_label, const std::string &green_band_label, const std::string &blue_band_label, const std::string &output_file_path,
4964 bool print_quality_report, ColorCorrectionAlgorithm algorithm, const std::string &ccm_export_file_path) {
4965
4966 // Step 1: Validate camera exists and get pixel UUID data
4967 if (cameras.find(camera_label) == cameras.end()) {
4968 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Camera '" + camera_label + "' does not exist. Make sure the camera was added to the radiation model.");
4969 }
4970
4971 // Get camera pixel UUID data from global data (needed for segmentation)
4972 std::string pixel_UUID_label = "camera_" + camera_label + "_pixel_UUID";
4973 if (!context->doesGlobalDataExist(pixel_UUID_label.c_str())) {
4974 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Camera pixel UUID data '" + pixel_UUID_label + "' does not exist for camera '" + camera_label + "'. Make sure the radiation model has been run.");
4975 }
4976
4977 // Step 2: Detect colorboard type using CameraCalibration helper
4978 CameraCalibration calibration(context);
4979 std::string colorboard_type;
4980 try {
4981 colorboard_type = calibration.detectColorBoardType();
4982 } catch (const std::exception &e) {
4983 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Failed to detect colorboard type. " + std::string(e.what()));
4984 }
4985
4986 // Step 3: Get reference Lab values for the detected colorboard
4987 std::vector<CameraCalibration::LabColor> reference_lab_values;
4988 if (colorboard_type == "DGK") {
4989 reference_lab_values = calibration.getReferenceLab_DGK();
4990 } else if (colorboard_type == "Calibrite") {
4991 reference_lab_values = calibration.getReferenceLab_Calibrite();
4992 } else if (colorboard_type == "SpyderCHECKR") {
4993 reference_lab_values = calibration.getReferenceLab_SpyderCHECKR();
4994 } else {
4995 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Unsupported colorboard type '" + colorboard_type + "'.");
4996 }
4997
4998 // Step 4: Generate segmentation masks for colorboard patches
4999 std::vector<uint> pixel_UUIDs;
5000 context->getGlobalData(pixel_UUID_label.c_str(), pixel_UUIDs);
5001 int2 camera_resolution = cameras.at(camera_label).resolution;
5002
5003 // Create segmentation masks by finding pixels that belong to colorboard patches
5004 std::map<int, std::vector<std::vector<bool>>> patch_masks;
5005 for (int patch_idx = 0; patch_idx < (int) reference_lab_values.size(); patch_idx++) {
5006 std::vector<std::vector<bool>> mask(camera_resolution.y, std::vector<bool>(camera_resolution.x, false));
5007
5008 // Find pixels that correspond to this colorboard patch
5009 for (int y = 0; y < camera_resolution.y; y++) {
5010 for (int x = 0; x < camera_resolution.x; x++) {
5011 int pixel_index = y * camera_resolution.x + x;
5012 uint pixel_UUID = pixel_UUIDs[pixel_index];
5013
5014 if (pixel_UUID > 0) { // Valid primitive
5015 pixel_UUID--; // Convert from 1-based to 0-based indexing
5016
5017 // Check if this primitive belongs to the colorboard patch
5018 std::string colorboard_data_label = "colorboard_" + colorboard_type;
5019 if (context->doesPrimitiveDataExist(pixel_UUID, colorboard_data_label.c_str())) {
5020 uint patch_id;
5021 context->getPrimitiveData(pixel_UUID, colorboard_data_label.c_str(), patch_id);
5022 // Patch indices are 0-based, compare directly
5023 if ((int) patch_id == patch_idx) {
5024 mask[y][x] = true;
5025 }
5026 }
5027 }
5028 }
5029 }
5030
5031 patch_masks[patch_idx] = mask;
5032 }
5033
5034 // Step 5: Extract RGB colors from processed camera data (same source as writeCameraImage)
5035 // Use the same data source that writeCameraImage() uses: cameras.pixel_data
5036 std::vector<float> red_data, green_data, blue_data;
5037
5038 // Check if bands exist in camera
5039 auto &camera_bands = cameras.at(camera_label).band_labels;
5040 if (std::find(camera_bands.begin(), camera_bands.end(), red_band_label) == camera_bands.end()) {
5041 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Red band '" + red_band_label + "' not found in camera '" + camera_label + "'.");
5042 }
5043 if (std::find(camera_bands.begin(), camera_bands.end(), green_band_label) == camera_bands.end()) {
5044 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Green band '" + green_band_label + "' not found in camera '" + camera_label + "'.");
5045 }
5046 if (std::find(camera_bands.begin(), camera_bands.end(), blue_band_label) == camera_bands.end()) {
5047 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Blue band '" + blue_band_label + "' not found in camera '" + camera_label + "'.");
5048 }
5049
5050 // Read processed camera data (same as writeCameraImage uses)
5051 red_data = cameras.at(camera_label).pixel_data.at(red_band_label);
5052 green_data = cameras.at(camera_label).pixel_data.at(green_band_label);
5053 blue_data = cameras.at(camera_label).pixel_data.at(blue_band_label);
5054
5055 // Check data range and normalize if needed
5056 float max_r = *std::max_element(red_data.begin(), red_data.end());
5057 float max_g = *std::max_element(green_data.begin(), green_data.end());
5058 float max_b = *std::max_element(blue_data.begin(), blue_data.end());
5059
5060 // Normalize camera data to [0,1] range if values are > 1
5061 float scale_factor = 1.0f;
5062 if (max_r > 1.0f || max_g > 1.0f || max_b > 1.0f) {
5063 scale_factor = 1.0f / std::max({max_r, max_g, max_b});
5064
5065 for (size_t i = 0; i < red_data.size(); i++) {
5066 red_data[i] *= scale_factor;
5067 green_data[i] *= scale_factor;
5068 blue_data[i] *= scale_factor;
5069 }
5070 }
5071
5072 std::vector<helios::vec3> measured_rgb_values;
5073 int visible_patches = 0;
5074
5075 for (const auto &[patch_idx, mask]: patch_masks) {
5076 float sum_r = 0.0f, sum_g = 0.0f, sum_b = 0.0f;
5077 int pixel_count = 0;
5078
5079 // Average RGB values over all pixels in this patch
5080 for (int y = 0; y < camera_resolution.y; y++) {
5081 for (int x = 0; x < camera_resolution.x; x++) {
5082 if (mask[y][x]) {
5083 int pixel_index = y * camera_resolution.x + x;
5084 sum_r += red_data[pixel_index];
5085 sum_g += green_data[pixel_index];
5086 sum_b += blue_data[pixel_index];
5087 pixel_count++;
5088 }
5089 }
5090 }
5091
5092 if (pixel_count > 10) { // Only consider patches with sufficient pixels
5093 helios::vec3 avg_rgb = make_vec3(sum_r / pixel_count, sum_g / pixel_count, sum_b / pixel_count);
5094 measured_rgb_values.push_back(avg_rgb);
5095
5096 visible_patches++;
5097 } else {
5098 // Add placeholder for missing patch
5099 measured_rgb_values.push_back(make_vec3(0, 0, 0));
5100 }
5101 }
5102
5103 // Convert measured RGB to Lab and calculate correction matrix
5104 std::vector<CameraCalibration::LabColor> measured_lab_values;
5105 for (const auto &rgb: measured_rgb_values) {
5106 if (rgb.magnitude() > 0) { // Only process non-zero values
5107 measured_lab_values.push_back(calibration.rgbToLab(rgb));
5108 }
5109 }
5110
5111 // Calculate color correction matrix based on selected algorithm
5112 std::vector<std::vector<float>> correction_matrix = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}};
5113
5114 // Report which algorithm is being used
5115 std::string algorithm_name;
5116 switch (algorithm) {
5118 algorithm_name = "Diagonal scaling (white balance only)";
5119 break;
5121 algorithm_name = "3x3 matrix with auto-fallback to diagonal";
5122 break;
5124 algorithm_name = "3x3 matrix (forced)";
5125 break;
5126 }
5127
5128 if (measured_lab_values.size() >= 6 && reference_lab_values.size() >= 6) {
5129 // Convert reference Lab back to RGB for matrix fitting
5130 std::vector<helios::vec3> target_rgb;
5131
5132 for (size_t i = 0; i < reference_lab_values.size(); i++) {
5133 CameraCalibration::LabColor ref_lab = reference_lab_values[i];
5134 helios::vec3 ref_rgb = calibration.labToRgb(ref_lab);
5135 target_rgb.push_back(ref_rgb);
5136 }
5137
5138 // Build matrices for least squares: M * Measured = Target
5139 // We need to solve for M where M is 3x3 matrix
5140 // This becomes: M = Target * Measured^T * (Measured * Measured^T)^(-1)
5141
5142 // Collect valid patches for matrix calculation
5143 std::vector<helios::vec3> valid_measured, valid_target;
5144 std::vector<float> patch_weights;
5145
5146 for (size_t i = 0; i < std::min(measured_rgb_values.size(), target_rgb.size()); i++) {
5147 if (measured_rgb_values[i].magnitude() > 0.01f) {
5148 valid_measured.push_back(measured_rgb_values[i]);
5149 valid_target.push_back(target_rgb[i]);
5150
5151 // Perceptually-weighted patch selection based on colour-science best practices
5152 float weight = 1.0f;
5153
5154 // Neutral patches (white to black series) - highest priority for white balance
5155 if (i >= 18 && i <= 23) {
5156 // White and light grays get highest weight (most visually important)
5157 if (i == 18)
5158 weight = 5.0f; // White patch - critical for white balance
5159 else if (i == 19)
5160 weight = 4.0f; // Light gray - very important for tone mapping
5161 else
5162 weight = 3.0f; // Darker grays - important for contrast
5163 }
5164 // Primary color patches - important for color accuracy
5165 else if (i == 14 || i == 13 || i == 12)
5166 weight = 3.0f; // Red, Green, Blue primaries
5167
5168 // Skin tone approximates - patches that represent common skin tones
5169 else if (i == 3 || i == 10)
5170 weight = 2.5f; // Foliage and Yellow Green (skin-like hues)
5171
5172 // Well-lit patches get higher weight based on luminance
5173 helios::vec3 measured_rgb = measured_rgb_values[i];
5174 float luminance = 0.299f * measured_rgb.x + 0.587f * measured_rgb.y + 0.114f * measured_rgb.z;
5175
5176 // Boost weight for brighter patches (they're more visually prominent)
5177 if (luminance > 0.6f)
5178 weight *= 1.5f;
5179 else if (luminance < 0.2f)
5180 weight *= 0.7f; // Reduce weight for very dark patches
5181
5182 // Additional quality checks for measured RGB values
5183 // Reduce weight for patches that seem poorly measured (extreme values)
5184 if (measured_rgb.magnitude() > 1.2f || measured_rgb.magnitude() < 0.1f) {
5185 weight *= 0.5f; // Reduce weight for suspicious measurements
5186 }
5187
5188 patch_weights.push_back(weight);
5189 }
5190 }
5191
5192 if (valid_measured.size() >= 6 && algorithm != ColorCorrectionAlgorithm::DIAGONAL_ONLY) {
5193
5194 // Compute robustly regularized weighted least squares solution
5195 // Use adaptive regularization to prevent extreme matrix coefficients
5196 bool matrix_valid = true;
5197
5198 // Try moderate regularization values - avoid extreme regularization that creates bad matrices
5199 std::vector<float> lambda_values = {0.01f, 0.05f, 0.1f, 0.15f, 0.2f};
5200 int lambda_attempt = 0;
5201
5202 while (lambda_attempt < lambda_values.size()) {
5203 float lambda = lambda_values[lambda_attempt];
5204 matrix_valid = true;
5205
5206 for (int row = 0; row < 3; row++) {
5207 // Build weighted normal equations with adaptive regularization
5208 float ATA[3][3] = {{0}}; // A^T * W * A + λI
5209 float ATb[3] = {0}; // A^T * W * b
5210
5211 for (size_t i = 0; i < valid_measured.size(); i++) {
5212 float weight = patch_weights[i];
5213 helios::vec3 m = valid_measured[i]; // measured RGB
5214 float target_val = (row == 0) ? valid_target[i].x : (row == 1) ? valid_target[i].y : valid_target[i].z;
5215
5216 // Update normal equations
5217 ATA[0][0] += weight * m.x * m.x;
5218 ATA[0][1] += weight * m.x * m.y;
5219 ATA[0][2] += weight * m.x * m.z;
5220 ATA[1][0] += weight * m.y * m.x;
5221 ATA[1][1] += weight * m.y * m.y;
5222 ATA[1][2] += weight * m.y * m.z;
5223 ATA[2][0] += weight * m.z * m.x;
5224 ATA[2][1] += weight * m.z * m.y;
5225 ATA[2][2] += weight * m.z * m.z;
5226
5227 ATb[0] += weight * m.x * target_val;
5228 ATb[1] += weight * m.y * target_val;
5229 ATb[2] += weight * m.z * target_val;
5230 }
5231
5232 // Add color-preserving regularization
5233 // Stronger regularization on diagonal (preserves primary colors)
5234 // Weaker regularization on off-diagonal (allows some color mixing)
5235 float diag_reg = lambda * 2.0f; // Stronger on diagonal
5236 float offdiag_reg = lambda * 0.5f; // Weaker on off-diagonal
5237
5238 ATA[0][0] += diag_reg; // Red preservation
5239 ATA[1][1] += diag_reg; // Green preservation
5240 ATA[2][2] += diag_reg; // Blue preservation
5241
5242 // Light off-diagonal regularization to prevent extreme color mixing
5243 ATA[0][1] += offdiag_reg;
5244 ATA[1][0] += offdiag_reg;
5245 ATA[0][2] += offdiag_reg;
5246 ATA[2][0] += offdiag_reg;
5247 ATA[1][2] += offdiag_reg;
5248 ATA[2][1] += offdiag_reg;
5249
5250 // Solve regularized 3x3 system using Cramer's rule
5251 float det = ATA[0][0] * (ATA[1][1] * ATA[2][2] - ATA[1][2] * ATA[2][1]) - ATA[0][1] * (ATA[1][0] * ATA[2][2] - ATA[1][2] * ATA[2][0]) + ATA[0][2] * (ATA[1][0] * ATA[2][1] - ATA[1][1] * ATA[2][0]);
5252
5253 if (fabs(det) < 1e-3f) {
5255 matrix_valid = false;
5256 break;
5257 }
5258 }
5259
5260 float inv_det = 1.0f / det;
5261 correction_matrix[row][0] = inv_det * (ATb[0] * (ATA[1][1] * ATA[2][2] - ATA[1][2] * ATA[2][1]) - ATb[1] * (ATA[0][1] * ATA[2][2] - ATA[0][2] * ATA[2][1]) + ATb[2] * (ATA[0][1] * ATA[1][2] - ATA[0][2] * ATA[1][1]));
5262
5263 correction_matrix[row][1] = inv_det * (ATb[1] * (ATA[0][0] * ATA[2][2] - ATA[0][2] * ATA[2][0]) - ATb[0] * (ATA[1][0] * ATA[2][2] - ATA[1][2] * ATA[2][0]) + ATb[2] * (ATA[1][0] * ATA[0][2] - ATA[1][2] * ATA[0][0]));
5264
5265 correction_matrix[row][2] = inv_det * (ATb[2] * (ATA[0][0] * ATA[1][1] - ATA[0][1] * ATA[1][0]) - ATb[0] * (ATA[1][0] * ATA[2][1] - ATA[1][1] * ATA[2][0]) + ATb[1] * (ATA[0][0] * ATA[2][1] - ATA[0][1] * ATA[2][0]));
5266 }
5267
5268 // Validate computed matrix elements are reasonable
5269 if (matrix_valid) {
5270 bool elements_reasonable = true;
5271 for (int i = 0; i < 3; i++) {
5272 for (int j = 0; j < 3; j++) {
5273 if (fabs(correction_matrix[i][j]) > 5.0f) {
5275 elements_reasonable = false;
5276 break;
5277 }
5278 }
5279 }
5280 if (!elements_reasonable)
5281 break;
5282 }
5283 matrix_valid = elements_reasonable;
5284 }
5285
5286 if (matrix_valid) {
5287 break; // Success!
5288 } else {
5289 lambda_attempt++;
5290 }
5291 }
5292
5293 if (!matrix_valid) {
5294
5295 // Enhanced perceptually-weighted diagonal correction
5296 // Calculate weighted averages for each color channel
5297 float total_weight = 0.0f;
5298 helios::vec3 weighted_correction = make_vec3(0, 0, 0);
5299
5300 for (size_t i = 0; i < valid_measured.size(); i++) {
5301 float weight = patch_weights[i];
5302 helios::vec3 measured = valid_measured[i];
5303 helios::vec3 target = valid_target[i];
5304
5305 // Calculate per-channel correction factors
5306 if (measured.x > 0.01f && measured.y > 0.01f && measured.z > 0.01f) {
5307 helios::vec3 channel_correction = make_vec3(target.x / measured.x, target.y / measured.y, target.z / measured.z);
5308
5309 weighted_correction.x += weight * channel_correction.x;
5310 weighted_correction.y += weight * channel_correction.y;
5311 weighted_correction.z += weight * channel_correction.z;
5312 total_weight += weight;
5313 }
5314 }
5315
5316 if (total_weight > 0.1f) {
5317 // Apply weighted average correction factors
5318 correction_matrix[0][0] = weighted_correction.x / total_weight;
5319 correction_matrix[1][1] = weighted_correction.y / total_weight;
5320 correction_matrix[2][2] = weighted_correction.z / total_weight;
5321
5322 // Apply conservative limits
5323 correction_matrix[0][0] = std::max(0.5f, std::min(2.0f, correction_matrix[0][0]));
5324 correction_matrix[1][1] = std::max(0.5f, std::min(2.0f, correction_matrix[1][1]));
5325 correction_matrix[2][2] = std::max(0.5f, std::min(2.0f, correction_matrix[2][2]));
5326 }
5327 }
5328
5329 if (!matrix_valid || algorithm == ColorCorrectionAlgorithm::DIAGONAL_ONLY) {
5330 // Use diagonal correction using white patch
5331 correction_matrix = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}};
5332
5333 // Enhanced perceptually-weighted diagonal correction using all valid patches
5334 // This is more robust than using just the white patch
5335 if (valid_measured.size() > 0 && patch_weights.size() == valid_measured.size()) {
5336 float total_weight = 0.0f;
5337 helios::vec3 weighted_measured_avg = make_vec3(0, 0, 0);
5338 helios::vec3 weighted_target_avg = make_vec3(0, 0, 0);
5339
5340 // Compute weighted averages using perceptual patch weights
5341 for (size_t i = 0; i < valid_measured.size(); i++) {
5342 float weight = patch_weights[i];
5343 weighted_measured_avg = weighted_measured_avg + weight * valid_measured[i];
5344 weighted_target_avg = weighted_target_avg + weight * valid_target[i];
5345 total_weight += weight;
5346 }
5347
5348 if (total_weight > 0) {
5349 weighted_measured_avg = weighted_measured_avg / total_weight;
5350 weighted_target_avg = weighted_target_avg / total_weight;
5351
5352 if (weighted_measured_avg.x > 0.05f && weighted_measured_avg.y > 0.05f && weighted_measured_avg.z > 0.05f) {
5353 correction_matrix[0][0] = weighted_target_avg.x / weighted_measured_avg.x;
5354 correction_matrix[1][1] = weighted_target_avg.y / weighted_measured_avg.y;
5355 correction_matrix[2][2] = weighted_target_avg.z / weighted_measured_avg.z;
5356
5357 // Apply conservative limits for stability
5358 correction_matrix[0][0] = std::max(0.5f, std::min(2.0f, correction_matrix[0][0]));
5359 correction_matrix[1][1] = std::max(0.5f, std::min(2.0f, correction_matrix[1][1]));
5360 correction_matrix[2][2] = std::max(0.5f, std::min(2.0f, correction_matrix[2][2]));
5361
5362 } else {
5363 // Fallback to original white-patch method
5364 size_t white_idx = 18;
5365 if (white_idx < measured_rgb_values.size() && white_idx < target_rgb.size()) {
5366 helios::vec3 measured_white = measured_rgb_values[white_idx];
5367 helios::vec3 target_white = target_rgb[white_idx];
5368 if (measured_white.x > 0.05f && measured_white.y > 0.05f && measured_white.z > 0.05f) {
5369 correction_matrix[0][0] = std::max(0.5f, std::min(2.0f, target_white.x / measured_white.x));
5370 correction_matrix[1][1] = std::max(0.5f, std::min(2.0f, target_white.y / measured_white.y));
5371 correction_matrix[2][2] = std::max(0.5f, std::min(2.0f, target_white.z / measured_white.z));
5372 }
5373 }
5374 }
5375 }
5376 } else {
5377 // Original simple approach as ultimate fallback
5378 size_t white_idx = 18;
5379 if (white_idx < measured_rgb_values.size() && white_idx < target_rgb.size()) {
5380 helios::vec3 measured_white = measured_rgb_values[white_idx];
5381 helios::vec3 target_white = target_rgb[white_idx];
5382 if (measured_white.x > 0.05f && measured_white.y > 0.05f && measured_white.z > 0.05f) {
5383 correction_matrix[0][0] = std::max(0.5f, std::min(2.0f, target_white.x / measured_white.x));
5384 correction_matrix[1][1] = std::max(0.5f, std::min(2.0f, target_white.y / measured_white.y));
5385 correction_matrix[2][2] = std::max(0.5f, std::min(2.0f, target_white.z / measured_white.z));
5386 }
5387 }
5388 }
5389 }
5390 } else if (algorithm == ColorCorrectionAlgorithm::DIAGONAL_ONLY) {
5391 // Apply diagonal correction using white patch
5392 size_t white_idx = 18;
5393 if (white_idx < measured_rgb_values.size() && white_idx < target_rgb.size() && measured_rgb_values[white_idx].magnitude() > 0) {
5394
5395 helios::vec3 measured_white = measured_rgb_values[white_idx];
5396 helios::vec3 target_white = target_rgb[white_idx];
5397
5398 if (measured_white.x > 0.05f && measured_white.y > 0.05f && measured_white.z > 0.05f) {
5399 correction_matrix[0][0] = target_white.x / measured_white.x;
5400 correction_matrix[1][1] = target_white.y / measured_white.y;
5401 correction_matrix[2][2] = target_white.z / measured_white.z;
5402
5403 // Apply limits
5404 correction_matrix[0][0] = std::max(0.5f, std::min(2.0f, correction_matrix[0][0]));
5405 correction_matrix[1][1] = std::max(0.5f, std::min(2.0f, correction_matrix[1][1]));
5406 correction_matrix[2][2] = std::max(0.5f, std::min(2.0f, correction_matrix[2][2]));
5407 }
5408 }
5409 } else {
5410 std::cout << "Insufficient valid patches (" << valid_measured.size() << " available), using identity matrix" << std::endl;
5411 }
5412 } else if (algorithm == ColorCorrectionAlgorithm::DIAGONAL_ONLY && measured_lab_values.size() > 0) {
5413 // Apply diagonal correction using white patch
5414 size_t white_idx = 18;
5415 if (white_idx < measured_rgb_values.size() && white_idx < reference_lab_values.size() && measured_rgb_values[white_idx].magnitude() > 0) {
5416
5417 CameraCalibration::LabColor ref_lab = reference_lab_values[white_idx];
5418 helios::vec3 target_white = calibration.labToRgb(ref_lab);
5419 helios::vec3 measured_white = measured_rgb_values[white_idx];
5420
5421 if (measured_white.x > 0.05f && measured_white.y > 0.05f && measured_white.z > 0.05f) {
5422 correction_matrix[0][0] = target_white.x / measured_white.x;
5423 correction_matrix[1][1] = target_white.y / measured_white.y;
5424 correction_matrix[2][2] = target_white.z / measured_white.z;
5425
5426 // Apply limits
5427 correction_matrix[0][0] = std::max(0.5f, std::min(2.0f, correction_matrix[0][0]));
5428 correction_matrix[1][1] = std::max(0.5f, std::min(2.0f, correction_matrix[1][1]));
5429 correction_matrix[2][2] = std::max(0.5f, std::min(2.0f, correction_matrix[2][2]));
5430 }
5431 }
5432 } else {
5433 std::cout << "Insufficient patches for correction (" << measured_lab_values.size() << " available), using identity matrix" << std::endl;
5434 }
5435
5436 // Generate quality of fit report if requested
5437 if (print_quality_report) {
5438 std::cout << "\n========== COLOR CALIBRATION QUALITY REPORT ==========" << std::endl;
5439 std::cout << "Colorboard type: " << colorboard_type << std::endl;
5440 std::cout << "Number of patches analyzed: " << visible_patches << std::endl;
5441 std::cout << "Algorithm used: " << algorithm_name << std::endl;
5442
5443 // Display matrix conditioning information
5444 bool is_diagonal_only = true;
5445 for (int i = 0; i < 3; i++) {
5446 for (int j = 0; j < 3; j++) {
5447 if (i != j && fabs(correction_matrix[i][j]) > 1e-6f) {
5448 is_diagonal_only = false;
5449 break;
5450 }
5451 }
5452 if (!is_diagonal_only)
5453 break;
5454 }
5455
5456 if (is_diagonal_only) {
5457 std::cout << "Color correction factors applied: R=" << correction_matrix[0][0] << ", G=" << correction_matrix[1][1] << ", B=" << correction_matrix[2][2] << std::endl;
5458 std::cout << "Matrix type: Diagonal (white balance only)" << std::endl;
5459 } else {
5460 std::cout << "Full 3x3 color correction matrix applied:" << std::endl;
5461 for (int i = 0; i < 3; i++) {
5462 std::cout << "[" << std::fixed << std::setprecision(4);
5463 for (int j = 0; j < 3; j++) {
5464 std::cout << std::setw(8) << correction_matrix[i][j];
5465 if (j < 2)
5466 std::cout << " ";
5467 }
5468 std::cout << "]" << std::endl;
5469 }
5470 std::cout << "Matrix type: Full 3x3 (corrects color casts and chromatic errors)" << std::endl;
5471
5472 // Calculate matrix determinant for conditioning info
5473 float det = correction_matrix[0][0] * (correction_matrix[1][1] * correction_matrix[2][2] - correction_matrix[1][2] * correction_matrix[2][1]) -
5474 correction_matrix[0][1] * (correction_matrix[1][0] * correction_matrix[2][2] - correction_matrix[1][2] * correction_matrix[2][0]) +
5475 correction_matrix[0][2] * (correction_matrix[1][0] * correction_matrix[2][1] - correction_matrix[1][1] * correction_matrix[2][0]);
5476
5477 std::cout << "Matrix determinant: " << std::scientific << std::setprecision(3) << det << std::endl;
5478 if (fabs(det) > 0.1f) {
5479 std::cout << "Matrix conditioning: Good (well-conditioned)" << std::endl;
5480 } else if (fabs(det) > 0.01f) {
5481 std::cout << "Matrix conditioning: Fair (moderately conditioned)" << std::endl;
5482 } else {
5483 std::cout << "Matrix conditioning: Poor (ill-conditioned)" << std::endl;
5484 }
5485 std::cout << std::fixed; // Reset formatting
5486 }
5487
5488 // Calculate and display quality metrics for each patch
5489 double total_delta_e = 0.0;
5490 int valid_patches = 0;
5491
5492 std::cout << "\nPer-patch analysis (after correction):" << std::endl;
5493 std::cout << "Patch | Corrected RGB | Reference RGB | Delta E " << std::endl;
5494 std::cout << "------|--------------------|--------------------|---------" << std::endl;
5495
5496 for (size_t i = 0; i < std::min(measured_rgb_values.size(), reference_lab_values.size()); i++) {
5497 if (measured_rgb_values[i].magnitude() > 0) {
5498 // Apply color correction to measured RGB values (with optional affine terms)
5499 helios::vec3 measured_rgb = measured_rgb_values[i];
5500 float corrected_r = correction_matrix[0][0] * measured_rgb.x + correction_matrix[0][1] * measured_rgb.y + correction_matrix[0][2] * measured_rgb.z;
5501 float corrected_g = correction_matrix[1][0] * measured_rgb.x + correction_matrix[1][1] * measured_rgb.y + correction_matrix[1][2] * measured_rgb.z;
5502 float corrected_b = correction_matrix[2][0] * measured_rgb.x + correction_matrix[2][1] * measured_rgb.y + correction_matrix[2][2] * measured_rgb.z;
5503
5504
5505 // Clamp corrected values to [0,1] - DISABLED FOR TESTING
5506 // corrected_r = std::max(0.0f, std::min(1.0f, corrected_r));
5507 // corrected_g = std::max(0.0f, std::min(1.0f, corrected_g));
5508 // corrected_b = std::max(0.0f, std::min(1.0f, corrected_b));
5509
5510 helios::vec3 corrected_rgb = make_vec3(corrected_r, corrected_g, corrected_b);
5511
5512 // Convert corrected RGB to Lab
5513 CameraCalibration::LabColor corrected_lab = calibration.rgbToLab(corrected_rgb);
5514 CameraCalibration::LabColor reference_lab = reference_lab_values[i];
5515
5516 // Calculate Delta E between corrected and reference
5517 // Use ΔE2000 for better perceptual color difference assessment
5518 double delta_E = calibration.deltaE2000(corrected_lab, reference_lab);
5519
5520 std::cout << std::setw(5) << i << " | " << std::fixed << std::setprecision(3) << "(" << std::setw(5) << corrected_rgb.x << "," << std::setw(5) << corrected_rgb.y << "," << std::setw(5) << corrected_rgb.z << ") | ";
5521
5522 helios::vec3 ref_rgb = calibration.labToRgb(reference_lab);
5523 std::cout << "(" << std::setw(5) << ref_rgb.x << "," << std::setw(5) << ref_rgb.y << "," << std::setw(5) << ref_rgb.z << ") | " << std::setw(7) << delta_E << std::endl;
5524
5525 total_delta_e += delta_E;
5526 valid_patches++;
5527 }
5528 }
5529
5530 // Overall statistics
5531 double mean_delta_e = total_delta_e / valid_patches;
5532 std::cout << "\n========== OVERALL CALIBRATION QUALITY ==========" << std::endl;
5533 std::cout << "Mean Delta E: " << std::fixed << std::setprecision(2) << mean_delta_e << std::endl;
5534
5535 std::cout << "======================================================\n" << std::endl;
5536 }
5537
5538 // Step 7: Apply correction to entire image with same pixel ordering as writeCameraImage
5539 std::vector<helios::RGBcolor> corrected_pixels;
5540 corrected_pixels.resize(red_data.size());
5541
5542 // Apply correction using the same pixel transformation as writeCameraImage uses
5543 for (int j = 0; j < camera_resolution.y; j++) {
5544 for (int i = 0; i < camera_resolution.x; i++) {
5545 // Get pixel from source data (no flip)
5546 int source_index = j * camera_resolution.x + i;
5547 float r = red_data[source_index];
5548 float g = green_data[source_index];
5549 float b = blue_data[source_index];
5550
5551 // Apply correction matrix (with optional affine terms)
5552 float corrected_r = correction_matrix[0][0] * r + correction_matrix[0][1] * g + correction_matrix[0][2] * b;
5553 float corrected_g = correction_matrix[1][0] * r + correction_matrix[1][1] * g + correction_matrix[1][2] * b;
5554 float corrected_b = correction_matrix[2][0] * r + correction_matrix[2][1] * g + correction_matrix[2][2] * b;
5555
5556
5557 // Clamp values to [0,1] - DISABLED FOR TESTING
5558 // corrected_r = std::max(0.0f, std::min(1.0f, corrected_r));
5559 // corrected_g = std::max(0.0f, std::min(1.0f, corrected_g));
5560 // corrected_b = std::max(0.0f, std::min(1.0f, corrected_b));
5561
5562 // Apply same coordinate transformation as writeCameraImage
5563 uint ii = camera_resolution.x - i - 1; // Horizontal flip
5564 uint jj = camera_resolution.y - j - 1; // Vertical flip
5565 uint dest_index = jj * camera_resolution.x + ii;
5566
5567 corrected_pixels[dest_index] = make_RGBcolor(corrected_r, corrected_g, corrected_b);
5568 }
5569 }
5570
5571 // Step 8: Write corrected image using writeJPEG
5572 std::string output_path = output_file_path;
5573 if (output_path.empty()) {
5574 output_path = "auto_calibrated_" + camera_label + ".jpg";
5575 }
5576
5577 try {
5578 helios::writeJPEG(output_path, camera_resolution.x, camera_resolution.y, corrected_pixels);
5579 std::cout << "Wrote corrected image to: " << output_path << std::endl;
5580 } catch (const std::exception &e) {
5581 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Failed to write corrected image. " + std::string(e.what()));
5582 }
5583
5584 // Export CCM to XML file if requested
5585 if (!ccm_export_file_path.empty()) {
5586 try {
5587 // Calculate quality metrics for export (even if not printed)
5588 double total_delta_e = 0.0;
5589 int valid_patches = 0;
5590
5591 for (size_t i = 0; i < std::min(measured_rgb_values.size(), reference_lab_values.size()); i++) {
5592 if (measured_rgb_values[i].magnitude() > 0) {
5593 // Apply color correction to measured RGB values
5594 helios::vec3 measured_rgb = measured_rgb_values[i];
5595 float corrected_r = correction_matrix[0][0] * measured_rgb.x + correction_matrix[0][1] * measured_rgb.y + correction_matrix[0][2] * measured_rgb.z;
5596 float corrected_g = correction_matrix[1][0] * measured_rgb.x + correction_matrix[1][1] * measured_rgb.y + correction_matrix[1][2] * measured_rgb.z;
5597 float corrected_b = correction_matrix[2][0] * measured_rgb.x + correction_matrix[2][1] * measured_rgb.y + correction_matrix[2][2] * measured_rgb.z;
5598
5599 helios::vec3 corrected_rgb = make_vec3(corrected_r, corrected_g, corrected_b);
5600
5601 // Convert corrected RGB to Lab
5602 CameraCalibration::LabColor corrected_lab = calibration.rgbToLab(corrected_rgb);
5603 CameraCalibration::LabColor reference_lab = reference_lab_values[i];
5604
5605 // Calculate Delta E between corrected and reference
5606 double delta_E = calibration.deltaE2000(corrected_lab, reference_lab);
5607 total_delta_e += delta_E;
5608 valid_patches++;
5609 }
5610 }
5611
5612 double mean_delta_e = (valid_patches > 0) ? (total_delta_e / valid_patches) : -1.0;
5613
5614 // Export CCM to XML file
5615 exportColorCorrectionMatrixXML(ccm_export_file_path, camera_label, correction_matrix, output_path, colorboard_type, (float) mean_delta_e);
5616
5617 std::cout << "Exported color correction matrix to: " << ccm_export_file_path << std::endl;
5618 } catch (const std::exception &e) {
5619 helios_runtime_error("ERROR (RadiationModel::autoCalibrateCameraImage): Failed to export CCM to XML. " + std::string(e.what()));
5620 }
5621 }
5622
5623 return output_path;
5624}
5625
5626void RadiationModel::applyCameraColorCorrectionMatrix(const std::string &camera_label, const std::string &red_band_label, const std::string &green_band_label, const std::string &blue_band_label, const std::string &ccm_file_path) {
5627
5628 // Step 1: Validate camera exists
5629 if (cameras.find(camera_label) == cameras.end()) {
5630 helios_runtime_error("ERROR (RadiationModel::applyCameraColorCorrectionMatrix): Camera '" + camera_label + "' does not exist. Make sure the camera was added to the radiation model.");
5631 }
5632
5633 // Step 2: Validate band labels exist in camera
5634 auto &camera_bands = cameras.at(camera_label).band_labels;
5635 if (std::find(camera_bands.begin(), camera_bands.end(), red_band_label) == camera_bands.end()) {
5636 helios_runtime_error("ERROR (RadiationModel::applyCameraColorCorrectionMatrix): Red band '" + red_band_label + "' not found in camera '" + camera_label + "'.");
5637 }
5638 if (std::find(camera_bands.begin(), camera_bands.end(), green_band_label) == camera_bands.end()) {
5639 helios_runtime_error("ERROR (RadiationModel::applyCameraColorCorrectionMatrix): Green band '" + green_band_label + "' not found in camera '" + camera_label + "'.");
5640 }
5641 if (std::find(camera_bands.begin(), camera_bands.end(), blue_band_label) == camera_bands.end()) {
5642 helios_runtime_error("ERROR (RadiationModel::applyCameraColorCorrectionMatrix): Blue band '" + blue_band_label + "' not found in camera '" + camera_label + "'.");
5643 }
5644
5645 // Step 3: Load color correction matrix from XML file
5646 std::string loaded_camera_label;
5647 std::vector<std::vector<float>> correction_matrix;
5648 try {
5649 correction_matrix = loadColorCorrectionMatrixXML(ccm_file_path, loaded_camera_label);
5650 } catch (const std::exception &e) {
5651 helios_runtime_error("ERROR (RadiationModel::applyCameraColorCorrectionMatrix): Failed to load CCM from XML file. " + std::string(e.what()));
5652 }
5653
5654 // Step 4: Validate matrix dimensions (should be 3x3 or 4x3)
5655 if (correction_matrix.size() != 3) {
5656 helios_runtime_error("ERROR (RadiationModel::applyCameraColorCorrectionMatrix): Invalid matrix dimensions. Expected 3x3 or 4x3 matrix, got " + std::to_string(correction_matrix.size()) + " rows.");
5657 }
5658
5659 bool is_3x3 = (correction_matrix[0].size() == 3);
5660 bool is_4x3 = (correction_matrix[0].size() == 4);
5661
5662 if (!is_3x3 && !is_4x3) {
5663 helios_runtime_error("ERROR (RadiationModel::applyCameraColorCorrectionMatrix): Invalid matrix dimensions. Expected 3x3 or 4x3 matrix, got " + std::to_string(correction_matrix.size()) + "x" + std::to_string(correction_matrix[0].size()) +
5664 " matrix.");
5665 }
5666
5667 // Step 5: Get camera data (same approach as applyImageProcessingPipeline)
5668 std::vector<float> &red_data = cameras.at(camera_label).pixel_data.at(red_band_label);
5669 std::vector<float> &green_data = cameras.at(camera_label).pixel_data.at(green_band_label);
5670 std::vector<float> &blue_data = cameras.at(camera_label).pixel_data.at(blue_band_label);
5671
5672 int2 camera_resolution = cameras.at(camera_label).resolution;
5673 size_t pixel_count = red_data.size();
5674
5675 // Step 6: Apply color correction matrix to all pixels in-place
5676 for (size_t i = 0; i < pixel_count; i++) {
5677 float r = red_data[i];
5678 float g = green_data[i];
5679 float b = blue_data[i];
5680
5681 // Apply color correction matrix (3x3 or 4x3)
5682 if (is_3x3) {
5683 // Standard 3x3 matrix transformation
5684 red_data[i] = correction_matrix[0][0] * r + correction_matrix[0][1] * g + correction_matrix[0][2] * b;
5685 green_data[i] = correction_matrix[1][0] * r + correction_matrix[1][1] * g + correction_matrix[1][2] * b;
5686 blue_data[i] = correction_matrix[2][0] * r + correction_matrix[2][1] * g + correction_matrix[2][2] * b;
5687 } else {
5688 // 4x3 matrix transformation with affine offset
5689 red_data[i] = correction_matrix[0][0] * r + correction_matrix[0][1] * g + correction_matrix[0][2] * b + correction_matrix[0][3];
5690 green_data[i] = correction_matrix[1][0] * r + correction_matrix[1][1] * g + correction_matrix[1][2] * b + correction_matrix[1][3];
5691 blue_data[i] = correction_matrix[2][0] * r + correction_matrix[2][1] * g + correction_matrix[2][2] * b + correction_matrix[2][3];
5692 }
5693 }
5694
5695 if (message_flag) {
5696 std::cout << "Applied color correction matrix from '" << ccm_file_path << "' to camera '" << camera_label << "'" << std::endl;
5697 std::cout << "Matrix type: " << (is_3x3 ? "3x3" : "4x3") << ", processed " << pixel_count << " pixels" << std::endl;
5698 }
5699}
5700
5701std::vector<float> RadiationModel::getCameraPixelData(const std::string &camera_label, const std::string &band_label) {
5702 if (cameras.find(camera_label) == cameras.end()) {
5703 helios_runtime_error("ERROR (RadiationModel::getCameraPixelData): Camera '" + camera_label + "' does not exist.");
5704 }
5705
5706 auto &camera_pixel_data = cameras.at(camera_label).pixel_data;
5707 if (camera_pixel_data.find(band_label) == camera_pixel_data.end()) {
5708 helios_runtime_error("ERROR (RadiationModel::getCameraPixelData): Band '" + band_label + "' does not exist in camera '" + camera_label + "'.");
5709 }
5710
5711 return camera_pixel_data.at(band_label);
5712}
5713
5714void RadiationModel::setCameraPixelData(const std::string &camera_label, const std::string &band_label, const std::vector<float> &pixel_data) {
5715 if (cameras.find(camera_label) == cameras.end()) {
5716 helios_runtime_error("ERROR (RadiationModel::setCameraPixelData): Camera '" + camera_label + "' does not exist.");
5717 }
5718
5719 cameras.at(camera_label).pixel_data[band_label] = pixel_data;
5720}
5721
5722void sutilHandleError(RTcontext context, RTresult code, const char *file, int line) {
5723 const char *message;
5724 char s[2048];
5725 rtContextGetErrorString(context, code, &message);
5726 sprintf(s, "%s\n(%s:%d)", message, file, line);
5727 sutilReportError(s);
5728 exit(1);
5729}
5730
5731void sutilReportError(const char *message) {
5732 fprintf(stderr, "OptiX Error: %s\n", message);
5733#if defined(_WIN32) && defined(RELEASE_PUBLIC)
5734 {
5735 char s[2048];
5736 sprintf(s, "OptiX Error: %s", message);
5737 MessageBox(0, s, "OptiX Error", MB_OK | MB_ICONWARNING | MB_SYSTEMMODAL);
5738 }
5739#endif
5740}