From 903d11454c100500619fd4b20e9b6e5bf23e0c83 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 11 Jun 2024 08:46:48 +0200 Subject: [PATCH 001/122] start commenting --- openmc/universe.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openmc/universe.py b/openmc/universe.py index 9fab9ae51b6..75f92ec23ef 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -1107,6 +1107,7 @@ def from_xml_element(cls, elem): return out + # Need to change this to enable coppy of hte ids... def _partial_deepcopy(self): """Clone all of the openmc.DAGMCUniverse object's attributes except for its cells, as they are copied within the clone function. This should From 66a2be429df74cc24ac41467481347feac9528c7 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 20 Jun 2024 15:59:06 +0200 Subject: [PATCH 002/122] test --- include/openmc/dagmc.h | 1 + openmc/cell.py | 3 +++ openmc/geometry.py | 22 ++++++++++++++++++++++ openmc/model/model.py | 3 +++ openmc/universe.py | 25 ++++++++++++++++++++++--- src/dagmc.cpp | 35 +++++++++++++++++++++++++++++++++-- src/geometry_aux.cpp | 2 ++ 7 files changed, 86 insertions(+), 5 deletions(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 2facf4fc05e..116a469ba72 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -184,6 +184,7 @@ class DAGUniverse : public Universe { //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume + std::map> instance_mat_assignment; }; //============================================================================== diff --git a/openmc/cell.py b/openmc/cell.py index fe3939bbe39..9f328ba3279 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -175,6 +175,9 @@ def fill(self, fill): f'non-Material or Universe fill "{fill}"') raise ValueError(msg) self._fill = fill + if isinstance(self.fill, openmc.DAGMCUniverse): + self.fill._num_instances += 1 + print("DAGUNIVERSE", self.fill._num_instances) # Info about atom content can now be invalid # (since fill has just changed) diff --git a/openmc/geometry.py b/openmc/geometry.py index 6cce4c18c70..bfca6825159 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -404,6 +404,25 @@ def get_all_nuclides(self) -> list[str]: for material in self.get_all_materials().values(): all_nuclides |= set(material.get_nuclides()) return sorted(all_nuclides) + + def get_all_dag_universes(self) -> typing.Dict[int, openmc.Universe]: + """Return all universes in the geometry. + + Returns + ------- + dict + Dictionary mapping universe IDs to :class:`openmc.Universe` + instances + + """ + universes = {} + universes[self.root_universe.id] = self.root_universe + universes.update(self.root_universe.get_all_universes()) + dag_universes = {} + for id, uni in dag_universes.items(): + if isinstance(uni, openmc.DAGMCUniverse): + dag_universes[id] = uni + return dag_universes def get_all_materials(self) -> dict[int, openmc.Material]: """Return all materials within the geometry. @@ -737,6 +756,9 @@ def determine_paths(self, instances_only=False): for material in self.get_all_materials().values(): material._paths = [] material._num_instances = 0 + for dag_uni in self.get_all_dag_universes().values(): + dag_uni._paths = [] + dag_uni._num_instances = 0 # Recursively traverse the CSG tree to count all cell instances self.root_universe._determine_paths(instances_only=instances_only) diff --git a/openmc/model/model.py b/openmc/model/model.py index 2ea579ab7df..f4d5afc514c 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1041,6 +1041,9 @@ def differentiate_depletable_mats(self, diff_volume_method: str): f"material with ID={mat.id}.") mat.volume /= mat.num_instances + for dag_uni in self.geometry.get_all_dag_universes().values(): + print(dag_uni.num_instances()) + if distribmats: # Assign distribmats to cells for cell in self.geometry.get_all_material_cells().values(): diff --git a/openmc/universe.py b/openmc/universe.py index 75f92ec23ef..2f02c8bd8d3 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -717,8 +717,10 @@ def _determine_paths(self, path='', instances_only=False): # If universe-filled, recursively count cells in filling universe if fill_type == 'universe': - fill._determine_paths(cell_path + '->', instances_only) - + if isinstance(fill, openmc.DAGMCUniverse): + print("DAGUNIVERSE determine", fill._num_instances) + else: + fill._determine_paths(cell_path + '->', instances_only) # If lattice-filled, recursively call for all universes in lattice elif fill_type == 'lattice': latt = fill @@ -823,12 +825,15 @@ def __init__(self, universe_id=None, name='', auto_geom_ids=False, - auto_mat_ids=False): + auto_mat_ids=False, + mat_assignment={}): super().__init__(universe_id, name) # Initialize class attributes self.filename = filename self.auto_geom_ids = auto_geom_ids self.auto_mat_ids = auto_mat_ids + self.mat_assignment = mat_assignment + self._num_instances = 0 def __repr__(self): string = super().__repr__() @@ -929,6 +934,14 @@ def decode_str_tag(tag_val): n += 1 return n + @property + def num_instances(self): + if self._num_instances is None: + raise ValueError( + 'Number of dagmc instances have not been determined. Call the ' + 'Geometry.determine_paths() method.') + return self._num_instances + @property def n_cells(self): return self._n_geom_elements('volume') @@ -955,6 +968,12 @@ def create_xml_subelement(self, xml_element, memo=None): if self.auto_mat_ids: dagmc_element.set('auto_mat_ids', 'true') dagmc_element.set('filename', str(self.filename)) + if self.mat_assignment : + mat_element = ET.Element('mat_assignment') + for key in self.mat_assignment: + mat_element.set(key, ' '.join( + t for t in self.mat_assignment[key])) + dagmc_element.append(mat_element) xml_element.append(dagmc_element) def bounding_region( diff --git a/src/dagmc.cpp b/src/dagmc.cpp index a29a2589f0b..6ad8fcfa6b4 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -72,6 +72,28 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) adjust_material_ids_ = get_node_value_bool(node, "auto_mat_ids"); } + // get material assignment overloading + if (check_for_node(node, "mat_assignment")) { + auto mat_node = node.child("mat_assignment"); + // loop over all attributes (each attribute corresponds to a material) + for (pugi::xml_attribute attr = mat_node.first_attribute(); attr; + attr = attr.next_attribute()) { + // Store assignment reference name + std::string mat_ref_assignment = attr.name(); + + // Get mat name for each assignement instances + std::stringstream iss {attr.value()}; + vector instance_mats; + std::string value; + while (iss >> value) + instance_mats.push_back(value); + + // Store mat name for each instances + instance_mat_assignment.insert( + std::make_pair(mat_ref_assignment, instance_mats)); + } + } + initialize(); } @@ -206,7 +228,6 @@ void DAGUniverse::init_geometry() if (mat_str == "graveyard") { graveyard = vol_handle; } - // material void checks if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); @@ -214,7 +235,16 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { - legacy_assign_material(mat_str, c); + if (instance_mat_assignment.size() > 0 and instance_mat_assignment.find(mat_str) != instance_mat_assignment.end()){ + + for (auto mat_str_instance: instance_mat_assignment.at(mat_str)){ + legacy_assign_material(mat_str_instance, c); + std::cout << mat_str_instance << std::endl; + } + } else { + std::cout << mat_str << std::endl; + legacy_assign_material(mat_str, c); + } } } @@ -495,6 +525,7 @@ void DAGUniverse::legacy_assign_material( if (!mat_found_by_name) { mat_found_by_name = true; c->material_.push_back(m->id_); + std::cout << mat_string << " " << c->material_.size() << std::endl; // report error if more than one material is found } else { fatal_error(fmt::format( diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 050d4db968c..315568a0033 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -435,11 +435,13 @@ void count_cell_instances(int32_t univ_indx) if (univ_counts != model::universe_cell_counts.end()) { for (const auto& it : univ_counts->second) { model::cells[it.first]->n_instances_ += it.second; + std::cout << model::cells[it.first]->n_instances_ << std::endl; } } else { for (int32_t cell_indx : model::universes[univ_indx]->cells_) { Cell& c = *model::cells[cell_indx]; ++c.n_instances_; + std::cout << c.n_instances_ << std::endl; model::universe_cell_counts[univ_indx][cell_indx] += 1; if (c.type_ == Fill::UNIVERSE) { From afd5a96da6276f764aac2c3bdba68102ea0ef898 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 20 Jun 2024 15:59:21 +0200 Subject: [PATCH 003/122] up --- src/dagmc.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 6ad8fcfa6b4..8993eb81f7e 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -235,13 +235,15 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { - if (instance_mat_assignment.size() > 0 and instance_mat_assignment.find(mat_str) != instance_mat_assignment.end()){ - - for (auto mat_str_instance: instance_mat_assignment.at(mat_str)){ + if (instance_mat_assignment.size() > 0 and + instance_mat_assignment.find(mat_str) != + instance_mat_assignment.end()) { + + for (auto mat_str_instance : instance_mat_assignment.at(mat_str)) { legacy_assign_material(mat_str_instance, c); std::cout << mat_str_instance << std::endl; } - } else { + } else { std::cout << mat_str << std::endl; legacy_assign_material(mat_str, c); } From 538313a3d913a8402fc4d224d8c75a65201a40dc Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 22 Jun 2024 16:49:56 +0200 Subject: [PATCH 004/122] new state --- openmc/model/model.py | 54 ++++++++++++++++++++++++++++++++++++++++--- openmc/universe.py | 6 ++++- src/dagmc.cpp | 8 ++++++- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index f4d5afc514c..34eb28d652b 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1026,14 +1026,34 @@ def differentiate_depletable_mats(self, diff_volume_method: str): volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. """ + + # Check if there is some DAGMC universe + if len(self.geometry.get_all_dag_universes()) > 0: + # Reset num_instances of models.materials + # (Updated from dag-verses appearance or from CSG presences...) + for mat in self._materials: + mat._num_instances = 0 + # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) + # Check if there is some DAGMC universe + dag_mats_n_inst = {} + if len(self.geometry.get_all_dag_universes()) > 0: + # Get material in the DAG-verses + for dag_verse in self.geometry.get_all_dag_universes().values(): + for mat_name in dag_verse.material_names: + dag_mats_n_inst[mat] = dag_mats_n_inst.get(mat, 0) + dag_verse.num_instances # Extract all depletable materials which have multiple instances distribmats = set( [mat for mat in self.materials if mat.depletable and mat.num_instances > 1]) + # Account for the multiplicity of the dag-verses materials + for mat in self.materials: + if mat.name in dag_mats_n_inst: + mat._num_instances += dag_mats_n_inst[mat.name] + if diff_volume_method == 'divide equally': for mat in distribmats: if mat.volume is None: @@ -1041,9 +1061,6 @@ def differentiate_depletable_mats(self, diff_volume_method: str): f"material with ID={mat.id}.") mat.volume /= mat.num_instances - for dag_uni in self.geometry.get_all_dag_universes().values(): - print(dag_uni.num_instances()) - if distribmats: # Assign distribmats to cells for cell in self.geometry.get_all_material_cells().values(): @@ -1062,7 +1079,38 @@ def differentiate_depletable_mats(self, diff_volume_method: str): ) cell.fill.volume = cell.volume + dag_verse_mats = [] + for dag_verse in self.geometry.get_all_dag_universes().values(): + if dag_verse.num_instances > 1: + deplete_mat_dict = {} + for mat_name in dag_verse.material_names: + mat_found = False + for mat in self.materials: + if mat.name == mat_name: + mat_found = True + if mat.depletable: + mats_clones = [mat.clone() for _ in range( + dag_verse.num_instances)] + for i, mat_clone in enumerate(mats_clones): + mat_clone.name += "_" + str(dag_verse.id) + "_" + str(i) + dag_verse_mats.append(mat_clone) + deplete_mat_dict[mat_name.lower()] = [ + mat.name for mat in mats_clones] + else: + dag_verse_mats.append(mat) + if not mat_found: + raise ValueError( + f"Material {mat_name} referenced in the dagmc " + "Universe {dag_verse.filename} not found in the " + "Model. Please add it to the Model. Please not " + "that uwuw formalism is not compatible with " + "differentiate_depletable_mats yet." + ) + dag_verse.mat_assignment = deplete_mat_dict + if self.materials is not None: self.materials = openmc.Materials( self.geometry.get_all_materials().values() ) + if dag_verse_mats: + self.materials.extend(dag_verse_mats) diff --git a/openmc/universe.py b/openmc/universe.py index 2f02c8bd8d3..8560860e3d6 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -718,7 +718,7 @@ def _determine_paths(self, path='', instances_only=False): # If universe-filled, recursively count cells in filling universe if fill_type == 'universe': if isinstance(fill, openmc.DAGMCUniverse): - print("DAGUNIVERSE determine", fill._num_instances) + fill._num_instances += 1 else: fill._determine_paths(cell_path + '->', instances_only) # If lattice-filled, recursively call for all universes in lattice @@ -888,6 +888,10 @@ def material_names(self): if candidate_tag.startswith('mat:'): # removes first 4 characters as openmc.Material name should be # set without the 'mat:' part of the tag + if candidate_tag.endswith('_comp'): + print(candidate_tag) + candidate_tag = candidate_tag[:-5] + print(candidate_tag) material_tags_ascii.append(candidate_tag[4:]) return sorted(set(material_tags_ascii)) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 8993eb81f7e..18cbf36eaba 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -223,6 +223,7 @@ void DAGUniverse::init_geometry() fatal_error(fmt::format("Volume {} has no material assignment.", c->id_)); } + to_lower(mat_str); if (mat_str == "graveyard") { @@ -235,6 +236,11 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { + std::cout << "assignement " << instance_mat_assignment.size() + << std::endl; + for (auto mat_inst: instance_mat_assignment){ + std::cout << mat_inst.first << std::endl; + } if (instance_mat_assignment.size() > 0 and instance_mat_assignment.find(mat_str) != instance_mat_assignment.end()) { @@ -244,7 +250,7 @@ void DAGUniverse::init_geometry() std::cout << mat_str_instance << std::endl; } } else { - std::cout << mat_str << std::endl; + std::cout << "NOT ASSIGNMENT DETECTED" << mat_str << std::endl; legacy_assign_material(mat_str, c); } } From e2c41c6d134918aa49a0ae561bd1a89742b43ba3 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 22 Jun 2024 16:50:12 +0200 Subject: [PATCH 005/122] new state --- src/dagmc.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 18cbf36eaba..d64f086d5f3 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -223,7 +223,6 @@ void DAGUniverse::init_geometry() fatal_error(fmt::format("Volume {} has no material assignment.", c->id_)); } - to_lower(mat_str); if (mat_str == "graveyard") { @@ -238,7 +237,7 @@ void DAGUniverse::init_geometry() } else { std::cout << "assignement " << instance_mat_assignment.size() << std::endl; - for (auto mat_inst: instance_mat_assignment){ + for (auto mat_inst : instance_mat_assignment) { std::cout << mat_inst.first << std::endl; } if (instance_mat_assignment.size() > 0 and From ceb12fbbdf62013550085e418404ee4951fcbf42 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 22 Jun 2024 17:11:08 +0200 Subject: [PATCH 006/122] removing print --- openmc/cell.py | 1 - openmc/geometry.py | 1 - openmc/universe.py | 7 +++---- src/dagmc.cpp | 8 -------- src/geometry_aux.cpp | 2 -- 5 files changed, 3 insertions(+), 16 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index 9f328ba3279..f4773067c7e 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -177,7 +177,6 @@ def fill(self, fill): self._fill = fill if isinstance(self.fill, openmc.DAGMCUniverse): self.fill._num_instances += 1 - print("DAGUNIVERSE", self.fill._num_instances) # Info about atom content can now be invalid # (since fill has just changed) diff --git a/openmc/geometry.py b/openmc/geometry.py index bfca6825159..10f82459710 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -757,7 +757,6 @@ def determine_paths(self, instances_only=False): material._paths = [] material._num_instances = 0 for dag_uni in self.get_all_dag_universes().values(): - dag_uni._paths = [] dag_uni._num_instances = 0 # Recursively traverse the CSG tree to count all cell instances diff --git a/openmc/universe.py b/openmc/universe.py index 8560860e3d6..434590b82b2 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -886,12 +886,11 @@ def material_names(self): candidate_tag = tag.tobytes().decode().replace('\x00', '') # tags might be for temperature or reflective surfaces if candidate_tag.startswith('mat:'): + # if name ends with _comp remove it, it is not parsed + if candidate_tag.endswith('_comp'): + candidate_tag = candidate_tag[:-5] # removes first 4 characters as openmc.Material name should be # set without the 'mat:' part of the tag - if candidate_tag.endswith('_comp'): - print(candidate_tag) - candidate_tag = candidate_tag[:-5] - print(candidate_tag) material_tags_ascii.append(candidate_tag[4:]) return sorted(set(material_tags_ascii)) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index d64f086d5f3..e7fd51ceb41 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -235,21 +235,14 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { - std::cout << "assignement " << instance_mat_assignment.size() - << std::endl; - for (auto mat_inst : instance_mat_assignment) { - std::cout << mat_inst.first << std::endl; - } if (instance_mat_assignment.size() > 0 and instance_mat_assignment.find(mat_str) != instance_mat_assignment.end()) { for (auto mat_str_instance : instance_mat_assignment.at(mat_str)) { legacy_assign_material(mat_str_instance, c); - std::cout << mat_str_instance << std::endl; } } else { - std::cout << "NOT ASSIGNMENT DETECTED" << mat_str << std::endl; legacy_assign_material(mat_str, c); } } @@ -532,7 +525,6 @@ void DAGUniverse::legacy_assign_material( if (!mat_found_by_name) { mat_found_by_name = true; c->material_.push_back(m->id_); - std::cout << mat_string << " " << c->material_.size() << std::endl; // report error if more than one material is found } else { fatal_error(fmt::format( diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index 315568a0033..050d4db968c 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -435,13 +435,11 @@ void count_cell_instances(int32_t univ_indx) if (univ_counts != model::universe_cell_counts.end()) { for (const auto& it : univ_counts->second) { model::cells[it.first]->n_instances_ += it.second; - std::cout << model::cells[it.first]->n_instances_ << std::endl; } } else { for (int32_t cell_indx : model::universes[univ_indx]->cells_) { Cell& c = *model::cells[cell_indx]; ++c.n_instances_; - std::cout << c.n_instances_ << std::endl; model::universe_cell_counts[univ_indx][cell_indx] += 1; if (c.type_ == Fill::UNIVERSE) { From 42dc9c323f04f14285f8cc913dfeaaac9adf2481 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 22 Jun 2024 17:24:59 +0200 Subject: [PATCH 007/122] removing unnecessary comment --- openmc/geometry.py | 4 ++-- openmc/model/model.py | 2 +- openmc/universe.py | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index 10f82459710..fdff1e779c4 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -405,13 +405,13 @@ def get_all_nuclides(self) -> list[str]: all_nuclides |= set(material.get_nuclides()) return sorted(all_nuclides) - def get_all_dag_universes(self) -> typing.Dict[int, openmc.Universe]: + def get_all_dag_universes(self) -> typing.Dict[int, openmc.DAGMCUniverse]: """Return all universes in the geometry. Returns ------- dict - Dictionary mapping universe IDs to :class:`openmc.Universe` + Dictionary mapping universe IDs to :class:`openmc.DAGMCUniverse` instances """ diff --git a/openmc/model/model.py b/openmc/model/model.py index 34eb28d652b..cfa71937747 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1037,7 +1037,7 @@ def differentiate_depletable_mats(self, diff_volume_method: str): # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) - # Check if there is some DAGMC universe + # Get mat instances from the different dag-verses dag_mats_n_inst = {} if len(self.geometry.get_all_dag_universes()) > 0: # Get material in the DAG-verses diff --git a/openmc/universe.py b/openmc/universe.py index 434590b82b2..5af12a00326 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -1129,7 +1129,6 @@ def from_xml_element(cls, elem): return out - # Need to change this to enable coppy of hte ids... def _partial_deepcopy(self): """Clone all of the openmc.DAGMCUniverse object's attributes except for its cells, as they are copied within the clone function. This should From 210221074ef68cca2d0bb9deee904c9e0c501b67 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 22 Jul 2024 16:19:03 +0200 Subject: [PATCH 008/122] fix --- openmc/geometry.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index fdff1e779c4..a92ce12bb59 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -415,11 +415,9 @@ def get_all_dag_universes(self) -> typing.Dict[int, openmc.DAGMCUniverse]: instances """ - universes = {} - universes[self.root_universe.id] = self.root_universe - universes.update(self.root_universe.get_all_universes()) + universes = self.get_all_universes() dag_universes = {} - for id, uni in dag_universes.items(): + for id, uni in universes.items(): if isinstance(uni, openmc.DAGMCUniverse): dag_universes[id] = uni return dag_universes From 52dcd3f86a93ea5e4d6335902cd66ea1016c263a Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 22 Jul 2024 16:46:01 +0200 Subject: [PATCH 009/122] add differente_mats into model --- openmc/model/model.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index cfa71937747..8e5845e0b32 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1018,6 +1018,22 @@ def differentiate_depletable_mats(self, diff_volume_method: str): .. versionadded:: 0.14.0 + Parameters + ---------- + diff_volume_method : str + Specifies how the volumes of the new materials should be found. + Default is to 'divide equally' which divides the original material + volume equally between the new materials, 'match cell' sets the + volume of the material to volume of the cell they fill. + """ + self.differentiate_mats(diff_volume_method, depletable_only=True) + + + def differentiate_mats(self, diff_volume_method: str, depletable_only: bool=True): + """Assign distribmats for each depletable material + + .. versionadded:: 0.14.0 + Parameters ---------- diff_volume_method : str @@ -1047,7 +1063,9 @@ def differentiate_depletable_mats(self, diff_volume_method: str): # Extract all depletable materials which have multiple instances distribmats = set( [mat for mat in self.materials - if mat.depletable and mat.num_instances > 1]) + if (mat.depletable or not depletable_only) and mat.num_instances > 1]) + + # Account for the multiplicity of the dag-verses materials for mat in self.materials: @@ -1088,7 +1106,7 @@ def differentiate_depletable_mats(self, diff_volume_method: str): for mat in self.materials: if mat.name == mat_name: mat_found = True - if mat.depletable: + if mat.depletable or not depletable_only: mats_clones = [mat.clone() for _ in range( dag_verse.num_instances)] for i, mat_clone in enumerate(mats_clones): @@ -1113,4 +1131,4 @@ def differentiate_depletable_mats(self, diff_volume_method: str): self.geometry.get_all_materials().values() ) if dag_verse_mats: - self.materials.extend(dag_verse_mats) + self.materials.extend(dag_verse_mats) \ No newline at end of file From b6df271f7fe6932fe2e4562529c3ae12f32ef149 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 22 Jul 2024 16:47:23 +0200 Subject: [PATCH 010/122] syntax --- openmc/model/model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 8e5845e0b32..c5929952ad2 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1028,8 +1028,9 @@ def differentiate_depletable_mats(self, diff_volume_method: str): """ self.differentiate_mats(diff_volume_method, depletable_only=True) - - def differentiate_mats(self, diff_volume_method: str, depletable_only: bool=True): + def differentiate_mats(self, + diff_volume_method: str, + depletable_only: bool = True): """Assign distribmats for each depletable material .. versionadded:: 0.14.0 From 8f827cc3ed8d5b6d56f8765357601c62f791caeb Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 22 Jul 2024 16:48:50 +0200 Subject: [PATCH 011/122] docstring --- openmc/model/model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index c5929952ad2..0e8c8c3f303 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1031,7 +1031,7 @@ def differentiate_depletable_mats(self, diff_volume_method: str): def differentiate_mats(self, diff_volume_method: str, depletable_only: bool = True): - """Assign distribmats for each depletable material + """Assign distribmats for each material .. versionadded:: 0.14.0 @@ -1042,6 +1042,8 @@ def differentiate_mats(self, Default is to 'divide equally' which divides the original material volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. + depletable_ony : bool + Differentiate depletable materials only. """ # Check if there is some DAGMC universe From a71e4885f3dd5ab664fed4d494e9fc27c8188557 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 24 Jul 2024 06:35:48 +0200 Subject: [PATCH 012/122] tmp commit --- openmc/cell.py | 1 + openmc/geometry.py | 1 + openmc/model/model.py | 15 +++++++++------ openmc/universe.py | 6 ++++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index f4773067c7e..636cd7a7968 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -364,6 +364,7 @@ def add_volume_information(self, volume_calc): Results from a stochastic volume calculation """ + print("FILL_TYPE", self.fill_type) if volume_calc.domain_type == 'cell': if self.id in volume_calc.volumes: self._volume = volume_calc.volumes[self.id].n diff --git a/openmc/geometry.py b/openmc/geometry.py index a92ce12bb59..fc0c721bc5c 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -102,6 +102,7 @@ def add_volume_information(self, volume_calc): if volume_calc.domain_type == 'cell': for cell in self.get_all_cells().values(): if cell.id in volume_calc.volumes: + print("found CELL ID", cell.id) cell.add_volume_information(volume_calc) elif volume_calc.domain_type == 'material': for material in self.get_all_materials().values(): diff --git a/openmc/model/model.py b/openmc/model/model.py index 0e8c8c3f303..d79812e6516 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1062,18 +1062,19 @@ def differentiate_mats(self, # Get material in the DAG-verses for dag_verse in self.geometry.get_all_dag_universes().values(): for mat_name in dag_verse.material_names: - dag_mats_n_inst[mat] = dag_mats_n_inst.get(mat, 0) + dag_verse.num_instances + dag_mats_n_inst[mat_name] = dag_mats_n_inst.get(mat_name, 0) + dag_verse.num_instances + # Account for the multiplicity of the dag-verses materials + for mat in self.materials: + if mat.name in dag_mats_n_inst: + mat._num_instances += dag_mats_n_inst[mat.name] + + # Extract all depletable materials which have multiple instances distribmats = set( [mat for mat in self.materials if (mat.depletable or not depletable_only) and mat.num_instances > 1]) - - # Account for the multiplicity of the dag-verses materials - for mat in self.materials: - if mat.name in dag_mats_n_inst: - mat._num_instances += dag_mats_n_inst[mat.name] if diff_volume_method == 'divide equally': for mat in distribmats: @@ -1112,6 +1113,8 @@ def differentiate_mats(self, if mat.depletable or not depletable_only: mats_clones = [mat.clone() for _ in range( dag_verse.num_instances)] + # if diff_volume_method == 'divide equally': + for i, mat_clone in enumerate(mats_clones): mat_clone.name += "_" + str(dag_verse.id) + "_" + str(i) dag_verse_mats.append(mat_clone) diff --git a/openmc/universe.py b/openmc/universe.py index 5af12a00326..85a1d3066da 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -876,6 +876,12 @@ def auto_mat_ids(self, val): cv.check_type('DAGMC automatic material ids', val, bool) self._auto_mat_ids = val + @property + def material_assignment(self): + dagmc_file_contents = h5py.File(self.filename) + material_tags_hex = dagmc_file_contents['/tstt/tags/NAME'].get( + 'values') + @property def material_names(self): dagmc_file_contents = h5py.File(self.filename) From 6a8000855b804765e00f874019811db3345d61b5 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 31 Jul 2024 18:34:55 +0200 Subject: [PATCH 013/122] computer change --- include/openmc/dagmc.h | 1 + openmc/geometry.py | 2 ++ openmc/lib/cell.py | 14 ++++++++++++++ openmc/model/model.py | 41 ++++++++++++++++++++++++++++++++++++++--- src/cell.cpp | 20 +++++++++++++++++++- src/dagmc.cpp | 17 +++++++++++++++++ src/initialize.cpp | 1 + 7 files changed, 92 insertions(+), 4 deletions(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 116a469ba72..f05de46c049 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -4,6 +4,7 @@ namespace openmc { extern "C" const bool DAGMC_ENABLED; extern "C" const bool UWUW_ENABLED; +extern "C" void openmc_... // full signature here } // namespace openmc // always include the XML interface header diff --git a/openmc/geometry.py b/openmc/geometry.py index fc0c721bc5c..c81d972b449 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -112,6 +112,8 @@ def add_volume_information(self, volume_calc): for universe in self.get_all_universes().values(): if universe.id in volume_calc.volumes: universe.add_volume_information(volume_calc) + print("Volume list", volume_calc.volumes) + def to_xml_element(self, remove_surfs=False) -> ET.Element: """Creates a 'geometry' element to be written to an XML file. diff --git a/openmc/lib/cell.py b/openmc/lib/cell.py index 971a24cba91..62d23bdab26 100644 --- a/openmc/lib/cell.py +++ b/openmc/lib/cell.py @@ -5,6 +5,7 @@ from weakref import WeakValueDictionary import numpy as np +from numpy.ctypeslib import as_array from ..exceptions import AllocationError, InvalidIDError from . import _dll @@ -170,12 +171,22 @@ def fill(self): indices = POINTER(c_int32)() n = c_int32() _dll.openmc_cell_get_fill(self._index, fill_type, indices, n) + print("value",fill_type.value) + print("all indicices", [i for i in indices[:n.value]]) + print("value", n.value, n) + print("cell", self.id) if fill_type.value == 0: if n.value > 1: return [Material(index=i) for i in indices[:n.value]] else: index = indices[0] return Material(index=index) + if fill_type.value == 1: + if n.value > 1: + return [Cell(index=i) for i in indices[:n.value]] + else: + index = indices[0] + return Cell(index=index) else: raise NotImplementedError @@ -314,4 +325,7 @@ def __len__(self): def __repr__(self): return repr(dict(self)) + cells = _CellMapping() + + diff --git a/openmc/model/model.py b/openmc/model/model.py index d79812e6516..dcc01986797 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -322,6 +322,10 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, # the user-provided intracomm which will either be None or an mpi4py # communicator openmc.lib.init(args=args, intracomm=intracomm, output=output) + # sync_dagmc_cells() + + # def sync_dagmc_cells(): + def finalize_lib(self): """Finalize simulation and free memory allocated for the C API @@ -1100,6 +1104,10 @@ def differentiate_mats(self, "diff_volume_method='match cell'." ) cell.fill.volume = cell.volume + else: + for _ in range(cell.num_instances): + cell.fill = mat.clone() + dag_verse_mats = [] for dag_verse in self.geometry.get_all_dag_universes().values(): @@ -1113,11 +1121,26 @@ def differentiate_mats(self, if mat.depletable or not depletable_only: mats_clones = [mat.clone() for _ in range( dag_verse.num_instances)] - # if diff_volume_method == 'divide equally': - + for i, mat_clone in enumerate(mats_clones): mat_clone.name += "_" + str(dag_verse.id) + "_" + str(i) dag_verse_mats.append(mat_clone) + + if diff_volume_method == 'match cell': + if self.is_initialized: + print(self.geometry.get_all_cells()[17]) + print(self.geometry.get_all_cells()[17].fill) + for cell in openmc.lib.cells.values(): + print(cell.fill) + # if cell.fill.name == mat.name: + # mat_clone.volume = cell.volume + print("IN") + else: + raise NotImplementedError( + "differentiate mats with DAGMC" + "universe and match cell " + "option is only available " + "when cpp_lib is initialiazed") deplete_mat_dict[mat_name.lower()] = [ mat.name for mat in mats_clones] else: @@ -1137,4 +1160,16 @@ def differentiate_mats(self, self.geometry.get_all_materials().values() ) if dag_verse_mats: - self.materials.extend(dag_verse_mats) \ No newline at end of file + self.materials.extend(dag_verse_mats) + + + # def get_all_material_cells(self) -> dict[int, Cell]: + # if self.is_initialized: + # material_cells = {} + # for id in openmc.lib.cells: + # try: + # if isinstance(openmc.lib.cells[id].fill, openmc.lib.Material): + # material_cells[id] = openmc.lib.cells[id] + # except NotImplementedError: + # print("NOT FILLED WITH MAT") + # return material_cells \ No newline at end of file diff --git a/src/cell.cpp b/src/cell.cpp index 88876678706..9adcdc7bd24 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1068,9 +1068,27 @@ extern "C" int openmc_cell_get_fill( Cell& c {*model::cells[index]}; *type = static_cast(c.type_); if (c.type_ == Fill::MATERIAL) { + std::cout << "mat size " << c.material_.size() << std::endl; + for (int i=0; i< c.material_.size(); i++){ + std::cout << "mat " << c.material_[i] << std::endl; + } *indices = c.material_.data(); *n = c.material_.size(); - } else { + } else if (c.type_ == Fill::UNIVERSE) { + std::unordered_map> contained_cells = + c.get_contained_cells(); + std::vector cells_id; + for (const auto& [key, _] : contained_cells) { + cells_id.push_back(key); + } + *indices = cells_id.data(); + std::cout << "cell out " << (*indices)[0] << std::endl; + std::cout << "cell out " << (*indices)[1] << std::endl; + std::cout << "cell out " << (*indices)[2] << std::endl; + *n = contained_cells.size(); + } + else + { *indices = &c.fill_; *n = 1; } diff --git a/src/dagmc.cpp b/src/dagmc.cpp index e7fd51ceb41..364e8159ce0 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -132,6 +132,7 @@ void DAGUniverse::set_id() void DAGUniverse::initialize() { + std::cout << "INITIALIZE DAGMC" << std::endl; geom_type() = GeometryType::DAG; init_dagmc(); @@ -846,6 +847,22 @@ int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ) return univp->cell_index(new_vol); } +void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t** ids, size_t* n) { + + // make sure the universe id is a DAGMC Universe + const auto& univ = universe_map[univ_id]; + + std::vector dag_cell_ids; + for (const auto& cell : univ->cells_) { + if (cell->geom_type_ == GeometryType::DAG) + dag_cell_ids.push_back(cell->id_); + } + + *ids = dag_cell_ids.data(); + *n = dag_cell_ids.size(); +} + + } // namespace openmc #else diff --git a/src/initialize.cpp b/src/initialize.cpp index cc1eac9cf35..fea00b692c8 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -316,6 +316,7 @@ int parse_command_line(int argc, char* argv[]) bool read_model_xml() { + std::cout << "READ_MODEL" << __FILE__ << std::endl; std::string model_filename = settings::path_input; // if the current filename is a directory, append the default model filename From 394d585ee68ffeafc5a6635208215a47254723ff Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 31 Jul 2024 18:35:45 +0200 Subject: [PATCH 014/122] computer change --- src/cell.cpp | 12 +++++------- src/dagmc.cpp | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cell.cpp b/src/cell.cpp index 9adcdc7bd24..1e459d82aea 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1068,10 +1068,10 @@ extern "C" int openmc_cell_get_fill( Cell& c {*model::cells[index]}; *type = static_cast(c.type_); if (c.type_ == Fill::MATERIAL) { - std::cout << "mat size " << c.material_.size() << std::endl; - for (int i=0; i< c.material_.size(); i++){ - std::cout << "mat " << c.material_[i] << std::endl; - } + std::cout << "mat size " << c.material_.size() << std::endl; + for (int i = 0; i < c.material_.size(); i++) { + std::cout << "mat " << c.material_[i] << std::endl; + } *indices = c.material_.data(); *n = c.material_.size(); } else if (c.type_ == Fill::UNIVERSE) { @@ -1086,9 +1086,7 @@ extern "C" int openmc_cell_get_fill( std::cout << "cell out " << (*indices)[1] << std::endl; std::cout << "cell out " << (*indices)[2] << std::endl; *n = contained_cells.size(); - } - else - { + } else { *indices = &c.fill_; *n = 1; } diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 364e8159ce0..90fccd1d91b 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -847,7 +847,8 @@ int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ) return univp->cell_index(new_vol); } -void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t** ids, size_t* n) { +void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t** ids, size_t* n) +{ // make sure the universe id is a DAGMC Universe const auto& univ = universe_map[univ_id]; @@ -862,7 +863,6 @@ void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t** ids, size_t* n) { *n = dag_cell_ids.size(); } - } // namespace openmc #else From 4ec6b2bed256ef5afedca4b1e4eb3b4ffb0a271f Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Fri, 2 Aug 2024 11:01:15 +0200 Subject: [PATCH 015/122] in progress --- include/openmc/capi.h | 1 + include/openmc/dagmc.h | 1 - openmc/cell.py | 52 ++++++++++++++++++++++++++++++++++++++++++ openmc/lib/cell.py | 14 ------------ openmc/lib/dagmc.py | 45 ++++++++++++++++++++++++++++++++++++ openmc/model/model.py | 24 ++++++++++++++++--- openmc/universe.py | 1 + src/cell.cpp | 16 ------------- src/dagmc.cpp | 11 +++++---- 9 files changed, 126 insertions(+), 39 deletions(-) create mode 100644 openmc/lib/dagmc.py diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 9401156a64f..32c01058f16 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -29,6 +29,7 @@ int openmc_cell_set_temperature( int32_t index, double T, const int32_t* instance, bool set_contained = false); int openmc_cell_set_translation(int32_t index, const double xyz[]); int openmc_cell_set_rotation(int32_t index, const double rot[], size_t rot_len); +int openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n); int openmc_energy_filter_get_bins( int32_t index, const double** energies, size_t* n); int openmc_energy_filter_set_bins( diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index f05de46c049..116a469ba72 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -4,7 +4,6 @@ namespace openmc { extern "C" const bool DAGMC_ENABLED; extern "C" const bool UWUW_ENABLED; -extern "C" void openmc_... // full signature here } // namespace openmc // always include the XML interface header diff --git a/openmc/cell.py b/openmc/cell.py index 636cd7a7968..bda6189c823 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -783,3 +783,55 @@ def from_xml_element(cls, elem, surfaces, materials, get_universe): univ_id = int(get_text(elem, 'universe', 0)) get_universe(univ_id).add_cell(c) return c + + +class DAGSpeudoCell(Cell): + def __init__(self, cell_id=None, name='', fill=None, region=None): + super().__init__(cell_id, name, fill, region) + + @property + def DAG_parent_universe(self): + """Get the parent universe of the cell.""" + return self._parent_universe + + @DAG_parent_universe.setter + def DAG_parent_universe(self, universe): + """Set the parent universe of the cell.""" + self._parent_universe = universe.id + + def boundingbox(self): + print("Warning: Bounding box is not available for cells in a DAGMC universe.") + return {} + + def get_all_cells(self, memo=None): + print("Warning: get_all_cells is not available for cells in a DAGMC universe.") + return {} + + def get_all_materials(self, memo=None): + print("Warning: get_all_materials is not available for cells in a DAGMC universe.") + return {} + + def get_all_universes(self, memo=None): + print("Warning: get_all_universes is not available for cells in a DAGMC universe.") + return {} + + def clone(self, clone_materials=True, clone_regions=True, memo=None): + print("Warning: clone is not available for cells in a DAGMC universe.") + return None + + def plot(self, *args, **kwargs): + print("Warning: plot is not available for cells in a DAGMC universe.") + return None + + def create_xml_subelement(self, xml_element, memo=None): + print("Warning: create_xml_subelement is not available for cells in a DAGMC universe.") + return None + + @classmethod + def from_xml_element(cls, elem, surfaces, materials, get_universe): + print("Warning: from_xml_element is not available for cells in a DAGMC universe.") + return None + + + + \ No newline at end of file diff --git a/openmc/lib/cell.py b/openmc/lib/cell.py index 62d23bdab26..971a24cba91 100644 --- a/openmc/lib/cell.py +++ b/openmc/lib/cell.py @@ -5,7 +5,6 @@ from weakref import WeakValueDictionary import numpy as np -from numpy.ctypeslib import as_array from ..exceptions import AllocationError, InvalidIDError from . import _dll @@ -171,22 +170,12 @@ def fill(self): indices = POINTER(c_int32)() n = c_int32() _dll.openmc_cell_get_fill(self._index, fill_type, indices, n) - print("value",fill_type.value) - print("all indicices", [i for i in indices[:n.value]]) - print("value", n.value, n) - print("cell", self.id) if fill_type.value == 0: if n.value > 1: return [Material(index=i) for i in indices[:n.value]] else: index = indices[0] return Material(index=index) - if fill_type.value == 1: - if n.value > 1: - return [Cell(index=i) for i in indices[:n.value]] - else: - index = indices[0] - return Cell(index=index) else: raise NotImplementedError @@ -325,7 +314,4 @@ def __len__(self): def __repr__(self): return repr(dict(self)) - cells = _CellMapping() - - diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py new file mode 100644 index 00000000000..12081c1d01e --- /dev/null +++ b/openmc/lib/dagmc.py @@ -0,0 +1,45 @@ +import sys + +from ctypes import c_int, c_int32, POINTER, c_size_t + +import numpy as np + +from . import _dll +from .error import _error_handler + + + +# DAGMC functions +_dll.openmc_get_dagmc_cell_ids.argtypes = [c_int32, POINTER(c_int), POINTER(c_int32), POINTER(c_size_t)] +_dll.openmc_get_dagmc_cell_ids.restype = c_int +_dll.openmc_get_dagmc_cell_ids.errcheck = _error_handler + + +def get_dagmc_cell_ids(volume_id, n_cells): + """Get the DAGMC cell IDs for a volume. + + Parameters + ---------- + volume_id : int + ID of the volume to get DAGMC cell IDs for. + n_cells : int + Number of cells in the volume. + + Returns + ------- + numpy.ndarray + DAGMC cell IDs for the volume. + + """ + cell_ids = np.empty(n_cells, dtype=np.int32) + n = c_size_t() + _dll.openmc_get_dagmc_cell_ids( + volume_id, + cell_ids.ctypes.data_as(POINTER(c_int32)), + n + ) + if n.value != n_cells: + raise ValueError("Number of cells obtained from DAGMC does not match " + "the expected number of cells." + ) + return cell_ids \ No newline at end of file diff --git a/openmc/model/model.py b/openmc/model/model.py index dcc01986797..eaa1b656f58 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -322,10 +322,28 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, # the user-provided intracomm which will either be None or an mpi4py # communicator openmc.lib.init(args=args, intracomm=intracomm, output=output) - # sync_dagmc_cells() + self.sync_dagmc_cells() - # def sync_dagmc_cells(): - + def sync_dagmc_cells(self): + """Synchronize DAGMC cell information between Python and C API + + .. versionadded:: 0.13.0 + + """ + if not self.is_initialized: + raise RuntimeError("Model must be initialized via Model.init_lib " + "before calling this method.") + + import openmc.lib + + for cell in self.geometry.get_all_cells(): + if isinstance(cell.fill, openmc.DAGMCUniverse): + for dag_cell_id in openmc.lib.get_dagmc_cells(cell.id): + dag_cell = openmc.lib.cells[dag_cell_id] + dag_pseudo_cell = openmc.DAGPseudoCell( + dag_cell_id, self._materials_by_id[dag_cell.fill.id] + ) + cell.fill._dagmc_cells.append(dag_pseudo_cell.id) def finalize_lib(self): """Finalize simulation and free memory allocated for the C API diff --git a/openmc/universe.py b/openmc/universe.py index 85a1d3066da..85b91951fa9 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -834,6 +834,7 @@ def __init__(self, self.auto_mat_ids = auto_mat_ids self.mat_assignment = mat_assignment self._num_instances = 0 + self._dagmc_cells = [] def __repr__(self): string = super().__repr__() diff --git a/src/cell.cpp b/src/cell.cpp index 1e459d82aea..88876678706 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1068,24 +1068,8 @@ extern "C" int openmc_cell_get_fill( Cell& c {*model::cells[index]}; *type = static_cast(c.type_); if (c.type_ == Fill::MATERIAL) { - std::cout << "mat size " << c.material_.size() << std::endl; - for (int i = 0; i < c.material_.size(); i++) { - std::cout << "mat " << c.material_[i] << std::endl; - } *indices = c.material_.data(); *n = c.material_.size(); - } else if (c.type_ == Fill::UNIVERSE) { - std::unordered_map> contained_cells = - c.get_contained_cells(); - std::vector cells_id; - for (const auto& [key, _] : contained_cells) { - cells_id.push_back(key); - } - *indices = cells_id.data(); - std::cout << "cell out " << (*indices)[0] << std::endl; - std::cout << "cell out " << (*indices)[1] << std::endl; - std::cout << "cell out " << (*indices)[2] << std::endl; - *n = contained_cells.size(); } else { *indices = &c.fill_; *n = 1; diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 90fccd1d91b..8931b2dcbf6 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -847,19 +847,20 @@ int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ) return univp->cell_index(new_vol); } -void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t** ids, size_t* n) +extern "C" void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n) { - // make sure the universe id is a DAGMC Universe const auto& univ = universe_map[univ_id]; + if (univ.geom_type_ != GeometryType::DAG){ + fatal_error("Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); + } std::vector dag_cell_ids; - for (const auto& cell : univ->cells_) { + for (const auto& cell : univ.cells_) { if (cell->geom_type_ == GeometryType::DAG) dag_cell_ids.push_back(cell->id_); } - - *ids = dag_cell_ids.data(); + std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids) *n = dag_cell_ids.size(); } From 5dc39fbcb11852ce0769bc1ba7911efdc2bdd921 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Fri, 2 Aug 2024 11:01:28 +0200 Subject: [PATCH 016/122] in progress --- include/openmc/capi.h | 2 +- src/dagmc.cpp | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 32c01058f16..cb8fb93007d 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -29,7 +29,7 @@ int openmc_cell_set_temperature( int32_t index, double T, const int32_t* instance, bool set_contained = false); int openmc_cell_set_translation(int32_t index, const double xyz[]); int openmc_cell_set_rotation(int32_t index, const double rot[], size_t rot_len); -int openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n); +int openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n); int openmc_energy_filter_get_bins( int32_t index, const double** energies, size_t* n); int openmc_energy_filter_set_bins( diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 8931b2dcbf6..b37a90b26a5 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -847,12 +847,14 @@ int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ) return univp->cell_index(new_vol); } -extern "C" void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n) +extern "C" void openmc_get_dagmc_cell_ids( + int32_t univ_id, int32_t* ids, size_t* n) { // make sure the universe id is a DAGMC Universe const auto& univ = universe_map[univ_id]; - if (univ.geom_type_ != GeometryType::DAG){ - fatal_error("Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); + if (univ.geom_type_ != GeometryType::DAG) { + fatal_error( + "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); } std::vector dag_cell_ids; @@ -860,8 +862,8 @@ extern "C" void openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* if (cell->geom_type_ == GeometryType::DAG) dag_cell_ids.push_back(cell->id_); } - std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids) - *n = dag_cell_ids.size(); + std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids)* n = + dag_cell_ids.size(); } } // namespace openmc From 0a1e6c5f3e4b08847fc4e2e35137bb51074ae151 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 7 Aug 2024 14:35:49 +0200 Subject: [PATCH 017/122] temp --- include/openmc/cell.h | 7 ++- openmc/cell.py | 24 ++++------- openmc/geometry.py | 3 -- openmc/lib/__init__.py | 1 + openmc/lib/dagmc.py | 11 +++-- openmc/model/model.py | 35 +++++---------- openmc/universe.py | 98 +++++++++++++++++++++++++++++++++++++++++- src/cell.cpp | 4 +- src/dagmc.cpp | 17 ++++---- 9 files changed, 139 insertions(+), 61 deletions(-) diff --git a/include/openmc/cell.h b/include/openmc/cell.h index d78614f057e..a2ebb6fc146 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -319,7 +319,6 @@ class Cell { int32_t universe_; //!< Universe # this cell is in int32_t fill_; //!< Universe # filling this cell int32_t n_instances_ {0}; //!< Number of instances of this cell - GeometryType geom_type_; //!< Geometric representation type (CSG, DAGMC) //! \brief Index corresponding to this cell in distribcell arrays int distribcell_index_ {C_NONE}; @@ -349,6 +348,12 @@ class Cell { vector rotation_; vector offset_; //!< Distribcell offset table + + const GeometryType& geom_type() const { return geom_type_; } + GeometryType& geom_type() { return geom_type_; } + + private: + GeometryType geom_type_; //!< Geometric representation type (CSG, DAGMC) }; struct CellInstanceItem { diff --git a/openmc/cell.py b/openmc/cell.py index bda6189c823..167c9113886 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -364,7 +364,6 @@ def add_volume_information(self, volume_calc): Results from a stochastic volume calculation """ - print("FILL_TYPE", self.fill_type) if volume_calc.domain_type == 'cell': if self.id in volume_calc.volumes: self._volume = volume_calc.volumes[self.id].n @@ -799,20 +798,19 @@ def DAG_parent_universe(self, universe): """Set the parent universe of the cell.""" self._parent_universe = universe.id - def boundingbox(self): - print("Warning: Bounding box is not available for cells in a DAGMC universe.") - return {} + # def boundingbox(self): + # print("Warning: Bounding box is not available for cells in a DAGMC universe.") + # return {} - def get_all_cells(self, memo=None): - print("Warning: get_all_cells is not available for cells in a DAGMC universe.") - return {} + # def get_all_cells(self, memo=None): + # print("Warning: get_all_cells is not available for cells in a DAGMC universe.") + # return {} - def get_all_materials(self, memo=None): - print("Warning: get_all_materials is not available for cells in a DAGMC universe.") - return {} + # def get_all_materials(self, memo=None): + # print("Warning: get_all_materials is not available for cells in a DAGMC universe.") + # return {} def get_all_universes(self, memo=None): - print("Warning: get_all_universes is not available for cells in a DAGMC universe.") return {} def clone(self, clone_materials=True, clone_regions=True, memo=None): @@ -831,7 +829,3 @@ def create_xml_subelement(self, xml_element, memo=None): def from_xml_element(cls, elem, surfaces, materials, get_universe): print("Warning: from_xml_element is not available for cells in a DAGMC universe.") return None - - - - \ No newline at end of file diff --git a/openmc/geometry.py b/openmc/geometry.py index c81d972b449..a92ce12bb59 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -102,7 +102,6 @@ def add_volume_information(self, volume_calc): if volume_calc.domain_type == 'cell': for cell in self.get_all_cells().values(): if cell.id in volume_calc.volumes: - print("found CELL ID", cell.id) cell.add_volume_information(volume_calc) elif volume_calc.domain_type == 'material': for material in self.get_all_materials().values(): @@ -112,8 +111,6 @@ def add_volume_information(self, volume_calc): for universe in self.get_all_universes().values(): if universe.id in volume_calc.volumes: universe.add_volume_information(volume_calc) - print("Volume list", volume_calc.volumes) - def to_xml_element(self, remove_surfs=False) -> ET.Element: """Creates a 'geometry' element to be written to an XML file. diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 9bb2efb38af..5fe35b9745d 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -68,6 +68,7 @@ def _uwuw_enabled(): from .math import * from .plot import * from .weight_windows import * +from .dagmc import * # Flag to denote whether or not openmc.lib.init has been called # TODO: Establish and use a flag in the C++ code to represent the status of the diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index 12081c1d01e..ab463d0c076 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -10,7 +10,7 @@ # DAGMC functions -_dll.openmc_get_dagmc_cell_ids.argtypes = [c_int32, POINTER(c_int), POINTER(c_int32), POINTER(c_size_t)] +_dll.openmc_get_dagmc_cell_ids.argtypes = [c_int32, POINTER(c_int32), POINTER(c_size_t)] _dll.openmc_get_dagmc_cell_ids.restype = c_int _dll.openmc_get_dagmc_cell_ids.errcheck = _error_handler @@ -34,12 +34,11 @@ def get_dagmc_cell_ids(volume_id, n_cells): cell_ids = np.empty(n_cells, dtype=np.int32) n = c_size_t() _dll.openmc_get_dagmc_cell_ids( - volume_id, - cell_ids.ctypes.data_as(POINTER(c_int32)), + volume_id, + cell_ids.ctypes.data_as(POINTER(c_int32)), n ) if n.value != n_cells: - raise ValueError("Number of cells obtained from DAGMC does not match " - "the expected number of cells." - ) + raise ValueError(f"Number of cells obtained {n.value} from DAGMC does " + f"not match the expected number of cells {n_cells}.") return cell_ids \ No newline at end of file diff --git a/openmc/model/model.py b/openmc/model/model.py index eaa1b656f58..27d92112d27 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -16,6 +16,7 @@ from openmc.executor import _process_CLI_arguments from openmc.checkvalue import check_type, check_value, PathLike from openmc.exceptions import InvalidIDError +import openmc.lib from openmc.utility_funcs import change_directory @@ -336,14 +337,14 @@ def sync_dagmc_cells(self): import openmc.lib - for cell in self.geometry.get_all_cells(): + for cell in self.geometry.get_all_cells().values(): if isinstance(cell.fill, openmc.DAGMCUniverse): - for dag_cell_id in openmc.lib.get_dagmc_cells(cell.id): + for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(cell.fill.id, cell.fill._n_geom_elements('volume')): dag_cell = openmc.lib.cells[dag_cell_id] - dag_pseudo_cell = openmc.DAGPseudoCell( - dag_cell_id, self._materials_by_id[dag_cell.fill.id] + dag_pseudo_cell = openmc.DAGSpeudoCell( + cell_id=dag_cell_id, fill=self._materials_by_id[dag_cell.fill.id] ) - cell.fill._dagmc_cells.append(dag_pseudo_cell.id) + cell.fill.add_cell(dag_pseudo_cell) def finalize_lib(self): """Finalize simulation and free memory allocated for the C API @@ -1146,13 +1147,9 @@ def differentiate_mats(self, if diff_volume_method == 'match cell': if self.is_initialized: - print(self.geometry.get_all_cells()[17]) - print(self.geometry.get_all_cells()[17].fill) - for cell in openmc.lib.cells.values(): - print(cell.fill) - # if cell.fill.name == mat.name: - # mat_clone.volume = cell.volume - print("IN") + for cell in dag_verse._cells.values(): + if cell.fill.name == mat.name: + mat_clone.volume = cell.volume else: raise NotImplementedError( "differentiate mats with DAGMC" @@ -1179,15 +1176,5 @@ def differentiate_mats(self, ) if dag_verse_mats: self.materials.extend(dag_verse_mats) - - - # def get_all_material_cells(self) -> dict[int, Cell]: - # if self.is_initialized: - # material_cells = {} - # for id in openmc.lib.cells: - # try: - # if isinstance(openmc.lib.cells[id].fill, openmc.lib.Material): - # material_cells[id] = openmc.lib.cells[id] - # except NotImplementedError: - # print("NOT FILLED WITH MAT") - # return material_cells \ No newline at end of file + self.materials = list(set(self.materials)) + \ No newline at end of file diff --git a/openmc/universe.py b/openmc/universe.py index 85b91951fa9..20c16ccbb2c 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -903,10 +903,43 @@ def material_names(self): return sorted(set(material_tags_ascii)) def get_all_cells(self, memo=None): - return {} + """Return all cells that are contained within the universe + + Returns + ------- + cells : dict + Dictionary whose keys are cell IDs and values are :class:`Cell` + instances + + """ + + if memo is None: + memo = set() + elif self in memo: + return {} + memo.add(self) + + # Add this Universe's cells to the dictionary + cells = {} + cells.update(self._cells) + + # Append all Cells in each Cell in the Universe to the dictionary + for cell in self._cells.values(): + cells.update(cell.get_all_cells(memo)) + + return cells def get_all_materials(self, memo=None): - return {} + if memo is None: + memo = set() + + materials = {} + + # Append all Cells in each Cell in the Universe to the dictionary + cells = self.get_all_cells(memo) + for cell in cells.values(): + materials.update(cell.get_all_materials(memo)) + return materials def _n_geom_elements(self, geom_type): """ @@ -1146,3 +1179,64 @@ def _partial_deepcopy(self): clone.auto_geom_ids = self.auto_geom_ids clone.auto_mat_ids = self.auto_mat_ids return clone + + def add_cell(self, cell): + """Add a cell to the universe. + + Parameters + ---------- + cell : openmc.Cell + Cell to add + + """ + + if not isinstance(cell, openmc.Cell): + msg = f'Unable to add a Cell to Universe ID="{self._id}" since ' \ + f'"{cell}" is not a Cell' + raise TypeError(msg) + + cell_id = cell.id + + if cell_id not in self._cells: + self._cells[cell_id] = cell + + def add_cells(self, cells): + """Add multiple cells to the universe. + + Parameters + ---------- + cells : Iterable of openmc.Cell + Cells to add + + """ + + if not isinstance(cells, Iterable): + msg = f'Unable to add Cells to Universe ID="{self._id}" since ' \ + f'"{cells}" is not iterable' + raise TypeError(msg) + + for cell in cells: + self.add_cell(cell) + + def remove_cell(self, cell): + """Remove a cell from the universe. + + Parameters + ---------- + cell : openmc.Cell + Cell to remove + + """ + + if not isinstance(cell, openmc.Cell): + msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ + f'since "{cell}" is not a Cell' + raise TypeError(msg) + + # If the Cell is in the Universe's list of Cells, delete it + self._cells.pop(cell.id, None) + + def clear_cells(self): + """Remove all cells from the universe.""" + + self._cells.clear() \ No newline at end of file diff --git a/src/cell.cpp b/src/cell.cpp index 88876678706..d4d28fb70e6 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -252,12 +252,12 @@ void Cell::to_hdf5(hid_t cell_group) const // default constructor CSGCell::CSGCell() { - geom_type_ = GeometryType::CSG; + geom_type() = GeometryType::CSG; } CSGCell::CSGCell(pugi::xml_node cell_node) { - geom_type_ = GeometryType::CSG; + geom_type() = GeometryType::CSG; if (check_for_node(cell_node, "id")) { id_ = std::stoi(get_node_value(cell_node, "id")); diff --git a/src/dagmc.cpp b/src/dagmc.cpp index b37a90b26a5..61d3be094cc 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -645,7 +645,7 @@ void DAGUniverse::uwuw_assign_material( DAGCell::DAGCell(std::shared_ptr dag_ptr, int32_t dag_idx) : Cell {}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) { - geom_type_ = GeometryType::DAG; + geom_type() = GeometryType::DAG; }; std::pair DAGCell::distance( @@ -847,23 +847,24 @@ int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ) return univp->cell_index(new_vol); } -extern "C" void openmc_get_dagmc_cell_ids( +extern "C" int openmc_get_dagmc_cell_ids( int32_t univ_id, int32_t* ids, size_t* n) { // make sure the universe id is a DAGMC Universe - const auto& univ = universe_map[univ_id]; - if (univ.geom_type_ != GeometryType::DAG) { + const auto& univ = model::universes[model::universe_map[univ_id]]; + if (univ->geom_type() != GeometryType::DAG) { fatal_error( "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); } std::vector dag_cell_ids; - for (const auto& cell : univ.cells_) { - if (cell->geom_type_ == GeometryType::DAG) + for (const auto& cell_index : univ->cells_) { + const auto& cell = model::cells[cell_index]; + if (cell->geom_type() == GeometryType::DAG) dag_cell_ids.push_back(cell->id_); } - std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids)* n = - dag_cell_ids.size(); + std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids); + *n = dag_cell_ids.size(); } } // namespace openmc From 7aa6f64ff09782c53f5d79c4d506849ec58fc832 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 7 Aug 2024 14:36:01 +0200 Subject: [PATCH 018/122] temp --- include/openmc/cell.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/openmc/cell.h b/include/openmc/cell.h index a2ebb6fc146..eec14b2aac5 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -351,9 +351,9 @@ class Cell { const GeometryType& geom_type() const { return geom_type_; } GeometryType& geom_type() { return geom_type_; } - - private: - GeometryType geom_type_; //!< Geometric representation type (CSG, DAGMC) + +private: + GeometryType geom_type_; //!< Geometric representation type (CSG, DAGMC) }; struct CellInstanceItem { From f7a9b23c4f24c2f7a5a90d93d7fc064ccbd2e616 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 12 Aug 2024 14:08:39 +0200 Subject: [PATCH 019/122] up --- openmc/cell.py | 51 ++++++++++++++++++++++++++++------- openmc/model/model.py | 62 ++++++++++++++++++++++++------------------- openmc/universe.py | 1 - src/initialize.cpp | 1 - 4 files changed, 77 insertions(+), 38 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index 167c9113886..133fa8480c8 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -798,17 +798,50 @@ def DAG_parent_universe(self, universe): """Set the parent universe of the cell.""" self._parent_universe = universe.id - # def boundingbox(self): - # print("Warning: Bounding box is not available for cells in a DAGMC universe.") - # return {} + def boundingbox(self): + print("Warning: Bounding box is not available for cells in a DAGMC universe.") + return {} + + def get_all_cells(self, memo=None): + return {} + + def get_all_materials(self, memo=None): + """Return all materials that are contained within the cell + + Returns + ------- + materials : dict + Dictionary whose keys are material IDs and values are + :class:`Material` instances + + """ + materials = {} + if self.fill_type == 'material': + materials[self.fill.id] = self.fill + elif self.fill_type == 'distribmat': + for m in self.fill: + if m is not None: + materials[m.id] = m + else: + # Append all Cells in each Cell in the Universe to the dictionary + cells = self.get_all_cells(memo) + for cell in cells.values(): + materials.update(cell.get_all_materials(memo)) - # def get_all_cells(self, memo=None): - # print("Warning: get_all_cells is not available for cells in a DAGMC universe.") - # return {} + return materials - # def get_all_materials(self, memo=None): - # print("Warning: get_all_materials is not available for cells in a DAGMC universe.") - # return {} + @property + def fill_type(self): + if isinstance(self.fill, openmc.Material): + return 'material' + elif isinstance(self.fill, openmc.UniverseBase): + return 'universe' + elif isinstance(self.fill, openmc.Lattice): + return 'lattice' + elif isinstance(self.fill, Iterable): + return 'distribmat' + else: + return 'void' def get_all_universes(self, memo=None): return {} diff --git a/openmc/model/model.py b/openmc/model/model.py index 27d92112d27..30582e55ba2 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -336,13 +336,24 @@ def sync_dagmc_cells(self): "before calling this method.") import openmc.lib + + if self.materials: + mats = self.materials + else: + mats = self.geometry.get_all_materials().values() + mats_per_id = {mat.id: mat for mat in mats} for cell in self.geometry.get_all_cells().values(): if isinstance(cell.fill, openmc.DAGMCUniverse): for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(cell.fill.id, cell.fill._n_geom_elements('volume')): dag_cell = openmc.lib.cells[dag_cell_id] + if isinstance(dag_cell.fill, Iterable): + fill = [mats_per_id[mat_id.id] + for mat_id in dag_cell.fill] + else: + fill = mats_per_id[dag_cell.fill.id] dag_pseudo_cell = openmc.DAGSpeudoCell( - cell_id=dag_cell_id, fill=self._materials_by_id[dag_cell.fill.id] + cell_id=dag_cell_id, fill=fill ) cell.fill.add_cell(dag_pseudo_cell) @@ -1068,7 +1079,6 @@ def differentiate_mats(self, depletable_ony : bool Differentiate depletable materials only. """ - # Check if there is some DAGMC universe if len(self.geometry.get_all_dag_universes()) > 0: # Reset num_instances of models.materials @@ -1091,14 +1101,11 @@ def differentiate_mats(self, if mat.name in dag_mats_n_inst: mat._num_instances += dag_mats_n_inst[mat.name] - # Extract all depletable materials which have multiple instances distribmats = set( [mat for mat in self.materials if (mat.depletable or not depletable_only) and mat.num_instances > 1]) - - if diff_volume_method == 'divide equally': for mat in distribmats: if mat.volume is None: @@ -1127,7 +1134,6 @@ def differentiate_mats(self, for _ in range(cell.num_instances): cell.fill = mat.clone() - dag_verse_mats = [] for dag_verse in self.geometry.get_all_dag_universes().values(): if dag_verse.num_instances > 1: @@ -1138,37 +1144,39 @@ def differentiate_mats(self, if mat.name == mat_name: mat_found = True if mat.depletable or not depletable_only: - mats_clones = [mat.clone() for _ in range( - dag_verse.num_instances)] - + mats_clones = [mat.clone() for _ in range(dag_verse.num_instances)] for i, mat_clone in enumerate(mats_clones): mat_clone.name += "_" + str(dag_verse.id) + "_" + str(i) dag_verse_mats.append(mat_clone) - if diff_volume_method == 'match cell': if self.is_initialized: for cell in dag_verse._cells.values(): - if cell.fill.name == mat.name: - mat_clone.volume = cell.volume + if not isinstance(cell.fill, list): + if cell.fill.name == mat.name: + cell.fill = mat_clone + mat_clone.volume = cell.volume + elif cell.fill.name.split("_")[0] == mat.name: + mat_clone.volume = cell.volume + cell.fill = [cell.fill] + cell.fill.append(mat_clone) + else: + if cell.fill[0].name.split("_")[0] == mat.name: + mat_clone.volume = cell.volume + cell.fill.append(mat_clone) else: - raise NotImplementedError( - "differentiate mats with DAGMC" - "universe and match cell " - "option is only available " - "when cpp_lib is initialiazed") - deplete_mat_dict[mat_name.lower()] = [ - mat.name for mat in mats_clones] + raise NotImplementedError("differentiate mats with DAGMC universe and match cell option is only available when cpp_lib is initialized") + deplete_mat_dict[mat_name.lower()] = [mat.name for mat in mats_clones] else: dag_verse_mats.append(mat) if not mat_found: - raise ValueError( - f"Material {mat_name} referenced in the dagmc " - "Universe {dag_verse.filename} not found in the " - "Model. Please add it to the Model. Please not " - "that uwuw formalism is not compatible with " - "differentiate_depletable_mats yet." - ) - dag_verse.mat_assignment = deplete_mat_dict + raise ValueError(f"Material {mat_name} referenced in " + f"the dagmc Universe " + f"{dag_verse.filename} not found in " + f"the Model. Please add it to the " + f"Model. Please note that uwuw " + f"formalism is not compatible with " + f"differentiate_depletable_mats yet.") + dag_verse.mat_assignment = deplete_mat_dict if self.materials is not None: self.materials = openmc.Materials( diff --git a/openmc/universe.py b/openmc/universe.py index 20c16ccbb2c..d47acc8644b 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -926,7 +926,6 @@ def get_all_cells(self, memo=None): # Append all Cells in each Cell in the Universe to the dictionary for cell in self._cells.values(): cells.update(cell.get_all_cells(memo)) - return cells def get_all_materials(self, memo=None): diff --git a/src/initialize.cpp b/src/initialize.cpp index fea00b692c8..cc1eac9cf35 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -316,7 +316,6 @@ int parse_command_line(int argc, char* argv[]) bool read_model_xml() { - std::cout << "READ_MODEL" << __FILE__ << std::endl; std::string model_filename = settings::path_input; // if the current filename is a directory, append the default model filename From f3acb0b8043bd4e048d010725cd3c595ff61828d Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 12 Aug 2024 14:24:57 +0200 Subject: [PATCH 020/122] cleaning printout --- src/dagmc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 61d3be094cc..2847eee37db 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -132,7 +132,6 @@ void DAGUniverse::set_id() void DAGUniverse::initialize() { - std::cout << "INITIALIZE DAGMC" << std::endl; geom_type() = GeometryType::DAG; init_dagmc(); From 9bfab402f43c64d794ccd93d71f7b948cc5ad64f Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 12 Aug 2024 14:30:58 +0200 Subject: [PATCH 021/122] adding openmc_get_dagmc_cell_ids to compilation without DAGMC --- src/dagmc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 2847eee37db..f853f137443 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -872,6 +872,9 @@ extern "C" int openmc_get_dagmc_cell_ids( namespace openmc { +extern "C" int openmc_get_dagmc_cell_ids( + int32_t univ_id, int32_t* ids, size_t* n) {}; + void read_dagmc_universes(pugi::xml_node node) { if (check_for_node(node, "dagmc_universe")) { From 635f1f5087c00dc1b9125ddcc2addcd11ba416b9 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 12 Aug 2024 15:29:45 +0200 Subject: [PATCH 022/122] Adding some test for dagmc_split --- tests/unit_tests/dagmc/UseCaseBam.h5m | Bin 0 -> 158140 bytes tests/unit_tests/dagmc/test_model.py | 112 ++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100755 tests/unit_tests/dagmc/UseCaseBam.h5m create mode 100644 tests/unit_tests/dagmc/test_model.py diff --git a/tests/unit_tests/dagmc/UseCaseBam.h5m b/tests/unit_tests/dagmc/UseCaseBam.h5m new file mode 100755 index 0000000000000000000000000000000000000000..fe0fc67a8c89642238cf010f7b21540c88443ae9 GIT binary patch literal 158140 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 Date: Mon, 12 Aug 2024 16:02:00 +0200 Subject: [PATCH 023/122] put test restriction on dagmc model test if there is no DAGMC --- tests/unit_tests/dagmc/test_model.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 134137c776e..50ac3dcee41 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -4,6 +4,10 @@ from pathlib import Path import numpy as np +pytestmark = pytest.mark.skipif( + not openmc.lib._dagmc_enabled(), + reason="DAGMC CAD geometry is not enabled.") + def test_model_differentiate_with_DAGMC(): PITCH = 1.26 From 25d29a65cbf514cafc1ed573595b03533ad2ba96 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 12 Aug 2024 16:08:53 +0200 Subject: [PATCH 024/122] adding missing import --- tests/unit_tests/dagmc/test_model.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 50ac3dcee41..733b0b77459 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -1,8 +1,11 @@ -import openmc -from openmc import ZPlane, YPlane, XPlane, Cell import pkg_resources from pathlib import Path + import numpy as np +import pytest + +import openmc +from openmc import ZPlane, YPlane, XPlane, Cell pytestmark = pytest.mark.skipif( not openmc.lib._dagmc_enabled(), From f53ec5c8abcb09afe1c0028f164838395b7685f1 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:53:43 +0200 Subject: [PATCH 025/122] Apply suggestions from code review suggestions from @pshriwise Co-authored-by: Patrick Shriwise --- include/openmc/cell.h | 1 + openmc/geometry.py | 2 +- openmc/lib/dagmc.py | 2 -- openmc/model/model.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/openmc/cell.h b/include/openmc/cell.h index eec14b2aac5..9b6a97219f6 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -349,6 +349,7 @@ class Cell { vector offset_; //!< Distribcell offset table + // Accessors const GeometryType& geom_type() const { return geom_type_; } GeometryType& geom_type() { return geom_type_; } diff --git a/openmc/geometry.py b/openmc/geometry.py index a92ce12bb59..2b063f40dd0 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -405,7 +405,7 @@ def get_all_nuclides(self) -> list[str]: all_nuclides |= set(material.get_nuclides()) return sorted(all_nuclides) - def get_all_dag_universes(self) -> typing.Dict[int, openmc.DAGMCUniverse]: + def get_all_dagmc_universes(self) -> typing.Dict[int, openmc.DAGMCUniverse]: """Return all universes in the geometry. Returns diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index ab463d0c076..5a3324e0f6d 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -1,5 +1,4 @@ import sys - from ctypes import c_int, c_int32, POINTER, c_size_t import numpy as np @@ -8,7 +7,6 @@ from .error import _error_handler - # DAGMC functions _dll.openmc_get_dagmc_cell_ids.argtypes = [c_int32, POINTER(c_int32), POINTER(c_size_t)] _dll.openmc_get_dagmc_cell_ids.restype = c_int diff --git a/openmc/model/model.py b/openmc/model/model.py index 30582e55ba2..3dc44158365 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1174,7 +1174,7 @@ def differentiate_mats(self, f"{dag_verse.filename} not found in " f"the Model. Please add it to the " f"Model. Please note that uwuw " - f"formalism is not compatible with " + f"materials are not compatible with " f"differentiate_depletable_mats yet.") dag_verse.mat_assignment = deplete_mat_dict From 4b84f677c5abe0d5a3b86c77e61b511553ea25a7 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 22 Aug 2024 11:02:13 +0200 Subject: [PATCH 026/122] updatE --- include/openmc/dagmc.h | 2 +- openmc/cell.py | 4 +- openmc/geometry.py | 2 - openmc/model/model.py | 121 ++++---------------------------------- openmc/universe.py | 130 +++++++++++++++++++++++++++++++---------- src/dagmc.cpp | 15 ++--- 6 files changed, 121 insertions(+), 153 deletions(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 116a469ba72..7a47be7f27b 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -184,7 +184,7 @@ class DAGUniverse : public Universe { //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume - std::map> instance_mat_assignment; + std::map> instance_material_overrides; }; //============================================================================== diff --git a/openmc/cell.py b/openmc/cell.py index 133fa8480c8..ef2e25cb2ee 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -175,8 +175,6 @@ def fill(self, fill): f'non-Material or Universe fill "{fill}"') raise ValueError(msg) self._fill = fill - if isinstance(self.fill, openmc.DAGMCUniverse): - self.fill._num_instances += 1 # Info about atom content can now be invalid # (since fill has just changed) @@ -784,7 +782,7 @@ def from_xml_element(cls, elem, surfaces, materials, get_universe): return c -class DAGSpeudoCell(Cell): +class DAGMCCell(Cell): def __init__(self, cell_id=None, name='', fill=None, region=None): super().__init__(cell_id, name, fill, region) diff --git a/openmc/geometry.py b/openmc/geometry.py index 2b063f40dd0..6c49431b13e 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -754,8 +754,6 @@ def determine_paths(self, instances_only=False): for material in self.get_all_materials().values(): material._paths = [] material._num_instances = 0 - for dag_uni in self.get_all_dag_universes().values(): - dag_uni._num_instances = 0 # Recursively traverse the CSG tree to count all cell instances self.root_universe._determine_paths(instances_only=instances_only) diff --git a/openmc/model/model.py b/openmc/model/model.py index 3dc44158365..b6287faf76b 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -323,39 +323,13 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, # the user-provided intracomm which will either be None or an mpi4py # communicator openmc.lib.init(args=args, intracomm=intracomm, output=output) - self.sync_dagmc_cells() - def sync_dagmc_cells(self): - """Synchronize DAGMC cell information between Python and C API - - .. versionadded:: 0.13.0 - - """ - if not self.is_initialized: - raise RuntimeError("Model must be initialized via Model.init_lib " - "before calling this method.") - - import openmc.lib - if self.materials: mats = self.materials else: mats = self.geometry.get_all_materials().values() - mats_per_id = {mat.id: mat for mat in mats} - - for cell in self.geometry.get_all_cells().values(): - if isinstance(cell.fill, openmc.DAGMCUniverse): - for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(cell.fill.id, cell.fill._n_geom_elements('volume')): - dag_cell = openmc.lib.cells[dag_cell_id] - if isinstance(dag_cell.fill, Iterable): - fill = [mats_per_id[mat_id.id] - for mat_id in dag_cell.fill] - else: - fill = mats_per_id[dag_cell.fill.id] - dag_pseudo_cell = openmc.DAGSpeudoCell( - cell_id=dag_cell_id, fill=fill - ) - cell.fill.add_cell(dag_pseudo_cell) + for dagmc_universe in self.geometry.get_all_dagmc_universes().values(): + dagmc_universe.sync_dagmc_cells(mats) def finalize_lib(self): """Finalize simulation and free memory allocated for the C API @@ -1062,10 +1036,9 @@ def differentiate_depletable_mats(self, diff_volume_method: str): """ self.differentiate_mats(diff_volume_method, depletable_only=True) - def differentiate_mats(self, - diff_volume_method: str, - depletable_only: bool = True): - """Assign distribmats for each material + def differentiate_depletable_mats(self, diff_volume_method: str, + depletable_only: bool = True): + """Assign distribmats for each depletable material .. versionadded:: 0.14.0 @@ -1076,31 +1049,10 @@ def differentiate_mats(self, Default is to 'divide equally' which divides the original material volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. - depletable_ony : bool - Differentiate depletable materials only. """ - # Check if there is some DAGMC universe - if len(self.geometry.get_all_dag_universes()) > 0: - # Reset num_instances of models.materials - # (Updated from dag-verses appearance or from CSG presences...) - for mat in self._materials: - mat._num_instances = 0 - # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) - # Get mat instances from the different dag-verses - dag_mats_n_inst = {} - if len(self.geometry.get_all_dag_universes()) > 0: - # Get material in the DAG-verses - for dag_verse in self.geometry.get_all_dag_universes().values(): - for mat_name in dag_verse.material_names: - dag_mats_n_inst[mat_name] = dag_mats_n_inst.get(mat_name, 0) + dag_verse.num_instances - # Account for the multiplicity of the dag-verses materials - for mat in self.materials: - if mat.name in dag_mats_n_inst: - mat._num_instances += dag_mats_n_inst[mat.name] - # Extract all depletable materials which have multiple instances distribmats = set( [mat for mat in self.materials @@ -1121,68 +1073,19 @@ def differentiate_mats(self, if diff_volume_method == 'divide equally': cell.fill = [mat.clone() for _ in range(cell.num_instances)] elif diff_volume_method == 'match cell': - for _ in range(cell.num_instances): - cell.fill = mat.clone() + cell.fill = [mat.clone() for _ in range(cell.num_instances)] + for i in range(cell.num_instances): if not cell.volume: raise ValueError( f"Volume of cell ID={cell.id} not specified. " "Set volumes of cells prior to using " "diff_volume_method='match cell'." ) - cell.fill.volume = cell.volume - else: - for _ in range(cell.num_instances): - cell.fill = mat.clone() - - dag_verse_mats = [] - for dag_verse in self.geometry.get_all_dag_universes().values(): - if dag_verse.num_instances > 1: - deplete_mat_dict = {} - for mat_name in dag_verse.material_names: - mat_found = False - for mat in self.materials: - if mat.name == mat_name: - mat_found = True - if mat.depletable or not depletable_only: - mats_clones = [mat.clone() for _ in range(dag_verse.num_instances)] - for i, mat_clone in enumerate(mats_clones): - mat_clone.name += "_" + str(dag_verse.id) + "_" + str(i) - dag_verse_mats.append(mat_clone) - if diff_volume_method == 'match cell': - if self.is_initialized: - for cell in dag_verse._cells.values(): - if not isinstance(cell.fill, list): - if cell.fill.name == mat.name: - cell.fill = mat_clone - mat_clone.volume = cell.volume - elif cell.fill.name.split("_")[0] == mat.name: - mat_clone.volume = cell.volume - cell.fill = [cell.fill] - cell.fill.append(mat_clone) - else: - if cell.fill[0].name.split("_")[0] == mat.name: - mat_clone.volume = cell.volume - cell.fill.append(mat_clone) - else: - raise NotImplementedError("differentiate mats with DAGMC universe and match cell option is only available when cpp_lib is initialized") - deplete_mat_dict[mat_name.lower()] = [mat.name for mat in mats_clones] - else: - dag_verse_mats.append(mat) - if not mat_found: - raise ValueError(f"Material {mat_name} referenced in " - f"the dagmc Universe " - f"{dag_verse.filename} not found in " - f"the Model. Please add it to the " - f"Model. Please note that uwuw " - f"materials are not compatible with " - f"differentiate_depletable_mats yet.") - dag_verse.mat_assignment = deplete_mat_dict - + cell.fill[i].volume = cell.volume + if isinstance(cell, openmc.DAGMCCell): + cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" + if self.materials is not None: self.materials = openmc.Materials( self.geometry.get_all_materials().values() - ) - if dag_verse_mats: - self.materials.extend(dag_verse_mats) - self.materials = list(set(self.materials)) - \ No newline at end of file + ) \ No newline at end of file diff --git a/openmc/universe.py b/openmc/universe.py index d47acc8644b..fd0b93f12a5 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -717,10 +717,7 @@ def _determine_paths(self, path='', instances_only=False): # If universe-filled, recursively count cells in filling universe if fill_type == 'universe': - if isinstance(fill, openmc.DAGMCUniverse): - fill._num_instances += 1 - else: - fill._determine_paths(cell_path + '->', instances_only) + fill._determine_paths(cell_path + '->', instances_only) # If lattice-filled, recursively call for all universes in lattice elif fill_type == 'lattice': latt = fill @@ -816,8 +813,14 @@ class DAGMCUniverse(UniverseBase): n_surfaces : int The number of surfaces in the model. - .. versionadded:: 0.13.2 + .. versionadded:: 0.15 + mat_overrides : dict + A dictionary of material overrides. The keys are material names as + strings and the values are openmc.Material objects. If a material name + is found in the DAGMC file, the material will be replaced with the + openmc.Material object in the value. + """ def __init__(self, @@ -826,14 +829,13 @@ def __init__(self, name='', auto_geom_ids=False, auto_mat_ids=False, - mat_assignment={}): + mat_overrides={}): super().__init__(universe_id, name) # Initialize class attributes self.filename = filename self.auto_geom_ids = auto_geom_ids self.auto_mat_ids = auto_mat_ids - self.mat_assignment = mat_assignment - self._num_instances = 0 + self.material_overrides = mat_overrides self._dagmc_cells = [] def __repr__(self): @@ -842,6 +844,10 @@ def __repr__(self): string += '{: <16}=\t{}\n'.format('\tFile', self.filename) return string + @property + def cells(self): + return self._cells + @property def bounding_box(self): with h5py.File(self.filename) as dagmc_file: @@ -877,12 +883,6 @@ def auto_mat_ids(self, val): cv.check_type('DAGMC automatic material ids', val, bool) self._auto_mat_ids = val - @property - def material_assignment(self): - dagmc_file_contents = h5py.File(self.filename) - material_tags_hex = dagmc_file_contents['/tstt/tags/NAME'].get( - 'values') - @property def material_names(self): dagmc_file_contents = h5py.File(self.filename) @@ -976,14 +976,6 @@ def decode_str_tag(tag_val): n += 1 return n - @property - def num_instances(self): - if self._num_instances is None: - raise ValueError( - 'Number of dagmc instances have not been determined. Call the ' - 'Geometry.determine_paths() method.') - return self._num_instances - @property def n_cells(self): return self._n_geom_elements('volume') @@ -1010,14 +1002,66 @@ def create_xml_subelement(self, xml_element, memo=None): if self.auto_mat_ids: dagmc_element.set('auto_mat_ids', 'true') dagmc_element.set('filename', str(self.filename)) - if self.mat_assignment : - mat_element = ET.Element('mat_assignment') - for key in self.mat_assignment: + if len(self.material_overrides) == 0: + mats = self.get_all_materials() + for mat in mats.values(): + if mat.name[:-4] in self.material_names: + self.material_overrides.setdefault( + mat.name[:-4].lower(), []).append(mat.name) + print(f"Material {mat.name} found in DAGMC file, ") + print(self.material_overrides) + + if self.material_overrides: + mat_element = ET.Element('material_overrides') + for key in self.material_overrides: mat_element.set(key, ' '.join( - t for t in self.mat_assignment[key])) + t for t in self.material_overrides[key])) dagmc_element.append(mat_element) xml_element.append(dagmc_element) + def _determine_paths(self, path='', instances_only=False): + """Count the number of instances for each cell in the universe, and + record the count in the :attr:`Cell.num_instances` properties.""" + + univ_path = path + f'u{self.id}' + + for cell in self.cells.values(): + cell_path = f'{univ_path}->c{cell.id}' + fill = cell._fill + fill_type = cell.fill_type + + # If universe-filled, recursively count cells in filling universe + if fill_type == 'universe': + fill._determine_paths(cell_path + '->', instances_only) + # If lattice-filled, recursively call for all universes in lattice + elif fill_type == 'lattice': + latt = fill + + # Count instances in each universe in the lattice + for index in latt._natural_indices: + latt_path = '{}->l{}({})->'.format( + cell_path, latt.id, ",".join(str(x) for x in index)) + univ = latt.get_universe(index) + univ._determine_paths(latt_path, instances_only) + + else: + if fill_type == 'material': + mat = fill + elif fill_type == 'distribmat': + mat = fill[cell._num_instances] + else: + mat = None + + if mat is not None: + mat._num_instances += 1 + if not instances_only: + mat._paths.append(f'{cell_path}->m{mat.id}') + + # Append current path + cell._num_instances += 1 + if not instances_only: + cell._paths.append(cell_path) + def bounding_region( self, bounded_type: str = 'box', @@ -1189,9 +1233,9 @@ def add_cell(self, cell): """ - if not isinstance(cell, openmc.Cell): - msg = f'Unable to add a Cell to Universe ID="{self._id}" since ' \ - f'"{cell}" is not a Cell' + if not isinstance(cell, openmc.DAGMCCell): + msg = f'Unable to add a DAGMCCell to DAGMCUniverse ID="{self._id}" since ' \ + f'"{cell}" is not a DAGMCCell' raise TypeError(msg) cell_id = cell.id @@ -1210,7 +1254,7 @@ def add_cells(self, cells): """ if not isinstance(cells, Iterable): - msg = f'Unable to add Cells to Universe ID="{self._id}" since ' \ + msg = f'Unable to add DAGMCCells to DAGMCUniverse ID="{self._id}" since ' \ f'"{cells}" is not iterable' raise TypeError(msg) @@ -1238,4 +1282,28 @@ def remove_cell(self, cell): def clear_cells(self): """Remove all cells from the universe.""" - self._cells.clear() \ No newline at end of file + self._cells.clear() + + def sync_dagmc_cells(self, mats={}): + """Synchronize DAGMC cell information between Python and C API + + .. versionadded:: 0.13.0 + + """ + import openmc.lib + if not openmc.lib.is_initialized: + raise RuntimeError("Model must be initialized via Model.init_lib " + "before calling this method.") + + mats_per_id = {mat.id: mat for mat in mats} + for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(self.id, self._n_geom_elements('volume')): + dag_cell = openmc.lib.cells[dag_cell_id] + if isinstance(dag_cell.fill, Iterable): + fill = [mats_per_id[mat_id.id] + for mat_id in dag_cell.fill] + else: + fill = mats_per_id[dag_cell.fill.id] + dag_pseudo_cell = openmc.DAGMCCell( + cell_id=dag_cell_id, fill=fill + ) + self.add_cell(dag_pseudo_cell) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index f853f137443..584f586084c 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -73,8 +73,8 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) } // get material assignment overloading - if (check_for_node(node, "mat_assignment")) { - auto mat_node = node.child("mat_assignment"); + if (check_for_node(node, "material_overrides")) { + auto mat_node = node.child("material_overrides"); // loop over all attributes (each attribute corresponds to a material) for (pugi::xml_attribute attr = mat_node.first_attribute(); attr; attr = attr.next_attribute()) { @@ -87,9 +87,10 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) std::string value; while (iss >> value) instance_mats.push_back(value); + std::cout<<"DAGMC " << mat_ref_assignment << " " << value << std::endl; // Store mat name for each instances - instance_mat_assignment.insert( + instance_material_overrides.insert( std::make_pair(mat_ref_assignment, instance_mats)); } } @@ -235,11 +236,11 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { - if (instance_mat_assignment.size() > 0 and - instance_mat_assignment.find(mat_str) != - instance_mat_assignment.end()) { + if (instance_material_overrides.size() > 0 and + instance_material_overrides.find(mat_str) != + instance_material_overrides.end()) { - for (auto mat_str_instance : instance_mat_assignment.at(mat_str)) { + for (auto mat_str_instance : instance_material_overrides.at(mat_str)) { legacy_assign_material(mat_str_instance, c); } } else { From 657da23def268f77d1e857e742d2dd685829e85e Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 22 Aug 2024 11:02:23 +0200 Subject: [PATCH 027/122] formating --- src/dagmc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 584f586084c..594d9cf1bbb 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -87,7 +87,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) std::string value; while (iss >> value) instance_mats.push_back(value); - std::cout<<"DAGMC " << mat_ref_assignment << " " << value << std::endl; + std::cout << "DAGMC " << mat_ref_assignment << " " << value << std::endl; // Store mat name for each instances instance_material_overrides.insert( @@ -240,7 +240,8 @@ void DAGUniverse::init_geometry() instance_material_overrides.find(mat_str) != instance_material_overrides.end()) { - for (auto mat_str_instance : instance_material_overrides.at(mat_str)) { + for (auto mat_str_instance : + instance_material_overrides.at(mat_str)) { legacy_assign_material(mat_str_instance, c); } } else { From ca2efe3a72aa3200b8b77179cc5924d31b60ea8b Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 22 Aug 2024 12:53:54 +0200 Subject: [PATCH 028/122] cells now contain distribmat after differentiate with match_cell --- tests/unit_tests/test_deplete_coupled_operator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_deplete_coupled_operator.py b/tests/unit_tests/test_deplete_coupled_operator.py index fe79d621b12..8cfc9ca5a8b 100644 --- a/tests/unit_tests/test_deplete_coupled_operator.py +++ b/tests/unit_tests/test_deplete_coupled_operator.py @@ -97,8 +97,10 @@ def test_diff_volume_method_match_cell(model_with_volumes): ) all_cells = list(operator.model.geometry.get_all_cells().values()) - assert all_cells[0].fill.volume == 4.19 - assert all_cells[1].fill.volume == 33.51 + for mat in all_cells[0].fill: + assert mat.volume == 4.19 + for mat in all_cells[1].fill: + assert mat.volume == 33.51 # mat2 is not depletable assert all_cells[2].fill.volume is None From c40385473d10bbe67b484cf87a37eade4ead2037 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 26 Aug 2024 13:01:15 +0200 Subject: [PATCH 029/122] move dagmc related methods into dagmc.py, factorise Universes method --- openmc/__init__.py | 1 + openmc/cell.py | 78 ---- openmc/dagmc.py | 498 +++++++++++++++++++++++++ openmc/model/model.py | 2 +- openmc/universe.py | 846 ++++++++---------------------------------- 5 files changed, 653 insertions(+), 772 deletions(-) create mode 100644 openmc/dagmc.py diff --git a/openmc/__init__.py b/openmc/__init__.py index 566d287068f..bb972b4e6ad 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -15,6 +15,7 @@ from openmc.weight_windows import * from openmc.surface import * from openmc.universe import * +from openmc.dagmc import * from openmc.source import * from openmc.settings import * from openmc.lattice import * diff --git a/openmc/cell.py b/openmc/cell.py index ef2e25cb2ee..8cc5dee4a47 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -782,81 +782,3 @@ def from_xml_element(cls, elem, surfaces, materials, get_universe): return c -class DAGMCCell(Cell): - def __init__(self, cell_id=None, name='', fill=None, region=None): - super().__init__(cell_id, name, fill, region) - - @property - def DAG_parent_universe(self): - """Get the parent universe of the cell.""" - return self._parent_universe - - @DAG_parent_universe.setter - def DAG_parent_universe(self, universe): - """Set the parent universe of the cell.""" - self._parent_universe = universe.id - - def boundingbox(self): - print("Warning: Bounding box is not available for cells in a DAGMC universe.") - return {} - - def get_all_cells(self, memo=None): - return {} - - def get_all_materials(self, memo=None): - """Return all materials that are contained within the cell - - Returns - ------- - materials : dict - Dictionary whose keys are material IDs and values are - :class:`Material` instances - - """ - materials = {} - if self.fill_type == 'material': - materials[self.fill.id] = self.fill - elif self.fill_type == 'distribmat': - for m in self.fill: - if m is not None: - materials[m.id] = m - else: - # Append all Cells in each Cell in the Universe to the dictionary - cells = self.get_all_cells(memo) - for cell in cells.values(): - materials.update(cell.get_all_materials(memo)) - - return materials - - @property - def fill_type(self): - if isinstance(self.fill, openmc.Material): - return 'material' - elif isinstance(self.fill, openmc.UniverseBase): - return 'universe' - elif isinstance(self.fill, openmc.Lattice): - return 'lattice' - elif isinstance(self.fill, Iterable): - return 'distribmat' - else: - return 'void' - - def get_all_universes(self, memo=None): - return {} - - def clone(self, clone_materials=True, clone_regions=True, memo=None): - print("Warning: clone is not available for cells in a DAGMC universe.") - return None - - def plot(self, *args, **kwargs): - print("Warning: plot is not available for cells in a DAGMC universe.") - return None - - def create_xml_subelement(self, xml_element, memo=None): - print("Warning: create_xml_subelement is not available for cells in a DAGMC universe.") - return None - - @classmethod - def from_xml_element(cls, elem, surfaces, materials, get_universe): - print("Warning: from_xml_element is not available for cells in a DAGMC universe.") - return None diff --git a/openmc/dagmc.py b/openmc/dagmc.py new file mode 100644 index 00000000000..a484d916d9e --- /dev/null +++ b/openmc/dagmc.py @@ -0,0 +1,498 @@ +from collections.abc import Iterable +from numbers import Integral +from pathlib import Path + +import h5py +import lxml.etree as ET +import numpy as np + +import openmc +import openmc.checkvalue as cv +from ._xml import get_text +from .checkvalue import check_type, check_value +from .surface import _BOUNDARY_TYPES + + +class DAGMCUniverse(openmc.UniverseBase): + """A reference to a DAGMC file to be used in the model. + + .. versionadded:: 0.13.0 + + Parameters + ---------- + filename : str + Path to the DAGMC file used to represent this universe. + universe_id : int, optional + Unique identifier of the universe. If not specified, an identifier will + automatically be assigned. + name : str, optional + Name of the universe. If not specified, the name is the empty string. + auto_geom_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between CSG and DAGMC (False) + auto_mat_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between OpenMC and UWUW materials (False) + + Attributes + ---------- + id : int + Unique identifier of the universe + name : str + Name of the universe + filename : str + Path to the DAGMC file used to represent this universe. + auto_geom_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between CSG and DAGMC (False) + auto_mat_ids : bool + Set IDs automatically on initialization (True) or report overlaps in ID + space between OpenMC and UWUW materials (False) + bounding_box : openmc.BoundingBox + Lower-left and upper-right coordinates of an axis-aligned bounding box + of the universe. + + .. versionadded:: 0.13.1 + material_names : list of str + Return a sorted list of materials names that are contained within the + DAGMC h5m file. This is useful when naming openmc.Material() objects + as each material name present in the DAGMC h5m file must have a + matching openmc.Material() with the same name. + + .. versionadded:: 0.13.2 + n_cells : int + The number of cells in the DAGMC model. This is the number of cells at + runtime and accounts for the implicit complement whether or not is it + present in the DAGMC file. + + .. versionadded:: 0.13.2 + n_surfaces : int + The number of surfaces in the model. + + .. versionadded:: 0.15 + mat_overrides : dict + A dictionary of material overrides. The keys are material names as + strings and the values are openmc.Material objects. If a material name + is found in the DAGMC file, the material will be replaced with the + openmc.Material object in the value. + + + """ + + def __init__(self, + filename, + universe_id=None, + name='', + auto_geom_ids=False, + auto_mat_ids=False, + mat_overrides={}): + super().__init__(universe_id, name) + # Initialize class attributes + self.filename = filename + self.auto_geom_ids = auto_geom_ids + self.auto_mat_ids = auto_mat_ids + self.material_overrides = mat_overrides + self._dagmc_cells = [] + + def __repr__(self): + string = super().__repr__() + string += '{: <16}=\t{}\n'.format('\tGeom', 'DAGMC') + string += '{: <16}=\t{}\n'.format('\tFile', self.filename) + return string + + @property + def bounding_box(self): + with h5py.File(self.filename) as dagmc_file: + coords = dagmc_file['tstt']['nodes']['coordinates'][()] + lower_left_corner = coords.min(axis=0) + upper_right_corner = coords.max(axis=0) + return openmc.BoundingBox(lower_left_corner, upper_right_corner) + + @property + def filename(self): + return self._filename + + @filename.setter + def filename(self, val): + cv.check_type('DAGMC filename', val, (Path, str)) + self._filename = val + + @property + def auto_geom_ids(self): + return self._auto_geom_ids + + @auto_geom_ids.setter + def auto_geom_ids(self, val): + cv.check_type('DAGMC automatic geometry ids', val, bool) + self._auto_geom_ids = val + + @property + def auto_mat_ids(self): + return self._auto_mat_ids + + @auto_mat_ids.setter + def auto_mat_ids(self, val): + cv.check_type('DAGMC automatic material ids', val, bool) + self._auto_mat_ids = val + + @property + def material_names(self): + dagmc_file_contents = h5py.File(self.filename) + material_tags_hex = dagmc_file_contents['/tstt/tags/NAME'].get( + 'values') + material_tags_ascii = [] + for tag in material_tags_hex: + candidate_tag = tag.tobytes().decode().replace('\x00', '') + # tags might be for temperature or reflective surfaces + if candidate_tag.startswith('mat:'): + # if name ends with _comp remove it, it is not parsed + if candidate_tag.endswith('_comp'): + candidate_tag = candidate_tag[:-5] + # removes first 4 characters as openmc.Material name should be + # set without the 'mat:' part of the tag + material_tags_ascii.append(candidate_tag[4:]) + + return sorted(set(material_tags_ascii)) + + def _n_geom_elements(self, geom_type): + """ + Helper function for retrieving the number geometric entities in a DAGMC + file + + Parameters + ---------- + geom_type : str + The type of geometric entity to count. One of {'Volume', 'Surface'}. Returns + the runtime number of voumes in the DAGMC model (includes implicit complement). + + Returns + ------- + int + Number of geometry elements of the specified type + """ + cv.check_value('geometry type', geom_type, ('volume', 'surface')) + + def decode_str_tag(tag_val): + return tag_val.tobytes().decode().replace('\x00', '') + + dagmc_filepath = Path(self.filename).resolve() + with h5py.File(dagmc_filepath) as dagmc_file: + category_data = dagmc_file['tstt/tags/CATEGORY/values'] + category_strs = map(decode_str_tag, category_data) + n = sum([v == geom_type.capitalize() for v in category_strs]) + + # check for presence of an implicit complement in the file and + # increment the number of cells if it doesn't exist + if geom_type == 'volume': + name_data = dagmc_file['tstt/tags/NAME/values'] + name_strs = map(decode_str_tag, name_data) + if not sum(['impl_complement' in n for n in name_strs]): + n += 1 + return n + + @property + def n_cells(self): + return self._n_geom_elements('volume') + + @property + def n_surfaces(self): + return self._n_geom_elements('surface') + + def create_xml_subelement(self, xml_element, memo=None): + if memo is None: + memo = set() + + if self in memo: + return + + memo.add(self) + + # Set xml element values + dagmc_element = ET.Element('dagmc_universe') + dagmc_element.set('id', str(self.id)) + + if self.auto_geom_ids: + dagmc_element.set('auto_geom_ids', 'true') + if self.auto_mat_ids: + dagmc_element.set('auto_mat_ids', 'true') + dagmc_element.set('filename', str(self.filename)) + if len(self.material_overrides) == 0: + mats = self.get_all_materials() + for mat in mats.values(): + if mat.name[:-4] in self.material_names: + self.material_overrides.setdefault( + mat.name[:-4].lower(), []).append(mat.name) + print(f"Material {mat.name} found in DAGMC file, ") + + if self.material_overrides: + mat_element = ET.Element('material_overrides') + for key in self.material_overrides: + mat_element.set(key, ' '.join( + t for t in self.material_overrides[key])) + dagmc_element.append(mat_element) + xml_element.append(dagmc_element) + + def bounding_region( + self, + bounded_type: str = 'box', + boundary_type: str = 'vacuum', + starting_id: int = 10000, + padding_distance: float = 0. + ): + """Creates a either a spherical or box shaped bounding region around + the DAGMC geometry. + + .. versionadded:: 0.13.1 + + Parameters + ---------- + bounded_type : str + The type of bounding surface(s) to use when constructing the region. + Options include a single spherical surface (sphere) or a rectangle + made from six planes (box). + boundary_type : str + Boundary condition that defines the behavior for particles hitting + the surface. Defaults to vacuum boundary condition. Passed into the + surface construction. + starting_id : int + Starting ID of the surface(s) used in the region. For bounded_type + 'box', the next 5 IDs will also be used. Defaults to 10000 to reduce + the chance of an overlap of surface IDs with the DAGMC geometry. + padding_distance : float + Distance between the bounding region surfaces and the minimal + bounding box. Allows for the region to be larger than the DAGMC + geometry. + + Returns + ------- + openmc.Region + Region instance + """ + + check_type('boundary type', boundary_type, str) + check_value('boundary type', boundary_type, _BOUNDARY_TYPES) + check_type('starting surface id', starting_id, Integral) + check_type('bounded type', bounded_type, str) + check_value('bounded type', bounded_type, ('box', 'sphere')) + + bbox = self.bounding_box.expand(padding_distance, True) + + if bounded_type == 'sphere': + radius = np.linalg.norm(bbox.upper_right - bbox.center) + bounding_surface = openmc.Sphere( + surface_id=starting_id, + x0=bbox.center[0], + y0=bbox.center[1], + z0=bbox.center[2], + boundary_type=boundary_type, + r=radius, + ) + + return -bounding_surface + + if bounded_type == 'box': + # defines plane surfaces for all six faces of the bounding box + lower_x = openmc.XPlane(bbox[0][0], surface_id=starting_id) + upper_x = openmc.XPlane(bbox[1][0], surface_id=starting_id+1) + lower_y = openmc.YPlane(bbox[0][1], surface_id=starting_id+2) + upper_y = openmc.YPlane(bbox[1][1], surface_id=starting_id+3) + lower_z = openmc.ZPlane(bbox[0][2], surface_id=starting_id+4) + upper_z = openmc.ZPlane(bbox[1][2], surface_id=starting_id+5) + + region = +lower_x & -upper_x & +lower_y & -upper_y & +lower_z & -upper_z + + for surface in region.get_surfaces().values(): + surface.boundary_type = boundary_type + + return region + + def bounded_universe(self, bounding_cell_id=10000, **kwargs): + """Returns an openmc.Universe filled with this DAGMCUniverse and bounded + with a cell. Defaults to a box cell with a vacuum surface however this + can be changed using the kwargs which are passed directly to + DAGMCUniverse.bounding_region(). + + Parameters + ---------- + bounding_cell_id : int + The cell ID number to use for the bounding cell, defaults to 10000 to reduce + the chance of overlapping ID numbers with the DAGMC geometry. + + Returns + ------- + openmc.Universe + Universe instance + """ + bounding_cell = openmc.Cell( + fill=self, cell_id=bounding_cell_id, region=self.bounding_region(**kwargs)) + return openmc.Universe(cells=[bounding_cell]) + + @classmethod + def from_hdf5(cls, group): + """Create DAGMC universe from HDF5 group + + Parameters + ---------- + group : h5py.Group + Group in HDF5 file + + Returns + ------- + openmc.DAGMCUniverse + DAGMCUniverse instance + + """ + id = int(group.name.split('/')[-1].lstrip('universe ')) + fname = group['filename'][()].decode() + name = group['name'][()].decode() if 'name' in group else None + + out = cls(fname, universe_id=id, name=name) + + out.auto_geom_ids = bool(group.attrs['auto_geom_ids']) + out.auto_mat_ids = bool(group.attrs['auto_mat_ids']) + + return out + + @classmethod + def from_xml_element(cls, elem): + """Generate DAGMC universe from XML element + + Parameters + ---------- + elem : lxml.etree._Element + `` element + + Returns + ------- + openmc.DAGMCUniverse + DAGMCUniverse instance + + """ + id = int(get_text(elem, 'id')) + fname = get_text(elem, 'filename') + + out = cls(fname, universe_id=id) + + name = get_text(elem, 'name') + if name is not None: + out.name = name + + out.auto_geom_ids = bool(elem.get('auto_geom_ids')) + out.auto_mat_ids = bool(elem.get('auto_mat_ids')) + + return out + + def _partial_deepcopy(self): + """Clone all of the openmc.DAGMCUniverse object's attributes except for + its cells, as they are copied within the clone function. This should + only to be used within the openmc.UniverseBase.clone() context. + """ + clone = openmc.DAGMCUniverse(name=self.name, filename=self.filename) + clone.volume = self.volume + clone.auto_geom_ids = self.auto_geom_ids + clone.auto_mat_ids = self.auto_mat_ids + return clone + + def add_cell(self, cell): + """Add a cell to the universe. + + Parameters + ---------- + cell : openmc.DAGMCCell + Cell to add + + """ + if not isinstance(cell, openmc.DAGMCCell): + msg = f'Unable to add a DAGMCCell to DAGMCUniverse ID="{self._id}" since ' \ + f'"{cell}" is not a DAGMCCell' + raise TypeError(msg) + + cell_id = cell.id + + if cell_id not in self._cells: + self._cells[cell_id] = cell + + def remove_cell(self, cell): + """Remove a cell from the universe. + + Parameters + ---------- + cell : openmc.Cell + Cell to remove + + """ + + if not isinstance(cell, openmc.DAGMCCell): + msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ + f'since "{cell}" is not a Cell' + raise TypeError(msg) + + # If the Cell is in the Universe's list of Cells, delete it + self._cells.pop(cell.id, None) + + def sync_dagmc_cells(self, mats={}): + """Synchronize DAGMC cell information between Python and C API + + .. versionadded:: 0.13.0 + + """ + import openmc.lib + if not openmc.lib.is_initialized: + raise RuntimeError("Model must be initialized via Model.init_lib " + "before calling this method.") + + mats_per_id = {mat.id: mat for mat in mats} + for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(self.id, self._n_geom_elements('volume')): + dag_cell = openmc.lib.cells[dag_cell_id] + if isinstance(dag_cell.fill, Iterable): + fill = [mats_per_id[mat_id.id] + for mat_id in dag_cell.fill] + else: + fill = mats_per_id[dag_cell.fill.id] + dag_pseudo_cell = openmc.DAGMCCell( + cell_id=dag_cell_id, fill=fill + ) + self.add_cell(dag_pseudo_cell) + + +class DAGMCCell(openmc.Cell): + def __init__(self, cell_id=None, name='', fill=None, region=None): + super().__init__(cell_id, name, fill, region) + + @property + def DAG_parent_universe(self): + """Get the parent universe of the cell.""" + return self._parent_universe + + @DAG_parent_universe.setter + def DAG_parent_universe(self, universe): + """Set the parent universe of the cell.""" + self._parent_universe = universe.id + + def boundingbox(self): + print("Warning: Bounding box is not available for cells in a DAGMC universe.") + return {} + + def get_all_cells(self, memo=None): + return {} + + def get_all_universes(self, memo=None): + return {} + + def clone(self, clone_materials=True, clone_regions=True, memo=None): + print("Warning: clone is not available for cells in a DAGMC universe.") + return None + + def plot(self, *args, **kwargs): + print("Warning: plot is not available for cells in a DAGMC universe.") + return None + + def create_xml_subelement(self, xml_element, memo=None): + print( + "Warning: create_xml_subelement is not available for cells in a DAGMC universe.") + return None + + @classmethod + def from_xml_element(cls, elem, surfaces, materials, get_universe): + print("Warning: from_xml_element is not available for cells in a DAGMC universe.") + return None diff --git a/openmc/model/model.py b/openmc/model/model.py index b6287faf76b..c2714f36fff 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1036,7 +1036,7 @@ def differentiate_depletable_mats(self, diff_volume_method: str): """ self.differentiate_mats(diff_volume_method, depletable_only=True) - def differentiate_depletable_mats(self, diff_volume_method: str, + def differentiate_mats(self, diff_volume_method: str, depletable_only: bool = True): """Assign distribmats for each depletable material diff --git a/openmc/universe.py b/openmc/universe.py index fd0b93f12a5..6735d95e447 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -53,6 +53,10 @@ def __repr__(self): def name(self): return self._name + @property + def cells(self): + return self._cells + @name.setter def name(self, name): if name is not None: @@ -133,6 +137,130 @@ def create_xml_subelement(self, xml_element, memo=None): """ + def _determine_paths(self, path='', instances_only=False): + """Count the number of instances for each cell in the universe, and + record the count in the :attr:`Cell.num_instances` properties.""" + + univ_path = path + f'u{self.id}' + + for cell in self.cells.values(): + cell_path = f'{univ_path}->c{cell.id}' + fill = cell._fill + fill_type = cell.fill_type + + # If universe-filled, recursively count cells in filling universe + if fill_type == 'universe': + fill._determine_paths(cell_path + '->', instances_only) + # If lattice-filled, recursively call for all universes in lattice + elif fill_type == 'lattice': + latt = fill + + # Count instances in each universe in the lattice + for index in latt._natural_indices: + latt_path = '{}->l{}({})->'.format( + cell_path, latt.id, ",".join(str(x) for x in index)) + univ = latt.get_universe(index) + univ._determine_paths(latt_path, instances_only) + + else: + if fill_type == 'material': + mat = fill + elif fill_type == 'distribmat': + mat = fill[cell._num_instances] + else: + mat = None + + if mat is not None: + mat._num_instances += 1 + if not instances_only: + mat._paths.append(f'{cell_path}->m{mat.id}') + + # Append current path + cell._num_instances += 1 + if not instances_only: + cell._paths.append(cell_path) + + def add_cells(self, cells): + """Add multiple cells to the universe. + + Parameters + ---------- + cells : Iterable of openmc.Cell + Cells to add + + """ + + if not isinstance(cells, Iterable): + msg = f'Unable to add Cells to Universe ID="{self._id}" since ' \ + f'"{cells}" is not iterable' + raise TypeError(msg) + + for cell in cells: + self.add_cell(cell) + + @abstractmethod + def add_cell(self, cell): + pass + + @abstractmethod + def remove_cell(self, cell): + pass + + def clear_cells(self): + """Remove all cells from the universe.""" + + self._cells.clear() + + def get_all_cells(self, memo=None): + """Return all cells that are contained within the universe + + Returns + ------- + cells : dict + Dictionary whose keys are cell IDs and values are :class:`Cell` + instances + + """ + + if memo is None: + memo = set() + elif self in memo: + return {} + memo.add(self) + + # Add this Universe's cells to the dictionary + cells = {} + cells.update(self._cells) + + # Append all Cells in each Cell in the Universe to the dictionary + for cell in self._cells.values(): + cells.update(cell.get_all_cells(memo)) + + return cells + + def get_all_materials(self, memo=None): + """Return all materials that are contained within the universe + + Returns + ------- + materials : dict + Dictionary whose keys are material IDs and values are + :class:`Material` instances + + """ + + if memo is None: + memo = set() + + materials = {} + + # Append all Cells in each Cell in the Universe to the dictionary + cells = self.get_all_cells(memo) + for cell in cells.values(): + materials.update(cell.get_all_materials(memo)) + + return materials + @abstractmethod def _partial_deepcopy(self): """Deepcopy all parameters of an openmc.UniverseBase object except its cells. @@ -225,9 +353,6 @@ def __repr__(self): string += '{: <16}=\t{}\n'.format('\tCells', list(self._cells.keys())) return string - @property - def cells(self): - return self._cells @property def bounding_box(self): @@ -526,67 +651,6 @@ def plot(self, origin=None, width=None, pixels=40000, axes.imshow(img, extent=(x_min, x_max, y_min, y_max), **kwargs) return axes - def add_cell(self, cell): - """Add a cell to the universe. - - Parameters - ---------- - cell : openmc.Cell - Cell to add - - """ - - if not isinstance(cell, openmc.Cell): - msg = f'Unable to add a Cell to Universe ID="{self._id}" since ' \ - f'"{cell}" is not a Cell' - raise TypeError(msg) - - cell_id = cell.id - - if cell_id not in self._cells: - self._cells[cell_id] = cell - - def add_cells(self, cells): - """Add multiple cells to the universe. - - Parameters - ---------- - cells : Iterable of openmc.Cell - Cells to add - - """ - - if not isinstance(cells, Iterable): - msg = f'Unable to add Cells to Universe ID="{self._id}" since ' \ - f'"{cells}" is not iterable' - raise TypeError(msg) - - for cell in cells: - self.add_cell(cell) - - def remove_cell(self, cell): - """Remove a cell from the universe. - - Parameters - ---------- - cell : openmc.Cell - Cell to remove - - """ - - if not isinstance(cell, openmc.Cell): - msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ - f'since "{cell}" is not a Cell' - raise TypeError(msg) - - # If the Cell is in the Universe's list of Cells, delete it - self._cells.pop(cell.id, None) - - def clear_cells(self): - """Remove all cells from the universe.""" - - self._cells.clear() - def get_nuclides(self): """Returns all nuclides in the universe @@ -634,55 +698,43 @@ def get_nuclide_densities(self): return nuclides - def get_all_cells(self, memo=None): - """Return all cells that are contained within the universe + def add_cell(self, cell): + """Add a cell to the universe. - Returns - ------- - cells : dict - Dictionary whose keys are cell IDs and values are :class:`Cell` - instances + Parameters + ---------- + cell : openmc.Cell + Cell to add """ - if memo is None: - memo = set() - elif self in memo: - return {} - memo.add(self) - - # Add this Universe's cells to the dictionary - cells = {} - cells.update(self._cells) + if not isinstance(cell, openmc.Cell): + msg = f'Unable to add a Cell to Universe ID="{self._id}" since ' \ + f'"{cell}" is not a Cell' + raise TypeError(msg) - # Append all Cells in each Cell in the Universe to the dictionary - for cell in self._cells.values(): - cells.update(cell.get_all_cells(memo)) + cell_id = cell.id - return cells + if cell_id not in self._cells: + self._cells[cell_id] = cell - def get_all_materials(self, memo=None): - """Return all materials that are contained within the universe + def remove_cell(self, cell): + """Remove a cell from the universe. - Returns - ------- - materials : dict - Dictionary whose keys are material IDs and values are - :class:`Material` instances + Parameters + ---------- + cell : openmc.Cell + Cell to remove """ - if memo is None: - memo = set() - - materials = {} - - # Append all Cells in each Cell in the Universe to the dictionary - cells = self.get_all_cells(memo) - for cell in cells.values(): - materials.update(cell.get_all_materials(memo)) + if not isinstance(cell, openmc.Cell): + msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ + f'since "{cell}" is not a Cell' + raise TypeError(msg) - return materials + # If the Cell is in the Universe's list of Cells, delete it + self._cells.pop(cell.id, None) def create_xml_subelement(self, xml_element, memo=None): if memo is None: @@ -704,48 +756,6 @@ def create_xml_subelement(self, xml_element, memo=None): cell_element.set("universe", str(self._id)) xml_element.append(cell_element) - def _determine_paths(self, path='', instances_only=False): - """Count the number of instances for each cell in the universe, and - record the count in the :attr:`Cell.num_instances` properties.""" - - univ_path = path + f'u{self.id}' - - for cell in self.cells.values(): - cell_path = f'{univ_path}->c{cell.id}' - fill = cell._fill - fill_type = cell.fill_type - - # If universe-filled, recursively count cells in filling universe - if fill_type == 'universe': - fill._determine_paths(cell_path + '->', instances_only) - # If lattice-filled, recursively call for all universes in lattice - elif fill_type == 'lattice': - latt = fill - - # Count instances in each universe in the lattice - for index in latt._natural_indices: - latt_path = '{}->l{}({})->'.format( - cell_path, latt.id, ",".join(str(x) for x in index)) - univ = latt.get_universe(index) - univ._determine_paths(latt_path, instances_only) - - else: - if fill_type == 'material': - mat = fill - elif fill_type == 'distribmat': - mat = fill[cell._num_instances] - else: - mat = None - - if mat is not None: - mat._num_instances += 1 - if not instances_only: - mat._paths.append(f'{cell_path}->m{mat.id}') - - # Append current path - cell._num_instances += 1 - if not instances_only: - cell._paths.append(cell_path) def _partial_deepcopy(self): """Clone all of the openmc.Universe object's attributes except for its cells, @@ -757,553 +767,3 @@ def _partial_deepcopy(self): return clone -class DAGMCUniverse(UniverseBase): - """A reference to a DAGMC file to be used in the model. - - .. versionadded:: 0.13.0 - - Parameters - ---------- - filename : str - Path to the DAGMC file used to represent this universe. - universe_id : int, optional - Unique identifier of the universe. If not specified, an identifier will - automatically be assigned. - name : str, optional - Name of the universe. If not specified, the name is the empty string. - auto_geom_ids : bool - Set IDs automatically on initialization (True) or report overlaps in ID - space between CSG and DAGMC (False) - auto_mat_ids : bool - Set IDs automatically on initialization (True) or report overlaps in ID - space between OpenMC and UWUW materials (False) - - Attributes - ---------- - id : int - Unique identifier of the universe - name : str - Name of the universe - filename : str - Path to the DAGMC file used to represent this universe. - auto_geom_ids : bool - Set IDs automatically on initialization (True) or report overlaps in ID - space between CSG and DAGMC (False) - auto_mat_ids : bool - Set IDs automatically on initialization (True) or report overlaps in ID - space between OpenMC and UWUW materials (False) - bounding_box : openmc.BoundingBox - Lower-left and upper-right coordinates of an axis-aligned bounding box - of the universe. - - .. versionadded:: 0.13.1 - material_names : list of str - Return a sorted list of materials names that are contained within the - DAGMC h5m file. This is useful when naming openmc.Material() objects - as each material name present in the DAGMC h5m file must have a - matching openmc.Material() with the same name. - - .. versionadded:: 0.13.2 - n_cells : int - The number of cells in the DAGMC model. This is the number of cells at - runtime and accounts for the implicit complement whether or not is it - present in the DAGMC file. - - .. versionadded:: 0.13.2 - n_surfaces : int - The number of surfaces in the model. - - .. versionadded:: 0.15 - mat_overrides : dict - A dictionary of material overrides. The keys are material names as - strings and the values are openmc.Material objects. If a material name - is found in the DAGMC file, the material will be replaced with the - openmc.Material object in the value. - - - """ - - def __init__(self, - filename, - universe_id=None, - name='', - auto_geom_ids=False, - auto_mat_ids=False, - mat_overrides={}): - super().__init__(universe_id, name) - # Initialize class attributes - self.filename = filename - self.auto_geom_ids = auto_geom_ids - self.auto_mat_ids = auto_mat_ids - self.material_overrides = mat_overrides - self._dagmc_cells = [] - - def __repr__(self): - string = super().__repr__() - string += '{: <16}=\t{}\n'.format('\tGeom', 'DAGMC') - string += '{: <16}=\t{}\n'.format('\tFile', self.filename) - return string - - @property - def cells(self): - return self._cells - - @property - def bounding_box(self): - with h5py.File(self.filename) as dagmc_file: - coords = dagmc_file['tstt']['nodes']['coordinates'][()] - lower_left_corner = coords.min(axis=0) - upper_right_corner = coords.max(axis=0) - return openmc.BoundingBox(lower_left_corner, upper_right_corner) - - @property - def filename(self): - return self._filename - - @filename.setter - def filename(self, val): - cv.check_type('DAGMC filename', val, (Path, str)) - self._filename = val - - @property - def auto_geom_ids(self): - return self._auto_geom_ids - - @auto_geom_ids.setter - def auto_geom_ids(self, val): - cv.check_type('DAGMC automatic geometry ids', val, bool) - self._auto_geom_ids = val - - @property - def auto_mat_ids(self): - return self._auto_mat_ids - - @auto_mat_ids.setter - def auto_mat_ids(self, val): - cv.check_type('DAGMC automatic material ids', val, bool) - self._auto_mat_ids = val - - @property - def material_names(self): - dagmc_file_contents = h5py.File(self.filename) - material_tags_hex = dagmc_file_contents['/tstt/tags/NAME'].get( - 'values') - material_tags_ascii = [] - for tag in material_tags_hex: - candidate_tag = tag.tobytes().decode().replace('\x00', '') - # tags might be for temperature or reflective surfaces - if candidate_tag.startswith('mat:'): - # if name ends with _comp remove it, it is not parsed - if candidate_tag.endswith('_comp'): - candidate_tag = candidate_tag[:-5] - # removes first 4 characters as openmc.Material name should be - # set without the 'mat:' part of the tag - material_tags_ascii.append(candidate_tag[4:]) - - return sorted(set(material_tags_ascii)) - - def get_all_cells(self, memo=None): - """Return all cells that are contained within the universe - - Returns - ------- - cells : dict - Dictionary whose keys are cell IDs and values are :class:`Cell` - instances - - """ - - if memo is None: - memo = set() - elif self in memo: - return {} - memo.add(self) - - # Add this Universe's cells to the dictionary - cells = {} - cells.update(self._cells) - - # Append all Cells in each Cell in the Universe to the dictionary - for cell in self._cells.values(): - cells.update(cell.get_all_cells(memo)) - return cells - - def get_all_materials(self, memo=None): - if memo is None: - memo = set() - - materials = {} - - # Append all Cells in each Cell in the Universe to the dictionary - cells = self.get_all_cells(memo) - for cell in cells.values(): - materials.update(cell.get_all_materials(memo)) - return materials - - def _n_geom_elements(self, geom_type): - """ - Helper function for retrieving the number geometric entities in a DAGMC - file - - Parameters - ---------- - geom_type : str - The type of geometric entity to count. One of {'Volume', 'Surface'}. Returns - the runtime number of voumes in the DAGMC model (includes implicit complement). - - Returns - ------- - int - Number of geometry elements of the specified type - """ - cv.check_value('geometry type', geom_type, ('volume', 'surface')) - - def decode_str_tag(tag_val): - return tag_val.tobytes().decode().replace('\x00', '') - - dagmc_filepath = Path(self.filename).resolve() - with h5py.File(dagmc_filepath) as dagmc_file: - category_data = dagmc_file['tstt/tags/CATEGORY/values'] - category_strs = map(decode_str_tag, category_data) - n = sum([v == geom_type.capitalize() for v in category_strs]) - - # check for presence of an implicit complement in the file and - # increment the number of cells if it doesn't exist - if geom_type == 'volume': - name_data = dagmc_file['tstt/tags/NAME/values'] - name_strs = map(decode_str_tag, name_data) - if not sum(['impl_complement' in n for n in name_strs]): - n += 1 - return n - - @property - def n_cells(self): - return self._n_geom_elements('volume') - - @property - def n_surfaces(self): - return self._n_geom_elements('surface') - - def create_xml_subelement(self, xml_element, memo=None): - if memo is None: - memo = set() - - if self in memo: - return - - memo.add(self) - - # Set xml element values - dagmc_element = ET.Element('dagmc_universe') - dagmc_element.set('id', str(self.id)) - - if self.auto_geom_ids: - dagmc_element.set('auto_geom_ids', 'true') - if self.auto_mat_ids: - dagmc_element.set('auto_mat_ids', 'true') - dagmc_element.set('filename', str(self.filename)) - if len(self.material_overrides) == 0: - mats = self.get_all_materials() - for mat in mats.values(): - if mat.name[:-4] in self.material_names: - self.material_overrides.setdefault( - mat.name[:-4].lower(), []).append(mat.name) - print(f"Material {mat.name} found in DAGMC file, ") - print(self.material_overrides) - - if self.material_overrides: - mat_element = ET.Element('material_overrides') - for key in self.material_overrides: - mat_element.set(key, ' '.join( - t for t in self.material_overrides[key])) - dagmc_element.append(mat_element) - xml_element.append(dagmc_element) - - def _determine_paths(self, path='', instances_only=False): - """Count the number of instances for each cell in the universe, and - record the count in the :attr:`Cell.num_instances` properties.""" - - univ_path = path + f'u{self.id}' - - for cell in self.cells.values(): - cell_path = f'{univ_path}->c{cell.id}' - fill = cell._fill - fill_type = cell.fill_type - - # If universe-filled, recursively count cells in filling universe - if fill_type == 'universe': - fill._determine_paths(cell_path + '->', instances_only) - # If lattice-filled, recursively call for all universes in lattice - elif fill_type == 'lattice': - latt = fill - - # Count instances in each universe in the lattice - for index in latt._natural_indices: - latt_path = '{}->l{}({})->'.format( - cell_path, latt.id, ",".join(str(x) for x in index)) - univ = latt.get_universe(index) - univ._determine_paths(latt_path, instances_only) - - else: - if fill_type == 'material': - mat = fill - elif fill_type == 'distribmat': - mat = fill[cell._num_instances] - else: - mat = None - - if mat is not None: - mat._num_instances += 1 - if not instances_only: - mat._paths.append(f'{cell_path}->m{mat.id}') - - # Append current path - cell._num_instances += 1 - if not instances_only: - cell._paths.append(cell_path) - - def bounding_region( - self, - bounded_type: str = 'box', - boundary_type: str = 'vacuum', - starting_id: int = 10000, - padding_distance: float = 0. - ): - """Creates a either a spherical or box shaped bounding region around - the DAGMC geometry. - - .. versionadded:: 0.13.1 - - Parameters - ---------- - bounded_type : str - The type of bounding surface(s) to use when constructing the region. - Options include a single spherical surface (sphere) or a rectangle - made from six planes (box). - boundary_type : str - Boundary condition that defines the behavior for particles hitting - the surface. Defaults to vacuum boundary condition. Passed into the - surface construction. - starting_id : int - Starting ID of the surface(s) used in the region. For bounded_type - 'box', the next 5 IDs will also be used. Defaults to 10000 to reduce - the chance of an overlap of surface IDs with the DAGMC geometry. - padding_distance : float - Distance between the bounding region surfaces and the minimal - bounding box. Allows for the region to be larger than the DAGMC - geometry. - - Returns - ------- - openmc.Region - Region instance - """ - - check_type('boundary type', boundary_type, str) - check_value('boundary type', boundary_type, _BOUNDARY_TYPES) - check_type('starting surface id', starting_id, Integral) - check_type('bounded type', bounded_type, str) - check_value('bounded type', bounded_type, ('box', 'sphere')) - - bbox = self.bounding_box.expand(padding_distance, True) - - if bounded_type == 'sphere': - radius = np.linalg.norm(bbox.upper_right - bbox.center) - bounding_surface = openmc.Sphere( - surface_id=starting_id, - x0=bbox.center[0], - y0=bbox.center[1], - z0=bbox.center[2], - boundary_type=boundary_type, - r=radius, - ) - - return -bounding_surface - - if bounded_type == 'box': - # defines plane surfaces for all six faces of the bounding box - lower_x = openmc.XPlane(bbox[0][0], surface_id=starting_id) - upper_x = openmc.XPlane(bbox[1][0], surface_id=starting_id+1) - lower_y = openmc.YPlane(bbox[0][1], surface_id=starting_id+2) - upper_y = openmc.YPlane(bbox[1][1], surface_id=starting_id+3) - lower_z = openmc.ZPlane(bbox[0][2], surface_id=starting_id+4) - upper_z = openmc.ZPlane(bbox[1][2], surface_id=starting_id+5) - - region = +lower_x & -upper_x & +lower_y & -upper_y & +lower_z & -upper_z - - for surface in region.get_surfaces().values(): - surface.boundary_type = boundary_type - - return region - - def bounded_universe(self, bounding_cell_id=10000, **kwargs): - """Returns an openmc.Universe filled with this DAGMCUniverse and bounded - with a cell. Defaults to a box cell with a vacuum surface however this - can be changed using the kwargs which are passed directly to - DAGMCUniverse.bounding_region(). - - Parameters - ---------- - bounding_cell_id : int - The cell ID number to use for the bounding cell, defaults to 10000 to reduce - the chance of overlapping ID numbers with the DAGMC geometry. - - Returns - ------- - openmc.Universe - Universe instance - """ - bounding_cell = openmc.Cell( - fill=self, cell_id=bounding_cell_id, region=self.bounding_region(**kwargs)) - return openmc.Universe(cells=[bounding_cell]) - - @classmethod - def from_hdf5(cls, group): - """Create DAGMC universe from HDF5 group - - Parameters - ---------- - group : h5py.Group - Group in HDF5 file - - Returns - ------- - openmc.DAGMCUniverse - DAGMCUniverse instance - - """ - id = int(group.name.split('/')[-1].lstrip('universe ')) - fname = group['filename'][()].decode() - name = group['name'][()].decode() if 'name' in group else None - - out = cls(fname, universe_id=id, name=name) - - out.auto_geom_ids = bool(group.attrs['auto_geom_ids']) - out.auto_mat_ids = bool(group.attrs['auto_mat_ids']) - - return out - - @classmethod - def from_xml_element(cls, elem): - """Generate DAGMC universe from XML element - - Parameters - ---------- - elem : lxml.etree._Element - `` element - - Returns - ------- - openmc.DAGMCUniverse - DAGMCUniverse instance - - """ - id = int(get_text(elem, 'id')) - fname = get_text(elem, 'filename') - - out = cls(fname, universe_id=id) - - name = get_text(elem, 'name') - if name is not None: - out.name = name - - out.auto_geom_ids = bool(elem.get('auto_geom_ids')) - out.auto_mat_ids = bool(elem.get('auto_mat_ids')) - - return out - - def _partial_deepcopy(self): - """Clone all of the openmc.DAGMCUniverse object's attributes except for - its cells, as they are copied within the clone function. This should - only to be used within the openmc.UniverseBase.clone() context. - """ - clone = openmc.DAGMCUniverse(name=self.name, filename=self.filename) - clone.volume = self.volume - clone.auto_geom_ids = self.auto_geom_ids - clone.auto_mat_ids = self.auto_mat_ids - return clone - - def add_cell(self, cell): - """Add a cell to the universe. - - Parameters - ---------- - cell : openmc.Cell - Cell to add - - """ - - if not isinstance(cell, openmc.DAGMCCell): - msg = f'Unable to add a DAGMCCell to DAGMCUniverse ID="{self._id}" since ' \ - f'"{cell}" is not a DAGMCCell' - raise TypeError(msg) - - cell_id = cell.id - - if cell_id not in self._cells: - self._cells[cell_id] = cell - - def add_cells(self, cells): - """Add multiple cells to the universe. - - Parameters - ---------- - cells : Iterable of openmc.Cell - Cells to add - - """ - - if not isinstance(cells, Iterable): - msg = f'Unable to add DAGMCCells to DAGMCUniverse ID="{self._id}" since ' \ - f'"{cells}" is not iterable' - raise TypeError(msg) - - for cell in cells: - self.add_cell(cell) - - def remove_cell(self, cell): - """Remove a cell from the universe. - - Parameters - ---------- - cell : openmc.Cell - Cell to remove - - """ - - if not isinstance(cell, openmc.Cell): - msg = f'Unable to remove a Cell from Universe ID="{self._id}" ' \ - f'since "{cell}" is not a Cell' - raise TypeError(msg) - - # If the Cell is in the Universe's list of Cells, delete it - self._cells.pop(cell.id, None) - - def clear_cells(self): - """Remove all cells from the universe.""" - - self._cells.clear() - - def sync_dagmc_cells(self, mats={}): - """Synchronize DAGMC cell information between Python and C API - - .. versionadded:: 0.13.0 - - """ - import openmc.lib - if not openmc.lib.is_initialized: - raise RuntimeError("Model must be initialized via Model.init_lib " - "before calling this method.") - - mats_per_id = {mat.id: mat for mat in mats} - for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(self.id, self._n_geom_elements('volume')): - dag_cell = openmc.lib.cells[dag_cell_id] - if isinstance(dag_cell.fill, Iterable): - fill = [mats_per_id[mat_id.id] - for mat_id in dag_cell.fill] - else: - fill = mats_per_id[dag_cell.fill.id] - dag_pseudo_cell = openmc.DAGMCCell( - cell_id=dag_cell_id, fill=fill - ) - self.add_cell(dag_pseudo_cell) From b215d9e31999122912a544c7cdc4370e5c75b239 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 26 Aug 2024 16:00:24 +0200 Subject: [PATCH 030/122] update dagmc.cpp --- src/dagmc.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 594d9cf1bbb..1e8e874c677 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -83,11 +83,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) // Get mat name for each assignement instances std::stringstream iss {attr.value()}; - vector instance_mats; - std::string value; - while (iss >> value) - instance_mats.push_back(value); - std::cout << "DAGMC " << mat_ref_assignment << " " << value << std::endl; + vector instance_mats = split(iss.str()); // Store mat name for each instances instance_material_overrides.insert( From fd2a7a28862a5b655b31a34c7e4b19c4a7f72db9 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 27 Aug 2024 10:23:01 +0200 Subject: [PATCH 031/122] raise an error if diff_volume_method is unknown and correct mat name for DAGMC cell regardless of the method used --- openmc/model/model.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index c2714f36fff..88f00982c0d 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1050,6 +1050,10 @@ def differentiate_mats(self, diff_volume_method: str, volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. """ + if diff_volume_method not in ['divide equally', 'match cell']: + raise ValueError( + f"diff_volume_method must be 'divide equally' or 'match cell', " + f"not '{diff_volume_method}'" # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) @@ -1082,8 +1086,8 @@ def differentiate_mats(self, diff_volume_method: str, "diff_volume_method='match cell'." ) cell.fill[i].volume = cell.volume - if isinstance(cell, openmc.DAGMCCell): - cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" + if isinstance(cell, openmc.DAGMCCell): + cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" if self.materials is not None: self.materials = openmc.Materials( From 95ff5bf328e8f07313e9952646a407b1b689373b Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 27 Aug 2024 13:05:19 +0200 Subject: [PATCH 032/122] forgot to close the parenthesis --- openmc/model/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 88f00982c0d..14fe3c15dfb 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1053,7 +1053,7 @@ def differentiate_mats(self, diff_volume_method: str, if diff_volume_method not in ['divide equally', 'match cell']: raise ValueError( f"diff_volume_method must be 'divide equally' or 'match cell', " - f"not '{diff_volume_method}'" + f"not '{diff_volume_method}'") # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) From 0fa8a44a2239052ce7541efddf3e544ba25586a1 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 28 Aug 2024 16:35:07 +0200 Subject: [PATCH 033/122] missing a loop --- openmc/model/model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 14fe3c15dfb..1562c1b029c 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1086,8 +1086,9 @@ def differentiate_mats(self, diff_volume_method: str, "diff_volume_method='match cell'." ) cell.fill[i].volume = cell.volume - if isinstance(cell, openmc.DAGMCCell): - cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" + for i in range(cell.num_instances): + if isinstance(cell, openmc.DAGMCCell): + cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" if self.materials is not None: self.materials = openmc.Materials( From 7ce1b0ab31d55bcdd3c3a60ec40a75bd179926b3 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 28 Aug 2024 21:38:47 +0200 Subject: [PATCH 034/122] proper order if --- openmc/model/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 1562c1b029c..08438b1da63 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1086,8 +1086,8 @@ def differentiate_mats(self, diff_volume_method: str, "diff_volume_method='match cell'." ) cell.fill[i].volume = cell.volume - for i in range(cell.num_instances): - if isinstance(cell, openmc.DAGMCCell): + if isinstance(cell, openmc.DAGMCCell): + for i in range(cell.num_instances): cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" if self.materials is not None: From 3197ee4d3616d3714018aadebece9e6b753d79a9 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 29 Aug 2024 10:24:35 +0200 Subject: [PATCH 035/122] diff without option is ok --- openmc/model/model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 08438b1da63..17472b1aac8 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1050,10 +1050,10 @@ def differentiate_mats(self, diff_volume_method: str, volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. """ - if diff_volume_method not in ['divide equally', 'match cell']: - raise ValueError( - f"diff_volume_method must be 'divide equally' or 'match cell', " - f"not '{diff_volume_method}'") + # if diff_volume_method not in ['divide equally', 'match cell']: + # raise ValueError( + # f"diff_volume_method must be 'divide equally' or 'match cell', " + # f"not '{diff_volume_method}'") # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) From 3a7c19c23846d50beccfcbebf2f284efceb8e05e Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 29 Aug 2024 13:19:46 +0200 Subject: [PATCH 036/122] rm commented code --- openmc/model/model.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 17472b1aac8..d215a3d3785 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1050,10 +1050,6 @@ def differentiate_mats(self, diff_volume_method: str, volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. """ - # if diff_volume_method not in ['divide equally', 'match cell']: - # raise ValueError( - # f"diff_volume_method must be 'divide equally' or 'match cell', " - # f"not '{diff_volume_method}'") # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) From 8f3ffa5beb128bc2143a863af3f52f8fb04835fa Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 29 Aug 2024 16:25:41 +0200 Subject: [PATCH 037/122] my enforcing was not the problem --- openmc/model/model.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openmc/model/model.py b/openmc/model/model.py index d215a3d3785..08438b1da63 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1050,6 +1050,10 @@ def differentiate_mats(self, diff_volume_method: str, volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. """ + if diff_volume_method not in ['divide equally', 'match cell']: + raise ValueError( + f"diff_volume_method must be 'divide equally' or 'match cell', " + f"not '{diff_volume_method}'") # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) From 8e5c3ef6ae4f635bbfb0a2f51febfd202d02d52b Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 4 Sep 2024 14:17:15 +0200 Subject: [PATCH 038/122] this adds a diff_volume_method as None by default, and just split materials without assigning volumes --- openmc/lib/dagmc.py | 16 ++++++++-------- openmc/model/model.py | 35 ++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index 5a3324e0f6d..a8ef4185233 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -14,29 +14,29 @@ def get_dagmc_cell_ids(volume_id, n_cells): - """Get the DAGMC cell IDs for a volume. + """get the dagmc cell ids for a volume. - Parameters + parameters ---------- volume_id : int - ID of the volume to get DAGMC cell IDs for. + id of the volume to get dagmc cell ids for. n_cells : int - Number of cells in the volume. + number of cells in the volume. - Returns + returns ------- numpy.ndarray - DAGMC cell IDs for the volume. + dagmc cell ids for the volume. """ cell_ids = np.empty(n_cells, dtype=np.int32) n = c_size_t() _dll.openmc_get_dagmc_cell_ids( volume_id, - cell_ids.ctypes.data_as(POINTER(c_int32)), + cell_ids.ctypes.data_as(pointer(c_int32)), n ) if n.value != n_cells: - raise ValueError(f"Number of cells obtained {n.value} from DAGMC does " + raise valueerror(f"number of cells obtained {n.value} from dagmc does " f"not match the expected number of cells {n_cells}.") return cell_ids \ No newline at end of file diff --git a/openmc/model/model.py b/openmc/model/model.py index 08438b1da63..5cda2a68620 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1021,7 +1021,7 @@ def update_material_volumes(self, names_or_ids, volume): self._change_py_lib_attribs(names_or_ids, volume, 'material', 'volume') - def differentiate_depletable_mats(self, diff_volume_method: str): + def differentiate_depletable_mats(self, diff_volume_method=None): """Assign distribmats for each depletable material .. versionadded:: 0.14.0 @@ -1036,8 +1036,7 @@ def differentiate_depletable_mats(self, diff_volume_method: str): """ self.differentiate_mats(diff_volume_method, depletable_only=True) - def differentiate_mats(self, diff_volume_method: str, - depletable_only: bool = True): + def differentiate_mats(self, diff_volume_method=None, depletable_only: bool = True): """Assign distribmats for each depletable material .. versionadded:: 0.14.0 @@ -1050,23 +1049,30 @@ def differentiate_mats(self, diff_volume_method: str, volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. """ - if diff_volume_method not in ['divide equally', 'match cell']: + if diff_volume_method not in ["divide equally", "match cell", None]: raise ValueError( f"diff_volume_method must be 'divide equally' or 'match cell', " - f"not '{diff_volume_method}'") + f"not '{diff_volume_method}'" + ) # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) # Extract all depletable materials which have multiple instances distribmats = set( - [mat for mat in self.materials - if (mat.depletable or not depletable_only) and mat.num_instances > 1]) - - if diff_volume_method == 'divide equally': + [ + mat + for mat in self.materials + if (mat.depletable or not depletable_only) and mat.num_instances > 1 + ] + ) + + if diff_volume_method == "divide equally": for mat in distribmats: if mat.volume is None: - raise RuntimeError("Volume not specified for depletable " - f"material with ID={mat.id}.") + raise RuntimeError( + "Volume not specified for depletable " + f"material with ID={mat.id}." + ) mat.volume /= mat.num_instances if distribmats: @@ -1074,9 +1080,8 @@ def differentiate_mats(self, diff_volume_method: str, for cell in self.geometry.get_all_material_cells().values(): if cell.fill in distribmats: mat = cell.fill - if diff_volume_method == 'divide equally': - cell.fill = [mat.clone() for _ in range(cell.num_instances)] - elif diff_volume_method == 'match cell': + cell.fill = [mat.clone() for _ in range(cell.num_instances)] + if diff_volume_method == 'match cell': cell.fill = [mat.clone() for _ in range(cell.num_instances)] for i in range(cell.num_instances): if not cell.volume: @@ -1093,4 +1098,4 @@ def differentiate_mats(self, diff_volume_method: str, if self.materials is not None: self.materials = openmc.Materials( self.geometry.get_all_materials().values() - ) \ No newline at end of file + ) From 293e2f097f8f625da2a3a39629fffb42c0ab4c97 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 5 Sep 2024 11:19:59 +0200 Subject: [PATCH 039/122] fixing dagmc geometry name --- .../{UseCaseBam.h5m => dagmc_differentiate_mat.h5m} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/unit_tests/dagmc/{UseCaseBam.h5m => dagmc_differentiate_mat.h5m} (100%) diff --git a/tests/unit_tests/dagmc/UseCaseBam.h5m b/tests/unit_tests/dagmc/dagmc_differentiate_mat.h5m similarity index 100% rename from tests/unit_tests/dagmc/UseCaseBam.h5m rename to tests/unit_tests/dagmc/dagmc_differentiate_mat.h5m From f96d6e34a2473986da6b52178459f3137015234a Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 9 Sep 2024 13:56:28 +0200 Subject: [PATCH 040/122] pointer should be POINTER --- openmc/lib/dagmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index a8ef4185233..35f71af9a1e 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -33,7 +33,7 @@ def get_dagmc_cell_ids(volume_id, n_cells): n = c_size_t() _dll.openmc_get_dagmc_cell_ids( volume_id, - cell_ids.ctypes.data_as(pointer(c_int32)), + cell_ids.ctypes.data_as(POINTER(c_int32)), n ) if n.value != n_cells: From 81168e00cd2b53013b228e4168a2dc400010969f Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:58:35 +0200 Subject: [PATCH 041/122] Update openmc/lib/dagmc.py Co-authored-by: azimG --- openmc/lib/dagmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index 35f71af9a1e..09bab822ae2 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -37,6 +37,6 @@ def get_dagmc_cell_ids(volume_id, n_cells): n ) if n.value != n_cells: - raise valueerror(f"number of cells obtained {n.value} from dagmc does " + raise ValueError(f"Number of cells obtained {n.value} from dagmc does " f"not match the expected number of cells {n_cells}.") return cell_ids \ No newline at end of file From 23a310899fb5cc295db8a37f25f8d462fae9f6ee Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:58:47 +0200 Subject: [PATCH 042/122] Update openmc/model/model.py Co-authored-by: azimG --- openmc/model/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 5cda2a68620..ac1e9769bcc 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1036,7 +1036,7 @@ def differentiate_depletable_mats(self, diff_volume_method=None): """ self.differentiate_mats(diff_volume_method, depletable_only=True) - def differentiate_mats(self, diff_volume_method=None, depletable_only: bool = True): + def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bool = True): """Assign distribmats for each depletable material .. versionadded:: 0.14.0 From 94f04ce35e587771e11a1e50daa3edb6bfbdfeaf Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:58:55 +0200 Subject: [PATCH 043/122] Update openmc/model/model.py Co-authored-by: azimG --- openmc/model/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index ac1e9769bcc..8fac3cafeff 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1051,7 +1051,7 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo """ if diff_volume_method not in ["divide equally", "match cell", None]: raise ValueError( - f"diff_volume_method must be 'divide equally' or 'match cell', " + "diff_volume_method must be 'divide equally' or 'match cell', " f"not '{diff_volume_method}'" ) # Count the number of instances for each cell and material From 429ee4aeb00a506eb163c12e8e5bdd282655e750 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:59:05 +0200 Subject: [PATCH 044/122] Update openmc/dagmc.py Co-authored-by: azimG --- openmc/dagmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index a484d916d9e..3671e1a73fb 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -470,7 +470,7 @@ def DAG_parent_universe(self, universe): self._parent_universe = universe.id def boundingbox(self): - print("Warning: Bounding box is not available for cells in a DAGMC universe.") + raise NotImplementedError("Bounding box is not available for cells in a DAGMC universe") return {} def get_all_cells(self, memo=None): From 8baa99ad36e0cd822901a772701f03b380fe2b83 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:59:49 +0200 Subject: [PATCH 045/122] Update openmc/model/model.py Co-authored-by: azimG --- openmc/model/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 8fac3cafeff..fc2a0e9c7cb 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1021,7 +1021,7 @@ def update_material_volumes(self, names_or_ids, volume): self._change_py_lib_attribs(names_or_ids, volume, 'material', 'volume') - def differentiate_depletable_mats(self, diff_volume_method=None): + def differentiate_depletable_mats(self, diff_volume_method : str = None): """Assign distribmats for each depletable material .. versionadded:: 0.14.0 From 01b4c9d8d103c715dc5898a027f53a86a0130dea Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 9 Sep 2024 14:04:16 +0200 Subject: [PATCH 046/122] fixe a to_lower shortcut typo --- openmc/lib/dagmc.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index 09bab822ae2..5a3324e0f6d 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -14,19 +14,19 @@ def get_dagmc_cell_ids(volume_id, n_cells): - """get the dagmc cell ids for a volume. + """Get the DAGMC cell IDs for a volume. - parameters + Parameters ---------- volume_id : int - id of the volume to get dagmc cell ids for. + ID of the volume to get DAGMC cell IDs for. n_cells : int - number of cells in the volume. + Number of cells in the volume. - returns + Returns ------- numpy.ndarray - dagmc cell ids for the volume. + DAGMC cell IDs for the volume. """ cell_ids = np.empty(n_cells, dtype=np.int32) @@ -37,6 +37,6 @@ def get_dagmc_cell_ids(volume_id, n_cells): n ) if n.value != n_cells: - raise ValueError(f"Number of cells obtained {n.value} from dagmc does " + raise ValueError(f"Number of cells obtained {n.value} from DAGMC does " f"not match the expected number of cells {n_cells}.") return cell_ids \ No newline at end of file From c8de658b5ed86bd885d5ebe2b0f25a2ec96ec191 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 9 Sep 2024 14:14:36 +0200 Subject: [PATCH 047/122] adding docstring in dagmc.py --- openmc/dagmc.py | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 3671e1a73fb..e3341ca78eb 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -456,6 +456,26 @@ def sync_dagmc_cells(self, mats={}): class DAGMCCell(openmc.Cell): + """ + A cell class for DAGMC-based geometries. + + Parameters + ---------- + cell_id : int or None, optional + Unique identifier for the cell. If None, an identifier will be automatically assigned. + name : str, optional + Name of the cell. + fill : openmc.Material or None, optional + Material filling the cell. If None, the cell is filled with vacuum. + region : openmc.Region or None, optional + Region of space that the cell occupies. If None, the cell is filled with vacuum. + + Attributes + ---------- + DAG_parent_universe : int + The parent universe of the cell. + + """ def __init__(self, cell_id=None, name='', fill=None, region=None): super().__init__(cell_id, name, fill, region) @@ -471,28 +491,22 @@ def DAG_parent_universe(self, universe): def boundingbox(self): raise NotImplementedError("Bounding box is not available for cells in a DAGMC universe") - return {} def get_all_cells(self, memo=None): - return {} + raise NotImplementedError("get_all_cells is not available for cells in a DAGMC universe") def get_all_universes(self, memo=None): - return {} + raise NotImplementedError("get_all_universes is not available for cells in a DAGMC universe") def clone(self, clone_materials=True, clone_regions=True, memo=None): - print("Warning: clone is not available for cells in a DAGMC universe.") - return None + raise NotImplementedError("clone is not available for cells in a DAGMC universe") def plot(self, *args, **kwargs): - print("Warning: plot is not available for cells in a DAGMC universe.") - return None + raise NotImplementedError("plot is not available for cells in a DAGMC universe") def create_xml_subelement(self, xml_element, memo=None): - print( - "Warning: create_xml_subelement is not available for cells in a DAGMC universe.") - return None + raise NotImplementedError("create_xml_subelement is not available for cells in a DAGMC universe") @classmethod def from_xml_element(cls, elem, surfaces, materials, get_universe): - print("Warning: from_xml_element is not available for cells in a DAGMC universe.") - return None + raise NotImplementedError("from_xml_element is not available for cells in a DAGMC universe") From 86cff5680c212cfdfc3b4f2f4afc06e52b8d1a04 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 10 Sep 2024 14:54:44 +0200 Subject: [PATCH 048/122] forgot renameing file open --- openmc/dagmc.py | 2 -- tests/unit_tests/dagmc/test_model.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index e3341ca78eb..1fd19f31d8c 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -75,8 +75,6 @@ class DAGMCUniverse(openmc.UniverseBase): strings and the values are openmc.Material objects. If a material name is found in the DAGMC file, the material will be replaced with the openmc.Material object in the value. - - """ def __init__(self, diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 733b0b77459..9d7ef980723 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -34,7 +34,7 @@ def test_model_differentiate_with_DAGMC(): mats["Water"].add_s_alpha_beta("c_H_in_H2O") mats["Water"].name = "Water" - p = pkg_resources.resource_filename(__name__, "UseCaseBam.h5m") + p = pkg_resources.resource_filename(__name__, "dagmc_differentiate_mat.h5m") daguniv = openmc.DAGMCUniverse(p,auto_geom_ids=True,) From 95136d759d624bfd45485dca5b25cf63060b9cc8 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Sep 2024 08:49:27 +0200 Subject: [PATCH 049/122] no run necesseray --- tests/unit_tests/dagmc/test_model.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 9d7ef980723..9c9348d7228 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -113,7 +113,4 @@ def pattern(center, bc): volume_after = np.sum([m.volume for m in model.materials if "Fuel" in m.name]) assert len(model.materials) == nmat + 3 assert np.isclose(volume_before, volume_after) - - model.run(cwd=p) - model.plot_geometry(cwd=p) model.finalize_lib() From 610bc9546c5f9930163e93dd9f627ff6787bb967 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Sep 2024 16:51:29 +0200 Subject: [PATCH 050/122] restore old behavior for diff_deplet_mat, warning instead of error for method in dagmc cell --- openmc/dagmc.py | 26 ++++++++++++------- openmc/model/model.py | 7 ++--- .../test_deplete_coupled_operator.py | 6 ++--- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 1fd19f31d8c..dd69b7de559 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -5,12 +5,14 @@ import h5py import lxml.etree as ET import numpy as np +import warnings import openmc import openmc.checkvalue as cv from ._xml import get_text from .checkvalue import check_type, check_value from .surface import _BOUNDARY_TYPES +from .bounding_box import BoundingBox class DAGMCUniverse(openmc.UniverseBase): @@ -220,7 +222,6 @@ def create_xml_subelement(self, xml_element, memo=None): if mat.name[:-4] in self.material_names: self.material_overrides.setdefault( mat.name[:-4].lower(), []).append(mat.name) - print(f"Material {mat.name} found in DAGMC file, ") if self.material_overrides: mat_element = ET.Element('material_overrides') @@ -488,23 +489,30 @@ def DAG_parent_universe(self, universe): self._parent_universe = universe.id def boundingbox(self): - raise NotImplementedError("Bounding box is not available for cells in a DAGMC universe") - + warnings.warn("Bounding box is not available for cells in a DAGMC universe", Warning) + return BoundingBox.infinite() + def get_all_cells(self, memo=None): - raise NotImplementedError("get_all_cells is not available for cells in a DAGMC universe") + warnings.warn("get_all_cells is not available for cells in a DAGMC universe", Warning) + return {} def get_all_universes(self, memo=None): - raise NotImplementedError("get_all_universes is not available for cells in a DAGMC universe") + warnings.warn("get_all_universes is not available for cells in a DAGMC universe", Warning) + return {} def clone(self, clone_materials=True, clone_regions=True, memo=None): - raise NotImplementedError("clone is not available for cells in a DAGMC universe") + warnings.warn("clone is not available for cells in a DAGMC universe", Warning) + return None def plot(self, *args, **kwargs): - raise NotImplementedError("plot is not available for cells in a DAGMC universe") + warnings.warn("plot is not available for cells in a DAGMC universe", Warning) + return None def create_xml_subelement(self, xml_element, memo=None): - raise NotImplementedError("create_xml_subelement is not available for cells in a DAGMC universe") + warnings.warn("create_xml_subelement is not available for cells in a DAGMC universe", Warning) + return None @classmethod def from_xml_element(cls, elem, surfaces, materials, get_universe): - raise NotImplementedError("from_xml_element is not available for cells in a DAGMC universe") + warnings.warn("from_xml_element is not available for cells in a DAGMC universe", Warning) + return None diff --git a/openmc/model/model.py b/openmc/model/model.py index fc2a0e9c7cb..0bfac73d112 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1080,9 +1080,10 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo for cell in self.geometry.get_all_material_cells().values(): if cell.fill in distribmats: mat = cell.fill - cell.fill = [mat.clone() for _ in range(cell.num_instances)] - if diff_volume_method == 'match cell': + if diff_volume_method != 'match cell': cell.fill = [mat.clone() for _ in range(cell.num_instances)] + elif diff_volume_method == 'match cell': + cell.fill = mat.clone() for i in range(cell.num_instances): if not cell.volume: raise ValueError( @@ -1090,7 +1091,7 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo "Set volumes of cells prior to using " "diff_volume_method='match cell'." ) - cell.fill[i].volume = cell.volume + cell.fill.volume = cell.volume if isinstance(cell, openmc.DAGMCCell): for i in range(cell.num_instances): cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" diff --git a/tests/unit_tests/test_deplete_coupled_operator.py b/tests/unit_tests/test_deplete_coupled_operator.py index 8cfc9ca5a8b..fe79d621b12 100644 --- a/tests/unit_tests/test_deplete_coupled_operator.py +++ b/tests/unit_tests/test_deplete_coupled_operator.py @@ -97,10 +97,8 @@ def test_diff_volume_method_match_cell(model_with_volumes): ) all_cells = list(operator.model.geometry.get_all_cells().values()) - for mat in all_cells[0].fill: - assert mat.volume == 4.19 - for mat in all_cells[1].fill: - assert mat.volume == 33.51 + assert all_cells[0].fill.volume == 4.19 + assert all_cells[1].fill.volume == 33.51 # mat2 is not depletable assert all_cells[2].fill.volume is None From 675628890d7cc93f30cd20ef515a69c4370ce1ce Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:09:51 +0200 Subject: [PATCH 051/122] Apply suggestions from code review Co-authored-by: Patrick Shriwise --- openmc/dagmc.py | 10 +++++----- openmc/model/model.py | 2 +- src/dagmc.cpp | 4 +--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index dd69b7de559..72487e3c54a 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -72,9 +72,9 @@ class DAGMCUniverse(openmc.UniverseBase): The number of surfaces in the model. .. versionadded:: 0.15 - mat_overrides : dict - A dictionary of material overrides. The keys are material names as - strings and the values are openmc.Material objects. If a material name + material_overrides : dict + A dictionary of material overrides. The keys are material name + strings and the values are Iterables of openmc.Material objects. If a material name is found in the DAGMC file, the material will be replaced with the openmc.Material object in the value. """ @@ -437,11 +437,11 @@ def sync_dagmc_cells(self, mats={}): """ import openmc.lib if not openmc.lib.is_initialized: - raise RuntimeError("Model must be initialized via Model.init_lib " + raise RuntimeError("This universe must be part of an openmc.Model initialized via Model.init_lib " "before calling this method.") mats_per_id = {mat.id: mat for mat in mats} - for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(self.id, self._n_geom_elements('volume')): + for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(self.id, self.n_cells): dag_cell = openmc.lib.cells[dag_cell_id] if isinstance(dag_cell.fill, Iterable): fill = [mats_per_id[mat_id.id] diff --git a/openmc/model/model.py b/openmc/model/model.py index 0bfac73d112..99960cc9779 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1049,7 +1049,7 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell they fill. """ - if diff_volume_method not in ["divide equally", "match cell", None]: + if diff_volume_method not in ("divide equally", "match cell", None): raise ValueError( "diff_volume_method must be 'divide equally' or 'match cell', " f"not '{diff_volume_method}'" diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 1e8e874c677..0b61feec661 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -232,9 +232,7 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { - if (instance_material_overrides.size() > 0 and - instance_material_overrides.find(mat_str) != - instance_material_overrides.end()) { + if (contains(instance_material_overrides, mat_str)) { for (auto mat_str_instance : instance_material_overrides.at(mat_str)) { From c0680dd186a0b892c8ebfc09eb781e2181f1c169 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 21 Sep 2024 11:57:18 +0200 Subject: [PATCH 052/122] adressing most @pshriwise comments --- include/openmc/capi.h | 5 +- openmc/dagmc.py | 73 ++++++++++++++++++++-------- openmc/lib/dagmc.py | 40 +++++++++++---- openmc/model/model.py | 30 +++++++----- src/dagmc.cpp | 16 ++++++ tests/unit_tests/dagmc/test_model.py | 1 + 6 files changed, 121 insertions(+), 44 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index cb8fb93007d..412ae8180af 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -30,8 +30,9 @@ int openmc_cell_set_temperature( int openmc_cell_set_translation(int32_t index, const double xyz[]); int openmc_cell_set_rotation(int32_t index, const double rot[], size_t rot_len); int openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n); -int openmc_energy_filter_get_bins( - int32_t index, const double** energies, size_t* n); +int openmc_dagmc_universe_get_num_cells( + int32_t univ_id, size_t* n) int openmc_energy_filter_get_bins(int32_t index, + const double** energies, size_t* n); int openmc_energy_filter_set_bins( int32_t index, size_t n, const double* energies); int openmc_energyfunc_filter_get_energy( diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 72487e3c54a..0e89ea5684b 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -74,8 +74,8 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.15 material_overrides : dict A dictionary of material overrides. The keys are material name - strings and the values are Iterables of openmc.Material objects. If a material name - is found in the DAGMC file, the material will be replaced with the + strings and the values are Iterables of openmc.Material objects. If a + material name is found in the DAGMC file, the material will be replaced with the openmc.Material object in the value. """ @@ -85,7 +85,7 @@ def __init__(self, name='', auto_geom_ids=False, auto_mat_ids=False, - mat_overrides={}): + mat_overrides=None): super().__init__(universe_id, name) # Initialize class attributes self.filename = filename @@ -112,6 +112,17 @@ def bounding_box(self): def filename(self): return self._filename + @property + def material_overrides(self): + return self._material_overrides + + @material_overrides.setter + def material_overrides(self, val): + if val is not None: + cv.check_type('material overrides', val, dict) + + self._material_overrides = val + @filename.setter def filename(self, val): cv.check_type('DAGMC filename', val, (Path, str)) @@ -216,7 +227,7 @@ def create_xml_subelement(self, xml_element, memo=None): if self.auto_mat_ids: dagmc_element.set('auto_mat_ids', 'true') dagmc_element.set('filename', str(self.filename)) - if len(self.material_overrides) == 0: + if not self.material_overrides: mats = self.get_all_materials() for mat in mats.values(): if mat.name[:-4] in self.material_names: @@ -379,6 +390,12 @@ def from_xml_element(cls, elem): out.auto_geom_ids = bool(elem.get('auto_geom_ids')) out.auto_mat_ids = bool(elem.get('auto_mat_ids')) + for item in elem.find('material_overrides').attrib: + origin_mat, overwrite = item + for mat_name in overwrite.split(): + out.material_overrides.setdefault( + origin_mat.lower(), []).append(mat_name) + return out def _partial_deepcopy(self): @@ -402,8 +419,8 @@ def add_cell(self, cell): """ if not isinstance(cell, openmc.DAGMCCell): - msg = f'Unable to add a DAGMCCell to DAGMCUniverse ID="{self._id}" since ' \ - f'"{cell}" is not a DAGMCCell' + msg = f'Unable to add a DAGMCCell to DAGMCUniverse ' \ + f'ID="{self._id}" since "{cell}" is not a DAGMCCell' raise TypeError(msg) cell_id = cell.id @@ -437,11 +454,19 @@ def sync_dagmc_cells(self, mats={}): """ import openmc.lib if not openmc.lib.is_initialized: - raise RuntimeError("This universe must be part of an openmc.Model initialized via Model.init_lib " - "before calling this method.") + raise RuntimeError("This universe must be part of an openmc.Model " + "initialized via Model.init_lib before calling " + "this method.") + + dagmc_cell_ids = openmc.lib.dagmc.get_dagmc_cell_ids(self.id) + if len(dagmc_cell_ids) != self.n_cells: + raise ValueError( + f"Number of cells in DAGMC universe {self.id} does not match " + f"the number of cells in the Python universe." + ) mats_per_id = {mat.id: mat for mat in mats} - for dag_cell_id in openmc.lib.dagmc.get_dagmc_cell_ids(self.id, self.n_cells): + for dag_cell_id in dagmc_cell_ids: dag_cell = openmc.lib.cells[dag_cell_id] if isinstance(dag_cell.fill, Iterable): fill = [mats_per_id[mat_id.id] @@ -461,13 +486,12 @@ class DAGMCCell(openmc.Cell): Parameters ---------- cell_id : int or None, optional - Unique identifier for the cell. If None, an identifier will be automatically assigned. + Unique identifier for the cell. If None, an identifier will be + automatically assigned. name : str, optional Name of the cell. fill : openmc.Material or None, optional Material filling the cell. If None, the cell is filled with vacuum. - region : openmc.Region or None, optional - Region of space that the cell occupies. If None, the cell is filled with vacuum. Attributes ---------- @@ -475,8 +499,8 @@ class DAGMCCell(openmc.Cell): The parent universe of the cell. """ - def __init__(self, cell_id=None, name='', fill=None, region=None): - super().__init__(cell_id, name, fill, region) + def __init__(self, cell_id=None, name='', fill=None): + super().__init__(cell_id, name, fill, None) @property def DAG_parent_universe(self): @@ -489,30 +513,37 @@ def DAG_parent_universe(self, universe): self._parent_universe = universe.id def boundingbox(self): - warnings.warn("Bounding box is not available for cells in a DAGMC universe", Warning) + warnings.warn("Bounding box is not available for cells in a DAGMC " + "universe", Warning) return BoundingBox.infinite() def get_all_cells(self, memo=None): - warnings.warn("get_all_cells is not available for cells in a DAGMC universe", Warning) + warnings.warn("get_all_cells is not available for cells in a DAGMC " + "universe", Warning) return {} def get_all_universes(self, memo=None): - warnings.warn("get_all_universes is not available for cells in a DAGMC universe", Warning) + warnings.warn("get_all_universes is not available for cells in a " + "DAGMC universe", Warning) return {} def clone(self, clone_materials=True, clone_regions=True, memo=None): - warnings.warn("clone is not available for cells in a DAGMC universe", Warning) + warnings.warn("clone is not available for cells in a DAGMC universe", + Warning) return None def plot(self, *args, **kwargs): - warnings.warn("plot is not available for cells in a DAGMC universe", Warning) + warnings.warn("plot is not available for cells in a DAGMC universe", + Warning) return None def create_xml_subelement(self, xml_element, memo=None): - warnings.warn("create_xml_subelement is not available for cells in a DAGMC universe", Warning) + warnings.warn("create_xml_subelement is not available for cells in a " + "DAGMC universe", Warning) return None @classmethod def from_xml_element(cls, elem, surfaces, materials, get_universe): - warnings.warn("from_xml_element is not available for cells in a DAGMC universe", Warning) + warnings.warn("from_xml_element is not available for cells in a DAGMC " + "universe", Warning) return None diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index 5a3324e0f6d..0bc5c024b05 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -11,17 +11,20 @@ _dll.openmc_get_dagmc_cell_ids.argtypes = [c_int32, POINTER(c_int32), POINTER(c_size_t)] _dll.openmc_get_dagmc_cell_ids.restype = c_int _dll.openmc_get_dagmc_cell_ids.errcheck = _error_handler +_dll.openmc_dagmc_universe_get_num_cells.argtypes = [c_int32, POINTER(c_size_t)] +_dll.openmc_dagmc_universe_get_num_cells.restype = c_int +_dll.openmc_dagmc_universe_get_num_cells.errcheck = _error_handler -def get_dagmc_cell_ids(volume_id, n_cells): +def get_dagmc_cell_ids(dagmc_id): """Get the DAGMC cell IDs for a volume. Parameters ---------- - volume_id : int - ID of the volume to get DAGMC cell IDs for. + dagmc_id : int + ID of the DAGMC Universe to get cell IDs from. n_cells : int - Number of cells in the volume. + Number of cells in the DAGMC Universe. Returns ------- @@ -29,14 +32,31 @@ def get_dagmc_cell_ids(volume_id, n_cells): DAGMC cell IDs for the volume. """ - cell_ids = np.empty(n_cells, dtype=np.int32) n = c_size_t() + _dll.openmc_dagmc_universe_get_num_cells(dagmc_id, n) + cell_ids = np.empty(n.value, dtype=np.int32) + _dll.openmc_get_dagmc_cell_ids( - volume_id, + dagmc_id, cell_ids.ctypes.data_as(POINTER(c_int32)), n ) - if n.value != n_cells: - raise ValueError(f"Number of cells obtained {n.value} from DAGMC does " - f"not match the expected number of cells {n_cells}.") - return cell_ids \ No newline at end of file + return cell_ids +if +def get_dagmc_universe_num_cells(dagmc_id): + """Get the number of cells in a DAGMC universe. + + Parameters + ---------- + dagmc_id : int + ID of the DAGMC Universe to get the number of cell from. + + Returns + ------- + int + Number of cells in the DAGMC Universe. + + """ + n = c_size_t() + _dll.openmc_dagmc_universe_get_num_cells(dagmc_id, n) + return n.value diff --git a/openmc/model/model.py b/openmc/model/model.py index 99960cc9779..39272b563e7 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -324,12 +324,24 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, # communicator openmc.lib.init(args=args, intracomm=intracomm, output=output) - if self.materials: - mats = self.materials + def sync_dagmc_universe(self): + """ + Synchronize all DAGMC universes with the current geometry. + This method iterates over all DAGMC universes in the geometry and + synchronizes their cells with the current material assignments. + Returns: + None + """ + if self.is_initialized: + if self.materials: + mats = self.materials + else: + mats = self.geometry.get_all_materials().values() + for dagmc_universe in self.geometry.get_all_dagmc_universes().values(): + dagmc_universe.sync_dagmc_cells(mats) else: - mats = self.geometry.get_all_materials().values() - for dagmc_universe in self.geometry.get_all_dagmc_universes().values(): - dagmc_universe.sync_dagmc_cells(mats) + raise ValueError("The model must be initialized before calling " + "this method") def finalize_lib(self): """Finalize simulation and free memory allocated for the C API @@ -1059,12 +1071,8 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo # Extract all depletable materials which have multiple instances distribmats = set( - [ - mat - for mat in self.materials - if (mat.depletable or not depletable_only) and mat.num_instances > 1 - ] - ) + [mat for mat in self.materials + if mat.depletable and mat.num_instances > 1]) if diff_volume_method == "divide equally": for mat in distribmats: diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 0b61feec661..0f456429dc5 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -862,6 +862,19 @@ extern "C" int openmc_get_dagmc_cell_ids( *n = dag_cell_ids.size(); } +extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) +{ + // make sure the universe id is a DAGMC Universe + const auto& univ = model::universes[model::universe_map[univ_id]]; + if (univ->geom_type() != GeometryType::DAG) { + fatal_error( + "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); + } + + std::vector dag_cell_ids; + *n = univ->cells_.size(); +} + } // namespace openmc #else @@ -871,6 +884,9 @@ namespace openmc { extern "C" int openmc_get_dagmc_cell_ids( int32_t univ_id, int32_t* ids, size_t* n) {}; +extern "C" int openmc_dagmc_universe_get_num_cells( + int32_t univ_id, size_t* n) {}; + void read_dagmc_universes(pugi::xml_node node) { if (check_for_node(node, "dagmc_universe")) { diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 9c9348d7228..fa02bc11615 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -106,6 +106,7 @@ def pattern(center, bc): p = Path("differentiate_depletable_mats/divide_equally") p.mkdir(parents=True, exist_ok=True) model.init_lib() + model.sync_dagmc_universe() model.calculate_volumes(cwd=p) volume_before = np.sum([m.volume for m in model.materials if m.name == "Fuel"]) nmat = len(model.materials) From 31fbcdc08e47169549061f13395a86557f18b28a Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 21 Sep 2024 12:16:24 +0200 Subject: [PATCH 053/122] cleaning --- include/openmc/capi.h | 6 +++--- src/dagmc.cpp | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 412ae8180af..8785d0506ef 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -30,9 +30,9 @@ int openmc_cell_set_temperature( int openmc_cell_set_translation(int32_t index, const double xyz[]); int openmc_cell_set_rotation(int32_t index, const double rot[], size_t rot_len); int openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n); -int openmc_dagmc_universe_get_num_cells( - int32_t univ_id, size_t* n) int openmc_energy_filter_get_bins(int32_t index, - const double** energies, size_t* n); +int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n); +int openmc_energy_filter_get_bins( + int32_t index, const double** energies, size_t* n); int openmc_energy_filter_set_bins( int32_t index, size_t n, const double* energies); int openmc_energyfunc_filter_get_energy( diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 0f456429dc5..1f2fa82d16f 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -871,7 +871,6 @@ extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); } - std::vector dag_cell_ids; *n = univ->cells_.size(); } From 9d55c282bb9730ec074825f4db3775dfb4732db8 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 21 Sep 2024 14:32:51 +0200 Subject: [PATCH 054/122] update --- openmc/lib/dagmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index 0bc5c024b05..8e3b9701daf 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -42,7 +42,7 @@ def get_dagmc_cell_ids(dagmc_id): n ) return cell_ids -if + def get_dagmc_universe_num_cells(dagmc_id): """Get the number of cells in a DAGMC universe. From 416e794b49996bf46b6b8a67b5fdb9961fec02af Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 21 Sep 2024 16:33:48 +0200 Subject: [PATCH 055/122] fixing compilation error when searching in map --- src/dagmc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 1f2fa82d16f..2ccd3f8d198 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -232,7 +232,7 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { - if (contains(instance_material_overrides, mat_str)) { + if (instance_material_overrides.count(mat_str)) { for (auto mat_str_instance : instance_material_overrides.at(mat_str)) { From f6a7e762a483765b698c60df93de2211ce1b040b Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sun, 22 Sep 2024 13:40:34 +0200 Subject: [PATCH 056/122] add safeguard against overridings DAGMC cell with more materials than the cell number instance --- openmc/dagmc.py | 8 ++++++++ src/dagmc.cpp | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 0e89ea5684b..c9162d4bf43 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -120,6 +120,14 @@ def material_overrides(self): def material_overrides(self, val): if val is not None: cv.check_type('material overrides', val, dict) + for key, value in val.items(): + # ensuring key is a string and exists in the DAGMC file + cv.check_type('material name', key, str) + if key not in self.material_names: + raise ValueError( + f"Material name '{key}' not found in DAGMC file") + # ensuring overrides is an iterable of material name (strings) + cv.check_iterable_type('material objects', value, str) self._material_overrides = val diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 2ccd3f8d198..80bf1f7f627 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -233,6 +233,14 @@ void DAGUniverse::init_geometry() uwuw_assign_material(vol_handle, c); } else { if (instance_material_overrides.count(mat_str)) { + if (instance_material_overrides.at(mat_str).size() != + c->n_instances_) { + fatal_error(fmt::format("DAGMC Cell assign with material {} has {} " + "instances but material_overrides has {} " + "material assignments for this material", + mat_str, c->n_instances_, + instance_material_overrides.at(mat_str).size())); + } for (auto mat_str_instance : instance_material_overrides.at(mat_str)) { From e2f392f21563c428b2827ec3e289587a79278ab9 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 23 Sep 2024 20:55:56 +0200 Subject: [PATCH 057/122] add single mat override --- openmc/dagmc.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index c9162d4bf43..016d30cb625 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -131,6 +131,25 @@ def material_overrides(self, val): self._material_overrides = val + def add_material_override(self, mat_name, overrides): + """Add a material override to the universe. + + Parameters + ---------- + key : str + Material name to override + value : Iterable of str + Material names to replace the key with + + """ + cv.check_type('material name', mat_name, str) + if mat_name not in self.material_names: + raise ValueError( + f"Material name '{mat_name}' not found in DAGMC file") + cv.check_iterable_type('material objects', overrides, str) + + self.material_overrides[mat_name] = overrides + @filename.setter def filename(self, val): cv.check_type('DAGMC filename', val, (Path, str)) From 8148957bb0d8e6997a1b99bcfdfd87c24876b209 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:00:05 +0200 Subject: [PATCH 058/122] Update src/dagmc.cpp Co-authored-by: Patrick Shriwise --- src/dagmc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 80bf1f7f627..98dc203c207 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -876,7 +876,7 @@ extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) const auto& univ = model::universes[model::universe_map[univ_id]]; if (univ->geom_type() != GeometryType::DAG) { fatal_error( - "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); + "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe"); } *n = univ->cells_.size(); From ce9967548162251775bb8ea38914ceac3ae4e459 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 2 Oct 2024 15:48:36 +0200 Subject: [PATCH 059/122] quickfix depletable only --- include/openmc/capi.h | 3 ++- openmc/dagmc.py | 15 ++++++++++++--- openmc/lib/dagmc.py | 8 ++++---- openmc/model/model.py | 2 +- src/dagmc.cpp | 4 ++-- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 8785d0506ef..8edd99c0785 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -29,7 +29,8 @@ int openmc_cell_set_temperature( int32_t index, double T, const int32_t* instance, bool set_contained = false); int openmc_cell_set_translation(int32_t index, const double xyz[]); int openmc_cell_set_rotation(int32_t index, const double rot[], size_t rot_len); -int openmc_get_dagmc_cell_ids(int32_t univ_id, int32_t* ids, size_t* n); +int openmc_dagmc_universe_get_cell_ids( + int32_t univ_id, int32_t* ids, size_t* n); int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n); int openmc_energy_filter_get_bins( int32_t index, const double** energies, size_t* n); diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 016d30cb625..99473f140b5 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -35,6 +35,11 @@ class DAGMCUniverse(openmc.UniverseBase): auto_mat_ids : bool Set IDs automatically on initialization (True) or report overlaps in ID space between OpenMC and UWUW materials (False) + material_overrides : dict + A dictionary of material overrides. The keys are material name + strings and the values are Iterables of openmc.Material objects. If a + material name is found in the DAGMC file, the material will be replaced + with the openmc.Material object in the value. Attributes ---------- @@ -75,8 +80,8 @@ class DAGMCUniverse(openmc.UniverseBase): material_overrides : dict A dictionary of material overrides. The keys are material name strings and the values are Iterables of openmc.Material objects. If a - material name is found in the DAGMC file, the material will be replaced with the - openmc.Material object in the value. + material name is found in the DAGMC file, the material will be replaced + with the openmc.Material object in the value. """ def __init__(self, @@ -118,7 +123,10 @@ def material_overrides(self): @material_overrides.setter def material_overrides(self, val): - if val is not None: + if val is None: + self._material_overrides = val + return + else: cv.check_type('material overrides', val, dict) for key, value in val.items(): # ensuring key is a string and exists in the DAGMC file @@ -508,6 +516,7 @@ def sync_dagmc_cells(self, mats={}): class DAGMCCell(openmc.Cell): """ + .. versionadded:: 0.13.2 A cell class for DAGMC-based geometries. Parameters diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index 8e3b9701daf..ec9924578c8 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -8,9 +8,9 @@ # DAGMC functions -_dll.openmc_get_dagmc_cell_ids.argtypes = [c_int32, POINTER(c_int32), POINTER(c_size_t)] -_dll.openmc_get_dagmc_cell_ids.restype = c_int -_dll.openmc_get_dagmc_cell_ids.errcheck = _error_handler +_dll.openmc_dagmc_universe_get_cell_ids.argtypes = [c_int32, POINTER(c_int32), POINTER(c_size_t)] +_dll.openmc_dagmc_universe_get_cell_ids.restype = c_int +_dll.openmc_dagmc_universe_get_cell_ids.errcheck = _error_handler _dll.openmc_dagmc_universe_get_num_cells.argtypes = [c_int32, POINTER(c_size_t)] _dll.openmc_dagmc_universe_get_num_cells.restype = c_int _dll.openmc_dagmc_universe_get_num_cells.errcheck = _error_handler @@ -36,7 +36,7 @@ def get_dagmc_cell_ids(dagmc_id): _dll.openmc_dagmc_universe_get_num_cells(dagmc_id, n) cell_ids = np.empty(n.value, dtype=np.int32) - _dll.openmc_get_dagmc_cell_ids( + _dll.openmc_dagmc_universe_get_cell_ids( dagmc_id, cell_ids.ctypes.data_as(POINTER(c_int32)), n diff --git a/openmc/model/model.py b/openmc/model/model.py index 39272b563e7..67630054d01 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1072,7 +1072,7 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo # Extract all depletable materials which have multiple instances distribmats = set( [mat for mat in self.materials - if mat.depletable and mat.num_instances > 1]) + if (mat.depletable or not depletable_only) and mat.num_instances > 1]) if diff_volume_method == "divide equally": for mat in distribmats: diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 98dc203c207..f1396495a77 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -850,7 +850,7 @@ int32_t next_cell(int32_t surf, int32_t curr_cell, int32_t univ) return univp->cell_index(new_vol); } -extern "C" int openmc_get_dagmc_cell_ids( +extern "C" int openmc_dagmc_universe_get_cell_ids( int32_t univ_id, int32_t* ids, size_t* n) { // make sure the universe id is a DAGMC Universe @@ -888,7 +888,7 @@ extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) namespace openmc { -extern "C" int openmc_get_dagmc_cell_ids( +extern "C" int openmc_dagmc_universe_get_cell_ids( int32_t univ_id, int32_t* ids, size_t* n) {}; extern "C" int openmc_dagmc_universe_get_num_cells( From b6ad4b7cc1625d7033437f7a878fd02268836380 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 2 Oct 2024 16:21:01 +0200 Subject: [PATCH 060/122] factoring method form Universe to UniverseBase, do DAGMCUniverse can benefit from them --- openmc/universe.py | 170 ++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/openmc/universe.py b/openmc/universe.py index 6735d95e447..b6f36a4c752 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -307,91 +307,7 @@ def clone(self, clone_materials=True, clone_regions=True, memo=None): memo[self] = clone return memo[self] - - -class Universe(UniverseBase): - """A collection of cells that can be repeated. - - Parameters - ---------- - universe_id : int, optional - Unique identifier of the universe. If not specified, an identifier will - automatically be assigned - name : str, optional - Name of the universe. If not specified, the name is the empty string. - cells : Iterable of openmc.Cell, optional - Cells to add to the universe. By default no cells are added. - - Attributes - ---------- - id : int - Unique identifier of the universe - name : str - Name of the universe - cells : dict - Dictionary whose keys are cell IDs and values are :class:`Cell` - instances - volume : float - Volume of the universe in cm^3. This can either be set manually or - calculated in a stochastic volume calculation and added via the - :meth:`Universe.add_volume_information` method. - bounding_box : openmc.BoundingBox - Lower-left and upper-right coordinates of an axis-aligned bounding box - of the universe. - - """ - - def __init__(self, universe_id=None, name='', cells=None): - super().__init__(universe_id, name) - - if cells is not None: - self.add_cells(cells) - - def __repr__(self): - string = super().__repr__() - string += '{: <16}=\t{}\n'.format('\tGeom', 'CSG') - string += '{: <16}=\t{}\n'.format('\tCells', list(self._cells.keys())) - return string - - - @property - def bounding_box(self): - regions = [c.region for c in self.cells.values() - if c.region is not None] - if regions: - return openmc.Union(regions).bounding_box - else: - return openmc.BoundingBox.infinite() - - @classmethod - def from_hdf5(cls, group, cells): - """Create universe from HDF5 group - - Parameters - ---------- - group : h5py.Group - Group in HDF5 file - cells : dict - Dictionary mapping cell IDs to instances of :class:`openmc.Cell`. - - Returns - ------- - openmc.Universe - Universe instance - - """ - universe_id = int(group.name.split('/')[-1].lstrip('universe ')) - cell_ids = group['cells'][()] - - # Create this Universe - universe = cls(universe_id) - - # Add each Cell to the Universe - for cell_id in cell_ids: - universe.add_cell(cells[cell_id]) - - return universe - + def find(self, point): """Find cells/universes/lattices which contain a given point @@ -698,6 +614,90 @@ def get_nuclide_densities(self): return nuclides + +class Universe(UniverseBase): + """A collection of cells that can be repeated. + + Parameters + ---------- + universe_id : int, optional + Unique identifier of the universe. If not specified, an identifier will + automatically be assigned + name : str, optional + Name of the universe. If not specified, the name is the empty string. + cells : Iterable of openmc.Cell, optional + Cells to add to the universe. By default no cells are added. + + Attributes + ---------- + id : int + Unique identifier of the universe + name : str + Name of the universe + cells : dict + Dictionary whose keys are cell IDs and values are :class:`Cell` + instances + volume : float + Volume of the universe in cm^3. This can either be set manually or + calculated in a stochastic volume calculation and added via the + :meth:`Universe.add_volume_information` method. + bounding_box : openmc.BoundingBox + Lower-left and upper-right coordinates of an axis-aligned bounding box + of the universe. + + """ + + def __init__(self, universe_id=None, name='', cells=None): + super().__init__(universe_id, name) + + if cells is not None: + self.add_cells(cells) + + def __repr__(self): + string = super().__repr__() + string += '{: <16}=\t{}\n'.format('\tGeom', 'CSG') + string += '{: <16}=\t{}\n'.format('\tCells', list(self._cells.keys())) + return string + + + @property + def bounding_box(self): + regions = [c.region for c in self.cells.values() + if c.region is not None] + if regions: + return openmc.Union(regions).bounding_box + else: + return openmc.BoundingBox.infinite() + + @classmethod + def from_hdf5(cls, group, cells): + """Create universe from HDF5 group + + Parameters + ---------- + group : h5py.Group + Group in HDF5 file + cells : dict + Dictionary mapping cell IDs to instances of :class:`openmc.Cell`. + + Returns + ------- + openmc.Universe + Universe instance + + """ + universe_id = int(group.name.split('/')[-1].lstrip('universe ')) + cell_ids = group['cells'][()] + + # Create this Universe + universe = cls(universe_id) + + # Add each Cell to the Universe + for cell_id in cell_ids: + universe.add_cell(cells[cell_id]) + + return universe + def add_cell(self, cell): """Add a cell to the universe. From 8b0906d424218b62ad1b12cc808b17061b6c93fc Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sat, 5 Oct 2024 16:30:43 +0200 Subject: [PATCH 061/122] allow assignement overload per cell_id for DAGMC universe --- openmc/dagmc.py | 26 +++++++++++++++++--------- src/dagmc.cpp | 30 ++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 99473f140b5..c47a7a606b7 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -253,6 +253,9 @@ def create_xml_subelement(self, xml_element, memo=None): memo.add(self) + # Ensure that the material overrides are up-t-date + self.build_overide_mat_from_cells() + # Set xml element values dagmc_element = ET.Element('dagmc_universe') dagmc_element.set('id', str(self.id)) @@ -262,13 +265,6 @@ def create_xml_subelement(self, xml_element, memo=None): if self.auto_mat_ids: dagmc_element.set('auto_mat_ids', 'true') dagmc_element.set('filename', str(self.filename)) - if not self.material_overrides: - mats = self.get_all_materials() - for mat in mats.values(): - if mat.name[:-4] in self.material_names: - self.material_overrides.setdefault( - mat.name[:-4].lower(), []).append(mat.name) - if self.material_overrides: mat_element = ET.Element('material_overrides') for key in self.material_overrides: @@ -277,6 +273,18 @@ def create_xml_subelement(self, xml_element, memo=None): dagmc_element.append(mat_element) xml_element.append(dagmc_element) + def build_overide_mat_from_cells(self): + """ + Builds the material override dictionary for cells with multiple instances. + + Returns: + None + """ + for cell in self.cells.values(): + if cell.n_instances > 1 and isinstance(cell.fill, Iterable): + for mat in cell.fill: + self.material_overrides[str(cell.id)] = [mat.name for mat in cell.fill] + def bounding_region( self, bounded_type: str = 'box', @@ -504,8 +512,8 @@ def sync_dagmc_cells(self, mats={}): for dag_cell_id in dagmc_cell_ids: dag_cell = openmc.lib.cells[dag_cell_id] if isinstance(dag_cell.fill, Iterable): - fill = [mats_per_id[mat_id.id] - for mat_id in dag_cell.fill] + fill = [mats_per_id[mat.id] + for mat in dag_cell.fill] else: fill = mats_per_id[dag_cell.fill.id] dag_pseudo_cell = openmc.DAGMCCell( diff --git a/src/dagmc.cpp b/src/dagmc.cpp index f1396495a77..148c8b012a2 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -232,14 +232,28 @@ void DAGUniverse::init_geometry() if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); } else { - if (instance_material_overrides.count(mat_str)) { - if (instance_material_overrides.at(mat_str).size() != - c->n_instances_) { - fatal_error(fmt::format("DAGMC Cell assign with material {} has {} " - "instances but material_overrides has {} " - "material assignments for this material", - mat_str, c->n_instances_, - instance_material_overrides.at(mat_str).size())); + if (instance_material_overrides.count(std::to_string(c->id_))) { + int n_override = + instance_material_overrides.at(std::to_string(c->id_)).size(); + if (n_override != c->n_instances_) { + fatal_error(fmt::format("material_overrides has for Cell {} has {}" + "material assignments for this material, " + "where the cell has {} instances.", + c->id_, n_override, c->n_instances_)); + } + + for (auto mat_str_instance : + instance_material_overrides.at(mat_str)) { + legacy_assign_material(mat_str_instance, c); + } + } else if (instance_material_overrides.count(mat_str)) { + int n_override = instance_material_overrides.at(mat_str).size(); + if (n_override != c->n_instances_) { + fatal_error( + fmt::format("DAGMC Cell assigned with material {} has {} " + "instances but material_overrides has {} " + "material assignments for this material", + mat_str, c->n_instances_, n_override)); } for (auto mat_str_instance : From 569348e87889295872ba03a6c30e86cc1745fd08 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 8 Oct 2024 11:12:53 +0200 Subject: [PATCH 062/122] reset fmt to upstream/develop val --- vendor/fmt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/fmt b/vendor/fmt index d141cdbeb0f..0c9fce2ffef 160000 --- a/vendor/fmt +++ b/vendor/fmt @@ -1 +1 @@ -Subproject commit d141cdbeb0fb422a3fb7173b285fd38e0d1772dc +Subproject commit 0c9fce2ffefecfdce794e1859584e25877b7b592 From a9e0306be2bc32d51cfb7d1718f88f636ed867c8 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:25:24 +0200 Subject: [PATCH 063/122] Apply suggestions from code review Co-authored-by: Patrick Shriwise --- openmc/cell.py | 1 - openmc/lib/dagmc.py | 1 + openmc/model/model.py | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index 8cc5dee4a47..e5c836c46a7 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -781,4 +781,3 @@ def from_xml_element(cls, elem, surfaces, materials, get_universe): get_universe(univ_id).add_cell(c) return c - diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index ec9924578c8..d42b5ea857f 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -43,6 +43,7 @@ def get_dagmc_cell_ids(dagmc_id): ) return cell_ids + def get_dagmc_universe_num_cells(dagmc_id): """Get the number of cells in a DAGMC universe. diff --git a/openmc/model/model.py b/openmc/model/model.py index c07fe7d8633..d90450bf8f4 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1217,8 +1217,8 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo ) cell.fill.volume = cell.volume if isinstance(cell, openmc.DAGMCCell): - for i in range(cell.num_instances): - cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" + for i, f in enumerate(cell.fill): + f.name += f"_{cell.id}_{i}" if self.materials is not None: self.materials = openmc.Materials( From dae5527bfbf2255da9cde6ed21f93588e7a2f011 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Fri, 11 Oct 2024 16:22:27 +0200 Subject: [PATCH 064/122] adressing third round of comment from @pshriwise --- include/openmc/surface.h | 8 ++++++- openmc/dagmc.py | 40 ++++++++++++++++++++++++-------- openmc/model/model.py | 50 +++++++++++++++++++++++++--------------- src/dagmc.cpp | 2 +- src/particle.cpp | 7 +++--- src/plot.cpp | 2 +- src/surface.cpp | 8 +++---- 7 files changed, 78 insertions(+), 39 deletions(-) diff --git a/include/openmc/surface.h b/include/openmc/surface.h index af235301c14..498f71d4f9b 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -38,7 +38,6 @@ class Surface { int id_; //!< Unique ID std::string name_; //!< User-defined name unique_ptr bc_; //!< Boundary condition - GeometryType geom_type_; //!< Geometry type indicator (CSG or DAGMC) bool surf_source_ {false}; //!< Activate source banking for the surface? explicit Surface(pugi::xml_node surf_node); @@ -91,6 +90,13 @@ class Surface { //! Get the BoundingBox for this surface. virtual BoundingBox bounding_box(bool /*pos_side*/) const { return {}; } + // Accessors + const GeometryType& geom_type() const { return geom_type_; } + GeometryType& geom_type() { return geom_type_; } + +private: + GeometryType geom_type_; //!< Geometry type indicator (CSG or DAGMC) + protected: virtual void to_hdf5_inner(hid_t group_id) const = 0; }; diff --git a/openmc/dagmc.py b/openmc/dagmc.py index c47a7a606b7..8cc61785f2a 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -20,6 +20,9 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.13.0 + .. versionadded:: 0.15.1-dev + Moved this classe from openmc.universe to openmc.dagmc + Parameters ---------- filename : str @@ -139,9 +142,11 @@ def material_overrides(self, val): self._material_overrides = val - def add_material_override(self, mat_name, overrides): + def add_material_override(self, mat_name=None, cell_id=None, overrides=None): """Add a material override to the universe. + .. versionadded:: 0.15 + Parameters ---------- key : str @@ -150,12 +155,27 @@ def add_material_override(self, mat_name, overrides): Material names to replace the key with """ - cv.check_type('material name', mat_name, str) - if mat_name not in self.material_names: - raise ValueError( - f"Material name '{mat_name}' not found in DAGMC file") + key = "" + if mat_name and cell_id: + raise ValueError("Only one of 'mat_name' or 'cell_id' can be set") + elif cell_id: + cv.check_type('cell id', cell_id, int) + if cell_id not in self.cells: + raise ValueError( + f"Cell ID '{cell_id}' not found in DAGMC universe") + else: + key = str(cell_id) + elif mat_name: + cv.check_type('material name', mat_name, str) + if mat_name not in self.material_names: + raise ValueError( + f"Material name '{mat_name}' not found in DAGMC file") + else: + key = mat_name + else: + raise ValueError("Either 'mat_name' or 'cell_id' must be set") + cv.check_iterable_type('material objects', overrides, str) - self.material_overrides[mat_name] = overrides @filename.setter @@ -281,7 +301,7 @@ def build_overide_mat_from_cells(self): None """ for cell in self.cells.values(): - if cell.n_instances > 1 and isinstance(cell.fill, Iterable): + if isinstance(cell.fill, Iterable): for mat in cell.fill: self.material_overrides[str(cell.id)] = [mat.name for mat in cell.fill] @@ -489,10 +509,10 @@ def remove_cell(self, cell): # If the Cell is in the Universe's list of Cells, delete it self._cells.pop(cell.id, None) - def sync_dagmc_cells(self, mats={}): + def sync_dagmc_cells(self, mats): """Synchronize DAGMC cell information between Python and C API - .. versionadded:: 0.13.0 + .. versionadded:: 0.15.1-dev """ import openmc.lib @@ -524,7 +544,7 @@ def sync_dagmc_cells(self, mats={}): class DAGMCCell(openmc.Cell): """ - .. versionadded:: 0.13.2 + .. versionadded:: 0.15.1-dev A cell class for DAGMC-based geometries. Parameters diff --git a/openmc/model/model.py b/openmc/model/model.py index d90450bf8f4..9d174a756be 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -330,16 +330,19 @@ def sync_dagmc_universe(self): Synchronize all DAGMC universes with the current geometry. This method iterates over all DAGMC universes in the geometry and synchronizes their cells with the current material assignments. + + .. versionadded:: 0.15.1-dev + Returns: None """ if self.is_initialized: if self.materials: - mats = self.materials + materials = self.materials else: - mats = self.geometry.get_all_materials().values() + materials = self.geometry.get_all_materials().values() for dagmc_universe in self.geometry.get_all_dagmc_universes().values(): - dagmc_universe.sync_dagmc_cells(mats) + dagmc_universe.sync_dagmc_cells(materials) else: raise ValueError("The model must be initialized before calling " "this method") @@ -1154,38 +1157,47 @@ def differentiate_depletable_mats(self, diff_volume_method : str = None): .. versionadded:: 0.14.0 + .. version added:: 0.15.1-dev + diff_volume_method default is None, do not apply volume to the new + materials. Is now a convenience method for + differentiate_mats(diff_volume_method, depletable_only=True) + Parameters ---------- diff_volume_method : str Specifies how the volumes of the new materials should be found. - Default is to 'divide equally' which divides the original material - volume equally between the new materials, 'match cell' sets the - volume of the material to volume of the cell they fill. + Default is to 'None', do not apply volume to the new materials, + 'divide equally' which divides the original material + volume equally between the new materials, + 'match cell' sets the volume of the material to volume of the cell + they fill. """ self.differentiate_mats(diff_volume_method, depletable_only=True) def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bool = True): - """Assign distribmats for each depletable material + """Assign distribmats for each material - .. versionadded:: 0.14.0 + .. versionadded:: 0.15.1-dev Parameters ---------- diff_volume_method : str Specifies how the volumes of the new materials should be found. - Default is to 'divide equally' which divides the original material - volume equally between the new materials, 'match cell' sets the - volume of the material to volume of the cell they fill. + Default is to 'None', do not apply volume to the new materials, + 'divide equally' which divides the original material + volume equally between the new materials, + 'match cell' sets the volume of the material to volume of the cell + they fill. + depletable_only : bool + Default is True, only depletable materials will be differentiated all materials will be + differentiated otherwise. """ - if diff_volume_method not in ("divide equally", "match cell", None): - raise ValueError( - "diff_volume_method must be 'divide equally' or 'match cell', " - f"not '{diff_volume_method}'" - ) + check_value('volume differentiation method', diff_volume_method, ["divide equally", "match cell", None]) + # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) - # Extract all depletable materials which have multiple instances + # Extract all or depletable_only materials which have multiple instance distribmats = set( [mat for mat in self.materials if (mat.depletable or not depletable_only) and mat.num_instances > 1]) @@ -1217,8 +1229,8 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo ) cell.fill.volume = cell.volume if isinstance(cell, openmc.DAGMCCell): - for i, f in enumerate(cell.fill): - f.name += f"_{cell.id}_{i}" + for i in range(cell.num_instances): + cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" if self.materials is not None: self.materials = openmc.Materials( diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 148c8b012a2..6af723cca09 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -765,7 +765,7 @@ BoundingBox DAGCell::bounding_box() const DAGSurface::DAGSurface(std::shared_ptr dag_ptr, int32_t dag_idx) : Surface {}, dagmc_ptr_(dag_ptr), dag_index_(dag_idx) { - geom_type_ = GeometryType::DAG; + geom_type() = GeometryType::DAG; } // empty constructor moab::EntityHandle DAGSurface::mesh_handle() const diff --git a/src/particle.cpp b/src/particle.cpp index 64c50c9438f..0ea8650143d 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -533,7 +533,7 @@ void Particle::cross_surface(const Surface& surf) // if we're crossing a CSG surface, make sure the DAG history is reset #ifdef DAGMC - if (surf.geom_type_ == GeometryType::CSG) + if (surf.geom_type() == GeometryType::CSG) history().reset(); #endif @@ -548,7 +548,7 @@ void Particle::cross_surface(const Surface& surf) #ifdef DAGMC // in DAGMC, we know what the next cell should be - if (surf.geom_type_ == GeometryType::DAG) { + if (surf.geom_type() == GeometryType::DAG) { int32_t i_cell = next_cell(std::abs(surface()), cell_last(n_coord() - 1), lowest_coord().universe) - 1; @@ -668,7 +668,8 @@ void Particle::cross_reflective_bc(const Surface& surf, Direction new_u) // the lower universes. // (unless we're using a dagmc model, which has exactly one universe) n_coord() = 1; - if (surf.geom_type_ != GeometryType::DAG && !neighbor_list_find_cell(*this)) { + if (surf.geom_type() != GeometryType::DAG && + !neighbor_list_find_cell(*this)) { mark_as_lost("Couldn't find particle after reflecting from surface " + std::to_string(surf.id_) + "."); return; diff --git a/src/plot.cpp b/src/plot.cpp index 348138570c1..43f25a9a32f 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -1301,7 +1301,7 @@ void ProjectionPlot::create_output() const int32_t i_surface = std::abs(p.surface()) - 1; if (i_surface > 0 && - model::surfaces[i_surface]->geom_type_ == GeometryType::DAG) { + model::surfaces[i_surface]->geom_type() == GeometryType::DAG) { #ifdef DAGMC int32_t i_cell = next_cell(i_surface, p.cell_last(p.n_coord() - 1), p.lowest_coord().universe); diff --git a/src/surface.cpp b/src/surface.cpp index 50ef2a12830..dbcaf849848 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -165,9 +165,9 @@ void Surface::to_hdf5(hid_t group_id) const { hid_t surf_group = create_group(group_id, fmt::format("surface {}", id_)); - if (geom_type_ == GeometryType::DAG) { + if (geom_type() == GeometryType::DAG) { write_string(surf_group, "geom_type", "dagmc", false); - } else if (geom_type_ == GeometryType::CSG) { + } else if (geom_type() == GeometryType::CSG) { write_string(surf_group, "geom_type", "csg", false); if (bc_) { @@ -189,11 +189,11 @@ void Surface::to_hdf5(hid_t group_id) const CSGSurface::CSGSurface() : Surface {} { - geom_type_ = GeometryType::CSG; + geom_type() = GeometryType::CSG; }; CSGSurface::CSGSurface(pugi::xml_node surf_node) : Surface {surf_node} { - geom_type_ = GeometryType::CSG; + geom_type() = GeometryType::CSG; }; //============================================================================== From 50cfcab84e86e106fb953689103e7c2b43d9f6e8 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Fri, 11 Oct 2024 17:19:14 +0200 Subject: [PATCH 065/122] Adding missing import for input_path --- openmc/dagmc.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 8e3c33b95f2..6c8a0a0f3fa 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -13,6 +13,7 @@ from .checkvalue import check_type, check_value from .surface import _BOUNDARY_TYPES from .bounding_box import BoundingBox +from .utility_funcs import input_path class DAGMCUniverse(openmc.UniverseBase): @@ -120,6 +121,11 @@ def bounding_box(self): def filename(self): return self._filename + @filename.setter + def filename(self, val: cv.PathLike): + cv.check_type('DAGMC filename', val, cv.PathLike) + self._filename = input_path(val) + @property def material_overrides(self): return self._material_overrides @@ -178,11 +184,6 @@ def add_material_override(self, mat_name=None, cell_id=None, overrides=None): cv.check_iterable_type('material objects', overrides, str) self.material_overrides[mat_name] = overrides - @filename.setter - def filename(self, val: cv.PathLike): - cv.check_type('DAGMC filename', val, cv.PathLike) - self._filename = input_path(val) - @property def auto_geom_ids(self): return self._auto_geom_ids From 46f1ecf330d6966a19c5e7dc657fe26a7bda1d0a Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:16:13 +0100 Subject: [PATCH 066/122] Apply suggestions from code review Co-authored-by: Patrick Shriwise --- openmc/model/model.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 9d174a756be..4c7d9798672 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -327,14 +327,12 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, def sync_dagmc_universe(self): """ - Synchronize all DAGMC universes with the current geometry. + Synchronize all DAGMC universes in the current geometry. This method iterates over all DAGMC universes in the geometry and synchronizes their cells with the current material assignments. .. versionadded:: 0.15.1-dev - Returns: - None """ if self.is_initialized: if self.materials: From 6b4efa9efc236d922ae214e069ac058384941fcb Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 28 Oct 2024 17:46:35 +0100 Subject: [PATCH 067/122] add missing docstring --- include/openmc/dagmc.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 7a47be7f27b..1984aacc3cb 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -184,7 +184,12 @@ class DAGUniverse : public Universe { //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume - std::map> instance_material_overrides; + std::map> + instance_material_overrides; ///!< Map of material overrides + ///!< keys correspond to the material name + ///!< or id + ///!< values are a list of materials used + ///!< git for the override }; //============================================================================== From 1d49274dc9ba4786680ed1df1ee33c8691ec6141 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 28 Oct 2024 17:51:38 +0100 Subject: [PATCH 068/122] direct import in init --- openmc/lib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 5fe35b9745d..4cdd53b6a70 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -68,7 +68,7 @@ def _uwuw_enabled(): from .math import * from .plot import * from .weight_windows import * -from .dagmc import * +from .dagmc import get_dagmc_cell_ids, get_dagmc_universe_num_cells # Flag to denote whether or not openmc.lib.init has been called # TODO: Establish and use a flag in the C++ code to represent the status of the From 89b4e37dd53b3f87b562610977ab2a19a1e0a735 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 30 Oct 2024 17:16:09 +0100 Subject: [PATCH 069/122] adressing @pshriwise comments --- include/openmc/dagmc.h | 6 ++ src/dagmc.cpp | 83 ++++++++++-------- .../dagmc/dagmc_differentiate_mat.h5m | Bin 158140 -> 0 bytes tests/unit_tests/dagmc/test_model.py | 53 ++++------- 4 files changed, 70 insertions(+), 72 deletions(-) delete mode 100755 tests/unit_tests/dagmc/dagmc_differentiate_mat.h5m diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 1984aacc3cb..3a636454fb5 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -133,6 +133,12 @@ class DAGUniverse : public Universe { void legacy_assign_material( std::string mat_string, std::unique_ptr& c) const; + //! Assign a material overriding normal assignement to a cell + //! \param[in] key The material key to override + //! \param[in] c The OpenMC cell to which the material is assigned + void override_assign_material(std::string key, moab::EntityHandle vol_handle, + std::unique_ptr& c) const; + //! Return the index into the model cells vector for a given DAGMC volume //! handle in the universe //! \param[in] vol MOAB handle to the DAGMC volume set diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 9b112b139b0..b0a33be9a4b 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -234,40 +234,14 @@ void DAGUniverse::init_geometry() if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { - if (uses_uwuw()) { + if (instance_material_overrides.count(std::to_string(c->id_)) || + instance_material_overrides.count( + mat_str)) { // Check for material override + override_assign_material(mat_str, vol_handle, c); + } else if (uses_uwuw()) { // UWUW assignement uwuw_assign_material(vol_handle, c); - } else { - if (instance_material_overrides.count(std::to_string(c->id_))) { - int n_override = - instance_material_overrides.at(std::to_string(c->id_)).size(); - if (n_override != c->n_instances_) { - fatal_error(fmt::format("material_overrides has for Cell {} has {}" - "material assignments for this material, " - "where the cell has {} instances.", - c->id_, n_override, c->n_instances_)); - } - - for (auto mat_str_instance : - instance_material_overrides.at(mat_str)) { - legacy_assign_material(mat_str_instance, c); - } - } else if (instance_material_overrides.count(mat_str)) { - int n_override = instance_material_overrides.at(mat_str).size(); - if (n_override != c->n_instances_) { - fatal_error( - fmt::format("DAGMC Cell assigned with material {} has {} " - "instances but material_overrides has {} " - "material assignments for this material", - mat_str, c->n_instances_, n_override)); - } - - for (auto mat_str_instance : - instance_material_overrides.at(mat_str)) { - legacy_assign_material(mat_str_instance, c); - } - } else { - legacy_assign_material(mat_str, c); - } + } else { // legacy assignement + legacy_assign_material(mat_str, c); } } @@ -657,6 +631,35 @@ void DAGUniverse::uwuw_assign_material( fatal_error("DAGMC was not configured with UWUW."); #endif // OPENMC_UWUW } + +void DAGUniverse::override_assign_material(std::string key, + moab::EntityHandle vol_handle, std::unique_ptr& c) const +{ + + // if Cell ID matches an override key, use it to override the material + // assignment else if UWUW is used, get the material assignment from the DAGMC + // metadata + if (instance_material_overrides.count(std::to_string(c->id_))) { + key = std::to_string(c->id_); + } else if (uses_uwuw()) { + key = dmd_ptr->volume_material_property_data_eh[vol_handle]; + } + + int n_override = instance_material_overrides.at(key).size(); + if (n_override != c->n_instances_) { + fatal_error( + fmt::format("material_overrides has for Cell or material {} has {}" + "material assignments for this material, " + "where the corresponding cell has {} instances.", + key, c->n_instances_, n_override)); + } + // Override the material assignment for each cell instance using the legacy + // assignement + for (auto mat_str_instance : instance_material_overrides.at(key)) { + legacy_assign_material(mat_str_instance, c); + } +} + //============================================================================== // DAGMC Cell implementation //============================================================================== @@ -884,6 +887,7 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( } std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids); *n = dag_cell_ids.size(); + return 0; } extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) @@ -894,8 +898,8 @@ extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) fatal_error( "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe"); } - *n = univ->cells_.size(); + return 0; } } // namespace openmc @@ -905,10 +909,15 @@ extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) namespace openmc { extern "C" int openmc_dagmc_universe_get_cell_ids( - int32_t univ_id, int32_t* ids, size_t* n) {}; + int32_t univ_id, int32_t* ids, size_t* n) +{ + fatal_error("OpenMC was not configured with DAGMC"); +}; -extern "C" int openmc_dagmc_universe_get_num_cells( - int32_t univ_id, size_t* n) {}; +extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) +{ + fatal_error("OpenMC was not configured with DAGMC"); +}; void read_dagmc_universes(pugi::xml_node node) { diff --git a/tests/unit_tests/dagmc/dagmc_differentiate_mat.h5m b/tests/unit_tests/dagmc/dagmc_differentiate_mat.h5m deleted file mode 100755 index fe0fc67a8c89642238cf010f7b21540c88443ae9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158140 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 Date: Thu, 31 Oct 2024 16:15:13 +0100 Subject: [PATCH 070/122] updte test and adding test to check differentiation and volume calculation --- include/openmc/string_utils.h | 1 + openmc/dagmc.py | 14 ++-- src/dagmc.cpp | 21 +++--- src/string_utils.cpp | 25 +++++++ tests/unit_tests/dagmc/test_model.py | 105 ++++++++++++++++++++++++++- 5 files changed, 145 insertions(+), 21 deletions(-) diff --git a/include/openmc/string_utils.h b/include/openmc/string_utils.h index 2e8b0d14f39..e7e613534a4 100644 --- a/include/openmc/string_utils.h +++ b/include/openmc/string_utils.h @@ -19,6 +19,7 @@ void to_lower(std::string& str); int word_count(const std::string& str); vector split(const std::string& in); +vector split(const std::string& in, char delim); bool ends_with(const std::string& value, const std::string& ending); diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 6c8a0a0f3fa..18d6500b5b5 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -288,7 +288,8 @@ def create_xml_subelement(self, xml_element, memo=None): if self.material_overrides: mat_element = ET.Element('material_overrides') for key in self.material_overrides: - mat_element.set(key, ' '.join( + print(key, self.material_overrides[key]) + mat_element.set('id_{}'.format(key), ';'.join( t for t in self.material_overrides[key])) dagmc_element.append(mat_element) xml_element.append(dagmc_element) @@ -300,10 +301,10 @@ def build_overide_mat_from_cells(self): Returns: None """ + self.material_overrides = {} for cell in self.cells.values(): if isinstance(cell.fill, Iterable): - for mat in cell.fill: - self.material_overrides[str(cell.id)] = [mat.name for mat in cell.fill] + self.material_overrides[str(cell.id)] = [mat.name for mat in cell.fill if mat] def bounding_region( self, @@ -533,9 +534,12 @@ def sync_dagmc_cells(self, mats): dag_cell = openmc.lib.cells[dag_cell_id] if isinstance(dag_cell.fill, Iterable): fill = [mats_per_id[mat.id] - for mat in dag_cell.fill] + for mat in dag_cell.fill if mat] else: - fill = mats_per_id[dag_cell.fill.id] + if dag_cell.fill: + fill = mats_per_id[dag_cell.fill.id] + else: + fill = None dag_pseudo_cell = openmc.DAGMCCell( cell_id=dag_cell_id, fill=fill ) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index b0a33be9a4b..39449bb382d 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -83,7 +83,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) // Get mat name for each assignement instances std::stringstream iss {attr.value()}; - vector instance_mats = split(iss.str()); + vector instance_mats = split(iss.str(), ';'); // Store mat name for each instances instance_material_overrides.insert( @@ -230,11 +230,13 @@ void DAGUniverse::init_geometry() if (mat_str == "graveyard") { graveyard = vol_handle; } + std::cout << "id_" << std::to_string(c->id_) << " " << c->n_instances_ + << std::endl; // material void checks if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { - if (instance_material_overrides.count(std::to_string(c->id_)) || + if (instance_material_overrides.count("id_" + std::to_string(c->id_)) || instance_material_overrides.count( mat_str)) { // Check for material override override_assign_material(mat_str, vol_handle, c); @@ -639,20 +641,15 @@ void DAGUniverse::override_assign_material(std::string key, // if Cell ID matches an override key, use it to override the material // assignment else if UWUW is used, get the material assignment from the DAGMC // metadata - if (instance_material_overrides.count(std::to_string(c->id_))) { - key = std::to_string(c->id_); + std::cout << "XML instance" + << instance_material_overrides.count("id_" + std::to_string(c->id_)) + << std::endl; + if (instance_material_overrides.count("id_" + std::to_string(c->id_))) { + key = "id_" + std::to_string(c->id_); } else if (uses_uwuw()) { key = dmd_ptr->volume_material_property_data_eh[vol_handle]; } - int n_override = instance_material_overrides.at(key).size(); - if (n_override != c->n_instances_) { - fatal_error( - fmt::format("material_overrides has for Cell or material {} has {}" - "material assignments for this material, " - "where the corresponding cell has {} instances.", - key, c->n_instances_, n_override)); - } // Override the material assignment for each cell instance using the legacy // assignement for (auto mat_str_instance : instance_material_overrides.at(key)) { diff --git a/src/string_utils.cpp b/src/string_utils.cpp index 74f048e8d24..454f2dca0be 100644 --- a/src/string_utils.cpp +++ b/src/string_utils.cpp @@ -71,6 +71,31 @@ vector split(const std::string& in) return out; } +vector split(const std::string& in, char delim) +{ + vector out; + + for (int i = 0; i < in.size();) { + // Increment i until we find a non-delimiter character. + if (in[i] == delim) { + i++; + + } else { + // Find the next delimiter character at j. + int j = i + 1; + while (j < in.size() && in[j] != delim) { + j++; + } + + // Push-back everything between i and j. + out.push_back(in.substr(i, j - i)); + i = j + 1; // j is delimiter so leapfrog to j+1 + } + } + + return out; +} + bool ends_with(const std::string& value, const std::string& ending) { if (ending.size() > value.size()) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index a386c854c4c..9c4635fde5d 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -11,7 +11,7 @@ not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") -def test_model_differentiate_with_DAGMC(): +def test_model_differentiate_depletable_with_DAGMC(): PITCH = 1.26 mats = {} @@ -20,9 +20,9 @@ def test_model_differentiate_with_DAGMC(): mats["no-void fuel"].add_nuclide("U238", 0.97) mats["no-void fuel"].add_nuclide("O16", 2.0) mats["no-void fuel"].set_density("g/cm3", 10.0) - mats["no-void fuel"].name = "Fuel" + mats["no-void fuel"].name = "no-void fuel" - mats["41"] = openmc.Material(name="h2o") + mats["41"] = openmc.Material(name="41") mats["41"].add_nuclide("H1", 2.0) mats["41"].add_element("O", 1.0) mats["41"].set_density("g/cm3", 1.0) @@ -93,8 +93,105 @@ def pattern(center, bc): model.calculate_volumes(cwd=p) volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) nmat = len(model.materials) + print(model.materials) model.differentiate_depletable_mats(diff_volume_method="divide equally") volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) - assert len(model.materials) == nmat + 3 + print(model.materials) + assert len(model.materials) == 4*2 +1 + assert np.isclose(volume_before, volume_after) + model.finalize_lib() + + +def test_model_differentiate_with_DAGMC(): + PITCH = 1.26 + + mats = {} + mats["no-void fuel"] = openmc.Material(1, "no-void fuel") + mats["no-void fuel"].add_nuclide("U235", 0.03) + mats["no-void fuel"].add_nuclide("U238", 0.97) + mats["no-void fuel"].add_nuclide("O16", 2.0) + mats["no-void fuel"].set_density("g/cm3", 10.0) + mats["no-void fuel"].name = "no-void fuel" + + mats["41"] = openmc.Material(name="41") + mats["41"].add_nuclide("H1", 2.0) + mats["41"].add_element("O", 1.0) + mats["41"].set_density("g/cm3", 1.0) + mats["41"].add_s_alpha_beta("c_H_in_H2O") + mats["41"].name = "41" + + file = pkg_resources.resource_filename(__name__, "dagmc.h5m") + + daguniv = openmc.DAGMCUniverse(file, auto_geom_ids=True,) + + def pattern(center, bc): + bc_ = { + "top": "transmission", + "bottom": "transmission", + "left": "transmission", + "right": "transmission", + } + bc_ |= bc + box = ( + -XPlane(center[0] + PITCH / 2, boundary_type=bc_["right"]) + & +XPlane(center[0] - PITCH / 2, boundary_type=bc_["left"]) + & -YPlane(center[1] + PITCH / 2, boundary_type=bc_["top"]) + & +YPlane(center[1] - PITCH / 2, boundary_type=bc_["bottom"]) + & -ZPlane(5, boundary_type="reflective") + & +ZPlane(-5, boundary_type="reflective") + ) + cell = Cell(region=box, fill=daguniv) + cell.translation = [*center, 0] + return [cell] + + root = openmc.Universe( + cells=[ + *pattern((-PITCH / 2, -PITCH / 2), + bc={"left": "reflective", "bottom": "reflective"}), + *pattern((-PITCH / 2, PITCH / 2), + bc={"left": "reflective", "top": "reflective"}), + *pattern((PITCH / 2, PITCH / 2), + bc={"right": "reflective", "top": "reflective"}), + *pattern((PITCH / 2, -PITCH / 2), + bc={"right": "reflective", "bottom": "reflective"}), + ] + ) + + point = openmc.stats.Point((0, 0, 0)) + source = openmc.IndependentSource(space=point) + + settings = openmc.Settings() + settings.source = source + settings.batches = 100 + settings.inactive = 10 + settings.particles = 1000 + + ll, ur = root.bounding_box + mat_vol = openmc.VolumeCalculation([mats["no-void fuel"]], 1000000, ll, ur) + cell_vol = openmc.VolumeCalculation( + list(root.cells.values()), 1000000, ll, ur) + settings.volume_calculations = [mat_vol, cell_vol] + + model = openmc.Model() + model.materials = openmc.Materials(mats.values()) + model.geometry = openmc.Geometry(root=root) + model.settings = settings + + p = Path("differentiate_depletable_mats/divide_equally") + p.mkdir(parents=True, exist_ok=True) + model.init_lib() + model.sync_dagmc_universe() + model.calculate_volumes(cwd=p) + volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + model.differentiate_mats(depletable_only=False) + mat_list = [m for m in model.materials] + mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) + settings.volume_calculations = [mat_vol, cell_vol] + model.finalize_lib() + model.init_lib() + model.calculate_volumes(cwd=p) + + volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) + assert len(model.materials) == 4*2 + 4 assert np.isclose(volume_before, volume_after) model.finalize_lib() From 4297bc797159f72de6b1eadd8c4d0c550b142fff Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 11 Nov 2024 16:59:23 +0100 Subject: [PATCH 071/122] factorising dagmc model tests --- tests/unit_tests/dagmc/test_model.py | 107 ++++++--------------------- 1 file changed, 24 insertions(+), 83 deletions(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 9c4635fde5d..d5de850ae87 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -11,7 +11,8 @@ not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") -def test_model_differentiate_depletable_with_DAGMC(): + +def set_dagmc_model(): PITCH = 1.26 mats = {} @@ -86,112 +87,52 @@ def pattern(center, bc): model.geometry = openmc.Geometry(root=root) model.settings = settings + +def test_model_differentiate_depletable_with_DAGMC(): + model = set_dagmc_model() + p = Path("differentiate_depletable_mats/divide_equally") p.mkdir(parents=True, exist_ok=True) model.init_lib() model.sync_dagmc_universe() model.calculate_volumes(cwd=p) + + # Get the volume of the no-void fuel material before differentiation volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) - nmat = len(model.materials) - print(model.materials) + + # Differentiate the depletable materials model.differentiate_depletable_mats(diff_volume_method="divide equally") + # Get the volume of the no-void fuel material after differentiation volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) - print(model.materials) - assert len(model.materials) == 4*2 +1 assert np.isclose(volume_before, volume_after) + + assert len(model.materials) == 4*2 +1 model.finalize_lib() def test_model_differentiate_with_DAGMC(): - PITCH = 1.26 - - mats = {} - mats["no-void fuel"] = openmc.Material(1, "no-void fuel") - mats["no-void fuel"].add_nuclide("U235", 0.03) - mats["no-void fuel"].add_nuclide("U238", 0.97) - mats["no-void fuel"].add_nuclide("O16", 2.0) - mats["no-void fuel"].set_density("g/cm3", 10.0) - mats["no-void fuel"].name = "no-void fuel" - - mats["41"] = openmc.Material(name="41") - mats["41"].add_nuclide("H1", 2.0) - mats["41"].add_element("O", 1.0) - mats["41"].set_density("g/cm3", 1.0) - mats["41"].add_s_alpha_beta("c_H_in_H2O") - mats["41"].name = "41" - - file = pkg_resources.resource_filename(__name__, "dagmc.h5m") - - daguniv = openmc.DAGMCUniverse(file, auto_geom_ids=True,) - - def pattern(center, bc): - bc_ = { - "top": "transmission", - "bottom": "transmission", - "left": "transmission", - "right": "transmission", - } - bc_ |= bc - box = ( - -XPlane(center[0] + PITCH / 2, boundary_type=bc_["right"]) - & +XPlane(center[0] - PITCH / 2, boundary_type=bc_["left"]) - & -YPlane(center[1] + PITCH / 2, boundary_type=bc_["top"]) - & +YPlane(center[1] - PITCH / 2, boundary_type=bc_["bottom"]) - & -ZPlane(5, boundary_type="reflective") - & +ZPlane(-5, boundary_type="reflective") - ) - cell = Cell(region=box, fill=daguniv) - cell.translation = [*center, 0] - return [cell] - - root = openmc.Universe( - cells=[ - *pattern((-PITCH / 2, -PITCH / 2), - bc={"left": "reflective", "bottom": "reflective"}), - *pattern((-PITCH / 2, PITCH / 2), - bc={"left": "reflective", "top": "reflective"}), - *pattern((PITCH / 2, PITCH / 2), - bc={"right": "reflective", "top": "reflective"}), - *pattern((PITCH / 2, -PITCH / 2), - bc={"right": "reflective", "bottom": "reflective"}), - ] - ) - - point = openmc.stats.Point((0, 0, 0)) - source = openmc.IndependentSource(space=point) - - settings = openmc.Settings() - settings.source = source - settings.batches = 100 - settings.inactive = 10 - settings.particles = 1000 - - ll, ur = root.bounding_box - mat_vol = openmc.VolumeCalculation([mats["no-void fuel"]], 1000000, ll, ur) - cell_vol = openmc.VolumeCalculation( - list(root.cells.values()), 1000000, ll, ur) - settings.volume_calculations = [mat_vol, cell_vol] - - model = openmc.Model() - model.materials = openmc.Materials(mats.values()) - model.geometry = openmc.Geometry(root=root) - model.settings = settings - + + model = set_dagmc_model() p = Path("differentiate_depletable_mats/divide_equally") p.mkdir(parents=True, exist_ok=True) model.init_lib() model.sync_dagmc_universe() model.calculate_volumes(cwd=p) + # Get the volume of the no-void fuel material before differentiation volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + + # Differentiate all the materials model.differentiate_mats(depletable_only=False) + + # Get the volume of the no-void fuel material after differentiation mat_list = [m for m in model.materials] mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) - settings.volume_calculations = [mat_vol, cell_vol] - model.finalize_lib() + cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) + model.settings.volume_calculations = [mat_vol, cell_vol] model.init_lib() model.calculate_volumes(cwd=p) - volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) - assert len(model.materials) == 4*2 + 4 assert np.isclose(volume_before, volume_after) + + assert len(model.materials) == 4*2 + 4 model.finalize_lib() From 0b7411e89bf70c53b52310acf1b45f34adb0be9a Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 11 Nov 2024 17:00:03 +0100 Subject: [PATCH 072/122] add comments --- tests/unit_tests/dagmc/test_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index d5de850ae87..48c2f2cfdfb 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -129,7 +129,7 @@ def test_model_differentiate_with_DAGMC(): mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) model.settings.volume_calculations = [mat_vol, cell_vol] - model.init_lib() + model.init_lib()i # need to reinitialize the lib after differentiating the materials model.calculate_volumes(cwd=p) volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) assert np.isclose(volume_before, volume_after) From 3078dc4256ac0cf77a94ac8cb8c98104b4d85c7e Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 11 Nov 2024 17:04:49 +0100 Subject: [PATCH 073/122] update --- src/dagmc.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 39449bb382d..1f73287ccce 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -230,8 +230,6 @@ void DAGUniverse::init_geometry() if (mat_str == "graveyard") { graveyard = vol_handle; } - std::cout << "id_" << std::to_string(c->id_) << " " << c->n_instances_ - << std::endl; // material void checks if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); From 8f7d9afffa6ab8d40927c3567bb831dbff7e1922 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 11 Nov 2024 17:06:13 +0100 Subject: [PATCH 074/122] typo --- tests/unit_tests/dagmc/test_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 48c2f2cfdfb..b50bd5b540b 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -129,7 +129,7 @@ def test_model_differentiate_with_DAGMC(): mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) model.settings.volume_calculations = [mat_vol, cell_vol] - model.init_lib()i # need to reinitialize the lib after differentiating the materials + model.init_lib() # need to reinitialize the lib after differentiating the materials model.calculate_volumes(cwd=p) volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) assert np.isclose(volume_before, volume_after) From 8579ae19e709eb056c344506f5621890355d6518 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 11 Nov 2024 19:51:39 +0100 Subject: [PATCH 075/122] forgot the return... --- tests/unit_tests/dagmc/test_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index b50bd5b540b..57c07699b9e 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -86,6 +86,7 @@ def pattern(center, bc): model.materials = openmc.Materials(mats.values()) model.geometry = openmc.Geometry(root=root) model.settings = settings + return model def test_model_differentiate_depletable_with_DAGMC(): From 5fdc49a005506efafd1c57632df49014e1f67565 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Thu, 5 Dec 2024 06:28:08 +0100 Subject: [PATCH 076/122] Apply suggestions from code review Co-authored-by: Patrick Shriwise --- openmc/model/model.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 188be487598..1fc32b42172 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -325,7 +325,7 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, # communicator openmc.lib.init(args=args, intracomm=intracomm, output=output) - def sync_dagmc_universe(self): + def sync_dagmc_universes(self): """ Synchronize all DAGMC universes in the current geometry. This method iterates over all DAGMC universes in the geometry and @@ -1189,7 +1189,7 @@ def differentiate_depletable_mats(self, diff_volume_method : str = None): ---------- diff_volume_method : str Specifies how the volumes of the new materials should be found. - Default is to 'None', do not apply volume to the new materials, + Default is 'None', do not apply volume to the new materials, 'divide equally' which divides the original material volume equally between the new materials, 'match cell' sets the volume of the material to volume of the cell @@ -1206,11 +1206,9 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo ---------- diff_volume_method : str Specifies how the volumes of the new materials should be found. - Default is to 'None', do not apply volume to the new materials, - 'divide equally' which divides the original material - volume equally between the new materials, - 'match cell' sets the volume of the material to volume of the cell - they fill. + - 'None': Do not assign volumes to the new materials (Default) + - 'divide_equally': Divide the original material volume equally between the new materials + - 'match cell': Set the volume of the material to the volume of the cell they fill depletable_only : bool Default is True, only depletable materials will be differentiated all materials will be differentiated otherwise. @@ -1229,7 +1227,7 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo for mat in distribmats: if mat.volume is None: raise RuntimeError( - "Volume not specified for depletable " + "Volume not specified for " f"material with ID={mat.id}." ) mat.volume /= mat.num_instances From 58ce43be9afc71840f26dc3df4df169fa390af43 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 5 Dec 2024 13:59:45 +0100 Subject: [PATCH 077/122] new round of edits --- docs/source/io_formats/geometry.rst | 7 ++++ include/openmc/dagmc.h | 10 +++--- openmc/dagmc.py | 27 +++++++-------- openmc/geometry.py | 2 +- openmc/model/model.py | 49 ++++++++++++++++------------ src/dagmc.cpp | 37 ++++++++++++--------- tests/unit_tests/dagmc/test_model.py | 7 ++-- 7 files changed, 82 insertions(+), 57 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index ac48e48d2d1..09f0c35c868 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -407,6 +407,13 @@ Each ```` element can have the following attributes or sub-eleme *Default*: None + :material_overrides: + Dictionnary of material overrides to be applied to the DAGMC universe. The keys are + the material name or the Cell ID in the DAGMC Geometry and the values are the + corresponding material IDs or name in the OpenMC model. + + *Default*: None + .. note:: A geometry.xml file containing only a DAGMC model for a file named `dagmc.h5m` (no CSG) looks as follows diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 3a636454fb5..7b251e4ac4a 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -191,11 +191,11 @@ class DAGUniverse : public Universe { bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume std::map> - instance_material_overrides; ///!< Map of material overrides - ///!< keys correspond to the material name - ///!< or id - ///!< values are a list of materials used - ///!< git for the override + material_overrides; ///!< Map of material overrides + ///!< keys correspond to the material name + ///!< or id + ///!< values are a list of materials used + ///!< git for the override }; //============================================================================== diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 18d6500b5b5..71e53c9903e 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -143,8 +143,8 @@ def material_overrides(self, val): if key not in self.material_names: raise ValueError( f"Material name '{key}' not found in DAGMC file") - # ensuring overrides is an iterable of material name (strings) - cv.check_iterable_type('material objects', value, str) + # ensuring overrides is an iterable of openmc.Material + cv.check_iterable_type('material objects', value, openmc.Material) self._material_overrides = val @@ -157,8 +157,8 @@ def add_material_override(self, mat_name=None, cell_id=None, overrides=None): ---------- key : str Material name to override - value : Iterable of str - Material names to replace the key with + value : Iterable of Materials + Materials to replace the key with """ key = "" @@ -170,7 +170,7 @@ def add_material_override(self, mat_name=None, cell_id=None, overrides=None): raise ValueError( f"Cell ID '{cell_id}' not found in DAGMC universe") else: - key = str(cell_id) + key = self.cells[cell_id] elif mat_name: cv.check_type('material name', mat_name, str) if mat_name not in self.material_names: @@ -181,7 +181,7 @@ def add_material_override(self, mat_name=None, cell_id=None, overrides=None): else: raise ValueError("Either 'mat_name' or 'cell_id' must be set") - cv.check_iterable_type('material objects', overrides, str) + cv.check_iterable_type('material objects', overrides, openmc.Materials) self.material_overrides[mat_name] = overrides @property @@ -288,9 +288,8 @@ def create_xml_subelement(self, xml_element, memo=None): if self.material_overrides: mat_element = ET.Element('material_overrides') for key in self.material_overrides: - print(key, self.material_overrides[key]) - mat_element.set('id_{}'.format(key), ';'.join( - t for t in self.material_overrides[key])) + mat_element.set('id_{}'.format(key.id), ';'.join( + t.name for t in self.material_overrides[key])) dagmc_element.append(mat_element) xml_element.append(dagmc_element) @@ -304,7 +303,7 @@ def build_overide_mat_from_cells(self): self.material_overrides = {} for cell in self.cells.values(): if isinstance(cell.fill, Iterable): - self.material_overrides[str(cell.id)] = [mat.name for mat in cell.fill if mat] + self.material_overrides[cell] = [mat for mat in cell.fill if mat] def bounding_region( self, @@ -428,7 +427,7 @@ def from_hdf5(cls, group): return out @classmethod - def from_xml_element(cls, elem): + def from_xml_element(cls, elem, mats): """Generate DAGMC universe from XML element Parameters @@ -457,8 +456,10 @@ def from_xml_element(cls, elem): for item in elem.find('material_overrides').attrib: origin_mat, overwrite = item for mat_name in overwrite.split(): - out.material_overrides.setdefault( - origin_mat.lower(), []).append(mat_name) + for mat in mats: + if mat_name == mat.name: + out.material_overrides.setdefault( + origin_mat.lower(), []).append(mat) return out diff --git a/openmc/geometry.py b/openmc/geometry.py index ef104b13123..cef8e3ccc61 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -217,7 +217,7 @@ def get_universe(univ_id): # Add any DAGMC universes for e in elem.findall('dagmc_universe'): - dag_univ = openmc.DAGMCUniverse.from_xml_element(e) + dag_univ = openmc.DAGMCUniverse.from_xml_element(e, mats) universes[dag_univ.id] = dag_univ # Dictionary that maps each universe to a list of cells/lattices that diff --git a/openmc/model/model.py b/openmc/model/model.py index 1fc32b42172..26af6eac435 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1218,19 +1218,34 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) - # Extract all or depletable_only materials which have multiple instance - distribmats = set( - [mat for mat in self.materials - if (mat.depletable or not depletable_only) and mat.num_instances > 1]) - - if diff_volume_method == "divide equally": - for mat in distribmats: - if mat.volume is None: - raise RuntimeError( - "Volume not specified for " - f"material with ID={mat.id}." - ) - mat.volume /= mat.num_instances + # Find all or depletable_only materials which have multiple instance + distribmats = set() + for mat in self.materials: + # Differentiate all materials with multiple instances + diff_mat = mat.num_instances > 1 + # If depletable_only is True, differentiate only depletable materials + if depletable_only: + diff_mat = diff_mat and mat.depletable + if diff_mat: + # Assign volumes to the materials according to requirements + if diff_volume_method == "divide equally": + if mat.volume is None: + raise RuntimeError( + "Volume not specified for " + f"material with ID={mat.id}.") + else: + mat.volume /= mat.num_instances + elif diff_volume_method == "match cell": + for cell in self.geometry.get_all_material_cells().values(): + if cell.fill == mat: + if not cell.volume: + raise ValueError( + f"Volume of cell ID={cell.id} not specified. " + "Set volumes of cells prior to using " + "diff_volume_method='match cell'.") + mat.volume = cell.volume + break + distribmats.add(mat) if distribmats: # Assign distribmats to cells @@ -1241,14 +1256,6 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo cell.fill = [mat.clone() for _ in range(cell.num_instances)] elif diff_volume_method == 'match cell': cell.fill = mat.clone() - for i in range(cell.num_instances): - if not cell.volume: - raise ValueError( - f"Volume of cell ID={cell.id} not specified. " - "Set volumes of cells prior to using " - "diff_volume_method='match cell'." - ) - cell.fill.volume = cell.volume if isinstance(cell, openmc.DAGMCCell): for i in range(cell.num_instances): cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 1f73287ccce..1b0d592c5e3 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -86,7 +86,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) vector instance_mats = split(iss.str(), ';'); // Store mat name for each instances - instance_material_overrides.insert( + material_overrides.insert( std::make_pair(mat_ref_assignment, instance_mats)); } } @@ -234,9 +234,8 @@ void DAGUniverse::init_geometry() if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { - if (instance_material_overrides.count("id_" + std::to_string(c->id_)) || - instance_material_overrides.count( - mat_str)) { // Check for material override + if (material_overrides.count("id_" + std::to_string(c->id_)) || + material_overrides.count(mat_str)) { // Check for material override override_assign_material(mat_str, vol_handle, c); } else if (uses_uwuw()) { // UWUW assignement uwuw_assign_material(vol_handle, c); @@ -639,10 +638,11 @@ void DAGUniverse::override_assign_material(std::string key, // if Cell ID matches an override key, use it to override the material // assignment else if UWUW is used, get the material assignment from the DAGMC // metadata - std::cout << "XML instance" - << instance_material_overrides.count("id_" + std::to_string(c->id_)) - << std::endl; - if (instance_material_overrides.count("id_" + std::to_string(c->id_))) { + std::stringstream msg; + msg << "XML instance" + << material_overrides.count("id_" + std::to_string(c->id_)); + write_message(msg.str(), 5); + if (material_overrides.count("id_" + std::to_string(c->id_))) { key = "id_" + std::to_string(c->id_); } else if (uses_uwuw()) { key = dmd_ptr->volume_material_property_data_eh[vol_handle]; @@ -650,7 +650,7 @@ void DAGUniverse::override_assign_material(std::string key, // Override the material assignment for each cell instance using the legacy // assignement - for (auto mat_str_instance : instance_material_overrides.at(key)) { + for (auto mat_str_instance : material_overrides.at(key)) { legacy_assign_material(mat_str_instance, c); } } @@ -870,15 +870,19 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( // make sure the universe id is a DAGMC Universe const auto& univ = model::universes[model::universe_map[univ_id]]; if (univ->geom_type() != GeometryType::DAG) { - fatal_error( + set_errmsg( "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); + return OPENMC_E_INVALID_TYPE; } std::vector dag_cell_ids; for (const auto& cell_index : univ->cells_) { const auto& cell = model::cells[cell_index]; - if (cell->geom_type() == GeometryType::DAG) - dag_cell_ids.push_back(cell->id_); + if (cell->geom_type() == GeometryType::CSG) { + set_errmsg("Cell " + std::to_string(cell->id_) + " is not a DAGMC Cell!"); + return OPENMC_E_INVALID_TYPE; + } + dag_cell_ids.push_back(cell->id_); } std::copy(dag_cell_ids.begin(), dag_cell_ids.end(), ids); *n = dag_cell_ids.size(); @@ -890,8 +894,9 @@ extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) // make sure the universe id is a DAGMC Universe const auto& univ = model::universes[model::universe_map[univ_id]]; if (univ->geom_type() != GeometryType::DAG) { - fatal_error( + set_errmsg( "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe"); + return OPENMC_E_INVALID_TYPE; } *n = univ->cells_.size(); return 0; @@ -906,12 +911,14 @@ namespace openmc { extern "C" int openmc_dagmc_universe_get_cell_ids( int32_t univ_id, int32_t* ids, size_t* n) { - fatal_error("OpenMC was not configured with DAGMC"); + set_errmsg("OpenMC was not configured with DAGMC"); + return OPENMC_E_UNASSIGNED; }; extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) { - fatal_error("OpenMC was not configured with DAGMC"); + set_errmsg("OpenMC was not configured with DAGMC"); + return OPENMC_E_UNASSIGNED; }; void read_dagmc_universes(pugi::xml_node node) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 57c07699b9e..4268d8f309b 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -95,7 +95,7 @@ def test_model_differentiate_depletable_with_DAGMC(): p = Path("differentiate_depletable_mats/divide_equally") p.mkdir(parents=True, exist_ok=True) model.init_lib() - model.sync_dagmc_universe() + model.sync_dagmc_universes() model.calculate_volumes(cwd=p) # Get the volume of the no-void fuel material before differentiation @@ -114,10 +114,13 @@ def test_model_differentiate_depletable_with_DAGMC(): def test_model_differentiate_with_DAGMC(): model = set_dagmc_model() + root = model.geometry.root_universe + ll, ur = root.bounding_box + p = Path("differentiate_depletable_mats/divide_equally") p.mkdir(parents=True, exist_ok=True) model.init_lib() - model.sync_dagmc_universe() + model.sync_dagmc_universes() model.calculate_volumes(cwd=p) # Get the volume of the no-void fuel material before differentiation volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) From d6f09136ec30a11668dba28b2884e5a1c2757f99 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 5 Dec 2024 15:44:32 +0100 Subject: [PATCH 078/122] fixing not working tests --- openmc/model/model.py | 3 +-- tests/unit_tests/dagmc/test_model.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 4ab5c508995..3f27847478c 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1243,8 +1243,6 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo f"Volume of cell ID={cell.id} not specified. " "Set volumes of cells prior to using " "diff_volume_method='match cell'.") - mat.volume = cell.volume - break distribmats.add(mat) if distribmats: @@ -1256,6 +1254,7 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo cell.fill = [mat.clone() for _ in range(cell.num_instances)] elif diff_volume_method == 'match cell': cell.fill = mat.clone() + cell.fill.volume = cell.volume if isinstance(cell, openmc.DAGMCCell): for i in range(cell.num_instances): cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 4268d8f309b..7e1fd7d49e4 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -111,8 +111,7 @@ def test_model_differentiate_depletable_with_DAGMC(): model.finalize_lib() -def test_model_differentiate_with_DAGMC(): - +def test_model_differentiate_with_DAGMC(): model = set_dagmc_model() root = model.geometry.root_universe ll, ur = root.bounding_box From 9f458c413c46ba4a0f19d218125878378de73bc5 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:53:35 +0100 Subject: [PATCH 079/122] Update src/dagmc.cpp Co-authored-by: Patrick Shriwise --- src/dagmc.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 1b0d592c5e3..f5614f3f2cf 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -638,10 +638,7 @@ void DAGUniverse::override_assign_material(std::string key, // if Cell ID matches an override key, use it to override the material // assignment else if UWUW is used, get the material assignment from the DAGMC // metadata - std::stringstream msg; - msg << "XML instance" - << material_overrides.count("id_" + std::to_string(c->id_)); - write_message(msg.str(), 5); + write_message(fmt::format("XML instance {}", c->id_), 8); if (material_overrides.count("id_" + std::to_string(c->id_))) { key = "id_" + std::to_string(c->id_); } else if (uses_uwuw()) { From 387f648c8449f1cf8520e78d12c9653bfe53a387 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:01:29 +0100 Subject: [PATCH 080/122] Update tests/unit_tests/dagmc/test_model.py Co-authored-by: Patrick Shriwise --- tests/unit_tests/dagmc/test_model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 7e1fd7d49e4..5fd655e1265 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -71,7 +71,6 @@ def pattern(center, bc): source = openmc.IndependentSource(space=point) settings = openmc.Settings() - settings.source = source settings.batches = 100 settings.inactive = 10 settings.particles = 1000 From e63f2856e68422621cfc12855f6a35562d72d2c4 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Fri, 6 Dec 2024 17:25:46 +0100 Subject: [PATCH 081/122] addressing comment, improving tests --- src/dagmc.cpp | 3 +- tests/unit_tests/dagmc/test_model.py | 46 +++++++--------------------- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index f5614f3f2cf..c0ab9988e8c 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -638,7 +638,8 @@ void DAGUniverse::override_assign_material(std::string key, // if Cell ID matches an override key, use it to override the material // assignment else if UWUW is used, get the material assignment from the DAGMC // metadata - write_message(fmt::format("XML instance {}", c->id_), 8); + // Notify User that an override is being applied on a DAGMCCell + write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8); if (material_overrides.count("id_" + std::to_string(c->id_))) { key = "id_" + std::to_string(c->id_); } else if (uses_uwuw()) { diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 5fd655e1265..5824f2d6c2c 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -34,41 +34,17 @@ def set_dagmc_model(): daguniv = openmc.DAGMCUniverse(p,auto_geom_ids=True,) - def pattern(center, bc): - bc_ = { - "top": "transmission", - "bottom": "transmission", - "left": "transmission", - "right": "transmission", - } - bc_ |= bc - box = ( - -XPlane(center[0] + PITCH / 2, boundary_type=bc_["right"]) - & +XPlane(center[0] - PITCH / 2, boundary_type=bc_["left"]) - & -YPlane(center[1] + PITCH / 2, boundary_type=bc_["top"]) - & +YPlane(center[1] - PITCH / 2, boundary_type=bc_["bottom"]) - & -ZPlane(5, boundary_type="reflective") - & +ZPlane(-5, boundary_type="reflective") - ) - cell = Cell(region=box, fill=daguniv) - cell.translation = [*center, 0] - return [cell] - - root = openmc.Universe( - cells=[ - *pattern((-PITCH / 2, -PITCH / 2), - bc={"left": "reflective", "bottom": "reflective"}), - *pattern((-PITCH / 2, PITCH / 2), - bc={"left": "reflective", "top": "reflective"}), - *pattern((PITCH / 2, PITCH / 2), - bc={"right": "reflective", "top": "reflective"}), - *pattern((PITCH / 2, -PITCH / 2), - bc={"right": "reflective", "bottom": "reflective"}), - ] - ) - - point = openmc.stats.Point((0, 0, 0)) - source = openmc.IndependentSource(space=point) + lattice = openmc.RectLattice() + lattice.dimension = [2, 2] + lattice.lower_left = [-PITCH, -PITCH] + lattice.pitch = [PITCH, PITCH] + lattice.universes = [ + [daguniv, daguniv], + [daguniv, daguniv]] + + box = openmc.model.RectangularParallelepiped(-PITCH, PITCH, -PITCH, PITCH, -5, 5) + + root = openmc.Universe(cells=[Cell(region= -box, fill=lattice)]) settings = openmc.Settings() settings.batches = 100 From c4f7259ae506acf32e79705b545d2dc9c30574bd Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Dec 2024 14:30:13 +0100 Subject: [PATCH 082/122] ongoing --- include/openmc/dagmc.h | 9 ++---- openmc/dagmc.py | 62 +++++++++++++++++++++++------------------- src/dagmc.cpp | 34 +++++++++++++++-------- 3 files changed, 59 insertions(+), 46 deletions(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 7b251e4ac4a..0b9c74aeeaf 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -134,10 +134,8 @@ class DAGUniverse : public Universe { std::string mat_string, std::unique_ptr& c) const; //! Assign a material overriding normal assignement to a cell - //! \param[in] key The material key to override //! \param[in] c The OpenMC cell to which the material is assigned - void override_assign_material(std::string key, moab::EntityHandle vol_handle, - std::unique_ptr& c) const; + void override_assign_material(std::unique_ptr& c) const; //! Return the index into the model cells vector for a given DAGMC volume //! handle in the universe @@ -190,10 +188,9 @@ class DAGUniverse : public Universe { //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume - std::map> + std::map> material_overrides; ///!< Map of material overrides - ///!< keys correspond to the material name - ///!< or id + ///!< keys correspond to the DAGMCCell id ///!< values are a list of materials used ///!< git for the override }; diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 71e53c9903e..3b13da25a81 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -100,7 +100,7 @@ def __init__(self, self.filename = filename self.auto_geom_ids = auto_geom_ids self.auto_mat_ids = auto_mat_ids - self.material_overrides = mat_overrides + self._material_overrides = mat_overrides self._dagmc_cells = [] def __repr__(self): @@ -145,10 +145,9 @@ def material_overrides(self, val): f"Material name '{key}' not found in DAGMC file") # ensuring overrides is an iterable of openmc.Material cv.check_iterable_type('material objects', value, openmc.Material) + self.add_material_override(key, value) - self._material_overrides = val - - def add_material_override(self, mat_name=None, cell_id=None, overrides=None): + def add_material_override(self, key, overrides=None): """Add a material override to the universe. .. versionadded:: 0.15 @@ -161,28 +160,35 @@ def add_material_override(self, mat_name=None, cell_id=None, overrides=None): Materials to replace the key with """ - key = "" - if mat_name and cell_id: - raise ValueError("Only one of 'mat_name' or 'cell_id' can be set") - elif cell_id: - cv.check_type('cell id', cell_id, int) - if cell_id not in self.cells: + keys = [] + if isinstance(key, str): + if key not in self.material_names: raise ValueError( - f"Cell ID '{cell_id}' not found in DAGMC universe") + f"Material name '{key}' not found in DAGMC file") + elif int(key) in self.cells: + keys = [self.cells[int(key)]] else: - key = self.cells[cell_id] - elif mat_name: - cv.check_type('material name', mat_name, str) - if mat_name not in self.material_names: + keys = [] + for cell in self.cells.values(): + if cell.fill.name == KeyError: + keys.append(self.cells[cell.id]) + elif isinstance(key, int()): + if key not in self.cells: raise ValueError( - f"Material name '{mat_name}' not found in DAGMC file") + f"Cell ID '{key}' not found in DAGMC universe") else: - key = mat_name + keys = [self.cells[key]] + else: - raise ValueError("Either 'mat_name' or 'cell_id' must be set") - + raise ValueError("Unrecognized key type. Must be a string or integer.") + + # Ensure that overrides is an iterable of openmc.Material + if not isinstance(overrides, Iterable): + overrides = [overrides] cv.check_iterable_type('material objects', overrides, openmc.Materials) - self.material_overrides[mat_name] = overrides + + for key in key: + self._material_overrides[key] = overrides @property def auto_geom_ids(self): @@ -285,11 +291,11 @@ def create_xml_subelement(self, xml_element, memo=None): if self.auto_mat_ids: dagmc_element.set('auto_mat_ids', 'true') dagmc_element.set('filename', str(self.filename)) - if self.material_overrides: + if self._material_overrides: mat_element = ET.Element('material_overrides') - for key in self.material_overrides: - mat_element.set('id_{}'.format(key.id), ';'.join( - t.name for t in self.material_overrides[key])) + for key in self._material_overrides: + mat_element.set('cell_{}'.format(key.id), ';'.join( + t.name for t in self._material_overrides[key])) dagmc_element.append(mat_element) xml_element.append(dagmc_element) @@ -300,10 +306,10 @@ def build_overide_mat_from_cells(self): Returns: None """ - self.material_overrides = {} + self._material_overrides = {} for cell in self.cells.values(): if isinstance(cell.fill, Iterable): - self.material_overrides[cell] = [mat for mat in cell.fill if mat] + self._material_overrides[cell] = [mat for mat in cell.fill if mat] def bounding_region( self, @@ -454,12 +460,12 @@ def from_xml_element(cls, elem, mats): out.auto_mat_ids = bool(elem.get('auto_mat_ids')) for item in elem.find('material_overrides').attrib: - origin_mat, overwrite = item + cell_id, overwrite = item for mat_name in overwrite.split(): for mat in mats: if mat_name == mat.name: out.material_overrides.setdefault( - origin_mat.lower(), []).append(mat) + cell_id.lower(), []).append(mat) return out diff --git a/src/dagmc.cpp b/src/dagmc.cpp index c0ab9988e8c..f4cf2033de6 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -79,7 +79,18 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) for (pugi::xml_attribute attr = mat_node.first_attribute(); attr; attr = attr.next_attribute()) { // Store assignment reference name - std::string mat_ref_assignment = attr.name(); + std::string ref_assignment = attr.name(); + // Check formating of the key name (should be cell_XX, with XX matching a + // DAGMC Cell_ID) + size_t cell_str_pos = ref_assignment.find("cell_"); + if (cell_str_pos != std::string::npos) { + ref_assignment = ref_assignment.substr(5); + } else { + fatal_error(fmt::format( + "Material override key name {} is not in the correct format. " + "It should be cell_XX, with XX matching a DAGMC Cell_ID", + ref_assignment)); + } // Get mat name for each assignement instances std::stringstream iss {attr.value()}; @@ -87,7 +98,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) // Store mat name for each instances material_overrides.insert( - std::make_pair(mat_ref_assignment, instance_mats)); + std::make_pair(std::stoi(ref_assignment), instance_mats)); } } @@ -234,9 +245,8 @@ void DAGUniverse::init_geometry() if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { - if (material_overrides.count("id_" + std::to_string(c->id_)) || - material_overrides.count(mat_str)) { // Check for material override - override_assign_material(mat_str, vol_handle, c); + if (material_overrides.count(c->id_)) { // Check for material override + override_assign_material(c); } else if (uses_uwuw()) { // UWUW assignement uwuw_assign_material(vol_handle, c); } else { // legacy assignement @@ -631,8 +641,7 @@ void DAGUniverse::uwuw_assign_material( #endif // OPENMC_UWUW } -void DAGUniverse::override_assign_material(std::string key, - moab::EntityHandle vol_handle, std::unique_ptr& c) const +void DAGUniverse::override_assign_material(std::unique_ptr& c) const { // if Cell ID matches an override key, use it to override the material @@ -640,15 +649,16 @@ void DAGUniverse::override_assign_material(std::string key, // metadata // Notify User that an override is being applied on a DAGMCCell write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8); - if (material_overrides.count("id_" + std::to_string(c->id_))) { - key = "id_" + std::to_string(c->id_); - } else if (uses_uwuw()) { - key = dmd_ptr->volume_material_property_data_eh[vol_handle]; + + if (c->n_instances_ != material_overrides.at(c->id_).size()) { + fatal_error(fmt::format("Material override key {} has a different number " + "of instances than the cell has instances", + c->id_)); } // Override the material assignment for each cell instance using the legacy // assignement - for (auto mat_str_instance : material_overrides.at(key)) { + for (auto mat_str_instance : material_overrides.at(c->id_)) { legacy_assign_material(mat_str_instance, c); } } From 84898c52d05b80c714b775c0b094638d5efba5e7 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Dec 2024 20:36:09 +0100 Subject: [PATCH 083/122] can't check n_instance match number of override as n_instance is counted later --- src/dagmc.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index f4cf2033de6..be09c9e1367 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -650,12 +650,6 @@ void DAGUniverse::override_assign_material(std::unique_ptr& c) const // Notify User that an override is being applied on a DAGMCCell write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8); - if (c->n_instances_ != material_overrides.at(c->id_).size()) { - fatal_error(fmt::format("Material override key {} has a different number " - "of instances than the cell has instances", - c->id_)); - } - // Override the material assignment for each cell instance using the legacy // assignement for (auto mat_str_instance : material_overrides.at(c->id_)) { From d6f9af8fb26bb1467260dc7a47c8d946ebd146ea Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Dec 2024 21:12:14 +0100 Subject: [PATCH 084/122] factorizing adding override --- openmc/dagmc.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 3b13da25a81..47581c3da24 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -100,7 +100,7 @@ def __init__(self, self.filename = filename self.auto_geom_ids = auto_geom_ids self.auto_mat_ids = auto_mat_ids - self._material_overrides = mat_overrides + self.material_overrides = mat_overrides self._dagmc_cells = [] def __repr__(self): @@ -138,13 +138,6 @@ def material_overrides(self, val): else: cv.check_type('material overrides', val, dict) for key, value in val.items(): - # ensuring key is a string and exists in the DAGMC file - cv.check_type('material name', key, str) - if key not in self.material_names: - raise ValueError( - f"Material name '{key}' not found in DAGMC file") - # ensuring overrides is an iterable of openmc.Material - cv.check_iterable_type('material objects', value, openmc.Material) self.add_material_override(key, value) def add_material_override(self, key, overrides=None): @@ -155,7 +148,7 @@ def add_material_override(self, key, overrides=None): Parameters ---------- key : str - Material name to override + Material name or ID of the Cell to override value : Iterable of Materials Materials to replace the key with @@ -172,23 +165,28 @@ def add_material_override(self, key, overrides=None): for cell in self.cells.values(): if cell.fill.name == KeyError: keys.append(self.cells[cell.id]) - elif isinstance(key, int()): + elif isinstance(key, int): if key not in self.cells: raise ValueError( f"Cell ID '{key}' not found in DAGMC universe") else: keys = [self.cells[key]] - + elif isinstance(key, openmc.Cell): + keys = [key] else: + print("Key is a ", type(key)) raise ValueError("Unrecognized key type. Must be a string or integer.") # Ensure that overrides is an iterable of openmc.Material if not isinstance(overrides, Iterable): overrides = [overrides] - cv.check_iterable_type('material objects', overrides, openmc.Materials) + cv.check_iterable_type('material objects', overrides, openmc.Material) - for key in key: - self._material_overrides[key] = overrides + # if material_overrides is not initialized, initialize it + if not self._material_overrides: + self._material_overrides = {} + for item in keys: + self._material_overrides[item] = overrides @property def auto_geom_ids(self): @@ -306,10 +304,9 @@ def build_overide_mat_from_cells(self): Returns: None """ - self._material_overrides = {} for cell in self.cells.values(): if isinstance(cell.fill, Iterable): - self._material_overrides[cell] = [mat for mat in cell.fill if mat] + self.add_material_override(cell, [mat for mat in cell.fill if mat]) def bounding_region( self, From da8cf37955054f45db451e7234cfc8e1516bd7f8 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Dec 2024 21:24:56 +0100 Subject: [PATCH 085/122] add a missing error --- openmc/dagmc.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 47581c3da24..97f1cb218c3 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -161,10 +161,8 @@ def add_material_override(self, key, overrides=None): elif int(key) in self.cells: keys = [self.cells[int(key)]] else: - keys = [] - for cell in self.cells.values(): - if cell.fill.name == KeyError: - keys.append(self.cells[cell.id]) + raise ValueError( + f"Material or Cell ID '{key}' not found in DAGMC universe") elif isinstance(key, int): if key not in self.cells: raise ValueError( From f71acc442abc9f32483dc7aa136daa9fadc4ddd7 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Dec 2024 21:26:16 +0100 Subject: [PATCH 086/122] add a missing error --- openmc/dagmc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 97f1cb218c3..dca1cfe9227 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -170,7 +170,11 @@ def add_material_override(self, key, overrides=None): else: keys = [self.cells[key]] elif isinstance(key, openmc.Cell): - keys = [key] + if key not in self.cells.values(): + raise ValueError( + f"Cell '{key.id}' not found in DAGMC universe") + else: + keys = [key] else: print("Key is a ", type(key)) raise ValueError("Unrecognized key type. Must be a string or integer.") From 1b9b58e0d298f01d17cf9cb9a8416100fd22f444 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Dec 2024 21:40:23 +0100 Subject: [PATCH 087/122] update from_xml for dagmc --- openmc/dagmc.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index dca1cfe9227..56172ca3726 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -459,13 +459,11 @@ def from_xml_element(cls, elem, mats): out.auto_mat_ids = bool(elem.get('auto_mat_ids')) for item in elem.find('material_overrides').attrib: - cell_id, overwrite = item - for mat_name in overwrite.split(): - for mat in mats: - if mat_name == mat.name: - out.material_overrides.setdefault( - cell_id.lower(), []).append(mat) - + cell_id = int(item.split('_')[1]) + mat_ids = elem.find('material_overrides').attrib[item].split(';') + mat_objs = [mats[mat_id] for mat_id in mat_ids] + out.add_material_override(cell_id, mat_objs) + return out def _partial_deepcopy(self): From f1e9935c6dbf4c1b41fe6337098350ec99da38a8 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Thu, 12 Dec 2024 21:49:54 +0100 Subject: [PATCH 088/122] update docs --- docs/source/io_formats/geometry.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index 09f0c35c868..078a0a482f7 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -409,8 +409,11 @@ Each ```` element can have the following attributes or sub-eleme :material_overrides: Dictionnary of material overrides to be applied to the DAGMC universe. The keys are - the material name or the Cell ID in the DAGMC Geometry and the values are the - corresponding material IDs or name in the OpenMC model. + the the Cell ID in the DAGMC Geometry and the values are a list of the material IDs + or name. If the list contains only one materials if will replace the original + material assignemnt of the DAGMC cell. If the list contains more than one material + the materials, each material of the list will be assigned to the different instances + of the DAGMC cell. *Default*: None From 287201abfac24eaa54c0dd79b9d8fe0a001041cd Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Mon, 16 Dec 2024 16:35:15 +0100 Subject: [PATCH 089/122] using material ID for the xml istead of the name per default --- openmc/dagmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 56172ca3726..4d77a0b9474 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -295,7 +295,7 @@ def create_xml_subelement(self, xml_element, memo=None): mat_element = ET.Element('material_overrides') for key in self._material_overrides: mat_element.set('cell_{}'.format(key.id), ';'.join( - t.name for t in self._material_overrides[key])) + str(t.id) for t in self._material_overrides[key])) dagmc_element.append(mat_element) xml_element.append(dagmc_element) From d88a4bbd7ff9b8a14a656cc138faafe2e355d4f6 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 17 Dec 2024 11:54:38 +0100 Subject: [PATCH 090/122] changing xml layout --- openmc/dagmc.py | 5 ++++- src/dagmc.cpp | 20 ++++---------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 4d77a0b9474..589aa6c7952 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -294,8 +294,11 @@ def create_xml_subelement(self, xml_element, memo=None): if self._material_overrides: mat_element = ET.Element('material_overrides') for key in self._material_overrides: - mat_element.set('cell_{}'.format(key.id), ';'.join( + cell_overrides = ET.Element('cell') + cell_overrides.set("id", str(key.id)) + cell_overrides.set("material", ';'.join( str(t.id) for t in self._material_overrides[key])) + mat_element.append(cell_overrides) dagmc_element.append(mat_element) xml_element.append(dagmc_element) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index be09c9e1367..d7898544f8a 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -76,25 +76,13 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) if (check_for_node(node, "material_overrides")) { auto mat_node = node.child("material_overrides"); // loop over all attributes (each attribute corresponds to a material) - for (pugi::xml_attribute attr = mat_node.first_attribute(); attr; - attr = attr.next_attribute()) { + for (pugi::xml_node cell_node : mat_node.children("cell")) { // Store assignment reference name - std::string ref_assignment = attr.name(); - // Check formating of the key name (should be cell_XX, with XX matching a - // DAGMC Cell_ID) - size_t cell_str_pos = ref_assignment.find("cell_"); - if (cell_str_pos != std::string::npos) { - ref_assignment = ref_assignment.substr(5); - } else { - fatal_error(fmt::format( - "Material override key name {} is not in the correct format. " - "It should be cell_XX, with XX matching a DAGMC Cell_ID", - ref_assignment)); - } + std::string ref_assignment = get_node_value(cell_node, "id"); // Get mat name for each assignement instances - std::stringstream iss {attr.value()}; - vector instance_mats = split(iss.str(), ';'); + std::string mat_overrides = get_node_value(cell_node, "material"); + vector instance_mats = split(mat_overrides, ';'); // Store mat name for each instances material_overrides.insert( From bec954e9a274aee24d5b7363b9b6d823ddc7e4dd Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 17 Dec 2024 19:58:23 +0100 Subject: [PATCH 091/122] removing ; separator, changing docs accordingly --- docs/source/io_formats/geometry.rst | 10 +++++----- include/openmc/dagmc.h | 4 ++-- openmc/dagmc.py | 4 ++-- src/dagmc.cpp | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index 078a0a482f7..07b4627c1b4 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -409,11 +409,11 @@ Each ```` element can have the following attributes or sub-eleme :material_overrides: Dictionnary of material overrides to be applied to the DAGMC universe. The keys are - the the Cell ID in the DAGMC Geometry and the values are a list of the material IDs - or name. If the list contains only one materials if will replace the original - material assignemnt of the DAGMC cell. If the list contains more than one material - the materials, each material of the list will be assigned to the different instances - of the DAGMC cell. + the the Cell ID in the DAGMC Geometry and the values are a list of the material IDs. + If the list contains only one materials if will replace the original material + assignemnt of the DAGMC cell. If the list contains more than one material the + materials, each material of the list will be assigned to the different instances of + the DAGMC cell. *Default*: None diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 0b9c74aeeaf..9dbdd0ac536 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -191,8 +191,8 @@ class DAGUniverse : public Universe { std::map> material_overrides; ///!< Map of material overrides ///!< keys correspond to the DAGMCCell id - ///!< values are a list of materials used - ///!< git for the override + ///!< values are a list of material ids used + ///!< for the override }; //============================================================================== diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 589aa6c7952..69bb68ab83d 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -296,7 +296,7 @@ def create_xml_subelement(self, xml_element, memo=None): for key in self._material_overrides: cell_overrides = ET.Element('cell') cell_overrides.set("id", str(key.id)) - cell_overrides.set("material", ';'.join( + cell_overrides.set("material", ' '.join( str(t.id) for t in self._material_overrides[key])) mat_element.append(cell_overrides) dagmc_element.append(mat_element) @@ -463,7 +463,7 @@ def from_xml_element(cls, elem, mats): for item in elem.find('material_overrides').attrib: cell_id = int(item.split('_')[1]) - mat_ids = elem.find('material_overrides').attrib[item].split(';') + mat_ids = elem.find('material_overrides').attrib[item].split(' ') mat_objs = [mats[mat_id] for mat_id in mat_ids] out.add_material_override(cell_id, mat_objs) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index d7898544f8a..4af36fe36e4 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -82,7 +82,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) // Get mat name for each assignement instances std::string mat_overrides = get_node_value(cell_node, "material"); - vector instance_mats = split(mat_overrides, ';'); + vector instance_mats = split(mat_overrides, ' '); // Store mat name for each instances material_overrides.insert( From 783547df187087ba01a875cf1f48224522988da3 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:03:09 +0100 Subject: [PATCH 092/122] Apply suggestions from code review Co-authored-by: Patrick Shriwise --- docs/source/io_formats/geometry.rst | 2 +- openmc/geometry.py | 4 ++-- openmc/model/model.py | 7 ++++--- src/dagmc.cpp | 3 +-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index 07b4627c1b4..bc411a96637 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -410,7 +410,7 @@ Each ```` element can have the following attributes or sub-eleme :material_overrides: Dictionnary of material overrides to be applied to the DAGMC universe. The keys are the the Cell ID in the DAGMC Geometry and the values are a list of the material IDs. - If the list contains only one materials if will replace the original material + If the list contains only one material if will replace the original material assignemnt of the DAGMC cell. If the list contains more than one material the materials, each material of the list will be assigned to the different instances of the DAGMC cell. diff --git a/openmc/geometry.py b/openmc/geometry.py index cef8e3ccc61..43aa3ded79f 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -415,9 +415,9 @@ def get_all_dagmc_universes(self) -> typing.Dict[int, openmc.DAGMCUniverse]: """ universes = self.get_all_universes() dag_universes = {} - for id, uni in universes.items(): + for id, univ in universes.items(): if isinstance(uni, openmc.DAGMCUniverse): - dag_universes[id] = uni + dag_universes[id] = univ return dag_universes def get_all_materials(self) -> dict[int, openmc.Material]: diff --git a/openmc/model/model.py b/openmc/model/model.py index 3f27847478c..d3e9c1b98df 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -338,9 +338,10 @@ def sync_dagmc_universes(self): if self.materials: materials = self.materials else: - materials = self.geometry.get_all_materials().values() - for dagmc_universe in self.geometry.get_all_dagmc_universes().values(): - dagmc_universe.sync_dagmc_cells(materials) + materials = list(self.geometry.get_all_materials().values()) + for univ in self.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + univ.sync_dagmc_cells(materials) else: raise ValueError("The model must be initialized before calling " "this method") diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 4af36fe36e4..3aaa0bbe701 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -884,8 +884,7 @@ extern "C" int openmc_dagmc_universe_get_num_cells(int32_t univ_id, size_t* n) // make sure the universe id is a DAGMC Universe const auto& univ = model::universes[model::universe_map[univ_id]]; if (univ->geom_type() != GeometryType::DAG) { - set_errmsg( - "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe"); + set_errmsg(fmt::format("Universe {} is not a DAGMC universe", univ_id)); return OPENMC_E_INVALID_TYPE; } *n = univ->cells_.size(); From 1ff5d6102e25eb0552bfda7161ee345e96b5d15c Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:04:47 +0100 Subject: [PATCH 093/122] Apply suggestions from code review Co-authored-by: Patrick Shriwise --- openmc/dagmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 69bb68ab83d..4e8ca4d3df6 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -302,7 +302,7 @@ def create_xml_subelement(self, xml_element, memo=None): dagmc_element.append(mat_element) xml_element.append(dagmc_element) - def build_overide_mat_from_cells(self): + def build_override_mat_from_cells(self): """ Builds the material override dictionary for cells with multiple instances. From 06133ed7c2a8ee4c9d52fc83c91bf9b7aa89e510 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Fri, 20 Dec 2024 11:52:25 +0100 Subject: [PATCH 094/122] Addressing next round of comment --- include/openmc/dagmc.h | 2 +- openmc/dagmc.py | 25 +++++++++++++++---------- src/dagmc.cpp | 11 ++++++----- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 9dbdd0ac536..3988c3c2124 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -188,7 +188,7 @@ class DAGUniverse : public Universe { //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume - std::map> + std::map> material_overrides; ///!< Map of material overrides ///!< keys correspond to the DAGMCCell id ///!< values are a list of material ids used diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 4e8ca4d3df6..bbb272b7949 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -155,13 +155,14 @@ def add_material_override(self, key, overrides=None): """ keys = [] if isinstance(key, str): - if key not in self.material_names: - raise ValueError( - f"Material name '{key}' not found in DAGMC file") + if key in self.material_names: + for cell in self.cells.values(): + if cell.fill.name == key: + keys.append(cell) elif int(key) in self.cells: keys = [self.cells[int(key)]] else: - raise ValueError( + raise ValueError( f"Material or Cell ID '{key}' not found in DAGMC universe") elif isinstance(key, int): if key not in self.cells: @@ -176,7 +177,6 @@ def add_material_override(self, key, overrides=None): else: keys = [key] else: - print("Key is a ", type(key)) raise ValueError("Unrecognized key type. Must be a string or integer.") # Ensure that overrides is an iterable of openmc.Material @@ -280,7 +280,7 @@ def create_xml_subelement(self, xml_element, memo=None): memo.add(self) # Ensure that the material overrides are up-t-date - self.build_overide_mat_from_cells() + self.build_override_mat_from_cells() # Set xml element values dagmc_element = ET.Element('dagmc_universe') @@ -312,6 +312,9 @@ def build_override_mat_from_cells(self): for cell in self.cells.values(): if isinstance(cell.fill, Iterable): self.add_material_override(cell, [mat for mat in cell.fill if mat]) + else: + if cell.fill: + self.add_material_override(cell, cell.fill) def bounding_region( self, @@ -461,10 +464,12 @@ def from_xml_element(cls, elem, mats): out.auto_geom_ids = bool(elem.get('auto_geom_ids')) out.auto_mat_ids = bool(elem.get('auto_mat_ids')) - for item in elem.find('material_overrides').attrib: - cell_id = int(item.split('_')[1]) - mat_ids = elem.find('material_overrides').attrib[item].split(' ') - mat_objs = [mats[mat_id] for mat_id in mat_ids] + el_mat_override = elem.find('material_overrides') + if el_mat_override is not None: + for elem in el_mat_override.findall('cell'): + cell_id = elem.attrib('id') + mat_ids = elem.attrib["material"].split(' ') + mat_objs = [mats[mat_id] for mat_id in mat_ids] out.add_material_override(cell_id, mat_objs) return out diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 3aaa0bbe701..0c90f7a9e29 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -82,7 +82,7 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) // Get mat name for each assignement instances std::string mat_overrides = get_node_value(cell_node, "material"); - vector instance_mats = split(mat_overrides, ' '); + vector instance_mats = get_node_array(cell_node, "material"); // Store mat name for each instances material_overrides.insert( @@ -641,7 +641,7 @@ void DAGUniverse::override_assign_material(std::unique_ptr& c) const // Override the material assignment for each cell instance using the legacy // assignement for (auto mat_str_instance : material_overrides.at(c->id_)) { - legacy_assign_material(mat_str_instance, c); + legacy_assign_material(std::to_string(mat_str_instance), c); } } @@ -860,8 +860,8 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( // make sure the universe id is a DAGMC Universe const auto& univ = model::universes[model::universe_map[univ_id]]; if (univ->geom_type() != GeometryType::DAG) { - set_errmsg( - "Universe " + std::to_string(univ_id) + " is not a DAGMC Universe!"); + set_errmsg(fmt::format( + "Universe {} is not a DAGMC Universe!", std::to_string(univ_id))); return OPENMC_E_INVALID_TYPE; } @@ -869,7 +869,8 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( for (const auto& cell_index : univ->cells_) { const auto& cell = model::cells[cell_index]; if (cell->geom_type() == GeometryType::CSG) { - set_errmsg("Cell " + std::to_string(cell->id_) + " is not a DAGMC Cell!"); + set_errmsg( + fmt::format("Cell {} is not a DAGMC Cell!", std::to_string(cell->id_))); return OPENMC_E_INVALID_TYPE; } dag_cell_ids.push_back(cell->id_); From ed04232db0040048381ac758e41b4d6aae96d3d9 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 12:29:57 -0600 Subject: [PATCH 095/122] Refactor DAGMC material diferentiation tests --- tests/unit_tests/dagmc/test_model.py | 105 +++++++++++++-------------- 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 5824f2d6c2c..ef77b2b4ccd 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -11,24 +11,22 @@ not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") - -def set_dagmc_model(): +@pytest.fixture() +def model(): PITCH = 1.26 mats = {} - mats["no-void fuel"] = openmc.Material(1, "no-void fuel") + mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") mats["no-void fuel"].add_nuclide("U235", 0.03) mats["no-void fuel"].add_nuclide("U238", 0.97) mats["no-void fuel"].add_nuclide("O16", 2.0) mats["no-void fuel"].set_density("g/cm3", 10.0) - mats["no-void fuel"].name = "no-void fuel" mats["41"] = openmc.Material(name="41") mats["41"].add_nuclide("H1", 2.0) mats["41"].add_element("O", 1.0) mats["41"].set_density("g/cm3", 1.0) mats["41"].add_s_alpha_beta("c_H_in_H2O") - mats["41"].name = "41" p = pkg_resources.resource_filename(__name__, "dagmc.h5m") @@ -43,7 +41,7 @@ def set_dagmc_model(): [daguniv, daguniv]] box = openmc.model.RectangularParallelepiped(-PITCH, PITCH, -PITCH, PITCH, -5, 5) - + root = openmc.Universe(cells=[Cell(region= -box, fill=lattice)]) settings = openmc.Settings() @@ -64,53 +62,48 @@ def set_dagmc_model(): return model -def test_model_differentiate_depletable_with_DAGMC(): - model = set_dagmc_model() - - p = Path("differentiate_depletable_mats/divide_equally") - p.mkdir(parents=True, exist_ok=True) - model.init_lib() - model.sync_dagmc_universes() - model.calculate_volumes(cwd=p) - - # Get the volume of the no-void fuel material before differentiation - volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) - - # Differentiate the depletable materials - model.differentiate_depletable_mats(diff_volume_method="divide equally") - # Get the volume of the no-void fuel material after differentiation - volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) - assert np.isclose(volume_before, volume_after) - - assert len(model.materials) == 4*2 +1 - model.finalize_lib() - - -def test_model_differentiate_with_DAGMC(): - model = set_dagmc_model() - root = model.geometry.root_universe - ll, ur = root.bounding_box - - p = Path("differentiate_depletable_mats/divide_equally") - p.mkdir(parents=True, exist_ok=True) - model.init_lib() - model.sync_dagmc_universes() - model.calculate_volumes(cwd=p) - # Get the volume of the no-void fuel material before differentiation - volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) - - # Differentiate all the materials - model.differentiate_mats(depletable_only=False) - - # Get the volume of the no-void fuel material after differentiation - mat_list = [m for m in model.materials] - mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) - cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) - model.settings.volume_calculations = [mat_vol, cell_vol] - model.init_lib() # need to reinitialize the lib after differentiating the materials - model.calculate_volumes(cwd=p) - volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) - assert np.isclose(volume_before, volume_after) - - assert len(model.materials) == 4*2 + 4 - model.finalize_lib() +def test_model_differentiate_depletable_with_dagmc(model, run_in_tmpdir): + try: + model.init_lib() + model.sync_dagmc_universes() + model.calculate_volumes() + + # Get the volume of the no-void fuel material before differentiation + volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + + # Differentiate the depletable materials + model.differentiate_depletable_mats(diff_volume_method="divide equally") + # Get the volume of the no-void fuel material after differentiation + volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) + assert np.isclose(volume_before, volume_after) + assert len(model.materials) == 4*2 +1 + finally: + model.finalize_lib() + + +def test_model_differentiate_with_dagmc(model, run_in_tmpdir): + try: + root = model.geometry.root_universe + ll, ur = root.bounding_box + + model.init_lib() + model.sync_dagmc_universes() + model.calculate_volumes() + # Get the volume of the no-void fuel material before differentiation + volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + + # Differentiate all the materials + model.differentiate_mats(depletable_only=False) + + # Get the volume of the no-void fuel material after differentiation + mat_list = [m for m in model.materials] + mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) + cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) + model.settings.volume_calculations = [mat_vol, cell_vol] + model.init_lib() # need to reinitialize the lib after differentiating the materials + model.calculate_volumes() + volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) + assert np.isclose(volume_before, volume_after) + assert len(model.materials) == 4*2 + 4 + finally: + model.finalize_lib() From ee1bec86aa7a14a697f84458dd32db8d26a58472 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Dec 2024 13:35:16 -0600 Subject: [PATCH 096/122] Update IO format docs for material_overrides --- docs/source/io_formats/geometry.rst | 38 ++++++++++++++++++----------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index bc411a96637..fd362c689c3 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -408,22 +408,32 @@ Each ```` element can have the following attributes or sub-eleme *Default*: None :material_overrides: - Dictionnary of material overrides to be applied to the DAGMC universe. The keys are - the the Cell ID in the DAGMC Geometry and the values are a list of the material IDs. - If the list contains only one material if will replace the original material - assignemnt of the DAGMC cell. If the list contains more than one material the - materials, each material of the list will be assigned to the different instances of - the DAGMC cell. + This element contains information on material overrides to be applied to the + DAGMC universe. It has the following sub-elements: - *Default*: None + :cell: + Material override information for a single cell. It contains the following + attributes: + + :id: + The cell ID in the DAGMC geometry for which the material override will + apply. + :material: + A list of material IDs that will apply to instances of the cell. If the + list contains only one ID, it will replace the original material + assignment of all instances of the DAGMC cell. If the list contains more + than one material, each material ID of the list will be assigned to the + various instances of the DAGMC cell. - .. note:: A geometry.xml file containing only a DAGMC model for a file named `dagmc.h5m` (no CSG) - looks as follows + *Default*: None - .. code-block:: xml +.. note:: A geometry.xml file containing only a DAGMC model for a file named + `dagmc.h5m` (no CSG) looks as follows: + + .. code-block:: xml - - - - + + + + From 415e614ff251141d634ee0914fbaf1d510f1312d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Dec 2024 13:35:27 -0600 Subject: [PATCH 097/122] Remove unused imports --- tests/unit_tests/dagmc/test_model.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index ef77b2b4ccd..8ab55072a26 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -1,11 +1,9 @@ import pkg_resources -from pathlib import Path import numpy as np import pytest import openmc -from openmc import ZPlane, YPlane, XPlane, Cell pytestmark = pytest.mark.skipif( not openmc.lib._dagmc_enabled(), @@ -42,7 +40,7 @@ def model(): box = openmc.model.RectangularParallelepiped(-PITCH, PITCH, -PITCH, PITCH, -5, 5) - root = openmc.Universe(cells=[Cell(region= -box, fill=lattice)]) + root = openmc.Universe(cells=[openmc.Cell(region= -box, fill=lattice)]) settings = openmc.Settings() settings.batches = 100 From 383a8789e2ec084ec06679b2d4c3eac00f280c98 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Dec 2024 13:38:30 -0600 Subject: [PATCH 098/122] Cleanup with material_overrides --- include/openmc/dagmc.h | 16 +++++++++++----- src/dagmc.cpp | 18 +++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 3988c3c2124..15023f22080 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -29,6 +29,12 @@ void check_dagmc_root_univ(); #include "openmc/particle.h" #include "openmc/position.h" #include "openmc/surface.h" +#include "openmc/vector.h" + +#include // for shared_ptr, unique_ptr +#include +#include +#include // for pair class UWUW; @@ -188,11 +194,11 @@ class DAGUniverse : public Universe { //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume - std::map> - material_overrides; ///!< Map of material overrides - ///!< keys correspond to the DAGMCCell id - ///!< values are a list of material ids used - ///!< for the override + std::unordered_map> + material_overrides_; //!< Map of material overrides + //!< keys correspond to the DAGMCCell id + //!< values are a list of material ids used + //!< for the override }; //============================================================================== diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 0c90f7a9e29..3a1913e83cf 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -75,18 +75,16 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) // get material assignment overloading if (check_for_node(node, "material_overrides")) { auto mat_node = node.child("material_overrides"); - // loop over all attributes (each attribute corresponds to a material) + // loop over all subelements (each subelement corresponds to a material) for (pugi::xml_node cell_node : mat_node.children("cell")) { // Store assignment reference name std::string ref_assignment = get_node_value(cell_node, "id"); // Get mat name for each assignement instances - std::string mat_overrides = get_node_value(cell_node, "material"); vector instance_mats = get_node_array(cell_node, "material"); // Store mat name for each instances - material_overrides.insert( - std::make_pair(std::stoi(ref_assignment), instance_mats)); + material_overrides_.emplace(std::stoi(ref_assignment), instance_mats); } } @@ -233,7 +231,7 @@ void DAGUniverse::init_geometry() if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { - if (material_overrides.count(c->id_)) { // Check for material override + if (material_overrides_.count(c->id_)) { // Check for material override override_assign_material(c); } else if (uses_uwuw()) { // UWUW assignement uwuw_assign_material(vol_handle, c); @@ -640,8 +638,8 @@ void DAGUniverse::override_assign_material(std::unique_ptr& c) const // Override the material assignment for each cell instance using the legacy // assignement - for (auto mat_str_instance : material_overrides.at(c->id_)) { - legacy_assign_material(std::to_string(mat_str_instance), c); + for (auto mat_str_instance : material_overrides_.at(c->id_)) { + this->legacy_assign_material(std::to_string(mat_str_instance), c); } } @@ -860,8 +858,7 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( // make sure the universe id is a DAGMC Universe const auto& univ = model::universes[model::universe_map[univ_id]]; if (univ->geom_type() != GeometryType::DAG) { - set_errmsg(fmt::format( - "Universe {} is not a DAGMC Universe!", std::to_string(univ_id))); + set_errmsg(fmt::format("Universe {} is not a DAGMC Universe!", univ_id)); return OPENMC_E_INVALID_TYPE; } @@ -869,8 +866,7 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( for (const auto& cell_index : univ->cells_) { const auto& cell = model::cells[cell_index]; if (cell->geom_type() == GeometryType::CSG) { - set_errmsg( - fmt::format("Cell {} is not a DAGMC Cell!", std::to_string(cell->id_))); + set_errmsg(fmt::format("Cell {} is not a DAGMC Cell!", cell->id_)); return OPENMC_E_INVALID_TYPE; } dag_cell_ids.push_back(cell->id_); From 2a8747a80202bb11843e129388c7e91b74424be8 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Dec 2024 13:40:57 -0600 Subject: [PATCH 099/122] Get rid of unused overload for split --- include/openmc/string_utils.h | 1 - src/string_utils.cpp | 25 ------------------------- 2 files changed, 26 deletions(-) diff --git a/include/openmc/string_utils.h b/include/openmc/string_utils.h index e7e613534a4..2e8b0d14f39 100644 --- a/include/openmc/string_utils.h +++ b/include/openmc/string_utils.h @@ -19,7 +19,6 @@ void to_lower(std::string& str); int word_count(const std::string& str); vector split(const std::string& in); -vector split(const std::string& in, char delim); bool ends_with(const std::string& value, const std::string& ending); diff --git a/src/string_utils.cpp b/src/string_utils.cpp index 454f2dca0be..74f048e8d24 100644 --- a/src/string_utils.cpp +++ b/src/string_utils.cpp @@ -71,31 +71,6 @@ vector split(const std::string& in) return out; } -vector split(const std::string& in, char delim) -{ - vector out; - - for (int i = 0; i < in.size();) { - // Increment i until we find a non-delimiter character. - if (in[i] == delim) { - i++; - - } else { - // Find the next delimiter character at j. - int j = i + 1; - while (j < in.size() && in[j] != delim) { - j++; - } - - // Push-back everything between i and j. - out.push_back(in.substr(i, j - i)); - i = j + 1; // j is delimiter so leapfrog to j+1 - } - } - - return out; -} - bool ends_with(const std::string& value, const std::string& ending) { if (ending.size() > value.size()) From 548c04e68dafbb153cd1dcce24f6bb55e997b885 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Dec 2024 13:56:06 -0600 Subject: [PATCH 100/122] Simplifications in lib/dagmc.py --- docs/source/pythonapi/capi.rst | 1 + openmc/cell.py | 1 - openmc/dagmc.py | 10 ++++----- openmc/lib/__init__.py | 2 +- openmc/lib/dagmc.py | 40 +++++++++------------------------- 5 files changed, 17 insertions(+), 37 deletions(-) diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index 995ad97fa74..9ceff83fde8 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -19,6 +19,7 @@ Functions finalize find_cell find_material + dagmc_universe_cell_ids global_bounding_box global_tallies hard_reset diff --git a/openmc/cell.py b/openmc/cell.py index e5c836c46a7..fe3939bbe39 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -780,4 +780,3 @@ def from_xml_element(cls, elem, surfaces, materials, get_universe): univ_id = int(get_text(elem, 'universe', 0)) get_universe(univ_id).add_cell(c) return c - diff --git a/openmc/dagmc.py b/openmc/dagmc.py index bbb272b7949..cf445751f6e 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -41,7 +41,7 @@ class DAGMCUniverse(openmc.UniverseBase): space between OpenMC and UWUW materials (False) material_overrides : dict A dictionary of material overrides. The keys are material name - strings and the values are Iterables of openmc.Material objects. If a + strings and the values are Iterables of openmc.Material objects. If a material name is found in the DAGMC file, the material will be replaced with the openmc.Material object in the value. @@ -83,11 +83,11 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.15 material_overrides : dict A dictionary of material overrides. The keys are material name - strings and the values are Iterables of openmc.Material objects. If a + strings and the values are Iterables of openmc.Material objects. If a material name is found in the DAGMC file, the material will be replaced with the openmc.Material object in the value. """ - + def __init__(self, filename: cv.PathLike, universe_id=None, @@ -471,7 +471,7 @@ def from_xml_element(cls, elem, mats): mat_ids = elem.attrib["material"].split(' ') mat_objs = [mats[mat_id] for mat_id in mat_ids] out.add_material_override(cell_id, mat_objs) - + return out def _partial_deepcopy(self): @@ -534,7 +534,7 @@ def sync_dagmc_cells(self, mats): "initialized via Model.init_lib before calling " "this method.") - dagmc_cell_ids = openmc.lib.dagmc.get_dagmc_cell_ids(self.id) + dagmc_cell_ids = openmc.lib.dagmc.dagmc_universe_cell_ids(self.id) if len(dagmc_cell_ids) != self.n_cells: raise ValueError( f"Number of cells in DAGMC universe {self.id} does not match " diff --git a/openmc/lib/__init__.py b/openmc/lib/__init__.py index 4cdd53b6a70..5fe35b9745d 100644 --- a/openmc/lib/__init__.py +++ b/openmc/lib/__init__.py @@ -68,7 +68,7 @@ def _uwuw_enabled(): from .math import * from .plot import * from .weight_windows import * -from .dagmc import get_dagmc_cell_ids, get_dagmc_universe_num_cells +from .dagmc import * # Flag to denote whether or not openmc.lib.init has been called # TODO: Establish and use a flag in the C++ code to represent the status of the diff --git a/openmc/lib/dagmc.py b/openmc/lib/dagmc.py index d42b5ea857f..18ec81a4be8 100644 --- a/openmc/lib/dagmc.py +++ b/openmc/lib/dagmc.py @@ -1,4 +1,3 @@ -import sys from ctypes import c_int, c_int32, POINTER, c_size_t import numpy as np @@ -7,6 +6,10 @@ from .error import _error_handler +__all__ = [ + 'dagmc_universe_cell_ids' +] + # DAGMC functions _dll.openmc_dagmc_universe_get_cell_ids.argtypes = [c_int32, POINTER(c_int32), POINTER(c_size_t)] _dll.openmc_dagmc_universe_get_cell_ids.restype = c_int @@ -16,48 +19,25 @@ _dll.openmc_dagmc_universe_get_num_cells.errcheck = _error_handler -def get_dagmc_cell_ids(dagmc_id): - """Get the DAGMC cell IDs for a volume. +def dagmc_universe_cell_ids(universe_id: int) -> np.ndarray: + """Return an array of cell IDs for a DAGMC universe. Parameters ---------- dagmc_id : int - ID of the DAGMC Universe to get cell IDs from. - n_cells : int - Number of cells in the DAGMC Universe. + ID of the DAGMC universe to get cell IDs from. Returns ------- numpy.ndarray - DAGMC cell IDs for the volume. + DAGMC cell IDs for the universe. """ n = c_size_t() - _dll.openmc_dagmc_universe_get_num_cells(dagmc_id, n) + _dll.openmc_dagmc_universe_get_num_cells(universe_id, n) cell_ids = np.empty(n.value, dtype=np.int32) _dll.openmc_dagmc_universe_get_cell_ids( - dagmc_id, - cell_ids.ctypes.data_as(POINTER(c_int32)), - n + universe_id, cell_ids.ctypes.data_as(POINTER(c_int32)), n ) return cell_ids - - -def get_dagmc_universe_num_cells(dagmc_id): - """Get the number of cells in a DAGMC universe. - - Parameters - ---------- - dagmc_id : int - ID of the DAGMC Universe to get the number of cell from. - - Returns - ------- - int - Number of cells in the DAGMC Universe. - - """ - n = c_size_t() - _dll.openmc_dagmc_universe_get_num_cells(dagmc_id, n) - return n.value From ff947b80eeb7d5b8acec34c48cf96d9e12abb24e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 20 Dec 2024 14:08:35 -0600 Subject: [PATCH 101/122] Remove get_all_dagmc_universes --- openmc/geometry.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/openmc/geometry.py b/openmc/geometry.py index 43aa3ded79f..28e1e48eca2 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -402,23 +402,6 @@ def get_all_nuclides(self) -> list[str]: for material in self.get_all_materials().values(): all_nuclides |= set(material.get_nuclides()) return sorted(all_nuclides) - - def get_all_dagmc_universes(self) -> typing.Dict[int, openmc.DAGMCUniverse]: - """Return all universes in the geometry. - - Returns - ------- - dict - Dictionary mapping universe IDs to :class:`openmc.DAGMCUniverse` - instances - - """ - universes = self.get_all_universes() - dag_universes = {} - for id, univ in universes.items(): - if isinstance(uni, openmc.DAGMCUniverse): - dag_universes[id] = univ - return dag_universes def get_all_materials(self) -> dict[int, openmc.Material]: """Return all materials within the geometry. From 6b00d6665d56b9ad90a3cc21a5ad6dac67fe9b2f Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Fri, 20 Dec 2024 21:15:57 +0100 Subject: [PATCH 102/122] Cell Id as key --- openmc/dagmc.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index cf445751f6e..fd6a3c3119c 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -82,10 +82,11 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.15 material_overrides : dict - A dictionary of material overrides. The keys are material name - strings and the values are Iterables of openmc.Material objects. If a - material name is found in the DAGMC file, the material will be replaced - with the openmc.Material object in the value. + A dictionary of material overrides. The keys are Cell id and values + are Iterables of openmc.Material objects. The material assignment of + each DAGMC Cell id key will be replaced with the openmc.Material object + in the value. If the value contains multiple openmc.Material objects, each + Material of the list will replace one instance of the Cell. """ def __init__(self, @@ -158,7 +159,7 @@ def add_material_override(self, key, overrides=None): if key in self.material_names: for cell in self.cells.values(): if cell.fill.name == key: - keys.append(cell) + keys.append(cell.id) elif int(key) in self.cells: keys = [self.cells[int(key)]] else: @@ -169,13 +170,13 @@ def add_material_override(self, key, overrides=None): raise ValueError( f"Cell ID '{key}' not found in DAGMC universe") else: - keys = [self.cells[key]] + keys = [key] elif isinstance(key, openmc.Cell): if key not in self.cells.values(): raise ValueError( f"Cell '{key.id}' not found in DAGMC universe") else: - keys = [key] + keys = [key.id] else: raise ValueError("Unrecognized key type. Must be a string or integer.") @@ -295,7 +296,7 @@ def create_xml_subelement(self, xml_element, memo=None): mat_element = ET.Element('material_overrides') for key in self._material_overrides: cell_overrides = ET.Element('cell') - cell_overrides.set("id", str(key.id)) + cell_overrides.set("id", str(key)) cell_overrides.set("material", ' '.join( str(t.id) for t in self._material_overrides[key])) mat_element.append(cell_overrides) From 11a6b508a595a7f3ffb8d99552c419bcf16dc4d2 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 23:22:10 -0600 Subject: [PATCH 103/122] Altering override data structure type on the C++ side. --- include/openmc/dagmc.h | 2 +- src/dagmc.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 15023f22080..47fcfe237e3 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -194,7 +194,7 @@ class DAGUniverse : public Universe { //!< generate new material IDs for the universe bool has_graveyard_; //!< Indicates if the DAGMC geometry has a "graveyard" //!< volume - std::unordered_map> + std::unordered_map> material_overrides_; //!< Map of material overrides //!< keys correspond to the DAGMCCell id //!< values are a list of material ids used diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 3a1913e83cf..288b2036f52 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -78,13 +78,13 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) // loop over all subelements (each subelement corresponds to a material) for (pugi::xml_node cell_node : mat_node.children("cell")) { // Store assignment reference name - std::string ref_assignment = get_node_value(cell_node, "id"); + int32_t ref_assignment = std::stoi(get_node_value(cell_node, "id")); // Get mat name for each assignement instances - vector instance_mats = get_node_array(cell_node, "material"); + vector instance_mats = get_node_array(cell_node, "material"); // Store mat name for each instances - material_overrides_.emplace(std::stoi(ref_assignment), instance_mats); + material_overrides_.emplace(ref_assignment, instance_mats); } } @@ -629,7 +629,6 @@ void DAGUniverse::uwuw_assign_material( void DAGUniverse::override_assign_material(std::unique_ptr& c) const { - // if Cell ID matches an override key, use it to override the material // assignment else if UWUW is used, get the material assignment from the DAGMC // metadata @@ -858,7 +857,7 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( // make sure the universe id is a DAGMC Universe const auto& univ = model::universes[model::universe_map[univ_id]]; if (univ->geom_type() != GeometryType::DAG) { - set_errmsg(fmt::format("Universe {} is not a DAGMC Universe!", univ_id)); + set_errmsg(fmt::format("Universe {} is not a DAGMC Universe", univ_id)); return OPENMC_E_INVALID_TYPE; } @@ -866,7 +865,7 @@ extern "C" int openmc_dagmc_universe_get_cell_ids( for (const auto& cell_index : univ->cells_) { const auto& cell = model::cells[cell_index]; if (cell->geom_type() == GeometryType::CSG) { - set_errmsg(fmt::format("Cell {} is not a DAGMC Cell!", cell->id_)); + set_errmsg(fmt::format("Cell {} is not a DAGMC Cell", cell->id_)); return OPENMC_E_INVALID_TYPE; } dag_cell_ids.push_back(cell->id_); From bf714c7b2c7f9fe6562240a99f11541eae147a11 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 23:23:26 -0600 Subject: [PATCH 104/122] Docstring updates and build_override_mat_from_cells method simplification --- openmc/dagmc.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index fd6a3c3119c..9713b4c5988 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -83,10 +83,10 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.15 material_overrides : dict A dictionary of material overrides. The keys are Cell id and values - are Iterables of openmc.Material objects. The material assignment of + are Iterables of openmc.Material objects. The material assignment of each DAGMC Cell id key will be replaced with the openmc.Material object in the value. If the value contains multiple openmc.Material objects, each - Material of the list will replace one instance of the Cell. + Material in the list be assigned to one instance of the Cell. """ def __init__(self, @@ -151,7 +151,7 @@ def add_material_override(self, key, overrides=None): key : str Material name or ID of the Cell to override value : Iterable of Materials - Materials to replace the key with + Materials to be applied to the Cell passed as the key """ keys = [] @@ -311,11 +311,8 @@ def build_override_mat_from_cells(self): None """ for cell in self.cells.values(): - if isinstance(cell.fill, Iterable): - self.add_material_override(cell, [mat for mat in cell.fill if mat]) - else: - if cell.fill: - self.add_material_override(cell, cell.fill) + fill = cell.fill if isinstance(cell.fill, openmc.Iterable) else [cell.fill] + self.add_material_override(cell.id, fill) def bounding_region( self, From 31e6ebc0de71dd3e3867e4101219af02e6f81a25 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 23:24:40 -0600 Subject: [PATCH 105/122] removing build_override_mat_from_cells method as it's just a few lines now --- openmc/dagmc.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 9713b4c5988..728c2dc11f5 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -280,8 +280,10 @@ def create_xml_subelement(self, xml_element, memo=None): memo.add(self) - # Ensure that the material overrides are up-t-date - self.build_override_mat_from_cells() + # Ensure that the material overrides are up-to-date + for cell in self.cells.values(): + fill = cell.fill if isinstance(cell.fill, openmc.Iterable) else [cell.fill] + self.add_material_override(cell.id, fill) # Set xml element values dagmc_element = ET.Element('dagmc_universe') @@ -303,17 +305,6 @@ def create_xml_subelement(self, xml_element, memo=None): dagmc_element.append(mat_element) xml_element.append(dagmc_element) - def build_override_mat_from_cells(self): - """ - Builds the material override dictionary for cells with multiple instances. - - Returns: - None - """ - for cell in self.cells.values(): - fill = cell.fill if isinstance(cell.fill, openmc.Iterable) else [cell.fill] - self.add_material_override(cell.id, fill) - def bounding_region( self, bounded_type: str = 'box', From 3898c2b7c87f252bda7b38716b91905081322c42 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 23:35:24 -0600 Subject: [PATCH 106/122] Skipping cells with a None fill while building material override to maintain current behavior --- openmc/dagmc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 728c2dc11f5..8a149d37ad9 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -183,7 +183,7 @@ def add_material_override(self, key, overrides=None): # Ensure that overrides is an iterable of openmc.Material if not isinstance(overrides, Iterable): overrides = [overrides] - cv.check_iterable_type('material objects', overrides, openmc.Material) + cv.check_iterable_type('material objects', overrides, (openmc.Material, type(None))) # if material_overrides is not initialized, initialize it if not self._material_overrides: @@ -282,8 +282,10 @@ def create_xml_subelement(self, xml_element, memo=None): # Ensure that the material overrides are up-to-date for cell in self.cells.values(): + if cell.fill is None: + continue fill = cell.fill if isinstance(cell.fill, openmc.Iterable) else [cell.fill] - self.add_material_override(cell.id, fill) + self.add_material_override(cell, fill) # Set xml element values dagmc_element = ET.Element('dagmc_universe') From 472930936521d563112fda223c83c5c4540e00c4 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 23:36:27 -0600 Subject: [PATCH 107/122] Simplify control flow in diff mats. Docstring updates --- openmc/model/model.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index d3e9c1b98df..dcc3c8d4afb 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -329,7 +329,8 @@ def sync_dagmc_universes(self): """ Synchronize all DAGMC universes in the current geometry. This method iterates over all DAGMC universes in the geometry and - synchronizes their cells with the current material assignments. + synchronizes their cells with the current material assignments. Requires + that the model has been initialized via :meth:`Model.init_lib`. .. versionadded:: 0.15.1-dev @@ -1207,14 +1208,14 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo ---------- diff_volume_method : str Specifies how the volumes of the new materials should be found. - - 'None': Do not assign volumes to the new materials (Default) + - None: Do not assign volumes to the new materials (Default) - 'divide_equally': Divide the original material volume equally between the new materials - 'match cell': Set the volume of the material to the volume of the cell they fill depletable_only : bool - Default is True, only depletable materials will be differentiated all materials will be - differentiated otherwise. + Default is True, only depletable materials will be differentiated. If False, all materials will be + differentiated. """ - check_value('volume differentiation method', diff_volume_method, ["divide equally", "match cell", None]) + check_value('volume differentiation method', diff_volume_method, ("divide equally", "match cell", None)) # Count the number of instances for each cell and material self.geometry.determine_paths(instances_only=True) @@ -1246,19 +1247,18 @@ def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bo "diff_volume_method='match cell'.") distribmats.add(mat) - if distribmats: - # Assign distribmats to cells - for cell in self.geometry.get_all_material_cells().values(): - if cell.fill in distribmats: - mat = cell.fill - if diff_volume_method != 'match cell': - cell.fill = [mat.clone() for _ in range(cell.num_instances)] - elif diff_volume_method == 'match cell': - cell.fill = mat.clone() - cell.fill.volume = cell.volume - if isinstance(cell, openmc.DAGMCCell): - for i in range(cell.num_instances): - cell.fill[i].name = f"{cell.fill[i].name}_{cell.id}_{i}" + if not distribmats: + return + + # Assign distribmats to cells + for cell in self.geometry.get_all_material_cells().values(): + if cell.fill in distribmats: + mat = cell.fill + if diff_volume_method != 'match cell': + cell.fill = [mat.clone() for _ in range(cell.num_instances)] + elif diff_volume_method == 'match cell': + cell.fill = mat.clone() + cell.fill.volume = cell.volume if self.materials is not None: self.materials = openmc.Materials( From ad6a877a29ac5bfa6b618be6671fad5452737de6 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 23:37:05 -0600 Subject: [PATCH 108/122] Better position for try in existing test. Add test for ValueErro when using an invalid cell ID in overrides --- tests/unit_tests/dagmc/test_model.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 8ab55072a26..3f8fd0952d9 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -80,10 +80,9 @@ def test_model_differentiate_depletable_with_dagmc(model, run_in_tmpdir): def test_model_differentiate_with_dagmc(model, run_in_tmpdir): + root = model.geometry.root_universe + ll, ur = root.bounding_box try: - root = model.geometry.root_universe - ll, ur = root.bounding_box - model.init_lib() model.sync_dagmc_universes() model.calculate_volumes() @@ -105,3 +104,16 @@ def test_model_differentiate_with_dagmc(model, run_in_tmpdir): assert len(model.materials) == 4*2 + 4 finally: model.finalize_lib() + + +def test_bad_override_cell_id(model, run_in_tmpdir): + try: + model.init_lib() + model.sync_dagmc_universes() + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="Cell ID '1' not found in DAGMC universe"): + univ.material_overrides = {1 : model.materials[0]} + finally: + model.finalize_lib() \ No newline at end of file From e1655e2e6151b04677fbf363fe6a266ae9cf69aa Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Fri, 20 Dec 2024 23:39:34 -0600 Subject: [PATCH 109/122] Style fixes --- src/dagmc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 288b2036f52..e7b861eccba 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -81,7 +81,8 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) int32_t ref_assignment = std::stoi(get_node_value(cell_node, "id")); // Get mat name for each assignement instances - vector instance_mats = get_node_array(cell_node, "material"); + vector instance_mats = + get_node_array(cell_node, "material"); // Store mat name for each instances material_overrides_.emplace(ref_assignment, instance_mats); From 4576e267a4eaa0c883e8329b7b29250d77c63055 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Sun, 22 Dec 2024 11:41:18 +0100 Subject: [PATCH 110/122] adding test for XML generation and creation from, fixing _from_XML method --- openmc/dagmc.py | 12 ++++++--- tests/unit_tests/dagmc/test.py | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 8a149d37ad9..9eddfe4ed3b 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -181,7 +181,7 @@ def add_material_override(self, key, overrides=None): raise ValueError("Unrecognized key type. Must be a string or integer.") # Ensure that overrides is an iterable of openmc.Material - if not isinstance(overrides, Iterable): + if not isinstance(overrides, openmc.Iterable): overrides = [overrides] cv.check_iterable_type('material objects', overrides, (openmc.Material, type(None))) @@ -436,6 +436,9 @@ def from_xml_element(cls, elem, mats): ---------- elem : lxml.etree._Element `` element + mats : dict + Dictionary mapping material ID strings to :class:`openmc.Material` + instances (defined in :meth:`openmc.Geometry.from_xml`) Returns ------- @@ -457,11 +460,12 @@ def from_xml_element(cls, elem, mats): el_mat_override = elem.find('material_overrides') if el_mat_override is not None: + out._material_overrides = {} for elem in el_mat_override.findall('cell'): - cell_id = elem.attrib('id') - mat_ids = elem.attrib["material"].split(' ') + cell_id = int(get_text(elem, 'id')) + mat_ids = get_text(elem, 'material').split(' ') mat_objs = [mats[mat_id] for mat_id in mat_ids] - out.add_material_override(cell_id, mat_objs) + out._material_overrides[cell_id] = mat_objs return out diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index e84b5317ede..47c5acc0a23 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -1,5 +1,7 @@ +import pkg_resources import shutil +import lxml.etree as ET import numpy as np from pathlib import Path import pytest @@ -84,3 +86,49 @@ def dagmc_model(request): def test_dagmc_temperatures(cell_id, exp_temp): cell = openmc.lib.cells[cell_id] assert np.isclose(cell.get_temperature(), exp_temp) + + +def test_dagmc_xml(): + + # Set the environment + mats = {} + mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") + mats["no-void fuel"].add_nuclide("U235", 0.03) + mats["no-void fuel"].add_nuclide("U238", 0.97) + mats["no-void fuel"].add_nuclide("O16", 2.0) + mats["no-void fuel"].set_density("g/cm3", 10.0) + + mats["41"] = openmc.Material(name="41") + mats["41"].add_nuclide("H1", 2.0) + mats["41"].add_element("O", 1.0) + mats["41"].set_density("g/cm3", 1.0) + mats["41"].add_s_alpha_beta("c_H_in_H2O") + + p = pkg_resources.resource_filename(__name__, "dagmc.h5m") + daguniv = openmc.DAGMCUniverse(p,auto_geom_ids=True,) + daguniv._material_overrides = {40: [mats["no-void fuel"]], 52: [mats["41"]]} + + root = ET.Element('dagmc_universe') + daguniv.create_xml_subelement(root) + dagmc_ele = root.find('dagmc_universe') + + assert dagmc_ele.get('id') == str(daguniv.id) + assert dagmc_ele.get('filename') == str(daguniv.filename) + assert dagmc_ele.get('auto_geom_ids') == str(daguniv.auto_geom_ids).lower() + + # Verify the material overrides element in the XML tree + override_eles = dagmc_ele.find('material_overrides').findall('cell') + assert len(override_eles) == 2 + assert override_eles[0].get('id') == '40' + assert override_eles[0].get('material') == str(mats["no-void fuel"].id) + assert override_eles[1].get('id') == '52' + assert override_eles[1].get('material') == str(mats["41"].id) + + # Create a dictionary of materials to pass to from_xml_element indexing by + # material ID strings + dict_mats = {str(m.id): m for m in mats.values()} + dagmcuni_2 = openmc.DAGMCUniverse.from_xml_element(dagmc_ele, dict_mats) + assert daguniv.id == dagmcuni_2.id + assert daguniv.filename == dagmcuni_2.filename + assert daguniv.auto_geom_ids == dagmcuni_2.auto_geom_ids + assert daguniv._material_overrides == dagmcuni_2._material_overrides From 2340aac265fe723f39142c53219047deef87a7f0 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 23 Dec 2024 14:17:06 -0600 Subject: [PATCH 111/122] Apply suggestion from @bam241 Co-authored-by: Baptiste Mouginot <15145274+bam241@users.noreply.github.com> --- openmc/dagmc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 9eddfe4ed3b..5b47fa5cf0d 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -284,7 +284,7 @@ def create_xml_subelement(self, xml_element, memo=None): for cell in self.cells.values(): if cell.fill is None: continue - fill = cell.fill if isinstance(cell.fill, openmc.Iterable) else [cell.fill] + fill = cell.fill self.add_material_override(cell, fill) # Set xml element values From 76564cdf64a36f22c611f8798fd6041da7c60dcd Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 23 Dec 2024 13:56:14 -0600 Subject: [PATCH 112/122] Separate and simplify how cell override assignments are set and create a new method for material replacement. Other changes: * remove unused DAGMCUniverse.dagmc_cells attribute * update documentation on material_overrides attribute * alter XML layout so potentially long strings of material IDs are text of an element rather than attributes * update XML documentation to reflect structure changes --- docs/source/io_formats/geometry.rst | 4 +- openmc/dagmc.py | 108 ++++++++++++++++------------ 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index fd362c689c3..68474c6302b 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -413,13 +413,13 @@ Each ```` element can have the following attributes or sub-eleme :cell: Material override information for a single cell. It contains the following - attributes: + attributes and sub-elements: :id: The cell ID in the DAGMC geometry for which the material override will apply. - :material: + :materials: A list of material IDs that will apply to instances of the cell. If the list contains only one ID, it will replace the original material assignment of all instances of the DAGMC cell. If the list contains more diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 5b47fa5cf0d..ce145e107f6 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -1,4 +1,4 @@ -from collections.abc import Iterable +from collections.abc import Iterable, Mapping from numbers import Integral from pathlib import Path @@ -82,11 +82,11 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.15 material_overrides : dict - A dictionary of material overrides. The keys are Cell id and values + A dictionary of material overrides. Keys are Cell IDs; values are Iterables of openmc.Material objects. The material assignment of each DAGMC Cell id key will be replaced with the openmc.Material object in the value. If the value contains multiple openmc.Material objects, each - Material in the list be assigned to one instance of the Cell. + Material in the list be assigned to the corresponding instance of the Cell. """ def __init__(self, @@ -101,8 +101,7 @@ def __init__(self, self.filename = filename self.auto_geom_ids = auto_geom_ids self.auto_mat_ids = auto_mat_ids - self.material_overrides = mat_overrides - self._dagmc_cells = [] + self._material_overrides = {} if mat_overrides is None else mat_overrides def __repr__(self): string = super().__repr__() @@ -137,10 +136,41 @@ def material_overrides(self, val): self._material_overrides = val return else: - cv.check_type('material overrides', val, dict) + cv.check_type('material overrides', val, Mapping) for key, value in val.items(): self.add_material_override(key, value) + def replace_material_assignment(self, material_name, material): + """Replace the material assignment of all cells filled with a material + in the DAGMC universe. The universe must be synchronized in an + initialized Model (see :meth:`openmc.DAGMCUniverse.sync_dagmc_cells`) + before calling this method. + + .. versionadded:: 0.15 + + Parameters + ---------- + material_name : str + Material name to replace + material : openmc.Material + Material to replace the material_name with + + """ + if material_name not in self.material_names: + raise ValueError( + f"No material with name '{material_name}' found in the DAGMC universe") + + if not self.cells: + raise RuntimeError("This DAGMC universe has not been synchronized in an initialized Model.") + + for cell in self.cells.values(): + if cell.fill is None: + continue + if isinstance(cell.fill, openmc.Iterable): + cell.fill = list(map(lambda x: material if x.name == material_name else x, cell.fill)) + else: + cell.fill = material if cell.fill.name == material_name else cell.fill + def add_material_override(self, key, overrides=None): """Add a material override to the universe. @@ -148,48 +178,30 @@ def add_material_override(self, key, overrides=None): Parameters ---------- - key : str - Material name or ID of the Cell to override - value : Iterable of Materials - Materials to be applied to the Cell passed as the key + key : openmc.DAGMCCell or int + Cell object or ID of the Cell to override + value : openmc.Material or Iterable of openmc.Material + Material(s) to be applied to the Cell passed as the key """ - keys = [] - if isinstance(key, str): - if key in self.material_names: - for cell in self.cells.values(): - if cell.fill.name == key: - keys.append(cell.id) - elif int(key) in self.cells: - keys = [self.cells[int(key)]] - else: - raise ValueError( - f"Material or Cell ID '{key}' not found in DAGMC universe") - elif isinstance(key, int): - if key not in self.cells: - raise ValueError( - f"Cell ID '{key}' not found in DAGMC universe") - else: - keys = [key] - elif isinstance(key, openmc.Cell): - if key not in self.cells.values(): - raise ValueError( - f"Cell '{key.id}' not found in DAGMC universe") - else: - keys = [key.id] - else: - raise ValueError("Unrecognized key type. Must be a string or integer.") + # Ensure that they key is a valid type + if not isinstance(key, (int, openmc.DAGMCCell)): + raise ValueError("Unrecognized key type. \ + Must be a string, integer, or openmc.DAGMCCell object") # Ensure that overrides is an iterable of openmc.Material - if not isinstance(overrides, openmc.Iterable): - overrides = [overrides] + overrides = overrides if isinstance(overrides, openmc.Iterable) else [overrides] cv.check_iterable_type('material objects', overrides, (openmc.Material, type(None))) - # if material_overrides is not initialized, initialize it - if not self._material_overrides: - self._material_overrides = {} - for item in keys: - self._material_overrides[item] = overrides + # if a DAGMCCell is passed, redcue the key to the ID of the cell + if isinstance(key, openmc.DAGMCCell): + key = key.id + + if key not in self.cells: + raise ValueError( + f"Cell ID '{key}' not found in DAGMC universe") + + self._material_overrides[key] = overrides @property def auto_geom_ids(self): @@ -299,10 +311,12 @@ def create_xml_subelement(self, xml_element, memo=None): if self._material_overrides: mat_element = ET.Element('material_overrides') for key in self._material_overrides: - cell_overrides = ET.Element('cell') + cell_overrides = ET.Element('cell_override') cell_overrides.set("id", str(key)) - cell_overrides.set("material", ' '.join( - str(t.id) for t in self._material_overrides[key])) + material_element = ET.Element('material_ids') + material_element.text = ' '.join( + str(t.id) for t in self._material_overrides[key]) + cell_overrides.append(material_element) mat_element.append(cell_overrides) dagmc_element.append(mat_element) xml_element.append(dagmc_element) @@ -461,9 +475,9 @@ def from_xml_element(cls, elem, mats): el_mat_override = elem.find('material_overrides') if el_mat_override is not None: out._material_overrides = {} - for elem in el_mat_override.findall('cell'): + for elem in el_mat_override.findall('cell_override'): cell_id = int(get_text(elem, 'id')) - mat_ids = get_text(elem, 'material').split(' ') + mat_ids = get_text(elem, 'material_ids').split(' ') mat_objs = [mats[mat_id] for mat_id in mat_ids] out._material_overrides[cell_id] = mat_objs From 9801c80a90d7cdf9d4a44b7d72acedad5700d4e4 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 23 Dec 2024 14:04:56 -0600 Subject: [PATCH 113/122] Update C++ tags to reflect XML structure changes --- src/dagmc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index e7b861eccba..7d6aac9c24c 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -76,13 +76,13 @@ DAGUniverse::DAGUniverse(pugi::xml_node node) if (check_for_node(node, "material_overrides")) { auto mat_node = node.child("material_overrides"); // loop over all subelements (each subelement corresponds to a material) - for (pugi::xml_node cell_node : mat_node.children("cell")) { + for (pugi::xml_node cell_node : mat_node.children("cell_override")) { // Store assignment reference name int32_t ref_assignment = std::stoi(get_node_value(cell_node, "id")); // Get mat name for each assignement instances vector instance_mats = - get_node_array(cell_node, "material"); + get_node_array(cell_node, "material_ids"); // Store mat name for each instances material_overrides_.emplace(ref_assignment, instance_mats); From 92ed191270dadf54be23e49fea47f20807311f4c Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 23 Dec 2024 14:06:58 -0600 Subject: [PATCH 114/122] Update tests related to XML IO --- tests/unit_tests/dagmc/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index 47c5acc0a23..2388d64e5c0 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -117,12 +117,12 @@ def test_dagmc_xml(): assert dagmc_ele.get('auto_geom_ids') == str(daguniv.auto_geom_ids).lower() # Verify the material overrides element in the XML tree - override_eles = dagmc_ele.find('material_overrides').findall('cell') + override_eles = dagmc_ele.find('material_overrides').findall('cell_override') assert len(override_eles) == 2 assert override_eles[0].get('id') == '40' - assert override_eles[0].get('material') == str(mats["no-void fuel"].id) + assert override_eles[0].find('material_ids').text == str(mats["no-void fuel"].id) assert override_eles[1].get('id') == '52' - assert override_eles[1].get('material') == str(mats["41"].id) + assert override_eles[1].find('material_ids').text == str(mats["41"].id) # Create a dictionary of materials to pass to from_xml_element indexing by # material ID strings From a08663aea493f176bfac2bbc829021804145c435 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 23 Dec 2024 14:17:50 -0600 Subject: [PATCH 115/122] Adding test with full roundtrip --- tests/unit_tests/dagmc/test_model.py | 51 +++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 3f8fd0952d9..f6b7848bc1e 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -116,4 +116,53 @@ def test_bad_override_cell_id(model, run_in_tmpdir): with pytest.raises(ValueError, match="Cell ID '1' not found in DAGMC universe"): univ.material_overrides = {1 : model.materials[0]} finally: - model.finalize_lib() \ No newline at end of file + model.finalize_lib() + + +def test_dagmc_xml(model, run_in_tmpdir): + # Set the environment + mats = {} + mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") + mats["no-void fuel"].add_nuclide("U235", 0.03) + mats["no-void fuel"].add_nuclide("U238", 0.97) + mats["no-void fuel"].add_nuclide("O16", 2.0) + mats["no-void fuel"].set_density("g/cm3", 10.0) + + mats[23] = openmc.Material(name="41") + mats[23].add_nuclide("H1", 2.0) + mats[23].add_element("O", 1.0) + mats[23].set_density("g/cm3", 1.0) + mats[23].add_s_alpha_beta("c_H_in_H2O") + + try: + model.init_lib() + model.sync_dagmc_universes() + finally: + model.finalize_lib() + + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + dag_univ = univ + break + + print(dag_univ.cells) + + for k, v in mats.items(): + if isinstance(k, int): + dag_univ.add_material_override(k, v) + elif isinstance(k, str): + dag_univ.replace_material_assignment(k, v) + + model.export_to_model_xml() + + xml_model = openmc.Model.from_model_xml() + + for univ in xml_model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + xml_dagmc_univ = univ + break + + assert xml_dagmc_univ._material_overrides.keys() == dag_univ._material_overrides.keys() + + for xml_mats, model_mats in zip(xml_dagmc_univ._material_overrides.values(), dag_univ._material_overrides.values()): + assert all([xml_mat.id == orig_mat.id for xml_mat, orig_mat in zip(xml_mats, model_mats)]) \ No newline at end of file From 4da07c6a8bcf369147230b842e9a05b9e0dee42a Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 23 Dec 2024 14:18:42 -0600 Subject: [PATCH 116/122] Simplifying further --- openmc/dagmc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index ce145e107f6..b6cfc722439 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -296,8 +296,7 @@ def create_xml_subelement(self, xml_element, memo=None): for cell in self.cells.values(): if cell.fill is None: continue - fill = cell.fill - self.add_material_override(cell, fill) + self.add_material_override(cell, cell.fill) # Set xml element values dagmc_element = ET.Element('dagmc_universe') From 1f28a9e8e75fa778dc1c27537fa156fc74a8ec13 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 23 Dec 2024 14:22:12 -0600 Subject: [PATCH 117/122] Removing errant print --- tests/unit_tests/dagmc/test_model.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index f6b7848bc1e..34c1f254945 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -145,8 +145,6 @@ def test_dagmc_xml(model, run_in_tmpdir): dag_univ = univ break - print(dag_univ.cells) - for k, v in mats.items(): if isinstance(k, int): dag_univ.add_material_override(k, v) From 23f2bc6160e74098b310b3991c8ff5f543ebb8f2 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 25 Dec 2024 12:14:28 +0100 Subject: [PATCH 118/122] consolidating all test related to dagmc material override xml generation --- tests/unit_tests/dagmc/test.py | 48 +--------------------------- tests/unit_tests/dagmc/test_model.py | 18 +++++++++++ 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index 2388d64e5c0..88aa25b0799 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -85,50 +85,4 @@ def dagmc_model(request): (3, 293.6))) # assigned by default def test_dagmc_temperatures(cell_id, exp_temp): cell = openmc.lib.cells[cell_id] - assert np.isclose(cell.get_temperature(), exp_temp) - - -def test_dagmc_xml(): - - # Set the environment - mats = {} - mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") - mats["no-void fuel"].add_nuclide("U235", 0.03) - mats["no-void fuel"].add_nuclide("U238", 0.97) - mats["no-void fuel"].add_nuclide("O16", 2.0) - mats["no-void fuel"].set_density("g/cm3", 10.0) - - mats["41"] = openmc.Material(name="41") - mats["41"].add_nuclide("H1", 2.0) - mats["41"].add_element("O", 1.0) - mats["41"].set_density("g/cm3", 1.0) - mats["41"].add_s_alpha_beta("c_H_in_H2O") - - p = pkg_resources.resource_filename(__name__, "dagmc.h5m") - daguniv = openmc.DAGMCUniverse(p,auto_geom_ids=True,) - daguniv._material_overrides = {40: [mats["no-void fuel"]], 52: [mats["41"]]} - - root = ET.Element('dagmc_universe') - daguniv.create_xml_subelement(root) - dagmc_ele = root.find('dagmc_universe') - - assert dagmc_ele.get('id') == str(daguniv.id) - assert dagmc_ele.get('filename') == str(daguniv.filename) - assert dagmc_ele.get('auto_geom_ids') == str(daguniv.auto_geom_ids).lower() - - # Verify the material overrides element in the XML tree - override_eles = dagmc_ele.find('material_overrides').findall('cell_override') - assert len(override_eles) == 2 - assert override_eles[0].get('id') == '40' - assert override_eles[0].find('material_ids').text == str(mats["no-void fuel"].id) - assert override_eles[1].get('id') == '52' - assert override_eles[1].find('material_ids').text == str(mats["41"].id) - - # Create a dictionary of materials to pass to from_xml_element indexing by - # material ID strings - dict_mats = {str(m.id): m for m in mats.values()} - dagmcuni_2 = openmc.DAGMCUniverse.from_xml_element(dagmc_ele, dict_mats) - assert daguniv.id == dagmcuni_2.id - assert daguniv.filename == dagmcuni_2.filename - assert daguniv.auto_geom_ids == dagmcuni_2.auto_geom_ids - assert daguniv._material_overrides == dagmcuni_2._material_overrides + assert np.isclose(cell.get_temperature(), exp_temp) \ No newline at end of file diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 34c1f254945..e41ea1f6431 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -1,5 +1,6 @@ import pkg_resources +import lxml.etree as ET import numpy as np import pytest @@ -148,9 +149,26 @@ def test_dagmc_xml(model, run_in_tmpdir): for k, v in mats.items(): if isinstance(k, int): dag_univ.add_material_override(k, v) + model.materials.append(v) elif isinstance(k, str): dag_univ.replace_material_assignment(k, v) + # Tesing the XML subelement generation + root = ET.Element('dagmc_universe') + dag_univ.create_xml_subelement(root) + dagmc_ele = root.find('dagmc_universe') + + assert dagmc_ele.get('id') == str(dag_univ.id) + assert dagmc_ele.get('filename') == str(dag_univ.filename) + assert dagmc_ele.get('auto_geom_ids') == str(dag_univ.auto_geom_ids).lower() + + override_eles = dagmc_ele.find('material_overrides').findall('cell_override') + assert len(override_eles) == 4 + + for i, override_ele in enumerate(override_eles): + cell_id = override_ele.get('id') + assert dag_univ.material_overrides[int(cell_id)][0].id == int(override_ele.find('material_ids').text) + model.export_to_model_xml() xml_model = openmc.Model.from_model_xml() From af175cd6acd06b912eaedf0afb875e0ef090698e Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Wed, 25 Dec 2024 13:17:20 +0100 Subject: [PATCH 119/122] Adding test for bad name for replacement, bad override type, override with cell, override with cell ID, and replacement with mat name --- openmc/dagmc.py | 4 +- tests/unit_tests/dagmc/test_model.py | 130 +++++++++++++++++++++++++-- 2 files changed, 127 insertions(+), 7 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index b6cfc722439..9ee23945308 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -186,8 +186,8 @@ def add_material_override(self, key, overrides=None): """ # Ensure that they key is a valid type if not isinstance(key, (int, openmc.DAGMCCell)): - raise ValueError("Unrecognized key type. \ - Must be a string, integer, or openmc.DAGMCCell object") + raise ValueError("Unrecognized key type. " + "Must be a integer, or openmc.DAGMCCell object") # Ensure that overrides is an iterable of openmc.Material overrides = overrides if isinstance(overrides, openmc.Iterable) else [overrides] diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index e41ea1f6431..af3472fcfb9 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -61,6 +61,94 @@ def model(): return model +def test_dagmc_replace_material_assignment(model, run_in_tmpdir): + mats = {} + + mats["foo"] = openmc.Material(name="foo") + mats["foo"].add_nuclide("H1", 2.0) + mats["foo"].add_element("O", 1.0) + mats["foo"].set_density("g/cm3", 1.0) + mats["foo"].add_s_alpha_beta("c_H_in_H2O") + + try: + model.init_lib() + model.sync_dagmc_universes() + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.replace_material_assignment("41", mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] + finally: + model.finalize_lib() + openmc.reset_auto_ids() + + +def test_dagmc_add_material_override_with_id(model, run_in_tmpdir): + mats = {} + mats["foo"] = openmc.Material(name="foo") + mats["foo"].add_nuclide("H1", 2.0) + mats["foo"].add_element("O", 1.0) + mats["foo"].set_density("g/cm3", 1.0) + mats["foo"].add_s_alpha_beta("c_H_in_H2O") + + try: + model.init_lib() + model.sync_dagmc_universes() + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.add_material_override(cell.id, mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] + finally: + model.finalize_lib() + openmc.reset_auto_ids() + + +def test_dagmc_add_material_override_with_cell(model, run_in_tmpdir): + mats = {} + mats["foo"] = openmc.Material(name="foo") + mats["foo"].add_nuclide("H1", 2.0) + mats["foo"].add_element("O", 1.0) + mats["foo"].set_density("g/cm3", 1.0) + mats["foo"].add_s_alpha_beta("c_H_in_H2O") + + try: + model.init_lib() + model.sync_dagmc_universes() + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.add_material_override(cell, mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] + finally: + model.finalize_lib() + openmc.reset_auto_ids() + + def test_model_differentiate_depletable_with_dagmc(model, run_in_tmpdir): try: model.init_lib() @@ -78,6 +166,7 @@ def test_model_differentiate_depletable_with_dagmc(model, run_in_tmpdir): assert len(model.materials) == 4*2 +1 finally: model.finalize_lib() + openmc.reset_auto_ids() def test_model_differentiate_with_dagmc(model, run_in_tmpdir): @@ -105,6 +194,7 @@ def test_model_differentiate_with_dagmc(model, run_in_tmpdir): assert len(model.materials) == 4*2 + 4 finally: model.finalize_lib() + openmc.reset_auto_ids() def test_bad_override_cell_id(model, run_in_tmpdir): @@ -118,6 +208,36 @@ def test_bad_override_cell_id(model, run_in_tmpdir): univ.material_overrides = {1 : model.materials[0]} finally: model.finalize_lib() + openmc.reset_auto_ids() + + +def test_bad_override_type(model, run_in_tmpdir): + not_a_dag_cell = openmc.Cell() + try: + model.init_lib() + model.sync_dagmc_universes() + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="Unrecognized key type. Must be a integer, or openmc.DAGMCCell object"): + univ.material_overrides = {not_a_dag_cell : model.materials[0]} + finally: + model.finalize_lib() + openmc.reset_auto_ids() + + +def test_bad_replacement_mat_name(model, run_in_tmpdir): + try: + model.init_lib() + model.sync_dagmc_universes() + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="No material with name 'not_a_mat' found in the DAGMC universe"): + univ.replace_material_assignment("not_a_mat", model.materials[0]) + finally: + model.finalize_lib() + openmc.reset_auto_ids() def test_dagmc_xml(model, run_in_tmpdir): @@ -129,11 +249,11 @@ def test_dagmc_xml(model, run_in_tmpdir): mats["no-void fuel"].add_nuclide("O16", 2.0) mats["no-void fuel"].set_density("g/cm3", 10.0) - mats[23] = openmc.Material(name="41") - mats[23].add_nuclide("H1", 2.0) - mats[23].add_element("O", 1.0) - mats[23].set_density("g/cm3", 1.0) - mats[23].add_s_alpha_beta("c_H_in_H2O") + mats[5] = openmc.Material(name="41") + mats[5].add_nuclide("H1", 2.0) + mats[5].add_element("O", 1.0) + mats[5].set_density("g/cm3", 1.0) + mats[5].add_s_alpha_beta("c_H_in_H2O") try: model.init_lib() From 120a1ded654d41d2065b063d021518be56059ffa Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 30 Dec 2024 15:15:00 -0600 Subject: [PATCH 120/122] Simplifying tests. Assigning materials directly to cells and adding messaging corresponding to material overrides --- docs/source/io_formats/geometry.rst | 2 +- openmc/dagmc.py | 6 +- openmc/model/model.py | 12 +- src/dagmc.cpp | 20 ++- tests/unit_tests/dagmc/test_model.py | 223 +++++++++++---------------- 5 files changed, 114 insertions(+), 149 deletions(-) diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index 68474c6302b..6d0a37a24fa 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -409,7 +409,7 @@ Each ```` element can have the following attributes or sub-eleme :material_overrides: This element contains information on material overrides to be applied to the - DAGMC universe. It has the following sub-elements: + DAGMC universe. It has the following attributes and sub-elements: :cell: Material override information for a single cell. It contains the following diff --git a/openmc/dagmc.py b/openmc/dagmc.py index 9ee23945308..af647bbcd5a 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -442,7 +442,7 @@ def from_hdf5(cls, group): return out @classmethod - def from_xml_element(cls, elem, mats): + def from_xml_element(cls, elem, mats = None): """Generate DAGMC universe from XML element Parameters @@ -473,6 +473,10 @@ def from_xml_element(cls, elem, mats): el_mat_override = elem.find('material_overrides') if el_mat_override is not None: + if mats is None: + raise ValueError("Material overrides found in DAGMC universe " + "but no materials were provided to populate " + "the mapping.") out._material_overrides = {} for elem in el_mat_override.findall('cell_override'): cell_id = int(get_text(elem, 'id')) diff --git a/openmc/model/model.py b/openmc/model/model.py index dcc3c8d4afb..8d855cc089a 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1183,19 +1183,17 @@ def differentiate_depletable_mats(self, diff_volume_method : str = None): .. versionadded:: 0.14.0 .. version added:: 0.15.1-dev - diff_volume_method default is None, do not apply volume to the new - materials. Is now a convenience method for + diff_volume_method default is None, do not set volumes on the new + material ovjects. Is now a convenience method for differentiate_mats(diff_volume_method, depletable_only=True) Parameters ---------- diff_volume_method : str Specifies how the volumes of the new materials should be found. - Default is 'None', do not apply volume to the new materials, - 'divide equally' which divides the original material - volume equally between the new materials, - 'match cell' sets the volume of the material to volume of the cell - they fill. + - None: Do not assign volumes to the new materials (Default) + - 'divide_equally': Divide the original material volume equally between the new materials + - 'match cell': Set the volume of the material to the volume of the cell they fill """ self.differentiate_mats(diff_volume_method, depletable_only=True) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 7d6aac9c24c..cb2cf3c8dae 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -232,11 +232,11 @@ void DAGUniverse::init_geometry() if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { - if (material_overrides_.count(c->id_)) { // Check for material override + if (material_overrides_.count(c->id_)) { override_assign_material(c); - } else if (uses_uwuw()) { // UWUW assignement + } else if (uses_uwuw()) { uwuw_assign_material(vol_handle, c); - } else { // legacy assignement + } else { legacy_assign_material(mat_str, c); } } @@ -636,10 +636,20 @@ void DAGUniverse::override_assign_material(std::unique_ptr& c) const // Notify User that an override is being applied on a DAGMCCell write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8); + if (settings::verbosity >= 10) { + auto msg = fmt::format("Assigning DAGMC cell {} material(s) based on override information (see input XML).", + c->id_); + write_message(msg, 10); + } + // Override the material assignment for each cell instance using the legacy // assignement - for (auto mat_str_instance : material_overrides_.at(c->id_)) { - this->legacy_assign_material(std::to_string(mat_str_instance), c); + for (auto mat_id : material_overrides_.at(c->id_)) { + if (model::material_map.find(mat_id) == model::material_map.end()) { + fatal_error(fmt::format("Material with ID '{}' not found for DAGMC cell {}", + mat_id, c->id_)); + } + c->material_.push_back(mat_id); } } diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index af3472fcfb9..88715b338cd 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -58,7 +58,14 @@ def model(): model.materials = openmc.Materials(mats.values()) model.geometry = openmc.Geometry(root=root) model.settings = settings - return model + + try: + model.init_lib() + model.sync_dagmc_universes() + yield model + finally: + model.finalize_lib() + openmc.reset_auto_ids() def test_dagmc_replace_material_assignment(model, run_in_tmpdir): @@ -70,25 +77,19 @@ def test_dagmc_replace_material_assignment(model, run_in_tmpdir): mats["foo"].set_density("g/cm3", 1.0) mats["foo"].add_s_alpha_beta("c_H_in_H2O") - try: - model.init_lib() - model.sync_dagmc_universes() - for univ in model.geometry.get_all_universes().values(): - if not isinstance(univ, openmc.DAGMCUniverse): - break - - cells_with_41 = [] - for cell in univ.cells.values(): - if cell.fill is None: - continue - if cell.fill.name == "41": - cells_with_41.append(cell.id) - univ.replace_material_assignment("41", mats["foo"]) - for cell_id in cells_with_41: - assert univ.cells[cell_id] == mats["foo"] - finally: - model.finalize_lib() - openmc.reset_auto_ids() + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.replace_material_assignment("41", mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] def test_dagmc_add_material_override_with_id(model, run_in_tmpdir): @@ -99,25 +100,19 @@ def test_dagmc_add_material_override_with_id(model, run_in_tmpdir): mats["foo"].set_density("g/cm3", 1.0) mats["foo"].add_s_alpha_beta("c_H_in_H2O") - try: - model.init_lib() - model.sync_dagmc_universes() - for univ in model.geometry.get_all_universes().values(): - if not isinstance(univ, openmc.DAGMCUniverse): - break - - cells_with_41 = [] - for cell in univ.cells.values(): - if cell.fill is None: - continue - if cell.fill.name == "41": - cells_with_41.append(cell.id) - univ.add_material_override(cell.id, mats["foo"]) - for cell_id in cells_with_41: - assert univ.cells[cell_id] == mats["foo"] - finally: - model.finalize_lib() - openmc.reset_auto_ids() + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.add_material_override(cell.id, mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] def test_dagmc_add_material_override_with_cell(model, run_in_tmpdir): @@ -128,116 +123,80 @@ def test_dagmc_add_material_override_with_cell(model, run_in_tmpdir): mats["foo"].set_density("g/cm3", 1.0) mats["foo"].add_s_alpha_beta("c_H_in_H2O") - try: - model.init_lib() - model.sync_dagmc_universes() - for univ in model.geometry.get_all_universes().values(): - if not isinstance(univ, openmc.DAGMCUniverse): - break - - cells_with_41 = [] - for cell in univ.cells.values(): - if cell.fill is None: - continue - if cell.fill.name == "41": - cells_with_41.append(cell.id) - univ.add_material_override(cell, mats["foo"]) - for cell_id in cells_with_41: - assert univ.cells[cell_id] == mats["foo"] - finally: - model.finalize_lib() - openmc.reset_auto_ids() + for univ in model.geometry.get_all_universes().values(): + if not isinstance(univ, openmc.DAGMCUniverse): + break + + cells_with_41 = [] + for cell in univ.cells.values(): + if cell.fill is None: + continue + if cell.fill.name == "41": + cells_with_41.append(cell.id) + univ.add_material_override(cell, mats["foo"]) + for cell_id in cells_with_41: + assert univ.cells[cell_id] == mats["foo"] def test_model_differentiate_depletable_with_dagmc(model, run_in_tmpdir): - try: - model.init_lib() - model.sync_dagmc_universes() - model.calculate_volumes() + model.calculate_volumes() - # Get the volume of the no-void fuel material before differentiation - volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + # Get the volume of the no-void fuel material before differentiation + volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) - # Differentiate the depletable materials - model.differentiate_depletable_mats(diff_volume_method="divide equally") - # Get the volume of the no-void fuel material after differentiation - volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) - assert np.isclose(volume_before, volume_after) - assert len(model.materials) == 4*2 +1 - finally: - model.finalize_lib() - openmc.reset_auto_ids() + # Differentiate the depletable materials + model.differentiate_depletable_mats(diff_volume_method="divide equally") + # Get the volume of the no-void fuel material after differentiation + volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) + assert np.isclose(volume_before, volume_after) + assert len(model.materials) == 4*2 +1 def test_model_differentiate_with_dagmc(model, run_in_tmpdir): root = model.geometry.root_universe ll, ur = root.bounding_box - try: - model.init_lib() - model.sync_dagmc_universes() - model.calculate_volumes() - # Get the volume of the no-void fuel material before differentiation - volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) - - # Differentiate all the materials - model.differentiate_mats(depletable_only=False) - - # Get the volume of the no-void fuel material after differentiation - mat_list = [m for m in model.materials] - mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) - cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) - model.settings.volume_calculations = [mat_vol, cell_vol] - model.init_lib() # need to reinitialize the lib after differentiating the materials - model.calculate_volumes() - volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) - assert np.isclose(volume_before, volume_after) - assert len(model.materials) == 4*2 + 4 - finally: - model.finalize_lib() - openmc.reset_auto_ids() + model.calculate_volumes() + # Get the volume of the no-void fuel material before differentiation + volume_before = np.sum([m.volume for m in model.materials if m.name == "no-void fuel"]) + + # Differentiate all the materials + model.differentiate_mats(depletable_only=False) + + # Get the volume of the no-void fuel material after differentiation + mat_list = [m for m in model.materials] + mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) + cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) + model.settings.volume_calculations = [mat_vol, cell_vol] + model.init_lib() # need to reinitialize the lib after differentiating the materials + model.calculate_volumes() + volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) + assert np.isclose(volume_before, volume_after) + assert len(model.materials) == 4*2 + 4 def test_bad_override_cell_id(model, run_in_tmpdir): - try: - model.init_lib() - model.sync_dagmc_universes() - for univ in model.geometry.get_all_universes().values(): - if isinstance(univ, openmc.DAGMCUniverse): - break - with pytest.raises(ValueError, match="Cell ID '1' not found in DAGMC universe"): - univ.material_overrides = {1 : model.materials[0]} - finally: - model.finalize_lib() - openmc.reset_auto_ids() + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="Cell ID '1' not found in DAGMC universe"): + univ.material_overrides = {1 : model.materials[0]} def test_bad_override_type(model, run_in_tmpdir): not_a_dag_cell = openmc.Cell() - try: - model.init_lib() - model.sync_dagmc_universes() - for univ in model.geometry.get_all_universes().values(): - if isinstance(univ, openmc.DAGMCUniverse): - break - with pytest.raises(ValueError, match="Unrecognized key type. Must be a integer, or openmc.DAGMCCell object"): - univ.material_overrides = {not_a_dag_cell : model.materials[0]} - finally: - model.finalize_lib() - openmc.reset_auto_ids() + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="Unrecognized key type. Must be a integer, or openmc.DAGMCCell object"): + univ.material_overrides = {not_a_dag_cell : model.materials[0]} def test_bad_replacement_mat_name(model, run_in_tmpdir): - try: - model.init_lib() - model.sync_dagmc_universes() - for univ in model.geometry.get_all_universes().values(): - if isinstance(univ, openmc.DAGMCUniverse): - break - with pytest.raises(ValueError, match="No material with name 'not_a_mat' found in the DAGMC universe"): - univ.replace_material_assignment("not_a_mat", model.materials[0]) - finally: - model.finalize_lib() - openmc.reset_auto_ids() + for univ in model.geometry.get_all_universes().values(): + if isinstance(univ, openmc.DAGMCUniverse): + break + with pytest.raises(ValueError, match="No material with name 'not_a_mat' found in the DAGMC universe"): + univ.replace_material_assignment("not_a_mat", model.materials[0]) def test_dagmc_xml(model, run_in_tmpdir): @@ -255,12 +214,6 @@ def test_dagmc_xml(model, run_in_tmpdir): mats[5].set_density("g/cm3", 1.0) mats[5].add_s_alpha_beta("c_H_in_H2O") - try: - model.init_lib() - model.sync_dagmc_universes() - finally: - model.finalize_lib() - for univ in model.geometry.get_all_universes().values(): if isinstance(univ, openmc.DAGMCUniverse): dag_univ = univ From 08fa0a999e3603fe338b5b76a2e35582fcd7f3e0 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Mon, 30 Dec 2024 15:22:21 -0600 Subject: [PATCH 121/122] C++ style --- src/dagmc.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dagmc.cpp b/src/dagmc.cpp index cb2cf3c8dae..134e31ecf3c 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -637,7 +637,8 @@ void DAGUniverse::override_assign_material(std::unique_ptr& c) const write_message(fmt::format("Applying override for DAGMCCell {}", c->id_), 8); if (settings::verbosity >= 10) { - auto msg = fmt::format("Assigning DAGMC cell {} material(s) based on override information (see input XML).", + auto msg = fmt::format("Assigning DAGMC cell {} material(s) based on " + "override information (see input XML).", c->id_); write_message(msg, 10); } @@ -646,8 +647,8 @@ void DAGUniverse::override_assign_material(std::unique_ptr& c) const // assignement for (auto mat_id : material_overrides_.at(c->id_)) { if (model::material_map.find(mat_id) == model::material_map.end()) { - fatal_error(fmt::format("Material with ID '{}' not found for DAGMC cell {}", - mat_id, c->id_)); + fatal_error(fmt::format( + "Material with ID '{}' not found for DAGMC cell {}", mat_id, c->id_)); } c->material_.push_back(mat_id); } From 636160e69b984a52d5d6185d0d8f89d99beb1459 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 7 Jan 2025 13:49:40 -0600 Subject: [PATCH 122/122] Updates throughout dagmc.py and tests --- openmc/dagmc.py | 126 ++++++++++++--------------- openmc/model/model.py | 15 ++-- openmc/universe.py | 10 +-- tests/unit_tests/dagmc/test.py | 4 +- tests/unit_tests/dagmc/test_model.py | 71 ++++++++------- 5 files changed, 100 insertions(+), 126 deletions(-) diff --git a/openmc/dagmc.py b/openmc/dagmc.py index af647bbcd5a..8ab0aaf69e7 100644 --- a/openmc/dagmc.py +++ b/openmc/dagmc.py @@ -1,6 +1,5 @@ from collections.abc import Iterable, Mapping from numbers import Integral -from pathlib import Path import h5py import lxml.etree as ET @@ -21,9 +20,6 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.13.0 - .. versionadded:: 0.15.1-dev - Moved this classe from openmc.universe to openmc.dagmc - Parameters ---------- filename : str @@ -39,11 +35,11 @@ class DAGMCUniverse(openmc.UniverseBase): auto_mat_ids : bool Set IDs automatically on initialization (True) or report overlaps in ID space between OpenMC and UWUW materials (False) - material_overrides : dict - A dictionary of material overrides. The keys are material name - strings and the values are Iterables of openmc.Material objects. If a - material name is found in the DAGMC file, the material will be replaced - with the openmc.Material object in the value. + material_overrides : dict, optional + A dictionary of material overrides. The keys are material name strings + and the values are Iterables of openmc.Material objects. If a material + name is found in the DAGMC file, the material will be replaced with the + openmc.Material object in the value. Attributes ---------- @@ -66,9 +62,9 @@ class DAGMCUniverse(openmc.UniverseBase): .. versionadded:: 0.13.1 material_names : list of str Return a sorted list of materials names that are contained within the - DAGMC h5m file. This is useful when naming openmc.Material() objects - as each material name present in the DAGMC h5m file must have a - matching openmc.Material() with the same name. + DAGMC h5m file. This is useful when naming openmc.Material() objects as + each material name present in the DAGMC h5m file must have a matching + openmc.Material() with the same name. .. versionadded:: 0.13.2 n_cells : int @@ -80,13 +76,16 @@ class DAGMCUniverse(openmc.UniverseBase): n_surfaces : int The number of surfaces in the model. - .. versionadded:: 0.15 + .. versionadded:: 0.13.2 material_overrides : dict - A dictionary of material overrides. Keys are Cell IDs; values - are Iterables of openmc.Material objects. The material assignment of - each DAGMC Cell id key will be replaced with the openmc.Material object - in the value. If the value contains multiple openmc.Material objects, each - Material in the list be assigned to the corresponding instance of the Cell. + A dictionary of material overrides. Keys are cell IDs; values are + iterables of :class:`openmc.Material` objects. The material assignment + of each DAGMC cell ID key will be replaced with the + :class:`~openmc.Material` object in the value. If the value contains + multiple :class:`~openmc.Material` objects, each Material in the list + will be assigned to the corresponding instance of the cell. + + .. versionadded:: 0.15.1 """ def __init__(self, @@ -95,13 +94,15 @@ def __init__(self, name='', auto_geom_ids=False, auto_mat_ids=False, - mat_overrides=None): + material_overrides=None): super().__init__(universe_id, name) # Initialize class attributes self.filename = filename self.auto_geom_ids = auto_geom_ids self.auto_mat_ids = auto_mat_ids - self._material_overrides = {} if mat_overrides is None else mat_overrides + self._material_overrides = {} + if material_overrides is not None: + self.material_overrides = material_overrides def __repr__(self): string = super().__repr__() @@ -132,21 +133,19 @@ def material_overrides(self): @material_overrides.setter def material_overrides(self, val): - if val is None: - self._material_overrides = val - return - else: - cv.check_type('material overrides', val, Mapping) - for key, value in val.items(): - self.add_material_override(key, value) + cv.check_type('material overrides', val, Mapping) + for key, value in val.items(): + self.add_material_override(key, value) - def replace_material_assignment(self, material_name, material): - """Replace the material assignment of all cells filled with a material - in the DAGMC universe. The universe must be synchronized in an - initialized Model (see :meth:`openmc.DAGMCUniverse.sync_dagmc_cells`) - before calling this method. + def replace_material_assignment(self, material_name: str, material: openmc.Material): + """Replace a material assignment within the DAGMC universe. - .. versionadded:: 0.15 + Replace the material assignment of all cells filled with a material in + the DAGMC universe. The universe must be synchronized in an initialized + Model (see :meth:`~openmc.DAGMCUniverse.sync_dagmc_cells`) before + calling this method. + + .. versionadded:: 0.15.1 Parameters ---------- @@ -161,7 +160,8 @@ def replace_material_assignment(self, material_name, material): f"No material with name '{material_name}' found in the DAGMC universe") if not self.cells: - raise RuntimeError("This DAGMC universe has not been synchronized in an initialized Model.") + raise RuntimeError("This DAGMC universe has not been synchronized " + "on an initialized Model.") for cell in self.cells.values(): if cell.fill is None: @@ -187,7 +187,7 @@ def add_material_override(self, key, overrides=None): # Ensure that they key is a valid type if not isinstance(key, (int, openmc.DAGMCCell)): raise ValueError("Unrecognized key type. " - "Must be a integer, or openmc.DAGMCCell object") + "Must be an integer or openmc.DAGMCCell object") # Ensure that overrides is an iterable of openmc.Material overrides = overrides if isinstance(overrides, openmc.Iterable) else [overrides] @@ -198,8 +198,7 @@ def add_material_override(self, key, overrides=None): key = key.id if key not in self.cells: - raise ValueError( - f"Cell ID '{key}' not found in DAGMC universe") + raise ValueError(f"Cell ID '{key}' not found in DAGMC universe") self._material_overrides[key] = overrides @@ -534,10 +533,15 @@ def remove_cell(self, cell): # If the Cell is in the Universe's list of Cells, delete it self._cells.pop(cell.id, None) - def sync_dagmc_cells(self, mats): + def sync_dagmc_cells(self, mats: Iterable[openmc.Material]): """Synchronize DAGMC cell information between Python and C API - .. versionadded:: 0.15.1-dev + .. versionadded:: 0.15.1 + + Parameters + ---------- + mats : iterable of openmc.Material + Iterable of materials to assign to the DAGMC cells """ import openmc.lib @@ -557,23 +561,16 @@ def sync_dagmc_cells(self, mats): for dag_cell_id in dagmc_cell_ids: dag_cell = openmc.lib.cells[dag_cell_id] if isinstance(dag_cell.fill, Iterable): - fill = [mats_per_id[mat.id] - for mat in dag_cell.fill if mat] + fill = [mats_per_id[mat.id] for mat in dag_cell.fill if mat] else: - if dag_cell.fill: - fill = mats_per_id[dag_cell.fill.id] - else: - fill = None - dag_pseudo_cell = openmc.DAGMCCell( - cell_id=dag_cell_id, fill=fill - ) - self.add_cell(dag_pseudo_cell) + fill = mats_per_id[dag_cell.fill.id] if dag_cell.fill else None + self.add_cell(openmc.DAGMCCell(cell_id=dag_cell_id, fill=fill)) class DAGMCCell(openmc.Cell): - """ - .. versionadded:: 0.15.1-dev - A cell class for DAGMC-based geometries. + """A cell class for DAGMC-based geometries. + + .. versionadded:: 0.15.1 Parameters ---------- @@ -604,38 +601,25 @@ def DAG_parent_universe(self, universe): """Set the parent universe of the cell.""" self._parent_universe = universe.id - def boundingbox(self): - warnings.warn("Bounding box is not available for cells in a DAGMC " - "universe", Warning) + def bounding_box(self): return BoundingBox.infinite() def get_all_cells(self, memo=None): - warnings.warn("get_all_cells is not available for cells in a DAGMC " - "universe", Warning) return {} def get_all_universes(self, memo=None): - warnings.warn("get_all_universes is not available for cells in a " - "DAGMC universe", Warning) return {} def clone(self, clone_materials=True, clone_regions=True, memo=None): - warnings.warn("clone is not available for cells in a DAGMC universe", - Warning) - return None + warnings.warn("clone is not available for cells in a DAGMC universe") + return self def plot(self, *args, **kwargs): - warnings.warn("plot is not available for cells in a DAGMC universe", - Warning) - return None + raise TypeError("plot is not available for DAGMC cells.") def create_xml_subelement(self, xml_element, memo=None): - warnings.warn("create_xml_subelement is not available for cells in a " - "DAGMC universe", Warning) - return None + raise TypeError("create_xml_subelement is not available for DAGMC cells.") @classmethod def from_xml_element(cls, elem, surfaces, materials, get_universe): - warnings.warn("from_xml_element is not available for cells in a DAGMC " - "universe", Warning) - return None + raise TypeError("from_xml_element is not available for DAGMC cells.") diff --git a/openmc/model/model.py b/openmc/model/model.py index 8d855cc089a..5137ea1abfc 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,7 +1,6 @@ from __future__ import annotations from collections.abc import Iterable from functools import lru_cache -import os from pathlib import Path from numbers import Integral from tempfile import NamedTemporaryFile @@ -15,7 +14,7 @@ import openmc._xml as xml from openmc.dummy_comm import DummyCommunicator from openmc.executor import _process_CLI_arguments -from openmc.checkvalue import check_type, check_value, PathLike +from openmc.checkvalue import check_type, check_value from openmc.exceptions import InvalidIDError import openmc.lib from openmc.utility_funcs import change_directory @@ -326,13 +325,13 @@ def init_lib(self, threads=None, geometry_debug=False, restart_file=None, openmc.lib.init(args=args, intracomm=intracomm, output=output) def sync_dagmc_universes(self): - """ - Synchronize all DAGMC universes in the current geometry. + """Synchronize all DAGMC universes in the current geometry. + This method iterates over all DAGMC universes in the geometry and synchronizes their cells with the current material assignments. Requires that the model has been initialized via :meth:`Model.init_lib`. - .. versionadded:: 0.15.1-dev + .. versionadded:: 0.15.1 """ if self.is_initialized: @@ -1177,12 +1176,12 @@ def update_material_volumes(self, names_or_ids, volume): self._change_py_lib_attribs(names_or_ids, volume, 'material', 'volume') - def differentiate_depletable_mats(self, diff_volume_method : str = None): + def differentiate_depletable_mats(self, diff_volume_method: str = None): """Assign distribmats for each depletable material .. versionadded:: 0.14.0 - .. version added:: 0.15.1-dev + .. versionchanged:: 0.15.1 diff_volume_method default is None, do not set volumes on the new material ovjects. Is now a convenience method for differentiate_mats(diff_volume_method, depletable_only=True) @@ -1200,7 +1199,7 @@ def differentiate_depletable_mats(self, diff_volume_method : str = None): def differentiate_mats(self, diff_volume_method: str = None, depletable_only: bool = True): """Assign distribmats for each material - .. versionadded:: 0.15.1-dev + .. versionadded:: 0.15.1 Parameters ---------- diff --git a/openmc/universe.py b/openmc/universe.py index 5409222229d..85ce6fd9656 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -2,22 +2,16 @@ import math from abc import ABC, abstractmethod from collections.abc import Iterable -from numbers import Integral, Real +from numbers import Real from pathlib import Path from tempfile import TemporaryDirectory import warnings -import h5py -import lxml.etree as ET import numpy as np import openmc import openmc.checkvalue as cv -from ._xml import get_text -from .checkvalue import check_type, check_value from .mixin import IDManagerMixin -from .surface import _BOUNDARY_TYPES -from .utility_funcs import input_path class UniverseBase(ABC, IDManagerMixin): @@ -207,7 +201,7 @@ def add_cell(self, cell): @abstractmethod def remove_cell(self, cell): pass - + def clear_cells(self): """Remove all cells from the universe.""" diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index 88aa25b0799..e84b5317ede 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -1,7 +1,5 @@ -import pkg_resources import shutil -import lxml.etree as ET import numpy as np from pathlib import Path import pytest @@ -85,4 +83,4 @@ def dagmc_model(request): (3, 293.6))) # assigned by default def test_dagmc_temperatures(cell_id, exp_temp): cell = openmc.lib.cells[cell_id] - assert np.isclose(cell.get_temperature(), exp_temp) \ No newline at end of file + assert np.isclose(cell.get_temperature(), exp_temp) diff --git a/tests/unit_tests/dagmc/test_model.py b/tests/unit_tests/dagmc/test_model.py index 88715b338cd..9fdfbcebc68 100644 --- a/tests/unit_tests/dagmc/test_model.py +++ b/tests/unit_tests/dagmc/test_model.py @@ -1,18 +1,19 @@ -import pkg_resources +from pathlib import Path import lxml.etree as ET import numpy as np import pytest - import openmc +from openmc.utility_funcs import change_directory pytestmark = pytest.mark.skipif( not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") + @pytest.fixture() -def model(): - PITCH = 1.26 +def model(request): + pitch = 1.26 mats = {} mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") @@ -27,21 +28,21 @@ def model(): mats["41"].set_density("g/cm3", 1.0) mats["41"].add_s_alpha_beta("c_H_in_H2O") - p = pkg_resources.resource_filename(__name__, "dagmc.h5m") + p = Path(request.fspath).parent / "dagmc.h5m" - daguniv = openmc.DAGMCUniverse(p,auto_geom_ids=True,) + daguniv = openmc.DAGMCUniverse(p, auto_geom_ids=True) lattice = openmc.RectLattice() lattice.dimension = [2, 2] - lattice.lower_left = [-PITCH, -PITCH] - lattice.pitch = [PITCH, PITCH] + lattice.lower_left = [-pitch, -pitch] + lattice.pitch = [pitch, pitch] lattice.universes = [ [daguniv, daguniv], [daguniv, daguniv]] - box = openmc.model.RectangularParallelepiped(-PITCH, PITCH, -PITCH, PITCH, -5, 5) + box = openmc.model.RectangularParallelepiped(-pitch, pitch, -pitch, pitch, -5, 5) - root = openmc.Universe(cells=[openmc.Cell(region= -box, fill=lattice)]) + root = openmc.Universe(cells=[openmc.Cell(region=-box, fill=lattice)]) settings = openmc.Settings() settings.batches = 100 @@ -50,8 +51,7 @@ def model(): ll, ur = root.bounding_box mat_vol = openmc.VolumeCalculation([mats["no-void fuel"]], 1000000, ll, ur) - cell_vol = openmc.VolumeCalculation( - list(root.cells.values()), 1000000, ll, ur) + cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) settings.volume_calculations = [mat_vol, cell_vol] model = openmc.Model() @@ -59,16 +59,17 @@ def model(): model.geometry = openmc.Geometry(root=root) model.settings = settings - try: - model.init_lib() - model.sync_dagmc_universes() - yield model - finally: - model.finalize_lib() - openmc.reset_auto_ids() + with change_directory(tmpdir=True): + try: + model.init_lib() + model.sync_dagmc_universes() + yield model + finally: + model.finalize_lib() + openmc.reset_auto_ids() -def test_dagmc_replace_material_assignment(model, run_in_tmpdir): +def test_dagmc_replace_material_assignment(model): mats = {} mats["foo"] = openmc.Material(name="foo") @@ -92,7 +93,7 @@ def test_dagmc_replace_material_assignment(model, run_in_tmpdir): assert univ.cells[cell_id] == mats["foo"] -def test_dagmc_add_material_override_with_id(model, run_in_tmpdir): +def test_dagmc_add_material_override_with_id(model): mats = {} mats["foo"] = openmc.Material(name="foo") mats["foo"].add_nuclide("H1", 2.0) @@ -115,7 +116,7 @@ def test_dagmc_add_material_override_with_id(model, run_in_tmpdir): assert univ.cells[cell_id] == mats["foo"] -def test_dagmc_add_material_override_with_cell(model, run_in_tmpdir): +def test_dagmc_add_material_override_with_cell(model): mats = {} mats["foo"] = openmc.Material(name="foo") mats["foo"].add_nuclide("H1", 2.0) @@ -152,7 +153,7 @@ def test_model_differentiate_depletable_with_dagmc(model, run_in_tmpdir): assert len(model.materials) == 4*2 +1 -def test_model_differentiate_with_dagmc(model, run_in_tmpdir): +def test_model_differentiate_with_dagmc(model): root = model.geometry.root_universe ll, ur = root.bounding_box model.calculate_volumes() @@ -163,35 +164,33 @@ def test_model_differentiate_with_dagmc(model, run_in_tmpdir): model.differentiate_mats(depletable_only=False) # Get the volume of the no-void fuel material after differentiation - mat_list = [m for m in model.materials] - mat_vol = openmc.VolumeCalculation(mat_list, 1000000, ll, ur) - cell_vol = openmc.VolumeCalculation(list(root.cells.values()), 1000000, ll, ur) - model.settings.volume_calculations = [mat_vol, cell_vol] - model.init_lib() # need to reinitialize the lib after differentiating the materials + mat_vol = openmc.VolumeCalculation(model.materials, 1000000, ll, ur) + model.settings.volume_calculations = [mat_vol] + model.init_lib() # need to reinitialize the lib after differentiating the materials model.calculate_volumes() volume_after = np.sum([m.volume for m in model.materials if "fuel" in m.name]) assert np.isclose(volume_before, volume_after) assert len(model.materials) == 4*2 + 4 -def test_bad_override_cell_id(model, run_in_tmpdir): +def test_bad_override_cell_id(model): for univ in model.geometry.get_all_universes().values(): if isinstance(univ, openmc.DAGMCUniverse): break with pytest.raises(ValueError, match="Cell ID '1' not found in DAGMC universe"): - univ.material_overrides = {1 : model.materials[0]} + univ.material_overrides = {1: model.materials[0]} -def test_bad_override_type(model, run_in_tmpdir): +def test_bad_override_type(model): not_a_dag_cell = openmc.Cell() for univ in model.geometry.get_all_universes().values(): if isinstance(univ, openmc.DAGMCUniverse): break - with pytest.raises(ValueError, match="Unrecognized key type. Must be a integer, or openmc.DAGMCCell object"): - univ.material_overrides = {not_a_dag_cell : model.materials[0]} + with pytest.raises(ValueError, match="Unrecognized key type. Must be an integer or openmc.DAGMCCell object"): + univ.material_overrides = {not_a_dag_cell: model.materials[0]} -def test_bad_replacement_mat_name(model, run_in_tmpdir): +def test_bad_replacement_mat_name(model): for univ in model.geometry.get_all_universes().values(): if isinstance(univ, openmc.DAGMCUniverse): break @@ -199,7 +198,7 @@ def test_bad_replacement_mat_name(model, run_in_tmpdir): univ.replace_material_assignment("not_a_mat", model.materials[0]) -def test_dagmc_xml(model, run_in_tmpdir): +def test_dagmc_xml(model): # Set the environment mats = {} mats["no-void fuel"] = openmc.Material(1, name="no-void fuel") @@ -254,4 +253,4 @@ def test_dagmc_xml(model, run_in_tmpdir): assert xml_dagmc_univ._material_overrides.keys() == dag_univ._material_overrides.keys() for xml_mats, model_mats in zip(xml_dagmc_univ._material_overrides.values(), dag_univ._material_overrides.values()): - assert all([xml_mat.id == orig_mat.id for xml_mat, orig_mat in zip(xml_mats, model_mats)]) \ No newline at end of file + assert all([xml_mat.id == orig_mat.id for xml_mat, orig_mat in zip(xml_mats, model_mats)])