Skip to content

Commit ce64d90

Browse files
committed
Added tests
1 parent f314a54 commit ce64d90

File tree

12 files changed

+310
-137
lines changed

12 files changed

+310
-137
lines changed

pyaml/arrays/bpm_array.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,22 @@ class RWBPMPosition(ReadFloatArray):
99
def __init__(self, name:str, bpms:list[BPM]):
1010
self.__bpms = bpms
1111
self.__name = name
12-
self.aggregator:DeviceAccessList = None
12+
self.__aggregator:DeviceAccessList = None
1313

1414
# Gets the values
1515
def get(self) -> np.array:
16-
if not self.aggregator:
16+
if not self.__aggregator:
1717
return np.array([b.positions.get() for b in self.__bpms])
1818
else:
19-
return self.aggregator.get().reshape(len(self.__bpms),2)
19+
return self.__aggregator.get().reshape(len(self.__bpms),2)
2020

2121
# Gets the unit of the values
2222
def unit(self) -> list[str]:
2323
return [b.positions.unit() for b in self.__bpms]
2424

2525
# Set the aggregator (Control system only)
2626
def set_aggregator(self,agg:DeviceAccessList):
27-
self.aggregator = agg
27+
self.__aggregator = agg
2828

2929

3030
class RWBPMSinglePosition(ReadFloatArray):
@@ -33,22 +33,22 @@ def __init__(self, name:str, bpms:list[BPM],idx: int):
3333
self.__bpms = bpms
3434
self.__name = name
3535
self.__idx = idx
36-
self.aggregator:DeviceAccessList = None
36+
self.__aggregator:DeviceAccessList = None
3737

3838
# Gets the values
3939
def get(self) -> np.array:
40-
if not self.aggregator:
40+
if not self.__aggregator:
4141
return np.array([b.positions.get()[self.__idx] for b in self.__bpms])
4242
else:
43-
return self.aggregator.get()
43+
return self.__aggregator.get()
4444

4545
# Gets the unit of the values
4646
def unit(self) -> list[str]:
4747
return [b.positions.unit() for b in self.__bpms]
4848

4949
# Set the aggregator (Control system only)
5050
def set_aggregator(self,agg:DeviceAccessList):
51-
self.aggregator = agg
51+
self.__aggregator = agg
5252

5353

5454

@@ -57,7 +57,7 @@ class BPMArray(list[BPM]):
5757
Class that implements access to a BPM array
5858
"""
5959

60-
def __init__(self,arrayName:str,bpms:list[BPM],holder):
60+
def __init__(self,arrayName:str,bpms:list[BPM],holder = None):
6161
"""
6262
Construct a BPM array
6363
@@ -68,34 +68,35 @@ def __init__(self,arrayName:str,bpms:list[BPM],holder):
6868
bpms: list[BPM]
6969
BPM iterator
7070
holder : Element holder
71-
Holder that contains element of this array (Simulator or Control System)
71+
Holder (Simulator or Control System) that contains element of this array used for aggregator
7272
"""
7373
super().__init__(i for i in bpms)
7474
self.__name = arrayName
7575
self.__hvpos = RWBPMPosition(arrayName,bpms)
7676
self.__hpos = RWBPMSinglePosition(arrayName,bpms,0)
7777
self.__vpos = RWBPMSinglePosition(arrayName,bpms,1)
7878

79-
aggs = holder.create_bpm_aggregators(bpms)
80-
self.__hvpos.set_aggregator(aggs[0])
81-
self.__hpos.set_aggregator(aggs[1])
82-
self.__vpos.set_aggregator(aggs[2])
79+
if holder is not None:
80+
aggs = holder.create_bpm_aggregators(bpms)
81+
self.__hvpos.set_aggregator(aggs[0])
82+
self.__hpos.set_aggregator(aggs[1])
83+
self.__vpos.set_aggregator(aggs[2])
8384

84-
@property
85+
@property
8586
def positions(self) -> RWBPMPosition:
8687
"""
8788
Give access to bpm posttions of each bpm of this array
8889
"""
8990
return self.__hvpos
9091

91-
@property
92+
@property
9293
def h(self) -> RWBPMSinglePosition:
9394
"""
9495
Give access to bpm H posttions of each bpm of this array
9596
"""
9697
return self.__hpos
9798

98-
@property
99+
@property
99100
def v(self) -> RWBPMSinglePosition:
100101
"""
101102
Give access to bpm V posttions of each bpm of this array

pyaml/arrays/magnet_array.py

Lines changed: 25 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -7,48 +7,26 @@
77
class RWMagnetStrength(ReadWriteFloatArray):
88

99
def __init__(self, name:str, magnets:list[Magnet]):
10-
self.__magnets = magnets
1110
self.__name = name
12-
self.aggregator:ScalarAggregator = None
11+
self.__magnets = magnets
12+
self.__nb = len(self.__magnets)
13+
self.__aggregator:ScalarAggregator = None
1314

1415
# Gets the values
1516
def get(self) -> np.array:
16-
if not self.aggregator:
17+
if not self.__aggregator:
1718
return np.array([m.strength.get() for m in self.__magnets])
1819
else:
19-
allHardwareValues = self.aggregator.get() # Read all hardware setpoints
20-
allStrength = np.zeros(len(self.__magnets))
21-
mIdx = 0
22-
idx = 0
23-
for m in self.__magnets:
24-
nbDev = len(m.model.get_devices())
25-
allStrength[mIdx] = m.model.compute_strengths(allHardwareValues[idx:idx+nbDev])[m.strength.index()]
26-
mIdx += 1
27-
idx += nbDev
28-
return allStrength
20+
return self.__aggregator.get()
2921

3022
# Sets the values
3123
def set(self, value:np.array):
32-
if not self.aggregator:
24+
nvalue = np.ones(self.__nb) * value if isinstance(value,float) else value
25+
if not self.__aggregator:
3326
for idx,m in enumerate(self.__magnets):
34-
m.strength.set(value[idx])
27+
m.strength.set(nvalue[idx])
3528
else:
36-
# TODO: if the array does not contains mappings to combined function
37-
# magnets, the algorithm below can be optimized
38-
allHardwareValues = self.aggregator.get() # Read all hardware setpoints
39-
newHardwareValues = np.zeros(len(self.aggregator))
40-
mIdx = 0
41-
idx = 0
42-
for m in self.__magnets:
43-
# m is a single function magnet or a mapping to a
44-
# combined function magnet (RWMapper)
45-
nbDev = len(m.model.get_devices())
46-
mStrengths = m.model.compute_strengths( allHardwareValues[idx:idx+nbDev] )
47-
mStrengths[m.strength.index()] = value[mIdx]
48-
newHardwareValues[idx:idx+nbDev] = m.model.compute_hardware_values(mStrengths)
49-
mIdx += 1
50-
idx += nbDev
51-
self.aggregator.set(newHardwareValues)
29+
self.__aggregator.set(nvalue)
5230

5331
# Sets the values and waits that the read values reach their setpoint
5432
def set_and_wait(self, value:np.array):
@@ -60,36 +38,31 @@ def unit(self) -> list[str]:
6038

6139
# Set the aggregator (Control system only)
6240
def set_aggregator(self,agg:ScalarAggregator):
63-
self.aggregator = agg
41+
self.__aggregator = agg
6442

6543
class RWMagnetHardware(ReadWriteFloatArray):
6644

6745
def __init__(self, name:str, magnets:list[Magnet]):
6846
self.__name = name
6947
self.__magnets = magnets
70-
self.aggregator:ScalarAggregator = None
71-
self.hasHardwareMapping = True
48+
self.__nb = len(self.__magnets)
49+
self.__aggregator:ScalarAggregator = None
7250

7351
# Gets the values
7452
def get(self) -> np.array:
75-
if not self.aggregator:
53+
if not self.__aggregator:
7654
return np.array([m.hardware.get() for m in self.__magnets])
7755
else:
78-
if not self.hasHardwareMapping:
79-
raise Exception(f"Array {self.__name} contains elements that that do not support hardware units")
80-
else:
81-
return self.aggregator.get()
56+
return self.__aggregator.get()
8257

8358
# Sets the values
8459
def set(self, value:np.array):
85-
if not self.aggregator:
60+
nvalue = np.ones(self.__nb) * value if isinstance(value,float) else value
61+
if not self.__aggregator:
8662
for idx,m in enumerate(self.__magnets):
8763
m.hardware.set(value[idx])
8864
else:
89-
if not self.hasHardwareMapping:
90-
raise Exception(f"Array {self.__name} contains elements that that do not support hardware units")
91-
else:
92-
self.aggregator.set(value)
65+
self.__aggregator.set(value)
9366

9467
# Sets the values and waits that the read values reach their setpoint
9568
def set_and_wait(self, value:np.array):
@@ -101,16 +74,14 @@ def unit(self) -> list[str]:
10174

10275
# Set the aggregator
10376
def set_aggregator(self,agg:ScalarAggregator):
104-
self.aggregator = agg
105-
for m in self.__magnets:
106-
self.hasHardwareMapping |= m.model.has_hardware()
77+
self.__aggregator = agg
10778

10879
class MagnetArray(list[Magnet]):
10980
"""
11081
Class that implements access to a magnet array
11182
"""
11283

113-
def __init__(self,arrayName:str,magnets:list[Magnet],holder):
84+
def __init__(self,arrayName:str,magnets:list[Magnet],holder = None):
11485
"""
11586
Construct a magnet array
11687
@@ -121,16 +92,18 @@ def __init__(self,arrayName:str,magnets:list[Magnet],holder):
12192
magnets: list[Magnet]
12293
Magnet iterator
12394
holder : Element holder
124-
Holder that contains element of this array (Simulator or Control System)
95+
Holder (Simulator or Control System) that contains element of this array used for aggregator
12596
"""
12697
super().__init__(i for i in magnets)
12798
self.__name = arrayName
12899
self.__rwstrengths = RWMagnetStrength(arrayName,magnets)
129100
self.__rwhardwares = RWMagnetHardware(arrayName,magnets)
130101

131-
agg = holder.create_magnet_aggregator(magnets)
132-
self.__rwstrengths.set_aggregator(agg)
133-
self.__rwhardwares.set_aggregator(agg)
102+
if holder is not None:
103+
aggs = holder.create_magnet_strength_aggregator(magnets)
104+
aggh = holder.create_magnet_harddware_aggregator(magnets)
105+
self.__rwstrengths.set_aggregator(aggs)
106+
self.__rwhardwares.set_aggregator(aggh)
134107

135108
@property
136109
def strengths(self) -> RWMagnetStrength:

pyaml/control/abstract_impl.py

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from ..control.deviceaccesslist import DeviceAccessList
33
from ..control.deviceaccess import DeviceAccess
44
from ..magnet.model import MagnetModel
5+
from ..magnet.magnet import Magnet
56
from ..bpm.bpm_model import BPMModel
67
from ..rf.rf_plant import RFPlant
78
from ..rf.rf_transmitter import RFTransmitter
@@ -15,29 +16,107 @@
1516

1617
class CSScalarAggregator(ScalarAggregator):
1718
"""
18-
Control system aggregator for a scalar value
19+
Basic control system aggregator for a list of scalar values
1920
"""
2021

2122
def __init__(self, devs:DeviceAccessList):
22-
self.__devs = devs
23+
self._devs = devs
2324

2425
def add_devices(self, devices:DeviceAccess | list[DeviceAccess] ):
25-
self.__devs.add_devices(devices)
26+
self._devs.add_devices(devices)
2627

2728
def set(self, value: NDArray[np.float64]):
28-
self.__devs.set(value)
29+
self._devs.set(value)
2930

3031
def set_and_wait(self, value: NDArray[np.float64]):
31-
self.__devs.set_and_wait(value)
32+
self._devs.set_and_wait(value)
3233

3334
def get(self) -> NDArray[np.float64]:
34-
return self.__devs.get()
35+
return self._devs.get()
3536

3637
def readback(self) -> np.array:
37-
return self.__devs.readback()
38+
return self._devs.readback()
3839

3940
def unit(self) -> str:
40-
return self.__devs.unit()
41+
return self._devs.unit()
42+
43+
def nb_device(self) -> int:
44+
return self._devs.__len__()
45+
46+
#------------------------------------------------------------------------------
47+
48+
class CSStrengthScalarAggregator(CSScalarAggregator):
49+
"""
50+
Control system aggregator for a list of magnet strengths.
51+
This aggregator is in charge of computing hardware setpoints and applying them without overlap.
52+
When virtual magnets exported from combined function mangets are present (RWMapper),
53+
the aggregator prevents to apply several times the same power supply setpoint.
54+
"""
55+
56+
def __init__(self, peer:CSScalarAggregator):
57+
CSScalarAggregator.__init__(self,peer._devs)
58+
self.__models: list[MagnetModel] = [] # List of magnet model
59+
self.__modelToMagnet: list[list[tuple[int,int]]] = [] # strengths indexing
60+
self.__nbMagnet = 0 # Number of magnet strengths
61+
62+
def add_magnet(self, magnet:Magnet):
63+
# Incoming magnet can be a magnet exported from a CombinedFunctionMagnet or simple magnet.
64+
# All magnets exported from a same CombinedFunctionMagnet share the same model
65+
# TODO: check that strength is supported (m.strength may be None)
66+
strengthIndex = magnet.strength.index() if isinstance(magnet.strength,abstract.RWMapper) else 0
67+
if magnet.model not in self.__models:
68+
index = len(self.__models)
69+
self.__models.append(magnet.model)
70+
self.__modelToMagnet.append([(self.__nbMagnet,strengthIndex)])
71+
self._devs.add_devices(magnet.model.get_devices())
72+
else:
73+
index = self.__models.index(magnet.model)
74+
self.__modelToMagnet[index].append((self.__nbMagnet,strengthIndex))
75+
self.__nbMagnet += 1
76+
77+
def set(self, value: NDArray[np.float64]):
78+
allHardwareValues = self._devs.get() # Read all hardware setpoints
79+
newHardwareValues = np.zeros(self.nb_device())
80+
hardwareIndex = 0
81+
for modelIndex,model in enumerate(self.__models):
82+
nbDev = len(model.get_devices())
83+
mStrengths = model.compute_strengths( allHardwareValues[hardwareIndex:hardwareIndex+nbDev] )
84+
for (valueIdx,strengthIdx) in self.__modelToMagnet[modelIndex]:
85+
mStrengths[strengthIdx] = value[valueIdx]
86+
newHardwareValues[hardwareIndex:hardwareIndex+nbDev] = model.compute_hardware_values(mStrengths)
87+
hardwareIndex += nbDev
88+
self._devs.set(newHardwareValues)
89+
90+
def set_and_wait(self, value: NDArray[np.float64]):
91+
raise NotImplementedError("Not implemented yet.")
92+
93+
def get(self) -> NDArray[np.float64]:
94+
allHardwareValues = self._devs.get() # Read all hardware setpoints
95+
allStrength = np.zeros(self.__nbMagnet)
96+
hardwareIndex = 0
97+
for modelIndex,model in enumerate(self.__models):
98+
nbDev = len(model.get_devices())
99+
mStrengths = model.compute_strengths( allHardwareValues[hardwareIndex:hardwareIndex+nbDev] )
100+
for (valueIdx,strengthIdx) in self.__modelToMagnet[modelIndex]:
101+
allStrength[valueIdx] = mStrengths[strengthIdx]
102+
hardwareIndex += nbDev
103+
return allStrength
104+
105+
def readback(self) -> np.array:
106+
allHardwareValues = self._devs.readback() # Read all hardware readback
107+
allStrength = np.zeros(self.__nbMagnet)
108+
hardwareIndex = 0
109+
for modelIndex,model in enumerate(self.__models):
110+
nbDev = len(model.get_devices())
111+
mStrengths = model.compute_strengths( allHardwareValues[hardwareIndex:hardwareIndex+nbDev] )
112+
for (valueIdx,strengthIdx) in self.__modelToMagnet[modelIndex]:
113+
allStrength[valueIdx] = mStrengths[strengthIdx]
114+
hardwareIndex += nbDev
115+
return allStrength
116+
117+
def unit(self) -> str:
118+
return self._devs.unit()
119+
41120

42121
#------------------------------------------------------------------------------
43122

@@ -61,9 +140,6 @@ def set_and_wait(self, value: double):
61140
def unit(self) -> str:
62141
return self.__model.get_hardware_units()[0]
63142

64-
def index(self) -> int:
65-
return 0
66-
67143
#------------------------------------------------------------------------------
68144

69145
class RWStrengthScalar(abstract.ReadWriteFloatScalar):
@@ -92,9 +168,6 @@ def set_and_wait(self, value:float):
92168
def unit(self) -> str:
93169
return self.__model.get_strength_units()[0]
94170

95-
def index(self) -> int:
96-
return 0
97-
98171
#------------------------------------------------------------------------------
99172

100173
class RWHardwareArray(abstract.ReadWriteFloatArray):

0 commit comments

Comments
 (0)