Skip to content
Snippets Groups Projects
Commit 7d37efa0 authored by SAURET's avatar SAURET
Browse files

Feedback class vf

parent 3e8ac74f
No related branches found
No related tags found
No related merge requests found
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Tue May 3 09:36:24 2022 This module defines both simple and FIR based feedback for tracking.
@author: sauret
""" """
from mbtrack2.tracking.element import Element from mbtrack2.tracking.element import Element
import numpy as np import numpy as np
import matplotlib.pyplot as plt
class FeedBackTransverse(Element) : class SimpleFB(Element) :
""" """
Transverse feedback system to counter transverse instabilities. Simple feedback system proceeding bunch by bunch correction to counter beam
instabilities
Parameters
----------
ring : Synchrotron object
damping_time : array of shape (3,)
Contains the desired damping time in seconds for each plane. Element 0 of
the array defines the horizontal plane as 1 and 2 define the vertical
and longitudinal plane respectively.
phase_diff : array of shape (3,)
Contains the desired phase in degree for each plane. Element 0 of the
array defines the horizontal plane as 1 and 2 define the vertical and
longitudinal plane respectively.
plane : bool array of shape (3,), optional
Allow to choose on which plane the feedback is active. State True to
turn on the feedback or False otherwise. Element 0 of the array defines
the horizontal plane as 1 and 2 define the vertical and longitudinal plane
respectively.
"""
def __init__(self, ring, damping_time, phase_diff, plane = np.zeros((3,), dtype= bool)):
self.ring = ring
self.damping_time = damping_time
self.phase_diff = phase_diff
self.plane = plane
@Element.parallel
def track(self, bunch):
"""
Tracking method for the feedback system
No bunch to bunch interaction, so written for Bunch object and
@Element.parallel is used to handle Beam object.
Parameters
----------
bunch : Bunch or Beam object
"""
if(self.plane[0] == True):
bunch["xp"] -= (2*self.ring.T0/self.damping_time[0])*np.sin(self.phase_diff[0])*bunch.mean[1]
if(self.plane[1] == True):
bunch["yp"] -= (2*self.ring.T0/self.damping_time[1])*np.sin(self.phase_diff[1])*bunch.mean[3]
if(self.plane[2] == True):
bunch["delta"] -= (2*self.ring.T0/self.damping_time[2])*np.sin(self.phase_diff[1])*bunch.mean[5]
class FIR_Feedback(Element) :
"""
Feedback system based on FIR filters, proceeding bunch by bunch correction
to counter beam instabilities.
Parameters Parameters
---------- ----------
ring : Synchrotron object ring : Synchrotron object
turn_delay : int, optional tune : float array of shape (3,)
Number of turn delay before applying kick. Betatron tune for horizontal and vertical plane in the first and second
tap_number : int, optional elements of the array. Synchrotron tune for the third element.
Number of tap for the FIR filters. turn_delay : int array of shape (3,)
ampl : float, optional Number of turn delay before applying kick. Element 0 of the array
Gain of the FIR filters. defines the horizontal plane as 1 and 2 define the vertical and
phase : float, optional longitudinal plane respectively.
Phase of the FIR filters in deg. tap_number : int array of shape (3,)
Number of tap for the FIR filters. Element 0 of the array defines
the horizontal plane as 1 and 2 define the vertical and longitudinal
plane respectively.
gain : float array of shape (3,)
Gain of the FIR filters. Element 0 of the array defines
the horizontal plane as 1 and 2 define the vertical and longitudinal
plane respectively.
phase : float array of shape (3,)
Phase of the FIR filters in degree. Element 0 of the array defines
the horizontal plane as 1 and 2 define the vertical and longitudinal
plane respectively.
bpm_error : float array of shape (3,), optional
Stores the BPM error [m] for horizontal and vertical, [s] for longitudinal.
First, second and third element corespond to the horizontal, vertical
and longitudinal plane respectively.
max_kick : float array of shape (3,), optional
Stores the maximum kick limitation. First, second and third
element correspond to the horizontal, vertical and longitudinal plane
respectively.
max_kick_state : bool array of shape (3,), optional
Allow to choose on which plane the max kick limitation is active. State
True to turn on the max kick limitation or False otherwise. Element 0
of the array defines the horizontal plane as 1 and 2 define the vertical
and longitudinal plane respectively.
plane : bool array of shape (3,), optional
Allow to choose on which plane the feedback is active. State True to
turn on the feedback or False otherwise. Element 0 of the array defines
the horizontal plane as 1 and 2 define the vertical and longitudinal
plane respectively.
Attributes Attributes
---------- ----------
x_pos : array x_pos : array
...@@ -31,112 +109,135 @@ class FeedBackTransverse(Element) : ...@@ -31,112 +109,135 @@ class FeedBackTransverse(Element) :
y_pos : array y_pos : array
Retrieves and stores the position of the bunch, measured by the BPM, Retrieves and stores the position of the bunch, measured by the BPM,
in vertical plane. in vertical plane.
tau_pos : array
Retrieves and stores the position of the bunch, measured by the BPM,
in longitudinal plane.
kick_x : array kick_x : array
Stores the numerical values of the kicks for the horizontal plane. Stores the numerical values of the kicks for the horizontal plane.
kick_y : array kick_y : array
Stores the numerical values of the kicks for the vertical plane. Stores the numerical values of the kicks for the vertical plane.
kick_tau : array
Stores the numerical values of the kicks for the longitudinal plane.
coef_x : array coef_x : array
Stores the coefficients of the FIR filter for the horizontal plane. Stores the coefficients of the FIR filter for the horizontal plane.
coef_y : array coef_y : array
Stores the coefficients of the FIR filter for the vertical plane. Stores the coefficients of the FIR filter for the vertical plane.
coef_tau : array
Stores the coefficients of the FIR filter for the longitudinal plane.
Methods
-------
get_fir(tap_number, tune, phase, turn_delay, gain)
Initialize the FIR filter for the desired plane and return an array
containing the FIR coefficients.
plot_fir(plane)
Plot the gain and the phase of the FIR filter selected.
References
----------
[1] T.Nakamura, S.Daté, K. Kobayashi, T. Ohshima. Proceedings of EPAC
2004. Transverse bunch by bunch feedback system for the Spring-8
storage ring.
""" """
def __init__(self, ring, turn_delay=1, tap_number=5, ampl =1e-1, phase = -90): def __init__(self, ring, tune, turn_delay, tap_number, gain, phase, bpm_error = np.zeros((3,)),
max_kick = np.zeros((3,)),plane = np.zeros((3,), dtype = bool)):
self.ring = ring self.ring = ring
self.tune = tune
self.turn_delay = turn_delay self.turn_delay = turn_delay
self.tap_number = tap_number self.tap_number = tap_number
self.ampl = ampl self.gain = gain
self.phase = phase self.phase = phase
self.x_pos = np.zeros((self.tap_number,)) self.bpm_error = bpm_error
self.y_pos = np.zeros((self.tap_number,)) self.max_kick = max_kick
self.kick_x = np.zeros((self.turn_delay+1,)) self.plane = plane
self.kick_y = np.zeros((self.turn_delay+1,)) self.x_pos = np.zeros((self.tap_number[0],))
self.coef_x = self.get_fir_x() self.y_pos = np.zeros((self.tap_number[1],))
self.coef_y = self.get_fir_y() self.tau_pos = np.zeros((self.tap_number[2],))
self.kick_x = np.zeros((self.turn_delay[0]+1,))
self.kick_y = np.zeros((self.turn_delay[1]+1,))
self.kick_tau = np.zeros((self.turn_delay[2]+1,))
self.coef_x = self.get_fir(self.tap_number[0], self.tune[0], self.phase[0], self.turn_delay[0], self.gain[0])
self.coef_y = self.get_fir(self.tap_number[1], self.tune[1], self.phase[1], self.turn_delay[1], self.gain[1])
self.coef_tau = self.get_fir(self.tap_number[2], self.tune[2], self.phase[2], self.turn_delay[2], self.gain[2])
def get_fir_x(self): def get_fir(self, tap_number, tune, phase, turn_delay, gain):
""" """
Compute the FIR coefficients for the horizontal plane. Compute the FIR coefficients for the selected plane(s).
This method is based on the FIR filter design algorithm developped by
T.Nakamura.
Returns Returns
------- -------
coef_x : array FIR_coef : array
Array containing the FIR coefficients for the horizontal plane. Array containing the FIR coefficients for the selected plane(s).
""" """
phi_x = 2*np.pi*self.ring.tune[0] it = np.zeros((tap_number,))
zeta = (self.phase*2*np.pi)/360 CC = np.zeros((5, tap_number,))
coef_x = np.zeros((self.tap_number,)) zeta = (phase*2*np.pi)/360
for k in range(tap_number):
it[k] = (-k - turn_delay)
it = np.zeros((self.tap_number,)) phi = 2*np.pi*tune
for k in range(self.tap_number): cs = np.cos(phi*it)
it[k] = (-k - self.turn_delay) sn = np.sin(phi*it)
cs_x = np.zeros((self.tap_number,)) CC[0][:] = 1
sn_x = np.zeros((self.tap_number,)) CC[1][:] = cs
for i in range(self.tap_number): CC[2][:] = sn
cs_x[i] = (np.cos(phi_x*it[i])) CC[3][:] = it*sn
sn_x[i] = (np.sin(phi_x*it[i])) CC[4][:] = it*cs
CC_x = np.zeros((5, self.tap_number)) TCC = np.transpose(CC)
for j in range(self.tap_number): W = np.linalg.inv(CC.dot(TCC))
CC_x[0][j] = 1 D = W.dot(CC)
CC_x[1][j] = cs_x[j]
CC_x[2][j] = sn_x[j] FIR_coef = gain*(D[1][:]*np.cos(zeta) + D[2][:]*np.sin(zeta))
CC_x[3][j] = it[j]*sn_x[j] return FIR_coef
CC_x[4][j] = it[j]*cs_x[j]
TCC_x = np.transpose(CC_x)
W_x = np.linalg.inv(CC_x.dot(TCC_x))
D_x = W_x.dot(CC_x)
for n in range(self.tap_number):
a_x = self.ampl*(D_x[1][n]*np.cos(zeta) + D_x[2][n])
coef_x[n] = (a_x)
return coef_x
def get_fir_y(self): def plot_fir(self, plane):
""" """
Compute the FIR coefficients for the vertical plane. Plot the gain and the phase of the FIR filter for the desired plane
Parameters
----------
plane : string
States "x" for the horizontal plane, "y" for the vertical and
"long" for the longitudinal one.
Returns Returns
------- -------
coef_y : array None.
Array containing the FIR coefficients for the vertical plane.
""" """
phi_y = 2*np.pi*self.ring.tune[1] tune = np.arange(0,1,0.0001)
zeta = (self.phase*2*np.pi)/360 plane_dict = {"x":0 , "y":1 , "long":2}
coef_y = np.zeros((self.tap_number,)) index_plane = plane_dict[plane]
t = np.array([len(self.coef_x), len(self.coef_y), len(self.coef_tau)])
it = np.zeros((self.tap_number,)) latency = np.exp(-1j*2*np.pi*tune*self.turn_delay[index_plane])
for k in range(0,self.tap_number): H_FIR = 0
it[k] = (-k - self.turn_delay) liste = []
if(plane == "x"):
cs_y = np.zeros((self.tap_number,)) liste = self.coef_x
sn_y = np.zeros((self.tap_number,)) elif(plane == "y"):
for i in range(self.tap_number): liste = self.coef_y
cs_y[i] = (np.cos(phi_y*it[i])) elif(plane == "long"):
sn_y[i] = (np.sin(phi_y*it[i])) liste = self.coef_tau
for k in range(t[index_plane]):
CC_y = np.zeros((5, self.tap_number)) H_FIR += liste[k]*np.exp(-1j*2*np.pi*(k)*tune)
for j in range(self.tap_number): H_tot = H_FIR * latency
CC_y[0][j] = 1 gain = np.abs(H_tot)
CC_y[1][j] = cs_y[j] phase = np.angle(H_tot, deg = True)
CC_y[2][j] = sn_y[j] plt.figure("Gain"+str(index_plane))
CC_y[3][j] = it[j]*sn_y[j] plt.plot(tune, gain)
CC_y[4][j] = it[j]*cs_y[j] plt.title("Gain" + str(index_plane))
plt.xlabel("Tune")
TCC_y = np.transpose(CC_y) plt.figure("Phase"+str(index_plane))
W_y = np.linalg.inv(CC_y.dot(TCC_y)) plt.plot(tune, phase)
D_y = W_y.dot(CC_y) plt.title("Phase in degree" + str(index_plane))
plt.xlabel("Tune")
for n in range(self.tap_number): plt.ylabel("Degree")
a_y = self.ampl*(D_y[1][n]*np.cos(zeta) + D_y[2][n]*np.sin(zeta))
coef_y[n] = (a_y)
return coef_y
@Element.parallel @Element.parallel
def track(self, bunch): def track(self, bunch):
""" """
...@@ -147,24 +248,51 @@ class FeedBackTransverse(Element) : ...@@ -147,24 +248,51 @@ class FeedBackTransverse(Element) :
Parameters Parameters
---------- ----------
bunch : Bunch or Beam object bunch : Bunch or Beam object
""" """
self.x_pos[0] = bunch.mean[0] if(self.plane[0] == True):
self.y_pos[0] = bunch.mean[2] self.x_pos[0] = bunch.mean[0] + np.random.normal(0, self.bpm_error[0])
x_kick = 0
x_kick = 0
y_kick = 0 for k in range(self.tap_number[0]):
x_kick += self.coef_x[k]*self.x_pos[k]
if(self.max_kick[0] != 0 and x_kick > self.max_kick[0]):
x_kick = self.max_kick[0]
if(self.max_kick[0] != 0 and x_kick < -1*self.max_kick[0]):
x_kick = -1*self.max_kick[0]
self.kick_x[-1] = x_kick
bunch["xp"] += self.kick_x[0]
self.x_pos = np.roll(self.x_pos,1)
self.kick_x = np.roll(self.kick_x, -1)
for k in range(self.tap_number): if(self.plane[1] == True):
self.y_pos[0] = bunch.mean[2] + np.random.normal(0, self.bpm_error[1])
y_kick = 0
x_kick += self.coef_x[k]*self.x_pos[k] for k in range(self.tap_number[1]):
y_kick += self.coef_y[k]*self.y_pos[k] y_kick += self.coef_y[k]*self.y_pos[k]
if(self.max_kick[1] != 0 and y_kick > self.max_kick[1]):
y_kick = self.max_kick[1]
if(self.max_kick[1] != 0 and y_kick < -1*self.max_kick[1]):
y_kick = -1*self.max_kick[1]
self.kick_y[-1] = y_kick
bunch["yp"] += self.kick_y[0]
self.y_pos = np.roll(self.y_pos,1)
self.kick_y = np.roll(self.kick_y, -1)
if(self.plane[2] == True):
self.tau_pos[0] = bunch.mean[4] + np.random.normal(0, self.bpm_error[2])
tau_kick = 0
self.kick_x[-1] = x_kick for k in range(self.tap_number[2]):
self.kick_y[-1] = y_kick tau_kick += self.coef_tau[k]*self.tau_pos[k]
if(self.max_kick[2] != 0 and tau_kick > self.max_kick[2]):
bunch["xp"] -= self.kick_x[0] tau_kick = self.max_kick[2]
bunch["yp"] -= self.kick_y[0] if(self.max_kick[2] != 0 and tau_kick < -1*self.max_kick[2]):
self.x_pos = np.roll(self.x_pos,1) tau_kick = -1*self.max_kick[2]
self.y_pos = np.roll(self.y_pos,1)
self.kick_x = np.roll(self.kick_x, -1) self.kick_tau[-1] = tau_kick
self.kick_y = np.roll(self.kick_y, -1) bunch["delta"] += self.kick_tau[0]
\ No newline at end of file self.tau_pos = np.roll(self.tau_pos,1)
self.kick_tau = np.roll(self.kick_tau, -1)
\ No newline at end of file
...@@ -15,11 +15,12 @@ class SimpleFB(Element) : ...@@ -15,11 +15,12 @@ class SimpleFB(Element) :
Parameters Parameters
---------- ----------
ring : Synchrotron object ring : Synchrotron object
damping_time : array of shape (3,), optional damping_time : array of shape (3,)
Contains the desired damping time in seconds for each plane in the same Contains the desired damping time in seconds for each plane in the same
order sepcified in plane. order sepcified in plane.
phase_diff : float, optional phase_diff : array of shape (3,)
Phase of the damper kick in degrees. Contains the desired phase in degree for each plane in the same
order sepcified in plane.
plane : bool array of shape (3,), optional plane : bool array of shape (3,), optional
Allow to choose on which plane the feedback is active. State True to Allow to choose on which plane the feedback is active. State True to
turn on the feedback or False otherwise. Element 0 of the array defines turn on the feedback or False otherwise. Element 0 of the array defines
...@@ -27,7 +28,7 @@ class SimpleFB(Element) : ...@@ -27,7 +28,7 @@ class SimpleFB(Element) :
respectively. respectively.
""" """
def __init__(self, ring,phase_diff = 90, damping_time = np.array([2e-3,2e-5,2e-5]),plane = np.ones((3,), dtype= bool)): def __init__(self, ring,phase_diff, damping_time, plane = np.zeros((3,), dtype= bool)):
self.ring = ring self.ring = ring
self.damping_time = damping_time self.damping_time = damping_time
self.phase_diff = phase_diff self.phase_diff = phase_diff
...@@ -45,10 +46,10 @@ class SimpleFB(Element) : ...@@ -45,10 +46,10 @@ class SimpleFB(Element) :
bunch : Bunch or Beam object bunch : Bunch or Beam object
""" """
if(self.plane[0] == True): if(self.plane[0] == True):
bunch["delta"] -= (2*self.ring.T0/self.damping_time[0])*np.sin(self.phase_diff)*bunch.mean[5] bunch["xp"] -= (2*self.ring.T0/self.damping_time[0])*np.sin(self.phase_diff[0])*bunch.mean[1]
if(self.plane[1] == True): if(self.plane[1] == True):
bunch["xp"] -= (2*self.ring.T0/self.damping_time[1])*np.sin(self.phase_diff)*bunch.mean[1] bunch["yp"] -= (2*self.ring.T0/self.damping_time[1])*np.sin(self.phase_diff[1])*bunch.mean[3]
if(self.plane[2] == True): if(self.plane[2] == True):
bunch["yp"] -= (2*self.ring.T0/self.damping_time[2])*np.sin(self.phase_diff)*bunch.mean[3] bunch["delta"] -= (2*self.ring.T0/self.damping_time[2])*np.sin(self.phase_diff[1])*bunch.mean[5]
\ No newline at end of file \ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment