Files
OM/src/sls2om/OMFacility.py
T
Benjamin Labrecque 4e94b2a9a3
Publish release to Gitea Packages / run (push) Successful in 10s
chore: create a 0.3.0 release to test cicd
2026-07-24 14:46:28 +02:00

3080 lines
103 KiB
Python

from copy import deepcopy, copy
from math import *
from random import gauss, seed
from os import system
import numpy as np
class Facility:
def __init__(self,LayoutFile):
self.D2R=pi/180.0
self.LayoutFile=LayoutFile
self.Parser()
self.TypeComp()
self.CommonComp()
self.FMapComp()
#exit()
self.BeamEnergy=2.7e9
self.Ring=None
self.Ring0=None # A copy of self.Ring without cycling
self.OverlapC={}
# When field map(s) is(are) included and the layout is used to generate
# a lattice for tracy-null (statements),
# this switch should be turned on (directly)
self.TracyFM=False
self.Description=''
seed(42)
def LayoutBL(self,expand='full',Kingdom='',cycle=0):
# Beamline or ring in general
layout=self.flatten(expand)
self.individuateBL(layout,Kingdom,cycle)
def Layout(self,expand='full',cycle=0):
layout=self.flatten(expand)
self.individuateS2(layout,cycle)
def CustomizedLayout(self,Pattern):
# A cutomized layout is needed to keep the common line structure in output.
# This method could be implemented in a better way..
TempSeq=[]
TempRing=[]
for i in range(1,13):
k='ARS'+str(i).zfill(2)
for e in self.Sector[k]:
TempRing.append(e)
TempSeq.append(deepcopy(TempRing))
alldone=0
while alldone==0:
alldone=1
TempRing=[]
for e in TempSeq[0]:
if e in self.Common.keys():
TempRing.append(deepcopy(self.Common[e][1]))
else:
TempRing.append(e)
TempSeq.insert(0,deepcopy(TempRing))
TempRing=[]
for e in TempSeq[0]:
if type(e)==list:
for le in e:
#if le in self.Overlap.keys():
# for oe in self.Overlap[le]:
# le=le+'|'+oe
TempRing.append(le)
else:
#if e in self.Overlap.keys():
# for oe in self.Overlap[e]:
# e=e+'|'+oe
TempRing.append(e)
TempSeq.insert(0,deepcopy(TempRing))
for e in TempSeq[0]:
if e in self.Common.keys():
alldone=0
#print (TempSeq[1])
#exit()
#nw=0
#n=0
#for e in TempSeq[0]:
# if e=='QAW|OQW':
# nw=nw+1
# if e=='QA|OQ':
# n=n+1
#print(nw,n)
#exit()
TypeV={}
Drift=[]
# Applying Pattern
Nqp=0
for i in range(0,len(TempSeq[1])):
e=TempSeq[1][i]
if type(e)==list:
for j in range(0,len(e)):
le=e[j]
if type(le)==str:
if '|' in le: # This is a bit crazy...
les=le.split('|')
fe=[]
ft=[]
for es in les:
if self.Type[es]['SN'] in Pattern.keys():
fe.append(Pattern[self.Type[es]['SN']][0])
ft.append(deepcopy(self.Type[es]))
ft[-1]['ORIGINAL']=es
Pattern[self.Type[es]['SN']].pop(0)
else:
fe.append(es)
ft.append(deepcopy(self.Type[es]))
fef='|'.join(fe)
TempSeq[1][i][j]=fef
TypeV[fef]={}
TypeV[fef]['SN']='Overlap'
TypeV[fef]['Type']=ft
else:
if self.Type[le]['SN'] in Pattern.keys():
TempSeq[1][i][j]=Pattern[self.Type[le]['SN']][0]
#TypeV[self.Type[le]['SN'][0]]=deepcopy(self.Type[le])
#TypeV[self.Type[le]['SN'][0]]['ORIGINAL']=le
TypeV[Pattern[self.Type[le]['SN']][0]]=deepcopy(self.Type[le])
TypeV[Pattern[self.Type[le]['SN']][0]]['ORIGINAL']=le
Pattern[self.Type[le]['SN']].pop(0)
else:
TypeV[le]=deepcopy(self.Type[le])
elif type(le)==float or type(le)==int:
if le not in Drift:
Drift.append(le)
elif type(e)==str:
if '|' in e: # This is a bit crazy...
es=e.split('|')
fe=[]
ft=[]
for s in es:
if self.Type[s]['SN'] in Pattern.keys():
fe.append(Pattern[self.Type[s]['SN']][0])
ft.append(deepcopy(self.Type[s]))
ft[-1]['ORIGINAL']=s
Pattern[self.Type[s]['SN']].pop(0)
else:
fe.append(s)
ft.append(deepcopy(self.Type[s]))
fef='|'.join(fe)
TempSeq[1][i]=fef
TypeV[fef]={}
TypeV[fef]['SN']='Overlap'
TypeV[fef]['Type']=ft
else:
if self.Type[e]['SN'] in Pattern.keys():
TempSeq[1][i]=Pattern[self.Type[e]['SN']][0]
TypeV[Pattern[self.Type[e]['SN']][0]]=deepcopy(self.Type[e])
TypeV[Pattern[self.Type[e]['SN']][0]]['ORIGINAL']=e
Pattern[self.Type[e]['SN']].pop(0)
else:
TypeV[e]=deepcopy(self.Type[e])
elif type(e)==float or type(e)==int:
if e not in Drift:
Drift.append(e)
#print ('=============')
#for k in sorted(list(TypeV.keys())):
# print (k,TypeV[k])
#exit()
#print (TempSeq[1])
#exit()
TempSeq.pop(0)
CommonVinv={}
Cmissed=[]
CC={}
while len(TempSeq)>2:
for i in range(0,len(TempSeq[0])):
e0=TempSeq[0][i]
e1=TempSeq[1][i]
if type(e0)==float or type(e0)==float:
pass
elif type(e0)==str:
#if e0 in TypeV.keys():
# pass
#elif e0 in self.Type.keys():
# pass
# #TypeV[e0]=self.Type[e0] # All types are already stored?
if e0!=e1:
TempSeq[1][i]=e0
elif e0!=self.Common[e1][1]:
if tuple(e0) in CommonVinv.keys():
pass
else:
if e1 not in CC.keys():
CC[e1]=0
CommonVinv[tuple(e0)]=e1
else:
CC[e1]=CC[e1]+1
CommonVinv[tuple(e0)]=e1+'_V'+str(CC[e1])
TempSeq[1][i]=CommonVinv[tuple(e0)]
else:
if tuple(e0) in CommonVinv.keys():
if CommonVinv[tuple(e0)]!=e1:
Cmissed.append(e1)
CommonVinv[tuple(e0)]=e1
TempSeq.pop(0)
#print (TempSeq[0])
#exit()
#TempSeqT=deepcopy(TempSeq[1])
ioff=0
for i in range(0,len(TempSeq[1])):
e1=TempSeq[1][i]
if type(e1)==list:
for j in range(0,len(e1)):
e0=TempSeq[0][i+ioff+j]
if e0!=e1[j]:
TempSeq[1][i][j]=e0
ioff=ioff+len(e1)-1
else:
e0=TempSeq[0][i+ioff]
if e0!=e1:
TempSeq[1][i]=e0
TempSeq.pop(0)
for i in range(0,len(TempSeq[0])):
e0=TempSeq[0][i]
e1=TempSeq[1][i]
if type(e0)==float or type(e0)==float:
pass
elif type(e0)==str:
#if e0 in TypeV.keys():
# pass
#elif e0 in self.Type.keys():
# pass
# #TypeV[e0]=self.Type[e0]
if e0!=e1:
TempSeq[1][i]=e0
elif e0!=self.Common[e1][1]:
if tuple(e0) in CommonVinv.keys():
pass
else:
if e1 not in CC.keys():
CC[e1]=0
CommonVinv[tuple(e0)]=e1
else:
CC[e1]=CC[e1]+1
CommonVinv[tuple(e0)]=e1+'_V'+str(CC[e1])
TempSeq[1][i]=CommonVinv[tuple(e0)]
else:
if tuple(e0) in CommonVinv.keys():
if CommonVinv[tuple(e0)]!=e1:
Cmissed.append(e1)
if tuple(e0) not in CommonVinv.keys():
CommonVinv[tuple(e0)]=e1
#for k in CommonVinv.values():
# if 'SS' in k:
# print (k)
#exit()
CommonV= {y:list(x) for x,y in CommonVinv.items()}
for k in Cmissed:
CommonV[k]=self.Common[k][1]
#print (TempSeq[1])
#print (self.Sector)
Ne=0
SectorV=[]
for i in range(1,13):
k='ARS'+str(i).zfill(2)
l=len(self.Sector[k])
SectorV.append(TempSeq[1][Ne:Ne+l])
Ne=Ne+l
#print (SectorV)
#for k in sorted(list(TypeV.keys())):
# print (k,TypeV[k])
#exit()
#print (sorted(Drift))
return TypeV,CommonV,SectorV,Drift
#print (Cmissed)
def Parser(self):
# Variables have to be self.xx to allow user-defined constants in Layout file...
self.Type={}
self.Common={} # CommonLine
self.Sector={}
self.Overlap={} # This will be removed...
self.Mode=None
try:
self.fin=open(self.LayoutFile,'r')
except:
print ('File does not exist?')
exit()
self.ftmp=[]
# Removing comments
for self.line in self.fin:
self.line=self.line
if self.line[0]=='!':
self.line=''
elif '!' in self.line:
self.line=self.line.split('!')[0]
else:
self.line=self.line.replace('\n','')
if self.line: self.ftmp.append(self.line)
self.fin.close()
self.AS=''
self.cont=0
for self.line in self.ftmp:
if '\\' in self.line:
if self.cont:
self.AS=self.AS+self.line.replace('\\','')
else:
self.AS=self.AS+'V^O^V'+self.line.replace('\\','')
self.cont=1
elif self.cont:
self.AS=self.AS+self.line.replace('\\','')
self.cont=0
else:
self.cont=0
if self.AS:
self.AS=self.AS+'V^O^V'+self.line # Hope this special pattern does not appear in Layout file...
else:
self.AS=self.line
self.AS=self.AS.upper().replace('CLASS','CLASS:').replace(' ','').replace('\t','')
self.AS=self.AS.upper().replace('VERSION','VERSION:').replace(' ','').replace('\t','')
self.fclean=self.AS.split('V^O^V')
self.SNtmp={}
for self.line in self.fclean:
try:
if 'CLASS' in self.line.lower().split(':')[0].upper():
self.Mode=self.line.split(':')[1].upper()
self.line=''
#print (Mode,len(Mode))
except:
pass
if 'VERSION' in self.line:
self.Version=self.line.replace('VERSION:','')
if self.Mode=='CONSTANT' and self.line:
try:
exec(self.line.upper())
#exec(self.line.split('=')[0].upper()+'='+self.line.split('=')[1])
except:
print ('Something wrong in Constant')
exit()
elif self.Mode=='TYPE' and self.line:
self.Ttmp={}
try:
if ':' in self.line:
self.sline=self.line.split(':')
self.TN=self.sline[0].upper() # Type
self.ssline=self.sline[1].split(',')
self.SN=self.ssline[0].upper() # Short name
self.Ttmp['SN']=self.SN
if self.SN not in self.SNtmp.keys():
self.SNtmp[self.SN]=[self.TN]
else:
self.SNtmp[self.SN].append(self.TN)
# From on-line model point of view, no need to distinguish'I' and 'O' types
if self.SNtmp[self.SN][-1][-1]=='I' or self.SNtmp[self.SN][-1][-1]=='O':
self.SNtmp[self.SN][-1]=self.SNtmp[self.SN][-1][:-1]
#print (self.P5)
for self.i in range(1,len(self.ssline)):
self.p=self.ssline[self.i]
self.p=self.p.split('=')
if self.p[0]=='ANG':
self.p[0]='ANGLE'
#print ('====',self.p[1])
if self.p[0]=='OVERLAP':
exec('self.v = '+self.p[1].replace('/',','))
self.Ttmp['FM'+self.p[0]]=self.v
elif '[' in self.p[1]:
self.sp=self.p[1].replace('[','').replace(']','').split('|')
#print ('++++',self.sp)
self.V=[]
for self.d in self.sp:
try:
exec('self.v = '+self.d)
self.V.append(self.v)
except:
print ('Something wrong!! 1')
exit()
self.Ttmp[self.p[0]]=self.V
else:
try:
exec('self.v = '+self.p[1])
self.Ttmp[self.p[0]]=self.v
except:
#print (self.p[1])
print ('Something wrong!! 2')
exit()
self.Type[self.TN]=deepcopy(self.Ttmp)
except:
print ('Something wrong in Type',self.line)
exit()
elif self.Mode=='COMMONLINE' and self.line:
try:
if ':' in self.line:
self.sline=self.line.split(':')
self.Unit=self.sline[0].replace(' ','').upper() # Unit
self.ssline=self.sline[1].split(',')
self.SN=self.ssline[0].replace(' ','').upper() # Short name
self.ssline=self.ssline[1:]
self.Ctmp=[]
for self.i in range(0,len(self.ssline)):
self.p=self.ssline[self.i].replace('[','').replace(']','').replace(' ','')
try:
exec('self.d = '+self.p)
self.Ctmp.append(self.d) # Drift described with constant
except:
#if '|' in self.ssline[self.i]:
# self.sp=self.p.split('|')
# self.Overlap[self.sp[0]]=self.sp[1:]
# self.Ctmp.append(self.sp[0])
#else:
# self.Ctmp.append(self.p)
self.Ctmp.append(self.p)
self.Common[self.Unit]=[self.SN,deepcopy(self.Ctmp)]
except:
print ('Something wrong in CommonLine')
exit()
elif self.Mode=='SEQUENCE' and self.line:
try:
self.Stmp=[]
if ':' in self.line:
self.sline=self.line.split(':')
self.SN=self.sline[0].upper() # Sector
#print ('++',self.sline[1])
self.ssline=self.sline[1].replace('[','').replace(']','').split(',')
for self.p in self.ssline:
try:
exec('self.d = '+self.p)
self.Stmp.append(self.d) # Drift described with constant
except:
#if '|' in self.p:
# self.sp=self.p.split('|')
# self.Overlap[self.sp[0]]=self.sp[1:]
# self.Stmp.append(self.sp[0])
#else:
# self.Stmp.append(self.p)
self.Stmp.append(self.p)
self.Sector[self.SN]=self.Stmp
except:
print ('Something wrong in Sequence')
exit()
#print (self.Sector)
self.Type['CYCLE']={'SN': 'GCYC', 'L': 0, 'RL': 0}
for self.p in self.Type.keys():
if 'L' not in self.Type[self.p].keys():
self.Type[self.p]['L']=0
if 'RL' not in self.Type[self.p].keys():
self.Type[self.p]['RL']=0
if 'ANGLE' in self.Type[self.p].keys(): # For BS, both E1 and E2 exist and this block is not relevant.
if ('E1' in self.Type[self.p].keys()) and ('E2' not in self.Type[self.p].keys()):
self.Type[self.p]['E2']=self.Type[self.p]['ANGLE']-self.Type[self.p]['E1']
elif ('E2' in self.Type[self.p].keys()) and ('E1' not in self.Type[self.p].keys()):
self.Type[self.p]['E2']=self.Type[self.p]['ANGLE']-self.Type[self.p]['E2']
elif ('E1' not in self.Type[self.p].keys()) and ('E2' not in self.Type[self.p].keys()):
self.Type[self.p]['E1']=self.Type[self.p]['ANGLE']/2
self.Type[self.p]['E2']=self.Type[self.p]['ANGLE']/2
self.Ttmp={}
for self.p in self.Type.keys():
#if ('E1' in self.Type[self.p].keys()) and ('E2' in self.Type[self.p].keys()):
if self.p=='VB': # Only -VB is needed. # VB no longer exists. It is VBI or VBO
self.d='-'+self.p # Reversed element, only dipoles
self.Ttmp[self.d]=deepcopy(self.Type[self.p])
self.Ttmp[self.d]['E1'],self.Ttmp[self.d]['E2']=self.Ttmp[self.d]['E2'],self.Ttmp[self.d]['E1']
#if type(self.Ttmp[self.d]['E1'])==list:
# self.Ttmp[self.d]['E1'].reverse()
# self.Ttmp[self.d]['E2'].reverse()
# self.Ttmp[self.d]['L'].reverse()
# self.Ttmp[self.d]['ANGLE'].reverse()
self.Type.update(deepcopy(self.Ttmp))
#print ('***************')
#print (self.Ttmp)
#print (self.Type)
#exit()
self.SN=self.SNtmp
for k in self.SN:
self.SN[k]=list(set(self.SN[k]))
for self.v in ['self.fin','self.ftmp','self.fclean','self.AS','self.cont','self.line','self.sline','self.ssline','self.Mode','self.SNtmp','self.i','self.v','self.p','self.sp','self.d','self.Ttmp','self.Ctmp','self.Unit']:
try:
exec('del '+self.v)
except:
pass
del self.v
def flatten(self,expand='full'):
# expand='full' - All the CommonLine will be flattened
# expand='common' - The unit defined in CommonLine will remain unflattened if short name is given (not None)
def flattenMixList(l):
# No built-in to flatten Mixed type list...
for i in l:
if isinstance(i, (str, bytes)):
yield i
else:
try:
for ii in flattenMixList(i):
yield ii
except TypeError:
yield i
def unnest(N):
nested=0
for i in range(0,len(N)):
e=N[i]
#print ('mmm',e,expand)
#print (N)
if e in self.Common.keys() and expand=='full':
N[i]=self.Common[e][1]
nested=1
elif e in self.Common.keys():
if self.Common[e][0]=='NONE':
N[i]=self.Common[e][1]
nested=1
if nested:
N=list(flattenMixList(N))
N=unnest(N)
return N
if expand!='full' and expand!='unit':
PL=deepcopy(self.Common[expand][1])
expand='full'
PL=unnest(PL)
return PL
if 'ARS12' in self.Sector.keys():
Sec=[]
for i in range(1,13):
k='ARS'+str(i).zfill(2)
PS=deepcopy(self.Sector[k])
#print ('lll',PS)
#exit()
Sec.append(unnest(PS))
if PS[0]!='GINI_SEC':
print ('Sector '+str(i)+' does not start with GINI_SEC')
exit()
Ring=list(flattenMixList(Sec))
else:
#if expand=='full':
# PS=deepcopy(self.Sector)
# Ring=unnest(list(PS.values())[0])
#else:
# Ring=list(self.Sector.values())[0]
PS=deepcopy(self.Sector)
Ring=unnest(list(PS.values())[0])
return Ring
def TypeComp(self):
# Some computation for sliced element
# This is not done for MMAP (field map), which is experimental
# and not intended to be linked with Holy list
for k in self.Type.keys():
if ('ANGLE' in self.Type[k].keys()) and (type(self.Type[k]['ANGLE'])==list):
dx=0
dy=0
ang=0
l=0
#print ('tgsbhstbhs',k,type(self.Type[k]['ANGLE']),self.Type[k])
for i in range(0,len(self.Type[k]['ANGLE'])):
rho=self.Type[k]['L'][i]/(self.D2R*self.Type[k]['ANGLE'][i])
N=2*rho*sin(self.D2R*self.Type[k]['ANGLE'][i]/2)
dx=dx+N*cos(self.D2R*ang+self.D2R*self.Type[k]['ANGLE'][i]/2)
dy=dy+N*sin(self.D2R*ang+self.D2R*self.Type[k]['ANGLE'][i]/2)
l=l+self.Type[k]['L'][i]
ang=ang+self.Type[k]['ANGLE'][i]
if i+1==len(self.Type[k]['ANGLE'])/2: # Even number slicing is assumed...
mdx=dx
mdy=dy
self.Type[k]['TL']=l # Total length
self.Type[k]['TANGLE']=ang # Total angle
self.Type[k]['DX']=dx # Total DX
self.Type[k]['DY']=dy # Total DY
self.Type[k]['MDX']=mdx # DX in the middle
self.Type[k]['MDY']=mdy # DY in the middle
#self.Type[k]['RL']=0
def CommonComp(self):
# Some computation for common line
cont=1
while cont:
cont=0
for k in self.Common.keys():
dx=0
dy=0
ang=0
l=0
d=1
if k in self.Common.keys() and len(self.Common[k])!=6:
d=0
cc=0
for ki in self.Common[k][1]:
if ki in self.Common.keys():
if len(self.Common[ki])==6:
l=l+self.Common[ki][2]
N=sqrt(self.Common[ki][4]**2+self.Common[ki][5]**2)
dx=dx+N*cos(self.D2R*ang+self.D2R*self.Common[ki][3]/2)
dy=dy+N*sin(self.D2R*ang+self.D2R*self.Common[ki][3]/2)
ang=ang+self.Common[ki][3]
else:
cc=1 # Common in common is not yet done
elif type(ki)==float or type(ki)==float:
l=l+ki
dx=dx+ki*cos(self.D2R*ang)
dy=dy+ki*sin(self.D2R*ang)
else:
if '|' in ki:
ki=ki.split('|')[0]
#print (ki,k,self.Common[k][1])
if 'ANGLE' in self.Type[ki].keys():
if type(self.Type[ki]['ANGLE'])==list:
l=l+self.Type[ki]['TL']
N=sqrt(self.Type[ki]['DX']**2+self.Type[ki]['DY']**2)
dx=dx+N*cos(self.D2R*ang+self.D2R*self.Type[ki]['TANGLE']/2)
dy=dy+N*sin(self.D2R*ang+self.D2R*self.Type[ki]['TANGLE']/2)
ang=ang+self.Type[ki]['TANGLE']
elif self.Type[ki]['ANGLE']:
rho=self.Type[ki]['L']/(self.D2R*self.Type[ki]['ANGLE'])
N=2*rho*sin(self.D2R*self.Type[ki]['ANGLE']/2)
dx=dx+N*cos(self.D2R*ang+self.D2R*self.Type[ki]['ANGLE']/2)
dy=dy+N*sin(self.D2R*ang+self.D2R*self.Type[ki]['ANGLE']/2)
l=l+self.Type[ki]['L']
ang=ang+self.Type[ki]['ANGLE']
else:
#if 'RL' not in self.Type[ki].keys():
# self.Type[ki]['RL']=0
#if 'L' not in self.Type[ki].keys():
# self.Type[ki]['L']=0
dl=self.Type[ki]['L']+self.Type[ki]['RL']*2
l=l+dl
dx=dx+dl*cos(self.D2R*ang)
dy=dy+dl*sin(self.D2R*ang)
else:
d=1
if d==0 and cc==0: # self.Common[NameOfCommonLine]=[SN,[List of elems],L,ANGLE,DX,DY]
self.Common[k].append(l)
self.Common[k].append(ang)
self.Common[k].append(dx)
self.Common[k].append(dy)
cont=1
#if d==1:
# cont=0
def FMapComp(self):
# Some computaion for MMAP
# Implementation assuming order=(6,6) always
# In Master layout file, L is total length,
# which is actually not used here!.
# L is now replaced by a list
# Total length is then stored as TL
for k in self.Type.keys():
if self.Type[k]['SN']=='MMAP':
self.Type[k]['L']=[]
self.Type[k]['TL']=0
self.Type[k]['STRENGTH']=[]
fin=open(self.Type[k]['FILENAME'].lower(),'r')
for line in fin:
sline=line.split()
if len(sline)>10:
l=float(sline[1])
self.Type[k]['TL']=self.Type[k]['TL']+l
self.Type[k]['L'].append(l)
self.Type[k]['STRENGTH'].append([float(sline[4]),
float(sline[6]),
float(sline[8]),
float(sline[10]),
float(sline[12]),
float(sline[14]),
float(sline[16]),
float(sline[18]),
float(sline[22]),
float(sline[24]),
float(sline[28]),
float(sline[34])])
fin.close()
#print (self.Type[k])
#exit()
def individuateBL(self,Ring,Kingdom,cycle=0):
# This method is for beam line or ring in general
del self.Ring
del self.OverlapC
self.OverlapC={}
RingClass=[]
# These short names are difined for SLS2 naming convention
# but used here since they are rather general
Gmark=['GMRK','GSRC']
# 'DSCR', screen monitor. Experimental, 08.12.2020
# DICT added 07.11.2022
Diag=['DBPM','DSCR','DICT']
Dmag=['MBEN','MBCF','MBSC']
Qmag=['MQUA','MQCO']
Sext=['MSXT']
Oct=['MOCT']
HKIK=['MCOX','MKIK','MSEP']
VKIK=['MCOY']
FM=['MMAP'] # Field map, experimental
RFcav=['RCAV','R3HC']
Coll=['VCOL'] # Collimator, 21.11.2023
CommonLineSN=[]
for k in self.Common.keys():
if self.Common[k][0]!='NONE':
CommonLineSN.append(self.Common[k][0])
s=0.0 # Longitudinal location
gl=0.0
gi=-1 # GIRDER number starts from zero. (Can be from 1, to be determined)
ang= 0 #total angle from the beginning (exclusive of its bending angle)
GL=[]
Prop=[]
# First survey
ndri=0
drir=[]
drirn=[]
for e in range(0,len(Ring)):
ele=Ring[e]
if Ring[e] in self.Common.keys():
p={}
p['L']=self.Common[Ring[e]][2]
p['ANGLE']=self.Common[Ring[e]][3]
p['TL']=self.Common[Ring[e]][2]
p['TANGLE']=self.Common[Ring[e]][3]
p['DX']=self.Common[Ring[e]][4]
p['DY']=self.Common[Ring[e]][5]
p['RL']=0
p['SN']=self.Common[Ring[e]][0]
p['TYPE']=Ring[e]
Prop.append(deepcopy(p))
elif type(Ring[e])==float or type(Ring[e])==int:
if Ring[e] not in drir:
drir.append(Ring[e])
drirn.append(ndri)
ndri=ndri+1
ndrip=ndri
else:
ndrip=drir.index(Ring[e])
ele='D'+str(ndri).zfill(4)
p={}
p['L']=Ring[e]
p['RL']=0
p['SN']='DRIF'
p['TYPE']='DRIFT'
p['DNAME']=ele
Prop.append(deepcopy(p))
else:
if '|' not in ele:
Prop.append(deepcopy(self.Type[ele]))
Prop[e]['TYPE']=ele
else:
elet=ele.split('|')[0]
Prop.append(deepcopy(self.Type[elet]))
Prop[e]['OVERLAP']=ele.split('|')[1:]
Prop[e]['TYPE']=elet
if ele=='G0':
#print (sn,gi,ele)
GL.append(gl)
gi=gi+1
gl=0
Prop[e]['SECTOR']=None # Not applicable for the beamline/ring in general
Prop[e]['GIRDER']=gi
#Prop[e]['TL'] causes a key error. I don't understand!!!!!
if type(Prop[e]['L'])==list:
for k in Prop[e].keys():
if k=='TL':
TL=Prop[e][k]
Prop[e]['SG']=gl+TL/2
else:
Prop[e]['SG']=gl+Prop[e]['L']/2+Prop[e]['RL']
Prop[e]['S']=s # Longitudinal position at the entrance of the element (including RL)
# Integrated bending at the entrance of element
# Deflection angle of THIS element is not included
Prop[e]['SANGLE']=ang
'''
if type(Prop[e]['L'])==list:
Prop[e]['MS']=s+Prop[e]['TL']/2 # Longitudinal position at the middle of the element
s=s+Prop[e]['TL']
gl=gl+Prop[e]['TL']
Prop[e]['MANGLE']=ang+Prop[e]['TANGLE']/2
ang=ang+Prop[e]['TANGLE']
else:
Prop[e]['MS']=s+Prop[e]['L']/2+Prop[e]['RL'] # Longitudinal position at the middle of the element
s=s+Prop[e]['L']+Prop[e]['RL']*2
gl=gl+Prop[e]['L']+Prop[e]['RL']*2
if 'ANGLE' in Prop[e].keys():
Prop[e]['MANGLE']=ang+Prop[e]['ANGLE']/2
ang=ang+Prop[e]['ANGLE']
else:
Prop[e]['MANGLE']=ang
'''
if type(Prop[e]['L'])==list and Prop[e]['SN']=='MBSC':
Prop[e]['MS']=s+Prop[e]['TL']/2 # Longitudinal position at the middle of the element
s=s+Prop[e]['TL']
gl=gl+Prop[e]['TL']
Prop[e]['MANGLE']=ang+Prop[e]['TANGLE']/2
ang=ang+Prop[e]['TANGLE']
elif type(Prop[e]['L'])==list:
Prop[e]['MS']=s+Prop[e]['TL']/2#+Prop[e]['RL'] # Longitudinal position at the middle of the element
s=s+Prop[e]['TL']#+Prop[e]['RL']*2
gl=gl+Prop[e]['TL']#+Prop[e]['RL']*2
if 'ANGLE' in Prop[e].keys():
Prop[e]['MANGLE']=ang+Prop[e]['ANGLE']/2
ang=ang+Prop[e]['ANGLE']
else:
Prop[e]['MANGLE']=ang
else:
Prop[e]['MS']=s+Prop[e]['L']/2+Prop[e]['RL'] # Longitudinal position at the middle of the element
s=s+Prop[e]['L']+Prop[e]['RL']*2
gl=gl+Prop[e]['L']+Prop[e]['RL']*2
if 'ANGLE' in Prop[e].keys():
Prop[e]['MANGLE']=ang+Prop[e]['ANGLE']/2
ang=ang+Prop[e]['ANGLE']
else:
Prop[e]['MANGLE']=ang
self.C=s
Prop.pop(-1)
#print (GL)
#exit()
# Second survey to finalize
#fout=open('temp.dat','w')
Reserved=[]
for i in range(0,len(GL)):
Reserved.append([])
RingClass=[]
for i in range(0,len(Prop)):
p=Prop[i]
TN=Ring[i]
#print(p['Sector'],p['Girder'])
#print (GL)
gl=GL[p['GIRDER']+1]
# Important to add 1e-7 to avoid a flactuation due to round error
indx=int((p['SG']+1e-7)/gl*100)
if indx==99:
indx=98
if p['SN']=='NONE' or p['SN']=='DRIF':
pass
else:
#print ('kiteru?----------',p['SN'],p['TYPE'],100*p['GIRDER']+indx,Reserved[p['GIRDER']],p['GIRDER'],gl)
if 100*p['GIRDER']+indx in Reserved[p['GIRDER']]:
print ('Overlap!!!!',p['GIRDER'],indx,p['SN'],p['L'],i)
if 100*p['GIRDER']+indx+1 not in Reserved[p['GIRDER']]:
indx=indx+1
else:
indx=indx+2
if p['TYPE']!='DRIFT': # This is always true...
Reserved[p['GIRDER']].append(100*p['GIRDER']+indx)
if indx==100:
indx=99
p['INDEX']=indx
if Kingdom:
p['NAME']=Kingdom+'-'+p['SN']+'-'+str(p['GIRDER'])+str(indx).zfill(2)
else:
p['NAME']='BL-'+p['SN']+'-'+str(p['GIRDER'])+str(indx).zfill(2)
def RL(pin):
pr1={}
pr1['L']=pin['RL']
pr1['RL']=0
pr1['SN']='DRIF'
pr1['NAME']=pin['NAME'].split('-')[0]+'-DRIF-'+pin['NAME'].split('-')[2]
pr1['INDEX']=indx
pr1['TYPE']='DRIFT'
pr1['DNAME']='DR'+pin['TYPE'] # Name that appears in OPA file
if type(pin['L'])==list:
pr1['S']=pin['S']-abs(pin['RL'])
pr1['MS']=pin['S']-abs(pin['RL'])/2
else:
pr1['S']=pin['S']
pr1['MS']=pin['S']+pin['RL']/2
pr1['SANGLE']=pin['SANGLE']
pr1['MANGLE']=pin['SANGLE']
pr1['SECTOR']=pin['SECTOR']
pr1['GIRDER']=pin['GIRDER']
pr1['RESERVE']=1
pr2=deepcopy(pr1)
if type(pin['L'])==list:
pr2['S']=pin['S']+pin['TL']
else:
pr2['S']=pin['S']+pin['L']+pin['RL']
pr2['MS']=pr2['S']+pin['RL']/2
pr2['RESERVE']=1
if 'ANGLE' in pin.keys():
pr2['SANGLE']=pin['SANGLE']+pin['ANGLE']
else:
pr2['SANGLE']=pin['SANGLE']
return pr1,pr2
if p['RL']:
pr1,pr2=RL(p)
if p['SN'] in Gmark:
RingClass.append(Marker(p))
elif p['SN'] in Diag:
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Monitor(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Monitor(p))
elif p['SN'] in Dmag:
p['K1']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Dipole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Dipole(p))
elif p['SN'] in Qmag:
p['K1']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Quadrupole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Quadrupole(p))
elif p['SN'] in Sext:
p['K2']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Sextupole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Sextupole(p))
elif p['SN'] in Oct:
p['K3L']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Octupole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Octupole(p))
elif p['SN'] in HKIK:
p['KICK']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(HKicker(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(HKicker(p))
elif p['SN'] in VKIK:
p['KICK']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(VKicker(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(VKicker(p))
elif p['SN'] in FM: # Experimental
if p['RL']>100:
if self.TracyFM:
pr1['L']=pr1['L']-100
RingClass.append(Drift(pr1))
RingClass.append(FieldMap(p))
if self.TracyFM:
pr2['L']=pr2['L']-100
RingClass.append(Drift(pr2))
else:
if p['RL']<0 and self.TracyFM:
pr1['L']=abs(pr1['L'])
RingClass.append(Drift(pr1))
RingClass.append(FieldMap(p))
if p['RL']>0 and self.TracyFM:
RingClass.append(Drift(pr2))
elif p['SN'] in RFcav:
RingClass.append(Cavity(p))
elif p['SN'] in Coll:
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Collimator(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Collimator(p))
elif p['SN'] in CommonLineSN:
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(CommonLine(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(CommonLine(p))
elif p['SN']=='NONE':
if p['L']:
p['DNAME']=p['TYPE']
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Drift(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Drift(p))
else:
RingClass.append(Marker(p))
elif p['SN']=='DRIF':
RingClass.append(Drift(p))
elif p['SN']=='GCYC' and cycle:
RingClass.append('CYCLE')
#print (p['TYPE'])
if 'OVERLAP' in p.keys():
#if p['SN']==RingClass[-1].SN:
# Pid=-1
#else:
# Pid=-2
#self.OverlapC[RingClass[Pid].NAME]=[]
self.OverlapC[p['NAME']]=[]
for t in p['OVERLAP']:
po={}
po['L']=p['L']
po['RL']=p['RL']
po['TYPE']=t
po['SN']=self.Type[t]['SN']
po['INDEX']=indx
po['S']=p['S']
po['SANGLE']=p['SANGLE']
po['MS']=p['MS']
po['MANGLE']=p['MANGLE']
po['SECTOR']=p['SECTOR']
po['GIRDER']=p['GIRDER']
po['NAME']=p['NAME'].split('-')[0]+'-'+po['SN']+'-'+p['NAME'].split('-')[2]
if po['SN']=='MOCT':
po['K3L']=0
self.OverlapC[p['NAME']].append(Octupole(po))
elif po['SN']=='MQSK':
po['K1']=0
#if 'SKEW' in self.Type[t].keys():
# po['SKEW']=self.Type[t]['SKEW']
po['SKEW']=1
self.OverlapC[p['NAME']].append(Quadrupole(po))
elif po['SN']=='GSRC':
self.OverlapC[p['NAME']].append(Marker(po))
elif po['SN']=='NONE':
self.OverlapC[p['NAME']].append(Marker(po))
# Only existing Overlap is implemented...
#exit()
if cycle<0: # self.Ring includes 'CYCLE' text
self.Ring=RingClass
elif cycle:
RingClassT=[]
Nc=len(RingClass)
CI=[index for index, value in enumerate(RingClass) if value == 'CYCLE']
Ci=CI[cycle-1]
Sinit=RingClass[Ci+1].S
SANGinit=RingClass[Ci+1].SANGLE
for i in range(Ci,len(RingClass)):
if RingClass[i]!='CYCLE':
RingClass[i].S=RingClass[i].S-Sinit
RingClass[i].SANGLE=RingClass[i].SANGLE-SANGinit
RingClass[i].MS=RingClass[i].MS-Sinit
RingClass[i].MANGLE=RingClass[i].MANGLE-SANGinit
RingClassT.append(RingClass[i])
if RingClass[i].NAME in self.OverlapC.keys():
for co in self.OverlapC[RingClass[i].NAME]:
co.S=co.S-Sinit
co.SANGLE=co.SANGLE-SANGinit
co.MS=co.MS-Sinit
co.MANGLE=co.MANGLE-SANGinit
for i in range(0,Ci):
if RingClass[i]!='CYCLE':
RingClass[i].S=self.C-Sinit+RingClass[i].S
RingClass[i].SANGLE=360-SANGinit+RingClass[i].SANGLE
RingClass[i].MS=self.C-Sinit+RingClass[i].MS
RingClass[i].MANGLE=360-SANGinit+RingClass[i].MANGLE
RingClassT.append(RingClass[i])
if RingClass[i].NAME in self.OverlapC.keys():
for co in self.OverlapC[RingClass[i].NAME]:
co.S=self.C-Sinit+co.S
co.SANGLE=360-SANGinit+co.SANGLE
co.MS=self.C-Sinit+co.MS
co.MANGLE=360-SANGinit+co.MANGLE
self.Ring=RingClassT
else:
self.Ring=RingClass
kl=list(self.OverlapC.keys())
for k in kl:
self.OverlapC[k+'0']=self.OverlapC.pop(k)
self.ElementDB={}
for c in self.Ring:
if type(c)==str:
pass
else:
self.ElementDB[c.NAME]=c
if c.NAME in self.OverlapC.keys():
for co in self.OverlapC[c]:
self.ElementDB[co.NAME]=co
def individuateS2(self,Ring,cycle=0):
# This method is strictly for the SLS2 structure
del self.Ring
del self.Ring0
del self.OverlapC
self.OverlapC={}
RingClass=[]
#print ('btatt',Ring)
#exit()
# These short name are difined for SLS2 naming convention
Gmark=['GMRK','GSRC']
# 'DSCR', screen monitor. Experimental, 08.12.2020
# DICT added 07.11.2022
Diag=['DBPM','DSCR','DICT']
Dmag=['MBEN','MBCF','MBSC']
Qmag=['MQUA','MQCO']
Sext=['MSXT']
Oct=['MOCT']
HKIK=['MCOX','MKIK','MSEP']
VKIK=['MCOY']
ID=['UIND']
FM=['MMAP'] # Field map, experimental
RFcav=['RCAV','R3HC']
Coll=['VCOL'] # Collimator, 21.11.2023
CommonLineSN=[]
for k in self.Common.keys():
if self.Common[k][0]!='NONE':
CommonLineSN.append(self.Common[k][0])
sn=0 # Sector no.
s=0.0 # Longitudinal location
ang= 0 #total angle from the beginning (exclusive of its bending angle)
GL=[]
Prop=[]
# First survey
ndri=0
drir=[]
drirn=[]
for e in range(0,len(Ring)):
ele=Ring[e]
if Ring[e] in self.Common.keys():
p={}
p['L']=self.Common[Ring[e]][2]
p['ANGLE']=self.Common[Ring[e]][3]
p['TL']=self.Common[Ring[e]][2]
p['TANGLE']=self.Common[Ring[e]][3]
p['DX']=self.Common[Ring[e]][4]
p['DY']=self.Common[Ring[e]][5]
p['RL']=0
p['SN']=self.Common[Ring[e]][0]
p['TYPE']=Ring[e]
Prop.append(deepcopy(p))
elif type(Ring[e])==float or type(Ring[e])==int:
if Ring[e] not in drir:
drir.append(Ring[e])
drirn.append(ndri)
ndri=ndri+1
ndrip=ndri
else:
ndrip=drir.index(Ring[e])
ele='D'+str(ndri).zfill(4)
p={}
p['L']=Ring[e]
p['RL']=0
p['SN']='DRIF'
p['TYPE']='DRIFT'
p['DNAME']=ele # Name that appears in OPA file. Is it used??
Prop.append(deepcopy(p))
else: # Need to implement overlap elems here?? maybe not. => In the second survey.
if '|' not in ele:
Prop.append(deepcopy(self.Type[ele]))
#if ele=='BNVB':
# print ('cccccc',self.Type[ele])
# exit()
Prop[e]['TYPE']=ele
else:
elet=ele.split('|')[0]
Prop.append(deepcopy(self.Type[elet]))
Prop[e]['OVERLAP']=ele.split('|')[1:]
Prop[e]['TYPE']=elet
if ele=='GINI_SEC':
sn=sn+1
GL.append([0,0,0,0,0,0])
gl=0
gi=0
if ele=='G0A' or ele=='G1A':
#print (sn,gi,ele)
GL[sn-1][gi]=gl
gi=gi+1
if gi==6:
gi=5
else:
gl=0
Prop[e]['SECTOR']=sn
Prop[e]['GIRDER']=gi
#Prop[e]['TL'] causes a key error. I don't understand!!!!!
if type(Prop[e]['L'])==list:
for k in Prop[e].keys():
if k=='TL':
TL=Prop[e][k]
Prop[e]['SG']=gl+TL/2
else:
Prop[e]['SG']=gl+Prop[e]['L']/2+Prop[e]['RL']
#if Ring[e]=='SOQO':
# print ('======',Prop[e])
# print (self.Common[Ring[e]])
# exit()
Prop[e]['S']=s # Longitudinal position at the entrance of the element (including RL)
# Integrated bending at the entrance of element
# Deflection angle of THIS element is not included
Prop[e]['SANGLE']=ang
if type(Prop[e]['L'])==list and Prop[e]['SN']=='MBSC':
Prop[e]['MS']=s+Prop[e]['TL']/2 # Longitudinal position at the middle of the element
s=s+Prop[e]['TL']
gl=gl+Prop[e]['TL']
Prop[e]['MANGLE']=ang+Prop[e]['TANGLE']/2
ang=ang+Prop[e]['TANGLE']
elif type(Prop[e]['L'])==list:
Prop[e]['MS']=s+Prop[e]['TL']/2#+Prop[e]['RL'] # Longitudinal position at the middle of the element
s=s+Prop[e]['TL']#+Prop[e]['RL']*2
gl=gl+Prop[e]['TL']#+Prop[e]['RL']*2
if 'ANGLE' in Prop[e].keys():
Prop[e]['MANGLE']=ang+Prop[e]['ANGLE']/2
ang=ang+Prop[e]['ANGLE']
else:
Prop[e]['MANGLE']=ang
else:
Prop[e]['MS']=s+Prop[e]['L']/2+Prop[e]['RL'] # Longitudinal position at the middle of the element
s=s+Prop[e]['L']+Prop[e]['RL']*2
gl=gl+Prop[e]['L']+Prop[e]['RL']*2
if 'ANGLE' in Prop[e].keys():
Prop[e]['MANGLE']=ang+Prop[e]['ANGLE']/2
ang=ang+Prop[e]['ANGLE']
else:
Prop[e]['MANGLE']=ang
#print (Prop)
#print (GL)
self.C=s
# Second survey to finalize
#fout=open('temp.dat','w')
Reserved=[[],[],[],[],[],[],[],[],[],[],[],[]]
RingClass=[]
for i in range(0,len(Prop)):
p=Prop[i]
TN=Ring[i]
#print(p['Sector'],p['Girder'])
#print (GL)
gl=GL[p['SECTOR']-1][p['GIRDER']]
# Important to add 1e-7 to avoid a flactuation due to round error
indx=int((p['SG']+1e-7)/gl*100)
#if p['SECTOR']==9 and p['GIRDER']==0:
# print ('debugging',indx,p['SG'],p['SN'])
# print (Reserved[p['SECTOR']-1])
if indx==99:
indx=98
if p['SN']=='NONE' or p['SN']=='DRIF':
pass
else:
if 100*p['GIRDER']+indx in Reserved[p['SECTOR']-1]:
#print ('Overlap!!!!!!!!!!!',100*p['GIRDER']+indx+1,p['SECTOR'],p['GIRDER'],indx,p['SN'],p['L'],i)
if 100*p['GIRDER']+indx+1 not in Reserved[p['SECTOR']-1]:
indx=indx+1
else:
indx=indx+2
if p['TYPE']!='DRIFT':
Reserved[p['SECTOR']-1].append(100*p['GIRDER']+indx)
if indx==100:
indx=99
p['INDEX']=indx
p['NAME']='ARS'+str(p['SECTOR']).zfill(2)+'-'+p['SN']+'-'+str(p['GIRDER'])+str(indx).zfill(2)
#if p['SN'] in Dmag:
#print (n)
def RL(pin):
pr1={}
pr1['L']=pin['RL']
pr1['RL']=0
pr1['SN']='DRIF'
pr1['NAME']=pin['NAME'].split('-')[0]+'-DRIF-'+pin['NAME'].split('-')[2]
pr1['INDEX']=indx
pr1['TYPE']='DRIFT'
pr1['DNAME']='DR'+pin['TYPE'] # Name that appears in OPA file
if type(pin['L'])==list:
pr1['S']=pin['S']-abs(pin['RL'])
pr1['MS']=pin['S']-abs(pin['RL'])/2
else:
pr1['S']=pin['S']
pr1['MS']=pin['S']+pin['RL']/2
pr1['SANGLE']=pin['SANGLE']
pr1['MANGLE']=pin['SANGLE']
pr1['SECTOR']=pin['SECTOR']
pr1['GIRDER']=pin['GIRDER']
pr1['RESERVE']=1
pr2=deepcopy(pr1)
if type(pin['L'])==list:
pr2['S']=pin['S']+pin['TL']
else:
pr2['S']=pin['S']+pin['L']+pin['RL']
pr2['MS']=pr2['S']+pin['RL']/2
pr2['RESERVE']=1
if 'ANGLE' in pin.keys():
pr2['SANGLE']=pin['SANGLE']+pin['ANGLE']
else:
pr2['SANGLE']=pin['SANGLE']
return pr1,pr2
if p['RL']:
pr1,pr2=RL(p)
if p['SN'] in Gmark:
RingClass.append(Marker(p))
elif p['SN'] in Diag:
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Monitor(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Monitor(p))
elif p['SN'] in Dmag:
p['K1']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Dipole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Dipole(p))
elif p['SN'] in Qmag:
p['K1']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Quadrupole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Quadrupole(p))
elif p['SN'] in Sext:
p['K2']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Sextupole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Sextupole(p))
elif p['SN'] in Oct:
p['K3L']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Octupole(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Octupole(p))
elif p['SN'] in HKIK:
p['KICK']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(HKicker(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(HKicker(p))
elif p['SN'] in VKIK:
p['KICK']=0.0
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(VKicker(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(VKicker(p))
elif p['SN'] in ID:
p['DNAME']=p['TYPE']
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Drift(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Drift(p))
elif p['SN'] in FM: # Experimental
if p['RL']>100:
if self.TracyFM:
pr1['L']=pr1['L']-100
RingClass.append(Drift(pr1))
RingClass.append(FieldMap(p))
if self.TracyFM:
pr2['L']=pr2['L']-100
RingClass.append(Drift(pr2))
else:
if p['RL']<0 and self.TracyFM:
pr1['L']=abs(pr1['L'])
RingClass.append(Drift(pr1))
RingClass.append(FieldMap(p))
if p['RL']>0 and self.TracyFM:
RingClass.append(Drift(pr2))
elif p['SN'] in RFcav:
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Cavity(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Cavity(p))
elif p['SN'] in Coll:
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Collimator(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Collimator(p))
elif p['SN'] in CommonLineSN:
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(CommonLine(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(CommonLine(p))
elif p['SN']=='NONE':
if p['L']:
p['DNAME']=p['TYPE']
if p['RL']:
RingClass.append(Drift(pr1))
RingClass.append(Drift(p))
RingClass.append(Drift(pr2))
else:
RingClass.append(Drift(p))
else:
RingClass.append(Marker(p))
elif p['SN']=='DRIF':
RingClass.append(Drift(p))
elif p['SN']=='GCYC' and cycle:
RingClass.append('CYCLE')
#print (p['TYPE'])
if 'OVERLAP' in p.keys():
#if p['SN']==RingClass[-1].SN:
# Pid=-1
#else:
# Pid=-2
#self.OverlapC[RingClass[Pid].NAME]=[]
self.OverlapC[p['NAME']]=[]
for t in p['OVERLAP']:
po={}
po['L']=p['L']
po['RL']=p['RL']
po['TYPE']=t
po['SN']=self.Type[t]['SN']
po['INDEX']=indx
po['S']=p['S']
po['SANGLE']=p['SANGLE']
po['MS']=p['MS']
po['MANGLE']=p['MANGLE']
po['SECTOR']=p['SECTOR']
po['GIRDER']=p['GIRDER']
po['NAME']=p['NAME'].split('-')[0]+'-'+po['SN']+'-'+p['NAME'].split('-')[2]
if po['SN']=='MOCT':
po['K3L']=0
self.OverlapC[p['NAME']].append(Octupole(po))
elif po['SN']=='MQSK':
po['K1']=0
#if 'SKEW' in self.Type[t].keys():
# po['SKEW']=self.Type[t]['SKEW']
po['SKEW']=1
self.OverlapC[p['NAME']].append(Quadrupole(po))
elif po['SN']=='GSRC':
self.OverlapC[p['NAME']].append(Marker(po))
elif po['SN']=='NONE':
self.OverlapC[p['NAME']].append(Marker(po))
# Only existing Overlap is implemented...
#exit()
if cycle<0: # self.Ring includes 'CYCLE' text
self.Ring=RingClass
self.Ring0=copy(self.Ring)
elif cycle:
RingClassT=[]
Nc=len(RingClass)
CI=[index for index, value in enumerate(RingClass) if value == 'CYCLE']
Ci=CI[cycle-1]
Sinit=RingClass[Ci+1].S
SANGinit=RingClass[Ci+1].SANGLE
for i in range(Ci,len(RingClass)):
if RingClass[i]!='CYCLE':
RingClass[i].S=RingClass[i].S-Sinit
RingClass[i].SANGLE=RingClass[i].SANGLE-SANGinit
RingClass[i].MS=RingClass[i].MS-Sinit
RingClass[i].MANGLE=RingClass[i].MANGLE-SANGinit
RingClassT.append(RingClass[i])
if RingClass[i].NAME in self.OverlapC.keys():
for co in self.OverlapC[RingClass[i].NAME]:
co.S=co.S-Sinit
co.SANGLE=co.SANGLE-SANGinit
co.MS=co.MS-Sinit
co.MANGLE=co.MANGLE-SANGinit
for i in range(0,Ci):
if RingClass[i]!='CYCLE':
RingClass[i].S=self.C-Sinit+RingClass[i].S
RingClass[i].SANGLE=360-SANGinit+RingClass[i].SANGLE
RingClass[i].MS=self.C-Sinit+RingClass[i].MS
RingClass[i].MANGLE=360-SANGinit+RingClass[i].MANGLE
RingClassT.append(RingClass[i])
if RingClass[i].NAME in self.OverlapC.keys():
for co in self.OverlapC[RingClass[i].NAME]:
co.S=self.C-Sinit+co.S
co.SANGLE=360-SANGinit+co.SANGLE
co.MS=self.C-Sinit+co.MS
co.MANGLE=360-SANGinit+co.MANGLE
self.Ring=RingClassT
self.Ring0=RingClass
else:
self.Ring=RingClass
self.Ring0=copy(self.Ring)
kl=list(self.OverlapC.keys())
#print (kl)
#exit()
for k in kl:
self.OverlapC[k+'0']=self.OverlapC.pop(k)
self.ElementDB={}
for c in self.Ring:
if type(c)==str:
pass
else:
self.ElementDB[c.NAME]=c
if c.NAME in self.OverlapC.keys():
for co in self.OverlapC[c.NAME]:
self.ElementDB[co.NAME]=co
if c.SN=='MMAP':
if c.OVERLAP:
prop={}
prop['L']=0
prop['RL']=0
prop['SN']='DBPM'
prop['S']=c.S
prop['SANGLE']=c.SANGLE
prop['MS']=c.MS
prop['MANGLE']=c.MANGLE
prop['SECTOR']=c.SECTOR
prop['GIRDER']=c.GIRDER
prop['TYPE']=c.TYPE
prop['INDEX']=c.INDEX
prop['NAME']=c.NAME
for oe in c.OVERLAP:
if oe[0]=='DBPM':
ele=Monitor(prop)
ele.MAX=c.MAX # Horizontal
ele.MAY=c.MAY # Vertical
ele.MAR=c.MAR # Rotation
ele.TYPE='BPM'
ele.NAME=c.NAME.replace('MMAP',oe[0])
self.ElementDB[ele.NAME]=ele
#print ('kiteru?',ele.NAME)
#print (self.ElementDB[ele.NAME])
#exit()
if oe[0]=='MCOX':
prop['KICK']=0
ele=HKicker(prop)
ele.MAX=c.MAX # Horizontal
ele.MAY=c.MAY # Vertical
ele.MAR=c.MAR # Rotation
ele.TYPE='CH'
ele.NAME=c.NAME.replace('MMAP',oe[0])
self.ElementDB[ele.NAME]=ele
if oe[0]=='MCOY':
prop['KICK']=0
ele=VKicker(prop)
ele.MAX=c.MAX # Horizontal
ele.MAY=c.MAY # Vertical
ele.MAR=c.MAR # Rotation
ele.TYPE='CV'
ele.NAME=c.NAME.replace('MMAP',oe[0])
self.ElementDB[ele.NAME]=ele
if oe[0]=='MQCO':
prop['K1']=0
ele=Quadrupole(prop)
ele.MAX=c.MAX # Horizontal
ele.MAY=c.MAY # Vertical
ele.MAR=c.MAR # Rotation
ele.L=oe[2]*0.001
ele.NAME=c.NAME.replace('MMAP',oe[0])
self.ElementDB[ele.NAME]=ele
def cycle(self,n):
# self.Layout method delete the existing ring
# Therefore, a simple cycle method is necessary...
# To use this method, self.Layout('full/unit',-1) <- cycle switch should be negative
if n:
RingClassT=[]
Nc=len(self.Ring)
CI=[index for index, value in enumerate(self.Ring) if value == 'CYCLE']
Ci=CI[n-1]
Sinit=self.Ring[Ci+1].S
SANGinit=self.Ring[Ci+1].SANGLE
for i in range(Ci,len(self.Ring)):
if self.Ring[i]!='CYCLE':
self.Ring[i].S=self.Ring[i].S-Sinit
self.Ring[i].SANGLE=self.Ring[i].SANGLE-SANGinit
self.Ring[i].MS=self.Ring[i].MS-Sinit
self.Ring[i].MANGLE=self.Ring[i].MANGLE-SANGinit
RingClassT.append(self.Ring[i])
if self.Ring[i].NAME in self.OverlapC.keys():
for co in self.OverlapC[self.Ring[i].NAME]:
co.S=co.S-Sinit
co.SANGLE=co.SANGLE-SANGinit
co.MS=co.MS-Sinit
co.MANGLE=co.MANGLE-SANGinit
for i in range(0,Ci):
if self.Ring[i]!='CYCLE':
self.Ring[i].S=self.C-Sinit+self.Ring[i].S
self.Ring[i].SANGLE=360-SANGinit+self.Ring[i].SANGLE
RingClassT.append(self.Ring[i])
self.Ring[i].MS=self.C-Sinit+self.Ring[i].MS
self.Ring[i].MANGLE=360-SANGinit+self.Ring[i].MANGLE
if self.Ring[i].NAME in self.OverlapC.keys():
for co in self.OverlapC[self.Ring[i].NAME]:
co.S=self.C-Sinit+co.S
co.SANGLE=360-SANGinit+co.SANGLE
co.MS=self.C-Sinit+co.MS
co.MANGLE=360-SANGinit+co.MANGLE
self.Ring=RingClassT
def insertMultipole(self,tn):
# Method to insert thin multipole after individuation.
# What is done actually is adding the element to OverlapC
# TYPE name tn can be a part of full type name
# ex. tn='VB' catches VBO, VBI, VBXI, VBXO
if not self.Ring:
return
for c in self.Ring:
if type(c)==str:
pass
else:
if tn in c.TYPE:
if c.NAME not in self.OverlapC.keys():
self.OverlapC[c.NAME]=[]
po={}
po['L']=0
po['RL']=0
po['TYPE']='MERR'
po['TARGET']=c.NAME
po['SN']='MERR'
po['INDEX']=c.INDEX
po['S']=c.S+c.L/2
po['SANGLE']=0
po['MS']=c.S+c.L/2
po['MANGLE']=0
po['SECTOR']=c.SECTOR
po['GIRDER']=c.GIRDER
po['NAME']=c.NAME.split('-')[0]+'-MERR-'+str(c.GIRDER)+str(c.INDEX).zfill(2)
oe=Multipole(po)
self.OverlapC[c.NAME].append(oe)
self.ElementDB[oe.NAME]=oe
def setStrengthAsFile(self,Pattern,Strength,OPA=False):
BDNkey=['MQUA','MSXT','MOCT','MQCO','MQSK']
StrPat={}
Tdone=[]
for k in Pattern.keys():
StrPat[k]=[]
for e in Pattern[k]:
StrPat[k].append(Strength[e])
if e not in Tdone:
Tdone.append(e)
for k in Strength.keys():
if k not in Tdone:
StrPat[k]=Strength[k]
if not OPA:
try:
del(StrPat['MKIK'])
except:
pass
for k in BDNkey:
if k in Pattern:
StrPat[k+'_BDN']=Pattern[k]
self.setStrength(StrPat)
def setStrength(self,Pattern):
# Pattern is a dictionary
# Pattern['TypeName']=float or possibly a list
# Pattern['SN'] also works.
# This may not work for MMAP with inserted elements...
Pat=deepcopy(Pattern)
if not self.Ring:
return
for c in self.Ring: # Need to elaborate this block?
if type(c)==str:
pass
else:
if c.SN in Pat.keys():
if type(Pat[c.SN])==float or type(Pat[c.SN])==int:
c.setStrength(Pat[c.SN])
# This may not be the case as MQUA and MSXT strengthes are given by a list normally.
if (c.SN=='MQUA' or c.SN=='MSXT') and Pat[c.SN]<0.0:
c.POL=-1
if c.SN+'_BDN' in Pat.keys():
c.BDN=Pat[c.SN+'_BDN']
else:
c.setStrength(Pat[c.SN][0])
if (c.SN=='MQUA' or c.SN=='MSXT') and Pat[c.SN][0]<0.0:
c.POL=-1
Pat[c.SN].pop(0)
if c.SN+'_BDN' in Pat.keys():
c.BDN=Pat[c.SN+'_BDN'][0]
Pat[c.SN+'_BDN'].pop(0)
elif c.TYPE in Pat.keys():
c.setStrength(Pat[c.TYPE])
if (c.SN=='MQUA' or c.SN=='MSXT') and Pat[c.TYPE]<0.0:
c.POL=-1
if c.NAME in self.OverlapC.keys():
for t in self.OverlapC[c.NAME]:
if t.SN in Pat.keys():
if type(Pat[t.SN])==float:
t.setStrength(Pat[t.SN])
else:
t.setStrength(Pat[t.SN][0])
Pat[t.SN].pop(0)
elif t.TYPE in Pat.keys():
t.setStrength(Pat[t.TYPE])
if t.SN+'_BDN' in Pat.keys():
t.BDN=Pat[t.SN+'_BDN'][0]
Pat[t.SN+'_BDN'].pop(0)
def setKL_SN(self,SN,KL,additive=False):
elem=self.getElement_SN(SN)
if type(KL)==list:
if len(elem)!=len(KL):
print ('Error: KL input length does not much the number of '+SN+' elements')
return
for i in range(0,len(elem)):
if additive:
elem.setKL(elem.KL()+KL[i])
else:
elem.setKL(KL[i])
else: # set all elem to KL (float). Mostly used for turning off
for e in elem:
if additive:
e.setKL(e.KL()+KL)
else:
e.setKL(KL)
def setKL_TYPE(self,TYPE,KL,additive=False,bothIO=True):
# Assumed that the order of KL and the order of elem is the same.
elem=self.getElement_TYPE(TYPE,bothIO=bothIO)
if type(KL)==list:
if len(elem)!=len(KL):
print ('Error: KL input length does not much the number of '+TYPE+' elements')
for i in range(0,len(elem)):
if additive:
elem[i].setKL(elem.KL()+KL[i])
else:
elem[i].setKL(KL[i])
else:
for e in elem:
if additive:
e.setKL(e.KL()+KL)
else:
e.setKL(KL)
def setKL_FM_MIX(self,KL,additive=False):
# Special method to set KL, when some magnets are "incorporated" om Field map
# KL is a dictionary {Element Name: KL}
for n in KL.keys():
elem=getElement(n)
if elem:
if additive:
elem.setKL(elem.KL()+KL[n])
else:
elem.setKL(KL[n])
else:
name=n.replace('_','-')
sn=name.split('-')[1]
efm=getElement(n.replace(sn,'MMAP'))
for oe in efm.OVERLAP:
if sn==oe[0]:
if additive:
oe[3]=oe[3]+KL[n]
else:
oe[3]=KL[n]
def getKL_SN(self,SN):
elem=self.getElement_SN(SN)
return [c.KL() for c in elem]
def getKL_TYPE(self,tn,bothIO=False):
elem=self.getElement_TYPE(tn,bothIO=bothIO)
return [c.KL() for c in elem]
def setMultipoleError(self,tn,order,val):
# val can be list if the error is not constant over individual elements
# order=2 is sextupole
if not self.Ring:
return
for c in self.Ring:
if type(c)==str:
pass
elif c.TYPE==tn:
if c.NAME not in self.OverlapC.keys():
print ('Insert multipole before assigning errors')
exit()
for oe in self.OverlapC[c.NAME]:
if oe.SN=='MERR':
oe.setStrength(order,val)
if type(val)==list:
val.pop(0)
def setAperture(self,Aperture):
# At this moment, the input is an dictionary
# defined in ApertureModel.<version>.py
if not self.Ring:
return
if not Aperture:
return
kl=list(Aperture.keys())
for c in self.Ring:
if type(c)==str:
pass
elif c.TYPE in kl:
c.APERTURE=Aperture[c.TYPE]
else:
c.APERTURE=Aperture['Nominal']
def setSextFamily(self,Pattern):
# Input is a list with a length corresponding to the number of sext
# Set before cycling
if not self.Ring:
return
Pat=deepcopy(Pattern)
Dad=[]
for c in self.Ring:
if type(c)==str:
pass
elif c.SN=='MSXT':
c.FAMILY=Pattern[0]
Pattern.pop(0)
if c.FAMILY not in Dad:
c.DAD=1
Dad.append(c.FAMILY)
def getSextFamily(self):
if not self.Ring:
return
SF={}
Dad=[]
for c in self.Ring:
if type(c)==str:
pass
elif c.SN=='MSXT':
if not c.FAMILY:
print ('Sextupoles are not yet grouped')
return SF,Dad
else:
if c.FAMILY in SF.keys():
SF[c.FAMILY].append(c.NAME)
else:
SF[c.FAMILY]=[c.NAME]
if c.DAD:
Dad.append(c.NAME)
return SF,Dad
def bindSextFamily(self):
if not self.Ring:
return
binded={}
for c in self.Ring:
if type(c)==str:
pass
elif c.SN=='MSXT':
if not c.FAMILY:
print ('Sextupoles are not yet in families')
return
else:
if c.FAMILY in binded.keys():
c.setKL(binded[c.FAMILY])
else:
binded[c.FAMILY]=c.KL()
def SigmaCut(self,Scut=2.0):
# Cut value in sigma
while True:
a=gauss(0,1)
if abs(a)<Scut:
return a
def resetMisalignmentS2(self):
# In MADX, it is too complicate to reset misalignment...
for c in self.Ring:
if type(c)==str:
pass
elif hasattr(c,'MAX'):
if c.SN=='MBSC':
c.MAX=[0]*len(c.L)
c.MAY=[0]*len(c.L)
c.MAR=0
else:
c.MAX=0
c.MAY=0
c.MAR=0
def multiplyMisalignmentS2(self,Factor):
for c in self.Ring:
if type(c)==str:
pass
elif hasattr(c,'MAX'):
if c.SN=='MBSC':
c.MAX=list(np.array(c.MAX)*Factor)
c.MAY=list(np.array(c.MAY)*Factor)
c.MAR=c.MAR*Factor
else:
c.MAX=c.MAX*Factor
c.MAY=c.MAY*Factor
c.MAR=c.MAR*Factor
def CorrelatedMisalignmentS2(self, BPMoffset=0.0,BPMrot=0.0):
#
# Correlation within a unit, such as SOQO, BPMCHV are not included.
# To be checked with Michael
# Also, roll errors are not implemented yet. -> Implemented
if not self.Ring:
return
GM=60e-6 # Girder absolute
GGM=20e-6 # Girder to girder
EM=30e-6 #Element to element
# No roll error for the time being.
# There is something wrong with tilt in Elegant...
EMR=300e-6 # Magnet rotation error, the value to be discussed.
if BPMrot:
BMR=BPMrot
else:
BMR=10e-3#*0 # BPM rotation error
Sall=[]
Gall=[]
SSG=0 # flag for straight section girder
for c in self.Ring:
if type(c)==str:
pass
elif 'NONE' in c.NAME:
if c.TYPE[0]=='G':
Sall.append(c.S)
if c.TYPE=='G0S':
if SSG==0:
Gall.append(0) # Girder in straight section
SSG=1
else:
SSG=0
elif c.TYPE=='G0A' and c.GIRDER==1:
Gall.append(c.GIRDER) # Girder in arc
elif c.TYPE=='G1A':
Gall.append(c.GIRDER) # Girder in arc
Sall.append(c.S)
Girders=[] # Sector No and girder No
Spair=[] # Longitudinal positions of girder start and end
for i in range(0,len(Sall),2):
Spair.append([Sall[i],Sall[i+1]])
#print (Spair)
#print (len(Sall),len(Spair))
GMx=[]
GMy=[]
for i in range(0,len(Gall)):
if Gall[i]==0 or Gall[i]==1:
GMx.append([GM*self.SigmaCut(),GM*self.SigmaCut()])
GMy.append([GM*self.SigmaCut(),GM*self.SigmaCut()])
else: # Girder joint play
GMx.append([GMx[-1][1]+GGM*self.SigmaCut(),GM*self.SigmaCut()])
GMy.append([GMy[-1][1]+GGM*self.SigmaCut(),GM*self.SigmaCut()])
'''
fout=open('temp.dat','w')
for i in range(0,len(Spair)):
if Gall[i]:
wline=str(Spair[i][0])+' '+str(GMx[i][0])+' '+str(GMy[i][0])+' '+str(Gall[i])+'\n'
fout.write(wline)
wline=str(Spair[i][1])+' '+str(GMx[i][1])+' '+str(GMy[i][1])+' '+str(Gall[i])+'\n'
fout.write(wline)
wline='\n'
fout.write(wline)
fout.close()
'''
fvb=0 # flag to detect VB mag (excluding VBX)
for c in self.Ring:
if type(c)==str:
pass
else:
# It would be clever to use Sector No and Girder index here
# but it is not straightforward for the girders in straight section
# since it can be more than one.
# So, the longitudinal position is used instead - brute force approach...
gi=-1
for i in range(0,len(Spair)):
if c.S>=Spair[i][0] and c.S<=Spair[i][1]:
gi=i
if gi<0: # Element with no girder?
c.MAX=EM*self.SigmaCut()
c.MAY=EM*self.SigmaCut()
if c.SN=='DBPM':
c.MAR=BMR*self.SigmaCut()
else:
c.MAR=EMR*self.SigmaCut()
if c.SN=='MMAP':
if c.NAME.replace('MMAP','DBPM') in ElementDB.keys():
ele=self.ElementDB[c.NAME.replace('MMAP','DBPM')]
ele.MAR=BMR*self.SigmaCut()
elif c.TYPE=='VBO' or c.TYPE=='VBI' or c.TYPE=='VBXO' or c.TYPE=='VBXI' or c.TYPE=='BN':
# This block is valid even for VB-BS-VB... Need to improve...?
if fvb==0:
if c.TYPE=='VBI' or c.TYPE=='VBXI':
fvb=-1
elif c.TYPE=='VBO' or c.TYPE=='VBXO':
fvb=1
dx=EM*self.SigmaCut()
dy=EM*self.SigmaCut()
dr=EMR*self.SigmaCut()
c.MAX=dx \
+(c.S+c.RL+c.L/2-Spair[gi][0])/(Spair[gi][1]-Spair[gi][0]) \
*(GMx[gi][1]-GMx[gi][0]) + GMx[gi][0]
c.MAY=dy \
+(c.S+c.RL+c.L/2-Spair[gi][0])/(Spair[gi][1]-Spair[gi][0]) \
*(GMy[gi][1]-GMy[gi][0]) + GMy[gi][0]
c.MAR=dr
if fvb==-1 and (c.TYPE=='VBO' or c.TYPE=='VBXO'):
fvb=0
elif fvb==1 and (c.TYPE=='VBI' or c.TYPE=='VBXI'):
fvb=0
#print ('kiteru?',fvb,c.TYPE)
else:
#print (c.NAME,c.SN,c.RL)
# Unfortunately, the misalignment of field map (polykick) in tracy-null is single value...
if type(c.L)==list and c.SN=='MBSC':
sj=c.S+c.RL
mx=[]
my=[]
mxf=self.SigmaCut()
myf=self.SigmaCut()
for j in range(0,len(c.L)):
sj=sj+c.L[j]/2
mx.append(EM*mxf \
+(sj-Spair[gi][0])/(Spair[gi][1]-Spair[gi][0]) \
*(GMx[gi][1]-GMx[gi][0]) + GMx[gi][0])
my.append(EM*myf \
+(sj-Spair[gi][0])/(Spair[gi][1]-Spair[gi][0]) \
*(GMy[gi][1]-GMy[gi][0]) + GMy[gi][0])
sj=sj+c.L[j]/2
c.MAX=mx
c.MAY=my
else:
if c.SN=='MMAP':
cL=c.TL
cRL=0
else:
cL=c.L
cRL=c.RL
c.MAX=EM*self.SigmaCut() \
+(c.S+cRL+cL/2-Spair[gi][0])/(Spair[gi][1]-Spair[gi][0]) \
*(GMx[gi][1]-GMx[gi][0]) + GMx[gi][0]
c.MAY=EM*self.SigmaCut() \
+(c.S+cRL+cL/2-Spair[gi][0])/(Spair[gi][1]-Spair[gi][0]) \
*(GMy[gi][1]-GMy[gi][0]) + GMy[gi][0]
if c.NAME.replace('MMAP','DBPM') in self.ElementDB.keys():
ele=self.ElementDB[c.NAME.replace('MMAP','DBPM')]
ele.MAR=BMR*self.SigmaCut()
if c.SN=='DBPM':
c.MAR=BMR*self.SigmaCut()
else:
c.MAR=EMR*self.SigmaCut()
if BPMoffset:
for c in self.Ring:
if type(c)==str:
pass
elif c.SN=='DBPM':
c.MAX=c.MAX+BPMoffset*self.SigmaCut()
c.MAY=c.MAY+BPMoffset*self.SigmaCut()
elif c.SN=='MMAP':
if c.NAME.replace('MMAP','DBPM') in self.ElementDB.keys():
ele=self.ElementDB[c.NAME.replace('MMAP','DBPM')]
ele.MAX=ele.MAX+BPMoffset*self.SigmaCut()
ele.MAY=ele.MAY+BPMoffset*self.SigmaCut()
ASN=[]
for k in self.OverlapC.keys():
sn=k.split('-')[1]
#if sn=='MBEN':
# print (self.OverlapC[k])
if sn not in ASN:
ASN.append(sn)
#print (ASN)
def FieldError(self,SN,RFE):
if not self.Ring:
return
for c in self.Ring:
if type(c)==str:
pass
elif c.SN==SN:
c.RFE=RFE*self.SigmaCut()
if c.SN=='MBCF' or c.SN=='MMAP':
c.RGE=RFE*self.SigmaCut()
if SN=='MOCT':
for k in self.OverlapC.keys():
for c in self.OverlapC[k]:
if c.SN==SN:
c.RFE=RFE*self.SigmaCut()
def pushI2KL(self,SM,CrossTalk=False,ZZ=False,Indiv=False):
if not self.Ring:
print ('Layout is not yet configured.')
return
for SN in ['MQUA','MSXT','MOCT','MQCO','MQSK','MCOX','MCOY']:
EL=self.getElement_SN(SN)
for c in EL:
if CrossTalk and SN=='MSXT':
KL=c.POL*SM.I2KL(c.BDN,c.I)
elif CrossTalk and SN=='MOCT':
if c.BDN in ['OXX','OXY','OYY']:
KL=SM.I2KL(c.TYPE,c.I)
elif c.BDN in ['OCY1','OCY2']:
KL=SM.I2KL(c.TYPE,c.I,CrossTalk=c.BDN)
else:
KL=SM.I2KL(c.BDN,c.I,CrossTalk=c.BDN)
else:
if c.SN=='MQUA' and Indiv:
KL=SM.I2KL(c.NAME,c.I)
else:
KL=SM.I2KL(c.TYPE,c.I)
if c.SN in ['MQUA','MSXT']:
KL=c.POL*KL
if ZZ and c.I==0: # Zero KL if I=0, ignoring b0 constant.
KL=0
c.setKL(KL)
def pushKL2I(self,SM,CrossTalk=False,ZZ=False,Indiv=False):
if not self.Ring:
print ('Layout is not yet configured.')
return
for SN in ['MQUA','MSXT','MOCT','MQCO','MQSK','MCOX','MCOY']:
EL=self.getElement_SN(SN)
for c in EL:
if CrossTalk and SN=='MSXT':
I=SM.KL2I(c.BDN,abs(c.KL()))
elif CrossTalk and SN=='MOCT':
if c.BDN in ['OXX','OXY','OYY']:
I=SM.KL2I(c.TYPE,c.KL())
elif c.BDN in ['OCY1','OCY2']:
I=SM.KL2I(c.TYPE,c.KL(),CrossTalk=c.BDN)
else:
I=SM.KL2I(c.BDN,c.KL(),CrossTalk=c.BDN)
else:
if c.SN=='MQUA' and Indiv:
I=SM.KL2I(c.NAME,c.KL())
else:
I=SM.KL2I(c.TYPE,c.KL())
if c.SN in ['MQUA','MSXT']:
I=abs(I)
if ZZ and c.KL()==0: # Zero A if KL=0, ignoring b0 constant.
I=0
c.I=I
def PatternMatch(self,Pattern):
# Find a series of elements that matches to Pattern in self.Ring
# Matching is by TYPE
# Return indices of the first element in Pattern
s_idx=[]
for i in range(0,len(self.Ring)):
c=self.Ring[i]
if type(c)!=str:
if c.TYPE==Pattern[0]:
Nstr=0
for j in range(1,len(Pattern)):
if type(self.Ring[i+j])==str:
if self.Ring[i+j]==Pattern[j]:
compatible=1
else:
break
elif self.Ring[i+j].TYPE==Pattern[j]:
compatible=1
else:
compatible=0
break
if compatible:
s_idx.append(i)
return s_idx
def setRandomSeed(self,rs):
seed(rs)
def setBeamEnergy(self,E):
self.BeamEnergy=E*1e9
def getElement(self,ename):
if not self.Ring:
return None
if ename.replace('_','-') in self.ElementDB:
return self.ElementDB[ename.replace('_','-')]
else:
return None
def getElement_SN(self,SN):
# Returns a list of element classes, corresponding to the given SN
EL=self.listElement_SN(SN)
if not EL:
return None
CL=[]
for e in EL:
CL.append(self.getElement(e))
return CL
def getElement_TYPE(self,tn,bothIO=False):
# Returns a list of element classes, corresponding to the given SN
EL=self.listElement_TYPE(tn,bothIO=bothIO)
if not EL:
return None
CL=[]
for e in EL:
CL.append(self.getElement(e))
return CL
def listElement_SN(self,SN):
if not self.Ring:
return None
fam=[]
for c in self.Ring:
if type(c)==str:
pass
else:
if c.SN==SN:
fam.append(c.NAME)
if c.NAME in self.OverlapC.keys():
for oe in self.OverlapC[c.NAME]:
if oe.SN==SN:
fam.append(oe.NAME)
if c.SN=='MMAP':
if c.OVERLAP:
for oe in c.OVERLAP:
if oe[0]==SN:
fam.append(c.NAME.replace('MMAP',oe[0]))
return fam
def listElement_TYPE(self,tn, bothIO=False, cycled=False):
if not self.Ring:
return None
if cycled:
typ=[]
for c in self.Ring:
if type(c)!=str:
if c.TYPE==tn:
typ.append(c.NAME)
if c.NAME in self.OverlapC.keys():
for oe in self.OverlapC[c.NAME]:
if oe.TYPE==tn:
typ.append(oe.NAME)
#if bothIO and c.TYPE[0:-1]==tn:
# The third condition required for 'KIN' to be fine with 'KINJ'
if bothIO and c.TYPE[0:-1]==tn and c.TYPE[-1] in ['I','O']:
typ.append(c.NAME)
else:
typ=[]
for c in self.ElementDB.values():
if c.TYPE==tn:
typ.append(c.NAME)
#if bothIO and c.TYPE[0:-1]==tn:
# The third condition required for 'KIN' to be fine with 'KINJ'
if bothIO and c.TYPE[0:-1]==tn and c.TYPE[-1] in ['I','O']:
typ.append(c.NAME)
typ.sort()
return typ
def changeParameter(self,ename,key,value):
if ename.replace('_','-') in self.ElementDB:
c=self.ElementDB[ename.replace('_','-')]
exec('c.'+key+'=value')
def Pair(self,SN1,SN2,SN3=None):
# Find pairs of SN1 and SN2 (or SN3) elements, which are closest, and return a list
if not self.Ring:
return None
G1=self.getElement_SN(SN1)
G2=self.getElement_SN(SN2)
if SN3:
G3=self.getElement_SN(SN3)
G2=G2+G3
p=[]
for e1 in G1:
smin=10000
pc=[]
for e2 in G2:
if abs(e1.MS-e2.MS)<smin:
e2save=e2
smin=abs(e1.MS-e2.MS)
pc=[e1.NAME,e2save.NAME,smin]
p.append(pc)
'''
p=[]
for c in self.Ring:
if type(c)==str:
pass
else:
if c.SN==SN1:
smin=500
for d in self.Ring:
if type(d)==str:
pass
else:
if d.SN==SN2 or d.SN==SN3:
if abs(c.S-d.S)<smin:
dsave=d
smin=abs(c.S-d.S)
p.append([c.NAME,dsave.NAME,smin])
'''
return p
def Pair_TYPE(self,T1,T2,T3=None):
# Find pairs of TYPE1 and TYPE2 (or TYPE3) elements, which are closest, and return a list
if not self.Ring:
return None
G1=self.getElement_TYPE(T1)
G2=self.getElement_TYPE(T2)
if T3:
G3=self.getElement_TYPE(T3)
G2=G2+G3
p=[]
for e1 in G1:
smin=10000
pc=[]
for e2 in G2:
if abs(e1.MS-e2.MS)<smin:
e2save=e2
smin=abs(e1.MS-e2.MS)
pc=[e1.NAME,e2save.NAME,smin]
p.append(pc)
return p
def BPMplus(self,TYPE='BPM'):
# Overwrite TYPE property of the BPM at the end of arc
if not self.Ring:
return None
pa=self.Pair_TYPE('BEHO','BPMA')
for p in pa:
self.getElement(p[1]).TYPE=TYPE
def UndulatorName(self,InputFile=None):
if not InputFile:
print ('No input file is given')
return None
if not self.Ring:
print ('No layout')
return None
exec(open(InputFile).read(),globals())
print (Ulabel)
for c in self.Ring:
if type(c)!=str:
if c.SN=='UIND':
if c.SECTOR in [2,4,6,8,10,12]:
SS='S'
elif c.SECTOR in [3,7,11]:
SS='M'
else:
SS='L'
sector=str(c.SECTOR).zfill(2)
Nameb=deepcopy(c.NAME)
c.NAME='X'+sector+SS+Ulabel[c.SECTOR-1][0]+c.NAME[5:]
Ulabel[c.SECTOR-1].pop(0)
self.ElementDB[c.NAME]=self.ElementDB[Nameb]
class ElementBase:
def __init__(self,prop):
self.L=prop['L']
self.RL=prop['RL']
self.SN=prop['SN']
self.S=prop['S']
self.SANGLE=prop['SANGLE']
self.MS=prop['MS']
self.MANGLE=prop['MANGLE']
self.NAME=prop['NAME']+'0' # 3 digits to 4 digits for vacuum
self.SECTOR=prop['SECTOR']
self.GIRDER=prop['GIRDER']
self.TYPE=prop['TYPE']
self.INDEX=prop['INDEX']
self.APERTURE=None # Aperture is implemented if necessary
# Misalignment
if self.SN=='MBSC':
self.MAX=[0]*len(self.L) # Horizontal
self.MAY=[0]*len(self.L) # Vertical
self.MAR=0 # Rotation
else:
self.MAX=0 # Horizontal
self.MAY=0 # Vertical
self.MAR=0 # Rotation
class CommonLine(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.DX=prop['DX']
self.DY=prop['DY']
self.TL=prop['TL']
self.TANGLE=prop['TANGLE']
class Marker(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.SRC=''
class Monitor(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
class Drift(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.DNAME=prop['DNAME']
if 'RESERVE' in prop.keys():
self.RESERVE=prop['RESERVE']
class Dipole(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.ANGLE=prop['ANGLE']
self.E1=prop['E1']
self.E2=prop['E2']
self.K1=prop['K1']
if type(prop['L'])==list:
self.TL=prop['TL']
self.TANGLE=prop['TANGLE']
self.DX=prop['DX']
self.DY=prop['DY']
self.MDX=prop['MDX']
self.MDY=prop['MDY']
self.RFE=0.0 # Relative field error
self.RGE=0.0 # Relative gradient error in case of MBCF
def setStrength(self,k):
self.K1=k
def setKL(self,kl):
if type(self.L)==list:
return 0
self.K1=kl/self.L
def KL(self):
if type(self.L)==list:
return 0
return self.K1*self.L
class Quadrupole(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.K1=prop['K1']
if 'SKEW' in prop.keys():
self.SKEW=prop['SKEW']
else:
self.SKEW=0
self.RFE=0.0 # Relative field error
# Polarity of power supply, 1 is either bipolar or unipolar but positive K1
# -1 is unipolar and negative K1
# Default is 1. Needed only for MQUA and MSXT
# Experimental, 10.03.2022
self.POL=1
self.BDN='' # Name in the original opa file (QP1, QP2, etc.)
self.I=0 # Current attribute to work on the actual machine
def setStrength(self,k):
self.K1=k
def setKL(self,kl):
#Confusing... but the skew is zero-length and, in the BD pattern file, K1L is given.
if self.SKEW:
self.K1=kl
else:
self.K1=kl/self.L
def KL(self):
if self.SKEW:
return self.K1
else:
return self.K1*self.L
class Sextupole(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.K2=prop['K2']
self.RFE=0.0 # Relative field error
# Polarity of power supply, 1 is either bipolar or unipolar but positive K1
# -1 is unipolar and negative K1
# Default is 1. Needed only for MQUA and MSXT
# Experimental, 10.03.2022
self.POL=1
# Sextupole family
self.FAMILY=None
self.DAD=0
self.BDN='' # Name in the original opa file (SD1, SF1, etc.)
self.I=0 # Current attribute to work on the actual machine
def setStrength(self,k):
self.K2=k
#print ('bffsb',k)
def setKL(self,kl):
self.K2=kl/self.L
def KL(self):
return self.K2*self.L
class Octupole(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.K3L=prop['K3L']
self.RFE=0.0 # Relative field error
self.BDN='' # Name in the original opa file (OXX, OCY1, etc.)
self.I=0 # Current attribute to work on the actual machine
def setStrength(self,k):
self.K3L=k
def setKL(self,kl):
self.K3L=kl
def KL(self):
return self.K3L
class HKicker(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.HKICK=prop['KICK']
self.I=0 # Current attribute to work on the actual machine
# Polarity property, used only for MKIK
# Experimental, 28.11.2022
self.POL=None
def setStrength(self,k):
self.HKICK=k
def setKL(self,kl):
self.HKICK=kl
def KL(self):
return self.HKICK
class VKicker(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.VKICK=prop['KICK']
self.I=0 # Current attribute to work on the actual machine
def setStrength(self,k):
self.VKICK=k
def setKL(self,kl):
self.VKICK=kl
def KL(self):
return self.VKICK
class Multipole(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
self.K2L=0
self.K3L=0
self.K4L=0
self.K5L=0
self.TARGET=prop['TARGET']
def setStrength(self,n,k):
if n==2:
self.K2L=k
if n==3:
self.K3L=k
if n==4:
self.K4L=k
if n==5:
self.K5L=k
class FieldMap:
# Experimental class for importing field map within the framework of trap by Bernard
def __init__(self,prop):
self.FILENAME=prop['FILENAME'].lower()
self.NAME=prop['NAME']
self.SN='MMAP'
self.APERTURE=None
self.MAX=0 # Horizontal
self.MAY=0 # Vertical
self.MAR=0 # Rotation
self.L=prop['L']
self.TL=prop['TL']
self.STRENGTH=prop['STRENGTH']
self.RL=prop['RL']
self.S=prop['S']
self.SANGLE=prop['SANGLE']
self.MS=prop['MS']
self.MANGLE=prop['MANGLE']
self.NAME=prop['NAME']+'0' # 3 digits to 4 digits for vacuum
self.SECTOR=prop['SECTOR']
self.GIRDER=prop['GIRDER']
self.TYPE=prop['TYPE']
self.INDEX=prop['INDEX']
self.ANGLE=prop['ANGLE']
self.RFE=0.0
self.RGE=0.0
if 'FMOVERLAP' in prop.keys():
self.OVERLAP=prop['FMOVERLAP']
else:
self.OVERLAP=None
# 23.06.2022. Experimental to include cavities to Master layout
class Cavity(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
if 'RESERVE' in prop.keys():
self.RESERVE=prop['RESERVE']
class Collimator(ElementBase):
def __init__(self, prop):
ElementBase.__init__(self,prop)
# 1-m open gap
self.HGAP=1.0
self.VGAP=1.0