REMI Lab
OPEN KNOWLEDGE / TOOLS

Simulation

이 페이지의 내용

Simulation holds one model: the domain and mesh, boundaries, materials, geometry, sources, ports and observables. You build it with add_* calls, check it with preflight(), and solve it with run() or, for gradients, forward(). This page lists the constructor and those entry points. The mental model behind them is in Core Concepts.

Constructor

Simulation(freq_max, domain, *, boundary="cpml", cpml_layers=16,
cpml_kappa_max=1.0, dx=None, mode="3d",
dz_profile=None, dx_profile=None, dy_profile=None, dt=None,
dt_min_cell=None, precision="float32", solver="yee",
adi_cfl_factor=2.0, stencil_order=2, interface_eps="sampled")
import numpy as np
from rfx import Box, GaussianPulse, Simulation
sim = Simulation(
freq_max=5e9, # highest frequency of interest (Hz)
domain=(0.04, 0.03, 0.03), # x, y, z size (m)
dx=2e-3, # cubic cell size (m)
boundary="cpml",
cpml_layers=6,
)
ArgumentMeaning
freq_maxHighest frequency you care about, in Hz. It sets the default time step, the default dx and the mesh checks in preflight.
domain(Lx, Ly, Lz) in metres. On a graded axis, pass 0 for that axis and the size comes from the profile’s sum.
dxCell size in metres. If omitted, rfx derives it from freq_max.
boundary"cpml" (default), "pec", "upml", or a BoundarySpec for per-face control, including "pmc" and "periodic". See Core Concepts.
cpml_layersAbsorber thickness in cells (default 16). rfx adds the absorber outside domain on each CPML face, so the whole domain is interior. Keep sources and structures a few cells from its edge.
dx_profile, dy_profile, dz_profileCell sizes along an axis, for a graded mesh. See Non-Uniform Mesh.
mode"3d" (default), "2d_tmz" (Ez, Hx, Hy) or "2d_tez" (Hz, Ex, Ey).
precision"float32" (default), "float64" (enable JAX x64 first) or "mixed" (float16 fields). The last two run on the uniform single-device solver only.
solver"yee" (default) or the experimental "adi". See ADI Solver.
stencil_order2 (default) or 4. Order 4 only runs on a uniform, PEC or periodic, non-dispersive model.
dt, dt_min_cellPin the time step on a graded mesh (for mesh-as-design-variable work). Refused on a uniform mesh.

Put PEC faces in a BoundarySpec. Neither is a finite ground plane: for that, draw a sheet with add_thin_conductor().

Building the model

Every add_* method returns the simulation, so calls can be chained.

MethodPurposeReference
add_material(name, *, eps_r, sigma, mu_r, ...)register a named materialGeometry & Materials
add(shape, *, material)fill a shape with a materialGeometry & Materials
add_thin_conductor(shape, ...)a zero-thickness metal sheetGeometry & Materials
add_source, add_polarized_source, add_tfsf_sourcefield excitation, no impedanceSources & Ports
add_port, add_msl_port, add_waveguide_port, add_coaxial_portports for S-parametersSources & Ports
add_lumped_rlcan R/L/C element in one cellSources & Ports
add_probe, add_vector_probe, add_dft_plane_probe, add_flux_monitor, add_ntff_boxobservablesResults & Observables
add_refinement(z_range, *, ratio=4, ...)a finer z slab (subgridding)experimental; only run() on a uniform mesh accepts it
sim.add_material("slab", eps_r=3.0, sigma=0.01)
sim.add(Box((0.018, 0.0, 0.0), (0.024, 0.03, 0.03)), material="slab")
sim.add_source((0.010, 0.015, 0.015), "ez",
waveform=GaussianPulse(f0=3e9, bandwidth=0.8),
amplitude_kind="current")
sim.add_probe((0.030, 0.015, 0.015), "ez")

Checking before you run

MethodReturnsUse
preflight(*, strict=False, check_ntff=True, check_resolution=True, ...)PreflightReportSetup errors and warnings. run() calls it for you.
preflight_sparameters(*, calculator="run", strict=False, ...)PreflightReportIs this port family valid for the S-parameter calculator you plan to call? See Sources & Ports.
fidelity_report(print_report=True)reportWhere each shape actually landed on the grid, next to what you drew.
freeze_mesh()Grid or NonUniformGridFix the mesh now, so you can read grid indices before placing more geometry.
plan_mesh(*, n_steps=None, available_memory_gb=None, sparameter_calculator=None, ...)MeshPlanA serializable summary of the mesh, memory estimate and checks. See Automation.
mesh_intelligence_report(...)MeshIntelligenceReportGrid, preflight issues and AD memory in one report.
report = sim.preflight()
print(report.ok, len(report.errors), len(report.warnings))
report.raise_for_failure() # raises only on errors
sim.fidelity_report()

PreflightReport is a list of coded issues. Read .ok, .errors, .warnings, .infos or .by_code(code). Don’t test it with if report:: that raises TypeError, because an advisory-only report is non-empty but fine. strict=True raises on errors instead of returning them.

run()

run() is the general entry point. It returns a Result; see Results & Observables.

run(*, n_steps=None, num_periods=20.0, until_decay=None,
decay_check_interval=50, decay_min_steps=100, decay_max_steps=50000,
decay_energy_consecutive=2, ..., checkpoint=False,
compute_s_params=None, s_param_freqs=None, s_param_n_steps=None,
snapshot=None, subpixel_smoothing=False, conformal_pec=None,
conformal_min_weight=0.1, devices=None, exchange_interval=1,
skip_preflight=False, report_every=None, report_label="",
ringdown=None) -> Result
result = sim.run(n_steps=300)
print(result.time_series.shape, f"settling {result.settling_db:.1f} dB")

Run length. Give exactly one of:

ArgumentMeaning
n_steps=Nexactly N steps
num_periods=PP periods at freq_max (default 20, used when nothing else is given)
until_decay=1e-3step until the interior field energy falls to that fraction of its peak, up to decay_max_steps

until_decay works on absorbing boundaries, on uniform and graded meshes. It watches the total energy in the interior, so it is a sound stop for spectra, flux and S-parameters. In a closed PEC box the energy does not decay. There the uniform solver falls back to a single field sample, which is reliable only for lossy or radiating ring-down, and a graded mesh runs the fixed n_steps with a warning. Use a fixed n_steps in closed structures and in convergence studies.

Other arguments you will use:

ArgumentMeaning
compute_s_params=True, s_param_freqs=S-parameters for lumped and wire ports (add_port) only. Other port families have their own calculator.
snapshot=SnapshotSpec(interval=10, components=("ez",), slice_axis=None, slice_index=None)Record field snapshots. See Visualization and Analysis.
report_every=NPrint progress (steps, rate, ETA) every N steps. Does not change the result.
subpixel_smoothing=TrueSmoothed permittivity at dielectric interfaces.
conformal_pec=TrueDey–Mittra conformal PEC for curved metal.
ringdown=RingdownSpec(...)Complete a wire port’s S-parameters when the record was cut while the structure still rings. See below.
devices=[...]Run across several devices (x-slab decomposition). exchange_interval must stay 1.
skip_preflight=TrueSkip the automatic preflight. Use only after you have read it once.

A run argument that the selected solver path does not implement raises before the first step, for example snapshots on a graded mesh or until_decay with the ADI solver. Nothing is silently dropped.

Warnings after the run. run() warns when a probe recorded no field, when a Gaussian-pulse probe record had not rung down (end of record above −40 dB of peak), and when time_series or lumped/wire s_params contain NaN/Inf or an |S| above 1.1. These checks cover only those arrays. For DFT planes, flux and NTFF data, check np.isfinite(...) yourself. No warning does not mean the run converged.

Ring-down completion

from rfx.ringdown import RingdownSpec, then run(..., ringdown=RingdownSpec()). For a wire-port run (add_port(..., extent=...)) whose record ended too early, rfx fits the slowly decaying poles in the last part of the record and adds the missing tail of each spectrum in closed form. The completed matrix is result.ringdown.s_params, on the same frequencies as result.s_params, and result.ringdown.report holds the fitted poles and the checks. report.ok is decided by the error witness (completing from [T/4, T] versus [T/2, T]). The two-window difference W2 is reported, not judged. None of the witnesses is an error bound. The source must be off in the fit window. It covers wire ports (one port on a uniform mesh, or a graded mesh) with a fixed n_steps. Other ports, TFSF, until_decay and devices= are refused with the reason. s_params is None when the report says no completion was produced.

forward(): the differentiable path

forward() runs the same physics with only the outputs a loss function needs, so it can sit inside jax.grad, jax.value_and_grad or jax.jit. It returns a ForwardResult with time_series, s_params/freqs (lumped/wire S11 vectors when port_s11_freqs= is set), ntff_data, dft_planes, grid and dt.

forward(*, eps_override=None, sigma_override=None, mu_r_override=None,
design_box=None, design_eps_override=None,
design_sigma_override=None, design_occupancy_override=None,
pec_mask_override=None, pec_occupancy_override=None, n_steps=None,
num_periods=20.0, checkpoint=True, checkpoint_segments=None,
emit_time_series=True, checkpoint_every=None, n_warmup=0,
skip_preflight=False, distributed=False, devices=None,
exchange_interval=1, port_s11_freqs=None,
rlc_values_override=None) -> ForwardResult
import jax
import jax.numpy as jnp
grid = sim.freeze_mesh()
i0 = grid.position_to_index((0.018, 0.0, 0.0))[0]
i1 = grid.position_to_index((0.024, 0.0, 0.0))[0]
def loss(eps_slab):
eps = jnp.ones(grid.shape).at[i0:i1].set(eps_slab)
out = sim.forward(eps_override=eps, n_steps=300, skip_preflight=True)
return jnp.sum(out.time_series[:, 0] ** 2)
print(jax.grad(loss)(3.0))
InputWhat it differentiates
eps_override, sigma_override, mu_r_overridewhole-grid material arrays, shape grid.shape; they replace the registered materials everywhere
design_box= with design_eps_override= / design_sigma_override= / design_occupancy_override=a box-sized design region; far less reverse-mode memory than a whole-grid override (pass checkpoint=False with it)
pec_occupancy_overriderelaxed metal occupancy in [0, 1]
port_s11_freqs=lumped/wire S11 vectors, uniform single-device only
rlc_values_override={index: {"R": ..., "L": ..., "C": ...}}values of registered add_lumped_rlc elements

Per-cell versus per-edge conductivity in a design box. A single design_sigma_override array is per cell. rfx averages the four cells around each E edge, and that average also reaches the edge layer on the box’s plus faces. A 3-tuple (sigma_x, sigma_y, sigma_z) is per edge at the box indices and is written as-is. If a per-edge design needs that plus-face layer, declare the box one cell larger on that side.

Keep anything that changes array shapes, the geometry list or the solver path outside the function you differentiate. A combination the differentiable path does not support raises instead of returning a zero gradient. A model with add_refinement is refused by forward(). Guides: Autodiff and Adjoint, Inverse Design, Memory Reduction.

S-parameter calculators

Each port family has its own calculator method on Simulation: compute_msl_s_matrix(), compute_waveguide_s_matrix(), compute_coaxial_line_reflection() and compute_coaxial_two_port(). Lumped and wire ports use run(compute_s_params=True). compute_s_matrix(lane=None, **kwargs) picks the right method from the registered ports and forwards your arguments. s_matrix_lane() tells you which one it would pick without running anything. Details: Sources & Ports.

Simulation.auto()

Simulation.auto(freq_range, *, accuracy="standard", **kwargs) -> Simulation
auto_sim = Simulation.auto(freq_range=(1.5e9, 3.5e9), accuracy="draft")

This derives dx, the domain and the CPML thickness from the frequency range alone. accuracy is "draft", "standard" or "high". Any keyword argument overrides the derived value. It warns on purpose: the domain was sized without your geometry, so check it after you add shapes. To derive a mesh from your geometry instead, use auto_configure or plan_mesh on the Automation page.

Other methods

These are documented in the generated reference: estimate_ad_memory, plan_ad_memory, ad_memory_preflight and explain_ad_memory (reverse-mode memory, see Memory Reduction); export_scene, export_artifact_bundle and artifact_report (saving a model and its results); conductor_mask.

Limits

  • A graded mesh accepts fewer features than a uniform one. Snapshots, lumped port S-parameters, coaxial ports, UPML and Floquet ports raise there. See Support Boundaries.
  • add_refinement works only through run() on a uniform mesh. forward(), optimize() and topology_optimize() refuse a refined model.
  • precision="mixed" raises the CPML reflection floor. Don’t use it for S-parameters or low-reflection work.
  • A completed run is not a converged run. Check result.settling_db and do a convergence study before you quote a number.
Back to top ↑