Skip to content

DMLaserTimeStepper

DMLaserTimeStepper(
*,
quantum_efficiency: Annotated[float, minimum=0],
spontaneous_emission_factor: Annotated[float, minimum=0],
carrier_lifetime: Annotated[float, exclusiveMinimum=0, units='s'],
gain_compression_factor: Annotated[float, minimum=0, units='m³'],
transparency_carrier_density: Annotated[float, minimum=0, units='m⁻³'],
differential_gain: Annotated[float, minimum=0, units='m²'],
n_group: Annotated[float, minimum=0],
linewidth_enhancement_factor: Annotated[float, minimum=0],
confinement_factor: Annotated[float, exclusiveMinimum=0],
photon_lifetime: Annotated[float, exclusiveMinimum=0, units='s'],
active_region_volume: Annotated[float, exclusiveMinimum=0, units='m³'],
rel_intensity_noise: Annotated[float, minimum=0, units='1/Hz'] = 0,
linewidth: Annotated[float, minimum=0, units='Hz'] = 0,
thermal_impedance: Annotated[float, minimum=0, units='K/W'] = 0,
thermal_time_constant: Annotated[
float, exclusiveMinimum=0, units='s'
] = 1e-06,
characteristic_temperature: Annotated[float, minimum=0, units='K'] = 0,
gain_temperature_coefficient: Annotated[
float, minimum=0, units='1/K'
] = 0,
dwavelength_dT: Annotated[float, units='m/K'] = 0,
injection_temperature: Annotated[float, minimum=0, units='K'] = 0,
turn_on_voltage: Annotated[float, minimum=0, units='V'] = 0,
series_resistance: Annotated[float, minimum=0, units='Ω'] = 0,
reflection: complex = 0,
z0: Annotated[complex, units='Ω']
| photonforge.Interpolator
| None = None,
mesh_refinement: Annotated[float, exclusiveMinimum=0] | None = None,
verbose: bool = True,
seed: Annotated[int, minimum=0] | None = None,
substeps: Annotated[int, exclusiveMinimum=0] | None = None,
) -> None

Time-stepper for a directly modulated laser source.

This model solves the single-mode semiconductor laser rate equations for photon density (pp), carrier density (nn), and optical phase (ϕ\phi). The model includes gain compression, intensity and phase noises, and optional self-heating.

The rate equations are:

dpdt=ΓG(p)(n−n0)p−pτp+βΓτnndndt=I(t)qVa−G(p)(n−n0)p−nτndϕdt=α2(Γvga0(n−n0)−1τp)G(p)=c0nga01+ϵp\begin{aligned} \frac{{\rm d} p}{{\rm d} t} &= \Gamma G(p) (n-n_0) p - \frac{p}{\tau_p} + \frac{\beta\Gamma}{\tau_n}n \\ \frac{{\rm d} n}{{\rm d} t} &= \frac{I(t)}{q V_a} - G(p) (n-n_0) p - \frac{n}{\tau_n} \\ \frac{{\rm d} \phi}{{\rm d} t} &= \frac{\alpha}{2} \left(\Gamma v_g a_0 (n-n_0) - \frac{1}{\tau_p}\right) \\ G(p) &= \frac{c_0}{n_g} \frac{a_0}{1 + \epsilon p} \end{aligned}

The junction temperature follows a first-order thermal circuit:

τthdΔTdt+ΔT=Rth(IV(I)−Pout)V(I)=V0+IRs\begin{gathered} \tau_{\rm th}\frac{{\rm d}\Delta T}{{\rm d}t} + \Delta T = R_{\rm th}\left(I V(I) - P_{\rm out}\right) \\ V(I) = V_0 + I R_s \end{gathered}

and acts on the rate equations through:

n0(ΔT)=n0 eΔT/T0a0(ΔT)=a0(1−cgΔT)ηi(ΔT)=e−ΔT/T1λ(ΔT)=λ+dλdTΔT\begin{aligned} n_0(\Delta T) &= n_0\,e^{\Delta T/T_0} \\ a_0(\Delta T) &= a_0\left(1 - c_g \Delta T\right) \\ \eta_i(\Delta T) &= e^{-\Delta T/T_1} \\ \lambda(\Delta T) &= \lambda + \frac{{\rm d}\lambda}{{\rm d}T}\Delta T \end{aligned}

The output envelope is A=Pout e−jϕA = \sqrt{P_{\rm out}}\,e^{-j\phi}, so ϕ\phi is the optical phase and the instantaneous frequency deviation is δν=(1/2π) dϕ/dt\delta\nu = (1/2\pi)\,{\rm d}\phi/{\rm d}t. With α>0\alpha > 0 the light shifts to higher frequency while the power rises (transient chirp) and sits higher for a higher power (adiabatic chirp), and self-heating with a positive dwavelength_dT shifts it to lower frequency.

Args

  • quantum_efficiency: Differential quantum efficiency.
  • spontaneous_emission_factor: Fraction of spontaneous emission coupled into the lasing mode.
  • carrier_lifetime: Carrier lifetime.
  • gain_compression_factor: Nonlinear gain suppression coefficient.
  • transparency_carrier_density: Carrier density at transparency.
  • differential_gain: Gain cross-section.
  • n_group: Group index of the cavity mode.
  • linewidth_enhancement_factor: Henry factor coupling gain to phase.
  • confinement_factor: Optical confinement factor.
  • photon_lifetime: Cavity photon lifetime.
  • active_region_volume: Volume of the active region.
  • rel_intensity_noise: One-sided relative intensity noise.
  • linewidth: Lorentzian linewidth used for phase noise.
  • thermal_impedance: Junction-to-ambient thermal resistance RthR_{\rm th}. Zero disables self-heating entirely.
  • thermal_time_constant: Thermal time constant τth\tau_{\rm th}.
  • characteristic_temperature: T0T_0 governing the threshold rise. Zero holds the threshold fixed.
  • gain_temperature_coefficient: Fractional loss of differential gain per kelvin. Zero holds the gain fixed.
  • dwavelength_dT: Emission wavelength temperature drift.
  • injection_temperature: characteristic temperature of carrier leakage T1T_1. Zero disables leakage.
  • turn_on_voltage: Diode voltage drop V0V_0, used together with series_resistance to compute the dissipated power.
  • series_resistance: Series resistance RsR_s.
  • reflection: Reflection coefficient for fields on the optical port.
  • z0: Reference impedance of the electrical port.
  • mesh_refinement: Mesh refinement factor used when the port impedance must be computed by a mode solve.
  • verbose: Print progress of any background impedance computation.
  • seed: Random number generator seed.
  • substeps: Number of Runge-Kutta substeps per time step used to integrate the rate equations. If None, the count keeps each substep within half the photon lifetime.

Note

PhotonForge uses the e−jωte^{-j\omega t} time convention, so an envelope e−j2πΔf te^{-j2\pi\Delta f\,t} is light at fc+Δff_c + \Delta f, with fcf_c the carrier frequency. The instantaneous frequency deviation of an output envelope a is therefore −(1/2π) darg⁡a/dt-(1/2\pi)\,{\rm d}\arg a/{\rm d}t, for example -numpy.gradient(numpy.unwrap(numpy.angle(a)), time_step) / (2 * numpy.pi), and the frequency axis of a numpy.fft spectrum must be reversed.

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.
threshold_currentCold threshold current of a rate-equation laser, in A.
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'],
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 determine the photon energy in the model.
  • 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.

threshold_current(
*,
transparency_carrier_density: Annotated[
float, minimum=0, units='m⁻³'
] = 1.5e24,
differential_gain: Annotated[
float, exclusiveMinimum=0, units='m²'
] = 5e-20,
confinement_factor: Annotated[float, exclusiveMinimum=0] = 0.04,
photon_lifetime: Annotated[
float, exclusiveMinimum=0, units='s'
] = 2.5e-12,
active_region_volume: Annotated[
float, minimum=0, units='m³'
] = 4.71e-19,
carrier_lifetime: Annotated[
float, exclusiveMinimum=0, units='s'
] = 1e-09,
n_group: Annotated[float, exclusiveMinimum=0] = 3.6,
) -> float

Cold threshold current of a rate-equation laser, in A.

Gain equals loss at threshold, which fixes the carrier density and hence the current needed to sustain it:

nth=n0+1Γvga0τpIth=qVanthτn\begin{gathered} n_{\rm th} = n_0 + \frac{1}{\Gamma v_g a_0 \tau_p} \\ I_{\rm th} = \frac{q V_a n_{\rm th}}{\tau_n} \end{gathered}

Useful for choosing active_region_volume to hit a target threshold, since the two are proportional. Arguments are in the SI units this class takes; the abstract builders quote values 1e18 away (μm⁻³, μm³, and nm² for the gain cross-section).

Args

  • transparency_carrier_density: n0n_0.
  • differential_gain: a0a_0.
  • confinement_factor: Γ\Gamma.
  • photon_lifetime: τp\tau_p.
  • active_region_volume: VaV_a.
  • carrier_lifetime: τn\tau_n.
  • n_group: Group index vgv_g.

Returns

Threshold current in A.

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
junction_temperaturePresent junction temperature rise above ambient, in K.
status

Present junction temperature rise above ambient, in K.

Exposed because the thermal wavelength shift usually cannot be read off the output field. A rise of a few tens of kelvin moves the emission by a few nanometres, which is hundreds of gigahertz, and the coarse time step that makes thermal simulation affordable puts the Nyquist limit far below that. Reading the temperature and applying dwavelength_dT gives the shift at any time step:

Δλ=dλdT ΔT\Delta\lambda = \frac{{\rm d}\lambda}{{\rm d}T} \,\Delta T

Returns

Temperature rise in kelvin, zero when self-heating is disabled.

No description available.

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.