First round of matching

This commit is contained in:
2025-05-07 16:20:11 +02:00
parent 7a362b191d
commit 3e858452d3
8 changed files with 453 additions and 17 deletions

View File

@@ -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)

15
issues.txt Normal file
View File

@@ -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

24
matching.py Normal file
View File

@@ -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)

68
matchingmanager.py Normal file
View File

@@ -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}

View File

@@ -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

View File

@@ -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')

View File

@@ -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..."))

View File

@@ -583,6 +583,195 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Absolute Matching</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>FODO</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QComboBox" name="UIMFodoList">
<item>
<property name="text">
<string>Injector</string>
</property>
</item>
<item>
<property name="text">
<string>Linac 1</string>
</property>
</item>
<item>
<property name="text">
<string>Linac 2</string>
</property>
</item>
<item>
<property name="text">
<string>Linac 3</string>
</property>
</item>
<item>
<property name="text">
<string>Aramis Undulator</string>
</property>
</item>
<item>
<property name="text">
<string>Athos Undulator</string>
</property>
</item>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Phase Advance</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="UIMFodoPhase"/>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="UIMFodoFlip">
<property name="text">
<string>Flipped Polarity</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Result</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="UIMFodoResult"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="UIMFodoMatchSingle">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 127);</string>
</property>
<property name="text">
<string>Match</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="UIMFodoMatchAll">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 127);</string>
</property>
<property name="text">
<string>Match All</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Reference Point</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QComboBox" name="comboBox"/>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Random Initialization</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>Result</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>380</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Relative Matching</string>
</attribute>
</widget>
</widget>
</item>
</layout>