diff --git a/OpticsTools.py b/OpticsTools.py
index 091c5b1..a156121 100644
--- a/OpticsTools.py
+++ b/OpticsTools.py
@@ -11,7 +11,7 @@ from model import Model
from machine import Machine
from reference import ReferenceManager
from sandbox import Sandbox
-
+from matching import Matching
class OpticsTools(QtWidgets.QMainWindow, Ui_OpticsGUI):
def __init__(self,phase=0, office = False):
@@ -26,11 +26,11 @@ class OpticsTools(QtWidgets.QMainWindow, Ui_OpticsGUI):
# initialize online model
self.model = Model(phase=phase,parent=self)
+ self.matching = Matching(parent=self,model = self.model)
if phase > 0:
office = True
self.machine = Machine(parent = True, office = office)
self.machine.initPVs(self.model.getElements())
- self.reference = ReferenceManager(parent = self)
self.sandbox = Sandbox(parent = self, machine = self.machine)
title = "SwissFEL Optics Tools - Lattice %s (Phase %d)" % (self.model.getLatticeVersion(),phase)
@@ -41,6 +41,7 @@ class OpticsTools(QtWidgets.QMainWindow, Ui_OpticsGUI):
# initialization
self.loadSettingsdirect("/sf/data/applications/BD-OpticsTools/reference/settings/ReferenceSetting.json")
self.sandbox.updateSandbox()
+ self.reference = ReferenceManager(parent=self)
# events handling
self.actionOpen_2.triggered.connect(self.loadSettings)
diff --git a/issues.txt b/issues.txt
new file mode 100644
index 0000000..ca3cc90
--- /dev/null
+++ b/issues.txt
@@ -0,0 +1,15 @@
+1) Energy update in model from Sandbox or machine
+2) Full update from machine
+3) export to machine for magnet settings
+4) export elegant lattice
+5) support of other reference files -> fill up the selection list
+6) matching
+7) when writing magnets do snapshot?
+8) Elegant support?
+9) initial beta-function saved, also to settings file
+10) Prepare several settings files
+11) About and website support
+12) install cpymax on machine network
+13) save/load settings : Initial energy and initial twiss
+14) right energy for tracking
+15) set initial condition for tracking cpymadx
diff --git a/matching.py b/matching.py
new file mode 100644
index 0000000..93c76c8
--- /dev/null
+++ b/matching.py
@@ -0,0 +1,24 @@
+from matchingmanager import MatchManager
+
+class Matching:
+ def __init__(self,parent=None,model=None):
+ self.parent = parent
+ self.model = model
+ self.manager = MatchManager(parent=parent)
+ self.parent.UIMFodoMatchSingle.clicked.connect(self.matchingFodo)
+
+ def matchingFodo(self):
+ print('matching')
+ par = self.manager.getFODOParameters()
+ if par is None:
+ return
+ quads=par['Quads']
+ vals=par['Init']
+ if par['FlipPol']:
+ vals = [-val for val in vals]
+ var = {name:vals[i] for i,name in enumerate(quads)}
+ cond = {'range':'#e','mux':par['mu'],'muy':par['mu']}
+ twiss,tar = self.model.match(sequence=par['Sequence'], destination=par['Destination'],variables=var, conditions=cond, periodic=True)
+ par['Result']=tar
+ par['Twiss']=twiss
+ self.manager.setFODOParameters(par)
diff --git a/matchingmanager.py b/matchingmanager.py
new file mode 100644
index 0000000..6c3c5be
--- /dev/null
+++ b/matchingmanager.py
@@ -0,0 +1,68 @@
+class MatchManager:
+ def __init__(self,parent=None):
+ self.parent=parent
+ self.FODO={}
+ self.defineFODO()
+ self.updateFODOWidget()
+ self.parent.UIMFodoList.currentIndexChanged.connect(self.updateFODOWidget)
+
+ def getFODOInfo(self,reference):
+ """
+ retrieve if a seciton is matched and if yes get initial fodo values
+ :param reference:
+ :return:
+ """
+ if reference == 'Injector':
+ a = 1
+ elif reference == 'Linac 1':
+ a = 1
+ elif reference == 'Linac 2':
+ a = 1
+ elif reference == 'Linac 3':
+ a = 1
+ elif reference == 'Aramis Undulator':
+ a = 1
+ elif reference == 'Athos Undulator':
+ a = 1
+
+ ###########################
+ ##### generic FODO matching
+
+ def updateFODOWidget(self):
+ reference = self.parent.UIMFodoList.currentText()
+ if not reference in self.FODO.keys():
+ print('Not Supported')
+ return
+ self.parent.UIMFodoPhase.setText('%7.3f' % self.FODO[reference]['mu'])
+ self.parent.UIMFodoFlip.setChecked(self.FODO[reference]['FlipPol'])
+ if self.FODO[reference]['Result'] is None:
+ self.parent.UIMFodoResult.setText('Not matched yet')
+ else:
+ self.parent.UIMFodoResult.setText('%7.3e' % self.FODO[reference]['Result'])
+
+ def setFODOParameters(self,par):
+ reference = self.parent.UIMFodoList.currentText()
+ if not reference in self.FODO.keys():
+ print('Not Supported')
+ return None
+ self.FODO[reference]=par
+
+ def getFODOParameters(self):
+ reference = self.parent.UIMFodoList.currentText()
+ if not reference in self.FODO.keys():
+ print('Not Supported')
+ return None
+ self.FODO[reference]['mu'] = float(str(self.parent.UIMFodoPhase.text()))
+ self.FODO[reference]['FlipPol'] = self.parent.UIMFodoFlip.isChecked()
+ return self.FODO[reference]
+
+ def defineFODO(self):
+ self.FODO.clear()
+ self.FODO['Injector']={'Sequence':'SINSB04','Destination': 'SARBD01',
+ 'Quads':['sinsb04.mqua130.k1','sinsb04.mqua230.k1'],
+ 'Init':[0.722,-0.7156],'FlipPol':False,'mu':0.2,'Result':None,'Twiss':None}
+
+ self.FODO['Linac 1']={'Sequence':'S10CB01','Destination': 'SARBD01',
+ 'Quads':['s10cb01.mqua230.k1','s10cb01.mqua430.k1'],
+ 'Init':[-1.491,1.4905],'FlipPol':False,'mu':0.1883,'Result':None,'Twiss':None}
+
diff --git a/model.py b/model.py
index 6f36bfa..d6bbcd3 100644
--- a/model.py
+++ b/model.py
@@ -1,5 +1,5 @@
import json
-
+import copy
import numpy as np
from onlinemodel.core import Facility
from onlinemodel.madx import CMadX
@@ -13,6 +13,10 @@ class Model:
self.order = None
self.madx = CMadX()
+ self.startTwiss = None
+ self.startEnergy = None
+ self.energyReference ='SINLH02.MBND100'
+
# hook up events
self.eventHandling()
@@ -20,10 +24,11 @@ class Model:
return self.om.Version
def updateEnergy(self,E0):
- self.om.forceEnergyAt('SINLH02.MBND100', E0*1e6)
+ if isinstance(E0,list):
+ E0=E0[0]
+ self.om.forceEnergyAt(self.energyReference, E0*1e6)
def updateElement(self,name,val):
-
if 'MQUA' in name or 'MQSK' in name:
L = self.om.getRegExpElement(name[0:7], name[8:15],'Length')[0]
self.om.setRegExpElement(name[0:7], name[8:15], 'k1', float(val[0])/L)
@@ -70,8 +75,8 @@ class Model:
rf={}
undulators={}
kicker={}
- loc = 'SINLH02.MBND100'
- energy={'location': loc, 'energy':self.om.EnergyAt(loc)[0]}
+ energy={'location': self.energyReference, 'energy' : 1e-6*self.om.EnergyAt(self.energyReference)[0]}
+
for ele in elements:
if 'MQUA' in ele.Name or 'MQSK' in ele.Name:
quadrupoles[ele.Name]={'k1':ele.k1,'k1L':ele.k1*ele.Length}
@@ -86,7 +91,8 @@ class Model:
rf[ele.Name]={'Gradient':ele.Gradient*ele.Length,'Phase':ele.Phase}
elif 'MKAC' in ele.Name or 'MKDC' in ele.Name:
kicker[ele.Name] = {'cory': ele.cory}
- return {'Quadrupole':quadrupoles,'Sextupole':sextupoles,'Dipole':dipoles,'RF':rf,'Undulator':undulators,'Kicker':kicker,'Energy':energy}
+ return {'Quadrupole':quadrupoles,'Sextupole':sextupoles,'Dipole':dipoles,'RF':rf,'Undulator':undulators,
+ 'Kicker':kicker,'Energy':energy, 'InitialCondition':self.startTwiss}
def loadSettingsGroup(self,group,fields):
for key in group.keys():
@@ -101,7 +107,11 @@ class Model:
self.loadSettingsGroup(settings['RF'], ['Gradient','Phase'])
self.loadSettingsGroup(settings['Undulator'], ['K','kx','ky'])
self.loadSettingsGroup(settings['Kicker'], ['cory'])
- self.om.forceEnergyAt(settings['Energy']['location'],settings['Energy']['energy'][0])
+ self.startEnergy = settings['Energy']['energy']
+ self.energyReference = settings['Energy']['location']
+ self.startTwiss = settings['InitialCondition']
+ print('initial condition:',self.startTwiss)
+ self.updateEnergy(self.startEnergy)
@@ -146,6 +156,18 @@ class Model:
def eventHandling(self):
self.parent.UITrack.clicked.connect(self.track)
+ def match(self,sequence,destination, variables,conditions,periodic=False,plot=True):
+ self.setBranch(destination.upper())
+ twiss={'energy0':150.}
+ self.madx.updateVariables(twiss)
+ res,twiss,tar=self.madx.match(sequence,variables,conditions,periodic)
+ energy = self.calcEnergyProfile(twiss)
+ matchtwiss={'betax':twiss.betx[0],'betay':twiss.betay[0],'alphax':twiss.alfx[0],'alphay':twiss.alfy[0]}
+ if plot:
+ self.parent.plot.newData(twiss, energy)
+ return matchtwiss,tar
+
+
def track(self):
start = str(self.parent.UITrackStart.text()).upper()
end = str(self.parent.UITrackEnd.text()).upper()
@@ -155,7 +177,9 @@ class Model:
end = end[0:7]
twiss0 = self.parent.reference.getReferencePoint()
refloc = self.parent.reference.getReferenceLocation().upper()
- twiss0['energy'] = 150.
+ if refloc == 'START':
+ refloc = start.upper()
+ twiss0['energy'] = self.startEnergy
start, end = self.checkRange(start, end, refloc[0:7])
if start is None:
return
@@ -168,13 +192,17 @@ class Model:
self.setBranch(end.upper())
if not refloc == start:
twiss0 = self.doBackTrack(refloc,start,twiss0)
+ self.startTwiss=copy.deepcopy(twiss0)
self.madx.updateVariables(twiss0)
twiss = self.madx.track(start+'$START',end+'$END')
+ energy = self.calcEnergyProfile(twiss)
+ if plot:
+ self.parent.plot.newData(twiss,energy)
+
+ def calcEnergyProfile(self,twiss):
energy = np.array([0. for i in range(len(twiss.betx))])
-
e0 = 0.
- de = 0
for i, name in enumerate(twiss.name):
if len(name) > 15:
elename = name[0:15]
@@ -185,8 +213,8 @@ class Model:
energy[:i]+=erg[0]*1e-6
e0 = (erg[0]+erg[1])*1e-6
energy[i]=e0
- if plot:
- self.parent.plot.newData(twiss,energy)
+ return energy
+
def doBackTrack(self,start=None,end=None,twiss0=None):
twiss0['alphax0'] = -twiss0['alphax0'] # revert particle trajectories
diff --git a/reference.py b/reference.py
index 7f8e9ac..027945e 100644
--- a/reference.py
+++ b/reference.py
@@ -36,7 +36,7 @@ class ReferenceManager:
value = 0
if 'beta' in key:
value = 30
- self.twisswidget[key].setText(str(value))
+ self.twisswidget[key].setText('%7.3f' % value)
def getReferencePoint(self):
self.updateReferenceWidgets() # enforce that the data is consistent
@@ -51,13 +51,18 @@ class ReferenceManager:
key = str(self.parent.UITrackReference.currentText())
if key == 'User Defined':
return
- twiss = self.reference['Reference'][key]['twiss']
- name = self.reference['Reference'][key]['location']
+ elif key == 'Start':
+ twiss = {key[0:-1]:self.parent.model.startTwiss[key] for key in self.parent.model.startTwiss.keys()} # strip of the zero
+ name = 'Start'
+ else:
+ twiss = self.reference['Reference'][key]['twiss']
+ name = self.reference['Reference'][key]['location']
self.updateReferenceLocation(name)
self.updateReferencePoint(twiss)
def updateReferenceComboBox(self):
self.parent.UITrackReference.clear()
+ self.parent.UITrackReference.addItem('Start')
for ref in self.reference['Reference'].keys():
self.parent.UITrackReference.addItem(ref)
self.parent.UITrackReference.addItem('User Defined')
diff --git a/ui/OpticsToolsGui.py b/ui/OpticsToolsGui.py
index 6d74334..d15c59d 100644
--- a/ui/OpticsToolsGui.py
+++ b/ui/OpticsToolsGui.py
@@ -346,6 +346,95 @@ class Ui_OpticsGUI(object):
self.verticalLayout_17.addWidget(self.SB2ModUnd)
self.horizontalLayout_5.addWidget(self.groupBox_9)
self.TabMaster.addTab(self.tab_8, "")
+ self.tab = QtWidgets.QWidget()
+ self.tab.setObjectName("tab")
+ self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.tab)
+ self.horizontalLayout_7.setObjectName("horizontalLayout_7")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.groupBox = QtWidgets.QGroupBox(self.tab)
+ self.groupBox.setObjectName("groupBox")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.UIMFodoList = QtWidgets.QComboBox(self.groupBox)
+ self.UIMFodoList.setObjectName("UIMFodoList")
+ self.UIMFodoList.addItem("")
+ self.UIMFodoList.addItem("")
+ self.UIMFodoList.addItem("")
+ self.UIMFodoList.addItem("")
+ self.UIMFodoList.addItem("")
+ self.UIMFodoList.addItem("")
+ self.verticalLayout_2.addWidget(self.UIMFodoList)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.label_11 = QtWidgets.QLabel(self.groupBox)
+ self.label_11.setObjectName("label_11")
+ self.horizontalLayout_2.addWidget(self.label_11)
+ self.UIMFodoPhase = QtWidgets.QLineEdit(self.groupBox)
+ self.UIMFodoPhase.setObjectName("UIMFodoPhase")
+ self.horizontalLayout_2.addWidget(self.UIMFodoPhase)
+ self.verticalLayout_2.addLayout(self.horizontalLayout_2)
+ self.UIMFodoFlip = QtWidgets.QCheckBox(self.groupBox)
+ self.UIMFodoFlip.setObjectName("UIMFodoFlip")
+ self.verticalLayout_2.addWidget(self.UIMFodoFlip)
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_12 = QtWidgets.QLabel(self.groupBox)
+ self.label_12.setObjectName("label_12")
+ self.horizontalLayout_3.addWidget(self.label_12)
+ self.UIMFodoResult = QtWidgets.QLineEdit(self.groupBox)
+ self.UIMFodoResult.setObjectName("UIMFodoResult")
+ self.horizontalLayout_3.addWidget(self.UIMFodoResult)
+ self.verticalLayout_2.addLayout(self.horizontalLayout_3)
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ self.UIMFodoMatchSingle = QtWidgets.QPushButton(self.groupBox)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.UIMFodoMatchSingle.setFont(font)
+ self.UIMFodoMatchSingle.setStyleSheet("background-color: rgb(255, 255, 127);")
+ self.UIMFodoMatchSingle.setObjectName("UIMFodoMatchSingle")
+ self.horizontalLayout_4.addWidget(self.UIMFodoMatchSingle)
+ self.UIMFodoMatchAll = QtWidgets.QPushButton(self.groupBox)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.UIMFodoMatchAll.setFont(font)
+ self.UIMFodoMatchAll.setStyleSheet("background-color: rgb(255, 255, 127);")
+ self.UIMFodoMatchAll.setObjectName("UIMFodoMatchAll")
+ self.horizontalLayout_4.addWidget(self.UIMFodoMatchAll)
+ self.verticalLayout_2.addLayout(self.horizontalLayout_4)
+ self.verticalLayout_5.addWidget(self.groupBox)
+ self.groupBox_2 = QtWidgets.QGroupBox(self.tab)
+ self.groupBox_2.setObjectName("groupBox_2")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_2)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.comboBox = QtWidgets.QComboBox(self.groupBox_2)
+ self.comboBox.setObjectName("comboBox")
+ self.verticalLayout_3.addWidget(self.comboBox)
+ self.checkBox = QtWidgets.QCheckBox(self.groupBox_2)
+ self.checkBox.setObjectName("checkBox")
+ self.verticalLayout_3.addWidget(self.checkBox)
+ self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_6.setObjectName("horizontalLayout_6")
+ self.label_13 = QtWidgets.QLabel(self.groupBox_2)
+ self.label_13.setObjectName("label_13")
+ self.horizontalLayout_6.addWidget(self.label_13)
+ self.lineEdit = QtWidgets.QLineEdit(self.groupBox_2)
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout_6.addWidget(self.lineEdit)
+ self.verticalLayout_3.addLayout(self.horizontalLayout_6)
+ self.verticalLayout_5.addWidget(self.groupBox_2)
+ spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_5.addItem(spacerItem2)
+ self.horizontalLayout_7.addLayout(self.verticalLayout_5)
+ spacerItem3 = QtWidgets.QSpacerItem(380, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_7.addItem(spacerItem3)
+ self.TabMaster.addTab(self.tab, "")
+ self.tab_2 = QtWidgets.QWidget()
+ self.tab_2.setObjectName("tab_2")
+ self.TabMaster.addTab(self.tab_2, "")
self.verticalLayout_4.addWidget(self.TabMaster)
OpticsGUI.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(OpticsGUI)
@@ -526,6 +615,23 @@ class Ui_OpticsGUI(object):
self.Mach2ModUnd.setText(_translate("OpticsGUI", "Model <- Sandbox <- Machine"))
self.SB2ModUnd.setText(_translate("OpticsGUI", "Model <- Sandbox"))
self.TabMaster.setTabText(self.TabMaster.indexOf(self.tab_8), _translate("OpticsGUI", "Sandbox"))
+ self.groupBox.setTitle(_translate("OpticsGUI", "FODO"))
+ self.UIMFodoList.setItemText(0, _translate("OpticsGUI", "Injector"))
+ self.UIMFodoList.setItemText(1, _translate("OpticsGUI", "Linac 1"))
+ self.UIMFodoList.setItemText(2, _translate("OpticsGUI", "Linac 2"))
+ self.UIMFodoList.setItemText(3, _translate("OpticsGUI", "Linac 3"))
+ self.UIMFodoList.setItemText(4, _translate("OpticsGUI", "Aramis Undulator"))
+ self.UIMFodoList.setItemText(5, _translate("OpticsGUI", "Athos Undulator"))
+ self.label_11.setText(_translate("OpticsGUI", "Phase Advance"))
+ self.UIMFodoFlip.setText(_translate("OpticsGUI", "Flipped Polarity"))
+ self.label_12.setText(_translate("OpticsGUI", "Result"))
+ self.UIMFodoMatchSingle.setText(_translate("OpticsGUI", "Match"))
+ self.UIMFodoMatchAll.setText(_translate("OpticsGUI", "Match All"))
+ self.groupBox_2.setTitle(_translate("OpticsGUI", "Reference Point"))
+ self.checkBox.setText(_translate("OpticsGUI", "Random Initialization"))
+ self.label_13.setText(_translate("OpticsGUI", "Result"))
+ self.TabMaster.setTabText(self.TabMaster.indexOf(self.tab), _translate("OpticsGUI", "Absolute Matching"))
+ self.TabMaster.setTabText(self.TabMaster.indexOf(self.tab_2), _translate("OpticsGUI", "Relative Matching"))
self.menuFile.setTitle(_translate("OpticsGUI", "File"))
self.menuHelp.setTitle(_translate("OpticsGUI", "Help"))
self.actionOpen_2.setText(_translate("OpticsGUI", "Open Settings..."))
diff --git a/ui/OpticsToolsGui.ui b/ui/OpticsToolsGui.ui
index 862b7cc..211ff92 100644
--- a/ui/OpticsToolsGui.ui
+++ b/ui/OpticsToolsGui.ui
@@ -583,6 +583,195 @@
+
+
+ Absolute Matching
+
+
+ -
+
+
-
+
+
+ FODO
+
+
+
-
+
+
-
+
+ Injector
+
+
+ -
+
+ Linac 1
+
+
+ -
+
+ Linac 2
+
+
+ -
+
+ Linac 3
+
+
+ -
+
+ Aramis Undulator
+
+
+ -
+
+ Athos Undulator
+
+
+
+
+ -
+
+
-
+
+
+ Phase Advance
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Flipped Polarity
+
+
+
+ -
+
+
-
+
+
+ Result
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+
+ 75
+ true
+
+
+
+ background-color: rgb(255, 255, 127);
+
+
+ Match
+
+
+
+ -
+
+
+
+ 75
+ true
+
+
+
+ background-color: rgb(255, 255, 127);
+
+
+ Match All
+
+
+
+
+
+
+
+
+ -
+
+
+ Reference Point
+
+
+
-
+
+
+ -
+
+
+ Random Initialization
+
+
+
+ -
+
+
-
+
+
+ Result
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 380
+ 20
+
+
+
+
+
+
+
+
+ Relative Matching
+
+