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

openmc-dev / openmc / 37216251345

04 Oct 2026 04:17PM UTC coverage: 81.295% (-0.1%) from 81.4%
37216251345

Pull #4166

github

web-flow
Merge a29a4711a into 4c0448d33
Pull Request #4166: Cache surface distances and senses when finding boundaries in complex cells

20094 of 29178 branches covered (68.87%)

Branch coverage included in aggregate %.

249 of 263 new or added lines in 5 files covered. (94.68%)

205 existing lines in 3 files now uncovered.

62624 of 72572 relevant lines covered (86.29%)

42980343.05 hits per line

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

53.81
/src/dagmc.cpp
1
#include "openmc/dagmc.h"
2

3
#include <array>
4
#include <cassert>
5

6
#include "openmc/constants.h"
7
#include "openmc/container_util.h"
8
#include "openmc/error.h"
9
#include "openmc/file_utils.h"
10
#include "openmc/geometry.h"
11
#include "openmc/geometry_aux.h"
12
#include "openmc/hdf5_interface.h"
13
#include "openmc/material.h"
14
#include "openmc/settings.h"
15
#include "openmc/string_utils.h"
16

17
#ifdef OPENMC_UWUW_ENABLED
18
#include "uwuw.hpp"
19
#endif
20
#include <fmt/core.h>
21

22
#include <algorithm>
23
#include <filesystem>
24
#include <fstream>
25
#include <sstream>
26
#include <string>
27

28
#ifdef OPENMC_DAGMC_ENABLED
29

30
namespace openmc {
31

32
//==============================================================================
33
// DAGMC Universe implementation
34
//==============================================================================
35

36
DAGUniverse::DAGUniverse(pugi::xml_node node)
39 ✔
37
{
38
  MaterialOverrides material_overrides;
39 ✔
39
  TemperatureOverrides temperature_overrides;
39 ✔
40
  DensityOverrides density_overrides;
39 ✔
41

42
  if (check_for_node(node, "id")) {
39 !
43
    id_ = std::stoi(get_node_value(node, "id"));
78 ✔
44
  } else {
UNCOV
45
    fatal_error("Must specify the id of the DAGMC universe");
×
46
  }
47

48
  if (check_for_node(node, "filename")) {
39 !
49
    filename_ = get_node_value(node, "filename");
39 ✔
50
    std::filesystem::path p(filename_);
39 ✔
51
    if (p.is_relative()) {
39 ✔
52
      std::filesystem::path d(dir_name(settings::path_input));
15 ✔
53
      filename_ = (d / p).string();
30 ✔
54
    }
15 ✔
UNCOV
55
  } else {
×
UNCOV
56
    fatal_error("Must specify a file for the DAGMC universe");
×
57
  }
58

59
  adjust_geometry_ids_ = false;
39 ✔
60
  if (check_for_node(node, "auto_geom_ids")) {
39 ✔
61
    adjust_geometry_ids_ = get_node_value_bool(node, "auto_geom_ids");
15 ✔
62
  }
63

64
  adjust_material_ids_ = false;
39 ✔
65
  if (check_for_node(node, "auto_mat_ids")) {
39 !
UNCOV
66
    adjust_material_ids_ = get_node_value_bool(node, "auto_mat_ids");
×
67
  }
68

69
  if (check_for_node(node, "length_multiplier")) {
39 ✔
70
    length_multiplier_ = std::stod(get_node_value(node, "length_multiplier"));
4 ✔
71
  }
72

73
  // Get material assignment overrides from nested DAGMC cell elements.
74
  if (node.child("cell")) {
39 ✔
75
    for (pugi::xml_node cell_node : node.children("cell")) {
12 ✔
76
      if (!check_for_node(cell_node, "id")) {
9 !
UNCOV
77
        fatal_error(
×
78
          "Must specify id for each DAGMC cell override in <dagmc_universe>.");
79
      }
80

81
      int32_t cell_id = std::stoi(get_node_value(cell_node, "id"));
18 ✔
82

83
      if (check_for_node(cell_node, "region")) {
9 !
UNCOV
84
        fatal_error(fmt::format(
×
85
          "DAGMC cell {} override cannot specify a region.", cell_id));
86
      }
87
      if (check_for_node(cell_node, "fill")) {
9 !
UNCOV
88
        fatal_error(fmt::format(
×
89
          "DAGMC cell {} override currently only supports material fills.",
90
          cell_id));
91
      }
92
      if (check_for_node(cell_node, "universe")) {
9 !
UNCOV
93
        fatal_error(fmt::format(
×
94
          "DAGMC cell {} override cannot specify a universe.", cell_id));
95
      }
96
      if (check_for_node(cell_node, "translation") ||
18 !
97
          check_for_node(cell_node, "rotation")) {
9 ✔
UNCOV
98
        fatal_error(fmt::format(
×
99
          "DAGMC cell {} override does not support translation or rotation.",
100
          cell_id));
101
      }
102
      if (!check_for_node(cell_node, "material")) {
9 !
UNCOV
103
        fatal_error(fmt::format(
×
104
          "DAGMC cell {} override must specify material.", cell_id));
105
      }
106

107
      auto inserted = material_overrides.emplace(
9 ✔
108
        cell_id, parse_cell_material_xml(cell_node, cell_id));
9 ✔
109
      if (!inserted.second) {
9 !
UNCOV
110
        fatal_error(fmt::format(
×
111
          "Duplicate DAGMC cell override specified for cell {}", cell_id));
112
      }
113

114
      if (check_for_node(cell_node, "temperature")) {
9 !
UNCOV
115
        temperature_overrides.emplace(
×
UNCOV
116
          cell_id, parse_cell_temperature_xml(cell_node, cell_id));
×
117
      }
118

119
      if (check_for_node(cell_node, "density")) {
9 !
UNCOV
120
        density_overrides.emplace(
×
UNCOV
121
          cell_id, parse_cell_density_xml(cell_node, cell_id));
×
122
      }
123
    }
124
  } else if (check_for_node(node, "material_overrides")) {
36 !
UNCOV
125
    if (node.child("cell")) {
×
UNCOV
126
      fatal_error("DAGMCUniverse cannot specify both <material_overrides> and "
×
127
                  "<cell> sub-elements. Use <cell> elements only.");
128
    }
UNCOV
129
    warning("DAGMCUniverse <material_overrides> is deprecated. Use nested "
×
130
            "<cell> elements under <dagmc_universe> instead.");
UNCOV
131
    for (pugi::xml_node co :
×
UNCOV
132
      node.child("material_overrides").children("cell_override")) {
×
UNCOV
133
      int32_t cell_id = std::stoi(get_node_value(co, "id"));
×
UNCOV
134
      std::istringstream iss(co.child("material_ids").text().get());
×
UNCOV
135
      vector<int32_t> mats;
×
UNCOV
136
      for (std::string s; iss >> s;) {
×
UNCOV
137
        mats.push_back(s == "void" ? MATERIAL_VOID : std::stoi(s));
×
UNCOV
138
      }
×
UNCOV
139
      material_overrides.emplace(cell_id, mats);
×
UNCOV
140
    }
×
141
  }
142

143
  initialize(material_overrides, temperature_overrides, density_overrides);
39 ✔
144
}
117 !
145

UNCOV
146
DAGUniverse::DAGUniverse(const std::string& filename, bool auto_geom_ids,
×
UNCOV
147
  bool auto_mat_ids, double length_multiplier)
×
UNCOV
148
  : filename_(filename), adjust_geometry_ids_(auto_geom_ids),
×
UNCOV
149
    adjust_material_ids_(auto_mat_ids), length_multiplier_(length_multiplier)
×
150
{
UNCOV
151
  set_id();
×
UNCOV
152
  initialize();
×
UNCOV
153
}
×
154

155
DAGUniverse::DAGUniverse(std::shared_ptr<moab::DagMC> dagmc_ptr,
1 ✔
156
  const std::string& filename, bool auto_geom_ids, bool auto_mat_ids,
157
  double length_multiplier)
1 ✔
158
  : dagmc_instance_(dagmc_ptr), filename_(filename),
2 !
159
    adjust_geometry_ids_(auto_geom_ids), adjust_material_ids_(auto_mat_ids),
1 ✔
160
    length_multiplier_(length_multiplier)
2 !
161
{
162
  MaterialOverrides material_overrides;
1 ✔
163
  TemperatureOverrides temperature_overrides;
1 ✔
164
  DensityOverrides density_overrides;
1 ✔
165
  set_id();
1 ✔
166
  init_metadata();
1 ✔
167
  init_geometry(material_overrides, temperature_overrides, density_overrides);
1 ✔
168
}
3 !
169

170
void DAGUniverse::set_id()
1 ✔
171
{
172
  // determine the next universe id
173
  int32_t next_univ_id = 0;
1 ✔
174
  for (const auto& u : model::universes) {
1 !
UNCOV
175
    if (u->id_ > next_univ_id)
×
176
      next_univ_id = u->id_;
177
  }
178
  next_univ_id++;
1 ✔
179

180
  // set the universe id
181
  id_ = next_univ_id;
1 ✔
182
}
1 ✔
183

UNCOV
184
void DAGUniverse::initialize()
×
185
{
UNCOV
186
  MaterialOverrides material_overrides;
×
UNCOV
187
  TemperatureOverrides temperature_overrides;
×
UNCOV
188
  initialize(material_overrides, temperature_overrides);
×
UNCOV
189
}
×
190

191
void DAGUniverse::initialize(const MaterialOverrides& material_overrides,
39 ✔
192
  const TemperatureOverrides& temperature_overrides,
193
  const DensityOverrides& density_overrides)
194
{
195
#ifdef OPENMC_UWUW_ENABLED
196
  // read uwuw materials from the .h5m file if present
197
  read_uwuw_materials();
39 ✔
198
#endif
199

200
  init_dagmc();
39 ✔
201

202
  init_metadata();
39 ✔
203

204
  init_geometry(material_overrides, temperature_overrides, density_overrides);
39 ✔
205
}
39 ✔
206

207
void DAGUniverse::init_dagmc()
39 ✔
208
{
209

210
  // create a new DAGMC instance
211
  dagmc_instance_ = std::make_shared<moab::DagMC>();
39 !
212

213
  // load the DAGMC geometry
214
  if (!file_exists(filename_)) {
39 !
UNCOV
215
    fatal_error("Geometry DAGMC file '" + filename_ + "' does not exist!");
×
216
  }
217
  moab::ErrorCode rval = dagmc_instance_->load_file(filename_.c_str());
39 ✔
218
  MB_CHK_ERR_CONT(rval);
39 !
219

220
  if (length_multiplier_ != 1.0) {
39 ✔
221
    moab::Range verts;
2 ✔
222
    rval =
2 ✔
223
      dagmc_instance_->moab_instance()->get_entities_by_dimension(0, 0, verts);
2 ✔
224
    MB_CHK_ERR_CONT(rval);
2 !
225

226
    for (auto vert : verts) {
1,284 ✔
227
      std::array<double, 3> coord;
1,280 ✔
228
      rval =
1,280 ✔
229
        dagmc_instance_->moab_instance()->get_coords(&vert, 1, coord.data());
1,280 ✔
230
      MB_CHK_ERR_CONT(rval);
1,280 !
231

232
      for (auto& c : coord) {
5,120 ✔
233
        c *= length_multiplier_;
3,840 ✔
234
      }
235

236
      rval =
1,280 ✔
237
        dagmc_instance_->moab_instance()->set_coords(&vert, 1, coord.data());
1,280 ✔
238
      MB_CHK_ERR_CONT(rval);
1,280 !
239
    }
240
  }
2 ✔
241

242
  // initialize acceleration data structures
243
  rval = dagmc_instance_->init_OBBTree();
39 ✔
244
  MB_CHK_ERR_CONT(rval);
39 !
245
}
39 ✔
246

247
void DAGUniverse::init_metadata()
40 ✔
248
{
249
  // parse model metadata
250
  dmd_ptr =
40 ✔
251
    std::make_unique<dagmcMetaData>(dagmc_instance_.get(), false, false);
40 ✔
252
  dmd_ptr->load_property_data();
40 ✔
253

254
  std::vector<std::string> keywords {"temp"};
80 !
255
  std::map<std::string, std::string> dum;
40 ✔
256
  std::string delimiters = ":/";
40 ✔
257
  moab::ErrorCode rval;
40 ✔
258
  rval = dagmc_instance_->parse_properties(keywords, dum, delimiters.c_str());
40 ✔
259
  MB_CHK_ERR_CONT(rval);
40 !
260
}
40 ✔
261

262
void DAGUniverse::init_geometry(const MaterialOverrides& material_overrides,
40 ✔
263
  const TemperatureOverrides& temperature_overrides,
264
  const DensityOverrides& density_overrides)
265
{
266
  moab::ErrorCode rval;
40 ✔
267

268
  // determine the next cell id
269
  int32_t next_cell_id = 0;
40 ✔
270
  for (const auto& c : model::cells) {
65 ✔
271
    if (c->id_ > next_cell_id)
25 ✔
272
      next_cell_id = c->id_;
273
  }
274
  cell_idx_offset_ = model::cells.size();
40 ✔
275
  next_cell_id++;
40 ✔
276

277
  // initialize cell objects
278
  int n_cells = dagmc_instance_->num_entities(3);
40 ✔
279
  moab::EntityHandle graveyard = 0;
40 ✔
280
  for (int i = 0; i < n_cells; i++) {
216 ✔
281
    moab::EntityHandle vol_handle = dagmc_instance_->entity_by_index(3, i + 1);
176 ✔
282

283
    // set cell ids using global IDs
284
    auto c = std::make_unique<DAGCell>(dagmc_instance_, i + 1);
176 ✔
285
    c->id_ = adjust_geometry_ids_
176 ✔
286
               ? next_cell_id++
176 ✔
287
               : dagmc_instance_->id_by_index(3, c->dag_index());
115 ✔
288
    c->universe_ = this->id_;
176 ✔
289
    c->fill_ = C_NONE; // no fill, single universe
176 ✔
290
    if (dagmc_instance_->is_implicit_complement(vol_handle)) {
176 ✔
291
      c->name_ = "implicit complement";
40 ✔
292
    }
293

294
    auto in_map = model::cell_map.find(c->id_);
176 !
295
    if (in_map == model::cell_map.end()) {
176 !
296
      model::cell_map[c->id_] = model::cells.size();
176 ✔
297
    } else {
UNCOV
298
      warning(fmt::format("DAGMC Cell IDs: {}", dagmc_ids_for_dim(3)));
×
UNCOV
299
      fatal_error(fmt::format(
×
300
        "DAGMC Universe {} contains a cell with ID {}, which "
301
        "already exists elsewhere in the geometry. Setting auto_geom_ids "
302
        "to True when initiating the DAGMC Universe may "
303
        "resolve this issue",
UNCOV
304
        this->id_, c->id_));
×
305
    }
306

307
    // --- Materials ---
308

309
    // determine volume material assignment
310
    std::string mat_str = dmd_ptr->get_volume_property("material", vol_handle);
176 ✔
311

312
    if (mat_str.empty()) {
176 !
UNCOV
313
      fatal_error(fmt::format("Volume {} has no material assignment.", c->id_));
×
314
    }
315

316
    to_lower(mat_str);
176 ✔
317

318
    if (mat_str == "graveyard") {
176 ✔
319
      graveyard = vol_handle;
31 ✔
320
    }
321
    if (material_overrides.count(c->id_)) {
176 ✔
322
      override_assign_material(c, material_overrides);
9 ✔
323
    } else if (mat_str == "void" || mat_str == "vacuum" ||
333 ✔
324
               mat_str == "graveyard") {
129 ✔
325
      c->material_.push_back(MATERIAL_VOID);
68 ✔
326
    } else if (uses_uwuw()) {
99 ✔
327
      uwuw_assign_material(vol_handle, c);
6 ✔
328
    } else {
329
      legacy_assign_material(mat_str, c);
186 ✔
330
    }
331

332
    if (temperature_overrides.count(c->id_)) {
176 !
UNCOV
333
      if (c->material_.empty() || c->material_[0] == MATERIAL_VOID) {
×
UNCOV
334
        fatal_error(fmt::format("DAGMC cell {} was specified with a "
×
335
                                "temperature but no non-void material.",
UNCOV
336
          c->id_));
×
337
      }
338

UNCOV
339
      c->sqrtkT_.clear();
×
UNCOV
340
      const auto& temp_overrides = temperature_overrides.at(c->id_);
×
UNCOV
341
      c->sqrtkT_.reserve(temp_overrides.size());
×
UNCOV
342
      for (auto T : temp_overrides) {
×
UNCOV
343
        c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * T));
×
344
      }
345

UNCOV
346
      if (settings::verbosity >= 10) {
×
UNCOV
347
        std::stringstream override_values;
×
UNCOV
348
        for (size_t i = 0; i < temp_overrides.size(); ++i) {
×
UNCOV
349
          if (i > 0) {
×
UNCOV
350
            override_values << " ";
×
351
          }
UNCOV
352
          override_values << temp_overrides[i];
×
353
        }
UNCOV
354
        auto msg = fmt::format("Overriding DAGMC cell {} property "
×
355
                               "'temperature [K]' with value(s): {}",
UNCOV
356
          c->id_, override_values.str());
×
UNCOV
357
        write_message(msg, 10);
×
UNCOV
358
      }
×
359
    }
360

361
    if (density_overrides.count(c->id_)) {
176 !
UNCOV
362
      if (c->material_.empty() || c->material_[0] == MATERIAL_VOID) {
×
UNCOV
363
        fatal_error(fmt::format("DAGMC cell {} was specified with a density "
×
364
                                "but no non-void material.",
UNCOV
365
          c->id_));
×
366
      }
367
      // density_mult_ holds the true density until materials are finalized,
368
      // at which point it is converted to a proper multiplier (same as CSG).
UNCOV
369
      c->density_mult_ = density_overrides.at(c->id_);
×
370

UNCOV
371
      if (settings::verbosity >= 10) {
×
UNCOV
372
        const auto& dens = density_overrides.at(c->id_);
×
UNCOV
373
        std::stringstream override_values;
×
UNCOV
374
        for (size_t i = 0; i < dens.size(); ++i) {
×
UNCOV
375
          if (i > 0)
×
UNCOV
376
            override_values << " ";
×
UNCOV
377
          override_values << dens[i];
×
378
        }
UNCOV
379
        write_message(fmt::format("Overriding DAGMC cell {} property "
×
380
                                  "'density [g/cm³]' with value(s): {}",
UNCOV
381
                        c->id_, override_values.str()),
×
382
          10);
UNCOV
383
      }
×
384
    }
385

386
    // check for temperature assignment
387
    std::string temp_value;
176 ✔
388

389
    // no temperature if void
390
    if (c->material_[0] == MATERIAL_VOID) {
176 ✔
391
      model::cells.emplace_back(std::move(c));
72 ✔
392
      continue;
72 ✔
393
    }
394

395
    // assign cell temperature if not explicitly overridden
396
    if (c->sqrtkT_.empty()) {
104 !
397
      const auto& mat =
104 ✔
398
        model::materials[model::material_map.at(c->material_[0])];
104 ✔
399
      if (dagmc_instance_->has_prop(vol_handle, "temp")) {
104 ✔
400
        rval = dagmc_instance_->prop_value(vol_handle, "temp", temp_value);
35 ✔
401
        MB_CHK_ERR_CONT(rval);
35 !
402
        double temp = std::stod(temp_value);
35 ✔
403
        c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * temp));
35 ✔
404
      } else if (mat->temperature() > 0.0) {
69 !
405
        c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * mat->temperature()));
69 ✔
406
      } else {
UNCOV
407
        c->sqrtkT_.push_back(
×
UNCOV
408
          std::sqrt(K_BOLTZMANN * settings::temperature_default));
×
409
      }
410
    }
411

412
    model::cells.emplace_back(std::move(c));
104 ✔
413
  }
176 ✔
414

415
  // allocate the cell overlap count if necessary
416
  if (settings::check_overlaps) {
40 !
UNCOV
417
    model::overlap_check_count.resize(model::cells.size(), 0);
×
418
  }
419

420
  has_graveyard_ = graveyard;
40 ✔
421

422
  // determine the next surface id
423
  int32_t next_surf_id = 0;
40 ✔
424
  for (const auto& s : model::surfaces) {
180 ✔
425
    if (s->id_ > next_surf_id)
140 ✔
426
      next_surf_id = s->id_;
427
  }
428
  surf_idx_offset_ = model::surfaces.size();
40 ✔
429
  next_surf_id++;
40 ✔
430

431
  // initialize surface objects
432
  int n_surfaces = dagmc_instance_->num_entities(2);
40 ✔
433
  for (int i = 0; i < n_surfaces; i++) {
738 ✔
434
    moab::EntityHandle surf_handle = dagmc_instance_->entity_by_index(2, i + 1);
698 ✔
435

436
    // set cell ids using global IDs
437
    auto s = std::make_unique<DAGSurface>(dagmc_instance_, i + 1);
698 ✔
438
    s->id_ = adjust_geometry_ids_ ? next_surf_id++
1,169 ✔
439
                                  : dagmc_instance_->id_by_index(2, i + 1);
471 ✔
440

441
    // set surface source attribute if needed
442
    if (contains(settings::source_write_surf_id, s->id_) ||
1,396 ✔
443
        settings::source_write_surf_id.empty()) {
695 ✔
444
      s->surf_source_ = true;
596 ✔
445
    }
446

447
    // set BCs
448
    std::string bc_value =
698 ✔
449
      dmd_ptr->get_surface_property("boundary", surf_handle);
698 ✔
450
    to_lower(bc_value);
698 ✔
451
    if (bc_value.empty() || bc_value == "transmit" ||
710 !
452
        bc_value == "transmission") {
12 !
453
      // set to transmission by default (nullptr)
454
    } else if (bc_value == "vacuum") {
12 ✔
455
      s->bc_ = make_unique<VacuumBC>();
6 ✔
456
    } else if (bc_value == "reflective" || bc_value == "reflect" ||
12 !
457
               bc_value == "reflecting") {
6 !
458
      s->bc_ = make_unique<ReflectiveBC>();
6 ✔
UNCOV
459
    } else if (bc_value == "periodic") {
×
UNCOV
460
      fatal_error("Periodic boundary condition not supported in DAGMC.");
×
461
    } else {
UNCOV
462
      fatal_error(fmt::format("Unknown boundary condition \"{}\" specified "
×
463
                              "on surface {}",
UNCOV
464
        bc_value, s->id_));
×
465
    }
466

467
    // graveyard check
468
    moab::Range parent_vols;
698 ✔
469
    rval = dagmc_instance_->moab_instance()->get_parent_meshsets(
698 ✔
470
      surf_handle, parent_vols);
471
    MB_CHK_ERR_CONT(rval);
698 !
472

473
    // if this surface belongs to the graveyard
474
    if (graveyard && parent_vols.find(graveyard) != parent_vols.end()) {
983 ✔
475
      // set graveyard surface BC's to vacuum
476
      s->bc_ = make_unique<VacuumBC>();
372 ✔
477
    }
478

479
    // add to global array and map
480

481
    auto in_map = model::surface_map.find(s->id_);
698 !
482
    if (in_map == model::surface_map.end()) {
698 !
483
      model::surface_map[s->id_] = model::surfaces.size();
698 ✔
484
    } else {
UNCOV
485
      warning(fmt::format("DAGMC Surface IDs: {}", dagmc_ids_for_dim(2)));
×
UNCOV
486
      fatal_error(fmt::format("Surface ID {} exists in both Universe {} "
×
487
                              "and the CSG geometry.",
UNCOV
488
        s->id_, this->id_));
×
489
    }
490

491
    model::surfaces.emplace_back(std::move(s));
698 ✔
492
  } // end surface loop
698 ✔
493
}
40 ✔
494

495
int32_t DAGUniverse::cell_index(moab::EntityHandle vol) const
46,716 ✔
496
{
497
  // return the index of the volume in the DAGMC instance and then
498
  // adjust by the offset into the model cells for this DAGMC universe
499
  return dagmc_ptr()->index_by_handle(vol) + cell_idx_offset_;
46,716 ✔
500
}
501

UNCOV
502
int32_t DAGUniverse::surface_index(moab::EntityHandle surf) const
×
503
{
504
  // return the index of the surface in the DAGMC instance and then
505
  // adjust by the offset into the model cells for this DAGMC universe
UNCOV
506
  return dagmc_ptr()->index_by_handle(surf) + surf_idx_offset_;
×
507
}
508

UNCOV
509
std::string DAGUniverse::dagmc_ids_for_dim(int dim) const
×
510
{
511
  // generate a vector of ids
UNCOV
512
  std::vector<int> id_vec;
×
UNCOV
513
  int n_ents = dagmc_instance_->num_entities(dim);
×
UNCOV
514
  for (int i = 1; i <= n_ents; i++) {
×
UNCOV
515
    id_vec.push_back(dagmc_instance_->id_by_index(dim, i));
×
516
  }
517

518
  // sort the vector of ids
UNCOV
519
  std::sort(id_vec.begin(), id_vec.end());
×
520

521
  // generate a string representation of the ID range(s)
UNCOV
522
  std::stringstream out;
×
523

UNCOV
524
  int i = 0;
×
UNCOV
525
  int start_id = id_vec[0]; // initialize with first ID
×
UNCOV
526
  int stop_id;
×
527
  // loop over all cells in the universe
UNCOV
528
  while (i < n_ents) {
×
529

UNCOV
530
    stop_id = id_vec[i];
×
531

532
    // if the next ID is not in this contiguous set of IDS,
533
    // figure out how to write the string representing this set
UNCOV
534
    if (id_vec[i + 1] > stop_id + 1) {
×
535

UNCOV
536
      if (start_id != stop_id) {
×
537
        // there are several IDs in a row, print condensed version (i.e. 1-10,
538
        // 12-20)
UNCOV
539
        out << start_id << "-" << stop_id;
×
540
      } else {
541
        // only one ID in this contiguous block (i.e. 3, 5, 7, 9)
UNCOV
542
        out << start_id;
×
543
      }
544
      // insert a comma as long as we aren't in the last ID set
UNCOV
545
      if (i < n_ents - 1) {
×
UNCOV
546
        out << ", ";
×
547
      }
548

549
      // if we are at the end of a set, set the start ID to the first value
550
      // in the next set.
UNCOV
551
      start_id = id_vec[++i];
×
552
    }
553

UNCOV
554
    i++;
×
555
  }
556

UNCOV
557
  return out.str();
×
UNCOV
558
}
×
559

560
int32_t DAGUniverse::implicit_complement_idx() const
757,666 ✔
561
{
562
  moab::EntityHandle ic;
757,666 ✔
563
  moab::ErrorCode rval =
757,666 ✔
564
    dagmc_instance_->geom_tool()->get_implicit_complement(ic);
1,515,332 !
565
  MB_CHK_SET_ERR_CONT(rval, "Failed to get implicit complement");
757,666 !
566
  // off-by-one: DAGMC indices start at one
567
  return cell_idx_offset_ + dagmc_instance_->index_by_handle(ic) - 1;
757,666 ✔
568
}
569

570
bool DAGUniverse::find_cell(GeometryState& p) const
7,143,805 ✔
571
{
572
  // if the particle isn't in any of the other DagMC
573
  // cells, place it in the implicit complement
574
  bool found = Universe::find_cell(p);
7,143,805 ✔
575
  if (!found && model::universe_map[this->id_] != model::root_universe) {
7,143,805 ✔
576
    p.lowest_coord().cell() = implicit_complement_idx();
757,485 ✔
577
    found = true;
757,485 ✔
578
  }
579
  return found;
7,143,805 ✔
580
}
581

582
void DAGUniverse::to_hdf5(hid_t universes_group) const
36 ✔
583
{
584
  // Create a group for this universe.
585
  auto group = create_group(universes_group, fmt::format("universe {}", id_));
36 ✔
586

587
  // Write the geometry representation type.
588
  write_string(group, "geom_type", "dagmc", false);
36 ✔
589

590
  // Write other properties of the DAGMC Universe
591
  write_string(group, "filename", filename_, false);
36 ✔
592
  write_attribute(
36 ✔
593
    group, "auto_geom_ids", static_cast<int>(adjust_geometry_ids_));
36 ✔
594
  write_attribute(
36 ✔
595
    group, "auto_mat_ids", static_cast<int>(adjust_material_ids_));
36 ✔
596
  write_attribute(group, "length_multiplier", length_multiplier_);
36 ✔
597

598
  close_group(group);
36 ✔
599
}
36 ✔
600

601
bool DAGUniverse::uses_uwuw() const
140 ✔
602
{
603
#ifdef OPENMC_UWUW_ENABLED
604
  return uwuw_ && !uwuw_->material_library.empty();
140 ✔
605
#else
606
  return false;
607
#endif // OPENMC_UWUW_ENABLED
608
}
609

610
std::string DAGUniverse::get_uwuw_materials_xml() const
2 ✔
611
{
612
#ifdef OPENMC_UWUW_ENABLED
613
  if (!uses_uwuw()) {
2 !
UNCOV
614
    throw std::runtime_error("This DAGMC Universe does not use UWUW materials");
×
615
  }
616

617
  std::stringstream ss;
2 ✔
618
  // write header
619
  ss << "<?xml version=\"1.0\"?>\n";
2 ✔
620
  ss << "<materials>\n";
2 ✔
621
  const auto& mat_lib = uwuw_->material_library;
2 ✔
622
  // write materials
623
  for (auto mat : mat_lib) {
6 ✔
624
    ss << mat.second->openmc("atom");
8 ✔
625
  }
4 ✔
626
  // write footer
627
  ss << "</materials>";
2 ✔
628

629
  return ss.str();
4 ✔
630
#else
631
  fatal_error("DAGMC was not configured with UWUW.");
632
#endif // OPENMC_UWUW_ENABLED
633
}
2 ✔
634

UNCOV
635
void DAGUniverse::write_uwuw_materials_xml(const std::string& outfile) const
×
636
{
637
#ifdef OPENMC_UWUW_ENABLED
UNCOV
638
  if (!uses_uwuw()) {
×
UNCOV
639
    throw std::runtime_error(
×
UNCOV
640
      "This DAGMC universe does not use UWUW materials.");
×
641
  }
642

UNCOV
643
  std::string xml_str = get_uwuw_materials_xml();
×
644
  // if there is a material library in the file
UNCOV
645
  std::ofstream mats_xml(outfile);
×
UNCOV
646
  mats_xml << xml_str;
×
UNCOV
647
  mats_xml.close();
×
648
#else
649
  fatal_error("DAGMC was not configured with UWUW.");
650
#endif // OPENMC_UWUW_ENABLED
UNCOV
651
}
×
652

653
void DAGUniverse::legacy_assign_material(
93 ✔
654
  std::string mat_string, std::unique_ptr<DAGCell>& c) const
655
{
656
  bool mat_found_by_name = false;
93 ✔
657
  // attempt to find a material with a matching name
658
  to_lower(mat_string);
93 ✔
659
  for (const auto& m : model::materials) {
284 ✔
660
    std::string m_name = m->name();
191 ✔
661
    to_lower(m_name);
191 ✔
662
    if (mat_string == m_name) {
191 ✔
663
      // assign the material with that name
664
      if (!mat_found_by_name) {
77 !
665
        mat_found_by_name = true;
77 ✔
666
        c->material_.push_back(m->id_);
77 ✔
667
        // report error if more than one material is found
668
      } else {
UNCOV
669
        fatal_error(fmt::format(
×
670
          "More than one material found with name '{}'. Please ensure "
671
          "materials "
672
          "have unique names if using this property to assign materials.",
673
          mat_string));
674
      }
675
    }
676
  }
191 ✔
677

678
  // if no material was set using a name, assign by id
679
  if (!mat_found_by_name) {
93 ✔
680
    bool found_by_id = true;
16 ✔
681
    try {
16 ✔
682
      auto id = std::stoi(mat_string);
16 ✔
683
      if (model::material_map.find(id) == model::material_map.end())
16 !
UNCOV
684
        found_by_id = false;
×
685
      c->material_.emplace_back(id);
16 ✔
UNCOV
686
    } catch (const std::invalid_argument&) {
×
UNCOV
687
      found_by_id = false;
×
UNCOV
688
    }
×
689

690
    // report failure for failed int conversion or missing material
691
    if (!found_by_id)
16 !
UNCOV
692
      fatal_error(
×
UNCOV
693
        fmt::format("Material with name/ID '{}' not found for volume (cell) {}",
×
UNCOV
694
          mat_string, c->id_));
×
695
  }
696

697
  if (settings::verbosity >= 10) {
93 !
UNCOV
698
    const auto& m = model::materials[model::material_map.at(c->material_[0])];
×
UNCOV
699
    std::stringstream msg;
×
UNCOV
700
    msg << "DAGMC material " << mat_string << " was assigned";
×
UNCOV
701
    if (mat_found_by_name) {
×
UNCOV
702
      msg << " using material name: " << m->name_;
×
703
    } else {
UNCOV
704
      msg << " using material id: " << m->id_;
×
705
    }
UNCOV
706
    write_message(msg.str(), 10);
×
UNCOV
707
  }
×
708
}
93 ✔
709

710
void DAGUniverse::read_uwuw_materials()
39 ✔
711
{
712
#ifdef OPENMC_UWUW_ENABLED
713
  // If no filename was provided, don't read UWUW materials
714
  if (filename_ == "")
39 !
715
    return;
37 ✔
716

717
  uwuw_ = std::make_shared<UWUW>(filename_.c_str());
39 !
718

719
  if (!uses_uwuw())
39 ✔
720
    return;
721

722
  // Notify user if UWUW materials are going to be used
723
  write_message("Found UWUW Materials in the DAGMC geometry file.", 6);
2 ✔
724

725
  // if we're using automatic IDs, update the UWUW material metadata
726
  if (adjust_material_ids_) {
2 !
UNCOV
727
    int32_t next_material_id = 0;
×
UNCOV
728
    for (const auto& m : model::materials) {
×
UNCOV
729
      next_material_id = std::max(m->id_, next_material_id);
×
730
    }
UNCOV
731
    next_material_id++;
×
732

UNCOV
733
    for (auto& mat : uwuw_->material_library) {
×
UNCOV
734
      mat.second->metadata["mat_number"] = next_material_id++;
×
735
    }
736
  }
737

738
  std::string mat_xml_string = get_uwuw_materials_xml();
2 ✔
739

740
  // create a pugi XML document from this string
741
  pugi::xml_document doc;
2 ✔
742
  auto result = doc.load_string(mat_xml_string.c_str());
2 ✔
743
  if (!result) {
2 !
UNCOV
744
    fatal_error("Error processing XML created using DAGMC UWUW materials.");
×
745
  }
746
  pugi::xml_node root = doc.document_element();
2 ✔
747
  for (pugi::xml_node material_node : root.children("material")) {
6 ✔
748
    model::materials.push_back(std::make_unique<Material>(material_node));
8 ✔
749
  }
750
#else
751
  fatal_error("DAGMC was not configured with UWUW.");
752
#endif // OPENMC_UWUW_ENABLED
753
}
2 ✔
754

755
void DAGUniverse::uwuw_assign_material(
6 ✔
756
  moab::EntityHandle vol_handle, std::unique_ptr<DAGCell>& c) const
757
{
758
#ifdef OPENMC_UWUW_ENABLED
759
  // lookup material in uwuw if present
760
  std::string uwuw_mat = dmd_ptr->volume_material_property_data_eh[vol_handle];
6 ✔
761
  if (uwuw_->material_library.count(uwuw_mat) != 0) {
12 !
762
    // Note: material numbers are set by UWUW
763
    int mat_number = uwuw_->material_library.get_material(uwuw_mat)
6 ✔
764
                       .metadata["mat_number"]
6 ✔
765
                       .asInt();
6 ✔
766
    c->material_.push_back(mat_number);
6 ✔
767
  } else {
UNCOV
768
    fatal_error(fmt::format("Material with value '{}' not found in the "
×
769
                            "UWUW material library",
770
      uwuw_mat));
771
  }
772
#else
773
  fatal_error("DAGMC was not configured with UWUW.");
774
#endif // OPENMC_UWUW_ENABLED
775
}
6 ✔
776

777
void DAGUniverse::override_assign_material(std::unique_ptr<DAGCell>& c,
9 ✔
778
  const MaterialOverrides& material_overrides) const
779
{
780
  // if Cell ID matches an override key, use it to override the material
781
  // assignment else if UWUW is used, get the material assignment from the DAGMC
782
  // metadata
783
  // Notify User that an override is being applied on a DAGMCCell
784
  write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8);
9 ✔
785

786
  const auto& mat_overrides = material_overrides.at(c->id_);
9 ✔
787
  if (settings::verbosity >= 10) {
9 !
UNCOV
788
    std::stringstream override_values;
×
UNCOV
789
    for (size_t i = 0; i < mat_overrides.size(); ++i) {
×
UNCOV
790
      if (i > 0) {
×
UNCOV
791
        override_values << " ";
×
792
      }
UNCOV
793
      if (mat_overrides[i] == MATERIAL_VOID) {
×
UNCOV
794
        override_values << "void";
×
795
      } else {
UNCOV
796
        override_values << mat_overrides[i];
×
797
      }
798
    }
UNCOV
799
    auto msg = fmt::format("Overriding DAGMC cell {} property 'material' "
×
800
                           "with value(s): {}",
UNCOV
801
      c->id_, override_values.str());
×
UNCOV
802
    write_message(msg, 10);
×
UNCOV
803
  }
×
804

805
  // Override the material assignment for each cell instance using the legacy
806
  // assignement
807
  for (auto mat_id : mat_overrides) {
27 ✔
808
    if (mat_id != MATERIAL_VOID &&
18 !
809
        model::material_map.find(mat_id) == model::material_map.end()) {
14 !
UNCOV
810
      fatal_error(fmt::format(
×
UNCOV
811
        "Material with ID '{}' not found for DAGMC cell {}", mat_id, c->id_));
×
812
    }
813
    c->material_.push_back(mat_id);
18 ✔
814
  }
815
}
9 ✔
816

817
//==============================================================================
818
// DAGMC Cell implementation
819
//==============================================================================
820

821
DAGCell::DAGCell(std::shared_ptr<moab::DagMC> dag_ptr, int32_t dag_idx)
176 ✔
822
  : Cell {}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) {};
176 !
823

824
std::pair<double, int32_t> DAGCell::distance(
350,506 ✔
825
  Position r, Direction u, int32_t on_surface, GeometryState* p) const
826
{
827
  // if we've changed direction or we're not on a surface,
828
  // reset the history and update last direction
829
  if (u != p->last_dir()) {
350,506 ✔
830
    p->last_dir() = u;
301,585 ✔
831
    p->history().reset();
301,585 ✔
832
  }
833
  if (on_surface == SURFACE_NONE) {
350,506 ✔
834
    p->history().reset();
281,740 ✔
835
  }
836

837
  const auto& univ = model::universes[p->lowest_coord().universe()];
350,506 !
838

839
  DAGUniverse* dag_univ = static_cast<DAGUniverse*>(univ.get());
350,506 !
840
  if (!dag_univ)
350,506 !
UNCOV
841
    fatal_error("DAGMC call made for particle in a non-DAGMC universe");
×
842

843
  // initialize to lost particle conditions
844
  int surf_idx = -1;
350,506 ✔
845
  double dist = INFINITY;
350,506 ✔
846

847
  moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_);
350,506 ✔
848
  moab::EntityHandle hit_surf;
350,506 ✔
849

850
  // create the ray
851
  double pnt[3] = {r.x, r.y, r.z};
350,506 ✔
852
  double dir[3] = {u.x, u.y, u.z};
350,506 ✔
853
  MB_CHK_ERR_CONT(
350,506 !
854
    dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist, &p->history()));
855
  if (hit_surf != 0) {
350,506 ✔
856
    surf_idx =
662,438 ✔
857
      dag_univ->surf_idx_offset_ + dagmc_ptr_->index_by_handle(hit_surf);
331,219 ✔
858
  } else if (!dagmc_ptr_->is_implicit_complement(vol) ||
38,258 ✔
859
             is_root_universe(dag_univ->id_)) {
18,971 ✔
860
    // Surface boundary conditions are ignored for projection plotting, meaning
861
    // that the particle may move through the graveyard (bounding) volume and
862
    // into the implicit complement on the other side where no intersection
863
    // will be found. A no-hit result is also expected when querying root cells
864
    // for the next boundary from undefined space, when no containing cell is
865
    // assigned. In both cases, return an infinite distance and invalid surface
866
    // index rather than marking a particle as lost.
867
    if (settings::run_mode == RunMode::PLOTTING ||
364 !
868
        p->lowest_coord().cell() == C_NONE)
364 !
869
      return {INFTY, -1};
364 ✔
870

871
    // the particle should be marked as lost immediately if an intersection
872
    // isn't found in a volume that is not the implicit complement. In the case
873
    // that the DAGMC model is the root universe of the geometry, even a missing
874
    // intersection in the implicit complement should trigger this condition.
UNCOV
875
    std::string material_id =
×
UNCOV
876
      p->material() == MATERIAL_VOID
×
877
        ? "-1 (VOID)"
UNCOV
878
        : std::to_string(model::materials[p->material()]->id());
×
UNCOV
879
    p->mark_as_lost(fmt::format(
×
UNCOV
880
      "No intersection found with DAGMC cell {}, filled with material {}", id_,
×
881
      material_id));
UNCOV
882
  }
×
883

884
  return {dist, surf_idx};
350,142 ✔
885
}
886

887
bool DAGCell::contains(Position r, Direction u, int32_t on_surface) const
8,493,232 ✔
888
{
889
  moab::ErrorCode rval;
8,493,232 ✔
890
  moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_);
8,493,232 ✔
891

892
  int result = 0;
8,493,232 ✔
893
  double pnt[3] = {r.x, r.y, r.z};
8,493,232 ✔
894
  double dir[3] = {u.x, u.y, u.z};
8,493,232 ✔
895
  rval = dagmc_ptr_->point_in_volume(vol, pnt, result, dir);
8,493,232 ✔
896
  MB_CHK_ERR_CONT(rval);
8,493,232 !
897
  return result;
8,493,232 ✔
898
}
899

900
moab::EntityHandle DAGCell::mesh_handle() const
46,716 ✔
901
{
902
  return dagmc_ptr()->entity_by_index(3, dag_index());
46,716 ✔
903
}
904

905
void DAGCell::to_hdf5_inner(hid_t group_id) const
160 ✔
906
{
907
  write_string(group_id, "geom_type", "dagmc", false);
160 ✔
908
}
160 ✔
909

UNCOV
910
BoundingBox DAGCell::bounding_box() const
×
911
{
UNCOV
912
  moab::ErrorCode rval;
×
UNCOV
913
  moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_);
×
UNCOV
914
  double min[3], max[3];
×
UNCOV
915
  rval = dagmc_ptr_->getobb(vol, min, max);
×
UNCOV
916
  MB_CHK_ERR_CONT(rval);
×
UNCOV
917
  return {{min[0], min[1], min[2]}, {max[0], max[1], max[2]}};
×
918
}
919

920
//==============================================================================
921
// DAGSurface implementation
922
//==============================================================================
923

924
DAGSurface::DAGSurface(std::shared_ptr<moab::DagMC> dag_ptr, int32_t dag_idx)
698 ✔
925
  : Surface {}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx)
698 !
926
{} // empty constructor
698 ✔
927

928
moab::EntityHandle DAGSurface::mesh_handle() const
49,474 ✔
929
{
930
  return dagmc_ptr()->entity_by_index(2, dag_index());
49,474 ✔
931
}
932

UNCOV
933
double DAGSurface::evaluate(Position r) const
×
934
{
UNCOV
935
  return 0.0;
×
936
}
937

UNCOV
938
double DAGSurface::distance(Position r, Direction u, bool coincident) const
×
939
{
UNCOV
940
  moab::ErrorCode rval;
×
UNCOV
941
  moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_);
×
UNCOV
942
  moab::EntityHandle hit_surf;
×
UNCOV
943
  double dist;
×
UNCOV
944
  double pnt[3] = {r.x, r.y, r.z};
×
UNCOV
945
  double dir[3] = {u.x, u.y, u.z};
×
UNCOV
946
  rval = dagmc_ptr_->ray_fire(surf, pnt, dir, hit_surf, dist, NULL, 0, 0);
×
UNCOV
947
  MB_CHK_ERR_CONT(rval);
×
UNCOV
948
  if (dist < 0.0)
×
UNCOV
949
    dist = INFTY;
×
UNCOV
950
  return dist;
×
951
}
952

UNCOV
953
Direction DAGSurface::normal(Position r) const
×
954
{
UNCOV
955
  moab::ErrorCode rval;
×
UNCOV
956
  moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_);
×
UNCOV
957
  double pnt[3] = {r.x, r.y, r.z};
×
UNCOV
958
  double dir[3];
×
UNCOV
959
  rval = dagmc_ptr_->get_angle(surf, pnt, dir);
×
UNCOV
960
  MB_CHK_ERR_CONT(rval);
×
UNCOV
961
  return dir;
×
962
}
963

964
Direction DAGSurface::reflect(Position r, Direction u, GeometryState* p) const
2,758 ✔
965
{
966
  assert(p);
2,758 ✔
967
  double pnt[3] = {r.x, r.y, r.z};
2,758 ✔
968
  double dir[3];
2,758 ✔
969
  moab::ErrorCode rval =
2,758 ✔
970
    dagmc_ptr_->get_angle(mesh_handle(), pnt, dir, &p->history());
2,758 ✔
971
  MB_CHK_ERR_CONT(rval);
2,758 !
972
  return u.reflect(dir);
2,758 ✔
973
}
974

975
//==============================================================================
976
// Non-member functions
977
//==============================================================================
978

979
void read_dagmc_universes(pugi::xml_node node)
890 ✔
980
{
981
  for (pugi::xml_node dag_node : node.children("dagmc_universe")) {
929 ✔
982
    model::universes.push_back(std::make_unique<DAGUniverse>(dag_node));
39 ✔
983
    model::universe_map[model::universes.back()->id_] =
39 ✔
984
      model::universes.size() - 1;
39 ✔
985
  }
986
}
890 ✔
987

988
void check_dagmc_root_univ()
891 ✔
989
{
990
  const auto& ru = model::universes[model::root_universe];
891 ✔
991
  if (ru->geom_type() == GeometryType::DAG) {
891 ✔
992
    // if the root universe contains DAGMC geometry, warn the user
993
    // if it does not contain a graveyard volume
994
    auto dag_univ = dynamic_cast<DAGUniverse*>(ru.get());
18 !
995
    if (dag_univ && !dag_univ->has_graveyard()) {
18 !
996
      warning(
2 ✔
997
        "No graveyard volume found in the DagMC model. "
998
        "This may result in lost particles and rapid simulation failure.");
999
    }
1000
  }
1001
}
891 ✔
1002

1003
int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ)
46,716 ✔
1004
{
1005
  auto surfp = dynamic_cast<DAGSurface*>(model::surfaces[surf].get());
46,716 !
1006
  auto cellp = dynamic_cast<DAGCell*>(model::cells[curr_cell].get());
46,716 !
1007
  auto univp = static_cast<DAGUniverse*>(model::universes[univ].get());
46,716 ✔
1008

1009
  moab::EntityHandle surf_handle = surfp->mesh_handle();
46,716 ✔
1010
  moab::EntityHandle curr_vol = cellp->mesh_handle();
46,716 ✔
1011

1012
  moab::EntityHandle new_vol;
46,716 ✔
1013
  moab::ErrorCode rval =
46,716 ✔
1014
    cellp->dagmc_ptr()->next_vol(surf_handle, curr_vol, new_vol);
46,716 ✔
1015
  if (rval != moab::MB_SUCCESS)
46,716 !
1016
    return -1;
1017

1018
  return univp->cell_index(new_vol);
46,716 ✔
1019
}
1020

1021
extern "C" int openmc_dagmc_universe_get_cell_ids(
11 ✔
1022
  int32_t univ_id, int32_t* ids, size_t* n)
1023
{
1024
  // make sure the universe id is a DAGMC Universe
1025
  const auto& univ = model::universes[model::universe_map[univ_id]];
11 ✔
1026
  if (univ->geom_type() != GeometryType::DAG) {
11 !
UNCOV
1027
    set_errmsg(fmt::format("Universe {} is not a DAGMC Universe", univ_id));
×
UNCOV
1028
    return OPENMC_E_INVALID_TYPE;
×
1029
  }
1030

1031
  std::vector<int32_t> dag_cell_ids;
11 ✔
1032
  for (const auto& cell_index : univ->cells_) {
51 ✔
1033
    const auto& cell = model::cells[cell_index];
40 ✔
1034
    if (cell->geom_type() == GeometryType::CSG) {
40 !
UNCOV
1035
      set_errmsg(fmt::format("Cell {} is not a DAGMC Cell", cell->id_));
×
UNCOV
1036
      return OPENMC_E_INVALID_TYPE;
×
1037
    }
1038
    dag_cell_ids.push_back(cell->id_);
40 ✔
1039
  }
1040
  std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids);
11 ✔
1041
  *n = dag_cell_ids.size();
11 ✔
1042
  return 0;
11 ✔
1043
}
11 ✔
1044

1045
extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n)
11 ✔
1046
{
1047
  // make sure the universe id is a DAGMC Universe
1048
  const auto& univ = model::universes[model::universe_map[univ_id]];
11 ✔
1049
  if (univ->geom_type() != GeometryType::DAG) {
11 !
UNCOV
1050
    set_errmsg(fmt::format("Universe {} is not a DAGMC universe", univ_id));
×
UNCOV
1051
    return OPENMC_E_INVALID_TYPE;
×
1052
  }
1053
  *n = univ->cells_.size();
11 ✔
1054
  return 0;
11 ✔
1055
}
1056

1057
} // namespace openmc
1058

1059
#else
1060

1061
namespace openmc {
1062

1063
extern "C" int openmc_dagmc_universe_get_cell_ids(
1064
  int32_t univ_id, int32_t* ids, size_t* n)
1065
{
1066
  set_errmsg("OpenMC was not configured with DAGMC");
1067
  return OPENMC_E_UNASSIGNED;
1068
};
1069

1070
extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n)
1071
{
1072
  set_errmsg("OpenMC was not configured with DAGMC");
1073
  return OPENMC_E_UNASSIGNED;
1074
};
1075

1076
void read_dagmc_universes(pugi::xml_node node)
1077
{
1078
  if (check_for_node(node, "dagmc_universe")) {
6,040 !
1079
    fatal_error("DAGMC Universes are present but OpenMC was not configured "
1080
                "with DAGMC");
1081
  }
1082
};
1083

1084
void check_dagmc_root_univ() {};
1085

1086
int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ);
1087

1088
} // namespace openmc
1089

1090
#endif // OPENMC_DAGMC_ENABLED
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc