REMI Lab
OPEN KNOWLEDGE / TOOLS

Quick Start

이 페이지의 내용

This page walks through one complete simulation: a Gaussian pulse passing through a dielectric slab in open space. Every rfx model follows the same six steps:

  1. Domain and mesh. Create a Simulation with its size, cell size and boundaries.
  2. Materials and geometry. Register materials and place shapes.
  3. Excitation. Add a source, or a port if you want S-parameters.
  4. Observables. Add probes, monitors or far-field boxes.
  5. Preflight. Let rfx check the setup before you spend time on a solve.
  6. Run and read. Call run() and read the returned Result.

The example runs in a few seconds on a laptop CPU.

1. Domain and mesh

import numpy as np
from rfx import Simulation, Box, GaussianPulse
sim = Simulation(
freq_max=5e9, # highest frequency you care about (Hz)
domain=(0.06, 0.04, 0.04), # x, y, z size in metres
dx=2e-3, # cubic cell size in metres
boundary="cpml", # absorbing (open) boundary on every face
cpml_layers=8, # absorber thickness in cells
)
  • All lengths are in metres, frequencies in hertz, and conductivity in S/m.
  • freq_max does two things. It sets the default time step, and it is the frequency preflight uses to judge whether the mesh is fine enough.
  • The CPML absorber is added outside the domain you give, so domain is the region you model. Structures that reach the domain edge continue into the absorber, which is how an infinite slab or a line leaving the box is modelled. Keep sources and probes a few cells in from the edge.

2. Materials and geometry

sim.add_material("slab", eps_r=3.0, sigma=0.005)
sim.add(Box((0.028, 0.0, 0.0), (0.036, 0.04, 0.04)), material="slab")

Box(corner_lo, corner_hi) is an axis-aligned block. Here the slab is 8 mm thick along x and spans the whole cross-section. Library materials such as "fr4", "rogers4350b" and "pec" need no add_material call. See Materials and Geometry.

3. Excitation

sim.add_source(
(0.014, 0.02, 0.02),
"ez",
waveform=GaussianPulse(f0=3e9, bandwidth=0.8),
amplitude_kind="current",
)

This is a soft point source that drives Ez in one cell. amplitude_kind="current" means the waveform amplitude is a current in amperes. Always pass it explicitly. A point source has no impedance, so it gives fields, resonances and far fields but not S-parameters. Use a port for those; see Sources and Ports.

4. Observables

sim.add_probe((0.020, 0.02, 0.02), "ez") # probe 0: between source and slab
sim.add_probe((0.046, 0.02, 0.02), "ez") # probe 1: behind the slab

A probe records one field component at one point for every time step.

5. Preflight

report = sim.preflight()
print(report.format())
report.raise_for_failure()

Preflight looks for mistakes that give a wrong answer without crashing:

  • a mesh too coarse for the material and freq_max;
  • structures or probes inside the absorber;
  • geometry that does not land where you drew it;
  • a lossless resonator whose Q can never converge.

Errors stop the run. Warnings are advisory, but read every one of them. run() repeats the check and prints any warnings.

Try eps_r=4.0 on the slab. At 2 mm cells that leaves exactly 15 cells per wavelength inside the dielectric, and preflight will warn about phase accuracy.

6. Run and read the result

result = sim.run(n_steps=1000)
ts = np.asarray(result.time_series) # shape (n_steps, n_probes)
print(ts.shape, f"dt = {result.dt:.3e} s")
spectrum = np.abs(np.fft.rfft(ts, axis=0))
freqs = np.fft.rfftfreq(ts.shape[0], d=result.dt)
in_band = (freqs > 1e9) & (freqs < 5e9)
ratio = spectrum[in_band, 1] / spectrum[in_band, 0]
print("settling:", result.settling_witness["status"]) # "absent" here: nothing to judge
  • result.time_series has one column per probe, in registration order.
  • result.settling_db judges whether the record was long enough for the spectral results the run computed (S-parameters, DFT planes, flux monitors, far fields). After the source ends, rfx fits the poles still ringing in the record and estimates, at every frequency those results read, how much of the value comes from what rings on past the record’s end. settling_db is the worst of those shares in dB; at or below −40 dB the record is long enough. result.settling_witness["status"] says pass, fail, undetermined (the source was still on, or the fit could not be judged) or absent (nothing the run reads was recorded to judge, as here). Only pass is a pass.
  • This run reads only probe time series, so the check is absent and the FFT above is yours to judge: if the probe signals have not decayed to a small fraction of their peak by the last step, run longer.

The ratio above only compares the spectra at two points. It is not a transmission coefficient, because probe 0 sees the incident and reflected waves together. For reflection and transmission, use two flux monitors and a reference run without the slab. The Probes and S-Parameters guide shows how.

Choosing the run length

run() offers three ways to decide how long to step:

ArgumentMeaning
n_steps=Nexactly N time steps
num_periods=PP periods at freq_max (default 20)
until_decay=1e-3step until the field energy has decayed to that fraction of its peak, up to decay_max_steps

With absorbing boundaries, until_decay watches the total energy inside the domain, which is a reliable stop for spectra, flux and S-parameters. A closed PEC box never loses energy, so there it falls back to a single field sample (on a graded mesh, a closed-boundary until_decay is refused instead). That fallback is only reliable for lossy or radiating ring-down, so use a fixed n_steps in a closed structure. Also use a fixed n_steps when you compare runs, for example in a convergence study.

Where to go next

  • Core Concepts: the mental model behind the six steps, including cells, boundaries, conductors, sources and ports.
  • Your First Patch Antenna: a real device on a graded mesh.
  • Examples: the ordered learning path of runnable scripts.
Back to top ↑