• 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

78.11
/coretrace/simulation_data/simdata_io.cpp
1

2
#include "simdata_io.hpp"
3

4
#include <array>
5
#include <cstring>
6
#include <exception>
7
#include <sstream>
8
#include <string>
9
#include <nlohmann/json.hpp>
10

11
#include "constants.hpp"
12
#include "basic_sun_position.hpp"
13
#include "ray_source.hpp"
14
#include "simulation_data.hpp"
15
#include "single_element.hpp"
16
#include "stage_element.hpp"
17
#include "sun.hpp"
18
#include "surface.hpp"
19
#include "utilities.hpp"
20
#include "json_helpers.hpp"
21

22
namespace SolTrace::Data {
23

24
DistributionType char_to_distribution(const char dist_char)
26✔
25
{
26
    switch (dist_char)
26✔
27
    {
28
    case ('g'):
26✔
29
    {
30
        return DistributionType::GAUSSIAN;
26✔
31
    }
32
    case ('p'):
×
33
    {
34
        return DistributionType::PILLBOX;
×
35
    }
36
    case ('f'):
×
37
    {
38
        return DistributionType::DIFFUSE;
×
39
    }
40
    case ('d'):
×
41
    {
42
        return DistributionType::USER_DEFINED;
×
43
    }
44
    default:
×
45
    {
46
        return DistributionType::GAUSSIAN;
×
47
    }
48
    }
49
}
50

51
SunShape char_to_sunshape(const char dist_char)
7✔
52
{
53
    switch (dist_char)
7✔
54
    {
55
    case ('g'):
×
56
    {
57
        return SunShape::GAUSSIAN;
×
58
    }
59
    case ('p'):
4✔
60
    {
61
        return SunShape::PILLBOX;
4✔
62
    }
63
    case ('d'):
3✔
64
    {
65
        return SunShape::USER_DEFINED;
3✔
66
    }
67
    default:
×
68
    {
69
        return SunShape::GAUSSIAN;
×
70
    }
71
    }
72
}
73

74
InteractionType int_to_interaction(const int interaction_int)
15,020✔
75
{
76
    switch (interaction_int)
15,020✔
77
    {
78
    case (1):
×
79
    {
80
        return InteractionType::REFRACTION;
×
81
    }
82
    case (2):
15,020✔
83
    {
84
        return InteractionType::REFLECTION;
15,020✔
85
    }
86
    default:
×
87
    {
88
        return InteractionType::REFLECTION;
×
89
    }
90
    }
91
}
92

93
ApertureType char_to_aperture(const char aperture_char)
15,020✔
94
{
95
    switch (aperture_char)
15,020✔
96
    {
97
    case ('c'):
×
98
    {
99
        return ApertureType::CIRCLE;
×
100
    }
101
    case ('h'):
76✔
102
    {
103
        return ApertureType::HEXAGON;
76✔
104
    }
105
    case ('t'):
×
106
    {
107
        return ApertureType::EQUILATERAL_TRIANGLE;
×
108
    }
109
    case ('r'):
14,941✔
110
    {
111
        return ApertureType::RECTANGLE;
14,941✔
112
    }
113
    case ('a'):
×
114
    {
115
        return ApertureType::ANNULUS;
×
116
    }
117
    case ('l'):
3✔
118
    {
119
        return ApertureType::SINGLE_AXIS_CURVATURE_SECTION;
3✔
120
    }
121
    case ('i'):
×
122
    {
123
        return ApertureType::IRREGULAR_TRIANGLE;
×
124
    }
125
    case ('q'):
×
126
    {
127
        return ApertureType::IRREGULAR_QUADRILATERAL;
×
128
    }
129
    default:
×
130
    {
131
        return ApertureType::APERTURE_UNKNOWN;
×
132
    }
133
    }
134
}
135

136
SurfaceType char_to_surface(const char surface_char)
15,020✔
137
{
138
    switch (surface_char)
15,020✔
139
    {
140
    case ('s'):
76✔
141
        return SurfaceType::SPHERE;
76✔
142
    case ('p'):
14,935✔
143
        return SurfaceType::PARABOLA;
14,935✔
144
    case ('o'):
×
145
        return SurfaceType::HYPER;
×
146
    case ('g'):
×
147
        return SurfaceType::GENERAL_SPENCER_MURTY;
×
148
    case ('f'):
6✔
149
        return SurfaceType::FLAT;
6✔
150
    case ('c'):
×
151
        return SurfaceType::CONE;
×
152
    case ('t'):
3✔
153
        return SurfaceType::CYLINDER;
3✔
154
    case ('d'):
×
155
        return SurfaceType::TORUS;
×
156
    default:
×
157
        return SurfaceType::SURFACE_UNKNOWN;
×
158
    }
159
}
160

161
static void read_line(char *buf, int len, FILE *fp)
15,224✔
162
{
163
    fgets(buf, len, fp);
15,224✔
164
    int nch = strlen(buf);
15,224✔
165
    if (nch > 0 && buf[nch - 1] == '\n')
15,224✔
166
        buf[nch - 1] = 0;
15,218✔
167
    if (nch - 1 > 0 && buf[nch - 2] == '\r')
15,224✔
168
        buf[nch - 2] = 0;
×
169
}
15,224✔
170

171
std::vector<std::string> split(const std::string &str,
15,046✔
172
                               const std::string &delim,
173
                               bool ret_empty,
174
                               bool ret_delim)
175
{
176
    std::vector<std::string> list;
15,046✔
177

178
    char cur_delim[2] = {0, 0};
15,046✔
179
    std::string::size_type m_pos = 0;
15,046✔
180
    std::string token;
15,046✔
181

182
    while (m_pos < str.length())
451,024✔
183
    {
184
        std::string::size_type pos = str.find_first_of(delim, m_pos);
435,978✔
185
        if (pos == std::string::npos)
435,978✔
186
        {
187
            cur_delim[0] = 0;
26✔
188
            token.assign(str, m_pos, std::string::npos);
26✔
189
            m_pos = str.length();
26✔
190
        }
191
        else
192
        {
193
            cur_delim[0] = str[pos];
435,952✔
194
            std::string::size_type len = pos - m_pos;
435,952✔
195
            token.assign(str, m_pos, len);
435,952✔
196
            m_pos = pos + 1;
435,952✔
197
        }
198

199
        if (token.empty() && !ret_empty)
435,978✔
200
            continue;
×
201

202
        list.push_back(token);
435,978✔
203

204
        if (ret_delim && cur_delim[0] != 0 && m_pos < str.length())
435,978✔
205
            list.push_back(std::string(cur_delim));
×
206
    }
207

208
    return list;
30,092✔
209
}
15,046✔
210

211
bool process_sun(FILE *fp, SimulationData &sd)
7✔
212
{
213
    char buf[1024];
214

215
    // Read Sun info
216
    int bi = 0, count = 0;
7✔
217
    char cshape = 'g';
7✔
218
    double Sigma, HalfWidth;
219
    bool PointSource;
220
    double X, Y, Z, Latitude, Day, Hour;
221
    bool UseLDHSpec;
222

223
    read_line(buf, 1023, fp);
7✔
224
    sscanf(buf, "SUN\tPTSRC\t%d\tSHAPE\t%c\tSIGMA\t%lg\tHALFWIDTH\t%lg",
7✔
225
           &bi, &cshape, &Sigma, &HalfWidth);
226
    PointSource = (bi != 0);
7✔
227
    cshape = tolower(cshape);
7✔
228

229
    read_line(buf, 1023, fp);
7✔
230

231
    sscanf(buf, "XYZ\t%lg\t%lg\t%lg\tUSELDH\t%d\tLDH\t%lg\t%lg\t%lg",
7✔
232
           &X, &Y, &Z, &bi, &Latitude, &Day, &Hour);
233
    UseLDHSpec = (bi != 0);
7✔
234

235
    read_line(buf, 1023, fp);
7✔
236
    sscanf(buf, "USER SHAPE DATA\t%d", &count);
7✔
237
    std::vector<double> angle_vec;
7✔
238
    std::vector<double> intensity_vec;
7✔
239
    if (count > 0)
7✔
240
    {
241
        for (int i = 0; i < count; i++)
90✔
242
        {
243
            double x, y;
244
            read_line(buf, 1023, fp);
84✔
245
            sscanf(buf, "%lg\t%lg", &x, &y);
84✔
246
            angle_vec.push_back(x);
84✔
247
            intensity_vec.push_back(y);
84✔
248
        }
249
    }
250

251
    // Make sun
252
    auto sun = make_ray_source<Sun>();
7✔
253

254
    // Define sun position
255
    if (UseLDHSpec)
7✔
256
    {
257
        // sun->set_position(Latitude, Day, Hour);
258
        st_sun_position(Latitude, Day, Hour, &X, &Y, &Z);
3✔
259
    }
260
    sun->set_position(X, Y, Z);
7✔
261
    // else
262
    // {
263
    //  sun->set_position(X, Y, Z);
264
    // }
265

266
    // Define sun shape
267
    SunShape sun_shape = char_to_sunshape(cshape);
7✔
268
    sun->set_shape(sun_shape, Sigma, HalfWidth, 0.0, angle_vec, intensity_vec);
7✔
269
    // TOD: Buie sun shape not implemented here
270

271
    // TODO set point source
272

273
    // Attach sun to simulation data
274
    sd.add_ray_source(sun);
7✔
275
    return true;
7✔
276
}
7✔
277

278
bool read_optic_surface(FILE *fp,
26✔
279
                        OpticalProperties &optics,
280
                        int &OpticalSurfaceNumber)
281
{
282
    if (!fp)
26✔
283
        return false;
×
284
    char buf[1024];
285
    read_line(buf, 1023, fp);
26✔
286
    std::vector<std::string> parts = split(std::string(buf), "\t", true, false);
78✔
287
    if (parts.size() < 15)
26✔
288
    {
289
        printf("too few tokens for optical surface: %zu\n", parts.size());
×
290
        printf("\t>> %s\n", buf);
×
291
        return false;
×
292
    }
293

294
    char ErrorDistribution = 'g';
26✔
295
    if (parts[1].length() > 0)
26✔
296
        ErrorDistribution = parts[1][0];
26✔
297

298
    int ApertureStopOrGratingType = atoi(parts[2].c_str());
26✔
299
    OpticalSurfaceNumber = atoi(parts[3].c_str());
26✔
300
    int DiffractionOrder = atoi(parts[4].c_str());
26✔
301
    double Reflectivity = atof(parts[5].c_str());
26✔
302
    double Transmissivity = atof(parts[6].c_str());
26✔
303
    double RMSSlope = atof(parts[7].c_str());
26✔
304
    double RMSSpecularity = atof(parts[8].c_str());
26✔
305
    double RefractionIndexReal = atof(parts[9].c_str());
26✔
306
    double RefractionIndexImag = atof(parts[10].c_str());
26✔
307
    double GratingCoeffs[4];
308
    GratingCoeffs[0] = atof(parts[11].c_str());
26✔
309
    GratingCoeffs[1] = atof(parts[12].c_str());
26✔
310
    GratingCoeffs[2] = atof(parts[13].c_str());
26✔
311
    GratingCoeffs[3] = atof(parts[14].c_str());
26✔
312

313
    bool UseReflectivityTable = false;
26✔
314
    int refl_npoints = 0;
26✔
315
    double *refl_angles = 0;
26✔
316
    double *refls = 0;
26✔
317

318
    bool UseTransmissivityTable = false;
26✔
319
    int trans_npoints = 0;
26✔
320
    double *trans_angles = 0;
26✔
321
    double *transs = 0;
26✔
322

323
    if (parts.size() >= 17)
26✔
324
    {
325
        UseReflectivityTable = (atoi(parts[15].c_str()) > 0);
2✔
326
        refl_npoints = atoi(parts[16].c_str());
2✔
327
        if (parts.size() >= 19)
2✔
328
        {
329
            UseTransmissivityTable = (atoi(parts[17].c_str()) > 0);
2✔
330
            trans_npoints = atoi(parts[18].c_str());
2✔
331
        }
332
    }
333

334
    if (UseReflectivityTable)
26✔
335
    {
336
        refl_angles = new double[refl_npoints];
×
337
        refls = new double[refl_npoints];
×
338

339
        for (int i = 0; i < refl_npoints; i++)
×
340
        {
341
            read_line(buf, 1023, fp);
×
342
            sscanf(buf, "%lg %lg", &refl_angles[i], &refls[i]);
×
343
        }
344
    }
345
    if (UseTransmissivityTable)
26✔
346
    {
347
        trans_angles = new double[trans_npoints];
×
348
        transs = new double[trans_npoints];
×
349

350
        for (int i = 0; i < trans_npoints; i++)
×
351
        {
352
            read_line(buf, 1023, fp);
×
353
            sscanf(buf, "%lg %lg", &trans_angles[i], &transs[i]);
×
354
        }
355
    }
356

357
    // Define optical properties
358
    InteractionType interaction = InteractionType::REFLECTION;
26✔
359
    DistributionType dist = char_to_distribution(ErrorDistribution);
26✔
360
    optics = OpticalProperties(interaction, dist, Transmissivity,
26✔
361
                               Reflectivity, RMSSlope, RMSSpecularity,
362
                               RefractionIndexReal, RefractionIndexImag);
363

364
    if (refl_angles != 0)
26✔
365
        delete[] refl_angles;
×
366
    if (refls != 0)
26✔
367
        delete[] refls;
×
368
    if (trans_angles != 0)
26✔
369
        delete[] trans_angles;
×
370
    if (transs != 0)
26✔
371
        delete[] transs;
×
372
    return true;
26✔
373
}
26✔
374

375
bool process_optics(
7✔
376
    FILE *fp,
377
    std::map<std::string, std::array<OpticalProperties, 2>> &optics_map)
378
{
379
    char buf[1024];
380

381
    // Read number of optics
382
    int count = 0;
7✔
383
    read_line(buf, 1023, fp);
7✔
384
    sscanf(buf, "OPTICS LIST COUNT\t%d", &count);
7✔
385

386
    // Define each optics
387
    for (int i = 0; i < count; i++)
20✔
388
    {
389
        // Read optical pair info line
390
        read_line(buf, 1023, fp);
13✔
391

392
        if (strncmp(buf, "OPTICAL PAIR", 12) == 0)
13✔
393
        {
394
            // int iopt = st_add_optic(cxt, (const char*)(buf + 13));
395
            std::string optics_name = std::string(buf + 13);
13✔
396
            OpticalProperties optics_front, optics_back;
13✔
397
            int OpticalSurfaceNumber = 0;
13✔
398
            read_optic_surface(fp, optics_front, OpticalSurfaceNumber);
13✔
399
            read_optic_surface(fp, optics_back, OpticalSurfaceNumber);
13✔
400

401
            optics_map[optics_name][0] = optics_front;
13✔
402
            optics_map[optics_name][1] = optics_back;
13✔
403
        }
13✔
404
        else
405
            return false;
×
406
    }
407

408
    return true;
7✔
409
}
410

411
bool read_element(
15,020✔
412
    FILE *fp,
413
    std::map<std::string, std::array<OpticalProperties, 2>> &optics_map,
414
    element_ptr &el)
415
{
416
    char buf[1024];
417
    read_line(buf, 1023, fp);
15,020✔
418

419
    std::vector<std::string> tok = split(buf, "\t", true, false);
45,060✔
420
    if (tok.size() < 29)
15,020✔
421
    {
422
        printf("too few tokens for element: %zu\n", tok.size());
×
423
        printf("\t>> %s\n", buf);
×
424
        return false;
×
425
    }
426

427
    bool enabled = atoi(tok[0].c_str()) ? 1 : 0;
15,020✔
428
    double xyz[3] = {
429
        atof(tok[1].c_str()),
15,020✔
430
        atof(tok[2].c_str()),
15,020✔
431
        atof(tok[3].c_str())};
30,040✔
432
    double aim[3] = {
433
        atof(tok[4].c_str()),
15,020✔
434
        atof(tok[5].c_str()),
15,020✔
435
        atof(tok[6].c_str())};
30,040✔
436
    double zrot = atof(tok[7].c_str());
15,020✔
437

438
    char ShapeIndex = ' ';
15,020✔
439
    if (tok[8].length() > 0)
15,020✔
440
    {
441
        ShapeIndex = tok[8][0];
15,020✔
442
    }
443
    else
444
    {
445
        printf("no aperture index specified for element\n");
×
446
        return false;
×
447
    }
448

449
    std::vector<double> aperture_params;
15,020✔
450
    for (int i = 0; i < 8; i++)
135,180✔
451
    {
452
        aperture_params.push_back(atof(tok[i + 9].c_str()));
120,160✔
453
    }
454

455
    char SurfaceIndex = ' ';
15,020✔
456
    if (tok[17].length() > 0)
15,020✔
457
    {
458
        SurfaceIndex = tok[17][0];
15,020✔
459
    }
460
    else
461
    {
462
        printf("no surface index specified for element\n");
×
463
        return false;
×
464
    }
465

466
    std::vector<double> surface_params;
15,020✔
467
    for (int i = 0; i < 8; i++)
135,180✔
468
    {
469
        surface_params.push_back(atof(tok[i + 18].c_str()));
120,160✔
470
    }
471

472
    // Skipping surface file for now
473
    std::string SurfaceFile = tok[26];
15,020✔
474
    std::string optics_name = tok[27].c_str();
15,020✔
475
    InteractionType interaction = int_to_interaction(atoi(tok[28].c_str()));
15,020✔
476

477
    // Create element
478
    el = make_element<SingleElement>();
15,020✔
479

480
    // Make aperture
481
    ApertureType aperture_type = char_to_aperture(ShapeIndex);
15,020✔
482
    if (aperture_type == ApertureType::APERTURE_UNKNOWN)
15,020✔
483
    {
484
        std::stringstream ss;
×
485
        ss << "Aperture character " << ShapeIndex
486
           << " returned unknown aperture type " << aperture_type;
×
487
        throw std::invalid_argument(ss.str());
×
488
    }
×
489
    
490
    aperture_ptr ap_ptr = Aperture::make_aperture_from_type(
491
        aperture_type, aperture_params);
15,020✔
492
    if (ap_ptr == nullptr)
15,020✔
493
    {
494
        std::stringstream ss;
×
495
        ss << "Unable to make aperture pointer -- "
496
           << "\nChar: " << ShapeIndex
497
           << "\nType: " << aperture_type
×
498
           << "\nParams: [";
×
499
        for (auto cit = aperture_params.cbegin();
×
500
             cit != aperture_params.cend();
×
501
             ++cit)
×
502
        {
503
            ss << *cit << ", ";
×
504
        }
505
        ss << "]" << std::endl;
×
506
        throw std::runtime_error(ss.str());
×
507
    }
×
508
    el->set_aperture(ap_ptr);
15,020✔
509

510
    // Make surface
511
    SurfaceType surface_type = char_to_surface(SurfaceIndex);
15,020✔
512
    if (surface_type == SurfaceType::SURFACE_UNKNOWN)
15,020✔
513
    {
514
        std::stringstream ss;
×
515
        ss << "Unknown surface type " << surface_type;
×
516
        throw std::invalid_argument(ss.str());
×
517
    }
×
518
    surface_ptr surf_ptr = make_surface_from_type(surface_type, surface_params);
15,020✔
519
    el->set_surface(surf_ptr);
15,020✔
520
    if (surface_type == SurfaceType::CYLINDER)
15,020✔
521
    {
522
        ap_ptr = el->get_aperture();
3✔
523
        auto rect = std::dynamic_pointer_cast<Rectangle>(ap_ptr);
3✔
524
        auto cyl = std::dynamic_pointer_cast<Cylinder>(surf_ptr);
3✔
525
        if (rect == nullptr || cyl == nullptr)
3✔
526
        {
527
            throw std::invalid_argument("This should not happen!");
×
528
        }
529
        rect->x_length = 2.0 * cyl->radius;
3✔
530
        rect->x_coord = -1.0 * cyl->radius;
3✔
531
    }
3✔
532

533
    // Set element position and orientation
534
    el->set_reference_frame_geometry(Vector3d(xyz),
30,040✔
535
                                     Vector3d(aim),
30,040✔
536
                                     zrot);
537

538
    // Set optical properties
539
    OpticalProperties optics_front = optics_map[optics_name][0];
15,020✔
540
    OpticalProperties optics_back = optics_map[optics_name][1];
15,020✔
541
    el->set_front_optical_properties(optics_front);
15,020✔
542
    el->set_back_optical_properties(optics_back);
15,020✔
543

544
    // Set optical interaction type
545
    el->get_front_optical_properties()->my_type = interaction;
15,020✔
546
    el->get_back_optical_properties()->my_type = interaction;
15,020✔
547

548
    return true;
15,020✔
549
}
15,020✔
550

551
bool process_stages(
7✔
552
    FILE *fp,
553
    SimulationData &sd,
554
    std::map<std::string, std::array<OpticalProperties, 2>> &optics_map)
555
{
556
    char buf[1024];
557

558
    // Loop through stages
559
    int count_stage = 0;
7✔
560
    read_line(buf, 1023, fp);
7✔
561
    sscanf(buf, "STAGE LIST COUNT\t%d", &count_stage);
7✔
562

563
    for (int i_stage = 0; i_stage < count_stage; i_stage++)
23✔
564
    {
565
        int virt = 0, multi = 1, count_element = 0, tr = 0;
16✔
566
        double X, Y, Z, AX, AY, AZ, ZRot;
567

568
        read_line(buf, 1023, fp);
16✔
569
        sscanf(buf, "STAGE\tXYZ\t%lg\t%lg\t%lg\tAIM\t%lg\t%lg\t%lg\tZROT\t%lg\tVIRTUAL\t%d\tMULTIHIT\t%d\tELEMENTS\t%d\tTRACETHROUGH\t%d",
16✔
570
               &X, &Y, &Z,
571
               &AX, &AY, &AZ,
572
               &ZRot,
573
               &virt,
574
               &multi,
575
               &count_element,
576
               &tr);
577

578
        read_line(buf, 1023, fp); // read name
16✔
579

580
        // Make stage
581
        stage_ptr stage = make_stage(i_stage);
16✔
582
        stage->set_origin(X, Y, Z);
16✔
583
        stage->set_aim_vector(AX, AY, AZ);
16✔
584
        stage->set_zrot(ZRot);
16✔
585
        stage->compute_coordinate_rotations();
16✔
586

587
        // Loop through elements
588
        for (int i_element = 0; i_element < count_element; i_element++)
15,036✔
589
        {
590
            element_ptr el;
15,020✔
591
            read_element(fp, optics_map, el);
15,020✔
592
            el->set_name(std::to_string(i_element));
15,020✔
593
            // TODO make virtual if stage is virtual?
594

595
            if (!Element::is_success(stage->add_element(el)))
15,020✔
596
            {
597
                std::cout << "Failed to add element to stage" << std::endl;
×
598
            }
599
        }
15,020✔
600

601
        if (!Element::is_success(sd.add_stage(stage)))
16✔
602
        {
603
            std::cout << "Failed to add stage to SimulationData" << std::endl;
×
604
        }
605
    }
16✔
606

607
    return true;
7✔
608
}
609

610
bool process_sim_par(FILE *fp, SimulationData &sd)
7✔
611
{
612
    char buf[1024];
613

614
    // Check if end of file
615
    if (feof(fp))
7✔
616
        return false;
×
617

618
    // Check for simulation parameters
619
    read_line(buf, 1023, fp);
7✔
620
    if (strncmp(buf, "TRACE", 5) != 0)
7✔
621
        return false;
6✔
622

623
    // Get simulation parameters
624
    int n_rays, n_rays_sun, n_cpu, seed, ss, err, pf;
625
    int n = sscanf(buf, "TRACE\tNRAY\t%d\tNSUN\t%d\tCPU\t%d\tSEED\t%d\tSUNSHAPE\t%d\tERRORS\t%d\tPTFOCUS\t%d",
1✔
626
                   &n_rays, &n_rays_sun, &n_cpu, &seed, &ss, &err, &pf);
627

628
    // Assign simulation parameters
629
    SimulationParameters &par = sd.get_simulation_parameters();
1✔
630
    par.number_of_rays = n_rays;
1✔
631
    par.max_number_of_rays = n_rays_sun;
1✔
632
    par.seed = seed;
1✔
633
    par.include_sun_shape_errors = ss;
1✔
634
    par.include_optical_errors = err;
1✔
635

636
    // TODO Assign number CPUs, point focus?
637
    return true;
1✔
638
}
639

640
bool load_stinput_file(SimulationData &sd, std::string filename)
7✔
641
{
642
    // TODO: Reset simulation data?
643

644
    // Read in file
645
    FILE *fp = fopen(filename.data(), "r");
7✔
646
    if (!fp)
7✔
647
    {
648
        printf("failed to open system input file: %s\n",
×
649
               filename.data());
650
        return false;
×
651
    }
652

653
    // Buffer to store read line
654
    char buf[1024];
655

656
    // Get version info (if first line starts with '#')
657
    int vmaj = 0, vmin = 0, vmic = 0;
7✔
658
    char c = fgetc(fp);
7✔
659
    if (c == '#')
7✔
660
    {
661
        read_line(buf, 1023, fp);
7✔
662
        sscanf(buf, " SOLTRACE VERSION %d.%d.%d INPUT FILE",
7✔
663
               &vmaj, &vmin, &vmic);
664

665
        // unsigned int file_version = vmaj*10000 + vmin*100 + vmic;
666

667
        printf("loading input file version %d.%d.%d\n",
7✔
668
               vmaj, vmin, vmic);
669
    }
670
    else
671
    {
672
        ungetc(c, fp);
×
673
    }
674

675
    // Read in Sun
676
    process_sun(fp, sd);
7✔
677

678
    // Read in Optics
679
    std::map<std::string, std::array<OpticalProperties, 2>> optics_map;
7✔
680
    process_optics(fp, optics_map);
7✔
681

682
    // Read in Stages
683
    process_stages(fp, sd, optics_map);
7✔
684

685
    // Read in simulation parameters (if any)
686
    process_sim_par(fp, sd);
7✔
687

688
    return true;
7✔
689
}
7✔
690

691
void write_json_file(SimulationData& sd, std::string filename)
10✔
692
{
693
    using json = nlohmann::ordered_json;
694

695
    // Create empty object
696
    json root;
10✔
697

698
    // Write general meta data
699
    {
700
        root["schema_version"] = kSchemaVersion;
10✔
701
        root["number_of_elements"] = sd.get_number_of_elements();
10✔
702
    }
703

704
    // Write parameters
705
    {
706
        json jpar;
10✔
707
        SolTrace::Data::SimulationParameters sim_par = sd.get_simulation_parameters();
10✔
708
        jpar["include_sun_shape_errors"] = sim_par.include_sun_shape_errors;   // bool
10✔
709
        jpar["include_optical_errors"] = sim_par.include_optical_errors;       // bool
10✔
710
        jpar["number_of_rays"] = sim_par.number_of_rays;                       // int
10✔
711
        jpar["max_number_of_rays"] = sim_par.max_number_of_rays;               // int
10✔
712
        jpar["tolerance"] = sim_par.tolerance;                                 // double
10✔
713
        jpar["latitude"] = sim_par.latitude;                                   // double
10✔
714
        jpar["longitude"] = sim_par.longitude;                                 // double
10✔
715
        jpar["seed"] = sim_par.seed;                                           // int
10✔
716

717
        root["simulation_parameters"] = jpar;
10✔
718
    }
10✔
719

720
    // Write ray sources
721
    {
722
        json jsources;
10✔
723
        for (auto it = sd.get_ray_source_iterator(); !sd.is_ray_source_at_end(it); ++it)
19✔
724
        {
725
            json jsrc;
9✔
726

727
            SolTrace::Data::ray_source_id i = it->first;
9✔
728
            auto ray_source = it->second;
9✔
729

730
            // Check source type
731
            if (auto sun_ptr = std::dynamic_pointer_cast<SolTrace::Data::Sun>(ray_source))
9✔
732
            {
733
                jsrc["source_type"] = "Sun";
9✔
734

735
                SolTrace::Data::SunShape shape = sun_ptr->get_shape();
9✔
736
                std::string shape_string = SolTrace::Data::SunShapeMap.at(shape);
9✔
737
                jsrc["my_shape"] = shape_string;                // string
9✔
738
                jsrc["sigma"] = sun_ptr->get_sigma();           // double
9✔
739
                jsrc["half_width"] = sun_ptr->get_half_width(); // double
9✔
740
                jsrc["csr"] = sun_ptr->get_circumsolar_ratio(); // double
9✔
741
                std::vector<double> user_angle, user_intensity;
9✔
742
                sun_ptr->get_user_data(user_angle, user_intensity);
9✔
743
                jsrc["user_angle"] = user_angle;                // vector<double>
9✔
744
                jsrc["user_intensity"] = user_intensity;        // vector<double>
9✔
745

746
                Vector3d pos = sun_ptr->get_position();
9✔
747
                jsrc["pos"] = pos.data;
9✔
748
            }
9✔
749
            else
750
            {
751
                // UNSUPPORTED type
NEW
752
                throw std::runtime_error("Unsupported ray source type");
×
753
            }
9✔
754

755
            jsources[std::to_string(i)] = jsrc;
9✔
756
        }
9✔
757

758
        root["ray_sources"] = jsources;
10✔
759
    }
10✔
760

761
    // Write Elements
762
    {
763
        json jelements_top;
10✔
764
        int i_top = 0;
10✔
765
        for (auto it = sd.get_iterator(); !sd.is_at_end(it); ++it)
2,473✔
766
        {
767
            json jelement;
2,463✔
768
            auto element = it->second;
2,463✔
769

770
            if (element->is_single() && (element->is_top_level() == false))
2,463✔
771
            {
772
                // Skip single elements that are within other stages/composites
773
                continue;
2,452✔
774
            }
775

776
            element->write_json(jelement);
11✔
777

778
            jelements_top[std::to_string(i_top)] = jelement;
11✔
779
            i_top++;
11✔
780
        }
4,915✔
781

782
        root["elements"] = jelements_top;
10✔
783
    }
10✔
784

785
    // Write to disk
786
    std::ofstream ofs(filename, std::ios::out | std::ios::trunc);
10✔
787
    if (!ofs.is_open())
10✔
NEW
788
        throw std::runtime_error("Failure writing json");
×
789
    ofs << root.dump(kJsonIndentSpaces) << '\n';
10✔
790

791
    return;
20✔
792
}
10✔
793

794
void load_json_file(SimulationData& sd, std::string filename)
9✔
795
{
796
    using json = nlohmann::ordered_json;
797

798
    // Clear simulation data
799
    sd.clear();
9✔
800

801
    // Load json file
802
    std::ifstream ifs(filename);
9✔
803
    if (!ifs.is_open())
9✔
NEW
804
        throw std::runtime_error("Failure opening json");
×
805

806
    // Load json from file stream
807
    json root;
9✔
808
    ifs >> root;
9✔
809

810
    // File meta data
811
    std::string schema_version = root.at("schema_version");
9✔
812
    int number_of_elements = root.at("number_of_elements");
9✔
813

814
    // Simulation parameters
815
    SolTrace::Data::SimulationParameters& sim_par = sd.get_simulation_parameters();
9✔
816
    json jpar = root["simulation_parameters"];
9✔
817
    sim_par.include_sun_shape_errors = jpar.at("include_sun_shape_errors");
9✔
818
    sim_par.include_optical_errors = jpar.at("include_optical_errors");
8✔
819
    sim_par.number_of_rays = jpar.at("number_of_rays");
8✔
820
    sim_par.max_number_of_rays = jpar.at("max_number_of_rays");
8✔
821
    sim_par.tolerance = jpar.at("tolerance");
8✔
822
    sim_par.latitude = jpar.at("latitude");
8✔
823
    sim_par.longitude = jpar.at("longitude");
8✔
824
    sim_par.seed = jpar.at("seed");
8✔
825

826
    // Ray sources
827
    json jsources = root["ray_sources"];
8✔
828
    for (auto& [key, jsrc] : jsources.items())
14✔
829
    {
830
        std::string source_type = jsrc.at("source_type");
8✔
831
        if (source_type != "Sun")
8✔
832
        {
833
            // UNSUPPORTED source type
834
            throw std::runtime_error("Unsupported ray source type");
1✔
835
        }
836

837
        std::string shape_string = jsrc.at("my_shape");
7✔
838
        SunShape shape = get_enum_from_string(shape_string, SunShapeMap, SunShape::UNKNOWN);
7✔
839
        if (shape == SunShape::UNKNOWN)
7✔
840
        {
841
            // Error reading sunshape
842
            throw std::runtime_error("Error reading sun shape");
1✔
843
        }
844
        double sigma = json_get_double(jsrc, "sigma");
12✔
845
        double half_width = json_get_double(jsrc, "half_width");
12✔
846
        double csr = json_get_double(jsrc, "csr");
6✔
847

848
        std::vector<double> user_angle = jsrc.at("user_angle");
6✔
849
        std::vector<double> user_intensity = jsrc.at("user_intensity");
6✔
850

851
        std::array<double, 3> pos_arr = jsrc.at("pos").get<std::array<double, 3>>();
6✔
852
        Vector3d pos_vec(pos_arr.data());
6✔
853

854
        // Make sun for simulation data
855
        auto sun = make_ray_source<Sun>();
6✔
856
        sun->set_position(pos_vec);
6✔
857
        sun->set_shape(shape, sigma, half_width, csr, user_angle, user_intensity);
6✔
858
        sd.add_ray_source(sun);
6✔
859
    }
19✔
860

861
    // Elements
862
    json jelements = root["elements"];
6✔
863
    for (auto& [key, jelement] : jelements.items())
14✔
864
    {
865
        // Check if stage
866
        // Note a stage is also a composite, so check stage first
867
        if (jelement.contains("is_stage") && jelement.at("is_stage") == true)
8✔
868
        {
869
            // Make stage
870
            stage_ptr stage = make_stage(jelement);
8✔
871
            sd.add_stage(stage);
8✔
872
        }
8✔
873
        // Composite
NEW
874
        else if (jelement.contains("is_composite") && jelement.at("is_composite") == true)
×
875
        {
NEW
876
            composite_element_ptr comp = make_element<CompositeElement>(jelement);
×
NEW
877
            sd.add_element(comp);
×
NEW
878
        }
×
879
        // Single Element
880
        else
881
        {
NEW
882
            single_element_ptr single = make_element<SingleElement>(jelement);
×
NEW
883
            sd.add_element(single);
×
NEW
884
        }
×
885
    }
6✔
886

887
    return;
12✔
888
}
20✔
889

890
} // namespace SolTrace::Data
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