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

openmc-dev / openmc / 15987857139

01 Jul 2025 01:55AM UTC coverage: 85.314%. First build
15987857139

Pull #3424

github

web-flow
Merge 786a855af into b939f9003
Pull Request #3424: Fixed a bug in distribcell offsets logic

37 of 39 new or added lines in 5 files covered. (94.87%)

52633 of 61693 relevant lines covered (85.31%)

36825848.71 hits per line

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

91.67
/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, int32_t> universe_counts;
29
std::unordered_map<int32_t, int32_t> universe_level_counts;
30
} // namespace model
31

32
void read_geometry_xml()
1,350✔
33
{
34
  // Display output message
35
  write_message("Reading geometry XML file...", 5);
1,350✔
36

37
  // Check if geometry.xml exists
38
  std::string filename = settings::path_input + "geometry.xml";
1,350✔
39
  if (!file_exists(filename)) {
1,350✔
40
    fatal_error("Geometry XML file '" + filename + "' does not exist!");
×
41
  }
42

43
  // Parse settings.xml file
44
  pugi::xml_document doc;
1,350✔
45
  auto result = doc.load_file(filename.c_str());
1,350✔
46
  if (!result) {
1,350✔
47
    fatal_error("Error processing geometry.xml file.");
×
48
  }
49

50
  // Get root element
51
  pugi::xml_node root = doc.document_element();
1,350✔
52

53
  read_geometry_xml(root);
1,350✔
54
}
1,350✔
55

56
void read_geometry_xml(pugi::xml_node root)
6,740✔
57
{
58
  // Read surfaces, cells, lattice
59
  read_surfaces(root);
6,740✔
60
  read_cells(root);
6,740✔
61
  read_lattices(root);
6,738✔
62

63
  // Check to make sure a boundary condition was applied to at least one
64
  // surface
65
  bool boundary_exists = false;
6,738✔
66
  for (const auto& surf : model::surfaces) {
17,031✔
67
    if (surf->bc_) {
16,998✔
68
      boundary_exists = true;
6,705✔
69
      break;
6,705✔
70
    }
71
  }
72

73
  if (settings::run_mode != RunMode::PLOTTING &&
6,738✔
74
      settings::run_mode != RunMode::VOLUME && !boundary_exists) {
6,462✔
75
    fatal_error("No boundary conditions were applied to any surfaces!");
×
76
  }
77

78
  // Allocate universes, universe cell arrays, and assign base universe
79
  model::root_universe = find_root_universe();
6,738✔
80

81
  // if the root universe is DAGMC geometry, make sure the model is well-formed
82
  check_dagmc_root_univ();
6,738✔
83
}
6,738✔
84

85
//==============================================================================
86

87
void adjust_indices()
6,740✔
88
{
89
  // Adjust material/fill idices.
90
  for (auto& c : model::cells) {
38,222✔
91
    if (c->fill_ != C_NONE) {
31,482✔
92
      int32_t id = c->fill_;
6,796✔
93
      auto search_univ = model::universe_map.find(id);
6,796✔
94
      auto search_lat = model::lattice_map.find(id);
6,796✔
95
      if (search_univ != model::universe_map.end()) {
6,796✔
96
        c->type_ = Fill::UNIVERSE;
5,001✔
97
        c->fill_ = search_univ->second;
5,001✔
98
      } else if (search_lat != model::lattice_map.end()) {
1,795✔
99
        c->type_ = Fill::LATTICE;
1,795✔
100
        c->fill_ = search_lat->second;
1,795✔
101
      } else {
102
        fatal_error(fmt::format("Specified fill {} on cell {} is neither a "
×
103
                                "universe nor a lattice.",
104
          id, c->id_));
×
105
      }
106
    } else {
107
      c->type_ = Fill::MATERIAL;
24,686✔
108
      for (auto& mat_id : c->material_) {
50,933✔
109
        if (mat_id != MATERIAL_VOID) {
26,247✔
110
          auto search = model::material_map.find(mat_id);
18,025✔
111
          if (search == model::material_map.end()) {
18,025✔
112
            fatal_error(
×
113
              fmt::format("Could not find material {} specified on cell {}",
×
114
                mat_id, c->id_));
×
115
          }
116
          // Change from ID to index
117
          mat_id = search->second;
18,025✔
118
        }
119
      }
120
    }
121
  }
122

123
  // Change cell.universe values from IDs to indices.
124
  for (auto& c : model::cells) {
38,222✔
125
    auto search = model::universe_map.find(c->universe_);
31,482✔
126
    if (search != model::universe_map.end()) {
31,482✔
127
      c->universe_ = search->second;
31,482✔
128
    } else {
129
      fatal_error(fmt::format("Could not find universe {} specified on cell {}",
×
130
        c->universe_, c->id_));
×
131
    }
132
  }
133

134
  // Change all lattice universe values from IDs to indices.
135
  for (auto& l : model::lattices) {
8,502✔
136
    l->adjust_indices();
1,762✔
137
  }
138
}
6,740✔
139

140
//==============================================================================
141
//! Partition some universes with many z-planes for faster find_cell searches.
142

143
void partition_universes()
6,740✔
144
{
145
  // Iterate over universes with more than 10 cells.  (Fewer than 10 is likely
146
  // not worth partitioning.)
147
  for (const auto& univ : model::universes) {
24,725✔
148
    if (univ->cells_.size() > 10) {
17,985✔
149
      // Collect the set of surfaces in this universe.
150
      std::unordered_set<int32_t> surf_inds;
182✔
151
      for (auto i_cell : univ->cells_) {
3,064✔
152
        for (auto token : model::cells[i_cell]->surfaces()) {
10,920✔
153
          surf_inds.insert(std::abs(token) - 1);
8,038✔
154
        }
2,882✔
155
      }
156

157
      // Partition the universe if there are more than 5 z-planes.  (Fewer than
158
      // 5 is likely not worth it.)
159
      int n_zplanes = 0;
182✔
160
      for (auto i_surf : surf_inds) {
2,342✔
161
        if (dynamic_cast<const SurfaceZPlane*>(model::surfaces[i_surf].get())) {
2,256✔
162
          ++n_zplanes;
640✔
163
          if (n_zplanes > 5) {
640✔
164
            univ->partitioner_ = make_unique<UniversePartitioner>(*univ);
96✔
165
            break;
96✔
166
          }
167
        }
168
      }
169
    }
182✔
170
  }
171
}
6,740✔
172

173
//==============================================================================
174

175
void assign_temperatures()
6,740✔
176
{
177
  for (auto& c : model::cells) {
38,222✔
178
    // Ignore non-material cells and cells with defined temperature.
179
    if (c->material_.size() == 0)
31,482✔
180
      continue;
6,796✔
181
    if (c->sqrtkT_.size() > 0)
24,686✔
182
      continue;
437✔
183

184
    c->sqrtkT_.reserve(c->material_.size());
24,249✔
185
    for (auto i_mat : c->material_) {
50,050✔
186
      if (i_mat == MATERIAL_VOID) {
25,801✔
187
        // Set void region to 0K.
188
        c->sqrtkT_.push_back(0);
8,222✔
189
      } else {
190
        const auto& mat {model::materials[i_mat]};
17,579✔
191
        c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * mat->temperature()));
17,579✔
192
      }
193
    }
194
  }
195
}
6,740✔
196

197
//==============================================================================
198

199
void get_temperatures(
6,464✔
200
  vector<vector<double>>& nuc_temps, vector<vector<double>>& thermal_temps)
201
{
202
  for (const auto& cell : model::cells) {
37,373✔
203
    // Skip non-material cells.
204
    if (cell->fill_ != C_NONE)
30,909✔
205
      continue;
6,783✔
206

207
    for (int j = 0; j < cell->material_.size(); ++j) {
49,813✔
208
      // Skip void materials
209
      int i_material = cell->material_[j];
25,687✔
210
      if (i_material == MATERIAL_VOID)
25,687✔
211
        continue;
8,106✔
212

213
      // Get temperature(s) of cell (rounding to nearest integer)
214
      vector<double> cell_temps;
17,581✔
215
      if (cell->sqrtkT_.size() == 1) {
17,581✔
216
        double sqrtkT = cell->sqrtkT_[0];
15,805✔
217
        cell_temps.push_back(sqrtkT * sqrtkT / K_BOLTZMANN);
15,805✔
218
      } else if (cell->sqrtkT_.size() == cell->material_.size()) {
1,776✔
219
        double sqrtkT = cell->sqrtkT_[j];
1,760✔
220
        cell_temps.push_back(sqrtkT * sqrtkT / K_BOLTZMANN);
1,760✔
221
      } else {
222
        for (double sqrtkT : cell->sqrtkT_)
80✔
223
          cell_temps.push_back(sqrtkT * sqrtkT / K_BOLTZMANN);
64✔
224
      }
225

226
      const auto& mat {model::materials[i_material]};
17,581✔
227
      for (const auto& i_nuc : mat->nuclide_) {
84,023✔
228
        for (double temperature : cell_temps) {
132,932✔
229
          // Add temperature if it hasn't already been added
230
          if (!contains(nuc_temps[i_nuc], temperature))
66,490✔
231
            nuc_temps[i_nuc].push_back(temperature);
26,891✔
232
        }
233
      }
234

235
      for (const auto& table : mat->thermal_tables_) {
20,705✔
236
        // Get index in data::thermal_scatt array
237
        int i_sab = table.index_table;
3,124✔
238

239
        for (double temperature : cell_temps) {
6,248✔
240
          // Add temperature if it hasn't already been added
241
          if (!contains(thermal_temps[i_sab], temperature))
3,124✔
242
            thermal_temps[i_sab].push_back(temperature);
1,081✔
243
        }
244
      }
245
    }
17,581✔
246
  }
247
}
6,464✔
248

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

251
void finalize_geometry()
6,740✔
252
{
253
  // Perform some final operations to set up the geometry
254
  adjust_indices();
6,740✔
255
  count_universe_instances();
6,740✔
256
  partition_universes();
6,740✔
257

258
  // Assign temperatures to cells that don't have temperatures already assigned
259
  assign_temperatures();
6,740✔
260

261
  // Determine number of nested coordinate levels in the geometry
262
  model::n_coord_levels = maximum_levels(model::root_universe);
6,740✔
263
}
6,740✔
264

265
//==============================================================================
266

267
int32_t find_root_universe()
6,740✔
268
{
269
  // Find all the universes listed as a cell fill.
270
  std::unordered_set<int32_t> fill_univ_ids;
6,740✔
271
  for (const auto& c : model::cells) {
38,222✔
272
    fill_univ_ids.insert(c->fill_);
31,482✔
273
  }
274

275
  // Find all the universes contained in a lattice.
276
  for (const auto& lat : model::lattices) {
8,502✔
277
    for (auto it = lat->begin(); it != lat->end(); ++it) {
884,161✔
278
      fill_univ_ids.insert(*it);
882,399✔
279
    }
280
    if (lat->outer_ != NO_OUTER_UNIVERSE) {
1,762✔
281
      fill_univ_ids.insert(lat->outer_);
385✔
282
    }
283
  }
284

285
  // Figure out which universe is not in the set.  This is the root universe.
286
  bool root_found {false};
6,740✔
287
  int32_t root_univ;
288
  for (int32_t i = 0; i < model::universes.size(); i++) {
24,725✔
289
    auto search = fill_univ_ids.find(model::universes[i]->id_);
17,985✔
290
    if (search == fill_univ_ids.end()) {
17,985✔
291
      if (root_found) {
6,740✔
292
        fatal_error("Two or more universes are not used as fill universes, so "
×
293
                    "it is not possible to distinguish which one is the root "
294
                    "universe.");
295
      } else {
296
        root_found = true;
6,740✔
297
        root_univ = i;
6,740✔
298
      }
299
    }
300
  }
301
  if (!root_found)
6,740✔
302
    fatal_error("Could not find a root universe.  Make sure "
×
303
                "there are no circular dependencies in the geometry.");
304

305
  return root_univ;
6,740✔
306
}
6,740✔
307

308
//==============================================================================
309

310
void prepare_distribcell(const std::vector<int32_t>* user_distribcells)
6,736✔
311
{
312
  write_message("Preparing distributed cell instances...", 5);
6,736✔
313

314
  std::unordered_set<int32_t> distribcells;
6,736✔
315

316
  // start with any cells manually specified via the C++ API
317
  if (user_distribcells) {
6,736✔
318
    distribcells.insert(user_distribcells->begin(), user_distribcells->end());
16✔
319
  }
320

321
  // Find all cells listed in a DistribcellFilter or CellInstanceFilter
322
  for (auto& filt : model::tally_filters) {
14,478✔
323
    auto* distrib_filt = dynamic_cast<DistribcellFilter*>(filt.get());
7,742✔
324
    auto* cell_inst_filt = dynamic_cast<CellInstanceFilter*>(filt.get());
7,742✔
325
    if (distrib_filt) {
7,742✔
326
      distribcells.insert(distrib_filt->cell());
177✔
327
    }
328
    if (cell_inst_filt) {
7,742✔
329
      const auto& filter_cells = cell_inst_filt->cells();
34✔
330
      distribcells.insert(filter_cells.begin(), filter_cells.end());
34✔
331
    }
332
  }
333

334
  // By default, add material cells to the list of distributed cells
335
  if (settings::material_cell_offsets) {
6,736✔
336
    for (int64_t i = 0; i < model::cells.size(); ++i) {
38,270✔
337
      if (model::cells[i]->type_ == Fill::MATERIAL)
31,534✔
338
        distribcells.insert(i);
24,706✔
339
    }
340
  }
341

342
  // Make sure that the number of materials/temperatures matches the number of
343
  // cell instances.
344
  for (int i = 0; i < model::cells.size(); i++) {
38,270✔
345
    Cell& c {*model::cells[i]};
31,534✔
346

347
    if (c.material_.size() > 1) {
31,534✔
348
      if (c.material_.size() != c.n_instances()) {
227✔
349
        fatal_error(fmt::format(
×
350
          "Cell {} was specified with {} materials but has {} distributed "
351
          "instances. The number of materials must equal one or the number "
352
          "of instances.",
NEW
353
          c.id_, c.material_.size(), c.n_instances()));
×
354
      }
355
    }
356

357
    if (c.sqrtkT_.size() > 1) {
31,534✔
358
      if (c.sqrtkT_.size() != c.n_instances()) {
240✔
359
        fatal_error(fmt::format(
×
360
          "Cell {} was specified with {} temperatures but has {} distributed "
361
          "instances. The number of temperatures must equal one or the number "
362
          "of instances.",
NEW
363
          c.id_, c.sqrtkT_.size(), c.n_instances()));
×
364
      }
365
    }
366
  }
367

368
  // Search through universes for material cells and assign each one a
369
  // distribcell array index according to the containing universe.
370
  vector<int32_t> target_univ_ids;
6,736✔
371
  for (const auto& u : model::universes) {
24,749✔
372
    for (auto idx : u->cells_) {
49,547✔
373
      if (distribcells.find(idx) != distribcells.end()) {
31,534✔
374
        if (std::find(target_univ_ids.begin(), target_univ_ids.end(), u->id_) ==
24,786✔
375
            target_univ_ids.end()) {
49,572✔
376
          target_univ_ids.push_back(u->id_);
15,451✔
377
        }
378
        model::cells[idx]->distribcell_index_ =
24,786✔
379
          std::find(target_univ_ids.begin(), target_univ_ids.end(), u->id_) -
24,786✔
380
          target_univ_ids.begin();
49,572✔
381
      }
382
    }
383
  }
384

385
  // Allocate the cell and lattice offset tables.
386
  int n_maps = target_univ_ids.size();
6,736✔
387
  for (auto& c : model::cells) {
38,270✔
388
    if (c->type_ != Fill::MATERIAL) {
31,534✔
389
      c->offset_.resize(n_maps, C_NONE);
6,828✔
390
    }
391
  }
392
  for (auto& lat : model::lattices) {
8,514✔
393
    lat->allocate_offset_table(n_maps);
1,778✔
394
  }
395

396
// Fill the cell and lattice offset tables.
397
#pragma omp parallel for
3,757✔
398
  for (int map = 0; map < target_univ_ids.size(); map++) {
7,157✔
399
    auto target_univ_id = target_univ_ids[map];
4,178✔
400
    std::unordered_map<int32_t, int32_t> univ_count_memo;
4,178✔
401
    for (const auto& univ : model::universes) {
21,290✔
402
      int32_t offset = 0;
17,112✔
403
      for (int32_t cell_indx : univ->cells_) {
79,758✔
404
        Cell& c = *model::cells[cell_indx];
62,646✔
405

406
        if (c.type_ == Fill::UNIVERSE) {
62,646✔
407
          c.offset_[map] = offset;
35,866✔
408
          int32_t search_univ = c.fill_;
35,866✔
409
          offset += count_universe_instances(
35,866✔
410
            search_univ, target_univ_id, univ_count_memo);
411

412
        } else if (c.type_ == Fill::LATTICE) {
26,780✔
413
          c.offset_[map] = offset;
2,845✔
414
          Lattice& lat = *model::lattices[c.fill_];
2,845✔
415
          offset += lat.fill_offset_table(target_univ_id, map, univ_count_memo);
2,845✔
416
        }
417
      }
418
    }
419
  }
4,178✔
420
}
6,736✔
421

422
//==============================================================================
423

424
void count_universe_instances()
6,740✔
425
{
426
  for (int32_t univ = 0; univ < model::universes.size(); ++univ) {
24,725✔
427
    std::unordered_map<int32_t, int32_t> univ_count_memo;
17,985✔
428
    int32_t result = count_universe_instances(
17,985✔
429
      model::root_universe, model::universes[univ]->id_, univ_count_memo);
17,985✔
430
    model::universe_counts[univ] = result;
17,985✔
431
  }
17,985✔
432
}
6,740✔
433

434
//==============================================================================
435

436
int count_universe_instances(int32_t search_univ, int32_t target_univ_id,
22,054,866✔
437
  std::unordered_map<int32_t, int32_t>& univ_count_memo)
438
{
439
  // If this is the target, it can't contain itself.
440
  if (model::universes[search_univ]->id_ == target_univ_id) {
22,054,866✔
441
    return 1;
2,494,348✔
442
  }
443

444
  // If we have already counted the number of instances, reuse that value.
445
  auto search = univ_count_memo.find(search_univ);
19,560,518✔
446
  if (search != univ_count_memo.end()) {
19,560,518✔
447
    return search->second;
7,495,172✔
448
  }
449

450
  int count {0};
12,065,346✔
451
  for (int32_t cell_indx : model::universes[search_univ]->cells_) {
24,312,792✔
452
    Cell& c = *model::cells[cell_indx];
12,247,446✔
453

454
    if (c.type_ == Fill::UNIVERSE) {
12,247,446✔
455
      int32_t next_univ = c.fill_;
168,131✔
456
      count +=
168,131✔
457
        count_universe_instances(next_univ, target_univ_id, univ_count_memo);
168,131✔
458

459
    } else if (c.type_ == Fill::LATTICE) {
12,079,315✔
460
      Lattice& lat = *model::lattices[c.fill_];
21,199✔
461
      for (auto it = lat.begin(); it != lat.end(); ++it) {
12,649,361✔
462
        int32_t next_univ = *it;
12,628,162✔
463
        count +=
12,628,162✔
464
          count_universe_instances(next_univ, target_univ_id, univ_count_memo);
12,628,162✔
465
      }
466
    }
467
  }
468

469
  // Remember the number of instances in this universe.
470
  univ_count_memo[search_univ] = count;
12,065,346✔
471

472
  return count;
12,065,346✔
473
}
474

475
//==============================================================================
476

477
std::string distribcell_path_inner(int32_t target_cell, int32_t map,
2,565,486✔
478
  int32_t target_offset, const Universe& search_univ, int32_t offset)
479
{
480
  std::stringstream path;
2,565,486✔
481

482
  path << "u" << search_univ.id_ << "->";
2,565,486✔
483

484
  // Check to see if this universe directly contains the target cell.  If so,
485
  // write to the path and return.
486
  for (int32_t cell_indx : search_univ.cells_) {
11,902,803✔
487
    if ((cell_indx == target_cell) && (offset == target_offset)) {
10,264,320✔
488
      Cell& c = *model::cells[cell_indx];
927,003✔
489
      path << "c" << c.id_;
927,003✔
490
      return path.str();
1,854,006✔
491
    }
492
  }
493

494
  // The target must be further down the geometry tree and contained in a fill
495
  // cell or lattice cell in this universe.  Find which cell contains the
496
  // target.
497
  vector<std::int32_t>::const_reverse_iterator cell_it {
498
    search_univ.cells_.crbegin()};
1,638,483✔
499
  for (; cell_it != search_univ.cells_.crend(); ++cell_it) {
9,337,152✔
500
    Cell& c = *model::cells[*cell_it];
9,337,152✔
501

502
    // Material cells don't contain other cells so ignore them.
503
    if (c.type_ != Fill::MATERIAL) {
9,337,152✔
504
      int32_t temp_offset = offset + c.offset_[map];
2,338,512✔
505
      if (c.type_ == Fill::LATTICE) {
2,338,512✔
506
        Lattice& lat = *model::lattices[c.fill_];
2,338,512✔
507
        int32_t indx = lat.universes_.size() * map + lat.begin().indx_;
2,338,512✔
508
        temp_offset += lat.offsets_[indx];
2,338,512✔
509
      }
510

511
      // The desired cell is the first cell that gives an offset smaller or
512
      // equal to the target offset.
513
      if (temp_offset <= target_offset)
2,338,512✔
514
        break;
1,638,483✔
515
    }
516
  }
517

518
  // if we get through the loop without finding an appropriate entry, throw
519
  // an error
520
  if (cell_it == search_univ.cells_.crend()) {
1,638,483✔
521
    fatal_error(
×
522
      fmt::format("Failed to generate a text label for distribcell with ID {}."
×
523
                  "The current label is: '{}'",
524
        model::cells[target_cell]->id_, path.str()));
×
525
  }
526

527
  // Add the cell to the path string.
528
  Cell& c = *model::cells[*cell_it];
1,638,483✔
529
  path << "c" << c.id_ << "->";
1,638,483✔
530

531
  if (c.type_ == Fill::UNIVERSE) {
1,638,483✔
532
    // Recurse into the fill cell.
533
    offset += c.offset_[map];
×
534
    path << distribcell_path_inner(
×
535
      target_cell, map, target_offset, *model::universes[c.fill_], offset);
×
536
    return path.str();
×
537
  } else {
538
    // Recurse into the lattice cell.
539
    Lattice& lat = *model::lattices[c.fill_];
1,638,483✔
540
    path << "l" << lat.id_;
1,638,483✔
541
    for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) {
289,048,650✔
542
      int32_t indx = lat.universes_.size() * map + it.indx_;
289,048,650✔
543
      int32_t temp_offset = offset + lat.offsets_[indx] + c.offset_[map];
289,048,650✔
544
      if (temp_offset <= target_offset) {
289,048,650✔
545
        offset = temp_offset;
1,638,483✔
546
        path << "(" << lat.index_to_string(it.indx_) << ")->";
1,638,483✔
547
        path << distribcell_path_inner(
3,276,966✔
548
          target_cell, map, target_offset, *model::universes[*it], offset);
3,276,966✔
549
        return path.str();
3,276,966✔
550
      }
551
    }
552
    throw std::runtime_error {"Error determining distribcell path."};
×
553
  }
554
}
2,565,486✔
555

556
std::string distribcell_path(
927,003✔
557
  int32_t target_cell, int32_t map, int32_t target_offset)
558
{
559
  auto& root_univ = *model::universes[model::root_universe];
927,003✔
560
  return distribcell_path_inner(target_cell, map, target_offset, root_univ, 0);
927,003✔
561
}
562

563
//==============================================================================
564

565
int maximum_levels(int32_t univ)
894,305✔
566
{
567

568
  const auto level_count = model::universe_level_counts.find(univ);
894,305✔
569
  if (level_count != model::universe_level_counts.end()) {
894,305✔
570
    return level_count->second;
876,488✔
571
  }
572

573
  int levels_below {0};
17,817✔
574

575
  for (int32_t cell_indx : model::universes[univ]->cells_) {
49,123✔
576
    Cell& c = *model::cells[cell_indx];
31,306✔
577
    if (c.type_ == Fill::UNIVERSE) {
31,306✔
578
      int32_t next_univ = c.fill_;
5,001✔
579
      levels_below = std::max(levels_below, maximum_levels(next_univ));
5,001✔
580
    } else if (c.type_ == Fill::LATTICE) {
26,305✔
581
      Lattice& lat = *model::lattices[c.fill_];
1,795✔
582
      for (auto it = lat.begin(); it != lat.end(); ++it) {
884,359✔
583
        int32_t next_univ = *it;
882,564✔
584
        levels_below = std::max(levels_below, maximum_levels(next_univ));
882,564✔
585
      }
586
    }
587
  }
588

589
  ++levels_below;
17,817✔
590
  model::universe_level_counts[univ] = levels_below;
17,817✔
591
  return levels_below;
17,817✔
592
}
593

594
bool is_root_universe(int32_t univ_id)
×
595
{
596
  return model::universe_map[univ_id] == model::root_universe;
×
597
}
598

599
//==============================================================================
600

601
void free_memory_geometry()
6,846✔
602
{
603
  model::cells.clear();
6,846✔
604
  model::cell_map.clear();
6,846✔
605

606
  model::universe_counts.clear();
6,846✔
607
  model::universes.clear();
6,846✔
608
  model::universe_map.clear();
6,846✔
609

610
  model::lattices.clear();
6,846✔
611
  model::lattice_map.clear();
6,846✔
612

613
  model::overlap_check_count.clear();
6,846✔
614
}
6,846✔
615

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