Skip to content
Snippets Groups Projects
Select Git revision
  • 0f66f13055d4c62056b670d3a27bb7e2b46d932d
  • main default protected
  • release_1_1_1
  • release_1_1_0
  • release_1_0_10
  • release_1_0_9
  • release_1_0_8
  • release_1_0_7
  • release_1_0_6
  • release_1_0_5
  • READY_DISCO_PHASE1
  • release_1_0_4
  • release_1_0_3
  • release_1_0_2
  • release_1_0_1
  • release_1_0_0
  • test_0_2
  • release_0_2
  • release_0_1
  • v0
20 results

Makefile.VC

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    rfbucket.py 7.09 KiB
    # -*- coding: utf-8 -*-
    """
    Created on Wed Mar 16 17:54:36 2022
    
    @author: foosang
    """
    
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.optimize import fsolve
    
    class RFBucket:
        """
        Calculate the RF bucket for a given synchronous phase. Valid for both
        sine and cosine RF convention, but limited to the following intervals:
            sine convention : [0,180°],
            cosine convention : [-90°,90°].
            
        Parameters
        ----------
        phi_s : float
            Synchronous phase in [rad].
        convention : {"cos", "sin"}, optional
            RF convention corresponding to input phi_s. Use "cos" or "sin" for a 
            cosine or sine convention, respectively. The default is "cos".
            
        Attributes
        ----------
        phi_s : float
            Synchronous phase in [rad].
        convention : str
            RF convention.
        above_transition : bool
            Depending on phi_s and convention, above_transition is True when the
            system is above the transition energy and is False if the system is 
            below the transition energy.
        """
      
        def __init__(self, phi_s, convention="cos"):
            if convention == "sin":
                if phi_s<0 or phi_s>np.pi:
                    raise ValueError("The synchronous phase is outside the valid range.")
                else:
                    if phi_s>=0 and phi_s<=np.pi/2:
                        self.above_transition = False
                    else:
                        self.above_transition = True
            elif convention == "cos":
                if phi_s<-np.pi/2 or phi_s>np.pi/2:
                    raise ValueError("The synchronous phase is outside the valid range.")
                else:
                    if phi_s>=-np.pi/2 and phi_s<=0:
                        self.above_transition = False
                    else:
                        self.above_transition = True
            else:
                raise ValueError("Invalid RF convention.")
            
            self.phi_s = phi_s
            self.convention = convention
            if self.convention == "cos":
                if self.above_transition is True:
                    self._phi_s = np.pi - (np.pi/2 - phi_s)
                else:
                    self._phi_s = np.pi/2 - phi_s
                    
            elif self.convention == "sin":
                if self.above_transition is True:
                    self._phi_s = np.pi - phi_s
                else:
                    self._phi_s = phi_s
            
        def separatrix(self, phi):
            """
            Calculate the separatrix in longitudinal phase space.
    
            Parameters
            ----------
            phi : array of float
                Phase interval on which the separatrix will be calculated in [rad]
    
            Returns
            -------
            y1 : array
                The positive half of the separatrix in the unit of (dphi/dt)/Omega_s,
                where dphi/dt is phase velocity and Omega_s is synchrotron 
                oscillation frequency.
            y2 : array
                The negative half of the separatrix in the same unit as y1. 
    
            """
            
            y1 = np.sqrt(2/np.cos(self._phi_s)*(np.cos(phi)+phi*np.sin(self._phi_s)-
                    np.cos(np.pi-self._phi_s)-(np.pi-self._phi_s)*np.sin(self._phi_s)))
            
            y2 = -1*y1
            
            return y1, y2
        
        def separatrix_root(self, x):
            """
            Function used for determining the root of the separatrix equation.
    
            """
            
            value =  np.cos(x) + x*np.sin(self._phi_s) - \
                np.cos(np.pi-self._phi_s) - (np.pi-self._phi_s)*np.sin(self._phi_s)
            return value
        
        def plot_separatrix(self, phi, unit="deg"):
            """
            Plot separatrix
    
            Parameters
            ----------
            phi : array of float
                Phase interval on which the separatrix will be calculated in [rad]
            unit : {"deg", "rad"}, optional
                Unit of horizontal axis. Use "deg" for degree or "rad" for radian.
                The default is "deg".
    
            Returns
            -------
            fig : Figure
                Figure object with the separatrix plotted on it.
    
            """
            
            y1, y2 = self.separatrix(phi)
            if self.convention == "cos":
                if self.above_transition is True:
                    phi = phi - np.pi/2             
                else:
                    phi = -1*(phi - np.pi/2)
            
            elif self.convention == "sin":
                if self.above_transition is True:
                    phi = np.pi - phi
            
            fig, ax = plt.subplots()
            
            if unit == "deg":
                phi = phi/np.pi * 180
                xlabel = "$\\phi$ (deg)"
            elif unit == "rad":
                xlabel = "$\\phi$ (rad)"
                
            ax.plot(phi, y1, color="tab:blue")
            ax.plot(phi, y2, color="tab:blue")
            ax.set_xlabel(xlabel)
            ax.set_ylabel('$\\dot{\\phi} / \\Omega_s$')
            
            return fig
                
        def bucket_height(self, ring, Vrf, E0, plane):
            """
            Calculate the height of the RF bucket which corresponds to the maximum
            energy deviation.
    
            Parameters
            ----------
            ring : Synchrotron object
            Vrf : float
                RF voltage in [V].
            E0 : float
                Beam energy in [eV].
            plane : {"x", "y"}
                The considered plane of motion of the beam. Use "x" for the
                horizontal plane or "y" for the vertical plane.
    
            Returns
            -------
            delta_max : float
                Maximum energy deviation.
    
            """
            G = np.sqrt(0.5* abs(2*np.cos(self._phi_s)-(np.pi-2*self._phi_s)*
                                 np.sin(self._phi_s)))
              
            plane_dict = {'x':0, 'y':1}
            index = plane_dict[plane]
            beta = ring.optics.local_beta[index]
            h = ring.h
            eta = ring.eta
            
            delta_max = G * np.sqrt(2*beta**2*E0*Vrf/(np.pi*h*abs(eta))) / E0
            
            return delta_max
        
        def extreme_phases(self):
            """
            Calculate the two extreme phases on the separatrix.
    
            Returns
            -------
            phi_min : float
                The minimum phase in [rad].
            phi_max : float
                The maximum phase in [rad].
    
            """
            
            if self._phi_s > np.pi/2 and self._phi_s <= np.pi:
                x0 = 270 / 180 * np.pi # Iinitial estimate of the root
                phi_min0 = np.pi - self._phi_s
                phi_max0 = fsolve(self.separatrix_root, x0=x0)[0]
                
            elif self._phi_s >= 0 and self._phi_s <= np.pi/2:
                x0 = -90 / 180 * np.pi # Initial estimate of the root
                phi_max0 = np.pi - self._phi_s
                phi_min0 = fsolve(self.separatrix_root, x0=x0)[0]
                
            if self.convention == "cos":
                if self.above_transition is True:
                    phi_min = phi_min0 - np.pi/2
                    phi_max = phi_max0 - np.pi/2                
                else:
                    phi_min = -1*(phi_max0 - np.pi/2)
                    phi_max = -1*(phi_min0 - np.pi/2)
            
            elif self.convention == "sin":
                if self.above_transition is True:
                    phi_min = np.pi - phi_max0
                    phi_max = np.pi - phi_min0
                else:
                    phi_min = phi_min0
                    phi_max = phi_max0
                
            return phi_min, phi_max