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

openmc-dev / openmc / 16646681306

31 Jul 2025 10:36AM UTC coverage: 84.927% (-0.2%) from 85.103%
16646681306

Pull #3508

github

web-flow
Merge ac07884d9 into a64cc96ed
Pull Request #3508: Automate workflow for mesh- or cell-based R2S calculations

42 of 215 new or added lines in 5 files covered. (19.53%)

400 existing lines in 32 files now uncovered.

52933 of 62328 relevant lines covered (84.93%)

36043131.63 hits per line

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

92.23
/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_level_counts;
29
} // namespace model
30

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

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

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

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

52
  read_geometry_xml(root);
1,359✔
53
}
1,359✔
54

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

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

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

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

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

84
//==============================================================================
85

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

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

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

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

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

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

172
//==============================================================================
173

174
void assign_temperatures()
6,854✔
175
{
176
  for (auto& c : model::cells) {
38,635✔
177
    // Ignore non-material cells and cells with defined temperature.
178
    if (c->material_.size() == 0)
31,781✔
179
      continue;
6,806✔
180
    if (c->sqrtkT_.size() > 0)
24,975✔
181
      continue;
440✔
182

183
    c->sqrtkT_.reserve(c->material_.size());
24,535✔
184
    for (auto i_mat : c->material_) {
50,655✔
185
      if (i_mat == MATERIAL_VOID) {
26,120✔
186
        // Set void region to 0K.
187
        c->sqrtkT_.push_back(0);
8,454✔
188
      } else {
189
        const auto& mat {model::materials[i_mat]};
17,666✔
190
        c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * mat->temperature()));
17,666✔
191
      }
192
    }
193
  }
194
}
6,854✔
195

196
//==============================================================================
197

198
void get_temperatures(
6,578✔
199
  vector<vector<double>>& nuc_temps, vector<vector<double>>& thermal_temps)
200
{
201
  for (const auto& cell : model::cells) {
37,786✔
202
    // Skip non-material cells.
203
    if (cell->fill_ != C_NONE)
31,208✔
204
      continue;
6,793✔
205

206
    for (int j = 0; j < cell->material_.size(); ++j) {
50,424✔
207
      // Skip void materials
208
      int i_material = cell->material_[j];
26,009✔
209
      if (i_material == MATERIAL_VOID)
26,009✔
210
        continue;
8,338✔
211

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

225
      const auto& mat {model::materials[i_material]};
17,671✔
226
      for (const auto& i_nuc : mat->nuclide_) {
83,919✔
227
        for (double temperature : cell_temps) {
132,544✔
228
          // Add temperature if it hasn't already been added
229
          if (!contains(nuc_temps[i_nuc], temperature))
66,296✔
230
            nuc_temps[i_nuc].push_back(temperature);
26,344✔
231
        }
232
      }
233

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

238
        for (double temperature : cell_temps) {
6,330✔
239
          // Add temperature if it hasn't already been added
240
          if (!contains(thermal_temps[i_sab], temperature))
3,165✔
241
            thermal_temps[i_sab].push_back(temperature);
1,078✔
242
        }
243
      }
244
    }
17,671✔
245
  }
246
}
6,578✔
247

248
//==============================================================================
249

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

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

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

264
//==============================================================================
265

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

274
  // Find all the universes contained in a lattice.
275
  for (const auto& lat : model::lattices) {
8,648✔
276
    for (auto it = lat->begin(); it != lat->end(); ++it) {
890,687✔
277
      fill_univ_ids.insert(*it);
888,893✔
278
    }
279
    if (lat->outer_ != NO_OUTER_UNIVERSE) {
1,794✔
280
      fill_univ_ids.insert(lat->outer_);
386✔
281
    }
282
  }
283

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

304
  return root_univ;
6,854✔
305
}
6,854✔
306

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

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

313
  std::unordered_set<int32_t> distribcells;
6,850✔
314

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

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

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

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

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

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

367
  // Search through universes for material cells and assign each one a
368
  // distribcell array index according to the containing universe.
369
  vector<int32_t> target_univ_ids;
6,850✔
370
  for (const auto& u : model::universes) {
25,009✔
371
    for (auto idx : u->cells_) {
49,992✔
372
      if (distribcells.find(idx) != distribcells.end()) {
31,833✔
373
        if (!contains(target_univ_ids, u->id_)) {
25,075✔
374
          target_univ_ids.push_back(u->id_);
15,585✔
375
        }
376
        model::cells[idx]->distribcell_index_ =
25,075✔
377
          std::find(target_univ_ids.begin(), target_univ_ids.end(), u->id_) -
25,075✔
378
          target_univ_ids.begin();
50,150✔
379
      }
380
    }
381
  }
382

383
  // Allocate the cell and lattice offset tables.
384
  int n_maps = target_univ_ids.size();
6,850✔
385
  for (auto& c : model::cells) {
38,683✔
386
    if (c->type_ != Fill::MATERIAL) {
31,833✔
387
      c->offset_.resize(n_maps, C_NONE);
6,838✔
388
    }
389
  }
390
  for (auto& lat : model::lattices) {
8,660✔
391
    lat->allocate_offset_table(n_maps);
1,810✔
392
  }
393

394
// Fill the cell and lattice offset tables.
395
#pragma omp parallel for
3,814✔
396
  for (int map = 0; map < target_univ_ids.size(); map++) {
7,281✔
397
    auto target_univ_id = target_univ_ids[map];
4,245✔
398
    std::unordered_map<int32_t, int32_t> univ_count_memo;
4,245✔
399
    for (const auto& univ : model::universes) {
21,459✔
400
      int32_t offset = 0;
17,214✔
401
      for (int32_t cell_indx : univ->cells_) {
80,073✔
402
        Cell& c = *model::cells[cell_indx];
62,859✔
403

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

410
        } else if (c.type_ == Fill::LATTICE) {
27,003✔
411
          c.offset_[map] = offset;
2,870✔
412
          Lattice& lat = *model::lattices[c.fill_];
2,870✔
413
          offset += lat.fill_offset_table(target_univ_id, map, univ_count_memo);
2,870✔
414
        }
415
      }
416
    }
417
  }
4,245✔
418
}
6,850✔
419

420
//==============================================================================
421

422
void count_universe_instances()
6,854✔
423
{
424
  for (auto& univ : model::universes) {
24,985✔
425
    std::unordered_map<int32_t, int32_t> univ_count_memo;
18,131✔
426
    univ->n_instances_ = count_universe_instances(
18,131✔
427
      model::root_universe, univ->id_, univ_count_memo);
18,131✔
428
  }
18,131✔
429
}
6,854✔
430

431
//==============================================================================
432

433
int count_universe_instances(int32_t search_univ, int32_t target_univ_id,
22,080,652✔
434
  std::unordered_map<int32_t, int32_t>& univ_count_memo)
435
{
436
  // If this is the target, it can't contain itself.
437
  if (model::universes[search_univ]->id_ == target_univ_id) {
22,080,652✔
438
    return 1;
2,507,404✔
439
  }
440

441
  // If we have already counted the number of instances, reuse that value.
442
  auto search = univ_count_memo.find(search_univ);
19,573,248✔
443
  if (search != univ_count_memo.end()) {
19,573,248✔
444
    return search->second;
7,507,788✔
445
  }
446

447
  int count {0};
12,065,460✔
448
  for (int32_t cell_indx : model::universes[search_univ]->cells_) {
24,313,188✔
449
    Cell& c = *model::cells[cell_indx];
12,247,728✔
450

451
    if (c.type_ == Fill::UNIVERSE) {
12,247,728✔
452
      int32_t next_univ = c.fill_;
168,107✔
453
      count +=
168,107✔
454
        count_universe_instances(next_univ, target_univ_id, univ_count_memo);
168,107✔
455

456
    } else if (c.type_ == Fill::LATTICE) {
12,079,621✔
457
      Lattice& lat = *model::lattices[c.fill_];
21,251✔
458
      for (auto it = lat.begin(); it != lat.end(); ++it) {
12,662,257✔
459
        int32_t next_univ = *it;
12,641,006✔
460
        count +=
12,641,006✔
461
          count_universe_instances(next_univ, target_univ_id, univ_count_memo);
12,641,006✔
462
      }
463
    }
464
  }
465

466
  // Remember the number of instances in this universe.
467
  univ_count_memo[search_univ] = count;
12,065,460✔
468

469
  return count;
12,065,460✔
470
}
471

472
//==============================================================================
473

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

479
  path << "u" << search_univ.id_ << "->";
2,565,486✔
480

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

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

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

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

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

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

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

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

560
//==============================================================================
561

562
int maximum_levels(int32_t univ)
900,891✔
563
{
564

565
  const auto level_count = model::universe_level_counts.find(univ);
900,891✔
566
  if (level_count != model::universe_level_counts.end()) {
900,891✔
567
    return level_count->second;
882,928✔
568
  }
569

570
  int levels_below {0};
17,963✔
571

572
  for (int32_t cell_indx : model::universes[univ]->cells_) {
49,568✔
573
    Cell& c = *model::cells[cell_indx];
31,605✔
574
    if (c.type_ == Fill::UNIVERSE) {
31,605✔
575
      int32_t next_univ = c.fill_;
4,979✔
576
      levels_below = std::max(levels_below, maximum_levels(next_univ));
4,979✔
577
    } else if (c.type_ == Fill::LATTICE) {
26,626✔
578
      Lattice& lat = *model::lattices[c.fill_];
1,827✔
579
      for (auto it = lat.begin(); it != lat.end(); ++it) {
890,885✔
580
        int32_t next_univ = *it;
889,058✔
581
        levels_below = std::max(levels_below, maximum_levels(next_univ));
889,058✔
582
      }
583
    }
584
  }
585

586
  ++levels_below;
17,963✔
587
  model::universe_level_counts[univ] = levels_below;
17,963✔
588
  return levels_below;
17,963✔
589
}
590

591
bool is_root_universe(int32_t univ_id)
16,882✔
592
{
593
  return model::universe_map[univ_id] == model::root_universe;
16,882✔
594
}
595

596
//==============================================================================
597

598
void free_memory_geometry()
6,972✔
599
{
600
  model::cells.clear();
6,972✔
601
  model::cell_map.clear();
6,972✔
602

603
  model::universes.clear();
6,972✔
604
  model::universe_map.clear();
6,972✔
605

606
  model::lattices.clear();
6,972✔
607
  model::lattice_map.clear();
6,972✔
608

609
  model::overlap_check_count.clear();
6,972✔
610
}
6,972✔
611

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