Creating a composite component

import finesse
from finesse.components import Mirror, Space, Laser
from finesse.parameter import float_parameter, bool_parameter
from finesse.element import ModelElement
from finesse.freeze import Freezable
import numpy as np
import matplotlib.pyplot as plt

Here’s an example on how to create composite objects in Finesse 3. Below we create a thick mirror that better defines the AR, substrate, and HR surface. Internally this thick mirror is made from two thin mirrors plus a space. These are added to the model when the `_on_add` method is called just after the composite object is added to a model. This interface is quite generic and internal connections and features of the components can be exposed or not. For example here we limit the AR surface to only ever use a loss.

Parameters are added to the composite element and the symbolically referenced to the internal components. Although there are geometric Parameters defined in the composite object, these are marked as false, as they simply map to the real geometric parameters of the component parts. Before adding the composite components we also modify where they will be stored in the namespace of the model. They are not added to the main model namspace to save confusion, but to the composite `.components` namespace.

Below we also redefine what the ports are called. The thick mirror has an AR and HR port, or alternatively a front and back port.

@float_parameter("Rc", "HR radius of curvature", units="m", is_geometric=False)
@float_parameter("Rc_AR", "AR radius of curvature", units="m", is_geometric=False)
@float_parameter("thickness", "Thickness", units="m", is_geometric=False)
@float_parameter("nr", "Substrate refractive index", units="", is_geometric=False)
@float_parameter("R", "HR reflectivity", units="", is_geometric=False)
@float_parameter("T", "HR transmissivity", units="", is_geometric=False)
@float_parameter("L", "HR loss", units="", is_geometric=False)
@float_parameter("R_AR", "AR reflectivity", units="", is_geometric=False)
@float_parameter("phi", "Tuning", units="degrees", is_geometric=False)
@float_parameter("xbeta", "Yaw misalignment", units="radians")
@float_parameter("ybeta", "Pitch misalignment", units="radians")
@bool_parameter("misaligned", "Misaligns mirror reflection (R=0 when True)")
class ThickMirror(ModelElement):
    def __init__(self, name, R=0.99, T=0.01, L=0, R_AR=0, phi=0, Rc=np.inf, Rc_AR=np.inf, thickness=0, nr=1, misaligned=False, xbeta=0, ybeta=0):
        super().__init__(name)
        self.phi.value = phi
        self.xbeta.value = xbeta
        self.ybeta.value = ybeta
        self.misaligned.value = misaligned
        self.R.value = R
        self.T.value = T
        self.L.value = L
        self.Rc.value = Rc

        self.thickness.value = thickness
        self.nr.value = nr

        self.R_AR.value = R_AR
        self.Rc_AR.value = Rc_AR

        self.components = Freezable()

    @property
    def fr(self):
        return getattr(self.components, f"{self.name}_front").p1
    
    @property
    def bk(self):
        return getattr(self.components, f"{self.name}_back").p2

    @property
    def HR(self):
        return getattr(self.components, f"{self.name}_front").p1
    
    @property
    def AR(self):
        return getattr(self.components, f"{self.name}_back").p2

    def _on_add(self, model):
        back = Mirror(f"{self.name}_back", Rc=self.Rc_AR.ref, R=0, L=self.R_AR.ref, T=1-self.R_AR.ref, phi=self.phi.ref, misaligned=self.misaligned.ref, xbeta=self.xbeta.ref, ybeta=self.ybeta.ref)
        front = Mirror(f"{self.name}_front", Rc=self.Rc.ref, R=self.R.ref, T=self.T.ref, L=self.L.ref, phi=self.phi.ref, misaligned=self.misaligned.ref, xbeta=self.xbeta.ref, ybeta=self.ybeta.ref)

        back._namespace = (f".{self.name}.components", )
        front._namespace = (f".{self.name}.components", )

        model.add(back, unremovable=True)
        model.add(front, unremovable=True)

        substrate = Space(f"{self.name}_substrate", back.p1, front.p2, L=self.thickness.ref, nr=self.nr.ref)
        substrate._namespace = (".spaces", f".{self.name}.components")
        model.add(substrate, unremovable=True)

To use it we simply add it to the model and then connect up the ports as required. We can then interact with the composite component parameters, such as scanning the cavity length, in the usual ways.

model = finesse.Model()
L0 = model.add(Laser('L0'))
ITM = model.add(ThickMirror('ITM'))
ETM = model.add(ThickMirror('ETM'))
model.link(L0.p1, ITM.AR)
model.link(ITM.HR, ETM.HR)
model.parse("pd P ITM.HR.i")

out = model.run("xaxis(ETM.phi, lin, -90, 90, 100)")
plt.semilogy(out.x1, out['P']

Leave a Reply