diff --git a/debye_bec/bec_widgets/widgets/digital_twin/calculations/calc_varia.py b/debye_bec/bec_widgets/widgets/digital_twin/calculations/calc_varia.py index 6027432..5c80afb 100644 --- a/debye_bec/bec_widgets/widgets/digital_twin/calculations/calc_varia.py +++ b/debye_bec/bec_widgets/widgets/digital_twin/calculations/calc_varia.py @@ -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 diff --git a/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py b/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py index 12b1bb3..bf44cbe 100644 --- a/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py +++ b/debye_bec/bec_widgets/widgets/digital_twin/digital_twin.py @@ -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: