Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c0bd34641 | |||
| a6814eff2b | |||
| 8be85f7a9a |
@@ -416,3 +416,16 @@ def pipe_geometries() -> list[dict[str, np.ndarray]]:
|
||||
}
|
||||
)
|
||||
return pipes
|
||||
|
||||
|
||||
def table_to_smpl_pos(table: str) -> float:
|
||||
"""
|
||||
Return the sample position based on the table name.
|
||||
|
||||
Args:
|
||||
table (str): Table name, e.g. ES1 or ES2
|
||||
"""
|
||||
|
||||
if table in bl.tables:
|
||||
return bl.tables[table]["smpl"]
|
||||
raise ValueError(f"Table {table} not found in beamline parameter file")
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Digital Twin: Custom BEC widget to support the beamline alignment.
|
||||
"""
|
||||
|
||||
import socket
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Literal, cast
|
||||
@@ -20,6 +21,7 @@ from qtpy.QtCore import Qt, QTimer
|
||||
from qtpy.QtGui import QFont
|
||||
from qtpy.QtWidgets import (
|
||||
QApplication,
|
||||
QComboBox,
|
||||
QDialog,
|
||||
QDialogButtonBox,
|
||||
QHBoxLayout,
|
||||
@@ -46,17 +48,17 @@ from debye_bec.bec_widgets.widgets.digital_twin.calculations.calc_varia import (
|
||||
mo1_bragg_angle,
|
||||
mo1_energy_resolution,
|
||||
sldi_gap_to_acc,
|
||||
table_to_smpl_pos,
|
||||
)
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.panels.input_panel import InputPanel
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.panels.mover_panel import MoverPanel
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.panels.plots import SideviewPlot, SurfacePlots
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.panels.settings_panel import SettingsPanel
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.types import ConfigDict
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.types import BeamlineId, ConfigDict
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.widgets.qt_widgets import ComboBox, InputNumberField
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
OFFSET_FILE = "debye_bec/debye_bec/bec_widgets/widgets/digital_twin/x01da_offsets.yaml"
|
||||
|
||||
|
||||
class DigitalTwin(BECWidget, QWidget):
|
||||
"""
|
||||
@@ -70,16 +72,28 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
super().__init__(parent=parent, theme_update=True, *arg, **kwargs)
|
||||
self.get_bec_shortcuts()
|
||||
|
||||
self.beamline = self.get_beamline_id()
|
||||
|
||||
# Debugging, override beamline!
|
||||
# self.beamline = BeamlineId.X10DA
|
||||
|
||||
self.offset_fie = None
|
||||
self.set_offset_file()
|
||||
|
||||
# Check if devices are all in config
|
||||
self.check_config()
|
||||
self.bec_dispatcher.connect_slot(self.check_config, MessageEndpoints.device_config_update())
|
||||
self.check_bec_config()
|
||||
self.bec_dispatcher.connect_slot(
|
||||
self.check_bec_config, MessageEndpoints.device_config_update()
|
||||
)
|
||||
|
||||
logger.info(f"Start Digital Twin with beamline {self.beamline} and all devices available")
|
||||
|
||||
central = QWidget()
|
||||
self.root_layout = QHBoxLayout(central)
|
||||
|
||||
self.input_widget = QWidget()
|
||||
self.input_layout = QVBoxLayout(self.input_widget)
|
||||
self.input = InputPanel()
|
||||
self.input = InputPanel(self.beamline)
|
||||
self.settings = SettingsPanel()
|
||||
self.input_layout.addWidget(self.input)
|
||||
self.input_layout.addWidget(self.settings)
|
||||
@@ -113,7 +127,11 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
self.input.fm_rotx.value_changed_connect(self.calc_assistant)
|
||||
self.input.fm_focx.value_changed_connect(self.calc_assistant)
|
||||
self.input.fm_focy.value_changed_connect(self.calc_assistant)
|
||||
self.input.smpl.value_changed_connect(self.calc_assistant)
|
||||
match self.input.smpl:
|
||||
case InputNumberField():
|
||||
self.input.smpl.value_changed_connect(self.calc_assistant)
|
||||
case ComboBox():
|
||||
self.input.smpl.activated_connect(self.calc_assistant)
|
||||
|
||||
self.input.adapt_reality.clicked_connect(self.adapt_reality)
|
||||
self.settings.load_offsets.clicked_connect(self.load_offsets)
|
||||
@@ -144,8 +162,40 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
self.surface_plots.apply_theme(theme)
|
||||
self.mover.apply_theme(theme)
|
||||
|
||||
def get_beamline_id(self) -> BeamlineId:
|
||||
"""
|
||||
Based on the bec servers hostname, tries to extract the beamline
|
||||
identifier (e.g. x01da, x10da, etc).
|
||||
|
||||
Raises:
|
||||
ValueError if beamline cannot be extracted from hostname or beamline not implemented.
|
||||
"""
|
||||
bec_hostname = socket.gethostname()
|
||||
start = bec_hostname.find("x")
|
||||
if start != -1:
|
||||
beamline = bec_hostname[start : start + 5]
|
||||
match beamline:
|
||||
case "x01da":
|
||||
return BeamlineId.X01DA
|
||||
case "x10da":
|
||||
return BeamlineId.X10DA
|
||||
case _:
|
||||
raise ValueError(f"Not implemented beamline {beamline}")
|
||||
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_config(self, *args):
|
||||
def check_bec_config(self, *args):
|
||||
"""
|
||||
Checks the BEC config and opens a window if not all necessary
|
||||
devices are loaded in the config. If called from a slot from
|
||||
@@ -161,7 +211,7 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
"sldi_gapy",
|
||||
"cm_trx",
|
||||
"cm_try",
|
||||
"cm_bnd_radius",
|
||||
"cm_bnd",
|
||||
"cm_rotx",
|
||||
"mo1_bragg",
|
||||
"mo1_trx",
|
||||
@@ -171,23 +221,33 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
"bm1_try",
|
||||
"fm_trx",
|
||||
"fm_try",
|
||||
"fm_bnd_radius",
|
||||
"fm_bnd",
|
||||
"fm_rotx",
|
||||
"fm_roty",
|
||||
"fm_rotz",
|
||||
"sl2_centery",
|
||||
"sl2_gapy",
|
||||
"bm2_try",
|
||||
"ot_try",
|
||||
"ot_rotx",
|
||||
"es0wi_try",
|
||||
"ot_es1_trz",
|
||||
]
|
||||
if self.beamline == "x01da": # X01DA specific devices
|
||||
devices.extend(
|
||||
[
|
||||
"cm_bnd_radius",
|
||||
"fm_bnd_radius",
|
||||
"sl2_centery",
|
||||
"sl2_gapy",
|
||||
"ot_try",
|
||||
"ot_es1_trz",
|
||||
]
|
||||
)
|
||||
if self.beamline == "x10da": # X10DA specific devices
|
||||
devices.extend(["mo1_rotx", "es1_try", "es1ic0_try", "es1ic1_try", "es1ic2_try"])
|
||||
|
||||
while True:
|
||||
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()
|
||||
@@ -327,6 +387,13 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
assert cm_trx is not None, f"No cm_trx found for given stripe {cm_stripe}!"
|
||||
assert fm_trx is not None, f"No fm_trx found for given stripe {fm_stripe}!"
|
||||
|
||||
match self.input.smpl:
|
||||
case InputNumberField():
|
||||
smpl = self.input.smpl.value()
|
||||
case ComboBox():
|
||||
table = self.input.smpl.currentText()
|
||||
smpl = table_to_smpl_pos(table)
|
||||
|
||||
config: ConfigDict = {
|
||||
"energy": self.input.energy.value(),
|
||||
"h_acc": self.input.sldi_hacc.value(),
|
||||
@@ -342,7 +409,7 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
"fm_trx": fm_trx,
|
||||
"fm_qy": fm_qy,
|
||||
"fm_gain_height": 1,
|
||||
"smpl": self.input.smpl.value(),
|
||||
"smpl": smpl,
|
||||
}
|
||||
|
||||
# Apply offsets
|
||||
@@ -508,9 +575,52 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
self.input.fm_focus.set_current_text("Manual")
|
||||
fm_rotx_real = 2 * pos["cm_rotx"] - pos["fm_rotx"]
|
||||
self.input.fm_rotx.set_number(fm_rotx_real)
|
||||
self.input.smpl.set_number(pos["ot_es1_trz"])
|
||||
match self.input.smpl:
|
||||
case InputNumberField():
|
||||
self.input.smpl.set_number(pos["ot_es1_trz"])
|
||||
case ComboBox():
|
||||
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, 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(self)
|
||||
dialog.setWindowTitle("Select Table")
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
text = QLabel("Select the current table in use.")
|
||||
|
||||
combo = QComboBox()
|
||||
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
|
||||
)
|
||||
buttons.accepted.connect(dialog.accept)
|
||||
buttons.rejected.connect(dialog.reject)
|
||||
|
||||
layout.addWidget(text)
|
||||
layout.addWidget(combo)
|
||||
layout.addWidget(buttons)
|
||||
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
return combo.currentText()
|
||||
return None
|
||||
|
||||
@SafeSlot()
|
||||
def load_offsets(self, *_, recalculate: bool = True):
|
||||
"""
|
||||
@@ -523,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)
|
||||
@@ -568,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)
|
||||
@@ -784,7 +894,12 @@ class DigitalTwin(BECWidget, QWidget):
|
||||
Literal["Defocused", "Focused", "Manual"], self.input.fm_focus.currentText()
|
||||
)
|
||||
fm_stripe = self.input.fm_stripe.currentText()
|
||||
smpl = self.input.smpl.value()
|
||||
match self.input.smpl:
|
||||
case InputNumberField():
|
||||
smpl = self.input.smpl.value()
|
||||
case ComboBox():
|
||||
table = self.input.smpl.currentText()
|
||||
smpl = table_to_smpl_pos(table)
|
||||
sldi_hacc = self.input.sldi_hacc.value() * 1e-3
|
||||
sldi_vacc = self.input.sldi_vacc.value() * 1e-3
|
||||
fm_focx = self.input.fm_focx.value()
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
Panel for user inputs of the digital twin widget
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
|
||||
# pylint: disable=E0611
|
||||
from qtpy.QtWidgets import QLayout, QVBoxLayout, QWidget
|
||||
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.types import BeamlineId
|
||||
from debye_bec.bec_widgets.widgets.digital_twin.widgets.qt_widgets import (
|
||||
Button,
|
||||
ComboBox,
|
||||
@@ -15,9 +18,14 @@ from debye_bec.bec_widgets.widgets.digital_twin.widgets.qt_widgets import (
|
||||
|
||||
|
||||
class InputPanel(QWidget):
|
||||
"""Panel for user inputs of the digital twin widget"""
|
||||
"""
|
||||
Panel for user inputs of the digital twin widget
|
||||
|
||||
def __init__(self, parent=None):
|
||||
Args:
|
||||
beamline (BeamlineId): Beamline id type
|
||||
"""
|
||||
|
||||
def __init__(self, beamline: BeamlineId, parent=None):
|
||||
super().__init__(parent)
|
||||
self._layout = QVBoxLayout(self)
|
||||
self._layout.setSizeConstraint(QLayout.SetFixedSize) # type: ignore
|
||||
@@ -91,9 +99,11 @@ class InputPanel(QWidget):
|
||||
)
|
||||
|
||||
# Focusing Mirror
|
||||
self.fm_stripe = ComboBox(
|
||||
"fm_stripe", "Stripe", ["Rh (toroid)", "Rh (flat)", "Pt (toroid)", "Pt (flat)"]
|
||||
)
|
||||
stripes: dict[BeamlineId, list[str]] = {
|
||||
BeamlineId.X01DA: ["Rh (toroid)", "Rh (flat)", "Pt (toroid)", "Pt (flat)"],
|
||||
BeamlineId.X10DA: ["Rh (toroid)", "Pt (toroid)"],
|
||||
}
|
||||
self.fm_stripe = ComboBox("fm_stripe", "Stripe", stripes[beamline])
|
||||
self.fm_focus = ComboBox("fm_focus", "Focus Type", ["Manual", "Focused", "Defocused"])
|
||||
self.fm_rotx = InputNumberField(
|
||||
"fm_rotx",
|
||||
@@ -144,18 +154,9 @@ class InputPanel(QWidget):
|
||||
|
||||
# Sample
|
||||
self.cm_fm_harm_suppr = NumberIndicator("Total Suppression Factor at x eV", "", decimals=0)
|
||||
self.smpl = InputNumberField(
|
||||
"smpl",
|
||||
"Sample Position",
|
||||
unit="mm",
|
||||
init=23511,
|
||||
decimals=0,
|
||||
single_step=100,
|
||||
ll=23000,
|
||||
hl=30000,
|
||||
)
|
||||
self.smpl = self._create_smpl(beamline)
|
||||
|
||||
# Assemble complete assitant group
|
||||
# Assemble complete assistant group
|
||||
self.input_group = Group(
|
||||
"User Input",
|
||||
[
|
||||
@@ -172,3 +173,19 @@ class InputPanel(QWidget):
|
||||
|
||||
self._layout.addWidget(self.input_group)
|
||||
self._layout.addStretch()
|
||||
|
||||
def _create_smpl(self, beamline: BeamlineId) -> Union[InputNumberField, ComboBox]:
|
||||
match beamline:
|
||||
case BeamlineId.X01DA:
|
||||
return InputNumberField(
|
||||
"smpl",
|
||||
"Sample Position",
|
||||
unit="mm",
|
||||
init=23511,
|
||||
decimals=0,
|
||||
single_step=100,
|
||||
ll=23000,
|
||||
hl=30000,
|
||||
)
|
||||
case BeamlineId.X10DA:
|
||||
return ComboBox("smpl", "Sample Position", ["ES1", "ES2"])
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
"""Types used for the beamline config and for plotting data"""
|
||||
|
||||
from enum import Enum
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
class BeamlineId(str, Enum):
|
||||
"""
|
||||
Identifier for supported beamlines.
|
||||
"""
|
||||
|
||||
X01DA = "x01da"
|
||||
X10DA = "x10da"
|
||||
|
||||
|
||||
class ConfigDict(TypedDict):
|
||||
"""
|
||||
Typed dictionary representing the beamline configuration.
|
||||
|
||||
@@ -304,6 +304,8 @@ smpl = sample(name="EH-SMPL", center=[0, 23365, sourceHeight])
|
||||
|
||||
smpl2 = sample(name="EH-SMPL2", center=[0, 27500, sourceHeight])
|
||||
|
||||
tables = {}
|
||||
|
||||
# Vacuum pipes
|
||||
# DN40CF ID = 35 mm oder 37 mm
|
||||
# DN50CF ID = 47.5 mm
|
||||
|
||||
@@ -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