diff --git a/README.md b/README.md index 190db6c..fac6912 100644 --- a/README.md +++ b/README.md @@ -94,9 +94,10 @@ cad_to_h5m( ) ``` -Creating a EXODUS mesh files compatible with the DAGMC Unstructured mesh format -is also possible. Another entry must be added to the ```files_with_tags``` -argument. The following command will produce a ```unstructured_mesh_file.exo``` +Creating a tet mesh files compatible with the OpenMC / DAGMC Unstructured mesh +format is also possible. Another key called ```tet_mesh``` to the ```files_with_tags``` dictionary will tirgger the meshing of that CAD file. +The value of the key will be passed to the Cubit mesh command as an instruction. +The following command will produce a ```unstructured_mesh_file.exo``` file that can then be used in DAGMC compatable neutronics codes. There are examples [1](https://docs.openmc.org/en/latest/examples/unstructured-mesh-part-i.html) [2](https://docs.openmc.org/en/latest/examples/unstructured-mesh-part-ii.html) @@ -106,13 +107,70 @@ for the use of unstructured meshes in OpenMC. from cad_to_h5m import cad_to_h5m cad_to_h5m( - files_with_tags=[{'cad_filename':'part1.sat', 'material_tags':'m1', 'tet_mesh': 'size 0.5'}], + files_with_tags=[ + { + 'cad_filename':'part1.sat', + 'material_tags':'m1', + 'tet_mesh': 'size 0.5' + } + ], h5m_filename='dagmc.h5m', cubit_path='/opt/Coreform-Cubit-2021.5/bin/' exo_filename='unstructured_mesh_file.exo' ) ``` +Use if ```exo``` files requires OpenMC to be compiled with LibMesh. OpenMC also +accepts DAGMC tet meshes made with MOAB which is another option. The following +example creates a ```cub``` file that contains a mesh. The MOAB tool +```mbconvert``` is then used to extract the tet mesh and save it as a ```h5m``` +file which cna be used in OpenMC as shown in the OpenMC [examples](https://docs.openmc.org/en/stable/examples/unstructured-mesh-part-i.html) + +```python +from cad_to_h5m import cad_to_h5m + +cad_to_h5m( + files_with_tags=[ + { + 'cad_filename':'part1.sat', + 'material_tags':'m1', + 'tet_mesh': 'size 0.5' + } + ], + h5m_filename='dagmc.h5m', + cubit_path='/opt/Coreform-Cubit-2021.5/bin/' + cubit_filename='unstructured_mesh_file.cub' +) +``` +mbconvert is a terminal command that is part of MOAB. +```bash +mbconvert unstructured_mesh_file.cub unstructured_mesh_file.h5m +``` + +Scaling geometry is also possible. This is useful as particle transport codes +often make use of cm as the default unit. CAD files typically appear in mm as +the default limit. Some CAD packages ignore units while others make use of them. +The h5m files are assumed to be in cm by particle transport codes so often it +is nessecary to scale up or down the geometry. This can be done by adding +another key called ```scale``` and a value to the ```files_with_tags``` +dictionary. This example multiplies the geometry by 10. + +```python +from cad_to_h5m import cad_to_h5m + +cad_to_h5m( + files_with_tags=[ + { + 'cad_filename':'part1.sat', + 'material_tags':'m1', + 'scale': 10 + } + ], + h5m_filename='dagmc.h5m', +) +``` + + # Installation The package is available via the PyPi package manager and the recommended diff --git a/cad_to_h5m/core.py b/cad_to_h5m/core.py index 299d9a6..d5dc77a 100644 --- a/cad_to_h5m/core.py +++ b/cad_to_h5m/core.py @@ -9,6 +9,7 @@ class FilesWithTags(TypedDict, total=False): filename: str material_tag: str tet_mesh: str + scale: float def cad_to_h5m( @@ -22,7 +23,7 @@ def cad_to_h5m( imprint: bool = True, geometry_details_filename: Optional[str] = None, surface_reflectivity_name: str = "reflective", - exo_filename: str = "tet_mesh.exo", + exo_filename: Optional[str] = None, ): """Converts a CAD files in STP or SAT format into a h5m file for use in DAGMC simulations. The h5m file contains material tags associated with the @@ -35,10 +36,16 @@ def cad_to_h5m( "mat2", "cad_filename": "part2.stp"}]. There is also an option to create a tet mesh of entries by including a "tet_mesh" key in the dictionary. The value is passed to the Cubit mesh command. An example entry would be - "tet_mesh": "size 0.5" - h5m_filename: the file name of the output h5m file + "tet_mesh": "size 0.5". The scale key can also be included to scale up + or down the geometry so that it is in cm units as required by most + particle transport codes. And example entry would be "scale": 10 which + would make the geometry 10 times bigger. + h5m_filename: the file name of the output h5m file which is suitable for + use in DAGMC enabled particle transport codes. cubit_filename: the file name of the output cubit file. Should end with .cub - or .cub5 + or .cub5. Includes any tet meshes produced and therefore this output + can be useful for producing unstructured meshs for use in DAGMC + simulations. cubit_path: the path to the Cubit directory used to import Cubit from. On Ubuntu with Cubit 2021.5 this would be "/opt/Coreform-Cubit-2021.5/bin/" merge_tolerance: The merge tolerance to apply when merging surfaces into @@ -100,18 +107,21 @@ def cad_to_h5m( geometry_details, total_number_of_volumes = find_number_of_volumes_in_each_step_file( files_with_tags, cubit) print(geometry_details) + + scale_geometry(cubit, geometry_details) + tag_geometry_with_mats(geometry_details, cubit) if imprint and total_number_of_volumes > 1: imprint_geometry(cubit) if total_number_of_volumes > 1: merge_geometry(merge_tolerance, cubit) + + # TODO method requires further testing find_reflecting_surfaces_of_reflecting_wedge( geometry_details, surface_reflectivity_name, cubit ) - create_tet_mesh(geometry_details, exo_filename, cubit) - save_output_files( make_watertight, geometry_details, @@ -119,6 +129,7 @@ def cad_to_h5m( cubit_filename, geometry_details_filename, faceting_tolerance, + exo_filename, cubit, ) return h5m_filename @@ -129,19 +140,25 @@ def create_tet_mesh(geometry_details, exo_filename, cubit): cubit.cmd("volume all size auto factor 5") for entry in geometry_details: + print('entry=', entry) if "tet_mesh" in entry.keys(): for volume in entry["volumes"]: + print('volume=', volume) cubit.cmd( "volume " + str(volume) + " size auto factor 6" ) # this number is the size of the mesh 1 is small 10 is large - cubit.cmd( - "volume all scheme tetmesh proximity layers off geometric sizing on") + cubit.cmd("volume all scheme tetmesh proximity layers off") # example entry ' size 0.5' - cubit.cmd(f"volume {str(volume)} " + entry["tet_mesh"]) + cubit.cmd(f"volume {volume} " + entry["tet_mesh"]) cubit.cmd("mesh volume " + str(volume)) print('meshed some volumes') - cubit.cmd(f'export mesh "{exo_filename}" overwrite') + +def scale_geometry(cubit, geometry_details): + for entry in geometry_details: + if 'scale' in entry.keys(): + cubit.cmd( + f'volume {" ".join(entry["volumes"])} scale {entry["scale"]}') # def save_tet_details_to_json_file( @@ -164,6 +181,7 @@ def save_output_files( cubit_filename, geometry_details_filename, faceting_tolerance, + exo_filename, cubit, ): """This saves the output files""" @@ -173,9 +191,6 @@ def save_output_files( with open(geometry_details_filename, "w") as outfile: json.dump(geometry_details, outfile, indent=4) - if cubit_filename is not None: - cubit.cmd('save as "' + cubit_filename + '" overwrite') - Path(h5m_filename).parents[0].mkdir(parents=True, exist_ok=True) print("using faceting_tolerance of ", faceting_tolerance) @@ -194,6 +209,16 @@ def save_output_files( + '" faceting_tolerance ' + str(faceting_tolerance) ) + + create_tet_mesh(geometry_details, exo_filename, cubit) + + if exo_filename is not None: + Path(exo_filename).parents[0].mkdir(parents=True, exist_ok=True) + cubit.cmd(f'export mesh "{exo_filename}" overwrite') + + if cubit_filename is not None: + cubit.cmd('save as "' + cubit_filename + '" overwrite') + return h5m_filename @@ -226,6 +251,8 @@ def find_all_surfaces_of_reflecting_wedge(new_vols, cubit): print("surface_info_dict", surface_info_dict) return surface_info_dict +# todo additional testing required + def find_reflecting_surfaces_of_reflecting_wedge( geometry_details, surface_reflectivity_name, cubit diff --git a/tests/test_python_api.py b/tests/test_python_api.py index a7efc29..9bad5c6 100644 --- a/tests/test_python_api.py +++ b/tests/test_python_api.py @@ -19,6 +19,9 @@ def setUp(self): tar.extractall("tests") tar.close() + url = "https://raw.githubusercontent.com/fusion-energy/neutronics_workflow/2f65bdeb802f2b1b25da683d13dcd2b29ffc9ed3/example_05_3D_unstructured_mesh_tally/stage_1_output/steel.stp" + urllib.request.urlretrieve(url, "tests/steel.stp") + def test_h5m_file_creation(self): """Checks that a h5m file is created from stp files when make_watertight is set to false""" @@ -140,34 +143,36 @@ def test_faceting_tolerance_increases_file_size(self): def test_exo_file_creation_with_different_sizes(self): """Checks that a h5m file is created from stp files""" - os.system("rm umesh_3.exo") + os.system("rm umesh_2.exo") cad_to_h5m( files_with_tags=[ { - "cad_filename": "tests/fusion_example_for_openmc_using_paramak-0.0.1/stp_files/pf_coils.stp", + "cad_filename": "tests/steel.stp", "material_tag": "mat1", - "tet_mesh": "size 3"}], - exo_filename="umesh_3.exo", + "tet_mesh": "size 2" + }], + exo_filename="umesh_2.exo", ) - assert Path("umesh_3.exo").is_file() + assert Path("umesh_2.exo").is_file() - os.system("rm umesh_10.exo") + os.system("rm umesh_3.exo") cad_to_h5m( files_with_tags=[ { - "cad_filename": "tests/fusion_example_for_openmc_using_paramak-0.0.1/stp_files/pf_coils.stp", + "cad_filename": "tests/steel.stp", "material_tag": "mat1", - "tet_mesh": "size 10"}], - exo_filename="umesh_10.exo", + "tet_mesh": "size 3" + }], + exo_filename="umesh_3.exo", ) - assert Path("umesh_10.exo").is_file() + assert Path("umesh_3.exo").is_file() - assert (Path("umesh_10.exo").stat().st_size > - Path("umesh_3.exo").stat().st_size) + assert (Path("umesh_3.exo").stat().st_size > + Path("umesh_2.exo").stat().st_size) def test_exo_file_creation_with_default_size(self): """Checks that a h5m file is created from stp files""" @@ -226,3 +231,25 @@ def incorrect_suffix(): cubit_filename="output_file_with.not_correct_suffix", ) self.assertRaises(ValueError, incorrect_suffix) + + def test_h5m_file_creation_with_scaling(self): + """Checks that a h5m file is created from stp files when make_watertight + is set to false""" + + os.system("rm test_dagmc.h5m") + + test_h5m_filename = "test_dagmc.h5m" + + returned_filename = cad_to_h5m( + files_with_tags=[ + { + "cad_filename": "tests/fusion_example_for_openmc_using_paramak-0.0.1/stp_files/blanket.stp", + "material_tag": "mat1", + "scale": 0.1, + }], + h5m_filename=test_h5m_filename, + ) + + assert Path(test_h5m_filename).is_file() + assert Path(returned_filename).is_file() + assert test_h5m_filename == returned_filename