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

NREL / SolTrace / 21046664913

15 Jan 2026 09:17PM UTC coverage: 87.999% (+0.2%) from 87.815%
21046664913

Pull #96

github

web-flow
Merge a5971e7da into e78d2bfb0
Pull Request #96: 95 implement embree runner

605 of 797 new or added lines in 17 files covered. (75.91%)

14 existing lines in 7 files now uncovered.

6255 of 7108 relevant lines covered (88.0%)

7221404.67 hits per line

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

92.17
/coretrace/simulation_runner/native_runner/native_runner.cpp
1

2
#include "native_runner.hpp"
3

4
#include <chrono>
5
#include <exception>
6
#include <map>
7
#include <mutex>
8
#include <thread>
9

10
// SimulationData headers
11
#include "composite_element.hpp"
12
#include "element.hpp"
13
#include "simulation_parameters.hpp"
14
#include "simulation_data.hpp"
15
#include "simulation_data_export.hpp"
16

17
// NativeRunner headers
18
#include "native_runner_types.hpp"
19
#include "trace.hpp"
20
#include "trace_logger.hpp"
21

22
namespace SolTrace::NativeRunner
23
{
24

25
    NativeRunner::NativeRunner() : SimulationRunner(),
26✔
26
                                   as_power_tower(false),
26✔
27
                                   number_of_threads(1)
26✔
28
    {
29
        this->my_logger = make_trace_logger();
26✔
30
        this->my_manager = make_thread_manager(this->my_logger);
26✔
31
        return;
26✔
UNCOV
32
    }
×
33

34
    NativeRunner::~NativeRunner()
26✔
35
    {
36
        this->my_manager = nullptr;
26✔
37
        this->my_logger = nullptr;
26✔
38
        return;
26✔
39
    }
26✔
40

41
    RunnerStatus NativeRunner::initialize()
24✔
42
    {
43
        return RunnerStatus::SUCCESS;
24✔
44
    }
45

46
    RunnerStatus NativeRunner::setup_simulation(const SimulationData *data)
25✔
47
    {
48

49
        RunnerStatus sts;
50

51
        this->tsys.ClearAll();
25✔
52

53
        sts = this->setup_parameters(data);
25✔
54

55
        if (sts == RunnerStatus::SUCCESS)
25✔
56
            sts = this->setup_sun(data);
25✔
57

58
        if (sts == RunnerStatus::SUCCESS)
25✔
59
            sts = this->setup_elements(data);
25✔
60

61
        return sts;
25✔
62
    }
63

64
    RunnerStatus NativeRunner::setup_parameters(const SimulationData *data)
25✔
65
    {
66
        // Get Parameter data
67
        const SimulationParameters &sim_params = data->get_simulation_parameters();
25✔
68
        this->tsys.sim_errors_sunshape = sim_params.include_sun_shape_errors;
25✔
69
        this->tsys.sim_errors_optical = sim_params.include_optical_errors;
25✔
70
        this->tsys.sim_raycount = sim_params.number_of_rays;
25✔
71
        this->tsys.sim_raymax = sim_params.max_number_of_rays;
25✔
72
        this->tsys.seed = sim_params.seed;
25✔
73
        return RunnerStatus::SUCCESS;
25✔
74
    }
75

76
    RunnerStatus NativeRunner::setup_sun(const SimulationData *data)
26✔
77
    {
78
        if (data->get_number_of_ray_sources() > 1)
26✔
79
        {
80
            throw std::invalid_argument("NativeRunner: Only 1 ray source is supported.");
×
81
        }
82
        else if (data->get_number_of_ray_sources() <= 0)
26✔
83
        {
84
            throw std::invalid_argument("NativeRunner: Ray source is required.");
×
85
        }
86

87
        ray_source_ptr sun = data->get_ray_source();
26✔
88
        vector_copy(this->tsys.Sun.Origin, sun->get_position());
26✔
89
        this->tsys.Sun.ShapeIndex = sun->get_shape();
26✔
90

91
        // Set sunshape data
92
        switch (sun->get_shape())
26✔
93
        {
94
        case SunShape::GAUSSIAN:
8✔
95
            this->tsys.Sun.Sigma = sun->get_sigma();
8✔
96
            break;
8✔
97
        case SunShape::PILLBOX:
11✔
98
            this->tsys.Sun.Sigma = sun->get_half_width();
11✔
99
            break;
11✔
100
        case SunShape::LIMBDARKENED:
1✔
101
            this->tsys.Sun.MaxAngle = 4.65; // [mrad]
1✔
102
            this->tsys.Sun.MaxIntensity = 1.0;
1✔
103
            break;
1✔
104
        case SunShape::BUIE_CSR:
1✔
105
        {
106
            this->tsys.Sun.MaxAngle = 43.6; // [mrad]
1✔
107
            this->tsys.Sun.MaxIntensity = 1.0;
1✔
108
            double kappa, gamma;
109
            sun->calculate_buie_parameters(kappa, gamma);
1✔
110
            this->tsys.Sun.buie_kappa = kappa;
1✔
111
            this->tsys.Sun.buie_gamma = gamma;
1✔
112
            break;
1✔
113
        }
114
        case SunShape::USER_DEFINED:
4✔
115
        {
116
            std::vector<double> angle, intensity;
4✔
117
            sun->get_user_data(angle, intensity);
4✔
118
            int npoints = angle.size();
4✔
119

120
            // Set user data
121
            this->tsys.Sun.MaxAngle = 0;
4✔
122
            this->tsys.Sun.MaxIntensity = 0;
4✔
123

124
            this->tsys.Sun.SunShapeAngle.resize(2 * npoints - 1);
4✔
125
            this->tsys.Sun.SunShapeIntensity.resize(2 * npoints - 1);
4✔
126

127
            for (int i = 0; i < npoints; i++)
92✔
128
            {
129
                this->tsys.Sun.SunShapeAngle[npoints + i - 1] = angle[i];
88✔
130
                this->tsys.Sun.SunShapeIntensity[npoints + i - 1] = intensity[i];
88✔
131

132
                if (angle[i] > this->tsys.Sun.MaxAngle)
88✔
133
                    this->tsys.Sun.MaxAngle = angle[i];
84✔
134
                if (intensity[i] > this->tsys.Sun.MaxIntensity)
88✔
135
                    this->tsys.Sun.MaxIntensity = intensity[i];
4✔
136
            }
137
            // fill negative angle side of array -> I don't think we need this.
138
            // for (int i = 0; i < npoints - 1; i++)
139
            //{
140
            //    this->tsys.Sun.SunShapeAngle[i] = -angle[npoints - i - 1];
141
            //    this->tsys.Sun.SunShapeIntensity[i] = intensity[npoints - i - 1];
142
            //}
143
            break;
4✔
144
        }
4✔
145
        default:
1✔
146
            if (data->get_simulation_parameters().include_sun_shape_errors)
1✔
147
            {
148
                throw std::invalid_argument("Unrecognized sun shape.");
×
149
            }
150
            break;
1✔
151
        }
152

153
        return RunnerStatus::SUCCESS;
26✔
154
    }
26✔
155

156
    RunnerStatus NativeRunner::setup_elements(const SimulationData *data)
25✔
157
    {
158
        // TODO: Improve error messages from this function.
159

160
        RunnerStatus sts = RunnerStatus::SUCCESS;
25✔
161
        auto my_map = std::map<int_fast64_t, tstage_ptr>();
25✔
162
        // int_fast64_t current_stage_id = -1;
163
        tstage_ptr current_stage = nullptr;
25✔
164
        // int_fast64_t element_number = 1;
165
        bool element_found_before_stage = false;
25✔
166

167
        if (data->get_number_of_elements() <= 0)
25✔
168
        {
169
            throw std::invalid_argument("SimulationData has no elements.");
×
170
        }
171

172
        for (auto iter = data->get_const_iterator();
25✔
173
             !data->is_at_end(iter);
19,131✔
174
             ++iter)
19,106✔
175
        {
176
            element_ptr el = iter->second;
19,106✔
177
            if (el->is_enabled() && el->is_stage())
19,106✔
178
            {
179
                tstage_ptr stage = make_tstage(el, this->eparams);
33✔
180
                auto retval = my_map.insert(
33✔
181
                    std::make_pair(el->get_stage(), stage));
66✔
182

183
                // current_stage_id = stage->stage_id;
184

185
                // std::cout << "Created stage " << el->get_stage()
186
                //           << " with " << stage->ElementList.size() << " elements"
187
                //           << std::endl;
188

189
                if (retval.second == false)
33✔
190
                {
191
                    // TODO: Duplicate stage numbers. Need to make an error
192
                    // message.
NEW
193
                    throw std::runtime_error("Duplicate stage numbers found.");
×
194
                    sts = RunnerStatus::ERROR;
195
                }
196

197
                current_stage = stage;
33✔
198
                // element_number = 1;
199
            }
33✔
200
            else if (el->is_enabled() && el->is_single())
19,073✔
201
            {
202
                if (current_stage == nullptr)
19,065✔
203
                {
204
                    element_found_before_stage = true;
81✔
205
                    continue;
81✔
206
                }
207
                else if (el->get_stage() != current_stage->stage_id)
18,984✔
208
                {
209
                    throw std::runtime_error(
210
                        "Element does not match current stage");
×
211
                }
212

213
                telement_ptr elem = make_telement(iter->second,
18,984✔
214
                                                  current_stage,
215
                                                  this->eparams);
37,968✔
216
                // ++element_number;
217
                // current_stage->ElementList.push_back(elem);
218
                current_stage->add_element(elem);
18,984✔
219
            }
18,984✔
220
        }
19,106✔
221

222
        if (my_map.size() != 0 && element_found_before_stage)
25✔
223
        {
224
            throw std::runtime_error("Element found without a stage");
×
225
        }
226

227
        if (my_map.size() == 0)
25✔
228
        {
229
            // No stage elements found in the passed in data. However,
230
            // the runner requires stages. So make a single stage
231
            // and put everything there. Note that the coordinates are
232
            // set to correspond to global coordinates. This is necessary
233
            // so that the element coordinate setup in make_element are
234
            // correct.
235
            // int_fast64_t element_number = 1;
236
            auto stage = make_tstage(this->eparams);
9✔
237
            stage->ElementList.reserve(data->get_number_of_elements());
9✔
238
            for (auto iter = data->get_const_iterator();
9✔
239
                 !data->is_at_end(iter);
97✔
240
                 ++iter)
88✔
241
            {
242
                element_ptr el = iter->second;
88✔
243
                if (el->is_enabled() && el->is_single())
88✔
244
                {
245
                    telement_ptr tel = make_telement(el,
246
                                                     stage,
247
                                                     this->eparams);
81✔
248
                    // stage->ElementList.push_back(tel);
249
                    // ++element_number;
250
                    stage->add_element(tel);
81✔
251
                }
81✔
252
            }
88✔
253
            my_map.insert(std::make_pair(0, stage));
9✔
254
        }
9✔
255

256
        // std::map (according to the documentation) is automatically
257
        // ordered by the keys so inserting into a map will sort the stages
258
        // and we can just transfer the pointers, in order, to the StageList
259
        // simply by pulling them out of the map.
260
        int_fast64_t last_stage_id = -1;
25✔
261
        for (auto iter = my_map.cbegin();
25✔
262
             iter != my_map.cend();
67✔
263
             ++iter)
42✔
264
        {
265
            assert(last_stage_id < iter->first);
42✔
266
            last_stage_id = iter->first;
42✔
267
            this->tsys.StageList.push_back(iter->second);
42✔
268
        }
269

270
        if (sts == RunnerStatus::SUCCESS)
25✔
271
        {
272
            // std::cout << "Setting ZAperture..." << std::endl;
273
            // Compute and set ZAperture field in each element
274
            bool success = set_aperture_planes(&this->tsys);
25✔
275
            sts = success ? RunnerStatus::SUCCESS : RunnerStatus::ERROR;
25✔
276
        }
277

278
        return sts;
25✔
279
    }
25✔
280

281
    RunnerStatus NativeRunner::update_simulation(const SimulationData *data)
×
282
    {
283
        // TODO: Do a more efficient implementation of this?
284
        this->tsys.ClearAll();
×
285
        this->setup_simulation(data);
×
286
        return RunnerStatus::SUCCESS;
×
287
    }
288

289
    RunnerStatus NativeRunner::run_simulation()
25✔
290
    {
291
        this->set_seeds();
25✔
292

293
        RunnerStatus sts = trace_native(
25✔
294
            this->my_manager,
25✔
295
            this->my_logger,
25✔
296
            &this->tsys,
297
            this->seeds,
25✔
298
            this->number_of_threads,
25✔
299
            this->tsys.sim_raycount,
25✔
300
            this->tsys.sim_raymax,
25✔
301
            this->tsys.sim_errors_sunshape,
25✔
302
            this->tsys.sim_errors_optical,
25✔
303
            this->as_power_tower);
25✔
304

305
        return sts;
25✔
306
    }
307

308
    RunnerStatus NativeRunner::status_simulation(double *progress)
2✔
309
    {
310
        return this->my_manager->status(progress);
2✔
311
    }
312

313
    RunnerStatus NativeRunner::cancel_simulation()
1✔
314
    {
315
        // TODO: Should this have some sort of wait here for the termination?
316
        this->my_manager->cancel();
1✔
317
        return this->my_manager->status();
1✔
318
    }
319

320
    RunnerStatus NativeRunner::report_simulation(SolTrace::Result::SimulationResult *result,
7✔
321
                                                 int level)
322
    {
323
        RunnerStatus retval = RunnerStatus::SUCCESS;
7✔
324

325
        const TSystem *sys = this->get_system();
7✔
326
        // const TRayData ray_data = sys->AllRayData;
327
        const TRayData ray_data = sys->RayData;
7✔
328
        std::map<unsigned int, SolTrace::Result::ray_record_ptr> ray_records;
7✔
329
        std::map<unsigned int, SolTrace::Result::ray_record_ptr>::iterator iter;
7✔
330
        uint_fast64_t ndata = ray_data.Count();
7✔
331

332
        bool sts;
333
        Vector3d point, cosines;
7✔
334
        int element;
335
        int stage;
336
        uint_fast64_t raynum;
337

338
        telement_ptr el = nullptr;
7✔
339
        element_id elid;
340
        SolTrace::Result::ray_record_ptr rec = nullptr;
7✔
341
        SolTrace::Result::interaction_ptr intr = nullptr;
7✔
342
        SolTrace::Result::RayEvent rev;
343

344
        // std::cout << "Num Events: " << ndata << std::endl;
345

346
        for (uint_fast64_t ii = 0; ii < ndata; ++ii)
317,447✔
347
        {
348
            sts = ray_data.Query(ii,
317,440✔
349
                                 point.data,
350
                                 cosines.data,
351
                                 &element,
352
                                 &stage,
353
                                 &raynum,
354
                                 &rev);
355

356
            if (!sts)
317,440✔
357
            {
358
                retval = RunnerStatus::ERROR;
×
359
                break;
×
360
            }
361

362
            // std::cout << "ii: " << ii
363
            //           << "\npoint: " << point
364
            //           << "\ndirection: " << cosines
365
            //           << "\nelement: " << element
366
            //           << "\nstage: " << stage
367
            //           << "\nraynum: " << raynum
368
            //           << "\nevent: " << ray_event_string(rev)
369
            //           << std::endl;
370

371
            iter = ray_records.find(raynum);
317,440✔
372
            if (iter == ray_records.end())
317,440✔
373
            {
374
                rec = SolTrace::Result::make_ray_record(raynum);
100,010✔
375
                result->add_ray_record(rec);
100,010✔
376
                ray_records[raynum] = rec;
100,010✔
377
                assert(rev == SolTrace::Result::RayEvent::CREATE);
100,010✔
378
            }
379
            else
380
            {
381
                rec = iter->second;
217,430✔
382
            }
383

384
            if (element > 0)
317,440✔
385
            {
386
                el = sys->StageList[stage - 1]->ElementList[element - 1];
186,310✔
387
                elid = el->sim_data_id;
186,310✔
388
            }
389
            else
390
            {
391
                elid = element;
131,130✔
392
            }
393

394
            intr = make_interaction_record(elid, rev, point, cosines);
317,440✔
395
            rec->add_interaction_record(intr);
317,440✔
396
        }
397

398
        return retval;
7✔
399
    }
7✔
400

401
    bool NativeRunner::set_aperture_planes(TSystem *tsys)
25✔
402
    {
403
        bool retval;
404

405
        for (auto iter = tsys->StageList.cbegin();
25✔
406
             iter != tsys->StageList.cend();
67✔
407
             ++iter)
42✔
408
        {
409
            retval = this->set_aperture_planes(*iter);
42✔
410
            if (!retval)
42✔
411
                break;
×
412
        }
413

414
        return retval;
25✔
415
    }
416

417
    bool NativeRunner::set_aperture_planes(tstage_ptr stage)
42✔
418
    {
419
        bool retval;
420

421
        for (auto eiter = stage->ElementList.begin();
42✔
422
             eiter != stage->ElementList.end();
19,107✔
423
             ++eiter)
19,065✔
424
        {
425
            retval = aperture_plane(*eiter);
19,065✔
426
            if (!retval)
19,065✔
427
                break;
×
428
        }
429

430
        return retval;
42✔
431
    }
432

433
    bool NativeRunner::aperture_plane(telement_ptr Element)
19,065✔
434
    {
435
        /*{Calculates the aperture plane of the element in element coord system.
436
        Applicable to rotationally symmetric apertures surfaces with small
437
        curvature: g, s, p, o, c, v, m, e, r, i.
438
          input - Element = Element record containing geometry of element
439
          output -
440
                 - Element.ZAperture  where ZAperture is the distance from
441
                   the origin to the plane.
442
        }*/
443

444
        Element->ZAperture =
38,130✔
445
            Element->icalc->compute_z_aperture(Element->aperture);
19,065✔
446

447
        return true;
19,065✔
448
    }
449

450
    void NativeRunner::set_seeds()
25✔
451
    {
452
        if (this->seeds.empty() ||
25✔
NEW
453
            this->seeds.size() != this->number_of_threads)
×
454
        {
455
            this->seeds.clear();
25✔
456
            for (unsigned k = 0; k < this->number_of_threads; ++k)
50✔
457
            {
458
                this->seeds.push_back(this->tsys.seed + 123 * k);
25✔
459
            }
460
        }
461
        else
462
        {
463
            ; // Intentional no-op
464
        }
465
        return;
25✔
466
    }
467

468
} // 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