RapidFEM
Electromagnetic FEM solver written in Rust, distributed as a Python package on PyPI. Two backends sit behind one geometry / material / physics API: a frequency-domain solver (Nédélec first-kind curl-conforming elements, complex-symmetric sparse linear algebra) and a time-domain DGTD solver (nodal discontinuous Galerkin, Krylov/ETD exponential, explicit and adaptive time integration). The geometry is non-dimensionalised before assembly, so sub-micron RFIC passives and metre-scale structures use the same numerical path. An optional Flask-based local UI provides a code editor and a live viewer.
Install
pip install rapidfem # solver only
pip install rapidfem[ui] # solver + local UI
Wheels for Windows, Linux, and macOS are built in CI; the Rust core is compiled ahead of time, so no Rust toolchain is needed on the user's machine. Geometry and meshing run on the built-in rapidmesh mesher, the sparse solves on the built-in rslab solver; there are no other native dependencies.
Quick start (Python API)
import numpy as np
import rapidfem as rf
# Build geometry; attach materials + physics directly to entities
g = rf.Geometry(maxh=rf.lambda_maxh(f_max=12e9))
air = g.box(22.86e-3, 10.16e-3, 30e-3, position=(-11.43e-3, -5.08e-3, 0),
material=rf.Air())
rf.RectWaveguidePort(air.faces.min(axis="z"))
rf.RectWaveguidePort(air.faces.max(axis="z"))
rf.PEC(*air.faces.unassigned)
g.mesh()
# Define the problem once, run any number of analyses on it
prob = rf.ProblemFD(g) # the frequency-domain problem
result = prob.sweep(np.linspace(8e9, 12e9, 21))
print(result.frequencies.shape, result.sparams.shape)
# The same Problem can also drive an eigenmode solve or a far-field pattern:
# modes = prob.eigenmode(target_frequency=10e9, n_modes=6)
# pattern = prob.farfield(result, freq_idx=10, port_idx=0)
python_src/rapidfem/examples/ has end-to-end runs: microstrip and coupled lines, iris and
stepped-impedance filters, patch / Vivaldi / inverted-F antennas (PML + far-field), pyramidal horns,
dielectric resonators, and the fd_rfic_* on-chip passives. RFIC geometry comes from a process stack
and layout via rapidfem.rfic (rfic.Stack, Geometry.from_gds).
Importing external geometry and meshes
g.load(path) brings external geometry into the scene; the action is chosen from the file extension.
g = rf.Geometry(maxh=rf.lambda_maxh(f_max=20e9))
# A STEP file brings one solid per body, its faces on their true surfaces, in
# metres whatever unit the file declares: a normal GeoObject, so boolean it,
# transform it, select its faces, attach materials and physics, exactly like
# a g.box(...). rotation= and position= place the part.
part = g.load("horn.step", material=rf.Air(),
rotation=(math.pi, (0, 0, 1)), position=(0, 0, 5e-3))
post = g.cylinder(radius=0.5e-3, height=5e-3)
g.cut(part, post) # compose with primitives
g.rotate(part, math.pi / 2, axis=(0, 1, 0)) # full transform API applies
rf.RectWaveguidePort(part.faces.max(axis="z"))
rf.PEC(*part.faces.unassigned)
g.mesh()
# A closed STL or OBJ surface becomes a solid too, split into smooth faces at
# its creases; STL carries no unit, unit= names the file's.
blob = g.load("antenna.stl", unit="MM", material=rf.Air())
# A pre-built .msh volume mesh (gmsh MSH 4.1 or 2.2) is already tessellated, so
# loading one switches the geometry into mesh mode: its named physical groups
# become selectable handles for materials and physics. g.mesh() bakes the
# bindings (no remeshing) and the usual Problem/sweep pipeline runs unchanged.
# g.save_mesh(path) writes such a file, the groups named as the solver sees them.
g = rf.Geometry()
scene = g.load("waveguide.msh")
scene.group("air").material = rf.Air()
rf.RectWaveguidePort(scene.group("port_in"))
rf.RectWaveguidePort(scene.group("port_out"))
rf.PEC(scene.group("walls"))
g.mesh()
result = rf.ProblemFD(g).sweep(np.linspace(8e9, 12e9, 21))
IGES and BREP are not supported; export STEP. examples/fd_stl_import.py is a full STL-driven
sweep.
Local UI
rapidfem serve ./my_project/
Opens a browser window with a CodeMirror Python editor, a 3D geometry / mesh / field viewer (raw
WebGL2), and S-parameter plots. The geometry view updates on save (Ctrl+S); mesh and solver runs
are explicit. Results stream in as the solve runs; fields are fetched on demand as you scrub
frequency and port. rapidfem.show(g) sends a geometry to the viewer.
Features
- Geometry builder on the vendored rapidmesh: primitives with boolean ops, transforms, and
fillet/chamfer.
Ready-made RF structures in
rf.structures(coax, microstrip, CPW, stripline, waveguides, helix) build geometry and ports in one call. - External CAD / mesh import:
g.load(path)pulls in STEP solids as composable primitives, closed STL / OBJ surfaces as solids, or loads a pre-built.mshand exposes its named groups for material / physics binding. - RFIC / GDS:
rapidfem.rficprocess stacks andGeometry.from_gdsbuild on-chip passives, solved after non-dimensionalisation down to sub-micron features. - Element: Nédélec first-kind curl-conforming tetrahedral elements, order 2 (20 DOFs per cell), in a
hierarchical basis. Opt-in per-cell mixed order (1-2) via
ProblemFD.sweep(order="adaptive"); the default is uniform order 2. - Excitations: rectangular waveguide ports (arbitrary TE modes), lumped ports (TEM, multi-line voltage integral), coax and wave ports, Floquet plane-wave port (normal incidence), first-order absorbing boundary.
- PML: anisotropic stretched-coordinate perfectly matched layer.
- Lossy materials: complex permittivity with loss tangent and conductivity, surface impedance for metals, Debye dispersion; cached across sweeps.
- Sparse solvers: pure-Rust
rslabreal and complex-symmetric LDLᵀ (Bunch-Kaufman) with a-priori memory estimates, vendored, no native dependencies; driven sweeps, eigenmodes and port modes all factor through it. - Frequency sweep: assembles E/B once, analyses the sparsity pattern once, and solves neighbouring points by COCG preconditioned with an earlier point's factorisation, refactoring only when the iteration gets expensive (2.6x faster on a 681k-DOF 21-point sweep).
- Eigenmode solver: shift-invert Lanczos in the B inner product, with a per-mode residual check.
- Error estimation: residual error indicator with Dörfler marking (
ProblemFD.element_errors). - Output: Touchstone (.s1p/.s2p/.snp), scikit-rf, far-field NFFT.
- Parallel assembly: rayon-based element matrix evaluation.
Time-domain backend (DGTD)
ProblemTD, behind the same API, compiles a structure into an explicit linear ODE dy/dt = A·y and
exposes it as a model at every level:
- DGTD: nodal discontinuous Galerkin on tetrahedra, upwind or energy-conserving central flux.
- Time integration: matrix-free Krylov/ETD propagator, exact for the linear system at any step size (no CFL limit); explicit LSERK4 substepped within the CFL limit; embedded KCL RK4(3) with PI step control. CPU or OpenCL GPU.
- Model export: the RHS, the verbatim sparse operator
A, an exponential stepper. - Materials: heterogeneous, lossy, anisotropic, and Debye-dispersive media; matched absorbing layers; periodic boundaries.
- Output: field probes, RFT transfer function, VTK field-animation export.
import numpy as np
import rapidfem as rf
ptd = rf.ProblemTD.box(size=(1, 1, 1), cells=(2, 2, 2), order=2)
y0 = np.random.default_rng(0).standard_normal(ptd.n_dofs)
traj = ptd.transient(y0, dt=0.02, steps=200) # turnkey transient
f = ptd.resonances(n=4) # cavity resonances
A = ptd.state_space() # the verbatim operator
Method notes and the ProblemTD API are in issue #31.
Sparse solver
Every factorisation runs on rslab, vendored in
vendor/rslab: a pure-Rust complex-symmetric LDLᵀ (Bunch-Kaufman) with numeric-only
refactorisation across sweeps and an a-priori RAM gate.
Verification
cargo test --release pins the element and solver kernels against independent symbolic derivations
to machine precision: the Nédélec element matrices, the barycentric integration coefficients, the
surface-element trace, the hierarchical-basis and exact-sequence properties, the global assembly, and
the eigensolver against a densely computed cavity spectrum. The derivations, completeness proofs, and
entrywise cross-checks live in derivations/.
python/tests/ runs end-to-end S-parameter checks on microstrip, stripline, CPW, waveguide, filter,
and antenna geometries against closed-form values. Those run real solves, so they are run locally
(cd python && pytest -m slow), not in CI.
Acknowledgments
rapidfem began as a Rust port of EMerge (PyPI), Robert Fennis' open-source Python electromagnetic FEM solver. Its script-first design shaped the geometry / material / physics API, and the early releases ported its Python implementation to Rust. Thanks to Robert Fennis for building and sharing it.
The solver kernels have since been independently re-derived from primary sources: Nédélec's
edge-element construction, standard microwave theory, and the barycentric integration identities. The
derivations and cross-checks are in derivations/.
License
AGPL-3.0-only; see LICENSE and NOTICE. Copyright (C) Milan Rother and rapidfem contributors; commercial terms available.
Metadata
Release files for rapidfem 0.23.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rapidfem-0.23.0.tar.gz | 84.8 MB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rapidfem-0.23.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| rapidfem-0.23.0-cp310-abi3-manylinux_2_39_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.39+ x86-64 | Details |
| rapidfem-0.23.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
Total release size: 348.8 MB
Release files / rapidfem-0.23.0.tar.gz
| Download URL | rapidfem-0.23.0.tar.gz |
|---|---|
| Size | 84.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f0805c1c61fbe01631039f049c1e7e0b4b4a1f10a04cbcd57299f78b62e82a5b
|
|
BLAKE2b-256 checksum How to use checksums |
491c7acaa9ebe8d2c18822d6bce7fa5130ba2a442fd2e44844e2a5e14e6939b9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / rapidfem-0.23.0-cp310-abi3-win_amd64.whl
| Download URL | rapidfem-0.23.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 88.5 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
149667f25fc8aca1b4f13d0e7152b55586945a9438a22d77dc86e0ed04f49af0
|
|
BLAKE2b-256 checksum How to use checksums |
f7c41ee604bf5b04b6c8fc4c36f5772a51060ea61b2e208f7ae08526da1f5f4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / rapidfem-0.23.0-cp310-abi3-manylinux_2_39_x86_64.whl
| Download URL | rapidfem-0.23.0-cp310-abi3-manylinux_2_39_x86_64.whl |
|---|---|
| Size | 88.4 MB |
| Tags | CPython 3.10 Linux glibc 2.39+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
f38028d9ba71d74b93fbf4f3af85e2f55405d4fe56932dbb4bd6ea94e5b33797
|
|
BLAKE2b-256 checksum How to use checksums |
eb66b6a4e1ad037b8205f116b36f206fefc9154acb79cb6c8c57e617aa5f14b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / rapidfem-0.23.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | rapidfem-0.23.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 87.2 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
cacfa764fbe73146827e1d9d3e2426b2a3b97b1c02f929e28b9bb1072296aa6e
|
|
BLAKE2b-256 checksum How to use checksums |
1014f2d949ec7d95a63683bdc0652b5934c070009126ebe91fd0b731101d3c80
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log