Skip to content

Commit

Permalink
Merge pull request #115 from fusion-energy/adding-mesh-sizes-for-each…
Browse files Browse the repository at this point in the history
…-volume

Adding option to specify different mesh sizes for each volume
  • Loading branch information
shimwell authored Jan 28, 2025
2 parents 1d34af2 + aa03a0b commit fd5c734
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 23 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci_with_conda_install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ jobs:
python examples/surface_mesh/single_stp_file.py
python examples/unstrucutred_volume_mesh/curved_cadquery_object_to_dagmc_volume_mesh.py
python examples/unstrucutred_volume_mesh/simulate_unstrucutred_volume_mesh_with_openmc.py
python examples/unstrucutred_volume_mesh/different_resolution_meshes.py
python examples/surface_mesh/different_resolution_meshes.py
python examples/surface_mesh/cadquery_assembly_with_scaled_geometry.py
2 changes: 2 additions & 0 deletions .github/workflows/ci_with_pip_install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ jobs:
python examples/surface_mesh/from_gmsh_mesh_file.py
python examples/unstrucutred_volume_mesh/curved_cadquery_object_to_dagmc_volume_mesh.py
python examples/unstrucutred_volume_mesh/simulate_unstrucutred_volume_mesh_with_openmc.py
python examples/unstrucutred_volume_mesh/different_resolution_meshes.py
python examples/surface_mesh/different_resolution_meshes.py
python examples/surface_mesh/cadquery_assembly_with_scaled_geometry.py
34 changes: 19 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,31 @@
[![PyPI](https://img.shields.io/pypi/v/cad_to_dagmc?color=brightgreen&label=pypi&logo=grebrightgreenen&logoColor=green)](https://pypi.org/project/cad_to_dagmc/)


A minimal package that converts CAD geometry to [DAGMC](https://github.com/svalinn/DAGMC/) compatible meshes
A minimal package that converts CAD geometry to [DAGMC](https://github.com/svalinn/DAGMC/) (h5m) files, [unstructured mesh](https://docs.openmc.org/en/latest/pythonapi/generated/openmc.UnstructuredMesh.html) files (vtk) and Gmsh (msh) files ready for use in neutronics simulations.

cad-to-dagmc can create DAGMC compatible:
- surface meshes / faceted geometry / triangular meshes
- unstructured mesh / tetrahedral meshes / volume meshes

cad-to-dagmc can convert the following in to DAGMC compatible meshes:
- STEP files
- CadQuery objects
- CadQuery objects (in memory)
- Gmsh meshes

Options include
- Ability to scale the output mesh (e.g from meters to cm)
- Ability to skip imprinting stage
- Specify meshing parameters for finer or coarser meshes or change the mesh algorithm
- Ability to specify a material tag for the DAGMC implicit complement
- Pass objects from CadQuery in memory (without writting to disk)
Cad-to-dagmc offers a wide range of features including.
- Geometry scaling with ```scale_factor``` argument
- Model wide mesh size parameters with ```min_mesh_size``` and ```max_mesh_size``` arguments
- Volume specific mesh sizing parameters with the ```set_size``` argument
- Parallel meshing to quickly mesh the geometry using multiple CPU cores
- Imprint and merging of CAD geometry, or disable with the ```imprint``` argument
- Add geometry from multiple sources ([STEP](http://www.steptools.com/stds/step/) files, [CadQuery](https://cadquery.readthedocs.io) objects and [Gmsh](https://gmsh.info/) meshes)
- Ability to tag the DAGMC implicit complement material using the ```implicit_complement_material_tag``` argument
- Selected different Gmesh mesh algorithms (defaults to 1) using the ```mesh_algorithm``` argument
- Pass CadQuery objects in memory for fast transfer of geometry using the ```method``` argument
- Easy to install with [pip](https://pypi.org/project/cad-to-dagmc/) and [Conda/Mamba](https://anaconda.org/conda-forge/cad_to_dagmc)
- Well tested both with [CI unit tests](https://github.com/fusion-energy/cad_to_dagmc/tree/main/tests), integration tests and the CSG [Model Benchmark Zoo](https://github.com/fusion-energy/model_benchmark_zoo).
- Compatible with [Paramak](https://github.com/fusion-energy/paramak) geometry for fusion simulations.

cad-to-dagmc aims to produce DAGMC compatible h5m and vtk files from CAD geometry is intended to convert [STEP](http://www.steptools.com/stds/step/) files, [CadQuery](https://cadquery.readthedocs.io) or [Gmsh]([https://cadquery.readthedocs.io](https://gmsh.info/)) meshes objects to a [DAGMC](https://github.com/svalinn/DAGMC/) compatible h5m file or a DAGMC compatible Unstruuctre vtk file.

The resulting DAGMC geometry can then be used for simulations in [OpenMC](https://github.com/openmc-dev/openmc/) or [other supported codes](https://svalinn.github.io/DAGMC/).

This package is tested with [pytest tests](https://github.com/fusion-energy/cad_to_dagmc/tree/main/tests) and also the DAGMC geometry made with this package is compared to simulation carried out with native constructive solid geometry, see [Model Benchmark Zoo](https://github.com/fusion-energy/model_benchmark_zoo) for more details.

Also checkout these other software projects that also create DAGMC geometry [CAD-to-OpenMC](https://github.com/openmsr/CAD_to_OpenMC), [Stellarmesh](https://github.com/Thea-Energy/stellarmesh) and [Coreform Cubit](https://coreform.com/products/coreform-cubit/)

# Installation options

Expand Down Expand Up @@ -153,3 +153,7 @@ Installing one of the package dependancies (Gmsh) with pip appears to result in
For examples showing creation of DAGMC h5m files, vtk files and usage within OpenMC transport code see the [examples folder](https://github.com/fusion-energy/cad_to_dagmc/tree/main/examples)

For more examples see the CAD tasks in the [neutronics-workshop](https://github.com/fusion-energy/neutronics-workshop) and [model benchmark zoo](https://github.com/fusion-energy/model_benchmark_zoo)

# Related software

Also checkout these other software projects that also create DAGMC geometry [CAD-to-OpenMC](https://github.com/openmsr/CAD_to_OpenMC), [Stellarmesh](https://github.com/Thea-Energy/stellarmesh) and [Coreform Cubit](https://coreform.com/products/coreform-cubit/).
44 changes: 44 additions & 0 deletions examples/surface_mesh/different_resolution_meshes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# This example makes 3 CAD boxes
# Meshes the 3 volumes with different resolutions
# exports the mesh to a DAGMC h5m file and GMsh msh file
import cadquery as cq
from cad_to_dagmc import CadToDagmc

box_set_size_course_mesh = cq.Workplane().box(1, 1, 2)
box_set_size_fine_mesh = cq.Workplane().moveTo(1, 0.5).box(1, 1, 1.5)
box_set_global_mesh = cq.Workplane().moveTo(2, 1).box(1, 1, 1)

assembly = cq.Assembly()
assembly.add(box_set_size_course_mesh, color=cq.Color(0, 0, 1))
assembly.add(box_set_size_fine_mesh, color=cq.Color(0, 1, 0))
assembly.add(box_set_global_mesh, color=cq.Color(1, 0, 0))

assembly.export("different_resolution_meshes.step")

# uncomment to see the assembly in a pop up vtk viewer
# from cadquery import vis
# vis.show(assembly)

model = CadToDagmc()
model.add_cadquery_object(assembly, material_tags=["mat1", "mat2", "mat3"])

model.export_dagmc_h5m_file(
filename="different_resolution_meshes.h5m",
min_mesh_size=0.01,
max_mesh_size=10,
set_size={
1: 0.9,
2: 0.1,
}, # not volume 3 is not specified in the set_size so it uses only the min max mesh sizes
)

model.export_gmsh_mesh_file(
filename="different_resolution_meshes.msh",
dimensions=2,
min_mesh_size=0.01,
max_mesh_size=10,
set_size={
1: 0.9,
2: 0.1,
}, # not volume 3 is not specified in the set_size so it uses only the min max mesh sizes
)
44 changes: 44 additions & 0 deletions examples/unstrucutred_volume_mesh/different_resolution_meshes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# This example makes 3 CAD boxes
# Meshes the 3 volumes with different resolutions
# exports the mesh to a DAGMC unstructured VTK file and Gmsh msh file
import cadquery as cq
from cad_to_dagmc import CadToDagmc

box_set_size_course_mesh = cq.Workplane().box(1, 1, 2)
box_set_size_fine_mesh = cq.Workplane().moveTo(1, 0.5).box(1, 1, 1.5)
box_set_global_mesh = cq.Workplane().moveTo(2, 1).box(1, 1, 1)

assembly = cq.Assembly()
assembly.add(box_set_size_course_mesh, color=cq.Color(0, 0, 1))
assembly.add(box_set_size_fine_mesh, color=cq.Color(0, 1, 0))
assembly.add(box_set_global_mesh, color=cq.Color(1, 0, 0))

assembly.export("different_resolution_meshes.step")

# uncomment to see the assembly in a pop up vtk viewer
# from cadquery import vis
# vis.show(assembly)

model = CadToDagmc()
model.add_cadquery_object(assembly, material_tags=["mat1", "mat2", "mat3"])

model.export_gmsh_mesh_file(
filename="different_resolution_meshes.msh",
dimensions=3,
min_mesh_size=0.01,
max_mesh_size=10,
set_size={
1: 0.9,
2: 0.1,
}, # not volume 3 is not specified in the set_size so it uses only the min max mesh sizes
)

model.export_unstructured_mesh_file(
filename="different_resolution_meshes.vtk",
min_mesh_size=0.01,
max_mesh_size=10,
set_size={
1: 0.9,
2: 0.1,
}, # not volume 3 is not specified in the set_size so it uses only the min max mesh sizes
)
64 changes: 56 additions & 8 deletions src/cad_to_dagmc/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,11 @@ def init_gmsh():

def _mesh_brep(
gmsh,
min_mesh_size: float = 1,
max_mesh_size: float = 10,
min_mesh_size: float | None = None,
max_mesh_size: float | None = None,
mesh_algorithm: int = 1,
dimensions: int = 2,
set_size: dict[int, float] | None = None,
):
"""Creates a conformal surface meshes of the volumes in a Brep file using Gmsh.
Expand All @@ -252,15 +253,49 @@ def _mesh_brep(
gmsh.option.setNumber("Mesh.Algorithm", mesh_algorithm)
dimensions: The number of dimensions, 2 for a surface mesh 3 for a
volume mesh. Passed to gmsh.model.mesh.generate()
set_size: a dictionary of volume ids (int) and target mesh sizes
(floats) to set for each volume, passed to gmsh.model.mesh.setSize.
Returns:
The resulting gmsh object and volumes
"""
if min_mesh_size and max_mesh_size:
if min_mesh_size > max_mesh_size:
raise ValueError(
f"min_mesh_size must be less than or equal to max_mesh_size. Currently min_mesh_size is set to {min_mesh_size} and max_mesh_size is set to {max_mesh_size}"
)

if min_mesh_size:
gmsh.option.setNumber("Mesh.MeshSizeMin", min_mesh_size)

if max_mesh_size:
gmsh.option.setNumber("Mesh.MeshSizeMax", max_mesh_size)

gmsh.option.setNumber("Mesh.Algorithm", mesh_algorithm)
gmsh.option.setNumber("Mesh.MeshSizeMin", min_mesh_size)
gmsh.option.setNumber("Mesh.MeshSizeMax", max_mesh_size)
gmsh.option.setNumber("General.NumThreads", 0) # Use all available cores

if set_size:
volumes = gmsh.model.getEntities(3)
available_volumes = [volume[1] for volume in volumes]
print("volumes", volumes)
for volume_id, size in set_size.items():
if volume_id in available_volumes:
size = set_size[volume_id]
if isinstance(size, dict):
# TODO face specific mesh sizes
pass
else:
boundaries = gmsh.model.getBoundary(
[(3, volume_id)], recursive=True
) # dim must be set to 3
print("boundaries", boundaries)
gmsh.model.mesh.setSize(boundaries, size)
print(f"Set size of {size} for volume {volume_id}")
else:
raise ValueError(
f"volume ID of {volume_id} set in set_sizes but not found in available volumes {volumes}"
)

gmsh.model.mesh.generate(dimensions)

return gmsh
Expand Down Expand Up @@ -508,6 +543,7 @@ def export_unstructured_mesh_file(
method: str = "file",
scale_factor: float = 1.0,
imprint: bool = True,
set_size: dict[int, float] | None = None,
):
"""
Exports an unstructured mesh file in VTK format for use with openmc.UnstructuredMesh.
Expand Down Expand Up @@ -539,6 +575,9 @@ def export_unstructured_mesh_file(
normally needed to ensure the geometry is meshed correctly. However if
you know your geometry does not need imprinting you can set this to False
and this can save time.
set_size: a dictionary of volume ids (int) and target mesh sizes
(floats) to set for each volume, passed to gmsh.model.mesh.setSize.
Returns:
--------
Expand Down Expand Up @@ -570,6 +609,7 @@ def export_unstructured_mesh_file(
max_mesh_size=max_mesh_size,
mesh_algorithm=mesh_algorithm,
dimensions=3,
set_size=set_size,
)

# makes the folder if it does not exist
Expand All @@ -589,13 +629,14 @@ def export_unstructured_mesh_file(
def export_gmsh_mesh_file(
self,
filename: str = "mesh.msh",
min_mesh_size: float = 1,
max_mesh_size: float = 5,
min_mesh_size: float | None = None,
max_mesh_size: float | None = None,
mesh_algorithm: int = 1,
dimensions: int = 2,
method: str = "file",
scale_factor: float = 1.0,
imprint: bool = True,
set_size: dict[int, float] | None = None,
):
"""Saves a GMesh msh file of the geometry in either 2D surface mesh or
3D volume mesh.
Expand All @@ -622,6 +663,8 @@ def export_gmsh_mesh_file(
normally needed to ensure the geometry is meshed correctly. However if
you know your geometry does not need imprinting you can set this to False
and this can save time.
set_size: a dictionary of volume ids (int) and target mesh sizes
(floats) to set for each volume, passed to gmsh.model.mesh.setSize.
"""

assembly = cq.Assembly()
Expand All @@ -643,6 +686,7 @@ def export_gmsh_mesh_file(
max_mesh_size=max_mesh_size,
mesh_algorithm=mesh_algorithm,
dimensions=dimensions,
set_size=set_size,
)

# makes the folder if it does not exist
Expand All @@ -662,13 +706,14 @@ def export_gmsh_mesh_file(
def export_dagmc_h5m_file(
self,
filename: str = "dagmc.h5m",
min_mesh_size: float = 1,
max_mesh_size: float = 5,
min_mesh_size: float | None = None,
max_mesh_size: float | None = None,
mesh_algorithm: int = 1,
implicit_complement_material_tag: str | None = None,
method: str = "file",
scale_factor: float = 1.0,
imprint: bool = True,
set_size: dict[int, float] | None = None,
) -> str:
"""Saves a DAGMC h5m file of the geometry
Expand All @@ -695,6 +740,8 @@ def export_dagmc_h5m_file(
normally needed to ensure the geometry is meshed correctly. However if
you know your geometry does not need imprinting you can set this to False
and this can save time.
set_size: a dictionary of volume ids (int) and target mesh sizes
(floats) to set for each volume, passed to gmsh.model.mesh.setSize.
Returns:
str: the DAGMC filename saved
Expand Down Expand Up @@ -739,6 +786,7 @@ def export_dagmc_h5m_file(
min_mesh_size=min_mesh_size,
max_mesh_size=max_mesh_size,
mesh_algorithm=mesh_algorithm,
set_size=set_size,
)

dims_and_vol_ids = volumes
Expand Down

0 comments on commit fd5c734

Please sign in to comment.