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

openmc-dev / openmc / 14260484407

04 Apr 2025 07:41AM UTC coverage: 84.589% (-0.3%) from 84.851%
14260484407

Pull #3279

github

web-flow
Merge 33e03305e into 07f533461
Pull Request #3279: Hexagonal mesh model

2 of 332 new or added lines in 3 files covered. (0.6%)

2798 existing lines in 84 files now uncovered.

51799 of 61236 relevant lines covered (84.59%)

38650400.15 hits per line

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

0.0
/src/hex_mesh.cpp
1
#include "openmc/hex_mesh.h"
2
#include <algorithm> // for copy, equal, min, min_element
3
#include <cmath>     // for ceil, sqrt
4
#include <cstddef>   // for size_t
5
#include <string>
6

7
#ifdef OPENMC_MPI
8
#include "mpi.h"
9
#endif
10

11
#include "xtensor/xbuilder.hpp"
12
#include "xtensor/xeval.hpp"
13
#include "xtensor/xmath.hpp"
14
#include "xtensor/xnorm.hpp"
15
#include "xtensor/xsort.hpp"
16
#include "xtensor/xtensor.hpp"
17
#include "xtensor/xview.hpp"
18
#include <fmt/core.h> // for fmt
19

20
#include "openmc/capi.h"
21
#include "openmc/constants.h"
22
#include "openmc/container_util.h"
23
#include "openmc/error.h"
24
#include "openmc/file_utils.h"
25
#include "openmc/geometry.h"
26
#include "openmc/hdf5_interface.h"
27
#include "openmc/material.h"
28
#include "openmc/memory.h"
29
#include "openmc/message_passing.h"
30
#include "openmc/openmp_interface.h"
31
#include "openmc/particle_data.h"
32
#include "openmc/plot.h"
33
#include "openmc/random_dist.h"
34
#include "openmc/search.h"
35
#include "openmc/settings.h"
36
#include "openmc/tallies/filter.h"
37
#include "openmc/tallies/tally.h"
38
#include "openmc/volume_calc.h"
39
#include "openmc/xml_interface.h"
40

41
/*#ifdef LIBMESH*/
42
/*#include "libmesh/mesh_modification.h"*/
43
/*#include "libmesh/mesh_tools.h"*/
44
/*#include "libmesh/numeric_vector.h"*/
45
/*#endif*/
46

47
#ifdef DAGMC
48
#include "moab/FileOptions.hpp"
49
#endif
50

51
namespace openmc {
52

53
//==============================================================================
54
// Global variables
55
//==============================================================================
56

NEW
57
HexagonalMesh::HexagonalMesh(pugi::xml_node node)
×
NEW
58
  : PeriodicStructuredMesh {node}
×
59
{
60
  // Determine number of dimensions for mesh
NEW
61
  if (!check_for_node(node, "dimension")) {
×
NEW
62
    fatal_error("Must specify <dimension> on a regular mesh.");
×
63
  }
64

NEW
65
  xt::xtensor<int, 1> shape = get_node_xarray<int>(node, "dimension");
×
NEW
66
  int n = n_dimension_ = shape.size();
×
NEW
67
  if (n != 1 && n != 2) {
×
NEW
68
    fatal_error("Mesh must be one or two dimensions.");
×
69
  }
NEW
70
  std::copy(shape.begin(), shape.end(), shape_.begin());
×
71

72
  // Check that dimensions are all greater than zero
NEW
73
  if (xt::any(shape <= 0)) {
×
NEW
74
    fatal_error("All entries on the <dimension> element for a tally "
×
75
                "mesh must be positive.");
76
  }
NEW
77
  if (shape_[0] % 2 == 0) {
×
NEW
78
    fatal_error("First shape coordinate must be odd to avoid non-integer "
×
79
                "ring count.");
80
  }
NEW
81
  hex_radius_ = (shape_[0] - 1) / 2;
×
82

83
  // Check for lower-left coordinates
NEW
84
  if (check_for_node(node, "lower_left")) {
×
85
    // Read mesh lower-left corner location
NEW
86
    lower_left_ = get_node_xarray<double>(node, "lower_left");
×
87
  } else {
NEW
88
    fatal_error("Must specify <lower_left> on a hexagonal mesh.");
×
89
  }
90

91
  // Make sure lower_left and dimension match
NEW
92
  if (shape.size() != lower_left_.size()) {
×
NEW
93
    fatal_error("Number of entries on <lower_left> must be the same "
×
94
                "as the number of entries on <dimension>.");
95
  }
96

NEW
97
  if (check_for_node(node, "width")) {
×
98
    // Make sure one of upper-right or width were specified
NEW
99
    if (check_for_node(node, "upper_right")) {
×
NEW
100
      fatal_error(
×
101
        "Cannot specify both <upper_right> and <width> on a hexgonal mesh.");
102
    }
103

104
    // Check to ensure width has same dimensions
NEW
105
    auto n = width_.size();
×
NEW
106
    if (n != lower_left_.size()) {
×
NEW
107
      fatal_error("Number of entries on <width> must be the same as "
×
108
                  "the number of entries on <lower_left>.");
109
    }
110

111
    // Check for negative widths
NEW
112
    if (xt::any(width_ < 0.0)) {
×
NEW
113
      fatal_error("Cannot have a negative <width> on a tally hexgonal mesh.");
×
114
    }
115

116
    // Set width and upper right coordinate
NEW
117
    upper_right_ = xt::eval(lower_left_ + shape * width_);
×
118

NEW
119
  } else if (check_for_node(node, "upper_right")) {
×
NEW
120
    upper_right_ = get_node_xarray<double>(node, "upper_right");
×
121

122
    // Check to ensure width has same dimensions
NEW
123
    auto n = upper_right_.size();
×
NEW
124
    if (n != lower_left_.size()) {
×
NEW
125
      fatal_error("Number of entries on <upper_right> must be the "
×
126
                  "same as the number of entries on <lower_left>.");
127
    }
128

129
    // Check that upper-right is above lower-left
NEW
130
    if (xt::any(upper_right_ < lower_left_)) {
×
NEW
131
      fatal_error("The <upper_right> coordinates must be greater than "
×
132
                  "the <lower_left> coordinates on a tally hexgonal mesh.");
133
    }
134

135
    // Set width
NEW
136
    width_ = xt::eval((upper_right_ - lower_left_) / shape);
×
137
  } else {
NEW
138
    fatal_error(
×
139
      "Must specify either <upper_right> or <width> on a hexagonal mesh.");
140
  }
141

142
  // n.b. must check that the number of hexes is odd
143
  //   or allow a parameter crown/ring
NEW
144
  int max_a = (shape[0] - 1) / 2;
×
NEW
145
  if (max_a == 0)
×
NEW
146
    hex_count_ = 1;
×
147
  else
NEW
148
    hex_count_ = 1 + 3 * (max_a) * (max_a - 1);
×
149

150
  // width[1] is the height of the full mesh block, width[0] is the width of
151
  // the full mesh block at its widest
NEW
152
  element_volume_ = width_[1] * width_[0] * width_[0] * sqrt(3);
×
153

154
  // Set material volumes
NEW
155
  volume_frac_ = 1.0 / hex_count_;
×
156

157
  // size of hex is defined as the radius of the circumscribed circle
NEW
158
  size_ = width_[0] / sqrt(3.0);
×
159

160
  // radius of enclosing cylinder
NEW
161
  r_encl_ = (hex_radius_ - 0.5) * sqrt(3) * size_ + (1 - sqrt(3) * 0.5) * size_;
×
162

163
  // set the plane normals or 3 principal directions in the hex mash
NEW
164
  init_plane_normals();
×
165

166
  // scale grid vectors of hexagonal mesh
NEW
167
  scale_grid_vectors(size_);
×
168
}
169

170
const std::string HexagonalMesh::mesh_type = "hexagonal";
171

NEW
172
double HexagonalMesh::volume(const StructuredMesh::MeshIndex& ijk) const
×
173
{
NEW
174
  return element_volume_;
×
175
}
176

NEW
177
int HexagonalMesh::scale_grid_vectors(double s)
×
178
{
179
  // scale basis vectors of hexagonal mesh
NEW
180
  r_ = r_ * s;
×
NEW
181
  q_ = q_ * s;
×
NEW
182
  s_ = s_ * s;
×
NEW
183
  return 0;
×
184
}
185

NEW
186
int HexagonalMesh::init_plane_normals()
×
187
{
NEW
188
  n0_ = 0.5 * (r_ + q_ * 0.5);
×
NEW
189
  n1_ = 0.5 * (-s_ - r_ * 0.5);
×
NEW
190
  n2_ = 0.5 * (q_ + s_ * 0.5);
×
191

NEW
192
  n0_ /= n0_.norm();
×
NEW
193
  n1_ /= n1_.norm();
×
NEW
194
  n2_ /= n2_.norm();
×
195

NEW
196
  return 0;
×
197
}
198

NEW
199
std::string HexagonalMesh::get_mesh_type() const
×
200
{
NEW
201
  return mesh_type;
×
202
}
203

NEW
204
int HexagonalMesh::hex_distance(
×
205
  const HexMeshIndex& ijkl0, const HexMeshIndex& ijkl1) const
206
{
207
  // return the integer lateral hex-distance between two hexes (ignores z)
NEW
208
  return std::max(
×
NEW
209
    std::max(std::abs(ijkl0[0] - ijkl1[0]), std::abs(ijkl0[1] - ijkl1[1])),
×
NEW
210
    std::abs(ijkl0[2] - ijkl1[2]));
×
211
}
212

NEW
213
int HexagonalMesh::hex_radius(const HexMeshIndex& ijkl) const
×
214
{
215
  // return the integer hex-radius of a hex. (ignores z)
NEW
216
  return std::max(
×
NEW
217
    std::max(std::abs(ijkl[0]), std::abs(ijkl[1])), std::abs(ijkl[2]));
×
218
}
219

NEW
220
int32_t HexagonalMesh::get_bin_from_hexindices(const HexMeshIndex& ijkl) const
×
221
{
222
  // get linear index from the HexMeshIndex
NEW
223
  int32_t r_0 = hex_radius(ijkl);
×
NEW
224
  int32_t start_of_ring = (1 + 3 * r_0 * (r_0 - 1));
×
NEW
225
  int32_t bin_no = (ijkl[3] - 1) * hex_count_ + (1 + 3 * r_0 * (r_0 - 1)) +
×
NEW
226
                   offset_in_ring(ijkl, r_0);
×
NEW
227
  return bin_no;
×
228
}
229

NEW
230
int32_t HexagonalMesh::offset_in_ring(const HexMeshIndex& ijkl, int32_t r) const
×
231
{
232
  // find the offset within a ring
NEW
233
  if (r == 0)
×
NEW
234
    return 0;
×
NEW
235
  HexMeshIndex i {ijkl};
×
NEW
236
  HexMeshIndex corner {r, 0, -r, 0};
×
237

NEW
238
  int segment {0};
×
NEW
239
  if (abs(i[2]) >= abs(i[1]) && abs(i[2]) >= abs(i[0])) {
×
NEW
240
    if (i[2] < 0)
×
NEW
241
      segment = 0;
×
242
    else
NEW
243
      segment = 3;
×
NEW
244
  } else if (abs(i[1]) >= abs(i[0])) {
×
NEW
245
    if (i[1] > 0)
×
NEW
246
      segment = 1;
×
247
    else
NEW
248
      segment = 4;
×
249
  } else {
NEW
250
    if (i[0] < 0)
×
NEW
251
      segment = 2;
×
252
    else
NEW
253
      segment = 5;
×
254
  }
255

NEW
256
  for (int k = 0; k < segment; k++)
×
NEW
257
    corner = rotate_hexindex(corner);
×
258

NEW
259
  int32_t hexd = hex_distance(corner, i);
×
NEW
260
  int ii = r * segment + hexd;
×
NEW
261
  return ii;
×
262
}
263

NEW
264
HexagonalMesh::HexMeshIndex HexagonalMesh::rotate_hexindex(
×
265
  const HexMeshIndex& ijkl) const
266
{
NEW
267
  HexMeshIndex new_ijkl {ijkl};
×
NEW
268
  new_ijkl[0] = -ijkl[1];
×
NEW
269
  new_ijkl[1] = -ijkl[2];
×
NEW
270
  new_ijkl[2] = -ijkl[0];
×
NEW
271
  return new_ijkl;
×
272
}
273

NEW
274
HexagonalMesh::HexMeshIndex HexagonalMesh::get_hexindices_from_bin(
×
275
  const int32_t bin) const
276
{
277
  std::array<HexMeshIndex, 6> directions;
NEW
278
  directions[0] = {-1, 1, 0, 0};
×
NEW
279
  directions[1] = {-1, 0, 1, 0};
×
NEW
280
  directions[2] = {0, -1, 1, 0};
×
NEW
281
  directions[3] = {1, -1, 0, 0};
×
NEW
282
  directions[4] = {1, 0, -1, 0};
×
NEW
283
  directions[5] = {0, 1, -1, 0};
×
284

NEW
285
  HexMeshIndex ijkl = {0, 0, 0, 1};
×
NEW
286
  ijkl[3] = (int)floor(bin / hex_count_) + 1;
×
NEW
287
  int spiral_index = bin % hex_count_;
×
NEW
288
  if (spiral_index == 0) {
×
NEW
289
    return ijkl;
×
290
  }
NEW
291
  int ring = (int)floor((sqrt(12 * spiral_index - 3) + 3) / 6);
×
NEW
292
  int start_of_ring = (1 + 3 * ring * (ring - 1));
×
293

NEW
294
  if (ring > 0) {
×
NEW
295
    int segment = (spiral_index - start_of_ring) / ring;
×
296

NEW
297
    ijkl[0] = ring;
×
NEW
298
    ijkl[2] = -ring;
×
299

NEW
300
    for (int k = 0; k < segment; k++)
×
NEW
301
      ijkl = rotate_hexindex(ijkl);
×
302

NEW
303
    for (int k = 0; k < spiral_index - start_of_ring - ring * segment; k++) {
×
NEW
304
      for (int l = 0; l < ijkl.size(); l++)
×
NEW
305
        ijkl[l] = ijkl[l] + directions[segment][l];
×
306
    }
307
  }
NEW
308
  return ijkl;
×
309
}
310

NEW
311
int HexagonalMesh::n_bins() const
×
312
{
NEW
313
  return hex_count_ * shape_[1];
×
314
}
315

NEW
316
int HexagonalMesh::n_surface_bins() const
×
317
{
NEW
318
  return 4 * 3 * n_bins();
×
319
}
320

NEW
321
xt::xtensor<int, 1> HexagonalMesh::get_x_shape() const
×
322
{
323
  // because method is const, shape_ is const as well and can't be adapted
NEW
324
  auto tmp_shape = shape_;
×
NEW
325
  tmp_shape[0] = hex_count_;
×
NEW
326
  return xt::adapt(tmp_shape, {2});
×
327
}
328

NEW
329
std::string HexagonalMesh::bin_label(int bin) const
×
330
{
NEW
331
  HexMeshIndex ijkl = get_hexindices_from_bin(bin);
×
NEW
332
  int hr = hex_radius(ijkl);
×
NEW
333
  int ofr = offset_in_ring(ijkl, hr);
×
334
  return fmt::format(
NEW
335
    "Mesh Index ({}, {}, {})", hr, offset_in_ring(ijkl, hr), ijkl[3]);
×
336
}
337

NEW
338
int HexagonalMesh::get_hexindex_in_direction(const Position& r, int i) const
×
339
{
NEW
340
  switch (i) {
×
NEW
341
  case 0:
×
NEW
342
    return std::round((2.0 / 3.0 * -r.y) / this->size_);
×
NEW
343
  case 1:
×
NEW
344
    return std::round((sqrt(3.0) / 3.0 * r.x + 1.0 / 3.0 * r.y) / this->size_);
×
NEW
345
  case 2:
×
NEW
346
    return -std::round((0.5 * r.x - 1.0 / (2 * sqrt(3)) * r.y) / this->size_) -
×
NEW
347
           std::round((1.0 / sqrt(3) * r.y) / this->size_);
×
NEW
348
  case 3:
×
349
    // z is idx 1 in width_ and lower_left_ / upper_right_
NEW
350
    return std::ceil((r.z - lower_left_[1]) / width_[1]);
×
351
  }
NEW
352
  return -1;
×
353
}
354

NEW
355
int HexagonalMesh::get_index_in_direction(double r, int i) const
×
356
{
NEW
357
  return 0;
×
358
}
359

NEW
360
Position HexagonalMesh::get_position_from_hexindex(HexMeshIndex ijkl) const
×
361
{
362
  // return the cartesian position of center of a hexagon indexed by ijkl
363
  // Note thet we have to use the plane normals as basis vectors, as opposed to
364
  // the grid vectors (r, q, s)
NEW
365
  Position r;
×
NEW
366
  r.x = ijkl[0] * n0_[0] * size_ * sqrt(3) + ijkl[1] * n1_[0] * size_ * sqrt(3);
×
NEW
367
  r.y = ijkl[0] * n0_[1] * size_ * sqrt(3) + ijkl[1] * n1_[1] * size_ * sqrt(3);
×
NEW
368
  r.z = ijkl[3] * width_[1];
×
369

NEW
370
  return r;
×
371
}
372

NEW
373
HexagonalMesh::HexMeshIndex HexagonalMesh::get_hexindices(
×
374
  Position r, bool& in_mesh) const
375
{
376
  // return index of mesh element in hexes
NEW
377
  local_coords(r);
×
378

379
  HexMeshIndex idx;
NEW
380
  idx[0] = get_hexindex_in_direction(r, 0);
×
NEW
381
  idx[1] = get_hexindex_in_direction(r, 1);
×
NEW
382
  idx[2] = -idx[0] - idx[1];
×
NEW
383
  idx[3] = get_hexindex_in_direction(r, 3);
×
384

385
  // check if either index is out of bounds
NEW
386
  in_mesh = in_hexmesh(idx);
×
NEW
387
  return idx;
×
388
}
389

NEW
390
bool HexagonalMesh::in_hexmesh(HexMeshIndex& ijkl) const
×
391
{
NEW
392
  for (auto it = ijkl.begin(); it != std::prev(ijkl.end()); it++) {
×
NEW
393
    auto elem = *it;
×
NEW
394
    if (abs(elem) > hex_radius_)
×
NEW
395
      return false;
×
396
  }
NEW
397
  if (ijkl[3] > shape_[1])
×
NEW
398
    return false;
×
NEW
399
  return true;
×
400
}
401

NEW
402
Position HexagonalMesh::sample_element(
×
403
  const HexMeshIndex& ijkl, uint64_t* seed) const
404
{
405
  // return random position inside mesh element
NEW
406
  double r_r = uniform_distribution(0, 1, seed);
×
NEW
407
  double q_r = uniform_distribution(0, 1, seed);
×
408

NEW
409
  double x = this->n0_[0] * (r_r + (ijkl[0] - 1)) * width_[0] +
×
NEW
410
             this->n1_[0] * (q_r + (ijkl[1] - 1)) * width_[0];
×
NEW
411
  double y = this->n0_[1] * (r_r + (ijkl[0] - 1)) * width_[0] +
×
NEW
412
             this->n1_[1] * (q_r + (ijkl[1] - 1)) * width_[0];
×
413

NEW
414
  double z = uniform_distribution(-width_[1] / 2.0, width_[1] / 2.0, seed);
×
415

NEW
416
  return origin_ + Position(x, y, z);
×
417
}
418

NEW
419
double HexagonalMesh::find_r_crossing(
×
420
  const Position& r, const Direction& u, double l) const
421
{
422
  // solve r.x^2 + r.y^2 == r0^2
423
  // x^2 + 2*s*u*x + s^2*u^2 + s^2*v^2+2*s*v*y + y^2 -r0^2 = 0
424
  // s^2 * (u^2 + v^2) + 2*s*(u*x+v*y) + x^2+y^2-r0^2 = 0
425

NEW
426
  const double r0 = r_encl_;
×
NEW
427
  if (r0 == 0.0)
×
NEW
428
    return INFTY;
×
429

NEW
430
  const double denominator = u.x * u.x + u.y * u.y;
×
431

432
  // Direction of flight is in z-direction. Will never intersect r.
NEW
433
  if (std::abs(denominator) < FP_PRECISION)
×
NEW
434
    return INFTY;
×
435

436
  // inverse of dominator to help the compiler to speed things up
NEW
437
  const double inv_denominator = 1.0 / denominator;
×
438

NEW
439
  const double p = (u.x * r.x + u.y * r.y) * inv_denominator;
×
NEW
440
  double c = r.x * r.x + r.y * r.y - r0 * r0;
×
NEW
441
  double D = p * p - c * inv_denominator;
×
442

NEW
443
  if (D < 0.0)
×
NEW
444
    return INFTY;
×
445

NEW
446
  D = std::sqrt(D);
×
447

448
  // the solution -p - D is always smaller as -p + D : Check this one first
NEW
449
  if (std::abs(c) <= RADIAL_MESH_TOL)
×
NEW
450
    return INFTY;
×
451

NEW
452
  if (-p - D > l)
×
NEW
453
    return -p - D;
×
NEW
454
  if (-p + D > l)
×
NEW
455
    return -p + D;
×
456

NEW
457
  return INFTY;
×
458
}
459

460
template<class T>
NEW
461
void HexagonalMesh::raytrace_mesh(
×
462
  Position r0, Position r1, const Direction& u, T tally) const
463
{
464
  // TODO: when c++-17 is available, use "if constexpr ()" to compile-time
465
  // enable/disable tally calls for now, T template type needs to provide both
466
  // surface and track methods, which might be empty. modern optimizing
467
  // compilers will (hopefully) eliminate the complete code (including
468
  // calculation of parameters) but for the future: be explicit
469

470
  // very similar to to the structured mesh raytrace_mesh but we cannot rely on
471
  // simply recomputing only a single distance. Also the distance to the outside
472
  // of the mesh is somewhat more complicated
NEW
473
  int iteration {0};
×
474
  // Compute the length of the entire track.
NEW
475
  double total_distance = (r1 - r0).norm();
×
NEW
476
  if (total_distance == 0.0 && settings::solver_type != SolverType::RANDOM_RAY)
×
NEW
477
    return;
×
478

NEW
479
  const int n = n_dimension_ * 2;
×
480

481
  // Flag if position is inside the mesh
482
  bool in_mesh;
483

484
  // Position is r = r0 + u * traveled_distance, start at r0
NEW
485
  double traveled_distance {0.0};
×
486

487
  // Calculate index of current cell. Offset the position a tiny bit in
488
  // direction of flight
NEW
489
  HexMeshIndex ijkl = get_hexindices(r0 + TINY_BIT * u, in_mesh);
×
490

491
  // If outside mesh and not on the way towards it (i.e. missing the
492
  // circumscribed cylinder) exit early.
NEW
493
  double dist_to_enclosing_cyl = find_r_crossing(r0, u, traveled_distance);
×
494

NEW
495
  if (!in_mesh) {
×
NEW
496
    if (dist_to_enclosing_cyl < INFTY &&
×
NEW
497
        dist_to_enclosing_cyl < total_distance) {
×
NEW
498
      traveled_distance = dist_to_enclosing_cyl;
×
499
    } else
NEW
500
      return;
×
501
  }
502

503
  // if track is very short, assume that it is completely inside one cell.
504
  // Only the current cell will score and no surfaces
NEW
505
  if (total_distance < 2 * TINY_BIT) {
×
NEW
506
    if (in_mesh) {
×
NEW
507
      tally.track(ijkl, 1.0);
×
508
    }
NEW
509
    return;
×
510
  }
511

512
  // translate start and end positions,
513
  // this needs to come after the get_hexindices call because it does its own
514
  // translation
NEW
515
  local_coords(r0);
×
NEW
516
  local_coords(r1);
×
517

518
  // Calculate initial distances to next surfaces in all three dimensions
NEW
519
  std::array<HexMeshDistance, 4> distances;
×
NEW
520
  for (int k = 0; k < n; ++k) {
×
NEW
521
    distances[k] = distance_to_hex_boundary(ijkl, k, r0, u, traveled_distance);
×
522
  }
523

524
  // Loop until r = r1 is eventually reached
NEW
525
  while (true) {
×
NEW
526
    iteration++;
×
527
    // std::cout << iteration << std::endl;
528
    //  find surface with minimal distance to current position
NEW
529
    const auto k =
×
NEW
530
      std::min_element(distances.begin(), distances.end()) - distances.begin();
×
531

NEW
532
    if (in_mesh) {
×
533
      // Tally track length delta since last step
NEW
534
      tally.track(ijkl,
×
NEW
535
        (std::min(distances[k].distance, total_distance) - traveled_distance) /
×
536
          total_distance);
537
    }
538

539
    // update position and leave, if we have reached end position
NEW
540
    traveled_distance = distances[k].distance;
×
NEW
541
    if (traveled_distance >= total_distance)
×
NEW
542
      return;
×
543

NEW
544
    if (in_mesh) {
×
545
      // If we have not reached r1, we have hit a surface. Tally outward current
NEW
546
      tally.surface(ijkl, k, distances[k].max_surface, false);
×
547
    }
548

549
    // Update cell and calculate distance to next surface in k-direction.
NEW
550
    ijkl = distances[k].next_index;
×
551
    // now the index has been updated recompute the distances - unfortunately
552
    // we have to do more than one again (as opposed to for cartesian mesh)
553

NEW
554
    if (k < 3) {
×
NEW
555
      for (int j = 0; j < 4; ++j) {
×
NEW
556
        distances[j] =
×
NEW
557
          distance_to_hex_boundary(ijkl, j, r0, u, traveled_distance);
×
558
      }
559
    } else {
NEW
560
      distances[3] =
×
NEW
561
        distance_to_hex_boundary(ijkl, 3, r0, u, traveled_distance);
×
562
    }
563

564
    // Check if we have left the interior of the mesh
565
    // Do this by getting new index
NEW
566
    in_mesh = in_hexmesh(ijkl);
×
567

568
    // If we are still inside the mesh, tally inward current for the next cell
NEW
569
    if (in_mesh)
×
NEW
570
      tally.surface(ijkl, k, !distances[k].max_surface, true);
×
571
  }
572
}
NEW
573

×
574
HexagonalMesh::HexMeshDistance HexagonalMesh::distance_to_hex_boundary(
575
  const HexMeshIndex& ijkl, int i, const Position& r0, const Direction& u,
576
  double l) const
577
{
578
  // Compute the distance to the element boundary of index i \in {0, ..., 6}
579
  // i==6 means z
580

581
  Position r = r0 - origin_;
582
  // Given the hex index - we now find the distance from r0 to the 0:q, 1:r, 2:s
583
  // plane(s) successively, given that the hex-center is given by the hexindex
584
  // quadruplet and hence also the planes.
NEW
585
  Position rh = get_position_from_hexindex(ijkl);
×
586
  // local position relative to hex center
NEW
587
  Position rloc = r0 + l * u - rh;
×
NEW
588
  HexMeshDistance d;
×
NEW
589
  d.next_index = ijkl;
×
590

NEW
591
  double dh = 0;
×
592

593
  if (i < 3) {
594
    if (std::abs(u[0]) + std::abs(u[1]) < FP_PRECISION)
595
      return d;
596
    switch (i) {
NEW
597
    case 0:
×
598
      dh = rh.dot(n0_) / u.dot(n0_);
599
      d.max_surface = n0_.dot(u) > 0;
600
      if (abs(ijkl[0]) > hex_radius_ + 1) {
NEW
601
        return d;
×
602
      }
603
      if (d.max_surface) {
604
        d.distance = dh + (this->r_ - r0).dot(this->n0_) / u.dot(this->n0_);
NEW
605
        d.next_index[0]++;
×
606
        d.next_index[2]--;
NEW
607
      } else {
×
NEW
608
        d.distance = dh + (-this->r_ - r0).dot(this->n0_) / u.dot(this->n0_);
×
NEW
609
        d.next_index[0]--;
×
NEW
610
        d.next_index[2]++;
×
611
      }
NEW
612
      break;
×
613
    case 1:
614
      dh = rh.dot(n1_) / u.dot(n1_);
615
      d.max_surface = n1_.dot(u) > 0;
616
      if (abs(ijkl[1]) > hex_radius_ + 1) {
NEW
617
        return d;
×
NEW
618
      }
×
NEW
619
      if (d.max_surface) {
×
620
        d.distance = dh + (-this->s_ - r0).dot(this->n1_) / u.dot(this->n1_);
NEW
621
        d.next_index[1]++;
×
622
        d.next_index[2]--;
623
      } else {
624
        d.distance = dh + (this->s_ - r0).dot(this->n1_) / u.dot(this->n1_);
625
        d.next_index[1]--;
626
        d.next_index[2]++;
NEW
627
      }
×
NEW
628
      break;
×
629
    case 2:
630
      dh = rh.dot(n2_) / u.dot(n2_);
NEW
631
      d.max_surface = n2_.dot(u) > 0;
×
NEW
632
      if (abs(ijkl[2]) > hex_radius_ + 1) {
×
NEW
633
        return d;
×
634
      }
635
      if (d.max_surface) {
636
        d.distance = dh + (this->q_ - r0).dot(this->n2_) / u.dot(this->n2_);
NEW
637
        d.next_index[0]--;
×
NEW
638
        d.next_index[1]++;
×
639
      } else {
640
        d.distance = dh + (-this->q_ - r0).dot(this->n2_) / u.dot(this->n2_);
NEW
641
        d.next_index[0]++;
×
NEW
642
        d.next_index[1]--;
×
643
      }
NEW
644
      break;
×
645
    }
NEW
646
  } else {
×
NEW
647
    if (std::abs(u[2]) < FP_PRECISION) {
×
648
      return d;
649
    }
650
    // Z-planes z-index has index 3 (nr 4) in ijkl.
651
    d.max_surface = (u[2] > 0);
NEW
652
    if (d.max_surface) {
×
NEW
653
      d.next_index[3]++;
×
NEW
654
      d.distance = ((lower_left_[1] + ijkl[3] * width_[1]) - r0[2]) / u[2];
×
655
    } else {
NEW
656
      d.next_index[3]--;
×
657
      d.distance =
NEW
658
        ((lower_left_[1] + (ijkl[3] - 1) * width_[1]) - r0[2]) / u[2];
×
659
    }
660
  }
661
  return d;
NEW
662
}
×
663

664
StructuredMesh::MeshDistance HexagonalMesh::distance_to_grid_boundary(
665
  const MeshIndex& ijk, int i, const Position& r0, const Direction& u,
NEW
666
  double l) const
×
NEW
667
{
×
NEW
668
  MeshDistance d;
×
NEW
669
  d.distance = INFTY;
×
670
  return d;
671
}
NEW
672

×
NEW
673
std::pair<vector<double>, vector<double>> HexagonalMesh::plot(
×
674
  Position plot_ll, Position plot_ur) const
675
{
676
  fatal_error("Plot of hexagonal Mesh not implemented");
677

NEW
678
  // Figure out which axes lie in the plane of the plot.
×
679
  array<vector<double>, 2> axis_lines;
680
  return {axis_lines[0], axis_lines[1]};
NEW
681
}
×
NEW
682

×
683
void HexagonalMesh::to_hdf5_inner(hid_t mesh_group) const
684
{
NEW
685
  write_dataset(mesh_group, "dimension", get_x_shape());
×
686
  write_dataset(mesh_group, "lower_left", lower_left_);
687
  write_dataset(mesh_group, "upper_right", upper_right_);
688
  write_dataset(mesh_group, "width", width_);
689
}
690

691
double HexagonalMesh::volume(const HexMeshIndex& ijkl) const
692
{
693
  double zdiff = (upper_right_[2] - lower_left_[2]) / shape_[2];
694
  return 6 * sqrt(3.0) * 0.25 * size_ * size_ * zdiff;
695
}
696

NEW
697
void HexagonalMesh::bins_crossed(Position r0, Position r1, const Direction& u,
×
698
  vector<int>& bins, vector<double>& lengths) const
NEW
699
{
×
NEW
700

×
NEW
701
  // Helper tally class.
×
702
  // stores a pointer to the mesh class and references to bins and lengths
NEW
703
  // parameters. Performs the actual tally through the track method.
×
704
  struct TrackAggregator {
705
    TrackAggregator(
706
      const HexagonalMesh* _mesh, vector<int>& _bins, vector<double>& _lengths)
707
      : mesh(_mesh), bins(_bins), lengths(_lengths)
708
    {}
NEW
709
    void surface(const HexMeshIndex& ijkl, int k, bool max, bool inward) const
×
710
    {}
711
    void track(const HexMeshIndex& ijkl, double l) const
712
    {
NEW
713
      bins.push_back(mesh->get_bin_from_hexindices(ijkl));
×
714
      lengths.push_back(l);
715
    }
716

NEW
717
    const HexagonalMesh* mesh;
×
718
    vector<int>& bins;
NEW
719
    vector<double>& lengths;
×
NEW
720
  };
×
NEW
721

×
NEW
722
  // Perform the mesh raytrace with the helper class.
×
723
  raytrace_mesh(r0, r1, u, TrackAggregator(this, bins, lengths));
NEW
724
}
×
725

726
void HexagonalMesh::surface_bins_crossed(
727
  Position r0, Position r1, const Direction& u, vector<int>& bins) const
728
{
NEW
729

×
NEW
730
  // Helper tally class.
×
NEW
731
  // stores a pointer to the mesh class and a reference to the bins parameter.
×
732
  // Performs the actual tally through the surface method.
NEW
733
  struct SurfaceAggregator {
×
734
    SurfaceAggregator(const HexagonalMesh* _mesh, vector<int>& _bins)
735
      : mesh(_mesh), bins(_bins)
736
    {}
737
    void surface(const HexMeshIndex& ijkl, int k, bool max, bool inward) const
738
    {
NEW
739
      int i_bin =
×
NEW
740
        4 * mesh->hex_count_ * mesh->get_bin_from_hexindices(ijkl) + 4 * k;
×
741
      if (max)
742
        i_bin += 2;
NEW
743
      if (inward)
×
NEW
744
        i_bin += 1;
×
NEW
745
      bins.push_back(i_bin);
×
746
    }
747
    void track(const HexMeshIndex& idx, double l) const {}
748

NEW
749
    const HexagonalMesh* mesh;
×
NEW
750
    vector<int>& bins;
×
751
  };
752

NEW
753
  // Perform the mesh raytrace with the helper class.
×
NEW
754
  raytrace_mesh(r0, r1, u, SurfaceAggregator(this, bins));
×
755
}
NEW
756

×
757
} // 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