finesse.analysis.actions.lti module

Collection of Actions that deal linear time invariant (LTI) modelling tasks.

class finesse.analysis.actions.lti.FrequencyResponse(f, inputs, outputs, *, carrier_solution=None, name='frequency_response')[source]

Bases: Action

Computes the frequency response of a signal injected at various nodes to compute transfer functions to multiple output nodes. Inputs and outputs should be electrical or mechanical nodes. It does this in an efficient way by using the same model and solving for multiple RHS input vectors.

This action does not alter the model state. This action will ignore any currently definied signal generator elements in the model.

Produces an output transfer matrix from each input node to some readout output. The shape of the output matrix is:

[frequencies, inputs, outputs]

To inject into optical nodes please see FrequencyResponse2 and FrequencyResponse3. To read out optical nodes please see FrequencyResponse3 and FrequencyResponse4.

Parameters

farray, double

Frequencies to compute the transfer functions over

inputsiterable[str or Element]

Mechanical or electrical nodes to inject signal at

outputsiterable[str or Element]

Mechanical or electrical nodes to measure output at

carrier_solutionDCFieldsSolutions, optional

When provided these DC fields will be used to compute the signal matrix with. It is up to the user to input a sensible carrier field.

namestr, optional

Solution name

Examples

Here we measure a set of transfer functions from DARM and CARM to four readouts for a particular model,

>>> sol = model.run(FrequencyResponse(np.geomspace(0.1, 50000, 100),
...         ('DARM', 'CARM'),
...         ('AS.DC', 'AS45.I', 'AS45.Q', 'REFL9.I'),
... ))

Single inputs and outputs can also be specified

>>> model.run(FrequencyResponse(np.geomspace(0.1, 50000, 100), 'DARM', AS.DC'))

The transfer functions can then be accessed like a 2D array by name, the ordering of inputs to outputs does not matter.

>>> sol['DARM'] # DARM to all outputs
>>> sol['DARM', 'AS.DC'] # DARM to AS.DC
>>> sol['DARM', ('AS.DC', 'AS45.I')]
>>> sol['AS.DC'] # All inputs to AS.DC readout
class finesse.analysis.actions.lti.FrequencyResponse2(f, inputs, outputs, *, carrier_solution=None, name='frequency_response2')[source]

Bases: Action

Computes the frequency response of a signal injected at an optical port at a particular optical frequency. This differs from FrequencyResponse in the way the inputs and outputs are prescribed. For FrequencyResponse2 you specify optical input nodes and a signal output node.

This action does not alter the model state. This action will ignore any currently definied signal generator elements in the model.

Produces an output transfer matrix from each HOM at a particular frequency and optical node to some readout output. The shape of the output matrix is:

[frequencies, outputs, inputs, HOMs]

It should be noted that when exciting a lower signal sideband frequency it will actually return the operator for propagating the conjugate of the lower sideband. This is because FINESSE is internally solving for the conjugate of the lower sideband to linearise non-linear optical effects.

To inject into mechanical and electrical nodes please see FrequencyResponse and FrequencyResponse4. To readout optical nodes please see FrequencyResponse3 and FrequencyResponse4.

Parameters

farray, double

Frequencies to compute the transfer functions over

inputsiterable[tuple[str or Node, Frequency]]

Optical node and frequency tuple to inject at. A symbolic refence to the model’s fsig.f parameter should always be used when defining a frequency to look at.

outputsiterable[str or Element]

Mechanical or electrical (signal)nodes to measure output to

carrier_solutionDCFieldsSolutions, optional

When provided these DC fields will be used to compute the signal matrix with. It is up to the user to input a sensible carrier field.

namestr, optional

Solution name

Examples

It is advisable to use always use a reference to the symbolic reference to the signal frequency model.fsig.f.ref instead of a fixed number incase it changes. This action will look for an initial frequency bin of X Hz to track during the frequency response analysis. A symbolic reference will always ensure the right bin is used, in cases such as looking at RF signal sidebands, 10e6+model.fsig.f.ref and 10e6-model.fsig.f.ref will always look at the upper and lower signal sideband around the +10MHz sideband.

>>> import finesse
>>> from finesse.analysis.actions import FrequencyResponse2
>>> model = finesse.script.parse('''
... l l1
... bs bs1 R=1 T=0 xbeta=1e-6 ybeta=1e-9
... readout_dc A
... link(l1, bs1, A)
... fsig(1)
... modes(maxtem=1)
... gauss g1 l1.p1.o w=1m Rc=inf
... ''')
>>> sol = model.run(
...     FrequencyResponse2(
...         [1, 10, 100],
...         [
...             ('bs1.p2.o', +model.fsig.f.ref),
...             ('bs1.p2.o', -model.fsig.f.ref)
...         ],
...         ['A.DC']
...     )
... )
class finesse.analysis.actions.lti.FrequencyResponse3(f, inputs, outputs, *, carrier_solution=None, name='frequency_response3')[source]

Bases: Action

Computes the frequency response of a signal injected at an optical port at a particular optical frequency. This differs from FrequencyResponse in the way the inputs and outputs are prescribed. For FrequencyResponse3 you specify optical input nodes and optical output nodes.

This action does not alter the model state. This action will ignore any currently definied signal generator elements in the model.

Produces an output transfer matrix from each HOM at a particular frequency and optical node to some other optical node and frequency. The shape of the output matrix is:

[frequencies, outputs, inputs, HOMs, HOMs]

It should be noted that when exciting a lower signal sideband frequency it will actually return the operator for propagating the conjugate of the lower sideband. This is because FINESSE is internally solving for the conjugate of the lower sideband to linearise non-linear optical effects.

To inject into mechanical and electrical nodes please see FrequencyResponse and FrequencyResponse4. To readout mechanical and electrical nodes please see FrequencyResponse and FrequencyResponse2.

Parameters

farray, double

Frequencies to compute the transfer functions over

inputsiterable[tuple[str or Node, Frequency]]

Optical node and frequency tuple to inject at. A symbolic reference to the model’s fsig.f parameter should always be used when defining a frequency to look at.

outputsiterable[tuple[str or Node, Frequency]]

Optical node and frequency tuple to measure output at. A symbolic reference to the model’s fsig.f parameter should always be used when defining a frequency to look at.

carrier_solutionDCFieldsSolutions, optional

When provided these DC fields will be used to compute the signal matrix with. It is up to the user to input a sensible carrier field.

namestr, optional

Solution name

Examples

It is advisable to use always use a reference to the symbolic reference to the signal frequency model.fsig.f.ref instead of a fixed number incase it changes. This action will look for an initial frequency bin of X Hz to track during the frequency response analysis. A symbolic reference will always ensure the right bin is used, in cases such as looking at RF signal sidebands, 10e6+model.fsig.f.ref and 10e6-model.fsig.f.ref will always look at the upper and lower signal sideband around the +10MHz sideband.

>>> import finesse
>>> from finesse.analysis.actions import FrequencyResponse3
>>> model = finesse.script.parse('''
... l l1
... bs bs1 R=1 T=0 xbeta=1e-6 ybeta=1e-9
... readout_dc A
... link(l1, bs1, A)
... fsig(1)
... modes(maxtem=1)
... gauss g1 l1.p1.o w=1m Rc=inf
... ''')
>>> sol = model.run(
...     FrequencyResponse3(
...         [1, 10, 100],
...         [
...             ('bs1.p2.o', +model.fsig.f.ref),
...             ('bs1.p2.o', -model.fsig.f.ref)
...         ],
...         [
...             ('A.p1.i', +model.fsig.f.ref),
...             ('A.p1.i', -model.fsig.f.ref)
...         ]
...     )
... )
class finesse.analysis.actions.lti.FrequencyResponse4(f, inputs, outputs, *, carrier_solution=None, name='frequency_response4')[source]

Bases: Action

Computes the frequency response of a signal injected at an electrical or mechanical port. This differs from FrequencyResponse in the way the inputs and outputs are prescribed. For FrequencyResponse4 you specify signal input nodes and optical output nodes.

This action does not alter the model state. This action will ignore any currently defined signal generator elements in the model.

Produces an output transfer matrix from each signal node to each HOM at a particular frequency and optical node. The shape of the output matrix is:

[frequencies, outputs, inputs, HOMs]

It should be noted that when exciting a lower signal sideband frequency it will actually return the operator for propagating the conjugate of the lower sideband. This is because FINESSE is internally solving for the conjugate of the lower sideband to linearise non-linear optical effects.

To inject into optical nodes please see FrequencyResponse2 and FrequencyResponse3. To readout mechanical and electrical nodes please see FrequencyResponse and FrequencyResponse2.

Parameters

farray, double

Frequencies to compute the transfer functions over

inputsiterable[str or Element]

Mechanical or electrical node to inject signal at

outputsiterable[tuple[str or Node, Frequency]]

Optical node and frequency tuple to measure output at. A symbolic reference to the model’s fsig.f parameter should always be used when defining a frequency to look at.

carrier_solutionDCFieldsSolutions, optional

When provided these DC fields will be used to compute the signal matrix with. It is up to the user to input a sensible carrier field.

namestr, optional

Solution name

Examples

It is advisable to use always use a reference to the symbolic reference to the signal frequency model.fsig.f.ref instead of a fixed number incase it changes. This action will look for an initial frequency bin of X Hz to track during the frequency response analysis. A symbolic reference will always ensure the right bin is used, in cases such as looking at RF signal sidebands, 10e6+model.fsig.f.ref and 10e6-model.fsig.f.ref will always look at the upper and lower signal sideband around the +10MHz sideband.

>>> sol = model.run(
...     FrequencyResponse(
...         [1, 10, 100],
...         [model.ETM.mech.z],
...         [
...             (model.ITM.p2.o, +model.fsig.f.ref),
...             (model.ITM.p2.o, -model.fsig.f.ref)
...         ]
...     )
... )
class finesse.analysis.actions.lti.FrequencyResponseSolution[source]

Bases: BaseSolution

A solution from running a FrequencyResponse action on a model. This solution contains the frequency vector and potentially multiple input and output transfer function matrix.

Attributes

farray_like

Frequency vector [Hz]

inputsarray_like

The input names injected into for this analysis

outputsarray_like

The output names read out for this analysis

outarrray_like[dtype=np.complex128]

A matrix of transfer functions for each input to every output over the array of frequencies requested. Depending on which frequency response action was run will decide what shape this output matrix actually is. The shape of out is dependent on the analysis done:

  • FrequencyResponse - (N_f, N_outputs, N_inputs)

  • FrequencyResponse2 - (N_f, N_outputs, N_inputs, N_hom)

  • FrequencyResponse3 - (N_f, N_outputs, N_inputs, N_hom, N_hom)

  • FrequencyResponse4 - (N_f, N_outputs, N_inputs, N_hom)

typetype

Type of FrequencyResponse that was used to generate this solution

Examples

Note that the name indexing below is only available when used with the FrequencyResponse action, the other frequency-response actions must be accessed using the out attribute.

Results from a FrequencyResponseSolution can be retrieved in two ways, first through the FrequencyResponseSolution.out array or by name using [outputs, inputs]. As an example we will create a fake solution:

>>> from finesse.analysis.actions.lti import FrequencyResponseSolution
>>> sol = FrequencyResponseSolution("name")
>>> sol.inputs = ("A", "B", "C")
>>> sol.outputs = ("D", "E", "F", "G")
>>> sol.out = np.random.rand(3, len(sol.outputs), len(sol.inputs))

The names will map to those provided in the FrequencyResponse action you called to generate the solution.

The following will work to select single transfer functions between some input and output by name:

>>> sol["D", "A"] # Select A -> D
>>> sol["D", "C"] # Select C -> D
>>> sol["F", "C"] # Select C -> F

Transfer function matrices can be extracted by providing multiple

>>> sol["F", ("C", "A")]
>>> sol[("F", "G"), ("C", "A")]

Slicing can also be used:

>>> sol["D", :]   # Select all inputs to "D"
>>> sol["D", ::2] # Select every other input to "D"
>>> sol[:, "B"]   # Select "B" to all outputs
>>> sol[1:, "B"]  # Select "B" to all but the first output
extra_outputs: dict | None
f: ndarray
inputs: list[Node | tuple[Node, Symbol]]
out: ndarray
outputs: list[Node | tuple[Node, Symbol]]
outputs_inputs_indices(outputs: list[str], inputs: list[str]) tuple[int, int][source]

Returns the indices to use for selecting certain inputs and outputs from the out attribute of this solution object.

Parameters

outputsList[str]

List of names of outputs, see outputs attribute for options

inputsList[str]

List of names of inputs, see inputs attribute for options

Returns

output_index, input_index

Indices for the outputs and inputs

Raises

KeyError

Raised when no input or output name can be found.

plot(*inputs, axs=None, max_width=4, show_unity=False, **kwargs)[source]

Plot all transfer functions on a NxM grid with a max_width.

Parameters

inputs*,

Names of inputs for each subplot

axs_type_, optional

Matplotlib axes to draw on

max_widthint, optional

Maximum number of subplots in width

show_unitybool, optional

Plot a line where unity is

Returns

figure, axes

Matplotlib figure and axes to plot on

plot_inputs(*inputs, axs=None, max_width=4, show_unity=False, **kwargs)[source]

Plot all transfer functions on a NxM grid with a max_width.

Parameters

inputs*,

Names of inputs for each subplot

axs_type_, optional

Matplotlib axes to draw on

max_widthint, optional

Maximum number of subplots in width

show_unitybool, optional

Plot a line where unity is

Returns

figure, axes

Matplotlib figure and axes to plot on

plot_outputs(*outputs, axs=None, max_width=4, show_unity=False, **kwargs)[source]

Plot all transfer functions on a NxM grid with a max_width.

Parameters

outputs*,

Names of outputs for each subplot

axs_type_, optional

Matplotlib axes to draw on

max_widthint, optional

Maximum number of subplots in width

show_unitybool, optional

Plot a line where unity is

Returns

figure, axes

Matplotlib figure and axes to plot on