# Your First Patch Antenna

Channel: release; package: 1.8.0; source: 7d1e97331656dd74ad0f5471653ac0c5effdff70

Page: https://remilab.ai/rfx/versions/v1.8.0/guide/first-patch/
Source: https://github.com/bk-squared/rfx/blob/7d1e97331656dd74ad0f5471653ac0c5effdff70/docs/public/guide/first-patch.mdx

This tutorial builds a finite-ground-plane patch model and runs a short setup
check. It does not assign a resonant frequency, return loss, or Q to the model
without a mesh/time-window study and an independent reference.

## 1. Calculate starting dimensions

The usual transmission-line formulas provide starting dimensions, not a solver
validation target:

```python
import numpy as np

C0 = 299_792_458.0
f0 = 2.4e9
eps_r = 4.4
h = 1.5e-3
tan_d = 0.02

W = C0 / (2 * f0) * np.sqrt(2 / (eps_r + 1))
eps_eff = (eps_r + 1) / 2 + (eps_r - 1) / 2 * (1 + 12 * h / W) ** -0.5
dL = 0.412 * h * (
    (eps_eff + 0.3) * (W / h + 0.264)
    / ((eps_eff - 0.258) * (W / h + 0.8))
)
L = C0 / (2 * f0 * np.sqrt(eps_eff)) - 2 * dL

# Constant-conductivity approximation to tan(delta) at f0 only.
sigma_fr4 = 2 * np.pi * f0 * 8.854_187_8128e-12 * eps_r * tan_d
print(f"L={L * 1e3:.2f} mm, W={W * 1e3:.2f} mm")
```

FR4 permittivity and loss vary by material system, frequency, copper roughness,
and manufacturing process. Replace these nominal values with the data needed by
the intended comparison.

## 2. Build a graded z mesh

The model keeps the structure outside eight CPML cells, uses six cells through
the substrate, and grades between 1 mm air cells and 0.25 mm substrate cells
with adjacent ratios below 1.3.

```python
from rfx import Box, GaussianPulse, Simulation

dx = 1.0e-3
dz_sub = h / 6

# 1.0 mm -> 0.25 mm transition; max adjacent ratio is below 1.3.
transition = np.array([1.000, 0.780, 0.610, 0.477, 0.373, 0.291, 0.250]) * 1e-3
lower_air = np.concatenate([np.full(10, dx), transition[1:]])
upper_air = np.concatenate([transition[-2::-1], np.full(24, dx)])
dz_profile = np.concatenate([lower_air, np.full(6, dz_sub), upper_air])

z_sub_lo = float(lower_air.sum())
z_sub_hi = z_sub_lo + h

dom_x, dom_y = 0.100, 0.095
sim = Simulation(
    freq_max=4e9,
    domain=(dom_x, dom_y, 0.0),  # z extent is the sum of dz_profile
    dx=dx,
    dz_profile=dz_profile,
    boundary="cpml",
    cpml_layers=8,
)
```

Do not specify an independent nonzero z-domain length with a `dz_profile`; its
sum defines the z extent. Preserve 1 mm first and last cells so the z-boundary
cell size matches `dx`.

## 3. Register the stack, source, and probe

The ground and patch are subcell sheets. A zero-thickness `Box` passed to
`sim.add()` would rasterize no volume, so the sheets use
`add_thin_conductor()` instead.

```python
sim.add_material("fr4_design", eps_r=eps_r, sigma=sigma_fr4)

gx_lo, gx_hi = 0.020, 0.080
gy_lo, gy_hi = 0.020, 0.075
sim.add(
    Box((gx_lo, gy_lo, z_sub_lo), (gx_hi, gy_hi, z_sub_hi)),
    material="fr4_design",
)

# Copper values at or above 1e6 S/m are routed to the PEC sheet mask.
ground = Box((gx_lo, gy_lo, z_sub_lo), (gx_hi, gy_hi, z_sub_lo))
sim.add_thin_conductor(ground, sigma_bulk=5.8e7, thickness=35e-6)

px0 = dom_x / 2 - L / 2
px1 = dom_x / 2 + L / 2
py0 = dom_y / 2 - W / 2
py1 = dom_y / 2 + W / 2
patch = Box((px0, py0, z_sub_hi), (px1, py1, z_sub_hi))
sim.add_thin_conductor(patch, sigma_bulk=5.8e7, thickness=35e-6)

src_z = z_sub_lo + 2.5 * dz_sub
feed = (px0 + 8e-3, dom_y / 2, src_z)
probe = (dom_x / 2 + 5e-3, dom_y / 2 + 5e-3, src_z)
sim.add_source(feed, "ez", waveform=GaussianPulse(f0=f0, bandwidth=0.8),
               amplitude_kind="current")   # required from 1.9
sim.add_probe(probe, "ez")

sim.preflight(strict=True)
```

The thin-conductor calls warn that the copper sheet is routed to PEC. That is
the represented model: conductor skin depth, surface roughness, and finite
copper loss are not included. Two routes put loss back.

The documented one is
`add_thin_conductor(shape, sigma_bulk=5.8e7, surface_impedance_f0=f0)`, which
models the sheet as a resistive sheet of sheet resistance
`Rs0 = sqrt(pi*f0*mu0/sigma_bulk)` — the thick-conductor surface resistance at
the band centre `f0` . Its scope, from the
"Surface-impedance sheets" row of `docs/guides/support_matrix.md`: it "supports
patterned shapes that rasterize to exactly one cell layer and at least one cell
on its documented uniform and graded-mesh lanes. It is not a general dispersive
or multi-runner conductor model." One frequency, one cell layer.

The cruder route is a finite sheet-resistance approximation through
`sigma_bulk`, which requires an explicitly justified value below 1e6 S/m — that
is the threshold at which the sheet stops being routed to PEC, so anything above
it silently reverts to the lossless model.

## 4. Check execution before interpreting RF results

A short run verifies execution and finite probe data only:

```python
import numpy as np

short_run = sim.run(num_periods=2)
assert short_run.time_series.size > 0
assert np.isfinite(np.asarray(short_run.time_series)).all()
```

It is too short to establish resonance or Q. For a resonance study, use a much
longer fixed window, discard the driven portion, and handle the case where
`find_resonances(...)` returns no modes. Repeat with finer x/y and z meshes,
larger air margins, and longer windows. Report the movement of the selected
mode, not just the finest result.

The short run may emit the ring-down-truncated advisory. That is expected for
this short window and is another reason not to interpret its spectrum.

The source above is a soft field source, not a 50-ohm feed. It cannot establish
return loss. An impedance-referenced study must use the matching port family and
calculator from [Sources and Ports](https://remilab.ai/rfx/versions/v1.8.0/guide/sources-ports/) and remain within
its support limits.

## 5. External comparison

From a source checkout, `validation/crossval/05_patch_antenna.py` runs the
repository's rfx/OpenEMS patch comparison. OpenEMS and CSXCAD are separate
requirements. If either is unavailable, the script reports a skipped comparison
and exits with status `2`, not a pass.
The script's evidence applies only to its committed geometry, mesh, frequency
band, metric, and tolerances. Do not transfer its frequency or Q to the model
above or to a modified feed without a new comparison.

Continue with [Examples](https://remilab.ai/rfx/versions/v1.8.0/examples/) for the ordered learning sequence; the
cross-validation script above is an accuracy fixture, not a lesson.
