@@ -59,8 +59,6 @@ from debye_bec.bec_widgets.widgets.digital_twin.widgets.qt_widgets import ComboB
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
OFFSET_FILE = "debye_bec/debye_bec/bec_widgets/widgets/digital_twin/x01da_offsets.yaml"
|
||||
|
||||
|
||||
class DigitalTwin(BECWidget, QWidget):
|
||||
"""
|
||||
@@ -79,6 +77,9 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
# Debugging, override beamline!
|
||||
# self.beamline = BeamlineId.X10DA
|
||||
|
||||
self.offset_fie = None
|
||||
self.set_offset_file()
|
||||
|
||||
# Check if devices are all in config
|
||||
self.check_bec_config()
|
||||
self.bec_dispatcher.connect_slot(
|
||||
@@ -183,6 +184,16 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
else:
|
||||
raise ValueError(f"Failed to extract beamline from bec server hostname {bec_hostname}")
|
||||
|
||||
def set_offset_file(self):
|
||||
"""
|
||||
Depending on the beamline, set the offset file path accordingly.
|
||||
"""
|
||||
files: dict[BeamlineId, str] = {
|
||||
BeamlineId.X01DA: "debye_bec/debye_bec/bec_widgets/widgets/digital_twin/x01da_offsets.yaml",
|
||||
BeamlineId.X10DA: "superxas_bec/superxas_bec/bec_widgets/widgets/digital_twin/x10da_offsets.yaml",
|
||||
}
|
||||
self.offset_file = files[self.beamline]
|
||||
|
||||
@SafeSlot()
|
||||
def check_bec_config(self, *args):
|
||||
"""
|
||||
@@ -236,7 +247,7 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
missing = [d for d in devices if d not in self.dev]
|
||||
if not missing:
|
||||
break
|
||||
dialog = QDialog()
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("Digital Twin - Config Check")
|
||||
dialog.setFixedWidth(400)
|
||||
layout = QVBoxLayout()
|
||||
@@ -568,26 +579,33 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
case InputNumberField():
|
||||
self.input.smpl.set_number(pos["ot_es1_trz"])
|
||||
case ComboBox():
|
||||
table = self.ask_table_selection()
|
||||
table = self.ask_table_selection(self.input.smpl.currentText())
|
||||
self.input.smpl.set_current_text(table)
|
||||
|
||||
self.calc_assistant(identifier="init")
|
||||
|
||||
def ask_table_selection(self) -> str | None:
|
||||
def ask_table_selection(self, preset=None) -> str | None:
|
||||
"""
|
||||
Opens a dialog asking the user to select a table (ES1 or ES2).
|
||||
|
||||
Args:
|
||||
preset (str): Preset text for the table, either 'ES1' or 'ES2'.
|
||||
|
||||
Returns:
|
||||
The selected table ('ES1' or 'ES2'), or None if the user cancelled.
|
||||
"""
|
||||
dialog = QDialog()
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle("Select Table")
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
text = QLabel("Select the current table in use.")
|
||||
|
||||
combo = QComboBox()
|
||||
combo.addItems(["ES1", "ES2"])
|
||||
choice = ["ES1", "ES2"]
|
||||
combo.addItems(choice)
|
||||
if preset is not None:
|
||||
if preset in choice:
|
||||
combo.setCurrentText(preset)
|
||||
|
||||
buttons = QDialogButtonBox(
|
||||
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||
@@ -615,9 +633,9 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
|
||||
if self.offsets == {}:
|
||||
# Load offsets
|
||||
file = Path(OFFSET_FILE)
|
||||
file = Path(self.offset_file)
|
||||
if not file.exists():
|
||||
raise FileNotFoundError(f"Offset file not found: {OFFSET_FILE}")
|
||||
raise FileNotFoundError(f"Offset file not found: {self.offset_file}")
|
||||
|
||||
with file.open("r", encoding="utf-8") as f:
|
||||
data = yaml.safe_load(f)
|
||||
@@ -660,7 +678,7 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
intro_label.setWordWrap(True)
|
||||
layout.addWidget(intro_label)
|
||||
|
||||
file = QLabel(OFFSET_FILE)
|
||||
file = QLabel(self.offset_file)
|
||||
file.setWordWrap(True)
|
||||
font = QFont()
|
||||
font.setItalic(True)
|
||||
|
||||
@@ -189,7 +189,7 @@ class MoverPanel(QWidget):
|
||||
)
|
||||
self.mover_widgets.append(self.es0wi_try)
|
||||
|
||||
self.es0_mov_group = Group("Expperimental Station 0", [self.es0wi_try])
|
||||
self.es0_mov_group = Group("Experimental Station 0", [self.es0wi_try])
|
||||
|
||||
# Experimental Station 1
|
||||
self.ot_es1_trz = MoveWidget(
|
||||
@@ -197,7 +197,7 @@ class MoverPanel(QWidget):
|
||||
)
|
||||
self.mover_widgets.append(self.ot_es1_trz)
|
||||
|
||||
self.es1_mov_group = Group("Expperimental Station 1", [self.ot_es1_trz])
|
||||
self.es1_mov_group = Group("Experimental Station 1", [self.ot_es1_trz])
|
||||
|
||||
# Assemble complete mover group
|
||||
self.mover_group = Group(
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
"""
|
||||
X10DA / SuperXAS Beamline Parameters.
|
||||
This file describes the parameter of each component of the SuperXAS beamline
|
||||
to be used for raytracing and geometrical calculations.
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
import numpy as np
|
||||
import xrt.backends.raycing.materials as rm
|
||||
|
||||
# XRT definitions
|
||||
filterBeryl = rm.Material("Be", rho=1.85, kind="plate") # pyright: ignore[reportArgumentType]
|
||||
filterDiamond = rm.Material("C", rho=3.52, kind="plate") # pyright: ignore[reportArgumentType]
|
||||
filterGraphite = rm.Material("C", rho=2.266, kind="plate") # pyright: ignore[reportArgumentType]
|
||||
|
||||
stripeSi = rm.Material("Si", rho=2.33) # pyright: ignore[reportArgumentType]
|
||||
stripePt = rm.Material("Pt", rho=21.45) # pyright: ignore[reportArgumentType]
|
||||
stripeRh = rm.Material("Rh", rho=12.41) # pyright: ignore[reportArgumentType]
|
||||
stripeCr = rm.Material("Cr", rho=7.14) # pyright: ignore[reportArgumentType]
|
||||
stripePyrex = rm.Material(
|
||||
"Si", rho=2.20
|
||||
) # Use Si as bare element and the density of SiO2 # pyright: ignore[reportArgumentType]
|
||||
|
||||
si111_1 = rm.CrystalSi(hkl=(1, 1, 1), tK=77) # first xtal surface
|
||||
si311_1 = rm.CrystalSi(hkl=(3, 1, 1), tK=77) # first xtal surface
|
||||
si333_1 = rm.CrystalSi(hkl=(3, 3, 3), tK=77) # first xtal surface
|
||||
si511_1 = rm.CrystalSi(hkl=(5, 1, 1), tK=77) # first xtal surface
|
||||
si111_2 = rm.CrystalSi(hkl=(1, 1, 1), tK=77) # second xtal surface
|
||||
si311_2 = rm.CrystalSi(hkl=(3, 1, 1), tK=77) # second xtal surface
|
||||
si333_2 = rm.CrystalSi(hkl=(3, 3, 3), tK=77) # second xtal surface
|
||||
si511_2 = rm.CrystalSi(hkl=(5, 1, 1), tK=77) # second xtal surface
|
||||
|
||||
filterDiamond = rm.Material("C", rho=3.52, kind="plate") # pyright: ignore[reportArgumentType]
|
||||
filterBe = rm.Material("Be", rho=1.85, kind="plate") # pyright: ignore[reportArgumentType]
|
||||
filterSi3N4 = rm.Material(
|
||||
["Si", "N"], quantities=[3, 4], rho=3.44, kind="plate"
|
||||
) # pyright: ignore[reportArgumentType]
|
||||
filterAl = rm.Material("Al", rho=2.69, kind="plate") # pyright: ignore[reportArgumentType]
|
||||
filterGraphite = rm.Material("C", rho=2.266, kind="plate") # pyright: ignore[reportArgumentType]
|
||||
|
||||
# General parameters
|
||||
sourceHeight = 0
|
||||
|
||||
# Synchrotron
|
||||
synchrotron = namedtuple(
|
||||
"synchrotron", ["eE", "eI", "eEspread", "eEpsilonX", "eEpsilonZ", "betaX", "betaZ"]
|
||||
)
|
||||
|
||||
sls1 = synchrotron(
|
||||
eE=2.4, eI=0.4, eEspread=0.878e-3, eEpsilonX=5.63, eEpsilonZ=0.007, betaX=0.45, betaZ=14.4
|
||||
)
|
||||
|
||||
sls2 = synchrotron(
|
||||
eE=2.7, eI=0.4, eEspread=1.147e-3, eEpsilonX=0.156, eEpsilonZ=0.01, betaX=0.18, betaZ=4.6
|
||||
)
|
||||
|
||||
# Source
|
||||
bendingMagnet = namedtuple("bendingMagnet", ["name", "center", "sync", "B0"])
|
||||
|
||||
sls1_14t = bendingMagnet(name="FE-BM-SLS1-1.4T", center=(0, 0, 0), sync=sls1, B0=1.4)
|
||||
|
||||
sls2_21t = bendingMagnet(name="FE-BM-SLS2-2.1T", center=(0, 0, 0), sync=sls2, B0=2.1)
|
||||
|
||||
sls2_35t = bendingMagnet(name="FE-BM-SLS2-3.5T", center=(0, 0, 0), sync=sls2, B0=3.5)
|
||||
|
||||
sls2_50t = bendingMagnet(name="FE-BM-SLS2-5.0T", center=(0, 0, 0), sync=sls2, B0=5.0)
|
||||
|
||||
# FE slits
|
||||
slits = namedtuple("slits", ["name", "center", "maxDivH", "maxDivV"])
|
||||
|
||||
feSlits = slits(name="FE-SLITS", center=(0, 5290, sourceHeight), maxDivH=1.8e-3, maxDivV=0.8e-3)
|
||||
|
||||
# Filters
|
||||
filt = namedtuple(
|
||||
"filt", ["name", "center", "pitch", "limPhysX", "limPhysY", "surface", "material", "thickness"]
|
||||
)
|
||||
|
||||
feWindow = filt(
|
||||
name="FE-WINDOW",
|
||||
center=(0.0, 6158, sourceHeight),
|
||||
pitch=np.pi / 2,
|
||||
limPhysX=(-6, 6),
|
||||
limPhysY=(-3.0, 3.0),
|
||||
surface="None",
|
||||
material=filterDiamond,
|
||||
thickness=0.1,
|
||||
)
|
||||
feWindow = feWindow._replace(
|
||||
surface="CVD Diamond window {0:0.0f} $\mu$m".format(feWindow.thickness * 1e3)
|
||||
)
|
||||
|
||||
feFilt = filt(
|
||||
name="FE-FI",
|
||||
center=(0.0, 6590, sourceHeight),
|
||||
pitch=np.pi / 2,
|
||||
limPhysX=(-15, 15),
|
||||
limPhysY=(-10, 10),
|
||||
surface="None",
|
||||
material=filterGraphite,
|
||||
thickness=0.25,
|
||||
)
|
||||
feFilt = feFilt._replace(surface="Graphite filter {0:0.0f} $\mu$m".format(feFilt.thickness * 1e3))
|
||||
|
||||
# Collimating mirror
|
||||
collimatingMirror = namedtuple(
|
||||
"collimatingMirror",
|
||||
[
|
||||
"name",
|
||||
"center",
|
||||
"surface",
|
||||
"material",
|
||||
"limPhysX",
|
||||
"limPhysY",
|
||||
"limOptX",
|
||||
"limOptY",
|
||||
"R",
|
||||
"pitch",
|
||||
"jack1",
|
||||
"jack2",
|
||||
"jack3",
|
||||
"tx1",
|
||||
"tx2",
|
||||
],
|
||||
)
|
||||
|
||||
cm = collimatingMirror(
|
||||
name="FE-CM",
|
||||
center=[0, 7618, sourceHeight],
|
||||
surface=("Si", "Pt", "Rh"),
|
||||
material=(stripeSi, stripePt, stripeRh),
|
||||
limPhysX=(-30, 30),
|
||||
limPhysY=(-600, 600),
|
||||
limOptX=((-21, -8, 5), (-11, 2, 21)),
|
||||
limOptY=((-500, -500, -500), (500, 500, 500)),
|
||||
R=[3e6, 15e6],
|
||||
pitch=[1.4e-3, 4.5e-3],
|
||||
jack1=[0.0, 7210.0, 0.0], # Tripod X, Y, Z (global)
|
||||
jack2=[-210.0, 8310.0, 0.0],
|
||||
jack3=[210.0, 8310.0, 0.0],
|
||||
tx1=[0.0, -575.5], # X-Stage 1 [x, y] (local)
|
||||
tx2=[0.0, 575],
|
||||
) # X-Stage 2
|
||||
|
||||
apertures = namedtuple("apertures", ["name", "center", "opening"])
|
||||
|
||||
fePS = apertures(
|
||||
name="FE-PS", center=[0, 8760, sourceHeight], opening=[-39 / 2, 39 / 2, -10, 29]
|
||||
) # left, right, bottom, top
|
||||
|
||||
opWbBsBlock = apertures(
|
||||
name="OP-WB-BS-BLOCK", center=[0.0, 13606 - 135, sourceHeight], opening=[-18.0, 18.0, 42, 76]
|
||||
) # left, right, bottom, top
|
||||
|
||||
opSlits = apertures(
|
||||
name="OP-SLITS", center=[0, 14145 - 135, sourceHeight], opening=[-35 / 2, 35 / 2, 47.5, 82.5]
|
||||
)
|
||||
|
||||
# Monochromator
|
||||
monochromator = namedtuple(
|
||||
"monochromator",
|
||||
[
|
||||
"name",
|
||||
"center",
|
||||
"xtal",
|
||||
"material1",
|
||||
"material2",
|
||||
"xtalWidth",
|
||||
"xtalOffsetX",
|
||||
"xtalLength1",
|
||||
"xtalLength2",
|
||||
"xtalGap",
|
||||
"rotOffset",
|
||||
"heightOffset",
|
||||
"braggLim",
|
||||
"jack1",
|
||||
"jack2",
|
||||
"jack3",
|
||||
"tx",
|
||||
],
|
||||
)
|
||||
|
||||
mo1 = monochromator(
|
||||
name="OP-CCM1",
|
||||
center=[0.0, 11670 - 135, sourceHeight],
|
||||
xtal=("Si311", "Si111"),
|
||||
material1=(si311_1, si111_1),
|
||||
material2=(si311_2, si111_2),
|
||||
xtalWidth=(20, 20),
|
||||
xtalOffsetX=(-19.2, 19.2),
|
||||
xtalLength1=(60, 60),
|
||||
xtalLength2=(60, 60),
|
||||
xtalGap=(8, 8),
|
||||
rotOffset=6, # not sure what it is
|
||||
heightOffset=8.5, # not sure what it is
|
||||
braggLim=[4, 35],
|
||||
jack1=[0.0, 11350.0, 0.0], # Tripod not available!
|
||||
jack2=[-400.0, 12350.0, 0.0],
|
||||
jack3=[400.0, 12350.0, 0.0],
|
||||
tx=0.0,
|
||||
) # X-Stage [x]
|
||||
|
||||
# Focusing mirror
|
||||
focusingMirror = namedtuple(
|
||||
"focusingMirror",
|
||||
[
|
||||
"name",
|
||||
"center",
|
||||
"surfaceToroid",
|
||||
"materialToroid",
|
||||
"limPhysXToroid",
|
||||
"limPhysYToroid",
|
||||
"limOptXToroid",
|
||||
"limOptYToroid",
|
||||
"R",
|
||||
"pitch",
|
||||
"r",
|
||||
"xToroid",
|
||||
"hToroid",
|
||||
"jack1",
|
||||
"jack2",
|
||||
"jack3",
|
||||
"tx1",
|
||||
"tx2",
|
||||
],
|
||||
)
|
||||
|
||||
fm = focusingMirror(
|
||||
name="OP-FM",
|
||||
center=[0.0, 15580 - 135, sourceHeight],
|
||||
surfaceToroid=("Rh (toroid)", "Pt (toroid)"),
|
||||
materialToroid=(stripeRh, stripePt),
|
||||
limPhysXToroid=(-54.0, 54.0),
|
||||
limPhysYToroid=(-565.0, 565.0),
|
||||
limOptXToroid=((4.865, -40.882), (43.388, -4.865)),
|
||||
limOptYToroid=((-500.0, -500.0), (500.0, 500.0)),
|
||||
R=[3e6, 15e6],
|
||||
pitch=[1.4e-3, 4.5e-3],
|
||||
r=[30, 20],
|
||||
xToroid=[24.126, -22, 874], # offset in local x
|
||||
hToroid=[7.0, 11.3], # depth of the cylinder at x = xCylinder1 and x = xCylinder2.
|
||||
jack1=[0.0, 14980.0, 0.0],
|
||||
jack2=[-75.0, 16180.0, 0.0],
|
||||
jack3=[75.0, 16180.0, 0.0],
|
||||
tx1=[0.0, -575.0], # X-Stage 1 [x, y]
|
||||
tx2=[0.0, 575.0],
|
||||
) # X-Stage 2 [x, y]
|
||||
|
||||
ehWindow = filt(
|
||||
name="EH-WINDOW",
|
||||
center=(0.0, 22225 - 135, sourceHeight),
|
||||
pitch=np.pi / 2,
|
||||
limPhysX=(-10.0, 10.0),
|
||||
limPhysY=(17.5, 92.5),
|
||||
surface="None",
|
||||
material=filterBe,
|
||||
thickness=0.25,
|
||||
)
|
||||
ehWindow = ehWindow._replace(
|
||||
surface="Beryllium window {0:0.0f} $\mu$m".format(ehWindow.thickness * 1e3)
|
||||
)
|
||||
|
||||
# Sample
|
||||
sample = namedtuple("sample", ["name", "center"])
|
||||
|
||||
smpl = sample(name="OP-SMPL", center=[0, 24000 - 135, sourceHeight])
|
||||
|
||||
ES1 = sample(name="EH-ES1", center=[0, 24000, sourceHeight])
|
||||
ES2 = sample(name="EH-ES2", center=[0, 25000, sourceHeight])
|
||||
Reference in New Issue
Block a user