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

NREL / SolTrace / 18984697163

31 Oct 2025 08:41PM UTC coverage: 89.643% (-0.3%) from 89.946%
18984697163

push

github

web-flow
New sun models (#75)

* Refactor sun shape handling to use SunShape enum

Introduces a new SunShape enum to distinguish sun shape types from error distribution types, updating all relevant interfaces, implementations, and tests. This improves clarity and maintainability by separating sun shape logic from error distribution logic, and adds support for diffuse and user-defined sun shapes.

* Add Buie CSR and limb-darkened sun shape models

Introduces support for Buie circumsolar ratio (CSR) and limb-darkened sun shape models in the simulation framework. Updates the Sun class and related interfaces to handle the new parameters, implements error checking for CSR, and extends the native runner and error tracing logic to support these sun shapes. Updates unit and regression tests to cover the new models and their error handling.

* Fix branch syntax in CI workflow

* Improve sun shape error handling and comments

Added explanatory comments for unit conversions and sun model behavior. Improved error handling by throwing an exception for unsupported sun shapes. Minor code cleanup and TODO notes for future improvements.

* Limit CI workflow branches to 'develop'

* fixing pull request issues found by Copilot

* Refactor error calculation and update phi computation

Removed unused variables and replaced hardcoded pi value with PI constant in phi calculation for both SurfaceNormalErrors and Errors functions. Added detailed TODO and notes for future refactoring to reduce code duplication and improve error handling.

* Refactor tower demo tests to use shared setup function

Introduced create_tower_demo_simulation_data(bool) to consolidate simulation data setup for all test cases. Updated all tests to use this function, reducing code duplication and improving maintainability. Sun shape modifications for specific tests are now performed after setup. Minor formatting and pointer type consistency improvements.

* Refactor sun shape handling and add new test cases

Rem... (continued)

120 of 147 new or added lines in 8 files covered. (81.63%)

5 existing lines in 2 files now uncovered.

4423 of 4934 relevant lines covered (89.64%)

9111326.33 hits per line

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

93.23
/coretrace/simulation_runner/native_runner/native_runner.cpp
1

2
#include "native_runner.hpp"
3

4
#include <exception>
5
#include <map>
6
// #include <algorithm>
7

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

15
// NativeRunner headers
16
#include "native_runner_types.hpp"
17
#include "trace.hpp"
18

19
namespace SolTrace::NativeRunner
20
{
21

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

28
    NativeRunner::~NativeRunner()
23✔
29
    {
30
    }
23✔
31

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

37
    RunnerStatus NativeRunner::setup_simulation(const SimulationData *data)
22✔
38
    {
39

40
        RunnerStatus sts;
41

42
        sts = this->setup_parameters(data);
22✔
43

44
        if (sts == RunnerStatus::SUCCESS)
22✔
45
            sts = this->setup_sun(data);
22✔
46

47
        if (sts == RunnerStatus::SUCCESS)
22✔
48
            sts = this->setup_elements(data);
22✔
49

50
        return sts;
22✔
51
    }
52

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

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

72
        ray_source_ptr sun = data->get_ray_source();
23✔
73
        vector_copy(this->tsys.Sun.Origin, sun->get_position());
23✔
74
        this->tsys.Sun.ShapeIndex = sun->get_shape();
23✔
75

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

103
            // Set user data
104
            this->tsys.Sun.MaxAngle = 0;
2✔
105
            this->tsys.Sun.MaxIntensity = 0;
2✔
106

107
            this->tsys.Sun.SunShapeAngle.resize(2 * npoints - 1);
2✔
108
            this->tsys.Sun.SunShapeIntensity.resize(2 * npoints - 1);
2✔
109

110
            for (int i = 0; i < npoints; i++)
38✔
111
            {
112
                this->tsys.Sun.SunShapeAngle[npoints + i - 1] = angle[i];
36✔
113
                this->tsys.Sun.SunShapeIntensity[npoints + i - 1] = intensity[i];
36✔
114

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

133
        return RunnerStatus::SUCCESS;
23✔
134
    }
23✔
135

136
    RunnerStatus NativeRunner::setup_elements(const SimulationData *data)
22✔
137
    {
138
        // TODO: Improve error messages from this function.
139

140
        RunnerStatus sts = RunnerStatus::SUCCESS;
22✔
141
        auto my_map = std::map<int_fast64_t, tstage_ptr>();
22✔
142
        // int_fast64_t current_stage_id = -1;
143
        tstage_ptr current_stage = nullptr;
22✔
144
        int_fast64_t element_number = 1;
22✔
145
        bool element_found_before_stage = false;
22✔
146

147
        for (auto iter = data->get_const_iterator();
22✔
148
             !data->is_at_end(iter);
8,661✔
149
             ++iter)
8,639✔
150
        {
151
            element_ptr el = iter->second;
8,639✔
152
            if (el->is_enabled() && el->is_stage())
8,639✔
153
            {
154
                tstage_ptr stage = make_tstage(el, this->eparams);
25✔
155
                auto retval = my_map.insert(
25✔
156
                    std::make_pair(el->get_stage(), stage));
50✔
157

158
                // current_stage_id = stage->stage_id;
159

160
                // std::cout << "Created stage " << el->get_stage()
161
                //           << " with " << stage->ElementList.size() << " elements"
162
                //           << std::endl;
163

164
                if (retval.second == false)
25✔
165
                {
166
                    // TODO: Duplicate stage numbers. Need to make an error
167
                    // message.
168
                    sts = RunnerStatus::ERROR;
×
169
                }
170

171
                current_stage = stage;
25✔
172
                element_number = 1;
25✔
173
            }
25✔
174
            else if (el->is_enabled() && el->is_single())
8,614✔
175
            {
176
                if (current_stage == nullptr)
8,366✔
177
                {
178
                    // throw std::runtime_error("No stage to add element to");
179
                    element_found_before_stage = true;
81✔
180
                    continue;
81✔
181
                }
182
                else if (el->get_stage() != current_stage->stage_id)
8,285✔
183
                {
184
                    throw std::runtime_error(
185
                        "Element does not match current stage");
×
186
                }
187

188
                telement_ptr elem = make_telement(iter->second,
16,570✔
189
                                                  element_number,
190
                                                  this->eparams);
8,285✔
191
                ++element_number;
8,285✔
192
                current_stage->ElementList.push_back(elem);
8,285✔
193
            }
8,285✔
194
        }
8,639✔
195

196
        if (my_map.size() != 0 && element_found_before_stage)
22✔
197
        {
198
            throw std::runtime_error("Element found without a stage");
×
199
        }
200

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

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

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

251
        return sts;
22✔
252
    }
22✔
253

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

262
    RunnerStatus NativeRunner::run_simulation()
22✔
263
    {
264
        bool trace_return = trace_native(
44✔
265
            &this->tsys,
266
            this->tsys.seed,
22✔
267
            this->tsys.sim_raycount,
22✔
268
            this->tsys.sim_raymax,
22✔
269
            this->tsys.sim_errors_sunshape,
22✔
270
            this->tsys.sim_errors_optical,
22✔
271
            this->as_power_tower);
22✔
272

273
        // this->tsys.CollectResults();
274

275
        return trace_return ? RunnerStatus::SUCCESS : RunnerStatus::ERROR;
22✔
276
    }
277

278
    RunnerStatus NativeRunner::report_simulation(SolTrace::Result::SimulationResult *result,
3✔
279
                                                 int level)
280
    {
281
        RunnerStatus retval = RunnerStatus::SUCCESS;
3✔
282

283
        const TSystem *sys = this->get_system();
3✔
284
        // const TRayData ray_data = sys->AllRayData;
285
        const TRayData ray_data = sys->RayData;
3✔
286
        std::map<unsigned int, SolTrace::Result::ray_record_ptr> ray_records;
3✔
287
        std::map<unsigned int, SolTrace::Result::ray_record_ptr>::iterator iter;
3✔
288
        size_t ndata = ray_data.Count();
3✔
289

290
        bool sts;
291
        Vector3d point, cosines;
3✔
292
        int element;
293
        int stage;
294
        unsigned int raynum;
295

296
        telement_ptr el = nullptr;
3✔
297
        element_id elid;
298
        SolTrace::Result::ray_record_ptr rec = nullptr;
3✔
299
        SolTrace::Result::interaction_ptr intr = nullptr;
3✔
300
        SolTrace::Result::RayEvent rev;
301

302
        for (size_t ii = 0; ii < ndata; ++ii)
62,247✔
303
        {
304
            sts = ray_data.Query(ii,
62,244✔
305
                                 point.data,
306
                                 cosines.data,
307
                                 &element,
308
                                 &stage,
309
                                 &raynum,
310
                                 &rev);
311

312
            if (!sts)
62,244✔
313
            {
314
                retval = RunnerStatus::ERROR;
×
315
                break;
×
316
            }
317

318
            // std::cout << "ii: " << ii
319
            //           << "\npoint: " << point
320
            //           << "\ndirection: " << cosines
321
            //           << "\nelement: " << element
322
            //           << "\nstage: " << stage
323
            //           << "\nraynum: " << raynum
324
            //           << "\nevent: " << ray_event_string(rev)
325
            //           << std::endl;
326

327
            iter = ray_records.find(raynum);
62,244✔
328
            if (iter == ray_records.end())
62,244✔
329
            {
330
                rec = SolTrace::Result::make_ray_record(raynum);
20,010✔
331
                result->add_ray_record(rec);
20,010✔
332
                ray_records[raynum] = rec;
20,010✔
333
                assert(rev == SolTrace::Result::RayEvent::CREATE);
20,010✔
334
            }
335
            else
336
            {
337
                rec = iter->second;
42,234✔
338
            }
339

340
            if (element > 0)
62,244✔
341
            {
342
                el = sys->StageList[stage - 1]->ElementList[element - 1];
35,633✔
343
                elid = el->sim_data_id;
35,633✔
344
            }
345
            else
346
            {
347
                elid = element;
26,611✔
348
            }
349

350
            intr = make_interaction_record(elid, rev, point, cosines);
62,244✔
351
            rec->add_interaction_record(intr);
62,244✔
352
        }
353

354
        return RunnerStatus::SUCCESS;
3✔
355
    }
3✔
356

357
    bool NativeRunner::set_aperture_planes(TSystem *tsys)
22✔
358
    {
359
        bool retval;
360

361
        for (auto iter = tsys->StageList.cbegin();
22✔
362
             iter != tsys->StageList.cend();
56✔
363
             ++iter)
34✔
364
        {
365
            retval = this->set_aperture_planes(*iter);
34✔
366
            if (!retval)
34✔
367
                break;
×
368
        }
369

370
        return retval;
22✔
371
    }
372

373
    bool NativeRunner::set_aperture_planes(tstage_ptr stage)
34✔
374
    {
375
        bool retval;
376

377
        for (auto eiter = stage->ElementList.begin();
34✔
378
             eiter != stage->ElementList.end();
8,400✔
379
             ++eiter)
8,366✔
380
        {
381
            retval = aperture_plane(*eiter);
8,366✔
382
            if (!retval)
8,366✔
383
                break;
×
384
        }
385

386
        return retval;
34✔
387
    }
388

389
    bool NativeRunner::aperture_plane(telement_ptr Element)
8,366✔
390
    {
391
        /*{Calculates the aperture plane of the element in element coord system.
392
        Applicable to rotationally symmetric apertures surfaces with small
393
        curvature: g, s, p, o, c, v, m, e, r, i.
394
          input - Element = Element record containing geometry of element
395
          output -
396
                 - Element.ZAperture  where ZAperture is the distance from
397
                   the origin to the plane.
398
        }*/
399

400
        Element->ZAperture =
16,732✔
401
            Element->icalc->compute_z_aperture(Element->aperture);
8,366✔
402

403
        return true;
8,366✔
404
    }
405

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