# 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`](#connect_ports) to wire named ports explicitly instead, including into branching topologies. See [Ports](../interface.md#ports) for the port model, the `Port` and `Bounds` classes, and [`make_angled_facet_port()`](../interface.md#make_angled_facet_port) for declaring an angled facet on a port. For a grating, [`grating_sections()`](../interface.md#grating_sections) gives the length of every section of a uniform, chirped or noisy grating.

### 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. It takes 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. It applies to junctions with a plane-wave side ([`pwd_section`](#pwd_section)) or an angled facet. A junction between two mode-solved cross-sections conserves power by construction and is not normalized.
  * **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: 1. When the largest junction error has not fallen by 20 % over three bisections, the bisection follows instead the power that the modes guided at both ends of the section lose at each junction, and if that stalls too it stops with a warning
  * **snap_window** (*float*) – the fraction of a slice’s length around its midpoint within which the automatic z-slicing bisects at a cross-section already solved elsewhere (for the same settings, mesh and wavelength) instead of at the midpoint, which saves a mode solve wherever two tapers or GDS sections share widths. 0 always bisects at the midpoint, default: 0.25
  * **initial_slices** (*str*) – how a taper or GDS section is sliced before the automatic z-slicing bisection refines it, either ‘auto’ (boundaries placed where the cross-section changes, from its rasterized refractive index: none along a straight run, evenly along a linear taper, densely inside a short fast feature, and at every corner of a GDS outline) or ‘single’ (one slice, the behavior before EMode 1.0.6). Refinement still runs afterward and decides the final slicing, so the choice moves results only within its tolerance and changes how many rounds it takes. A `warm_start` in [`EME`](#EME) takes its slices from the previous solve instead, default: ‘auto’
  * **simulation_name** (*str*) – simulation name, default: ‘emode’

A section’s own `settings` dictionary accepts the keys above (except `simulation_name`), and five more that tune the slicing. They are rarely needed, and `EME_settings` does not take them.

* `schedule_samples`: the number of positions along the section at which the refractive index is rasterized (no mode solve) to find where it changes, at least 2, default: 128
* `schedule_change_per_slice`: the relative change in the rasterized index that one initial slice spans, so a smaller value starts with more slices, default: 0.05
* `schedule_max_slices`: the largest number of initial slices, default: 64
* `schedule_coarsen`: how many times coarser than the profile’s mesh the index is rasterized, at least 1, default: 4
* `slice_phase_limit`: a slice over which the cross-section changes is kept shorter than `slice_phase_limit * wavelength / (2 n_max)`, where `n_max` is the largest effective index of its modes. Equal slices longer than about `wavelength / (2 n_max)` act as a grating and reflect in phase, which refinement cannot see. The initial slices are cut evenly under this limit, up to `schedule_max_slices`. Refinement then estimates how much power each guided mode loses to the steps’ in-phase reflections and conversions, and cuts a slice under the limit only where that power is significant, so a long, slow taper keeps about `schedule_max_slices` slices. Between 0 and 1, or None to turn it off, default: 0.8

For example, `taper_section(..., settings={'schedule_change_per_slice': 0.02})`. Initial slice boundaries closer together than `minimum_z_step` are merged.

### 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. A single value is taken as the x-offset, default: (0, 0). **Legacy**: this is the same displacement as [`Port`](../interface.md#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*](../interface.md#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`](../interface.md#Port), default: None (keep existing/default ports)
  * **nonlinear** (*SHGProcess* *or* *SFGProcess* *or* *DFGProcess* *or* [*PulsedProcess*](#PulsedProcess) *or* *False* *or* *None*) – declare a $\chi^{(2)}$ process on this section (EMode<sub>3D</sub> 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), and 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). Setting `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` (or passing `None`) when updating an existing section leaves the declared process in place, passing a new one replaces it, and `nonlinear=False` removes it, so the section propagates linearly again. A pump must also be declared, see `excitation` in [`settings`](setup.md#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. A pulse’s nonlinearity is declared instead with a `PulsedProcess`, see [pulsed nonlinear propagation](#pulsed-propagation). Default: None
  * **settings** (*dict* *or* *None*) – per-section overrides of the [`EME_settings`](#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. It 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*](../interface.md#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`](../interface.md#Port), default: None (keep existing/default ports)
  * **settings** (*dict* *or* *None*) – per-section overrides of the [`EME_settings`](#EME_settings), default: None
  * **simulation_name** (*str*) – simulation name, default: ‘emode’

#### NOTE
A junction with a free space section matches the two continuity conditions (for E and for H) against the modes of the side the light arrives from. When the other side has fewer modes, the junction cannot represent every incident field, and some incident modes leave it with almost none of their power: they are dropped by the truncated basis, not attenuated by any physics. EMode warns when an incident mode keeps less than 1 % of its power, naming the dropped modes and the reflected plus transmitted power over all of the incident modes. Raise the mode count on the side with fewer modes, `num_modes` of the waveguide section or `max_order`, `max_kt` or `num_modes` of the free space section.

#### NOTE
The reflection of a confined mode at a facet into free space is not the modal Fresnel value `((n_eff - n) / (n_eff + n))^2`. That formula gives the free space the admittance of a single plane wave at normal incidence. A mode confined to a fraction of a wavelength is a spread of plane waves, and those at an angle see a different admittance: smaller for the field component normal to their plane of incidence, larger for the one in it. A quasi-TE mode therefore reflects more than its modal Fresnel value and a quasi-TM mode less, more so the tighter the confinement. A 2640 x 816 nm Si guide meeting SiO2 reflects 0.216 of its quasi-TE fundamental, 1.35 times its modal Fresnel value of 0.160, and an independent solve with an empty section in place of the free space agrees within 1.4 %. Give the waveguide section periodic walls (`boundary_condition='PP'`) and the free space section the same window, so that both describe the same far side. Walls on a section that stands in for free space raise the reflection when they are close to the guide (by 35 % with walls 1 µm from that guide’s core).

### 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*](../interface.md#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`](../interface.md#Port), default: None (keep existing/default ports)
  * **nonlinear** (*SHGProcess* *or* *SFGProcess* *or* *DFGProcess* *or* [*PulsedProcess*](#PulsedProcess) *or* *False* *or* *None*) – declare a $\chi^{(2)}$ process on this section (EMode<sub>3D</sub> 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), and 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). Setting `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` (or passing `None`) when updating an existing section leaves the declared process in place, passing a new one replaces it, and `nonlinear=False` removes it, so the section propagates linearly again. A pump must also be declared, see `excitation` in [`settings`](setup.md#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. A pulse’s nonlinearity is declared instead with a `PulsedProcess`, see [pulsed nonlinear propagation](#pulsed-propagation). Default: None
  * **settings** (*dict* *or* *None*) – per-section overrides of the [`EME_settings`](#EME_settings) (notably `overlap_variation` and `minimum_z_step`, which control the automatic z-slicing), default: None
  * **simulation_name** (*str*) – simulation name, default: ‘emode’

### bend_section

Define a bend for the EME solver: the cross-section of `profile` swept along a curve. The bend is placed in the layout like any other section, and sections declared after it continue from its end, turned by the bend’s angle.

The curve is a circular arc (`radius` and `sweep_angle`), or a curve whose radius varies along its length, which avoids the abrupt change of curvature where a circular bend meets a straight or where an S-bend reverses. A tuple of radii with `sweep_angle` gives a clothoid, whose curvature changes linearly with arc length, and `inf` stands for a straight end, so `radius=(inf, 5000)` eases a straight into a 5 um bend. `euler=dict(angle=..., min_radius=...)` gives a symmetric Euler bend, its curvature rising linearly from zero to `1 / min_radius` at its middle and back to zero, of length `2 |angle| min_radius`. `curvature` with `length` gives any other profile. A bend whose radius varies is sliced along its length like a taper, each slice solved at its own radius, and refined by [`EME`](#EME). Its layout follows the curve.

An anisotropic crystal is fixed in the chip, so the guide sees it turn along the bend. When a material is anisotropic in the plane of the chip (x-cut lithium niobate, for example), the bend is sliced along its heading and refined like a taper, and every section placed after a turn sees the crystal turned with it. A circular bend of an isotropic material, or of a crystal whose optic axis is vertical (z-cut thin film), is a single slice and a single mode solve. A [`copy_section`](#copy_section) reuses its source’s S-matrix, so it raises an error when its own placement would show the crystal differently, and asks for the copy to be declared as its own section.

A bend has no mirror symmetry about x, so an x symmetry wall in the profile’s `boundary_condition` (such as ‘0S’ or ‘SA’) is replaced by ‘0’ with a warning, and ‘TE’ and ‘TM’ keep only the vertical fold. In a circuit that also has straight sections, the x fold then applies to none of them (see [`EME`](#EME)). Updating an existing bend changes only the arguments given.

* **Parameters:**
  * **name** (*str*) – section name, default: an auto-generated number
  * **profile** (*str*) – name of the profile that defines the cross-section
  * **radius** (*float* *or* *list* *[**float* *]*) – [nm] the bend radius, measured to the origin of the profile’s x axis, must be positive. A list or tuple of radii, equally spaced in arc length with the curvature linear between them, gives a bend whose radius varies, `inf` for straight. A new bend needs `radius` and `sweep_angle`, `euler`, or `curvature` and `length`
  * **sweep_angle** (*float*) – [rad] the angle the bend turns through, positive toward +x and negative toward -x, not zero. With a list of radii it sets the bend’s length
  * **euler** (*dict* *or* *None*) – a symmetric Euler bend, `dict(angle=..., min_radius=...)`, with `angle` in radians (positive toward +x) and `min_radius` in nm, default: None
  * **curvature** (*list* *[**float* *] or* *None*) – [1/nm] the signed curvature (positive toward +x) at equally spaced points along the bend, from start to end, linear between them, at least two values. A change of sign gives an S-bend, default: None
  * **length** (*float*) – [nm] the arc length of a bend given by `curvature`, default: None
  * **method** (*str*) – how each slice is solved, either ‘cylindrical’ (the bend in cylindrical coordinates, as `bend_radius` in [`settings`](setup.md#settings)) or ‘TO’ (transformation optics, see `bend_method` in [`settings`](setup.md#settings)), default: ‘cylindrical’
  * **ports** (*list* *[*[*Port*](../interface.md#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`](../interface.md#Port), default: None (keep existing/default ports)
  * **settings** (*dict* *or* *None*) – per-section overrides of the [`EME_settings`](#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
  * **nonlinear** ([*PulsedProcess*](#PulsedProcess) *or* *False* *or* *None*) – declare a pulse’s nonlinearity on this section as a `PulsedProcess` (see [pulsed nonlinear propagation](#pulsed-propagation)), `False` to remove one. The CW $\chi^{(2)}$ processes are not available on a GDS section. Default: None
  * **ports** (*list* *[*[*Port*](../interface.md#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`](../interface.md#Port), default: None (keep existing/default ports)
  * **settings** (*dict* *or* *None*) – per-section overrides of the [`EME_settings`](#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`](#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*](../interface.md#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`](../interface.md#Port), default: None (keep existing/default ports)
  * **settings** (*dict* *or* *None*) – per-section overrides of the [`EME_settings`](#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 center 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`](../interface.md#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*](#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`](#EME) build time. An entry may carry an optional **third** element, a [`JunctionOverride`](#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`](#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, not one side of it: which algebra joins their mode sets and how the resulting scattering matrix is normalized.

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`](#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, one of True (full SVD unitarization), False (raw, unnormalized), or ‘gain’ (clip singular values at 1 while keeping the raw lossy mode-matching), for a junction with a plane-wave side or an angled facet, default: None (inherit)

### EME

Calculate the complete scattering matrix of all defined sections using the eigenmode expansion method (EME). Before solving anything it checks the layout (see [`check_layout`](#check_layout)) and raises an error when two sections overlap or a loop of connected sections misses closing (its two ports more than `minimum_z_step` apart, or more than 1e-6 rad from facing each other), and warns when a section’s outline crosses itself or a section is reached by no connection.

A `boundary_condition` of ‘TE’, ‘TM’, ‘TE-h’, ‘TE-v’, ‘TM-h’ or ‘TM-v’ folds a cross-section only about a middle it is mirror symmetric about (see [`settings`](setup.md#settings)). In a circuit, a fold applies only when every section is symmetric about it, because a folded section has no modes of the other parity, and where it meets an unfolded section the power those modes would carry is lost. When some sections fold about a middle and others cannot, `EME()` warns and solves the circuit again with that fold taken out of every section. Set a boundary condition without it (for example ‘TE-v’ instead of ‘TE’ for a circuit with offset or bent guides) to solve once.

* **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’

### check_layout

Check the layout of the declared sections without solving. Every section is placed where its connections put it and drawn top down from its shapes that do not span the window. Returns a `LayoutReport` with `overlaps` (pairs of sections whose outlines overlap, as `(section, section, area)` with the area in nm<sub>2</sub>, where sections that only share a port edge do not overlap), `self_intersecting` (sections whose own outline crosses itself, such as a bend swept past a full turn), `closures` (for each connection that closes a loop, how far apart its two ports are in nm and how far from facing each other in radians), and `unplaced` (sections no connection reaches, which are placed after the section declared before them). `max_closure()` returns the largest closure distance and angle, or zeros without a loop. `plot(view='layout')` draws the same layout. `LayoutReport` is defined in `emodeconnection`.

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

### chi2_spectrum

Conversion spectrum of a $\chi^{(2)}$ section, solved sparsely in wavelength (EMode<sub>3D</sub> 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`](#straight_section)/[`taper_section`](#taper_section)) and a declared pump (`excitation` in [`settings`](setup.md#settings)).

* **Parameters:**
  * **values** (*list* *or* *array*) – pump wavelengths [nm] to report at (the signal wavelength follows from 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, that the sampler could not resolve to `tolerance` before reaching `minimum_step`).

### free_space_field

Declare, or update, a free-space field: a Gaussian, Hermite-Gauss or Laguerre-Gauss beam, or a combination of the port’s own modes, 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`](#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* *or* *tuple* *[**float* *,* *float* *]*) – [nm] the 1/e field radius $w_0$ in $\exp(-r^2/w_0^2)$ at the beam’s waist, required when declaring a new Gaussian beam. A pair `(w_x, w_y)` declares an elliptical beam, $\exp(-x^2/w_x^2 - y^2/w_y^2)$ at its waist
  * **waist_definition** (*str*) – how `waist` is measured: `'1/e2_radius'` is $w_0$ above, the radius where the intensity falls to $1/e^2$, `'fwhm'` the full width at half maximum of the intensity ($w_0 = \mathrm{FWHM}/\sqrt{2 \ln 2}$), and `'mfd'` the mode field diameter $2 w_0$, the fiber convention. The beam stores $w_0$, and a pair converts each axis. Default: `'1/e2_radius'`
  * **center** (*tuple* *[**float* *,* *float* *]*) – [nm] beam center `(x, y)` in the port’s cross-section coordinates, default: (0, 0)
  * **tilt** (*tuple* *[**float* *,* *float* *]*) – [degrees] `(theta_x, theta_y)`, where 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'`. Give `component` or `polarization`, not both
  * **polarization** (*tuple* *[**float* *,* *float* *] or* *tuple* *[**float* *,* *float* *,* *float* *] or* *None*) – the beam on both transverse E components, `(a_x, a_y)` or `(a_x, a_y, delta)`: $E_x$ carries $a_x$ and $E_y$ carries $a_y e^{j\delta}$, with `delta` in degrees (default 0). The pair is scaled so that $|a_x|^2 + |a_y|^2 = 1$, so `amplitude` keeps its meaning, and at least one of $a_x$ and $a_y$ must be nonzero. `(1, 1, 90)` is circular polarization, and other values give linear or elliptical polarization. It replaces `component`. Default: None (the beam on `component` alone)
  * **envelope** (*str*) – the beam’s transverse shape, `'gaussian'`, `'hermite_gauss'` or `'laguerre_gauss'`. Each order is normalized to the power of the Gaussian of the same waist and amplitude, and order (0, 0) is the Gaussian. A Hermite-Gauss beam is separable, so it takes a pair `waist` or `waist_position`. A Laguerre-Gauss beam is circular, so it takes one `waist` and one `waist_position`, and a pair raises an error. Default: `'gaussian'`
  * **order** (*tuple* *[**int* *,* *int* *]*) – the beam’s order, `(m, n)` of a Hermite-Gauss beam ($H_m$ along x and $H_n$ along y, both at least 0) or `(p, l)` of a Laguerre-Gauss beam (the radial index $p \ge 0$ and the azimuthal index $l$, which may be negative, with the phase $e^{j l \phi}$). Away from the waist the beam carries $m + n + 1$ times the Gaussian’s Gouy phase for Hermite-Gauss, and $2p + |l| + 1$ times it for Laguerre-Gauss. Default: (0, 0)
  * **amplitude** (*float*) – peak field value at the waist, default: 1
  * **wavelength** (*float*) – [nm], default: None (each port’s own wavelength)
  * **waist_position** (*float* *or* *tuple* *[**float* *,* *float* *]*) – [nm] the distance from the port to the beam’s waist, along the direction the beam travels. A positive value focuses the beam inside the chain, a negative one puts the waist behind the port (a diverging beam). The launched beam carries the wavefront curvature and Gouy phase of that distance, with the Rayleigh range $z_R = \pi n w_0^2/\lambda$ of the port’s background index $n$. A pair `(z_x, z_y)` puts the waist along x and along y at different distances (an astigmatic beam). With a pair in `waist` or `waist_position`, each axis has its own Rayleigh range, wavefront curvature and half of the Gouy phase, and the beam’s power does not depend on either distance. Default: 0 (the waist on the port)
  * **phase** (*float*) – [radians] global phase of the launched field, default: 0
  * **modes** (*list* *[**int* *]*) – launch a combination of the target port’s own modes instead of a beam: their indices. Giving a beam’s `name` a list of `modes` (or a mode combination’s `name` a `waist`) replaces it with the other kind
  * **powers** (*list* *[**float* *]*) – one power per entry of `modes`, default: 1 each. Each mode is launched with the amplitude $\sqrt{P}\,e^{j\phi}$, as a `Source` is
  * **phases** (*list* *[**float* *]*) – [radians] one phase per entry of `modes`, default: 0 each
  * **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`](#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’

<a id="monitors"></a>

## Monitors

A monitor is a plane inside the chain on which the solved field is sampled after [`EME`](#EME). It is output only: it is not a port, does not enter the scattering matrix and does not change the solve, so monitors can be added, moved or removed without solving again. The plane runs across the full window in x and is tilted about x, so it can sample the light a grating coupler radiates upward. [`monitor_field`](#monitor_field) returns the field on it, and [`monitor_coupling`](#monitor_coupling) its coupling to a tilted Gaussian beam, such as the mode of a fiber above a grating. `plot()` draws each monitor’s plane on the ‘z-y’ panel, and `sweep(result='monitor_coupling')` sweeps the coupling (see [`sweep`](solver-analysis.md#sweep)). The ‘EME: Grating Coupler’ example uses all three.

### monitor

Declare a monitor. Declaring the same name again replaces it.

* **Parameters:**
  * **name** (*str*) – the monitor’s name
  * **w** (*float*) – [nm] the position along the chain, from its left port, that the plane passes through
  * **y** (*float*) – [nm] the height that the plane passes through at `w`. An untilted plane must lie inside the window, between 0 and `window_height`
  * **angle** (*float*) – [degrees] the tilt of the plane about x from horizontal, strictly between -90 and 90, where a positive angle tips the far end of the plane up, default: 0
  * **length** (*float* *or* *None*) – [nm] the extent along the plane, centered on (`w`, `y`), default: None (as far as the chain and the window allow)
  * **resolution** (*float* *or* *None*) – [nm] the sampling step along the plane, default: None (the window’s `x_resolution`)
  * **simulation_name** (*str*) – simulation name, default: ‘emode’

### monitor_field

The field on a monitor’s plane after [`EME`](#EME), for 1 W launched in one mode of one port.

* **Parameters:**
  * **name** (*str*) – the monitor’s name
  * **port** (*str*) – the external port the chain is excited from, default: ‘left’
  * **mode** (*int*) – the launched mode, default: 0
  * **wavelength** (*float* *or* *None*) – [nm] which of a multi-wavelength chain’s wavelengths to use, the nearest one present, default: None (the chain’s first)
  * **simulation_name** (*str*) – simulation name, default: ‘emode’

Returns a `PlaneField` with `fields`, a dictionary of the six components (‘Ex’, ‘Ey’, ‘Ez’, ‘Hx’, ‘Hy’, ‘Hz’) on the plane, each a complex array of shape (len(x), len(s)), where `x` [nm] runs across the plane and `s` [nm] along it, at chain positions `w` and heights `y` [nm]. `Sn` is the time-averaged Poynting flux through the plane (positive along its upward normal), `power_through_plane` [W] its integral, and `power_in` the 1 W launched, so `power_through_plane` is also the fraction of the launched power that crosses the plane.

### monitor_coupling

The coupling between a launched waveguide mode and a Gaussian beam through a monitor’s plane, after [`EME`](#EME). It is the fraction of the launched power crossing the plane times the normalized overlap of the field’s `polarization` component with the beam. By reciprocity the same number is the coupling from the beam into the launched mode, which is the figure a grating coupler is designed for. Only the overlap is computed, so trying other beams costs no solve.

* **Parameters:**
  * **name** (*str*) – the monitor’s name
  * **mode_field_diameter** (*float*) – [nm] the beam’s mode field diameter, for example 10.4e3 for SMF-28 at 1550 nm
  * **angle** (*float*) – [degrees] the beam’s angle from the plane’s normal, positive toward +s (along the chain), default: 0
  * **center** (*tuple* *[**float* *,* *float* *] or* *None*) – [nm] the beam’s center (x, s) on the plane, default: None (centered on the plane)
  * **index** (*float* *or* *None*) – the refractive index of the medium the beam travels in, default: None (the profile’s background material)
  * **polarization** (*str*) – the field component the beam couples to, ‘x’, ‘y’, or ‘s’ (along the plane), default: ‘x’
  * **port** (*str*) – the launching port, default: ‘left’
  * **mode** (*int*) – the launched mode, default: 0
  * **wavelength** (*float* *or* *None*) – [nm] which of a multi-wavelength chain’s wavelengths to use, default: None (the chain’s first)
  * **simulation_name** (*str*) – simulation name, default: ‘emode’

Returns a `BeamCoupling` with `coupling`, its two factors `power_fraction` and `overlap`, and the beam it was computed for (`mode_field_diameter`, `center`, `angle`, `index`, `polarization`) at `wavelength`.

<a id="pulsed-propagation"></a>

## Pulsed nonlinear propagation

A femtosecond pulse is declared as the excitation, `settings(excitation=PulsedSource(...))`, and a section’s nonlinearity with `nonlinear=PulsedProcess(...)` on [`straight_section`](#straight_section), [`taper_section`](#taper_section) or [`gds_section`](#gds_section) (EMode<sub>3D</sub> only). [`EME`](#EME) refines the chain at the pulse’s center wavelength, solves each distinct cross-section at `dispersion_samples` wavelengths across the pulse’s band, and propagates the pulse forward through every slice and junction with $\chi^{(2)}$, Kerr ($\chi^{(3)}$), Raman and two-photon absorption, three- and four-photon absorption when asked for, and the free carriers that absorption makes. Reflections and power scattered into modes that are not tracked are discarded and reported, with a warning where that is not safe. The classes below are imported from `emodeconnection`.

The nonlinear coefficients come from the materials: $\chi^{(2)}$ from the material’s $d$ tensor, $\chi^{(3)}$ from its nonlinear index $n_2$ as $\chi^{(3)} = \tfrac{4}{3}\varepsilon_0 c n^2 n_2$, the Raman response from the material that holds most of the Kerr overlap, and N-photon absorption from its coefficients, $\beta_\mathrm{TPA}$ for two photons and $\beta_3$ and $\beta_4$ for three and four, each active only where N photons carry more than the material’s bandgap. In the material database, `SiO2`, `SiN`, `Si`, `Diamond`, `LN` and `LN_MgO` carry $n_2$ (`Air` and `Vacuum` carry an $n_2$ of zero), `SiO2` (Blow and Wood) and `LN`/`LN_MgO` (four A<sub>1</sub>(TO) lines, Bache and Schiek) a Raman response, `Si`, `GaAs`, `AlGaAs`, `Diamond`, `LN` and `LN_MgO` a two-photon coefficient, `Si`, `GaAs`, `LN` and `LN_MgO` a three-photon coefficient, and `Si` a free-carrier model (Soref and Bennett), each with its reference in `property_refs`. A material of your own takes the same through `MaterialProperties(n=..., n2=..., raman=(fraction, [(weight, position, half_width), ...]), tpa=..., beta_3=..., beta_4=..., bandgap=...)`, with $n_2$ in m<sub>2</sub>/W, the Raman line positions and half widths in cm<sub>-1</sub>, $\beta_\mathrm{TPA}$ in m/W, $\beta_3$ in m<sub>3</sub>/W<sub>2</sub>, $\beta_4$ in m<sub>5</sub>/W<sub>3</sub> and the bandgap in eV. `PulsedProcess(chi3=True)` raises an error naming a material that has no $n_2$.

### *class* PulsedSource

A pulse launched into one port of the circuit, for `settings(excitation=...)`.

* **Parameters:**
  * **center_wavelength** (*float*) – [nm] the carrier wavelength, inside `wavelength_range`
  * **duration** (*float*) – [fs] the intensity FWHM, required except for `shape='cw'` and `shape='measured'`
  * **energy** (*float*) – [pJ] the pulse energy, give exactly one of `energy` and `peak_power` (a measured pulse is scaled to either, as an analytic one is)
  * **peak_power** (*float*) – [W] the peak power
  * **port** (*str*) – the launch port, default: ‘left’
  * **mode** (*int* *or* *dict*) – the tracked mode that carries the pulse, or a dictionary `{mode: amplitude}` of complex amplitudes that splits it between tracked modes (normalized, so only their ratios matter), default: 0
  * **shape** (*str*) – ‘sech’, ‘gaussian’, ‘cw’ (a continuous wave of `peak_power` at `center_wavelength`, with no `duration` or `energy`), or ‘measured’ (the pulse of `measurement`, with no `duration` or `chirp`), default: ‘sech’
  * **chirp** (*float*) – the chirp $C$ of $\exp(j C t^2 / 2 T_0^2)$, where a positive value raises the instantaneous frequency across the pulse, default: 0
  * **measurement** ([*PulseMeasurement*](#PulseMeasurement) *or* *None*) – a measured pulse for `shape='measured'`, see [`PulseMeasurement`](#PulseMeasurement). A measured spectrum is launched at its own wavelengths, and `center_wavelength` stays the wavelength at which the circuit is refined. A measured temporal trace is centered on `center_wavelength`. Default: None
  * **repetition_rate** (*float*) – [Hz] for average powers, default: None
  * **wavelength_range** (*tuple* *[**float* *,* *float* *]*) – [nm] the span of the frequency grid, default: (450, 3500)
  * **num_points** (*int*) – frequency points, even and at least 16, which with `wavelength_range` set the time window (the number of points over the frequency span, 28 ps at the defaults), default: 16384
  * **alias** (*int*) – oversampling of the real-field products, 1, 2 or 3 (2 suffices for $\chi^{(2)}$, 3 for $\chi^{(3)}$), default: 2

### *class* PulseMeasurement

A measured pulse, for `PulsedSource(shape='measured', measurement=...)`: a sampled spectrum (as from a spectrometer, often with a phase retrieved by FROG or SPIDER) or a sampled temporal trace. Give exactly one of `wavelength` and `time`.

The samples are never launched as they are. [`settings`](setup.md#settings) fits them first, so noise and sparse sampling do not reach the propagation, and refuses the source with an error when the fit is not good enough. The fit’s quality is 1 minus its normalized residual: the $R^2$ of the intensity, less the intensity-weighted mean of $1 - \cos$ of the phase error when a phase is given. Below `min_fit_quality` the error states the quality reached, the model and where the residual is largest, so you can choose another model, clean the data, or lower `min_fit_quality` knowingly. The accepted pulse is resampled onto the frequency grid, centered in the time window and scaled to the source’s `energy` or `peak_power`. The fit is reported on the result as `get('pulse_response').measured_fit`, with its `model`, `quality`, `fwhm` [fs], `center_wavelength` [nm], `gdd` [fs<sub>2</sub>], `tod` [fs<sub>3</sub>], `chirp` (for ‘sech’ and ‘gaussian’), `transform_limited`, `energy_outside` (the fraction of the energy cut by the window), `residual_peak` and `background` (the constant background subtracted, as a fraction of the samples’ peak).

Data that stops while the pulse is still bright (above 5 % of its peak), or whose pulse lies mostly outside the source’s `wavelength_range`, is refused. A spectrometer’s dark counts or stray light put a constant background under the whole trace, which reads as a pulse that never reaches zero: give `background` to subtract it before the fit and this check. Data that reaches past `wavelength_range` (or past the time window) is cut with a smooth window, with a warning giving the fraction of the energy outside.

* **Parameters:**
  * **wavelength** (*list* *[**float* *] or* *numpy array* *or* *None*) – [nm] the wavelengths of a measured spectrum
  * **time** (*list* *[**float* *] or* *numpy array* *or* *None*) – [fs] the times of a measured temporal trace
  * **intensity** (*list* *[**float* *] or* *numpy array*) – the intensity at each sample, on any scale. A spectrum’s intensity is per unit wavelength, as a spectrometer records it.
  * **phase** (*list* *[**float* *] or* *numpy array* *or* *None*) – [rad] the retrieved spectral or temporal phase at each sample, in the convention of FROG and SPIDER retrievals: the group delay is $d\varphi/d\omega$, so a positive quadratic spectral phase is a positive group delay dispersion, and the instantaneous angular frequency is $\omega_0 - d\varphi/dt$. Without a phase the pulse is taken as transform limited, with a warning. Default: None
  * **fit** (*str*) – the model fitted to the samples: ‘smooth’ (a smoothing spline of the intensity, with a polynomial phase of order 2 to 4 chosen by the fit, or a smoothing spline of the phase weighted by the intensity when no polynomial reaches `min_fit_quality`, as for a pulse shaper’s output or a pulse broadened by self-phase modulation), ‘sech’ or ‘gaussian’ (the analytic shapes of [`PulsedSource`](#PulsedSource), fitted for width, center and chirp), default: ‘smooth’
  * **min_fit_quality** (*float*) – the lowest fit quality accepted, in [0, 1], default: 0.99
  * **background** (*float* *or*  *'auto'* *or* *None*) – a constant background subtracted from `intensity` before the fit, on the same scale as `intensity` (per unit wavelength for a spectrum), at least 0 and below the samples’ peak. ‘auto’ takes it as the mean of the outer 5 % of the samples at each end, and refuses the measurement when those ends slope or sit at different levels (by more than 1 % of the peak, or four times their noise), since they are then the pulse’s wings rather than a background. None subtracts nothing. Default: None

### *class* PulsedProcess

The nonlinear optics of one section for a pulsed simulation, for a section’s `nonlinear=`.

* **Parameters:**
  * **chi2** (*bool*) – include $\chi^{(2)}$, default: True
  * **chi3** (*bool*) – include the Kerr effect, default: True
  * **raman** (*bool*) – include the delayed Raman part of the Kerr response, default: True
  * **raman_mode** (*str*) – ‘dominant’ (the response of the strongest field pattern, fast) or ‘full’, default: ‘dominant’
  * **tpa** (*bool* *or* *None*) – two-photon absorption on (True), off (False), or on wherever a material has a coefficient (None), default: None
  * **multi_photon** (*bool* *or* *None*) – three- and four-photon absorption, on (True) wherever a material has a three- or four-photon coefficient, or off (False or None). True raises an error for a cross-section in which no material has one. Default: None (off)
  * **free_carriers** (*bool* *or* *None*) – the electron-hole pairs that two-, three- and four-photon absorption make, with their absorption and index change: on (True), off (False), or on wherever an absorbing material has a free-carrier model (None). True raises an error for a cross-section in which no absorbing material has one. Default: None
  * **carrier_lifetime** (*float* *or* *None*) – [ns] the free-carrier lifetime, which depends on the waveguide, in place of the material’s own (1 ns for `Si`). It must be positive. Default: None (the material’s lifetime)
  * **modes** (*list* *[**int* *] or* *None*) – the tracked mode indices at the center wavelength, default: None (the source’s mode or modes)
  * **poling** ([*Poling*](#Poling) *or* *None*) – the section’s domain pattern, default: None (unpoled)
  * **dispersion_samples** (*int* *or* *None*) – wavelengths at which each cross-section is solved, Chebyshev nodes over the source’s `wavelength_range`, at least 4, default: None (as many as the band’s width needs)
  * **pattern_threshold** (*float*) – singular value, relative to the first, below which field patterns are dropped, in [0, 1), default: 1e-3
  * **max_rank** (*int*) – the largest number of field patterns. `PulseResponse.rank_report` says where it binds and how much of each mode’s field that drops. Default: 12
  * **max_harmonic_modes** (*int*) – on a $\chi^{(2)}$ section, the most second-harmonic modes added to the tracked modes, ranked by their $\chi^{(2)}$ overlap with the first tracked mode times the length they build over. 0 adds none. Default: 3
  * **harmonic_coupling** (*float*) – the weakest second-harmonic mode added, relative to the strongest, in [0, 1], default: 0.1
  * **modal_search** (*str* *or* *tuple* *[**float* *,* *float* *]*) – where an unpoled $\chi^{(2)}$ section’s modal phase matches are searched for: ‘broadened’ (where the launched pulse’s spectrum, or the pump’s spectrum anywhere in the section as it broadens, is within 30 dB of its peak), ‘launch’ (only where the launched pulse’s spectrum is within 30 dB of its peak), or a band `(min, max)` of fundamental wavelengths [nm]. When a match lies outside the launched spectrum, ‘broadened’ first runs the pump alone (one mode, one field pattern, no second-harmonic modes) to find it. Default: ‘broadened’

Several $\chi^{(2)}$ sections over one cross-section share its modes, which take the largest `max_harmonic_modes` and the smallest `harmonic_coupling` among them.

### *class* Poling

The domain pattern of a poled section. Give exactly one of `period`, `z_knots` with `periods`, `domain_walls`, and `domain_lengths`.

* **Parameters:**
  * **period** (*float*) – [nm] a constant poling period
  * **z_knots** (*list* *[**float* *]*) – [nm] positions along the section at which `periods` are given, interpolated between them (a chirp), at least 2
  * **periods** (*list* *[**float* *]*) – [nm] the local period at each knot
  * **domain_walls** (*list* *[**float* *]*) – [nm] explicit domain wall positions along the section, strictly increasing
  * **domain_lengths** (*list* *[**float* *]*) – [nm] the length of every domain in order, each > 0, the signs alternating from the first. The walls are the running sum: a wall at or past the end of the section is dropped, and when the domains end before the section does, the sign flips once more and the last domain runs to the end. This is the form [`optimize`](optimization.md#optimize) varies, every domain on its own
  * **duty_cycle** (*float*) – the fraction of each period in the first domain, default: 0.5
  * **interpolation** (*str*) – ‘linear’ or ‘spline’ between the knots, default: ‘linear’
  * **inverted_first** (*bool*) – start with an inverted domain, default: False
  * **jitter_rms** (*float*) – [nm] rms of a Gaussian random displacement of every wall, for fabrication tolerance studies, default: 0
  * **seed** (*int*) – the seed of the jitter, default: None

`walls(section_length)` returns the domain walls [nm] of any form over a section of that length, and `to_domain_lengths(section_length)` the length of every domain, which sum to the section length, so a design can start from a fixed or chirped poling.

### *class* PulseResponse

Returned by `get('pulse_response')` after [`EME`](#EME) or [`pulse_propagate`](#pulse_propagate). `spectrum` is (records, modes, points), the root-energy spectrum of every tracked mode in $\sqrt{\mathrm{J/Hz}}$ on `frequency` [Hz], with `wavelength` [nm] the same axis, `z` [nm] the record positions and `energy` [pJ] per record and mode. `junction_loss` is, per junction, the fraction of the arriving energy that left the tracked modes there, and `rank_per_slice` and `samples_per_slice` report the field patterns and mode solves of each slice. `window_gap` is, per record, the longest stretch of the time window (as a fraction of it) that holds almost none of the energy of all the tracked modes together. The window is periodic, so below about 0.05 the pulse’s leading and trailing parts have met around it and wrapped onto each other, and `num_points` should grow (the solver warns). It does not depend on the comoving frame. `edge_energy` is, per record, the fraction of the energy in the outer 5 % of the time window around the comoving frame (the first tracked mode’s group delay), which says where the pulse sits relative to that frame, not whether it wraps. `domain_walls` [nm] the positions along the chain of the domain walls the run used (jitter included), and `steps` and `rejected_steps` count the propagation steps. For a measured source, `measured_fit` is the fit that was launched (see [`PulseMeasurement`](#PulseMeasurement)), and None otherwise. `rank_report` gives, per slice, the field patterns used (`rank`), the number `pattern_threshold` asks for (`natural_rank`), the `max_rank` that caps it and the share of each tracked mode’s field weight the patterns leave out (`dropped_weight`). `absorbed_energy` is the energy [pJ] each nonlinear absorption process took out of the tracked modes over the whole chain, a dictionary keyed `'two_photon'`, `'three_photon'`, `'four_photon'` and `'free_carrier'` for the processes that ran.

`band_energy(wavelength_min, wavelength_max, z=None, mode=None)` returns the energy [pJ] in a band at the record nearest `z` (default: the output), summed over the tracked modes unless `mode` is given. `band_power(...)` returns the average power [W] of a pulse train, with `repetition_rate` taken from the source unless given. `psd(z, mode)` returns the spectral density in dB relative to 1 pJ/nm, `envelope(z, mode)` the time [fs] and complex envelope [$\sqrt{\mathrm{W}}$], `smallest_window_gap()` the smallest `window_gap` at any record (None for a response saved before it was reported), `energy_outside_window()` the largest `edge_energy`, and `total_absorbed_energy()` the sum of `absorbed_energy` [pJ], the energy every nonlinear absorption process took together.

### pulse_propagate

Propagate the pulse again through the chain the last [`EME`](#EME) prepared, without any mode solve (EMode<sub>3D</sub> only). Use it to try another poling, for example a chirp or fabrication jitter on the domain walls, at the cost of the propagation alone.

* **Parameters:**
  * **poling** ([*Poling*](#Poling) *or* *dict* *or* *None*) – a new domain pattern for every section with a `PulsedProcess` (a single `Poling`), or for the named sections (a dictionary of section name to `Poling`), default: None (keep the declared poling)
  * **records** (*int*) – number of evenly spaced positions along the chain at which the spectrum is recorded, default: 100
  * **local_error** (*float*) – relative error allowed per propagation step, default: 1e-6
  * **simulation_name** (*str*) – simulation name, default: ‘emode’

The prepared chain lives for the session. After `open()` of a saved file, run [`EME`](#EME) again first. The result is read back with `get('pulse_response')`.
