Skip to content

Commit 45d94d2

Browse files
authored
Merge pull request #25 from python-accelerator-middle-layer/4-implement-magnet-hardware-link
4 implement magnet hardware link
2 parents bc26a3a + 126032e commit 45d94d2

28 files changed

+488
-81
lines changed

pyaml/arrays/magnet_array.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def unit(self) -> list[str]:
102102
def set_aggregator(self,agg:DeviceAccessList):
103103
self.aggregator = agg
104104
for m in self.__magnets:
105-
self.hasHardwareMapping |= m.model.hasHardwareMapping()
105+
self.hasHardwareMapping |= m.model.has_hardware()
106106

107107
class MagnetArray(list[Magnet]):
108108
"""

pyaml/control/abstract_impl.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1+
from numpy import double
2+
13
from pyaml.control import abstract
24
from pyaml.magnet.model import MagnetModel
35
import numpy as np
46

57
#------------------------------------------------------------------------------
68

7-
class RWHardwareScalar(abstract.ReadFloatScalar):
9+
class RWHardwareScalar(abstract.ReadWriteFloatScalar):
810
"""
911
Class providing read write access to a magnet of a control system (in hardware units)
1012
"""
13+
1114
def __init__(self, model:MagnetModel):
12-
self.model = model
15+
self.__model = model
1316

1417
def get(self) -> float:
15-
return self.model.read_hardware_values()[0]
18+
return self.__model.read_hardware_values()[0]
1619

1720
def set(self, value:float):
18-
self.model.send_harware_values([value])
19-
21+
self.__model.send_hardware_values(np.array([value]))
22+
23+
def set_and_wait(self, value: double):
24+
raise NotImplementedError("Not implemented yet.")
25+
2026
def unit(self) -> str:
21-
return self.model.get_hardware_units()[0]
27+
return self.__model.get_hardware_units()[0]
2228

2329
def index(self) -> int:
2430
return 0
@@ -36,12 +42,12 @@ def __init__(self, model:MagnetModel):
3642
# Gets the value
3743
def get(self) -> float:
3844
currents = self.__model.read_hardware_values()
39-
return self.__model.compute_strengths(currents)[0]
45+
return self.__model.compute_strengths(currents)
4046

4147
# Sets the value
4248
def set(self, value:float):
4349
current = self.__model.compute_hardware_values([value])
44-
self.__model.send_harware_values(current)
50+
self.__model.send_hardware_values(current)
4551

4652
# Sets the value and wait that the read value reach the setpoint
4753
def set_and_wait(self, value:float):
@@ -61,15 +67,15 @@ class RWHardwareArray(abstract.ReadWriteFloatArray):
6167
Class providing read write access to a magnet array of a control system (in hardware units)
6268
"""
6369
def __init__(self, model:MagnetModel):
64-
self.model = model
70+
self.__model = model
6571

6672
# Gets the value
6773
def get(self) -> np.array:
68-
return self.model.read_hardware_values()
74+
return self.__model.read_hardware_values()
6975

7076
# Sets the value
7177
def set(self, value:np.array):
72-
self.model.send_harware_values(value)
78+
self.__model.send_hardware_values(value)
7379

7480
# Sets the value and waits that the read value reach the setpoint
7581
def set_and_wait(self, value:np.array):
@@ -78,7 +84,7 @@ def set_and_wait(self, value:np.array):
7884

7985
# Gets the unit of the value
8086
def unit(self) -> list[str]:
81-
return self.model.get_hardware_units()
87+
return self.__model.get_hardware_units()
8288

8389
#------------------------------------------------------------------------------
8490

@@ -87,26 +93,26 @@ class RWStrengthArray(abstract.ReadWriteFloatArray):
8793
Class providing read write access to magnet strengths of a control system
8894
"""
8995
def __init__(self, model:MagnetModel):
90-
self.model = model
96+
self.__model = model
9197

9298
# Gets the value
9399
def get(self) -> np.array:
94-
r = self.model.read_hardware_values()
95-
str = self.model.compute_strengths(r)
100+
r = self.__model.read_hardware_values()
101+
str = self.__model.compute_strengths(r)
96102
return str
97103

98104
# Sets the value
99105
def set(self, value:np.array):
100-
cur = self.model.compute_hardware_values(value)
101-
self.model.send_harware_values(cur)
102-
106+
cur = self.__model.compute_hardware_values(value)
107+
self.__model.send_hardware_values(cur)
108+
103109
# Sets the value and waits that the read value reach the setpoint
104110
def set_and_wait(self, value:np.array):
105111
raise NotImplementedError("Not implemented yet.")
106112

107113
# Gets the unit of the value
108114
def unit(self) -> list[str]:
109-
return self.model.get_strength_units()
115+
return self.__model.get_strength_units()
110116

111117

112118

pyaml/control/controlsystem.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ def fill_device(self,elements:list[Element]):
4646
"""
4747
for e in elements:
4848
if isinstance(e,Magnet):
49-
current = RWHardwareScalar(e.model)
50-
strength = RWStrengthScalar(e.model)
49+
current = RWHardwareScalar(e.model) if e.model.has_hardware() else None
50+
strength = RWStrengthScalar(e.model) if e.model.has_physics() else None
5151
# Create a unique ref for this control system
52-
m = e.attach(strength,current)
52+
m = e.attach(strength, current)
5353
self.add_magnet(str(m),m)
5454
elif isinstance(e,CombinedFunctionMagnet):
5555
self.add_magnet(str(e),e)
56-
currents = RWHardwareArray(e.model)
57-
strengths = RWStrengthArray(e.model)
56+
currents = RWHardwareArray(e.model) if e.model.has_hardware() else None
57+
strengths = RWStrengthArray(e.model) if e.model.has_physics() else None
5858
# Create unique refs of each function for this control system
5959
ms = e.attach(strengths,currents)
6060
for m in ms:

pyaml/lattice/simulator.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,17 @@ def set_energy(self,E:float):
5252

5353
def fill_device(self,elements:list[Element]):
5454
for e in elements:
55+
# Need conversion to physics unit to work with simulator
5556
if isinstance(e,Magnet):
56-
current = RWHardwareScalar(self.get_at_elems(e.name),e.polynom,e.model)
57-
strength = RWStrengthScalar(self.get_at_elems(e.name),e.polynom,e.model)
57+
current = RWHardwareScalar(self.get_at_elems(e.name),e.polynom,e.model) if e.model.has_physics() else None
58+
strength = RWStrengthScalar(self.get_at_elems(e.name),e.polynom,e.model) if e.model.has_physics() else None
5859
# Create a unique ref for this simulator
5960
m = e.attach(strength,current)
6061
self.add_magnet(str(m),m)
6162
elif isinstance(e,CombinedFunctionMagnet):
6263
self.add_magnet(str(e),e)
63-
currents = RWHardwareArray(self.get_at_elems(e.name),e.polynoms,e.model)
64-
strengths = RWStrengthArray(self.get_at_elems(e.name),e.polynoms,e.model)
64+
currents = RWHardwareArray(self.get_at_elems(e.name),e.polynoms,e.model) if e.model.has_physics() else None
65+
strengths = RWStrengthArray(self.get_at_elems(e.name),e.polynoms,e.model) if e.model.has_physics() else None
6566
# Create unique refs of each function for this simulator
6667
ms = e.attach(strengths,currents)
6768
for m in ms:

pyaml/magnet/cfm_magnet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def attach(self, strengths: abstract.ReadWriteFloatArray, hardwares: abstract.Re
6868
args = {"name":m[1]}
6969
mclass:Magnet = _fmap[m[0]](ElementConfigModel(**args))
7070
strength = RWMapper(strengths,idx)
71-
hardware = RWMapper(hardwares,idx) if self.model.hasHardwareMapping() else None
71+
hardware = RWMapper(hardwares,idx) if self.model.has_hardware() else None
7272
l.append(mclass.attach(strength,hardware))
7373
return l
7474

pyaml/magnet/hcorrector.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class HCorrector(Magnet):
1313
def __init__(self, cfg: ConfigModel):
1414
super().__init__(
1515
cfg.name,
16-
cfg.hardware if hasattr(cfg, "hardware") else None,
1716
cfg.model if hasattr(cfg, "model") else None,
1817
)
1918
self._cfg = cfg

pyaml/magnet/identity_cfm_model.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import numpy as np
2+
from pydantic import BaseModel,ConfigDict
3+
4+
from .model import MagnetModel
5+
from .. import PyAMLException
6+
from ..configuration.curve import Curve
7+
from ..control.deviceaccess import DeviceAccess
8+
9+
# Define the main class name for this module
10+
PYAMLCLASS = "IdentityCFMagnetModel"
11+
12+
class ConfigModel(BaseModel):
13+
14+
model_config = ConfigDict(arbitrary_types_allowed=True,extra="forbid")
15+
16+
multipoles: list[str]
17+
"""List of supported functions: A0,B0,A1,B1,etc (i.e. [B0,A1,B2])"""
18+
powerconverters: list[DeviceAccess] | None = None
19+
"""Power converter device to apply current"""
20+
physics: list[DeviceAccess] | None = None
21+
"""Magnet device to apply strength"""
22+
units: list[str]
23+
"""List of strength unit (i.e. ['rad','m-1','m-2'])"""
24+
25+
class IdentityCFMagnetModel(MagnetModel):
26+
"""
27+
Class that map values to underlying devices without conversion
28+
"""
29+
30+
def __init__(self, cfg: ConfigModel):
31+
self._cfg = cfg
32+
33+
# Check config
34+
self.__nbFunction: int = len(cfg.multipoles)
35+
36+
if cfg.physics is None and cfg.powerconverter is None:
37+
raise Exception("Invalid IdentityCFMagnetModel configuration, physics or powerconverters device required")
38+
if cfg.physics is not None and cfg.powerconverter is not None:
39+
raise Exception("Invalid IdentityCFMagnetModel configuration, physics or powerconverters device required but not both")
40+
if cfg.physics:
41+
self.__devices = cfg.physics
42+
else:
43+
self.__devices = cfg.powerconverter
44+
45+
self.__nbDev: int = len(self.__devices)
46+
47+
self.__check_len(cfg.units,"units",self.__nbFunction)
48+
49+
def __check_len(self,obj,name,expected_len):
50+
lgth = len(obj)
51+
if lgth != expected_len:
52+
raise Exception(
53+
f"{name} does not have the expected "
54+
f"number of items ({expected_len} items expected but got {lgth})"
55+
)
56+
57+
def compute_hardware_values(self, strengths: np.array) -> np.array:
58+
return strengths
59+
60+
def compute_strengths(self, currents: np.array) -> np.array:
61+
return currents
62+
63+
def get_strength_units(self) -> list[str]:
64+
return self._cfg.units
65+
66+
def get_hardware_units(self) -> list[str]:
67+
return self._cfg.units
68+
69+
def read_hardware_values(self) -> np.array:
70+
return np.array([p.get() for p in self.__devices])
71+
72+
def readback_hardware_values(self) -> np.array:
73+
return np.array([p.readback() for p in self.__devices])
74+
75+
def send_hardware_values(self, currents: np.array):
76+
for idx, p in enumerate(self.__devices):
77+
p.set(currents[idx])
78+
79+
def get_devices(self) -> list[DeviceAccess]:
80+
return self.__devices
81+
82+
def set_magnet_rigidity(self, brho: np.double):
83+
pass
84+
85+
def has_physics(self) -> bool:
86+
return self._cfg.physics is not None
87+
88+
def has_hardware(self) -> bool:
89+
return self._cfg.powerconverters is not None

pyaml/magnet/identity_model.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import numpy as np
2+
from pydantic import BaseModel,ConfigDict
3+
4+
from .model import MagnetModel
5+
from .. import PyAMLException
6+
from ..configuration.curve import Curve
7+
from ..control.deviceaccess import DeviceAccess
8+
9+
# Define the main class name for this module
10+
PYAMLCLASS = "IdentityMagnetModel"
11+
12+
class ConfigModel(BaseModel):
13+
14+
model_config = ConfigDict(arbitrary_types_allowed=True,extra="forbid")
15+
16+
powerconverter: DeviceAccess|None = None
17+
"""Power converter device to apply current"""
18+
physics: DeviceAccess|None = None
19+
"""Magnet device to apply strength"""
20+
unit: str
21+
"""Unit of the strength (i.e. 1/m or m-1)"""
22+
23+
class IdentityMagnetModel(MagnetModel):
24+
"""
25+
Class that map value to underlying device without conversion
26+
"""
27+
28+
def __init__(self, cfg: ConfigModel):
29+
self._cfg = cfg
30+
self.__unit = cfg.unit
31+
if cfg.physics is None and cfg.powerconverter is None:
32+
raise Exception("Invalid IdentityMagnetModel configuration, physics or powerconverter device required")
33+
if cfg.physics is not None and cfg.powerconverter is not None:
34+
raise Exception("Invalid IdentityMagnetModel configuration, physics or powerconverter device required but not both")
35+
if cfg.physics:
36+
self.__device = cfg.physics
37+
else:
38+
self.__device = cfg.powerconverter
39+
40+
def compute_hardware_values(self, strengths: np.array) -> np.array:
41+
return strengths
42+
43+
def compute_strengths(self, currents: np.array) -> np.array:
44+
return currents
45+
46+
def get_strength_units(self) -> list[str]:
47+
return [self.__unit]
48+
49+
def get_hardware_units(self) -> list[str]:
50+
return [self.__unit]
51+
52+
def read_hardware_values(self) -> np.array:
53+
return [self.__device.get()]
54+
55+
def readback_hardware_values(self) -> np.array:
56+
return [self.__device.readback()]
57+
58+
def send_hardware_values(self, currents: np.array):
59+
self.__device.set(currents[0])
60+
61+
def get_devices(self) -> list[DeviceAccess]:
62+
return [self.__device]
63+
64+
def set_magnet_rigidity(self, brho: np.double):
65+
pass
66+
67+
def has_physics(self) -> bool:
68+
return self._cfg.physics is not None
69+
70+
def has_hardware(self) -> bool:
71+
return self._cfg.powerconverter is not None
72+
73+
def __repr__(self):
74+
return "%s(unit=%s)" % (
75+
self.__class__.__name__,
76+
self.__unit,
77+
)

pyaml/magnet/linear_cfm_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def read_hardware_values(self) -> np.array:
149149
def readback_hardware_values(self) -> np.array:
150150
return np.array([p.readback() for p in self._cfg.powerconverters])
151151

152-
def send_harware_values(self, currents: np.array):
152+
def send_hardware_values(self, currents: np.array):
153153
for idx, p in enumerate(self._cfg.powerconverters):
154154
p.set(currents[idx])
155155

@@ -159,6 +159,6 @@ def get_devices(self) -> list[DeviceAccess]:
159159
def set_magnet_rigidity(self, brho: np.double):
160160
self._brho = brho
161161

162-
def hasHardwareMapping(self) -> bool:
162+
def has_hardware(self) -> bool:
163163
return (self.__nbPS == self.__nbFunction) and np.allclose(self.__matrix, np.eye(self.__nbFunction))
164164

pyaml/magnet/linear_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def read_hardware_values(self) -> np.array:
6666
def readback_hardware_values(self) -> np.array:
6767
return [self.__ps.readback()]
6868

69-
def send_harware_values(self, currents: np.array):
69+
def send_hardware_values(self, currents: np.array):
7070
self.__ps.set(currents[0])
7171

7272
def get_devices(self) -> list[DeviceAccess]:

0 commit comments

Comments
 (0)