Modelling transfer functions¶
Transfer funtions are frequency domain functions that relate how much signal is measured
at an output relative to some injected signal. When modelling a linear time invariant
(LTI) system we must assume any signal is small enough that there are no non-linear
behaviours introduced by injecting the signal in the first place. In Finesse, when we
say a signal we refer to some small sinusoidal oscillation in either a model parameter
or detector output. And when we say we inject a signal, we mean that we are exciting
some property of the model by shaking it some small amount at a frequency we refer to as
fsig
— the signal frequency. This frequency is set by the model.fsig.f
parameter and is in units of Hertz. In various documentation we may also refer to the
signal frequency in units of radians-per-second using the symbol \(\Omega \equiv
2\pi f_{\mathrm{sig}}\). The signal frequency must always be greater than zero as it must
be oscillating.
For example, the output signal in an interferometric system is usually some optical power measurement, a small oscillation in units of Watts of optical power measured. An input signal would be a small mirror displacement in units of meters. Therefore a transfer funtion from mirror displacement to measured optical power would be in W/m, and will typically vary in amplitude and phase at different frequencies.
Operating points: Carrier and signals — DC and AC¶
It is important at this stage to understand some of Finesse’s nomenclature when it
comes to LTI systems. Optomechanical systems are typically non-linear in nature with
respect to any of its defining parameters. They are only approximately linear for small
changes in these parameters. Mathematically, this is equivalent to taking the non-linear
equations describing the system and Taylor expanding around a specific point. This point
is known as the operating point, the DC state of the model, or the linearisation
point. In Finesse this operating point is set by the current value of all the various
elements and their parameters that are included in the model. For example, the laser
power, the mirror tunings, the modulator frequency, etc. For those familiar to Simulink,
this is what the linmod
functions are performing. Finesse does this linearisation
for you.
The operating point determines the steady state of the optical fields in a model (we do not model DC changes in electrical or mechanical systems currently). For example, the circulating power in a Fabry-Perot cavity depends non-linearly on the microscopic tunings of the cavity mirrors. We refer to this as solving the carrier fields, by carrier fields we mean any static optical fields generated by lasers or modulators, so this includes radio-frequency sidebands at all optical nodes in the model.
The optical signal fields are the small sidebands around these carrier fields generated
by any injected signals in the model. Signals can only be injected into electrical or
mechanical nodes. For example, you can inject a signal into a mirror mechanical z
node, to oscillate its position along the optical axis. This in turn will create optical
signal sidebands around any carrier fields being reflected from the mirror to model an
oscillating phase modulation. Or you could inject a signal into a laser’s electronic
amplitude node to modulate the power of the outputted optical field by some small
amount. Signal fields are sometimes refered to as the AC fields.
Computing transfer functions - manual method¶
Note
The fsig
command from Finesse 2 previously set the signal frequency,
amplitude, phase, and a target for injecting. This is now split between fsig
and
signal_generator
. fsig
only sets the signal frequency in version 3.
Let us consider a simple example of a laser field refelected from a mirror 100m away then back towards the laser. We add some power detectors to look at the DC and AC state of the optical fields.
import finesse
finesse.init_plotting()
model = finesse.Model()
model.parse("""
l l1 P=1
s s1 l1.p1 m1.p1 L=100
m m1 R=1 T=0
fsig(1) # model 1Hz oscillations
pd P_DC l1.p1.i # a power detector looking at the DC power
pd1 P_AC l1.p1.i f=fsig # a power detector looking at the fsig frequency fluctuations|
""")
sol = model.run()
sol['P_DC'], sol['P_AC']
(1.0000000000000002, 0j)
We see that we measure 1W of DC power, and 0W of AC fluctuations. To inject a signal we
need to use the signal_generator
(sgen
for short) component and connect it to
some electrical or mechanical node. As we are measuring optical power we can modulate
the laser amplitude:
model = finesse.Model()
model.parse("""
l l1 P=1
s s1 l1.p1 m1.p1 L=100
m m1 R=1 T=0
fsig(1) # model 1Hz oscillations
sgen sig l1.amp # inject a signal to modulate the laser amplitude
pd P_DC l1.p1.i # a power detector looking at the DC power
pd1 P_AC l1.p1.i f=fsig # a power detector looking at the fsig frequency fluctuations|
""")
sol = model.run()
sol['P_DC'], sol['P_AC']
(1.0000000000000002, (0.9999999999912151-4.19169004389109e-06j))
Now we see that the DC state is not affected, signals do not change carrier fields or the DC state of the simulation. But we do see that the AC detector is measuring a complex value which absolute value of 1 and a small phase component. So what is this complex value? It is a single point of a transfer function, it tells us the relationship between a 1Hz oscillation applied at the laser amplitude input relative to the optical power fluctuation at the power detector. 1V applied at the laser amplitude node gives 1W of power fluctuation. Therefore our transfer function has units of W/V.
The absolute value tells us how much signal is generated, 1W/V, nothing particularly exciting, but all we are doing is reflecting the laser from the mirror. If the mirror was not a perfect reflector we see we get a small transfer function
model.m1.set_RTL(R=0.5, T=0.5)
sol = model.run()
sol['P_AC']
(0.4999999999956076-2.095845021945545e-06j)
This phase component is from the time delay taken by injecting the signal at the laser and it propagating to the mirror and back to the detectors. If we make it longer we can see we accumulate more delay
model.m1.set_RTL(R=1, T=0)
model.spaces.s1.L = 10e6
sol = model.run()
sol['P_AC']
(0.9134274721511403-0.40700153945602985j)
The process described above is the same for any model, no matter how complicated. If you introduce more signal generators they will all be coherently injected! If you want to model multiple signal injections with this manual method you’ll need to switch off certain signal generators and switch on others then recompute the transfer functions.
If you want to compute a plot we can simply sweep the signal frequency. From this we can see that the amplitude remains constant, but there is an evolving phase delay with frequency.
model.parse("xaxis(fsig, log, 1, 100, 1000)")
sol = model.run()
sol.plot('P_AC');
We can also apply some electronic filters to shape our inputs if required. For example, take a Mephisto NPRO laser, the curent drive modulation has a 5kHz bandwidth on it. Below we can quickly add this in with a 5kHz lowpass Butteworth filter and then excite the input to that instead.
model = finesse.Model()
model.parse("""
l l1 P=1
s s1 l1.p1 m1.p1 L=10k
m m1 R=1 T=0
fsig(1) # model 1Hz oscillations
# 5kHz bandwidth with a butter filter
butter current_drive 1 lowpass 5k
link(current_drive, l1.amp)
sgen sig current_drive.p1.i
pd P_DC l1.p1.i # a power detector looking at the DC power
pd1 P_AC l1.p1.i f=fsig # a power detector looking at the fsig frequency fluctuations|
xaxis(fsig, log, 100, 100k, 100)
""")
sol = model.run()
sol.plot('P_AC', logx=True, logy=True);