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

openmc-dev / openmc / 22599867459

02 Mar 2026 11:05PM UTC coverage: 81.514% (+0.004%) from 81.51%
22599867459

Pull #3845

github

web-flow
Merge b9dc1eb97 into 823b4c96c
Pull Request #3845: Refactor Ray class into its own file

17478 of 25172 branches covered (69.43%)

Branch coverage included in aggregate %.

50 of 57 new or added lines in 2 files covered. (87.72%)

1 existing line in 1 file now uncovered.

57680 of 67031 relevant lines covered (86.05%)

44979087.27 hits per line

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

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

3
#include "openmc/geometry.h"
4

5
namespace openmc {
6

7
void Ray::compute_distance()
3,014,638✔
8
{
9
  boundary() = distance_to_boundary(*this);
3,014,638✔
10
}
3,014,638✔
11

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

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

45
  // Advance to the boundary of the model
46
  while (!inside_cell) {
15,618,438!
47
    advance_to_boundary_from_void();
15,618,438✔
48
    inside_cell = exhaustive_find_cell(*this, settings::verbosity >= 10);
15,618,438✔
49

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

57
    // Exit this loop and enter into cell-to-cell ray tracing (which uses
58
    // neighbor lists)
59
    if (inside_cell)
15,618,438✔
60
      break;
61

62
    // if there is no intersection with the model, we're done
63
    if (boundary().surface() == SURFACE_NONE)
14,068,604✔
64
      return;
65

66
    event_counter_++;
12,097,382✔
67
    if (event_counter_ > MAX_INTERSECTIONS) {
12,097,382!
NEW
68
      warning("Likely infinite loop in ray traced plot");
×
NEW
69
      return;
×
70
    }
71
  }
72

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

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

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

97
    compute_distance();
2,308,570✔
98

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

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

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

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

137
    // Record how far the ray has traveled
138
    traversal_distance_ += boundary().distance();
2,308,570✔
139
    inside_cell = neighbor_list_find_cell(*this, settings::verbosity >= 10);
2,308,570✔
140

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

155
    if (!inside_cell)
2,273,007✔
156
      return;
157

158
    event_counter_++;
758,736✔
159
    if (event_counter_ > MAX_INTERSECTIONS) {
758,736!
NEW
160
      warning("Likely infinite loop in ray traced plot");
×
NEW
161
      return;
×
162
    }
163
  }
164
}
165

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