EME

Every section function below accepts a ports argument, which names, windows, or displaces the ports on either end of the section, in place of its default left and right ports. Sections are otherwise connected in the order they are declared; use connect_ports to wire named ports explicitly instead, including into branching topologies. See Ports for the port model, the Port and Bounds classes, and make_angled_facet_port() for declaring an angled facet on a port.

EME_settings

Declare global EME defaults that are inherited by sections created afterward. Each parameter left unspecified leaves the corresponding stored default unchanged. A section’s own settings argument at creation still overrides these globals. Sections created before this call are not affected.

Parameters:
  • apply_scattering (bool or list[float]) – whether interfacial scattering loss is automatically applied to the S-matrix, either for every wavelength (True), no wavelengths (False), or only the listed wavelengths, default: False

  • beta_interpolation (str) – either ‘uniform’ or ‘linear’ to specify how the propagation constant is interpolated between z-slices of a taper/gds section, default: ‘uniform’

  • junction_normalization (bool or str or dict) – controls how junction S-matrices are power-normalized; either a single bool/’gain’ value applied to both junction kinds, or a dict with ‘inter_section’ and ‘intra_section’ keys, each accepting True (full SVD unitarization), False (raw, unnormalized), or ‘gain’ (clip singular values at 1 while keeping the raw lossy mode-matching), default: ‘gain’ for inter-section junctions and True for intra-section junctions

  • overlap_variation (float) – threshold of mode-list overlap between z-slices used by the automatic z-slicing bisection for taper/gds sections, default: 0.01

  • minimum_z_step (float) – [nm] the minimum allowed z-step for the automatic z-slicing bisection, default: 0.1

  • simulation_name (str) – simulation name, default: ‘emode’

straight_section

Define a uniform-cross-section waveguide section for the EME solver. Sections are placed sequentially in the z-direction in the order they are declared (or updated, if name already exists).

Parameters:
  • name (str) – section name, default: an auto-generated number

  • profile (str) – name of the profile that defines the section’s cross-section

  • length (float) – [nm] the length of the section

  • offset (float or tuple[float, float]) – [nm] transverse displacement of this section relative to whatever its ports connect to; if only a single value is given it is assumed to be the x-offset, default: (0, 0). Legacy: this is the same displacement as Port’s own offset applied to every port of the section at once, and setting both on one section raises an error. Prefer declaring offset on the specific port that sits off-axis.

  • ports (list[Port] or None) – replace the section’s default [Port('left', None, 'left'), Port('right', None, 'right')] on a per-side basis, where a supplied port replaces every existing port on the same side, and multiple ports may share a side. See Port, default: None (keep existing/default ports)

  • nonlinear (SHGProcess or SFGProcess or DFGProcess or None) – declare a \(\chi^{(2)}\) process on this section (EMode3D only), one of SHGProcess(pump_wavelength=...), SFGProcess(pump_wavelength_1=..., pump_wavelength_2=...) or DFGProcess(pump_wavelength=..., signal_wavelength=...) from emodeconnection. You name the process’s input wavelength(s); the remaining one follows from energy conservation and is matched against the section’s own profile wavelengths. Quasi-phase matching is declared on the process with poling_period [nm], plus duty_cycle (default: 0.5) and qpm_order (default: 1); inverted=True instead flips the local \(\chi^{(2)}\) sign for this section alone, the building block for poling assembled by hand from alternating sections. The two routes agree. Omitting nonlinear when updating an existing section leaves the declared process in place, and passing a new one replaces it. A declared process cannot currently be removed – nonlinear=None is indistinguishable from omitting the argument – so redeclare the section under a new name instead. A pump must also be declared, see excitation in settings, and the power-dependent result is read back with get('response'). The process also carries z_samples (default: 100), the number of points at which the forward amplitudes are recorded along each slice of this section once the driven solve converges. Those reach the caller as get('response').power_vs_z(wavelength), which returns the pump or signal power against position from a single solve rather than one solve per length. Set it to 0 to skip the sampling. Default: None

  • settings (dict or None) – per-section overrides of the EME_settings, merged onto the current global EME settings (or onto the section’s existing settings, when updating), e.g. settings={'overlap_variation': 0.005}, default: None

  • simulation_name (str) – simulation name, default: ‘emode’

pwd_section

Define a free space section, in which the field is expanded in plane waves (a plane-wave decomposition, PWD) over a uniform material, rather than in the guided modes of a cross-section. This is intended for gaps, facets, and free space propagation between waveguide sections; the junctions between a free space section and its neighbors are computed analytically instead of from a mode solve.

The basis is the set of plane-wave harmonics of the section window, with transverse wavevectors kx = 2*pi*m/window_width and ky = 2*pi*n/window_height, each harmonic carrying both a TE and a TM polarization. At least one of max_order, max_kt, or num_modes is required to truncate the basis; when more than one is given, they are applied in the order max_order, max_kt, num_modes.

Parameters:
  • name (str) – section name, default: an auto-generated number

  • length (float) – [nm] the length of the section

  • material (str or ~MaterialProperties or ~MaterialSpec) – the material filling the section, either a material name (from the material database) or a ~MaterialProperties or ~MaterialSpec object; must be isotropic, default: “Vacuum”

  • profile (str) – name of the profile that supplies the default window and the wavelength(s) of the section, default: ‘_default’

  • window (tuple[float, float] or None) – [nm] the (width, height) of the plane-wave window, which overrides the window of profile, default: None (use the window_width and window_height of profile)

  • offset (tuple[float, float]) – [nm] transverse displacement of the window’s origin, which is its horizontal center and bottom edge, default: (0, 0)

  • max_order (tuple[int, int] or None) – the largest harmonic order to keep in the x- and y-directions, so that |m| <= max_order[0] and |n| <= max_order[1], default: None

  • max_kt (float) – [1/nm] the largest transverse wavevector magnitude to keep, default: None

  • num_modes (int) – the largest number of modes to keep, counting both polarizations of each harmonic, and applied after max_order and max_kt; the basis always keeps a harmonic together with its opposite-direction pair, so the resulting count can land slightly below the requested value, default: None

  • ports (list[Port] or None) – replace the section’s default [Port('left', None, 'left'), Port('right', None, 'right')] on a per-side basis, where a supplied port replaces every existing port on the same side, and multiple ports may share a side. See Port, default: None (keep existing/default ports)

  • settings (dict or None) – per-section overrides of the EME_settings, default: None

  • simulation_name (str) – simulation name, default: ‘emode’

taper_section

Define a section that linearly interpolates its cross-section between two profiles for the EME solver, automatically slicing the z-direction to track the mode evolution.

Parameters:
  • name (str) – section name, default: an auto-generated number

  • profile (str) – name of the profile that defines the section beginning

  • profile_end (str) – name of the profile that defines the section ending

  • length (float) – [nm] the length of the section

  • ports (list[Port] or None) – replace the section’s default [Port('left', None, 'left'), Port('right', None, 'right')] on a per-side basis, where a supplied port replaces every existing port on the same side, and multiple ports may share a side. See Port, default: None (keep existing/default ports)

  • nonlinear (SHGProcess or SFGProcess or DFGProcess or None) – declare a \(\chi^{(2)}\) process on this section (EMode3D only), one of SHGProcess(pump_wavelength=...), SFGProcess(pump_wavelength_1=..., pump_wavelength_2=...) or DFGProcess(pump_wavelength=..., signal_wavelength=...) from emodeconnection. You name the process’s input wavelength(s); the remaining one follows from energy conservation and is matched against the section’s own profile wavelengths. Quasi-phase matching is declared on the process with poling_period [nm], plus duty_cycle (default: 0.5) and qpm_order (default: 1); inverted=True instead flips the local \(\chi^{(2)}\) sign for this section alone, the building block for poling assembled by hand from alternating sections. The two routes agree. Omitting nonlinear when updating an existing section leaves the declared process in place, and passing a new one replaces it. A declared process cannot currently be removed – nonlinear=None is indistinguishable from omitting the argument – so redeclare the section under a new name instead. A pump must also be declared, see excitation in settings, and the power-dependent result is read back with get('response'). The process also carries z_samples (default: 100), the number of points at which the forward amplitudes are recorded along each slice of this section once the driven solve converges. Those reach the caller as get('response').power_vs_z(wavelength), which returns the pump or signal power against position from a single solve rather than one solve per length. Set it to 0 to skip the sampling. Default: None

  • settings (dict or None) – per-section overrides of the EME_settings (notably overlap_variation and minimum_z_step, which control the automatic z-slicing), default: None

  • simulation_name (str) – simulation name, default: ‘emode’

gds_section

Define a section whose cross-section varies along z according to a mask imported from a GDS file, for more complex geometries defined with third-party layout tools.

Parameters:
  • name (str) – section name, default: an auto-generated number

  • profile (str) – name of the profile that defines the section’s cross-section outside of the GDS mask

  • gds_path (str) – path to the GDS file used to mask the section

  • layer_map (dict[str, int]) – mapping of profile shape names to GDS layer numbers, used to determine which shapes in profile are masked by which GDS layer

  • length (float) – [nm] the length of the section, default: derived from the GDS bounding box

  • cell_index (int or str) – the cell index (or name) in the specified GDS file, default: 0

  • scale (float) – unit scale factor applied to the GDS coordinates (e.g. use 1000 if the GDS file is drawn in microns but the simulation is in nanometers), default: 1

  • port_poses (dict) – explicit port positions/orientations at the section boundaries, default: derived from the GDS bounding box (left port at w=0, right port at w=length)

  • heal (bool) – whether to heal small gaps/self-intersections in the imported GDS polygons before meshing, default: True

  • grow (float) – amount to grow imported GDS polygons by, to close small gaps, default: 1e-3

  • ports (list[Port] or None) – replace the section’s default [Port('left', None, 'left'), Port('right', None, 'right')] on a per-side basis, where a supplied port replaces every existing port on the same side, and multiple ports may share a side. See Port, default: None (keep existing/default ports)

  • settings (dict or None) – per-section overrides of the EME_settings, default: None

  • simulation_name (str) – simulation name, default: ‘emode’

Note

Once sections are declared, call plot() with plane='z-x' or plane='z-y' to preview a geometry-only ‘Outline’ of the section layout before running EME.

copy_section

Place a second instance of an already-declared section in the chain, reusing its already-computed S-matrix instead of re-solving it.

Parameters:
  • name (str) – section name, default: an auto-generated number

  • section_name (str) – name of the existing section to copy

  • mirror (bool) – whether to reverse the placement along the propagation direction (swapping the left/right ports), default: False

  • port_swap (dict) – explicit port-name mapping to use instead of the automatically computed left/right swap when mirror is True, default: None

  • ports (list[Port] or None) – relabel/window this copy’s own left/right boundary, with the same semantics as any other section’s ports, independent of port_swap and of the source section’s own ports. See Port, default: None (keep existing/default ports)

  • settings (dict or None) – per-section overrides of the EME_settings, default: None

  • simulation_name (str) – simulation name, default: ‘emode’

connect_ports

Override the default left/right adjacency wiring between named ports declared with the ports argument of any section function.

Connecting two ports also pins their coordinate systems together: the two ports’ origins (the horizontal centre and bottom edge of each cross-section) are made to coincide, so the connection is what positions one section relative to the other. Use Port’s offset to displace a section off that shared origin, and its window to choose which part of a wider cross-section the port exposes. The first section you declare defines the global frame that plots are drawn in.

Parameters:
  • pairs (list[tuple[str, str]] or list[tuple[str, str, JunctionOverride]]) – a list of ('<section_name>.<port_name>', '<section_name>.<port_name>') pairs to connect, e.g. [('a.right', 'b.left'), ('c.right', 'd.left')]. A port can only have one connection, so declaring a new connection for a port that already had one supersedes it. Names are resolved against the actual declared sections/ports at EME build time. An entry may carry an optional third element, a JunctionOverride, configuring the junction that connection forms, e.g. [('a.right', 'b.left', JunctionOverride(junction_normalization=False))].

  • simulation_name (str) – simulation name, default: ‘emode’

class JunctionOverride

Settings for a single junction, passed as the optional third element of a connect_ports entry. Every parameter left unspecified inherits the value the junction’s sections resolved, so setting one does not disturb the others.

These settings belong to the connection rather than to either port because each one describes the interface between the two sections – which algebra joins their mode sets, how the resulting scattering matrix is normalized – and not one side of it.

Only an explicitly connected junction can carry these. A junction formed by the default left/right adjacency has nowhere to declare them, so name that pair in connect_ports to configure it, which supersedes the adjacency. The internal junctions of a taper or GDS section’s automatic z-slicing are controlled by that section’s settings instead.

Parameters:

junction_normalization (bool or str or None) – how this junction’s scattering matrix is power-normalized – True (full SVD unitarization), False (raw, unnormalized), or ‘gain’ (clip singular values at 1 while keeping the raw lossy mode-matching), default: None (inherit)

EME

Calculate the complete scattering matrix of all defined sections using the eigenmode expansion method (EME).

Parameters:
  • warm_start (bool) – whether to seed each taper/gds section’s automatic z-slice refinement from the previously solved chain (when available), instead of re-refining from scratch, default: False

  • simulation_name (str) – simulation name, default: ‘emode’

chi2_spectrum

Conversion spectrum of a \(\chi^{(2)}\) section, solved sparsely in wavelength (EMode3D only). Returns the signal power out over the pump power in at every wavelength in values.

A nonlinear spectrum oscillates with the phase mismatch, so it cannot be interpolated – at any useful resolution it has to be evaluated point by point. What can be interpolated is what the coupled-mode equations consume: the coupling and the propagation constants, which vary smoothly with wavelength. So the modes are solved at a handful of wavelengths chosen by how fast the mode list is actually moving, and the response is then evaluated at every requested wavelength from interpolated coupling. Against a directly solved 13-point spectrum across a phase-matching peak, five solves reproduce every point to four decimals, sidelobes included.

Quasi-phase matching is included: the grating enters as a wavevector shift and a Fourier coefficient built from poling_period, duty_cycle and qpm_order, none of which depend on wavelength, so a poled section keeps its exact grating while its phase mismatch moves with the interpolated propagation constants.

Requires a chain with a declared \(\chi^{(2)}\) section (see nonlinear on straight_section/taper_section) and a declared pump (excitation in settings).

Parameters:
  • values (list or array) – pump wavelengths [nm] to report at; the signal wavelength follows the declared process

  • section (str) – which nonlinear section to scan, default: the only one declared

  • tolerance (float) – the largest mode-list change tolerated between adjacent solves, default: 1e-3

  • minimum_step (float) – [nm] the smallest wavelength gap the sampler will bisect into, default: the output spacing, since resolving finer than you asked to see cannot change the reported curve

  • initial_points (int) – solves to place before any bisection, default: 5

  • power (float) – [W] pump power, default: the declared excitation’s

  • simulation_name (str) – simulation name, default: ‘emode’

Returns a dictionary with values, conversion (signal out over pump in), solved_wavelengths (where the solver actually ran), and unconverged – the intervals, if any, the sampler could not resolve to tolerance before reaching minimum_step.

free_space_field

Declare, or update, a free-space field: a Gaussian beam launched into a port of the chain. This is an excitation, not a section – it does not appear in the scattering matrix or in the layout.

After EME, plot(excitation=name) (or the plot window’s Mode menu, '<name> (free space)') renders the field the beam launches, projected onto the target port’s modes with the same pairing the junctions use. Re-declaring an existing name updates only the arguments you give.

Parameters:
  • name (str) – free-space field name, default: the next number

  • port (str) – the external port the beam enters, either a bare side 'left' (default) or 'right', or a qualified '<section>.<port>' name. The beam travels inbound from that port.

  • waist (float) – [nm] the 1/e field radius \(w\) in \(\exp(-r^2/w^2)\); required when declaring a new field

  • center (tuple[float, float]) – [nm] beam centre (x, y) in the port’s cross-section coordinates, default: (0, 0)

  • tilt (tuple[float, float]) – [degrees] (theta_x, theta_y); a positive theta_x steers the beam toward +x. This is the angle inside the medium the beam travels through, default: (0, 0)

  • component (str) – the one transverse E component the beam carries, 'Ex' (default) or 'Ey'

  • amplitude (float) – peak field value, default: 1

  • wavelength (float) – [nm], default: None (each port’s own wavelength)

  • simulation_name (str) – simulation name, default: ‘emode’

free_space_field_diagnostics

How much of a declared free-space field’s power the target port’s modal basis actually captures, after EME has solved the chain. The solver already warns when this falls below 90%; this returns it as a value a script can check or plot instead of parsing a log line.

A low captured fraction means the port’s modes cannot represent the beam – raise that section’s num_modes, or widen its window, before trusting a coupling number computed from it.

Parameters:
  • name (str) – the free-space field to diagnose

  • wavelength (float) – [nm] which of a multi-wavelength chain’s ports to diagnose, default: None (the lowest wavelength present)

  • simulation_name (str) – simulation name, default: ‘emode’

Returns a FreeSpaceProjection with captured_fraction, its complement dropped_fraction, the resolved port and wavelength, and the launch basis’s num_modes.

plot_S_matrix

Colormap plot of the transmission or reflection component of the EME scattering matrix.

Parameters:
  • input (int or str) – input field (first number of the scattering matrix identifier), default: 0

  • output (int or str) – output field (second number of the scattering matrix identifier), default: 1

  • plot_function (str or None) – specify the function to apply to the plot data (‘abs^2’, ‘angle’, ‘real’, ‘imag’), default: ‘abs^2’

  • file_name (str) – if specified, forces the function to save a file with the given parameters and will not display the interactive plot, file is saved under a directory called EM_figures, default: None

  • file_type (str) – file type for saving a plot, ‘pdf’ or ‘png’, default: ‘’ (no extension is added to file_name unless a file type is given)

  • simulation_name (str) – simulation name, default: ‘emode’