From 903d11454c100500619fd4b20e9b6e5bf23e0c83 Mon Sep 17 00:00:00 2001 From: Baptiste Mouginot Date: Tue, 11 Jun 2024 08:46:48 +0200 Subject: [PATCH 01/75] 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 02/75] 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 03/75] 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 04/75] 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 05/75] 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 06/75] 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 07/75] 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 08/75] 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 09/75] 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 10/75] 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 11/75] 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 12/75] 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 13/75] 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 14/75] 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 15/75] 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 16/75] 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 17/75] 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 18/75] 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 19/75] 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 20/75] 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 21/75] 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 22/75] 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 23/75] 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 24/75] 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 25/75] 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 26/75] 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 27/75] 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 28/75] 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 29/75] 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 30/75] 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 31/75] 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 32/75] 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 33/75] 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 34/75] 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 35/75] 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 36/75] 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 37/75] 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 38/75] 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 39/75] 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 40/75] 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 41/75] 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 42/75] 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 43/75] 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 44/75] 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 45/75] 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 46/75] 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 47/75] 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 48/75] 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 49/75] 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 50/75] 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 51/75] 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 52/75] 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 53/75] 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 54/75] 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 55/75] 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 56/75] 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 57/75] 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 58/75] 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 59/75] 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 60/75] 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 61/75] 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 62/75] 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 63/75] 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 64/75] 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 65/75] 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 66/75] 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 67/75] 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 68/75] 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 69/75] 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 70/75] 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 71/75] 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 72/75] 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 73/75] 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 74/75] 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 75/75] 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():