diff --git a/nbs/_palace_ihp_rf_devices_mesh.ipynb b/nbs/_palace_ihp_rf_devices_mesh.ipynb
new file mode 100644
index 0000000..2e037b6
--- /dev/null
+++ b/nbs/_palace_ihp_rf_devices_mesh.ipynb
@@ -0,0 +1,881 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "49ac428f",
+ "metadata": {},
+ "source": [
+ "# Palace Mesh Generation: IHP RF Devices\n",
+ "\n",
+ "This notebook discovers RF components from `ihp.cells.rf_devices`, prints their ports, and generates Palace meshes for each component.\n",
+ "\n",
+ "**Requirements:**\n",
+ "- IHP PDK from GitHub (`pip install git+https://github.com/gdsfactory/IHP.git`)\n",
+ "- gsim environment with Palace meshing dependencies"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e270eea1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "WARNING:2026-06-09 18:31:05,866:jax._src.xla_bridge:852: An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n"
+ ]
+ }
+ ],
+ "source": [
+ "from __future__ import annotations\n",
+ "\n",
+ "from pathlib import Path\n",
+ "\n",
+ "import ihp.cells.rf_devices as rf_devices\n",
+ "import pandas as pd\n",
+ "from ihp import PDK\n",
+ "\n",
+ "from gsim.common.stack import get_stack\n",
+ "from gsim.palace import DrivenSim\n",
+ "\n",
+ "PDK.activate()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8f79956b",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Discovered RF components:\n",
+ "- branch_line_coupler\n",
+ "- coupled_line_bandpass_filter\n",
+ "- coupler_tline\n",
+ "- directional_coupler\n",
+ "- hairpin_coupled_line_bandpass_filter\n",
+ "- quarter_wave_transformer\n",
+ "- wilkinson_power_divider\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Discover RF component factories defined in ihp.cells.rf_devices and registered in the active PDK.\n",
+ "rf_module_factories = {\n",
+ " name\n",
+ " for name, obj in vars(rf_devices).items()\n",
+ " if callable(obj) and not name.startswith(\"_\")\n",
+ "}\n",
+ "\n",
+ "rf_keywords = (\n",
+ " \"wilkinson\",\n",
+ " \"filter\",\n",
+ " \"coupler\",\n",
+ " \"divider\",\n",
+ " \"directional\",\n",
+ " \"hybrid\",\n",
+ " \"branch\",\n",
+ " \"transformer\",\n",
+ ")\n",
+ "\n",
+ "component_names = sorted(\n",
+ " name\n",
+ " for name in PDK.cells\n",
+ " if name in rf_module_factories and any(k in name.lower() for k in rf_keywords)\n",
+ ")\n",
+ "\n",
+ "print(\"Discovered RF components:\")\n",
+ "for name in component_names:\n",
+ " print(f\"- {name}\")\n",
+ "\n",
+ "if not component_names:\n",
+ " raise RuntimeError(\n",
+ " \"No RF device components were discovered in ihp.cells.rf_devices\"\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9d585d09",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " component | \n",
+ " port | \n",
+ " orientation_deg | \n",
+ " width_um | \n",
+ " center_um | \n",
+ " layer | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " branch_line_coupler | \n",
+ " e1 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (-100.0, 0.84) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " branch_line_coupler | \n",
+ " e2 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (3838.657, 0.84) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " branch_line_coupler | \n",
+ " e3 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (3838.657, -3733.505) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " branch_line_coupler | \n",
+ " e4 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (-100.0, -3733.505) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " coupled_line_bandpass_filter | \n",
+ " e1 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (0.0, 0.0) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " coupled_line_bandpass_filter | \n",
+ " e2 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (15037.564, 25.204) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " coupler_tline | \n",
+ " e1 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (0.0, 13.442) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 7 | \n",
+ " coupler_tline | \n",
+ " e2 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (10.0, 13.442) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 8 | \n",
+ " coupler_tline | \n",
+ " e3 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (10.0, -13.442) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 9 | \n",
+ " coupler_tline | \n",
+ " e4 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (0.0, -13.442) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 10 | \n",
+ " directional_coupler | \n",
+ " e1 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (-100.0, 1.108) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 11 | \n",
+ " directional_coupler | \n",
+ " e2 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (3834.391, 1.108) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 12 | \n",
+ " directional_coupler | \n",
+ " e3 | \n",
+ " 270.0 | \n",
+ " 1.68 | \n",
+ " (3735.231, -101.94800000000001) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 13 | \n",
+ " directional_coupler | \n",
+ " e4 | \n",
+ " 270.0 | \n",
+ " 1.68 | \n",
+ " (-0.84, -101.94800000000001) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 14 | \n",
+ " hairpin_coupled_line_bandpass_filter | \n",
+ " e1 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (0.0, 0.0) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 15 | \n",
+ " hairpin_coupled_line_bandpass_filter | \n",
+ " e2 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (148.196, 0.0) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 16 | \n",
+ " quarter_wave_transformer | \n",
+ " e1 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (-100.0, 0.0) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 17 | \n",
+ " quarter_wave_transformer | \n",
+ " e2 | \n",
+ " 0.0 | \n",
+ " 0.20 | \n",
+ " (846.878, 0.0) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 18 | \n",
+ " wilkinson_power_divider | \n",
+ " e1 | \n",
+ " 180.0 | \n",
+ " 1.68 | \n",
+ " (0.0, 0.0) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 19 | \n",
+ " wilkinson_power_divider | \n",
+ " e2 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (731.163, 17.506) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ " | 20 | \n",
+ " wilkinson_power_divider | \n",
+ " e3 | \n",
+ " 0.0 | \n",
+ " 1.68 | \n",
+ " (731.163, -17.506) | \n",
+ " TopMetal2drawing | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " component port orientation_deg width_um \\\n",
+ "0 branch_line_coupler e1 180.0 1.68 \n",
+ "1 branch_line_coupler e2 0.0 1.68 \n",
+ "2 branch_line_coupler e3 0.0 1.68 \n",
+ "3 branch_line_coupler e4 180.0 1.68 \n",
+ "4 coupled_line_bandpass_filter e1 180.0 1.68 \n",
+ "5 coupled_line_bandpass_filter e2 0.0 1.68 \n",
+ "6 coupler_tline e1 180.0 1.68 \n",
+ "7 coupler_tline e2 0.0 1.68 \n",
+ "8 coupler_tline e3 0.0 1.68 \n",
+ "9 coupler_tline e4 180.0 1.68 \n",
+ "10 directional_coupler e1 180.0 1.68 \n",
+ "11 directional_coupler e2 0.0 1.68 \n",
+ "12 directional_coupler e3 270.0 1.68 \n",
+ "13 directional_coupler e4 270.0 1.68 \n",
+ "14 hairpin_coupled_line_bandpass_filter e1 180.0 1.68 \n",
+ "15 hairpin_coupled_line_bandpass_filter e2 0.0 1.68 \n",
+ "16 quarter_wave_transformer e1 180.0 1.68 \n",
+ "17 quarter_wave_transformer e2 0.0 0.20 \n",
+ "18 wilkinson_power_divider e1 180.0 1.68 \n",
+ "19 wilkinson_power_divider e2 0.0 1.68 \n",
+ "20 wilkinson_power_divider e3 0.0 1.68 \n",
+ "\n",
+ " center_um layer \n",
+ "0 (-100.0, 0.84) TopMetal2drawing \n",
+ "1 (3838.657, 0.84) TopMetal2drawing \n",
+ "2 (3838.657, -3733.505) TopMetal2drawing \n",
+ "3 (-100.0, -3733.505) TopMetal2drawing \n",
+ "4 (0.0, 0.0) TopMetal2drawing \n",
+ "5 (15037.564, 25.204) TopMetal2drawing \n",
+ "6 (0.0, 13.442) TopMetal2drawing \n",
+ "7 (10.0, 13.442) TopMetal2drawing \n",
+ "8 (10.0, -13.442) TopMetal2drawing \n",
+ "9 (0.0, -13.442) TopMetal2drawing \n",
+ "10 (-100.0, 1.108) TopMetal2drawing \n",
+ "11 (3834.391, 1.108) TopMetal2drawing \n",
+ "12 (3735.231, -101.94800000000001) TopMetal2drawing \n",
+ "13 (-0.84, -101.94800000000001) TopMetal2drawing \n",
+ "14 (0.0, 0.0) TopMetal2drawing \n",
+ "15 (148.196, 0.0) TopMetal2drawing \n",
+ "16 (-100.0, 0.0) TopMetal2drawing \n",
+ "17 (846.878, 0.0) TopMetal2drawing \n",
+ "18 (0.0, 0.0) TopMetal2drawing \n",
+ "19 (731.163, 17.506) TopMetal2drawing \n",
+ "20 (731.163, -17.506) TopMetal2drawing "
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Build each component and tabulate ports.\n",
+ "port_rows = []\n",
+ "components = {}\n",
+ "\n",
+ "for name in component_names:\n",
+ " c = PDK.cells[name]()\n",
+ " components[name] = c\n",
+ " port_rows.extend(\n",
+ " {\n",
+ " \"component\": name,\n",
+ " \"port\": p.name,\n",
+ " \"orientation_deg\": p.orientation,\n",
+ " \"width_um\": p.width,\n",
+ " \"center_um\": tuple(float(x) for x in p.center),\n",
+ " \"layer\": str(p.layer),\n",
+ " }\n",
+ " for p in c.ports\n",
+ " )\n",
+ "\n",
+ "ports_df = (\n",
+ " pd.DataFrame(port_rows).sort_values([\"component\", \"port\"]).reset_index(drop=True)\n",
+ ")\n",
+ "ports_df"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "5ac0ae94",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Small conductor feature detected (1.680 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.\n",
+ "Small conductor feature detected (1.634 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.\n",
+ "Small conductor feature detected (1.680 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.\n",
+ "Small conductor feature detected (0.536 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.\n",
+ "Small conductor feature detected (1.078 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.\n",
+ "Small conductor feature detected (0.200 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.\n",
+ "Small conductor feature detected (0.200 um) may be under-resolved by refined_mesh_size=5.000 um. Pass auto_size=True to scale the mesh down.\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " component | \n",
+ " status | \n",
+ " mesh_path | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " branch_line_coupler | \n",
+ " ok | \n",
+ " palace-sim-ihp-rf/branch_line_coupler/palace.msh | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " coupled_line_bandpass_filter | \n",
+ " ok | \n",
+ " palace-sim-ihp-rf/coupled_line_bandpass_filter... | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " coupler_tline | \n",
+ " ok | \n",
+ " palace-sim-ihp-rf/coupler_tline/palace.msh | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " directional_coupler | \n",
+ " ok | \n",
+ " palace-sim-ihp-rf/directional_coupler/palace.msh | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " hairpin_coupled_line_bandpass_filter | \n",
+ " ok | \n",
+ " palace-sim-ihp-rf/hairpin_coupled_line_bandpas... | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " quarter_wave_transformer | \n",
+ " ok | \n",
+ " palace-sim-ihp-rf/quarter_wave_transformer/pal... | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " wilkinson_power_divider | \n",
+ " ok | \n",
+ " palace-sim-ihp-rf/wilkinson_power_divider/pala... | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " component status \\\n",
+ "0 branch_line_coupler ok \n",
+ "1 coupled_line_bandpass_filter ok \n",
+ "2 coupler_tline ok \n",
+ "3 directional_coupler ok \n",
+ "4 hairpin_coupled_line_bandpass_filter ok \n",
+ "5 quarter_wave_transformer ok \n",
+ "6 wilkinson_power_divider ok \n",
+ "\n",
+ " mesh_path \n",
+ "0 palace-sim-ihp-rf/branch_line_coupler/palace.msh \n",
+ "1 palace-sim-ihp-rf/coupled_line_bandpass_filter... \n",
+ "2 palace-sim-ihp-rf/coupler_tline/palace.msh \n",
+ "3 palace-sim-ihp-rf/directional_coupler/palace.msh \n",
+ "4 palace-sim-ihp-rf/hairpin_coupled_line_bandpas... \n",
+ "5 palace-sim-ihp-rf/quarter_wave_transformer/pal... \n",
+ "6 palace-sim-ihp-rf/wilkinson_power_divider/pala... "
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Generate a mesh per component under ./palace-sim-ihp-rf/.\n",
+ "stack = get_stack()\n",
+ "base_dir = Path(\"./palace-sim-ihp-rf\")\n",
+ "base_dir.mkdir(parents=True, exist_ok=True)\n",
+ "\n",
+ "mesh_rows = []\n",
+ "\n",
+ "for name, c in components.items():\n",
+ " out_dir = base_dir / name\n",
+ " out_dir.mkdir(parents=True, exist_ok=True)\n",
+ "\n",
+ " try:\n",
+ " sim = DrivenSim()\n",
+ " sim.set_output_dir(out_dir)\n",
+ " sim.set_geometry(c)\n",
+ " sim.set_stack(stack)\n",
+ "\n",
+ " # Add all component ports as via ports so port geometries are meshed.\n",
+ " for port in c.ports:\n",
+ " sim.add_port(\n",
+ " port.name,\n",
+ " from_layer=\"metal3\",\n",
+ " to_layer=\"topmetal2\",\n",
+ " geometry=\"via\",\n",
+ " )\n",
+ "\n",
+ " sim.set_driven(fmin=1e9, fmax=110e9, num_points=21)\n",
+ " result = sim.mesh(preset=\"default\")\n",
+ "\n",
+ " mesh_rows.append(\n",
+ " {\n",
+ " \"component\": name,\n",
+ " \"status\": \"ok\",\n",
+ " \"mesh_path\": str(result.mesh_path),\n",
+ " }\n",
+ " )\n",
+ " except Exception as e:\n",
+ " mesh_rows.append(\n",
+ " {\n",
+ " \"component\": name,\n",
+ " \"status\": \"error\",\n",
+ " \"mesh_path\": \"\",\n",
+ " \"error\": str(e),\n",
+ " }\n",
+ " )\n",
+ "\n",
+ "mesh_df = (\n",
+ " pd.DataFrame(mesh_rows).sort_values([\"status\", \"component\"]).reset_index(drop=True)\n",
+ ")\n",
+ "mesh_df"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9b6f6027",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/markdown": [
+ "### branch_line_coupler"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "71f860ee590848b8a628c671f1e69a39",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Widget(value='