Source code for orbit.parsers.sad_parser

import os
import sys
import re
import math


class _possibleElementType:
    """This class keeps all possible element's types"""

    def __init__(self):
        self.__names_type = []
        self.__names_type.append("MARK")
        self.__names_type.append("MONI")
        self.__names_type.append("DRIFT")
        self.__names_type.append("APERT")
        self.__names_type.append("BEND")
        self.__names_type.append("SEXT")
        self.__names_type.append("QUAD")
        self.__names_type.append("MULT")
        self.__names_type.append("CAVI")

    def __del__(self):
        del self.__names_type

    def checkType(self, type_in):
        type = type_in.upper()
        if self.__names_type.count(type) == 0:
            print("Error of creating lattice element.")
            print("There can not be an element with type:", type)
            print("Stop.")
            sys.exit(0)
        return type


# ===============================================================


[docs]class SAD_LattElement: """An Arbitrary Element in the Lattice""" _typeChecker = _possibleElementType() def __init__(self, name, Typename): """Create instance with name, typeName and type""" self.__name = name self.__type = self._typeChecker.checkType(Typename) self.__par = {} def __del__(self): del self.__par
[docs] def getName(self): """Returns name of the element""" return self.__name
[docs] def getType(self): """Returns type of the element""" return self.__type
[docs] def setType(self, tp): """Sets the type of the element without checking""" self.__type = tp
[docs] def addParameter(self, nameOfPar, parVal): self.__par[nameOfPar] = parVal
[docs] def getParameter(self, nameOfPar): if (nameOfPar in self.__par) == 0: print("class SAD_LattElement, method getParameter") print("The name of Element =", self.__name) print("The type of Element =", self.__type) print("The Element's key-val =", self.__par) print("This Element does not have Parameter=", nameOfPar) print("Stop.") sys.exit(0) return self.__par[nameOfPar]
[docs] def hasParameter(self, nameOfPar): return nameOfPar in self.__par
[docs] def getParameters(self): """Returns the dictionary with (key=name, val) pairs""" return self.__par
[docs] def getElements(self): """Returns list of elements (only one here)""" elements = [] elements.append(self) return elements
# ====================================================================
[docs]class SAD_LattLine: """ An Arbitrary Line in the Lattice. It includes SAD Lattice Lines and Elements """ def __init__(self, name): """Create instance with list of lines or elements""" self.__name = name self.__items = [] self.__sign_arr = [] def __del__(self): del self.__name del self.__items
[docs] def getType(self): return "LINE"
[docs] def getName(self): """Returns name of the line""" return self.__name
[docs] def addItem(self, item, sign=+1): """Adds a line or element to this line with cetain direction.""" self.__items.append(item) self.__sign_arr.append(sign)
[docs] def getItems(self): """Returns list with elements and lines""" return self.__items[:]
[docs] def getLinesDict(self): """Returns the dictionary with all lattice lines inside, recursive.""" dict = {} for item in self.__items: if item.getType() == self.getType(): dict[item.getName()] = item return dict
[docs] def getElements(self): """Returns list of elements""" elements = [] # print "debug ================= name line = ",self.getName() for i in range(len(self.__items)): item = self.__items[i] sign = self.__sign_arr[i] # print " debug item=",item.getName()," sign=",sign elems = item.getElements() if sign == -1: elems.reverse() for el in elems: elements.append(el) return elements
class _SAD_String: """ The line of a SAD file. It could be one of three types: variable, accelerator line, or accelerator element. """ def __init__(self): """ Constructor. Creates the SAD file line class instance. """ self.__line = "" self.__type = None def setLine(self, line): self.__line = line def getLine(self): return self.__line def setType(self, type): self.__type = type def getType(self): return self.__type class _variable: """ The SAD variable class. It keeps initial string (line) from SAD file. """ def __init__(self): self._name = None self._expression = "" self._value = None def getType(): """ Method. It is static method of this class. It returns the name of the type. """ return "variable" getType = staticmethod(getType) def getValue(self): """ Method. It returns the numerical value of this variable. """ return self._value def setValue(self, val): """ Method. It sets the numerical value of this variable. """ self._value = val def getName(self): return self._name def getExpression(self): return self._expression def parseLine(self, line_init): """ Method. It does the first parsing of the initial string. """ # divide string onto two parts: name of value and value names_vals = re.compile(r"\A\s*([^=\s]+)\s*={1}(.*)").findall(line_init) if len(names_vals) != 1 and len(names_vals[0]) != 2: print("The SAD file line=", line_init) print("It cannot be parsed. It is not variable declaration") print("Stop.") sys.exit(0) self._name = names_vals[0][0] # replace all math cos,sin, etc by math.cos, math.sin, GEV, DEG etc self._expression = StringFunctions.replaceMath(names_vals[0][1]) class _element: """ The SAD element class. It also keeps initial string (line) from SAD file. """ def __init__(self): self._name = None self._elementType = None self._expression = "" self._parameters = {} self._numParameters = {} def getType(): """ Method. It is static method of this class. It returns the name of the type. """ return "element" getType = staticmethod(getType) def getName(self): return self._name def getExpression(self): return self._expression def getElementType(self): return self._elementType def getParameters(self): """ Method. It returns the dictionary with key:string pairs """ return self._parameters def getNumParameters(self): """ Method. It returns the dictionary with {key:(numeric value)} pairs """ return self._numParameters def parseLine(self, line_init): """ Method. It does the first parsing of the initial string. """ type_name_arr = re.compile(r"\A\s*(\S+)\s+([^=\s]+)\s*={1,1}").findall(line_init) if len(type_name_arr) < 1: print("The SAD file line:", line_init) print("The cannot find TYPE ELEMENT_NAME pair at the beginning!") print("Stop.") sys.exit(0) type_name = type_name_arr[0] type = type_name[0] name = type_name[1] self._name = name self._elementType = type self._expression = line_init # ================================================== # let's get key-val pairs # ================================================== indStart = line_init.find(name) line_init = line_init[indStart:] (ind0, ind1) = StringFunctions.findBracketsPos(line_init) elemLine = line_init[ind0 + 1 : ind1] keys = re.compile(r"\s*([^=\s]+)\s*={1,1}").findall(elemLine) for i in range(len(keys)): i_start = elemLine.find(keys[i]) i_stop = len(elemLine) if i != (len(keys) - 1): i_stop = elemLine.find(keys[i + 1]) vals = re.compile(r"\s*[^=\s]+\s*={1,1}(.*)").findall(elemLine[i_start:i_stop]) if len(vals) != 1: print("The SAD file line:", line_init) print("The sub-string for parsing:", elemLine[i_start:i_stop]) print("It cannot be parsed. Cannot parse the key ", keys[i], " value") print("Stop.") sys.exit(0) self._parameters[keys[i].upper()] = vals[0] self._numParameters[keys[i].upper()] = vals[0] class _accLine: """ The SAD accelerator class. It also keeps initial string (line) from SAD file. The component lines could have reverse order of element. We keep sign in compSign array. """ def __init__(self): self._name = None self._expression = "" self._components = [] def getType(): """ Method. It is static method of this class. It returns the name of the type. """ return "accLine" getType = staticmethod(getType) def getName(self): return self._name def getExpression(self): return self._expression def getComponents(self): """ Method. It returns the set with (components' names, sign) The sign define the order of elements from sub-line. The sign +- 1. The -1 means the revers order. """ return self._components def parseLine(self, line_init): """ Method. It does the first parsing of the initial string. Parses the SAD file line with lattice line definition. """ # the name was found before name = re.compile(r"\A\s*([^=\s]+)\s*={1,1}").findall(line_init)[0] self._name = name (ind0, ind1) = StringFunctions.findBracketsPos(line_init) lineLine = line_init[ind0 + 1 : ind1] self._expression = lineLine # define names of components (lines or elements) line_names = re.compile(r"\s*(\S+)\s*").findall(lineLine) # print "debug line_names=",line_names # ========================================= # deal with the N*name expressions # ========================================= line_names_new = [] for it_in in line_names: sign = +1 it = it_in if it.find("-") == 0: sign = -1 it = it_in[1:] n_name = re.compile(r"([\d]+?)\*{1}(.*)").findall(it) if len(n_name) > 0: n_rep = int(n_name[0][0]) it_new = n_name[0][1] # print "debug n=",n_rep," name=",name," rep_name=",it_new for i in range(1, n_rep + 1): line_names_new.append((it_new, sign)) else: line_names_new.append((it, sign)) # print "debug line name=", self._name ," _components=",line_names_new self._components = line_names_new class StringFunctions: """ This class defines the set of static string functions. """ def calculateString(self, str_in, localDict): """ Method. It returns a tuple (True,value) if the expression can be evaluated and (False,None) otherwise. """ try: val = eval(self.replaceMath(str_in), globals(), localDict) return (True, val) except: return (False, None) calculateString = classmethod(calculateString) def replaceMath(self, str_in): """ Method. It replaces math symbols to make them readable for python eval(). """ # replace .e by .0e str_out = re.sub(r"\.e", ".0e", str_in) str_out = str_out.strip() # check the math operatons str_out = re.sub(r"sin\(", "math.sin(", str_out) str_out = re.sub(r"SIN\(", "math.sin(", str_out) str_out = re.sub(r"cos\(", "math.cos(", str_out) str_out = re.sub(r"COS\(", "math.cos(", str_out) str_out = re.sub(r"tan\(", "math.tan(", str_out) str_out = re.sub(r"TAN\(", "math.tan(", str_out) str_out = re.sub(r"exp\(", "math.exp(", str_out) str_out = re.sub(r"EXP\(", "math.exp(", str_out) str_out = re.sub(r"log\(", "math.log(", str_out) str_out = re.sub(r"LOG\(", "math.log(", str_out) str_out = re.sub(r"acos\(", "math.acos(", str_out) str_out = re.sub(r"ACOS\(", "math.acos(", str_out) str_out = re.sub(r"asin\(", "math.asin(", str_out) str_out = re.sub(r"ASIN\(", "math.asin(", str_out) str_out = re.sub(r"atan\(", "math.atan(", str_out) str_out = re.sub(r"ATAN\(", "math.atan(", str_out) str_out = re.sub(r"sqrt\(", "math.sqrt(", str_out) str_out = re.sub(r"SQRT\(", "math.sqrt(", str_out) # in SAD file we can have GEV and DEG res = re.compile(r"(.*)\s+GEV\s*").findall(str_out) if len(res) == 1: str_out = res[0] res = re.compile(r"(.*)\s+gev\s*").findall(str_out) if len(res) == 1: str_out = res[0] res = re.compile(r"(.*)\s+DEG\s*").findall(str_out) if len(res) == 1: str_out = "(" + res[0] + ")*math.pi/180." res = re.compile(r"(.*)\s+deg\s*").findall(str_out) if len(res) == 1: str_out = "(" + res[0] + ")*math.pi/180." return str_out replaceMath = classmethod(replaceMath) def findLastBracketPos(self, line, nPos, nOpen): """The method will find a last closing bracket.""" for ip in range(nPos, len(line)): if line[ip] == "(": nOpen = nOpen + 1 if line[ip] == ")": nOpen = nOpen - 1 if nOpen == 0: return ip return -1 findLastBracketPos = classmethod(findLastBracketPos) def findFirstBracketPos(self, line, nPos): """The method will find a first openning bracket.""" for ip in range(nPos, len(line)): if line[ip] == "(": return ip return -1 findFirstBracketPos = classmethod(findFirstBracketPos) def findBracketsPos(self, line): """The method returns the tuple with ( and ) positions.""" i0 = self.findFirstBracketPos(line, 0) if i0 == -1: return (-1, -1) i1 = self.findLastBracketPos(line, i0, 0) if i1 == 1: return (-1, -1) return (i0, i1) findBracketsPos = classmethod(findBracketsPos) # ====================================================================
[docs]class SAD_Parser: """SAD parser""" _typeChecker = _possibleElementType() SADFilePath = "" def __init__(self): """Create instance of the SAD_Parser class""" self.__SAD_Strings = [] self.__accValues = [] self.__accElements = [] self.__accLines = [] self.__unknownLines = [] # old style lattice elements and lines self.__lattElems = [] self.__lattLines = [] def __del__(self): del self.__SAD_Strings
[docs] def parse(self, SADfileName): # 1-st stage read SAD file into the lines array self.SADFilePath = os.path.dirname(SADfileName) fileName = os.path.basename(SADfileName) # the initilize can be recursive if there are nested SAD files self.__init__() self.initialize(fileName) # ----------------------------------------------------------- # The SADLines has all lines # Let's create values, elements, and accelerator lines arrays # ----------------------------------------------------------- for line in self.__SAD_Strings: if line.getType() == _accLine.getType(): accLine = _accLine() accLine.parseLine(line.getLine()) self.__accLines.append(accLine) continue if line.getType() == _variable.getType(): var = _variable() var.parseLine(line.getLine()) self.__accValues.append(var) continue if line.getType() == _element.getType(): elem = _element() elem.parseLine(line.getLine()) self.__accElements.append(elem) continue print("Error in parsing the SAD file.") print("Cannot parse the line:", line.getLine()) print("Stop.") sys.exit(0) # print "debug size values=",len(self.__accValues) # print "debug size elements=",len(self.__accElements) # print "debug size accLines=",len(self.__accLines) # ---------------------------------------------------------- # Check if there is a redefinition of elements or variables # ---------------------------------------------------------- dict = {} for var in self.__accValues: if var.getName() in dict: print("Warning the variable:", var.getName(), " was redefined. Are you are sure?") dict[var.getName()] = var dict = {} for elem in self.__accElements: if elem.getName() in dict: print("Warning the element:", elem.getName(), " was redefined. Are you are sure?") dict[elem.getName()] = elem dict = {} for line in self.__accLines: if line.getName() in dict: print("Warning the line:", line.getName(), " was redefined. Are you are sure?") dict[line.getName()] = line # ------------------------------------------------------- # Then let's calculate numerical values. # They can be defined recursivelly, so we need iterations # ------------------------------------------------------- accVarDict = {} for var in self.__accValues: accVarDict[var.getName()] = var localValDict = {} doNotStop = True while doNotStop: doNotStop = False accVarDictInner = accVarDict.copy() # print "debug variables dict. size=",len(accVarDictInner) for name, var in accVarDictInner.items(): str_in = var.getExpression() res, val = StringFunctions.calculateString(str_in.lower(), localValDict) if res: localValDict[name.lower()] = val var.setValue(val) del accVarDict[name] else: doNotStop = True if len(accVarDictInner) == len(accVarDict): print("=========== Unresolved Variables============") for name, var in accVarDictInner.items(): print("name=", name, " str=", var.getExpression()) print("=========== SAD File Problem ===============") print("=================STOP=======================") sys.exit(1) """ #debug printing for var in self.__accValues: print "debug val_name=",var.getName()," val=",var.getValue() """ # ------------------------------------------- # Now calculate all parameters in key,string_value # for accelerator elements # -------------------------------------------- for accElem in self.__accElements: kvs = accElem.getParameters() kvNums = accElem.getNumParameters() for key, val in kvs.items(): val_out = None if val != None: res, val_out = StringFunctions.calculateString(val.lower(), localValDict) if not res: print("=============SAD File problem ==============", end=" ") print("Problem with acc. element:", accElem.getName()) print("SAD file line:", accElem.getExpression()) print("Parameter name:", key) print("Can not calculate string:", val) print("============ STOP ==========================") sys.exit(1) kvNums[key] = val_out """ #debug printing i = 0 for accElem in self.__accElements: i = i + 1 print "i=",i,"type=",accElem.getElementType()," name=",accElem.getName(), for key,val in accElem.getNumParameters().iteritems(): print " ",key,":",val," ", print " " sys.exit(1) """ # --------------------------------------------- # Let's create all lattice elements (old style) # --------------------------------------------- for accElem in self.__accElements: lattElem = SAD_LattElement(accElem.getName(), accElem.getElementType()) self.__lattElems.append(lattElem) kvs = accElem.getNumParameters() for key, val in kvs.items(): lattElem.addParameter(key, val) # ---------------------------------------------- # Let's create lattice lines (old style) # ---------------------------------------------- lattElemDict = {} for elem in self.__lattElems: lattElemDict[elem.getName()] = elem accLineDict = {} for accLine in self.__accLines: accLineDict[accLine.getName()] = accLine lattLineDict = {} for accLine in self.__accLines: lattLine = SAD_LattLine(accLine.getName()) self.__lattLines.append(lattLine) lattLineDict[accLine.getName()] = lattLine for lattLine in self.__lattLines: name = lattLine.getName() accLine = accLineDict[name] childs = accLine.getComponents() for child, sign in childs: # print "debug ????????? name=",name," child=",child," sign=",sign if child in lattElemDict and child in accLineDict: print("=========== SAD File Problem ===============") print("Accelerator line and element have the same name:", child) print("=================STOP=======================") if (child not in lattElemDict) and (child not in accLineDict): print("=========== SAD File Problem ===============") print("Can not find Accelerator line and element with name:", child) print("=================STOP=======================") if child in lattElemDict: lattLine.addItem(lattElemDict[child], sign) else: lattLine.addItem(lattLineDict[child], sign)
# print "debug ????????????????????????????? child=",lattLineDict[child].getName()," sign=",sign
[docs] def initialize(self, SAD_file_name): """This method will do the preliminary editing the input file.""" fl = open(os.path.join(self.SADFilePath, SAD_file_name), "r") sad_strings = [] str_local = "" for str_in in fl.readlines(): # check if the line is a comment str0 = "" if str_in.find("!") == 0: continue if str_in.find("!") > 0: str0 = str_in[0 : str_in.find("!")].strip() else: str0 = str_in.strip() if str0.rfind(";") >= 0: str_local = str_local + " " + str0 strs = re.compile(r"([^;]*);{1,1}").findall(str_local) for str1 in strs: str1 = str1.strip() if len(str1) > 0: sad_strings.append(str1.expandtabs(1)) str_local = "" else: str_local = str_local + " " + str0 # -------------------------------------------- """ #debug printing the input file after initial modifications i = 0 for str in sad_strings: i = i + 1 print "i=",i," str=",str """ fl.close() # the sad_strings list should be transformed to # the list of _SAD_String with types for str0 in sad_strings: # check if it is a line type_name = re.compile(r"\A\s*(\S*)\s*").findall(str0) if len(type_name) == 1 and type_name[0].upper() == "LINE": # find the first line name name = re.compile(r"\A\s*\S+\s+(\S+)\s*={1,1}").findall(str0)[0] # cut the LINE from the beggining of the string str_rest0 = str0[(str0.find(type_name[0]) + len(type_name[0])) :] str_rest = str_rest0[str_rest0.find(name) :] while str_rest != None and len(str_rest) > 0: # loop through all lines NAME = (...) (ind0, ind1) = StringFunctions.findBracketsPos(str_rest) if ind0 < 0 or ind1 < 0: break sadString = _SAD_String() sadString.setLine(str_rest[0 : ind1 + 1]) sadString.setType(_accLine.getType()) self.__SAD_Strings.append(sadString) str_rest = str_rest[ind1 + 1 :].strip() continue # check if it is element - str = TYPE NAME = type_name = re.compile(r"\A\s*(\S+)\s+\S+\s*={1,1}").findall(str0) if len(type_name) == 1 and self._typeChecker.checkType(type_name[0]) != None: type0 = self._typeChecker.checkType(type_name[0]) # find the first line name name = re.compile(r"\A\s*\S+\s+(\S+)\s*={1,1}").findall(str0)[0] # cut the LINE from the beggining of the string str_rest0 = str0[(str0.find(type_name[0]) + len(type_name[0])) :] str_rest = str_rest0[str_rest0.find(name) :] while str_rest != None and len(str_rest) > 0: # loop through all lines NAME = (...) (ind0, ind1) = StringFunctions.findBracketsPos(str_rest) if ind0 < 0 or ind1 < 0: break sadString = _SAD_String() sadString.setLine(type0 + " " + str_rest[0 : ind1 + 1]) sadString.setType(_element.getType()) self.__SAD_Strings.append(sadString) str_rest = str_rest[ind1 + 1 :].strip() continue # check the valriables name = re.compile(r"\A\s*(\S+)\s*={1,1}.*").findall(str0) if len(name) == 1: sadString = _SAD_String() sadString.setLine(str0) sadString.setType(_variable.getType()) self.__SAD_Strings.append(sadString) continue self.__unknownLines.append(str0) continue
# -------------------------------------------- # SADLine list is ready for sorting according # the types # --------------------------------------------
[docs] def getUnknownLines(self): """ Returns the list of unreccongized string lines in the file. """ return self.__unknownLines
[docs] def getSAD_Lines(self): """ Method. It returns the list of the lattice lines that are defined in the SAD file. """ return self.__lattLines
[docs] def getSAD_Elements(self): """ Method. It returns the list of the lattice elements that are defined in the SAD file. """ return self.__lattElems
[docs] def getSAD_Variables(self): """ Method. It returns the list of the variables that are defined in the SAD file. """ return self.__accValues
[docs] def getSAD_LinesDict(self): """ Method. It returns the dictionary of the lattice lines that are defined in the SAD file. """ dict = {} for lattLine in self.__lattLines: dict[lattLine.getName()] = lattLine return dict
[docs] def getSAD_ElementsDict(self): """ Method. It returns the dictionary of the lattice elements that are defined in the SAD file. """ dict = {} for lattElem in self.__lattElems: dict[lattElem.getName()] = lattElem return dict
[docs] def getSAD_VariablesDict(self): """ Method. It returns the dictionary of the variables that are defined in the SAD file. """ dict = {} for var in self.__accValues: dict[var.getName()] = var.getValue() return dict