From 95bf9d7a3c62a30493f36e6d9fad3da96142407c Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Tue, 16 Dec 2025 14:18:13 -0500 Subject: [PATCH 1/2] Added materials support by postfixing the material name onto the volume name --- assembly_mesh_plugin/plugin.py | 34 ++++++++++++++++++++++++------ tests/sample_assemblies.py | 17 +++++++++++++++ tests/test_meshes.py | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/assembly_mesh_plugin/plugin.py b/assembly_mesh_plugin/plugin.py index 3743372..b992388 100644 --- a/assembly_mesh_plugin/plugin.py +++ b/assembly_mesh_plugin/plugin.py @@ -161,6 +161,7 @@ def get_gmsh(self, imprint=True): tagged_faces = {} multi_material_groups = {} surface_groups = {} + solid_materials = [] gmsh.initialize() gmsh.option.setNumber( @@ -171,13 +172,23 @@ def get_gmsh(self, imprint=True): # Get all of the subshapes and their corresponding names/positions extract_subshape_names(self, self.name) - # Imprint the assembly - imprinted_assembly, imprinted_solids_with_orginal_ids = ( - cq.occ_impl.assembly.imprint(self) - ) - # Handle the imprinted assembly if imprint: + # Imprint the assembly + imprinted_assembly, imprinted_solids_with_orginal_ids = ( + cq.occ_impl.assembly.imprint(self) + ) + + # Collect the materials + for imp_solid, solid_id in imprinted_solids_with_orginal_ids.items(): + # Track down the original assembly object so that we can retrieve materials, if present + short_id = solid_id[0].split("/")[-1] if "/" in solid_id[0] else solid_id[0] + subassy = self.objects[short_id] + + # Save the assembly material associated with this solid + if subassy.material: + solid_materials.append(subassy.material.name) + for solid, name in imprinted_solids_with_orginal_ids.items(): # Get just the name of the current assembly short_name = name[0].split("/")[-1] @@ -202,11 +213,22 @@ def get_gmsh(self, imprint=True): # Add faces to the mesh and handle tagged faces add_faces_to_mesh(gmsh, solid, short_name, loc) + # Keep track of the materials + if self.objects[name.split("/")[-1]].material: + solid_materials.append(self.objects[name.split("/")[-1]].material.name) + # Step through each of the volumes and add physical groups for each for volume_id in volumes.keys(): gmsh.model.occ.synchronize() + + # Include the material name, if present ps = gmsh.model.addPhysicalGroup(3, volumes[volume_id][0]) - gmsh.model.setPhysicalName(3, ps, f"{volume_map[volume_id]}") + + # See if we need to include the material name as part of the physical volume name + volume_name = f"{volume_map[volume_id]}" + if len(solid_materials) >= volume_id: + volume_name = f"{volume_map[volume_id]}~{solid_materials[volume_id - 1]}" + gmsh.model.setPhysicalName(3, ps, volume_name) # Handle tagged surface groups for t_name, surf_group in surface_groups.items(): diff --git a/tests/sample_assemblies.py b/tests/sample_assemblies.py index 4e546c6..e99140f 100644 --- a/tests/sample_assemblies.py +++ b/tests/sample_assemblies.py @@ -315,3 +315,20 @@ def generate_subshape_assembly(): ) return assy + + +def generate_materials_assembly(): + """ + Generates a simple assembly with materials. + """ + + # Create the assembly children + cube_1 = cq.Workplane().box(10, 10, 10) + cube_2 = cq.Workplane().box(5, 5, 5) + + # Put the assembly together + assy = cq.Assembly(name="top-level") + assy.add(cube_1, name="cube_1", material="copper") + assy.add(cube_2, name="cube_2", loc=cq.Location(0, 0, 5), material="steel") + + return assy diff --git a/tests/test_meshes.py b/tests/test_meshes.py index 24485c9..1728cf6 100644 --- a/tests/test_meshes.py +++ b/tests/test_meshes.py @@ -8,6 +8,7 @@ generate_test_cross_section, generate_assembly, generate_subshape_assembly, + generate_materials_assembly, ) @@ -175,3 +176,40 @@ def _check_physical_groups(): # Ensure that there are physical groups _check_physical_groups() + + +def test_mesh_materials(): + """ + Tests to make sure that assembly materials are preserved in the mesh data. + """ + + # Create the basic assembly with materials + assy = generate_materials_assembly() + + # + # Imprinted assembly + # + gmsh = assy.getGmsh(imprint=True) + gmsh.model.mesh.generate(3) + + phys_groups = gmsh.model.getPhysicalGroups(3) + + # Make sure we got the correct names + name = gmsh.model.getPhysicalName(3, 1) + assert name == "cube_1~copper" + name = gmsh.model.getPhysicalName(3, 2) + assert name == "cube_2~steel" + + # + # Non-imprinted assembly + # + gmsh = assy.getGmsh(imprint=False) + gmsh.model.mesh.generate(3) + + phys_groups = gmsh.model.getPhysicalGroups(3) + + # Make sure we got the correct names + name = gmsh.model.getPhysicalName(3, 1) + assert name == "cube_1~copper" + name = gmsh.model.getPhysicalName(3, 2) + assert name == "cube_2~steel" From ae8219147d3336dabcdf693b28d5f431bc4ec703 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Wed, 17 Dec 2025 09:54:24 -0500 Subject: [PATCH 2/2] Added material names via a different physical group --- .github/workflows/tests.yml | 1 + assembly_mesh_plugin/plugin.py | 10 +++++----- tests/test_meshes.py | 16 ++++++++++++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d85fc92..0ed520e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,5 +40,6 @@ jobs: - name: Install dependencies run: | python -m pip install .[dev] + python -m pip install --upgrade git+https://github.com/CadQuery/cadquery.git - name: Run tests run: python -m pytest -v diff --git a/assembly_mesh_plugin/plugin.py b/assembly_mesh_plugin/plugin.py index b992388..5620938 100644 --- a/assembly_mesh_plugin/plugin.py +++ b/assembly_mesh_plugin/plugin.py @@ -221,14 +221,14 @@ def get_gmsh(self, imprint=True): for volume_id in volumes.keys(): gmsh.model.occ.synchronize() - # Include the material name, if present + # Attach the name to the volume ps = gmsh.model.addPhysicalGroup(3, volumes[volume_id][0]) + gmsh.model.setPhysicalName(3, ps, f"{volume_map[volume_id]}") - # See if we need to include the material name as part of the physical volume name - volume_name = f"{volume_map[volume_id]}" + # Attach the material to the volume, if the material is present if len(solid_materials) >= volume_id: - volume_name = f"{volume_map[volume_id]}~{solid_materials[volume_id - 1]}" - gmsh.model.setPhysicalName(3, ps, volume_name) + ps1 = gmsh.model.addPhysicalGroup(3, volumes[volume_id][0]) + gmsh.model.setPhysicalName(3, ps1, f"mat:{solid_materials[volume_id - 1]}") # Handle tagged surface groups for t_name, surf_group in surface_groups.items(): diff --git a/tests/test_meshes.py b/tests/test_meshes.py index 1728cf6..7a9e226 100644 --- a/tests/test_meshes.py +++ b/tests/test_meshes.py @@ -196,9 +196,13 @@ def test_mesh_materials(): # Make sure we got the correct names name = gmsh.model.getPhysicalName(3, 1) - assert name == "cube_1~copper" + assert name == "cube_1" name = gmsh.model.getPhysicalName(3, 2) - assert name == "cube_2~steel" + assert name == "mat:copper" + name = gmsh.model.getPhysicalName(3, 3) + assert name == "cube_2" + name = gmsh.model.getPhysicalName(3, 4) + assert name == "mat:steel" # # Non-imprinted assembly @@ -210,6 +214,10 @@ def test_mesh_materials(): # Make sure we got the correct names name = gmsh.model.getPhysicalName(3, 1) - assert name == "cube_1~copper" + assert name == "cube_1" name = gmsh.model.getPhysicalName(3, 2) - assert name == "cube_2~steel" + assert name == "mat:copper" + name = gmsh.model.getPhysicalName(3, 3) + assert name == "cube_2" + name = gmsh.model.getPhysicalName(3, 4) + assert name == "mat:steel"