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

openmc-dev / openmc / 20868058727

09 Jan 2026 11:00PM UTC coverage: 82.202% (+0.03%) from 82.171%
20868058727

Pull #3705

github

web-flow
Merge 233dcfa80 into 8c8867ea1
Pull Request #3705: Flux Energy Group Conversion using lethargy-weighted redistribution

17109 of 23668 branches covered (72.29%)

Branch coverage included in aggregate %.

34 of 39 new or added lines in 2 files covered. (87.18%)

265 existing lines in 9 files now uncovered.

55259 of 64369 relevant lines covered (85.85%)

43504872.3 hits per line

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

77.38
/src/boundary_condition.cpp
1
#include "openmc/boundary_condition.h"
2

3
#include <exception>
4

5
#include <fmt/core.h>
6

7
#include "openmc/constants.h"
8
#include "openmc/error.h"
9
#include "openmc/random_ray/random_ray.h"
10
#include "openmc/surface.h"
11

12
namespace openmc {
13

14
//==============================================================================
15
// VacuumBC implementation
16
//==============================================================================
17

18
void VacuumBC::handle_particle(Particle& p, const Surface& surf) const
57,684,235✔
19
{
20
  // Random ray and Monte Carlo need different treatments at vacuum BCs
21
  if (settings::solver_type == SolverType::RANDOM_RAY) {
57,684,235✔
22
    // Reflect ray off of the surface
23
    ReflectiveBC().handle_particle(p, surf);
23,673,129✔
24

25
    // Set ray's angular flux spectrum to vacuum conditions (zero)
26
    RandomRay* r = static_cast<RandomRay*>(&p);
23,673,129✔
27
    std::fill(r->angular_flux_.begin(), r->angular_flux_.end(), 0.0);
23,673,129✔
28

29
  } else {
30
    p.cross_vacuum_bc(surf);
34,011,106✔
31
  }
32
}
57,684,235✔
33

34
//==============================================================================
35
// ReflectiveBC implementation
36
//==============================================================================
37

38
void ReflectiveBC::handle_particle(Particle& p, const Surface& surf) const
638,431,292✔
39
{
40
  Direction u = surf.reflect(p.r(), p.u(), &p);
638,431,292✔
41
  u /= u.norm();
638,431,292✔
42

43
  // Handle the effects of the surface albedo on the particle's weight.
44
  BoundaryCondition::handle_albedo(p, surf);
638,431,292✔
45

46
  p.cross_reflective_bc(surf, u);
638,431,292✔
47
}
638,431,292✔
48

49
//==============================================================================
50
// WhiteBC implementation
51
//==============================================================================
52

53
void WhiteBC::handle_particle(Particle& p, const Surface& surf) const
1,007,149✔
54
{
55
  Direction u = surf.diffuse_reflect(p.r(), p.u(), p.current_seed());
1,007,149✔
56
  u /= u.norm();
1,007,149✔
57

58
  // Handle the effects of the surface albedo on the particle's weight.
59
  BoundaryCondition::handle_albedo(p, surf);
1,007,149✔
60

61
  p.cross_reflective_bc(surf, u);
1,007,149✔
62
}
1,007,149✔
63

64
//==============================================================================
65
// TranslationalPeriodicBC implementation
66
//==============================================================================
67

68
TranslationalPeriodicBC::TranslationalPeriodicBC(int i_surf, int j_surf)
234✔
69
  : PeriodicBC(i_surf, j_surf)
234✔
70
{
71
  Surface& surf1 {*model::surfaces[i_surf_]};
234✔
72
  Surface& surf2 {*model::surfaces[j_surf_]};
234✔
73

74
  // Make sure the first surface has an appropriate type.
75
  if (const auto* ptr = dynamic_cast<const SurfaceXPlane*>(&surf1)) {
234!
76
  } else if (const auto* ptr = dynamic_cast<const SurfaceYPlane*>(&surf1)) {
180!
77
  } else if (const auto* ptr = dynamic_cast<const SurfaceZPlane*>(&surf1)) {
158!
78
  } else if (const auto* ptr = dynamic_cast<const SurfacePlane*>(&surf1)) {
80!
79
  } else {
80
    throw std::invalid_argument(fmt::format(
×
81
      "Surface {} is an invalid type for "
82
      "translational periodic BCs. Only planes are supported for these BCs.",
83
      surf1.id_));
×
84
  }
85

86
  // Make sure the second surface has an appropriate type.
87
  if (const auto* ptr = dynamic_cast<const SurfaceXPlane*>(&surf2)) {
234!
88
  } else if (const auto* ptr = dynamic_cast<const SurfaceYPlane*>(&surf2)) {
180!
89
  } else if (const auto* ptr = dynamic_cast<const SurfaceZPlane*>(&surf2)) {
158!
90
  } else if (const auto* ptr = dynamic_cast<const SurfacePlane*>(&surf2)) {
80!
91
  } else {
92
    throw std::invalid_argument(fmt::format(
×
93
      "Surface {} is an invalid type for "
94
      "translational periodic BCs. Only planes are supported for these BCs.",
95
      surf2.id_));
×
96
  }
97

98
  // Compute the distance from the first surface to the origin.  Check the
99
  // surface evaluate function to decide if the distance is positive, negative,
100
  // or zero.
101
  Position origin {0, 0, 0};
234✔
102
  Direction u = surf1.normal(origin);
234✔
103
  double d1;
104
  double e1 = surf1.evaluate(origin);
234✔
105
  if (e1 > FP_COINCIDENT) {
234✔
106
    d1 = -surf1.distance(origin, -u, false);
117✔
107
  } else if (e1 < -FP_COINCIDENT) {
117!
108
    d1 = surf1.distance(origin, u, false);
117✔
109
  } else {
110
    d1 = 0.0;
×
111
  }
112

113
  // Compute the distance from the second surface to the origin.
114
  double d2;
115
  double e2 = surf2.evaluate(origin);
234✔
116
  if (e2 > FP_COINCIDENT) {
234✔
117
    d2 = -surf2.distance(origin, -u, false);
117✔
118
  } else if (e2 < -FP_COINCIDENT) {
117!
119
    d2 = surf2.distance(origin, u, false);
117✔
120
  } else {
121
    d2 = 0.0;
×
122
  }
123

124
  // Set the translation vector; it's length is the difference in the two
125
  // distances.
126
  translation_ = u * (d2 - d1);
234✔
127
}
234✔
128

129
void TranslationalPeriodicBC::handle_particle(
296,797✔
130
  Particle& p, const Surface& surf) const
131
{
132
  auto new_r = p.r() + translation_;
296,797✔
133
  int new_surface = p.surface() > 0 ? j_surf_ + 1 : -(j_surf_ + 1);
296,797✔
134

135
  // Handle the effects of the surface albedo on the particle's weight.
136
  BoundaryCondition::handle_albedo(p, surf);
296,797✔
137

138
  // Pass the new location and surface to the particle.
139
  p.cross_periodic_bc(surf, new_r, p.u(), new_surface);
296,797✔
140
}
296,797✔
141

142
//==============================================================================
143
// RotationalPeriodicBC implementation
144
//==============================================================================
145

146
RotationalPeriodicBC::RotationalPeriodicBC(
334✔
147
  int i_surf, int j_surf, PeriodicAxis axis)
334✔
148
  : PeriodicBC(std::abs(i_surf) - 1, std::abs(j_surf) - 1)
334✔
149
{
150
  Surface& surf1 {*model::surfaces[i_surf_]};
334✔
151
  Surface& surf2 {*model::surfaces[j_surf_]};
334✔
152

153
  // below convention for right handed coordinate system
154
  switch (axis) {
334!
155
  case x:
32✔
156
    zero_axis_idx_ = 0; // x component of plane must be zero
32✔
157
    axis_1_idx_ = 1;    // y component independent
32✔
158
    axis_2_idx_ = 2;    // z component dependent
32✔
159
    break;
32✔
160
  case y:
32✔
161
    zero_axis_idx_ = 1; // y component of plane must be zero
32✔
162
    axis_1_idx_ = 2;    // z component independent
32✔
163
    axis_2_idx_ = 0;    // x component dependent
32✔
164
    break;
32✔
165
  case z:
270✔
166
    zero_axis_idx_ = 2; // z component of plane must be zero
270✔
167
    axis_1_idx_ = 0;    // x component independent
270✔
168
    axis_2_idx_ = 1;    // y component dependent
270✔
169
    break;
270✔
UNCOV
170
  default:
×
UNCOV
171
    throw std::invalid_argument(
×
UNCOV
172
      fmt::format("You've specified an axis that is not x, y, or z."));
×
173
  }
174

175
  Direction ax = {0.0, 0.0, 0.0};
334✔
176
  ax[zero_axis_idx_] = 1.0;
334✔
177

178
  auto i_sign = std::copysign(1, i_surf);
334✔
179
  auto j_sign = -std::copysign(1, j_surf);
334✔
180

181
  // Compute the surface normal vectors and make sure they are perpendicular
182
  // to the correct axis
183
  Direction norm1 = i_sign * surf1.normal({0, 0, 0});
334✔
184
  Direction norm2 = j_sign * surf2.normal({0, 0, 0});
334✔
185
  // Make sure both surfaces intersect the origin
186
  if (std::abs(surf1.evaluate({0, 0, 0})) > FP_COINCIDENT) {
334!
UNCOV
187
    throw std::invalid_argument(fmt::format(
×
188
      "Rotational periodic BCs are only "
189
      "supported for rotations about the origin, but surface {} does not "
190
      "intersect the origin.",
191
      surf1.id_));
×
192
  }
193
  if (std::abs(surf2.evaluate({0, 0, 0})) > FP_COINCIDENT) {
334!
UNCOV
194
    throw std::invalid_argument(fmt::format(
×
195
      "Rotational periodic BCs are only "
196
      "supported for rotations about the origin, but surface {} does not "
197
      "intersect the origin.",
UNCOV
198
      surf2.id_));
×
199
  }
200

201
  // Compute the signed rotation angle about the periodic axis. Note that
202
  // (n1×n2)·a = |n1||n2|sin(θ) and n1·n2 = |n1||n2|cos(θ), where a is the axis
203
  // of rotation.
204
  auto c = norm1.cross(norm2);
334✔
205
  angle_ = std::atan2(c.dot(ax), norm1.dot(norm2));
334✔
206

207
  // If the normals point in the same general direction, the surface sense
208
  // should change when crossing the boundary
209
  flip_sense_ = (i_sign * j_sign > 0.0);
334✔
210

211
  // Warn the user if the angle does not evenly divide a circle
212
  double rem = std::abs(std::remainder((2 * PI / angle_), 1.0));
334✔
213
  if (rem > FP_REL_PRECISION && rem < 1 - FP_REL_PRECISION) {
334!
UNCOV
214
    warning(fmt::format(
×
215
      "Rotational periodic BC specified with a rotation "
216
      "angle of {} degrees which does not evenly divide 360 degrees.",
UNCOV
217
      angle_ * 180 / PI));
×
218
  }
219
}
334✔
220

221
void RotationalPeriodicBC::handle_particle(
1,948,562✔
222
  Particle& p, const Surface& surf) const
223
{
224
  int new_surface = p.surface() > 0 ? -(j_surf_ + 1) : j_surf_ + 1;
1,948,562✔
225
  if (flip_sense_)
1,948,562✔
226
    new_surface = -new_surface;
787,270✔
227

228
  // Rotate the particle's position and direction.
229
  Position r = p.r();
1,948,562✔
230
  Direction u = p.u();
1,948,562✔
231
  double cos_theta = std::cos(angle_);
1,948,562✔
232
  double sin_theta = std::sin(angle_);
1,948,562✔
233

234
  Position new_r;
1,948,562✔
235
  new_r[zero_axis_idx_] = r[zero_axis_idx_];
1,948,562✔
236
  new_r[axis_1_idx_] = cos_theta * r[axis_1_idx_] - sin_theta * r[axis_2_idx_];
1,948,562✔
237
  new_r[axis_2_idx_] = sin_theta * r[axis_1_idx_] + cos_theta * r[axis_2_idx_];
1,948,562✔
238

239
  Direction new_u;
1,948,562✔
240
  new_u[zero_axis_idx_] = u[zero_axis_idx_];
1,948,562✔
241
  new_u[axis_1_idx_] = cos_theta * u[axis_1_idx_] - sin_theta * u[axis_2_idx_];
1,948,562✔
242
  new_u[axis_2_idx_] = sin_theta * u[axis_1_idx_] + cos_theta * u[axis_2_idx_];
1,948,562✔
243

244
  // Handle the effects of the surface albedo on the particle's weight.
245
  BoundaryCondition::handle_albedo(p, surf);
1,948,562✔
246

247
  // Pass the new location, direction, and surface to the particle.
248
  p.cross_periodic_bc(surf, new_r, new_u, new_surface);
1,948,562✔
249
}
1,948,562✔
250

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