Skip to content

RingTimeStepper

RingTimeStepper(
*,
kappa1: complex,
kappa2: complex | None = None,
n_eff: complex,
length: Annotated[float, units='μm'],
propagation_loss: Annotated[float, minimum=0, units='dB/μm'] = 0.0,
n_group: float | None = None,
dispersion: Annotated[float, units='s/μm²'] = 0.0,
dispersion_slope: Annotated[float, units='s/μm³'] = 0.0,
reference_frequency: Annotated[float, minimum=0, units='Hz']
| None = None,
dn_dT: Annotated[complex, units='1/K'] = 0.0,
dL_dT: Annotated[float, units='dB/μm/K'] = 0.0,
temperature: Annotated[float, minimum=0, units='K'] = 293.0,
reference_temperature: Annotated[float, minimum=0, units='K'] = 293.0,
dn_dv: Annotated[complex, units='1/V'] = 0.0,
dn_dv2: Annotated[complex, units='1/V²'] = 0.0,
dloss_dv: Annotated[float, units='dB/μm/V'] = 0.0,
dloss_dv2: Annotated[float, units='dB/μm/V²'] = 0.0,
effective_area: Annotated[float, exclusiveMinimum=0, units='m²']
| None = None,
kerr_index: Annotated[float, units='m²/W'] = 0.0,
tpa_coefficient: Annotated[float, minimum=0, units='m/W'] = 0.0,
fca_cross_section: Annotated[float, minimum=0, units='m²'] = 0.0,
fcd_coefficient: Annotated[float, units='m³'] = 0.0,
confinement: Annotated[float, maximum=1, minimum=0] = 1.0,
carrier_lifetime: Annotated[float, exclusiveMinimum=0]
| photonforge.Interpolator
| None = None,
thermal_time_constant: Annotated[float, exclusiveMinimum=0]
| None = None,
thermal_capacity: Annotated[float, exclusiveMinimum=0, units='J/K']
| None = None,
absorbed_fraction: Annotated[float, maximum=1, minimum=0] = 1.0,
heater_resistance: Annotated[float, exclusiveMinimum=0, units='Ω']
| None = None,
carrier_samples: Annotated[int, minimum=2] = 401,
z0: Annotated[complex, units='Ω']
| photonforge.Interpolator
| None = None,
f_3dB: Annotated[float, minimum=0, units='Hz'] = 0,
ports: Annotated[collections.abc.Sequence[str], maxItems=2, minItems=2]
| Annotated[collections.abc.Sequence[str], maxItems=4, minItems=4]
| None = None,
mesh_refinement: Annotated[float, exclusiveMinimum=0] | None = None,
verbose: bool = True,
) -> None

Analytic ring resonator time-stepper with single or double bus.

This model implements a 2 or 4-port micro-ring or racetrack resonator. It simulates the time-domain behavior using one bus coupler per waveguide and a delay-line for the ring’s round-trip propagation. In the double-bus case the two couplers sit half a round trip apart, splitting the delay line into two arcs. The model includes both electro-optic and thermo-optic tuning of the ring’s effective index and loss. An optional first-order low-pass filter can be applied to the electrical tuning input.

Each bus coupler is the symmetric, lossless matrix

C=[τκκτ]C = \begin{bmatrix} \tau & \kappa \\ \kappa & \tau \\ \end{bmatrix}

with the (possibly complex) cross-coupling κ\kappa supplied directly and the through coefficient derived as τ=−iexp⁡(iarg⁡κ)1−∣κ∣2\tau = -i \exp(i\arg\kappa) \sqrt{1 - |\kappa|^2}. This is the same convention used by RingModel, so both models share resonance positions and line shapes.

Args

  • kappa1: Complex cross-coupling coefficient for the first bus coupler. Its magnitude must not exceed 1.
  • kappa2: Complex cross-coupling coefficient for the second bus coupler. Its magnitude must not exceed 1. If None, models a single-bus ring.
  • n_eff: Effective refractive index (loss can be included here by using complex values).
  • length: Round-trip length of the ring.
  • propagation_loss: Propagation loss.
  • n_group: Group index of the optical mode, used to calculate delay. If None, the real part of ‘n_eff’ is used.
  • dispersion: Group velocity dispersion D at reference_frequency.
  • dispersion_slope: Dispersion slope S at reference_frequency.
  • reference_frequency: Frequency at which n_eff, n_group, dispersion and dispersion_slope apply, as in RingModel. The phase and group delay at the carrier follow from them along the same Taylor series. If None, the carrier frequency is used.
  • dn_dT: Temperature sensitivity for n_eff.
  • dL_dT: Temperature sensitivity for propagation_loss.
  • temperature: Operating temperature.
  • reference_temperature: Reference temperature.
  • dn_dv: Linear voltage-dependent effective index coefficient.
  • dn_dv2: Quadratic voltage-dependent effective index coefficient.
  • dloss_dv: Linear voltage-dependent propagation loss coefficient.
  • dloss_dv2: Quadratic voltage-dependent propagation loss coefficient.
  • effective_area: Nonlinear effective area AeffA_{\rm eff}, in m².
  • kerr_index: Kerr coefficient n2n_2, in m²/W. Positive values red-shift the resonance.
  • tpa_coefficient: Two-photon absorption βTPA\beta_{\rm TPA}, in m/W.
  • fca_cross_section: Free-carrier absorption cross-section, in m².
  • fcd_coefficient: Free-carrier index change per unit density σFCD\sigma_{\rm FCD}, in m³.
  • confinement: Fraction of the optical mode overlapping the two-photon material. It scales the TPA loss and the carriers it generates.
  • carrier_lifetime: Free-carrier lifetime, either a constant or an Interpolator over carrier density in 1/m³.
  • thermal_time_constant: Thermal relaxation τth\tau_{\rm th}.
  • thermal_capacity: Cth=mcpC_{\rm th} = m c_p. The steady-state thermal impedance is τth/Cth\tau_{\rm th}/C_{\rm th}.
  • absorbed_fraction: Fraction of the linear loss that is absorption rather than scattering or radiation.
  • heater_resistance: If set, the electrical port also acts as a resistive heater of this resistance, contributing V2/RV^2/R to the thermal source.
  • carrier_samples: Points used to resample an interpolator lifetime onto a dense grid at setup.
  • z0: Characteristic impedance of the electrical port used to convert the input field amplitude to voltage. If None, derived from port impedance, calculated by mode-solving, or set to 50 Ω.
  • f_3dB: -3 dB frequency cutoff for bandwidth limiting. Only active for positive values.
  • ports: List of port names. If not set, the sorted list of port names from the component is used.
  • mesh_refinement: Minimal number of mesh elements per wavelength used for mode solving.
  • verbose: Flag setting the verbosity of mode solver runs.

Note

The group delay ngℓ/c0n_g \ell / c_0 is implemented as a fixed multiple of the time step. For the double-bus ring the two couplers are placed half a round trip apart, so an even round-trip delay is recommended for a symmetric drop response.

Dispersion enters through the phase and the group delay at the carrier, so the resonance there, and its neighbourhood, match RingModel. Resonances further away miss the curvature β2Ω2ℓ/2\beta_2 \Omega^2 \ell / 2 of the round-trip phase at an offset Ω\Omega from the carrier; for several resonances use a ring built from an AnalyticWaveguideModel and a coupler.

Ports follow the same order as RingModel: the (sorted) port names or ports argument map to input, through, drop, and add. For a unit drive at the input, the steady-state responses are:

============ ========== =================================== Port (index) Role Coupled to the input via ============ ========== =================================== ports[0] input — ports[1] through same bus, first coupler (t1t_1) ports[2] drop second bus, half round trip (dd) ports[3] add second bus, isolated from input (0) ============ ========== ===================================

giving the per-mode S matrix (single bus keeps only the upper-left 2x2 block, with no drop/add ports):

S=[0t1d0t100dd00t20dt20]S = \begin{bmatrix} 0 & t_1 & d & 0 \\ t_1 & 0 & 0 & d \\ d & 0 & 0 & t_2 \\ 0 & d & t_2 & 0 \\ \end{bmatrix}

See Also

srh_lifetime for the density-dependent form of carrier_lifetime, whose shortening at high injection sets the self-pulsing period.

MethodDescription
bind_nativeBind a compiled step function.
resetReset internal state.
setupInitialize the time stepper.
setup_stateInitialize internal state.
stepCompute the outputs of this time stepper, given inputs.
step_singleTake a single time step on the given inputs.
updateUpdate this time stepper.
write_verilog_aWrite this configured time stepper as one Verilog-A module.

bind_native(self, step, keys, state=None, cstate=None, context=None)

Bind a compiled step function.

Instead of implementing step_single, a time stepper can hand the simulator the address of a C function with the signature:

int64 step(inputs, outputs, time_index, update_state, shutdown,
context, state, cstate)

inputs and outputs are complex128 arrays with one entry per key, state and cstate are the float64 and complex128 arrays, and a nonzero return signals failure. The function must overwrite every element of outputs and only modify the state arrays when update_state is true. The arrays must not be reallocated after binding; reset restores them to the values given here, so a subclass overriding reset must call super().reset().

Calls with update_state false must give the same outputs: circuits may call the function several times in a time step, and setup checks this for steppers that declare path_delays. Random numbers must therefore come from a generator whose state lives in the arrays (e.g., a seed and a counter): output a draw kept there and draw the next one only when update_state is true, which also makes reset replay the stream. Do not use numpy.random in the function: numba draws it from a global stream that differs on every call and survives a reset.

The function can be compiled with numba (when available).

Args

  • step (int | object): Address of the step function, or an object with an address attribute such as a numba cfunc.
  • keys (Sequence[str]): Input/output keys.
  • state (ndarray | None): Contiguous float64 array the function may use.
  • cstate (ndarray | None): Contiguous complex128 array the function may use.
  • context (int | None): Opaque pointer passed to the function.

Example

A two-port element that leaks the field entering its first port out of its second one:

>>> from numba import cfunc
>>> from photonforge.time_steppers import STEP_SIGNATURE
>>>
>>> @cfunc(STEP_SIGNATURE, cache=True) # doctest: +SKIP
... def leaky_step(inputs, outputs, time_index, update_state,
... shutdown, context, state, cstate):
... # state[0]: decay per step
... # cstate[0]: field leaving the second port
... outputs[0] = 0j
... outputs[1] = cstate[0] * state[0] + inputs[0]
... if update_state:
... cstate[0] = outputs[1]
... return 0
...
>>> class LeakyTimeStepper(pf.TimeStepper):
... def __init__(self, *, k=0.5):
... super().__init__(k=k)
...
... def setup_state(self, *, component, time_step, **kwargs):
... keys = tuple(f"{p}@0" for p in sorted(component.ports))
... state = numpy.array([
... self.parametric_kwargs["k"] ** time_step
... ])
... self.bind_native(
... leaky_step, keys, state, numpy.zeros(1, complex)
... )
...
>>> pf.register_time_stepper_class(LeakyTimeStepper)

reset(self) -> None

Reset internal state.

setup(
self,
component,
time_step,
*,
carrier_frequency=0,
show_progress=True,
**kwargs,
)

Initialize the time stepper.

Args

  • component (Component): Component for the time stepper.
  • time_step (float): The interval between time steps (in seconds).
  • carrier_frequency (float): The carrier frequency used to construct the time stepper. The carrier should be omitted from the input signals, as it is handled automatically by the time stepper.
  • show_progress (bool): If True, show setup progress.
  • **kwargs (object): Keyword args forwarded to setup_state.

Returns

TimeStepper: This time stepper.

setup_state(
self,
*,
component: photonforge.Component,
time_step: Annotated[float, minimum=0, units='s'],
carrier_frequency: Annotated[float, minimum=0, units='Hz'],
temperature: Annotated[float, minimum=0, units='K'] | None = None,
verbose: bool | None = None,
**kwargs: object,
) -> object

Initialize internal state.

Args

  • component: Component representing the laser source.
  • time_step: The interval between time steps (in seconds).
  • carrier_frequency: The carrier frequency used to construct the time stepper. The carrier should be omitted from the input signals, as it is handled automatically by the time stepper.
  • temperature: If set, overrides the time stepper’s temperature.
  • verbose: If set, overrides the time stepper’s verbose attribute.
  • kwargs: Unused.

step(self, inputs=None, steps=0, time_step=0, show_progress=True)

Compute the outputs of this time stepper, given inputs.

Args

  • inputs (TimeSeries): Inputs to the time stepper.
  • steps (int): The number of time steps to execute. If inputs is defined, this value is not used.
  • time_step (float): The interval between time steps (in seconds). If inputs is a defined, this value is not used.
  • show_progress (bool): If True, show computation progress.

Returns

TimeSeries: Computed outputs.

step_single(self, inputs, outputs, time_index, update_state, shutdown)

Take a single time step on the given inputs.

Important

Derived classes must implement this method unless they bind a compiled step with bind_native, in which case the default implementation calls it and, when shutdown is set, the optional _after_native_run hook.

The method takes the inputs as a numpy array of type complex with values in the order defined by keys. It advances a single time step, updating its internal state (if any) according to the update_state flag. It writes the outputs in-place to the pre-allocated, zero-initialized outputs array in the same order.

Args

  • inputs (ndarray): Input values at the current time step. Must be a 1D array of complex values ordered according to keys.
  • outputs (ndarray): Pre-allocated output array where results will be stored. Same size and type as inputs.
  • time_index (int): Time series index for the current input.
  • update_state (bool): Whether to update the internal stepper state.
  • shutdown (bool): Whether this is the last call to the single stepping function for the original TimeSeries.

update(self, *args, **kwargs)

Update this time stepper.

All contents of the time stepper are updated with the contents from the updated version. Object contents or modifications introduced after the time stepper creation (outside of the original parametric function) are not propagated.

Args

  • *args (object): Positional arguments to the parametric time stepper function.
  • **kwargs (object): Keyword arguments to the parametric time stepper function.

Returns

TimeStepper: This time stepper.

Note

The original keyword arguments used to generate the time stepper are stored and updated with the passed kwargs. This updated version is used in the function call and will be stored for future updates. Positional arguments are not stored.

write_verilog_a(
self,
path,
*,
backend='openvaf',
port_convention='travelling_wave',
module_name=None,
z0=50.0,
field_impedance=None,
)

Write this configured time stepper as one Verilog-A module.

The time stepper must first be configured with setup or Component.setup_time_stepper. The writer creates source only; it does not compile the model or create a simulator deck. Requires a PhotonForge Photonic Canvas license.

Args

  • path (str | os.PathLike): Destination .va file.
  • backend (Literal[“openvaf”, “spectre”]): Compiler capability target recorded in the source.
  • port_convention (Literal[“travelling_wave”, “gf_45spclo_npe”]): External optical port convention. GF packed ports require the Spectre backend.
  • module_name (str | None): Module identifier derived from path when omitted.
  • z0 (float): Travelling-wave optical reference impedance.
  • field_impedance (float | None): GF electric-field scaling.

Returns

pathlib.Path: Absolute destination path.

PropertyDescription
carriersFree-carrier density in the ring, in m⁻³.
circulating_powerRing-averaged circulating power over both directions, in W.
status
temperature_riseSelf-heating temperature rise above the operating temperature, in K.

Free-carrier density in the ring, in m⁻³.

Ring-averaged circulating power over both directions, in W.

No description available.

Self-heating temperature rise above the operating temperature, in K.

AttributeDescription
parametric_functionstr: Function used to update the time stepper.
parametric_kwargsdict[str, object]: Keyword arguments used to update the time stepper.
path_delaysdict[tuple[str, str], int] | None: Minimum delay in time steps of each (input key, output key) signal path; missing pairs have no path and None means every input reaches every output within the time step.
propertiesProperties: Object properties.
random_variableslist[RandomVariable]: Random variables associated to the time stepper’s parameters.

str: Function used to update the time stepper.

dict[str, object]: Keyword arguments used to update the time stepper.

dict[tuple[str, str], int] | None: Minimum delay in time steps of each (input key, output key) signal path; missing pairs have no path and None means every input reaches every output within the time step.

Properties: Object properties.

list[RandomVariable]: Random variables associated to the time stepper’s parameters.