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

openmc-dev / openmc / 9966309227

17 Jul 2024 12:53AM UTC coverage: 84.815% (+0.008%) from 84.807%
9966309227

push

github

web-flow
Linear Source Random Ray (#3072)

Co-authored-by: John Tramm <john.tramm@gmail.com>
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>

336 of 369 new or added lines in 9 files covered. (91.06%)

36 existing lines in 4 files now uncovered.

49336 of 58169 relevant lines covered (84.81%)

32020493.4 hits per line

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

95.82
/src/random_ray/random_ray.cpp
1
#include "openmc/random_ray/random_ray.h"
2

3
#include "openmc/constants.h"
4
#include "openmc/geometry.h"
5
#include "openmc/message_passing.h"
6
#include "openmc/mgxs_interface.h"
7
#include "openmc/random_ray/flat_source_domain.h"
8
#include "openmc/random_ray/linear_source_domain.h"
9
#include "openmc/search.h"
10
#include "openmc/settings.h"
11
#include "openmc/simulation.h"
12
#include "openmc/source.h"
13

14
namespace openmc {
15

16
//==============================================================================
17
// Non-method functions
18
//==============================================================================
19

20
// returns 1 - exp(-tau)
21
// Equivalent to -(_expm1f(-tau)), but faster
22
// Written by Colin Josey.
23
float cjosey_exponential(float tau)
94,315,260✔
24
{
25
  constexpr float c1n = -1.0000013559236386308f;
94,315,260✔
26
  constexpr float c2n = 0.23151368626911062025f;
94,315,260✔
27
  constexpr float c3n = -0.061481916409314966140f;
94,315,260✔
28
  constexpr float c4n = 0.0098619906458127653020f;
94,315,260✔
29
  constexpr float c5n = -0.0012629460503540849940f;
94,315,260✔
30
  constexpr float c6n = 0.00010360973791574984608f;
94,315,260✔
31
  constexpr float c7n = -0.000013276571933735820960f;
94,315,260✔
32

33
  constexpr float c0d = 1.0f;
94,315,260✔
34
  constexpr float c1d = -0.73151337729389001396f;
94,315,260✔
35
  constexpr float c2d = 0.26058381273536471371f;
94,315,260✔
36
  constexpr float c3d = -0.059892419041316836940f;
94,315,260✔
37
  constexpr float c4d = 0.0099070188241094279067f;
94,315,260✔
38
  constexpr float c5d = -0.0012623388962473160860f;
94,315,260✔
39
  constexpr float c6d = 0.00010361277635498731388f;
94,315,260✔
40
  constexpr float c7d = -0.000013276569500666698498f;
94,315,260✔
41

42
  float x = -tau;
94,315,260✔
43

44
  float den = c7d;
94,315,260✔
45
  den = den * x + c6d;
94,315,260✔
46
  den = den * x + c5d;
94,315,260✔
47
  den = den * x + c4d;
94,315,260✔
48
  den = den * x + c3d;
94,315,260✔
49
  den = den * x + c2d;
94,315,260✔
50
  den = den * x + c1d;
94,315,260✔
51
  den = den * x + c0d;
94,315,260✔
52

53
  float num = c7n;
94,315,260✔
54
  num = num * x + c6n;
94,315,260✔
55
  num = num * x + c5n;
94,315,260✔
56
  num = num * x + c4n;
94,315,260✔
57
  num = num * x + c3n;
94,315,260✔
58
  num = num * x + c2n;
94,315,260✔
59
  num = num * x + c1n;
94,315,260✔
60
  num = num * x;
94,315,260✔
61

62
  return num / den;
94,315,260✔
63
}
64

65
// The below two functions (exponentialG and exponentialG2) were developed
66
// by Colin Josey. The implementation of these functions is closely based
67
// on the OpenMOC versions of these functions. The OpenMOC license is given
68
// below:
69

70
// Copyright (C) 2012-2023 Massachusetts Institute of Technology and OpenMOC
71
// contributors
72
//
73
// Permission is hereby granted, free of charge, to any person obtaining a copy
74
// of this software and associated documentation files (the "Software"), to deal
75
// in the Software without restriction, including without limitation the rights
76
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
77
// copies of the Software, and to permit persons to whom the Software is
78
// furnished to do so, subject to the following conditions:
79
//
80
// The above copyright notice and this permission notice shall be included in
81
// all copies or substantial portions of the Software.
82
//
83
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
84
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
85
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
86
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
87
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
88
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
89
// SOFTWARE.
90

91
// Computes y = 1/x-(1-exp(-x))/x**2 using a 5/6th order rational
92
// approximation. It is accurate to 2e-7 over [0, 1e5]. Developed by Colin
93
// Josey using Remez's algorithm, with original implementation in OpenMOC at:
94
// https://github.com/mit-crpg/OpenMOC/blob/develop/src/exponentials.h
95
float exponentialG(float tau)
156,111,840✔
96
{
97
  // Numerator coefficients in rational approximation for 1/x - (1 - exp(-x)) /
98
  // x^2
99
  constexpr float d0n = 0.5f;
156,111,840✔
100
  constexpr float d1n = 0.176558112351595f;
156,111,840✔
101
  constexpr float d2n = 0.04041584305811143f;
156,111,840✔
102
  constexpr float d3n = 0.006178333902037397f;
156,111,840✔
103
  constexpr float d4n = 0.0006429894635552992f;
156,111,840✔
104
  constexpr float d5n = 0.00006064409107557148f;
156,111,840✔
105

106
  // Denominator coefficients in rational approximation for 1/x - (1 - exp(-x))
107
  // / x^2
108
  constexpr float d0d = 1.0f;
156,111,840✔
109
  constexpr float d1d = 0.6864462055546078f;
156,111,840✔
110
  constexpr float d2d = 0.2263358514260129f;
156,111,840✔
111
  constexpr float d3d = 0.04721469893686252f;
156,111,840✔
112
  constexpr float d4d = 0.006883236664917246f;
156,111,840✔
113
  constexpr float d5d = 0.0007036272419147752f;
156,111,840✔
114
  constexpr float d6d = 0.00006064409107557148f;
156,111,840✔
115

116
  float x = tau;
156,111,840✔
117

118
  float num = d5n;
156,111,840✔
119
  num = num * x + d4n;
156,111,840✔
120
  num = num * x + d3n;
156,111,840✔
121
  num = num * x + d2n;
156,111,840✔
122
  num = num * x + d1n;
156,111,840✔
123
  num = num * x + d0n;
156,111,840✔
124

125
  float den = d6d;
156,111,840✔
126
  den = den * x + d5d;
156,111,840✔
127
  den = den * x + d4d;
156,111,840✔
128
  den = den * x + d3d;
156,111,840✔
129
  den = den * x + d2d;
156,111,840✔
130
  den = den * x + d1d;
156,111,840✔
131
  den = den * x + d0d;
156,111,840✔
132

133
  return num / den;
156,111,840✔
134
}
135

136
// Computes G2 : y = 2/3 - (1 + 2/x) * (1/x + 0.5 - (1 + 1/x) * (1-exp(-x)) /
137
// x) using a 5/5th order rational approximation. It is accurate to 1e-6 over
138
// [0, 1e6]. Developed by Colin Josey using Remez's algorithm, with original
139
// implementation in OpenMOC at:
140
// https://github.com/mit-crpg/OpenMOC/blob/develop/src/exponentials.h
141
float exponentialG2(float tau)
156,111,840✔
142
{
143

144
  // Coefficients for numerator in rational approximation
145
  constexpr float g1n = -0.08335775885589858f;
156,111,840✔
146
  constexpr float g2n = -0.003603942303847604f;
156,111,840✔
147
  constexpr float g3n = 0.0037673183263550827f;
156,111,840✔
148
  constexpr float g4n = 0.00001124183494990467f;
156,111,840✔
149
  constexpr float g5n = 0.00016837426505799449f;
156,111,840✔
150

151
  // Coefficients for denominator in rational approximation
152
  constexpr float g1d = 0.7454048371823628f;
156,111,840✔
153
  constexpr float g2d = 0.23794300531408347f;
156,111,840✔
154
  constexpr float g3d = 0.05367250964303789f;
156,111,840✔
155
  constexpr float g4d = 0.006125197988351906f;
156,111,840✔
156
  constexpr float g5d = 0.0010102514456857377f;
156,111,840✔
157

158
  float x = tau;
156,111,840✔
159

160
  float num = g5n;
156,111,840✔
161
  num = num * x + g4n;
156,111,840✔
162
  num = num * x + g3n;
156,111,840✔
163
  num = num * x + g2n;
156,111,840✔
164
  num = num * x + g1n;
156,111,840✔
165
  num = num * x;
156,111,840✔
166

167
  float den = g5d;
156,111,840✔
168
  den = den * x + g4d;
156,111,840✔
169
  den = den * x + g3d;
156,111,840✔
170
  den = den * x + g2d;
156,111,840✔
171
  den = den * x + g1d;
156,111,840✔
172
  den = den * x + 1.0f;
156,111,840✔
173

174
  return num / den;
156,111,840✔
175
}
176

177
//==============================================================================
178
// RandomRay implementation
179
//==============================================================================
180

181
// Static Variable Declarations
182
double RandomRay::distance_inactive_;
183
double RandomRay::distance_active_;
184
unique_ptr<Source> RandomRay::ray_source_;
185
RandomRaySourceShape RandomRay::source_shape_ {RandomRaySourceShape::FLAT};
186

187
RandomRay::RandomRay()
123,600✔
188
  : angular_flux_(data::mg.num_energy_groups_),
123,600✔
189
    delta_psi_(data::mg.num_energy_groups_),
123,600✔
190
    negroups_(data::mg.num_energy_groups_)
247,200✔
191
{
192
  if (source_shape_ == RandomRaySourceShape::LINEAR ||
123,600✔
193
      source_shape_ == RandomRaySourceShape::LINEAR_XY) {
100,800✔
194
    delta_moments_.resize(negroups_);
45,600✔
195
  }
196
}
123,600✔
197

198
RandomRay::RandomRay(uint64_t ray_id, FlatSourceDomain* domain) : RandomRay()
123,600✔
199
{
200
  initialize_ray(ray_id, domain);
123,600✔
201
}
123,600✔
202

203
// Transports ray until termination criteria are met
204
uint64_t RandomRay::transport_history_based_single_ray()
123,600✔
205
{
206
  using namespace openmc;
207
  while (alive()) {
59,619,564✔
208
    event_advance_ray();
59,619,564✔
209
    if (!alive())
59,619,564✔
210
      break;
123,600✔
211
    event_cross_surface();
59,495,964✔
212
  }
213

214
  return n_event();
123,600✔
215
}
216

217
// Transports ray across a single source region
218
void RandomRay::event_advance_ray()
59,619,564✔
219
{
220
  // Find the distance to the nearest boundary
221
  boundary() = distance_to_boundary(*this);
59,619,564✔
222
  double distance = boundary().distance;
59,619,564✔
223

224
  if (distance <= 0.0) {
59,619,564✔
225
    mark_as_lost("Negative transport distance detected for particle " +
×
226
                 std::to_string(id()));
×
227
    return;
×
228
  }
229

230
  if (is_active_) {
59,619,564✔
231
    // If the ray is in the active length, need to check if it has
232
    // reached its maximum termination distance. If so, reduce
233
    // the ray traced length so that the ray does not overrun the
234
    // maximum numerical length (so as to avoid numerical bias).
235
    if (distance_travelled_ + distance >= distance_active_) {
49,639,932✔
236
      distance = distance_active_ - distance_travelled_;
123,600✔
237
      wgt() = 0.0;
123,600✔
238
    }
239

240
    distance_travelled_ += distance;
49,639,932✔
241
    attenuate_flux(distance, true);
49,639,932✔
242
  } else {
243
    // If the ray is still in the dead zone, need to check if it
244
    // has entered the active phase. If so, split into two segments (one
245
    // representing the final part of the dead zone, the other representing the
246
    // first part of the active length) and attenuate each. Otherwise, if the
247
    // full length of the segment is within the dead zone, attenuate as normal.
248
    if (distance_travelled_ + distance >= distance_inactive_) {
9,979,632✔
249
      is_active_ = true;
123,600✔
250
      double distance_dead = distance_inactive_ - distance_travelled_;
123,600✔
251
      attenuate_flux(distance_dead, false);
123,600✔
252

253
      double distance_alive = distance - distance_dead;
123,600✔
254

255
      // Ensure we haven't travelled past the active phase as well
256
      if (distance_alive > distance_active_) {
123,600✔
257
        distance_alive = distance_active_;
×
258
        wgt() = 0.0;
×
259
      }
260

261
      attenuate_flux(distance_alive, true);
123,600✔
262
      distance_travelled_ = distance_alive;
123,600✔
263
    } else {
264
      distance_travelled_ += distance;
9,856,032✔
265
      attenuate_flux(distance, false);
9,856,032✔
266
    }
267
  }
268

269
  // Advance particle
270
  for (int j = 0; j < n_coord(); ++j) {
237,835,140✔
271
    coord(j).r += distance * coord(j).u;
178,215,576✔
272
  }
273
}
274

275
void RandomRay::attenuate_flux(double distance, bool is_active)
59,743,164✔
276
{
277
  switch (source_shape_) {
59,743,164✔
278
  case RandomRaySourceShape::FLAT:
30,753,948✔
279
    attenuate_flux_flat_source(distance, is_active);
30,753,948✔
280
    break;
30,753,948✔
281
  case RandomRaySourceShape::LINEAR:
28,989,216✔
282
  case RandomRaySourceShape::LINEAR_XY:
283
    attenuate_flux_linear_source(distance, is_active);
28,989,216✔
284
    break;
28,989,216✔
NEW
285
  default:
×
NEW
286
    fatal_error("Unknown source shape for random ray transport.");
×
287
  }
288
}
59,743,164✔
289

290
// This function forms the inner loop of the random ray transport process.
291
// It is responsible for several tasks. Based on the incoming angular flux
292
// of the ray and the source term in the region, the outgoing angular flux
293
// is computed. The delta psi between the incoming and outgoing fluxes is
294
// contributed to the estimate of the total scalar flux in the source region.
295
// Additionally, the contribution of the ray path to the stochastically
296
// estimated volume is also kept track of. All tasks involving writing
297
// to the data for the source region are done with a lock over the entire
298
// source region.  Locks are used instead of atomics as all energy groups
299
// must be written, such that locking once is typically much more efficient
300
// than use of many atomic operations corresponding to each energy group
301
// individually (at least on CPU). Several other bookkeeping tasks are also
302
// performed when inside the lock.
303
void RandomRay::attenuate_flux_flat_source(double distance, bool is_active)
30,753,948✔
304
{
305
  // The number of geometric intersections is counted for reporting purposes
306
  n_event()++;
30,753,948✔
307

308
  // Determine source region index etc.
309
  int i_cell = lowest_coord().cell;
30,753,948✔
310

311
  // The source region is the spatial region index
312
  int64_t source_region =
313
    domain_->source_region_offsets_[i_cell] + cell_instance();
30,753,948✔
314

315
  // The source element is the energy-specific region index
316
  int64_t source_element = source_region * negroups_;
30,753,948✔
317
  int material = this->material();
30,753,948✔
318

319
  // Temperature and angle indices, if using multiple temperature
320
  // data sets and/or anisotropic data sets.
321
  // TODO: Currently assumes we are only using single temp/single
322
  // angle data.
323
  const int t = 0;
30,753,948✔
324
  const int a = 0;
30,753,948✔
325

326
  // MOC incoming flux attenuation + source contribution/attenuation equation
327
  for (int g = 0; g < negroups_; g++) {
125,069,208✔
328
    float sigma_t = data::mg.macro_xs_[material].get_xs(
94,315,260✔
329
      MgxsType::TOTAL, g, NULL, NULL, NULL, t, a);
94,315,260✔
330
    float tau = sigma_t * distance;
94,315,260✔
331
    float exponential = cjosey_exponential(tau); // exponential = 1 - exp(-tau)
94,315,260✔
332
    float new_delta_psi =
333
      (angular_flux_[g] - domain_->source_[source_element + g]) * exponential;
94,315,260✔
334
    delta_psi_[g] = new_delta_psi;
94,315,260✔
335
    angular_flux_[g] -= new_delta_psi;
94,315,260✔
336
  }
337

338
  // If ray is in the active phase (not in dead zone), make contributions to
339
  // source region bookkeeping
340
  if (is_active) {
30,753,948✔
341

342
    // Aquire lock for source region
343
    domain_->lock_[source_region].lock();
25,628,868✔
344

345
    // Accumulate delta psi into new estimate of source region flux for
346
    // this iteration
347
    for (int g = 0; g < negroups_; g++) {
104,198,184✔
348
      domain_->scalar_flux_new_[source_element + g] += delta_psi_[g];
78,569,316✔
349
    }
350

351
    // If the source region hasn't been hit yet this iteration,
352
    // indicate that it now has
353
    if (domain_->was_hit_[source_region] == 0) {
25,628,868✔
354
      domain_->was_hit_[source_region] = 1;
1,595,421✔
355
    }
356

357
    // Accomulate volume (ray distance) into this iteration's estimate
358
    // of the source region's volume
359
    domain_->volume_[source_region] += distance;
25,628,868✔
360

361
    // Tally valid position inside the source region (e.g., midpoint of
362
    // the ray) if not done already
363
    if (!domain_->position_recorded_[source_region]) {
25,628,868✔
364
      Position midpoint = r() + u() * (distance / 2.0);
159,582✔
365
      domain_->position_[source_region] = midpoint;
159,582✔
366
      domain_->position_recorded_[source_region] = 1;
159,582✔
367
    }
368

369
    // Release lock
370
    domain_->lock_[source_region].unlock();
25,628,868✔
371
  }
372
}
30,753,948✔
373

374
void RandomRay::attenuate_flux_linear_source(double distance, bool is_active)
28,989,216✔
375
{
376
  // Cast domain to LinearSourceDomain
377
  LinearSourceDomain* domain = dynamic_cast<LinearSourceDomain*>(domain_);
28,989,216✔
378
  if (!domain) {
28,989,216✔
NEW
379
    fatal_error("RandomRay::attenuate_flux_linear_source() called with "
×
380
                "non-LinearSourceDomain domain.");
381
  }
382

383
  // The number of geometric intersections is counted for reporting purposes
384
  n_event()++;
28,989,216✔
385

386
  // Determine source region index etc.
387
  int i_cell = lowest_coord().cell;
28,989,216✔
388

389
  // The source region is the spatial region index
390
  int64_t source_region =
391
    domain_->source_region_offsets_[i_cell] + cell_instance();
28,989,216✔
392

393
  // The source element is the energy-specific region index
394
  int64_t source_element = source_region * negroups_;
28,989,216✔
395
  int material = this->material();
28,989,216✔
396

397
  // Temperature and angle indices, if using multiple temperature
398
  // data sets and/or anisotropic data sets.
399
  // TODO: Currently assumes we are only using single temp/single
400
  // angle data.
401
  const int t = 0;
28,989,216✔
402
  const int a = 0;
28,989,216✔
403

404
  Position& centroid = domain->centroid_[source_region];
28,989,216✔
405
  Position midpoint = r() + u() * (distance / 2.0);
28,989,216✔
406

407
  // Determine the local position of the midpoint and the ray origin
408
  // relative to the source region's centroid
409
  Position rm_local;
28,989,216✔
410
  Position r0_local;
28,989,216✔
411

412
  // In the first few iterations of the simulation, the source region
413
  // may not yet have had any ray crossings, in which case there will
414
  // be no estimate of its centroid. We detect this by checking if it has
415
  // any accumulated volume. If its volume is zero, just use the midpoint
416
  // of the ray as the region's centroid.
417
  if (domain->volume_t_[source_region]) {
28,989,216✔
418
    rm_local = midpoint - centroid;
26,081,088✔
419
    r0_local = r() - centroid;
26,081,088✔
420
  } else {
421
    rm_local = {0.0, 0.0, 0.0};
2,908,128✔
422
    r0_local = -u() * 0.5 * distance;
2,908,128✔
423
  }
424
  double distance_2 = distance * distance;
28,989,216✔
425

426
  // Linear Source MOC incoming flux attenuation + source
427
  // contribution/attenuation equation
428
  for (int g = 0; g < negroups_; g++) {
185,101,056✔
429

430
    // Compute tau, the optical thickness of the ray segment
431
    float sigma_t = data::mg.macro_xs_[material].get_xs(
156,111,840✔
432
      MgxsType::TOTAL, g, NULL, NULL, NULL, t, a);
156,111,840✔
433
    float tau = sigma_t * distance;
156,111,840✔
434

435
    // If tau is very small, set it to zero to avoid numerical issues.
436
    // The following computations will still work with tau = 0.
437
    if (tau < 1.0e-8f) {
156,111,840✔
NEW
438
      tau = 0.0f;
×
439
    }
440

441
    // Compute linear source terms, spatial and directional (dir),
442
    // calculated from the source gradients dot product with local centroid
443
    // and direction, respectively.
444
    float spatial_source =
445
      domain_->source_[source_element + g] +
156,111,840✔
446
      rm_local.dot(domain->source_gradients_[source_element + g]);
156,111,840✔
447
    float dir_source = u().dot(domain->source_gradients_[source_element + g]);
156,111,840✔
448

449
    float gn = exponentialG(tau);
156,111,840✔
450
    float f1 = 1.0f - tau * gn;
156,111,840✔
451
    float f2 = (2.0f * gn - f1) * distance_2;
156,111,840✔
452
    float new_delta_psi = (angular_flux_[g] - spatial_source) * f1 * distance -
156,111,840✔
453
                          0.5 * dir_source * f2;
156,111,840✔
454

455
    float h1 = f1 - gn;
156,111,840✔
456
    float g1 = 0.5f - h1;
156,111,840✔
457
    float g2 = exponentialG2(tau);
156,111,840✔
458
    g1 = g1 * spatial_source;
156,111,840✔
459
    g2 = g2 * dir_source * distance * 0.5f;
156,111,840✔
460
    h1 = h1 * angular_flux_[g];
156,111,840✔
461
    h1 = (g1 + g2 + h1) * distance_2;
156,111,840✔
462
    spatial_source = spatial_source * distance + new_delta_psi;
156,111,840✔
463

464
    // Store contributions for this group into arrays, so that they can
465
    // be accumulated into the source region's estimates inside of the locked
466
    // region.
467
    delta_psi_[g] = new_delta_psi;
156,111,840✔
468
    delta_moments_[g] = r0_local * spatial_source + u() * h1;
156,111,840✔
469

470
    // Update the angular flux for this group
471
    angular_flux_[g] -= new_delta_psi * sigma_t;
156,111,840✔
472

473
    // If 2D mode is enabled, the z-component of the flux moments is forced
474
    // to zero
475
    if (source_shape_ == RandomRaySourceShape::LINEAR_XY) {
156,111,840✔
476
      delta_moments_[g].z = 0.0;
78,055,920✔
477
    }
478
  }
479

480
  // If ray is in the active phase (not in dead zone), make contributions to
481
  // source region bookkeeping
482
  if (is_active) {
28,989,216✔
483
    // Compute an estimate of the spatial moments matrix for the source
484
    // region based on parameters from this ray's crossing
485
    MomentMatrix moment_matrix_estimate;
486
    moment_matrix_estimate.compute_spatial_moments_matrix(
24,134,664✔
487
      rm_local, u(), distance);
24,134,664✔
488

489
    // Aquire lock for source region
490
    domain_->lock_[source_region].lock();
24,134,664✔
491

492
    // Accumulate deltas into the new estimate of source region flux for this
493
    // iteration
494
    for (int g = 0; g < negroups_; g++) {
154,150,224✔
495
      domain_->scalar_flux_new_[source_element + g] += delta_psi_[g];
130,015,560✔
496
      domain->flux_moments_new_[source_element + g] += delta_moments_[g];
130,015,560✔
497
    }
498

499
    // Accumulate the volume (ray segment distance), centroid, and spatial
500
    // momement estimates into the running totals for the iteration for this
501
    // source region. The centroid and spatial momements estimates are scaled by
502
    // the ray segment length as part of length averaging of the estimates.
503
    domain_->volume_[source_region] += distance;
24,134,664✔
504
    domain->centroid_iteration_[source_region] += midpoint * distance;
24,134,664✔
505
    moment_matrix_estimate *= distance;
24,134,664✔
506
    domain->mom_matrix_[source_region] += moment_matrix_estimate;
24,134,664✔
507

508
    // If the source region hasn't been hit yet this iteration,
509
    // indicate that it now has
510
    if (domain_->was_hit_[source_region] == 0) {
24,134,664✔
511
      domain_->was_hit_[source_region] = 1;
670,110✔
512
    }
513

514
    // Tally valid position inside the source region (e.g., midpoint of
515
    // the ray) if not done already
516
    if (!domain_->position_recorded_[source_region]) {
24,134,664✔
517
      domain_->position_[source_region] = midpoint;
67,038✔
518
      domain_->position_recorded_[source_region] = 1;
67,038✔
519
    }
520

521
    // Release lock
522
    domain_->lock_[source_region].unlock();
24,134,664✔
523
  }
524
}
28,989,216✔
525

526
void RandomRay::initialize_ray(uint64_t ray_id, FlatSourceDomain* domain)
123,600✔
527
{
528
  domain_ = domain;
123,600✔
529

530
  // Reset particle event counter
531
  n_event() = 0;
123,600✔
532

533
  is_active_ = (distance_inactive_ <= 0.0);
123,600✔
534

535
  wgt() = 1.0;
123,600✔
536

537
  // set identifier for particle
538
  id() = simulation::work_index[mpi::rank] + ray_id;
123,600✔
539

540
  // set random number seed
541
  int64_t particle_seed =
542
    (simulation::current_batch - 1) * settings::n_particles + id();
123,600✔
543
  init_particle_seeds(particle_seed, seeds());
123,600✔
544
  stream() = STREAM_TRACKING;
123,600✔
545

546
  // Sample from ray source distribution
547
  SourceSite site {ray_source_->sample(current_seed())};
123,600✔
548
  site.E = lower_bound_index(
123,600✔
549
    data::mg.rev_energy_bins_.begin(), data::mg.rev_energy_bins_.end(), site.E);
550
  site.E = negroups_ - site.E - 1.;
123,600✔
551
  this->from_source(&site);
123,600✔
552

553
  // Locate ray
554
  if (lowest_coord().cell == C_NONE) {
123,600✔
555
    if (!exhaustive_find_cell(*this)) {
123,600✔
556
      this->mark_as_lost(
×
557
        "Could not find the cell containing particle " + std::to_string(id()));
×
558
    }
559

560
    // Set birth cell attribute
561
    if (cell_born() == C_NONE)
123,600✔
562
      cell_born() = lowest_coord().cell;
123,600✔
563
  }
564

565
  // Initialize ray's starting angular flux to starting location's isotropic
566
  // source
567
  int i_cell = lowest_coord().cell;
123,600✔
568
  int64_t source_region_idx =
569
    domain_->source_region_offsets_[i_cell] + cell_instance();
123,600✔
570

571
  for (int g = 0; g < negroups_; g++) {
463,200✔
572
    angular_flux_[g] = domain_->source_[source_region_idx * negroups_ + g];
339,600✔
573
  }
574
}
123,600✔
575

576
} // namespace openmc
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