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

openmc-dev / openmc / 13183515203

06 Feb 2025 04:36PM UTC coverage: 82.601% (-2.3%) from 84.867%
13183515203

Pull #3087

github

web-flow
Merge d68c72d5e into 6e0f156d3
Pull Request #3087: wheel building with scikit build core

107123 of 129687 relevant lines covered (82.6%)

12608333.34 hits per line

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

93.73
/src/lattice.cpp
1
#include "openmc/lattice.h"
2

3
#include <cmath>
4
#include <string>
5

6
#include <fmt/core.h>
7

8
#include "openmc/cell.h"
9
#include "openmc/error.h"
10
#include "openmc/geometry.h"
11
#include "openmc/geometry_aux.h"
12
#include "openmc/hdf5_interface.h"
13
#include "openmc/string_utils.h"
14
#include "openmc/vector.h"
15
#include "openmc/xml_interface.h"
16

17
namespace openmc {
18

19
//==============================================================================
20
// Global variables
21
//==============================================================================
22

23
namespace model {
24
std::unordered_map<int32_t, int32_t> lattice_map;
25
vector<unique_ptr<Lattice>> lattices;
26
} // namespace model
27

28
//==============================================================================
29
// Lattice implementation
30
//==============================================================================
31

32
Lattice::Lattice(pugi::xml_node lat_node)
1,449✔
33
{
34
  if (check_for_node(lat_node, "id")) {
1,449✔
35
    id_ = std::stoi(get_node_value(lat_node, "id"));
1,449✔
36
  } else {
37
    fatal_error("Must specify id of lattice in geometry XML file.");
×
38
  }
39

40
  if (check_for_node(lat_node, "name")) {
1,449✔
41
    name_ = get_node_value(lat_node, "name");
357✔
42
  }
43

44
  if (check_for_node(lat_node, "outer")) {
1,449✔
45
    outer_ = std::stoi(get_node_value(lat_node, "outer"));
238✔
46
  }
47
}
1,449✔
48

49
//==============================================================================
50

51
LatticeIter Lattice::begin()
316,130,572✔
52
{
53
  return LatticeIter(*this, 0);
316,130,572✔
54
}
55

56
LatticeIter Lattice::end()
22,624,160✔
57
{
58
  return LatticeIter(*this, universes_.size());
22,624,160✔
59
}
60

61
int32_t& Lattice::back()
12✔
62
{
63
  return universes_.back();
12✔
64
}
65

66
ReverseLatticeIter Lattice::rbegin()
1,787,088✔
67
{
68
  return ReverseLatticeIter(*this, universes_.size() - 1);
1,787,088✔
69
}
70

71
ReverseLatticeIter Lattice::rend()
315,324,552✔
72
{
73
  return ReverseLatticeIter(*this, -1);
315,324,552✔
74
}
75

76
//==============================================================================
77

78
void Lattice::adjust_indices()
1,449✔
79
{
80
  // Adjust the indices for the universes array.
81
  for (LatticeIter it = begin(); it != end(); ++it) {
720,410✔
82
    int uid = *it;
718,961✔
83
    auto search = model::universe_map.find(uid);
718,961✔
84
    if (search != model::universe_map.end()) {
718,961✔
85
      *it = search->second;
718,961✔
86
    } else {
87
      fatal_error(fmt::format(
×
88
        "Invalid universe number {} specified on lattice {}", uid, id_));
×
89
    }
90
  }
91

92
  // Adjust the index for the outer universe.
93
  if (outer_ != NO_OUTER_UNIVERSE) {
1,449✔
94
    auto search = model::universe_map.find(outer_);
238✔
95
    if (search != model::universe_map.end()) {
238✔
96
      outer_ = search->second;
238✔
97
    } else {
98
      fatal_error(fmt::format(
×
99
        "Invalid universe number {} specified on lattice {}", outer_, id_));
×
100
    }
101
  }
102
}
1,449✔
103

104
//==============================================================================
105

106
int32_t Lattice::fill_offset_table(int32_t offset, int32_t target_univ_id,
15,474✔
107
  int map, std::unordered_map<int32_t, int32_t>& univ_count_memo)
108
{
109
  // If the offsets have already been determined for this "map", don't bother
110
  // recalculating all of them and just return the total offset. Note that the
111
  // offsets_ array doesn't actually include the offset accounting for the last
112
  // universe, so we get the before-last offset for the given map and then
113
  // explicitly add the count for the last universe.
114
  if (offsets_[map * universes_.size() + this->begin().indx_] != C_NONE) {
15,474✔
115
    int last_offset =
116
      offsets_[(map + 1) * universes_.size() - this->begin().indx_ - 1];
12✔
117
    int last_univ = this->back();
12✔
118
    return last_offset +
119
           count_universe_instances(last_univ, target_univ_id, univ_count_memo);
12✔
120
  }
121

122
  for (LatticeIter it = begin(); it != end(); ++it) {
5,846,730✔
123
    offsets_[map * universes_.size() + it.indx_] = offset;
5,831,268✔
124
    offset += count_universe_instances(*it, target_univ_id, univ_count_memo);
5,831,268✔
125
  }
126

127
  return offset;
15,462✔
128
}
129

130
//==============================================================================
131

132
void Lattice::to_hdf5(hid_t lattices_group) const
1,032✔
133
{
134
  // Make a group for the lattice.
135
  std::string group_name {"lattice "};
1,032✔
136
  group_name += std::to_string(id_);
1,032✔
137
  hid_t lat_group = create_group(lattices_group, group_name);
1,032✔
138

139
  // Write the name and outer universe.
140
  if (!name_.empty()) {
1,032✔
141
    write_string(lat_group, "name", name_, false);
240✔
142
  }
143

144
  if (outer_ != NO_OUTER_UNIVERSE) {
1,032✔
145
    int32_t outer_id = model::universes[outer_]->id_;
156✔
146
    write_dataset(lat_group, "outer", outer_id);
156✔
147
  } else {
148
    write_dataset(lat_group, "outer", outer_);
876✔
149
  }
150

151
  // Call subclass-overriden function to fill in other details.
152
  to_hdf5_inner(lat_group);
1,032✔
153

154
  close_group(lat_group);
1,032✔
155
}
1,032✔
156

157
//==============================================================================
158
// RectLattice implementation
159
//==============================================================================
160

161
RectLattice::RectLattice(pugi::xml_node lat_node) : Lattice {lat_node}
1,364✔
162
{
163
  type_ = LatticeType::rect;
1,364✔
164

165
  // Read the number of lattice cells in each dimension.
166
  std::string dimension_str {get_node_value(lat_node, "dimension")};
1,364✔
167
  vector<std::string> dimension_words {split(dimension_str)};
1,364✔
168
  if (dimension_words.size() == 2) {
1,364✔
169
    n_cells_[0] = std::stoi(dimension_words[0]);
1,051✔
170
    n_cells_[1] = std::stoi(dimension_words[1]);
1,051✔
171
    n_cells_[2] = 1;
1,051✔
172
    is_3d_ = false;
1,051✔
173
  } else if (dimension_words.size() == 3) {
313✔
174
    n_cells_[0] = std::stoi(dimension_words[0]);
313✔
175
    n_cells_[1] = std::stoi(dimension_words[1]);
313✔
176
    n_cells_[2] = std::stoi(dimension_words[2]);
313✔
177
    is_3d_ = true;
313✔
178
  } else {
179
    fatal_error("Rectangular lattice must be two or three dimensions.");
×
180
  }
181

182
  // Read the lattice lower-left location.
183
  std::string ll_str {get_node_value(lat_node, "lower_left")};
1,364✔
184
  vector<std::string> ll_words {split(ll_str)};
1,364✔
185
  if (ll_words.size() != dimension_words.size()) {
1,364✔
186
    fatal_error("Number of entries on <lower_left> must be the same as the "
×
187
                "number of entries on <dimension>.");
188
  }
189
  lower_left_[0] = stod(ll_words[0]);
1,364✔
190
  lower_left_[1] = stod(ll_words[1]);
1,364✔
191
  if (is_3d_) {
1,364✔
192
    lower_left_[2] = stod(ll_words[2]);
313✔
193
  }
194

195
  // Read the lattice pitches.
196
  std::string pitch_str {get_node_value(lat_node, "pitch")};
1,364✔
197
  vector<std::string> pitch_words {split(pitch_str)};
1,364✔
198
  if (pitch_words.size() != dimension_words.size()) {
1,364✔
199
    fatal_error("Number of entries on <pitch> must be the same as the "
×
200
                "number of entries on <dimension>.");
201
  }
202
  pitch_[0] = stod(pitch_words[0]);
1,364✔
203
  pitch_[1] = stod(pitch_words[1]);
1,364✔
204
  if (is_3d_) {
1,364✔
205
    pitch_[2] = stod(pitch_words[2]);
313✔
206
  }
207

208
  // Read the universes and make sure the correct number was specified.
209
  std::string univ_str {get_node_value(lat_node, "universes")};
1,364✔
210
  vector<std::string> univ_words {split(univ_str)};
1,364✔
211
  if (univ_words.size() != n_cells_[0] * n_cells_[1] * n_cells_[2]) {
1,364✔
212
    fatal_error(fmt::format(
×
213
      "Expected {} universes for a rectangular lattice of size {}x{}x{} but {} "
214
      "were specified.",
215
      n_cells_[0] * n_cells_[1] * n_cells_[2], n_cells_[0], n_cells_[1],
×
216
      n_cells_[2], univ_words.size()));
×
217
  }
218

219
  // Parse the universes.
220
  universes_.resize(n_cells_[0] * n_cells_[1] * n_cells_[2], C_NONE);
1,364✔
221
  for (int iz = 0; iz < n_cells_[2]; iz++) {
5,950✔
222
    for (int iy = n_cells_[1] - 1; iy > -1; iy--) {
58,101✔
223
      for (int ix = 0; ix < n_cells_[0]; ix++) {
743,780✔
224
        int indx1 = n_cells_[0] * n_cells_[1] * iz +
690,265✔
225
                    n_cells_[0] * (n_cells_[1] - iy - 1) + ix;
690,265✔
226
        int indx2 = n_cells_[0] * n_cells_[1] * iz + n_cells_[0] * iy + ix;
690,265✔
227
        universes_[indx1] = std::stoi(univ_words[indx2]);
690,265✔
228
      }
229
    }
230
  }
231
}
1,364✔
232

233
//==============================================================================
234

235
const int32_t& RectLattice::operator[](const array<int, 3>& i_xyz)
301,154,545✔
236
{
237
  return universes_[get_flat_index(i_xyz)];
301,154,545✔
238
}
239

240
//==============================================================================
241

242
bool RectLattice::are_valid_indices(const array<int, 3>& i_xyz) const
816,177,563✔
243
{
244
  return ((i_xyz[0] >= 0) && (i_xyz[0] < n_cells_[0]) && (i_xyz[1] >= 0) &&
1,630,145,302✔
245
          (i_xyz[1] < n_cells_[1]) && (i_xyz[2] >= 0) &&
2,147,483,647✔
246
          (i_xyz[2] < n_cells_[2]));
1,624,916,986✔
247
}
248

249
//==============================================================================
250

251
std::pair<double, array<int, 3>> RectLattice::distance(
523,232,388✔
252
  Position r, Direction u, const array<int, 3>& i_xyz) const
253
{
254
  // Get short aliases to the coordinates.
255
  double x = r.x;
523,232,388✔
256
  double y = r.y;
523,232,388✔
257
  double z = r.z;
523,232,388✔
258

259
  // Determine the oncoming edge.
260
  double x0 {copysign(0.5 * pitch_[0], u.x)};
523,232,388✔
261
  double y0 {copysign(0.5 * pitch_[1], u.y)};
523,232,388✔
262

263
  // Left and right sides
264
  double d {INFTY};
523,232,388✔
265
  array<int, 3> lattice_trans;
266
  if ((std::abs(x - x0) > FP_PRECISION) && u.x != 0) {
523,232,388✔
267
    d = (x0 - x) / u.x;
523,232,388✔
268
    if (u.x > 0) {
523,232,388✔
269
      lattice_trans = {1, 0, 0};
272,838,111✔
270
    } else {
271
      lattice_trans = {-1, 0, 0};
250,394,277✔
272
    }
273
  }
274

275
  // Front and back sides
276
  if ((std::abs(y - y0) > FP_PRECISION) && u.y != 0) {
523,232,388✔
277
    double this_d = (y0 - y) / u.y;
523,232,388✔
278
    if (this_d < d) {
523,232,388✔
279
      d = this_d;
263,363,543✔
280
      if (u.y > 0) {
263,363,543✔
281
        lattice_trans = {0, 1, 0};
137,228,859✔
282
      } else {
283
        lattice_trans = {0, -1, 0};
126,134,684✔
284
      }
285
    }
286
  }
287

288
  // Top and bottom sides
289
  if (is_3d_) {
523,232,388✔
290
    double z0 {copysign(0.5 * pitch_[2], u.z)};
202,085,004✔
291
    if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) {
202,085,004✔
292
      double this_d = (z0 - z) / u.z;
202,085,004✔
293
      if (this_d < d) {
202,085,004✔
294
        d = this_d;
67,712,928✔
295
        if (u.z > 0) {
67,712,928✔
296
          lattice_trans = {0, 0, 1};
37,219,812✔
297
        } else {
298
          lattice_trans = {0, 0, -1};
30,493,116✔
299
        }
300
      }
301
    }
302
  }
303

304
  return {d, lattice_trans};
1,046,464,776✔
305
}
306

307
//==============================================================================
308

309
void RectLattice::get_indices(
51,847,257✔
310
  Position r, Direction u, array<int, 3>& result) const
311
{
312
  // Determine x index, accounting for coincidence
313
  double ix_ {(r.x - lower_left_.x) / pitch_.x};
51,847,257✔
314
  long ix_close {std::lround(ix_)};
51,847,257✔
315
  if (coincident(ix_, ix_close)) {
51,847,257✔
316
    result[0] = (u.x > 0) ? ix_close : ix_close - 1;
17,326,112✔
317
  } else {
318
    result[0] = std::floor(ix_);
34,521,145✔
319
  }
320

321
  // Determine y index, accounting for coincidence
322
  double iy_ {(r.y - lower_left_.y) / pitch_.y};
51,847,257✔
323
  long iy_close {std::lround(iy_)};
51,847,257✔
324
  if (coincident(iy_, iy_close)) {
51,847,257✔
325
    result[1] = (u.y > 0) ? iy_close : iy_close - 1;
17,568,609✔
326
  } else {
327
    result[1] = std::floor(iy_);
34,278,648✔
328
  }
329

330
  // Determine z index, accounting for coincidence
331
  result[2] = 0;
51,847,257✔
332
  if (is_3d_) {
51,847,257✔
333
    double iz_ {(r.z - lower_left_.z) / pitch_.z};
25,516,896✔
334
    long iz_close {std::lround(iz_)};
25,516,896✔
335
    if (coincident(iz_, iz_close)) {
25,516,896✔
336
      result[2] = (u.z > 0) ? iz_close : iz_close - 1;
5,639,568✔
337
    } else {
338
      result[2] = std::floor(iz_);
19,877,328✔
339
    }
340
  }
341
}
51,847,257✔
342

343
int RectLattice::get_flat_index(const array<int, 3>& i_xyz) const
301,154,545✔
344
{
345
  return n_cells_[0] * n_cells_[1] * i_xyz[2] + n_cells_[0] * i_xyz[1] +
301,154,545✔
346
         i_xyz[0];
301,154,545✔
347
}
348

349
//==============================================================================
350

351
Position RectLattice::get_local_position(
305,772,241✔
352
  Position r, const array<int, 3>& i_xyz) const
353
{
354
  r.x -= (lower_left_.x + (i_xyz[0] + 0.5) * pitch_.x);
305,772,241✔
355
  r.y -= (lower_left_.y + (i_xyz[1] + 0.5) * pitch_.y);
305,772,241✔
356
  if (is_3d_) {
305,772,241✔
357
    r.z -= (lower_left_.z + (i_xyz[2] + 0.5) * pitch_.z);
192,698,928✔
358
  }
359
  return r;
305,772,241✔
360
}
361

362
//==============================================================================
363

364
int32_t& RectLattice::offset(int map, const array<int, 3>& i_xyz)
507,584,878✔
365
{
366
  return offsets_[n_cells_[0] * n_cells_[1] * n_cells_[2] * map +
507,584,878✔
367
                  n_cells_[0] * n_cells_[1] * i_xyz[2] +
507,584,878✔
368
                  n_cells_[0] * i_xyz[1] + i_xyz[0]];
1,015,169,756✔
369
}
370

371
//==============================================================================
372

373
int32_t RectLattice::offset(int map, int indx) const
323✔
374
{
375
  return offsets_[n_cells_[0] * n_cells_[1] * n_cells_[2] * map + indx];
323✔
376
}
377

378
//==============================================================================
379

380
std::string RectLattice::index_to_string(int indx) const
1,787,088✔
381
{
382
  int iz {indx / (n_cells_[0] * n_cells_[1])};
1,787,088✔
383
  int iy {(indx - n_cells_[0] * n_cells_[1] * iz) / n_cells_[0]};
1,787,088✔
384
  int ix {indx - n_cells_[0] * n_cells_[1] * iz - n_cells_[0] * iy};
1,787,088✔
385
  std::string out {std::to_string(ix)};
1,787,088✔
386
  out += ',';
1,787,088✔
387
  out += std::to_string(iy);
1,787,088✔
388
  if (is_3d_) {
1,787,088✔
389
    out += ',';
×
390
    out += std::to_string(iz);
×
391
  }
392
  return out;
1,787,088✔
393
}
×
394

395
//==============================================================================
396

397
void RectLattice::to_hdf5_inner(hid_t lat_group) const
984✔
398
{
399
  // Write basic lattice information.
400
  write_string(lat_group, "type", "rectangular", false);
984✔
401
  if (is_3d_) {
984✔
402
    write_dataset(lat_group, "pitch", pitch_);
228✔
403
    write_dataset(lat_group, "lower_left", lower_left_);
228✔
404
    write_dataset(lat_group, "dimension", n_cells_);
228✔
405
  } else {
406
    array<double, 2> pitch_short {{pitch_[0], pitch_[1]}};
756✔
407
    write_dataset(lat_group, "pitch", pitch_short);
756✔
408
    array<double, 2> ll_short {{lower_left_[0], lower_left_[1]}};
756✔
409
    write_dataset(lat_group, "lower_left", ll_short);
756✔
410
    array<int, 2> nc_short {{n_cells_[0], n_cells_[1]}};
756✔
411
    write_dataset(lat_group, "dimension", nc_short);
756✔
412
  }
413

414
  // Write the universe ids.  The convention here is to switch the ordering on
415
  // the y-axis to match the way universes are input in a text file.
416
  if (is_3d_) {
984✔
417
    hsize_t nx {static_cast<hsize_t>(n_cells_[0])};
228✔
418
    hsize_t ny {static_cast<hsize_t>(n_cells_[1])};
228✔
419
    hsize_t nz {static_cast<hsize_t>(n_cells_[2])};
228✔
420
    vector<int> out(nx * ny * nz);
228✔
421

422
    for (int m = 0; m < nz; m++) {
2,808✔
423
      for (int k = 0; k < ny; k++) {
32,832✔
424
        for (int j = 0; j < nx; j++) {
389,232✔
425
          int indx1 = nx * ny * m + nx * k + j;
358,980✔
426
          int indx2 = nx * ny * m + nx * (ny - k - 1) + j;
358,980✔
427
          out[indx2] = model::universes[universes_[indx1]]->id_;
358,980✔
428
        }
429
      }
430
    }
431

432
    hsize_t dims[3] {nz, ny, nx};
228✔
433
    write_int(lat_group, 3, dims, "universes", out.data(), false);
228✔
434

435
  } else {
228✔
436
    hsize_t nx {static_cast<hsize_t>(n_cells_[0])};
756✔
437
    hsize_t ny {static_cast<hsize_t>(n_cells_[1])};
756✔
438
    vector<int> out(nx * ny);
756✔
439

440
    for (int k = 0; k < ny; k++) {
9,324✔
441
      for (int j = 0; j < nx; j++) {
149,088✔
442
        int indx1 = nx * k + j;
140,520✔
443
        int indx2 = nx * (ny - k - 1) + j;
140,520✔
444
        out[indx2] = model::universes[universes_[indx1]]->id_;
140,520✔
445
      }
446
    }
447

448
    hsize_t dims[3] {1, ny, nx};
756✔
449
    write_int(lat_group, 3, dims, "universes", out.data(), false);
756✔
450
  }
756✔
451
}
984✔
452

453
//==============================================================================
454
// HexLattice implementation
455
//==============================================================================
456

457
HexLattice::HexLattice(pugi::xml_node lat_node) : Lattice {lat_node}
85✔
458
{
459
  type_ = LatticeType::hex;
85✔
460

461
  // Read the number of lattice cells in each dimension.
462
  n_rings_ = std::stoi(get_node_value(lat_node, "n_rings"));
85✔
463
  if (check_for_node(lat_node, "n_axial")) {
85✔
464
    n_axial_ = std::stoi(get_node_value(lat_node, "n_axial"));
34✔
465
    is_3d_ = true;
34✔
466
  } else {
467
    n_axial_ = 1;
51✔
468
    is_3d_ = false;
51✔
469
  }
470

471
  // Read the lattice orientation.  Default to 'y'.
472
  if (check_for_node(lat_node, "orientation")) {
85✔
473
    std::string orientation = get_node_value(lat_node, "orientation");
17✔
474
    if (orientation == "y") {
17✔
475
      orientation_ = Orientation::y;
×
476
    } else if (orientation == "x") {
17✔
477
      orientation_ = Orientation::x;
17✔
478
    } else {
479
      fatal_error("Unrecognized orientation '" + orientation +
×
480
                  "' for lattice " + std::to_string(id_));
×
481
    }
482
  } else {
17✔
483
    orientation_ = Orientation::y;
68✔
484
  }
485

486
  // Read the lattice center.
487
  std::string center_str {get_node_value(lat_node, "center")};
85✔
488
  vector<std::string> center_words {split(center_str)};
85✔
489
  if (is_3d_ && (center_words.size() != 3)) {
85✔
490
    fatal_error("A hexagonal lattice with <n_axial> must have <center> "
×
491
                "specified by 3 numbers.");
492
  } else if (!is_3d_ && center_words.size() != 2) {
85✔
493
    fatal_error("A hexagonal lattice without <n_axial> must have <center> "
×
494
                "specified by 2 numbers.");
495
  }
496
  center_[0] = stod(center_words[0]);
85✔
497
  center_[1] = stod(center_words[1]);
85✔
498
  if (is_3d_) {
85✔
499
    center_[2] = stod(center_words[2]);
34✔
500
  }
501

502
  // Read the lattice pitches.
503
  std::string pitch_str {get_node_value(lat_node, "pitch")};
85✔
504
  vector<std::string> pitch_words {split(pitch_str)};
85✔
505
  if (is_3d_ && (pitch_words.size() != 2)) {
85✔
506
    fatal_error("A hexagonal lattice with <n_axial> must have <pitch> "
×
507
                "specified by 2 numbers.");
508
  } else if (!is_3d_ && (pitch_words.size() != 1)) {
85✔
509
    fatal_error("A hexagonal lattice without <n_axial> must have <pitch> "
×
510
                "specified by 1 number.");
511
  }
512
  pitch_[0] = stod(pitch_words[0]);
85✔
513
  if (is_3d_) {
85✔
514
    pitch_[1] = stod(pitch_words[1]);
34✔
515
  }
516

517
  // Read the universes and make sure the correct number was specified.
518
  int n_univ = (3 * n_rings_ * n_rings_ - 3 * n_rings_ + 1) * n_axial_;
85✔
519
  std::string univ_str {get_node_value(lat_node, "universes")};
85✔
520
  vector<std::string> univ_words {split(univ_str)};
85✔
521
  if (univ_words.size() != n_univ) {
85✔
522
    fatal_error(fmt::format(
×
523
      "Expected {} universes for a hexagonal lattice with {} rings and {} "
524
      "axial levels but {} were specified.",
525
      n_univ, n_rings_, n_axial_, univ_words.size()));
×
526
  }
527

528
  // Parse the universes.
529
  // Universes in hexagonal lattices are stored in a manner that represents
530
  // a skewed coordinate system: (x, alpha) in case of 'y' orientation
531
  // and (alpha,y) in 'x' one rather than (x, y).  There is
532
  // no obvious, direct relationship between the order of universes in the
533
  // input and the order that they will be stored in the skewed array so
534
  // the following code walks a set of index values across the skewed array
535
  // in a manner that matches the input order.  Note that i_x = 0, i_a = 0
536
  // or i_a = 0, i_y = 0 corresponds to the center of the hexagonal lattice.
537
  universes_.resize((2 * n_rings_ - 1) * (2 * n_rings_ - 1) * n_axial_, C_NONE);
85✔
538
  if (orientation_ == Orientation::y) {
85✔
539
    fill_lattice_y(univ_words);
68✔
540
  } else {
541
    fill_lattice_x(univ_words);
17✔
542
  }
543
}
85✔
544

545
//==============================================================================
546

547
void HexLattice::fill_lattice_x(const vector<std::string>& univ_words)
17✔
548
{
549
  int input_index = 0;
17✔
550
  for (int m = 0; m < n_axial_; m++) {
51✔
551
    // Initialize lattice indecies.
552
    int i_a = -(n_rings_ - 1);
34✔
553
    int i_y = n_rings_ - 1;
34✔
554

555
    // Map upper region of hexagonal lattice which is found in the
556
    // first n_rings-1 rows of the input.
557
    for (int k = 0; k < n_rings_ - 1; k++) {
374✔
558

559
      // Iterate over the input columns.
560
      for (int j = 0; j < k + n_rings_; j++) {
5,610✔
561
        int indx = (2 * n_rings_ - 1) * (2 * n_rings_ - 1) * m +
5,270✔
562
                   (2 * n_rings_ - 1) * (i_y + n_rings_ - 1) +
5,270✔
563
                   (i_a + n_rings_ - 1);
5,270✔
564
        universes_[indx] = std::stoi(univ_words[input_index]);
5,270✔
565
        input_index++;
5,270✔
566
        // Move to the next right neighbour cell
567
        i_a += 1;
5,270✔
568
      }
569

570
      // Return the lattice index to the start of the current row.
571
      i_a = -(n_rings_ - 1);
340✔
572
      i_y -= 1;
340✔
573
    }
574

575
    // Map the lower region from the centerline of cart to down side
576
    for (int k = 0; k < n_rings_; k++) {
408✔
577
      // Walk the index to the lower-right neighbor of the last row start.
578
      i_a = -(n_rings_ - 1) + k;
374✔
579

580
      // Iterate over the input columns.
581
      for (int j = 0; j < 2 * n_rings_ - k - 1; j++) {
6,358✔
582
        int indx = (2 * n_rings_ - 1) * (2 * n_rings_ - 1) * m +
5,984✔
583
                   (2 * n_rings_ - 1) * (i_y + n_rings_ - 1) +
5,984✔
584
                   (i_a + n_rings_ - 1);
5,984✔
585
        universes_[indx] = std::stoi(univ_words[input_index]);
5,984✔
586
        input_index++;
5,984✔
587
        // Move to the next right neighbour cell
588
        i_a += 1;
5,984✔
589
      }
590

591
      // Return lattice index to start of current row.
592
      i_y -= 1;
374✔
593
    }
594
  }
595
}
17✔
596

597
//==============================================================================
598

599
void HexLattice::fill_lattice_y(const vector<std::string>& univ_words)
68✔
600
{
601
  int input_index = 0;
68✔
602
  for (int m = 0; m < n_axial_; m++) {
170✔
603
    // Initialize lattice indecies.
604
    int i_x = 1;
102✔
605
    int i_a = n_rings_ - 1;
102✔
606

607
    // Map upper triangular region of hexagonal lattice which is found in the
608
    // first n_rings-1 rows of the input.
609
    for (int k = 0; k < n_rings_ - 1; k++) {
680✔
610
      // Walk the index to lower-left neighbor of last row start.
611
      i_x -= 1;
578✔
612

613
      // Iterate over the input columns.
614
      for (int j = 0; j < k + 1; j++) {
3,468✔
615
        int indx = (2 * n_rings_ - 1) * (2 * n_rings_ - 1) * m +
2,890✔
616
                   (2 * n_rings_ - 1) * (i_a + n_rings_ - 1) +
2,890✔
617
                   (i_x + n_rings_ - 1);
2,890✔
618
        universes_[indx] = std::stoi(univ_words[input_index]);
2,890✔
619
        input_index++;
2,890✔
620
        // Walk the index to the right neighbor (which is not adjacent).
621
        i_x += 2;
2,890✔
622
        i_a -= 1;
2,890✔
623
      }
624

625
      // Return the lattice index to the start of the current row.
626
      i_x -= 2 * (k + 1);
578✔
627
      i_a += (k + 1);
578✔
628
    }
629

630
    // Map the middle square region of the hexagonal lattice which is found in
631
    // the next 2*n_rings-1 rows of the input.
632
    for (int k = 0; k < 2 * n_rings_ - 1; k++) {
1,360✔
633
      if ((k % 2) == 0) {
1,258✔
634
        // Walk the index to the lower-left neighbor of the last row start.
635
        i_x -= 1;
680✔
636
      } else {
637
        // Walk the index to the lower-right neighbor of the last row start.
638
        i_x += 1;
578✔
639
        i_a -= 1;
578✔
640
      }
641

642
      // Iterate over the input columns.
643
      for (int j = 0; j < n_rings_ - (k % 2); j++) {
12,920✔
644
        int indx = (2 * n_rings_ - 1) * (2 * n_rings_ - 1) * m +
11,662✔
645
                   (2 * n_rings_ - 1) * (i_a + n_rings_ - 1) +
11,662✔
646
                   (i_x + n_rings_ - 1);
11,662✔
647
        universes_[indx] = std::stoi(univ_words[input_index]);
11,662✔
648
        input_index++;
11,662✔
649
        // Walk the index to the right neighbor (which is not adjacent).
650
        i_x += 2;
11,662✔
651
        i_a -= 1;
11,662✔
652
      }
653

654
      // Return the lattice index to the start of the current row.
655
      i_x -= 2 * (n_rings_ - (k % 2));
1,258✔
656
      i_a += n_rings_ - (k % 2);
1,258✔
657
    }
658

659
    // Map the lower triangular region of the hexagonal lattice.
660
    for (int k = 0; k < n_rings_ - 1; k++) {
680✔
661
      // Walk the index to the lower-right neighbor of the last row start.
662
      i_x += 1;
578✔
663
      i_a -= 1;
578✔
664

665
      // Iterate over the input columns.
666
      for (int j = 0; j < n_rings_ - k - 1; j++) {
3,468✔
667
        int indx = (2 * n_rings_ - 1) * (2 * n_rings_ - 1) * m +
2,890✔
668
                   (2 * n_rings_ - 1) * (i_a + n_rings_ - 1) +
2,890✔
669
                   (i_x + n_rings_ - 1);
2,890✔
670
        universes_[indx] = std::stoi(univ_words[input_index]);
2,890✔
671
        input_index++;
2,890✔
672
        // Walk the index to the right neighbor (which is not adjacent).
673
        i_x += 2;
2,890✔
674
        i_a -= 1;
2,890✔
675
      }
676

677
      // Return lattice index to start of current row.
678
      i_x -= 2 * (n_rings_ - k - 1);
578✔
679
      i_a += n_rings_ - k - 1;
578✔
680
    }
681
  }
682
}
68✔
683

684
//==============================================================================
685

686
const int32_t& HexLattice::operator[](const array<int, 3>& i_xyz)
31,730,616✔
687
{
688
  return universes_[get_flat_index(i_xyz)];
31,730,616✔
689
}
690

691
//==============================================================================
692

693
// The HexLattice iterators need their own versions b/c the universes array is
694
// "square", meaning that it is allocated with entries that are intentionally
695
// left empty. As such, the iterator indices need to skip the empty entries to
696
// get cell instances and geometry paths correct. See the image in the Theory
697
// and Methodology section on "Hexagonal Lattice Indexing" for a visual of where
698
// the empty positions are.
699
LatticeIter HexLattice::begin()
2,104✔
700
{
701
  return LatticeIter(*this, n_rings_ - 1);
2,104✔
702
}
703

704
ReverseLatticeIter HexLattice::rbegin()
84✔
705
{
706
  return ReverseLatticeIter(*this, universes_.size() - n_rings_);
84✔
707
}
708

709
int32_t& HexLattice::back()
×
710
{
711
  return universes_[universes_.size() - n_rings_];
×
712
}
713

714
LatticeIter HexLattice::end()
1,131,350✔
715
{
716
  return LatticeIter(*this, universes_.size() - n_rings_ + 1);
1,131,350✔
717
}
718

719
ReverseLatticeIter HexLattice::rend()
336✔
720
{
721
  return ReverseLatticeIter(*this, n_rings_ - 2);
336✔
722
}
723

724
//==============================================================================
725

726
bool HexLattice::are_valid_indices(const array<int, 3>& i_xyz) const
122,978,558✔
727
{
728
  // Check if (x, alpha, z) indices are valid, accounting for number of rings
729
  return ((i_xyz[0] >= 0) && (i_xyz[1] >= 0) && (i_xyz[2] >= 0) &&
245,366,524✔
730
          (i_xyz[0] < 2 * n_rings_ - 1) && (i_xyz[1] < 2 * n_rings_ - 1) &&
119,387,162✔
731
          (i_xyz[0] + i_xyz[1] > n_rings_ - 2) &&
118,538,270✔
732
          (i_xyz[0] + i_xyz[1] < 3 * n_rings_ - 2) && (i_xyz[2] < n_axial_));
245,366,524✔
733
}
734

735
//==============================================================================
736

737
std::pair<double, array<int, 3>> HexLattice::distance(
92,724,012✔
738
  Position r, Direction u, const array<int, 3>& i_xyz) const
739
{
740
  // Short description of the direction vectors used here.  The beta, gamma, and
741
  // delta vectors point towards the flat sides of each hexagonal tile.
742
  // Y - orientation:
743
  //   basis0 = (1, 0)
744
  //   basis1 = (-1/sqrt(3), 1)   = +120 degrees from basis0
745
  //   beta   = (sqrt(3)/2, 1/2)  = +30 degrees from basis0
746
  //   gamma  = (sqrt(3)/2, -1/2) = -60 degrees from beta
747
  //   delta  = (0, 1)            = +60 degrees from beta
748
  // X - orientation:
749
  //   basis0 = (1/sqrt(3), -1)
750
  //   basis1 = (0, 1)            = +120 degrees from basis0
751
  //   beta   = (1, 0)            = +30 degrees from basis0
752
  //   gamma  = (1/2, -sqrt(3)/2) = -60 degrees from beta
753
  //   delta  = (1/2, sqrt(3)/2)  = +60 degrees from beta
754
  // The z-axis is considered separately.
755
  double beta_dir;
756
  double gamma_dir;
757
  double delta_dir;
758
  if (orientation_ == Orientation::y) {
92,724,012✔
759
    beta_dir = u.x * std::sqrt(3.0) / 2.0 + u.y / 2.0;
76,939,344✔
760
    gamma_dir = u.x * std::sqrt(3.0) / 2.0 - u.y / 2.0;
76,939,344✔
761
    delta_dir = u.y;
76,939,344✔
762
  } else {
763
    beta_dir = u.x;
15,784,668✔
764
    gamma_dir = u.x / 2.0 - u.y * std::sqrt(3.0) / 2.0;
15,784,668✔
765
    delta_dir = u.x / 2.0 + u.y * std::sqrt(3.0) / 2.0;
15,784,668✔
766
  }
767

768
  // Note that hexagonal lattice distance calculations are performed
769
  // using the particle's coordinates relative to the neighbor lattice
770
  // cells, not relative to the particle's current cell.  This is done
771
  // because there is significant disagreement between neighboring cells
772
  // on where the lattice boundary is due to finite precision issues.
773

774
  // beta direction
775
  double d {INFTY};
92,724,012✔
776
  array<int, 3> lattice_trans;
777
  double edge = -copysign(0.5 * pitch_[0], beta_dir); // Oncoming edge
92,724,012✔
778
  Position r_t;
92,724,012✔
779
  if (beta_dir > 0) {
92,724,012✔
780
    const array<int, 3> i_xyz_t {i_xyz[0] + 1, i_xyz[1], i_xyz[2]};
46,399,380✔
781
    r_t = get_local_position(r, i_xyz_t);
46,399,380✔
782
  } else {
783
    const array<int, 3> i_xyz_t {i_xyz[0] - 1, i_xyz[1], i_xyz[2]};
46,324,632✔
784
    r_t = get_local_position(r, i_xyz_t);
46,324,632✔
785
  }
786
  double beta;
787
  if (orientation_ == Orientation::y) {
92,724,012✔
788
    beta = r_t.x * std::sqrt(3.0) / 2.0 + r_t.y / 2.0;
76,939,344✔
789
  } else {
790
    beta = r_t.x;
15,784,668✔
791
  }
792
  if ((std::abs(beta - edge) > FP_PRECISION) && beta_dir != 0) {
92,724,012✔
793
    d = (edge - beta) / beta_dir;
92,724,012✔
794
    if (beta_dir > 0) {
92,724,012✔
795
      lattice_trans = {1, 0, 0};
46,399,380✔
796
    } else {
797
      lattice_trans = {-1, 0, 0};
46,324,632✔
798
    }
799
  }
800

801
  // gamma direction
802
  edge = -copysign(0.5 * pitch_[0], gamma_dir);
92,724,012✔
803
  if (gamma_dir > 0) {
92,724,012✔
804
    const array<int, 3> i_xyz_t {i_xyz[0] + 1, i_xyz[1] - 1, i_xyz[2]};
46,378,236✔
805
    r_t = get_local_position(r, i_xyz_t);
46,378,236✔
806
  } else {
807
    const array<int, 3> i_xyz_t {i_xyz[0] - 1, i_xyz[1] + 1, i_xyz[2]};
46,345,776✔
808
    r_t = get_local_position(r, i_xyz_t);
46,345,776✔
809
  }
810
  double gamma;
811
  if (orientation_ == Orientation::y) {
92,724,012✔
812
    gamma = r_t.x * std::sqrt(3.0) / 2.0 - r_t.y / 2.0;
76,939,344✔
813
  } else {
814
    gamma = r_t.x / 2.0 - r_t.y * std::sqrt(3.0) / 2.0;
15,784,668✔
815
  }
816
  if ((std::abs(gamma - edge) > FP_PRECISION) && gamma_dir != 0) {
92,724,012✔
817
    double this_d = (edge - gamma) / gamma_dir;
92,724,012✔
818
    if (this_d < d) {
92,724,012✔
819
      if (gamma_dir > 0) {
46,392,300✔
820
        lattice_trans = {1, -1, 0};
23,223,108✔
821
      } else {
822
        lattice_trans = {-1, 1, 0};
23,169,192✔
823
      }
824
      d = this_d;
46,392,300✔
825
    }
826
  }
827

828
  // delta direction
829
  edge = -copysign(0.5 * pitch_[0], delta_dir);
92,724,012✔
830
  if (delta_dir > 0) {
92,724,012✔
831
    const array<int, 3> i_xyz_t {i_xyz[0], i_xyz[1] + 1, i_xyz[2]};
46,341,168✔
832
    r_t = get_local_position(r, i_xyz_t);
46,341,168✔
833
  } else {
834
    const array<int, 3> i_xyz_t {i_xyz[0], i_xyz[1] - 1, i_xyz[2]};
46,382,844✔
835
    r_t = get_local_position(r, i_xyz_t);
46,382,844✔
836
  }
837
  double delta;
838
  if (orientation_ == Orientation::y) {
92,724,012✔
839
    delta = r_t.y;
76,939,344✔
840
  } else {
841
    delta = r_t.x / 2.0 + r_t.y * std::sqrt(3.0) / 2.0;
15,784,668✔
842
  }
843
  if ((std::abs(delta - edge) > FP_PRECISION) && delta_dir != 0) {
92,724,012✔
844
    double this_d = (edge - delta) / delta_dir;
92,724,012✔
845
    if (this_d < d) {
92,724,012✔
846
      if (delta_dir > 0) {
30,950,700✔
847
        lattice_trans = {0, 1, 0};
15,467,544✔
848
      } else {
849
        lattice_trans = {0, -1, 0};
15,483,156✔
850
      }
851
      d = this_d;
30,950,700✔
852
    }
853
  }
854

855
  // Top and bottom sides
856
  if (is_3d_) {
92,724,012✔
857
    double z = r.z;
21,905,112✔
858
    double z0 {copysign(0.5 * pitch_[1], u.z)};
21,905,112✔
859
    if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) {
21,905,112✔
860
      double this_d = (z0 - z) / u.z;
21,905,112✔
861
      if (this_d < d) {
21,905,112✔
862
        d = this_d;
3,370,404✔
863
        if (u.z > 0) {
3,370,404✔
864
          lattice_trans = {0, 0, 1};
1,684,116✔
865
        } else {
866
          lattice_trans = {0, 0, -1};
1,686,288✔
867
        }
868
        d = this_d;
3,370,404✔
869
      }
870
    }
871
  }
872

873
  return {d, lattice_trans};
185,448,024✔
874
}
875

876
//==============================================================================
877

878
void HexLattice::get_indices(
12,891,972✔
879
  Position r, Direction u, array<int, 3>& result) const
880
{
881
  // Offset the xyz by the lattice center.
882
  Position r_o {r.x - center_.x, r.y - center_.y, r.z};
12,891,972✔
883
  if (is_3d_) {
12,891,972✔
884
    r_o.z -= center_.z;
2,722,296✔
885
  }
886

887
  // Index the z direction, accounting for coincidence
888
  result[2] = 0;
12,891,972✔
889
  if (is_3d_) {
12,891,972✔
890
    double iz_ {r_o.z / pitch_[1] + 0.5 * n_axial_};
2,722,296✔
891
    long iz_close {std::lround(iz_)};
2,722,296✔
892
    if (coincident(iz_, iz_close)) {
2,722,296✔
893
      result[2] = (u.z > 0) ? iz_close : iz_close - 1;
844,392✔
894
    } else {
895
      result[2] = std::floor(iz_);
1,877,904✔
896
    }
897
  }
898

899
  if (orientation_ == Orientation::y) {
12,891,972✔
900
    // Convert coordinates into skewed bases.  The (x, alpha) basis is used to
901
    // find the index of the global coordinates to within 4 cells.
902
    double alpha = r_o.y - r_o.x / std::sqrt(3.0);
12,001,536✔
903
    result[0] = std::floor(r_o.x / (0.5 * std::sqrt(3.0) * pitch_[0]));
12,001,536✔
904
    result[1] = std::floor(alpha / pitch_[0]);
12,001,536✔
905
  } else {
906
    // Convert coordinates into skewed bases.  The (alpha, y) basis is used to
907
    // find the index of the global coordinates to within 4 cells.
908
    double alpha = r_o.y - r_o.x * std::sqrt(3.0);
890,436✔
909
    result[0] = std::floor(-alpha / (std::sqrt(3.0) * pitch_[0]));
890,436✔
910
    result[1] = std::floor(r_o.y / (0.5 * std::sqrt(3.0) * pitch_[0]));
890,436✔
911
  }
912

913
  // Add offset to indices (the center cell is (i1, i2) = (0, 0) but
914
  // the array is offset so that the indices never go below 0).
915
  result[0] += n_rings_ - 1;
12,891,972✔
916
  result[1] += n_rings_ - 1;
12,891,972✔
917

918
  // Calculate the (squared) distance between the particle and the centers of
919
  // the four possible cells.  Regular hexagonal tiles form a Voronoi
920
  // tessellation so the xyz should be in the hexagonal cell that it is closest
921
  // to the center of.  This method is used over a method that uses the
922
  // remainders of the floor divisions above because it provides better finite
923
  // precision performance.  Squared distances are used because they are more
924
  // computationally efficient than normal distances.
925

926
  // COINCIDENCE CHECK
927
  // if a distance to center, d, is within the coincidence tolerance of the
928
  // current minimum distance, d_min, the particle is on an edge or vertex.
929
  // In this case, the dot product of the position vector and direction vector
930
  // for the current indices, dp, and the dot product for the currently selected
931
  // indices, dp_min, are compared. The cell which the particle is moving into
932
  // is kept (i.e. the cell with the lowest dot product as the vectors will be
933
  // completely opposed if the particle is moving directly toward the center of
934
  // the cell).
935
  int i1_chg;
936
  int i2_chg;
937
  double d_min {INFTY};
12,891,972✔
938
  double dp_min {INFTY};
12,891,972✔
939
  for (int i = 0; i < 2; i++) {
38,675,916✔
940
    for (int j = 0; j < 2; j++) {
77,351,832✔
941
      // get local coordinates
942
      const array<int, 3> i_xyz {result[0] + j, result[1] + i, 0};
51,567,888✔
943
      Position r_t = get_local_position(r, i_xyz);
51,567,888✔
944
      // calculate distance
945
      double d = r_t.x * r_t.x + r_t.y * r_t.y;
51,567,888✔
946
      // check for coincidence. Because the numerical error incurred
947
      // in hex geometry is higher than other geometries, the relative
948
      // coincidence is checked here so that coincidence is successfully
949
      // detected on large hex lattice with particles far from the origin
950
      // which have rounding errors larger than the FP_COINCIDENT thresdhold.
951
      bool on_boundary = coincident(1.0, d_min / d);
51,567,888✔
952
      if (d < d_min || on_boundary) {
51,567,888✔
953
        // normalize r_t and find dot product
954
        r_t /= std::sqrt(d);
32,321,076✔
955
        double dp = u.x * r_t.x + u.y * r_t.y;
32,321,076✔
956
        // do not update values if particle is on a
957
        // boundary and not moving into this cell
958
        if (on_boundary && dp > dp_min)
32,321,076✔
959
          continue;
2,249,100✔
960
        // update values
961
        d_min = d;
30,071,976✔
962
        i1_chg = j;
30,071,976✔
963
        i2_chg = i;
30,071,976✔
964
        dp_min = dp;
30,071,976✔
965
      }
966
    }
967
  }
968

969
  // update outgoing indices
970
  result[0] += i1_chg;
12,891,972✔
971
  result[1] += i2_chg;
12,891,972✔
972
}
12,891,972✔
973

974
int HexLattice::get_flat_index(const array<int, 3>& i_xyz) const
31,730,616✔
975
{
976
  return (2 * n_rings_ - 1) * (2 * n_rings_ - 1) * i_xyz[2] +
31,730,616✔
977
         (2 * n_rings_ - 1) * i_xyz[1] + i_xyz[0];
31,730,616✔
978
}
979

980
//==============================================================================
981

982
Position HexLattice::get_local_position(
366,247,788✔
983
  Position r, const array<int, 3>& i_xyz) const
984
{
985
  if (orientation_ == Orientation::y) {
366,247,788✔
986
    // x_l = x_g - (center + pitch_x*cos(30)*index_x)
987
    r.x -=
310,688,892✔
988
      center_.x + std::sqrt(3.0) / 2.0 * (i_xyz[0] - n_rings_ + 1) * pitch_[0];
310,688,892✔
989
    // y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y)
990
    r.y -= (center_.y + (i_xyz[1] - n_rings_ + 1) * pitch_[0] +
310,688,892✔
991
            (i_xyz[0] - n_rings_ + 1) * pitch_[0] / 2.0);
310,688,892✔
992
  } else {
993
    // x_l = x_g - (center + pitch_x*index_a + pitch_y*sin(30)*index_y)
994
    r.x -= (center_.x + (i_xyz[0] - n_rings_ + 1) * pitch_[0] +
55,558,896✔
995
            (i_xyz[1] - n_rings_ + 1) * pitch_[0] / 2.0);
55,558,896✔
996
    // y_l = y_g - (center + pitch_y*cos(30)*index_y)
997
    r.y -=
55,558,896✔
998
      center_.y + std::sqrt(3.0) / 2.0 * (i_xyz[1] - n_rings_ + 1) * pitch_[0];
55,558,896✔
999
  }
1000

1001
  if (is_3d_) {
366,247,788✔
1002
    r.z -= center_.z - (0.5 * n_axial_ - i_xyz[2] - 0.5) * pitch_[1];
85,356,756✔
1003
  }
1004

1005
  return r;
366,247,788✔
1006
}
1007

1008
//==============================================================================
1009

1010
bool HexLattice::is_valid_index(int indx) const
639,146✔
1011
{
1012
  int nx {2 * n_rings_ - 1};
639,146✔
1013
  int ny {2 * n_rings_ - 1};
639,146✔
1014
  int iz = indx / (nx * ny);
639,146✔
1015
  int iy = (indx - nx * ny * iz) / nx;
639,146✔
1016
  int ix = indx - nx * ny * iz - nx * iy;
639,146✔
1017
  array<int, 3> i_xyz {ix, iy, iz};
639,146✔
1018
  return are_valid_indices(i_xyz);
1,278,292✔
1019
}
1020

1021
//==============================================================================
1022

1023
int32_t& HexLattice::offset(int map, const array<int, 3>& i_xyz)
83,180,976✔
1024
{
1025
  int nx {2 * n_rings_ - 1};
83,180,976✔
1026
  int ny {2 * n_rings_ - 1};
83,180,976✔
1027
  int nz {n_axial_};
83,180,976✔
1028
  return offsets_[nx * ny * nz * map + nx * ny * i_xyz[2] + nx * i_xyz[1] +
83,180,976✔
1029
                  i_xyz[0]];
166,361,952✔
1030
}
1031

1032
int32_t HexLattice::offset(int map, int indx) const
×
1033
{
1034
  int nx {2 * n_rings_ - 1};
×
1035
  int ny {2 * n_rings_ - 1};
×
1036
  int nz {n_axial_};
×
1037
  return offsets_[nx * ny * nz * map + indx];
×
1038
}
1039

1040
//==============================================================================
1041

1042
std::string HexLattice::index_to_string(int indx) const
84✔
1043
{
1044
  int nx {2 * n_rings_ - 1};
84✔
1045
  int ny {2 * n_rings_ - 1};
84✔
1046
  int iz {indx / (nx * ny)};
84✔
1047
  int iy {(indx - nx * ny * iz) / nx};
84✔
1048
  int ix {indx - nx * ny * iz - nx * iy};
84✔
1049
  std::string out {std::to_string(ix - n_rings_ + 1)};
84✔
1050
  out += ',';
84✔
1051
  out += std::to_string(iy - n_rings_ + 1);
84✔
1052
  if (is_3d_) {
84✔
1053
    out += ',';
×
1054
    out += std::to_string(iz);
×
1055
  }
1056
  return out;
84✔
1057
}
×
1058

1059
//==============================================================================
1060

1061
void HexLattice::to_hdf5_inner(hid_t lat_group) const
48✔
1062
{
1063
  // Write basic lattice information.
1064
  write_string(lat_group, "type", "hexagonal", false);
48✔
1065
  write_dataset(lat_group, "n_rings", n_rings_);
48✔
1066
  write_dataset(lat_group, "n_axial", n_axial_);
48✔
1067
  if (orientation_ == Orientation::y) {
48✔
1068
    write_string(lat_group, "orientation", "y", false);
36✔
1069
  } else {
1070
    write_string(lat_group, "orientation", "x", false);
12✔
1071
  }
1072
  if (is_3d_) {
48✔
1073
    write_dataset(lat_group, "pitch", pitch_);
24✔
1074
    write_dataset(lat_group, "center", center_);
24✔
1075
  } else {
1076
    array<double, 1> pitch_short {{pitch_[0]}};
24✔
1077
    write_dataset(lat_group, "pitch", pitch_short);
24✔
1078
    array<double, 2> center_short {{center_[0], center_[1]}};
24✔
1079
    write_dataset(lat_group, "center", center_short);
24✔
1080
  }
1081

1082
  // Write the universe ids.
1083
  hsize_t nx {static_cast<hsize_t>(2 * n_rings_ - 1)};
48✔
1084
  hsize_t ny {static_cast<hsize_t>(2 * n_rings_ - 1)};
48✔
1085
  hsize_t nz {static_cast<hsize_t>(n_axial_)};
48✔
1086
  vector<int> out(nx * ny * nz);
48✔
1087

1088
  for (int m = 0; m < nz; m++) {
132✔
1089
    for (int k = 0; k < ny; k++) {
1,440✔
1090
      for (int j = 0; j < nx; j++) {
28,224✔
1091
        int indx = nx * ny * m + nx * k + j;
26,868✔
1092
        if (j + k < n_rings_ - 1) {
26,868✔
1093
          // This array position is never used; put a -1 to indicate this.
1094
          out[indx] = -1;
3,348✔
1095
        } else if (j + k > 3 * n_rings_ - 3) {
23,520✔
1096
          // This array position is never used; put a -1 to indicate this.
1097
          out[indx] = -1;
3,348✔
1098
        } else {
1099
          out[indx] = model::universes[universes_[indx]]->id_;
20,172✔
1100
        }
1101
      }
1102
    }
1103
  }
1104

1105
  hsize_t dims[3] {nz, ny, nx};
48✔
1106
  write_int(lat_group, 3, dims, "universes", out.data(), false);
48✔
1107
}
48✔
1108

1109
//==============================================================================
1110
// Non-method functions
1111
//==============================================================================
1112

1113
void read_lattices(pugi::xml_node node)
5,480✔
1114
{
1115
  for (pugi::xml_node lat_node : node.children("lattice")) {
6,844✔
1116
    model::lattices.push_back(make_unique<RectLattice>(lat_node));
1,364✔
1117
  }
1118
  for (pugi::xml_node lat_node : node.children("hex_lattice")) {
5,565✔
1119
    model::lattices.push_back(make_unique<HexLattice>(lat_node));
85✔
1120
  }
1121

1122
  // Fill the lattice map.
1123
  for (int i_lat = 0; i_lat < model::lattices.size(); i_lat++) {
6,929✔
1124
    int id = model::lattices[i_lat]->id_;
1,449✔
1125
    auto in_map = model::lattice_map.find(id);
1,449✔
1126
    if (in_map == model::lattice_map.end()) {
1,449✔
1127
      model::lattice_map[id] = i_lat;
1,449✔
1128
    } else {
1129
      fatal_error(
×
1130
        fmt::format("Two or more lattices use the same unique ID: {}", id));
×
1131
    }
1132
  }
1133
}
5,480✔
1134

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

© 2025 Coveralls, Inc