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

openmc-dev / openmc / 22598228340

02 Mar 2026 10:16PM UTC coverage: 81.514% (+0.004%) from 81.51%
22598228340

Pull #3845

github

web-flow
Merge eb3b578b7 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%)

44975766.45 hits per line

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

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

3
namespace openmc {
4

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

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

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

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

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

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

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

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

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

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

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

95
    compute_distance();
2,308,570✔
96

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

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

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

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

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

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

153
    if (!inside_cell)
2,273,007✔
154
      return;
155

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

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