Coaxial cable mesh¶
from palacetoolkit.viz import run_with_scrollable_output, view_mesh
from palacetoolkit.geometry import extract_tag, xmin, xmax, ymin, ymax, zmin, zmax
import math
import gmsh
Parameters:
- filename - the filename to use for the generated mesh.
- refinement - measure of how many elements to include, 0 is least
- order - the polynomial order of the approximation, minimum 1
- inner_diameter_mm - the inner diameter of the cable, in millimeters
- outer_diameter_mm - the outer diameter of the cable, in millimeters
- length_mm - the length of the cable, in millimeters
- verbose - flag to dictate the level of print to REPL, passed to Gmsh
- gui - whether to launch the Gmsh GUI on mesh generation
filename : str = "coax.msh"
refinement : int = 2
order : int = 2
inner_diameter_mm : float = 1.6383
outer_diameter_mm : float = 5.461
length_mm : float = 40.0
verbose : int = 5
gui : bool = True
Check that the geometry makes sense.
assert outer_diameter_mm > inner_diameter_mm > 0
assert length_mm > 0
assert refinement >= 0
assert order > 0
Initialization of the model.¶
# Creates a reference to the geometry module based on OpenCASCADE (OCC).
kernel = gmsh.model.occ
# Initialize gmsh.
gmsh.initialize()
# Set the verbosity of the output.
gmsh.option.setNumber("General.Verbosity", verbose)
Geometry definitions.¶
# Geometry (en mm)
inner_radio = inner_diameter_mm / 2
outer_radio = outer_diameter_mm / 2
# Mesh parameters
n_circum = 4 * 2**refinement
n_length = 4 * 2**refinement
# Geometry
p0 = kernel.addPoint(inner_radio, 0, 0)
p1 = kernel.addPoint(outer_radio, 0, 0)
l0 = kernel.addLine(p0, p1)
Revolve operation - base face generation.¶
Rotate the line (l_0) around the z-axis.
- First revolve (base_face_0) is made with a 180° degree and n_circum//2 divisions.
- Seconde revolve (base_face_1) with a -180° degree and n_circum//2 divisions.
base_face_0 = kernel.revolve(
[(1, l0)], # list of entities to rotate (dim=1 → curve, tag=l0)
0.0, 0.0, 0.0, # origin point of rotation
0.0, 0.0, 1.0, # rotation axis (here, the z-axis)
math.pi, # rotation angle (180°)
[n_circum // 2], # number of divisions in the angular direction
[1.0], # scale factor for the mesh size (1.0 means no scaling)
recombine=True # recombinar las caras resultantes
)
base_face_1 = kernel.revolve(
[(1, l0)],
0.0, 0.0, 0.0,
0.0, 0.0, 1.0,
-math.pi,
[n_circum//2],
[1.0],
recombine=True
)
kernel.revolve returns a list of entities created by the revolution, each one represented as a tuple (dim, tag).
- dim is the dimension of the entity (0 for points, 1 for lines, 2 for surfaces, 3 for volumes).
- A tag is a unique identifier assigned by Gmsh to each entity created.
We will filter these lists to keep only the surfaces (dim=2) that represent the cylinder faces, discarding any other entities that may have been created during the revolution.
In this case the revolution returns [(1, 4), (2, 1), (1, 2), (1, 3)]. We are only interested in (2, 1), which is the surface created by the revolution, and we discard the lines (1, 4), (1, 2), (1, 3).
base_face_0 = [x for x in base_face_0 if x[0] == 2]
base_face_1 = [x for x in base_face_1 if x[0] == 2]
# Check that there was generated only one face.
assert len(base_face_0) == 1 and len(base_face_1) == 1
Now, we extrude the base faces to get the cylinder.
# Extrude the faces along the z-axis.
cylinder_0 = kernel.extrude(
base_face_0, # List of entities (dim=2, tag)
0.0, 0.0, length_mm, # How much extrusion in (x, y, z)
[n_length], # Divisions in the direction of the extrusion
[1.0], # Scale
recombine=True # recombine faces
)
cylinder_1 = kernel.extrude(
base_face_1,
0.0, 0.0, length_mm,
[n_length],
[1.0],
recombine=True
)
We keep only the tags of the faces and volumes created by the extrusion, filtering by dimension. Remember that cylinder_0 and cylinder_1 are lists of tuples (dim, tag) representing all entities created by the extrusion, including lines, surfaces, and volumes. We are interested only in the faces (dim=2) that form the cylinder end caps, and in the volumes (dim=3) that represent the cylinder body.
base_face_0 = base_face_0[0][1] # Tag of the first surface.
base_face_1 = base_face_1[0][1]
far_face_0 = cylinder_0[0][1]
cylinder_0 = cylinder_0[1][1]
far_face_1 = cylinder_1[0][1]
cylinder_1 = cylinder_1[1][1]
Fragment the geometry to create a valid mesh. This is necessary because we have extruded two faces that share an edge, and we need to ensure that the resulting geometry is properly connected.
# Remove duplicates (if there is any) and preserve the original geometry.
kernel.fragment(kernel.getEntities(), [])
kernel.synchronize()
Info : [ 0%] Fragments Info : [ 10%] Fragments Info : [ 20%] Fragments Info : [ 30%] Fragments Info : [ 40%] Fragments Info : [ 50%] Fragments Info : [ 60%] Fragments Info : [ 70%] Fragments - Filling splits of vertices Info : [ 80%] Fragments - Splitting faces Info : [ 90%] Fragments - Looking for internal shapes Info : Rebinding OpenCASCADE point 3 Info : Rebinding OpenCASCADE point 4 Info : Cannot bind existing OpenCASCADE point 3 to second tag 5 Info : Could not preserve tag of 0D object 5 (->3) Info : Cannot bind existing OpenCASCADE point 4 to second tag 6 Info : Could not preserve tag of 0D object 6 (->4) Info : Rebinding OpenCASCADE point 7 Info : Rebinding OpenCASCADE point 8 Info : Rebinding OpenCASCADE point 9 Info : Rebinding OpenCASCADE point 10 Info : Cannot bind existing OpenCASCADE point 7 to second tag 11 Info : Could not preserve tag of 0D object 11 (->7) Info : Cannot bind existing OpenCASCADE point 8 to second tag 12 Info : Could not preserve tag of 0D object 12 (->8) Info : Cannot bind existing OpenCASCADE point 9 to second tag 13 Info : Could not preserve tag of 0D object 13 (->9) Info : Cannot bind existing OpenCASCADE point 10 to second tag 14 Info : Could not preserve tag of 0D object 14 (->10) Info : Rebinding OpenCASCADE curve 2 Info : Rebinding OpenCASCADE curve 3 Info : Rebinding OpenCASCADE curve 4 Info : Rebinding OpenCASCADE curve 5 Info : Rebinding OpenCASCADE curve 6 Info : Cannot bind existing OpenCASCADE curve 4 to second tag 7 Info : Could not preserve tag of 1D object 7 (->4) Info : Rebinding OpenCASCADE curve 8 Info : Rebinding OpenCASCADE curve 9 Info : Rebinding OpenCASCADE curve 10 Info : Rebinding OpenCASCADE curve 11 Info : Rebinding OpenCASCADE curve 12 Info : Rebinding OpenCASCADE curve 13 Info : Rebinding OpenCASCADE curve 14 Info : Rebinding OpenCASCADE curve 15 Info : Cannot bind existing OpenCASCADE curve 8 to second tag 16 Info : Could not preserve tag of 1D object 16 (->8) Info : Cannot bind existing OpenCASCADE curve 9 to second tag 17 Info : Could not preserve tag of 1D object 17 (->9) Info : Rebinding OpenCASCADE curve 18 Info : Cannot bind existing OpenCASCADE curve 11 to second tag 19 Info : Could not preserve tag of 1D object 19 (->11) Info : Cannot bind existing OpenCASCADE curve 12 to second tag 20 Info : Could not preserve tag of 1D object 20 (->12) Info : Rebinding OpenCASCADE curve 21 Info : Cannot bind existing OpenCASCADE curve 14 to second tag 22 Info : Could not preserve tag of 1D object 22 (->14) Info : Cannot bind existing OpenCASCADE curve 15 to second tag 23 Info : Could not preserve tag of 1D object 23 (->15) Info : Rebinding OpenCASCADE surface 1 Info : Rebinding OpenCASCADE surface 2 Info : Rebinding OpenCASCADE surface 3 Info : Rebinding OpenCASCADE surface 4 Info : Rebinding OpenCASCADE surface 5 Info : Rebinding OpenCASCADE surface 6 Info : Rebinding OpenCASCADE surface 7 Info : Rebinding OpenCASCADE surface 8 Info : Rebinding OpenCASCADE surface 9 Info : Cannot bind existing OpenCASCADE surface 5 to second tag 10 Info : Could not preserve tag of 2D object 10 (->5) Info : Cannot bind existing OpenCASCADE surface 6 to second tag 11 Info : Could not preserve tag of 2D object 11 (->6) Info : Rebinding OpenCASCADE surface 12
# Get the boundaries deleting non necessary surfaces
boundaries = []
for cylinder in [cylinder_0, cylinder_1]:
_, local_boundaries = gmsh.model.getAdjacencies(3, cylinder)
local_delete = []
for boundary in local_boundaries:
if boundary not in boundaries:
boundaries.append(boundary)
else:
local_delete.append(boundaries.index(boundary))
# eliminar duplicados
for idx in sorted(local_delete, reverse=True):
del boundaries[idx]
# Delete faces
for face in [base_face_0, base_face_1, far_face_0, far_face_1]:
if face in boundaries:
boundaries.remove(face)
Defining the Physical groups.¶
cylinder_group = gmsh.model.addPhysicalGroup(3, [cylinder_0, cylinder_1], -1, "cylinder")
boundary_group = gmsh.model.addPhysicalGroup(2, boundaries, -1, "boundaries")
port1_group = gmsh.model.addPhysicalGroup(2, [base_face_0, base_face_1], -1, "port1")
port2_group = gmsh.model.addPhysicalGroup(2, [far_face_0, far_face_1], -1, "port2")
Mesh settings and generation.¶
def _generate_coax_mesh():
# Mesh options
gmsh.option.setNumber("Mesh.MinimumCurveNodes", 2)
gmsh.option.setNumber("Mesh.MinimumCircleNodes", 0)
gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 0)
gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 0)
gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
# Set mesh algorithms
gmsh.option.setNumber("Mesh.Algorithm", 6) # frontal (2D)
gmsh.option.setNumber("Mesh.Algorithm3D", 1) # Delaunay (3D)
# Generate 3D mesh
gmsh.model.mesh.generate(3)
# Order of the mesh elements (1 = linear, 2 = quadratic)
gmsh.model.mesh.setOrder(order)
# Save mesh
gmsh.option.setNumber("Mesh.MshFileVersion", 2.2)
gmsh.option.setNumber("Mesh.Binary", 1)
gmsh.write(filename)
if verbose > 0:
print("\nFinished generating mesh. Physical group tags:")
print("Cylinder:", cylinder_group)
print("Boundaries:", boundary_group)
print("Port 1:", port1_group)
print("Port 2:", port2_group)
print()
run_with_scrollable_output(_generate_coax_mesh, title="Coax mesh generation", max_lines=10)
if gui:
gmsh.fltk.run()
gmsh.finalize()
view_mesh(filename)
Finished generating mesh. Physical group tags: Cylinder: 1 Boundaries: 2 Port 1: 3 Port 2: 4
------------------------------------------------------- Version : 4.15.2 License : GNU General Public License Build OS : Linux64-sdk Build date : 20260324 Build host : gmsh.info Build options : 64Bit ALGLIB[contrib] ANN[contrib] Bamg Blas[petsc] Blossom Cgns DIntegration Dlopen DomHex Eigen[contrib] Fltk Gmm[contrib] Hxt Jpeg Kbipack Lapack[petsc] LinuxJoystick MathEx[contrib] Med Mesh Metis[contrib] Mmg Mpeg Netgen Nii2mesh ONELAB ONELABMetamodel OpenCASCADE OpenCASCADE-CAF OpenGL OpenMP OptHom PETSc Parser Plugins Png Post QuadMeshingTools QuadTri Solver TetGen/BR TinyXML2[contrib] Untangle Voro++[contrib] WinslowUntangler Zlib tinyobjloader FLTK version : 1.3.11 PETSc version : 3.14.4 (real arithmtic) OCC version : 7.8.1 MED version : 4.1.0 Packaged by : geuzaine Web site : https://gmsh.info Issue tracker : https://gitlab.onelab.info/gmsh/gmsh/issues -------------------------------------------------------
Loading mesh file: coax.msh
Groups to render transparent: ['air_none', 'air_plastic_enclosure']
Mesh loaded successfully with 2 cell blocks
Found 1088 triangles total
Physical group tags in mesh: {2: 'boundaries', 3: 'port1', 4: 'port2'}