Skip to content

Commit 597b11c

Browse files
committed
Changing over to the cell subelement approach for DAGMC universes
1 parent 53d98ce commit 597b11c

5 files changed

Lines changed: 254 additions & 78 deletions

File tree

include/openmc/dagmc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ class DAGUniverse : public Universe {
202202
bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard"
203203
//!< volume
204204
std::unordered_map<int32_t, vector<int32_t>>
205-
material_overrides_; //!< Map of material overrides
205+
material_overrides_; //!< Map of DAGMC cell material overrides
206206
//!< keys correspond to the DAGMCCell id
207207
//!< values are a list of material ids used
208208
//!< for the override

openmc/dagmc.py

Lines changed: 91 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ class DAGMCUniverse(openmc.UniverseBase):
3737
Set IDs automatically on initialization (True) or report overlaps in ID
3838
space between OpenMC and UWUW materials (False)
3939
material_overrides : dict, optional
40-
A dictionary of material overrides. The keys are material name strings
41-
and the values are Iterables of openmc.Material objects. If a material
42-
name is found in the DAGMC file, the material will be replaced with the
43-
openmc.Material object in the value.
40+
A dictionary of material overrides. Keys are cell IDs (or
41+
:class:`openmc.DAGMCCell` objects), and values are
42+
:class:`openmc.Material` objects or iterables of
43+
:class:`openmc.Material` objects.
4444
4545
Attributes
4646
----------
@@ -130,6 +130,8 @@ def filename(self, val: cv.PathLike):
130130

131131
@property
132132
def material_overrides(self):
133+
if self.cells:
134+
return self._get_cell_material_overrides()
133135
return self._material_overrides
134136

135137
@material_overrides.setter
@@ -201,7 +203,11 @@ def add_material_override(self, key, overrides=None):
201203
if key not in self.cells:
202204
raise ValueError(f"Cell ID '{key}' not found in DAGMC universe")
203205

204-
self._material_overrides[key] = overrides
206+
self._material_overrides[key] = list(overrides)
207+
if len(overrides) == 1:
208+
self.cells[key].fill = overrides[0]
209+
else:
210+
self.cells[key].fill = list(overrides)
205211

206212
@property
207213
def auto_geom_ids(self):
@@ -281,6 +287,21 @@ def n_cells(self):
281287
def n_surfaces(self):
282288
return self._n_geom_elements('surface')
283289

290+
def _get_cell_material_overrides(self):
291+
overrides = {}
292+
for cell in self.cells.values():
293+
if cell.fill_type == 'material':
294+
overrides[cell.id] = [cell.fill]
295+
elif cell.fill_type == 'distribmat':
296+
overrides[cell.id] = list(cell.fill)
297+
elif cell.fill_type == 'void':
298+
overrides[cell.id] = [None]
299+
else:
300+
raise ValueError(
301+
"Only material fills are supported for DAGMC cell "
302+
"overrides.")
303+
return overrides
304+
284305
def create_xml_subelement(self, xml_element, memo=None):
285306
if memo is None:
286307
memo = set()
@@ -290,12 +311,6 @@ def create_xml_subelement(self, xml_element, memo=None):
290311

291312
memo.add(self)
292313

293-
# Ensure that the material overrides are up-to-date
294-
for cell in self.cells.values():
295-
if cell.fill is None:
296-
continue
297-
self.add_material_override(cell, cell.fill)
298-
299314
# Set xml element values
300315
dagmc_element = ET.Element('dagmc_universe')
301316
dagmc_element.set('id', str(self.id))
@@ -307,17 +322,11 @@ def create_xml_subelement(self, xml_element, memo=None):
307322
if self.auto_mat_ids:
308323
dagmc_element.set('auto_mat_ids', 'true')
309324
dagmc_element.set('filename', str(self.filename))
310-
if self._material_overrides:
311-
mat_element = ET.Element('material_overrides')
312-
for key in self._material_overrides:
313-
cell_overrides = ET.Element('cell_override')
314-
cell_overrides.set("id", str(key))
315-
material_element = ET.Element('material_ids')
316-
material_element.text = ' '.join(
317-
str(t.id) for t in self._material_overrides[key])
318-
cell_overrides.append(material_element)
319-
mat_element.append(cell_overrides)
320-
dagmc_element.append(mat_element)
325+
if self.cells:
326+
self._material_overrides = self._get_cell_material_overrides()
327+
for cell in self.cells.values():
328+
cell_element = cell.create_xml_subelement(xml_element, memo)
329+
dagmc_element.append(cell_element)
321330
xml_element.append(dagmc_element)
322331

323332
def bounding_region(
@@ -442,7 +451,7 @@ def from_hdf5(cls, group):
442451
return out
443452

444453
@classmethod
445-
def from_xml_element(cls, elem, mats = None):
454+
def from_xml_element(cls, elem, mats=None):
446455
"""Generate DAGMC universe from XML element
447456
448457
Parameters
@@ -471,21 +480,59 @@ def from_xml_element(cls, elem, mats = None):
471480
out.auto_geom_ids = bool(get_text(elem, "auto_geom_ids"))
472481
out.auto_mat_ids = bool(get_text(elem, "auto_mat_ids"))
473482

474-
el_mat_override = elem.find('material_overrides')
475-
if el_mat_override is not None:
476-
if mats is None:
477-
raise ValueError("Material overrides found in DAGMC universe "
478-
"but no materials were provided to populate "
479-
"the mapping.")
480-
out._material_overrides = {}
481-
for elem in el_mat_override.findall('cell_override'):
482-
cell_id = int(get_text(elem, 'id'))
483-
mat_ids = get_elem_list(elem, "material_ids", str) or []
484-
mat_objs = [mats[mat_id] for mat_id in mat_ids]
485-
out._material_overrides[cell_id] = mat_objs
483+
if elem.find('material_overrides') is not None:
484+
raise ValueError(
485+
"DAGMCUniverse <material_overrides> is no longer supported. "
486+
"Use nested <cell> elements under <dagmc_universe> instead.")
487+
488+
if elem.find('cell') is not None:
489+
out._parse_cell_overrides(elem, mats)
486490

487491
return out
488492

493+
def _parse_cell_overrides(self, elem, mats):
494+
if mats is None:
495+
raise ValueError("DAGMC cell overrides found in DAGMC universe but "
496+
"no materials were provided to populate the "
497+
"mapping.")
498+
499+
self._material_overrides = {}
500+
for cell_elem in elem.findall('cell'):
501+
cell_id = int(get_text(cell_elem, 'id'))
502+
name = get_text(cell_elem, 'name')
503+
504+
if get_text(cell_elem, 'region') is not None:
505+
raise ValueError("DAGMC cell overrides cannot include a region.")
506+
if get_text(cell_elem, 'fill') is not None:
507+
raise ValueError("DAGMC cell overrides currently only support "
508+
"material fills.")
509+
if get_text(cell_elem, 'universe') is not None:
510+
raise ValueError("DAGMC cell overrides cannot specify a "
511+
"universe.")
512+
for tag in ('temperature', 'density', 'translation', 'rotation', 'volume'):
513+
if get_text(cell_elem, tag) is not None:
514+
raise ValueError(
515+
"DAGMC cell overrides currently only support material "
516+
f"fills (found unsupported '{tag}' for cell {cell_id}).")
517+
518+
mat_ids = get_elem_list(cell_elem, 'material', str)
519+
if mat_ids is None:
520+
raise ValueError(
521+
f"DAGMC cell {cell_id} must specify a material override.")
522+
523+
mat_objs = [mats[mat_id] for mat_id in mat_ids]
524+
if len(mat_objs) == 1:
525+
fill = mat_objs[0]
526+
else:
527+
fill = mat_objs
528+
529+
if cell_id in self.cells:
530+
raise ValueError(
531+
f"Duplicate DAGMC cell override specified for cell {cell_id}.")
532+
self.add_cell(openmc.DAGMCCell(
533+
cell_id=cell_id, name=name or '', fill=fill))
534+
self._material_overrides[cell_id] = mat_objs
535+
489536
def _partial_deepcopy(self):
490537
"""Clone all of the openmc.DAGMCUniverse object's attributes except for
491538
its cells, as they are copied within the clone function. This should
@@ -625,7 +672,15 @@ def plot(self, *args, **kwargs):
625672
raise TypeError("plot is not available for DAGMC cells.")
626673

627674
def create_xml_subelement(self, xml_element, memo=None):
628-
raise TypeError("create_xml_subelement is not available for DAGMC cells.")
675+
if self.fill_type not in ('void', 'material', 'distribmat'):
676+
raise TypeError("DAGMC cell overrides currently only support "
677+
"material fills.")
678+
if any(getattr(self, attr) is not None for attr in (
679+
'temperature', 'density', 'translation', 'rotation', 'volume'
680+
)):
681+
raise TypeError("DAGMC cell overrides currently only support "
682+
"material fills.")
683+
return super().create_xml_subelement(xml_element, memo)
629684

630685
@classmethod
631686
def from_xml_element(cls, elem, surfaces, materials, get_universe):

openmc/model/model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ def sync_dagmc_universes(self):
427427
This method iterates over all DAGMC universes in the geometry and
428428
synchronizes their cells with the current material assignments. Requires
429429
that the model has been initialized via :meth:`Model.init_lib`.
430+
Synchronized DAGMC cells can then be edited and exported as nested
431+
`<cell>` overrides inside each `<dagmc_universe>` element.
430432
431433
.. versionadded:: 0.15.1
432434

src/dagmc.cpp

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,71 @@ DAGUniverse::DAGUniverse(pugi::xml_node node)
7676
adjust_material_ids_ = get_node_value_bool(node, "auto_mat_ids");
7777
}
7878

79-
// get material assignment overloading
80-
if (check_for_node(node, "material_overrides")) {
81-
auto mat_node = node.child("material_overrides");
82-
// loop over all subelements (each subelement corresponds to a material)
83-
for (pugi::xml_node cell_node : mat_node.children("cell_override")) {
84-
// Store assignment reference name
85-
int32_t ref_assignment = std::stoi(get_node_value(cell_node, "id"));
86-
87-
// Get mat name for each assignement instances
88-
vector<int32_t> instance_mats =
89-
get_node_array<int32_t>(cell_node, "material_ids");
90-
91-
// Store mat name for each instances
92-
material_overrides_.emplace(ref_assignment, instance_mats);
79+
// Get material assignment overrides from nested DAGMC cell elements.
80+
if (node.child("cell")) {
81+
for (pugi::xml_node cell_node : node.children("cell")) {
82+
if (!check_for_node(cell_node, "id")) {
83+
fatal_error(
84+
"Must specify id for each DAGMC cell override in <dagmc_universe>.");
85+
}
86+
87+
int32_t cell_id = std::stoi(get_node_value(cell_node, "id"));
88+
89+
if (check_for_node(cell_node, "region")) {
90+
fatal_error(fmt::format(
91+
"DAGMC cell {} override cannot specify a region.", cell_id));
92+
}
93+
if (check_for_node(cell_node, "fill")) {
94+
fatal_error(fmt::format(
95+
"DAGMC cell {} override currently only supports material fills.",
96+
cell_id));
97+
}
98+
if (check_for_node(cell_node, "universe")) {
99+
fatal_error(fmt::format(
100+
"DAGMC cell {} override cannot specify a universe.", cell_id));
101+
}
102+
if (check_for_node(cell_node, "temperature") ||
103+
check_for_node(cell_node, "density") ||
104+
check_for_node(cell_node, "translation") ||
105+
check_for_node(cell_node, "rotation") ||
106+
check_for_node(cell_node, "volume")) {
107+
fatal_error(fmt::format(
108+
"DAGMC cell {} override currently only supports material fills.",
109+
cell_id));
110+
}
111+
if (!check_for_node(cell_node, "material")) {
112+
fatal_error(fmt::format(
113+
"DAGMC cell {} override must specify material.", cell_id));
114+
}
115+
116+
vector<std::string> mats =
117+
get_node_array<std::string>(cell_node, "material", true);
118+
if (mats.empty()) {
119+
fatal_error(fmt::format(
120+
"DAGMC cell {} override has an empty material specification.",
121+
cell_id));
122+
}
123+
124+
vector<int32_t> override_mats;
125+
override_mats.reserve(mats.size());
126+
for (const auto& mat : mats) {
127+
if (mat == "void") {
128+
override_mats.push_back(MATERIAL_VOID);
129+
} else {
130+
override_mats.push_back(std::stoi(mat));
131+
}
132+
}
133+
134+
auto inserted = material_overrides_.emplace(cell_id, override_mats);
135+
if (!inserted.second) {
136+
fatal_error(fmt::format(
137+
"Duplicate DAGMC cell override specified for cell {}", cell_id));
138+
}
93139
}
140+
} else if (check_for_node(node, "material_overrides")) {
141+
fatal_error(
142+
"DAGMCUniverse <material_overrides> is no longer supported. Use nested "
143+
"<cell> elements under <dagmc_universe> instead.");
94144
}
95145

96146
initialize();
@@ -230,17 +280,15 @@ void DAGUniverse::init_geometry()
230280
if (mat_str == "graveyard") {
231281
graveyard = vol_handle;
232282
}
233-
// material void checks
234-
if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") {
283+
if (material_overrides_.count(c->id_)) {
284+
override_assign_material(c);
285+
} else if (mat_str == "void" || mat_str == "vacuum" ||
286+
mat_str == "graveyard") {
235287
c->material_.push_back(MATERIAL_VOID);
288+
} else if (uses_uwuw()) {
289+
uwuw_assign_material(vol_handle, c);
236290
} else {
237-
if (material_overrides_.count(c->id_)) {
238-
override_assign_material(c);
239-
} else if (uses_uwuw()) {
240-
uwuw_assign_material(vol_handle, c);
241-
} else {
242-
legacy_assign_material(mat_str, c);
243-
}
291+
legacy_assign_material(mat_str, c);
244292
}
245293

246294
// check for temperature assignment
@@ -638,17 +686,30 @@ void DAGUniverse::override_assign_material(std::unique_ptr<DAGCell>& c) const
638686
// Notify User that an override is being applied on a DAGMCCell
639687
write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8);
640688

689+
const auto& mat_overrides = material_overrides_.at(c->id_);
641690
if (settings::verbosity >= 10) {
642-
auto msg = fmt::format("Assigning DAGMC cell {} material(s) based on "
643-
"override information (see input XML).",
644-
c->id_);
691+
std::stringstream override_values;
692+
for (size_t i = 0; i < mat_overrides.size(); ++i) {
693+
if (i > 0) {
694+
override_values << " ";
695+
}
696+
if (mat_overrides[i] == MATERIAL_VOID) {
697+
override_values << "void";
698+
} else {
699+
override_values << mat_overrides[i];
700+
}
701+
}
702+
auto msg = fmt::format("Overriding DAGMC cell {} property 'material' "
703+
"with value(s): {}",
704+
c->id_, override_values.str());
645705
write_message(msg, 10);
646706
}
647707

648708
// Override the material assignment for each cell instance using the legacy
649709
// assignement
650-
for (auto mat_id : material_overrides_.at(c->id_)) {
651-
if (model::material_map.find(mat_id) == model::material_map.end()) {
710+
for (auto mat_id : mat_overrides) {
711+
if (mat_id != MATERIAL_VOID &&
712+
model::material_map.find(mat_id) == model::material_map.end()) {
652713
fatal_error(fmt::format(
653714
"Material with ID '{}' not found for DAGMC cell {}", mat_id, c->id_));
654715
}

0 commit comments

Comments
 (0)