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

NREL / SolTrace / 19580451842

21 Nov 2025 06:52PM UTC coverage: 89.184% (+0.4%) from 88.811%
19580451842

push

github

web-flow
Merge pull request #85 from NREL/simdata_io_json

Add json import/export to SimulationData

423 of 451 new or added lines in 17 files covered. (93.79%)

9 existing lines in 3 files now uncovered.

5970 of 6694 relevant lines covered (89.18%)

7997777.28 hits per line

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

93.24
/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

21
namespace SolTrace::NativeRunner
22
{
23

24
    NativeRunner::NativeRunner() : SimulationRunner(),
26✔
25
                                   as_power_tower(false),
26✔
26
                                   number_of_threads(1)
26✔
27
    {
28
    }
26✔
29

30
    NativeRunner::~NativeRunner()
26✔
31
    {
32
    }
26✔
33

34
    RunnerStatus NativeRunner::initialize()
24✔
35
    {
36
        return RunnerStatus::SUCCESS;
24✔
37
    }
38

39
    RunnerStatus NativeRunner::setup_simulation(const SimulationData *data)
25✔
40
    {
41

42
        RunnerStatus sts;
43

44
        this->tsys.ClearAll();
25✔
45

46
        sts = this->setup_parameters(data);
25✔
47

48
        if (sts == RunnerStatus::SUCCESS)
25✔
49
            sts = this->setup_sun(data);
25✔
50

51
        if (sts == RunnerStatus::SUCCESS)
25✔
52
            sts = this->setup_elements(data);
25✔
53

54
        return sts;
25✔
55
    }
56

57
    RunnerStatus NativeRunner::setup_parameters(const SimulationData *data)
25✔
58
    {
59
        // Get Parameter data
60
        // TODO: Check that these parameters are used as expected
61
        const SimulationParameters &sim_params = data->get_simulation_parameters();
25✔
62
        this->tsys.sim_errors_sunshape = sim_params.include_sun_shape_errors;
25✔
63
        this->tsys.sim_errors_optical = sim_params.include_optical_errors;
25✔
64
        this->tsys.sim_raycount = sim_params.number_of_rays;
25✔
65
        this->tsys.sim_raymax = sim_params.max_number_of_rays;
25✔
66
        this->tsys.seed = sim_params.seed;
25✔
67
        return RunnerStatus::SUCCESS;
25✔
68
    }
69

70
    RunnerStatus NativeRunner::setup_sun(const SimulationData *data)
26✔
71
    {
72
        // TODO: This should throw an error...
73
        // Get RaySource data (this runner assumes there is only the Sun)
74
        assert(data->get_number_of_ray_sources() == 1);
26✔
75

76
        ray_source_ptr sun = data->get_ray_source();
26✔
77
        vector_copy(this->tsys.Sun.Origin, sun->get_position());
26✔
78
        this->tsys.Sun.ShapeIndex = sun->get_shape();
26✔
79

80
        // Set sunshape data
81
        switch (sun->get_shape())
26✔
82
        {
83
        case SunShape::GAUSSIAN:
8✔
84
            this->tsys.Sun.Sigma = sun->get_sigma();
8✔
85
            break;
8✔
86
        case SunShape::PILLBOX:
12✔
87
            this->tsys.Sun.Sigma = sun->get_half_width();
12✔
88
            break;
12✔
89
        case SunShape::LIMBDARKENED:
1✔
90
            this->tsys.Sun.MaxAngle = 4.65; // [mrad]
1✔
91
            this->tsys.Sun.MaxIntensity = 1.0;
1✔
92
            break;
1✔
93
        case SunShape::BUIE_CSR:
1✔
94
        {
95
            this->tsys.Sun.MaxAngle = 43.6; // [mrad]
1✔
96
            this->tsys.Sun.MaxIntensity = 1.0;
1✔
97
            double kappa, gamma;
98
            sun->calculate_buie_parameters(kappa, gamma);
1✔
99
            this->tsys.Sun.buie_kappa = kappa;
1✔
100
            this->tsys.Sun.buie_gamma = gamma;
1✔
101
            break;
1✔
102
        }
103
        case SunShape::USER_DEFINED:
3✔
104
        {
105
            std::vector<double> angle, intensity;
3✔
106
            sun->get_user_data(angle, intensity);
3✔
107
            int npoints = angle.size();
3✔
108

109
            // Set user data
110
            this->tsys.Sun.MaxAngle = 0;
3✔
111
            this->tsys.Sun.MaxIntensity = 0;
3✔
112

113
            this->tsys.Sun.SunShapeAngle.resize(2 * npoints - 1);
3✔
114
            this->tsys.Sun.SunShapeIntensity.resize(2 * npoints - 1);
3✔
115

116
            for (int i = 0; i < npoints; i++)
65✔
117
            {
118
                this->tsys.Sun.SunShapeAngle[npoints + i - 1] = angle[i];
62✔
119
                this->tsys.Sun.SunShapeIntensity[npoints + i - 1] = intensity[i];
62✔
120

121
                if (angle[i] > this->tsys.Sun.MaxAngle)
62✔
122
                    this->tsys.Sun.MaxAngle = angle[i];
59✔
123
                if (intensity[i] > this->tsys.Sun.MaxIntensity)
62✔
124
                    this->tsys.Sun.MaxIntensity = intensity[i];
3✔
125
            }
126
            // fill negative angle side of array -> I don't think we need this.
127
            // for (int i = 0; i < npoints - 1; i++)
128
            //{
129
            //    this->tsys.Sun.SunShapeAngle[i] = -angle[npoints - i - 1];
130
            //    this->tsys.Sun.SunShapeIntensity[i] = intensity[npoints - i - 1];
131
            //}
132
            break;
3✔
133
        }
3✔
134
        default:
1✔
135
            // TODO: add error
136
            break;
1✔
137
        }
138

139
        return RunnerStatus::SUCCESS;
26✔
140
    }
26✔
141

142
    RunnerStatus NativeRunner::setup_elements(const SimulationData *data)
25✔
143
    {
144
        // TODO: Improve error messages from this function.
145

146
        RunnerStatus sts = RunnerStatus::SUCCESS;
25✔
147
        auto my_map = std::map<int_fast64_t, tstage_ptr>();
25✔
148
        // int_fast64_t current_stage_id = -1;
149
        tstage_ptr current_stage = nullptr;
25✔
150
        int_fast64_t element_number = 1;
25✔
151
        bool element_found_before_stage = false;
25✔
152

153
        for (auto iter = data->get_const_iterator();
25✔
154
             !data->is_at_end(iter);
15,009✔
155
             ++iter)
14,984✔
156
        {
157
            element_ptr el = iter->second;
14,984✔
158
            if (el->is_enabled() && el->is_stage())
14,984✔
159
            {
160
                tstage_ptr stage = make_tstage(el, this->eparams);
33✔
161
                auto retval = my_map.insert(
33✔
162
                    std::make_pair(el->get_stage(), stage));
66✔
163

164
                // current_stage_id = stage->stage_id;
165

166
                // std::cout << "Created stage " << el->get_stage()
167
                //           << " with " << stage->ElementList.size() << " elements"
168
                //           << std::endl;
169

170
                if (retval.second == false)
33✔
171
                {
172
                    // TODO: Duplicate stage numbers. Need to make an error
173
                    // message.
174
                    sts = RunnerStatus::ERROR;
×
175
                }
176

177
                current_stage = stage;
33✔
178
                element_number = 1;
33✔
179
            }
33✔
180
            else if (el->is_enabled() && el->is_single())
14,951✔
181
            {
182
                if (current_stage == nullptr)
14,703✔
183
                {
184
                    // throw std::runtime_error("No stage to add element to");
185
                    element_found_before_stage = true;
81✔
186
                    continue;
81✔
187
                }
188
                else if (el->get_stage() != current_stage->stage_id)
14,622✔
189
                {
190
                    throw std::runtime_error(
191
                        "Element does not match current stage");
×
192
                }
193

194
                telement_ptr elem = make_telement(iter->second,
29,244✔
195
                                                  element_number,
196
                                                  this->eparams);
14,622✔
197
                ++element_number;
14,622✔
198
                current_stage->ElementList.push_back(elem);
14,622✔
199
            }
14,622✔
200
        }
14,984✔
201

202
        if (my_map.size() != 0 && element_found_before_stage)
25✔
203
        {
204
            throw std::runtime_error("Element found without a stage");
×
205
        }
206

207
        if (my_map.size() == 0)
25✔
208
        {
209
            // No stage elements found in the passed in data. However,
210
            // the runner requires stages. So make a single stage
211
            // and put everything there. Note that the coordinates are
212
            // set to correspond to global coordinates. This is necessary
213
            // so that the element coordinate setup in make_element are
214
            // correct.
215
            int_fast64_t element_number = 1;
9✔
216
            auto stage = make_tstage(this->eparams);
9✔
217
            stage->ElementList.reserve(data->get_number_of_elements());
9✔
218
            for (auto iter = data->get_const_iterator();
9✔
219
                 !data->is_at_end(iter);
97✔
220
                 ++iter)
88✔
221
            {
222
                element_ptr el = iter->second;
88✔
223
                if (el->is_enabled() && el->is_single())
88✔
224
                {
225
                    telement_ptr tel = make_telement(el,
226
                                                     element_number,
227
                                                     this->eparams);
81✔
228
                    stage->ElementList.push_back(tel);
81✔
229
                    ++element_number;
81✔
230
                }
81✔
231
            }
88✔
232
            my_map.insert(std::make_pair(0, stage));
9✔
233
        }
9✔
234

235
        // std::map (according to the documentation) is automatically
236
        // ordered by the keys so inserting into a map will sort the stages
237
        // and we can just transfer the pointers, in order, to the StageList
238
        // simply by pulling them out of the map.
239
        int_fast64_t last_stage_id = -1;
25✔
240
        for (auto iter = my_map.cbegin();
25✔
241
             iter != my_map.cend();
67✔
242
             ++iter)
42✔
243
        {
244
            assert(last_stage_id < iter->first);
42✔
245
            last_stage_id = iter->first;
42✔
246
            this->tsys.StageList.push_back(iter->second);
42✔
247
        }
248

249
        if (sts == RunnerStatus::SUCCESS)
25✔
250
        {
251
            // std::cout << "Setting ZAperture..." << std::endl;
252
            // Compute and set ZAperture field in each element
253
            bool success = set_aperture_planes(&this->tsys);
25✔
254
            sts = success ? RunnerStatus::SUCCESS : RunnerStatus::ERROR;
25✔
255
        }
256

257
        return sts;
25✔
258
    }
25✔
259

260
    RunnerStatus NativeRunner::update_simulation(const SimulationData *data)
×
261
    {
262
        // TODO: Do a more efficient implementation of this?
263
        this->tsys.ClearAll();
×
264
        this->setup_simulation(data);
×
265
        return RunnerStatus::SUCCESS;
×
266
    }
267

268
    RunnerStatus NativeRunner::run_simulation()
25✔
269
    {
270
        RunnerStatus sts = trace_native(
50✔
271
            &this->tsys,
272
            this->tsys.seed,
25✔
273
            this->tsys.sim_raycount,
25✔
274
            this->tsys.sim_raymax,
25✔
275
            this->tsys.sim_errors_sunshape,
25✔
276
            this->tsys.sim_errors_optical,
25✔
277
            this->as_power_tower);
25✔
278

279
        {
280
            // Hack to match up current state with return type
281
            std::lock_guard<std::mutex> lk(this->tsys.state_mutex);
25✔
282
            this->tsys.current_state = sts;
25✔
283
        }
25✔
284

285
        return sts;
25✔
286
    }
287

288
    RunnerStatus NativeRunner::status_simulation(double *progress)
2✔
289
    {
290
        RunnerStatus sts = RunnerStatus::ERROR;
2✔
291
        // Create isolated scope for lock guard
292
        {
293
            std::lock_guard<std::mutex> lk(this->tsys.state_mutex);
2✔
294
            sts = this->tsys.current_state;
2✔
295
            if (progress != nullptr)
2✔
296
            {
297
                *progress = this->tsys.progress;
1✔
298
            }
299
        }
2✔
300
        return sts;
2✔
301
    }
302

303
    RunnerStatus NativeRunner::cancel_simulation()
1✔
304
    {
305
        RunnerStatus sts = RunnerStatus::ERROR;
1✔
306

307
        // Create isolated scope for the lock
308
        {
309
            std::lock_guard<std::mutex> my_lock(this->tsys.state_mutex);
1✔
310
            this->tsys.cancel = true;
1✔
311
        }
1✔
312

313
        int count = 0;
1✔
314
        while (true)
315
        {
316
            ++count;
1✔
317
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
318

319
            // Create isolated scope for the lock
320
            {
321
                std::lock_guard<std::mutex> my_lock(this->tsys.state_mutex);
1✔
322
                if (this->tsys.current_state != RunnerStatus::RUNNING)
1✔
323
                {
324
                    sts = this->tsys.current_state;
1✔
325
                    break;
1✔
326
                }
327
            }
1✔
328

UNCOV
329
            if (count > 30)
×
330
            {
331
                sts = RunnerStatus::TIMEOUT;
×
332
                break;
×
333
            }
UNCOV
334
        }
×
335

336
        return sts;
1✔
337
    }
338

339
    RunnerStatus NativeRunner::report_simulation(SolTrace::Result::SimulationResult *result,
6✔
340
                                                 int level)
341
    {
342
        RunnerStatus retval = RunnerStatus::SUCCESS;
6✔
343

344
        const TSystem *sys = this->get_system();
6✔
345
        // const TRayData ray_data = sys->AllRayData;
346
        const TRayData ray_data = sys->RayData;
6✔
347
        std::map<unsigned int, SolTrace::Result::ray_record_ptr> ray_records;
6✔
348
        std::map<unsigned int, SolTrace::Result::ray_record_ptr>::iterator iter;
6✔
349
        size_t ndata = ray_data.Count();
6✔
350

351
        bool sts;
352
        Vector3d point, cosines;
6✔
353
        int element;
354
        int stage;
355
        unsigned int raynum;
356

357
        telement_ptr el = nullptr;
6✔
358
        element_id elid;
359
        SolTrace::Result::ray_record_ptr rec = nullptr;
6✔
360
        SolTrace::Result::interaction_ptr intr = nullptr;
6✔
361
        SolTrace::Result::RayEvent rev;
362

363
        for (size_t ii = 0; ii < ndata; ++ii)
272,954✔
364
        {
365
            sts = ray_data.Query(ii,
272,948✔
366
                                 point.data,
367
                                 cosines.data,
368
                                 &element,
369
                                 &stage,
370
                                 &raynum,
371
                                 &rev);
372

373
            if (!sts)
272,948✔
374
            {
375
                retval = RunnerStatus::ERROR;
×
376
                break;
×
377
            }
378

379
            // std::cout << "ii: " << ii
380
            //           << "\npoint: " << point
381
            //           << "\ndirection: " << cosines
382
            //           << "\nelement: " << element
383
            //           << "\nstage: " << stage
384
            //           << "\nraynum: " << raynum
385
            //           << "\nevent: " << ray_event_string(rev)
386
            //           << std::endl;
387

388
            iter = ray_records.find(raynum);
272,948✔
389
            if (iter == ray_records.end())
272,948✔
390
            {
391
                rec = SolTrace::Result::make_ray_record(raynum);
90,010✔
392
                result->add_ray_record(rec);
90,010✔
393
                ray_records[raynum] = rec;
90,010✔
394
                assert(rev == SolTrace::Result::RayEvent::CREATE);
90,010✔
395
            }
396
            else
397
            {
398
                rec = iter->second;
182,938✔
399
            }
400

401
            if (element > 0)
272,948✔
402
            {
403
                el = sys->StageList[stage - 1]->ElementList[element - 1];
168,229✔
404
                elid = el->sim_data_id;
168,229✔
405
            }
406
            else
407
            {
408
                elid = element;
104,719✔
409
            }
410

411
            intr = make_interaction_record(elid, rev, point, cosines);
272,948✔
412
            rec->add_interaction_record(intr);
272,948✔
413
        }
414

415
        return RunnerStatus::SUCCESS;
6✔
416
    }
6✔
417

418
    bool NativeRunner::set_aperture_planes(TSystem *tsys)
25✔
419
    {
420
        bool retval;
421

422
        for (auto iter = tsys->StageList.cbegin();
25✔
423
             iter != tsys->StageList.cend();
67✔
424
             ++iter)
42✔
425
        {
426
            retval = this->set_aperture_planes(*iter);
42✔
427
            if (!retval)
42✔
428
                break;
×
429
        }
430

431
        return retval;
25✔
432
    }
433

434
    bool NativeRunner::set_aperture_planes(tstage_ptr stage)
42✔
435
    {
436
        bool retval;
437

438
        for (auto eiter = stage->ElementList.begin();
42✔
439
             eiter != stage->ElementList.end();
14,745✔
440
             ++eiter)
14,703✔
441
        {
442
            retval = aperture_plane(*eiter);
14,703✔
443
            if (!retval)
14,703✔
444
                break;
×
445
        }
446

447
        return retval;
42✔
448
    }
449

450
    bool NativeRunner::aperture_plane(telement_ptr Element)
14,703✔
451
    {
452
        /*{Calculates the aperture plane of the element in element coord system.
453
        Applicable to rotationally symmetric apertures surfaces with small
454
        curvature: g, s, p, o, c, v, m, e, r, i.
455
          input - Element = Element record containing geometry of element
456
          output -
457
                 - Element.ZAperture  where ZAperture is the distance from
458
                   the origin to the plane.
459
        }*/
460

461
        Element->ZAperture =
29,406✔
462
            Element->icalc->compute_z_aperture(Element->aperture);
14,703✔
463

464
        return true;
14,703✔
465
    }
466

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