From 0ed6c08013fbf040fda563363920487964a97efc Mon Sep 17 00:00:00 2001 From: PONS Date: Fri, 31 Jul 2026 16:19:48 +0200 Subject: [PATCH 1/5] Added input arg check --- pyaml/tuning_tools/tune.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyaml/tuning_tools/tune.py b/pyaml/tuning_tools/tune.py index 382092bf5..db267725e 100644 --- a/pyaml/tuning_tools/tune.py +++ b/pyaml/tuning_tools/tune.py @@ -137,6 +137,8 @@ def set(self, tune: np.array, iter: int = 1, wait_time: float = 0.0): wait_time : float Time to wait in second between 2 iterations """ + if np.shape(tune) != (2,): + raise PyAMLException("Tune.add(): invalid input tune dimension, (2,) expected") for i in range(iter): diff_tune = tune - self.readback() if i == iter: @@ -168,6 +170,8 @@ def add(self, dtune: np.array, wait_time: float = 0.0): iter_nb: int wait_time: float """ + if np.shape(dtune) != (2,): + raise PyAMLException("Tune.add(): invalid input dtune dimension, (2,) expected") strengths = self._quads.strengths.get() strengths += self.correct(dtune) self._quads.strengths.set(strengths) From 8eab77581c9c0bec3b64ef01171394051419c94e Mon Sep 17 00:00:00 2001 From: PONS Date: Fri, 31 Jul 2026 16:20:38 +0200 Subject: [PATCH 2/5] Allow access to other strength (model only) --- pyaml/lattice/abstract_impl.py | 80 ++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/pyaml/lattice/abstract_impl.py b/pyaml/lattice/abstract_impl.py index abe26360d..26e733a4d 100644 --- a/pyaml/lattice/abstract_impl.py +++ b/pyaml/lattice/abstract_impl.py @@ -23,37 +23,37 @@ class RWHardwareScalar(abstract.ReadWriteFloatScalar): """ def __init__(self, elements: list[at.Element], poly: PolynomInfo, model: MagnetModel): - self.__model = model - self.__elements = elements - self.__poly = [e.__getattribute__(poly.attName) for e in elements] - self.__sign = poly.sign - self.__polyIdx = poly.index - self.__length: float = 0.0 + self._model = model + self._elements = elements + self._poly = [e.__getattribute__(poly.attName) for e in elements] + self._sign = poly.sign + self._polyIdx = poly.index + self._length: float = 0.0 for e in elements: - self.__length += e.Length + self._length += e.Length def get_length(self) -> float: - return self.__length + return self._length def get(self) -> float: s = 0 - for idx, e in enumerate(self.__elements): - s += self.__poly[idx][self.__polyIdx] * self.__sign * e.Length - return self.__model.compute_hardware_values([s])[0] + for idx, e in enumerate(self._elements): + s += self._poly[idx][self._polyIdx] * self._sign * e.Length + return self._model.compute_hardware_values([s])[0] def set(self, value: float): - s = self.__model.compute_strengths([value])[0] - for idx, _ in enumerate(self.__elements): - self.__poly[idx][self.__polyIdx] = s / (self.__length * self.__sign) + s = self._model.compute_strengths([value])[0] + for idx, _ in enumerate(self._elements): + self._poly[idx][self._polyIdx] = s / (self._length * self._sign) def set_and_wait(self, value: float): raise NotImplementedError("Not implemented yet.") def unit(self) -> str: - return self.__model.get_hardware_units()[0] + return self._model.get_hardware_units()[0] def get_model(self) -> MagnetModel: - return self.__model + return self._model # ------------------------------------------------------------------------------ @@ -65,29 +65,45 @@ class RWStrengthScalar(abstract.ReadWriteFloatScalar): """ def __init__(self, elements: list[at.Element], poly: PolynomInfo, model: MagnetModel): - self.__model = model - self.__elements = elements - self.__poly = [e.__getattribute__(poly.attName) for e in elements] - self.__sign = poly.sign - self.__polyIdx = poly.index - self.__length = 0 + self._model = model + self._elements = elements + self._poly = [e.__getattribute__(poly.attName) for e in elements] + self._sign = poly.sign + self._polyIdx = poly.index + self._length = 0 for e in elements: - self.__length += e.Length + self._length += e.Length def get_element_length(self) -> float: - return self.__length + return self._length # Gets the value - def get(self) -> float: + def get(self, polynom: str = None, polyidx: int = None) -> float: + if polynom is None: + pIdx = self._polyIdx + poly = self._poly + else: + # Override strength access + pIdx = polyidx + poly = [e.__getattribute__(polynom) for e in self._elements] + s = 0 - for idx, e in enumerate(self.__elements): - s += self.__poly[idx][self.__polyIdx] * self.__sign * e.Length + for idx, e in enumerate(self._elements): + s += poly[idx][pIdx] * self._sign * e.Length return s # Sets the value - def set(self, value: float): - for idx, _ in enumerate(self.__elements): - self.__poly[idx][self.__polyIdx] = value / (self.__length * self.__sign) + def set(self, value: float, polynom: str = None, polyidx: int = None): + if polynom is None: + pIdx = self._polyIdx + poly = self._poly + else: + # Override strength access + pIdx = polyidx + poly = [e.__getattribute__(polynom) for e in self._elements] + + for idx, _ in enumerate(self._elements): + poly[idx][pIdx] = value / (self._length * self._sign) # Sets the value and wait that the read value reach the setpoint def set_and_wait(self, value: float): @@ -95,11 +111,11 @@ def set_and_wait(self, value: float): # Gets the unit of the value def unit(self) -> str: - return self.__model.get_strength_units()[0] + return self._model.get_strength_units()[0] # ------------------------------------------------------------------------------ def get_model(self) -> MagnetModel: - return self.__model + return self._model # ------------------------------------------------------------------------------ From a7b4d8f2c8a5339ba6b76ea073ad6751fdfcaece Mon Sep 17 00:00:00 2001 From: PONS Date: Fri, 31 Jul 2026 16:23:21 +0200 Subject: [PATCH 3/5] Added new BBA method --- pyaml/tuning_tools/bba2.py | 410 ++++ tests/config/EBSOrbit.yaml | 1641 ++++++++++++++++- tests/config/catalogs/ebs_bpm_catalogs.yaml | 12 + .../config/catalogs/ebs_magnets_catalogs.yaml | 4 +- 4 files changed, 2064 insertions(+), 3 deletions(-) create mode 100644 pyaml/tuning_tools/bba2.py diff --git a/pyaml/tuning_tools/bba2.py b/pyaml/tuning_tools/bba2.py new file mode 100644 index 000000000..969da26d8 --- /dev/null +++ b/pyaml/tuning_tools/bba2.py @@ -0,0 +1,410 @@ +import logging +import time +from typing import Callable, Optional + +import numpy as np +from pydantic import ConfigDict + +from ..common.constants import Action +from ..common.exception import PyAMLException +from .measurement_tool import MeasurementTool, MeasurementToolConfigModel + +logger = logging.getLogger(__name__) + +PYAMLCLASS = "BBA2" + + +class ConfigModel(MeasurementToolConfigModel): + """ + Configuration model for Beam Based Alignment. + BBA finds the magnetic center of a quad (zero crossing). + + Parameters + ---------- + bpm_array_name : str + BPM array name (orbit) + bpm_name : str + BPM to be corrected (close to the quad) + hcorr_name : str + Horizontal corrector used to make a deviation in the quad + vcorr_name : str + Vertical corrector used to make a deviation in the quad + quad_name : str + Quadrupole used to find the center + hcorr_delta : float + Horizontal corrector delta strength + vcorr_delta : float + Vertical corrector delta strength + quad_delta : float + Quadrupole delta strength + """ + + model_config = ConfigDict(arbitrary_types_allowed=True, extra="forbid") + + bpm_array_name: str + bpm_name: str + hcorr_name: str + vcorr_name: str + quad_name: str + hcorr_delta: float + vcorr_delta: float + quad_delta: float + + +class BBA2(MeasurementTool): + def __init__(self, cfg: ConfigModel): + super().__init__(cfg.name) + self._cfg = cfg + + def _sort_x_vs_y(self, x1, x2, x3, x4, y): + x1s = [a for _, a in sorted(zip(y, x1, strict=True))] + x2s = [a for _, a in sorted(zip(y, x2, strict=True))] + x3s = [a for _, a in sorted(zip(y, x3, strict=True))] + x4s = [a for _, a in sorted(zip(y, x4, strict=True))] + return x1s, x2s, x3s, x4s + + def _quad_response(self, bpmname: str, quadname: str, dk0=1e-5): + # Return normalized response of a dipolar kick in a quad from the model + + # Retrieve model handle + design = self.peer.peer.design + + # handles + quad = design.get_magnet(quadname) + orbit = design.get_bpms(bpmname).positions + a0 = quad.strength.get("PolynomA", 0) + b0 = quad.strength.get("PolynomB", 0) + step = [-dk0, 0, dk0] + + orb0 = orbit.get() + quad.strength.set(b0 - dk0, "PolynomB", 0) + orbm = orbit.get() + quad.strength.set(b0 + dk0, "PolynomB", 0) + orbp = orbit.get() + quad.strength.set(b0, "PolynomB", 0) + + orbx = [orbm[:, 0], orb0[:, 0], orbp[:, 0]] + fit = np.polynomial.polynomial.polyfit(step, orbx, 1) + ref_ios_x = fit[1] + + quad.strength.set(a0 - dk0, "PolynomA", 0) + orbm = orbit.get() + quad.strength.set(a0 + dk0, "PolynomA", 0) + orbp = orbit.get() + quad.strength.set(a0, "PolynomA", 0) + + orby = [orbm[:, 1], orb0[:, 1], orbp[:, 1]] + fit = np.polynomial.polynomial.polyfit(step, orby, 1) + ref_ios_y = fit[1] + + ref = np.array([ref_ios_x, ref_ios_y]).T + + return ref + + def _fit_kick(self, meas, ref, mask): + xy = np.multiply(meas[mask], ref[mask]) + xx = np.multiply(ref[mask], ref[mask]) + if sum(xx) == 0: + return 0 + else: + return sum(xy) / sum(xx) + + def _get_averaged_orbit(self): + avgorb = np.zeros((len(self._bpms), 2)) + for avg in range(self._nb_meas): + o = self._bpms.positions.get() + avgorb += o + if avg < self._nb_meas - 1: + time.sleep(self._sleep_meas) + + avgorb /= float(self._nb_meas) + return avgorb + + def _one_step_dk(self, dk0, dk1): + if any(abs(dk) > 200e-6 for dk in dk0): + raise PyAMLException("Requested dk too high (>200urad), consider using bump") + + # Set steerer + if np.fabs(dk0[0]) > 0: + _str = dk0[0] + self._initial_k0[0] + self._h_steer.strength.set(_str) + self.send_callback(Action.APPLY, {"step": self._step, "magnet": self._h_steer.name, "strength": _str}) + if np.fabs(dk0[1]) > 0: + _str = dk0[1] + self._initial_k0[1] + self._v_steer.strength.set(_str) + self.send_callback(Action.APPLY, {"step": self._step, "magnet": self._v_steer.name, "strength": _str}) + + time.sleep(self._sleep_step) + orb0 = self._get_averaged_orbit() + + # Set quad + _str = self._initial_k1 + dk1 * self._quad_polarity + self._quad.strength.set(_str) + self.send_callback(Action.APPLY, {"step": self._step, "magnet": self._quad.name, "strength": _str}) + time.sleep(self._sleep_step) + orbp = self._get_averaged_orbit() + + ios_x = [] + ios_y = [] + + if False: + # one more point at k1 - dk1 + _str = self._initial_k1 - dk1 * self._quad_polarity + self._quad.strength.set(_str) + self.send_callback(Action.APPLY, {"step": self._step, "magnet": self._quad.name, "strength": _str}) + time.sleep(self._sleep_step) + orbm = self._get_averaged_orbit() + # take slopes and compute corresponding delta orbit + stepx = [-self._dk1 * self._quad_polarity, 0, self._dk1 * self._quad_polarity] + orbx = [orbm[:, 0], orb0[:, 0], orbp[:, 0]] + fit = np.polynomial.polynomial.polyfit(stepx, orbx, 1) + ios_x = fit[1] * dk1 + + orby = [orbm[:, 1], orb0[:, 1], orbp[:, 1]] + fit = np.polynomial.polynomial.polyfit(stepx, orby, 1) + ios_y = fit[1] * dk1 + else: + ios_x = orbp[:, 0] - orb0[:, 0] + ios_y = orbp[:, 1] - orb0[:, 1] + + # resotre quad + _str = self._initial_k1 + self._quad.strength.set(_str) + self.send_callback(Action.APPLY, {"step": self._step, "magnet": self._quad.name, "strength": _str}) + + nanx = ~np.isnan(ios_x) + x = orb0[self._bpmi, 0] + sx2 = np.sum(np.square(ios_x[nanx])) + dkx = self._fit_kick(ios_x, self._ref_ios[:, 0], nanx) + + nany = ~np.isnan(ios_y) + y = orb0[self._bpmi, 1] + sy2 = np.sum(np.square(ios_y[nany])) + dky = self._fit_kick(ios_y, self._ref_ios[:, 1], nany) + return x, dkx, y, dky, sx2, sy2, ios_x, ios_y + + def measure( + self, + sleep_between_step: Optional[float] = None, + n_avg_meas: Optional[int] = None, + sleep_between_meas: Optional[float] = None, + callback: Optional[Callable] = None, + plane: Optional[str] = None, + ): + """ + Measure BBA. + + **Example** + + .. code-block:: python + + TODO + + Parameters + ---------- + sleep_between_step: float + Default time sleep after steerer or quad exitation + Default: from config + n_avg_meas : int, optional + Default number of orbit measurement per step used for averaging + Default from config + sleep_between_meas: float + Default time sleep between two orbit measurment + Default: from config + callback : Callable, optional + example: callback(action:int, callback_data: 'Complicated struct') + callback is executed after each strength setting and after each orbit + reading. + If the callback returns false, then the process is aborted. + plane: str, optional + Plane to perform ("H" or "V", None => both plane) + """ + self._nb_meas = n_avg_meas if n_avg_meas is not None else self._cfg.n_avg_meas + self._sleep_step = sleep_between_step if sleep_between_step is not None else self._cfg.sleep_between_step + self._sleep_meas = sleep_between_meas if sleep_between_meas is not None else self._cfg.sleep_between_meas + + # Device handles + self.check_peer() + self._h_steer = self.peer.get_magnet(self._cfg.hcorr_name) + self._v_steer = self.peer.get_magnet(self._cfg.vcorr_name) + self._quad = self.peer.get_magnet(self._cfg.quad_name) + self._bpms = self.peer.get_bpms(self._cfg.bpm_array_name) + self._bpmi = self._bpms.names().index(self._cfg.bpm_name) + + # Initial values + self._initial_k0 = [self._h_steer.strength.get(), self._v_steer.strength.get()] + self._initial_k1 = self._quad.strength.get() + self._quad_polarity = np.sign(self._initial_k1) + + self._ref_ios = self._quad_response(self._cfg.bpm_array_name, self._cfg.quad_name) + + dk0h = self._cfg.hcorr_delta if plane is None or plane == "H" else 0 + dk0v = self._cfg.vcorr_delta if plane is None or plane == "V" else 0 + dk1 = self._cfg.quad_delta + aborted = False + err = None + + print(f"Initial H corrector value: {self._initial_k0[0]} rad") + print(f"Initial V corrector value: {self._initial_k0[1]} rad") + print(f"Initial quad value: {self._initial_k1} m-1") + + try: + self._register_callback(callback) + self._init_measure() + self.latest_measurement["HData"] = None + self.latest_measurement["VData"] = None + + kx = [] + bpm_posx = [] + allbpm_posx = [] + stdx = [] + ky = [] + bpm_posy = [] + allbpm_posy = [] + stdy = [] + stepsx = [] + stepsy = [] + opt_found = False + self._step = 0 + stx = 0 + sty = 0 + while not opt_found and self._step < 9: + ist = self._step % 3 + _from = f"{stx},{sty}" + + if ist == 0: + stx -= dk0h + sty -= dk0v + _to = f"{stx},{sty}" + print(f"Moving in the negative direction: {_from} -> {_to}") + + if ist == 1: + stx += 2 * dk0h + sty += 2 * dk0v + _to = f"{stx},{sty}" + print(f"Moving in the positive direction: {_from} -> {_to}") + + if ist == 2: + xx = np.linalg.solve(np.array([[stepsx[-2], 1], [stepsx[-1], 1]]), np.array(kx[-2:])) + guessx = -xx[1] / xx[0] + yy = np.linalg.solve(np.array([[stepsy[-2], 1], [stepsy[-1], 1]]), np.array(ky[-2:])) + guessy = -yy[1] / yy[0] + stx = guessx + sty = guessy + if stepsx[-2] <= stx <= stepsx[-1] and stepsy[-2] <= sty <= stepsy[-1]: + opt_found = True + _to = f"{stx},{sty}" + print(f"Moving to the best guess: {_from} -> {_to}") + + step = [stx, sty] + x, dkx, y, dky, sx2, sy2, dataxbpm, dataybpm = self._one_step_dk(step, dk1) + stepsx.append(stx) + kx.append(dkx) + bpm_posx.append(x) + allbpm_posx.append(dataxbpm) + stdx.append(sx2) + + stepsy.append(sty) + ky.append(dky) + bpm_posy.append(y) + allbpm_posy.append(dataybpm) + stdy.append(sy2) + + if np.fabs(dk0h) > 0: + self.send_callback(Action.MEASURE, {"step": self._step, "plane": "H", "bpm_pos": x, "ios": dataxbpm}) + + if np.fabs(dk0v) > 0: + self.send_callback(Action.MEASURE, {"step": self._step, "plane": "V", "bpm_pos": y, "ios": dataybpm}) + + self._step += 1 + + # Final step + + sx = [x for _, x in sorted(zip(kx, stepsx, strict=True))][:3] + sy = [x for _, x in sorted(zip(ky, stepsy, strict=True))][:3] + kxs = np.sort(kx)[:3] + kys = np.sort(ky)[:3] + yy = np.polyfit(sy, kys, 1) + xx = np.polyfit(sx, kxs, 1) + optx = -xx[1] / xx[0] + opty = -yy[1] / yy[0] + stx = optx + sty = opty + _to = f"{stx},{sty}" + print(f"Moving to the optimum: {_to}") + + step = [stx, sty] + x, dkx, y, dky, sx2, sy2, dataxbpm, dataybpm = self._one_step_dk(step, dk1) + stepsx.append(stx) + kx.append(dkx) + bpm_posx.append(x) + allbpm_posx.append(dataxbpm) + stdx.append(sx2) + + stepsy.append(sty) + ky.append(dky) + bpm_posy.append(y) + allbpm_posy.append(dataybpm) + stdy.append(sy2) + + kx, bpm_posx, stdx, allbpm_posx = self._sort_x_vs_y(kx, bpm_posx, stdx, allbpm_posx, stepsx) + ky, bpm_posy, stdy, allbpm_posy = self._sort_x_vs_y(ky, bpm_posy, stdy, allbpm_posy, stepsy) + stepsx = list(np.sort(stepsx)) + stepsy = list(np.sort(stepsy)) + + self.latest_measurement["HData"] = { + "steps": stepsx, + "bpm_pos": bpm_posx, + "allbpm_pos": allbpm_posx, + "k": kx, + "std": stdx, + "offset": x, + "offset_error": np.sqrt(sx2), + } + self.latest_measurement["VData"] = { + "steps": stepsy, + "bpm_pos": bpm_posy, + "allbpm_pos": allbpm_posy, + "k": ky, + "std": stdy, + "offset": y, + "offset_error": np.sqrt(sy2), + } + + except Exception as ex: + err = ex + except KeyboardInterrupt as ex: + aborted = True + finally: + # Restore steerer/quad strength + if dk0h > 0: + self._h_steer.strength.set(self._initial_k0[0]) + if dk0v > 0: + self._v_steer.strength.set(self._initial_k0[1]) + self._quad.strength.set(self._initial_k1) + self.send_callback( + Action.RESTORE, + {}, + raiseException=False, + ) + + if err is not None: + raise (err) + + if aborted: + logger.warning(f"{self.get_name()} : measurement aborted (settings not restored)") + return False + + return True + + def h_offset(self) -> float: + return self.latest_measurement["HData"]["offset"] if self.latest_measurement["HData"] is not None else np.nan + + def h_offset_error(self) -> float: + return self.latest_measurement["HData"]["offset_error"] if self.latest_measurement["HData"] is not None else np.nan + + def v_offset(self) -> float: + return self.latest_measurement["VData"]["offset"] if self.latest_measurement["VData"] is not None else np.nan + + def v_offset_error(self) -> float: + return self.latest_measurement["VData"]["offset_error"] if self.latest_measurement["VData"] is not None else np.nan diff --git a/tests/config/EBSOrbit.yaml b/tests/config/EBSOrbit.yaml index a6e3f654c..3b393641e 100644 --- a/tests/config/EBSOrbit.yaml +++ b/tests/config/EBSOrbit.yaml @@ -35,6 +35,11 @@ arrays: name: BPM elements: - BPM* + - type: pyaml.arrays.magnet + name: QForTune + elements: + - QD2* + - QF1* devices: - type: pyaml.tuning_tools.bba name: BBA-BPM_C04-04 @@ -48,6 +53,16 @@ devices: vcorr_delta : 1e-05 hquad_delta : 0.005 vquad_delta : 0.005 +- type: pyaml.tuning_tools.bba2 + name: BBA2-BPM_C04-04 + bpm_array_name: BPM + bpm_name : BPM_C04-04 + hcorr_name : SF2E-C02-H + vcorr_name : SD1A-C26-V + quad_name : QF6B-C04 + hcorr_delta : 1e-05 + vcorr_delta : 1e-05 + quad_delta : 0.005 - type: pyaml.diagnostics.tune_monitor name: BETATRON_TUNE tune_h: srdiag/beam-tune/main/Qh @@ -58,6 +73,16 @@ devices: rf_plant_name: RF bpm_array_name: BPM n_step: 5 +- type: pyaml.tuning_tools.tune + name: DEFAULT_TUNE_CORRECTION + quad_array_name: QForTune + betatron_tune_name: BETATRON_TUNE + response_matrix: ${path:tune_response.json} +- type: pyaml.tuning_tools.tune_response_matrix + name: DEFAULT_TUNE_RESPONSE_MATRIX + quad_array_name: QForTune + betatron_tune_name: BETATRON_TUNE + quad_delta: 1e-4 - type: pyaml.tuning_tools.chromaticity_response_matrix name: DEFAULT_CHROMATICITY_RESPONSE_MATRIX sextu_array_name: Sext @@ -108,7 +133,7 @@ devices: file: sr/magnet_models/QF6_strength.csv unit: 1/m hardware_unit: A - powerconverter: srmag/vps-qf6/c04-b + powerconverter: srmag/vps-qf6/c04-b/current - type: pyaml.magnet.cfm_magnet name: SH1A-C04 mapping: @@ -5703,6 +5728,8 @@ devices: name: BPM_C04-04 x_pos: srdiag/bpm/c04-04/SA_HPosition y_pos: srdiag/bpm/c04-04/SA_VPosition + x_offset: srdiag/bpm/c04-04/HOffset + y_offset: srdiag/bpm/c04-04/VOffset - type: pyaml.bpm.bpm name: BPM_C04-05 x_pos: srdiag/bpm/c04-05/SA_HPosition @@ -6967,3 +6994,1615 @@ devices: name: BPM_C03-10 x_pos: srdiag/bpm/c03-10/SA_HPosition y_pos: srdiag/bpm/c03-10/SA_VPosition +- type: pyaml.magnet.quadrupole + name: QF1E-C04 + description: "QF1E-C04 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00054 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c04-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C05 + description: "QF1A-C05 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.996841 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c05-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C05 + description: "QF1E-C05 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00191 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c05-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C06 + description: "QF1A-C06 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0003 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c06-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C06 + description: "QF1E-C06 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997615 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c06-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C07 + description: "QF1A-C07 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998152 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c07-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C07 + description: "QF1E-C07 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00262 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c07-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C08 + description: "QF1A-C08 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00319 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c08-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C08 + description: "QF1E-C08 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.996180929 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c08-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C09 + description: "QF1A-C09 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00206 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c09-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C09 + description: "QF1E-C09 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999285 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c09-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C10 + description: "QF1A-C10 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00232 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c10-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C10 + description: "QF1E-C10 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998212 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c10-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C11 + description: "QF1A-C11 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999225 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c11-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C11 + description: "QF1E-C11 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998033 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c11-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C12 + description: "QF1A-C12 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998748 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c12-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C12 + description: "QF1E-C12 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0023 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c12-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C13 + description: "QF1A-C13 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00337 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c13-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C13 + description: "QF1E-C13 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00403 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c13-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C14 + description: "QF1A-C14 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00182 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c14-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C14 + description: "QF1E-C14 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998303 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c14-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C15 + description: "QF1A-C15 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998748 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c15-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C15 + description: "QF1E-C15 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.994098 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c15-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C16 + description: "QF1A-C16 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00232 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c16-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C16 + description: "QF1E-C16 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.99884 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c16-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C17 + description: "QF1A-C17 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0079 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c17-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C17 + description: "QF1E-C17 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999523 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c17-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C18 + description: "QF1A-C18 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c18-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C18 + description: "QF1E-C18 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999854 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c18-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C19 + description: "QF1A-C19 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.99845 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c19-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C19 + description: "QF1E-C19 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c19-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C20 + description: "QF1A-C20 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00095 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c20-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C20 + description: "QF1E-C20 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.996753 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c20-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C21 + description: "QF1A-C21 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00334 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c21-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C21 + description: "QF1E-C21 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997707 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c21-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C22 + description: "QF1A-C22 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00152 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c22-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C22 + description: "QF1E-C22 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998124 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c22-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C23 + description: "QF1A-C23 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.996662 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c23-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C23 + description: "QF1E-C23 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0017 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c23-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C24 + description: "QF1A-C24 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999642 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c24-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C24 + description: "QF1E-C24 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00042 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c24-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C25 + description: "QF1A-C25 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00471 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c25-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C25 + description: "QF1E-C25 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00393 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c25-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C26 + description: "QF1A-C26 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.992906 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c26-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C26 + description: "QF1E-C26 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00268 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c26-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C27 + description: "QF1A-C27 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00248 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c27-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C27 + description: "QF1E-C27 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00203 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c27-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C28 + description: "QF1A-C28 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998030791 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c28-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C28 + description: "QF1E-C28 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00232 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c28-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C29 + description: "QF1A-C29 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.995052 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c29-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C29 + description: "QF1E-C29 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00155 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c29-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C30 + description: "QF1A-C30 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998271 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c30-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C30 + description: "QF1E-C30 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999702 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c30-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C31 + description: "QF1A-C31 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998005 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c31-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C31 + description: "QF1E-C31 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999463 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c31-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C32 + description: "QF1A-C32 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997794 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c32-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C32 + description: "QF1E-C32 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00203 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c32-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C01 + description: "QF1A-C01 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00504 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c01-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C01 + description: "QF1E-C01 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998212 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c01-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C02 + description: "QF1A-C02 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0037 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c02-a/current +- type: pyaml.magnet.quadrupole + name: QF1E-C02 + description: "QF1E-C02 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00093 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c02-e/current +- type: pyaml.magnet.quadrupole + name: QF1A-C03 + description: "QF1A-C03 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0025 + crosstalk: 1.0 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QF1_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qf1/c03-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C04 + description: "QD2E-C04 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999305341 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c04-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C05 + description: "QD2A-C05 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997452918 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c05-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C05 + description: "QD2E-C05 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997993208 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c05-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C06 + description: "QD2A-C06 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.006031322 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c06-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C06 + description: "QD2E-C06 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998547233 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c06-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C07 + description: "QD2A-C07 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.002327856 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c07-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C07 + description: "QD2E-C07 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.000154369 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c07-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C08 + description: "QD2A-C08 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.009580478 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c08-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C08 + description: "QD2E-C08 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.00447669 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c08-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C09 + description: "QD2A-C09 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0015563 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c09-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C09 + description: "QD2E-C09 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997221365 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c09-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C10 + description: "QD2A-C10 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.005954167 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c10-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C10 + description: "QD2E-C10 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.006725723 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c10-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C11 + description: "QD2A-C11 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.994535144 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c11-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C11 + description: "QD2E-C11 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999550256 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c11-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C12 + description: "QD2A-C12 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.001479145 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c12-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C12 + description: "QD2E-C12 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.000321811 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c12-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C13 + description: "QD2A-C13 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.006715036 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c13-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C13 + description: "QD2E-C13 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0036395 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c13-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C14 + description: "QD2A-C14 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997684471 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c14-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C14 + description: "QD2E-C14 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.000385922 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c14-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C15 + description: "QD2A-C15 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999010167 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c15-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C15 + description: "QD2E-C15 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998392922 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c15-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C16 + description: "QD2A-C16 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999318789 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c16-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C16 + description: "QD2E-C16 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.996926967 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c16-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C17 + description: "QD2A-C17 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.000849027 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c17-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C17 + description: "QD2E-C17 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.003392444 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c17-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C18 + description: "QD2A-C18 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.001312133 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c18-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C18 + description: "QD2E-C18 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.005402902 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c18-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C19 + description: "QD2A-C19 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.995846789 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c19-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C19 + description: "QD2E-C19 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.005877011 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c19-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C20 + description: "QD2A-C20 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.001016211 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c20-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C20 + description: "QD2E-C20 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999010167 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c20-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C21 + description: "QD2A-C21 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.007265811 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c21-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C21 + description: "QD2E-C21 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998238611 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c21-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C22 + description: "QD2A-C22 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998161456 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c22-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C22 + description: "QD2E-C22 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.004642522 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c22-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C23 + description: "QD2A-C23 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.001479145 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c23-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C23 + description: "QD2E-C23 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.001942078 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c23-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C24 + description: "QD2A-C24 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999087322 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c24-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C24 + description: "QD2E-C24 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998547233 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c24-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C25 + description: "QD2A-C25 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.001698055 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c25-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C25 + description: "QD2E-C25 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999704567 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c25-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C26 + description: "QD2A-C26 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999164478 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c26-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C26 + description: "QD2E-C26 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.000694659 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c26-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C27 + description: "QD2A-C27 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.99945971 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c27-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C27 + description: "QD2E-C27 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999922816 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c27-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C28 + description: "QD2A-C28 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999922816 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c28-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C28 + description: "QD2E-C28 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.997838839 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c28-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C29 + description: "QD2A-C29 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.003318926 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c29-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C29 + description: "QD2E-C29 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.004179589 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c29-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C30 + description: "QD2A-C30 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.999010167 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c30-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C30 + description: "QD2E-C30 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.9987787 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c30-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C31 + description: "QD2A-C31 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.004411056 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c31-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C31 + description: "QD2E-C31 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0029451 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c31-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C32 + description: "QD2A-C32 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.009426167 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c32-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C32 + description: "QD2E-C32 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.0050283 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c32-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C01 + description: "QD2A-C01 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 0.998533498 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c01-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C01 + description: "QD2E-C01 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.003485189 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c01-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C02 + description: "QD2A-C02 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.001389318 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c02-a/current +- type: pyaml.magnet.quadrupole + name: QD2E-C02 + description: "QD2E-C02 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.006108478 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c02-e/current +- type: pyaml.magnet.quadrupole + name: QD2A-C03 + description: "QD2A-C03 quadrupole" + model: + type: pyaml.magnet.linear_model + calibration_factor: 1.002096389 + crosstalk: 0.99912 + curve: + type: pyaml.magnet.csvcurve + file: sr/magnet_models/QD2_strength.csv + unit: 1/m + hardware_unit: A + powerconverter: srmag/vps-qd2/c03-a/current diff --git a/tests/config/catalogs/ebs_bpm_catalogs.yaml b/tests/config/catalogs/ebs_bpm_catalogs.yaml index 33c16e4a2..ced75f81c 100644 --- a/tests/config/catalogs/ebs_bpm_catalogs.yaml +++ b/tests/config/catalogs/ebs_bpm_catalogs.yaml @@ -46,6 +46,18 @@ type: tango.pyaml.attribute_read_only attribute: srdiag/bpm/c04-04/SA_VPosition unit: m +- type: tango.pyaml.static_catalog_entry + key: srdiag/bpm/c04-04/HOffset + device: + type: tango.pyaml.attribute + attribute: srdiag/bpm/c04-04/HOffset + unit: m +- type: tango.pyaml.static_catalog_entry + key: srdiag/bpm/c04-04/VOffset + device: + type: tango.pyaml.attribute + attribute: srdiag/bpm/c04-04/VOffset + unit: m - type: tango.pyaml.static_catalog_entry key: srdiag/bpm/c04-05/SA_HPosition device: diff --git a/tests/config/catalogs/ebs_magnets_catalogs.yaml b/tests/config/catalogs/ebs_magnets_catalogs.yaml index 73471daa7..79923fd6c 100644 --- a/tests/config/catalogs/ebs_magnets_catalogs.yaml +++ b/tests/config/catalogs/ebs_magnets_catalogs.yaml @@ -867,9 +867,9 @@ range: [10,109] unit: A - type: tango.pyaml.static_catalog_entry - key: srmag/vps-qf6/c04-b + key: srmag/vps-qf6/c04-b/current device: type: tango.pyaml.attribute - attribute: srmag/vps-qf6/c04-b + attribute: srmag/vps-qf6/c04-b/current range: [10,109] unit: A From 576d81d5e2a1d4492ac6f2eea31dc89931051e33 Mon Sep 17 00:00:00 2001 From: PONS Date: Fri, 31 Jul 2026 16:32:28 +0200 Subject: [PATCH 4/5] Added logging --- pyaml/tuning_tools/bba2.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyaml/tuning_tools/bba2.py b/pyaml/tuning_tools/bba2.py index 969da26d8..dd7f330e0 100644 --- a/pyaml/tuning_tools/bba2.py +++ b/pyaml/tuning_tools/bba2.py @@ -244,9 +244,9 @@ def measure( aborted = False err = None - print(f"Initial H corrector value: {self._initial_k0[0]} rad") - print(f"Initial V corrector value: {self._initial_k0[1]} rad") - print(f"Initial quad value: {self._initial_k1} m-1") + logger.debug(f"Initial H corrector value: {self._initial_k0[0]} rad") + logger.debug(f"Initial V corrector value: {self._initial_k0[1]} rad") + logger.debug(f"Initial quad value: {self._initial_k1} m-1") try: self._register_callback(callback) @@ -276,13 +276,13 @@ def measure( stx -= dk0h sty -= dk0v _to = f"{stx},{sty}" - print(f"Moving in the negative direction: {_from} -> {_to}") + logger.debug(f"Moving in the negative direction: {_from} -> {_to}") if ist == 1: stx += 2 * dk0h sty += 2 * dk0v _to = f"{stx},{sty}" - print(f"Moving in the positive direction: {_from} -> {_to}") + logger.debug(f"Moving in the positive direction: {_from} -> {_to}") if ist == 2: xx = np.linalg.solve(np.array([[stepsx[-2], 1], [stepsx[-1], 1]]), np.array(kx[-2:])) @@ -294,7 +294,7 @@ def measure( if stepsx[-2] <= stx <= stepsx[-1] and stepsy[-2] <= sty <= stepsy[-1]: opt_found = True _to = f"{stx},{sty}" - print(f"Moving to the best guess: {_from} -> {_to}") + logger.debug(f"Moving to the best guess: {_from} -> {_to}") step = [stx, sty] x, dkx, y, dky, sx2, sy2, dataxbpm, dataybpm = self._one_step_dk(step, dk1) From af6501f1f3da39e866f67f1740adf5d21671effb Mon Sep 17 00:00:00 2001 From: PONS Date: Tue, 4 Aug 2026 09:07:40 +0200 Subject: [PATCH 5/5] Fix for single plane, tune correction, little code cleaning --- pyaml/tuning_tools/bba2.py | 215 ++++++++++++++++++++----------------- tests/config/EBSOrbit.yaml | 1 + 2 files changed, 118 insertions(+), 98 deletions(-) diff --git a/pyaml/tuning_tools/bba2.py b/pyaml/tuning_tools/bba2.py index dd7f330e0..b90332996 100644 --- a/pyaml/tuning_tools/bba2.py +++ b/pyaml/tuning_tools/bba2.py @@ -31,6 +31,8 @@ class ConfigModel(MeasurementToolConfigModel): Vertical corrector used to make a deviation in the quad quad_name : str Quadrupole used to find the center + tune_correction_name: str + Tune tuning tool hcorr_delta : float Horizontal corrector delta strength vcorr_delta : float @@ -46,24 +48,60 @@ class ConfigModel(MeasurementToolConfigModel): hcorr_name: str vcorr_name: str quad_name: str + tune_correction_name: str hcorr_delta: float vcorr_delta: float quad_delta: float +class BBAData: + def __init__(self): + self.k = [] # Quadrupole kick + self.bpm_pos = [] # BPM#i position + self.allbpm_pos = [] # IOS (Induced Orbit Shift) + self.rms = [] # RMS + self.steps = [] # steerer step + + self.offset = np.nan + self.error = np.nan + + def append(self, st, dk, bpm, allbpm, rms): + # Append new dataset + self.steps.append(st) + self.k.append(dk) + self.bpm_pos.append(bpm) + self.allbpm_pos.append(allbpm) + self.rms.append(rms) + + def sort(self): + # Sort data according to step + indices = range(len(self.steps)) + sindices = [a for _, a in sorted(zip(self.steps, indices, strict=True))] + self.steps = [self.steps[i] for i in sindices] + self.k = [self.k[i] for i in sindices] + self.bpm_pos = [self.bpm_pos[i] for i in sindices] + self.allbpm_pos = [self.allbpm_pos[i] for i in sindices] + self.rms = [self.rms[i] for i in sindices] + + def update_offset(self): + # Update offset value + self.offset = self.bpm_pos[-1] + self.error = np.sqrt(self.rms[-1]) + + class BBA2(MeasurementTool): def __init__(self, cfg: ConfigModel): super().__init__(cfg.name) self._cfg = cfg - def _sort_x_vs_y(self, x1, x2, x3, x4, y): - x1s = [a for _, a in sorted(zip(y, x1, strict=True))] - x2s = [a for _, a in sorted(zip(y, x2, strict=True))] - x3s = [a for _, a in sorted(zip(y, x3, strict=True))] - x4s = [a for _, a in sorted(zip(y, x4, strict=True))] - return x1s, x2s, x3s, x4s + @staticmethod + def _intersect_yzero(x, k, n): + # Linear fit on last n points + xx = np.polynomial.polynomial.polyfit(x[-n:], k[-n:], 1) + # y=0 intesection + return -xx[0] / xx[1] - def _quad_response(self, bpmname: str, quadname: str, dk0=1e-5): + def _quad_response(self, tunename: str, bpmname: str, quadname: str, dk0=1e-5): # Return normalized response of a dipolar kick in a quad from the model # Retrieve model handle @@ -72,6 +110,16 @@ def _quad_response(self, bpmname: str, quadname: str, dk0=1e-5): # handles quad = design.get_magnet(quadname) orbit = design.get_bpms(bpmname).positions + tune_design = design.get_tune_tuning(tunename) + tune_live = self._peer.get_tune_tuning(tunename) + + # Get tune from live and adjust the model to improve quad response phase + tune0 = tune_design.readback() + tune = tune_live.readback() + logger.debug(f"Live tune: {tune}") + logger.debug(f"Model tune: {tune0}") + tune_design.set(tune) + a0 = quad.strength.get("PolynomA", 0) b0 = quad.strength.get("PolynomB", 0) step = [-dk0, 0, dk0] @@ -97,11 +145,16 @@ def _quad_response(self, bpmname: str, quadname: str, dk0=1e-5): fit = np.polynomial.polynomial.polyfit(step, orby, 1) ref_ios_y = fit[1] + # Restore tune + tune_design.set(tune0) + ref = np.array([ref_ios_x, ref_ios_y]).T return ref def _fit_kick(self, meas, ref, mask): + # Correlate measured ios and theoretical one + xy = np.multiply(meas[mask], ref[mask]) xx = np.multiply(ref[mask], ref[mask]) if sum(xx) == 0: @@ -110,6 +163,8 @@ def _fit_kick(self, meas, ref, mask): return sum(xy) / sum(xx) def _get_averaged_orbit(self): + # Get averaged orbit + avgorb = np.zeros((len(self._bpms), 2)) for avg in range(self._nb_meas): o = self._bpms.positions.get() @@ -121,6 +176,8 @@ def _get_averaged_orbit(self): return avgorb def _one_step_dk(self, dk0, dk1): + # Measrue IOS + if any(abs(dk) > 200e-6 for dk in dk0): raise PyAMLException("Requested dk too high (>200urad), consider using bump") @@ -155,7 +212,7 @@ def _one_step_dk(self, dk0, dk1): time.sleep(self._sleep_step) orbm = self._get_averaged_orbit() # take slopes and compute corresponding delta orbit - stepx = [-self._dk1 * self._quad_polarity, 0, self._dk1 * self._quad_polarity] + stepx = [-dk1 * self._quad_polarity, 0, dk1 * self._quad_polarity] orbx = [orbm[:, 0], orb0[:, 0], orbp[:, 0]] fit = np.polynomial.polynomial.polyfit(stepx, orbx, 1) ios_x = fit[1] * dk1 @@ -236,11 +293,13 @@ def measure( self._initial_k1 = self._quad.strength.get() self._quad_polarity = np.sign(self._initial_k1) - self._ref_ios = self._quad_response(self._cfg.bpm_array_name, self._cfg.quad_name) + self._ref_ios = self._quad_response(self._cfg.tune_correction_name, self._cfg.bpm_array_name, self._cfg.quad_name) dk0h = self._cfg.hcorr_delta if plane is None or plane == "H" else 0 dk0v = self._cfg.vcorr_delta if plane is None or plane == "V" else 0 dk1 = self._cfg.quad_delta + doH = dk0h != 0 + doV = dk0v != 0 aborted = False err = None @@ -253,17 +312,9 @@ def measure( self._init_measure() self.latest_measurement["HData"] = None self.latest_measurement["VData"] = None + X = BBAData() + Y = BBAData() - kx = [] - bpm_posx = [] - allbpm_posx = [] - stdx = [] - ky = [] - bpm_posy = [] - allbpm_posy = [] - stdy = [] - stepsx = [] - stepsy = [] opt_found = False self._step = 0 stx = 0 @@ -285,91 +336,59 @@ def measure( logger.debug(f"Moving in the positive direction: {_from} -> {_to}") if ist == 2: - xx = np.linalg.solve(np.array([[stepsx[-2], 1], [stepsx[-1], 1]]), np.array(kx[-2:])) - guessx = -xx[1] / xx[0] - yy = np.linalg.solve(np.array([[stepsy[-2], 1], [stepsy[-1], 1]]), np.array(ky[-2:])) - guessy = -yy[1] / yy[0] - stx = guessx - sty = guessy - if stepsx[-2] <= stx <= stepsx[-1] and stepsy[-2] <= sty <= stepsy[-1]: - opt_found = True + H_found = False + V_found = False + + if doH: + stx = BBA2._intersect_yzero(X.steps, X.k, 2) + H_found = X.steps[-2] <= stx <= X.steps[-1] # don't rely on extrapolation + if doV: + sty = BBA2._intersect_yzero(Y.steps, Y.k, 2) + V_found = Y.steps[-2] <= sty <= Y.steps[-1] # don't rely on extrapolation + + opt_found = ( + doH and not doV and H_found or not doH and doV and V_found or doH and doV and H_found and V_found + ) + _to = f"{stx},{sty}" logger.debug(f"Moving to the best guess: {_from} -> {_to}") - step = [stx, sty] - x, dkx, y, dky, sx2, sy2, dataxbpm, dataybpm = self._one_step_dk(step, dk1) - stepsx.append(stx) - kx.append(dkx) - bpm_posx.append(x) - allbpm_posx.append(dataxbpm) - stdx.append(sx2) - - stepsy.append(sty) - ky.append(dky) - bpm_posy.append(y) - allbpm_posy.append(dataybpm) - stdy.append(sy2) - - if np.fabs(dk0h) > 0: + x, dkx, y, dky, sx2, sy2, dataxbpm, dataybpm = self._one_step_dk([stx, sty], dk1) + X.append(stx, dkx, x, dataxbpm, sx2) + Y.append(sty, dky, y, dataybpm, sy2) + + if doH: self.send_callback(Action.MEASURE, {"step": self._step, "plane": "H", "bpm_pos": x, "ios": dataxbpm}) - if np.fabs(dk0v) > 0: + if doV: self.send_callback(Action.MEASURE, {"step": self._step, "plane": "V", "bpm_pos": y, "ios": dataybpm}) self._step += 1 # Final step - sx = [x for _, x in sorted(zip(kx, stepsx, strict=True))][:3] - sy = [x for _, x in sorted(zip(ky, stepsy, strict=True))][:3] - kxs = np.sort(kx)[:3] - kys = np.sort(ky)[:3] - yy = np.polyfit(sy, kys, 1) - xx = np.polyfit(sx, kxs, 1) - optx = -xx[1] / xx[0] - opty = -yy[1] / yy[0] - stx = optx - sty = opty + if doH: + stx = BBA2._intersect_yzero(X.steps, X.k, 3) + + if doV: + sty = BBA2._intersect_yzero(Y.steps, Y.k, 3) + _to = f"{stx},{sty}" - print(f"Moving to the optimum: {_to}") - - step = [stx, sty] - x, dkx, y, dky, sx2, sy2, dataxbpm, dataybpm = self._one_step_dk(step, dk1) - stepsx.append(stx) - kx.append(dkx) - bpm_posx.append(x) - allbpm_posx.append(dataxbpm) - stdx.append(sx2) - - stepsy.append(sty) - ky.append(dky) - bpm_posy.append(y) - allbpm_posy.append(dataybpm) - stdy.append(sy2) - - kx, bpm_posx, stdx, allbpm_posx = self._sort_x_vs_y(kx, bpm_posx, stdx, allbpm_posx, stepsx) - ky, bpm_posy, stdy, allbpm_posy = self._sort_x_vs_y(ky, bpm_posy, stdy, allbpm_posy, stepsy) - stepsx = list(np.sort(stepsx)) - stepsy = list(np.sort(stepsy)) - - self.latest_measurement["HData"] = { - "steps": stepsx, - "bpm_pos": bpm_posx, - "allbpm_pos": allbpm_posx, - "k": kx, - "std": stdx, - "offset": x, - "offset_error": np.sqrt(sx2), - } - self.latest_measurement["VData"] = { - "steps": stepsy, - "bpm_pos": bpm_posy, - "allbpm_pos": allbpm_posy, - "k": ky, - "std": stdy, - "offset": y, - "offset_error": np.sqrt(sy2), - } + logger.debug(f"Moving to the optimum: {_to}") + + x, dkx, y, dky, sx2, sy2, dataxbpm, dataybpm = self._one_step_dk([stx, sty], dk1) + X.append(stx, dkx, x, dataxbpm, sx2) + Y.append(sty, dky, y, dataybpm, sy2) + + if doH: + X.update_offset() + X.sort() + self.latest_measurement["HData"] = X + + if doV: + Y.update_offset() + Y.sort() + self.latest_measurement["VData"] = Y except Exception as ex: err = ex @@ -377,9 +396,9 @@ def measure( aborted = True finally: # Restore steerer/quad strength - if dk0h > 0: + if doH: self._h_steer.strength.set(self._initial_k0[0]) - if dk0v > 0: + if doV: self._v_steer.strength.set(self._initial_k0[1]) self._quad.strength.set(self._initial_k1) self.send_callback( @@ -398,13 +417,13 @@ def measure( return True def h_offset(self) -> float: - return self.latest_measurement["HData"]["offset"] if self.latest_measurement["HData"] is not None else np.nan + return self.latest_measurement["HData"].offset if self.latest_measurement["HData"] is not None else np.nan def h_offset_error(self) -> float: - return self.latest_measurement["HData"]["offset_error"] if self.latest_measurement["HData"] is not None else np.nan + return self.latest_measurement["HData"].error if self.latest_measurement["HData"] is not None else np.nan def v_offset(self) -> float: - return self.latest_measurement["VData"]["offset"] if self.latest_measurement["VData"] is not None else np.nan + return self.latest_measurement["VData"].offset if self.latest_measurement["VData"] is not None else np.nan def v_offset_error(self) -> float: - return self.latest_measurement["VData"]["offset_error"] if self.latest_measurement["VData"] is not None else np.nan + return self.latest_measurement["VData"].error if self.latest_measurement["VData"] is not None else np.nan diff --git a/tests/config/EBSOrbit.yaml b/tests/config/EBSOrbit.yaml index 3b393641e..2032180e9 100644 --- a/tests/config/EBSOrbit.yaml +++ b/tests/config/EBSOrbit.yaml @@ -60,6 +60,7 @@ devices: hcorr_name : SF2E-C02-H vcorr_name : SD1A-C26-V quad_name : QF6B-C04 + tune_correction_name: DEFAULT_TUNE_CORRECTION hcorr_delta : 1e-05 vcorr_delta : 1e-05 quad_delta : 0.005