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

openmc-dev / openmc / 14645405719

24 Apr 2025 03:23PM UTC coverage: 84.416% (-0.4%) from 84.851%
14645405719

Pull #3279

github

web-flow
Merge 8a3e52b5d into 820648dae
Pull Request #3279: Hexagonal mesh model

94 of 475 new or added lines in 5 files covered. (19.79%)

3299 existing lines in 93 files now uncovered.

52250 of 61896 relevant lines covered (84.42%)

36990428.73 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
namespace openmc {
42

43
//==============================================================================
44
// Global variables
45
//==============================================================================
46

NEW
47
HexagonalMesh::HexagonalMesh(pugi::xml_node node)
×
NEW
48
  : PeriodicStructuredMesh {node}
×
49
{
50
  // Determine number of dimensions for mesh
NEW
51
  if (!check_for_node(node, "dimension")) {
×
NEW
52
    fatal_error("Must specify <dimension> on a regular mesh.");
×
53
  }
54

NEW
55
  xt::xtensor<int, 1> shape = get_node_xarray<int>(node, "dimension");
×
NEW
56
  int n = n_dimension_ = shape.size();
×
NEW
57
  if (n != 1 && n != 2) {
×
NEW
58
    fatal_error("Mesh must be one or two dimensions.");
×
59
  }
NEW
60
  std::copy(shape.begin(), shape.end(), shape_.begin());
×
61

62
  // Check that dimensions are all greater than zero
NEW
63
  if (xt::any(shape <= 0)) {
×
NEW
64
    fatal_error("All entries on the <dimension> element for a tally "
×
65
                "mesh must be positive.");
66
  }
NEW
67
  if (shape_[0] % 2 == 0) {
×
NEW
68
    fatal_error("First shape coordinate must be odd to avoid non-integer "
×
69
                "ring count.");
70
  }
NEW
71
  hex_radius_ = (shape_[0] - 1) / 2;
×
72

73
  // Check for lower-left coordinates
NEW
74
  if (check_for_node(node, "lower_left")) {
×
75
    // Read mesh lower-left corner location
NEW
76
    lower_left_ = get_node_xarray<double>(node, "lower_left");
×
77
  } else {
NEW
78
    fatal_error("Must specify <lower_left> on a hexagonal mesh.");
×
79
  }
80

81
  // Make sure lower_left and dimension match
NEW
82
  if (shape.size() != lower_left_.size()) {
×
NEW
83
    fatal_error("Number of entries on <lower_left> must be the same "
×
84
                "as the number of entries on <dimension>.");
85
  }
86

NEW
87
  if (check_for_node(node, "width")) {
×
NEW
88
    width_ = get_node_xarray<double>(node, "width");
×
89
    // Make sure one of upper-right or width were specified
NEW
90
    if (check_for_node(node, "upper_right")) {
×
NEW
91
      fatal_error(
×
92
        "Cannot specify both <upper_right> and <width> on a hexgonal mesh.");
93
    }
94

95
    // Check to ensure width has same dimensions
NEW
96
    auto n = width_.size();
×
NEW
97
    if (n != lower_left_.size()) {
×
NEW
98
      fatal_error("Number of entries on <width> must be the same as "
×
99
                  "the number of entries on <lower_left>.");
100
    }
101

102
    // Check for negative widths
NEW
103
    if (xt::any(width_ < 0.0)) {
×
NEW
104
      fatal_error("Cannot have a negative <width> on a tally hexgonal mesh.");
×
105
    }
106

107
    // Set width and upper right coordinate
NEW
108
    upper_right_ = xt::eval(lower_left_ + shape * width_);
×
109

NEW
110
  } else if (check_for_node(node, "upper_right")) {
×
NEW
111
    upper_right_ = get_node_xarray<double>(node, "upper_right");
×
112

113
    // Check to ensure width has same dimensions
NEW
114
    auto n = upper_right_.size();
×
NEW
115
    if (n != lower_left_.size()) {
×
NEW
116
      fatal_error("Number of entries on <upper_right> must be the "
×
117
                  "same as the number of entries on <lower_left>.");
118
    }
119

120
    // Check that upper-right is above lower-left
NEW
121
    if (xt::any(upper_right_ < lower_left_)) {
×
NEW
122
      fatal_error("The <upper_right> coordinates must be greater than "
×
123
                  "the <lower_left> coordinates on a tally hexgonal mesh.");
124
    }
125

126
    // Set width
NEW
127
    width_ = xt::eval((upper_right_ - lower_left_) / shape);
×
128
  } else {
NEW
129
    fatal_error(
×
130
      "Must specify either <upper_right> or <width> on a hexagonal mesh.");
131
  }
132

133
  // n.b. must check that the number of hexes is odd
134
  //   or allow a parameter crown/ring
NEW
135
  if (hex_radius_ == 0)
×
NEW
136
    hex_count_ = 1;
×
137
  else
NEW
138
    hex_count_ = 1 + 3 * (hex_radius_ + 1) * hex_radius_;
×
139

140
  // width[1] is the height of the full mesh block, width[0] is the width of
141
  // the hexagon from flat end to flat end
NEW
142
  element_volume_ = width_[1] * width_[0] * width_[0] * sqrt(3);
×
143

144
  // Set material volumes
NEW
145
  volume_frac_ = 1.0 / hex_count_;
×
146

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

150
  // radius of enclosing cylinder
NEW
151
  r_encl_ = (hex_radius_ - 0.5) * sqrt(3) * size_ + (1 - sqrt(3) * 0.5) * size_;
×
152

153
  // set the plane normals or 3 principal directions in the hex mash
NEW
154
  init_plane_normals();
×
155

156
  // scale grid vectors of hexagonal mesh
NEW
157
  scale_grid_vectors(size_);
×
158
}
159

160
const std::string HexagonalMesh::mesh_type = "hexagonal";
161

NEW
162
double HexagonalMesh::volume(const StructuredMesh::MeshIndex& ijk) const
×
163
{
NEW
164
  return element_volume_;
×
165
}
166

NEW
167
int HexagonalMesh::scale_grid_vectors(double s)
×
168
{
169
  // scale basis vectors of hexagonal mesh
NEW
170
  r_ = r_ * s;
×
NEW
171
  q_ = q_ * s;
×
NEW
172
  s_ = s_ * s;
×
NEW
173
  return 0;
×
174
}
175

NEW
176
int HexagonalMesh::init_plane_normals()
×
177
{
NEW
178
  n0_ = 0.5 * (r_ + q_ * 0.5);
×
NEW
179
  n1_ = 0.5 * (-s_ - r_ * 0.5);
×
NEW
180
  n2_ = 0.5 * (q_ + s_ * 0.5);
×
181

NEW
182
  n0_ /= n0_.norm();
×
NEW
183
  n1_ /= n1_.norm();
×
NEW
184
  n2_ /= n2_.norm();
×
185

NEW
186
  return 0;
×
187
}
188

NEW
189
std::string HexagonalMesh::get_mesh_type() const
×
190
{
NEW
191
  return mesh_type;
×
192
}
193

NEW
194
int HexagonalMesh::hex_distance(
×
195
  const HexMeshIndex& ijkl0, const HexMeshIndex& ijkl1) const
196
{
197
  // return the integer lateral hex-distance between two hexes (ignores z)
NEW
198
  return std::max(
×
NEW
199
    std::max(std::abs(ijkl0[0] - ijkl1[0]), std::abs(ijkl0[1] - ijkl1[1])),
×
NEW
200
    std::abs(ijkl0[2] - ijkl1[2]));
×
201
}
202

NEW
203
int HexagonalMesh::hex_radius(const HexMeshIndex& ijkl) const
×
204
{
205
  // return the integer hex-radius of a hex. (ignores z)
NEW
206
  return std::max(
×
NEW
207
    std::max(std::abs(ijkl[0]), std::abs(ijkl[1])), std::abs(ijkl[2]));
×
208
}
209

NEW
210
int32_t HexagonalMesh::get_bin_from_hexindices(const HexMeshIndex& ijkl) const
×
211
{
212
  // get linear index from the HexMeshIndex
NEW
213
  int32_t r_0 = hex_radius(ijkl);
×
NEW
214
  int32_t start_of_ring = (1 + 3 * r_0 * (r_0 - 1));
×
NEW
215
  int32_t bin_no = (ijkl[3] - 1) * hex_count_ + (1 + 3 * r_0 * (r_0 - 1)) +
×
NEW
216
                   offset_in_ring(ijkl, r_0);
×
NEW
217
  return bin_no;
×
218
}
219

NEW
220
int32_t HexagonalMesh::offset_in_ring(const HexMeshIndex& ijkl, int32_t r) const
×
221
{
222
  // find the offset within a ring
NEW
223
  if (r == 0)
×
NEW
224
    return 0;
×
NEW
225
  HexMeshIndex i {ijkl};
×
NEW
226
  HexMeshIndex corner {r, 0, -r, 0};
×
227

NEW
228
  int segment {0};
×
NEW
229
  if (abs(i[2]) >= abs(i[1]) && abs(i[2]) >= abs(i[0])) {
×
NEW
230
    if (i[2] < 0)
×
NEW
231
      segment = 0;
×
232
    else
NEW
233
      segment = 3;
×
NEW
234
  } else if (abs(i[1]) >= abs(i[0])) {
×
NEW
235
    if (i[1] > 0)
×
NEW
236
      segment = 1;
×
237
    else
NEW
238
      segment = 4;
×
239
  } else {
NEW
240
    if (i[0] < 0)
×
NEW
241
      segment = 2;
×
242
    else
NEW
243
      segment = 5;
×
244
  }
245

NEW
246
  for (int k = 0; k < segment; k++)
×
NEW
247
    corner = rotate_hexindex(corner);
×
248

NEW
249
  int32_t hexd = hex_distance(corner, i);
×
NEW
250
  int ii = r * segment + hexd;
×
NEW
251
  return ii;
×
252
}
253

NEW
254
HexagonalMesh::HexMeshIndex HexagonalMesh::rotate_hexindex(
×
255
  const HexMeshIndex& ijkl) const
256
{
NEW
257
  HexMeshIndex new_ijkl {ijkl};
×
NEW
258
  new_ijkl[0] = -ijkl[1];
×
NEW
259
  new_ijkl[1] = -ijkl[2];
×
NEW
260
  new_ijkl[2] = -ijkl[0];
×
NEW
261
  return new_ijkl;
×
262
}
263

NEW
264
HexagonalMesh::HexMeshIndex HexagonalMesh::get_hexindices_from_bin(
×
265
  const int32_t bin) const
266
{
267
  std::array<HexMeshIndex, 6> directions;
NEW
268
  directions[0] = {-1, 1, 0, 0};
×
NEW
269
  directions[1] = {-1, 0, 1, 0};
×
NEW
270
  directions[2] = {0, -1, 1, 0};
×
NEW
271
  directions[3] = {1, -1, 0, 0};
×
NEW
272
  directions[4] = {1, 0, -1, 0};
×
NEW
273
  directions[5] = {0, 1, -1, 0};
×
274

NEW
275
  HexMeshIndex ijkl = {0, 0, 0, 1};
×
NEW
276
  ijkl[3] = (int)floor(bin / hex_count_) + 1;
×
NEW
277
  int spiral_index = bin % hex_count_;
×
NEW
278
  if (spiral_index == 0) {
×
NEW
279
    return ijkl;
×
280
  }
NEW
281
  int ring = (int)floor((sqrt(12 * spiral_index - 3) + 3) / 6);
×
NEW
282
  int start_of_ring = (1 + 3 * ring * (ring - 1));
×
283

NEW
284
  if (ring > 0) {
×
NEW
285
    int segment = (spiral_index - start_of_ring) / ring;
×
286

NEW
287
    ijkl[0] = ring;
×
NEW
288
    ijkl[2] = -ring;
×
289

NEW
290
    for (int k = 0; k < segment; k++)
×
NEW
291
      ijkl = rotate_hexindex(ijkl);
×
292

NEW
293
    for (int k = 0; k < spiral_index - start_of_ring - ring * segment; k++) {
×
NEW
294
      for (int l = 0; l < ijkl.size(); l++)
×
NEW
295
        ijkl[l] = ijkl[l] + directions[segment][l];
×
296
    }
297
  }
NEW
298
  return ijkl;
×
299
}
300

NEW
301
int HexagonalMesh::n_bins() const
×
302
{
NEW
303
  return hex_count_ * shape_[1];
×
304
}
305

NEW
306
int HexagonalMesh::n_surface_bins() const
×
307
{
NEW
308
  return 4 * 3 * n_bins();
×
309
}
310

NEW
311
xt::xtensor<int, 1> HexagonalMesh::get_x_shape() const
×
312
{
313
  // because method is const, shape_ is const as well and can't be adapted
NEW
314
  auto tmp_shape = shape_;
×
NEW
315
  tmp_shape[0] = hex_count_;
×
NEW
316
  return xt::adapt(tmp_shape, {2});
×
317
}
318

NEW
319
std::string HexagonalMesh::bin_label(int bin) const
×
320
{
NEW
321
  HexMeshIndex ijkl = get_hexindices_from_bin(bin);
×
NEW
322
  int hr = hex_radius(ijkl);
×
NEW
323
  int ofr = offset_in_ring(ijkl, hr);
×
324
  return fmt::format(
NEW
325
    "Mesh Index ({}, {}, {})", hr, offset_in_ring(ijkl, hr), ijkl[3]);
×
326
}
327

NEW
328
int HexagonalMesh::get_hexindex_in_direction(const Position& r, int i) const
×
329
{
NEW
330
  switch (i) {
×
NEW
331
  case 0:
×
NEW
332
    return std::round((2.0 / 3.0 * -r.y) / this->size_);
×
NEW
333
  case 1:
×
NEW
334
    return std::round((sqrt(3.0) / 3.0 * r.x + 1.0 / 3.0 * r.y) / this->size_);
×
NEW
335
  case 2:
×
NEW
336
    return -std::round((0.5 * r.x - 1.0 / (2 * sqrt(3)) * r.y) / this->size_) -
×
NEW
337
           std::round((1.0 / sqrt(3) * r.y) / this->size_);
×
NEW
338
  case 3:
×
339
    // z is idx 1 in width_ and lower_left_ / upper_right_
NEW
340
    return std::ceil((r.z - lower_left_[1]) / width_[1]);
×
341
  }
NEW
342
  return -1;
×
343
}
344

NEW
345
int HexagonalMesh::get_index_in_direction(double r, int i) const
×
346
{
NEW
347
  return 0;
×
348
}
349

NEW
350
Position HexagonalMesh::get_position_from_hexindex(HexMeshIndex ijkl) const
×
351
{
352
  // return the cartesian position of center of a hexagon indexed by ijkl
353
  // Note thet we have to use the plane normals as basis vectors, as opposed to
354
  // the grid vectors (r, q, s)
NEW
355
  Position r;
×
NEW
356
  r.x = ijkl[0] * n0_[0] * size_ * sqrt(3) + ijkl[1] * n1_[0] * size_ * sqrt(3);
×
NEW
357
  r.y = ijkl[0] * n0_[1] * size_ * sqrt(3) + ijkl[1] * n1_[1] * size_ * sqrt(3);
×
NEW
358
  r.z = ijkl[3] * width_[1];
×
359

NEW
360
  return r;
×
361
}
362

NEW
363
HexagonalMesh::HexMeshIndex HexagonalMesh::get_hexindices(
×
364
  Position r, bool& in_mesh) const
365
{
366
  // return index of mesh element in hexes
NEW
367
  local_coords(r);
×
368

369
  HexMeshIndex idx;
NEW
370
  idx[0] = get_hexindex_in_direction(r, 0);
×
NEW
371
  idx[1] = get_hexindex_in_direction(r, 1);
×
NEW
372
  idx[2] = -idx[0] - idx[1];
×
NEW
373
  idx[3] = get_hexindex_in_direction(r, 3);
×
374

375
  // check if either index is out of bounds
NEW
376
  in_mesh = in_hexmesh(idx);
×
NEW
377
  return idx;
×
378
}
379

NEW
380
bool HexagonalMesh::in_hexmesh(HexMeshIndex& ijkl) const
×
381
{
NEW
382
  for (auto it = ijkl.begin(); it != std::prev(ijkl.end()); it++) {
×
NEW
383
    auto elem = *it;
×
NEW
384
    if (abs(elem) > hex_radius_)
×
NEW
385
      return false;
×
386
  }
NEW
387
  if (ijkl[3] > shape_[1])
×
NEW
388
    return false;
×
NEW
389
  return true;
×
390
}
391

NEW
392
Position HexagonalMesh::sample_element(
×
393
  const HexMeshIndex& ijkl, uint64_t* seed) const
394
{
395
  // return random position inside mesh element
NEW
396
  double r_r = uniform_distribution(0, 1, seed);
×
NEW
397
  double q_r = uniform_distribution(0, 1, seed);
×
398

NEW
399
  double x = this->n0_[0] * (r_r + (ijkl[0] - 1)) * width_[0] +
×
NEW
400
             this->n1_[0] * (q_r + (ijkl[1] - 1)) * width_[0];
×
NEW
401
  double y = this->n0_[1] * (r_r + (ijkl[0] - 1)) * width_[0] +
×
NEW
402
             this->n1_[1] * (q_r + (ijkl[1] - 1)) * width_[0];
×
403

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

NEW
406
  return origin_ + Position(x, y, z);
×
407
}
408

NEW
409
double HexagonalMesh::find_r_crossing(
×
410
  const Position& r, const Direction& u, double l) const
411
{
412
  // solve r.x^2 + r.y^2 == r0^2
413
  // x^2 + 2*s*u*x + s^2*u^2 + s^2*v^2+2*s*v*y + y^2 -r0^2 = 0
414
  // s^2 * (u^2 + v^2) + 2*s*(u*x+v*y) + x^2+y^2-r0^2 = 0
415

NEW
416
  const double r0 = r_encl_;
×
NEW
417
  if (r0 == 0.0)
×
NEW
418
    return INFTY;
×
419

NEW
420
  const double denominator = u.x * u.x + u.y * u.y;
×
421

422
  // Direction of flight is in z-direction. Will never intersect r.
NEW
423
  if (std::abs(denominator) < FP_PRECISION)
×
NEW
424
    return INFTY;
×
425

426
  // inverse of dominator to help the compiler to speed things up
NEW
427
  const double inv_denominator = 1.0 / denominator;
×
428

NEW
429
  const double p = (u.x * r.x + u.y * r.y) * inv_denominator;
×
NEW
430
  double c = r.x * r.x + r.y * r.y - r0 * r0;
×
NEW
431
  double D = p * p - c * inv_denominator;
×
432

NEW
433
  if (D < 0.0)
×
NEW
434
    return INFTY;
×
435

NEW
436
  D = std::sqrt(D);
×
437

438
  // the solution -p - D is always smaller as -p + D : Check this one first
NEW
439
  if (std::abs(c) <= RADIAL_MESH_TOL)
×
NEW
440
    return INFTY;
×
441

NEW
442
  if (-p - D > l)
×
NEW
443
    return -p - D;
×
NEW
444
  if (-p + D > l)
×
NEW
445
    return -p + D;
×
446

NEW
447
  return INFTY;
×
448
}
449

450
template<class T>
NEW
451
void HexagonalMesh::raytrace_mesh(
×
452
  Position r0, Position r1, const Direction& u, T tally) const
453
{
454
  // TODO: when c++-17 is available, use "if constexpr ()" to compile-time
455
  // enable/disable tally calls for now, T template type needs to provide both
456
  // surface and track methods, which might be empty. modern optimizing
457
  // compilers will (hopefully) eliminate the complete code (including
458
  // calculation of parameters) but for the future: be explicit
459

460
  // very similar to to the structured mesh raytrace_mesh but we cannot rely on
461
  // simply recomputing only a single distance. Also the distance to the outside
462
  // of the mesh is somewhat more complicated
NEW
463
  int iteration {0};
×
464
  // Compute the length of the entire track.
NEW
465
  double total_distance = (r1 - r0).norm();
×
NEW
466
  if (total_distance == 0.0 && settings::solver_type != SolverType::RANDOM_RAY)
×
NEW
467
    return;
×
468

NEW
469
  const int n = n_dimension_ * 2;
×
470

471
  // Flag if position is inside the mesh
472
  bool in_mesh;
473

474
  // Position is r = r0 + u * traveled_distance, start at r0
NEW
475
  double traveled_distance {0.0};
×
476

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

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

NEW
485
  if (!in_mesh) {
×
NEW
486
    if (dist_to_enclosing_cyl < INFTY &&
×
NEW
487
        dist_to_enclosing_cyl < total_distance) {
×
NEW
488
      traveled_distance = dist_to_enclosing_cyl;
×
489
    } else
NEW
490
      return;
×
491
  }
492

493
  // if track is very short, assume that it is completely inside one cell.
494
  // Only the current cell will score and no surfaces
NEW
495
  if (total_distance < 2 * TINY_BIT) {
×
NEW
496
    if (in_mesh) {
×
NEW
497
      tally.track(ijkl, 1.0);
×
498
    }
NEW
499
    return;
×
500
  }
501

502
  // translate start and end positions,
503
  // this needs to come after the get_hexindices call because it does its own
504
  // translation
NEW
505
  local_coords(r0);
×
NEW
506
  local_coords(r1);
×
507

508
  // Calculate initial distances to next surfaces in all three dimensions
NEW
509
  std::array<HexMeshDistance, 4> distances;
×
NEW
510
  for (int k = 0; k < n; ++k) {
×
NEW
511
    distances[k] = distance_to_hex_boundary(ijkl, k, r0, u, traveled_distance);
×
512
  }
513

514
  // Loop until r = r1 is eventually reached
NEW
515
  while (true) {
×
NEW
516
    iteration++;
×
517
    // std::cout << iteration << std::endl;
518
    //  find surface with minimal distance to current position
NEW
519
    const auto k =
×
NEW
520
      std::min_element(distances.begin(), distances.end()) - distances.begin();
×
521

NEW
522
    if (in_mesh) {
×
523
      // Tally track length delta since last step
NEW
524
      tally.track(ijkl,
×
NEW
525
        (std::min(distances[k].distance, total_distance) - traveled_distance) /
×
526
          total_distance);
527
    }
528

529
    // update position and leave, if we have reached end position
NEW
530
    traveled_distance = distances[k].distance;
×
NEW
531
    if (traveled_distance >= total_distance)
×
NEW
532
      return;
×
533

NEW
534
    if (in_mesh) {
×
535
      // If we have not reached r1, we have hit a surface. Tally outward current
NEW
536
      tally.surface(ijkl, k, distances[k].max_surface, false);
×
537
    }
538

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

NEW
544
    if (k < 3) {
×
NEW
545
      for (int j = 0; j < 4; ++j) {
×
NEW
546
        distances[j] =
×
NEW
547
          distance_to_hex_boundary(ijkl, j, r0, u, traveled_distance);
×
548
      }
549
    } else {
NEW
550
      distances[3] =
×
NEW
551
        distance_to_hex_boundary(ijkl, 3, r0, u, traveled_distance);
×
552
    }
553

554
    // Check if we have left the interior of the mesh
555
    // Do this by getting new index
NEW
556
    in_mesh = in_hexmesh(ijkl);
×
557

558
    // If we are still inside the mesh, tally inward current for the next cell
NEW
559
    if (in_mesh)
×
NEW
560
      tally.surface(ijkl, k, !distances[k].max_surface, true);
×
561
  }
562
}
NEW
563

×
564
HexagonalMesh::HexMeshDistance HexagonalMesh::distance_to_hex_boundary(
565
  const HexMeshIndex& ijkl, int i, const Position& r0, const Direction& u,
566
  double l) const
567
{
568
  // Compute the distance to the element boundary of index i \in {0, ..., 6}
569
  // i==6 means z
570

571
  Position r = r0 - origin_;
572
  // Given the hex index - we now find the distance from r0 to the 0:q, 1:r, 2:s
573
  // plane(s) successively, given that the hex-center is given by the hexindex
574
  // quadruplet and hence also the planes.
NEW
575
  Position rh = get_position_from_hexindex(ijkl);
×
576
  // local position relative to hex center
NEW
577
  Position rloc = r0 + l * u - rh;
×
NEW
578
  HexMeshDistance d;
×
NEW
579
  d.next_index = ijkl;
×
580

NEW
581
  double dh = 0;
×
582

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

654
StructuredMesh::MeshDistance HexagonalMesh::distance_to_grid_boundary(
655
  const MeshIndex& ijk, int i, const Position& r0, const Direction& u,
NEW
656
  double l) const
×
NEW
657
{
×
NEW
658
  MeshDistance d;
×
NEW
659
  d.distance = INFTY;
×
660
  return d;
661
}
NEW
662

×
NEW
663
std::pair<vector<double>, vector<double>> HexagonalMesh::plot(
×
664
  Position plot_ll, Position plot_ur) const
665
{
666
  fatal_error("Plot of hexagonal Mesh not implemented");
667

NEW
668
  // Figure out which axes lie in the plane of the plot.
×
669
  array<vector<double>, 2> axis_lines;
670
  return {axis_lines[0], axis_lines[1]};
NEW
671
}
×
NEW
672

×
673
void HexagonalMesh::to_hdf5_inner(hid_t mesh_group) const
674
{
NEW
675
  write_dataset(mesh_group, "dimension", get_x_shape());
×
676
  write_dataset(mesh_group, "lower_left", lower_left_);
677
  write_dataset(mesh_group, "upper_right", upper_right_);
678
  write_dataset(mesh_group, "width", width_);
679
}
680

681
double HexagonalMesh::volume(const HexMeshIndex& ijkl) const
682
{
683
  double zdiff = (upper_right_[2] - lower_left_[2]) / shape_[2];
684
  return 6 * sqrt(3.0) * 0.25 * size_ * size_ * zdiff;
685
}
686

NEW
687
void HexagonalMesh::bins_crossed(Position r0, Position r1, const Direction& u,
×
688
  vector<int>& bins, vector<double>& lengths) const
NEW
689
{
×
NEW
690

×
NEW
691
  // Helper tally class.
×
692
  // stores a pointer to the mesh class and references to bins and lengths
NEW
693
  // parameters. Performs the actual tally through the track method.
×
694
  struct TrackAggregator {
695
    TrackAggregator(
696
      const HexagonalMesh* _mesh, vector<int>& _bins, vector<double>& _lengths)
697
      : mesh(_mesh), bins(_bins), lengths(_lengths)
698
    {}
NEW
699
    void surface(const HexMeshIndex& ijkl, int k, bool max, bool inward) const
×
700
    {}
701
    void track(const HexMeshIndex& ijkl, double l) const
702
    {
NEW
703
      bins.push_back(mesh->get_bin_from_hexindices(ijkl));
×
704
      lengths.push_back(l);
705
    }
706

NEW
707
    const HexagonalMesh* mesh;
×
708
    vector<int>& bins;
NEW
709
    vector<double>& lengths;
×
NEW
710
  };
×
NEW
711

×
NEW
712
  // Perform the mesh raytrace with the helper class.
×
713
  raytrace_mesh(r0, r1, u, TrackAggregator(this, bins, lengths));
NEW
714
}
×
715

716
void HexagonalMesh::surface_bins_crossed(
717
  Position r0, Position r1, const Direction& u, vector<int>& bins) const
718
{
NEW
719

×
NEW
720
  // Helper tally class.
×
NEW
721
  // stores a pointer to the mesh class and a reference to the bins parameter.
×
722
  // Performs the actual tally through the surface method.
NEW
723
  struct SurfaceAggregator {
×
724
    SurfaceAggregator(const HexagonalMesh* _mesh, vector<int>& _bins)
725
      : mesh(_mesh), bins(_bins)
726
    {}
727
    void surface(const HexMeshIndex& ijkl, int k, bool max, bool inward) const
728
    {
NEW
729
      int i_bin =
×
NEW
730
        4 * mesh->hex_count_ * mesh->get_bin_from_hexindices(ijkl) + 4 * k;
×
731
      if (max)
732
        i_bin += 2;
NEW
733
      if (inward)
×
NEW
734
        i_bin += 1;
×
NEW
735
      bins.push_back(i_bin);
×
736
    }
737
    void track(const HexMeshIndex& idx, double l) const {}
738

NEW
739
    const HexagonalMesh* mesh;
×
NEW
740
    vector<int>& bins;
×
741
  };
742

NEW
743
  // Perform the mesh raytrace with the helper class.
×
NEW
744
  raytrace_mesh(r0, r1, u, SurfaceAggregator(this, bins));
×
745
}
NEW
746

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