Skip to content

GDSFactory FDTD API

gsim.fdtd separates simulation setup into geometry, materials, source, monitors, domain, and solver concerns. It generates the transfer mesh and validated config.json, submits one source to one cloud job, and returns typed results.

Configure and run

from gsim import fdtd

sim = fdtd.Simulation(pdk=gpdk)
sim.materials(background="SiO2")
sim.geometry(component=mmi, geometry_tolerance_nm=10)
sim.source(
    port="o1",
    wavelength_um=1.55,
    wavelength_span_um=0.1,
    num_wavelengths=101,
)
sim.domain(padding_um=0.75, pml_cells=16)
sim.solver(
    cell_size_nm=40,
    energy_decay_fraction=1e-6,
    max_wall_seconds=3600,
)

result = sim.run(check_cache=True)
result.plot()
result.plot_plotly()

The transfer mesh preserves the resolved CAD boundaries exactly and uses coarse tetrahedra in volume interiors. Its default maximum target size is 1 µm; mesh_size_nm remains available as an advanced override. Gmsh conforms to every explicit component, hole, gap, tooth, and port edge even when it is smaller than that target. geometry_tolerance_nm is a geometry-error bound, not a mesh edge size.

Transfer meshing is independent of cell_size_nm, which requests the actual Yee grid used by the FDTD solver and defaults to 60 nm. Source sweeps default to 101 wavelengths. Guided-port monitors are implicit; one port source returns one S-matrix column.

Linear PDK sidewall angles are represented as continuous ruled solids between their exact bottom and top contours. If polygon offsetting changes component, hole, or edge topology, meshing safely falls back to vertical slices bounded by geometry_tolerance_nm instead of constructing an invalid loft.

Use write(path) to generate mesh.msh and config.json without submitting. The original flat constructor keywords remain accepted for compatibility.

Optional x_bounds, y_bounds, and z_bounds tuples set the non-PML physical domain in micrometers. Omitted axes remain automatically sized from the component, PDK stack, and padding. Explicit bounds must contain the geometry, sources, and monitors; guided ports must remain on a domain face. The backend adds PML outside these bounds.

sim.domain(
    x_bounds=(0.0, 35.0),
    y_bounds=(-12.0, 12.0),
    z_bounds=(-1.0, 1.55),
)

Setup visualization

FDTD setup views use the ported GDSFactory FDTD Three.js viewer and do not depend on PyVista. All physical groups are shown by default. Smaller material groups are listed first, followed by the largest volumetric group and then the ports.

sim.plot_3d()                                   # solid interactive geometry
sim.plot_3d(show_mesh=True)                     # add Gmsh surface edges
sim.plot_3d(zoom_to_cursor=False)               # zoom toward the view center
sim.plot_2d(axis="z", position_um=0.11)         # filled cross-section
sim.plot_2d(axis="x", position_um=0, show_mesh=True)  # add cell edges

Omit position_um to start at the geometry midpoint. The 2D viewer includes a slider that moves the plane through the mesh. Both methods return a standalone MeshViewer. Set show_mesh=True when element edges are useful, or use viewer.save("mesh.html") to share the visualization outside a notebook. Zooming moves toward the pointer by default; set zoom_to_cursor=False to zoom toward the view center instead. The same choice is available in the viewer's Zoom toward controls.

The viewer also summarizes the physical domain, estimated Yee-grid dimensions and total cell count (including PML). These values are estimates; the solver backend is authoritative when it constructs the final grid.

Sources

PortSource requires an explicit sim.source(port="...") selection before artifacts can be written or a job can run. A guided PDK port maps to an eigenmode source. Selecting a vertical_te or vertical_tm PDK port instead derives a Gaussian beam and fiber monitor from the port metadata.

Replace sim.source to use another physical source:

sim.source = fdtd.DipoleSource(
    position_um=(0, 0, 0.11),
    current_axis="z",
    wavelength_um=1.55,
)

sim.source = fdtd.LineCurrentSource(
    position_um=(0, -0.5, 0.11),
    line_axis="y",
    current_axis="z",
    length_um=1,
)

GaussianBeamSource exposes the aperture center and size, propagation and polarization vectors, focal point, waist, and optional background index. Only PortSource on a guided port produces normalized S-parameters. Other source types return outgoing port amplitudes and powers.

Plane monitors

Additional plane monitors can independently record flux, scalar heatmaps, and Gaussian fiber overlap:

sim.monitors.add_plane(
    name="top",
    center_um=(0, 0, 1),
    size_um=(10, 6, 0),
    normal="+z",
    flux=True,
    heatmap=fdtd.Heatmap(
        quantity="intensity",
        wavelengths_um=[1.55],
    ),
)

For a vertical PortSource, request a field image from its implicit port plane:

sim.source(
    port="o2",
    vertical_monitor_heatmap=fdtd.Heatmap(
        quantity="abs_e",
        wavelengths_um=[1.55],
    ),
)

Monitor names are unique. Use add(), remove(), and clear() to manage the collection. A plane's size must be zero along its normal.

Results

result.s_parameters.plot()
result.s_parameters.plot_plotly()
s_parameter_table = result.s_parameters.to_dataframe()

result.port_outputs.plot(quantity="modal_power")
port_table = result.port_outputs.to_dataframe()

# Net monitor flux can be useful as a diagnostic normalization.
result.plot_plotly(normalize_to="top")

# For a Gaussian source, use its analytic incident power and mask weak tails.
coupling = fdtd.gaussian_coupling_efficiency(
    result, sim.source, port="o1"
)
coupling.plot_plotly()

# An eigenmode-normalized fiber overlap is an amplitude; plot its power.
fiber = fdtd.fiber_coupling_efficiency(result.monitors["fiber"])
fiber.plot_plotly()

top = result.monitors["top"]
top.plot_flux()
top.plot_heatmap(wavelength_um=1.55)

Heatmap arrays are loaded lazily from their .npy sidecars. S-parameter plots gap samples flagged below the source noise floor by default. Convergence, grid, timing, and resolved runtime settings remain available on the result object.

Simulation setup views remain separate from these result plots: sim.plot_*() inspects the submitted geometry and mesh, while result.plot*() displays solver output.

Reference

Simulation

Simulation(
    pdk: Any | None = None,
    *,
    geometry: Geometry | Mapping[str, Any] | None = None,
    materials: Materials | Mapping[str, Any] | None = None,
    source: SourceType | Mapping[str, Any] | None = None,
    monitors: Monitors | list[PlaneMonitor | Mapping[str, Any]] | None = None,
    domain: Domain | Mapping[str, Any] | None = None,
    solver: Solver | Mapping[str, Any] | None = None,
    **legacy: Any,
)

Bases: CloudWorkflowMixin, RuntimeConfigMixin

Configure, serialize, and run a GDSFactory FDTD simulation.

Methods:

  • write

    Write mesh.msh and config.json for cloud execution.

  • plot_2d

    Show a filled cross-section, optionally with intersected cell edges.

  • plot_3d

    Show the 3D geometry, optionally with Gmsh surface edges.

write

write(output_dir: str | Path) -> SimulationArtifacts

Write mesh.msh and config.json for cloud execution.

plot_2d

plot_2d(
    *,
    axis: Axis = "z",
    position_um: float | None = None,
    show_mesh: bool = False,
    show_groups: Sequence[str] | None = None,
    hide_groups: Sequence[str] = (),
    zoom_to_cursor: bool = True,
    height: int = 600,
) -> Any

Show a filled cross-section, optionally with intersected cell edges.

plot_3d

plot_3d(
    *,
    show_mesh: bool = False,
    show_groups: Sequence[str] | None = None,
    hide_groups: Sequence[str] = (),
    zoom_to_cursor: bool = True,
    height: int = 600,
) -> Any

Show the 3D geometry, optionally with Gmsh surface edges.

PortSource

Bases: Source

Excite an explicitly selected PDK port.

Methods:

  • __call__

    Validate and apply several settings atomically.

  • validate_spectrum

    Require a physical sweep and a nonzero source.

Attributes:

amplitude class-attribute instance-attribute

amplitude: float = 1.0

model_config class-attribute instance-attribute

model_config = ConfigDict(validate_assignment=True, extra='forbid')

num_wavelengths class-attribute instance-attribute

num_wavelengths: int = Field(default=101, ge=1)

waveform class-attribute instance-attribute

waveform: Waveform = 'pulse'

wavelength_halfspan_um property

wavelength_halfspan_um: float

Return the half-span expected by the GDSFactory FDTD runtime schema.

wavelength_span_um class-attribute instance-attribute

wavelength_span_um: float = Field(default=0.1, ge=0)

wavelength_um class-attribute instance-attribute

wavelength_um: float = Field(default=1.55, gt=0)

__call__

__call__(**updates: Any) -> Self

Validate and apply several settings atomically.

validate_spectrum

validate_spectrum() -> Self

Require a physical sweep and a nonzero source.

DipoleSource

Bases: Source

Single-cell electric-current source.

Methods:

  • __call__

    Validate and apply several settings atomically.

  • validate_spectrum

    Require a physical sweep and a nonzero source.

Attributes:

amplitude class-attribute instance-attribute

amplitude: float = 1.0

model_config class-attribute instance-attribute

model_config = ConfigDict(validate_assignment=True, extra='forbid')

num_wavelengths class-attribute instance-attribute

num_wavelengths: int = Field(default=101, ge=1)

waveform class-attribute instance-attribute

waveform: Waveform = 'pulse'

wavelength_halfspan_um property

wavelength_halfspan_um: float

Return the half-span expected by the GDSFactory FDTD runtime schema.

wavelength_span_um class-attribute instance-attribute

wavelength_span_um: float = Field(default=0.1, ge=0)

wavelength_um class-attribute instance-attribute

wavelength_um: float = Field(default=1.55, gt=0)

__call__

__call__(**updates: Any) -> Self

Validate and apply several settings atomically.

validate_spectrum

validate_spectrum() -> Self

Require a physical sweep and a nonzero source.

LineCurrentSource

Bases: Source

Line of in-phase electric-current sources.

Methods:

  • __call__

    Validate and apply several settings atomically.

  • validate_spectrum

    Require a physical sweep and a nonzero source.

Attributes:

amplitude class-attribute instance-attribute

amplitude: float = 1.0

model_config class-attribute instance-attribute

model_config = ConfigDict(validate_assignment=True, extra='forbid')

num_wavelengths class-attribute instance-attribute

num_wavelengths: int = Field(default=101, ge=1)

waveform class-attribute instance-attribute

waveform: Waveform = 'pulse'

wavelength_halfspan_um property

wavelength_halfspan_um: float

Return the half-span expected by the GDSFactory FDTD runtime schema.

wavelength_span_um class-attribute instance-attribute

wavelength_span_um: float = Field(default=0.1, ge=0)

wavelength_um class-attribute instance-attribute

wavelength_um: float = Field(default=1.55, gt=0)

__call__

__call__(**updates: Any) -> Self

Validate and apply several settings atomically.

validate_spectrum

validate_spectrum() -> Self

Require a physical sweep and a nonzero source.

GaussianBeamSource

Bases: Source

Focused Gaussian beam injected through an axis-aligned aperture.

Methods:

  • __call__

    Validate and apply several settings atomically.

  • validate_spectrum

    Require a physical sweep and a nonzero source.

Attributes:

amplitude class-attribute instance-attribute

amplitude: float = 1.0

model_config class-attribute instance-attribute

model_config = ConfigDict(validate_assignment=True, extra='forbid')

num_wavelengths class-attribute instance-attribute

num_wavelengths: int = Field(default=101, ge=1)

waveform class-attribute instance-attribute

waveform: Waveform = 'pulse'

wavelength_halfspan_um property

wavelength_halfspan_um: float

Return the half-span expected by the GDSFactory FDTD runtime schema.

wavelength_span_um class-attribute instance-attribute

wavelength_span_um: float = Field(default=0.1, ge=0)

wavelength_um class-attribute instance-attribute

wavelength_um: float = Field(default=1.55, gt=0)

__call__

__call__(**updates: Any) -> Self

Validate and apply several settings atomically.

validate_spectrum

validate_spectrum() -> Self

Require a physical sweep and a nonzero source.

PlaneMonitor

Bases: _MutableModel

Flux, field, and optional fiber-overlap measurement on a plane.

Methods:

  • __call__

    Validate and apply several settings atomically.

Attributes:

model_config class-attribute instance-attribute

model_config = ConfigDict(validate_assignment=True, extra='forbid')

__call__

__call__(**updates: Any) -> Self

Validate and apply several settings atomically.

FDTDResult dataclass

FDTDResult(
    excitation_type: str,
    excited_port: str | None,
    ports: list[str],
    wavelength_um: ndarray,
    frequency_hz: ndarray,
    valid: ndarray,
    s_parameters: SParameterResults,
    port_outputs: PortOutputResults,
    monitors: MonitorResults,
    convergence: dict[str, Any],
    grid: dict[str, Any],
    timing: dict[str, Any],
    config_resolved: dict[str, Any],
    output_path: Path,
    sim_dir: Path | None = None,
    job_name: str = "",
    files: dict[str, Path] = dict(),
)

Complete typed result from one GDSFactory FDTD cloud run.

Methods:

  • from_file

    Load a result JSON and lazy references to any heatmap sidecars.

  • from_run_result

    Locate and parse GDSFactory FDTD output downloaded by :mod:gsim.gcloud.

  • plot

    Plot S-parameters, or port power for a non-eigenmode source.

  • plot_plotly

    Interactively plot S-parameters, or non-eigenmode port power.

from_file classmethod

from_file(
    path: str | Path,
    *,
    files: Mapping[str, Path] | None = None,
    sim_dir: str | Path | None = None,
    job_name: str = "",
) -> FDTDResult

Load a result JSON and lazy references to any heatmap sidecars.

from_run_result classmethod

from_run_result(run_result: Any) -> FDTDResult

Locate and parse GDSFactory FDTD output downloaded by :mod:gsim.gcloud.

plot

plot(**kwargs: Any) -> tuple[Any, Any]

Plot S-parameters, or port power for a non-eigenmode source.

plot_plotly

plot_plotly(**kwargs: Any) -> Any

Interactively plot S-parameters, or non-eigenmode port power.