{ "cells": [ { "cell_type": "markdown", "id": "2034d9a9", "metadata": { "papermill": { "duration": 0.002551, "end_time": "2026-05-27T20:02:19.863987+00:00", "exception": false, "start_time": "2026-05-27T20:02:19.861436+00:00", "status": "completed" }, "tags": [] }, "source": [ "### Vivaldi antenna\n", "\n", "Full parametric Vivaldi antenna mesh using gmsh/OpenCASCADE.\n", "\n", "The exponential taper profile follows:\n", "$$y = \\pm\\frac{w_s}{2} \\cdot e^{C(x - x_0)}$$\n", "where $C$ is the opening rate (25 here) and $x_0$ is the taper start.\n", "\n", "**Coordinate convention** (matches the diagram, x is the long axis):\n", "- Ground plane centred at origin in XY plane\n", "- Aperture opens toward +x\n", "- Cavity is on the −x side\n", "- Z is vertical (substrate thickness)" ] }, { "cell_type": "code", "execution_count": 11, "id": "49322625", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:19.871351Z", "iopub.status.busy": "2026-05-27T20:02:19.871175Z", "iopub.status.idle": "2026-05-27T20:02:20.239971Z", "shell.execute_reply": "2026-05-27T20:02:20.239461Z" }, "papermill": { "duration": 0.37286, "end_time": "2026-05-27T20:02:20.240994+00:00", "exception": false, "start_time": "2026-05-27T20:02:19.868134+00:00", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "import gmsh\n", "import math\n", "import numpy as np\n", "from palacetoolkit.mesh import (\n", " Entity, \n", " run_meshing_pipeline, \n", " generate_3d_mesh, \n", " refine_near_surfaces\n", ")\n", "from palacetoolkit.viz import run_with_scrollable_output, view_mesh \n" ] }, { "cell_type": "markdown", "id": "ae61f8ba", "metadata": { "papermill": { "duration": 0.00394, "end_time": "2026-05-27T20:02:20.247279+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.243339+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Antenna parameters" ] }, { "cell_type": "code", "execution_count": 12, "id": "396c6947", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.252949Z", "iopub.status.busy": "2026-05-27T20:02:20.252625Z", "iopub.status.idle": "2026-05-27T20:02:20.257443Z", "shell.execute_reply": "2026-05-27T20:02:20.256974Z" }, "papermill": { "duration": 0.008424, "end_time": "2026-05-27T20:02:20.258056+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.249632+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ground plane: [-0.1500, 0.1500]\n", "Taper starts at: x = -0.0930\n", "Parallel section: x = [-0.1160, -0.0930] length = 23.0 mm (= cavity_to_taper)\n", "Cavity centre at: x = -0.1280\n", "Cavity right edge: x = -0.1160 (= x_taper_start - s)\n" ] } ], "source": [ "taper_length: float = 0.243 \n", "aperture_width: float = 0.105 \n", "opening_rate: float = 25.0 \n", "slot_width: float = 5e-4 \n", "cavity_diameter: float = 0.024 \n", "cavity_to_taper: float = 0.023 \n", "ground_plane_length: float = 0.300 \n", "ground_plane_width: float = 0.125 \n", "h_sub: float = 0.015 \n", "air_height: float = 0.05 \n", "air_margin: float = 0.05\n", "\n", "freq_ghz = 4.5\n", "c0 = 3e8\n", "wavelength = c0 / (freq_ghz * 1e9)\n", "\n", "mesh_file = \"vivaldi.msh\"\n", "\n", "# Derived coordinates\n", "Lx = ground_plane_length\n", "Ly = ground_plane_width\n", "\n", "# Left edge of the ground plane.\n", "x0 = -Lx/2 \n", "\n", "# Right edge of the ground plane.\n", "x1 = Lx/2 \n", "\n", "# x_taper_start: where the exponential section begins\n", "x_taper_start = x1 - taper_length\n", "\n", "# x_slot_left: start of parallel section = end of cavity = x_taper_start - s\n", "x_slot_left = x_taper_start - cavity_to_taper\n", "\n", "# cavity centre\n", "x_cav = x_slot_left - cavity_diameter / 2\n", "\n", "print(f\"Ground plane: [{x0:.4f}, {x1:.4f}]\")\n", "print(f\"Taper starts at: x = {x_taper_start:.4f}\")\n", "print(f\"Parallel section: x = [{x_slot_left:.4f}, {x_taper_start:.4f}] \"\n", " f\"length = {cavity_to_taper*1e3:.1f} mm (= cavity_to_taper)\")\n", "print(f\"Cavity centre at: x = {x_cav:.4f}\")\n", "print(f\"Cavity right edge: x = {x_slot_left:.4f} (= x_taper_start - s)\")\n" ] }, { "cell_type": "markdown", "id": "taper_math", "metadata": { "papermill": { "duration": 0.001973, "end_time": "2026-05-27T20:02:20.262490+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.260517+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Exponential taper mathematics\n", "\n", "The upper edge of the Vivaldi slot follows:\n", "$$y_{\\text{upper}}(x) = \\frac{w_s}{2} \\cdot e^{C(x - x_{\\text{ts}})}$$\n", "scaled so that $y_{\\text{upper}}(x_1) = w_a/2$.\n", "\n", "We solve for the normalisation constant $A$:\n", "$$A = \\frac{w_a/2}{e^{C(x_1 - x_{\\text{ts}})}}$$\n", "which gives:\n", "$$y_{\\text{upper}}(x) = A \\cdot e^{C(x - x_{\\text{ts}})}$$\n", "\n", "The lower edge is the mirror: $y_{\\text{lower}} = -y_{\\text{upper}}$." ] }, { "cell_type": "code", "execution_count": 13, "id": "taper_func", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.267403Z", "iopub.status.busy": "2026-05-27T20:02:20.267182Z", "iopub.status.idle": "2026-05-27T20:02:20.270265Z", "shell.execute_reply": "2026-05-27T20:02:20.269902Z" }, "papermill": { "duration": 0.006451, "end_time": "2026-05-27T20:02:20.271000+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.264549+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "y at taper start : +0.00012 (expected ≈ +0.00025)\n", "y at aperture : +0.05250 (expected ≈ +0.05250)\n" ] } ], "source": [ "def taper_y(x: float, sign: float = 1.0) -> float:\n", " \n", " # Normalisation: amplitude A chosen so y(x1) = aperture_width/2\n", " A = (aperture_width / 2) / math.exp(opening_rate * (x1 - x_taper_start))\n", " \n", " return sign * A * math.exp(opening_rate * (x - x_taper_start))\n", "\n", "# Quick sanity checks\n", "print(f\"y at taper start : {taper_y(x_taper_start):+.5f} (expected ≈ {slot_width/2:+.5f})\")\n", "print(f\"y at aperture : {taper_y(x1):+.5f} (expected ≈ {aperture_width/2:+.5f})\")" ] }, { "cell_type": "markdown", "id": "gmsh_init_md", "metadata": { "papermill": { "duration": 0.00211, "end_time": "2026-05-27T20:02:20.275683+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.273573+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Initialise gmsh" ] }, { "cell_type": "code", "execution_count": 14, "id": "7054c50f", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.280342Z", "iopub.status.busy": "2026-05-27T20:02:20.280186Z", "iopub.status.idle": "2026-05-27T20:02:20.283421Z", "shell.execute_reply": "2026-05-27T20:02:20.282977Z" }, "papermill": { "duration": 0.006346, "end_time": "2026-05-27T20:02:20.284045+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.277699+00:00", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "gmsh.initialize()\n", "gmsh.model.add(\"vivaldi_antenna\")\n", "kernel = gmsh.model.occ " ] }, { "cell_type": "markdown", "id": "volumes_md", "metadata": { "papermill": { "duration": 0.001871, "end_time": "2026-05-27T20:02:20.287968+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.286097+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Build the 3-D volumes (substrate + air sphere)" ] }, { "cell_type": "code", "execution_count": 15, "id": "c3ee11e8", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.292561Z", "iopub.status.busy": "2026-05-27T20:02:20.292417Z", "iopub.status.idle": "2026-05-27T20:02:20.295692Z", "shell.execute_reply": "2026-05-27T20:02:20.295263Z" }, "papermill": { "duration": 0.00617, "end_time": "2026-05-27T20:02:20.296066+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.289896+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Substrate tag: 1\n", "Air sphere tag: 2\n" ] } ], "source": [ "# Bounding box extents \n", "total_xmin = x0 - air_margin\n", "total_xmax = x1 + air_margin\n", "total_ymin = -Ly/2 - air_margin\n", "total_ymax = Ly/2 + air_margin\n", "total_zmax = h_sub + air_height\n", "\n", "# Substrate \n", "substrate = kernel.addBox(\n", " x0, -Ly/2, 0,\n", " Lx, Ly, h_sub\n", ")\n", "\n", "# Air sphere (replace air box, consistent with patch_antenna workflow)\n", "airsphere_radius = max(abs(total_xmin), abs(total_xmax), abs(total_ymin), abs(total_ymax), total_zmax)\n", "air_sphere = kernel.addSphere(0.0, 0.0, 0.0, airsphere_radius)\n", "\n", "print(\"Substrate tag:\", substrate)\n", "print(\"Air sphere tag:\", air_sphere)" ] }, { "cell_type": "markdown", "id": "copper_md", "metadata": { "papermill": { "duration": 0.001954, "end_time": "2026-05-27T20:02:20.300083+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.298129+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Build the copper patch (ground plane + taper slot + cavity)\n", "\n", "Strategy:\n", "1. Start with a full rectangular ground-plane surface.\n", "2. Subtract the exponential slot (built from a spline boundary).\n", "3. Subtract the circular cavity.\n", "4. The result is the physical copper surface at z = h_sub." ] }, { "cell_type": "code", "execution_count": 16, "id": "copper_patch", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.304875Z", "iopub.status.busy": "2026-05-27T20:02:20.304649Z", "iopub.status.idle": "2026-05-27T20:02:20.313937Z", "shell.execute_reply": "2026-05-27T20:02:20.313489Z" }, "papermill": { "duration": 0.012274, "end_time": "2026-05-27T20:02:20.314387+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.302113+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Parallel section: x=[-0.1160, -0.0930] y=±0.00025\n", "Exponential section: x=[-0.0930, 0.1500]\n", "Aperture width at x1: 105.00 mm (target 105.00 mm)\n", "Top rectangle tag: 8\n", "Slot surface tag: 9\n", "Cavity surface tag: 10\n" ] } ], "source": [ "# Top rectangle. We´ll cut the slot and cavity out of this.\n", "top_rect = kernel.addRectangle(x0, -Ly/2, h_sub, Lx, Ly)\n", "\n", "# Parallel section geometry.\n", "p_ul = kernel.addPoint(x_slot_left, +slot_width/2, h_sub)\n", "p_ur = kernel.addPoint(x_taper_start, +slot_width/2, h_sub)\n", "p_lr = kernel.addPoint(x_taper_start, -slot_width/2, h_sub)\n", "p_ll = kernel.addPoint(x_slot_left, -slot_width/2, h_sub)\n", "\n", "# Straight lines\n", "line_top_par = kernel.addLine(p_ul, p_ur) # upper parallel edge\n", "line_bot_par = kernel.addLine(p_lr, p_ll) # lower parallel edge (reversed for CCW)\n", "line_left = kernel.addLine(p_ll, p_ul) # left closing line (at x_slot_left)\n", "\n", "# Interpolate points along the exponential taper curve from x_taper_start to x1.\n", "N_pts = 100\n", "xs = np.linspace(x_taper_start, x1, N_pts)\n", "\n", "# Upper spline\n", "upper_inner = [kernel.addPoint(float(x), taper_y(float(x), +1.0), h_sub)\n", " for x in xs[1:]]\n", "upper_spline = kernel.addSpline([p_ur] + upper_inner)\n", "\n", "# Lower spline: starts at p_lr, reversed direction for CCW loop.\n", "lower_inner = [kernel.addPoint(float(x), taper_y(float(x), -1.0), h_sub)\n", " for x in xs[1:]]\n", "\n", "# In the loop we traverse lower in reverse (aperture → taper_start),\n", "# so we list points aperture-end first.\n", "lower_spline = kernel.addSpline(lower_inner[::-1] + [p_lr])\n", "\n", "# Aperture closing line (right edge, x = x1)\n", "p_apt = upper_inner[-1] # top-right aperture point\n", "p_apb = lower_inner[-1] # bottom-right aperture point\n", "line_aperture = kernel.addLine(p_apt, p_apb)\n", "\n", "slot_loop = kernel.addCurveLoop([\n", " line_left, # up the left edge (x_slot_left, bot→top)\n", " line_top_par, # rightward along upper parallel\n", " upper_spline, # upper exponential curve to aperture\n", " line_aperture, # down the aperture edge\n", " lower_spline, # lower exponential back to x_taper_start (reversed)\n", " line_bot_par, # leftward along lower parallel back to start\n", "])\n", "\n", "slot_surf = kernel.addPlaneSurface([slot_loop])\n", "\n", "# Circular cavity\n", "cav_r = cavity_diameter / 2\n", "cav_cx = x_cav\n", "cav_cy = 0.0\n", "\n", "cav_circle = kernel.addCircle(cav_cx, cav_cy, h_sub, cav_r)\n", "cav_loop = kernel.addCurveLoop([cav_circle])\n", "cav_surf = kernel.addPlaneSurface([cav_loop])\n", "\n", "print(f\"Parallel section: x=[{x_slot_left:.4f}, {x_taper_start:.4f}] y=±{slot_width/2:.5f}\")\n", "print(f\"Exponential section: x=[{x_taper_start:.4f}, {x1:.4f}]\")\n", "print(f\"Aperture width at x1: {2*taper_y(x1,1)*1e3:.2f} mm (target {aperture_width*1e3:.2f} mm)\")\n", "print(\"Top rectangle tag: \", top_rect)\n", "print(\"Slot surface tag: \", slot_surf)\n", "print(\"Cavity surface tag: \", cav_surf)" ] }, { "cell_type": "markdown", "id": "feed_md", "metadata": { "papermill": { "duration": 0.001966, "end_time": "2026-05-27T20:02:20.318557+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.316591+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Feed port\n", "\n", "From the inset diagram, the feed port is a **square face** in the YZ plane\n", "at the left edge of the ground plane (`x = x0`). It is centred on the slot\n", "(`y = 0`) and spans the substrate thickness in z.\n", "\n", "- `feed_offset` is the **x-axis** offset that positions where along the slot\n", " the port is referenced — here it locates the port at `x = x0 + feed_offset`\n", " inside the ground plane, but the excitation face itself sits flush at\n", " `x = x0` (the left wall of the computational domain).\n", "- The port is square: width = height = `slot_width` in the YZ cross-section.\n", "- It is centred at `y = 0, z = h_sub / 2` (mid-height of the substrate)." ] }, { "cell_type": "code", "execution_count": 17, "id": "feed_strip", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.323243Z", "iopub.status.busy": "2026-05-27T20:02:20.323093Z", "iopub.status.idle": "2026-05-27T20:02:20.326790Z", "shell.execute_reply": "2026-05-27T20:02:20.326377Z" }, "papermill": { "duration": 0.006756, "end_time": "2026-05-27T20:02:20.327292+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.320536+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Feed port surface tag: 11\n", "Port centre: x=-0.0932 y=0 z=0.01500 (top of substrate)\n", "Port x range: [-0.0935, -0.0930]\n", "Port y range: [-0.00025, 0.00025]\n", "Port size: 0.50 mm × 0.50 mm (square)\n", "Sanity: port_x_ctr (-0.0932) should be between cavity right edge (-0.1160) and taper start (-0.0930)\n" ] } ], "source": [ "port_size = slot_width # square side length [m]\n", "port_x_ctr = x_taper_start - port_size / 2 # port centre along x\n", "port_x0 = port_x_ctr - port_size / 2 # left x of port\n", "port_y0 = -port_size / 2 # bottom y (centred on slot)\n", "\n", "# Flat XY-plane rectangle at z = h_sub — no rotation needed.\n", "feed_port_surf = kernel.addRectangle(\n", " port_x0, # x start\n", " port_y0, # y start (centred: -w_s/2 .. +w_s/2)\n", " h_sub, # z = top of substrate\n", " port_size, # dx = slot_width (square in x)\n", " port_size, # dy = slot_width (square in y, touches both copper edges)\n", ")\n", "\n", "print(f\"Feed port surface tag: {feed_port_surf}\")\n", "print(f\"Port centre: x={port_x_ctr:.4f} y=0 z={h_sub:.5f} (top of substrate)\")\n", "print(f\"Port x range: [{port_x0:.4f}, {port_x0+port_size:.4f}]\")\n", "print(f\"Port y range: [{port_y0:.5f}, {-port_y0:.5f}]\")\n", "print(f\"Port size: {port_size*1e3:.2f} mm × {port_size*1e3:.2f} mm (square)\")\n", "print(f\"Sanity: port_x_ctr ({port_x_ctr:.4f}) should be between \"\n", " f\"cavity right edge ({x_cav + cavity_diameter/2:.4f}) \"\n", " f\"and taper start ({x_taper_start:.4f})\")" ] }, { "cell_type": "markdown", "id": "boolean_md", "metadata": { "papermill": { "duration": 0.001988, "end_time": "2026-05-27T20:02:20.331385+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.329397+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Boolean operations — assemble the copper patch\n", "\n", "Cut the slot and cavity out of the ground-plane rectangle.\n", "The feed strip is kept separate (it is a distinct conductor patch)." ] }, { "cell_type": "code", "execution_count": 18, "id": "boolean_ops", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.336694Z", "iopub.status.busy": "2026-05-27T20:02:20.336478Z", "iopub.status.idle": "2026-05-27T20:02:20.347289Z", "shell.execute_reply": "2026-05-27T20:02:20.346755Z" }, "papermill": { "duration": 0.013803, "end_time": "2026-05-27T20:02:20.347700+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.333897+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Copper patch surfaces after boolean cut: \n", " dim=2, tag=8\n" ] } ], "source": [ "kernel.synchronize()\n", "\n", "# Cut slot + cavity from ground-plane rectangle\n", "# BooleanCut returns (result_dimtags, map)\n", "copper_patch, _ = kernel.cut(\n", " [(2, top_rect)], # object: full rectangle\n", " [(2, slot_surf), (2, cav_surf)], # tools: slot + cavity\n", " removeObject=True, removeTool=True\n", ")\n", "\n", "kernel.synchronize()\n", "print(\"Copper patch surfaces after boolean cut:\")\n", "for dim, tag in copper_patch:\n", " print(f\" dim={dim}, tag={tag}\")" ] }, { "cell_type": "markdown", "id": "embed_md", "metadata": { "papermill": { "duration": 0.002539, "end_time": "2026-05-27T20:02:20.352824+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.350285+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Entity definition. \n", "\n", "In order to get a good mesh we need to fragment it and restore it, run_meshing_pipeline does this and also defines the physical groups." ] }, { "cell_type": "code", "execution_count": 19, "id": "7691895e", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:20.358127Z", "iopub.status.busy": "2026-05-27T20:02:20.357919Z", "iopub.status.idle": "2026-05-27T20:02:30.335499Z", "shell.execute_reply": "2026-05-27T20:02:30.334975Z" }, "papermill": { "duration": 9.981465, "end_time": "2026-05-27T20:02:30.336552+00:00", "exception": false, "start_time": "2026-05-27T20:02:20.355087+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Physical group 'substrate' (dim=3): pg=1, tags=[1] \n", " Physical group 'air_sphere' (dim=3): pg=2, tags=[2]\n", " Physical group 'copper_patch' (dim=2): pg=3, tags=[8]\n", " Physical group 'feed_port' (dim=2): pg=4, tags=[11]\n", " Physical group 'air_sphere__substrate' (dim=2): pg=5, tags=[12, 13, 14, 15, 5, 16, 17, 18]\n", " Physical group 'air_sphere__None' (dim=2): pg=6, tags=[19]\n", " ppw_near=300 ppw_far=5\n", " SizeMax=0.0133 transition=0.0167\n", " global: 16 curves, SizeMin=0.0002\n", "Info : Meshing 1D...\n", "Info : [ 0%] Meshing curve 4 (Line)\n", "Info : [ 10%] Meshing curve 8 (Line)\n", "Info : [ 10%] Meshing curve 9 (Line)\n", "Info : [ 20%] Meshing curve 11 (Line)\n", "Info : [ 20%] Meshing curve 12 (Line)\n", "Info : [ 20%] Meshing curve 13 (Line)\n", "Info : [ 30%] Meshing curve 14 (Line)\n", "Info : [ 30%] Meshing curve 15 (Line)\n", "Info : [ 30%] Meshing curve 16 (Line)\n", "Info : [ 40%] Meshing curve 17 (Line)\n", "Info : [ 40%] Meshing curve 18 (Line)\n", "Info : [ 40%] Meshing curve 19 (Line)\n", "Info : [ 50%] Meshing curve 20 (Line)\n", "Info : [ 50%] Meshing curve 21 (Line)\n", "Info : [ 60%] Meshing curve 22 (Circle)\n", "Info : [ 60%] Meshing curve 23 (BSpline)\n", "Info : [ 60%] Meshing curve 24 (Line)\n", "Info : [ 70%] Meshing curve 25 (BSpline)\n", "Info : [ 70%] Meshing curve 26 (Line)\n", "Info : [ 70%] Meshing curve 27 (Line)\n", "Info : [ 80%] Meshing curve 28 (Line)\n", "Info : [ 80%] Meshing curve 29 (Line)\n", "Info : [ 80%] Meshing curve 30 (Line)\n", "Info : [ 90%] Meshing curve 32 (Circle)\n", "Info : [100%] Meshing curve 34 (Line)\n", "Info : [100%] Meshing curve 35 (Line)\n", "Info : Done meshing 1D (Wall 1.88283s, CPU 1.81569s)\n", "Info : Meshing 2D...\n", "Info : [ 0%] Meshing surface 5 (Plane, MeshAdapt)\n", "Info : [ 10%] Meshing surface 8 (Plane, MeshAdapt)\n", "Info : [ 20%] Meshing surface 11 (Plane, MeshAdapt)\n", "Info : [ 30%] Meshing surface 12 (Plane, MeshAdapt)\n", "Info : [ 40%] Meshing surface 13 (Plane, MeshAdapt)\n", "Info : [ 50%] Meshing surface 14 (Plane, MeshAdapt)\n", "Info : [ 60%] Meshing surface 15 (Plane, MeshAdapt)\n", "Info : [ 70%] Meshing surface 16 (Plane, MeshAdapt)\n", "Info : [ 80%] Meshing surface 17 (Plane, MeshAdapt)\n", "Info : [ 90%] Meshing surface 18 (Plane, MeshAdapt)\n", "Info : [100%] Meshing surface 19 (Sphere, MeshAdapt)\n", "Info : Done meshing 2D (Wall 5.92237s, CPU 5.90935s)\n", "Info : Meshing 3D...\n", "Info : 3D Meshing 2 volumes with 1 connected component\n", "Info : Tetrahedrizing 18829 nodes...\n", "Info : Done tetrahedrizing 18837 nodes (Wall 0.295651s, CPU 0.290184s)\n", "Info : Reconstructing mesh...\n", "Info : - Creating surface mesh\n", "Info : - Identifying boundary edges\n", "Info : - Recovering boundary\n", "Info : Done reconstructing mesh (Wall 0.580503s, CPU 0.575125s)\n", "Info : Found volume 2\n", "Info : Found volume 1\n", "Info : It. 0 - 0 nodes created - worst tet radius 34.952 (nodes removed 0 0)\n", "Info : It. 500 - 496 nodes created - worst tet radius 3.19998 (nodes removed 0 4)\n", "Info : It. 1000 - 992 nodes created - worst tet radius 3.29385 (nodes removed 0 8)\n", "Info : It. 1500 - 1488 nodes created - worst tet radius 2.98787 (nodes removed 0 12)\n", "Info : It. 2000 - 1980 nodes created - worst tet radius 2.21376 (nodes removed 0 20)\n", "Info : It. 2500 - 2480 nodes created - worst tet radius 2.03058 (nodes removed 0 20)\n", "Info : It. 3000 - 2980 nodes created - worst tet radius 1.97895 (nodes removed 0 20)\n", "Info : It. 3500 - 3476 nodes created - worst tet radius 1.8197 (nodes removed 0 24)\n", "Info : It. 4000 - 3972 nodes created - worst tet radius 1.74223 (nodes removed 0 28)\n", "Info : It. 4500 - 4469 nodes created - worst tet radius 1.67595 (nodes removed 0 31)\n", "Info : It. 5000 - 4968 nodes created - worst tet radius 1.61457 (nodes removed 0 32)\n", "Info : It. 5500 - 5467 nodes created - worst tet radius 1.86937 (nodes removed 0 33)\n", "Info : It. 6000 - 5967 nodes created - worst tet radius 1.51066 (nodes removed 0 33)\n", "Info : It. 6500 - 6467 nodes created - worst tet radius 1.46905 (nodes removed 0 33)\n", "Info : It. 7000 - 6967 nodes created - worst tet radius 1.43197 (nodes removed 0 33)\n", "Info : It. 7500 - 7461 nodes created - worst tet radius 1.39799 (nodes removed 0 39)\n", "Info : It. 8000 - 7961 nodes created - worst tet radius 1.36684 (nodes removed 0 39)\n", "Info : It. 8500 - 8455 nodes created - worst tet radius 1.33936 (nodes removed 0 45)\n", "Info : It. 9000 - 8955 nodes created - worst tet radius 1.31112 (nodes removed 0 45)\n", "Info : It. 9500 - 9452 nodes created - worst tet radius 1.28486 (nodes removed 0 48)\n", "Info : It. 10000 - 9952 nodes created - worst tet radius 1.26075 (nodes removed 0 48)\n", "Info : It. 10500 - 10452 nodes created - worst tet radius 1.2391 (nodes removed 0 48)\n", "Info : It. 11000 - 10950 nodes created - worst tet radius 1.21802 (nodes removed 0 50)\n", "Info : It. 11500 - 11450 nodes created - worst tet radius 1.19886 (nodes removed 0 50)\n", "Info : It. 12000 - 11950 nodes created - worst tet radius 1.1814 (nodes removed 0 50)\n", "Info : It. 12500 - 12450 nodes created - worst tet radius 1.16408 (nodes removed 0 50)\n", "Info : It. 13000 - 12950 nodes created - worst tet radius 1.14617 (nodes removed 0 50)\n", "Info : It. 13500 - 13450 nodes created - worst tet radius 1.13022 (nodes removed 0 50)\n", "Info : It. 14000 - 13950 nodes created - worst tet radius 1.11679 (nodes removed 0 50)\n", "Info : It. 14500 - 14450 nodes created - worst tet radius 1.10362 (nodes removed 0 50)\n", "Info : It. 15000 - 14950 nodes created - worst tet radius 1.1308 (nodes removed 0 50)\n", "Info : It. 15500 - 15448 nodes created - worst tet radius 1.07942 (nodes removed 0 52)\n", "Info : It. 16000 - 15948 nodes created - worst tet radius 1.06716 (nodes removed 0 52)\n", "Info : It. 16500 - 16448 nodes created - worst tet radius 1.0563 (nodes removed 0 52)\n", "Info : It. 17000 - 16948 nodes created - worst tet radius 1.04573 (nodes removed 0 52)\n", "Info : It. 17500 - 17448 nodes created - worst tet radius 1.03526 (nodes removed 0 52)\n", "Info : It. 18000 - 17948 nodes created - worst tet radius 1.02558 (nodes removed 0 52)\n", "Info : It. 18500 - 18448 nodes created - worst tet radius 1.01597 (nodes removed 0 52)\n", "Info : It. 19000 - 18948 nodes created - worst tet radius 1.00774 (nodes removed 0 52)\n", "Info : 3D refinement terminated (38264 nodes total):\n", "Info : - 26 Delaunay cavities modified for star shapeness\n", "Info : - 52 nodes could not be inserted\n", "Info : - 216865 tetrahedra created in 2.73033 sec. (79428 tets/s)\n", "Info : 4 node relocations\n", "Info : Done meshing 3D (Wall 4.25849s, CPU 4.26071s)\n", "Info : Optimizing mesh...\n", "Info : Optimizing volume 1\n", "Info : Optimization starts (volume = 0.0005625) with worst = 0.000289261 / average = 0.592864:\n", "Info : 0.00 < quality < 0.10 : 297 elements\n", "Info : 0.10 < quality < 0.20 : 801 elements\n", "Info : 0.20 < quality < 0.30 : 1681 elements\n", "Info : 0.30 < quality < 0.40 : 5167 elements\n", "Info : 0.40 < quality < 0.50 : 8958 elements\n", "Info : 0.50 < quality < 0.60 : 11495 elements\n", "Info : 0.60 < quality < 0.70 : 11119 elements\n", "Info : 0.70 < quality < 0.80 : 9732 elements\n", "Info : 0.80 < quality < 0.90 : 5468 elements\n", "Info : 0.90 < quality < 1.00 : 1491 elements\n", "Info : 2287 edge swaps, 100 node relocations (volume = 0.0005625): worst = 0.000309648 / average = 0.608497 (Wall 0.0431121s, CPU 0.043229s)\n", "Info : 2423 edge swaps, 115 node relocations (volume = 0.0005625): worst = 0.000329345 / average = 0.6088 (Wall 0.0576463s, CPU 0.057853s)\n", "Info : 2441 edge swaps, 120 node relocations (volume = 0.0005625): worst = 0.000329345 / average = 0.608884 (Wall 0.0702314s, CPU 0.070517s)\n", "Info : 0.00 < quality < 0.10 : 6 elements\n", "Info : 0.10 < quality < 0.20 : 1 elements\n", "Info : 0.20 < quality < 0.30 : 375 elements\n", "Info : 0.30 < quality < 0.40 : 5510 elements\n", "Info : 0.40 < quality < 0.50 : 9027 elements\n", "Info : 0.50 < quality < 0.60 : 11469 elements\n", "Info : 0.60 < quality < 0.70 : 11237 elements\n", "Info : 0.70 < quality < 0.80 : 9789 elements\n", "Info : 0.80 < quality < 0.90 : 5513 elements\n", "Info : 0.90 < quality < 1.00 : 1489 elements\n", "Info : Optimizing volume 2\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Warning : 2 ill-shaped tets are still in the mesh\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Info : Optimization starts (volume = 0.0328953) with worst = 0.000224913 / average = 0.678141:\n", "Info : 0.00 < quality < 0.10 : 589 elements\n", "Info : 0.10 < quality < 0.20 : 1579 elements\n", "Info : 0.20 < quality < 0.30 : 3071 elements\n", "Info : 0.30 < quality < 0.40 : 7814 elements\n", "Info : 0.40 < quality < 0.50 : 15648 elements\n", "Info : 0.50 < quality < 0.60 : 23015 elements\n", "Info : 0.60 < quality < 0.70 : 27328 elements\n", "Info : 0.70 < quality < 0.80 : 31555 elements\n", "Info : 0.80 < quality < 0.90 : 34975 elements\n", "Info : 0.90 < quality < 1.00 : 15082 elements\n", "Info : 4755 edge swaps, 225 node relocations (volume = 0.0328953): worst = 0.000316063 / average = 0.692736 (Wall 0.126227s, CPU 0.125273s)\n", "Info : 4882 edge swaps, 254 node relocations (volume = 0.0328953): worst = 0.000316063 / average = 0.692877 (Wall 0.162867s, CPU 0.161823s)\n", "Info : 0.00 < quality < 0.10 : 8 elements\n", "Info : 0.10 < quality < 0.20 : 0 elements\n", "Info : 0.20 < quality < 0.30 : 240 elements\n", "Info : 0.30 < quality < 0.40 : 7963 elements\n", "Info : 0.40 < quality < 0.50 : 15629 elements\n", "Info : 0.50 < quality < 0.60 : 23060 elements\n", "Info : 0.60 < quality < 0.70 : 27511 elements\n", "Info : 0.70 < quality < 0.80 : 31853 elements\n", "Info : 0.80 < quality < 0.90 : 35235 elements\n", "Info : 0.90 < quality < 1.00 : 14954 elements\n", "Info : Done optimizing mesh (Wall 0.5531s, CPU 0.529983s)\n", "Info : 38460 nodes 253619 elements\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Warning : 2 ill-shaped tets are still in the mesh\n", "Warning : ------------------------------\n", "Warning : Mesh generation error summary\n", "Warning : 2 warnings\n", "Warning : 0 errors\n", "Warning : Check the full log for details\n", "Warning : ------------------------------\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Info : Optimizing mesh (Netgen)...\n", "Info : Optimizing volume 1\n", "Info : CalcLocalH: 17912 Points 54439 Elements 30154 Surface Elements \n", "Info : Remove Illegal Elements \n", "Info : 5520 illegal tets \n", "Info : SplitImprove \n", "Info : badmax = 313633 \n", "Info : 751 splits performed \n", "Info : SwapImprove \n", "Info : 456 swaps performed \n", "Info : SwapImprove2 \n", "Info : 7 swaps performed \n", "Info : 4151 illegal tets \n", "Info : SplitImprove \n", "Info : badmax = 73860.9 \n", "Info : 772 splits performed \n", "Info : SwapImprove \n", "Info : 154 swaps performed \n", "Info : SwapImprove2 \n", "Info : 141 swaps performed \n", "Info : 2307 illegal tets \n", "Info : SplitImprove \n", "Info : badmax = 9194.41 \n", "Info : 566 splits performed \n", "Info : SwapImprove \n", "Info : 82 swaps performed \n", "Info : SwapImprove2 \n", "Info : 66 swaps performed \n", "Info : 680 illegal tets \n", "Info : SplitImprove \n", "Info : badmax = 9194.41 \n", "Info : 199 splits performed \n", "Info : SwapImprove \n", "Info : 15 swaps performed \n", "Info : SwapImprove2 \n", "Info : 12 swaps performed \n", "Info : 52 illegal tets \n", "Info : SplitImprove \n", "Info : badmax = 9194.41 \n", "Info : 16 splits performed \n", "Info : SwapImprove \n", "Info : 2 swaps performed \n", "Info : SwapImprove2 \n", "Info : 1 swaps performed \n", "Info : 3 illegal tets \n", "Info : SplitImprove \n", "Info : badmax = 9194.41 \n", "Info : 1 splits performed \n", "Info : SwapImprove \n", "Info : 0 swaps performed \n", "Info : SwapImprove2 \n", "Info : 0 swaps performed \n", "Info : 0 illegal tets \n", "Info : Volume Optimization \n", "Info : CombineImprove \n", "Info : 737 elements combined \n", "Info : ImproveMesh \n", "Info : Total badness = 144887 \n", "Info : Total badness = 132150 \n", "Info : SplitImprove \n", "Info : badmax = 8364.09 \n", "Info : 1 splits performed \n", "Info : ImproveMesh \n", "Info : Total badness = 132174 \n", "Info : Total badness = 131064 \n", "Info : SwapImprove \n", "Info : 3859 swaps performed \n", "Info : SwapImprove2 \n", "Info : 3 swaps performed \n", "Info : ImproveMesh \n", "Info : Total badness = 122719 \n", "Info : Total badness = 117831 \n", "Info : CombineImprove \n", "Info : 129 elements combined \n", "Info : ImproveMesh \n", "Info : Total badness = 116163 \n", "Info : Total badness = 115773 \n", "Info : SplitImprove \n", "Info : badmax = 7026.46 \n", "Info : 0 splits performed \n", "Info : ImproveMesh \n", "Info : Total badness = 115773 \n", "Info : Total badness = 115745 \n", "Info : SwapImprove \n", "Info : 1511 swaps performed \n", "Info : SwapImprove2 \n", "Info : 0 swaps performed \n", "Info : ImproveMesh \n", "Info : Total badness = 113432 \n", "Info : Total badness = 111438 \n", "Info : CombineImprove \n", "Info : 33 elements combined \n", "Info : ImproveMesh \n", "Info : Total badness = 111069 \n", "Info : Total badness = 110950 \n", "Info : SplitImprove \n", "Info : badmax = 7006.38 \n", "Info : 0 splits performed \n", "Info : ImproveMesh \n", "Info : Total badness = 110950 \n", "Info : Total badness = 110944 \n", "Info : SwapImprove \n", "Info : 732 swaps performed \n", "Info : SwapImprove2 \n", "Info : 0 swaps performed \n", "Info : ImproveMesh \n", "Info : Total badness = 110077 \n", "Info : Total badness = 109160 \n", "Info : Optimizing volume 2\n", "Info : CalcLocalH: 35431 Points 156676 Elements 37650 Surface Elements \n", "Info : Remove Illegal Elements \n", "Info : 0 illegal tets \n", "Info : Volume Optimization \n", "Info : CombineImprove \n", "Info : 644 elements combined \n", "Info : ImproveMesh \n", "Info : Total badness = 263887 \n", "Info : Total badness = 237532 \n", "Info : SplitImprove \n", "Info : badmax = 20417.9 \n", "Info : 1 splits performed \n", "Info : ImproveMesh \n", "Info : Total badness = 237611 \n", "Info : Total badness = 234276 \n", "Info : SwapImprove \n", "Info : 8283 swaps performed \n", "Info : SwapImprove2 \n", "Info : 0 swaps performed \n", "Info : ImproveMesh \n", "Info : Total badness = 219376 \n", "Info : Total badness = 214404 \n", "Info : CombineImprove \n", "Info : 135 elements combined \n", "Info : ImproveMesh \n", "Info : Total badness = 213139 \n", "Info : Total badness = 212597 \n", "Info : SplitImprove \n", "Info : badmax = 7390.53 \n", "Info : 0 splits performed \n", "Info : ImproveMesh \n", "Info : Total badness = 212597 \n", "Info : Total badness = 212495 \n", "Info : SwapImprove \n", "Info : 1586 swaps performed \n", "Info : SwapImprove2 \n", "Info : 0 swaps performed \n", "Info : ImproveMesh \n", "Info : Total badness = 211012 \n", "Info : Total badness = 209983 \n", "Info : CombineImprove \n", "Info : 32 elements combined \n", "Info : ImproveMesh \n", "Info : Total badness = 209706 \n", "Info : Total badness = 209607 \n", "Info : SplitImprove \n", "Info : badmax = 7234.06 \n", "Info : 0 splits performed \n", "Info : ImproveMesh \n", "Info : Total badness = 209607 \n", "Info : Total badness = 209588 \n", "Info : SwapImprove \n", "Info : 574 swaps performed \n", "Info : SwapImprove2 \n", "Info : 0 swaps performed \n", "Info : ImproveMesh \n", "Info : Total badness = 209152 \n", "Info : Total badness = 208774 \n", "Info : Done optimizing mesh (Wall 12.149s, CPU 12.1483s)\n", "Info : Writing 'vivaldi.msh'...\n", "Mesh saved to vivaldi.mshInfo : Done writing 'vivaldi.msh'\n", "\n", " Nodes: 39057\n", " Elements: 250451\n", "Info : Writing 'vivaldi.msh'...\n", "Info : Done writing 'vivaldi.msh'\n" ] } ], "source": [ "entities = [\n", " Entity(\"copper_patch\", dim=2, btype=\"pec\", mesh_order=1, tags=[copper_patch[0][1]]),\n", " Entity(\"substrate\", dim=3, btype=\"dielectric\", mesh_order=1, tags=[substrate], loss_tan=0.0009, eps_r=2.2, mu_r=1.0),\n", " Entity(\"air_sphere\", dim=3, btype=\"dielectric\", mesh_order=2, tags=[air_sphere], loss_tan=0.0, eps_r=1.0, mu_r=1.0),\n", " Entity(\"feed_port\", dim=2, btype=\"waveport\", mesh_order=0, tags=[feed_port_surf]),\n", "]\n", "\n", "pg_map = run_meshing_pipeline(entities)\n", "refine_near_surfaces(entities[0].dimtags + entities[-1].dimtags, \n", " wavelength, \n", " ppw_near=300, \n", " ppw_far=5, \n", " transition_distance=wavelength/4,\n", " set_as_background=True)\n", "\n", "generate_3d_mesh(entities, mesh_file, optimize=True)\n", "gmsh.option.setNumber(\"Mesh.MshFileVersion\", 2.2)\n", "gmsh.write(mesh_file)\n", "gmsh.finalize()" ] }, { "cell_type": "markdown", "id": "mesh_md", "metadata": { "papermill": { "duration": 0.003282, "end_time": "2026-05-27T20:02:30.343738+00:00", "exception": false, "start_time": "2026-05-27T20:02:30.340456+00:00", "status": "completed" }, "tags": [] }, "source": [ "#### Mesh generation" ] }, { "cell_type": "code", "execution_count": 20, "id": "93234454", "metadata": { "execution": { "iopub.execute_input": "2026-05-27T20:02:30.351224Z", "iopub.status.busy": "2026-05-27T20:02:30.351030Z", "iopub.status.idle": "2026-05-27T20:02:31.656985Z", "shell.execute_reply": "2026-05-27T20:02:31.656288Z" }, "papermill": { "duration": 1.32537, "end_time": "2026-05-27T20:02:31.672340+00:00", "exception": false, "start_time": "2026-05-27T20:02:30.346970+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading mesh file: vivaldi.msh\n", "Groups to render transparent: air_sphere__None\n", "\n", "Mesh loaded successfully with 2 cell blocks\n", "Found 37650 triangles total\n", "Physical group tags in mesh: {3: 'copper_patch', 4: 'feed_port', 5: 'air_sphere__substrate', 6: 'air_sphere__None'}\n" ] }, { "data": { "text/html": [ "
" ], "text/plain": [ "