From df62638a06afb6747d45be30bd9ac385503e5a73 Mon Sep 17 00:00:00 2001 From: PONS Date: Tue, 4 Nov 2025 15:34:10 +0100 Subject: [PATCH 1/8] __repr__() for magnets --- pyaml/bpm/bpm.py | 2 +- pyaml/common/element.py | 54 ++++++++++++++++++++++ pyaml/common/element_holder.py | 5 +- pyaml/configuration/factory.py | 3 +- pyaml/control/controlsystem.py | 6 +-- pyaml/instrument.py | 2 +- pyaml/lattice/attribute_linker.py | 2 +- pyaml/lattice/element.py | 33 ------------- pyaml/lattice/lattice_elements_linker.py | 2 +- pyaml/lattice/simulator.py | 8 ++-- pyaml/magnet/cfm_magnet.py | 23 ++++++--- pyaml/magnet/magnet.py | 59 +++++++++++++++++++++--- pyaml/rf/rf_plant.py | 2 +- pyaml/rf/rf_transmitter.py | 2 +- tests/test_load_quad.py | 12 +++-- 15 files changed, 150 insertions(+), 65 deletions(-) create mode 100644 pyaml/common/element.py delete mode 100644 pyaml/lattice/element.py diff --git a/pyaml/bpm/bpm.py b/pyaml/bpm/bpm.py index e2fe5f60..76e81cf1 100644 --- a/pyaml/bpm/bpm.py +++ b/pyaml/bpm/bpm.py @@ -1,4 +1,4 @@ -from ..lattice.element import Element, ElementConfigModel +from ..common.element import Element, ElementConfigModel from ..lattice.abstract_impl import RBpmArray, RWBpmOffsetArray, RWBpmTiltScalar from ..bpm.bpm_model import BPMModel from ..common.exception import PyAMLException diff --git a/pyaml/common/element.py b/pyaml/common/element.py new file mode 100644 index 00000000..0d8c8e76 --- /dev/null +++ b/pyaml/common/element.py @@ -0,0 +1,54 @@ +from .exception import PyAMLException + +from pydantic import BaseModel,ConfigDict + +class ElementConfigModel(BaseModel): + + model_config = ConfigDict(arbitrary_types_allowed=True,extra="forbid") + + name : str + """Element name""" + +class Element(object): + """ + Class providing access to one element of a physical or simulated lattice + + Attributes: + name: str + The unique name identifying the element in the configuration file + """ + def __init__(self,name:str): + self.__name: str = name + self._peer = None # Peer: ControlSystem, Simulator + + def get_name(self): + """ + Returns the name of the element + """ + return self.__name + + def set_energy(self,E:float): + """ + Set the instrument energy on this element + """ + pass + + def check_peer(self): + """ + Throws an exception if the element is not attacched to a simulator or to a control system + """ + if self._peer is None: + raise PyAMLException(f"{str(self)} is not attached to a control system or the a simulator") + + def get_peer(self) -> str: + """ + Returns a string representation of peer simulator or control system + """ + return "None" if self._peer is None else f"{self._peer.__class__.__name__}:{self._peer.name()}" + + def __repr__(self): + return "%s(name='%s', peer='%s')" % ( + self.__class__.__name__, + self.__name, + self.get_peer() + ) diff --git a/pyaml/common/element_holder.py b/pyaml/common/element_holder.py index 01a732b4..480803ca 100644 --- a/pyaml/common/element_holder.py +++ b/pyaml/common/element_holder.py @@ -1,7 +1,7 @@ """ Module handling element references for simulators and control system """ -from ..lattice.element import Element +from .element import Element from ..magnet.magnet import Magnet from ..rf.rf_plant import RFPlant from ..rf.rf_transmitter import RFTransmitter @@ -52,6 +52,9 @@ def get_magnet(self,name:str) -> Magnet: return self.__MAGNETS[name] def add_magnet(self,name:str,m:Magnet): + if name in self.__MAGNETS: + print(self.__MAGNETS) + raise PyAMLException(f"Duplicate magnet name {name}") from None self.__MAGNETS[name] = m def get_magnets(self,name:str) -> MagnetArray: diff --git a/pyaml/configuration/factory.py b/pyaml/configuration/factory.py index 8c9c1c06..7c821147 100644 --- a/pyaml/configuration/factory.py +++ b/pyaml/configuration/factory.py @@ -3,7 +3,7 @@ from threading import Lock from ..common.exception import PyAMLConfigException -from ..lattice.element import Element +from ..common.element import Element from pydantic import ValidationError class BuildStrategy: @@ -154,6 +154,7 @@ def register_element(self, elt): if isinstance(elt,Element): name = elt.get_name() if name in self._elements: + print(self._elements) raise PyAMLConfigException(f"element {name} already defined") self._elements[name] = elt diff --git a/pyaml/control/controlsystem.py b/pyaml/control/controlsystem.py index 7d02fce2..3e19863e 100644 --- a/pyaml/control/controlsystem.py +++ b/pyaml/control/controlsystem.py @@ -1,7 +1,7 @@ from abc import ABCMeta, abstractmethod from ..common.element_holder import ElementHolder from ..common.abstract import RWMapper -from ..lattice.element import Element +from ..common.element import Element from ..control.abstract_impl import RWHardwareScalar,RWHardwareArray,RWStrengthScalar,RWStrengthArray from ..bpm.bpm import BPM from ..control.abstract_impl import RWBpmTiltScalar,RWBpmOffsetArray, RBpmArray @@ -101,7 +101,7 @@ def fill_device(self,elements:list[Element]): current = RWHardwareScalar(e.model) if e.model.has_hardware() else None strength = RWStrengthScalar(e.model) if e.model.has_physics() else None # Create a unique ref for this control system - m = e.attach(strength, current) + m = e.attach(self,strength, current) self.add_magnet(m.get_name(),m) elif isinstance(e,CombinedFunctionMagnet): @@ -109,7 +109,7 @@ def fill_device(self,elements:list[Element]): currents = RWHardwareArray(e.model) if e.model.has_hardware() else None strengths = RWStrengthArray(e.model) if e.model.has_physics() else None # Create unique refs of each function for this control system - ms = e.attach(strengths,currents) + ms = e.attach(self,strengths,currents) for m in ms: self.add_magnet(m.get_name(),m) elif isinstance(e,BPM): diff --git a/pyaml/instrument.py b/pyaml/instrument.py index 3ee284ab..db04e678 100644 --- a/pyaml/instrument.py +++ b/pyaml/instrument.py @@ -3,7 +3,7 @@ """ from .control.controlsystem import ControlSystem -from .lattice.element import Element +from .common.element import Element from .lattice.simulator import Simulator from .arrays.array import ArrayConfig from pydantic import BaseModel,ConfigDict diff --git a/pyaml/lattice/attribute_linker.py b/pyaml/lattice/attribute_linker.py index d4efb7df..f0dd97ef 100644 --- a/pyaml/lattice/attribute_linker.py +++ b/pyaml/lattice/attribute_linker.py @@ -1,7 +1,7 @@ import at from pydantic import ConfigDict -from pyaml.lattice.element import Element +from pyaml.common.element import Element from pyaml.lattice.lattice_elements_linker import LinkerIdentifier, LinkerConfigModel, LatticeElementsLinker PYAMLCLASS = "PyAtAttributeElementsLinker" diff --git a/pyaml/lattice/element.py b/pyaml/lattice/element.py deleted file mode 100644 index 145694eb..00000000 --- a/pyaml/lattice/element.py +++ /dev/null @@ -1,33 +0,0 @@ -from pydantic import BaseModel,ConfigDict - -class ElementConfigModel(BaseModel): - - model_config = ConfigDict(arbitrary_types_allowed=True,extra="forbid") - - name : str - """Element name""" - -class Element(object): - """ - Class providing access to one element of a physical or simulated lattice - - Attributes: - name (str): The name identifying the element in the configuration file - """ - def __init__(self,name:str): - self.__name: str = name - - def get_name(self): - """ - Returns the name of the element - """ - return self.__name - - def set_energy(self,E:float): - pass - - def __repr__(self): - return "%s(%s)" % ( - self.__class__.__name__, - self.__name - ) diff --git a/pyaml/lattice/lattice_elements_linker.py b/pyaml/lattice/lattice_elements_linker.py index 103000af..297365f0 100644 --- a/pyaml/lattice/lattice_elements_linker.py +++ b/pyaml/lattice/lattice_elements_linker.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, ConfigDict from pyaml import PyAMLException -from pyaml.lattice.element import Element +from pyaml.common.element import Element class LinkerConfigModel(BaseModel): diff --git a/pyaml/lattice/simulator.py b/pyaml/lattice/simulator.py index 4cf1ed75..3088650d 100644 --- a/pyaml/lattice/simulator.py +++ b/pyaml/lattice/simulator.py @@ -1,7 +1,7 @@ from .attribute_linker import PyAtAttributeElementsLinker, ConfigModel as PyAtAttrLinkerConfigModel from .lattice_elements_linker import LatticeElementsLinker from ..configuration import get_root_folder -from .element import Element +from ..common.element import Element from pathlib import Path from ..magnet.magnet import Magnet from pyaml.bpm.bpm import BPM @@ -92,7 +92,7 @@ def fill_device(self,elements:list[Element]): current = RWHardwareScalar(self.get_at_elems(e),e.polynom,e.model) if e.model.has_physics() else None strength = RWStrengthScalar(self.get_at_elems(e),e.polynom,e.model) if e.model.has_physics() else None # Create a unique ref for this simulator - m = e.attach(strength,current) + m = e.attach(self,strength,current) self.add_magnet(m.get_name(),m) elif isinstance(e,CombinedFunctionMagnet): @@ -100,10 +100,10 @@ def fill_device(self,elements:list[Element]): currents = RWHardwareArray(self.get_at_elems(e),e.polynoms,e.model) if e.model.has_physics() else None strengths = RWStrengthArray(self.get_at_elems(e),e.polynoms,e.model) if e.model.has_physics() else None # Create unique refs of each function for this simulator - ms = e.attach(strengths,currents) + ms = e.attach(self,strengths,currents) for m in ms: self.add_magnet(m.get_name(), m) - self.add_magnet(m.get_name(), m) + elif isinstance(e,BPM): # This assumes unique BPM names in the pyAT lattice tilt = RWBpmTiltScalar(self.get_at_elems(e)[0]) diff --git a/pyaml/magnet/cfm_magnet.py b/pyaml/magnet/cfm_magnet.py index 91a5ea2b..085f49f0 100644 --- a/pyaml/magnet/cfm_magnet.py +++ b/pyaml/magnet/cfm_magnet.py @@ -1,6 +1,6 @@ from .model import MagnetModel -from ..lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from ..common import abstract from ..common.abstract import RWMapper from ..common.exception import PyAMLException @@ -13,8 +13,8 @@ from .octupole import Octupole from .skewoctu import SkewOctu from .magnet import Magnet,MagnetConfigModel +from ..configuration import Factory -from pydantic import SerializeAsAny from scipy.constants import speed_of_light _fmap:dict = { @@ -45,13 +45,14 @@ def __init__(self, cfg: ConfigModel): super().__init__(cfg.name) self._cfg = cfg self.model = cfg.model + self.virtuals:list[Magnet] = [] if self.model is not None and not hasattr(self.model._cfg,"multipoles"): raise PyAMLException(f"{cfg.name} model: mutipoles field required for combined function magnet") idx = 0 self.polynoms = [] - for m in cfg.mapping: + for idx,m in enumerate(cfg.mapping): # Check mapping validity if len(m)!=2: raise PyAMLException("Invalid CombinedFunctionMagnet mapping for {m}") @@ -60,17 +61,25 @@ def __init__(self, cfg: ConfigModel): if m[0] not in self.model._cfg.multipoles: raise PyAMLException(m[0] + " not found in underlying magnet model") self.polynoms.append(_fmap[m[0]].polynom) + # Create the virtual magnet for the correspoding multipole + vm = self.create_virutal_manget(m[1],m[0]) + self.virtuals.append(vm) + # Register the virtual element in the factory to have a coherent factory and improve error reporting + Factory.register_element(vm) - def attach(self, strengths: abstract.ReadWriteFloatArray, hardwares: abstract.ReadWriteFloatArray) -> list[Magnet]: + def create_virutal_manget(self,name:str,idx:int) -> Magnet: + args = {"name":name,"model":self.model} + mVirtual:Magnet = _fmap[idx](MagnetConfigModel(**args)) + mVirtual.set_model_name(self.get_name()) + return mVirtual + def attach(self, peer, strengths: abstract.ReadWriteFloatArray, hardwares: abstract.ReadWriteFloatArray) -> list[Magnet]: # Construct a single function magnet for each multipole of this combined function magnet l = [] for idx,m in enumerate(self._cfg.mapping): - args = {"name":m[1],"model":self.model} - mclass:Magnet = _fmap[m[0]](MagnetConfigModel(**args)) strength = RWMapper(strengths,idx) hardware = RWMapper(hardwares,idx) if self.model.has_hardware() else None - l.append(mclass.attach(strength,hardware)) + l.append(self.virtuals[idx].attach(peer,strength,hardware)) return l def set_energy(self,E:float): diff --git a/pyaml/magnet/magnet.py b/pyaml/magnet/magnet.py index 92aa3e65..c31870c8 100644 --- a/pyaml/magnet/magnet.py +++ b/pyaml/magnet/magnet.py @@ -1,7 +1,8 @@ -from pyaml.lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from .. import PyAMLException from ..common import abstract from .model import MagnetModel + from scipy.constants import speed_of_light try: from typing import Self # Python 3.11+ @@ -32,32 +33,76 @@ def __init__(self, name:str, model:MagnetModel = None): """ super().__init__(name) self.__model = model - self.__strength = None - self.__hardware = None + self.__strength:abstract.ReadWriteFloatScalar = None + self.__hardware:abstract.ReadWriteFloatScalar = None + self.__modelName = self.get_name() @property def strength(self) -> abstract.ReadWriteFloatScalar: + """ + Gives access to the strength of this magnet in physics unit + """ + self.check_peer() if self.__strength is None: - raise PyAMLException(f"{str(self)} is unattached or has no model that supports physics units") + raise PyAMLException(f"{str(self)} has no model that supports physics units") return self.__strength @property def hardware(self) -> abstract.ReadWriteFloatScalar: + """ + Gives access to the strength of this magnet in hardware unit when possible + """ + self.check_peer() if self.__hardware is None: - raise PyAMLException(f"{str(self)} is unattached or has no model that supports hardware units") + raise PyAMLException(f"{str(self)} has no model that supports hardware units") return self.__hardware @property def model(self) -> MagnetModel: + """ + Returns a handle to the underlying magnet model + """ return self.__model - def attach(self, strength: abstract.ReadWriteFloatScalar, hardware: abstract.ReadWriteFloatScalar) -> Self: - # Attach strength and current attribute and returns a new reference + def attach(self, peer, strength: abstract.ReadWriteFloatScalar, hardware: abstract.ReadWriteFloatScalar) -> Self: + """ + Create a new reference to attach this magnet to a simulator or a control systemand. + """ obj = self.__class__(self._cfg) + obj.__modelName = self.__modelName obj.__strength = strength obj.__hardware = hardware + obj._peer = peer return obj def set_energy(self, energy:float): + """ + Set the energy in eV to compute and set the magnet rigidity on the underlying magnet model. + """ if self.__model is not None: self.__model.set_magnet_rigidity(np.double(energy / speed_of_light)) + + def set_model_name(self, name:str): + """ + Sets the name of this magnet in the model (Used for combined function manget) + """ + self.__modelName = name + + def get_strength_str(self) -> str: + """ + Returns a string representation of the magnet strength + """ + if self.__strength is not None: + return f"{str(self.__strength.get())} {self.__strength.unit()}" + elif self.__hardware is not None: + return f"{str(self.__hardware.get())} {self.__hardware.unit()}" + + def __str__(self): + return "%s(name='%s', model='%s', peer='%s', strength=%f, hardware=%f)" % ( + self.__class__.__name__, + self.get_name(), + self.__modelName, + self.get_peer(), + self.__strength.get() if self.__strength is not None else np.nan, + self.__hardware.get() if self.__hardware is not None else np.nan + ) diff --git a/pyaml/rf/rf_plant.py b/pyaml/rf/rf_plant.py index e39f4ee0..81be5ca4 100644 --- a/pyaml/rf/rf_plant.py +++ b/pyaml/rf/rf_plant.py @@ -8,7 +8,7 @@ from .rf_transmitter import RFTransmitter from .. import PyAMLException from ..control.deviceaccess import DeviceAccess -from ..lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from ..common import abstract # Define the main class name for this module diff --git a/pyaml/rf/rf_transmitter.py b/pyaml/rf/rf_transmitter.py index 6162bbb1..3560cdf3 100644 --- a/pyaml/rf/rf_transmitter.py +++ b/pyaml/rf/rf_transmitter.py @@ -7,7 +7,7 @@ from .. import PyAMLException from ..control.deviceaccess import DeviceAccess -from ..lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from ..common import abstract # Define the main class name for this module diff --git a/tests/test_load_quad.py b/tests/test_load_quad.py index 129358dd..d7278d94 100644 --- a/tests/test_load_quad.py +++ b/tests/test_load_quad.py @@ -19,6 +19,12 @@ #def test_json(): # print(json.dumps(QuadrupoleConfigModel.model_json_schema(),indent=2)) +class DummyPeer: + def __init__(self): + pass + def name(self)->str: + return "Dummy" + @pytest.mark.parametrize("install_test_package", [{ "name": "pyaml_external", "path": "tests/external" @@ -29,7 +35,7 @@ def test_quad_external_model(install_test_package, config_root_dir): hcorr_with_external_model: HCorrector = Factory.depth_first_build(cfg_hcorr_yaml) strength = RWStrengthScalar(hcorr_with_external_model.model) hardware = RWHardwareScalar(hcorr_with_external_model.model) - ref_corr = hcorr_with_external_model.attach(strength,hardware) + ref_corr = hcorr_with_external_model.attach(DummyPeer(),strength,hardware) ref_corr.strength.set(10.0) print(ref_corr.strength.get()) Factory.clear() @@ -51,7 +57,7 @@ def test_quad_linear(magnet_file, install_test_package, config_root_dir): quad:Quadrupole = Factory.depth_first_build(cfg_quad) hardware = RWHardwareScalar(quad.model) if quad.model.has_hardware() else None strength = RWStrengthScalar(quad.model) if quad.model.has_physics() else None - ref_quad = quad.attach(strength,hardware) + ref_quad = quad.attach(DummyPeer(),strength,hardware) ref_quad.model.set_magnet_rigidity(6e9 / speed_of_light) try: @@ -93,7 +99,7 @@ def test_combined_function_magnets(magnet_file, config_root_dir): hUnits = sh.model.get_hardware_units() assert( sUnits[0] == 'rad' and sUnits[1] == 'rad' and sUnits[2] == 'm-1' ) assert( hUnits[0] == 'A' and hUnits[1] == 'A' and hUnits[2] == 'A') - ms = sh.attach(strengths,currents) + ms = sh.attach(DummyPeer(),strengths,currents) hCorr = ms[0] vCorr = ms[1] sqCorr = ms[2] From ee9cf3f194dcbb2258dcf5942170d0b0878ec167 Mon Sep 17 00:00:00 2001 From: PONS Date: Tue, 4 Nov 2025 15:34:10 +0100 Subject: [PATCH 2/8] __repr__() for magnets --- pyaml/bpm/bpm.py | 2 +- pyaml/common/element.py | 54 ++++++++++++++++++++++ pyaml/common/element_holder.py | 5 +- pyaml/configuration/factory.py | 3 +- pyaml/control/controlsystem.py | 6 +-- pyaml/instrument.py | 2 +- pyaml/lattice/attribute_linker.py | 2 +- pyaml/lattice/element.py | 33 ------------- pyaml/lattice/lattice_elements_linker.py | 2 +- pyaml/lattice/simulator.py | 8 ++-- pyaml/magnet/cfm_magnet.py | 23 ++++++--- pyaml/magnet/magnet.py | 59 +++++++++++++++++++++--- pyaml/rf/rf_plant.py | 2 +- pyaml/rf/rf_transmitter.py | 2 +- tests/test_load_quad.py | 12 +++-- 15 files changed, 150 insertions(+), 65 deletions(-) create mode 100644 pyaml/common/element.py delete mode 100644 pyaml/lattice/element.py diff --git a/pyaml/bpm/bpm.py b/pyaml/bpm/bpm.py index e2fe5f60..76e81cf1 100644 --- a/pyaml/bpm/bpm.py +++ b/pyaml/bpm/bpm.py @@ -1,4 +1,4 @@ -from ..lattice.element import Element, ElementConfigModel +from ..common.element import Element, ElementConfigModel from ..lattice.abstract_impl import RBpmArray, RWBpmOffsetArray, RWBpmTiltScalar from ..bpm.bpm_model import BPMModel from ..common.exception import PyAMLException diff --git a/pyaml/common/element.py b/pyaml/common/element.py new file mode 100644 index 00000000..0d8c8e76 --- /dev/null +++ b/pyaml/common/element.py @@ -0,0 +1,54 @@ +from .exception import PyAMLException + +from pydantic import BaseModel,ConfigDict + +class ElementConfigModel(BaseModel): + + model_config = ConfigDict(arbitrary_types_allowed=True,extra="forbid") + + name : str + """Element name""" + +class Element(object): + """ + Class providing access to one element of a physical or simulated lattice + + Attributes: + name: str + The unique name identifying the element in the configuration file + """ + def __init__(self,name:str): + self.__name: str = name + self._peer = None # Peer: ControlSystem, Simulator + + def get_name(self): + """ + Returns the name of the element + """ + return self.__name + + def set_energy(self,E:float): + """ + Set the instrument energy on this element + """ + pass + + def check_peer(self): + """ + Throws an exception if the element is not attacched to a simulator or to a control system + """ + if self._peer is None: + raise PyAMLException(f"{str(self)} is not attached to a control system or the a simulator") + + def get_peer(self) -> str: + """ + Returns a string representation of peer simulator or control system + """ + return "None" if self._peer is None else f"{self._peer.__class__.__name__}:{self._peer.name()}" + + def __repr__(self): + return "%s(name='%s', peer='%s')" % ( + self.__class__.__name__, + self.__name, + self.get_peer() + ) diff --git a/pyaml/common/element_holder.py b/pyaml/common/element_holder.py index 24da1b37..66ef0b98 100644 --- a/pyaml/common/element_holder.py +++ b/pyaml/common/element_holder.py @@ -1,7 +1,7 @@ """ Module handling element references for simulators and control system """ -from ..lattice.element import Element +from .element import Element from ..magnet.magnet import Magnet from ..rf.rf_plant import RFPlant from ..rf.rf_transmitter import RFTransmitter @@ -53,6 +53,9 @@ def get_magnet(self,name:str) -> Magnet: return self.__MAGNETS[name] def add_magnet(self,name:str,m:Magnet): + if name in self.__MAGNETS: + print(self.__MAGNETS) + raise PyAMLException(f"Duplicate magnet name {name}") from None self.__MAGNETS[name] = m def get_magnets(self,name:str) -> MagnetArray: diff --git a/pyaml/configuration/factory.py b/pyaml/configuration/factory.py index 8c9c1c06..7c821147 100644 --- a/pyaml/configuration/factory.py +++ b/pyaml/configuration/factory.py @@ -3,7 +3,7 @@ from threading import Lock from ..common.exception import PyAMLConfigException -from ..lattice.element import Element +from ..common.element import Element from pydantic import ValidationError class BuildStrategy: @@ -154,6 +154,7 @@ def register_element(self, elt): if isinstance(elt,Element): name = elt.get_name() if name in self._elements: + print(self._elements) raise PyAMLConfigException(f"element {name} already defined") self._elements[name] = elt diff --git a/pyaml/control/controlsystem.py b/pyaml/control/controlsystem.py index 49ba2b1d..7ef07694 100644 --- a/pyaml/control/controlsystem.py +++ b/pyaml/control/controlsystem.py @@ -1,7 +1,7 @@ from abc import ABCMeta, abstractmethod from ..common.element_holder import ElementHolder from ..common.abstract import RWMapper -from ..lattice.element import Element +from ..common.element import Element from ..control.abstract_impl import RWHardwareScalar,RWHardwareArray,RWStrengthScalar,RWStrengthArray from ..bpm.bpm import BPM from ..diagnostics.tune_monitor import BetatronTuneMonitor @@ -103,7 +103,7 @@ def fill_device(self,elements:list[Element]): current = RWHardwareScalar(e.model) if e.model.has_hardware() else None strength = RWStrengthScalar(e.model) if e.model.has_physics() else None # Create a unique ref for this control system - m = e.attach(strength, current) + m = e.attach(self,strength, current) self.add_magnet(m.get_name(),m) elif isinstance(e,CombinedFunctionMagnet): @@ -111,7 +111,7 @@ def fill_device(self,elements:list[Element]): currents = RWHardwareArray(e.model) if e.model.has_hardware() else None strengths = RWStrengthArray(e.model) if e.model.has_physics() else None # Create unique refs of each function for this control system - ms = e.attach(strengths,currents) + ms = e.attach(self,strengths,currents) for m in ms: self.add_magnet(m.get_name(),m) elif isinstance(e,BPM): diff --git a/pyaml/instrument.py b/pyaml/instrument.py index 3ee284ab..db04e678 100644 --- a/pyaml/instrument.py +++ b/pyaml/instrument.py @@ -3,7 +3,7 @@ """ from .control.controlsystem import ControlSystem -from .lattice.element import Element +from .common.element import Element from .lattice.simulator import Simulator from .arrays.array import ArrayConfig from pydantic import BaseModel,ConfigDict diff --git a/pyaml/lattice/attribute_linker.py b/pyaml/lattice/attribute_linker.py index d4efb7df..f0dd97ef 100644 --- a/pyaml/lattice/attribute_linker.py +++ b/pyaml/lattice/attribute_linker.py @@ -1,7 +1,7 @@ import at from pydantic import ConfigDict -from pyaml.lattice.element import Element +from pyaml.common.element import Element from pyaml.lattice.lattice_elements_linker import LinkerIdentifier, LinkerConfigModel, LatticeElementsLinker PYAMLCLASS = "PyAtAttributeElementsLinker" diff --git a/pyaml/lattice/element.py b/pyaml/lattice/element.py deleted file mode 100644 index 145694eb..00000000 --- a/pyaml/lattice/element.py +++ /dev/null @@ -1,33 +0,0 @@ -from pydantic import BaseModel,ConfigDict - -class ElementConfigModel(BaseModel): - - model_config = ConfigDict(arbitrary_types_allowed=True,extra="forbid") - - name : str - """Element name""" - -class Element(object): - """ - Class providing access to one element of a physical or simulated lattice - - Attributes: - name (str): The name identifying the element in the configuration file - """ - def __init__(self,name:str): - self.__name: str = name - - def get_name(self): - """ - Returns the name of the element - """ - return self.__name - - def set_energy(self,E:float): - pass - - def __repr__(self): - return "%s(%s)" % ( - self.__class__.__name__, - self.__name - ) diff --git a/pyaml/lattice/lattice_elements_linker.py b/pyaml/lattice/lattice_elements_linker.py index 103000af..297365f0 100644 --- a/pyaml/lattice/lattice_elements_linker.py +++ b/pyaml/lattice/lattice_elements_linker.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, ConfigDict from pyaml import PyAMLException -from pyaml.lattice.element import Element +from pyaml.common.element import Element class LinkerConfigModel(BaseModel): diff --git a/pyaml/lattice/simulator.py b/pyaml/lattice/simulator.py index 169540b8..d42d9a48 100644 --- a/pyaml/lattice/simulator.py +++ b/pyaml/lattice/simulator.py @@ -1,7 +1,7 @@ from .attribute_linker import PyAtAttributeElementsLinker, ConfigModel as PyAtAttrLinkerConfigModel from .lattice_elements_linker import LatticeElementsLinker from ..configuration import get_root_folder -from .element import Element +from ..common.element import Element from pathlib import Path from ..magnet.magnet import Magnet from pyaml.bpm.bpm import BPM @@ -94,7 +94,7 @@ def fill_device(self,elements:list[Element]): current = RWHardwareScalar(self.get_at_elems(e),e.polynom,e.model) if e.model.has_physics() else None strength = RWStrengthScalar(self.get_at_elems(e),e.polynom,e.model) if e.model.has_physics() else None # Create a unique ref for this simulator - m = e.attach(strength,current) + m = e.attach(self,strength,current) self.add_magnet(m.get_name(),m) elif isinstance(e,CombinedFunctionMagnet): @@ -102,10 +102,10 @@ def fill_device(self,elements:list[Element]): currents = RWHardwareArray(self.get_at_elems(e),e.polynoms,e.model) if e.model.has_physics() else None strengths = RWStrengthArray(self.get_at_elems(e),e.polynoms,e.model) if e.model.has_physics() else None # Create unique refs of each function for this simulator - ms = e.attach(strengths,currents) + ms = e.attach(self,strengths,currents) for m in ms: self.add_magnet(m.get_name(), m) - self.add_magnet(m.get_name(), m) + elif isinstance(e,BPM): # This assumes unique BPM names in the pyAT lattice tilt = RWBpmTiltScalar(self.get_at_elems(e)[0]) diff --git a/pyaml/magnet/cfm_magnet.py b/pyaml/magnet/cfm_magnet.py index 91a5ea2b..085f49f0 100644 --- a/pyaml/magnet/cfm_magnet.py +++ b/pyaml/magnet/cfm_magnet.py @@ -1,6 +1,6 @@ from .model import MagnetModel -from ..lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from ..common import abstract from ..common.abstract import RWMapper from ..common.exception import PyAMLException @@ -13,8 +13,8 @@ from .octupole import Octupole from .skewoctu import SkewOctu from .magnet import Magnet,MagnetConfigModel +from ..configuration import Factory -from pydantic import SerializeAsAny from scipy.constants import speed_of_light _fmap:dict = { @@ -45,13 +45,14 @@ def __init__(self, cfg: ConfigModel): super().__init__(cfg.name) self._cfg = cfg self.model = cfg.model + self.virtuals:list[Magnet] = [] if self.model is not None and not hasattr(self.model._cfg,"multipoles"): raise PyAMLException(f"{cfg.name} model: mutipoles field required for combined function magnet") idx = 0 self.polynoms = [] - for m in cfg.mapping: + for idx,m in enumerate(cfg.mapping): # Check mapping validity if len(m)!=2: raise PyAMLException("Invalid CombinedFunctionMagnet mapping for {m}") @@ -60,17 +61,25 @@ def __init__(self, cfg: ConfigModel): if m[0] not in self.model._cfg.multipoles: raise PyAMLException(m[0] + " not found in underlying magnet model") self.polynoms.append(_fmap[m[0]].polynom) + # Create the virtual magnet for the correspoding multipole + vm = self.create_virutal_manget(m[1],m[0]) + self.virtuals.append(vm) + # Register the virtual element in the factory to have a coherent factory and improve error reporting + Factory.register_element(vm) - def attach(self, strengths: abstract.ReadWriteFloatArray, hardwares: abstract.ReadWriteFloatArray) -> list[Magnet]: + def create_virutal_manget(self,name:str,idx:int) -> Magnet: + args = {"name":name,"model":self.model} + mVirtual:Magnet = _fmap[idx](MagnetConfigModel(**args)) + mVirtual.set_model_name(self.get_name()) + return mVirtual + def attach(self, peer, strengths: abstract.ReadWriteFloatArray, hardwares: abstract.ReadWriteFloatArray) -> list[Magnet]: # Construct a single function magnet for each multipole of this combined function magnet l = [] for idx,m in enumerate(self._cfg.mapping): - args = {"name":m[1],"model":self.model} - mclass:Magnet = _fmap[m[0]](MagnetConfigModel(**args)) strength = RWMapper(strengths,idx) hardware = RWMapper(hardwares,idx) if self.model.has_hardware() else None - l.append(mclass.attach(strength,hardware)) + l.append(self.virtuals[idx].attach(peer,strength,hardware)) return l def set_energy(self,E:float): diff --git a/pyaml/magnet/magnet.py b/pyaml/magnet/magnet.py index 92aa3e65..c31870c8 100644 --- a/pyaml/magnet/magnet.py +++ b/pyaml/magnet/magnet.py @@ -1,7 +1,8 @@ -from pyaml.lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from .. import PyAMLException from ..common import abstract from .model import MagnetModel + from scipy.constants import speed_of_light try: from typing import Self # Python 3.11+ @@ -32,32 +33,76 @@ def __init__(self, name:str, model:MagnetModel = None): """ super().__init__(name) self.__model = model - self.__strength = None - self.__hardware = None + self.__strength:abstract.ReadWriteFloatScalar = None + self.__hardware:abstract.ReadWriteFloatScalar = None + self.__modelName = self.get_name() @property def strength(self) -> abstract.ReadWriteFloatScalar: + """ + Gives access to the strength of this magnet in physics unit + """ + self.check_peer() if self.__strength is None: - raise PyAMLException(f"{str(self)} is unattached or has no model that supports physics units") + raise PyAMLException(f"{str(self)} has no model that supports physics units") return self.__strength @property def hardware(self) -> abstract.ReadWriteFloatScalar: + """ + Gives access to the strength of this magnet in hardware unit when possible + """ + self.check_peer() if self.__hardware is None: - raise PyAMLException(f"{str(self)} is unattached or has no model that supports hardware units") + raise PyAMLException(f"{str(self)} has no model that supports hardware units") return self.__hardware @property def model(self) -> MagnetModel: + """ + Returns a handle to the underlying magnet model + """ return self.__model - def attach(self, strength: abstract.ReadWriteFloatScalar, hardware: abstract.ReadWriteFloatScalar) -> Self: - # Attach strength and current attribute and returns a new reference + def attach(self, peer, strength: abstract.ReadWriteFloatScalar, hardware: abstract.ReadWriteFloatScalar) -> Self: + """ + Create a new reference to attach this magnet to a simulator or a control systemand. + """ obj = self.__class__(self._cfg) + obj.__modelName = self.__modelName obj.__strength = strength obj.__hardware = hardware + obj._peer = peer return obj def set_energy(self, energy:float): + """ + Set the energy in eV to compute and set the magnet rigidity on the underlying magnet model. + """ if self.__model is not None: self.__model.set_magnet_rigidity(np.double(energy / speed_of_light)) + + def set_model_name(self, name:str): + """ + Sets the name of this magnet in the model (Used for combined function manget) + """ + self.__modelName = name + + def get_strength_str(self) -> str: + """ + Returns a string representation of the magnet strength + """ + if self.__strength is not None: + return f"{str(self.__strength.get())} {self.__strength.unit()}" + elif self.__hardware is not None: + return f"{str(self.__hardware.get())} {self.__hardware.unit()}" + + def __str__(self): + return "%s(name='%s', model='%s', peer='%s', strength=%f, hardware=%f)" % ( + self.__class__.__name__, + self.get_name(), + self.__modelName, + self.get_peer(), + self.__strength.get() if self.__strength is not None else np.nan, + self.__hardware.get() if self.__hardware is not None else np.nan + ) diff --git a/pyaml/rf/rf_plant.py b/pyaml/rf/rf_plant.py index e39f4ee0..81be5ca4 100644 --- a/pyaml/rf/rf_plant.py +++ b/pyaml/rf/rf_plant.py @@ -8,7 +8,7 @@ from .rf_transmitter import RFTransmitter from .. import PyAMLException from ..control.deviceaccess import DeviceAccess -from ..lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from ..common import abstract # Define the main class name for this module diff --git a/pyaml/rf/rf_transmitter.py b/pyaml/rf/rf_transmitter.py index 6162bbb1..3560cdf3 100644 --- a/pyaml/rf/rf_transmitter.py +++ b/pyaml/rf/rf_transmitter.py @@ -7,7 +7,7 @@ from .. import PyAMLException from ..control.deviceaccess import DeviceAccess -from ..lattice.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel from ..common import abstract # Define the main class name for this module diff --git a/tests/test_load_quad.py b/tests/test_load_quad.py index 129358dd..d7278d94 100644 --- a/tests/test_load_quad.py +++ b/tests/test_load_quad.py @@ -19,6 +19,12 @@ #def test_json(): # print(json.dumps(QuadrupoleConfigModel.model_json_schema(),indent=2)) +class DummyPeer: + def __init__(self): + pass + def name(self)->str: + return "Dummy" + @pytest.mark.parametrize("install_test_package", [{ "name": "pyaml_external", "path": "tests/external" @@ -29,7 +35,7 @@ def test_quad_external_model(install_test_package, config_root_dir): hcorr_with_external_model: HCorrector = Factory.depth_first_build(cfg_hcorr_yaml) strength = RWStrengthScalar(hcorr_with_external_model.model) hardware = RWHardwareScalar(hcorr_with_external_model.model) - ref_corr = hcorr_with_external_model.attach(strength,hardware) + ref_corr = hcorr_with_external_model.attach(DummyPeer(),strength,hardware) ref_corr.strength.set(10.0) print(ref_corr.strength.get()) Factory.clear() @@ -51,7 +57,7 @@ def test_quad_linear(magnet_file, install_test_package, config_root_dir): quad:Quadrupole = Factory.depth_first_build(cfg_quad) hardware = RWHardwareScalar(quad.model) if quad.model.has_hardware() else None strength = RWStrengthScalar(quad.model) if quad.model.has_physics() else None - ref_quad = quad.attach(strength,hardware) + ref_quad = quad.attach(DummyPeer(),strength,hardware) ref_quad.model.set_magnet_rigidity(6e9 / speed_of_light) try: @@ -93,7 +99,7 @@ def test_combined_function_magnets(magnet_file, config_root_dir): hUnits = sh.model.get_hardware_units() assert( sUnits[0] == 'rad' and sUnits[1] == 'rad' and sUnits[2] == 'm-1' ) assert( hUnits[0] == 'A' and hUnits[1] == 'A' and hUnits[2] == 'A') - ms = sh.attach(strengths,currents) + ms = sh.attach(DummyPeer(),strengths,currents) hCorr = ms[0] vCorr = ms[1] sqCorr = ms[2] From fbfb0048740a7ab5f96b18a9fc03924d34c7b49d Mon Sep 17 00:00:00 2001 From: PONS Date: Wed, 5 Nov 2025 07:38:31 +0100 Subject: [PATCH 3/8] Added repr() --- pyaml/bpm/bpm.py | 5 ++++- pyaml/bpm/bpm_simple_model.py | 3 +++ pyaml/configuration/csvcurve.py | 3 +++ pyaml/configuration/csvmatrix.py | 3 +++ pyaml/control/controlsystem.py | 6 +++--- pyaml/lattice/simulator.py | 9 ++++++--- pyaml/magnet/cfm_magnet.py | 3 +++ pyaml/magnet/identity_cfm_model.py | 3 +++ pyaml/magnet/identity_model.py | 5 +---- pyaml/magnet/linear_cfm_model.py | 2 ++ pyaml/magnet/linear_model.py | 7 ++----- pyaml/magnet/magnet.py | 14 ++------------ pyaml/rf/rf_plant.py | 6 +++++- pyaml/rf/rf_transmitter.py | 8 +++----- 14 files changed, 43 insertions(+), 34 deletions(-) diff --git a/pyaml/bpm/bpm.py b/pyaml/bpm/bpm.py index 76e81cf1..39791440 100644 --- a/pyaml/bpm/bpm.py +++ b/pyaml/bpm/bpm.py @@ -63,7 +63,7 @@ def tilt(self) -> RWBpmTiltScalar: raise PyAMLException(f"{str(self)} has no attached tilt") return self.__tilt - def attach(self, positions: RBpmArray , offset: RWBpmOffsetArray, + def attach(self, peer, positions: RBpmArray , offset: RWBpmOffsetArray, tilt: RWBpmTiltScalar) -> Self: # Attach positions, offset and tilt attributes and returns a new # reference @@ -72,5 +72,8 @@ def attach(self, positions: RBpmArray , offset: RWBpmOffsetArray, obj.__positions = positions obj.__offset = offset obj.__tilt = tilt + obj._peer = peer return obj + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/bpm/bpm_simple_model.py b/pyaml/bpm/bpm_simple_model.py index 1de6033f..069d97e7 100644 --- a/pyaml/bpm/bpm_simple_model.py +++ b/pyaml/bpm/bpm_simple_model.py @@ -113,3 +113,6 @@ def get_offset_devices(self) -> list[DeviceAccess]: Array of DeviceAcess """ return [] + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/configuration/csvcurve.py b/pyaml/configuration/csvcurve.py index f5fbbf68..004227ab 100644 --- a/pyaml/configuration/csvcurve.py +++ b/pyaml/configuration/csvcurve.py @@ -38,3 +38,6 @@ def __init__(self, cfg: ConfigModel): def get_curve(self) -> np.array: return self._curve + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/configuration/csvmatrix.py b/pyaml/configuration/csvmatrix.py index 709511d8..a70e162e 100644 --- a/pyaml/configuration/csvmatrix.py +++ b/pyaml/configuration/csvmatrix.py @@ -33,3 +33,6 @@ def __init__(self, cfg: ConfigModel): def get_matrix(self) -> np.array: return self._mat + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/control/controlsystem.py b/pyaml/control/controlsystem.py index 7ef07694..070a438c 100644 --- a/pyaml/control/controlsystem.py +++ b/pyaml/control/controlsystem.py @@ -118,7 +118,7 @@ def fill_device(self,elements:list[Element]): tilt = RWBpmTiltScalar(e.model) offsets = RWBpmOffsetArray(e.model) positions = RBpmArray(e.model) - e = e.attach(positions, offsets, tilt) + e = e.attach(self,positions, offsets, tilt) self.add_bpm(e.get_name(),e) @@ -128,13 +128,13 @@ def fill_device(self,elements:list[Element]): for t in e._cfg.transmitters: voltage = RWRFVoltageScalar(t) phase = RWRFPhaseScalar(t) - nt = t.attach(voltage,phase) + nt = t.attach(self,voltage,phase) self.add_rf_transnmitter(nt.get_name(),nt) attachedTrans.append(nt) frequency = RWRFFrequencyScalar(e) voltage = RWTotalVoltage(attachedTrans) - ne = e.attach(frequency,voltage) + ne = e.attach(self,frequency,voltage) self.add_rf_plant(ne.get_name(),ne) elif isinstance(e,BetatronTuneMonitor): betatron_tune = RBetatronTuneArray(e) diff --git a/pyaml/lattice/simulator.py b/pyaml/lattice/simulator.py index d42d9a48..360f73f7 100644 --- a/pyaml/lattice/simulator.py +++ b/pyaml/lattice/simulator.py @@ -111,7 +111,7 @@ def fill_device(self,elements:list[Element]): tilt = RWBpmTiltScalar(self.get_at_elems(e)[0]) offsets = RWBpmOffsetArray(self.get_at_elems(e)[0]) positions = RBpmArray(self.get_at_elems(e)[0],self.ring) - e = e.attach(positions, offsets, tilt) + e = e.attach(self,positions, offsets, tilt) self.add_bpm(e.get_name(),e) elif isinstance(e,RFPlant): @@ -133,14 +133,14 @@ def fill_device(self,elements:list[Element]): voltage = RWRFVoltageScalar(cavsPerTrans,t) phase = RWRFPhaseScalar(cavsPerTrans,t) - nt = t.attach(voltage,phase) + nt = t.attach(self,voltage,phase) attachedTrans.append(nt) self.add_rf_transnmitter(nt.get_name(),nt) cavs.extend(cavsPerTrans) frequency = RWRFFrequencyScalar(cavs,harmonics,e) voltage = RWTotalVoltage(attachedTrans) - ne = e.attach(frequency,voltage) + ne = e.attach(self,frequency,voltage) self.add_rf_plant(ne.get_name(),ne) elif isinstance(e, BetatronTuneMonitor): betatron_tune = RBetatronTuneArray(self.ring) @@ -154,3 +154,6 @@ def get_at_elems(self,element:Element) -> list[at.Element]: if not element_list: raise PyAMLException(f"{identifier} not found in lattice:{self._cfg.lattice}") return element_list + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) \ No newline at end of file diff --git a/pyaml/magnet/cfm_magnet.py b/pyaml/magnet/cfm_magnet.py index 085f49f0..443dffcf 100644 --- a/pyaml/magnet/cfm_magnet.py +++ b/pyaml/magnet/cfm_magnet.py @@ -85,3 +85,6 @@ def attach(self, peer, strengths: abstract.ReadWriteFloatArray, hardwares: abstr def set_energy(self,E:float): if(self.model is not None): self.model.set_magnet_rigidity(E/speed_of_light) + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/magnet/identity_cfm_model.py b/pyaml/magnet/identity_cfm_model.py index bc179ddd..9f951dfe 100644 --- a/pyaml/magnet/identity_cfm_model.py +++ b/pyaml/magnet/identity_cfm_model.py @@ -86,3 +86,6 @@ def has_physics(self) -> bool: def has_hardware(self) -> bool: return self._cfg.powerconverters is not None + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/magnet/identity_model.py b/pyaml/magnet/identity_model.py index dff6bdd7..d45721f0 100644 --- a/pyaml/magnet/identity_model.py +++ b/pyaml/magnet/identity_model.py @@ -70,7 +70,4 @@ def has_hardware(self) -> bool: return self._cfg.powerconverter is not None def __repr__(self): - return "%s(unit=%s)" % ( - self.__class__.__name__, - self.__unit, - ) + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/magnet/linear_cfm_model.py b/pyaml/magnet/linear_cfm_model.py index 42041575..db4b39e4 100644 --- a/pyaml/magnet/linear_cfm_model.py +++ b/pyaml/magnet/linear_cfm_model.py @@ -163,3 +163,5 @@ def set_magnet_rigidity(self, brho: np.double): def has_hardware(self) -> bool: return (self.__nbPS == self.__nbFunction) and np.allclose(self.__matrix, np.eye(self.__nbFunction)) + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) diff --git a/pyaml/magnet/linear_model.py b/pyaml/magnet/linear_model.py index 5c99decd..fb499207 100644 --- a/pyaml/magnet/linear_model.py +++ b/pyaml/magnet/linear_model.py @@ -76,8 +76,5 @@ def set_magnet_rigidity(self, brho: np.double): self.__brho = brho def __repr__(self): - return "%s(curve[%d pts], unit=%s)" % ( - self.__class__.__name__, - len(self.__curve), - self.__strength_unit, - ) + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + \ No newline at end of file diff --git a/pyaml/magnet/magnet.py b/pyaml/magnet/magnet.py index c31870c8..093dcb13 100644 --- a/pyaml/magnet/magnet.py +++ b/pyaml/magnet/magnet.py @@ -88,21 +88,11 @@ def set_model_name(self, name:str): """ self.__modelName = name - def get_strength_str(self) -> str: - """ - Returns a string representation of the magnet strength - """ - if self.__strength is not None: - return f"{str(self.__strength.get())} {self.__strength.unit()}" - elif self.__hardware is not None: - return f"{str(self.__hardware.get())} {self.__hardware.unit()}" - def __str__(self): - return "%s(name='%s', model='%s', peer='%s', strength=%f, hardware=%f)" % ( + return "%s(name='%s', model='%s', peer='%s', magnet_model=%s)" % ( self.__class__.__name__, self.get_name(), self.__modelName, self.get_peer(), - self.__strength.get() if self.__strength is not None else np.nan, - self.__hardware.get() if self.__hardware is not None else np.nan + repr(self.__model) ) diff --git a/pyaml/rf/rf_plant.py b/pyaml/rf/rf_plant.py index 81be5ca4..0a28aa10 100644 --- a/pyaml/rf/rf_plant.py +++ b/pyaml/rf/rf_plant.py @@ -44,12 +44,16 @@ def voltage(self) -> abstract.ReadWriteFloatScalar: raise PyAMLException(f"{str(self)} has no trasmitter device defined") return self.__voltage - def attach(self, frequency: abstract.ReadWriteFloatScalar, voltage: abstract.ReadWriteFloatScalar) -> Self: + def attach(self, peer, frequency: abstract.ReadWriteFloatScalar, voltage: abstract.ReadWriteFloatScalar) -> Self: # Attach frequency attribute and returns a new reference obj = self.__class__(self._cfg) obj.__frequency = frequency obj.__voltage = voltage + obj._peer = peer return obj + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) class RWTotalVoltage(abstract.ReadWriteFloatScalar): diff --git a/pyaml/rf/rf_transmitter.py b/pyaml/rf/rf_transmitter.py index 3560cdf3..8abee386 100644 --- a/pyaml/rf/rf_transmitter.py +++ b/pyaml/rf/rf_transmitter.py @@ -52,15 +52,13 @@ def phase(self) -> abstract.ReadWriteFloatScalar: raise PyAMLException(f"{str(self)} is unattached or has no phase device defined") return self.__phase - def attach(self, voltage: abstract.ReadWriteFloatScalar, phase: abstract.ReadWriteFloatScalar) -> Self: + def attach(self, peer, voltage: abstract.ReadWriteFloatScalar, phase: abstract.ReadWriteFloatScalar) -> Self: # Attach voltage and phase attribute and returns a new reference obj = self.__class__(self._cfg) obj.__voltage = voltage obj.__phase = phase + obj._peer = peer return obj def __repr__(self): - return "%s(%s)" % ( - self.__class__.__name__, - self._cfg.name - ) + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) From 5580c51ea55accb07102a0990f96232d12a6a7d7 Mon Sep 17 00:00:00 2001 From: PONS Date: Tue, 4 Nov 2025 15:34:10 +0100 Subject: [PATCH 4/8] __repr__() for magnets --- pyaml/common/element_holder.py | 14 ++++---------- pyaml/control/abstract_impl.py | 2 +- pyaml/control/controlsystem.py | 4 +++- pyaml/diagnostics/tune_monitor.py | 18 +++++++++++------- pyaml/lattice/abstract_impl.py | 2 +- pyaml/lattice/simulator.py | 9 +++++---- tests/config/EBSOrbit.yaml | 10 ++++++++++ 7 files changed, 35 insertions(+), 24 deletions(-) diff --git a/pyaml/common/element_holder.py b/pyaml/common/element_holder.py index 66ef0b98..f8aea27b 100644 --- a/pyaml/common/element_holder.py +++ b/pyaml/common/element_holder.py @@ -8,6 +8,7 @@ from ..arrays.magnet_array import MagnetArray from ..arrays.bpm_array import BPMArray from ..common.exception import PyAMLException +from ..diagnostics.tune_monitor import BetatronTuneMonitor class ElementHolder(object): """ @@ -107,17 +108,10 @@ def get_rf_trasnmitter(self,name:str) -> RFTransmitter: raise PyAMLException(f"RFTransmitter {name} not defined") return self.__RFTRANSMITTER[name] - - - def get_bpm(self,name:str) -> Element: - if name not in self.__BPMS: - raise Exception(f"BPM {name} not defined") - return self.__BPMS[name] - def add_bpm(self,name:str,bpm:Element): - self.__BPMS[name] = bpm - - def get_betatron_tune_monitor(self, name:str) -> Element: + # Tune monitor + + def get_betatron_tune_monitor(self, name:str) -> BetatronTuneMonitor: if name not in self.__DIAG: raise Exception(f"Diagnostic devices array does not contain {name}") return self.__DIAG[name] diff --git a/pyaml/control/abstract_impl.py b/pyaml/control/abstract_impl.py index 8902965d..275e42fa 100644 --- a/pyaml/control/abstract_impl.py +++ b/pyaml/control/abstract_impl.py @@ -351,7 +351,7 @@ def unit(self) -> str: #------------------------------------------------------------------------------ -class RBetatronTuneArray(abstract.ReadFloatScalar): +class RBetatronTuneArray(abstract.ReadFloatArray): """ Class providing read write access to betatron tune of a control system. """ diff --git a/pyaml/control/controlsystem.py b/pyaml/control/controlsystem.py index 070a438c..306d22cc 100644 --- a/pyaml/control/controlsystem.py +++ b/pyaml/control/controlsystem.py @@ -114,6 +114,7 @@ def fill_device(self,elements:list[Element]): ms = e.attach(self,strengths,currents) for m in ms: self.add_magnet(m.get_name(),m) + elif isinstance(e,BPM): tilt = RWBpmTiltScalar(e.model) offsets = RWBpmOffsetArray(e.model) @@ -136,7 +137,8 @@ def fill_device(self,elements:list[Element]): voltage = RWTotalVoltage(attachedTrans) ne = e.attach(self,frequency,voltage) self.add_rf_plant(ne.get_name(),ne) + elif isinstance(e,BetatronTuneMonitor): betatron_tune = RBetatronTuneArray(e) - e = e.attach(betatron_tune) + e = e.attach(self,betatron_tune) self.add_betatron_tune_monitor(e.get_name(), e) diff --git a/pyaml/diagnostics/tune_monitor.py b/pyaml/diagnostics/tune_monitor.py index 5ff79969..c99ce647 100644 --- a/pyaml/diagnostics/tune_monitor.py +++ b/pyaml/diagnostics/tune_monitor.py @@ -1,7 +1,7 @@ - -from ..lattice.element import Element, ElementConfigModel -from ..lattice.abstract_impl import RBetatronTuneArray +from ..common.element import Element, ElementConfigModel +from ..common.abstract import ReadFloatArray from ..control.deviceaccess import DeviceAccess + from typing import Self from pydantic import ConfigDict @@ -37,12 +37,16 @@ def __init__(self, cfg: ConfigModel): self.__tune = None @property - def tune(self) -> RBetatronTuneArray: + def tune(self) -> ReadFloatArray: + self.check_peer() return self.__tune - def attach(self, betatron_tune: RBetatronTuneArray) -> Self: - + def attach(self, peer, betatron_tune: ReadFloatArray) -> Self: obj = self.__class__(self._cfg) obj.__tune = betatron_tune + obj._peer = peer return obj - + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + diff --git a/pyaml/lattice/abstract_impl.py b/pyaml/lattice/abstract_impl.py index fc47f9a3..b998a0fc 100644 --- a/pyaml/lattice/abstract_impl.py +++ b/pyaml/lattice/abstract_impl.py @@ -380,7 +380,7 @@ def unit(self) -> str: #------------------------------------------------------------------------------ -class RBetatronTuneArray(abstract.ReadFloatScalar): +class RBetatronTuneArray(abstract.ReadFloatArray): """ Class providing read-only access to the betatron tune of a ring. """ diff --git a/pyaml/lattice/simulator.py b/pyaml/lattice/simulator.py index 360f73f7..642a19b1 100644 --- a/pyaml/lattice/simulator.py +++ b/pyaml/lattice/simulator.py @@ -2,10 +2,9 @@ from .lattice_elements_linker import LatticeElementsLinker from ..configuration import get_root_folder from ..common.element import Element -from pathlib import Path from ..magnet.magnet import Magnet -from pyaml.bpm.bpm import BPM -from pyaml.diagnostics.tune_monitor import BetatronTuneMonitor +from ..bpm.bpm import BPM +from ..diagnostics.tune_monitor import BetatronTuneMonitor from ..magnet.cfm_magnet import CombinedFunctionMagnet from ..rf.rf_plant import RFPlant,RWTotalVoltage from ..rf.rf_transmitter import RFTransmitter @@ -21,6 +20,7 @@ from pydantic import BaseModel,ConfigDict import at +from pathlib import Path # Define the main class name for this module PYAMLCLASS = "Simulator" @@ -142,9 +142,10 @@ def fill_device(self,elements:list[Element]): voltage = RWTotalVoltage(attachedTrans) ne = e.attach(self,frequency,voltage) self.add_rf_plant(ne.get_name(),ne) + elif isinstance(e, BetatronTuneMonitor): betatron_tune = RBetatronTuneArray(self.ring) - e = e.attach(betatron_tune) + e = e.attach(self,betatron_tune) self.add_betatron_tune_monitor(e.get_name(), e) diff --git a/tests/config/EBSOrbit.yaml b/tests/config/EBSOrbit.yaml index 04bdf4e1..fa00863a 100644 --- a/tests/config/EBSOrbit.yaml +++ b/tests/config/EBSOrbit.yaml @@ -919,6 +919,16 @@ instruments: - BPM_C03-09 - BPM_C03-10 devices: + - type: pyaml.diagnostics.tune_monitor + name: BETATRON_TUNE + tune_h: + type: tango.pyaml.attribute_read_only + attribute: sys/ringsimulator/ebs/Tune_h + unit: "" + tune_v: + type: tango.pyaml.attribute_read_only + attribute: sys/ringsimulator/ebs/Tune_v + unit: "" - type: pyaml.rf.rf_plant name: RF masterclock: From f0a5038575c06b72c2acb8ce5bb5bb77e44f4d53 Mon Sep 17 00:00:00 2001 From: PONS Date: Wed, 5 Nov 2025 08:48:08 +0100 Subject: [PATCH 5/8] Improved array error detection --- pyaml/arrays/bpm_array.py | 17 ++++++++++++----- pyaml/arrays/magnet_array.py | 16 +++++++++++----- tests/test_arrays.py | 4 ++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/pyaml/arrays/bpm_array.py b/pyaml/arrays/bpm_array.py index 6abb1552..f47145d8 100644 --- a/pyaml/arrays/bpm_array.py +++ b/pyaml/arrays/bpm_array.py @@ -1,6 +1,7 @@ from ..common.abstract import ReadFloatArray from ..bpm.bpm import BPM from ..control.deviceaccesslist import DeviceAccessList +from ..common.exception import PyAMLException import numpy as np @@ -57,7 +58,7 @@ class BPMArray(list[BPM]): Class that implements access to a BPM array """ - def __init__(self,arrayName:str,bpms:list[BPM],holder = None): + def __init__(self,arrayName:str,bpms:list[BPM],use_aggregator = True): """ Construct a BPM array @@ -66,17 +67,23 @@ def __init__(self,arrayName:str,bpms:list[BPM],holder = None): arrayName : str Array name bpms: list[BPM] - BPM iterator - holder : Element holder - Holder (Simulator or Control System) that contains element of this array used for aggregator + BPM list, all elements must be attached to the same instance of + either a Simulator or a ControlSystem. + use_aggregator : bool + Use aggregator to increase performance by using paralell access to underlying devices. """ + super().__init__(i for i in bpms) + holder = bpms[0]._peer if len(bpms)>0 else None + if holder is None or any([m._peer!=holder for m in bpms]): + raise PyAMLException(f"BPMArray {arrayName} : All elements must be attached to the same instance of either a Simulator or a ControlSystem") + super().__init__(i for i in bpms) self.__name = arrayName self.__hvpos = RWBPMPosition(arrayName,bpms) self.__hpos = RWBPMSinglePosition(arrayName,bpms,0) self.__vpos = RWBPMSinglePosition(arrayName,bpms,1) - if holder is not None: + if use_aggregator: aggs = holder.create_bpm_aggregators(bpms) self.__hvpos.set_aggregator(aggs[0]) self.__hpos.set_aggregator(aggs[1]) diff --git a/pyaml/arrays/magnet_array.py b/pyaml/arrays/magnet_array.py index e70a93cd..63540625 100644 --- a/pyaml/arrays/magnet_array.py +++ b/pyaml/arrays/magnet_array.py @@ -1,6 +1,7 @@ from ..common.abstract import ReadWriteFloatArray from ..magnet.magnet import Magnet from ..common.abstract_aggregator import ScalarAggregator +from ..common.exception import PyAMLException import numpy as np @@ -81,7 +82,7 @@ class MagnetArray(list[Magnet]): Class that implements access to a magnet array """ - def __init__(self,arrayName:str,magnets:list[Magnet],holder = None): + def __init__(self,arrayName:str,magnets:list[Magnet],use_aggregator = True): """ Construct a magnet array @@ -90,16 +91,21 @@ def __init__(self,arrayName:str,magnets:list[Magnet],holder = None): arrayName : str Array name magnets: list[Magnet] - Magnet iterator - holder : Element holder - Holder (Simulator or Control System) that contains element of this array used for aggregator + Magnet list, all elements must be attached to the same instance of + either a Simulator or a ControlSystem. + use_aggregator : bool + Use aggregator to increase performance by using paralell access to underlying devices. """ super().__init__(i for i in magnets) + holder = magnets[0]._peer if len(magnets)>0 else None + if holder is None or any([m._peer!=holder for m in magnets]): + raise PyAMLException(f"MagnetArray {arrayName} : All elements must be attached to the same instance of either a Simulator or a ControlSystem") + self.__name = arrayName self.__rwstrengths = RWMagnetStrength(arrayName,magnets) self.__rwhardwares = RWMagnetHardware(arrayName,magnets) - if holder is not None: + if use_aggregator: aggs = holder.create_magnet_strength_aggregator(magnets) aggh = holder.create_magnet_harddware_aggregator(magnets) self.__rwstrengths.set_aggregator(aggs) diff --git a/tests/test_arrays.py b/tests/test_arrays.py index a79f539c..e82d1887 100644 --- a/tests/test_arrays.py +++ b/tests/test_arrays.py @@ -97,7 +97,7 @@ def test_arrays(install_test_package): mags = [] for m in sr.live.get_magnets("HVCORR"): mags.append(m) - array = MagnetArray("HVCOOR_noagg",mags) # holder = None, no aggregator + array = MagnetArray("HVCOOR_noagg",mags,use_aggregator=False) array.strengths.set([0.000010,-0.000008,0.000015,-0.000017]) ps1 = sr.live.get_magnet("SH1A-C01-H").model.read_hardware_values() ps2 = sr.live.get_magnet("SH1A-C02-H").model.read_hardware_values() @@ -123,7 +123,7 @@ def test_arrays(install_test_package): for b in sr.design.get_bpms("BPMS"): bpms.append(b) - bpms = BPMArray("BPM_noagg",bpms) # holder = None, no aggregator + bpms = BPMArray("BPM_noagg",bpms,use_aggregator=False) pos = bpms.positions.get() assert(np.abs(pos[0][0] + 7.21154171490481e-05)<1e-10) assert(np.abs(pos[0][1] - 3.3988843436571406e-05)<1e-10) From e2038c09d4e46d8f6e71034f2cd57de124bc6b48 Mon Sep 17 00:00:00 2001 From: PONS Date: Wed, 5 Nov 2025 08:53:03 +0100 Subject: [PATCH 6/8] Added repr() methods in dummy CS --- tests/dummy_cs/tango-pyaml/tango/pyaml/attribute.py | 3 +++ tests/dummy_cs/tango-pyaml/tango/pyaml/controlsystem.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/dummy_cs/tango-pyaml/tango/pyaml/attribute.py b/tests/dummy_cs/tango-pyaml/tango/pyaml/attribute.py index 79360f38..ea64ceb0 100644 --- a/tests/dummy_cs/tango-pyaml/tango/pyaml/attribute.py +++ b/tests/dummy_cs/tango-pyaml/tango/pyaml/attribute.py @@ -44,3 +44,6 @@ def readback(self) -> Value: def unit(self) -> str: return self._unit + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) \ No newline at end of file diff --git a/tests/dummy_cs/tango-pyaml/tango/pyaml/controlsystem.py b/tests/dummy_cs/tango-pyaml/tango/pyaml/controlsystem.py index fb32e382..922052fc 100644 --- a/tests/dummy_cs/tango-pyaml/tango/pyaml/controlsystem.py +++ b/tests/dummy_cs/tango-pyaml/tango/pyaml/controlsystem.py @@ -30,3 +30,6 @@ def scalar_aggregator(self) -> str | None: def vector_aggregator(self) -> str | None: return None + + def __repr__(self): + return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) \ No newline at end of file From 4fbd544c28e2d0096f95c75164e748c3d5cec151 Mon Sep 17 00:00:00 2001 From: PONS Date: Wed, 5 Nov 2025 09:29:34 +0100 Subject: [PATCH 7/8] Fixed for peer printing --- pyaml/bpm/bpm.py | 2 +- pyaml/diagnostics/tune_monitor.py | 2 +- pyaml/magnet/magnet.py | 4 ++-- pyaml/rf/rf_plant.py | 2 +- pyaml/rf/rf_transmitter.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyaml/bpm/bpm.py b/pyaml/bpm/bpm.py index 39791440..131e6e0f 100644 --- a/pyaml/bpm/bpm.py +++ b/pyaml/bpm/bpm.py @@ -76,4 +76,4 @@ def attach(self, peer, positions: RBpmArray , offset: RWBpmOffsetArray, return obj def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ") diff --git a/pyaml/diagnostics/tune_monitor.py b/pyaml/diagnostics/tune_monitor.py index c99ce647..10ec5c1a 100644 --- a/pyaml/diagnostics/tune_monitor.py +++ b/pyaml/diagnostics/tune_monitor.py @@ -48,5 +48,5 @@ def attach(self, peer, betatron_tune: ReadFloatArray) -> Self: return obj def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ") diff --git a/pyaml/magnet/magnet.py b/pyaml/magnet/magnet.py index 093dcb13..6559357d 100644 --- a/pyaml/magnet/magnet.py +++ b/pyaml/magnet/magnet.py @@ -89,10 +89,10 @@ def set_model_name(self, name:str): self.__modelName = name def __str__(self): - return "%s(name='%s', model='%s', peer='%s', magnet_model=%s)" % ( + return "%s(peer='%s', name='%s', model='%s', magnet_model=%s)" % ( self.__class__.__name__, + self.get_peer(), self.get_name(), self.__modelName, - self.get_peer(), repr(self.__model) ) diff --git a/pyaml/rf/rf_plant.py b/pyaml/rf/rf_plant.py index 0a28aa10..b6c32ecb 100644 --- a/pyaml/rf/rf_plant.py +++ b/pyaml/rf/rf_plant.py @@ -53,7 +53,7 @@ def attach(self, peer, frequency: abstract.ReadWriteFloatScalar, voltage: abstra return obj def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ") class RWTotalVoltage(abstract.ReadWriteFloatScalar): diff --git a/pyaml/rf/rf_transmitter.py b/pyaml/rf/rf_transmitter.py index 8abee386..371b716d 100644 --- a/pyaml/rf/rf_transmitter.py +++ b/pyaml/rf/rf_transmitter.py @@ -61,4 +61,4 @@ def attach(self, peer, voltage: abstract.ReadWriteFloatScalar, phase: abstract.R return obj def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ") From eeb6d4bf2c8cfd5076c733563642c8d3f7f71034 Mon Sep 17 00:00:00 2001 From: PONS Date: Wed, 5 Nov 2025 10:37:44 +0100 Subject: [PATCH 8/8] Factorized __repr__ --- pyaml/bpm/bpm.py | 4 +--- pyaml/bpm/bpm_simple_model.py | 5 ++++- pyaml/bpm/bpm_tiltoffset_model.py | 5 +++++ pyaml/common/element.py | 25 ++++++++++++++++++++----- pyaml/diagnostics/tune_monitor.py | 3 --- pyaml/magnet/cfm_magnet.py | 5 +++-- pyaml/magnet/identity_cfm_model.py | 3 ++- pyaml/magnet/identity_model.py | 3 ++- pyaml/magnet/linear_cfm_model.py | 4 +++- pyaml/magnet/linear_model.py | 4 +++- pyaml/magnet/spline_model.py | 8 +++----- pyaml/rf/rf_plant.py | 3 --- pyaml/rf/rf_transmitter.py | 3 --- 13 files changed, 46 insertions(+), 29 deletions(-) diff --git a/pyaml/bpm/bpm.py b/pyaml/bpm/bpm.py index 131e6e0f..dd016c3a 100644 --- a/pyaml/bpm/bpm.py +++ b/pyaml/bpm/bpm.py @@ -74,6 +74,4 @@ def attach(self, peer, positions: RBpmArray , offset: RWBpmOffsetArray, obj.__tilt = tilt obj._peer = peer return obj - - def __repr__(self): - return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ") + \ No newline at end of file diff --git a/pyaml/bpm/bpm_simple_model.py b/pyaml/bpm/bpm_simple_model.py index 069d97e7..5365526e 100644 --- a/pyaml/bpm/bpm_simple_model.py +++ b/pyaml/bpm/bpm_simple_model.py @@ -2,6 +2,8 @@ from pydantic import BaseModel,ConfigDict import numpy as np from ..control.deviceaccess import DeviceAccess +from ..common.element import __pyaml_repr__ + from numpy.typing import NDArray # Define the main class name for this module PYAMLCLASS = "BPMSimpleModel" @@ -115,4 +117,5 @@ def get_offset_devices(self) -> list[DeviceAccess]: return [] def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return __pyaml_repr__(self) + diff --git a/pyaml/bpm/bpm_tiltoffset_model.py b/pyaml/bpm/bpm_tiltoffset_model.py index a70bb59d..92bb2b0f 100644 --- a/pyaml/bpm/bpm_tiltoffset_model.py +++ b/pyaml/bpm/bpm_tiltoffset_model.py @@ -3,6 +3,8 @@ from pydantic import BaseModel,ConfigDict import numpy as np from ..control.deviceaccess import DeviceAccess +from ..common.element import __pyaml_repr__ + from numpy.typing import NDArray # Define the main class name for this module PYAMLCLASS = "BPMTiltOffsetModel" @@ -113,3 +115,6 @@ def get_offset_devices(self) -> list[DeviceAccess]: Array of DeviceAcess """ return [self.__x_offset,self.__y_offset] + + def __repr__(self): + return __pyaml_repr__(self) diff --git a/pyaml/common/element.py b/pyaml/common/element.py index 0d8c8e76..c5b94d4d 100644 --- a/pyaml/common/element.py +++ b/pyaml/common/element.py @@ -2,6 +2,21 @@ from pydantic import BaseModel,ConfigDict + +def __pyaml_repr__(obj): + """ + Returns a string representation of a pyaml object + """ + if hasattr(obj,"_cfg"): + if isinstance(obj,Element): + return repr(obj._cfg).replace("ConfigModel(",obj.__class__.__name__ + "(peer='" + obj.get_peer() + "', ") + else: + # no peer + return repr(obj._cfg).replace("ConfigModel",obj.__class__.__name__ ) + else: + # Default to repr + return repr(obj) + class ElementConfigModel(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True,extra="forbid") @@ -47,8 +62,8 @@ def get_peer(self) -> str: return "None" if self._peer is None else f"{self._peer.__class__.__name__}:{self._peer.name()}" def __repr__(self): - return "%s(name='%s', peer='%s')" % ( - self.__class__.__name__, - self.__name, - self.get_peer() - ) + return __pyaml_repr__(self) + + + + \ No newline at end of file diff --git a/pyaml/diagnostics/tune_monitor.py b/pyaml/diagnostics/tune_monitor.py index 10ec5c1a..613783ab 100644 --- a/pyaml/diagnostics/tune_monitor.py +++ b/pyaml/diagnostics/tune_monitor.py @@ -47,6 +47,3 @@ def attach(self, peer, betatron_tune: ReadFloatArray) -> Self: obj._peer = peer return obj - def __repr__(self): - return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ") - diff --git a/pyaml/magnet/cfm_magnet.py b/pyaml/magnet/cfm_magnet.py index 443dffcf..100499aa 100644 --- a/pyaml/magnet/cfm_magnet.py +++ b/pyaml/magnet/cfm_magnet.py @@ -1,6 +1,6 @@ from .model import MagnetModel -from ..common.element import Element,ElementConfigModel +from ..common.element import Element,ElementConfigModel,__pyaml_repr__ from ..common import abstract from ..common.abstract import RWMapper from ..common.exception import PyAMLException @@ -87,4 +87,5 @@ def set_energy(self,E:float): self.model.set_magnet_rigidity(E/speed_of_light) def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return __pyaml_repr__(self) + diff --git a/pyaml/magnet/identity_cfm_model.py b/pyaml/magnet/identity_cfm_model.py index 9f951dfe..d200a14e 100644 --- a/pyaml/magnet/identity_cfm_model.py +++ b/pyaml/magnet/identity_cfm_model.py @@ -4,6 +4,7 @@ from .model import MagnetModel from .. import PyAMLException from ..control.deviceaccess import DeviceAccess +from ..common.element import __pyaml_repr__ # Define the main class name for this module PYAMLCLASS = "IdentityCFMagnetModel" @@ -88,4 +89,4 @@ def has_hardware(self) -> bool: return self._cfg.powerconverters is not None def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return __pyaml_repr__(self) diff --git a/pyaml/magnet/identity_model.py b/pyaml/magnet/identity_model.py index d45721f0..dfcade7e 100644 --- a/pyaml/magnet/identity_model.py +++ b/pyaml/magnet/identity_model.py @@ -1,6 +1,7 @@ from .model import MagnetModel from .. import PyAMLException from ..control.deviceaccess import DeviceAccess +from ..common.element import __pyaml_repr__ import numpy as np from pydantic import BaseModel,ConfigDict @@ -70,4 +71,4 @@ def has_hardware(self) -> bool: return self._cfg.powerconverter is not None def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return __pyaml_repr__(self) diff --git a/pyaml/magnet/linear_cfm_model.py b/pyaml/magnet/linear_cfm_model.py index db4b39e4..1ece0abb 100644 --- a/pyaml/magnet/linear_cfm_model.py +++ b/pyaml/magnet/linear_cfm_model.py @@ -3,6 +3,7 @@ from ..control.deviceaccess import DeviceAccess from .model import MagnetModel from ..common.exception import PyAMLException +from ..common.element import __pyaml_repr__ from pydantic import BaseModel,ConfigDict import numpy as np @@ -164,4 +165,5 @@ def has_hardware(self) -> bool: return (self.__nbPS == self.__nbFunction) and np.allclose(self.__matrix, np.eye(self.__nbFunction)) def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return __pyaml_repr__(self) + diff --git a/pyaml/magnet/linear_model.py b/pyaml/magnet/linear_model.py index fb499207..9afc990d 100644 --- a/pyaml/magnet/linear_model.py +++ b/pyaml/magnet/linear_model.py @@ -1,6 +1,7 @@ from .model import MagnetModel from ..configuration.curve import Curve from ..control.deviceaccess import DeviceAccess +from ..common.element import __pyaml_repr__ import numpy as np from pydantic import BaseModel,ConfigDict @@ -76,5 +77,6 @@ def set_magnet_rigidity(self, brho: np.double): self.__brho = brho def __repr__(self): - return repr(self._cfg).replace("ConfigModel",self.__class__.__name__) + return __pyaml_repr__(self) + \ No newline at end of file diff --git a/pyaml/magnet/spline_model.py b/pyaml/magnet/spline_model.py index 0b0f6c48..b926a99d 100644 --- a/pyaml/magnet/spline_model.py +++ b/pyaml/magnet/spline_model.py @@ -5,6 +5,7 @@ from .model import MagnetModel from ..configuration.curve import Curve from ..control.deviceaccess import DeviceAccess +from ..common.element import __pyaml_repr__ # Define the main class name for this module PYAMLCLASS = "SplineMagnetModel" @@ -77,8 +78,5 @@ def set_magnet_rigidity(self, brho: np.double): self.__brho = brho def __repr__(self): - return "%s(curve[%d], unit=%s)" % ( - self.__class__.__name__, - len(self.__curve), - self.__strength_unit, - ) + return __pyaml_repr__(self) + diff --git a/pyaml/rf/rf_plant.py b/pyaml/rf/rf_plant.py index b6c32ecb..ae10371c 100644 --- a/pyaml/rf/rf_plant.py +++ b/pyaml/rf/rf_plant.py @@ -52,9 +52,6 @@ def attach(self, peer, frequency: abstract.ReadWriteFloatScalar, voltage: abstra obj._peer = peer return obj - def __repr__(self): - return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ") - class RWTotalVoltage(abstract.ReadWriteFloatScalar): def __init__(self, transmitters: list[RFTransmitter]): diff --git a/pyaml/rf/rf_transmitter.py b/pyaml/rf/rf_transmitter.py index 371b716d..63ded2e9 100644 --- a/pyaml/rf/rf_transmitter.py +++ b/pyaml/rf/rf_transmitter.py @@ -59,6 +59,3 @@ def attach(self, peer, voltage: abstract.ReadWriteFloatScalar, phase: abstract.R obj.__phase = phase obj._peer = peer return obj - - def __repr__(self): - return repr(self._cfg).replace("ConfigModel(",self.__class__.__name__ + "(peer='" + self.get_peer() + "', ")