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

openmc-dev / openmc / 13344433530

15 Feb 2025 11:03AM UTC coverage: 84.938% (-0.03%) from 84.969%
13344433530

Pull #3305

github

web-flow
Merge 49da803a6 into 11587786e
Pull Request #3305: New Feature Development: Introduction of the CLSCell Class( #3286 )

4 of 26 new or added lines in 1 file covered. (15.38%)

218 existing lines in 6 files now uncovered.

50245 of 59155 relevant lines covered (84.94%)

35373994.5 hits per line

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

91.41
/src/geometry_aux.cpp
1
#include "openmc/geometry_aux.h"
2

3
#include <algorithm> // for std::max
4
#include <sstream>
5
#include <unordered_set>
6

7
#include <fmt/core.h>
8
#include <pugixml.hpp>
9

10
#include "openmc/cell.h"
11
#include "openmc/constants.h"
12
#include "openmc/container_util.h"
13
#include "openmc/dagmc.h"
14
#include "openmc/error.h"
15
#include "openmc/file_utils.h"
16
#include "openmc/geometry.h"
17
#include "openmc/lattice.h"
18
#include "openmc/material.h"
19
#include "openmc/settings.h"
20
#include "openmc/surface.h"
21
#include "openmc/tallies/filter.h"
22
#include "openmc/tallies/filter_cell_instance.h"
23
#include "openmc/tallies/filter_distribcell.h"
24

25
namespace openmc {
26

27
namespace model {
28
std::unordered_map<int32_t, std::unordered_map<int32_t, int32_t>>
29
  universe_cell_counts;
30
std::unordered_map<int32_t, int32_t> universe_level_counts;
31
} // namespace model
32

33
// adds the cell counts of universe b to universe a
34
void update_universe_cell_count(int32_t a, int32_t b)
730,784✔
35
{
36
  auto& universe_a_counts = model::universe_cell_counts[a];
730,784✔
37
  const auto& universe_b_counts = model::universe_cell_counts[b];
730,784✔
38
  for (const auto& it : universe_b_counts) {
2,224,907✔
39
    universe_a_counts[it.first] += it.second;
1,494,123✔
40
  }
41
}
730,784✔
42

43
void read_geometry_xml()
1,537✔
44
{
45
  // Display output message
46
  write_message("Reading geometry XML file...", 5);
1,537✔
47

48
  // Check if geometry.xml exists
49
  std::string filename = settings::path_input + "geometry.xml";
1,537✔
50
  if (!file_exists(filename)) {
1,537✔
UNCOV
51
    fatal_error("Geometry XML file '" + filename + "' does not exist!");
×
52
  }
53

54
  // Parse settings.xml file
55
  pugi::xml_document doc;
1,537✔
56
  auto result = doc.load_file(filename.c_str());
1,537✔
57
  if (!result) {
1,537✔
UNCOV
58
    fatal_error("Error processing geometry.xml file.");
×
59
  }
60

61
  // Get root element
62
  pugi::xml_node root = doc.document_element();
1,537✔
63

64
  read_geometry_xml(root);
1,537✔
65
}
1,537✔
66

67
void read_geometry_xml(pugi::xml_node root)
6,869✔
68
{
69
  // Read surfaces, cells, lattice
70
  read_surfaces(root);
6,869✔
71
  read_cells(root);
6,869✔
72
  read_lattices(root);
6,867✔
73

74
  // Check to make sure a boundary condition was applied to at least one
75
  // surface
76
  bool boundary_exists = false;
6,867✔
77
  for (const auto& surf : model::surfaces) {
17,171✔
78
    if (surf->bc_) {
17,135✔
79
      boundary_exists = true;
6,831✔
80
      break;
6,831✔
81
    }
82
  }
83

84
  if (settings::run_mode != RunMode::PLOTTING &&
6,867✔
85
      settings::run_mode != RunMode::VOLUME && !boundary_exists) {
6,581✔
UNCOV
86
    fatal_error("No boundary conditions were applied to any surfaces!");
×
87
  }
88

89
  // Allocate universes, universe cell arrays, and assign base universe
90
  model::root_universe = find_root_universe();
6,867✔
91

92
  // if the root universe is DAGMC geometry, make sure the model is well-formed
93
  check_dagmc_root_univ();
6,867✔
94
}
6,867✔
95

96
//==============================================================================
97

98
void adjust_indices()
6,869✔
99
{
100
  // Adjust material/fill idices.
101
  for (auto& c : model::cells) {
38,405✔
102
    if (c->fill_ != C_NONE) {
31,536✔
103
      int32_t id = c->fill_;
6,552✔
104
      auto search_univ = model::universe_map.find(id);
6,552✔
105
      auto search_lat = model::lattice_map.find(id);
6,552✔
106
      if (search_univ != model::universe_map.end()) {
6,552✔
107
        c->type_ = Fill::UNIVERSE;
4,939✔
108
        c->fill_ = search_univ->second;
4,939✔
109
      } else if (search_lat != model::lattice_map.end()) {
1,613✔
110
        c->type_ = Fill::LATTICE;
1,613✔
111
        c->fill_ = search_lat->second;
1,613✔
112
      } else {
UNCOV
113
        fatal_error(fmt::format("Specified fill {} on cell {} is neither a "
×
114
                                "universe nor a lattice.",
115
          id, c->id_));
×
116
      }
117
    } else {
118
      c->type_ = Fill::MATERIAL;
24,984✔
119
      for (auto& mat_id : c->material_) {
52,332✔
120
        if (mat_id != MATERIAL_VOID) {
27,348✔
121
          auto search = model::material_map.find(mat_id);
19,099✔
122
          if (search == model::material_map.end()) {
19,099✔
UNCOV
123
            fatal_error(
×
UNCOV
124
              fmt::format("Could not find material {} specified on cell {}",
×
UNCOV
125
                mat_id, c->id_));
×
126
          }
127
          // Change from ID to index
128
          mat_id = search->second;
19,099✔
129
        }
130
      }
131
    }
132
  }
133

134
  // Change cell.universe values from IDs to indices.
135
  for (auto& c : model::cells) {
38,405✔
136
    auto search = model::universe_map.find(c->universe_);
31,536✔
137
    if (search != model::universe_map.end()) {
31,536✔
138
      c->universe_ = search->second;
31,536✔
139
    } else {
UNCOV
140
      fatal_error(fmt::format("Could not find universe {} specified on cell {}",
×
UNCOV
141
        c->universe_, c->id_));
×
142
    }
143
  }
144

145
  // Change all lattice universe values from IDs to indices.
146
  for (auto& l : model::lattices) {
8,446✔
147
    l->adjust_indices();
1,577✔
148
  }
149
}
6,869✔
150

151
//==============================================================================
152
//! Partition some universes with many z-planes for faster find_cell searches.
153

154
void partition_universes()
6,869✔
155
{
156
  // Iterate over universes with more than 10 cells.  (Fewer than 10 is likely
157
  // not worth partitioning.)
158
  for (const auto& univ : model::universes) {
24,700✔
159
    if (univ->cells_.size() > 10) {
17,831✔
160
      // Collect the set of surfaces in this universe.
161
      std::unordered_set<int32_t> surf_inds;
206✔
162
      for (auto i_cell : univ->cells_) {
3,407✔
163
        for (auto token : model::cells[i_cell]->surfaces()) {
12,158✔
164
          surf_inds.insert(std::abs(token) - 1);
8,957✔
165
        }
3,201✔
166
      }
167

168
      // Partition the universe if there are more than 5 z-planes.  (Fewer than
169
      // 5 is likely not worth it.)
170
      int n_zplanes = 0;
206✔
171
      for (auto i_surf : surf_inds) {
2,602✔
172
        if (dynamic_cast<const SurfaceZPlane*>(model::surfaces[i_surf].get())) {
2,498✔
173
          ++n_zplanes;
680✔
174
          if (n_zplanes > 5) {
680✔
175
            univ->partitioner_ = make_unique<UniversePartitioner>(*univ);
102✔
176
            break;
102✔
177
          }
178
        }
179
      }
180
    }
206✔
181
  }
182
}
6,869✔
183

184
//==============================================================================
185

186
void assign_temperatures()
6,869✔
187
{
188
  for (auto& c : model::cells) {
38,405✔
189
    // Ignore non-material cells and cells with defined temperature.
190
    if (c->material_.size() == 0)
31,536✔
191
      continue;
6,552✔
192
    if (c->sqrtkT_.size() > 0)
24,984✔
193
      continue;
448✔
194

195
    c->sqrtkT_.reserve(c->material_.size());
24,536✔
196
    for (auto i_mat : c->material_) {
51,427✔
197
      if (i_mat == MATERIAL_VOID) {
26,891✔
198
        // Set void region to 0K.
199
        c->sqrtkT_.push_back(0);
8,249✔
200
      } else {
201
        const auto& mat {model::materials[i_mat]};
18,642✔
202
        c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * mat->temperature()));
18,642✔
203
      }
204
    }
205
  }
206
}
6,869✔
207

208
//==============================================================================
209

210
void get_temperatures(
6,583✔
211
  vector<vector<double>>& nuc_temps, vector<vector<double>>& thermal_temps)
212
{
213
  for (const auto& cell : model::cells) {
37,537✔
214
    // Skip non-material cells.
215
    if (cell->fill_ != C_NONE)
30,954✔
216
      continue;
6,540✔
217

218
    for (int j = 0; j < cell->material_.size(); ++j) {
51,192✔
219
      // Skip void materials
220
      int i_material = cell->material_[j];
26,778✔
221
      if (i_material == MATERIAL_VOID)
26,778✔
222
        continue;
8,153✔
223

224
      // Get temperature(s) of cell (rounding to nearest integer)
225
      vector<double> cell_temps;
18,625✔
226
      if (cell->sqrtkT_.size() == 1) {
18,625✔
227
        double sqrtkT = cell->sqrtkT_[0];
15,965✔
228
        cell_temps.push_back(sqrtkT * sqrtkT / K_BOLTZMANN);
15,965✔
229
      } else if (cell->sqrtkT_.size() == cell->material_.size()) {
2,660✔
230
        double sqrtkT = cell->sqrtkT_[j];
2,643✔
231
        cell_temps.push_back(sqrtkT * sqrtkT / K_BOLTZMANN);
2,643✔
232
      } else {
233
        for (double sqrtkT : cell->sqrtkT_)
85✔
234
          cell_temps.push_back(sqrtkT * sqrtkT / K_BOLTZMANN);
68✔
235
      }
236

237
      const auto& mat {model::materials[i_material]};
18,625✔
238
      for (const auto& i_nuc : mat->nuclide_) {
97,719✔
239
        for (double temperature : cell_temps) {
158,239✔
240
          // Add temperature if it hasn't already been added
241
          if (!contains(nuc_temps[i_nuc], temperature))
79,145✔
242
            nuc_temps[i_nuc].push_back(temperature);
29,677✔
243
        }
244
      }
245

246
      for (const auto& table : mat->thermal_tables_) {
21,885✔
247
        // Get index in data::thermal_scatt array
248
        int i_sab = table.index_table;
3,260✔
249

250
        for (double temperature : cell_temps) {
6,520✔
251
          // Add temperature if it hasn't already been added
252
          if (!contains(thermal_temps[i_sab], temperature))
3,260✔
253
            thermal_temps[i_sab].push_back(temperature);
1,128✔
254
        }
255
      }
256
    }
18,625✔
257
  }
258
}
6,583✔
259

260
//==============================================================================
261

262
void finalize_geometry()
6,869✔
263
{
264
  // Perform some final operations to set up the geometry
265
  adjust_indices();
6,869✔
266
  count_cell_instances(model::root_universe);
6,869✔
267
  partition_universes();
6,869✔
268

269
  // Assign temperatures to cells that don't have temperatures already assigned
270
  assign_temperatures();
6,869✔
271

272
  // Determine number of nested coordinate levels in the geometry
273
  model::n_coord_levels = maximum_levels(model::root_universe);
6,869✔
274
}
6,869✔
275

276
//==============================================================================
277

278
int32_t find_root_universe()
6,869✔
279
{
280
  // Find all the universes listed as a cell fill.
281
  std::unordered_set<int32_t> fill_univ_ids;
6,869✔
282
  for (const auto& c : model::cells) {
38,405✔
283
    fill_univ_ids.insert(c->fill_);
31,536✔
284
  }
285

286
  // Find all the universes contained in a lattice.
287
  for (const auto& lat : model::lattices) {
8,446✔
288
    for (auto it = lat->begin(); it != lat->end(); ++it) {
727,242✔
289
      fill_univ_ids.insert(*it);
725,665✔
290
    }
291
    if (lat->outer_ != NO_OUTER_UNIVERSE) {
1,577✔
292
      fill_univ_ids.insert(lat->outer_);
274✔
293
    }
294
  }
295

296
  // Figure out which universe is not in the set.  This is the root universe.
297
  bool root_found {false};
6,869✔
298
  int32_t root_univ;
299
  for (int32_t i = 0; i < model::universes.size(); i++) {
24,700✔
300
    auto search = fill_univ_ids.find(model::universes[i]->id_);
17,831✔
301
    if (search == fill_univ_ids.end()) {
17,831✔
302
      if (root_found) {
6,869✔
UNCOV
303
        fatal_error("Two or more universes are not used as fill universes, so "
×
304
                    "it is not possible to distinguish which one is the root "
305
                    "universe.");
306
      } else {
307
        root_found = true;
6,869✔
308
        root_univ = i;
6,869✔
309
      }
310
    }
311
  }
312
  if (!root_found)
6,869✔
UNCOV
313
    fatal_error("Could not find a root universe.  Make sure "
×
314
                "there are no circular dependencies in the geometry.");
315

316
  return root_univ;
6,869✔
317
}
6,869✔
318

319
//==============================================================================
320

321
void prepare_distribcell(const std::vector<int32_t>* user_distribcells)
6,884✔
322
{
323
  write_message("Preparing distributed cell instances...", 5);
6,884✔
324

325
  std::unordered_set<int32_t> distribcells;
6,884✔
326

327
  // start with any cells manually specified via the C++ API
328
  if (user_distribcells) {
6,884✔
329
    distribcells.insert(user_distribcells->begin(), user_distribcells->end());
17✔
330
  }
331

332
  // Find all cells listed in a DistribcellFilter or CellInstanceFilter
333
  for (auto& filt : model::tally_filters) {
14,344✔
334
    auto* distrib_filt = dynamic_cast<DistribcellFilter*>(filt.get());
7,460✔
335
    auto* cell_inst_filt = dynamic_cast<CellInstanceFilter*>(filt.get());
7,460✔
336
    if (distrib_filt) {
7,460✔
337
      distribcells.insert(distrib_filt->cell());
189✔
338
    }
339
    if (cell_inst_filt) {
7,460✔
340
      const auto& filter_cells = cell_inst_filt->cells();
36✔
341
      distribcells.insert(filter_cells.begin(), filter_cells.end());
36✔
342
    }
343
  }
344

345
  // By default, add material cells to the list of distributed cells
346
  if (settings::material_cell_offsets) {
6,884✔
347
    for (gsl::index i = 0; i < model::cells.size(); ++i) {
38,495✔
348
      if (model::cells[i]->type_ == Fill::MATERIAL)
31,611✔
349
        distribcells.insert(i);
25,025✔
350
    }
351
  }
352

353
  // Make sure that the number of materials/temperatures matches the number of
354
  // cell instances.
355
  for (int i = 0; i < model::cells.size(); i++) {
38,495✔
356
    Cell& c {*model::cells[i]};
31,611✔
357

358
    if (c.material_.size() > 1) {
31,611✔
359
      if (c.material_.size() != c.n_instances_) {
308✔
UNCOV
360
        fatal_error(fmt::format(
×
361
          "Cell {} was specified with {} materials but has {} distributed "
362
          "instances. The number of materials must equal one or the number "
363
          "of instances.",
UNCOV
364
          c.id_, c.material_.size(), c.n_instances_));
×
365
      }
366
    }
367

368
    if (c.sqrtkT_.size() > 1) {
31,611✔
369
      if (c.sqrtkT_.size() != c.n_instances_) {
322✔
UNCOV
370
        fatal_error(fmt::format(
×
371
          "Cell {} was specified with {} temperatures but has {} distributed "
372
          "instances. The number of temperatures must equal one or the number "
373
          "of instances.",
UNCOV
374
          c.id_, c.sqrtkT_.size(), c.n_instances_));
×
375
      }
376
    }
377
  }
378

379
  // Search through universes for material cells and assign each one a
380
  // unique distribcell array index.
381
  int distribcell_index = 0;
6,884✔
382
  vector<int32_t> target_univ_ids;
6,884✔
383
  for (const auto& u : model::universes) {
24,764✔
384
    for (auto idx : u->cells_) {
49,491✔
385
      if (distribcells.find(idx) != distribcells.end()) {
31,611✔
386
        model::cells[idx]->distribcell_index_ = distribcell_index++;
25,110✔
387
        target_univ_ids.push_back(u->id_);
25,110✔
388
      }
389
    }
390
  }
391

392
  // Allocate the cell and lattice offset tables.
393
  int n_maps = target_univ_ids.size();
6,884✔
394
  for (auto& c : model::cells) {
38,495✔
395
    if (c->type_ != Fill::MATERIAL) {
31,611✔
396
      c->offset_.resize(n_maps, C_NONE);
6,586✔
397
    }
398
  }
399
  for (auto& lat : model::lattices) {
8,478✔
400
    lat->allocate_offset_table(n_maps);
1,594✔
401
  }
402

403
// Fill the cell and lattice offset tables.
404
#pragma omp parallel for
3,638✔
405
  for (int map = 0; map < target_univ_ids.size(); map++) {
12,206✔
406
    auto target_univ_id = target_univ_ids[map];
8,960✔
407
    std::unordered_map<int32_t, int32_t> univ_count_memo;
8,960✔
408
    for (const auto& univ : model::universes) {
43,031✔
409
      int32_t offset = 0;
34,071✔
410
      for (int32_t cell_indx : univ->cells_) {
179,130✔
411
        Cell& c = *model::cells[cell_indx];
145,059✔
412

413
        if (c.type_ == Fill::UNIVERSE) {
145,059✔
414
          c.offset_[map] = offset;
48,851✔
415
          int32_t search_univ = c.fill_;
48,851✔
416
          offset += count_universe_instances(
48,851✔
417
            search_univ, target_univ_id, univ_count_memo);
418

419
        } else if (c.type_ == Fill::LATTICE) {
96,208✔
420
          c.offset_[map] = offset;
7,568✔
421
          Lattice& lat = *model::lattices[c.fill_];
7,568✔
422
          offset +=
7,568✔
423
            lat.fill_offset_table(offset, target_univ_id, map, univ_count_memo);
7,568✔
424
        }
425
      }
426
    }
427
  }
8,960✔
428
}
6,884✔
429

430
//==============================================================================
431

432
void count_cell_instances(int32_t univ_indx)
737,653✔
433
{
434
  const auto univ_counts = model::universe_cell_counts.find(univ_indx);
737,653✔
435
  if (univ_counts != model::universe_cell_counts.end()) {
737,653✔
436
    for (const auto& it : univ_counts->second) {
2,187,656✔
437
      model::cells[it.first]->n_instances_ += it.second;
1,467,643✔
438
    }
439
  } else {
440
    for (int32_t cell_indx : model::universes[univ_indx]->cells_) {
48,977✔
441
      Cell& c = *model::cells[cell_indx];
31,337✔
442
      ++c.n_instances_;
31,337✔
443
      model::universe_cell_counts[univ_indx][cell_indx] += 1;
31,337✔
444

445
      if (c.type_ == Fill::UNIVERSE) {
31,337✔
446
        // This cell contains another universe.  Recurse into that universe.
447
        count_cell_instances(c.fill_);
4,939✔
448
        update_universe_cell_count(univ_indx, c.fill_);
4,939✔
449
      } else if (c.type_ == Fill::LATTICE) {
26,398✔
450
        // This cell contains a lattice.  Recurse into the lattice universes.
451
        Lattice& lat = *model::lattices[c.fill_];
1,613✔
452
        for (auto it = lat.begin(); it != lat.end(); ++it) {
727,458✔
453
          count_cell_instances(*it);
725,845✔
454
          update_universe_cell_count(univ_indx, *it);
725,845✔
455
        }
456
      }
457
    }
458
  }
459
}
737,653✔
460

461
//==============================================================================
462

463
int count_universe_instances(int32_t search_univ, int32_t target_univ_id,
15,091,280✔
464
  std::unordered_map<int32_t, int32_t>& univ_count_memo)
465
{
466
  // If this is the target, it can't contain itself.
467
  if (model::universes[search_univ]->id_ == target_univ_id) {
15,091,280✔
468
    return 1;
1,960,916✔
469
  }
470

471
  // If we have already counted the number of instances, reuse that value.
472
  auto search = univ_count_memo.find(search_univ);
13,130,364✔
473
  if (search != univ_count_memo.end()) {
13,130,364✔
474
    return search->second;
7,091,909✔
475
  }
476

477
  int count {0};
6,038,455✔
478
  for (int32_t cell_indx : model::universes[search_univ]->cells_) {
12,203,234✔
479
    Cell& c = *model::cells[cell_indx];
6,164,779✔
480

481
    if (c.type_ == Fill::UNIVERSE) {
6,164,779✔
482
      int32_t next_univ = c.fill_;
98,518✔
483
      count +=
98,518✔
484
        count_universe_instances(next_univ, target_univ_id, univ_count_memo);
98,518✔
485

486
    } else if (c.type_ == Fill::LATTICE) {
6,066,261✔
487
      Lattice& lat = *model::lattices[c.fill_];
7,467✔
488
      for (auto it = lat.begin(); it != lat.end(); ++it) {
3,059,997✔
489
        int32_t next_univ = *it;
3,052,530✔
490
        count +=
3,052,530✔
491
          count_universe_instances(next_univ, target_univ_id, univ_count_memo);
3,052,530✔
492
      }
493
    }
494
  }
495

496
  // Remember the number of instances in this universe.
497
  univ_count_memo[search_univ] = count;
6,038,455✔
498

499
  return count;
6,038,455✔
500
}
501

502
//==============================================================================
503

504
std::string distribcell_path_inner(int32_t target_cell, int32_t map,
2,798,712✔
505
  int32_t target_offset, const Universe& search_univ, int32_t offset)
506
{
507
  std::stringstream path;
2,798,712✔
508

509
  path << "u" << search_univ.id_ << "->";
2,798,712✔
510

511
  // Check to see if this universe directly contains the target cell.  If so,
512
  // write to the path and return.
513
  for (int32_t cell_indx : search_univ.cells_) {
12,984,876✔
514
    if ((cell_indx == target_cell) && (offset == target_offset)) {
11,197,440✔
515
      Cell& c = *model::cells[cell_indx];
1,011,276✔
516
      path << "c" << c.id_;
1,011,276✔
517
      return path.str();
2,022,552✔
518
    }
519
  }
520

521
  // The target must be further down the geometry tree and contained in a fill
522
  // cell or lattice cell in this universe.  Find which cell contains the
523
  // target.
524
  vector<std::int32_t>::const_reverse_iterator cell_it {
525
    search_univ.cells_.crbegin()};
1,787,436✔
526
  for (; cell_it != search_univ.cells_.crend(); ++cell_it) {
10,185,984✔
527
    Cell& c = *model::cells[*cell_it];
10,185,984✔
528

529
    // Material cells don't contain other cells so ignore them.
530
    if (c.type_ != Fill::MATERIAL) {
10,185,984✔
531
      int32_t temp_offset;
532
      if (c.type_ == Fill::UNIVERSE) {
2,551,104✔
UNCOV
533
        temp_offset =
×
UNCOV
534
          offset + c.offset_[map]; // TODO: should also apply to lattice fills?
×
535
      } else {
536
        Lattice& lat = *model::lattices[c.fill_];
2,551,104✔
537
        int32_t indx = lat.universes_.size() * map + lat.begin().indx_;
2,551,104✔
538
        temp_offset = offset + lat.offsets_[indx];
2,551,104✔
539
      }
540

541
      // The desired cell is the first cell that gives an offset smaller or
542
      // equal to the target offset.
543
      if (temp_offset <= target_offset - c.offset_[map])
2,551,104✔
544
        break;
1,787,436✔
545
    }
546
  }
547

548
  // if we get through the loop without finding an appropriate entry, throw
549
  // an error
550
  if (cell_it == search_univ.cells_.crend()) {
1,787,436✔
551
    fatal_error(
×
552
      fmt::format("Failed to generate a text label for distribcell with ID {}."
×
553
                  "The current label is: '{}'",
UNCOV
554
        model::cells[target_cell]->id_, path.str()));
×
555
  }
556

557
  // Add the cell to the path string.
558
  Cell& c = *model::cells[*cell_it];
1,787,436✔
559
  path << "c" << c.id_ << "->";
1,787,436✔
560

561
  if (c.type_ == Fill::UNIVERSE) {
1,787,436✔
562
    // Recurse into the fill cell.
UNCOV
563
    offset += c.offset_[map];
×
UNCOV
564
    path << distribcell_path_inner(
×
UNCOV
565
      target_cell, map, target_offset, *model::universes[c.fill_], offset);
×
UNCOV
566
    return path.str();
×
567
  } else {
568
    // Recurse into the lattice cell.
569
    Lattice& lat = *model::lattices[c.fill_];
1,787,436✔
570
    path << "l" << lat.id_;
1,787,436✔
571
    for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) {
315,325,800✔
572
      int32_t indx = lat.universes_.size() * map + it.indx_;
315,325,800✔
573
      int32_t temp_offset = offset + lat.offsets_[indx];
315,325,800✔
574
      if (temp_offset <= target_offset - c.offset_[map]) {
315,325,800✔
575
        offset = temp_offset;
1,787,436✔
576
        path << "(" << lat.index_to_string(it.indx_) << ")->";
1,787,436✔
577
        path << distribcell_path_inner(target_cell, map, target_offset,
3,574,872✔
578
          *model::universes[*it], offset + c.offset_[map]);
3,574,872✔
579
        return path.str();
3,574,872✔
580
      }
581
    }
582
    throw std::runtime_error {"Error determining distribcell path."};
×
583
  }
584
}
2,798,712✔
585

586
std::string distribcell_path(
1,011,276✔
587
  int32_t target_cell, int32_t map, int32_t target_offset)
588
{
589
  auto& root_univ = *model::universes[model::root_universe];
1,011,276✔
590
  return distribcell_path_inner(target_cell, map, target_offset, root_univ, 0);
1,011,276✔
591
}
592

593
//==============================================================================
594

595
int maximum_levels(int32_t univ)
737,653✔
596
{
597

598
  const auto level_count = model::universe_level_counts.find(univ);
737,653✔
599
  if (level_count != model::universe_level_counts.end()) {
737,653✔
600
    return level_count->second;
720,013✔
601
  }
602

603
  int levels_below {0};
17,640✔
604

605
  for (int32_t cell_indx : model::universes[univ]->cells_) {
48,977✔
606
    Cell& c = *model::cells[cell_indx];
31,337✔
607
    if (c.type_ == Fill::UNIVERSE) {
31,337✔
608
      int32_t next_univ = c.fill_;
4,939✔
609
      levels_below = std::max(levels_below, maximum_levels(next_univ));
4,939✔
610
    } else if (c.type_ == Fill::LATTICE) {
26,398✔
611
      Lattice& lat = *model::lattices[c.fill_];
1,613✔
612
      for (auto it = lat.begin(); it != lat.end(); ++it) {
727,458✔
613
        int32_t next_univ = *it;
725,845✔
614
        levels_below = std::max(levels_below, maximum_levels(next_univ));
725,845✔
615
      }
616
    }
617
  }
618

619
  ++levels_below;
17,640✔
620
  model::universe_level_counts[univ] = levels_below;
17,640✔
621
  return levels_below;
17,640✔
622
}
623

UNCOV
624
bool is_root_universe(int32_t univ_id)
×
625
{
UNCOV
626
  return model::universe_map[univ_id] == model::root_universe;
×
627
}
628

629
//==============================================================================
630

631
void free_memory_geometry()
6,990✔
632
{
633
  model::cells.clear();
6,990✔
634
  model::cell_map.clear();
6,990✔
635

636
  model::universes.clear();
6,990✔
637
  model::universe_map.clear();
6,990✔
638

639
  model::lattices.clear();
6,990✔
640
  model::lattice_map.clear();
6,990✔
641

642
  model::overlap_check_count.clear();
6,990✔
643
}
6,990✔
644

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