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

NREL / SolTrace / 19436441296

17 Nov 2025 04:15PM UTC coverage: 88.811% (+0.06%) from 88.752%
19436441296

push

github

web-flow
80 add status and cancel functions to simulationrunner api (#81)

* Adding the functions to the SimulationRunner class; stub versions for NativeRunner and OptixRunner

* NativeRunner implementation of status and cancel functions

* Address copilot comments

64 of 70 new or added lines in 6 files covered. (91.43%)

5540 of 6238 relevant lines covered (88.81%)

8562956.42 hits per line

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

93.21
/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(),
24✔
25
                                   as_power_tower(false),
24✔
26
                                   number_of_threads(1)
24✔
27
    {
28
    }
24✔
29

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

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

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

42
        RunnerStatus sts;
43

44
        sts = this->setup_parameters(data);
23✔
45

46
        if (sts == RunnerStatus::SUCCESS)
23✔
47
            sts = this->setup_sun(data);
23✔
48

49
        if (sts == RunnerStatus::SUCCESS)
23✔
50
            sts = this->setup_elements(data);
23✔
51

52
        return sts;
23✔
53
    }
54

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

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

74
        ray_source_ptr sun = data->get_ray_source();
24✔
75
        vector_copy(this->tsys.Sun.Origin, sun->get_position());
24✔
76
        this->tsys.Sun.ShapeIndex = sun->get_shape();
24✔
77

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

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

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

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

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

137
        return RunnerStatus::SUCCESS;
24✔
138
    }
24✔
139

140
    RunnerStatus NativeRunner::setup_elements(const SimulationData *data)
23✔
141
    {
142
        // TODO: Improve error messages from this function.
143

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

151
        for (auto iter = data->get_const_iterator();
23✔
152
             !data->is_at_end(iter);
14,947✔
153
             ++iter)
14,924✔
154
        {
155
            element_ptr el = iter->second;
14,924✔
156
            if (el->is_enabled() && el->is_stage())
14,924✔
157
            {
158
                tstage_ptr stage = make_tstage(el, this->eparams);
27✔
159
                auto retval = my_map.insert(
27✔
160
                    std::make_pair(el->get_stage(), stage));
54✔
161

162
                // current_stage_id = stage->stage_id;
163

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

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

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

192
                telement_ptr elem = make_telement(iter->second,
29,136✔
193
                                                  element_number,
194
                                                  this->eparams);
14,568✔
195
                ++element_number;
14,568✔
196
                current_stage->ElementList.push_back(elem);
14,568✔
197
            }
14,568✔
198
        }
14,924✔
199

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

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

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

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

255
        return sts;
23✔
256
    }
23✔
257

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

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

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

283
        return sts;
23✔
284
    }
285

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

301
    RunnerStatus NativeRunner::cancel_simulation()
1✔
302
    {
303
        RunnerStatus sts = RunnerStatus::ERROR;
1✔
304

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

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

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

327
            if (count > 30)
1✔
328
            {
NEW
329
                sts = RunnerStatus::TIMEOUT;
×
NEW
330
                break;
×
331
            }
332
        }
1✔
333

334
        return sts;
1✔
335
    }
336

337
    RunnerStatus NativeRunner::report_simulation(SolTrace::Result::SimulationResult *result,
4✔
338
                                                 int level)
339
    {
340
        RunnerStatus retval = RunnerStatus::SUCCESS;
4✔
341

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

349
        bool sts;
350
        Vector3d point, cosines;
4✔
351
        int element;
352
        int stage;
353
        unsigned int raynum;
354

355
        telement_ptr el = nullptr;
4✔
356
        element_id elid;
357
        SolTrace::Result::ray_record_ptr rec = nullptr;
4✔
358
        SolTrace::Result::interaction_ptr intr = nullptr;
4✔
359
        SolTrace::Result::RayEvent rev;
360

361
        for (size_t ii = 0; ii < ndata; ++ii)
208,568✔
362
        {
363
            sts = ray_data.Query(ii,
208,564✔
364
                                 point.data,
365
                                 cosines.data,
366
                                 &element,
367
                                 &stage,
368
                                 &raynum,
369
                                 &rev);
370

371
            if (!sts)
208,564✔
372
            {
373
                retval = RunnerStatus::ERROR;
×
374
                break;
×
375
            }
376

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

386
            iter = ray_records.find(raynum);
208,564✔
387
            if (iter == ray_records.end())
208,564✔
388
            {
389
                rec = SolTrace::Result::make_ray_record(raynum);
70,010✔
390
                result->add_ray_record(rec);
70,010✔
391
                ray_records[raynum] = rec;
70,010✔
392
                assert(rev == SolTrace::Result::RayEvent::CREATE);
70,010✔
393
            }
394
            else
395
            {
396
                rec = iter->second;
138,554✔
397
            }
398

399
            if (element > 0)
208,564✔
400
            {
401
                el = sys->StageList[stage - 1]->ElementList[element - 1];
129,283✔
402
                elid = el->sim_data_id;
129,283✔
403
            }
404
            else
405
            {
406
                elid = element;
79,281✔
407
            }
408

409
            intr = make_interaction_record(elid, rev, point, cosines);
208,564✔
410
            rec->add_interaction_record(intr);
208,564✔
411
        }
412

413
        return RunnerStatus::SUCCESS;
4✔
414
    }
4✔
415

416
    bool NativeRunner::set_aperture_planes(TSystem *tsys)
23✔
417
    {
418
        bool retval;
419

420
        for (auto iter = tsys->StageList.cbegin();
23✔
421
             iter != tsys->StageList.cend();
59✔
422
             ++iter)
36✔
423
        {
424
            retval = this->set_aperture_planes(*iter);
36✔
425
            if (!retval)
36✔
426
                break;
×
427
        }
428

429
        return retval;
23✔
430
    }
431

432
    bool NativeRunner::set_aperture_planes(tstage_ptr stage)
36✔
433
    {
434
        bool retval;
435

436
        for (auto eiter = stage->ElementList.begin();
36✔
437
             eiter != stage->ElementList.end();
14,685✔
438
             ++eiter)
14,649✔
439
        {
440
            retval = aperture_plane(*eiter);
14,649✔
441
            if (!retval)
14,649✔
442
                break;
×
443
        }
444

445
        return retval;
36✔
446
    }
447

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

459
        Element->ZAperture =
29,298✔
460
            Element->icalc->compute_z_aperture(Element->aperture);
14,649✔
461

462
        return true;
14,649✔
463
    }
464

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