Materials and Geometry
Building a structure in rfx is two steps: pick a material — a built-in library name or one you register with add_material() — and attach it to a CSG shape with sim.add().
Material library
Section titled “Material library”MATERIAL_LIBRARY is a dict of pre-defined RF/microwave materials that ships with rfx.
from rfx import MATERIAL_LIBRARYprint(list(MATERIAL_LIBRARY.keys()))# ['vacuum', 'air', 'fr4', 'rogers4003c', 'rogers4350b', 'rt_duroid_5880',# 'alumina', 'silicon', 'ptfe', 'copper', 'aluminum', 'pec', 'water_20c']| Name | εᵣ | σ (S/m) | Notes |
|---|---|---|---|
vacuum | 1.0 | 0 | Lossless free space |
air | 1.0006 | 0 | Standard atmosphere |
fr4 | 4.4 | 0.025 | Lossy PCB laminate (constant-σ loss model) |
rogers4003c | 3.55 | 2.67×10⁻³ | Low-loss substrate; σ from tan δ 0.0027 @ 5 GHz (process Dk 3.55) |
rogers4350b | 3.48 | 7.16×10⁻³ | Low-loss substrate; σ from tan δ 0.0037 @ 10 GHz (design Dk 3.48) |
rt_duroid_5880 | 2.20 | 1.10×10⁻³ | Very-low-loss PTFE composite; σ from tan δ 0.0009 @ 10 GHz |
alumina | 9.8 | 0 | Ceramic substrate |
silicon | 11.9 | 0.01 | Lightly doped semiconductor |
ptfe | 2.1 | 0 | PTFE / Teflon |
copper | 1.0 | 5.8×10⁷ | Good conductor — mask-enforced as PEC (see below) |
aluminum | 1.0 | 3.5×10⁷ | Good conductor — mask-enforced as PEC (see below) |
pec | 1.0 | 1×10¹⁰ | Perfect electric conductor (mask-enforced) |
water_20c | 4.9 + Debye | 0 | Water at 20 °C with one Debye pole |
The tabulated σ for the substrate laminates is a fixed effective conductivity derived from the datasheet loss tangent at the stated frequency, not a frequency-dependent model.
Library materials are available directly by name; no add_material() call is needed:
from rfx import Simulation, Box
sim = Simulation(freq_max=10e9, domain=(0.05, 0.05, 0.02), boundary="cpml")sim.add(Box((0.005, 0.005, 0.005), (0.045, 0.045, 0.0066)), material="fr4")sim.add(Box((0.005, 0.005, 0.004), (0.045, 0.045, 0.005)), material="pec")Custom material registration
Section titled “Custom material registration”Register a named material with add_material() before using it in add().
sim.add_material( "my_ceramic", eps_r=9.0, sigma=0.001, mu_r=1.0, # optional, default 1.0)sim.add(Box((0.01, 0.01, 0.0), (0.04, 0.04, 0.002)), material="my_ceramic")add_material() returns self, so calls can be chained:
(sim .add_material("sub", eps_r=3.55, sigma=0.0) .add_material("metal", eps_r=1.0, sigma=5.8e7) .add(Box((0, 0, 0), (0.05, 0.03, 0.001)), material="sub") .add(Box((0.005, 0.005, 0.001), (0.045, 0.025, 0.0015)), material="metal"))Box is volumetric: every upper bound must exceed its lower bound. Use
add_thin_conductor(...) for a sheet whose physical thickness is below one
cell; a zero-thickness Box added with sim.add() rasterizes no volume.
Dispersive materials
Section titled “Dispersive materials”Debye poles
Section titled “Debye poles”Debye dispersion models frequency-dependent permittivity caused by dipolar relaxation (e.g., water, biological tissue):
ε(ω) = ε_∞ + Δε / (1 + jωτ)from rfx import DebyePole
sim.add_material( "water_body", eps_r=4.9, # ε_∞ (high-frequency limit) debye_poles=[ DebyePole(delta_eps=74.1, tau=8.3e-12), # primary relaxation ],)DebyePole fields:
| Field | Description |
|---|---|
delta_eps | Relaxation strength Δε = εₛ − ε_∞ |
tau | Relaxation time (seconds) |
Lorentz poles
Section titled “Lorentz poles”Lorentz poles model phonon resonances and plasma-like media. Build them from
physical parameters with the lorentz_pole() and drude_pole() helpers, which
return a LorentzPole and pass straight into lorentz_poles=[...]:
import numpy as npfrom rfx import lorentz_pole, drude_pole
# Lorentz oscillator from (Δε, resonance, damping)sim.add_material( "resonant_material", eps_r=1.0, lorentz_poles=[ lorentz_pole(delta_eps=2.0, omega_0=2 * np.pi * 10e9, delta=1e8), ],)
# Drude (free-electron) model for metals — gold, illustrative constantsgold = drude_pole(omega_p=1.37e16, gamma=4.05e13)sim.add_material("gold", eps_r=1.0, lorentz_poles=[gold])lorentz_pole() parameters:
| Parameter | Description |
|---|---|
delta_eps | Oscillator strength Δε (dimensionless) |
omega_0 | Resonance angular frequency (rad/s) |
delta | Damping coefficient (rad/s) |
drude_pole() parameters:
| Parameter | Description |
|---|---|
omega_p | Plasma frequency (rad/s) |
gamma | Collision rate (rad/s) |
Both helpers return the low-level LorentzPole(omega_0, delta, kappa) tuple used
internally, where kappa = delta_eps · omega_0² (Lorentz) or omega_p² (Drude).
Prefer the helpers over constructing LorentzPole directly.
Geometry: CSG shapes
Section titled “Geometry: CSG shapes”rfx uses constructive solid geometry (CSG). Shapes are rasterised onto the Yee grid at simulation time.
from rfx import Box
# Box(corner_lo, corner_hi) — axis-aligned rectangular prismslab = Box((0.0, 0.0, 0.0), (0.05, 0.05, 0.002))metal_volume = Box((0.01, 0.01, 0.002), (0.04, 0.04, 0.0025))Coordinates are in meters. corner_lo and corner_hi are (x, y, z) tuples.
Sphere
Section titled “Sphere”from rfx import Sphere
# Sphere(center, radius)ball = Sphere((0.025, 0.025, 0.010), radius=0.005)sim.add(ball, material="alumina")Cylinder
Section titled “Cylinder”from rfx import Cylinder
# Cylinder(center, radius, height, axis)# axis: "x", "y", or "z"via = Cylinder((0.025, 0.015, 0.000), radius=0.0005, height=0.002, axis="z")rod = Cylinder((0.010, 0.025, 0.000), radius=0.001, height=0.050, axis="x")sim.add(via, material="copper")MeshShape (CAD import)
Section titled “MeshShape (CAD import)”from rfx import MeshShape # needs: pip install "rfx-fdtd[cad]"
# STL / OBJ / PLY natively; STEP / STP via the cad extra's OpenCASCADE backend.# scale converts the file's length unit to metres (STL in mm -> scale=1e-3;# STEP is converted to metres already -> scale=1.0).part = MeshShape.from_file("bracket.stl", scale=1e-3, translate=(0.01, 0.0, 0.0))sim.add(part, material="pec")!!! warning “Not differentiable”
MeshShape rasterizes host-side via trimesh point-in-mesh containment
and raises on traced coordinates — CAD-imported geometry cannot carry
gradients through forward()/optimize(). Use the CSG primitives for
any shape that must be a design variable. The design-interop layer
(rfx.interop) likewise refuses MeshShape rather than approximating it.
Box, Sphere, and Cylinder cover most structures. rfx also ships
PolylineWire, Via, and CurvedPatch; see the
Geometry and Materials API reference for their
signatures.
Composing shapes with draw order
Section titled “Composing shapes with draw order”rfx has no Shape-level Boolean CSG that you can pass to sim.add(). Instead,
shapes are rasterised in the order they are added, and on overlapping cells a
later shape overwrites the (εᵣ, σ, μᵣ) fill of an earlier one. Carve or layer a
structure by adding shapes back-to-front:
# Dielectric block with a lower-εr insert carved into itsim.add(Box((0.010, 0.010, 0.0), (0.040, 0.040, 0.004)), material="alumina")sim.add(Box((0.020, 0.020, 0.0), (0.030, 0.030, 0.004)), material="air") # wins here!!! warning
Draw-order overwrite applies only to dielectric / conductivity fills. PEC
cells (σ ≥ 10⁶ S/m, including copper, aluminum, and pec) accumulate in
a Boolean mask that is never cleared, so a later shape cannot carve a hole
in PEC. Model hollow or patterned metal by placing conductor only where metal
should exist.
PEC mask enforcement
Section titled “PEC mask enforcement”Any material with σ ≥ 10⁶ S/m is enforced via a Boolean PEC mask rather than
through the conductivity update: the E-field is set to zero on every PEC cell at
each time step, which avoids the numerical instability that very large σ would
cause in the standard update equations. This threshold captures library pec
(σ = 10¹⁰) and the good-metal entries copper (5.8×10⁷) and aluminum
(3.5×10⁷) — all three are modelled as perfect conductors, so finite-conductivity
loss is not represented on this path.
!!! warning
To model a genuinely lossy conductor, keep σ below 10⁶ S/m; any material at or
above that threshold is silently promoted to PEC. The library copper and
aluminum entries are already above it, so they behave as perfect conductors,
not lossy metals.
Thin conductor correction
Section titled “Thin conductor correction”For printed traces and patches thinner than one cell, use the subcell thin-conductor model instead of refining the mesh to resolve the sheet:
from rfx import Box
trace = Box((0.010, 0.014, 0.0016), (0.040, 0.016, 0.0016))sim.add_thin_conductor( trace, sigma_bulk=5.8e7, # copper thickness=35e-6, # 1 oz copper = 35 µm)add_thin_conductor picks one of two regimes from sigma_bulk:
- σ_bulk ≥ 10⁶ S/m (copper, as above): the sheet is enforced as a thin PEC sheet — its cells are added to the PEC mask, no volumetric meshing needed. rfx emits a warning noting this routing. This is the common case for printed copper.
- σ_bulk < 10⁶ S/m (resistive films, inks): rfx sets an effective conductivity
σ_eff = σ_bulk · (thickness / Δx)on the intersected Yee cells rather than fully filling them, preserving the sheet resistanceR_s = 1 / (σ_bulk · thickness).