Thank you very much for your early reply! In my recent test, I found that for the two-dimensional case, points on adjacent boundaries can be included in two sub-grids, but the three-dimensional case seems to be a little different? Here is my two-dimensional test example:
import gmsh
import os
from dolfinx.io import gmshio
from mpi4py import MPI
from dolfinx.mesh import create_submesh
import numpy as np
# Create output directory
output_dir = "./data"
if not os.path.exists(output_dir):
os.makedirs(output_dir)
gmsh.initialize()
gmsh.model.add("two_regions_3d")
# Create geometry
outer_radius = 1.0
inner_radius = 0.3
region1_full = gmsh.model.occ.addDisk(0, 0, 0, outer_radius, outer_radius)
temp_cut = gmsh.model.occ.addDisk(0, 0, 0, inner_radius, inner_radius)
region2 = gmsh.model.occ.addDisk(0, 0, 0, inner_radius, inner_radius)
region1 = gmsh.model.occ.cut([(2, region1_full)], [(2, temp_cut)])
gmsh.model.occ.synchronize()
region1_tag = region1[0][0][1]
# Set mesh parameters and create physical groups
mesh_size = 0.05
gmsh.model.mesh.setSize(gmsh.model.getEntities(0), mesh_size)
gmsh.model.addPhysicalGroup(2, [region1_tag], 11111)
gmsh.model.addPhysicalGroup(2, [region2], 22222)
# Mark boundaries
boundary1 = gmsh.model.getBoundary([(2, region1_tag)], oriented=False)
boundary2 = gmsh.model.getBoundary([(2, region2)], oriented=False)
boundary1_tags = [tag for dim, tag in boundary1]
boundary2_tags = [tag for dim, tag in boundary2]
gmsh.model.addPhysicalGroup(1, boundary1_tags, 44444) # outer boundary
gmsh.model.addPhysicalGroup(1, boundary2_tags, 33333) # interface
gmsh.model.mesh.generate(2)
gmsh.write(os.path.join(output_dir, "two_regions_2d.msh"))
gmsh.finalize()
# Read mesh and process
file = "./data/two_regions_2d.msh"
domain, cell_markers, facet_markers = gmshio.read_from_msh(file, MPI.COMM_WORLD, gdim=2)
# Create submeshes
tdim = domain.topology.dim
submesh1, map1, vertex_map1, geom_map1 = create_submesh(domain, tdim, cell_markers.find(11111))
submesh2, map2, vertex_map2, geom_map2 = create_submesh(domain, tdim, cell_markers.find(22222))
# Get interface vertices
fdim = tdim - 1
domain.topology.create_connectivity(fdim, 0)
face_to_vertex = domain.topology.connectivity(fdim, 0)
interface_vertices = []
interface_facets = facet_markers.find(33333)
for facet in interface_facets:
vertices = face_to_vertex.links(facet)
interface_vertices.extend(vertices)
interface_vertices = np.unique(interface_vertices)
# Print mesh information
print(f"Total vertices in original mesh: {domain.geometry.x.shape[0]}")
print(f"Vertices in region1: {submesh1.geometry.x.shape[0]}")
print(f"Vertices in region2: {submesh2.geometry.x.shape[0]}")
print(f"Number of interface vertices: {len(interface_vertices)}")
# Check interface vertices membership
def find_matching_vertices(coords, reference_coords, tolerance=1e-5):
matches = 0
for ref_coord in reference_coords:
distances = np.linalg.norm(coords - ref_coord, axis=1)
if np.any(distances < tolerance):
matches += 1
return matches
coords1 = submesh1.geometry.x
coords2 = submesh2.geometry.x
interface_coords = domain.geometry.x[interface_vertices]
matches1 = find_matching_vertices(coords1, interface_coords)
matches2 = find_matching_vertices(coords2, interface_coords)
print(f"\nInterface vertices analysis:")
print(f"- {matches1} vertices found in region1")
print(f"- {matches2} vertices found in region2")
The result is as follows:
Number of interface vertices: 38
Interface vertices analysis:
- 38 vertices found in region1
- 38 vertices found in region2
but for the 3D case, the situation is different:
import gmsh
import os
from dolfinx.io import gmshio
from mpi4py import MPI
from dolfinx.mesh import create_submesh
import numpy as np
# Create output directory
output_dir = "./data"
if not os.path.exists(output_dir):
os.makedirs(output_dir)
gmsh.initialize()
gmsh.model.add("two_regions_2d")
# Create geometry
outer_radius = 1.0
inner_radius = 0.3
region1_full = gmsh.model.occ.addSphere(0, 0, 0, outer_radius, 1)
temp_cut = gmsh.model.occ.addSphere(0, 0, 0, inner_radius, 2)
region2 = gmsh.model.occ.addSphere(0, 0, 0, inner_radius, 3)
region1 = gmsh.model.occ.cut([(3, 1)], [(3, 2)], 4)
gmsh.model.occ.synchronize()
region1_tag = region1[0][0][1]
# Set mesh parameters and create physical groups
mesh_size = 0.1
gmsh.model.mesh.setSize(gmsh.model.getEntities(0), mesh_size)
gmsh.model.addPhysicalGroup(3, [region1_tag], 11111)
gmsh.model.addPhysicalGroup(3, [region2], 22222)
# Mark boundaries
boundary1 = gmsh.model.getBoundary([(3, region1_tag)], oriented=False)
boundary2 = gmsh.model.getBoundary([(3, region2)], oriented=False)
boundary1_tags = [tag for dim, tag in boundary1]
boundary2_tags = [tag for dim, tag in boundary2]
gmsh.model.addPhysicalGroup(2, boundary1_tags, 44444) # outer boundary
gmsh.model.addPhysicalGroup(2, boundary2_tags, 33333) # interface
gmsh.model.mesh.generate(3)
gmsh.write(os.path.join(output_dir, "two_regions_3d.msh"))
gmsh.finalize()
# Read mesh and process
file = "./data/two_regions_3d.msh"
domain, cell_markers, facet_markers = gmshio.read_from_msh(file, MPI.COMM_WORLD, gdim=3)
# Create submeshes
tdim = domain.topology.dim
submesh1, map1, vertex_map1, geom_map1 = create_submesh(domain, tdim, cell_markers.find(11111))
submesh2, map2, vertex_map2, geom_map2 = create_submesh(domain, tdim, cell_markers.find(22222))
# Get interface vertices
fdim = tdim - 1
domain.topology.create_connectivity(fdim, 0)
face_to_vertex = domain.topology.connectivity(fdim, 0)
interface_vertices = []
interface_facets = facet_markers.find(33333)
for facet in interface_facets:
vertices = face_to_vertex.links(facet)
interface_vertices.extend(vertices)
interface_vertices = np.unique(interface_vertices)
# Print mesh information
print(f"Total vertices in original mesh: {domain.geometry.x.shape[0]}")
print(f"Vertices in region1: {submesh1.geometry.x.shape[0]}")
print(f"Vertices in region2: {submesh2.geometry.x.shape[0]}")
print(f"Number of interface vertices: {len(interface_vertices)}")
# Check interface vertices membership
def find_matching_vertices(coords, reference_coords, tolerance=1e-5):
matches = 0
for ref_coord in reference_coords:
distances = np.linalg.norm(coords - ref_coord, axis=1)
if np.any(distances < tolerance):
matches += 1
return matches
coords1 = submesh1.geometry.x
coords2 = submesh2.geometry.x
interface_coords = domain.geometry.x[interface_vertices]
matches1 = find_matching_vertices(coords1, interface_coords)
matches2 = find_matching_vertices(coords2, interface_coords)
print(f"\nInterface vertices analysis:")
print(f"- {matches1} vertices found in region1")
print(f"- {matches2} vertices found in region2")
The result is:
- 14 vertices found in region1
- 160 vertices found in region2
How can I make the two sub-meshes have the same nodes on adjacent boundary in 3D so that I can pass the boundary conditions?