From a048677abe018f026837b4b6a81b4a547d1e58a9 Mon Sep 17 00:00:00 2001 From: PONS Date: Thu, 12 Mar 2026 08:33:18 +0100 Subject: [PATCH] Unifying ResponseMatrix, hide pySC from pyAML API --- pyaml/tuning_tools/orbit.py | 61 +++++++++++++-------------- pyaml/tuning_tools/response_matrix.py | 30 ++++++++++++- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/pyaml/tuning_tools/orbit.py b/pyaml/tuning_tools/orbit.py index f75f9523..207a32e6 100644 --- a/pyaml/tuning_tools/orbit.py +++ b/pyaml/tuning_tools/orbit.py @@ -18,8 +18,6 @@ from ..arrays.magnet_array import MagnetArray from ..common.element import Element, ElementConfigModel from ..common.exception import PyAMLException -from ..configuration.factory import Factory -from ..configuration.fileloader import load from ..external.pySC_interface import pySCInterface from ..rf.rf_plant import RFPlant from .response_matrix import ResponseMatrix @@ -51,6 +49,7 @@ def __init__(self, cfg: ConfigModel): self.bpm_array_name = cfg.bpm_array_name self.hcorr_array_name = cfg.hcorr_array_name self.vcorr_array_name = cfg.vcorr_array_name + self._pySC_response_matrix = None self.virtual_target = cfg.virtual_target @@ -71,16 +70,17 @@ def __init__(self, cfg: ConfigModel): self.singular_values_H = cfg.singular_values self.singular_values_V = cfg.singular_values + # If the configuration response matrix is a filename, load it if type(cfg.response_matrix) is str: - response_matrix_filename = cfg.response_matrix - # assigns self.response_matrix - if Path(response_matrix_filename).exists(): - self.load_response_matrix(response_matrix_filename) - else: - logger.warning(f"{response_matrix_filename} does not exist.") - self.response_matrix = None - else: - self.response_matrix = pySC_ResponseMatrix.model_validate( + try: + cfg.response_matrix = ResponseMatrix.load(cfg.response_matrix) + except Exception as e: + logger.warning(f"{str(e)}") + cfg.response_matrix = None + + # assigns self._pySC_response_matrix + if cfg.response_matrix: + self._pySC_response_matrix = pySC_ResponseMatrix.model_validate( cfg.response_matrix._cfg.model_dump() ) @@ -89,6 +89,10 @@ def __init__(self, cfg: ConfigModel): self._hvcorr: MagnetArray = None self._rf_plant: RFPlant = None + @property + def reponse_matrix(self) -> ResponseMatrix | None: + return self._cfg.response_matrix + def correct( self, plane: Optional[Literal["H", "V"]] = None, @@ -137,7 +141,7 @@ def correct( None or if plane = 'H'. """ - if self.response_matrix is None: + if self._pySC_response_matrix is None: raise PyAMLException(f"{self.get_name()} does not have a response_matrix.") interface = pySCInterface( @@ -161,7 +165,7 @@ def correct( if plane is None or plane == "H": trims_h = orbit_correction( interface=interface, - response_matrix=self.response_matrix, + response_matrix=self._pySC_response_matrix, method="svd_values", parameter=svH, virtual=True, @@ -175,7 +179,7 @@ def correct( if plane is None or plane == "V": trims_v = orbit_correction( interface=interface, - response_matrix=self.response_matrix, + response_matrix=self._pySC_response_matrix, method="svd_values", parameter=svV, virtual=False, @@ -241,15 +245,15 @@ def correct( def set_weight( self, name: str, weight: float, plane: Optional[Literal["H", "V"]] = None ) -> None: - self.response_matrix.set_weight(name, weight, plane=plane) + self._pySC_response_matrix.set_weight(name, weight, plane=plane) return def set_virtual_weight(self, weight: float) -> None: - self.response_matrix.virtual_weight = weight + self._pySC_response_matrix.virtual_weight = weight return def set_rf_weight(self, weight: float) -> None: - self.response_matrix.rf_weight = weight + self._pySC_response_matrix.rf_weight = weight return def get_weight(self, name: str, plane: Optional[Literal["H", "V"]] = None) -> float: @@ -257,9 +261,9 @@ def get_weight(self, name: str, plane: Optional[Literal["H", "V"]] = None) -> fl planes = [] weights = [] - inames = self.response_matrix.input_names - iplanes = self.response_matrix.input_planes - iweights = self.response_matrix.input_weights + inames = self._pySC_response_matrix.input_names + iplanes = self._pySC_response_matrix.input_planes + iweights = self._pySC_response_matrix.input_weights for iname, iplane, iw in zip(inames, iplanes, iweights, strict=True): if name == iname: if plane is None or plane == iplane: @@ -267,9 +271,9 @@ def get_weight(self, name: str, plane: Optional[Literal["H", "V"]] = None) -> fl planes.append(iplane) weights.append(iw) - onames = self.response_matrix.output_names - oplanes = self.response_matrix.output_planes - oweights = self.response_matrix.output_weights + onames = self._pySC_response_matrix.output_names + oplanes = self._pySC_response_matrix.output_planes + oweights = self._pySC_response_matrix.output_weights for oname, oplane, ow in zip(onames, oplanes, oweights, strict=True): if name == oname: if plane is None or plane == oplane: @@ -286,10 +290,10 @@ def get_weight(self, name: str, plane: Optional[Literal["H", "V"]] = None) -> fl ) def get_virtual_weight(self) -> float: - return self.response_matrix.virtual_weight + return self._pySC_response_matrix.virtual_weight def get_rf_weight(self) -> float: - return self.response_matrix.rf_weight + return self._pySC_response_matrix.rf_weight def post_init(self): self._hcorr = self._peer.get_magnets(self._cfg.hcorr_array_name) @@ -309,10 +313,3 @@ def attach(self, peer: "ElementHolder") -> Self: obj = self.__class__(self._cfg) obj._peer = peer return obj - - def load_response_matrix(self, filename: str) -> None: - path = Path(filename) - config_dict = load(str(path.resolve())) - rm = Factory.depth_first_build(config_dict, ignore_external=False) - self.response_matrix = pySC_ResponseMatrix.model_validate(rm._cfg.model_dump()) - return None diff --git a/pyaml/tuning_tools/response_matrix.py b/pyaml/tuning_tools/response_matrix.py index bfd485da..02da0bac 100644 --- a/pyaml/tuning_tools/response_matrix.py +++ b/pyaml/tuning_tools/response_matrix.py @@ -1,7 +1,14 @@ +from pathlib import Path from typing import Optional from pydantic import BaseModel, ConfigDict +from pyaml.common.element import __pyaml_repr__ +from pyaml.configuration.factory import Factory + +from .. import PyAMLException +from ..configuration.fileloader import load + PYAMLCLASS = "ResponseMatrix" @@ -14,9 +21,9 @@ class ConfigModel(BaseModel): matrix : list[list[float]] Response matrix data input_names : list[str], optional - Input names + Input names, basically the actuators output_names : list[str] - Output names + Output names, basically the measurements rf_response : list[float], optional RF response data """ @@ -32,5 +39,24 @@ class ConfigModel(BaseModel): class ResponseMatrix(object): + """ + Generic response matrix loader + """ + def __init__(self, cfg: ConfigModel): self._cfg = cfg + + @staticmethod + def load(filename: str) -> None: + """ + Load a reponse matrix from a configuration file + """ + path = Path(filename) + if path.exists(): + config_dict = load(str(path.resolve())) + return Factory.depth_first_build(config_dict, ignore_external=False) + else: + raise PyAMLException(f"{filename}: file not found") + + def __repr__(self): + return __pyaml_repr__(self)