Skip to content

Commit 33df1fa

Browse files
Correction of serialized magnets array and new tune correction test with serialized magnets
1 parent 4cf53ed commit 33df1fa

File tree

5 files changed

+285
-26
lines changed

5 files changed

+285
-26
lines changed

pyaml/arrays/serialized_magnet_array.py

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from ..magnet.serialized_magnet import SerializedMagnets
66
from .element_array import ElementArray
77

8-
# TODO handle aggregator for CFM
8+
# TODO handle aggregator for serialized magnets
99

1010

1111
class RWMagnetStrengths(ReadWriteFloatArray):
@@ -16,19 +16,13 @@ def __init__(self, name: str, magnets: list[SerializedMagnets]):
1616

1717
# Gets the values
1818
def get(self) -> np.array:
19-
r = np.zeros(self.__nb)
20-
idx = 0
21-
for m in self.__magnets:
22-
r[idx : idx + m.get_nb_magnets()] = m.strengths.get()
23-
idx += m.get_nb_magnets()
24-
return r
19+
return np.array([m.strengths.get() for m in self.__magnets])
2520

2621
# Sets the values
2722
def set(self, value: np.array):
28-
nvalue = np.ones(self.__nb) * value if isinstance(value, float) else value
29-
for idx, m in enumerate(self.__magnets):
30-
m.strengths.set(nvalue[idx])
31-
idx += m.get_nb_magnets()
23+
nvalue = np.ones(len(self.__magnets)) * value if isinstance(value, float) else value
24+
for value, m in zip(nvalue, self.__magnets, strict=True):
25+
m.strengths.set(value)
3226

3327
# Sets the values and waits that the read values reach their setpoint
3428
def set_and_wait(self, value: np.array):
@@ -50,18 +44,13 @@ def __init__(self, name: str, magnets: list[SerializedMagnets]):
5044

5145
# Gets the values
5246
def get(self) -> np.array:
53-
r = np.zeros(self.__nb)
54-
idx = 0
55-
for m in self.__magnets:
56-
r[idx : idx + m.get_nb_magnets()] = m.hardwares.get()
57-
idx += m.get_nb_magnets()
58-
return r
47+
return np.array([m.hardwares.get() for m in self.__magnets])
5948

6049
# Sets the values
6150
def set(self, value: np.array):
62-
nvalue = np.ones(self.__nb) * value if isinstance(value, float) else value
63-
for idx, m in enumerate(self.__magnets):
64-
m.hardwares.set(nvalue[idx])
51+
nvalue = np.ones(len(self.__magnets)) * value if isinstance(value, float) else value
52+
for value, m in zip(nvalue, self.__magnets, strict=True):
53+
m.hardwares.set(value)
6554

6655
# Sets the values and waits that the read values reach their setpoint
6756
def set_and_wait(self, value: np.array):
@@ -103,11 +92,7 @@ def __init__(
10392
self.__rwhardwares = RWMagnetHardwares(arrayName, magnets)
10493

10594
if use_aggregator:
106-
raise (
107-
PyAMLException(
108-
"Aggregator not implemented for CombinedFunctionMagnetArray"
109-
)
110-
)
95+
raise (PyAMLException("Aggregator not implemented for CombinedFunctionMagnetArray"))
11196

11297
@property
11398
def strengths(self) -> RWMagnetStrengths:

pyaml/common/element_holder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def fill_serialized_magnet_array(self, arrayName: str, elementNames: list[str]):
204204
self.__SERIALIZED_MAGNETS_ARRAYS,
205205
)
206206

207-
def get_serialized_magnet(self, name: str) -> Magnet:
207+
def get_serialized_magnet(self, name: str) -> SerializedMagnets:
208208
return self.__get("SerializedMagnets", name, self.__SERIALIZED_MAGNETS)
209209

210210
def add_serialized_magnet(self, m: Magnet):

tests/config/sr_serialized_magnets.yaml

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ arrays:
1212
name: series
1313
elements:
1414
- mySeriesOfMagnets
15+
- type: pyaml.arrays.serialized_magnet
16+
name: QForTune
17+
elements:
18+
- QD2?
19+
- QF1?
1520
devices:
1621
- type: pyaml.magnet.serialized_magnet
1722
name: mySeriesOfMagnets
@@ -34,3 +39,211 @@ devices:
3439
type: tango.pyaml.attribute
3540
attribute: srmag/ps-corr-sh1/c01-a-ch1/current
3641
unit: A
42+
- type: pyaml.magnet.serialized_magnet
43+
name: QF1A
44+
function: B1
45+
elements:
46+
- QF1A-C05
47+
- QF1A-C06
48+
- QF1A-C07
49+
- QF1A-C08
50+
- QF1A-C09
51+
- QF1A-C10
52+
- QF1A-C11
53+
- QF1A-C12
54+
- QF1A-C13
55+
- QF1A-C14
56+
- QF1A-C15
57+
- QF1A-C16
58+
- QF1A-C17
59+
- QF1A-C18
60+
- QF1A-C19
61+
- QF1A-C20
62+
- QF1A-C21
63+
- QF1A-C22
64+
- QF1A-C23
65+
- QF1A-C24
66+
- QF1A-C25
67+
- QF1A-C26
68+
- QF1A-C27
69+
- QF1A-C28
70+
- QF1A-C29
71+
- QF1A-C30
72+
- QF1A-C31
73+
- QF1A-C32
74+
- QF1A-C01
75+
- QF1A-C02
76+
- QF1A-C03
77+
model:
78+
type: pyaml.magnet.linear_serialized_model
79+
calibration_factors: 1
80+
calibration_offsets: 0.0
81+
unit: m-1
82+
curves:
83+
type: pyaml.configuration.csvcurve
84+
file: sr/magnet_models/QF1_strength.csv
85+
powerconverter:
86+
type: tango.pyaml.attribute
87+
attribute: srmag/ps-corr-sh1/c01-a-ch1/current
88+
unit: A
89+
- type: pyaml.magnet.serialized_magnet
90+
name: QF1E
91+
function: B1
92+
elements:
93+
- QF1E-C04
94+
- QF1E-C05
95+
- QF1E-C06
96+
- QF1E-C07
97+
- QF1E-C08
98+
- QF1E-C09
99+
- QF1E-C10
100+
- QF1E-C11
101+
- QF1E-C12
102+
- QF1E-C13
103+
- QF1E-C14
104+
- QF1E-C15
105+
- QF1E-C16
106+
- QF1E-C17
107+
- QF1E-C18
108+
- QF1E-C19
109+
- QF1E-C20
110+
- QF1E-C21
111+
- QF1E-C22
112+
- QF1E-C23
113+
- QF1E-C24
114+
- QF1E-C25
115+
- QF1E-C26
116+
- QF1E-C27
117+
- QF1E-C28
118+
- QF1E-C29
119+
- QF1E-C30
120+
- QF1E-C31
121+
- QF1E-C32
122+
- QF1E-C01
123+
- QF1E-C02
124+
model:
125+
type: pyaml.magnet.linear_serialized_model
126+
calibration_factors: 1
127+
calibration_offsets: 0.0
128+
unit: m-1
129+
curves:
130+
type: pyaml.configuration.csvcurve
131+
file: sr/magnet_models/QF1_strength.csv
132+
powerconverter:
133+
type: tango.pyaml.attribute
134+
attribute: srmag/ps-corr-sh1/c01-a-ch1/current
135+
unit: A
136+
- type: pyaml.magnet.serialized_magnet
137+
name: QD2A
138+
function: B1
139+
elements:
140+
- QD2A-C05
141+
- QD2A-C06
142+
- QD2A-C07
143+
- QD2A-C08
144+
- QD2A-C09
145+
- QD2A-C10
146+
- QD2A-C11
147+
- QD2A-C12
148+
- QD2A-C13
149+
- QD2A-C14
150+
- QD2A-C15
151+
- QD2A-C16
152+
- QD2A-C17
153+
- QD2A-C18
154+
- QD2A-C19
155+
- QD2A-C20
156+
- QD2A-C21
157+
- QD2A-C22
158+
- QD2A-C23
159+
- QD2A-C24
160+
- QD2A-C25
161+
- QD2A-C26
162+
- QD2A-C27
163+
- QD2A-C28
164+
- QD2A-C29
165+
- QD2A-C30
166+
- QD2A-C31
167+
- QD2A-C32
168+
- QD2A-C01
169+
- QD2A-C02
170+
- QD2A-C03
171+
model:
172+
type: pyaml.magnet.linear_serialized_model
173+
calibration_factors: 1
174+
calibration_offsets: 0.0
175+
unit: m-1
176+
curves:
177+
type: pyaml.configuration.csvcurve
178+
file: sr/magnet_models/QD2_strength.csv
179+
powerconverter:
180+
type: tango.pyaml.attribute
181+
attribute: srmag/ps-corr-sh1/c01-a-ch1/current
182+
unit: A
183+
- type: pyaml.magnet.serialized_magnet
184+
name: QD2E
185+
function: B1
186+
elements:
187+
- QD2E-C04
188+
- QD2E-C05
189+
- QD2E-C06
190+
- QD2E-C07
191+
- QD2E-C08
192+
- QD2E-C09
193+
- QD2E-C10
194+
- QD2E-C11
195+
- QD2E-C12
196+
- QD2E-C13
197+
- QD2E-C14
198+
- QD2E-C15
199+
- QD2E-C16
200+
- QD2E-C17
201+
- QD2E-C18
202+
- QD2E-C19
203+
- QD2E-C20
204+
- QD2E-C21
205+
- QD2E-C22
206+
- QD2E-C23
207+
- QD2E-C24
208+
- QD2E-C25
209+
- QD2E-C26
210+
- QD2E-C27
211+
- QD2E-C28
212+
- QD2E-C29
213+
- QD2E-C30
214+
- QD2E-C31
215+
- QD2E-C32
216+
- QD2E-C01
217+
- QD2E-C02
218+
model:
219+
type: pyaml.magnet.linear_serialized_model
220+
calibration_factors: 1
221+
calibration_offsets: 0.0
222+
unit: m-1
223+
curves:
224+
type: pyaml.configuration.csvcurve
225+
file: sr/magnet_models/QD2_strength.csv
226+
powerconverter:
227+
type: tango.pyaml.attribute
228+
attribute: srmag/ps-corr-sh1/c01-a-ch1/current
229+
unit: A
230+
- type: pyaml.diagnostics.tune_monitor
231+
name: BETATRON_TUNE
232+
tune_h:
233+
type: tango.pyaml.attribute_read_only
234+
attribute: srdiag/beam-tune/main/Qh
235+
unit: mm
236+
tune_v:
237+
type: tango.pyaml.attribute_read_only
238+
attribute: srdiag/beam-tune/main/Qv
239+
unit: mm
240+
- type: pyaml.tuning_tools.tune
241+
name: DEFAULT_TUNE_CORRECTION
242+
quad_array_name: QForTune
243+
betatron_tune_name: BETATRON_TUNE
244+
response_matrix: file:tune_response.json
245+
- type: pyaml.tuning_tools.tune_response_matrix
246+
name: DEFAULT_TUNE_RESPONSE_MATRIX
247+
quad_array_name: QForTune
248+
betatron_tune_name: BETATRON_TUNE
249+
quad_delta: 1e-4

tests/test_serialized_magnets.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ def test_config_load(sr_file):
4848
assert check_no_diff(currents)
4949

5050

51+
def print_magnet_list(magnet_list: list) -> None:
52+
for magnet in magnet_list:
53+
print(f"- {magnet.FamName}")
54+
55+
5156
@pytest.mark.parametrize(
5257
"sr_file",
5358
[
@@ -57,6 +62,8 @@ def test_config_load(sr_file):
5762
def test_magnet_modification(sr_file):
5863
sr = Accelerator.load(sr_file, use_fast_loader=True, ignore_external=True)
5964

65+
print(sr.yellow_pages)
66+
6067
sm: SerializedMagnets = sr.design.get_serialized_magnet("mySeriesOfMagnets")
6168
element_names = sm._SerializedMagnets__elements
6269

@@ -98,3 +105,53 @@ def test_magnet_modification(sr_file):
98105
print(element_names[ii], sm.strengths.elements[ii].get())
99106

100107
assert check_no_diff([sm.strengths.elements[ii].get() for ii in range(len(sm.strengths.elements))])
108+
109+
110+
@pytest.mark.parametrize(
111+
"sr_file",
112+
[
113+
"tests/config/sr_serialized_magnets.yaml",
114+
],
115+
)
116+
def test_tune(sr_file):
117+
sr = Accelerator.load(sr_file, use_fast_loader=True, ignore_external=True)
118+
sr.design.get_lattice().disable_6d()
119+
120+
quadForTuneDesign = sr.design.get_serialized_magnets("QForTune")
121+
tune_monitor = sr.design.get_betatron_tune_monitor("BETATRON_TUNE")
122+
# Build tune response matrix
123+
tunemat = np.zeros((len(quadForTuneDesign), 2))
124+
125+
# Magnet are not actually in series. Here a trick to set them to the same strengths
126+
for m in quadForTuneDesign:
127+
strength = m.strengths.get()
128+
m.strengths.set(strength)
129+
tune = tune_monitor.tune.get()
130+
print(f"tune={tune}")
131+
132+
for idx, m in enumerate(quadForTuneDesign):
133+
strength = m.strengths.get()
134+
m.strengths.set(strength + 1e-4)
135+
dq = tune_monitor.tune.get() - tune
136+
tunemat[idx] = dq * 1e4
137+
m.strengths.set(strength)
138+
139+
# Compute correction matrix
140+
correctionmat = np.linalg.pinv(tunemat.T)
141+
print(f"correctionmat.shape={correctionmat.shape}")
142+
print(f"correctionmat={correctionmat}")
143+
144+
# Correct tune
145+
strengths = quadForTuneDesign.strengths.get()
146+
print(f"len(strengths)={len(strengths)}")
147+
print(f"strengths={strengths}")
148+
strengths += np.matmul(correctionmat, [0.1, 0.05]) # Ask for correction [dqx,dqy]
149+
print(f"strengths={strengths}")
150+
quadForTuneDesign.strengths.set(strengths)
151+
newTune = tune_monitor.tune.get()
152+
print(f"newTune={newTune}")
153+
diffTune = newTune - tune
154+
155+
print(f"diffTune={diffTune}")
156+
assert np.abs(diffTune[0] - 0.1) < 1e-3
157+
assert np.abs(diffTune[1] - 0.05) < 1e-3

tests/test_tune.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ def test_tune():
2929

3030
# Compute correction matrix
3131
correctionmat = np.linalg.pinv(tunemat.T)
32+
print(correctionmat.shape)
33+
print(correctionmat)
3234

3335
# Correct tune
3436
strs = quadForTuneDesign.strengths.get()
37+
print(len(strs))
38+
print(strs)
3539
strs += np.matmul(correctionmat, [0.1, 0.05]) # Ask for correction [dqx,dqy]
3640
quadForTuneDesign.strengths.set(strs)
3741
newTune = tune_monitor.tune.get()

0 commit comments

Comments
 (0)