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

openmc-dev / openmc / 22074794849

16 Feb 2026 07:11PM UTC coverage: 81.681% (-0.04%) from 81.721%
22074794849

Pull #3810

github

web-flow
Merge 566c383f8 into c6ef84d1d
Pull Request #3810: Implementation of migration area score

17577 of 24724 branches covered (71.09%)

Branch coverage included in aggregate %.

36 of 55 new or added lines in 5 files covered. (65.45%)

300 existing lines in 24 files now uncovered.

57057 of 66648 relevant lines covered (85.61%)

52253611.33 hits per line

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

70.86
/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/simulation.h"
11
#include "openmc/surface.h"
12

13
namespace openmc {
14

15
//==============================================================================
16
// VacuumBC implementation
17
//==============================================================================
18

19
void VacuumBC::handle_particle(Particle& p, const Surface& surf) const
52,613,624✔
20
{
21
  // Random ray and Monte Carlo need different treatments at vacuum BCs
22
  if (settings::solver_type == SolverType::RANDOM_RAY) {
52,613,624✔
23
    // Reflect ray off of the surface
24
    ReflectiveBC().handle_particle(p, surf);
21,657,372✔
25

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

30
  } else {
31
    p.cross_vacuum_bc(surf);
30,956,252✔
32
  }
33
}
52,613,624✔
34

35
//==============================================================================
36
// ReflectiveBC implementation
37
//==============================================================================
38

39
void ReflectiveBC::handle_particle(Particle& p, const Surface& surf) const
620,776,481✔
40
{
41
  Direction u = surf.reflect(p.r(), p.u(), &p);
620,776,481✔
42
  u /= u.norm();
620,776,481✔
43

44
  // Handle the effects of the surface albedo on the particle's weight.
45
  BoundaryCondition::handle_albedo(p, surf);
620,776,481✔
46

47
  // Handle phantom birth location if migration present
48
  if (simulation::migration_present) {
620,776,481✔
49
    auto r = p.r();
2,220✔
50
    auto n = surf.normal(r);
2,220✔
51
    n /= n.norm();
2,220✔
52
    auto r_born = p.r_born();
2,220✔
53
    p.r_born() = r_born - 2.0 * (r_born - r).dot(n) * n;
2,220✔
54
  }
55

56
  p.cross_reflective_bc(surf, u);
620,776,481✔
57
}
620,776,481✔
58

59
//==============================================================================
60
// WhiteBC implementation
61
//==============================================================================
62

63
void WhiteBC::handle_particle(Particle& p, const Surface& surf) const
915,590✔
64
{
65
  Direction u = surf.diffuse_reflect(p.r(), p.u(), p.current_seed());
915,590✔
66
  u /= u.norm();
915,590✔
67

68
  // Handle the effects of the surface albedo on the particle's weight.
69
  BoundaryCondition::handle_albedo(p, surf);
915,590✔
70

71
  // Handle phantom birth location if migration present
72
  if (simulation::migration_present)
915,590!
NEW
73
    fatal_error("Cannot tally migration area with white boundary conditions.");
×
74

75
  p.cross_reflective_bc(surf, u);
915,590✔
76
}
915,590✔
77

78
//==============================================================================
79
// TranslationalPeriodicBC implementation
80
//==============================================================================
81

82
TranslationalPeriodicBC::TranslationalPeriodicBC(int i_surf, int j_surf)
208✔
83
  : PeriodicBC(i_surf, j_surf)
208✔
84
{
85
  Surface& surf1 {*model::surfaces[i_surf_]};
208✔
86
  Surface& surf2 {*model::surfaces[j_surf_]};
208✔
87

88
  // Make sure the first surface has an appropriate type.
89
  if (const auto* ptr = dynamic_cast<const SurfaceXPlane*>(&surf1)) {
208!
90
  } else if (const auto* ptr = dynamic_cast<const SurfaceYPlane*>(&surf1)) {
160!
91
  } else if (const auto* ptr = dynamic_cast<const SurfaceZPlane*>(&surf1)) {
140!
92
  } else if (const auto* ptr = dynamic_cast<const SurfacePlane*>(&surf1)) {
70!
93
  } else {
94
    throw std::invalid_argument(fmt::format(
×
95
      "Surface {} is an invalid type for "
96
      "translational periodic BCs. Only planes are supported for these BCs.",
97
      surf1.id_));
×
98
  }
99

100
  // Make sure the second surface has an appropriate type.
101
  if (const auto* ptr = dynamic_cast<const SurfaceXPlane*>(&surf2)) {
208!
102
  } else if (const auto* ptr = dynamic_cast<const SurfaceYPlane*>(&surf2)) {
160!
103
  } else if (const auto* ptr = dynamic_cast<const SurfaceZPlane*>(&surf2)) {
140!
104
  } else if (const auto* ptr = dynamic_cast<const SurfacePlane*>(&surf2)) {
70!
105
  } else {
106
    throw std::invalid_argument(fmt::format(
×
107
      "Surface {} is an invalid type for "
108
      "translational periodic BCs. Only planes are supported for these BCs.",
109
      surf2.id_));
×
110
  }
111

112
  // Compute the distance from the first surface to the origin.  Check the
113
  // surface evaluate function to decide if the distance is positive, negative,
114
  // or zero.
115
  Position origin {0, 0, 0};
208✔
116
  Direction u = surf1.normal(origin);
208✔
117
  double d1;
118
  double e1 = surf1.evaluate(origin);
208✔
119
  if (e1 > FP_COINCIDENT) {
208✔
120
    d1 = -surf1.distance(origin, -u, false);
104✔
121
  } else if (e1 < -FP_COINCIDENT) {
104!
122
    d1 = surf1.distance(origin, u, false);
104✔
123
  } else {
124
    d1 = 0.0;
×
125
  }
126

127
  // Compute the distance from the second surface to the origin.
128
  double d2;
129
  double e2 = surf2.evaluate(origin);
208✔
130
  if (e2 > FP_COINCIDENT) {
208✔
131
    d2 = -surf2.distance(origin, -u, false);
104✔
132
  } else if (e2 < -FP_COINCIDENT) {
104!
133
    d2 = surf2.distance(origin, u, false);
104✔
134
  } else {
135
    d2 = 0.0;
×
136
  }
137

138
  // Set the translation vector; it's length is the difference in the two
139
  // distances.
140
  translation_ = u * (d2 - d1);
208✔
141
}
208✔
142

143
void TranslationalPeriodicBC::handle_particle(
269,528✔
144
  Particle& p, const Surface& surf) const
145
{
146
  auto new_r = p.r() + translation_;
269,528✔
147
  int new_surface = p.surface() > 0 ? j_surf_ + 1 : -(j_surf_ + 1);
269,528✔
148

149
  // Handle the effects of the surface albedo on the particle's weight.
150
  BoundaryCondition::handle_albedo(p, surf);
269,528✔
151

152
  // Handle phantom birth location if migration present
153
  if (simulation::migration_present) {
269,528!
NEW
154
    p.r_born() += translation_;
×
155
  }
156

157
  // Pass the new location and surface to the particle.
158
  p.cross_periodic_bc(surf, new_r, p.u(), new_surface);
269,528✔
159
}
269,528✔
160

161
//==============================================================================
162
// RotationalPeriodicBC implementation
163
//==============================================================================
164

165
RotationalPeriodicBC::RotationalPeriodicBC(
296✔
166
  int i_surf, int j_surf, PeriodicAxis axis)
296✔
167
  : PeriodicBC(std::abs(i_surf) - 1, std::abs(j_surf) - 1)
296✔
168
{
169
  Surface& surf1 {*model::surfaces[i_surf_]};
296✔
170
  Surface& surf2 {*model::surfaces[j_surf_]};
296✔
171

172
  // below convention for right handed coordinate system
173
  switch (axis) {
296!
174
  case x:
28✔
175
    zero_axis_idx_ = 0; // x component of plane must be zero
28✔
176
    axis_1_idx_ = 1;    // y component independent
28✔
177
    axis_2_idx_ = 2;    // z component dependent
28✔
178
    break;
28✔
179
  case y:
28✔
180
    zero_axis_idx_ = 1; // y component of plane must be zero
28✔
181
    axis_1_idx_ = 2;    // z component independent
28✔
182
    axis_2_idx_ = 0;    // x component dependent
28✔
183
    break;
28✔
184
  case z:
240✔
185
    zero_axis_idx_ = 2; // z component of plane must be zero
240✔
186
    axis_1_idx_ = 0;    // x component independent
240✔
187
    axis_2_idx_ = 1;    // y component dependent
240✔
188
    break;
240✔
189
  default:
×
190
    throw std::invalid_argument(
×
191
      fmt::format("You've specified an axis that is not x, y, or z."));
×
192
  }
193

194
  Direction ax = {0.0, 0.0, 0.0};
296✔
195
  ax[zero_axis_idx_] = 1.0;
296✔
196

197
  auto i_sign = std::copysign(1, i_surf);
296✔
198
  auto j_sign = -std::copysign(1, j_surf);
296✔
199

200
  // Compute the surface normal vectors and make sure they are perpendicular
201
  // to the correct axis
202
  Direction norm1 = i_sign * surf1.normal({0, 0, 0});
296✔
203
  Direction norm2 = j_sign * surf2.normal({0, 0, 0});
296✔
204
  // Make sure both surfaces intersect the origin
205
  if (std::abs(surf1.evaluate({0, 0, 0})) > FP_COINCIDENT) {
296!
206
    throw std::invalid_argument(fmt::format(
×
207
      "Rotational periodic BCs are only "
208
      "supported for rotations about the origin, but surface {} does not "
209
      "intersect the origin.",
210
      surf1.id_));
×
211
  }
212
  if (std::abs(surf2.evaluate({0, 0, 0})) > FP_COINCIDENT) {
296!
213
    throw std::invalid_argument(fmt::format(
×
214
      "Rotational periodic BCs are only "
215
      "supported for rotations about the origin, but surface {} does not "
216
      "intersect the origin.",
217
      surf2.id_));
×
218
  }
219

220
  // Compute the signed rotation angle about the periodic axis. Note that
221
  // (n1×n2)·a = |n1||n2|sin(θ) and n1·n2 = |n1||n2|cos(θ), where a is the axis
222
  // of rotation.
223
  auto c = norm1.cross(norm2);
296✔
224
  angle_ = std::atan2(c.dot(ax), norm1.dot(norm2));
296✔
225

226
  // If the normals point in the same general direction, the surface sense
227
  // should change when crossing the boundary
228
  flip_sense_ = (i_sign * j_sign > 0.0);
296✔
229

230
  // Warn the user if the angle does not evenly divide a circle
231
  double rem = std::abs(std::remainder((2 * PI / angle_), 1.0));
296✔
232
  if (rem > FP_REL_PRECISION && rem < 1 - FP_REL_PRECISION) {
296!
233
    warning(fmt::format(
×
234
      "Rotational periodic BC specified with a rotation "
235
      "angle of {} degrees which does not evenly divide 360 degrees.",
236
      angle_ * 180 / PI));
×
237
  }
238
}
296✔
239

240
void RotationalPeriodicBC::handle_particle(
1,771,493✔
241
  Particle& p, const Surface& surf) const
242
{
243
  int new_surface = p.surface() > 0 ? -(j_surf_ + 1) : j_surf_ + 1;
1,771,493✔
244
  if (flip_sense_)
1,771,493✔
245
    new_surface = -new_surface;
813,788✔
246

247
  // Rotate the particle's position and direction.
248
  Position r = p.r();
1,771,493✔
249
  Direction u = p.u();
1,771,493✔
250
  double cos_theta = std::cos(angle_);
1,771,493✔
251
  double sin_theta = std::sin(angle_);
1,771,493✔
252

253
  Position new_r;
1,771,493✔
254
  new_r[zero_axis_idx_] = r[zero_axis_idx_];
1,771,493✔
255
  new_r[axis_1_idx_] = cos_theta * r[axis_1_idx_] - sin_theta * r[axis_2_idx_];
1,771,493✔
256
  new_r[axis_2_idx_] = sin_theta * r[axis_1_idx_] + cos_theta * r[axis_2_idx_];
1,771,493✔
257

258
  Direction new_u;
1,771,493✔
259
  new_u[zero_axis_idx_] = u[zero_axis_idx_];
1,771,493✔
260
  new_u[axis_1_idx_] = cos_theta * u[axis_1_idx_] - sin_theta * u[axis_2_idx_];
1,771,493✔
261
  new_u[axis_2_idx_] = sin_theta * u[axis_1_idx_] + cos_theta * u[axis_2_idx_];
1,771,493✔
262

263
  // Handle the effects of the surface albedo on the particle's weight.
264
  BoundaryCondition::handle_albedo(p, surf);
1,771,493✔
265

266
  // Handle phantom birth location if migration present
267
  if (simulation::migration_present) {
1,771,493!
NEW
268
    auto r_born = p.r_born();
×
NEW
269
    Position new_r_born;
×
NEW
270
    new_r_born[zero_axis_idx_] = r_born[zero_axis_idx_];
×
NEW
271
    new_r_born[axis_1_idx_] =
×
NEW
272
      cos_theta * r_born[axis_1_idx_] - sin_theta * r_born[axis_2_idx_];
×
NEW
273
    new_r_born[axis_2_idx_] =
×
NEW
274
      sin_theta * r_born[axis_1_idx_] + cos_theta * r_born[axis_2_idx_];
×
NEW
275
    p.r_born() = new_r_born;
×
276
  }
277

278
  // Pass the new location, direction, and surface to the particle.
279
  p.cross_periodic_bc(surf, new_r, new_u, new_surface);
1,771,493✔
280
}
1,771,493✔
281

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