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

openmc-dev / openmc / 23148760986

16 Mar 2026 02:27PM UTC coverage: 81.202% (-0.2%) from 81.435%
23148760986

Pull #3757

github

web-flow
Merge 6206e3966 into 157869812
Pull Request #3757: Testing point detectors

17624 of 25493 branches covered (69.13%)

Branch coverage included in aggregate %.

44 of 227 new or added lines in 14 files covered. (19.38%)

1 existing line in 1 file now uncovered.

58081 of 67737 relevant lines covered (85.74%)

44465291.14 hits per line

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

59.03
/src/ray.cpp
1
#include "openmc/ray.h"
2

3
#include "openmc/error.h"
4
#include "openmc/geometry.h"
5
#include "openmc/material.h"
6
#include "openmc/mgxs_interface.h"
7
#include "openmc/settings.h"
8

9
namespace openmc {
10

11
void Ray::compute_distance()
3,014,638✔
12
{
13
  boundary() = distance_to_boundary(*this);
3,014,638✔
14
}
3,014,638✔
15

16
void Ray::trace()
3,521,056✔
17
{
18
  // To trace the ray from its origin all the way through the model, we have
19
  // to proceed in two phases. In the first, the ray may or may not be found
20
  // inside the model. If the ray is already in the model, phase one can be
21
  // skipped. Otherwise, the ray has to be advanced to the boundary of the
22
  // model where all the cells are defined. Importantly, this is assuming that
23
  // the model is convex, which is a very reasonable assumption for any
24
  // radiation transport model.
25
  //
26
  // After phase one is done, we can starting tracing from cell to cell within
27
  // the model. This step can use neighbor lists to accelerate the ray tracing.
28

29
  bool inside_cell;
3,521,056✔
30
  // Check for location if the particle is already known
31
  if (lowest_coord().cell() == C_NONE) {
3,521,056!
32
    // The geometry position of the particle is either unknown or outside of the
33
    // edge of the model.
34
    if (lowest_coord().universe() == C_NONE) {
3,521,056!
35
      // Attempt to initialize the particle. We may have to
36
      // enter a loop to move it up to the edge of the model.
37
      inside_cell = exhaustive_find_cell(*this, settings::verbosity >= 10);
3,521,056✔
38
    } else {
39
      // It has been already calculated that the current position is outside of
40
      // the edge of the model.
41
      inside_cell = false;
42
    }
43
  } else {
44
    // Availability of the cell means that the particle is located inside the
45
    // edge.
46
    inside_cell = true;
47
  }
48

49
  // Advance to the boundary of the model
50
  while (!inside_cell) {
15,618,438!
51
    advance_to_boundary_from_void();
15,618,438✔
52
    inside_cell = exhaustive_find_cell(*this, settings::verbosity >= 10);
15,618,438✔
53

54
    // If true this means no surface was intersected. See cell.cpp and search
55
    // for numeric_limits to see where we return it.
56
    if (surface() == std::numeric_limits<int>::max()) {
15,618,438!
57
      warning(fmt::format("Lost a ray, r = {}, u = {}", r(), u()));
×
58
      return;
×
59
    }
60

61
    // Exit this loop and enter into cell-to-cell ray tracing (which uses
62
    // neighbor lists)
63
    if (inside_cell)
15,618,438✔
64
      break;
65

66
    // if there is no intersection with the model, we're done
67
    if (boundary().surface() == SURFACE_NONE)
14,068,604✔
68
      return;
69

70
    event_counter_++;
12,097,382✔
71
    if (event_counter_ > MAX_INTERSECTIONS) {
12,097,382!
72
      warning("Likely infinite loop in ray traced plot");
×
73
      return;
×
74
    }
75
  }
76

77
  // Call the specialized logic for this type of ray. This is for the
78
  // intersection for the first intersection if we had one.
79
  if (boundary().surface() != SURFACE_NONE) {
1,549,834!
80
    // set the geometry state's surface attribute to be used for
81
    // surface normal computation
82
    surface() = boundary().surface();
1,549,834✔
83
    on_intersection();
1,549,834✔
84
    if (stop_)
1,549,834!
85
      return;
86
  }
87

88
  // reset surface attribute to zero after the first intersection so that it
89
  // doesn't perturb surface crossing logic from here on out
90
  surface() = 0;
1,549,834✔
91

92
  // This is the ray tracing loop within the model. It exits after exiting
93
  // the model, which is equivalent to assuming that the model is convex.
94
  // It would be nice to factor out the on_intersection at the end of this
95
  // loop and then do "while (inside_cell)", but we can't guarantee it's
96
  // on a surface in that case. There might be some other way to set it
97
  // up that is perhaps a little more elegant, but this is what works just
98
  // fine.
99
  while (true) {
2,308,570✔
100

101
    compute_distance();
2,308,570✔
102

103
    // There are no more intersections to process
104
    // if we hit the edge of the model, so stop
105
    // the particle in that case. Also, just exit
106
    // if a negative distance was somehow computed.
107
    if (boundary().distance() == INFTY || boundary().distance() == INFINITY ||
2,308,570!
108
        boundary().distance() < 0) {
2,308,570!
109
      return;
110
    }
111

112
    // See below comment where call_on_intersection is checked in an
113
    // if statement for an explanation of this.
114
    bool call_on_intersection {true};
2,308,570✔
115
    if (boundary().distance() < 10 * TINY_BIT) {
2,308,570✔
116
      call_on_intersection = false;
593,285✔
117
    }
118

119
    // DAGMC surfaces expect us to go a little bit further than the advance
120
    // distance to properly check cell inclusion.
121
    boundary().distance() += TINY_BIT;
2,308,570✔
122

123
    // Advance particle, prepare for next intersection
124
    for (int lev = 0; lev < n_coord(); ++lev) {
4,617,140✔
125
      coord(lev).r() += boundary().distance() * coord(lev).u();
2,308,570✔
126
    }
127
    surface() = boundary().surface();
2,308,570✔
128
    // Initialize last cells from the current cell, because the cell() variable
129
    // does not contain the data for the case of a single-segment ray
130
    for (int j = 0; j < n_coord(); ++j) {
4,617,140✔
131
      cell_last(j) = coord(j).cell();
2,308,570✔
132
    }
133
    n_coord_last() = n_coord();
2,308,570✔
134
    n_coord() = boundary().coord_level();
2,308,570!
135
    if (boundary().lattice_translation()[0] != 0 ||
2,308,570!
136
        boundary().lattice_translation()[1] != 0 ||
2,308,570!
137
        boundary().lattice_translation()[2] != 0) {
2,308,570!
138
      cross_lattice(*this, boundary(), settings::verbosity >= 10);
×
139
    }
140

141
    update_distance();
2,308,570✔
142

143
    inside_cell = neighbor_list_find_cell(*this, settings::verbosity >= 10);
2,308,570✔
144

145
    // Call the specialized logic for this type of ray. Note that we do not
146
    // call this if the advance distance is very small. Unfortunately, it seems
147
    // darn near impossible to get the particle advanced to the model boundary
148
    // and through it without sometimes accidentally calling on_intersection
149
    // twice. This incorrectly shades the region as occluded when it might not
150
    // actually be. By screening out intersection distances smaller than a
151
    // threshold 10x larger than the scoot distance used to advance up to the
152
    // model boundary, we can avoid that situation.
153
    if (call_on_intersection) {
2,308,570✔
154
      on_intersection();
1,715,285✔
155
      if (stop_)
1,715,285✔
156
        return;
157
    }
158

159
    if (!inside_cell)
2,273,007✔
160
      return;
161

162
    event_counter_++;
758,736✔
163
    if (event_counter_ > MAX_INTERSECTIONS) {
758,736!
164
      warning("Likely infinite loop in ray traced plot");
×
165
      return;
×
166
    }
167
  }
168
}
169

170
void Ray::update_distance()
2,308,570✔
171
{
172
  // Record how far the ray has traveled
173
  traversal_distance_ += boundary().distance();
2,308,570✔
174
}
2,308,570✔
175

NEW
176
void ParticleRay::on_intersection() {}
×
177

NEW
178
void ParticleRay::update_distance()
×
179
{
NEW
180
  Ray::update_distance();
×
181

NEW
182
  time() += boundary().distance() / speed();
×
183

184
  // Calculate microscopic and macroscopic cross sections
NEW
185
  if (material() != MATERIAL_VOID) {
×
NEW
186
    if (settings::run_CE) {
×
NEW
187
      if (material() != material_last() || sqrtkT() != sqrtkT_last() ||
×
NEW
188
          density_mult() != density_mult_last()) {
×
189
        // If the material is the same as the last material and the
190
        // temperature hasn't changed, we don't need to lookup cross
191
        // sections again.
NEW
192
        model::materials[material()]->calculate_xs(*this);
×
193
      }
194
    } else {
195
      // Get the MG data; unlike the CE case above, we have to re-calculate
196
      // cross sections for every collision since the cross sections may
197
      // be angle-dependent
NEW
198
      data::mg.macro_xs_[material()].calculate_xs(*this);
×
199

200
      // Update the particle's group while we know we are multi-group
NEW
201
      g_last() = g();
×
202
    }
203
  } else {
NEW
204
    macro_xs().total = 0.0;
×
NEW
205
    macro_xs().absorption = 0.0;
×
NEW
206
    macro_xs().fission = 0.0;
×
NEW
207
    macro_xs().nu_fission = 0.0;
×
208
  }
209

NEW
210
  traversal_mfp_ += macro_xs().total * boundary().distance();
×
NEW
211
}
×
212

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