AnalyticWaveguideTimeStepper
AnalyticWaveguideTimeStepper( *, n_eff: complex, length: Annotated[float, units='μm'] | None = None, propagation_loss: Annotated[float, minimum=0, units='dB/μm'] = 0.0, extra_loss: Annotated[float, minimum=0, units='dB'] = 0.0, n_group: float = 0, 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, fir_taps: Annotated[int, exclusiveMinimum=0] | None = None, dn_kerr: photonforge.Interpolator = Interpolator( x=[0, 0.5, 1], y=[0, 0, 0], method="linear", coords="real_imag", extrapolation="extrapolate", parameter_names=(), ), tau_fc: Annotated[float, minimum=0, units='s'] = 0, dn_fc: photonforge.Interpolator = Interpolator( x=[0, 0.5, 1], y=[0, 0, 0], method="linear", coords="real_imag", extrapolation="extrapolate", parameter_names=(), ), dn_fc_map: photonforge.Interpolator | None = None, tau_th: Annotated[float, minimum=0, units='s'] = 0, dn_th: photonforge.Interpolator = Interpolator( x=[0, 0.5, 1], y=[0, 0, 0], method="linear", coords="real_imag", extrapolation="extrapolate", parameter_names=(), ), sections: Annotated[int, exclusiveMinimum=0] | None = None, peak_power: Annotated[float, minimum=0, units='W'] | None = None, max_phase: Annotated[float, exclusiveMinimum=0, units='rad'] = 0.15, power_samples: Annotated[int, exclusiveMinimum=0] = 1024,) -> NoneAnalytic waveguide time-stepper with power-dependent effects.
Two-port optical waveguide whose effective index is perturbed by power-dependent effects, plus chromatic dispersion:
The perturbation interpolators may be complex: the imaginary part is
an index change and therefore a power-dependent loss, used to express
two-photon and free-carrier absorptions. The relaxation form above is
the carrier rate equation once
is supplied. has no relaxation at all: it
follows the power within the step, which is the Kerr effect, and is
supplied as dn_kerr.
Relaxing the index change is exact only when it is proportional to the
carrier density. Otherwise, as for the hole term of silicon plasma
dispersion, , supply dn_fc_map: dn_fc
then gives the steady-state carrier density , the
density itself relaxes, and the map converts it to the index change:
Dispersion is applied as an all-pass filter on the envelope,
with the linear-in- term already carried by the group
delay. dispersion and dispersion_slope use the same names, units
and conversion as AnalyticWaveguideModel.
Every mode of the ports propagates with the same parameters, and is the total power over the modes in each direction, so two modes are the two polarizations of one waveguide mode.
Args
- n_eff: Effective refractive index (loss can be included here by using complex values).
- length: Length of the waveguide. If not provided, the length is measured by
route_lengthor ports distance. - propagation_loss: Propagation loss.
- extra_loss: Length-independent additional loss.
- n_group: Group index of the optical mode, used to calculate delay.
- dispersion: Group velocity dispersion D at
reference_frequency. - dispersion_slope: Dispersion slope S at
reference_frequency. - reference_frequency: Frequency at which
n_eff,dispersionanddispersion_slopeapply, as inAnalyticWaveguideModel. The phase, group delay and dispersion at the carrier follow from them along the same Taylor series as in that model, so a carrier away from this frequency puts every resonance where the model does. IfNone, the carrier frequency is used. - fir_taps: Length of the dispersion filter, ignored when dispersion is zero. If
None, it is set at setup, based on the time step. - dn_kerr: Instantaneous power-dependent index variation, for the Kerr effect. May be complex, the imaginary part being loss.
- tau_fc: Time constant for the free-carrier effects. If zero, free-carrier effects are disabled.
- dn_fc: Power-dependent, steady-state index variation due to free-carrier effects. May be complex. If
dn_fc_mapis given, the real, steady-state carrier density instead. - dn_fc_map: Index variation as a function of the relaxed carrier density. May be complex. If
None,dn_fcis an index variation and relaxes directly. - tau_th: Time constant for the thermal effects. If zero, thermal effects are disabled.
- dn_th: Power-dependent, steady-state index variation due to thermal effects. May be complex.
- sections: Number of sections the waveguide is split into. If
None, it is chosen frompeak_powerandmax_phase. - peak_power: Highest optical power expected at the input, used only to choose the number of sections. If
NonewithsectionsalsoNone, a single section is used. - max_phase: Largest Kerr phase per section when the number of sections is chosen from
peak_power. 0.15 keeps the error below that of the earlier first-order scheme at 0.05. - power_samples: Number of points used to tabulate the interpolators that are neither linear nor a single polynomial.
Note
Each section is a lumped element: its perturbations are evaluated at the power estimated halfway along it, from the decay its linear loss and absorption give at its entrance, and assumed uniform along it. Taking them halfway makes the error second order in the section’s own loss, as the symmetric dispersion split does for dispersion. Splitting the waveguide into sections interleaves the nonlinear phase, the loss and the dispersion, which is a split-step scheme; its error falls with the nonlinear phase per section.
With a nonlinearity and dispersion, the dispersion is split symmetrically: half a step where the light enters, a full step between dispersion steps, and half where it leaves, so each nonlinear step sits midway through its dispersion. The phase of light the nonlinearity generates, such as a four-wave-mixing idler, then converges as the square of the number of steps instead of linearly. The split costs one more filter’s latency; where the group delay cannot pay it, one dispersion step fewer is used, and if even that does not fit, the dispersion follows each nonlinear step unsplit. A waveguide with a single dispersion step, such as a ring arc, is not split: it would pay two filters for one.
Sections are stepped in the frame moving with the envelope, so the group delay is applied once for the whole waveguide. Each dispersion filter adds latency, so when a filter per section would need more than the group delay, dispersion is applied in fewer, longer steps, as many as the group delay allows. Light travelling in the opposite direction enters the index of each section with the power it had there one time step earlier, except at the section where it enters the waveguide.
A filter’s latency is half its length. When fir_taps is None
and the group delay is too short even for one filter of the default
length, as in a ring, the longest filter that fits the delay is used
instead, provided it still matches the dispersion to 1e-4.
When the number of sections is chosen from peak_power, a warning
is issued at the end of a run if the Kerr phase in one section
exceeded max_phase by more than 20%, which means the power
exceeded the hint.
Methods
Section titled “Methods”| Method | Description |
|---|---|
bind_native | Bind a compiled step function. |
reset | Reset any internal state of the time stepper. |
setup | Initialize the time stepper. |
setup_state | Initialize internal state. |
step | Compute the outputs of this time stepper, given inputs. |
step_single | Take a single time step on the given inputs. |
update | Update this time stepper. |
write_verilog_a | Write this configured time stepper as one Verilog-A module. |
bind_native
Section titled “bind_native”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
addressattribute such as a numbacfunc. - 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)Reset any internal state of the time stepper.
The default implementation restores the arrays given to
bind_native, if any, and performs no other action. Derived
classes override it to reset the state they hold themselves.
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
Section titled “setup_state”setup_state( self, *, component: photonforge.Component, time_step: Annotated[float, minimum=0, units='s'], carrier_frequency: Annotated[float, minimum=0, units='Hz'], **kwargs: object,) -> NoneInitialize internal state.
Args
- component: Component representing the waveguide.
- 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.
- 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
inputsis defined, this value is not used. - time_step (float): The interval between time steps (in seconds). If
inputsis a defined, this value is not used. - show_progress (bool): If
True, show computation progress.
Returns
TimeSeries: Computed outputs.
step_single
Section titled “step_single”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
Section titled “update”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
Section titled “write_verilog_a”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
.vafile. - 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
pathwhen omitted. - z0 (float): Travelling-wave optical reference impedance.
- field_impedance (float | None): GF electric-field scaling.
Returns
pathlib.Path: Absolute destination path.
Attributes
Section titled “Attributes”| Attribute | Description |
|---|---|
parametric_function | str: Function used to update the time stepper. |
parametric_kwargs | dict[str, object]: Keyword arguments used to update the time stepper. |
path_delays | 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 | Properties: Object properties. |
random_variables | list[RandomVariable]: Random variables associated to the time stepper’s parameters. |
parametric_function
Section titled “parametric_function”str: Function used to update the time stepper.
parametric_kwargs
Section titled “parametric_kwargs”dict[str, object]: Keyword arguments used to update the time stepper.
path_delays
Section titled “path_delays”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
Section titled “properties”Properties: Object properties.
random_variables
Section titled “random_variables”list[RandomVariable]: Random variables associated to the time stepper’s parameters.