S. Albright, H. Damerau, L. Intelisano, A. Lasheen, D. Quartullo, F. Tecker, C. Völlinger, M. Zampetakis
In this hands-on session we will either
Please make your choice according to your interest.
import matplotlib.pyplot as plt
import numpy as np
import scipy.constants as sciCont
(see, e.g. F. Burkart et al., SPS energy upgrade considerations)
In this hands-on session we will design the RF system for a proton synchrotron.
The goal of the session is to calculate the relevant longitudinal parameters.
The notebook is constructed with the following purpose in mind:
Basic parameters of the superconducting Proton Synchrotron (scSPS) at CERN
Parameter | |
---|---|
Energy range | $E_\mathrm{kin} = 13.1\,\mathrm{GeV}\ldots1300\,\mathrm{GeV}$ |
Circumference | $2 \pi R = 6911.5\,\mathrm{m}$ |
Bending radius | $\rho = 741.3\,\mathrm{m}$ |
Transition gamma | $\gamma_\mathrm{tr} = 18$ |
Acceleration time | $4\,\mathrm{s}$ |
Longitudinal emittance per bunch | $\varepsilon_\mathrm{l} = 0.4\,\mathrm{eVs}\ldots0.5\,\mathrm{eVs}$ |
Maximum bucket filling factor | $\varepsilon_\mathrm{l}/A_\mathrm{bucket} = 0.8$ |
Total beam intensity | $N = 1 \cdot 10^{13} \,\mathrm{protons}$ |
Minimum bunch spacing | $25 \,\mathrm{ns}$ |
injectionEnergy = 13.1E9 #eV
extractionEnergy = 1.3E12 #eV
c0 = sciCont.c
e0 = sciCont.e
protonMass = sciCont.physical_constants['proton mass energy equivalent in MeV'][0]*1E6
protonCharge = 1 #e
momentumInjection = np.sqrt((protonMass + injectionEnergy)**2 - protonMass**2)
momentumExtraction = np.sqrt((protonMass + extractionEnergy)**2 - protonMass**2)
print("Momentum at injection: "+str(momentumInjection/1E9) +" GeV/c")
print("Momentum at extraction: "+str(momentumExtraction/1E9) +" GeV/c")
Momentum at injection: 14.00688147696667 GeV/c Momentum at extraction: 1300.9379337344328 GeV/c
circumference = 2*np.pi*1100 #m
bendingRadius = 741.3 #m
accelerationDuration = 4 #s
magneticFieldInjection = momentumInjection / (c0*bendingRadius*protonCharge)
magneticFieldExtraction = momentumExtraction / (c0*bendingRadius*protonCharge)
averageBDot = (magneticFieldExtraction - magneticFieldInjection)/accelerationDuration
print("Average B-Dot: "+str(averageBDot)+" T/s")
Average B-Dot: 1.4477067045348289 T/s
averageEnergyGainPerTurn = 2*np.pi*protonCharge*circumference/(2*np.pi)*bendingRadius*averageBDot
print("Average per turn energy gain: "+str(averageEnergyGainPerTurn/1e6)+" MeV")
Average per turn energy gain: 7.417322108539338 MeV
rfVoltage = 20000E3 #V
stablePhase = np.arcsin(averageEnergyGainPerTurn/rfVoltage)
print("Stable phase angle: "+str(180/np.pi*stablePhase)+" deg")
Stable phase angle: 21.769042161823684 deg
Keep in mind that power is nothing but an change of energy within a given time, the duration of the acceleration.
nProtons = 1E13
energyGainJoules = (extractionEnergy - injectionEnergy)*e0*nProtons
averagePowerToBeam = energyGainJoules / accelerationDuration
print("Average power to beam: "+str(averagePowerToBeam/1E3)+" kW")
Average power to beam: 515.4602775736499 kW
betaInjection = np.sqrt(1 - (protonMass/(protonMass + injectionEnergy))**2)
betaExtraction = np.sqrt(1 - (protonMass/(protonMass + extractionEnergy))**2)
print(f"beta injection: {betaInjection}, beta extraction: {betaExtraction}")
beta injection: 0.9977639262869249, beta extraction: 0.9999997399156176
revolutionFrequencyInjection = c0*betaInjection/circumference
revolutionFrequencyExtraction = c0*betaExtraction/circumference
print("Revolution frequency at injection: "+str(revolutionFrequencyInjection/1E3)+ "kHz")
print("Revolution frequency at extraction: "+str(revolutionFrequencyExtraction/1E3)+ "kHz")
Revolution frequency at injection: 43.278873452276045kHz Revolution frequency at extraction: 43.375853802587116kHz
Again, many solutions are possible. Your beam dynamics and engineering arguments count!
flatTopRFFrequency = 1/25E-9
harmonicNumber = flatTopRFFrequency/revolutionFrequencyExtraction
print("40 MHz/revolution frequency at extraction: "+str(harmonicNumber))
harmonicNumber = int(round(harmonicNumber))
print("Nearest integer harmonic number, h = "+str(harmonicNumber))
print("Present SPS RF systems runs at h = 4620 (corresponding to 200 MHz)")
40 MHz/revolution frequency at extraction: 922.172049501288 Nearest integer harmonic number, h = 922 Present SPS RF systems runs at h = 4620 (corresponding to 200 MHz)
The difference between lowest and highest frequency of an RF cavity is also referred to as tuning range.
injectionRFFrequency = harmonicNumber * revolutionFrequencyInjection
extractionRFFrequency = harmonicNumber * revolutionFrequencyExtraction
frequencySwingFRev = revolutionFrequencyExtraction - revolutionFrequencyInjection
frequencySwingRF = extractionRFFrequency - injectionRFFrequency
print("Revolution frequency swing: "+str(frequencySwingFRev/1E3)+" kHz ("+str(100*frequencySwingFRev/revolutionFrequencyInjection)+" %)")
print("RF frequency swing: "+str(frequencySwingRF/1E3)+" kHz")
Revolution frequency swing: 0.09698035031107428 kHz (0.2240824276953865 %) RF frequency swing: 89.41588298681378 kHz
longitudinalEmittance = 0.45 #eVs
targetFillingFactor = 0.8
targetBucketArea = longitudinalEmittance / targetFillingFactor
print("Target bucket area: "+str(targetBucketArea)+" eVs")
Target bucket area: 0.5625 eVs
import matplotlib.pyplot as plt
timeRange = np.linspace(0, accelerationDuration, 1000)
BFieldRange = np.linspace(magneticFieldInjection, magneticFieldExtraction, len(timeRange))
momentumRange = protonCharge*bendingRadius*BFieldRange*c0
plt.figure()
plt.plot(timeRange, momentumRange/1e9)
plt.xlabel("Cycle Time (s)")
plt.ylabel("Beam Momentum (GeV/c)")
plt.show()
energyRange = np.sqrt(momentumRange**2 + protonMass**2)
plt.figure()
plt.plot(timeRange, (energyRange - protonMass)/1E9)
plt.xlabel("Cycle Time (s)")
plt.ylabel("Beam Kinetic Energy (GeV)")
plt.show()
betaRange = momentumRange/energyRange
gammaRange = 1/np.sqrt(1-betaRange**2)
fRevRange = c0*betaRange/circumference
plt.figure()
plt.plot(timeRange, fRevRange/1e3)
plt.xlabel("Cycle Time (s)")
plt.ylabel("Revolution Frequency (kHz)")
plt.show()
Define an auxiliary function for the bucket area reduction due to the energy loss per turn.
def reduction_ratio(deltaE, voltage):
phis = np.arcsin(deltaE/voltage)
return (1-np.sin(phis))/(1+np.sin(phis))
rfHarmonic = 4620
rfVoltage = 20E6 #V
gamma_T = 18
timeRange = np.linspace(0, accelerationDuration, 1000)
BFieldRange = np.linspace(magneticFieldInjection, magneticFieldExtraction, len(timeRange))
momentumRange = protonCharge*bendingRadius*BFieldRange*c0
energyRange = np.sqrt(momentumRange**2 + protonMass**2)
gammaRange = 1/np.sqrt(1-betaRange**2)
phaseSlipFactor = 1/gamma_T**2 - 1/gammaRange**2
reductionFactor = reduction_ratio(averageEnergyGainPerTurn, rfVoltage)
bucketAreaRange = 4/c0*circumference*np.sqrt((2*energyRange*rfVoltage)/ \
(np.pi**3*rfHarmonic**3*np.abs(phaseSlipFactor))) \
*reductionFactor
plt.figure()
plt.plot(timeRange, bucketAreaRange)
plt.axhline(targetBucketArea, linestyle = '--')
plt.ylim(0,5*targetBucketArea)
plt.xlabel("Cycle Time (s)")
plt.ylabel("Bucket Area (eVs)")
plt.show()
plt.figure()
plt.plot(timeRange, bucketAreaRange)
plt.axhline(targetBucketArea, linestyle = '--')
plt.ylim(0,5*targetBucketArea)
plt.xlim(-0.1,0.5)
plt.xlabel("Cycle Time (s)")
plt.ylabel("Bucket Area (eVs)")
plt.show()
harmonicNumber = rfHarmonic
RUponQ = 100
VInduced = nProtons*e0*RUponQ*harmonicNumber*revolutionFrequencyExtraction*2*np.pi
print("Beam loading induced voltage (flat top): "+str(VInduced/1E3)+" kV")
Beam loading induced voltage (flat top): 201.73454545993272 kV
beamCurrent = e0*nProtons*revolutionFrequencyExtraction
beamLoadingPower = VInduced*beamCurrent
print(f"Beam loading power (flat top): "+str(beamLoadingPower/1E3)+" kW")
Beam loading power (flat top): 14.01969947717716 kW
Parameter | Unit | scSPS | SPS | Tevatron |
---|---|---|---|---|
Beam energy, $E_\mathrm{kin}$ | GeV | 26 to 1300 | 14 to 450 | 120 to 980 |
Circumference, $2\pi R$ | km | 6.9 | 6.9 | 6.3 |
Bending radius, $\rho$ | m | 741.3 | 741.3 | 754.1 |
Acceleration time | s | 4 | 8 | 60 |
RF harmonic, $h$ | 2310 and 4620 | 4620 | 1113 | |
RF frequency, $f_\mathrm{RF}$ | MHz | 200 | 200 | 53.1 |
RF frequency swing, $\Delta f_\mathrm{RF}$ | kHz | 143 | 143 | 1.1 |
RF voltage, $V_\mathrm{RF}$ | MV | 20 | Max. 15 | 1 |
Number of cavities | $10\ldots12$ | $6$ | $2\times 4$ | |
Longiduinal emittance, $\varepsilon_\mathrm{l}$ | eVs | 0.45 | 0.45 | 3 to 4 |
In this hands-on session we will develop the RF system for a hypothetical beam energy and current upgrade for an electron storage ring (Soleil). As a storage ring the particle energy should just be kept constant.
The goal of the session is to calculate the relevant longitudinal parameters.
The notebook is constructed with the following purpose in mind:
Key upgrade parameters:
Basic parameters of the Soleil electron storage ring (parameter table)
Parameter | |
---|---|
Beam energy | $E = 2.75\,\mathrm{GeV}\rightarrow 3.5\,\mathrm{GeV}$ |
Beam current | $I_\mathrm{b} = 500\,\mathrm{mA} \rightarrow 800\,\mathrm{mA}$ |
Circumference | $2 \pi R = 354.097\,\mathrm{m}$ |
Bending radius | $\rho = 5.36\,\mathrm{m}$ |
Phase slip factor and mometum compaction | $\eta = 1/\gamma^2_\mathrm{tr} - 1/\gamma^2 \simeq 1/\gamma^2_\mathrm{tr} = \alpha = 4.16 \cdot 10^{-4}$ |
Harmonic of RF system | $h = 416$ |
RF frequency | $f_\mathrm{RF} = 352.2\,\mathrm{MHz}$ |
electronMass = sciCont.physical_constants['electron mass energy equivalent in MeV'][0]*1E6 #eV
electronCharge = 1 #e
bendingRadius = 5.36 #m
epsilon0 = sciCont.epsilon_0 #F/m
c0 = sciCont.c #m/s
e0 = sciCont.e #C
beamEnergyBefore = 2.75E9 #eV
energyLossPerTurnBefore = e0**2*(beamEnergyBefore/electronMass)**4/(3*epsilon0*bendingRadius)/e0
print("Energy loss per turn before upgrade: "+str(energyLossPerTurnBefore/1E3)+" keV")
Energy loss per turn before upgrade: 943.9008208467744 keV
beamEnergyAfter = 3.5E9 #eV
energyLossPerTurnAfter = e0**2*(beamEnergyAfter/electronMass)**4/(3*epsilon0*bendingRadius)/e0
print("Energy loss per turn after upgrade: "+str(energyLossPerTurnAfter/1E3)+" keV")
Energy loss per turn after upgrade: 2476.6678460248404 keV
beamEnergyRange = np.linspace(beamEnergyBefore, beamEnergyAfter, 100)
energyLossPerTurn = e0**2*(beamEnergyRange/electronMass)**4/(3*epsilon0*bendingRadius)/e0
plt.figure()
plt.plot(beamEnergyRange/1E9, energyLossPerTurn/1E3)
plt.xlabel("$E$ [GeV]")
plt.ylabel("$\Delta E/$turn [keV]")
plt.show()
#Electrons therefore beta = 1
circumference = 354.097
revolutionFrequency = c0/circumference
print("Revolution frequency: "+str(revolutionFrequency/1E3)+" kHz")
Revolution frequency: 846.63936153088 kHz
beamCurrent = 500E-3 #A
nElectrons = (beamCurrent/e0)/revolutionFrequency
radiationPowerBefore = energyLossPerTurnBefore*revolutionFrequency*nElectrons*e0
print(f"Average power to beam before upgrade: "+str(radiationPowerBefore/1E3)+" kW")
Average power to beam before upgrade: 471.95041042338727 kW
beamCurrent = 800E-3 #A
nElectrons = (beamCurrent/e0)/revolutionFrequency
radiationPowerAfter = energyLossPerTurnAfter*revolutionFrequency*nElectrons*e0
print(f"Average power to beam before upgrade: "+str(radiationPowerAfter/1E3)+" kW")
Average power to beam before upgrade: 1981.3342768198727 kW
beamEnergyRange = np.linspace(beamEnergyBefore, beamEnergyAfter, 100)
energyLossPerTurn = e0**2*(beamEnergyRange/electronMass)**4/(3*epsilon0*bendingRadius)/e0
beamCurrent = 500E-3
radiationPowerBeforeCurrent = energyLossPerTurn*revolutionFrequency*beamCurrent/revolutionFrequency
beamCurrent = 800E-3
radiationPowerAfterCurrent = energyLossPerTurn*revolutionFrequency*beamCurrent/revolutionFrequency
plt.figure()
plt.plot(beamEnergyRange/1E9, radiationPowerBeforeCurrent/1E3)
plt.plot(beamEnergyRange/1E9, radiationPowerAfterCurrent/1E3)
plt.xlabel("$E$ [GeV]")
plt.ylabel("$P$ [kW]")
plt.show()
Many solutions are possible. Your beam dynamics and engineering arguments count!
minimumFrequency = 1/25E-9
minimumHarmonic = minimumFrequency/revolutionFrequency
print("Minimum frequency: "+str(minimumFrequency/1E6)+" MHz")
print("40 MHz/revolution frequency at extraction: "+str(minimumHarmonic))
Minimum frequency: 40.0 MHz 40 MHz/revolution frequency at extraction: 47.24561816695202
chosenHarmonic = 2*416
chosenFrequency = chosenHarmonic*revolutionFrequency
bucketSpacing = 1/chosenFrequency
print("Bucket spacing: "+str(bucketSpacing*1E9)+" ns")
Bucket spacing: 1.419639968958895 ns
Again, there are many correct answers. No calculation is required for this part and everything depends on the arguments.
RUponQ = 44
VInduced = nElectrons*e0*RUponQ*chosenHarmonic*revolutionFrequency*2*np.pi
print("Beam loading induced voltage: "+str(VInduced/1E3)+" kV")
Beam loading induced voltage: 184.01187818018423 kV
beamCurrent = 800E-3 #A
beamLoadingPower = VInduced*beamCurrent
print(f"Beam loading power (flat top): "+str(beamLoadingPower/1E3)+" kW")
Beam loading power (flat top): 147.2095025441474 kW
momentumRatioAcceptance = 0.5E-2
beamEnergy = 2.75E9 #eV
designMomentum = np.sqrt(beamEnergy**2 - electronMass**2)
lossMomentum = designMomentum*(1 - momentumRatioAcceptance)
lossEnergy = np.sqrt(lossMomentum**2 + electronMass**2)
energyLossPerTurn = e0**2*(beamEnergy/electronMass)**4/(3*epsilon0*bendingRadius)/e0
print("Momentum at which particles are lost: "+str(lossMomentum/1E9)+" GeV/c")
print("Energy at which particles are lost: "+str(lossEnergy/1E9)+" GeV")
nTurnsUntilLost = (beamEnergy - lossEnergy)/energyLossPerTurn
lifeTime = nTurnsUntilLost/revolutionFrequency
print("Life time (before upgrade): "+str(lifeTime*1E6)+" us ("+str(nTurnsUntilLost)+" turns)")
Momentum at which particles are lost: 2.7362499527610313 GeV/c Energy at which particles are lost: 2.7362500004759562 GeV Life time (before upgrade): 17.205918592325006 us (14.567207931558341 turns)
momentumRatioAcceptance = 0.5E-2
beamEnergy = 3.5E9 #eV
designMomentum = np.sqrt(beamEnergy**2 - electronMass**2)
lossMomentum = designMomentum*(1 - momentumRatioAcceptance)
lossEnergy = np.sqrt(lossMomentum**2 + electronMass**2)
energyLossPerTurn = e0**2*(beamEnergy/electronMass)**4/(3*epsilon0*bendingRadius)/e0
print("Momentum at which particles are lost: "+str(lossMomentum/1E9)+" GeV/c")
print("Energy at which particles are lost: "+str(lossEnergy/1E9)+" GeV")
nTurnsUntilLost = (beamEnergy - lossEnergy)/energyLossPerTurn
lifeTime = nTurnsUntilLost/revolutionFrequency
print("Life time (after upgrade): "+str(lifeTime*1E6)+" us ("+str(nTurnsUntilLost)+" turns)")
Momentum at which particles are lost: 3.4824999628836673 GeV/c Energy at which particles are lost: 3.4825000003739657 GeV Life time (after upgrade): 8.34587388838258 us (7.0659453402774695 turns)
beamEnergyInitial = 3.5E9
nTurns = 100
turnRange = np.array(range(nTurns))
energyTurnByTurn = np.zeros(nTurns)
energyLossPerTurnInitial = e0**2*(beamEnergyInitial/electronMass)**4/(3*epsilon0*bendingRadius)/e0
beamEnergy = beamEnergyInitial
for turn in turnRange:
energyTurnByTurn[turn] = beamEnergy
energyLossPerTurn = e0**2*(beamEnergy/electronMass)**4/(3*epsilon0*bendingRadius)/e0
beamEnergy = beamEnergy - energyLossPerTurn
plt.figure()
plt.plot(turnRange, energyTurnByTurn/1E9)
plt.plot(turnRange, (beamEnergyInitial - turnRange*energyLossPerTurnInitial)/1E9)
plt.xlabel("Turn number")
plt.ylabel("Beam energy [GeV]")
plt.show()
revolutionTime = 1/revolutionFrequency
momentumCompationFactor = 4.16E-4
dampingIntegralD = momentumCompationFactor * circumference/(2*np.pi*bendingRadius)
print("D = "+str(dampingIntegralD)+" (well below one, 2+D can be approximate to 2)")
dampingTimeBefore = beamEnergyBefore*revolutionTime/energyLossPerTurnBefore #not radiationPowerBefore (which is for the whole beam)
dampingTimeAfter = beamEnergyAfter*revolutionTime/energyLossPerTurnAfter #not radiationPowerAfter
print("SR Damping time before: "+str(dampingTimeBefore*1E3)+" ms")
print("SR Damping time after: "+str(dampingTimeAfter*1E3)+" ms")
D = 0.004373920850699349 (well below one, 2+D can be approximate to 2) SR Damping time before: 3.441183837581627 ms SR Damping time after: 1.6691748133458983 ms
Parameter | Unit | ESRF | Soleil | Soleil (CAS upgrade) |
---|---|---|---|---|
Beam energy, $E$ | GeV | 6 | 2.75 | 3.5 |
Beam current, $I_\mathrm{beam}$ | mA | 200 | 500 | 800 |
Circumference, $2 \pi R$ | m | 844 | 354 | 354 |
Bending radius, $\rho$ | m | 23.37 | 5.36 | 5.36 |
RF harmonic, $h$ | 992 | 416 | 416 and 832 | |
RF frequency, $f_\mathrm{RF}$ | MHz | 352 | 352 | 352 and 704 |
RF voltage, $V_\mathrm{RF}$ | MV | 6.5 | 3 | 3 + >1.5 |
Number of cavities | 14 | 4 | 4 + ~4 |