• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NREL / SolTrace / 20379147445

19 Dec 2025 06:34PM UTC coverage: 89.725% (+0.5%) from 89.184%
20379147445

push

github

web-flow
Implement multi-threading in NativeRunner (#91)

* Initial implementation of multi-threading in NativeRunner

* Fix seeding issue; address copilot comments; implement logging

* Add missing header

* Run performance tests only for release build; adjust parameters for cancel multithread test

* Fix clear and setup call ordering in RayData

* Update profile flags; fix printf compiler warning

* Still trying to fix coverage

* Remove multi-threading tests from coverage; remove performance tests for debug builds

* Add missing header

* Increase test coverage; address copilot comments

* Make number of rays traced consistent across number of threads

* Increase time for native runner validation test

430 of 471 new or added lines in 11 files covered. (91.3%)

2 existing lines in 2 files now uncovered.

6104 of 6803 relevant lines covered (89.73%)

7499675.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.76
/coretrace/simulation_runner/native_runner/trace.cpp
1

2
/*******************************************************************************************************
3
 *  Copyright 2018 Alliance for Sustainable Energy, LLC
4
 *
5
 *  NOTICE: This software was developed at least in part by Alliance for Sustainable Energy, LLC
6
 *  ("Alliance") under Contract No. DE-AC36-08GO28308 with the U.S. Department of Energy and the U.S.
7
 *  The Government retains for itself and others acting on its behalf a nonexclusive, paid-up,
8
 *  irrevocable worldwide license in the software to reproduce, prepare derivative works, distribute
9
 *  copies to the public, perform publicly and display publicly, and to permit others to do so.
10
 *
11
 *  Redistribution and use in source and binary forms, with or without modification, are permitted
12
 *  provided that the following conditions are met:
13
 *
14
 *  1. Redistributions of source code must retain the above copyright notice, the above government
15
 *  rights notice, this list of conditions and the following disclaimer.
16
 *
17
 *  2. Redistributions in binary form must reproduce the above copyright notice, the above government
18
 *  rights notice, this list of conditions and the following disclaimer in the documentation and/or
19
 *  other materials provided with the distribution.
20
 *
21
 *  3. The entire corresponding source code of any redistribution, with or without modification, by a
22
 *  research entity, including but not limited to any contracting manager/operator of a United States
23
 *  National Laboratory, any institution of higher learning, and any non-profit organization, must be
24
 *  made publicly available under this license for as long as the redistribution is made available by
25
 *  the research entity.
26
 *
27
 *  4. Redistribution of this software, without modification, must refer to the software by the same
28
 *  designation. Redistribution of a modified version of this software (i) may not refer to the modified
29
 *  version by the same designation, or by any confusingly similar designation, and (ii) must refer to
30
 *  the underlying software originally provided by Alliance as "SolTrace". Except to comply with the
31
 *  foregoing, the term "SolTrace", or any confusingly similar designation may not be used to refer to
32
 *  any modified version of this software or any modified version of the underlying software originally
33
 *  provided by Alliance without the prior written consent of Alliance.
34
 *
35
 *  5. The name of the copyright holder, contributors, the United States Government, the United States
36
 *  Department of Energy, or any of their employees may not be used to endorse or promote products
37
 *  derived from this software without specific prior written permission.
38
 *
39
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
40
 *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
41
 *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER,
42
 *  CONTRIBUTORS, UNITED STATES GOVERNMENT OR UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR
43
 *  EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44
 *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
46
 *  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
47
 *  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
 *******************************************************************************************************/
49

50
#include "trace.hpp"
51

52
// Standard library headers
53
#include <algorithm>
54
#include <cmath>
55
#include <limits>
56
#include <sstream>
57
#include <vector>
58

59
// SimulationData headers
60
#include <constants.hpp>
61
#include <matvec.hpp>
62
#include <simulation_data_export.hpp>
63

64
// SimulationRunner header
65
#include <simulation_runner.hpp>
66

67
// NativeRunner headers
68
#include "find_element_hit.hpp"
69
#include "generate_ray.hpp"
70
#include "native_runner_types.hpp"
71
#include "process_interaction.hpp"
72
#include "pt_optimizations.hpp"
73
#include "sun_to_primary_stage.hpp"
74
#include "thread_manager.hpp"
75
#include "treemesh.hpp"
76

77
namespace SolTrace::NativeRunner
78
{
79

80
        using SolTrace::Result::RayEvent;
81
        using SolTrace::Runner::RunnerStatus;
82

83
        // Trace method
84
        RunnerStatus trace_native(
25✔
85
                thread_manager_ptr manager,
86
                TSystem *System,
87
                const std::vector<unsigned int> &seeds,
88
                uint_fast64_t nthreads,
89
                uint_fast64_t NumberOfRays,
90
                uint_fast64_t MaxNumberOfRays,
91
                bool IncludeSunShape,
92
                bool IncludeErrors,
93
                bool AsPowerTower)
94
        {
95
                // Initialize Sun
96
                Vector3d PosSunStage;
25✔
97
                if (!SunToPrimaryStage(manager,
25✔
98
                                                           System,
99
                                                           System->StageList[0].get(),
25✔
100
                                                           &System->Sun,
101
                                                           PosSunStage.data))
NEW
102
                        return RunnerStatus::ERROR;
×
103

104
                // Determine if PT optimizations should be applied
105
                bool PT_override = false;
25✔
106
                if (System->StageList.size() > 0 &&
57✔
107
                        (System->StageList[0]->ElementList.size() < 10 || System->StageList.size() == 1))
32✔
108
                {
109
                        PT_override = true;
20✔
110
                }
111

112
                // Calculate hash tree for reflection to receiver plane(polar coordinates).
113
                st_hash_tree sun_hash;
25✔
114
                st_hash_tree rec_hash;
25✔
115
                // double reccm_helio[3]; // receiver centroid in heliostat field coordinates
116
                Vector3d reccm_helio;
25✔
117
                if (!PT_override)
25✔
118
                {
119
                        SetupPTOptimizations(System, AsPowerTower, sun_hash,
5✔
120
                                                                 rec_hash, reccm_helio.data);
121
                }
122

123
                // Bundle many args into a struct because the compiler was
124
                // having trouble with all the arguments...
125
                ThreadInfo my_info;
25✔
126
                my_info.manager = manager;
25✔
127
                my_info.System = System;
25✔
128
                // my_info.NumberOfRays = NumberOfRays / nthreads;
129
                uint_fast64_t rem = NumberOfRays % nthreads;
25✔
130
                uint_fast64_t nrays_per_thread = NumberOfRays / nthreads;
25✔
131

132
                my_info.MaxNumberOfRays = MaxNumberOfRays / nthreads + 1;
25✔
133
                my_info.IncludeSunShape = IncludeSunShape;
25✔
134
                my_info.IncludeErrors = IncludeErrors;
25✔
135
                my_info.AsPowerTower = AsPowerTower;
25✔
136
                my_info.PosSunStage = PosSunStage;
25✔
137
                my_info.sun_hash = &sun_hash;
25✔
138
                my_info.rec_hash = &rec_hash;
25✔
139
                my_info.reccm_helio = reccm_helio;
25✔
140

141
                System->RayData.SetUp(nthreads, NumberOfRays);
25✔
142
                System->SunRayCount = 0;
25✔
143

144
                for (unsigned int k = 0; k < nthreads; ++k)
50✔
145
                {
146
                        my_info.NumberOfRays = (k < rem
50✔
147
                                                                                ? nrays_per_thread + 1
25✔
148
                                                                                : nrays_per_thread);
149

150
                        ThreadManager::future my_future = std::async(
151
                                std::launch::async,
152
                                trace_single_compact,
153
                                k,
154
                                seeds[k],
25✔
155
                                my_info);
25✔
156

157
                        manager->manage(k, std::move(my_future));
25✔
158
                }
25✔
159

160
                return manager->monitor_until_completion();
25✔
161
        }
25✔
162

163
        RunnerStatus trace_single_thread(
25✔
164
                unsigned thread_id,
165
                thread_manager_ptr manager,
166
                TSystem *System,
167
                unsigned int seed,
168
                uint_fast64_t NumberOfRays,
169
                uint_fast64_t MaxNumberOfRays,
170
                bool IncludeSunShape,
171
                bool IncludeErrors,
172
                bool AsPowerTower,
173
                const Vector3d &PosSunStage,
174
                st_hash_tree *sun_hash,
175
                st_hash_tree *rec_hash,
176
                const Vector3d &reccm_helio)
177
        {
178
                // Initialize variables
179
                MTRand myrng(seed);
25✔
180

181
                // std::stringstream ss;
182
                // ss << "Thread " << thread_id
183
                //    << " tracing " << NumberOfRays << " rays"
184
                //    << std::endl;
185
                // std::cout << ss.str();
186

187
                // Determine if PT optimizations should be applied
188
                bool PT_override = false;
25✔
189
                if (System->StageList.size() > 0 &&
57✔
190
                        (System->StageList[0]->ElementList.size() < 10 ||
32✔
191
                         System->StageList.size() == 1))
7✔
192
                {
193
                        PT_override = true;
20✔
194
                }
195

196
                uint_fast64_t update_rate = std::min(
25✔
197
                        std::max(static_cast<uint_fast64_t>(1), NumberOfRays / 10),
50✔
198
                        static_cast<uint_fast64_t>(1000));
25✔
199
                uint_fast64_t update_count = 0;
25✔
200
                double total_work = System->StageList.size() * NumberOfRays;
25✔
201

202
                // Initialize Internal State Variables
203
                uint_fast64_t RayNumber = 1; // Ray Number of current ray
25✔
204
                bool PreviousStageHasRays = false;
25✔
205
                uint_fast64_t LastRayNumberInPreviousStage = NumberOfRays;
25✔
206

207
                // Define IncomingRays
208
                std::vector<GlobalRay_refactored> IncomingRays; // Vector of rays from previous stage, going into next stage
25✔
209
                IncomingRays.resize(NumberOfRays);
25✔
210

211
                // Start the clock
212
                // clock_t startTime = clock();
213
                // int rays_per_callback_estimate = 50;
214
                // uint_fast64_t RaysTracedTotal = 0;
215

216
                // Initialize stage variables
217
                uint_fast64_t StageDataArrayIndex = 0;
25✔
218
                uint_fast64_t PreviousStageDataArrayIndex = 0;
25✔
219
                uint_fast64_t n_rays_active = NumberOfRays;
25✔
220
                uint_fast64_t sun_ray_count_local = 0;
25✔
221

222
                // Loop through stages
223
                for (uint_fast64_t i = 0; i < System->StageList.size(); i++)
65✔
224
                {
225
                        // std::cout << "Processing stage " << i << "..." << std::endl;
226
                        // Check if previous stage has rays
227
                        bool StageHasRays = true;
41✔
228
                        if (i > 0 && PreviousStageHasRays == false)
41✔
229
                        {
230
                                StageHasRays = false;
×
231
                        }
232

233
                        // Get Current Stage
234
                        tstage_ptr Stage = System->StageList[i];
41✔
235

236
                        // Initialize stage variables
237
                        StageDataArrayIndex = 0;
41✔
238
                        PreviousStageDataArrayIndex = 0;
41✔
239

240
                        // Loop through rays
241
                        while (StageHasRays)
11,738,818✔
242
                        {
243
                                // Initialize Global Coordinates
244
                                double PosRayGlob[3] = {0.0, 0.0, 0.0};
11,738,818✔
245
                                double CosRayGlob[3] = {0.0, 0.0, 0.0};
11,738,818✔
246

247
                                // Initialize Stage Coordinates
248
                                double PosRayStage[3] = {0.0, 0.0, 0.0};
11,738,818✔
249
                                double CosRayStage[3] = {0.0, 0.0, 0.0};
11,738,818✔
250

251
                                // Initialize PT Optimization variables
252
                                bool has_elements = true;
11,738,818✔
253
                                std::vector<void *> sunint_elements;
11,738,818✔
254

255
                                // Get Ray
256
                                if (i == 0)
11,738,818✔
257
                                {
258
                                        // TODO: This function seems to ignore the MaxNumberOfRays
259
                                        // argument. Should fix that.
260

261
                                        // Make ray (if first stage)
262
                                        double PosRaySun[3];
263
                                        GenerateRay(myrng, PosSunStage.data, Stage->Origin,
11,017,808✔
264
                                                                Stage->RLocToRef, &System->Sun,
11,017,808✔
265
                                                                PosRayGlob, CosRayGlob, PosRaySun);
266
                                        sun_ray_count_local++;
11,017,808✔
267

268
                                        // If using PT optimizations, check if stage has elements
269
                                        // that could interact with ray
270
                                        if (!PT_override)
11,017,808✔
271
                                        {
272
                                                has_elements =
470,016✔
273
                                                        sun_hash->get_all_data_at_loc(sunint_elements,
470,016✔
274
                                                                                                                  PosRaySun[0],
275
                                                                                                                  PosRaySun[1]);
276
                                        }
277
                                }
278
                                else
279
                                {
280
                                        // Get ray from previous stage
281
                                        RayNumber = IncomingRays[StageDataArrayIndex].Num;
721,010✔
282
                                        CopyVec3(PosRayGlob, IncomingRays[StageDataArrayIndex].Pos);
721,010✔
283
                                        CopyVec3(CosRayGlob, IncomingRays[StageDataArrayIndex].Cos);
721,010✔
284
                                        StageDataArrayIndex++;
721,010✔
285
                                }
286

287
                                // transform the global incoming ray to local stage coordinates
288
                                TransformToLocal(PosRayGlob, CosRayGlob,
11,738,818✔
289
                                                                 Stage->Origin, Stage->RRefToLoc,
11,738,818✔
290
                                                                 PosRayStage, CosRayStage);
291

292
                                // Initialize internal variables for ray intersection tracing
293
                                bool RayInStage = true;
11,738,818✔
294
                                bool in_multi_hit_loop = false;
11,738,818✔
295
                                double LastPosRaySurfElement[3] = {0.0, 0.0, 0.0};
11,738,818✔
296
                                double LastCosRaySurfElement[3] = {0.0, 0.0, 0.0};
11,738,818✔
297
                                double LastPosRaySurfStage[3] = {0.0, 0.0, 0.0};
11,738,818✔
298
                                double LastCosRaySurfStage[3] = {0.0, 0.0, 0.0};
11,738,818✔
299
                                double LastDFXYZ[3] = {0.0, 0.0, 0.0};
11,738,818✔
300
                                uint_fast64_t LastElementNumber = 0;
11,738,818✔
301
                                uint_fast64_t LastRayNumber = 0;
11,738,818✔
302
                                int ErrorFlag;
303
                                int LastHitBackSide;
304
                                bool StageHit;
305
                                int MultipleHitCount = 0;
11,738,818✔
306
                                double PosRayOutElement[3] = {0.0, 0.0, 0.0};
11,738,818✔
307
                                double CosRayOutElement[3] = {0.0, 0.0, 0.0};
11,738,818✔
308

309
                                // Start Loop to trace ray until it leaves stage
310
                                bool RayIsAbsorbed = false;
11,738,818✔
311
                                while (RayInStage)
12,786,743✔
312
                                {
313
                                        // Set number of elements to search through
314
                                        uint_fast64_t nintelements = 0;
12,786,743✔
315
                                        std::vector<void *> reflint_elements;
12,786,743✔
316
                                        if (!PT_override) // if using opt AND first stage
12,786,743✔
317
                                        {
318
                                                nintelements = GetPTElements(AsPowerTower, Stage, i,
617,966✔
319
                                                                                                         in_multi_hit_loop, PosRayStage,
320
                                                                                                         reccm_helio.data, rec_hash,
617,966✔
321
                                                                                                         sunint_elements,
322
                                                                                                         reflint_elements, has_elements);
323
                                        }
324
                                        else
325
                                        {
326
                                                nintelements = Stage->ElementList.size();
12,168,777✔
327
                                        }
328

329
                                        // Find the element the ray hits
330
                                        FindElementHit(i, Stage, PT_override, AsPowerTower,
12,786,743✔
331
                                                                   nintelements, sunint_elements,
332
                                                                   reflint_elements,
333
                                                                   RayNumber, in_multi_hit_loop,
334
                                                                   PosRayStage, CosRayStage,
335
                                                                   LastPosRaySurfElement,
336
                                                                   LastCosRaySurfElement,
337
                                                                   LastDFXYZ,
338
                                                                   LastElementNumber, LastRayNumber,
339
                                                                   LastPosRaySurfStage, LastCosRaySurfStage,
340
                                                                   ErrorFlag, LastHitBackSide, StageHit);
341

342
                                        // Breakout if ray left stage
343
                                        if (!StageHit)
12,786,743✔
344
                                        {
345
                                                RayInStage = false;
10,924,304✔
346
                                                break;
10,924,304✔
347
                                        }
348

349
                                        // Increment MultipleHitCount
350
                                        MultipleHitCount++;
1,862,439✔
351

352
                                        if (i == 0 && MultipleHitCount == 1)
1,862,439✔
353
                                        {
354
                                                auto r = System->RayData.Append(thread_id,
355
                                                                                                                PosRayGlob,
356
                                                                                                                CosRayGlob,
357
                                                                                                                ELEMENT_NULL,
358
                                                                                                                i + 1,
890,718✔
359
                                                                                                                LastRayNumber,
360
                                                                                                                RayEvent::CREATE);
890,718✔
361
                                                if (r == nullptr)
890,718✔
362
                                                {
NEW
363
                                                        std::stringstream ss;
×
NEW
364
                                                        ss << "Thread " << thread_id
×
NEW
365
                                                           << " failed to record ray data.\n";
×
NEW
366
                                                        manager->error_log(ss.str());
×
NEW
367
                                                }
×
368
                                        }
890,718✔
369

370
                                        // Get optics and check for absorption
371
                                        const OpticalProperties *optics = 0;
1,862,439✔
372
                                        RayEvent rev = RayEvent::VIRTUAL;
1,862,439✔
373
                                        if (Stage->Virtual)
1,862,439✔
374
                                        {
375
                                                // If stage is virtual, there is no interaction
376
                                                CopyVec3(PosRayOutElement, LastPosRaySurfElement);
×
377
                                                CopyVec3(CosRayOutElement, LastCosRaySurfElement);
×
378
                                        }
379
                                        else
380
                                        {
381
                                                // trace through the interaction
382
                                                telement_ptr optelm = Stage->ElementList[LastElementNumber - 1];
1,862,439✔
383

384
                                                if (LastHitBackSide)
1,862,439✔
385
                                                        optics = &optelm->Optics.Back;
208,881✔
386
                                                else
387
                                                        optics = &optelm->Optics.Front;
1,653,558✔
388

389
                                                double TestValue;
390
                                                double UnitLastDFXYZ[3] = {0.0, 0.0, 0.0};
1,862,439✔
391
                                                double IncidentAngle = 0;
1,862,439✔
392
                                                // switch (optelm->InteractionType)
393
                                                switch (optics->my_type)
1,862,439✔
394
                                                {
395
                                                case InteractionType::REFRACTION: // refraction
194,853✔
396
                                                        // TODO: Implement transmissivity table?
397
                                                        // if (optics->UseTransmissivityTable)
398
                                                        // {
399
                                                        //         int npoints = optics->TransmissivityTable.size();
400
                                                        //         int m = 0;
401

402
                                                        //         UnitLastDFXYZ[0] = -LastDFXYZ[0] / sqrt(DOT(LastDFXYZ, LastDFXYZ));
403
                                                        //         UnitLastDFXYZ[1] = -LastDFXYZ[1] / sqrt(DOT(LastDFXYZ, LastDFXYZ));
404
                                                        //         UnitLastDFXYZ[2] = -LastDFXYZ[2] / sqrt(DOT(LastDFXYZ, LastDFXYZ));
405
                                                        //         IncidentAngle = acos(DOT(LastCosRaySurfElement, UnitLastDFXYZ)) * 1000.; //[mrad]
406
                                                        //         if (IncidentAngle >= optics->TransmissivityTable[npoints - 1].angle)
407
                                                        //         {
408
                                                        //                 TestValue = optics->TransmissivityTable[npoints - 1].trans;
409
                                                        //         }
410
                                                        //         else
411
                                                        //         {
412
                                                        //                 while (optics->TransmissivityTable[m].angle < IncidentAngle)
413
                                                        //                         m++;
414

415
                                                        //                 if (m == 0)
416
                                                        //                         TestValue = optics->TransmissivityTable[m].trans;
417
                                                        //                 else
418
                                                        //                         TestValue = (optics->TransmissivityTable[m].trans + optics->TransmissivityTable[m - 1].trans) / 2.0;
419
                                                        //         }
420
                                                        // }
421
                                                        // else
422
                                                        //         TestValue = optics->Transmissivity;
423
                                                        TestValue = optics->transmitivity;
194,853✔
424
                                                        rev = RayEvent::TRANSMIT;
194,853✔
425
                                                        break;
194,853✔
426
                                                case InteractionType::REFLECTION: // reflection
1,667,586✔
427
                                                        // TODO: Implement reflectivity table?
428
                                                        // if (optics->UseReflectivityTable)
429
                                                        // {
430
                                                        //         int npoints = optics->ReflectivityTable.size();
431
                                                        //         int m = 0;
432
                                                        //         UnitLastDFXYZ[0] = -LastDFXYZ[0] / sqrt(DOT(LastDFXYZ, LastDFXYZ));
433
                                                        //         UnitLastDFXYZ[1] = -LastDFXYZ[1] / sqrt(DOT(LastDFXYZ, LastDFXYZ));
434
                                                        //         UnitLastDFXYZ[2] = -LastDFXYZ[2] / sqrt(DOT(LastDFXYZ, LastDFXYZ));
435
                                                        //         IncidentAngle = acos(DOT(LastCosRaySurfElement, UnitLastDFXYZ)) * 1000.; //[mrad]
436
                                                        //         if (IncidentAngle >= optics->ReflectivityTable[npoints - 1].angle)
437
                                                        //         {
438
                                                        //                 TestValue = optics->ReflectivityTable[npoints - 1].refl;
439
                                                        //         }
440
                                                        //         else
441
                                                        //         {
442
                                                        //                 while (optics->ReflectivityTable[m].angle < IncidentAngle)
443
                                                        //                         m++;
444

445
                                                        //                 if (m == 0)
446
                                                        //                         TestValue = optics->ReflectivityTable[m].refl;
447
                                                        //                 else
448
                                                        //                         TestValue = (optics->ReflectivityTable[m].refl + optics->ReflectivityTable[m - 1].refl) / 2.0;
449
                                                        //         }
450
                                                        // }
451
                                                        // else
452
                                                        //         TestValue = optics->Reflectivity;
453
                                                        TestValue = optics->reflectivity;
1,667,586✔
454
                                                        rev = RayEvent::REFLECT;
1,667,586✔
455
                                                        break;
1,667,586✔
456
                                                default:
×
NEW
457
                                                        std::stringstream ss;
×
458
                                                        ss << "Bad optical interaction."
NEW
459
                                                           << " Type: " << static_cast<int>(optics->my_type)
×
NEW
460
                                                           << " Stage: " << i
×
NEW
461
                                                           << " Thread: " << thread_id
×
NEW
462
                                                           << "\n";
×
NEW
463
                                                        manager->error_log(ss.str());
×
UNCOV
464
                                                        return RunnerStatus::ERROR;
×
465
                                                }
466

467
                                                // Apply MonteCarlo probability of absorption. Limited
468
                                                // for now, but can make more complex later on if desired
469
                                                // if (TestValue <= myrng())
470
                                                double flip = myrng();
1,862,439✔
471
                                                if (TestValue <= flip)
1,862,439✔
472
                                                {
473
                                                        // ray was fully absorbed
474
                                                        RayIsAbsorbed = true;
802,306✔
475
                                                        break;
802,306✔
476
                                                }
477
                                        }
1,862,439✔
478

479
                                        // Process Interaction
480
                                        int_fast64_t k = LastElementNumber - 1;
1,060,133✔
481
                                        ProcessInteraction(System,
1,060,133✔
482
                                                                           myrng,
483
                                                                           IncludeSunShape,
484
                                                                           optics,
485
                                                                           IncludeErrors,
486
                                                                           i, Stage, // k,
487
                                                                           MultipleHitCount,
488
                                                                           LastDFXYZ,
489
                                                                           LastCosRaySurfElement,
490
                                                                           ErrorFlag,
491
                                                                           CosRayOutElement,
492
                                                                           LastPosRaySurfElement,
493
                                                                           PosRayOutElement);
494

495
                                        // Transform ray back to stage coordinate system
496
                                        TransformToReference(PosRayOutElement,
1,060,133✔
497
                                                                                 CosRayOutElement,
498
                                                                                 Stage->ElementList[k]->Origin,
1,060,133✔
499
                                                                                 Stage->ElementList[k]->RLocToRef,
1,060,133✔
500
                                                                                 PosRayStage,
501
                                                                                 CosRayStage);
502
                                        TransformToReference(PosRayStage,
1,060,133✔
503
                                                                                 CosRayStage,
504
                                                                                 Stage->Origin,
1,060,133✔
505
                                                                                 Stage->RLocToRef,
1,060,133✔
506
                                                                                 PosRayGlob,
507
                                                                                 CosRayGlob);
508

509
                                        System->RayData.Append(thread_id,
1,060,133✔
510
                                                                                   PosRayGlob,
511
                                                                                   CosRayGlob,
512
                                                                                   LastElementNumber,
513
                                                                                   i + 1,
1,060,133✔
514
                                                                                   LastRayNumber,
515
                                                                                   rev);
516

517
                                        // Break out if multiple hits are not allowed
518
                                        if (!Stage->MultiHitsPerRay)
1,060,133✔
519
                                        {
520
                                                StageHit = false;
12,208✔
521
                                                break;
12,208✔
522
                                        }
523
                                        else
524
                                        {
525
                                                in_multi_hit_loop = true;
1,047,925✔
526
                                        }
527
                                }
12,786,743✔
528

529
                                ++update_count;
11,738,818✔
530
                                if (update_count % update_rate == 0)
11,738,818✔
531
                                {
532
                                        double progress = update_count / total_work;
11,759✔
533
                                        manager->progress_update(thread_id, progress);
11,759✔
534
                                        if (manager->terminate(thread_id))
11,759✔
535
                                                return RunnerStatus::CANCEL;
1✔
536
                                }
537

538
                                // Handle if Ray was absorbed
539
                                if (RayIsAbsorbed)
11,738,817✔
540
                                {
541
                                        TransformToReference(LastPosRaySurfStage,
802,306✔
542
                                                                                 LastCosRaySurfStage,
543
                                                                                 Stage->Origin,
802,306✔
544
                                                                                 Stage->RLocToRef,
802,306✔
545
                                                                                 PosRayGlob,
546
                                                                                 CosRayGlob);
547

548
                                        System->RayData.Append(thread_id,
802,306✔
549
                                                                                   PosRayGlob,
550
                                                                                   CosRayGlob,
551
                                                                                   LastElementNumber,
552
                                                                                   i + 1,
802,306✔
553
                                                                                   LastRayNumber,
554
                                                                                   RayEvent::ABSORB);
555

556
                                        n_rays_active--;
802,306✔
557

558
                                        // ray was fully absorbed
559
                                        if (RayNumber == LastRayNumberInPreviousStage)
802,306✔
560
                                        {
561
                                                PreviousStageHasRays = false;
17✔
562
                                                if (PreviousStageDataArrayIndex > 0)
17✔
563
                                                {
564
                                                        PreviousStageDataArrayIndex--;
16✔
565
                                                        PreviousStageHasRays = true;
16✔
566
                                                }
567
                                                break;
17✔
568
                                        }
569
                                        else
570
                                        {
571
                                                if (i == 0)
802,289✔
572
                                                {
573
                                                        if (RayNumber == NumberOfRays)
145,482✔
574
                                                                break;
×
575
                                                        else
576
                                                                RayNumber++;
145,482✔
577
                                                }
578

579
                                                // Next ray in loop
580
                                                continue;
802,289✔
581
                                        }
582
                                }
583

584
                                // Ray has left the stage
585
                                bool FlagMiss = false;
10,936,511✔
586
                                if (i == 0)
10,936,511✔
587
                                {
588
                                        if (MultipleHitCount == 0)
10,872,319✔
589
                                        {
590
                                                // Ray in first stage missed stage entirely
591
                                                // Generate new ray
592
                                                continue;
10,127,089✔
593
                                        }
594
                                        else
595
                                        {
596
                                                // Ray hit an element, so save it for next stage
597
                                                CopyVec3(IncomingRays[PreviousStageDataArrayIndex].Pos,
745,230✔
598
                                                                 PosRayGlob);
599
                                                CopyVec3(IncomingRays[PreviousStageDataArrayIndex].Cos,
745,230✔
600
                                                                 CosRayGlob);
601
                                                IncomingRays[PreviousStageDataArrayIndex].Num = RayNumber;
745,230✔
602

603
                                                // Is Ray the last in the stage?
604
                                                if (RayNumber == NumberOfRays)
745,230✔
605
                                                {
606
                                                        StageHasRays = false;
18✔
607
                                                        break;
18✔
608
                                                }
609

610
                                                PreviousStageDataArrayIndex++;
745,212✔
611
                                                PreviousStageHasRays = true;
745,212✔
612

613
                                                // Move on to next ray
614
                                                RayNumber++;
745,212✔
615
                                                continue;
745,212✔
616
                                        }
617
                                }
618
                                else
619
                                {
620
                                        // After the first stage
621
                                        // Ray hit element OR is traced through stage
622
                                        if (Stage->TraceThrough || MultipleHitCount > 0)
64,192✔
623
                                        {
624
                                                // Ray is saved for the next stage
625
                                                CopyVec3(IncomingRays[PreviousStageDataArrayIndex].Pos,
57,836✔
626
                                                                 PosRayGlob);
627
                                                CopyVec3(IncomingRays[PreviousStageDataArrayIndex].Cos,
57,836✔
628
                                                                 CosRayGlob);
629
                                                IncomingRays[PreviousStageDataArrayIndex].Num = RayNumber;
57,836✔
630

631
                                                // Check if ray is last in stage
632
                                                if (RayNumber == LastRayNumberInPreviousStage)
57,836✔
633
                                                {
634
                                                        StageHasRays = false;
4✔
635
                                                        break;
4✔
636
                                                }
637

638
                                                PreviousStageDataArrayIndex++;
57,832✔
639
                                                PreviousStageHasRays = true;
57,832✔
640

641
                                                if (MultipleHitCount == 0)
57,832✔
642
                                                {
643
                                                        FlagMiss = true;
46,529✔
644
                                                }
645

646
                                                // Go to next ray
647
                                                continue;
57,832✔
648
                                        }
649
                                        // Ray missed stage entirely and is not traced
650
                                        else
651
                                        {
652
                                                FlagMiss = true;
6,356✔
653
                                        }
654

655
                                        // Handle FlagMiss condition (
656
                                        if (FlagMiss == true)
6,356✔
657
                                        {
658
                                                LastRayNumber = RayNumber;
6,356✔
659

660
                                                System->RayData.Append(thread_id,
6,356✔
661
                                                                                           PosRayGlob,
662
                                                                                           CosRayGlob,
663
                                                                                           ELEMENT_NULL,
664
                                                                                           i + 1,
6,356✔
665
                                                                                           LastRayNumber,
666
                                                                                           RayEvent::EXIT);
667

668
                                                n_rays_active--;
6,356✔
669

670
                                                if (RayNumber == LastRayNumberInPreviousStage)
6,356✔
671
                                                {
672
                                                        if (!Stage->TraceThrough)
1✔
673
                                                        {
674
                                                                PreviousStageHasRays = false;
1✔
675
                                                                if (PreviousStageDataArrayIndex > 0)
1✔
676
                                                                {
677
                                                                        PreviousStageHasRays = true;
1✔
678
                                                                        PreviousStageDataArrayIndex--; // last ray was previous one
1✔
679
                                                                }
680
                                                        }
681

682
                                                        // Exit stage
683
                                                        StageHasRays = false;
1✔
684
                                                        break;
1✔
685
                                                }
686
                                                else
687
                                                {
688
                                                        if (i == 0)
6,355✔
689
                                                                RayNumber++; // generate new sun ray
×
690

691
                                                        // Start new ray
692
                                                        continue;
6,355✔
693
                                                }
694
                                        }
695
                                }
696
                        }
11,738,818✔
697

698
                        // EndStage section...
699

700
                        // skipping save_st_data logic
701

702
                        if (!PreviousStageHasRays)
40✔
703
                        {
704
                                LastRayNumberInPreviousStage = 0;
2✔
705
                                continue; // No rays to carry forward
2✔
706
                        }
707

708
                        if (PreviousStageDataArrayIndex < IncomingRays.size())
38✔
709
                        {
710
                                LastRayNumberInPreviousStage = IncomingRays[PreviousStageDataArrayIndex].Num;
38✔
711
                                if (LastRayNumberInPreviousStage == 0)
38✔
712
                                {
713
                                        return RunnerStatus::ERROR;
×
714
                                }
715
                        }
716
                        else
717
                        {
718
                                return RunnerStatus::ERROR;
×
719
                        }
720
                }
41✔
721

722
                // Close out any remaining rays as misses
723
                unsigned idx = System->StageList.size() - 1;
24✔
724
                tstage_ptr Stage = System->StageList[idx];
24✔
725
                for (uint_fast64_t k = 0; k < n_rays_active; ++k)
81,479✔
726
                {
727
                        GlobalRay_refactored ray = IncomingRays[k];
81,455✔
728
                        System->RayData.Append(thread_id,
81,455✔
729
                                                                   ray.Pos,
730
                                                                   ray.Cos,
731
                                                                   ELEMENT_NULL,
732
                                                                   idx + 1,
81,455✔
733
                                                                   ray.Num,
734
                                                                   RayEvent::EXIT);
735
                }
736

737
                // System->SunRayCount is atomic so this is thread safe
738
                System->SunRayCount += sun_ray_count_local;
24✔
739

740
                return RunnerStatus::SUCCESS;
24✔
741
        }
25✔
742

743
} // namespace SolTrace::NativeRunner
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc