Open ended stub antenna#

An open-ended stub antenna is a simple radiating element consisting of a transmission line (typically microstrip or coaxial cable) with one end open-circuited, allowing electromagnetic energy to radiate directly into free space.

import gmsh
import math

from palacetoolkit.viz import view_mesh
from palacetoolkit.mesh import (
    Entity, 
    run_meshing_pipeline, 
    generate_3d_mesh, 
    refine_near_surfaces
)
from palacetoolkit.simulation import Simulation

Paramters#

  • l1 : Ground plane length along x-axis, specified as a scalar in meters

  • w1 : Ground plane width along y-axis, specified as a scalar in meters

  • h : Patch height along z-axis, specified as a scalar in meters.

  • strip_line_length : Notch length along x-axis, specified as a scalar in meters.

  • strip_lined_width: Notch width along y-axis, specified as a scalar in meters.

  • air_height : Air-domain height reference for sizing the enclosing sphere, specified as a scalar in meters.

  • air_margin : Air-domain margin along x and y axes used for sphere sizing, specified as a scalar in meters.

  • freq : Simulation frequency in GHz, specified as a scalar.

  • filename : Output mesh filename, specified as a string.

l1: float = 0.06
w1: float = 0.06
strip_line_length: float = 0.04
strip_line_width: float = 0.002
h: float = 0.0013
air_height: float = 0.025    
air_margin: float = 0.025    
freq: float = 3.3
filename: str = "open_ended_antenna.msh"

wavelength = 3e8 / (freq * 1e9)

Model initialization#

gmsh.initialize()
gmsh.model.add("patch_antenna")
kernel = gmsh.model.occ

Geometry construction#

# Total domain bounds
total_xmin = -l1/2 - air_margin
total_xmax = l1/2 + air_margin
total_ymin = -w1/2 - air_margin
total_ymax = w1/2 + air_margin
total_zmax = h + air_height

# Substrate box
substrate = kernel.addBox(-l1/2, -w1/2, 0, l1, w1, h)

# Ground plane
ground_plane = kernel.addRectangle(-l1/2, -w1/2, 0, l1, w1)

# Top conductor
strip_line_1 = kernel.addRectangle(-l1/2, -strip_line_width/2, h, strip_line_length, strip_line_width)

# gap bewteen the ground plane and the bottom of the lumped port.
gap = 0

# lumped port
lumped_port = kernel.addRectangle(-l1/2 + gap, -strip_line_width/2, 0, h - gap, strip_line_width)
kernel.rotate([(2, lumped_port)], -l1/2, 0, 0, 0, 1, 0, -math.pi/2)

# Air sphere
airsphere_radius = max(abs(total_xmin), abs(total_xmax), abs(total_ymin), abs(total_ymax), total_zmax)
air_sphere = kernel.addSphere(0.0, 0.0, 0.0, airsphere_radius)

# Synchronize everything!
kernel.synchronize()
# Define the entities which later will become the physical groups.
entities = [
    Entity("air_sphere", dim=3, btype="dielectric", mesh_order=2, tags=[air_sphere], eps_r=1.0, mu_r=1.0, loss_tan=0.0),
    Entity("substrate", dim=3, btype="dielectric", mesh_order=1, tags=[substrate], eps_r=2.2, mu_r=1.0, loss_tan=0.0009),
    Entity("top_conductor", dim=2, btype="pec", mesh_order=1, tags=[strip_line_1]),
    Entity("ground_plane", dim=2, btype="pec", mesh_order=1, tags=[ground_plane]),
    Entity("lumped_port", dim=2, btype="lumped_port", mesh_order=0, tags=[lumped_port], R=50.0, direction="+Z")
]

# Boolean operations to guarantee a nice mesh, algo it returns the
# physical group map.
pg_map = run_meshing_pipeline(entities)

# Refine near the top conductor and locally the lumped port
refine_near_surfaces(entities[2].dimtags, 
                     wavelength, 
                     ppw_near=50, 
                     ppw_far=30, 
                     set_as_background=True,
                     local_refinements = {entities[-1].dimtags[0]: 150})
# Mesh sizes
mesh_sizes = {
    "substrate": wavelength / 12,
    "air_sphere": wavelength / 4,
    "lumped_port": wavelength / 150,
    "ground_plane" : wavelength / 10,
    "top_conductor": wavelength / 50
}

# Generate the 3d mesh.
generate_3d_mesh(entities, mesh_sizes, filename, optimize = True)
  Physical group 'air_sphere' (dim=3): pg=1, tags=[2]                                                                                            
  Physical group 'substrate' (dim=3): pg=2, tags=[1]
  Physical group 'top_conductor' (dim=2): pg=3, tags=[8]
  Physical group 'ground_plane' (dim=2): pg=4, tags=[7]
  Physical group 'lumped_port' (dim=2): pg=5, tags=[9]
  Physical group 'air_sphere__None' (dim=2): pg=6, tags=[16]
  Physical group 'air_sphere__substrate' (dim=2): pg=7, tags=[10, 11, 13, 15, 14, 12]
  ppw_near=50  ppw_far=30
  SizeMax=0.0030  transition=0.0227
  global: 4 curves, SizeMin=0.0018
  local (2, 9): 4 curves, SizeMin=0.0006
  Merged 2 fields with Min → field 5
Info    : Meshing 1D...
Info    : [  0%] Meshing curve 24 (Line)
Info    : [ 10%] Meshing curve 25 (Line)
Info    : [ 10%] Meshing curve 26 (Line)
Info    : [ 20%] Meshing curve 27 (Line)
Info    : [ 20%] Meshing curve 28 (Line)
Info    : [ 30%] Meshing curve 29 (Line)
Info    : [ 30%] Meshing curve 30 (Line)
Info    : [ 30%] Meshing curve 31 (Line)
Info    : [ 40%] Meshing curve 32 (Line)
Info    : [ 40%] Meshing curve 33 (Line)
Info    : [ 50%] Meshing curve 34 (Line)
Info    : [ 50%] Meshing curve 35 (Line)
Info    : [ 60%] Meshing curve 36 (Line)
Info    : [ 60%] Meshing curve 37 (Line)
Info    : [ 60%] Meshing curve 38 (Line)
Info    : [ 70%] Meshing curve 39 (Line)
Info    : [ 70%] Meshing curve 40 (Line)
Info    : [ 80%] Meshing curve 41 (Line)
Info    : [ 80%] Meshing curve 42 (Line)
Info    : [ 80%] Meshing curve 43 (Line)
Info    : [ 90%] Meshing curve 45 (Circle)
Info    : [100%] Meshing curve 47 (Line)
Info    : Done meshing 1D (Wall 0.0227882s, CPU 0.023435s)
Info    : Meshing 2D...
Info    : [  0%] Meshing surface 7 (Plane, MeshAdapt)
Info    : [ 20%] Meshing surface 8 (Plane, MeshAdapt)
Info    : [ 30%] Meshing surface 9 (Plane, MeshAdapt)
Info    : [ 40%] Meshing surface 10 (Plane, MeshAdapt)
Info    : [ 50%] Meshing surface 11 (Plane, MeshAdapt)
Info    : [ 60%] Meshing surface 12 (Plane, MeshAdapt)
Info    : [ 70%] Meshing surface 13 (Plane, MeshAdapt)
Info    : [ 80%] Meshing surface 14 (Plane, MeshAdapt)
Info    : [ 90%] Meshing surface 15 (Plane, MeshAdapt)
Info    : [100%] Meshing surface 16 (Sphere, MeshAdapt)
Info    : Done meshing 2D (Wall 2.3741s, CPU 2.35231s)
Info    : Meshing 3D...
Info    : 3D Meshing 2 volumes with 1 connected component
Info    : Tetrahedrizing 7351 nodes...
Info    : Done tetrahedrizing 7359 nodes (Wall 0.0740404s, CPU 0.069042s)
Info    : Reconstructing mesh...
Info    :  - Creating surface mesh
Info    :  - Identifying boundary edges
Info    :  - Recovering boundary
Info    : Done reconstructing mesh (Wall 0.215381s, CPU 0.197498s)
Info    : Found volume 2
Info    : Found volume 1
Info    : It. 0 - 0 nodes created - worst tet radius 15.8281 (nodes removed 0 0)
Info    : It. 500 - 500 nodes created - worst tet radius 3.01409 (nodes removed 0 0)
Info    : It. 1000 - 1000 nodes created - worst tet radius 2.48289 (nodes removed 0 0)
Info    : It. 1500 - 1500 nodes created - worst tet radius 2.19831 (nodes removed 0 0)
Info    : It. 2000 - 2000 nodes created - worst tet radius 2.01023 (nodes removed 0 0)
Info    : It. 2500 - 2500 nodes created - worst tet radius 1.87804 (nodes removed 0 0)
Info    : It. 3000 - 3000 nodes created - worst tet radius 1.77311 (nodes removed 0 0)
Info    : It. 3500 - 3500 nodes created - worst tet radius 1.6934 (nodes removed 0 0)
Info    : It. 4000 - 4000 nodes created - worst tet radius 1.62406 (nodes removed 0 0)
Info    : It. 4500 - 4500 nodes created - worst tet radius 1.56586 (nodes removed 0 0)
Info    : It. 5000 - 5000 nodes created - worst tet radius 1.51457 (nodes removed 0 0)
Info    : It. 5500 - 5500 nodes created - worst tet radius 1.4705 (nodes removed 0 0)
Info    : It. 6000 - 6000 nodes created - worst tet radius 1.43014 (nodes removed 0 0)
Info    : It. 6500 - 6500 nodes created - worst tet radius 1.39468 (nodes removed 0 0)
Info    : It. 7000 - 7000 nodes created - worst tet radius 1.36281 (nodes removed 0 0)
Info    : It. 7500 - 7500 nodes created - worst tet radius 1.33488 (nodes removed 0 0)
Info    : It. 8000 - 8000 nodes created - worst tet radius 1.30946 (nodes removed 0 0)
Info    : It. 8500 - 8500 nodes created - worst tet radius 1.28468 (nodes removed 0 0)
Info    : It. 9000 - 9000 nodes created - worst tet radius 1.26165 (nodes removed 0 0)
Info    : It. 9500 - 9500 nodes created - worst tet radius 1.24006 (nodes removed 0 0)
Info    : It. 10000 - 10000 nodes created - worst tet radius 1.21952 (nodes removed 0 0)
Info    : It. 10500 - 10500 nodes created - worst tet radius 1.20277 (nodes removed 0 0)
Info    : It. 11000 - 11000 nodes created - worst tet radius 1.18561 (nodes removed 0 0)
Info    : It. 11500 - 11500 nodes created - worst tet radius 1.16931 (nodes removed 0 0)
Info    : It. 12000 - 12000 nodes created - worst tet radius 1.15293 (nodes removed 0 0)
Info    : It. 12500 - 12500 nodes created - worst tet radius 1.13751 (nodes removed 0 0)
Info    : It. 13000 - 13000 nodes created - worst tet radius 1.12296 (nodes removed 0 0)
Info    : It. 13500 - 13500 nodes created - worst tet radius 1.1098 (nodes removed 0 0)
Info    : It. 14000 - 14000 nodes created - worst tet radius 1.09709 (nodes removed 0 0)
Info    : It. 14500 - 14500 nodes created - worst tet radius 1.08546 (nodes removed 0 0)
Info    : It. 15000 - 15000 nodes created - worst tet radius 1.07404 (nodes removed 0 0)
Info    : It. 15500 - 15500 nodes created - worst tet radius 1.0625 (nodes removed 0 0)
Info    : It. 16000 - 16000 nodes created - worst tet radius 1.05158 (nodes removed 0 0)
Info    : It. 16500 - 16500 nodes created - worst tet radius 1.04114 (nodes removed 0 0)
Info    : It. 17000 - 17000 nodes created - worst tet radius 1.03064 (nodes removed 0 0)
Info    : It. 17500 - 17500 nodes created - worst tet radius 1.02124 (nodes removed 0 0)
Info    : It. 18000 - 18000 nodes created - worst tet radius 1.01253 (nodes removed 0 0)
Info    : It. 18500 - 18500 nodes created - worst tet radius 1.0038 (nodes removed 0 0)
Info    : 3D refinement terminated (26079 nodes total):
Info    :  - 1 Delaunay cavities modified for star shapeness
Info    :  - 0 nodes could not be inserted
Info    :  - 147911 tetrahedra created in 1.26757 sec. (116688 tets/s)
Info    : 0 node relocations
Info    : Done meshing 3D (Wall 1.88412s, CPU 1.86293s)
Info    : Optimizing mesh...
Info    : Optimizing volume 1
Info    : Optimization starts (volume = 4.68e-06) with worst = 0.043499 / average = 0.765452:
Info    : 0.00 < quality < 0.10 :        11 elements
Info    : 0.10 < quality < 0.20 :        42 elements
Info    : 0.20 < quality < 0.30 :        58 elements
Info    : 0.30 < quality < 0.40 :        76 elements
Info    : 0.40 < quality < 0.50 :       113 elements
Info    : 0.50 < quality < 0.60 :       224 elements
Info    : 0.60 < quality < 0.70 :       797 elements
Info    : 0.70 < quality < 0.80 :      1513 elements
Info    : 0.80 < quality < 0.90 :      1954 elements
Info    : 0.90 < quality < 1.00 :       686 elements
Info    : 109 edge swaps, 0 node relocations (volume = 4.68e-06): worst = 0.170489 / average = 0.776728 (Wall 0.00127495s, CPU 0.001333s)
Info    : 111 edge swaps, 0 node relocations (volume = 4.68e-06): worst = 0.170489 / average = 0.776554 (Wall 0.00161795s, CPU 0.001698s)
Info    : No ill-shaped tets in the mesh :-)
Info    : 0.00 < quality < 0.10 :         0 elements
Info    : 0.10 < quality < 0.20 :         1 elements
Info    : 0.20 < quality < 0.30 :         1 elements
Info    : 0.30 < quality < 0.40 :        78 elements
Info    : 0.40 < quality < 0.50 :       110 elements
Info    : 0.50 < quality < 0.60 :       215 elements
Info    : 0.60 < quality < 0.70 :       809 elements
Info    : 0.70 < quality < 0.80 :      1521 elements
Info    : 0.80 < quality < 0.90 :      1956 elements
Info    : 0.90 < quality < 1.00 :       684 elements
Info    : Optimizing volume 2
Info    : Optimization starts (volume = 0.000691485) with worst = 0.00995949 / average = 0.774371:
Info    : 0.00 < quality < 0.10 :       299 elements
Info    : 0.10 < quality < 0.20 :       929 elements
Info    : 0.20 < quality < 0.30 :      1647 elements
Info    : 0.30 < quality < 0.40 :      2640 elements
Info    : 0.40 < quality < 0.50 :      4182 elements
Info    : 0.50 < quality < 0.60 :      7314 elements
Info    : 0.60 < quality < 0.70 :     15270 elements
Info    : 0.70 < quality < 0.80 :     32557 elements
Info    : 0.80 < quality < 0.90 :     51512 elements
Info    : 0.90 < quality < 1.00 :     26087 elements
Info    : 2833 edge swaps, 108 node relocations (volume = 0.000691485): worst = 0.146052 / average = 0.786592 (Wall 0.0896043s, CPU 0.090486s)
Info    : 2850 edge swaps, 110 node relocations (volume = 0.000691485): worst = 0.249473 / average = 0.786642 (Wall 0.117308s, CPU 0.118229s)
Info    : No ill-shaped tets in the mesh :-)
Info    : 0.00 < quality < 0.10 :         0 elements
Info    : 0.10 < quality < 0.20 :         0 elements
Info    : 0.20 < quality < 0.30 :         7 elements
Info    : 0.30 < quality < 0.40 :      2648 elements
Info    : 0.40 < quality < 0.50 :      4039 elements
Info    : 0.50 < quality < 0.60 :      7230 elements
Info    : 0.60 < quality < 0.70 :     15182 elements
Info    : 0.70 < quality < 0.80 :     32926 elements
Info    : 0.80 < quality < 0.90 :     51938 elements
Info    : 0.90 < quality < 1.00 :     25960 elements
Info    : Done optimizing mesh (Wall 0.34924s, CPU 0.326631s)
Info    : 26079 nodes 160343 elements
Info    : Optimizing mesh (Netgen)...
Info    : Optimizing volume 1
Info    : CalcLocalH: 1847 Points 5377 Elements 3682 Surface Elements 
Info    : Remove Illegal Elements 
Info    : 399 illegal tets 
Info    : SplitImprove 
Info    : badmax = 79.1788 
Info    : 55 splits performed 
Info    : SwapImprove  
Info    : 66 swaps performed 
Info    : SwapImprove2  
Info    : 1 swaps performed 
Info    : 311 illegal tets 
Info    : SplitImprove 
Info    : badmax = 30.1979 
Info    : 60 splits performed 
Info    : SwapImprove  
Info    : 32 swaps performed 
Info    : SwapImprove2  
Info    : 6 swaps performed 
Info    : 167 illegal tets 
Info    : SplitImprove 
Info    : badmax = 1829.43 
Info    : 44 splits performed 
Info    : SwapImprove  
Info    : 9 swaps performed 
Info    : SwapImprove2  
Info    : 3 swaps performed 
Info    : 57 illegal tets 
Info    : SplitImprove 
Info    : badmax = 3076.72 
Info    : 17 splits performed 
Info    : SwapImprove  
Info    : 3 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : 16 illegal tets 
Info    : SplitImprove 
Info    : badmax = 952.702 
Info    : 3 splits performed 
Info    : SwapImprove  
Info    : 0 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : 10 illegal tets 
Info    : SplitImprove 
Info    : badmax = 952.702 
Info    : 2 splits performed 
Info    : SwapImprove  
Info    : 0 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : 7 illegal tets 
Info    : SplitImprove 
Info    : badmax = 952.702 
Info    : 2 splits performed 
Info    : SwapImprove  
Info    : 1 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : 0 illegal tets 
Info    : Volume Optimization 
Info    : CombineImprove 
Info    : 48 elements combined 
Info    : ImproveMesh 
Info    : Total badness = 9677.41 
Info    : Total badness = 9485.14 
Info    : SplitImprove 
Info    : badmax = 36.321 
Info    : 1 splits performed 
Info    : ImproveMesh 
Info    : Total badness = 9490.21 
Info    : Total badness = 9477.09 
Info    : SwapImprove  
Info    : 278 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : ImproveMesh 
Info    : Total badness = 8983.19 
Info    : Total badness = 8900.51 
Info    : CombineImprove 
Info    : 6 elements combined 
Info    : ImproveMesh 
Info    : Total badness = 8843.31 
Info    : Total badness = 8838.64 
Info    : SplitImprove 
Info    : badmax = 13.2784 
Info    : 0 splits performed 
Info    : ImproveMesh 
Info    : Total badness = 8838.64 
Info    : Total badness = 8838.36 
Info    : SwapImprove  
Info    : 40 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : ImproveMesh 
Info    : Total badness = 8768.92 
Info    : Total badness = 8747.16 
Info    : CombineImprove 
Info    : 3 elements combined 
Info    : ImproveMesh 
Info    : Total badness = 8723.24 
Info    : Total badness = 8721.58 
Info    : SplitImprove 
Info    : badmax = 13.2756 
Info    : 0 splits performed 
Info    : ImproveMesh 
Info    : Total badness = 8721.58 
Info    : Total badness = 8721.53 
Info    : SwapImprove  
Info    : 20 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : ImproveMesh 
Info    : Total badness = 8699.54 
Info    : Total badness = 8688.24 
Info    : Optimizing volume 2
Info    : CalcLocalH: 26075 Points 139930 Elements 14694 Surface Elements 
Info    : Remove Illegal Elements 
Info    : 0 illegal tets 
Info    : Volume Optimization 
Info    : CombineImprove 
Info    : 1265 elements combined 
Info    : ImproveMesh 
Info    : Total badness = 181848 
Info    : Total badness = 173241 
Info    : SplitImprove 
Info    : badmax = 14.1834 
Info    : 0 splits performed 
Info    : ImproveMesh 
Info    : Total badness = 173241 
Info    : Total badness = 171897 
Info    : SwapImprove  
Info    : 7804 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : ImproveMesh 
Info    : Total badness = 158135 
Info    : Total badness = 154358 
Info    : CombineImprove 
Info    : 287 elements combined 
Info    : ImproveMesh 
Info    : Total badness = 151838 
Info    : Total badness = 151261 
Info    : SplitImprove 
Info    : badmax = 8.91933 
Info    : 0 splits performed 
Info    : ImproveMesh 
Info    : Total badness = 151261 
Info    : Total badness = 151150 
Info    : SwapImprove  
Info    : 952 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : ImproveMesh 
Info    : Total badness = 150467 
Info    : Total badness = 149885 
Info    : CombineImprove 
Info    : 56 elements combined 
Info    : ImproveMesh 
Info    : Total badness = 149406 
Info    : Total badness = 149297 
Info    : SplitImprove 
Info    : badmax = 7.26293 
Info    : 0 splits performed 
Info    : ImproveMesh 
Info    : Total badness = 149297 
Info    : Total badness = 149269 
Info    : SwapImprove  
Info    : 268 swaps performed 
Info    : SwapImprove2  
Info    : 0 swaps performed 
Info    : ImproveMesh 
Info    : Total badness = 149165 
Info    : Total badness = 148999 
Info    : Done optimizing mesh (Wall 7.31954s, CPU 7.3046s)
Info    : Writing 'open_ended_antenna.msh'...
Mesh saved to open_ended_antenna.mshInfo    : Done writing 'open_ended_antenna.msh'

  Nodes: 24598
  Elements: 144515

Mesh visualization.#

# Render the physical groups. The air sphere is rendered transparent.
view_mesh(filename, transparent_groups= "air_sphere__None")
Loading mesh file: open_ended_antenna.msh
Groups to render transparent: air_sphere__None

Mesh loaded successfully with 2 cell blocks
Found 14694 triangles total
Physical group tags in mesh: {3: 'top_conductor', 4: 'ground_plane', 5: 'lumped_port', 6: 'air_sphere__None', 7: 'air_sphere__substrate'}

Generate palace JSON config#

config_filename: str = "op_spalace.conf"
freq_min: float = 3.0
freq_max: float = 3.5
freq_step: float = 0.005
eps_r: float = 2.2
loss_tan: float = 0.0009
port_impedance: float = 50.0
solver_order: int = 2
sim = Simulation()

def attr(name):
    return [pg_map[name]] if name in pg_map else []

sim.set_mesh_file(f"/work/{filename}")
sim.set_config_option("Problem.Output", "/work/results/open_ended_antenna/")

sim.set_config_option("Domains.Materials", [
    {
        "Attributes": attr("substrate"),
        "Permittivity": eps_r,
        "Permeability": 1.0,
        "LossTan": loss_tan
    },
    {
        "Attributes": attr("air"),
        "Permittivity": 1.0,
        "Permeability": 1.0
    }
])

sim.set_config_option("Boundaries.PEC", {
    "Attributes": attr("ground_plane") + attr("patch")
})

sim.set_config_option("Boundaries.LumpedPort", [
    {
        "Index": 1,
        "Attributes": attr("lumped_port"),
        "R": port_impedance,
        "Excitation": True,
        "Direction": "+Z"
    }
])
sim.set_config_option("Boundaries.Absorbing", {
    "Attributes": attr("farfield"),
    "Order": 1
})

sim.set_config_option("Solver.Order", solver_order)
sim.set_config_option("Solver.Driven.MinFreq", freq_min)
sim.set_config_option("Solver.Driven.MaxFreq", freq_max)
sim.set_config_option("Solver.Driven.FreqStep", freq_step)
sim.set_config_option("Solver.Driven.AdaptiveTol", 0.001)

config_path = sim.write_config(config_filename)
print(f"Wrote {config_path}")
Palace config written to op_spalace.conf
Wrote op_spalace.conf