From ebd806ea915a09966b628cc1e7032565d88359c7 Mon Sep 17 00:00:00 2001 From: Gamelin Alexis <alexis.gamelin@synchrotron-soleil.fr> Date: Tue, 11 Jul 2023 17:49:13 +0200 Subject: [PATCH] Add the interface for FB/loops in CavityResonator Add an interface in CavityResonator to allow for external FB and loops. Add the ProportionalLoop and TunerLoop using what was already done in the RF-FB branch. --- mbtrack2/tracking/__init__.py | 4 +- mbtrack2/tracking/rf.py | 120 +++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 3 deletions(-) diff --git a/mbtrack2/tracking/__init__.py b/mbtrack2/tracking/__init__.py index 38a0f8d..2e53921 100644 --- a/mbtrack2/tracking/__init__.py +++ b/mbtrack2/tracking/__init__.py @@ -6,7 +6,9 @@ from mbtrack2.tracking.particles import (Electron, Particle) from mbtrack2.tracking.synchrotron import Synchrotron from mbtrack2.tracking.rf import (RFCavity, - CavityResonator) + CavityResonator, + ProportionalLoop, + TunerLoop) from mbtrack2.tracking.parallel import Mpi from mbtrack2.tracking.element import (Element, LongitudinalMap, diff --git a/mbtrack2/tracking/rf.py b/mbtrack2/tracking/rf.py index 81d9a48..ad397ff 100644 --- a/mbtrack2/tracking/rf.py +++ b/mbtrack2/tracking/rf.py @@ -58,6 +58,9 @@ class CavityResonator(): If used with mpi, beam.mpi.share_distributions must be called before the track method call. + Different kind of RF feeback and loops can be added using: + cavity_resonator.feedback.append(loop) + Parameters ---------- ring : Synchrotron object @@ -212,6 +215,7 @@ class CavityResonator(): self.theta_gr = 0 self.Pg = 0 self.n_bin = int(n_bin) + self.feedback = [] def init_tracking(self, beam): """ @@ -318,10 +322,13 @@ class CavityResonator(): # phasor decay to be at t=0 of the next bunch self.phasor_decay(self.ring.T1, ref_frame="beam") + + # apply different kind of RF feedback + for fb in self.feedback: + fb.track(self) self.nturn += 1 - def init_phasor_track(self, beam): """ Initialize the beam phasor for a given beam distribution using a @@ -894,4 +901,113 @@ class CavityResonator(): def deltaVRF(self, z, I0, F = 1, PHI = 0): """Return the generator voltage minus beam loading voltage taking into account form factor amplitude F and form factor phase PHI""" - return -1*self.Vg*(self.ring.k1*self.m)**2*np.cos(self.ring.k1*self.m*z + self.theta_g) - self.Vb(I0)*F*(self.ring.k1*self.m)**2*np.cos(self.ring.k1*self.m*z + self.psi - PHI) \ No newline at end of file + return -1*self.Vg*(self.ring.k1*self.m)**2*np.cos(self.ring.k1*self.m*z + self.theta_g) - self.Vb(I0)*F*(self.ring.k1*self.m)**2*np.cos(self.ring.k1*self.m*z + self.psi - PHI) + +class ProportionalLoop(): + """ + Proportional feedback loop to control a CavityResonator amplitude and phase. + + Feedback setpoints are cavity_resonator.Vc and cavity_resonator.theta. + + The loop must be added to the CavityResonator object using: + cavity_resonator.feedback.append(loop) + + Parameters + ---------- + ring : Synchrotron object + cavity_resonator : CavityResonator + The CavityResonator which is to be controlled. + gain_A : float + Amplitude (voltage) gain of the feedback. + gain_P : float + Phase gain of the feedback. + delay : int + Feedback delay in unit of turns. + + """ + def __init__(self, ring, cavity_resonator, gain_A, gain_P, delay): + self.ring = ring + self.gain_A = gain_A + self.gain_P = gain_P + self.delay = int(delay) + self.volt_delay = np.ones(self.delay)*cavity_resonator.Vc + self.phase_delay = np.ones(self.delay)*cavity_resonator.theta + + def track(self, cavity_resonator): + """ + Tracking method for the amplitude and phase loop. + + Returns + ------- + None. + + """ + diff_A = self.volt_delay[-1] - cavity_resonator.Vc + diff_P = self.phase_delay[-1] - cavity_resonator.theta + cavity_resonator.Vg -= self.gain_A*diff_A + cavity_resonator.theta_g -= self.gain_P*diff_P + cavity_resonator.generator_phasor_record = np.ones(self.ring.h)*cavity_resonator.generator_phasor + self.volt_delay = np.roll(self.volt_delay, 1) + self.phase_delay = np.roll(self.phase_delay, 1) + self.volt_delay[0] = cavity_resonator.cavity_voltage + self.phase_delay[0] = cavity_resonator.cavity_phase + +class TunerLoop(): + """ + Cavity tuner loop used to control a CavityResonator tuning (psi or detune) + in order to keep the phase between cavity and generator current constant. + + Only a proportional controller is assumed. + + The loop must be added to the CavityResonator object using: + cavity_resonator.feedback.append(loop) + + Parameters + ---------- + ring : Synchrotron object + cavity_resonator : CavityResonator + The CavityResonator which is to be controlled. + gain : float + Proportional gain of the tuner loop. + If not specified, 0.01 is used. + avering_period: + Period during which the phase difference is monitored and averaged. + Then the feedback correction is applied every avering_period turn. + Unit is turn number. + A value longer than one synchrotron period (1/fs) is recommended. + If not specified, 2-synchrotron period (2/fs) is used, although it is + too fast compared to the actual situation. + offset : float + Tuning offset in [rad]. + + """ + def __init__(self, ring, cavity_resonator, gain=0.01, avering_period=0, + offset=0): + self.ring = ring + if avering_period == 0: + fs = self.ring.synchrotron_tune(cavity_resonator.Vc)*self.ring.f1/self.ring.h + avering_period = 2/fs/self.ring.T0 + + self.Pgain = gain + self.offset = offset + self.avering_period = int(avering_period) + self.diff = 0 + self.count = 0 + + def track(self, cavity_resonator): + """ + Tracking method for the tuner loop. + + Returns + ------- + None. + + """ + if self.count == self.avering_period: + diff = self.diff/self.avering_period - self.offset + cavity_resonator.psi -= diff * self.Pgain + self.count = 0 + self.diff = 0 + else: + self.diff += cavity_resonator.cavity_phase - cavity_resonator.theta_g + cavity_resonator.psi + self.count += 1 -- GitLab