From 9bef0adc13e2a70fbefd0b9a1a77135ea55fe3d3 Mon Sep 17 00:00:00 2001 From: Sven Date: Thu, 5 Mar 2026 11:00:17 +0100 Subject: [PATCH] Added support to import LongTrack settings file and to match with user defined twiss values --- OpticsTools.py | 15 ++++- matchmaker.py | 5 +- model.py | 5 +- sandbox.py | 86 +++++++++++++++++++++++++ ui/OpticsToolsGui.py | 102 +++++++++++++++++++++++++++--- ui/OpticsToolsGui.ui | 147 ++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 340 insertions(+), 20 deletions(-) diff --git a/OpticsTools.py b/OpticsTools.py index b696084..5b1283f 100644 --- a/OpticsTools.py +++ b/OpticsTools.py @@ -87,7 +87,12 @@ class OpticsTools(QtWidgets.QMainWindow, QtCore.QObject, Ui_OpticsGUI): vars = self.getMatchVariables() else: vars = None - twiss = self.match.match(self.model.om, variables = vars, Injector = injector,Athos = athos, Aramis = aramis, Porthos = porthos) + inittwiss={} + inittwiss['betax']=float(str(self.UIInitBetax.text())) + inittwiss['betay'] = float(str(self.UIInitBetay.text())) + inittwiss['alphax'] = float(str(self.UIInitAlphax.text())) + inittwiss['alphay'] = float(str(self.UIInitAlphay.text())) + twiss = self.match.match(self.model.om, variables = vars, initCond = inittwiss, Injector = injector,Athos = athos, Aramis = aramis, Porthos = porthos) self.updateMatchResult(self.match.matchresult) energy = self.model.calcEnergyProfile(twiss) #enfore the writing of a new lattice so that the magnet settings are in the new lattice @@ -187,7 +192,7 @@ class OpticsTools(QtWidgets.QMainWindow, QtCore.QObject, Ui_OpticsGUI): "Json Files (*.json)", options=options) if not fileName: return - self.saveSettingdirect(fileName) + self.saveSettingsdirect(fileName) @@ -206,9 +211,15 @@ class OpticsTools(QtWidgets.QMainWindow, QtCore.QObject, Ui_OpticsGUI): def loadSettingsdirect(self,fileName): + print('Loading settings from %s ...' % fileName) with open(fileName, 'r', encoding='utf-8') as f: settings = json.load(f) status=self.model.loadSettings(settings) + if 'InitialCondition' in settings.keys(): + self.UIInitBetax.setText('%7.3f' % settings['InitialCondition']['betx']) + self.UIInitBetay.setText('%7.3f' % settings['InitialCondition']['bety']) + self.UIInitAlphax.setText('%7.3f' % settings['InitialCondition']['alfx']) + self.UIInitAlphay.setText('%7.3f' % settings['InitialCondition']['alfy']) if status: print('Reference loaded from file %s' % fileName) self.status('Reference file loaded') diff --git a/matchmaker.py b/matchmaker.py index e506b19..fb333a4 100644 --- a/matchmaker.py +++ b/matchmaker.py @@ -81,7 +81,7 @@ class MatchMaker: - def match(self, om, variables = None, Injector=True, Athos = True, Aramis = False, Porthos = False): + def match(self, om, variables = None, initCond=None, Injector=True, Athos = True, Aramis = False, Porthos = False): self.matchresult.clear() cwd = os.getcwd() @@ -98,6 +98,9 @@ class MatchMaker: madx.updateLattice(om, target) # write lattice madx.commonHeader('SwissFEL', '#s/#e', None) # sets header madx.madx.call('initTwiss.madx') + if not initCond is None: + madx.madx.input('Twiss0: beta0, betx = %f, alfx = %f, bety = %f, alfy = %f;\n' + % (initCond['betax'],initCond['alphax'],initCond['betay'],initCond['alphay'])) if not variables is None: madx.definePresets(variables) if Injector: diff --git a/model.py b/model.py index 84e921f..e7f8727 100644 --- a/model.py +++ b/model.py @@ -105,11 +105,12 @@ class Model: self.om.setRegExpElement(name[0:7], name[8:15], 'kx', kx) self.om.setRegExpElement(name[0:7], name[8:15], 'ky', ky) if 'RSYS' in name: - grad = float(val[0]) + grad = float(val[0])*1e6 phase = float(val[1]) if 'CB' in name[0:7]: grad = grad/ 4. - grad = grad/2 + if 'SINSB03' in name[0:7] or 'SINSB04' in name[0:7] or 'SINXB' in name[0:7]: + grad = grad/2. L = self.om.getRegExpElement(name[0:7], 'RACC', 'Length')[0] self.om.setRegExpElement(name[0:7], 'RACC', 'Gradient', grad/L) self.om.setRegExpElement(name[0:7], 'RACC', 'Phase', phase) diff --git a/sandbox.py b/sandbox.py index 411ead2..d607b25 100644 --- a/sandbox.py +++ b/sandbox.py @@ -1,6 +1,10 @@ +from dataclasses import field + from PyQt5 import QtCore, QtGui, QtWidgets import numpy as np import re +import scipy +import json CGrey = QtGui.QColor(230,230,230) CBeige = QtGui.QColor(250, 240, 200) @@ -20,6 +24,7 @@ class Sandbox: self.parent.Mach2ModUnd.clicked.connect(self.updateModelEvent) self.parent.SB2ModUnd.clicked.connect(self.updateModelEvent) self.parent.SB2MachineMag.clicked.connect(self.updateMachine) + self.parent.SBLongTrack.clicked.connect(self.updateFromLongTrack) def updateMachine(self): vals = self.getSBbyCol(self.parent.MagSB,1) @@ -244,3 +249,84 @@ class Sandbox: colors.append(sbcol) self.updateSandboxCommon(sb, values, colors, labels) + def updateFromLongTrack(self): + options = QtWidgets.QFileDialog.Options() + options |= QtWidgets.QFileDialog.DontUseNativeDialog + fileName,_ = QtWidgets.QFileDialog.getOpenFileName(self.parent, "Open LongTrack Settings",".","Json Files (*.json)", options=options) + if not fileName: + return + with open(fileName,'r') as f: + data = json.load(f) + for sec in ['SINSB','SINXB','Linac1-1','Linac1-2','Linac2','Linac3']: + self.updateLTRF(data,sec) + for sec in ['BC1','BC2','Laser Heater']: + self.updateLTBC(data,sec) + self.updateSandbox() + self.parent.model.forceLatticeUpdate() + self.parent.status('Sandbox mode') + + def updateLTBC(self,data,section): + if not section in data.keys(): + return + self.rootR56=data[section]['R56'] + self.rootLb = data[section]['Dipole Length'] + self.rootLd = data[section]['L1'] + x=data[section]['Angle'] + if self.rootR56 > 0: + res = scipy.optimize.root_scalar(self.calc56,method='bisect',bracket=[1e-5,0.2]) + angle = -res.root*180/np.pi + else: + angle = 0. + if 'BC1' in section: + self.parent.model.updateElement('SINBC02.MBND', [angle]) + elif 'BC2' in section: + self.parent.model.updateElement('S10BC02.MBND', [angle]) + elif 'Laser Heater' in section: + self.parent.model.updateElement('SINLH02.MBND', [angle]) + + + def calc56(self, x): + return 2* (4* x / np.sin(x) * self.rootLb + 2 * self.rootLd / np.cos(x)-4*self.rootLb-2*self.rootLd)-self.rootR56 + + def updateLTRF(self,data,section): + if not section in data.keys(): + return + phase=data[section]['Phase'] + gain=data[section]['Voltage'] + if 'Linac3' in section: + gain/=13. + for i in range(1,14): + field='S30CB%2.2d-RSYS' % i + val = [gain, phase] + self.parent.model.updateElement(field.replace('-', '.'), val) + elif 'Linac2' in section: + gain/=4 + for i in range(1,5): + field='S20CB%2.2d-RSYS' % i + val = [gain, phase] + self.parent.model.updateElement(field.replace('-', '.'), val) + elif 'Linac1-2' in section: + gain/=7 + for i in range(3,10): + field='S10CB%2.2d-RSYS' % i + val = [gain, phase] + self.parent.model.updateElement(field.replace('-', '.'), val) + elif 'Linac1-1' in section: + gain/=2 + for i in range(1,3): + field='S10CB%2.2d-RSYS' % i + val = [gain, phase] + self.parent.model.updateElement(field.replace('-', '.'), val) + elif 'SINSB' in section: + gain/=2 + for i in range(3,5): + field='SINSB%2.2d-RSYS' % i + val = [gain, phase] + self.parent.model.updateElement(field.replace('-', '.'), val) + + elif 'SINXB' in section: + for i in range(1,2): + field='SINXB%2.2d-RSYS' % i + val = [gain, phase] + self.parent.model.updateElement(field.replace('-', '.'), val) + diff --git a/ui/OpticsToolsGui.py b/ui/OpticsToolsGui.py index 73a9af2..1e737c4 100644 --- a/ui/OpticsToolsGui.py +++ b/ui/OpticsToolsGui.py @@ -17,8 +17,8 @@ class Ui_OpticsGUI(object): OpticsGUI.resize(938, 791) self.centralwidget = QtWidgets.QWidget(OpticsGUI) self.centralwidget.setObjectName("centralwidget") - self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.centralwidget) - self.verticalLayout_4.setObjectName("verticalLayout_4") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") self.TabMaster = QtWidgets.QTabWidget(self.centralwidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) @@ -356,6 +356,80 @@ class Ui_OpticsGUI(object): self.UIInitAllMagnets = QtWidgets.QCheckBox(self.widget) self.UIInitAllMagnets.setObjectName("UIInitAllMagnets") self.verticalLayout_3.addWidget(self.UIInitAllMagnets) + self.label_20 = QtWidgets.QLabel(self.widget) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_20.setFont(font) + self.label_20.setObjectName("label_20") + self.verticalLayout_3.addWidget(self.label_20) + self.gridLayout_6 = QtWidgets.QGridLayout() + self.gridLayout_6.setObjectName("gridLayout_6") + self.label_15 = QtWidgets.QLabel(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_15.sizePolicy().hasHeightForWidth()) + self.label_15.setSizePolicy(sizePolicy) + self.label_15.setObjectName("label_15") + self.gridLayout_6.addWidget(self.label_15, 0, 2, 1, 1) + self.UIInitAlphax = QtWidgets.QLineEdit(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.UIInitAlphax.sizePolicy().hasHeightForWidth()) + self.UIInitAlphax.setSizePolicy(sizePolicy) + self.UIInitAlphax.setObjectName("UIInitAlphax") + self.gridLayout_6.addWidget(self.UIInitAlphax, 2, 1, 1, 1) + self.label_14 = QtWidgets.QLabel(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_14.sizePolicy().hasHeightForWidth()) + self.label_14.setSizePolicy(sizePolicy) + self.label_14.setObjectName("label_14") + self.gridLayout_6.addWidget(self.label_14, 0, 1, 1, 1) + self.label_16 = QtWidgets.QLabel(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_16.sizePolicy().hasHeightForWidth()) + self.label_16.setSizePolicy(sizePolicy) + self.label_16.setObjectName("label_16") + self.gridLayout_6.addWidget(self.label_16, 1, 0, 1, 1) + self.UIInitBetax = QtWidgets.QLineEdit(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.UIInitBetax.sizePolicy().hasHeightForWidth()) + self.UIInitBetax.setSizePolicy(sizePolicy) + self.UIInitBetax.setObjectName("UIInitBetax") + self.gridLayout_6.addWidget(self.UIInitBetax, 1, 1, 1, 1) + self.UIInitBetay = QtWidgets.QLineEdit(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.UIInitBetay.sizePolicy().hasHeightForWidth()) + self.UIInitBetay.setSizePolicy(sizePolicy) + self.UIInitBetay.setObjectName("UIInitBetay") + self.gridLayout_6.addWidget(self.UIInitBetay, 1, 2, 1, 1) + self.UIInitAlphay = QtWidgets.QLineEdit(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.UIInitAlphay.sizePolicy().hasHeightForWidth()) + self.UIInitAlphay.setSizePolicy(sizePolicy) + self.UIInitAlphay.setObjectName("UIInitAlphay") + self.gridLayout_6.addWidget(self.UIInitAlphay, 2, 2, 1, 1) + self.label_17 = QtWidgets.QLabel(self.widget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_17.sizePolicy().hasHeightForWidth()) + self.label_17.setSizePolicy(sizePolicy) + self.label_17.setObjectName("label_17") + self.gridLayout_6.addWidget(self.label_17, 2, 0, 1, 1) + self.verticalLayout_3.addLayout(self.gridLayout_6) self.label_19 = QtWidgets.QLabel(self.widget) font = QtGui.QFont() font.setBold(True) @@ -380,9 +454,6 @@ class Ui_OpticsGUI(object): item = QtWidgets.QTableWidgetItem() self.UIMatchKnobs.setHorizontalHeaderItem(1, item) self.verticalLayout_3.addWidget(self.UIMatchKnobs) - self.UIReportMagnetStrength = QtWidgets.QPushButton(self.widget) - self.UIReportMagnetStrength.setObjectName("UIReportMagnetStrength") - self.verticalLayout_3.addWidget(self.UIReportMagnetStrength) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.verticalLayout_3.addItem(spacerItem1) self.horizontalLayout_4.addWidget(self.widget) @@ -417,6 +488,9 @@ class Ui_OpticsGUI(object): self.UIReportMagnetResult.setFont(font) self.UIReportMagnetResult.setObjectName("UIReportMagnetResult") self.verticalLayout_2.addWidget(self.UIReportMagnetResult) + self.UIReportMagnetStrength = QtWidgets.QPushButton(self.groupBox) + self.UIReportMagnetStrength.setObjectName("UIReportMagnetStrength") + self.verticalLayout_2.addWidget(self.UIReportMagnetStrength) self.horizontalLayout_4.addWidget(self.groupBox) spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.horizontalLayout_4.addItem(spacerItem2) @@ -504,9 +578,13 @@ class Ui_OpticsGUI(object): self.SB2ModUnd = QtWidgets.QPushButton(self.groupBox_9) self.SB2ModUnd.setObjectName("SB2ModUnd") self.verticalLayout_17.addWidget(self.SB2ModUnd) + self.SBLongTrack = QtWidgets.QPushButton(self.groupBox_9) + self.SBLongTrack.setStyleSheet("background-color: rgb(255, 255, 127);") + self.SBLongTrack.setObjectName("SBLongTrack") + self.verticalLayout_17.addWidget(self.SBLongTrack) self.horizontalLayout_5.addWidget(self.groupBox_9) self.TabMaster.addTab(self.tab_8, "") - self.verticalLayout_4.addWidget(self.TabMaster) + self.horizontalLayout_2.addWidget(self.TabMaster) OpticsGUI.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(OpticsGUI) self.menubar.setGeometry(QtCore.QRect(0, 0, 938, 22)) @@ -692,16 +770,25 @@ class Ui_OpticsGUI(object): self.UIMatchAramis.setText(_translate("OpticsGUI", "Aramis")) self.UISaveMatchSettings.setText(_translate("OpticsGUI", "Save matched machine settings automtically")) self.UIInitAllMagnets.setText(_translate("OpticsGUI", "Initialize all magnets to zero before matching")) + self.label_20.setText(_translate("OpticsGUI", "Initial Conditions")) + self.label_15.setText(_translate("OpticsGUI", "y")) + self.UIInitAlphax.setText(_translate("OpticsGUI", "0")) + self.label_14.setText(_translate("OpticsGUI", "x")) + self.label_16.setText(_translate("OpticsGUI", "beta")) + self.UIInitBetax.setText(_translate("OpticsGUI", "20")) + self.UIInitBetay.setText(_translate("OpticsGUI", "20")) + self.UIInitAlphay.setText(_translate("OpticsGUI", "0")) + self.label_17.setText(_translate("OpticsGUI", "alpha")) self.label_19.setText(_translate("OpticsGUI", "Matching Variables")) self.UIModifyKnobs.setText(_translate("OpticsGUI", "Overwrite default values for matching variables ")) item = self.UIMatchKnobs.horizontalHeaderItem(0) item.setText(_translate("OpticsGUI", "Parameter")) item = self.UIMatchKnobs.horizontalHeaderItem(1) item.setText(_translate("OpticsGUI", "Value")) - self.UIReportMagnetStrength.setText(_translate("OpticsGUI", "Report Matched Quadrupole Strength")) self.groupBox.setTitle(_translate("OpticsGUI", "Matching Results")) self.label_11.setText(_translate("OpticsGUI", "Matching")) self.label_12.setText(_translate("OpticsGUI", "Magnet Strength")) + self.UIReportMagnetStrength.setText(_translate("OpticsGUI", "Report Matched Quadrupole Strength")) self.TabMaster.setTabText(self.TabMaster.indexOf(self.tab_3), _translate("OpticsGUI", "Track")) self.groupBox_8.setTitle(_translate("OpticsGUI", "Magnets (Angle/ k1L / k2L)")) self.label_23.setText(_translate("OpticsGUI", "Start")) @@ -713,6 +800,7 @@ class Ui_OpticsGUI(object): self.label_26.setText(_translate("OpticsGUI", "Energy")) self.Mach2ModUnd.setText(_translate("OpticsGUI", "Model <- Sandbox <- Machine")) self.SB2ModUnd.setText(_translate("OpticsGUI", "Model <- Sandbox")) + self.SBLongTrack.setText(_translate("OpticsGUI", "Import LongTracker Settings")) self.TabMaster.setTabText(self.TabMaster.indexOf(self.tab_8), _translate("OpticsGUI", "Sandbox")) self.menuFile.setTitle(_translate("OpticsGUI", "File")) self.menuHelp.setTitle(_translate("OpticsGUI", "Help")) diff --git a/ui/OpticsToolsGui.ui b/ui/OpticsToolsGui.ui index 8372bee..21f48ea 100644 --- a/ui/OpticsToolsGui.ui +++ b/ui/OpticsToolsGui.ui @@ -14,7 +14,7 @@ SwissFEL Optics - + @@ -581,6 +581,127 @@ + + + + + 75 + true + + + + Initial Conditions + + + + + + + + + + 0 + 0 + + + + y + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + x + + + + + + + + 0 + 0 + + + + beta + + + + + + + + 0 + 0 + + + + 20 + + + + + + + + 0 + 0 + + + + 20 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + alpha + + + + + @@ -621,13 +742,6 @@ - - - - Report Matched Quadrupole Strength - - - @@ -693,6 +807,13 @@ + + + + Report Matched Quadrupole Strength + + + @@ -843,6 +964,16 @@ + + + + background-color: rgb(255, 255, 127); + + + Import LongTracker Settings + + +