diff --git a/bin/test_fsig.py b/bin/test_fsig.py new file mode 100644 index 0000000000000000000000000000000000000000..2e5b318d0d47161be5de3bac4828c8531c5decb4 --- /dev/null +++ b/bin/test_fsig.py @@ -0,0 +1,50 @@ +from pykat import finesse +from pykat.detectors import * +from pykat.components import * +from pykat.commands import * +from pykat.structs import * + +import numpy as np +import pylab as pl + +code = """ +l l1 2 0 n1 +m m1 0.99 0.01 0 n1 n2 +s cav1 1200 n2 n3 +m m2 0.99 0.01 -0.1 n3 n4 + +attr m2 m 1 # mech sus1 + +ad up_refl 0 n1 +ad low_refl 0 n1 + +qd refl_A 0 0 n1 +qd refl_Q 0 90 n1 +qd tran_A 0 0 n4 +qd tran_Q 0 90 n4 + +put up_refl f $x1 +put low_refl f $mx1 + +yaxis log re:im +""" + +kat = finesse.kat(kat_code=code) + +kat.signals.apply(kat.l1.power, 1, 0) +kat.signals.apply(kat.m1.phi, 1, 90) + +kat.add(xaxis('log', [1, 1000], kat.signals.f, 100)) + +out = kat.run(printout=0, printerr=0) + +# using real and imag part compute the complex value of the upper and lower sidebands +a_up = out.y[:,0] + out.y[:,1]*1j +a_lo = out.y[:,2] + out.y[:,3]*-1j + +pl.figure(1) +pl.loglog(out.x, np.abs(a_up + a_lo), out.x, np.abs((a_up - a_lo) / (1j))) +pl.xlabel(out.xlabel) +pl.title("Reflection quadratures with no relative carrier phase") +pl.legend(["Amplitude","Phase"]) +pl.show() \ No newline at end of file diff --git a/pykat/commands.py b/pykat/commands.py index 27a3bdad90adad26b3787863c88adb01b5d8a34c..a47b0dd0a51e6bb3573a579a933af2bf0b39cca7 100644 --- a/pykat/commands.py +++ b/pykat/commands.py @@ -10,6 +10,7 @@ import exceptions from components import * from structs import * from pykat.param import Param, putter +import pykat.exceptions as pkex class Command(object): def __init__(self): @@ -50,7 +51,7 @@ class gauss(object): class xaxis(Command): - def __init__(self, scale, limits, comp, param, steps, axis_type="xaxis"): + def __init__(self, scale, limits, param, steps, comp=None, axis_type="xaxis"): self._axis_type = axis_type self.x = putter("x1") @@ -79,20 +80,17 @@ class xaxis(Command): self.steps = int(steps) - # if entered component is a string then just store and use that - if isinstance(comp, str): - self.__comp = comp - elif not isinstance(comp, Component): - raise exceptions.ValueError("{0} has not been added".format(comp)) - else: - self.__comp = comp - if isinstance(param, str): self.__param = param + if comp == None: + raise pkex.BasePyKatException("If parameter is set with a string, the comp argument must set the component name") + + self.__comp = comp elif not isinstance(param, Param) : - raise exceptions.ValueError("param argument is not of type Param") + raise pkex.BasePyKatException("param argument is not of type Param") else: self.__param = param + self.__comp = param._owner.name @staticmethod def parseFinesseText(text): @@ -108,7 +106,7 @@ class xaxis(Command): if len(values) != 6: raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text)) - return xaxis(values[2], [values[3], values[4]], values[0], values[1], values[5], axis_type=axis_type) + return xaxis(values[2], [values[3], values[4]], values[1], values[5], comp=values[0], axis_type=axis_type) def getFinesseText(self): # store either the component name of the string provided @@ -120,8 +118,8 @@ class xaxis(Command): min(self.limits), max(self.limits), self.steps, axis_type=self._axis_type); class x2axis(xaxis): - def __init__(self, scale, limits, comp, param, steps): - xaxis.__init__(self, scale, limits, comp, param, steps, axis_type="x2axis") + def __init__(self, scale, limits, param, steps, comp=None): + xaxis.__init__(self, scale, limits, param, steps, comp=comp, axis_type="x2axis") self.x = putter("x2") self.mx = putter("mx2") diff --git a/pykat/components.py b/pykat/components.py index 8a37f6f0dc12998dc789107c2ce6d2a6a3f260d9..696c42eeb5575cb0ce2b9ca42f1c38c87f4c5813 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -158,11 +158,11 @@ class AbstractMirrorComponent(Component): self.__R = Param("R", self, SIfloat(R)) self.__T = Param("T", self, SIfloat(T)) - self.__phi = Param("phi", self, SIfloat(phi)) + self.__phi = Param("phi", self, SIfloat(phi), canFsig=True, fsig_name="phs") self.__Rcx = AttrParam("Rcx", self, SIfloat(Rcx)) self.__Rcy = AttrParam("Rcy", self, SIfloat(Rcy)) - self.__xbeta = AttrParam("xbeta", self, SIfloat(xbeta)) - self.__ybeta = AttrParam("ybeta", self, SIfloat(ybeta)) + self.__xbeta = AttrParam("xbeta", self, SIfloat(xbeta), canFsig=True, fsig_name="x") + self.__ybeta = AttrParam("ybeta", self, SIfloat(ybeta), canFsig=True, fsig_name="y") self.__mass = AttrParam("mass", self, SIfloat(mass)) self.__r_ap = AttrParam("r_ap", self, SIfloat(r_ap)) @@ -682,9 +682,9 @@ class laser(Component): self._requested_node_names.append(node) - self.__power = Param("P", self, SIfloat(P)) - self.__f_offset = Param("f", self, SIfloat(f_offset)) - self.__phase = Param("phase", self, SIfloat(phase)) + self.__power = Param("P", self, SIfloat(P), canFsig=True, fsig_name="amp") + self.__f_offset = Param("f", self, SIfloat(f_offset), canFsig=True, fsig_name="f") + self.__phase = Param("phase", self, SIfloat(phase), canFsig=True, fsig_name="phs") self.__noise = AttrParam("noise", self, 0) @property diff --git a/pykat/finesse.py b/pykat/finesse.py index a40f85813920ef8dc4e5c006f4acac657d58c5e8..c94ef794149175dcd8c08d3bf5eaf7debc16406e 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -40,6 +40,7 @@ from pykat.components import Component from pykat.commands import Command, xaxis from pykat.gui.gui import pyKatGUI from pykat.SIfloat import * +from pykat.param import Param, AttrParam import pykat.exceptions as pkex @@ -127,7 +128,98 @@ class katRun2D(object): return self.z[idx].squeeze() else: raise pkex.BasePyKatException("No output by the name {0} found".format(str(value))) - + +class Signals: + class fsig: + def __init__(self, param, name, amplitude, phase): + self._params = [] + self.__target = param + self.__name = name + self.__amplitude = Param("amp", self, SIfloat(amplitude)) + self.__phase = Param("phase", self, SIfloat(phase)) + + # unfortunatenly the target names for fsig are not the same as the + # various parameter names of the components, e.g. mirror xbeta is x + # for fsig. So we need to check here what type of component we are targetting + # and then based on the parameter specfied get the name + if not param.canFsig: + raise pkex.BasePyKatException("Cannot fsig parameter {1} on component {0}".format(str(param._owner), param.name)) + + + def _register_param(self, param): + self._params.append(param) + + @property + def name(self): return self.__name + + @property + def amplitude(self): return self.__amplitude + + @property + def phase(self): return self.__phase + + @property + def target(self): return self.__target.fsig_name + + @property + def owner(self): return self.__target._owner.name + + def getFinesseText(self): + rtn = [] + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + @property + def name(self): + # if we don't have any signals yet then use a dummy name + # however we need to always tune a real fsig command + # so need to get the name of at least one of them + # as if you tune one you tune them all + if len(self.targets) == 0: + return "signal" + else: + return self.targets[0].name + + @property + def f(self): return self.__f + + def __init__(self): + + self.targets = [] + self._params = [] + + self.__f = Param("f", self, 1) + + def _register_param(self, param): + self._params.append(param) + + def apply(self, target, amplitude, phase, name=None): + + if target == None: + raise pkex.BasePyKatException("No target was specified for signal to be applied") + + if name == None: + name = "sig_" +target._owner.name + "_" + target.name + + self.targets.append(Signals.fsig(target, name, amplitude, phase)) + + + def getFinesseText(self): + rtn = [] + + for t in self.targets: + rtn.extend(t.getFinesseText()) + rtn.append("fsig {name} {comp} {target} {frequency} {phase} {amplitude}".format(name = t.name, comp=t.owner, target=t.target, frequency=str(self.f), phase=str(t.phase), amplitude=str(t.amplitude))) + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + class Block: def __init__(self, name): self.__name = name @@ -154,10 +246,11 @@ class kat(object): self.__tempdir = tempdir self.__tempname = tempname self.pykatgui = None - - # initialise default block - self.__currentTag= NO_BLOCK - self.__blocks[NO_BLOCK] = Block(NO_BLOCK) + self.__signals = Signals() + + # initialise default block + self.__currentTag= NO_BLOCK + self.__blocks[NO_BLOCK] = Block(NO_BLOCK) # Various options for running finesse, typicaly the commands with just 1 input # and have no name attached to them. @@ -181,6 +274,9 @@ class kat(object): cls = type(self) self.__class__ = type(cls.__name__, (cls,), {}) + @property + def signals(self): return self.__signals + @property def maxtem(self): return self.__maxtem @maxtem.setter @@ -689,6 +785,16 @@ class kat(object): out.append(txt + "\n") + # now get any signal commands + txt = self.signals.getFinesseText() + + if txt != None: + if isinstance(txt,list): + for t in txt: out.append(t+ "\n") + else: + out.append(txt + "\n") + + if self.scale != None and self.scale !='': out.append("scale {0}\n".format(self.scale)) if self.phase != None: out.append("phase {0}\n".format(self.phase)) if self.maxtem != None: out.append("maxtem {0}\n".format(self.maxtem)) diff --git a/pykat/param.py b/pykat/param.py index e47d378b248c218aef32e905d24df4f17a838ae7..52a5fc88554f8ce86990f5d672a8ec652fd65c09 100644 --- a/pykat/param.py +++ b/pykat/param.py @@ -57,13 +57,22 @@ class putter(object): class Param(putable, putter): - def __init__(self, name, owner, value, isPutable=True, isPutter=True, isTunable=True, var_name=None): + def __init__(self, name, owner, value, canFsig=False, fsig_name=None, isPutable=True, isPutter=True, isTunable=True, var_name=None): self._name = name self._owner = owner self._value = value self._isPutter = isPutter self._isTunable = isTunable self._owner._register_param(self) + self._canFsig = False + + if canFsig: + self._canFsig = True + + if fsig_name == None: + raise pkex.BasePyKatException("If parameter is a possible fsig target the fsig_name argument must be set") + + self.__fsig_name = fsig_name if isPutter: if var_name == None: @@ -73,7 +82,13 @@ class Param(putable, putter): if isPutable: putable.__init__(self, owner.name, name, isPutable) - + + @property + def canFsig(self): return self._canFsig + + @property + def fsig_name(self): return self.__fsig_name + @property def name(self): return self._name