Skip to content

Commit fc17f8a

Browse files
pshriwiseclaude
andcommitted
Add density and volume override support to DAGMC cell spec
Density overrides: - Add parse_cell_density_xml() free function (cell.h/cell.cpp), used by both CSGCell::CSGCell (replacing inline parsing) and DAGUniverse XML constructor - Add DensityOverrides type alias and thread it through initialize() and init_geometry() in DAGUniverse; apply density_mult_ in init_geometry() using the same deferred-multiplier convention as CSG cells - Remove density from the forbidden-attribute block in the DAGMC XML constructor and from DAGMCCell validation Volume overrides: - Volume is Python-only (not stored in C++ Cell); no C++ changes needed - Remove volume from forbidden-attribute checks in DAGMCCell - Add a Notes section to DAGMCCell docstring warning that manually specifying volume may be inconsistent with DAGMC's triangulated-surface geometry, which can support exact volume computation translation and rotation remain unsupported and are rejected explicitly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 704f30d commit fc17f8a

5 files changed

Lines changed: 91 additions & 28 deletions

File tree

include/openmc/cell.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ vector<int32_t> parse_cell_material_xml(pugi::xml_node node, int32_t cell_id);
162162
//! \return Vector of temperatures in Kelvin
163163
vector<double> parse_cell_temperature_xml(pugi::xml_node node, int32_t cell_id);
164164

165+
//! Parse densities (in g/cm³) from a <cell> XML node.
166+
//! Validates that all values are positive and the list is non-empty.
167+
//! \param node XML node containing a "density" attribute or child element
168+
//! \param cell_id Cell ID used in error messages
169+
//! \return Vector of densities in g/cm³
170+
vector<double> parse_cell_density_xml(pugi::xml_node node, int32_t cell_id);
171+
165172
//==============================================================================
166173

167174
class Cell {

include/openmc/dagmc.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class DAGUniverse : public Universe {
9696
public:
9797
using MaterialOverrides = std::unordered_map<int32_t, vector<int32_t>>;
9898
using TemperatureOverrides = std::unordered_map<int32_t, vector<double>>;
99+
using DensityOverrides = std::unordered_map<int32_t, vector<double>>;
99100

100101
explicit DAGUniverse(pugi::xml_node node);
101102

@@ -116,7 +117,8 @@ class DAGUniverse : public Universe {
116117
//! assignments, etc.
117118
void initialize();
118119
void initialize(const MaterialOverrides& material_overrides,
119-
const TemperatureOverrides& temperature_overrides);
120+
const TemperatureOverrides& temperature_overrides,
121+
const DensityOverrides& density_overrides = {});
120122

121123
//! Reads UWUW materials and returns an ID map
122124
void read_uwuw_materials();
@@ -194,7 +196,8 @@ class DAGUniverse : public Universe {
194196
void init_dagmc(); //!< Create and initialise DAGMC pointer
195197
void init_metadata(); //!< Create and initialise dagmcMetaData pointer
196198
void init_geometry(const MaterialOverrides& material_overrides,
197-
const TemperatureOverrides& temperature_overrides);
199+
const TemperatureOverrides& temperature_overrides,
200+
const DensityOverrides& density_overrides);
198201

199202
std::string
200203
filename_; //!< Name of the DAGMC file used to create this universe

openmc/dagmc.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,14 @@ class DAGMCCell(openmc.Cell):
617617
DAG_parent_universe : int
618618
The parent universe of the cell.
619619
620+
Notes
621+
-----
622+
DAGMC geometries are composed of triangulated surfaces, which means cell
623+
volumes can in principle be computed exactly (e.g. via mesh-based
624+
integration). Manually specifying :attr:`volume` overrides any such
625+
calculation and may introduce inconsistencies if the value does not
626+
accurately reflect the true geometric volume.
627+
620628
"""
621629
def __init__(self, cell_id=None, name='', fill=None):
622630
super().__init__(cell_id, name, fill, None)
@@ -656,11 +664,16 @@ def create_xml_subelement(self, xml_element, memo=None):
656664
):
657665
raise TypeError("DAGMC cell temperature overrides require a "
658666
"material fill.")
667+
if self.density is not None and self.fill_type not in (
668+
'material', 'distribmat'
669+
):
670+
raise TypeError("DAGMC cell density overrides require a "
671+
"material fill.")
659672
if any(getattr(self, attr) is not None for attr in (
660-
'density', 'translation', 'rotation', 'volume'
673+
'translation', 'rotation'
661674
)):
662-
raise TypeError("DAGMC cell overrides currently only support "
663-
"material fills.")
675+
raise TypeError("DAGMC cell overrides do not support translation "
676+
"or rotation.")
664677
return super().create_xml_subelement(xml_element, memo)
665678

666679
@classmethod
@@ -687,11 +700,11 @@ def from_xml_element(cls, elem, mats):
687700
if get_text(elem, tag) is not None:
688701
raise ValueError(
689702
f"DAGMC cell {cell_id} override cannot specify '{tag}'.")
690-
for tag in ('density', 'translation', 'rotation', 'volume'):
703+
for tag in ('translation', 'rotation'):
691704
if get_text(elem, tag) is not None:
692705
raise ValueError(
693-
f"DAGMC cell {cell_id} override currently only supports "
694-
f"material fills (found unsupported '{tag}').")
706+
f"DAGMC cell {cell_id} override does not support "
707+
f"'{tag}'.")
695708
if get_elem_list(elem, 'material', str) is None:
696709
raise ValueError(
697710
f"DAGMC cell {cell_id} must specify a material override.")

src/cell.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,23 @@ vector<double> parse_cell_temperature_xml(pugi::xml_node node, int32_t cell_id)
379379
return temperatures;
380380
}
381381

382+
vector<double> parse_cell_density_xml(pugi::xml_node node, int32_t cell_id)
383+
{
384+
auto densities = get_node_array<double>(node, "density");
385+
if (densities.empty()) {
386+
fatal_error(fmt::format(
387+
"An empty density element was specified for cell {}", cell_id));
388+
}
389+
for (auto rho : densities) {
390+
if (rho <= 0) {
391+
fatal_error(fmt::format(
392+
"Cell {} was specified with a density less than or equal to zero",
393+
cell_id));
394+
}
395+
}
396+
return densities;
397+
}
398+
382399
//==============================================================================
383400
// CSGCell implementation
384401
//==============================================================================
@@ -457,7 +474,7 @@ CSGCell::CSGCell(pugi::xml_node cell_node)
457474
// Note: calculating the actual density multiplier is deferred until materials
458475
// are finalized. density_mult_ contains the true density in the meantime.
459476
if (check_for_node(cell_node, "density")) {
460-
density_mult_ = get_node_array<double>(cell_node, "density");
477+
density_mult_ = parse_cell_density_xml(cell_node, id_);
461478
density_mult_.shrink_to_fit();
462479

463480
// Make sure this is a material-filled cell.
@@ -478,15 +495,6 @@ CSGCell::CSGCell(pugi::xml_node cell_node)
478495
id_));
479496
}
480497
}
481-
482-
// Make sure all densities are non-negative and greater than zero.
483-
for (auto rho : density_mult_) {
484-
if (rho <= 0) {
485-
fatal_error(fmt::format(
486-
"Cell {} was specified with a density less than or equal to zero",
487-
id_));
488-
}
489-
}
490498
}
491499

492500
// Read the region specification.

src/dagmc.cpp

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node)
5252
{
5353
MaterialOverrides material_overrides;
5454
TemperatureOverrides temperature_overrides;
55+
DensityOverrides density_overrides;
5556

5657
if (check_for_node(node, "id")) {
5758
id_ = std::stoi(get_node_value(node, "id"));
@@ -102,12 +103,10 @@ DAGUniverse::DAGUniverse(pugi::xml_node node)
102103
fatal_error(fmt::format(
103104
"DAGMC cell {} override cannot specify a universe.", cell_id));
104105
}
105-
if (check_for_node(cell_node, "density") ||
106-
check_for_node(cell_node, "translation") ||
107-
check_for_node(cell_node, "rotation") ||
108-
check_for_node(cell_node, "volume")) {
106+
if (check_for_node(cell_node, "translation") ||
107+
check_for_node(cell_node, "rotation")) {
109108
fatal_error(fmt::format(
110-
"DAGMC cell {} override currently only supports material fills.",
109+
"DAGMC cell {} override does not support translation or rotation.",
111110
cell_id));
112111
}
113112
if (!check_for_node(cell_node, "material")) {
@@ -126,14 +125,19 @@ DAGUniverse::DAGUniverse(pugi::xml_node node)
126125
temperature_overrides.emplace(
127126
cell_id, parse_cell_temperature_xml(cell_node, cell_id));
128127
}
128+
129+
if (check_for_node(cell_node, "density")) {
130+
density_overrides.emplace(
131+
cell_id, parse_cell_density_xml(cell_node, cell_id));
132+
}
129133
}
130134
} else if (check_for_node(node, "material_overrides")) {
131135
fatal_error(
132136
"DAGMCUniverse <material_overrides> is no longer supported. Use nested "
133137
"<cell> elements under <dagmc_universe> instead.");
134138
}
135139

136-
initialize(material_overrides, temperature_overrides);
140+
initialize(material_overrides, temperature_overrides, density_overrides);
137141
}
138142

139143
DAGUniverse::DAGUniverse(
@@ -152,9 +156,10 @@ DAGUniverse::DAGUniverse(std::shared_ptr<moab::DagMC> dagmc_ptr,
152156
{
153157
MaterialOverrides material_overrides;
154158
TemperatureOverrides temperature_overrides;
159+
DensityOverrides density_overrides;
155160
set_id();
156161
init_metadata();
157-
init_geometry(material_overrides, temperature_overrides);
162+
init_geometry(material_overrides, temperature_overrides, density_overrides);
158163
}
159164

160165
void DAGUniverse::set_id()
@@ -179,7 +184,8 @@ void DAGUniverse::initialize()
179184
}
180185

181186
void DAGUniverse::initialize(const MaterialOverrides& material_overrides,
182-
const TemperatureOverrides& temperature_overrides)
187+
const TemperatureOverrides& temperature_overrides,
188+
const DensityOverrides& density_overrides)
183189
{
184190
#ifdef OPENMC_UWUW_ENABLED
185191
// read uwuw materials from the .h5m file if present
@@ -190,7 +196,7 @@ void DAGUniverse::initialize(const MaterialOverrides& material_overrides,
190196

191197
init_metadata();
192198

193-
init_geometry(material_overrides, temperature_overrides);
199+
init_geometry(material_overrides, temperature_overrides, density_overrides);
194200
}
195201

196202
void DAGUniverse::init_dagmc()
@@ -227,7 +233,8 @@ void DAGUniverse::init_metadata()
227233
}
228234

229235
void DAGUniverse::init_geometry(const MaterialOverrides& material_overrides,
230-
const TemperatureOverrides& temperature_overrides)
236+
const TemperatureOverrides& temperature_overrides,
237+
const DensityOverrides& density_overrides)
231238
{
232239
moab::ErrorCode rval;
233240

@@ -324,6 +331,31 @@ void DAGUniverse::init_geometry(const MaterialOverrides& material_overrides,
324331
}
325332
}
326333

334+
if (density_overrides.count(c->id_)) {
335+
if (c->material_.empty() || c->material_[0] == MATERIAL_VOID) {
336+
fatal_error(fmt::format("DAGMC cell {} was specified with a density "
337+
"but no non-void material.",
338+
c->id_));
339+
}
340+
// density_mult_ holds the true density until materials are finalized,
341+
// at which point it is converted to a proper multiplier (same as CSG).
342+
c->density_mult_ = density_overrides.at(c->id_);
343+
344+
if (settings::verbosity >= 10) {
345+
const auto& dens = density_overrides.at(c->id_);
346+
std::stringstream override_values;
347+
for (size_t i = 0; i < dens.size(); ++i) {
348+
if (i > 0)
349+
override_values << " ";
350+
override_values << dens[i];
351+
}
352+
write_message(fmt::format("Overriding DAGMC cell {} property "
353+
"'density [g/cm³]' with value(s): {}",
354+
c->id_, override_values.str()),
355+
10);
356+
}
357+
}
358+
327359
// check for temperature assignment
328360
std::string temp_value;
329361

0 commit comments

Comments
 (0)