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

NREL / SolTrace / 18889752882

28 Oct 2025 09:28PM UTC coverage: 89.87% (-0.08%) from 89.946%
18889752882

Pull #76

github

web-flow
Merge e6c58895d into f6f121007
Pull Request #76: Fix NativeRunner ray tracing failure for parabola surface

944 of 994 new or added lines in 28 files covered. (94.97%)

2 existing lines in 1 file now uncovered.

4418 of 4916 relevant lines covered (89.87%)

10138914.15 hits per line

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

78.76
/coretrace/simulation_runner/native_runner/tracing_errors.cpp
1

2
#include "tracing_errors.hpp"
3

4
#include "simulation_data_export.hpp"
5

6
namespace SolTrace::NativeRunner {
7

8
void SurfaceNormalErrors(MTRand &myrng,
330,471✔
9
                                                 double CosIn[3],
10
                                                 //  TOpticalProperties *OptProperties,
11
                                                 const OpticalProperties *OptProperties,
12
                                                 double CosOut[3]) noexcept(false) // throw(nanexcept)
13
{
14

15
        /*{Purpose:  To add error terms to the surface normal vector at the surface in question
16

17
                           Input - Seed    = Seed for RNG
18
                                           CosIn   = Direction cosine vector of surface normal to which errors will be applied.
19
                                           Element = Element data record
20
                                           DFXYZ   = surface normal vector at interaction point
21

22
                           Output - CosOut  = Output direction cosine vector of surface normal after error terms have been included
23
                                           }*/
24

25
        int i = 0;
330,471✔
26
        double Origin[3] = {0.0, 0.0, 0.0},
330,471✔
27
                   Euler[3] = {0.0, 0.0, 0.0};
330,471✔
28
        double PosIn[3] = {0.0, 0.0, 0.0},
330,471✔
29
                   PosOut[3] = {0.0, 0.0, 0.0};
330,471✔
30
        // char dist = ' ';
31
        DistributionType dist;
32
        double delop = 0.0, delop3 = 0.0, thetax = 0.0,
330,471✔
33
                   thetay = 0.0, ttheta = 0.0, theta2 = 0.0,
330,471✔
34
                   phi = 0.0, theta = 0.0;
330,471✔
35
        double RRefToLoc[3][3] = {{0.0, 0.0, 0.0},
330,471✔
36
                                                          {0.0, 0.0, 0.0},
37
                                                          {0.0, 0.0, 0.0}};
38
        double RLocToRef[3][3] = {{0.0, 0.0, 0.0},
330,471✔
39
                                                          {0.0, 0.0, 0.0},
40
                                                          {0.0, 0.0, 0.0}};
41

42
        if (CosIn[2] == 0.0)
330,471✔
43
        {
44
                if (CosIn[0] == 0.0)
×
45
                {
46
                        Euler[0] = 0.0;
×
47
                        Euler[1] = PI / 2.0;
×
48
                        goto Label_9;
×
49
                }
50
                else
51
                {
52
                        Euler[0] = PI / 2.0;
×
53
                        goto Label_8;
×
54
                }
55
        }
56

57
        Euler[0] = atan2(CosIn[0], CosIn[2]);
330,471✔
58
Label_8:
330,471✔
59
        Euler[1] = atan2(CosIn[1], sqrt(CosIn[0] * CosIn[0] + CosIn[2] * CosIn[2]));
330,471✔
60
Label_9:
330,471✔
61
        Euler[2] = 0.0;
330,471✔
62

63
        CalculateTransformMatrices(Euler, RRefToLoc, RLocToRef);
330,471✔
64

65
        // TODO: Add distribution type to optical properties
66
        // dist = OptProperties->DistributionType;
67
        dist = OptProperties->error_distribution_type;
330,471✔
68
        // delop = OptProperties->RMSSlopeError / 1000.0;
69
        delop = OptProperties->slope_error / 1000.0;
330,471✔
70

71
        int nninner = 0;
330,471✔
72
        switch (dist)
330,471✔
73
        {
74
        // case 'g':
75
        // case 'G':
76
        case DistributionType::GAUSSIAN:
330,471✔
77
                // gaussian distribution
78
                thetax = myrng.randNorm(0., delop);
330,471✔
79
                thetay = myrng.randNorm(0., delop);
330,471✔
80

81
                theta2 = thetax * thetax + thetay * thetay;
330,471✔
82

83
                break;
330,471✔
84

85
        // case 'p':
86
        // case 'P':
87
        case DistributionType::PILLBOX:
×
88
                // pillbox distribution
89
                do
90
                {
91
                        thetax = 2.0 * delop * myrng() - delop;
×
92
                        thetay = 2.0 * delop * myrng() - delop;
×
93
                        theta2 = thetax * thetax + thetay * thetay;
×
94
                } while (theta2 > (delop * delop));
×
95

96
                break;
×
97
        default:
×
98
                // TODO: Need an error here.
99
                break;
×
100
        }
101

102
        /* {Transform to local coordinate system of ray to set up rotation matrices for coord and inverse
103
           transforms} */
104

105
        TransformToLocal(PosIn, CosIn, Origin, RRefToLoc, PosOut, CosOut);
330,471✔
106

107
        /* {Generate errors in terms of direction cosines in local ray coordinate system} */
108
        theta = sqrt(theta2);
330,471✔
109
        // phi = atan2(thetay, thetax); //This function appears to  present irregularities that bias results incorrectly for small values of thetay or thetax
110
        phi = myrng() * 2.0 * 3.1415926535897932385; // Therefore have chosen to randomize phi rather than calculate from randomized theta components
330,471✔
111
                                                                                                 //  obtained from the distribution. The two approaches are equivalent save for this issue with
112
                                                                                                 //  arctan2.      wendelin 01-12-11
113

114
        CosOut[0] = sin(theta) * cos(phi);
330,471✔
115
        CosOut[1] = sin(theta) * sin(phi);
330,471✔
116
        CosOut[2] = cos(theta);
330,471✔
117

118
        for (i = 0; i < 3; i++)
1,321,884✔
119
        {
120
                PosIn[i] = PosOut[i];
991,413✔
121
                CosIn[i] = CosOut[i];
991,413✔
122
        }
123

124
        /*{Transform perturbed ray back to element system}*/
125
        TransformToReference(PosIn, CosIn, Origin, RLocToRef, PosOut, CosOut);
330,471✔
126
}
330,471✔
127

128
void Errors(
479,680✔
129
        MTRand &myrng,
130
        double CosIn[3],
131
        int Source,
132
        TSun *Sun,
133
        // telement_ptr Element,
134
        const OpticalProperties *OptProperties,
135
        // TElement *Element,
136
        // TOpticalProperties *OptProperties,
137
        double CosOut[3],
138
        double DFXYZ[3])
139
{
140
        /*{Purpose:  To add error terms to the perturbed ray at the surface in question
141

142
                           Input - Seed    = Seed for RNG
143
                                           CosIn   = Direction cosine vector of ray to which errors will be applied.
144
                                                                  If Source below is 1 (i.e. sunshape) then this ray vector is before interaction with element surface
145
                                                                  If Source below is 2 (i.e. surface error) then this ray vector is after interaction with element surface
146
                                                                        (i.e. reflected ray or transmitted ray)
147

148
                                           Source  = Source indicator flag
149
                                                           = 1 for Sunshape error (Can be gaussian, pillbox or profile data distribution)
150
                                                           = 2 for surface errors (Can be gaussian or pillbox distribution)
151
                                           Sun     = Sun data record
152
                                           Element = Element data record
153
                                           DFXYZ   = surface normal vector at interaction point
154

155
                           Output - CosOut  = Output direction cosine vector of ray after error terms have been included
156
                                           }*/
157

158
        double Origin[3] = {0.0, 0.0, 0.0};
479,680✔
159
        double Euler[3] = {0.0, 0.0, 0.0};
479,680✔
160
        double PosIn[3] = {0.0, 0.0, 0.0};
479,680✔
161
        double PosOut[3] = {0.0, 0.0, 0.0};
479,680✔
162
        // char dist = 'g';
163
        DistributionType dist = DistributionType::GAUSSIAN;
479,680✔
164
        double delop = 0, delop3 = 0, thetax = 0, thetay = 0, ttheta = 0, theta2 = 0, phi = 0, theta = 0, stest = 0;
479,680✔
165
        uint_fast64_t i;
166
        double RRefToLoc[3][3] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
479,680✔
167
        double RLocToRef[3][3] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
479,680✔
168

169
        // TODO: Rework function without goto statements...
170

171
        if (CosIn[2] == 0.0)
479,680✔
172
        {
173
                if (CosIn[0] == 0.0)
×
174
                {
175
                        Euler[0] = 0.0;
×
176
                        Euler[1] = PI / 2.0;
×
177
                        goto Label_9;
×
178
                }
179
                else
180
                {
181
                        Euler[0] = PI / 2.0;
×
182
                        goto Label_8;
×
183
                }
184
        }
185

186
        Euler[0] = atan2(CosIn[0], CosIn[2]);
479,680✔
187

188
Label_8:
479,680✔
189
        Euler[1] = atan2(CosIn[1], sqrt(CosIn[0] * CosIn[0] + CosIn[2] * CosIn[2]));
479,680✔
190

191
Label_9:
479,680✔
192
        Euler[2] = 0.0;
479,680✔
193

194
        CalculateTransformMatrices(Euler, RRefToLoc, RLocToRef);
479,680✔
195

196
        // g,p,d
197
        if (Source == 1)
479,680✔
198
        {
199
                dist = Sun->ShapeIndex; // sun
149,209✔
200
                delop = Sun->Sigma / 1000.0;
149,209✔
201
        }
202

203
        if (Source == 2)
479,680✔
204
        {
205
                // dist = OptProperties->DistributionType; // errors
206
                dist = OptProperties->error_distribution_type;
330,471✔
207
                // // delop = sqrt(4.0*sqr(OptProperties->RMSSlopeError)+sqr(OptProperties->RMSSpecError))/1000.0;
208
                // delop = OptProperties->RMSSpecError / 1000.0;
209
                delop = OptProperties->specularity_error / 1000.0;
330,471✔
210
        }
211

212
        unsigned int maxcall = 0;
479,680✔
213

214
Label_50:
479,680✔
215
        switch (dist)
479,680✔
216
        {
217
        // case 'g':
218
        // case 'G': // gaussian distribution
219
        case DistributionType::GAUSSIAN:
340,471✔
220
                thetax = myrng.randNorm(0., delop);
340,471✔
221
                thetay = myrng.randNorm(0., delop);
340,471✔
222

223
                theta2 = thetax * thetax + thetay * thetay;
340,471✔
224

225
                break;
340,471✔
226

227
        // case 'p':
228
        // case 'P': // pillbox distribution
229
        case DistributionType::PILLBOX:
230
        Label_200:
149,997✔
231
                thetax = 2.0 * delop * myrng() - delop;
149,997✔
232
                thetay = 2.0 * delop * myrng() - delop;
149,997✔
233
                theta2 = thetax * thetax + thetay * thetay;
149,997✔
234
                if (theta2 > (delop * delop))
149,997✔
235
                        goto Label_200;
10,788✔
236
                break;
139,209✔
237

238
                // TODO: Do we need to the below code?
239
                // case 'd':
240
                // case 'D': // sunshape data  (for sunshape only)
241
                // Label_300:
242
                //         thetax = 2.0 * Sun->MaxAngle * myrng() - Sun->MaxAngle;
243
                //         thetay = 2.0 * Sun->MaxAngle * myrng() - Sun->MaxAngle;
244
                //         theta2 = thetax * thetax + thetay * thetay;
245
                //         theta = sqrt(theta2); // wendelin 1-9-12  do the test once on theta NOT individually on thetax and thetay as before
246

247
                //         i = 0;
248
                //         while (i < Sun->SunShapeAngle.size() - 1 && Sun->SunShapeAngle[i] < theta)
249
                //                 i++;
250

251
                //         if (i == 0)
252
                //                 stest = Sun->SunShapeIntensity[0];
253
                //         else // change from average interpolation between data points to linear interpolation  12-20-11 wendelin
254
                //                 stest = Sun->SunShapeIntensity[i - 1] + (Sun->SunShapeIntensity[i] - Sun->SunShapeIntensity[i - 1]) * (theta - Sun->SunShapeAngle[i - 1]) /
255
                //                                                                                                         (Sun->SunShapeAngle[i] - Sun->SunShapeAngle[i - 1]);
256
                //         // stest = (Sun->SunShapeIntensity[i] + Sun->SunShapeIntensity[i-1])/2.0;
257

258
                //         if (myrng() > (stest / Sun->MaxIntensity))
259
                //                 goto Label_300;
260

261
                //         if (theta2 > (Sun->MaxAngle * Sun->MaxAngle))
262
                //                 goto Label_300;
263
                //         theta2 = theta2 / 1000000.0;
264
                //         break;
265

266
                // case 'f': // gray diffuse distribution
267
                // case 'F':
268
                //         theta2 = pow(asin(sqrt(myrng())), 2);
269
                //         break;
270
        default:
×
271
                // TODO: Add error message here.
272
                break;
×
273
        }
274

275
        /*{Transform to local coordinate system of ray to set up rotation matrices for coord and inverse
276
          transforms}*/
277
        TransformToLocal(PosIn, CosIn, Origin, RRefToLoc, PosOut, CosOut);
479,680✔
278

279
        // {Generate errors in terms of direction cosines in local ray coordinate system}
280
        theta = sqrt(theta2);
479,680✔
281

282
        // phi = atan2(thetay, thetax); //This function appears to  present irregularities that bias results incorrectly for small values of thetay or thetax
283
        phi = myrng() * 2.0 * 3.1415926535897932385; // Therefore have chosen to randomize phi rather than calculate from randomized theta components
479,680✔
284
                                                                                                 //  obtained from the distribution. The two approaches are equivalent save for this issue with
285
                                                                                                 //  arctan2.      wendelin 01-12-11
286

287
        CosOut[0] = sin(theta) * cos(phi);
479,680✔
288
        CosOut[1] = sin(theta) * sin(phi);
479,680✔
289
        CosOut[2] = cos(theta);
479,680✔
290

291
        for (i = 0; i < 3; i++)
1,918,720✔
292
        {
293
                PosIn[i] = PosOut[i];
1,439,040✔
294
                CosIn[i] = CosOut[i];
1,439,040✔
295
        }
296

297
        //{Transform perturbed ray back to element system}
298
        TransformToReference(PosIn, CosIn, Origin, RLocToRef, PosOut, CosOut);
479,680✔
299

300
        /*{If reflection error applicaton and new ray direction (after errors) physically goes through opaque surface,
301
        then go back and get new perturbation 06-12-07}*/
302
        if ((Source == 2) &&
810,151✔
303
                (OptProperties->my_type == InteractionType::REFLECTION) &&
330,471✔
304
                (DOT(CosOut, DFXYZ) < 0) &&
810,151✔
UNCOV
305
                maxcall++ < 50000)
×
306
        {
UNCOV
307
                goto Label_50;
×
308
        }
309
}
479,680✔
310
// End of Procedure--------------------------------------------------------------
311

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