Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions assembly_mesh_plugin/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,131 @@ def assembly_to_gmsh(self, mesh_path="tagged_mesh.msh"):
gmsh.finalize()


def assembly_to_imprinted_gmsh(self, mesh_path="tagged_mesh.msh"):
"""
Exports an imprinted assembly to capture conformal meshes.
"""

gmsh.initialize()
gmsh.option.setNumber("General.Terminal", 0)
gmsh.model.add("assembly")

# The mesh volume and surface ids should line up with the order of solids and faces in the assembly
vol_id = 1
surface_id = 1

# Tracks multi-surface physical groups
multi_material_groups = {}
surface_groups = {}

# Tracks the solids with tagged faces
tagged_faces = {}
solids_with_tagged_faces = {}

# Imprint the assembly
imprinted_assembly, imprinted_solids_with_orginal_ids = (
cq.occ_impl.assembly.imprint(self)
)

print(imprinted_solids_with_orginal_ids)
for solid, id in imprinted_solids_with_orginal_ids.items():
# Add the current solid to the mesh
# Work-around for a segfault with in-memory passing of OCCT objects
with tempfile.NamedTemporaryFile(suffix=".brep") as temp_file:
solid.exportBrep(temp_file.name)
ps = gmsh.model.occ.importShapes(temp_file.name)
gmsh.model.occ.synchronize()

# Technically, importShapes could import multiple entities/dimensions, so filter those
vol_ents = []
for p in ps:
if p[0] == 3:
vol_ents.append(p[1])

# Set the physical name to be the part name in the assembly for all the solids
ps2 = gmsh.model.addPhysicalGroup(3, vol_ents)
gmsh.model.setPhysicalName(3, ps2, f"{id[0].split('/')[-1]}")

# Get the original assembly part
object_name = id[0].split("/")[-1]
assembly_part = self.objects[object_name]

# Collect any tags from the part
for tag, wp in assembly_part.obj.ctx.tags.items():
tagged_face = wp.faces().all()[0].val()
for face in wp.faces().all():
tagged_faces[face.val()] = tag

# Iterate over the faces of the assembly part
for face in assembly_part.obj.faces():
for tagged_face, tag in tagged_faces.items():
if TopoDS_Shape.IsEqual(face.wrapped, tagged_face.wrapped):
print(f"{vol_id}_{surface_id}", tag)
solids_with_tagged_faces[f"{vol_id}_{surface_id}"] = (
object_name,
tag,
)

surface_id += 1
vol_id += 1

# Reset the volume and surface IDs
vol_id = 1
surface_id = 1

# Step through the imprinted assembly/shape and check for tagged faces
for solid in imprinted_assembly.solids():
for face in solid.faces().Faces():
# Check to see if this face has been tagged
if f"{vol_id}_{surface_id}" in solids_with_tagged_faces.keys():
short_name = solids_with_tagged_faces[f"{vol_id}_{surface_id}"][0]
tag = solids_with_tagged_faces[f"{vol_id}_{surface_id}"][1]

# Find out if this is a multi-material tag
if tag.startswith("~"):
# Set the surface name to be the name of the tag without the ~
group_name = tag.replace("~", "").split("-")[0]

# Add this face to the multi-material group
if group_name in multi_material_groups:
multi_material_groups[group_name].append(surface_id)
else:
multi_material_groups[group_name] = [surface_id]
else:
# We want to track all surfaces that might be in a tag group
cur_tag_name = f"{short_name}_{tag}"
if cur_tag_name in surface_groups:
print("Append: ", cur_tag_name, short_name, surface_id)
surface_groups[cur_tag_name].append(surface_id)
else:
print("New: ", cur_tag_name, short_name, surface_id)
surface_groups[cur_tag_name] = [surface_id]

surface_id += 1
vol_id += 1

# Handle tagged surface groups
for t_name, surf_group in surface_groups.items():
ps = gmsh.model.addPhysicalGroup(2, surf_group)
gmsh.model.setPhysicalName(2, ps, t_name)

# Handle multi-material tags
for group_name, mm_group in multi_material_groups.items():
ps = gmsh.model.addPhysicalGroup(2, mm_group)
gmsh.model.setPhysicalName(2, ps, f"{group_name}")

gmsh.model.occ.synchronize()

gmsh.model.mesh.field.setAsBackgroundMesh(2)

gmsh.model.mesh.generate(3)
gmsh.write(mesh_path)

gmsh.finalize()


# Patch the new assembly functions into CadQuery's importers package
cq.Assembly.assemblyToGmsh = assembly_to_gmsh
cq.Assembly.saveToGmsh = assembly_to_gmsh # Alias name that works better on cq.Assembly
cq.Assembly.getTaggedGmsh = get_tagged_gmsh
cq.Assembly.assemblyToImprintedGmsh = assembly_to_imprinted_gmsh
5 changes: 3 additions & 2 deletions environment.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
name: tagged-meshes
channels:
- conda-forge
- cadquery
dependencies:
- python>=3.9,<=3.12
- cadquery
- cadquery=master
- pytest
- gmsh
- python-gmsh
- appdirs
- pip
- pip:
- --editable=.
- --editable=.
31 changes: 31 additions & 0 deletions tests/test_meshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,34 @@ def test_subshape_assembly():
continue

assert cur_name in ["cube_1_cube_1_top_face"]


def test_imprinted_assembly():
# Create the basic assembly
assy = generate_simple_nested_boxes()

assy.assemblyToImprintedGmsh("tagged_imprinted_mesh.msh")

gmsh.initialize()

gmsh.open("tagged_imprinted_mesh.msh")

# Check the solids for the correct tags
physical_groups = gmsh.model.getPhysicalGroups(3)
for group in physical_groups:
# Get the name for the current volume
cur_name = gmsh.model.getPhysicalName(3, group[1])

assert cur_name in ["shell", "insert"]

# Check the surfaces for the correct tags
physical_groups = gmsh.model.getPhysicalGroups(2)
for group in physical_groups:
# Get the name for this group
cur_name = gmsh.model.getPhysicalName(2, group[1])

# Skip any groups that are not tagged explicitly
if "_surface_" in cur_name:
continue

assert cur_name in ["shell_inner-right", "insert_outer-right", "in_contact"]
Loading