chore: remove OM2TN and OMEpics -- contain external packages I don't know how to package or don't have access to

This commit is contained in:
Benjamin Labrecque
2026-07-24 11:06:57 +02:00
parent 5b29143726
commit 2556e9e59e
2 changed files with 0 additions and 1247 deletions
-532
View File
@@ -1,532 +0,0 @@
"""
import of M.Aiba's OnlineModel to tracy-null via the general tracy-null dictionary statement parser
draft version from July 21 by Bernard Riemann.
This version is only kept as a 'snapshot', as Masamitsu will modify and adapt it as the model develops.
the main function to be used is: onlinemodel_to_statements
Further Notes:
the onlinemodel reader, onlinemodel_to_statements, returns only a list of dictionaries.
The dictionaries are understood by the general tracy-null input parser
(the conversion into the c tracy lattice happens in the function trap/readers.py : push_family,
note that push_family should not be changed too much, as a reinstall/update of tracy in the environment is required for changes to take effect)
if it is problematic to load OMFacility on Merlin or you want to sahre the results with someone who dont uses the OMFacility, you can export the statement list of dictionaries into .json format (see data/sls2/b068/import.ipynb for examples). Another person can then load the dictionaries again from json and import
"""
from OMFacility import *
#exec(open('/afs/psi.ch/project/SLS2/BD/OnlineModel/OMFacility.py').read())
from trap.trapc import LatticeType # from c module, see trap/src/main.cpp or tracy/src/*.{cc,h}
from trap.readers import check_stadict, replace_subsequence # see trap/readers.py
from numpy import deg2rad
from warnings import warn
from copy import copy, deepcopy
def ocell_to_dict(ocell, include_markers=False) -> list:
"""
for a given cell in the OMFacility.Facility, return a list of statements.
if ocell.APERTURE is defined, aperture information is included (aper.. keys)
Typically, there is only one entry in the list.
But sometimes, elements are ignored (no entries) or have more than one statement (like step-wise bends, MBSC)
returned dicts are input for the tracy-null python parser
"""
otype = ocell.SN
# note: not using round(ocell.L, 6) gives only ~2micron total length diff to elegant
# Sign of 'roll' to be checked, i.e., consistency among elegant, MADX and tracy...
if otype=='MMAP':
outdict = {'name': ocell.NAME,
'dx': ocell.MAX, 'dy': ocell.MAY, 'roll': ocell.MAR}
elif otype=='DRIF':
outdict = {'name': ocell.NAME, 'length': ocell.L,
'dx': 0, 'dy': 0, 'roll': 0}
elif otype in ('RCAV','R3HC'):
outdict = {'name': ocell.NAME, 'length': ocell.L,
'dx': 0, 'dy': 0, 'roll': 0}
else:
outdict = {'name': ocell.NAME, 'length': ocell.L,
'dx': ocell.MAX, 'dy': ocell.MAY, 'roll': ocell.MAR}
# note: RFE and RGE are only used internally for parsing the OM file. they are not themselves propagated through tracy, although their effects in multipole strengths are considered
try:
outdict['RFE'] = ocell.RFE
except AttributeError:
pass
try:
outdict['RGE'] = ocell.RGE
except AttributeError:
pass
if ocell.APERTURE is not None:
try:
aperxa, aperya, shp = ocell.APERTURE
# for the moment, shp='E' elliptical is treated like rectangular 'R'
outdict.update({'aperxa': aperxa, 'aperya': aperya})
except ValueError:
# 4 entries instead of 3
aperx, apery, shp, shpa = ocell.APERTURE
if shpa=='+x':
outdict['aperxp'] = aperx
elif shpa=='-x':
outdict['aperxn'] = aperx
elif shpa=='+y':
# if aperyp is defined here, it will be understood by the tracy parser
raise NotImplementedError
elif shpa=='-y':
# if aperyn is defined here, it will be understood by the tracy parser
raise NotImplementedError
else:
raise SyntaxError('aperture specification not understood')
if otype=='DRIF':
outdict['etype'] = 'drift'
elif otype in ('RCAV','R3HC'):
outdict['etype'] = 'drift'
elif otype=='MMAP':
# Only MQCO implemented for the moment. 19.07.2022
# Update to include more
Qrange=None
SQrange=None
Srange=None
Orange=None
dk=0
dks=0
if ocell.OVERLAP:
for oe in ocell.OVERLAP:
if oe[0]=='MQCO':
Qrange=range(oe[1],oe[1]+oe[2])
dk=oe[3]
if oe[0]=='MQSK':
SQrange=range(oe[1],oe[1]+oe[2])
dks=oe[3]
if oe[0]=='MSXT':
Srange=range(oe[1],oe[1]+oe[2])
dk2=oe[3]
if oe[0]=='MOCT':
Orange=range(oe[1],oe[1]+oe[2])
dk3=oe[3]/(oe[2]*0.001)
if ocell.RGE or dk or dks:
fin=open(ocell.FILENAME,'r')
fout=open(ocell.FILENAME+'.'+ocell.NAME,'w')
il=0
for line in fin:
sline=line.split()
if len(sline)>10:
k1p=float(sline[10]) # k1 value without field error
k1n=k1p*(1+ocell.RGE)
k03p=float(sline[6])
k03n=k03p*(1+ocell.RGE) # Sextupole and quadrupole gradient error will be the same... let's accept it
k21p=float(sline[16])
k21n=k21p*(1+ocell.RGE)
k13p=float(sline[12])
k13n=k13p*(1+ocell.RGE) # The same for octupole
k31p=float(sline[22])
k31n=k31p*(1+ocell.RGE)
if Qrange:
if il in Qrange:
k1n=k1n+dk
sline[10]=str(k1n)
if SQrange:
if il in SQrange:
# Factor of 1/2, not clear why needed...
sline[15]=str(dks/2)
if Srange:
if il in Srange:
k21n=k21n+dk2
k03n=k03n-dk2/3.0
sline[6]=str(k03n)
sline[16]=str(k21n)
if Orange:
if il in Orange:
k13n=k13n-dk3
k31n=k31n+dk3
sline[12]=str(k13n)
sline[22]=str(k31n)
wline='len'
for si in range(1,len(sline)):
wline=wline+' '+sline[si]
wline=wline+'\n'
il=il+1
else:
wline=line
fout.write(wline)
fin.close()
fout.close()
tfile=ocell.FILENAME+'.'+ocell.NAME
else:
tfile=ocell.FILENAME
outdict['etype']='magtubefile'
outdict['filename']= tfile
#fgt=open(ocell.FILENAME,'r')
#for line in fgt:
# if 'driftlen' in line:
# sline=line.split()
#fgt.close()
# The following is experimental. 10.11.2021
# enclose in kick
kick_name = outdict['name']+'_E'
kick_elem = {'name': kick_name, 'etype': 'corrector', 'k0l': -deg2rad(ocell.ANGLE)*ocell.RFE/2}
# trick: when only a name(string) like kick_name is passed as a list element,
# it is agreed for onlinemodel_to_statements function,
# that it will just append the name to the sequence, not defining a new element
return [kick_elem, check_stadict(outdict), kick_name]
elif otype=='UIND':
print (ocell.NAME,ocell.L)
#outdict['etype'] = 'drift'
outdict.update({'etype': 'bend', 'angle': 0, 'k1': 0, 'e1': 0, 'e2': 0})
elif otype in ('MBEN', 'MBCF'):
k1 = ocell.K1*(1+ocell.RGE) if otype=='MBCF' else 0
outdict.update({'etype': 'bend', 'angle': deg2rad(ocell.ANGLE), 'k1': k1,
'e1': deg2rad(ocell.E1), 'e2': deg2rad(ocell.E2)})
# enclose in kick
kick_name = outdict['name']+'_E'
kick_elem = {'name': kick_name, 'etype': 'corrector', 'k0l': -outdict['angle']*ocell.RFE/2}
# trick: when only a name(string) like kick_name is passed as a list element,
# it is agreed for onlinemodel_to_statements function,
# that it will just append the name to the sequence, not defining a new element
return [kick_elem, check_stadict(outdict), kick_name]
elif otype=='MBSC':
# interpretation: multiple bends
outdict['etype'] = 'bend'
name = outdict.pop('name')
kick_name = name+'_E'
kick_elem = {'name': kick_name, 'etype': 'corrector', 'k0l': -deg2rad(ocell.TANGLE)*ocell.RFE/2}
# start sequence of elements with kick
li=[kick_elem]
# interior
lengths = outdict.pop('length') # it was a list of lengths
dxs = outdict.pop('dx')
dys = outdict.pop('dy')
for n, (length, angle, e1deg, e2deg, dx, dy) in enumerate(zip(lengths, ocell.ANGLE, ocell.E1, ocell.E2, dxs, dys)):
outdict.update({'name': f'{name}_{n}', 'length': length, 'angle': deg2rad(angle),
'e1': deg2rad(e1deg), 'e2': deg2rad(e2deg), 'dx': dx, 'dy': dy})
li.append(copy(check_stadict(outdict)))
# only kick_name string to end the list, see MBEN/MBCF entry
li.append(kick_name)
return li
elif otype=='MQUA':
outdict.update({'etype': 'quadrupole', 'k1': ocell.K1*(1+ocell.RFE)})
elif otype=='MQCO':
outdict.update({'etype': 'quadrupole', 'k1': ocell.K1*(1+ocell.RFE)})
elif otype=='MSXT':
outdict.update({'etype': 'sextupole', 'k2': ocell.K2*(1+ocell.RFE)})
elif otype=='MOCT':
outdict.update({'etype': 'sextupole', 'k3l': ocell.K3L*(1+ocell.RFE)})
elif otype in ('MCOX', 'MCOY'):
outdict['etype'] = 'corrector'
vertical = otype.endswith('Y')
length = outdict['length']
kick = ocell.VKICK if vertical else -ocell.HKICK
if length==0:
outdict['j0l' if vertical else 'k0l'] = kick
else:
outdict['j0' if vertical else 'k0'] = kick / length
elif otype in ('MKIK', 'MSEP'):
# interpretation: kicker set to zero
#outdict['etype'] = 'corrector'
#outdict['etype'] = 'drift'
#outdict['etype'] = 'corrector'
outdict.update({'etype': 'bend', 'angle': 0, 'k1': 0, 'e1': 0, 'e2': 0})
elif otype=='DBPM':
outdict['etype'] = 'monitor'
elif otype=='VCOL':
outdict['etype'] = 'drift'
elif otype=='NONE':
if outdict['length']==0.0:
return []
# interpretation: just treat it as a drift with length ocell.L (already in outdict)
outdict['etype'] = 'drift'
elif otype in ('GMRK', 'GSRC'):
if include_markers:
return [{'name': outdict['name'], 'etype': 'marker'}] #every other thing (apertures etc.) ignored
else:
return []
elif otype in ('EMALIGN', 'EWATCH'):
return [] #ignored
else:
#outdict.update({'etype': 'NOT IMPL'})
raise NotImplementedError(f'{otype = }, {ocell = }')
return [check_stadict(outdict)] # when you arriive here, list has only one entry
# for aperture settings, see OMFacility.Facility.setAperture()
def onlinemodel_to_statements(omf, cavity: int, undulator_spaces: bool):
"""
convert an instance omf of OMFacility.Facility into a list of statement-dictionaries, that can be read as input to the tracy-null parser
uses aperture information if it was set in onlinemodel by .setApertures
cavity: toggles inclusion of predefined cavity into sequence
undulator_spaces: toggles inclusion of predefined cavity into sequence
"""
statements = list() # a list of statement dictionaries
sequence = list() # names of elements
statements.append( {'name': 'energy', 'variable': '2.7'} ) # in every other place in this example, it can be a number instead of string..
usedDriftNames = dict() # to look up if a drift name has already been used
for ocell in omf.Ring:
if isinstance(ocell, str):
pass
else:
# basic statement parsing (no overlays and special drift treatments, but already step-wise bends):
new_statements = ocell_to_dict(ocell)
for stadict in new_statements:
if isinstance(stadict, str): #if list item is a string, just append it to sequence
sequence.append(stadict)
continue # skip the rest, to next iteration
name = stadict['name']
if stadict['etype']=='drift':
if name in usedDriftNames:
usedDriftNames[name] += 1
name = f'{name}_{usedDriftNames[name]}'
stadict['name'] = name
else:
usedDriftNames[name] = 0
# impose overlaps. Note: applied for any name that occurs in OverlapC, not just MQCO
if name in omf.OverlapC:
for ov_ocell in omf.OverlapC[name]:
# set additional skew quad (j1) or octupole (k3) strength of existing multipole
if ov_ocell.SN=='MQSK':
# assumption: skew quadrupole strength oe.k1*(..) is INTEGRATED strength j1*l like in OMElegant.py,
# is this a typo and should read .K1L similar to octupole?
#stadict['j1'] = ov_ocell.K1 * (1+stadict['RFE']) / stadict['length']
stadict['j1'] = ov_ocell.K1 * (1+ov_ocell.RFE) / stadict['length']
elif ov_ocell.SN=='MOCT':
stadict['k3'] = ov_ocell.K3L* (1+ov_ocell.RFE) / stadict['length']
# remove temporary entries
stadict.pop('RFE', 0.0)
stadict.pop('RGE', 0.0)
sequence.append(stadict['name'])
statements.append(stadict)
# now come some modifiers
if cavity>=0:
# the online model so far has no cavity, so it is appended manually here
#stadict = {'name': 'cav', 'etype': 'cavity',
# 'freq': 480*2.99792458e8/288.0, 'volt': 1.44e6, 'phase': deg2rad(151.8), 'h': 480}
# No phase entry/ Bernard's mail on 1.08.2021
stadict = {'name': 'cav', 'etype': 'cavity',
'freq': 480*2.99792458e8/288.0, 'volt': 1.44e6, 'h': 480}
statements.append(stadict)
sequence.insert(cavity, 'cav') # insert at beginning of list
if undulator_spaces: # use this before exporting to Simona for specific undulator locations
# BR: this is just an example for a single blank unulator space, i don't know the actual lengths
# note that the length of the replaced elements should match those of the new one
stadict = {'name': 'fancy_costly_undulator', 'etype': 'multipole', 'length': 1.23}
statements.append(stadict)
# this will throw a warning, as there is no element 'abcd' present in the sequence:
n = replace_subsequence(sequence, ('ARS08-DRIF-1970', 'abcd'), ('fancy_costly_undulator',))
print(f'{n} replacements for ' + stadict['name'])
# here i added keys for k1 (quadruople), j1 (skew quadrupole) and k3 (octupole)
stadict = {'name': 'cheap_undulator', 'etype': 'multipole', 'length': 4.56,
'k1': 0.2, 'j1': 89.7, 'k3': 42.0}
statements.append(stadict)
# this will show 1 replacement
n = replace_subsequence(sequence, ('ARS01-MKIK-0140', 'ARS01-DRIF-0140_1'), ('cheap_undulator',))
print(f'{n} replacements for ' + stadict['name'])
# sequence is just packed as a statement an appended
statements.append( {'name': 'ring', 'sequence': sequence} )
return statements
def get_onlinemodel(masterfile: str, PF: dict, Aperture: bool):
# its just the things you normally do with onlinemodel
# this needs to be executed in your onlinemodel folder.
# (idea for future: make OMFacility a module that can be installed in environment)
omf=Facility(masterfile)
#if omf.Version!=PF['Pversion'].upper():
# print (omf.Version)
# print (Pversion.upper())
# raise Exception('Version of Master layout and Pattern file does not match.')
omf.TracyFM=True
omf.Layout('full',-1) # StrPat is for the ring with no cycle. CYCLE marker appears with a switch of -1 (or any negtive int)
StrPat={}
Tdone=[]
for k in PF['Pattern'].keys():
StrPat[k]=[]
for e in PF['Pattern'][k]:
print (e)
StrPat[k].append(PF['Strength'][e])
if e not in Tdone:
Tdone.append(e)
for k in PF['Strength'].keys():
if k not in Tdone:
StrPat[k]=PF['Strength'][k]
del(StrPat['MKIK'])
omf.setStrength(StrPat)
if Aperture:
print('using aperture')
omf.setAperture(Aperture)
return omf
def replace_elems(omf, sub_sequence: list, replace_with: list):
# replace routine as in trap.readers but specifically for 'statements' that is generated through this OM2TN module
# defs is the definition of elements that are in replace_with
compatible=0
for i in range(0,len(omf.Ring)):
if type(omf.Ring[i])==str:
# Assumed that the first element of sub_sequence is not CYCLE
pass
elif omf.Ring[i].NAME==sub_sequence[0]:
s_idx=i
for j in range(1,len(sub_sequence)):
if omf.Ring[i+j].NAME==sub_sequence[j]:
compatible=1
else:
compatible=0
break
if not compatible:
raise Exception('Sub-sequence does not match to OMF layout')
omf.Ring=omf.Ring[0:s_idx]+replace_with+omf.Ring[s_idx+len(sub_sequence):]
return omf
def FieldMapLengthCorrection(omf):
# The length of the field map is shorter by a half step size in tracy-null.
# If self.get_onlinemodel is used (or Facility instance is geneerated with omf.TracyFM=True)
# this missing length is adjusted by Facility.individuateS2.
# If not, this routine needs to be called.
if not omf.Ring:
return None
imap=[]
for i in range(0,len(omf.Ring)):
c=omf.Ring[i]
if type(c)==str:
pass
else:
if c.SN=='MMAP':
if c.RL:
imap.append(i)
while imap:
c=omf.Ring[imap[-1]]
pr1={}
pr1['L']=abs(c.RL)
pr1['RL']=0
pr1['SN']='DRIF'
pr1['NAME']=c.NAME.replace('MMAP','DRIF')
pr1['INDEX']=c.INDEX
pr1['TYPE']='DRIFT'
pr1['DNAME']='DR'+c.TYPE # Name that appears in OPA file
pr1['S']=c.S-abs(c.RL) # Is that correct? not c.S? The same is in OMFacility...
pr1['MS']=c.S-abs(c.RL)/2
pr1['SANGLE']=c.SANGLE
pr1['MANGLE']=c.SANGLE
pr1['SECTOR']=c.SECTOR
pr1['GIRDER']=c.GIRDER
pr1['RESERVE']=1
pr2=deepcopy(pr1)
pr2['S']=c.S+c.TL # Is that correct? -c.RL might be missing? The same is in OMFacility...
pr2['MS']=pr2['S']+c.RL/2
pr2['RESERVE']=1
if 'ANGLE' in c.__dict__.keys():
pr2['SANGLE']=c.SANGLE+c.ANGLE
else:
pr2['SANGLE']=c.SANGLE
if c.RL<0:
drif=Drift(pr1)
omf.Ring.insert(imap[-1],drif)
else:
drif=Drift(pr2)
omf.Ring.insert(imap[-1]+1,drif)
imap.pop(-1)
return omf
-715
View File
@@ -1,715 +0,0 @@
import PyCafe
from OMSLS2Magnet import *
import pickle
from time import sleep
class EpicsChannel:
def __init__(self,S2,VA=False,Default=False):
# "Dictionary" for the epics channels,
# the names of which may be changing
# S2 is an instance of OMFacility
# The names are temporary. Change them only in this script.
# The actual epics name shoud not be used
# in other script for consistency.
if VA:
self.pf=VA+'-'
else:
self.pf=''
self.cafe=PyCafe.CyCafe()
self.cafe.init()
self.cyca=PyCafe.CyCa()
if S2:
# Relevant modules
self.S2=S2
self.SM=SLS2Magnet()
# Some important channels
# RF frequency
self.frf=self.pf+'AGARF-TIM:BO-FREQ-SET' # This should be used. 'AGARF' is not typo
#self.frf=self.pf+'AGERF-MO01:FREQ-SET'
# from 0.1 Hz to 100000(?) Hz == 10 Hz/s to 10 MHz/s
self.frfrate=self.pf+'AGERF-MO01:FREQ-STEP'
# Trigger
self.TriggerEnable=self.pf+'AGETI-CVME-MASTER-TMA:SR-Inj-Status-Sel'
#self.Trigger=self.pf+'ALIRF-VME-A-GUN:CH1-SWTRIG' # Gun trigger
#self.Trigger=self.pf+'ALIRF-VME-A-GUN:CH1-MODE' # Gun trigger
self.Trigger=self.pf+'AGETI-CVME-MASTER-TMA:Evt-10-Ena-Sel'
self.BPMTrigger='AGETI-CVME-MASTER-TMA:Evt-11-Ena-Sel'
# This may be used to keep the booster and the linac running. Masked -> Down ramp, not extracted
self.TriggerMask=self.pf+'AGETI-CVME-MASTER-TMA:Evt-BO-Ext-Mask-SP'
self.ch={}
self.Handle={}
#TbT
self.TbTopen=False
# Edit here in case Epics channel is changed...
self.Atr_PS={'I':'I-SET',
'I-READ':'I-READ',
'ON':'ONOFF'}
# Kicker power supply
self.Atr_KPS={'I':'I-SET',
'I-READ':'I-READ',
'DELAY':'DELAY',
'ON':'ONOFF'}
if self.pf:
self.Atr_BPM={'X':'X',
'Y':'Y',
'Q':'Q',
'OX':'OFFS-X',
'OY':'OFFS-Y',
'TBTX':'TBT-X',
'TBTY':'TBT-Y',
'TBTZ':'TBT-Z',
'TBTQ':'TBT-Q',
'MODE':'MODE'}
else:
self.Atr_BPM={'X':'POS-STG2-X',
'Y':'POS-STG2-Y',
'Q':'POS-STG2-CHARGE',
'OX':'OFF-BBA-X',
'OY':'OFF-BBA-Y',
'OMX':'OFF-PU-X', # Mechanical
'OMY':'OFF-PU-Y',
'OEX':'OFF-EL-X', # Electronics
'OEY':'OFF-EL-Y',
'TBTX':'TBT-STG0-X',
'TBTY':'TBT-STG0-Y',
'TBTQ':'TBT-STG0-Q',
'TBTX2':'DAQ-BEAM-STG0-X',
'TBTY2':'DAQ-BEAM-STG0-Y',
'TBTQ2':'DAQ-BEAM-STG0-CHARGE',
'REF-OP-X':'X-B-REF-OP',
'REF-OP-Y':'Y-B-REF-OP',
'MODE':'MODE'}
self.Atr_ID={'GAP':'GAP-SET',
'GAP-READ':'GAP-READ'}
self.Atr_RF={'Phase':'PHASE-SHIFT'}
# Phase of 500 MHz (for all 4 cavities)
self.rfphase=self.pf+'ARS05-RSYS-0000:'+self.Atr_RF['Phase']
if Default and S2:
def bySN(SN,attribute):
elem=S2.listElement_SN(SN)
#for s in attribute:
# self.ch[SN+'_'+s[0]]=[self.pf+e+':'+s[1] for e in elem]
for k in attribute.keys():
self.ch[SN+'_'+k]=[self.pf+e+':'+attribute[k] for e in elem]
def byTYPE(TYPE,attribute,bothIO):
elem=S2.listElement_TYPE(TYPE,bothIO)
#for s in attribute:
# self.ch[TYPE+'_'+s[0]]=[self.pf+e+':'+s[1] for e in elem]
for k in attribute.keys():
self.ch[TYPE+'_'+k]=[self.pf+e+':'+attribute[k] for e in elem]
#def byTYPE(TYPE,bothIO):
# Atr[i]=['Name easy to remember','Real Epics attribute (it is subject to be changed)']
Atr=self.Atr_PS
bySN('MQUA', Atr)
for t in S2.SN['MQUA']:
byTYPE(t, Atr, True)
#bySN('MSXT', Atr)
#for t in S2.SN['MSXT']:
# byTYPE(t, Atr, True)
# Sextupole are in families...
SFamily,Dad=S2.getSextFamily()
for k in Atr.keys():
self.ch['MSXT_'+k]=[self.pf+e+':'+Atr[k] for e in Dad]
for t in S2.SN['MSXT']:
elem=S2.listElement_TYPE(t,True)
for k in Atr.keys():
self.ch[t+'_'+k]=[]
for e in elem:
if e in Dad:
self.ch[t+'_'+k].append(self.pf+e+':'+Atr[k])
bySN('MOCT', Atr)
for t in S2.SN['MOCT']:
byTYPE(t, Atr, True)
bySN('MCOX', Atr)
for t in S2.SN['MCOX']:
byTYPE(t, Atr, True)
bySN('MCOY', Atr)
for t in S2.SN['MCOY']:
byTYPE(t, Atr, True)
bySN('MQCO', Atr)
for t in S2.SN['MQCO']:
byTYPE(t, Atr, True)
bySN('MQSK', Atr)
for t in S2.SN['MQSK']:
byTYPE(t, Atr, True)
Atr=self.Atr_KPS
byTYPE('KIN', Atr, False)
Atr=self.Atr_ID
bySN('UIND', Atr)
Atr=self.Atr_BPM
if VA:
Atr['SIMX']='SIM-X'
Atr['SIMY']='SIM-Y'
bySN('DBPM', Atr)
for t in S2.SN['DBPM']:
byTYPE(t, Atr, True)
# For convenience.
self.ch['BPM_X']=self.ch['DBPM_X']
self.ch['BPM_Y']=self.ch['DBPM_Y']
self.ch['BPM_OX']=self.ch['DBPM_OX']
self.ch['BPM_OY']=self.ch['DBPM_OY']
self.ch['BPM_MODE']=self.ch['DBPM_MODE']
self.ch['BPM_TBTX']=self.ch['DBPM_TBTX']
self.ch['BPM_TBTY']=self.ch['DBPM_TBTY']
self.ch['BPM_TBTQ']=self.ch['DBPM_TBTQ']
if VA:
# Beam position from simultion
# X=SIM-X + OFFS-X
# The operator in the above equation (+ or -) is subject to be confirmed.
self.ch['BPM_SIMX']=self.ch['DBPM_SIMX']
self.ch['BPM_SIMY']=self.ch['DBPM_SIMY']
# creating handles
self.createHandle(self.ch.keys())
def get(self, Channel,stat=False,dtype='native'):
if type(Channel)==list:
v=[]
for n in Channel:
vi=self.cafe.get(n,dt=dtype)
v.append(vi)
return v
elif Channel in self.Handle.keys():
[values, s, slist]=self.cafe.getGroup(self.Handle[Channel],dtype)
if stat:
return values, s, slist
else:
return values
elif ':' in Channel:
v=self.cafe.get(Channel,dt=dtype)
return v
else:
print ('Error: Input to EC.get "'+Channel+'" is wrong')
return -1
def getNtimes(self, Channel,N,stat=False,dtype='native',waiting=1):
if type(Channel)==list:
v=[[] for _ in range(0,len(Channel))]
for nm in range(0,N):
for i in range(0,len(Channel)):
vi=self.cafe.get(Channel[i],dt=dtype)
v[i].append(vi)
sleep(waiting)
vstd=[]
vmean=[]
for vi in v:
vi=np.array(vi)
vstd.append(vi.std())
vmean.append(vi.mean())
return v.tolist(),vmean.tolist(),vstd.tolist()
elif Channel in self.Handle.keys():
values=[]
s=[]
slist=[]
for i in range(0,n):
[valuesi, si, slisti]=self.cafe.getGroup(self.Handle[Channel],dtype)
sleep (waiting)
values.append(i)
s.append(si)
slist.append(slisti)
values=np.array(values)
values=values.transpose()
s=np.array(s)
s=s.transpose()
slist=np.array(slist)
slist=slist.transpose()
vstd=[]
vmean=[]
for vi in v:
vi=np.array(vi)
vstd.append(vi.std())
vmean.append(vi.mean())
if stat:
return values.tolist(), vmean.tolist(), vstd.tolist(),s.tolist(), slist.tolist()
else:
return values.tolist(), vmean.tolist(), vstd.tolist()
elif ':' in Channel:
v=[]
for i in range(0,N):
vi=self.cafe.get(Channel,dt=dtype)
v.append(vi)
sleep(waiting)
v=np.array(v)
vmean=v.mean()
vstd=v.std()
return v.tolist(),vmean.tolist(),vstd.tolist()
else:
print ('Error: Input to EC.get "'+Channel+'" is wrong')
return -1
def put(self, Channel, value):
if type(Channel)==list:
for i in range(0,len(Channel)):
self.cafe.set(Channel[i],value[i],dt=dtype)
return v
elif Channel in self.Handle.keys():
if len(value)!=len(self.ch[Channel]):
print ('Error: the number of input values does not match to the number of channels of the group, '+Channel)
return
s,slist=self.cafe.setGroup(self.Handle[Channel],value)
return s, slist
elif ':' in Channel:
s=self.cafe.set(Channel,value)
return s
def createHandle(self,key):
self.cafe.openGroupPrepare()
if type(key)=='str':
key=[key]
for k in key:
if k in self.ch.keys() and k not in self.Handle.keys():
if self.pf:
for i in range(0,len(self.ch[k])):
if self.pf not in self.ch[k][i]:
self.ch[k][i]=self.pf+self.ch[k][i]
self.Handle[k]=self.cafe.grouping(k, self.ch[k])
if len(key)<30:
wait=len(key)*0.5
else:
wait=20
self.cafe.openGroupNowAndWait(wait)
def getHandle(self,key):
if key not in self.Handle.keys():
print('OMEpics: No such a handle',key)
return None
return self.Handle[key]
def createGroup(self,key,Channel):
if self.pf:
for i in range(0,len(Channel)):
c=Channel[i]
if self.pf not in c:
Channel[i]=self.pf+c
self.cafe.openGroupPrepare()
Handle=self.cafe.grouping(key, Channel)
self.cafe.openGroupNowAndWait(1)
self.Handle[key]=Handle
self.ch[key]=Channel
return Handle
# Interface to the machine
def updateMachine_TYPE(self,TYPE):
KL=self.S2.getKL_TYPE(TYPE,bothIO=True)
I=self.SM.KL2I(TYPE,KL)
if TYPE in self.S2.SN['MQUA']+self.S2.SN['MSXT']+['KIN']:
I=list(np.abs(np.array(I))) # All quad and sext PSs are unipolar
self.put(TYPE+'_I',I)
def updateModel_TYPE(self,TYPE):
# Machine to Model
I,s,status=self.get(TYPE+'_I',stat=True)
KL=self.SM.I2KL(TYPE,I)
elem=self.S2.getElement_TYPE(TYPE,bothIO=True)
for i in range(0,len(elem)):
e=elem[i]
if e.SN=='MQUA' or e.SN=='MSXT' or e.TYPE=='KIN':
if e.POL==-1:
KL[i]=-KL[i]
#print (e.SN, e.POL, KL[i])
#if TYPE=='QSOS2A':
# print ('debuggingggg',KL)
self.S2.setKL_TYPE(TYPE,KL,bothIO=True)
def updateQuad_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
for t in self.S2.SN['MQUA']:
self.updateMachine_TYPE(t)
def updateQuad_Model(self):
# Machine to Model
for t in self.S2.SN['MQUA']:
self.updateModel_TYPE(t)
return self.S2
def updateSext_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
SFamily,Dad=self.S2.getSextFamily()
for t in self.S2.SN['MSXT']:
#self.updateMachine_TYPE(t)
elem=self.S2.getElement_TYPE(t,bothIO=True)
KL=[]
for e in elem:
if e.NAME in Dad:
KL.append(e.KL())
I=self.SM.KL2I(t,KL)
I=list(np.abs(np.array(I))) # All sext PSs are unipolar
self.put(t+'_I',I)
def updateSext_Model(self):
# Machine to Model
SFamily,Dad=self.S2.getSextFamily()
for t in self.S2.SN['MSXT']:
#self.updateModel_TYPE(t)
I,s,status=self.get(t+'_I',stat=True)
KL=self.SM.I2KL(t,I)
#elem=self.S2.getElement_TYPE(TYPE,bothIO=True)
for n in Dad:
e=self.S2.getElement(n)
if e.TYPE==t:
if e.POL==-1:
KL[0]=-KL[0]
e.setKL(KL[0])
KL.pop(0) # fine to consume
self.S2.bindSextFamily()
return self.S2
def updateOct_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
for t in self.S2.SN['MOCT']:
self.updateMachine_TYPE(t)
def updateOct_Model(self):
# Machine to Model
for t in self.S2.SN['MOCT']:
self.updateModel_TYPE(t)
return self.S2
def updateCH_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
for t in self.S2.SN['MCOX']:
self.updateMachine_TYPE(t)
def updateCH_Model(self):
# Machine to Model
for t in self.S2.SN['MCOX']:
self.updateModel_TYPE(t)
return self.S2
def updateCV_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
for t in self.S2.SN['MCOY']:
self.updateMachine_TYPE(t)
def updateCV_Model(self):
# Machine to Model
for t in self.S2.SN['MCOY']:
self.updateModel_TYPE(t)
return self.S2
def updateCorr_Machine(self,S2):
# Model to Machine
self.updateCH_Machine(S2)
self.updateCV_Machine(S2)
def updateCorr_Model(self):
# Machine to Model
self.updateCH_Model()
self.updateCV_Model()
return self.S2
def updateQcorr_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
for t in self.S2.SN['MQCO']:
self.updateMachine_TYPE(t)
def updateQcorr_Model(self):
# Machine to Model
for t in self.S2.SN['MQCO']:
self.updateModel_TYPE(t)
return self.S2
def updateSkewQ_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
for t in self.S2.SN['MQSK']:
self.updateMachine_TYPE(t)
def updateSkewQ_Model(self):
# Machine to Model
for t in self.S2.SN['MQSK']:
self.updateModel_TYPE(t)
return self.S2
def updateInjKicker_Machine(self,S2):
# Model to Machine
self.S2=S2 # synchronization
self.updateMachine_TYPE('KIN')
def updateInjKicker_Model(self):
# Machine to Model
self.updateModel_TYPE('KIN')
return self.S2
def updateMachine(self,S2):
# Model to Machine, all electromagnets
# Superconducting superbend is not included
self.updateQuad_Machine(S2)
self.updateSext_Machine(S2)
self.updateOct_Machine(S2)
self.updateCorr_Machine(S2)
self.updateQcorr_Machine(S2)
self.updateSkewQ_Machine(S2)
self.updateInjKicker_Machine(S2)
def updateModel(self):
# Machine to Model, all electromagnets
# Superconducting superbend is not included
self.updateQuad_Model()
self.updateSext_Model()
self.updateOct_Model()
self.updateCorr_Model()
self.updateQcorr_Model()
self.updateSkewQ_Model()
self.updateInjKicker_Model()
return self.S2
def getTbT(self,Nturn=4000):
# Used in Virtual accelerator but general function
# "TbT DAQ server" from Jan may be available in the future
if Nturn>4000:
Nturn=4000
if self.ch.keys():
sx,vx,v=self.cafe.getGroup(self.getHandle('BPM_TBTX'))
sy,vy,v=self.cafe.getGroup(self.getHandle('BPM_TBTY'))
if vx and vy:
return sx,sy
else:
print ('Error: Something wrong with TbT channels')
return -1,-1
else:
print ('Error: Default group must be listed before getting TbT data.')
return -1,-1
def putTbT(self,X,Y,Q):
# Method for Virtual Accelerator
if self.ch.keys():
self.cafe.setGroup(self.getHandle('BPM_TBTX'),X)
self.cafe.setGroup(self.getHandle('BPM_TBTY'),Y)
self.cafe.setGroup(self.getHandle('BPM_TBTQ'),Q)
else:
print ('Error: Default group must be listed before putting TbT data.')
return -1
#self.cafe.set(EpicsName,)