Merge branch 'feat/widget-development' into fix/typo-on-widget-development
CI for debye_bec / test (pull_request) Successful in 58s
CI for debye_bec / test (push) Successful in 1m12s

This commit is contained in:
2026-07-30 09:10:19 +02:00
2 changed files with 82 additions and 3 deletions
@@ -262,6 +262,12 @@ def fm_ideal_pitch(
Returns:
tuple[float, float | None]: Pitch of mirror in rad, qy in mm
"""
# logger.info("Calculate pitch and qy now...")
# logger.info(f"sldi_hacc: {sldi_hacc}")
# logger.info(f"sldi_vacc: {sldi_vacc}")
# logger.info(f"fm_stripe: {fm_stripe}")
# logger.info(f"smpl: {smpl}")
p_cm = bl.cm.center[1] # posCM
p = bl.fm.center[1] # posFM
q = smpl - bl.fm.center[1] # dist posFM to posEX
@@ -298,10 +304,67 @@ def fm_ideal_pitch(
pitch = np.arcsin(bl.fm.r[0] / (2 * f)) # ideal pitch for FM
if "Pt" in fm_stripe:
pitch = np.arcsin(bl.fm.r[1] / (2 * f)) # ideal pitch for FM
# logger.info(f"pitch: {pitch}")
# logger.info(f"fm_pitch: {pitch}")
# logger.info(f"qy: {qy}")
return pitch, qy
def calc_beamsize(
sldi_hacc: float,
sldi_vacc: float,
fm_stripe: str,
fm_pitch: float,
fm_radius: float,
smpl: float,
) -> tuple[float, float | None]:
"""
Calculate the resulting beamsize according to the input parameters
Args:
sldi_hacc(float): Horizontal acceptance of frontend slits
sldi_vacc(float): Vertical acceptance of frontend slits
fm_stripe(str): Mirror stripe
fm_pitch(float): Focusing mirror pitch in rad
fm_radius(float): Focusing mirror bender radius in m
smpl(float): Sample position in mm from source
Returns:
tuple[float, float | None]: horizontal spot size, vertical spot size, both in mm
"""
# logger.info("Calculate beamsize now...")
# logger.info(f"sldi_hacc: {sldi_hacc}")
# logger.info(f"sldi_vacc: {sldi_vacc}")
# logger.info(f"fm_stripe: {fm_stripe}")
# logger.info(f"fm_pitch: {fm_pitch}")
# logger.info(f"fm_radius: {fm_radius}")
# logger.info(f"smpl: {smpl}")
p_cm = bl.cm.center[1] # posCM
p = bl.fm.center[1] # posFM
q = smpl - bl.fm.center[1] # dist posFM to posEX
qy = fm_radius * np.sin(fm_pitch) / 2
a = 2 * np.tan(sldi_hacc) * bl.fm.center[1] # Beam width at focusing mirror
b = 2 * np.tan(sldi_vacc) * bl.cm.center[1] # Beam height at focusing mirror (collimated beam)
f = 0
if "Rh" in fm_stripe:
f = bl.fm.r[0] / (2 * np.sin(fm_pitch))
if "Pt" in fm_stripe:
f = bl.fm.r[1] / (2 * np.sin(fm_pitch))
qx = p * f / (p - f)
x = a * (qx - q) / p
y = b * (qy - q) / p_cm
# Change this | to a plus if calculation is not correct
fm_focx = -4 * (64043 - 125000 * np.sqrt(0.26249637 + 0.395284 * x)) / 98821
# Change this | to a plus if calculation is not correct
fm_focy = -1 * (314591 - 250000 * np.sqrt(1.58347995 + 12.734248 * y)) / 1591781
# logger.info(f"f: {f}")
# logger.info(f"qx: {qx}")
# logger.info(f"qy: {qy}")
# logger.info(f"fm_focx: {fm_focx}")
# logger.info(f"fm_focy: {fm_focy}")
return fm_focx, fm_focy
def cm_critical_angle(cm_stripe: Literal["Si", "Pt", "Rh"], energy) -> float:
"""
Calculate the critical angle of the mirror stripe
@@ -40,6 +40,7 @@ from .calculations.calc_positions import calc_positions
from .calculations.calc_sideview import calc_sideview
from .calculations.calc_surfaces import calc_surfaces
from .calculations.calc_varia import (
calc_beamsize,
cm_critical_angle,
cm_reflectivity,
cm_stripe_to_trx,
@@ -168,6 +169,7 @@ class DigitalTwin(BECWidget, QWidget):
# Initialize all values
self.load_offsets(recalculate=False)
self.calc_assistant(identifier="init")
self.adapt_reality()
# Timer: update reality plots every 1 second
self._timer = QTimer(self)
@@ -576,6 +578,7 @@ class DigitalTwin(BECWidget, QWidget):
pos["mo1_trx"] = self.dev.mo1_trx.read(cached=True)["mo1_trx"]["value"]
pos["fm_trx"] = self.dev.fm_trx.read(cached=True)["fm_trx"]["value"]
pos["fm_rotx"] = self.dev.fm_rotx.read(cached=True)["fm_rotx"]["value"]
pos["fm_bnd_radius"] = self.dev.fm_bnd_radius.read(cached=True)["fm_bnd_radius"]["value"]
if self.beamline == "x01da":
pos["ot_es1_trz"] = self.dev.ot_es1_trz.read(cached=True)["ot_es1_trz"]["value"]
@@ -605,17 +608,30 @@ class DigitalTwin(BECWidget, QWidget):
self.input.mo1_xtal.set_current_text(
self.dev.mo1_bragg.read(cached=True)["mo1_bragg_crystal_current_xtal_string"]["value"]
)
self.input.fm_stripe.set_current_text(fm_trx_to_stripe(-pos["fm_trx"]))
self.input.fm_focus.set_current_text("Manual")
fm_stripe = fm_trx_to_stripe(-pos["fm_trx"])
self.input.fm_stripe.set_current_text(fm_stripe)
fm_rotx_real = 2 * pos["cm_rotx"] - pos["fm_rotx"]
self.input.fm_rotx.set_number(fm_rotx_real)
match self.input.smpl:
case InputNumberField():
smpl = pos["ot_es1_trz"]
self.input.smpl.set_number(pos["ot_es1_trz"])
case ComboBox():
table = self.ask_table_selection(self.input.smpl.currentText())
smpl = table_to_smpl_pos(table)
self.input.smpl.set_current_text(table)
fm_focx, fm_focy = calc_beamsize(
h_acc, v_acc, fm_stripe, -fm_rotx_real * 1e-3, pos["fm_bnd_radius"] * 1e6, smpl
)
if fm_focx < 0.08 and fm_focy < 0.08:
self.input.fm_focus.set_current_text("Focused")
else:
self.input.fm_focus.set_current_text("Defocused")
self.input.fm_focx.set_number(fm_focx)
self.input.fm_focy.set_number(fm_focy)
self.calc_assistant(identifier="init")
def ask_table_selection(self, preset=None) -> str | None: