Skip to content

Commit 53cc879

Browse files
pshriwiseclaude
andcommitted
Add backward compatibility for deprecated <material_overrides> XML schema
Both Python and C++ now accept the old <material_overrides> format with a DeprecationWarning/warning() instead of a hard error, converting each <cell_override> into an equivalent DAGMCCell or MaterialOverrides entry. - Python: replace the ValueError in DAGMCUniverse.from_xml_element with a DeprecationWarning; add _parse_legacy_material_overrides() which builds DAGMCCell objects from <cell_override id> / <material_ids> text - C++: replace the fatal_error in DAGUniverse XML constructor with a warning() + conversion loop into material_overrides map - Both sides raise an error if <material_overrides> and <cell> appear together on the same <dagmc_universe> - Replace test_dagmc_xml_reject_legacy_material_overrides with six new tests covering single-material, distribmat, void, both-raises, deprecation-warning, and round-trip-to-new-format scenarios Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e032cdc commit 53cc879

3 files changed

Lines changed: 121 additions & 12 deletions

File tree

openmc/dagmc.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -480,16 +480,43 @@ def from_xml_element(cls, elem, mats=None):
480480
out.auto_geom_ids = bool(get_text(elem, "auto_geom_ids"))
481481
out.auto_mat_ids = bool(get_text(elem, "auto_mat_ids"))
482482

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.")
483+
has_overrides = elem.find('material_overrides') is not None
484+
has_cells = elem.find('cell') is not None
487485

488-
if elem.find('cell') is not None:
486+
if has_overrides and has_cells:
487+
raise ValueError(
488+
"DAGMCUniverse cannot specify both <material_overrides> and "
489+
"<cell> sub-elements. Use <cell> elements only.")
490+
491+
if has_overrides:
492+
warnings.warn(
493+
"DAGMCUniverse <material_overrides> is deprecated and will be "
494+
"removed in a future version. Use nested <cell> elements "
495+
"instead.", DeprecationWarning, stacklevel=2)
496+
out._parse_legacy_material_overrides(elem, mats)
497+
elif has_cells:
489498
out._parse_cell_overrides(elem, mats)
490499

491500
return out
492501

502+
def _parse_legacy_material_overrides(self, elem, mats):
503+
"""Parse the deprecated <material_overrides> XML format and populate
504+
the universe with equivalent DAGMCCell objects."""
505+
if mats is None:
506+
raise ValueError(
507+
"DAGMC material overrides found but no materials were "
508+
"provided to populate the mapping.")
509+
mo_elem = elem.find('material_overrides')
510+
for co_elem in mo_elem.findall('cell_override'):
511+
cell_id = int(get_text(co_elem, 'id'))
512+
mat_ids = co_elem.find('material_ids').text.split()
513+
fill_objs = [mats[mid] for mid in mat_ids]
514+
fill = fill_objs[0] if len(fill_objs) == 1 else fill_objs
515+
if cell_id in self.cells:
516+
raise ValueError(
517+
f"Duplicate DAGMC cell override specified for cell {cell_id}.")
518+
self.add_cell(DAGMCCell(cell_id=cell_id, fill=fill))
519+
493520
def _parse_cell_overrides(self, elem, mats):
494521
if mats is None:
495522
raise ValueError("DAGMC cell overrides found in DAGMC universe but "

src/dagmc.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,22 @@ DAGUniverse::DAGUniverse(pugi::xml_node node)
132132
}
133133
}
134134
} else if (check_for_node(node, "material_overrides")) {
135-
fatal_error(
136-
"DAGMCUniverse <material_overrides> is no longer supported. Use nested "
137-
"<cell> elements under <dagmc_universe> instead.");
135+
if (node.child("cell")) {
136+
fatal_error("DAGMCUniverse cannot specify both <material_overrides> and "
137+
"<cell> sub-elements. Use <cell> elements only.");
138+
}
139+
warning("DAGMCUniverse <material_overrides> is deprecated. Use nested "
140+
"<cell> elements under <dagmc_universe> instead.");
141+
for (pugi::xml_node co :
142+
node.child("material_overrides").children("cell_override")) {
143+
int32_t cell_id = std::stoi(get_node_value(co, "id"));
144+
std::istringstream iss(co.child("material_ids").text().get());
145+
vector<int32_t> mats;
146+
for (std::string s; iss >> s;) {
147+
mats.push_back(s == "void" ? MATERIAL_VOID : std::stoi(s));
148+
}
149+
material_overrides.emplace(cell_id, mats);
150+
}
138151
}
139152

140153
initialize(material_overrides, temperature_overrides, density_overrides);

tests/unit_tests/dagmc/test_model.py

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,19 +319,88 @@ def test_dagmc_xml_reject_region_override():
319319
openmc.DAGMCUniverse.from_xml_element(elem, mats)
320320

321321

322-
def test_dagmc_xml_reject_legacy_material_overrides():
323-
mats = {'1': openmc.Material(1), 'void': None}
322+
def _legacy_xml(cell_overrides):
323+
"""Helper to build a <dagmc_universe> with old-format <material_overrides>."""
324+
inner = ''.join(
325+
f'<cell_override id="{cid}"><material_ids>{mids}</material_ids></cell_override>'
326+
for cid, mids in cell_overrides.items()
327+
)
328+
return ET.fromstring(
329+
f'<dagmc_universe id="1" filename="dagmc.h5m">'
330+
f'<material_overrides>{inner}</material_overrides>'
331+
f'</dagmc_universe>'
332+
)
333+
334+
335+
def test_dagmc_xml_legacy_single_material_compat():
336+
mat = openmc.Material(1)
337+
mats = {'1': mat, 'void': None}
338+
elem = _legacy_xml({3: '1'})
339+
with pytest.warns(DeprecationWarning, match="deprecated"):
340+
univ = openmc.DAGMCUniverse.from_xml_element(elem, mats)
341+
assert 3 in univ.cells
342+
assert univ.cells[3].fill is mat
343+
344+
345+
def test_dagmc_xml_legacy_distribmat_compat():
346+
mat1, mat2 = openmc.Material(2), openmc.Material(3)
347+
mats = {'2': mat1, '3': mat2, 'void': None}
348+
elem = _legacy_xml({5: '2 3'})
349+
with pytest.warns(DeprecationWarning):
350+
univ = openmc.DAGMCUniverse.from_xml_element(elem, mats)
351+
assert univ.cells[5].fill_type == 'distribmat'
352+
assert list(univ.cells[5].fill) == [mat1, mat2]
353+
354+
355+
def test_dagmc_xml_legacy_void_compat():
356+
mats = {'void': None}
357+
elem = _legacy_xml({7: 'void'})
358+
with pytest.warns(DeprecationWarning):
359+
univ = openmc.DAGMCUniverse.from_xml_element(elem, mats)
360+
assert univ.cells[7].fill_type == 'void'
361+
362+
363+
def test_dagmc_xml_legacy_both_raises():
364+
mat = openmc.Material(1)
365+
mats = {'1': mat, 'void': None}
324366
elem = ET.fromstring(
325367
'<dagmc_universe id="1" filename="dagmc.h5m">'
326368
'<material_overrides>'
327-
'<cell_override id="1"><material_ids>1</material_ids></cell_override>'
369+
'<cell_override id="3"><material_ids>1</material_ids></cell_override>'
328370
'</material_overrides>'
371+
'<cell id="5" material="1"/>'
329372
'</dagmc_universe>'
330373
)
331-
with pytest.raises(ValueError, match="no longer supported"):
374+
with pytest.raises(ValueError, match="both"):
332375
openmc.DAGMCUniverse.from_xml_element(elem, mats)
333376

334377

378+
def test_dagmc_xml_legacy_deprecation_warning():
379+
mats = {'1': openmc.Material(1), 'void': None}
380+
elem = _legacy_xml({3: '1'})
381+
with pytest.warns(DeprecationWarning):
382+
openmc.DAGMCUniverse.from_xml_element(elem, mats)
383+
384+
385+
def test_dagmc_xml_legacy_roundtrip():
386+
"""Old-format XML loads correctly and re-exports using the new <cell> format."""
387+
mat = openmc.Material(1)
388+
mats = {'1': mat, 'void': None}
389+
elem = _legacy_xml({3: '1'})
390+
with pytest.warns(DeprecationWarning):
391+
univ = openmc.DAGMCUniverse.from_xml_element(elem, mats)
392+
393+
root = ET.Element('geometry')
394+
univ.create_xml_subelement(root)
395+
dagmc_elem = root.find('dagmc_universe')
396+
397+
assert dagmc_elem.find('material_overrides') is None
398+
cell_elems = dagmc_elem.findall('cell')
399+
assert len(cell_elems) == 1
400+
assert int(cell_elems[0].get('id')) == 3
401+
assert cell_elems[0].get('material') == '1'
402+
403+
335404
def test_dagmc_xml_temperature_roundtrip():
336405
mat = openmc.Material(1)
337406
mats = {'1': mat, 'void': None}

0 commit comments

Comments
 (0)