Adapters

Export a Maxwell Scene to a Tidy3D Simulation, and import or export extruded polygon geometry through GDS files.

This page covers the interoperability adapters in witwin.maxwell.adapters. Use them to cross-check a Maxwell scene in Tidy3D, or to move planar layout geometry between Maxwell and a GDS file.

The adapter functions are not re-exported from witwin.maxwell. Import them from their modules:

from witwin.maxwell.adapters.tidy3d import scene_to_tidy3d
from witwin.maxwell.adapters.gds import from_gds, to_gds_file

Each adapter needs an optional package that Maxwell does not install for you:

AdapterPackageInstall
witwin.maxwell.adapters.tidy3dtidy3dpip install tidy3d
witwin.maxwell.adapters.gdsgdstkpip install gdstk

The package is imported only when you call a function. If it is missing, the call raises ImportError with the install command.

For a task-oriented walkthrough, see Interoperability.

scene_to_tidy3d#

scene_to_tidy3d(
    scene,
    *,
    frequencies=None,
    run_time=None,
    length_scale=1e6,
    **kwargs,
) -> tidy3d.Simulation

Converts a Maxwell Scene to a tidy3d.Simulation. Scene.to_tidy3d(...) calls this function with the same arguments.

sceneScenerequired

Scene to convert. It must have a Domain.

frequenciesfloat | Sequence[float] | Nonedefault None

Frequencies in Hz used for frequency-domain monitors, port drives, and some material fits. Required when a frequency-domain monitor has no frequencies of its own, when the scene has a WavePort or LumpedPort, or when a material is a lossy metal.

run_timefloat | Nonedefault None

Tidy3D run time in seconds. None uses 10 light-crossing times of the largest domain extent.

length_scalefloatdefault 1e6

Factor from Maxwell lengths (metres) to Tidy3D lengths. The default converts metres to micrometres, the Tidy3D convention. Frequencies stay in Hz.

Any other keyword argument is forwarded to tidy3d.Simulation and overrides the converted value. A courant keyword is also used to compute the time step for time-domain monitors (0.99 when absent).

Returns a tidy3d.Simulation.

Raises ImportError when tidy3d is not installed, ValueError when required frequencies or a domain are missing, and NotImplementedError for scene features that have no Tidy3D equivalent (listed below).

import witwin.maxwell as mw
 
td_sim = scene.to_tidy3d(frequencies=1e9)
# or, without the Scene method:
from witwin.maxwell.adapters.tidy3d import scene_to_tidy3d
td_sim = scene_to_tidy3d(scene, frequencies=[0.6e9, 1.0e9, 1.6e9], run_time=1e-9)

The function only builds the Tidy3D object. Running it (for example with tidy3d.web.run) is up to you and your Tidy3D account.

What the export maps#

MaxwellTidy3D
Box, Sphere, CylinderBox, Sphere, Cylinder
ConeCylinder with a sidewall angle (rotated cones become a TriangleMesh)
EllipsoidSphere when all radii are equal, otherwise TriangleMesh
PolySlabPolySlab when unrotated, at the origin, and vertical; otherwise TriangleMesh
Other geometry with a surface meshTriangleMesh
PEC materialPECMedium
Non-dispersive materialMedium
Dispersive materialPoleResidue
PointDipole, PlaneWave, GaussianBeam, AstigmaticGaussianBeam, ModeSource, UniformCurrentSource, CustomFieldSource, CustomCurrentSourceSource of the same name (a TFSF plane wave or beam maps to a TFSF source)
CW / GaussianPulse / RickerWaveletContinuousWave / GaussianPulse / GaussianPulse
WavePort / LumpedPortReference mode or current source plus receive monitors
PointMonitor, PlaneMonitor, FinitePlaneMonitorFieldMonitor, or FluxMonitor when compute_flux=True
FieldTimeMonitor, FluxTimeMonitorFieldTimeMonitor, FluxTimeMonitor
DiffractionMonitor, PermittivityMonitor, ModeMonitorMonitor of the same name
PML, periodic, PEC, PMC, Bloch, mixed boundariesMatching BoundarySpec ("none" faces become PEC)

Port drive convention: when the scene has wave ports, the first WavePort drives the export; otherwise the first LumpedPort does. Every wave port gets a receive mode monitor. TerminalPort is not mapped.

Symmetry exports only when it is equivalent to Tidy3D's center-based symmetry. Along each symmetry axis, every structure and MaterialRegion must be a Box that spans the full domain, and every source must be a PlaneWave with no direction component along that axis. Otherwise the export raises NotImplementedError. The folded face (low or high) is dropped.

What raises NotImplementedError#

The export fails instead of approximating when the scene uses:

  • Magnetic materials (mu_r other than 1, magnetic conductivity, magnetic dispersion, gyromagnetic ferrites, anisotropic permeability).
  • A full off-diagonal permittivity tensor combined with dispersion.
  • Second-order (chi2) nonlinearity.
  • Time modulation that Tidy3D cannot represent, or a spatially varying modulation profile.
  • A spatially varying custom pole or perturbation medium.
  • A generic SurfaceImpedanceMedium.
  • A MaterialRegion that is not a Box, has a density filter, has non-uniform density, or has permeability other than 1.
  • A source or monitor type that is not in the table above.
  • Automatic Bloch wavevectors that were not resolved by Simulation.prepare() (raises ValueError).

from_gds#

from_gds(
    gds_path,
    *,
    layer,
    bounds,
    datatype=0,
    cell=None,
    axis="z",
    sidewall_angle=0.0,
    reference_plane="middle",
    length_scale=None,
) -> list[PolySlab]

Reads polygons from one layer of a GDS file and returns them as extruded PolySlab geometries. Cell references are flattened (repetitions applied) and paths are converted to polygons. Each GDS polygon becomes one PolySlab; holes drawn as separate loops import as separate slabs.

gds_pathstr | Pathrequired

Path to the .gds file.

layerintrequired

GDS layer number to import.

boundstuple[float, float]required

(min, max) extrusion extent along axis, in metres.

datatypeintdefault 0

GDS datatype number to import.

cellstr | Nonedefault None

Cell name to read. None selects the unique top-level cell.

axisstr | intdefault 'z'

Extrusion axis: "x", "y", "z", or 0, 1, 2.

sidewall_anglefloatdefault 0.0

PolySlab sidewall angle in radians.

reference_planestrdefault 'middle'

PolySlab reference plane: "bottom", "middle", or "top".

length_scalefloat | Nonedefault None

Metres per GDS user unit. None uses the file's library.unit (typically 1e-6), so vertices land in metres.

Returns one PolySlab per polygon on (layer, datatype). See Geometry.

Raises ImportError when gdstk is missing. Raises ValueError when the file does not exist, the cell is not found, there is more than one top-level cell and cell is None, or the layer has no polygons.

import witwin.maxwell as mw
from witwin.maxwell.adapters.gds import from_gds
 
slabs = from_gds("layout.gds", layer=1, bounds=(0.0, 220e-9))
silicon = mw.Material(eps_r=12.0)
for index, slab in enumerate(slabs):
    scene.add_structure(mw.Structure(name=f"wg{index}", geometry=slab, material=silicon))

to_gds_file#

to_gds_file(
    geometries,
    gds_path,
    *,
    cell_name="TOP",
    layer=0,
    datatype=0,
    length_scale=1e6,
)

Writes the 2D cross-sections of PolySlab geometries to a GDS file. The in-plane part of each position is applied. Extrusion bounds, sidewall_angle, and reference_plane have no GDS representation and are dropped; the reference-plane cross-section is written. Multi-loop geometry (a ComplexPolySlab with holes) is folded with XOR into simple polygons so holes stay holes.

geometriesPolySlab | Iterable[PolySlab]required

One PolySlab or ComplexPolySlab, or an iterable of them.

gds_pathstr | Pathrequired

Output .gds file path.

cell_namestrdefault 'TOP'

Name of the written cell.

layerintdefault 0

GDS layer number for all polygons.

datatypeintdefault 0

GDS datatype number for all polygons.

length_scalefloatdefault 1e6

GDS user units per metre. The default writes micrometre user units; the library unit is 1 / length_scale.

Returns None.

Raises ImportError when gdstk is missing. Raises ValueError for geometry that is not a PolySlab or has a non-identity rotation.

from witwin.maxwell.adapters.gds import from_gds, to_gds_file
 
slabs = from_gds("layout.gds", layer=1, bounds=(0.0, 220e-9))
to_gds_file(slabs, "layout_out.gds", layer=1)