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

NREL / SolTrace / 18760276758

23 Oct 2025 07:54PM UTC coverage: 88.981% (-1.0%) from 89.946%
18760276758

Pull #75

github

web-flow
Merge 98a52da76 into f6f121007
Pull Request #75: New sun models

79 of 138 new or added lines in 8 files covered. (57.25%)

5 existing lines in 2 files now uncovered.

4385 of 4928 relevant lines covered (88.98%)

8888236.75 hits per line

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

93.37
/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(),
22✔
23
                                   as_power_tower(false),
22✔
24
                                   number_of_threads(1)
22✔
25
    {
26
    }
22✔
27

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

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

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

40
        RunnerStatus sts;
41

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

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

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

50
        return sts;
21✔
51
    }
52

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

66
    RunnerStatus NativeRunner::setup_sun(const SimulationData *data)
22✔
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);
22✔
71

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

76
        // Set sunshape data
77
        switch (sun->get_shape())
22✔
78
        {
79
        case SunShape::GAUSSIAN:
6✔
80
            this->tsys.Sun.Sigma = sun->get_sigma();
6✔
81
            break;
20✔
82
        case SunShape::PILLBOX:
12✔
83
            this->tsys.Sun.Sigma = sun->get_half_width();
12✔
84
            break;
12✔
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
            // Calculate kappa and gamma parameters
93
            // Creates the Buie (2003) sun shape based on CSR
94
            // [1] Buie, D., Dey, C., & Bosi, S. (2003). The effective size of the solar cone for solar concentrating systems. Solar energy, 74(2003), 417-427.
95
            // [2] Buie, D., Monger, A., & Dey, C. (2003). Sun shape distributions for terrestrial solar simulations. Solar Energy, 74(March 2003), 113-122.
96
            double csr = sun->get_circumsolar_ratio();
1✔
97
            double chi;
98
            if (csr > 0.145)
1✔
NEW
99
                chi = -0.04419909985804843 + csr * (1.401323894233574 + csr * (-0.3639746714505299 + csr * (-0.9579768560161194 + 1.1550475450828657 * csr)));
×
100
            else if (csr > 0.035)
1✔
101
                chi = 0.022652077593662934 + csr * (0.5252380349996234 + (2.5484334534423887 - 0.8763755326550412 * csr) * csr);
1✔
102
            else
NEW
103
                chi = 0.004733749294807862 + csr * (4.716738065192151 + csr * (-463.506669149804 + csr * (24745.88727411664 + csr * (-606122.7511711778 + 5521693.445014727 * csr))));
×
104
            this->tsys.Sun.buie_kappa = 0.9 * log(13.5 * chi) * pow(chi, -0.3);
1✔
105
            this->tsys.Sun.buie_gamma = 2.2 * log(0.52 * chi) * pow(chi, 0.43) - 0.1;
1✔
106
            break;
1✔
107
        }
108
        case SunShape::USER_DEFINED:
2✔
109
            std::vector<double> angle, intensity;
4✔
110
            sun->get_user_data(angle, intensity);
2✔
111
            int npoints = angle.size();
2✔
112

113
            // Set user data
114
            this->tsys.Sun.MaxAngle = 0;
2✔
115
            this->tsys.Sun.MaxIntensity = 0;
2✔
116

117
            this->tsys.Sun.SunShapeAngle.resize(2 * npoints - 1);
2✔
118
            this->tsys.Sun.SunShapeIntensity.resize(2 * npoints - 1);
2✔
119

120
            for (int i = 0; i < npoints; i++)
38✔
121
            {
122
                this->tsys.Sun.SunShapeAngle[npoints + i - 1] = angle[i];
36✔
123
                this->tsys.Sun.SunShapeIntensity[npoints + i - 1] = intensity[i];
36✔
124

125
                if (angle[i] > this->tsys.Sun.MaxAngle)
36✔
126
                    this->tsys.Sun.MaxAngle = angle[i];
34✔
127
                if (intensity[i] > this->tsys.Sun.MaxIntensity)
36✔
128
                    this->tsys.Sun.MaxIntensity = intensity[i];
2✔
129
            }
130

131
            // fill negative angle side of array
132
            for (int i = 0; i < npoints - 1; i++)
36✔
133
            {
134
                this->tsys.Sun.SunShapeAngle[i] = -angle[npoints - i - 1];
34✔
135
                this->tsys.Sun.SunShapeIntensity[i] = intensity[npoints - i - 1];
34✔
136
            }
137
        }
138

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

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

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

153
        for (auto iter = data->get_const_iterator();
21✔
154
             !data->is_at_end(iter);
8,654✔
155
             ++iter)
8,633✔
156
        {
157
            element_ptr el = iter->second;
8,633✔
158
            if (el->is_enabled() && el->is_stage())
8,633✔
159
            {
160
                tstage_ptr stage = make_tstage(el, this->eparams);
23✔
161
                auto retval = my_map.insert(
23✔
162
                    std::make_pair(el->get_stage(), stage));
46✔
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)
23✔
171
                {
172
                    // TODO: Duplicate stage numbers. Need to make an error
173
                    // message.
174
                    sts = RunnerStatus::ERROR;
×
175
                }
176

177
                current_stage = stage;
23✔
178
                element_number = 1;
23✔
179
            }
23✔
180
            else if (el->is_enabled() && el->is_single())
8,610✔
181
            {
182
                if (current_stage == nullptr)
8,362✔
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)
8,281✔
189
                {
190
                    throw std::runtime_error(
191
                        "Element does not match current stage");
×
192
                }
193

194
                telement_ptr elem = make_telement(iter->second,
16,562✔
195
                                                  element_number,
196
                                                  this->eparams);
8,281✔
197
                ++element_number;
8,281✔
198
                current_stage->ElementList.push_back(elem);
8,281✔
199
            }
8,281✔
200
        }
8,633✔
201

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

207
        if (my_map.size() == 0)
21✔
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;
21✔
240
        for (auto iter = my_map.cbegin();
21✔
241
             iter != my_map.cend();
53✔
242
             ++iter)
32✔
243
        {
244
            assert(last_stage_id < iter->first);
32✔
245
            last_stage_id = iter->first;
32✔
246
            this->tsys.StageList.push_back(iter->second);
32✔
247
        }
248

249
        if (sts == RunnerStatus::SUCCESS)
21✔
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);
21✔
254
            sts = success ? RunnerStatus::SUCCESS : RunnerStatus::ERROR;
21✔
255
        }
256

257
        return sts;
21✔
258
    }
21✔
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()
21✔
269
    {
270
        bool trace_return = trace_native(
42✔
271
            &this->tsys,
272
            this->tsys.seed,
21✔
273
            this->tsys.sim_raycount,
21✔
274
            this->tsys.sim_raymax,
21✔
275
            this->tsys.sim_errors_sunshape,
21✔
276
            this->tsys.sim_errors_optical,
21✔
277
            this->as_power_tower);
21✔
278

279
        // this->tsys.CollectResults();
280

281
        return trace_return ? RunnerStatus::SUCCESS : RunnerStatus::ERROR;
21✔
282
    }
283

284
    RunnerStatus NativeRunner::report_simulation(SolTrace::Result::SimulationResult *result,
3✔
285
                                                 int level)
286
    {
287
        RunnerStatus retval = RunnerStatus::SUCCESS;
3✔
288

289
        const TSystem *sys = this->get_system();
3✔
290
        // const TRayData ray_data = sys->AllRayData;
291
        const TRayData ray_data = sys->RayData;
3✔
292
        std::map<unsigned int, SolTrace::Result::ray_record_ptr> ray_records;
3✔
293
        std::map<unsigned int, SolTrace::Result::ray_record_ptr>::iterator iter;
3✔
294
        size_t ndata = ray_data.Count();
3✔
295

296
        bool sts;
297
        Vector3d point, cosines;
3✔
298
        int element;
299
        int stage;
300
        unsigned int raynum;
301

302
        telement_ptr el = nullptr;
3✔
303
        element_id elid;
304
        SolTrace::Result::ray_record_ptr rec = nullptr;
3✔
305
        SolTrace::Result::interaction_ptr intr = nullptr;
3✔
306
        SolTrace::Result::RayEvent rev;
307

308
        for (size_t ii = 0; ii < ndata; ++ii)
62,247✔
309
        {
310
            sts = ray_data.Query(ii,
62,244✔
311
                                 point.data,
312
                                 cosines.data,
313
                                 &element,
314
                                 &stage,
315
                                 &raynum,
316
                                 &rev);
317

318
            if (!sts)
62,244✔
319
            {
320
                retval = RunnerStatus::ERROR;
×
321
                break;
×
322
            }
323

324
            // std::cout << "ii: " << ii
325
            //           << "\npoint: " << point
326
            //           << "\ndirection: " << cosines
327
            //           << "\nelement: " << element
328
            //           << "\nstage: " << stage
329
            //           << "\nraynum: " << raynum
330
            //           << "\nevent: " << ray_event_string(rev)
331
            //           << std::endl;
332

333
            iter = ray_records.find(raynum);
62,244✔
334
            if (iter == ray_records.end())
62,244✔
335
            {
336
                rec = SolTrace::Result::make_ray_record(raynum);
20,010✔
337
                result->add_ray_record(rec);
20,010✔
338
                ray_records[raynum] = rec;
20,010✔
339
                assert(rev == SolTrace::Result::RayEvent::CREATE);
20,010✔
340
            }
341
            else
342
            {
343
                rec = iter->second;
42,234✔
344
            }
345

346
            if (element > 0)
62,244✔
347
            {
348
                el = sys->StageList[stage - 1]->ElementList[element - 1];
35,633✔
349
                elid = el->sim_data_id;
35,633✔
350
            }
351
            else
352
            {
353
                elid = element;
26,611✔
354
            }
355

356
            intr = make_interaction_record(elid, rev, point, cosines);
62,244✔
357
            rec->add_interaction_record(intr);
62,244✔
358
        }
359

360
        return RunnerStatus::SUCCESS;
3✔
361
    }
3✔
362

363
    bool NativeRunner::set_aperture_planes(TSystem *tsys)
21✔
364
    {
365
        bool retval;
366

367
        for (auto iter = tsys->StageList.cbegin();
21✔
368
             iter != tsys->StageList.cend();
53✔
369
             ++iter)
32✔
370
        {
371
            retval = this->set_aperture_planes(*iter);
32✔
372
            if (!retval)
32✔
373
                break;
×
374
        }
375

376
        return retval;
21✔
377
    }
378

379
    bool NativeRunner::set_aperture_planes(tstage_ptr stage)
32✔
380
    {
381
        bool retval;
382

383
        for (auto eiter = stage->ElementList.begin();
32✔
384
             eiter != stage->ElementList.end();
8,394✔
385
             ++eiter)
8,362✔
386
        {
387
            retval = aperture_plane(*eiter);
8,362✔
388
            if (!retval)
8,362✔
389
                break;
×
390
        }
391

392
        return retval;
32✔
393
    }
394

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

406
        Element->ZAperture =
16,724✔
407
            Element->icalc->compute_z_aperture(Element->aperture);
8,362✔
408

409
        return true;
8,362✔
410
    }
411

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