Use Maxwell in WiTwin Studio

Get the Maxwell Plugin, build an FDTD Scene from Studio components and generators, run the solve, and view 2D field slices and 3D volumes.

This guide is for WiTwin Studio users who want to run Maxwell without writing a solver script. The Maxwell Plugin adds components that describe a witwin.maxwell Scene, a Library of ready-made objects and parametric models, an "EM Design" Project template, and an FDTD solver that draws the field in the viewport. The last two sections show how each Studio object becomes a witwin.maxwell object and which Maxwell features still need the Python API.

What the Plugin adds#

PartWhat you get
ComponentsMaxwellSolver (shown as "Maxwell Solver"), PointDipoleSource, PlaneWaveSource, GaussianBeamSource, ModeSource, PointMonitor, PlaneMonitor, FinitePlaneMonitor, ClosedSurfaceMonitor, ModeMonitor, ModePort and MaterialRegion, all in the component category Simulation/Maxwell. Materials use Studio's shared Material component.
Library itemsA Maxwell category with "Maxwell 3D Field (Demo)", "Maxwell Settings", "Dielectric Box", "Dipole Source" and "Plane Monitor".
Generators45 parametric model files in the Library categories Antennas, Transmission lines, Passives, Connectors, Packages and Periodic structures.
Project template"EM Design": a 1 GHz point dipole in a 1.2 m FDTD domain.
Solverwitwin.maxwell.fdtd ("Maxwell FDTD"). It runs Simulation.fdtd in a separate process and answers slice and volume queries.
Viewport drawingSources in blue (dipole arrow, plane-wave and beam direction arrows, TFSF box), monitors as dashed green planes, boxes and point markers, and the PML as a dashed grey box inside the domain.

The Plugin id is witwin.maxwell. Its Python distribution is wt-maxwell (import name wt_maxwell), and its source is at https://github.com/witwin-ai/wt_maxwell.

Get the Plugin#

WiTwin Plugins never come from PyPI. Use one of these sources.

SourceHow
Studio defaultwitwin.maxwell is one of Studio's system default Plugins. When a Project opens and Maxwell is missing, the ProjectServer tries to install the latest stable release from the WiTwin Plugin registry and enables it.
GitOn the Plugins page, open the add menu, choose Install from Git and enter https://github.com/witwin-ai/wt_maxwell.
Local folderOn the Plugins page, choose Add Local Plugin Path and point it at a checkout of wt_maxwell, for this Project or for the whole computer. A local path wins over a registry package with the same id.
Project dependencyAdd the Plugin to the Project's pyproject.toml with the witwin command (below). Studio then loads it from the Project's Python environment.

Registry and index not online yet

As of November 2026, the WiTwin Plugin registry does not serve Plugins and the witwin index (https://witwin-ai.github.io/python-index/simple/) returns 404. The automatic default install therefore fails silently, and uv sync cannot resolve packages pinned to the witwin index, including in a new EM Design Project. Use Install from Git or Add Local Plugin Path until they are online.

# Run in the Project folder. Pick one.
witwin add git+https://github.com/witwin-ai/wt_maxwell
witwin add ../wt_maxwell          # a local checkout, installed editable

witwin add writes the dependency, pins first-party packages to the witwin index (never PyPI), runs uv lock and syncs the environment. It accepts --tag, --rev or --branch for a git source.

An installed Plugin that Studio does not trust yet shows Trust & Enable on its card; Studio runs its code only after you choose it.

Install the solver runtime#

The Plugin itself is light: Studio imports only its components and Library entries. The FDTD solve runs in a child process on the Project's Python: <project>/.venv when it exists, otherwise the interpreter Studio runs on (the WITWIN_PROJECT_PYTHON environment variable overrides both). That interpreter needs witwin-maxwell and a CUDA build of PyTorch, plus an NVIDIA GPU. Install them as described in Installation.

Note

The Plugin's solve extra lists the solver stack (witwin[maxwell]>=0.4 and torch>=2.4). If you manage the Project with uv, declare the solver packages in pyproject.toml instead of installing them by hand: uv sync removes packages that the Project does not declare.

Create a Project from the EM Design template#

While no Project is open, Studio shows Project template cards in the Chat column. The Maxwell Plugin contributes the EM Design card. If the Plugin is not installed, the card shows a download mark, and clicking it opens the Plugins page filtered to the Plugin. Otherwise, clicking the card creates a new Project from the template and opens it.

The new Project is also an ordinary Python project:

FileContents
witwin.tomlThe Studio manifest. The main Scene is main.
pyproject.tomlDependencies witwin-studio>=0.1,<0.2 and wt-maxwell>=0.1.0,<0.2, both pinned to the witwin index.
scenes/main.wtsceneTwo objects: Maxwell Settings and Point Source.
scripts/prepare_em.pyPrints the FDTD setup and checks that the source is inside the domain. --frequency-ghz retunes the solve and the source and saves the Scene.
tests/test_scene.pyChecks that the source frequency is one of the solve frequencies.

The Scene holds no geometry. Maxwell Settings sets a domain from -0.6 m to 0.6 m on each axis, 7 points per wavelength at the 1 GHz solve frequency (a cell of about 4.3 cm), and PML on every face with 10 layers. Point Source is a z-polarised point dipole at the origin with a Gaussian pulse at 1 GHz and 0.2 GHz spectral width.

To solve it, select Maxwell Settings and press Solve in the Results group of its Maxwell Solver component. The radiated field appears in the viewport as a 3D volume.

You can change the template Scene from a terminal as well. The script edits scenes/main.wtscene, the same file Studio edits:

uv sync
uv run python scripts/prepare_em.py --frequency-ghz 1.5

Set up the Scene#

A Maxwell Scene in Studio is a set of objects. One object carries the Maxwell Solver component and holds the scene-wide settings. Every other object is a structure, a source, a monitor, a port or a material region, depending on its components.

To start a Scene by hand, drag Maxwell Settings from the Maxwell Library category into the Scene. "Maxwell 3D Field (Demo)" adds the same object plus a point source at the domain centre.

All lengths are in metres, frequencies in hertz, and times in seconds.

Domain#

The Domain group of the Maxwell Solver component sets the simulation box.

FieldDefaultMeaning
min_bound[-1.0, -1.0, -1.0]Minimum corner of the domain (m).
max_bound[1.0, 1.0, 1.0]Maximum corner of the domain (m).

Both corners are relative to the Maxwell Settings object: the export adds the object's world position to them. You can also drag the domain box in the viewport.

Grid#

The grid is always uniform. You choose its density, not its cell size.

FieldDefaultMeaning
ppw"10"Points per wavelength, an integer from 4 to 20 (stepped slider).

The cell size is c / f_max / ppw, where c is 299792458 m/s and f_max is the highest solve frequency (1 GHz when the list is empty). The wavelength is the free-space one, so raise ppw when the Scene contains high-permittivity material or features smaller than a few cells.

Boundary#

FieldDefaultMeaning
mode"global"global (one kind for all faces), per_axis (fields x, y, z) or per_face (fields x_low to z_high).
kind"pml"Boundary kind: none, pml, periodic, bloch, pec or pmc. In the per_axis and per_face modes it is the default for faces you do not set.
num_layers15PML layer count. Shown when kind is pml.
strength1e6PML strength. Shown when kind is pml.
bloch_wavevector[0.0, 0.0, 0.0]Bloch wavevector (rad/m). Shown when kind is bloch.
x, y, z"pml"Per-axis kinds in per_axis mode.
x_low ... z_high"pml"Per-face kinds in per_face mode.

See Grids and boundaries for what each kind does.

Settings#

FieldDefaultMeaning
frequencies[]Solve frequencies (Hz). The solve refuses to start while this list is empty. It also sets the grid.
subpixel_uniformTrueUse one subpixel sample count for all axes (subpixel), or one per axis (subpixel_x, subpixel_y, subpixel_z).
subpixel1Subpixel material samples per axis.
symmetry_x, symmetry_y, symmetry_z"none"Symmetry plane normal to that axis: none, PEC or PMC. The plane sits on the low face.
device"cuda"cuda or cpu. Studio warns when you solve on the CPU; practical FDTD scenes need CUDA.
absorber"cpml"cpml or pml.
time_steps_autoTrueLet Maxwell choose the run time. Turn it off to set time_steps (default 1000).

Materials#

Any object with geometry can carry Studio's Material component. Its Electromagnetic group feeds Maxwell:

FieldMeaning
material_nameAn ITU-R P.2040 material that fills permittivity and conductivity, or Custom.
modelscalar, dispersive or anisotropic. It decides which of the fields below apply.
eps_rComplex relative permittivity (real and imaginary part). A nonzero imaginary part is passed to Maxwell as a complex eps_r.
mu_r, sigma_eRelative permeability and electric conductivity (S/m).
debye_poles, drude_poles, lorentz_polesElectric dispersion poles, used in the dispersive model.
mu_debye_poles, mu_drude_poles, mu_lorentz_polesMagnetic dispersion poles, used in the dispersive model.
epsilon_tensor, mu_tensor, sigma_e_tensorDiagonal tensors, used in the anisotropic model.
kerr_chi3Kerr third-order susceptibility. Studio shows it only in the anisotropic model, which the export does not combine with Kerr, so set Kerr from Python.

The component's default eps_r is 10, not 1, so set the material of every structure. Maxwell does not combine dispersion with anisotropy, or Kerr with either of them, so the export stops with an error for those combinations. See Materials for the meaning of the poles.

MaterialRegion marks a box (an object with box PlatformGeometry) as a density-parameterized design region for topology optimization. Its fields are region_name, basis, bounds, eps_bounds, mu_bounds, the design grid density_nx, density_ny, density_nz, an optional blur filter (use_filter, filter_radius in m), an optional tanh projection (use_projection, projection_beta) and symmetry. Studio stores only the shape: the export always starts from a zero density, and the Plugin runs no optimization loop.

Structures#

Each visible object that is not a source, monitor, port or material region becomes a structure:

  • An object with a mesh (an imported mesh, a primitive, a generated body) is exported as a mesh at its full world transform, including scale.
  • An object with PlatformGeometry and no mesh is exported as that geometry.
  • The object's Material component sets the material. Without one, the structure gets mw.Material() (vacuum).
  • A StructureMeta component, when present, sets priority, enabled, tags and metadata.

Hidden objects are left out of the solve.

Sources#

Each source is an empty object with one source component.

ComponentFieldsPosition
PointDipoleSourcepolarization_mode (Ex, Ey, Ez or vector with polarization), width (m, default 0.02), profile (gaussian or ideal)Object's world position
PlaneWaveSourcedirection, polarization, injection (soft or tfsf with tfsf_min, tfsf_max in m), injection_axis (auto, x, y, z)Direction only
GaussianBeamSourceThe plane-wave fields plus beam_waist (m, default 0.5) and focus (m)Direction only
ModeSourcesize (one axis zero), mode_index, direction (+ or -), polarization (auto, Ex, Ey, Ez)Object's world position

Every source has a Source Time group:

FieldDefaultMeaning
st_enabledTrueThe source has a temporal profile.
st_kind"cw"cw, gaussian_pulse or ricker.
st_frequency1e9Centre frequency (Hz).
st_amplitude1.0Amplitude.
st_phase0.0Phase (rad). Not used by ricker.
st_fwidth1e8Spectral width (Hz), for gaussian_pulse.
st_auto_delayTrueLet Maxwell compute the pulse delay. Turn it off to set st_delay (s).

If the Scene has no source when you press Solve, the Plugin adds an object named "Default FDTD Source": an Ez point dipole with a Gaussian pulse at the first solve frequency and 20 % of it as spectral width.

See Sources for how to choose a source and its time profile.

Monitors#

Each monitor is an empty object with one monitor component. Every frequencies field lists frequencies in Hz; an empty list means a broadband DFT.

ComponentFields
PointMonitorfields: recorded components, from Ex, Ey, Ez, Hx, Hy, Hz (default ["Ez"]).
PlaneMonitoraxis, position (m along the axis), fields, frequencies, compute_flux, normal_direction. With compute_flux on it becomes a flux monitor.
FinitePlaneMonitorsize (one axis zero), fields, frequencies, compute_flux, normal_direction.
ClosedSurfaceMonitorsize: the box extent (m) around the object position, and frequencies.
ModeMonitorsize, mode_index, direction, polarization, frequencies, normal_direction.

Ports#

ModePort couples a mode source and a mode monitor on one plane. Its fields are size (one axis zero), mode_index, direction, polarization, frequencies, monitor_offset (m along the normal) and normal_direction. Turn on is_source to make the port inject; it then shows the same Source Time fields as a source. The port stays one object in Studio; Maxwell splits it into a source and a monitor when it solves. See Mode ports.

Experimental

ModeSource, ModeMonitor and ModePort are experimental in Maxwell. A mode plane in the viewport shows only its outline until a mode solver publishes mode profiles for it.

Positions of monitors and ports

Sources read the object's world position. PointMonitor, FinitePlaneMonitor, ClosedSurfaceMonitor, ModeMonitor and ModePort read the position of the object's own Transform. Keep these objects at the top level of the Scene hierarchy, or their solver position differs from the one you see when a parent is moved.

Add geometry from the generators#

The generators are parametric model files that ship with the Plugin. Each one is a Library item:

Library categoryModels
Antennas (20)Rectangular patch (inset feed or coax probe), circular patch, aperture-coupled patch, half-wave dipole, quarter-wave monopole, PIFA, printed inverted-F, Yagi-Uda, log-periodic dipole array, axial-mode helix, Archimedean spiral, pyramidal, conical and corrugated horns, Vivaldi, half-wave slot, prime-focus reflector, linear array, planar array
Transmission lines (10)Microstrip, stripline, coplanar waveguide, grounded coplanar waveguide, coaxial line, rectangular waveguide, waveguide E-plane and H-plane bends, waveguide T-junction, circular waveguide
Passives (6)Coupled microstrip lines, open or shorted stub, stepped-impedance low-pass filter, hairpin band-pass filter, Wilkinson power divider, branch-line coupler
Connectors (1)SMA edge-launch connector
Packages (3)QFN package, BGA package, wire bond
Periodic structures (5)Square-loop, Jerusalem-cross and split-ring unit cells, FSS patch or aperture cell, photonic crystal slab
  1. Drop the model

    Drag a generator from the Library into the Scene. Studio adds an empty group with a Script Geometry component whose source is the Plugin model, and builds it once with the default parameters. The defaults are a working design, not a placeholder.

  2. Change the parameters

    The model parameters appear as fields of the Script Geometry component, shown in the model's units. Changing one rebuilds the group. Regenerate approves the model's current code and runs it again. Each generated body is a child object with a mesh and a Material component, so it becomes a Maxwell structure.

  3. Size it from a specification

    Most models have a design(spec) function that turns a target (for example a centre frequency and a substrate) into parameter values from textbook formulas. Studio's Library drop does not ask for a spec; ask the Agent to place the model with a design spec (the geometry.place Tool), or run the model from Python as below.

  4. Copy it into the Project (optional)

    Copy to Project copies the model file and its helper modules into the Project's models/ folder so you can edit the code.

Model files use millimetres (micrometres for the photonic crystal slab) with z up; Studio converts them to metres when it places them. Conductors use the material "PEC" and dielectrics carry eps_r and sigma_e. Each model has port anchors (for example feed, or port1 and port2) that mark where to put a port or source.

You can build and check a model without Studio:

from witwin.studio.geometry import run_model
 
run = run_model("models/lines/microstrip.py", params={"w": 2.9})

Generated boards stand on edge

Studio's Scene is y up. A z-up model enters the Scene rotated so that its board normal points along Scene +y, and the Maxwell export maps Scene axes to solver axes unchanged. The layer normal therefore reaches Maxwell as +y. This matters when your setup assumes layers normal to z, for example periodic boundaries in x and y around a unit cell. In that case rotate the group or use a model with CONFIG["up"] = "y".

The export also ignores the fill and surface_thickness of generated bodies: a copper sheet drawn as a single surface is exported as that surface mesh, with no thickness.

Run the solve#

  1. Set the solve frequencies

    Add at least one value to frequencies in the Settings group. Make the source frequency match one of them.

  2. Check the grid and boundary

    Choose ppw and the boundary. The dashed grey box in the viewport shows where the PML starts.

  3. Press Solve

    Solve is in the Results group. You can cancel a running solve.

When you press Solve, the Plugin:

  1. adds the default source if the Scene has none, and applies the current timeline pose to the Scene;
  2. sends the Scene to the witwin.maxwell.fdtd solver, which rebuilds a witwin.maxwell.Scene from it and runs Simulation.fdtd(...).run() with full_field_dft=True;
  3. keeps the solver's result, then updates the field slice and, when show_3d_field is on, the 3D volume.

The solver process stays running between solves (it stops after 300 s idle) and runs one solve at a time. A notification reports success or the error message. The Plugin solves with FDTD only.

View 2D slices and 3D volumes#

The Results group of the Maxwell Solver component controls what you see:

FieldDefaultMeaning
slice_axis"z"Axis normal to the slice.
slice_position0.0Slice coordinate along that axis (m).
component"Ez"Ex, Ey, Ez, Hx, Hy or Hz.
value_mode"abs"abs, real or imag of the complex field.
frequency_index0Which solve frequency to show, as an index into frequencies.
log_scaleFalseShow log10 of the magnitude (slice only).
show_3d_fieldTrueUpdate the 3D volume after each solve.
  • Update Slice draws the slice in the "Field Slice" figure of the component.
  • Show 3D Volume loads the whole component volume onto the Maxwell Settings object and switches its Rendering tab to Volume. The Rendering tabs (None, Slices, Volume, SDF, Vector) choose how the viewport draws that field.

Both buttons need a solve first. They query the last result, so you can change the component, value mode or frequency index without solving again. The field data is runtime state and is not saved in the Scene file.

How the Studio Scene maps to witwin.maxwell#

The Plugin rebuilds a witwin.maxwell.Scene from the Studio Scene each time you solve.

Studiowitwin.maxwell
Maxwell Settings object (Maxwell Solver component)mw.Scene(domain=..., grid=..., boundary=..., subpixel_samples=..., symmetry=..., device=...)
min_bound, max_bound plus the object positionmw.Domain(bounds=...)
ppw and frequenciesmw.GridSpec.uniform(c / f_max / ppw)
Boundary groupmw.BoundarySpec.pml(...), .bloch(...), .none(), .periodic(), .pec(), .pmc(), or mw.BoundarySpec.faces(...) for per-axis and per-face modes
Object with a mesh or PlatformGeometrymw.Structure(geometry, material, name=...) added with scene.add_structure
Material componentmw.Material(eps_r=..., mu_r=..., sigma_e=..., ...) with mw.DebyePole, mw.DrudePole, mw.LorentzPole and mw.DiagonalTensor3
MaterialRegionmw.MaterialRegion(...) with a zero density
Source Time groupmw.CW, mw.GaussianPulse or mw.RickerWavelet
PointDipoleSource, PlaneWaveSource, GaussianBeamSource, ModeSourcemw.PointDipole, mw.PlaneWave, mw.GaussianBeam, mw.ModeSource; TFSF injection becomes mw.TFSF(bounds=...)
PointMonitor, FinitePlaneMonitor, ModeMonitormw.PointMonitor, mw.FinitePlaneMonitor, mw.ModeMonitor
PlaneMonitormw.PlaneMonitor, or mw.FluxMonitor when compute_flux is on
ClosedSurfaceMonitormw.ClosedSurfaceMonitor.box(name, position=..., size=..., frequencies=...)
ModePortmw.ModePort(...) added with scene.add_port
Solvemw.Simulation.fdtd(scene, frequencies=..., absorber=..., full_field_dft=True, run_time=...)

Object names become the name of the Maxwell object. time_steps becomes run_time=mw.TimeConfig(time_steps=...); with time_steps_auto on, run_time is None.

For example, the EM Design template Scene is equivalent to this script:

import witwin.maxwell as mw
 
scene = mw.Scene(
    domain=mw.Domain(bounds=((-0.6, 0.6), (-0.6, 0.6), (-0.6, 0.6))),
    grid=mw.GridSpec.uniform(299792458.0 / 1e9 / 7),   # ppw = 7 at 1 GHz
    boundary=mw.BoundarySpec.pml(num_layers=10, strength=1e6),
    device="cuda",
)
scene.add_source(mw.PointDipole(
    position=(0.0, 0.0, 0.0), polarization="Ez", width=0.02, profile="gaussian",
    source_time=mw.GaussianPulse(frequency=1e9, fwidth=2e8, delay=4.7746482323418604e-09),
    name="Point Source"))
result = mw.Simulation.fdtd(scene, frequencies=(1e9,), absorber="cpml", full_field_dft=True).run()

The Plugin also translates in the other direction, from a witwin.maxwell.Scene into Studio objects. That direction loses what the Studio components cannot hold: an anisotropic grid comes back as a uniform grid, with the ppw of its finest spacing rounded and clamped to 4 to 20. Source, monitor and port types other than those above are not supported, and thin wires, lumped elements, networks and circuits are left out.

What needs the Python API#

The Plugin covers the FDTD setup above. Use witwin.maxwell directly, in a Project Script or your own code, for everything else:

FeatureWhere to read
FDFD solvesFDTD vs FDFD
Non-uniform and anisotropic grids, GridSpec.auto, LayerRefinementSpec, MeshOverrideStructureGrids and boundaries
Other sources: AstigmaticGaussianBeam, UniformCurrentSource, CustomFieldSource, CustomCurrentSourceSources API
Other monitors: FieldTimeMonitor, FluxTimeMonitor, DiffractionMonitor, PermittivityMonitor, PowerLossMonitorMonitors API
RF ports: LumpedPort, WavePort, TerminalPortRF ports, Ports API
S-parameters, networks and TouchstoneNetworks API
Lumped elements and circuitsCircuits API
Thin wiresThin-wire API
Post-processing: far field, directivity, RCS, mode overlapFar field, Postprocess API
Antenna arrays and beam dataAntennas and arrays API
Full Tensor3x3 anisotropy, Graphene, Medium2D, LossyMetalMedium, GyromagneticFerrite and other mediaMaterials API
ElectrostaticsElectrostatics API
SAR and exposureSafety API
Batch runs and multi-GPUBatch and multi-GPU runs
Gradients, adjoint and topology optimizationDifferentiability
Run controls: shutoff, spectral_sampler, cpml_config, run time other than a step countSimulation API
Result data beyond a slice or volume of one field componentResults, Result API