Skip to content

Commit 2770f81

Browse files
authored
Merge pull request #92 from python-accelerator-middle-layer/feature/config-orbit-corr
Attachment of orbit correction to simulator/control system and generic ResponseMatrix pyAML object
2 parents 17a09b5 + 89a2603 commit 2770f81

File tree

12 files changed

+143
-193
lines changed

12 files changed

+143
-193
lines changed

examples/ESRF_ORM_example/correct_orbit.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,8 @@
1515
sr = Accelerator.load(config_path)
1616
ebs = sr.design
1717

18-
orbit = Orbit(
19-
element_holder=ebs,
20-
cfg=Orbit_ConfigModel(
21-
bpm_array_name="BPM",
22-
hcorr_array_name="HCorr",
23-
vcorr_array_name="VCorr",
24-
singular_values=162,
25-
response_matrix_file=str(
26-
pyaml_folder.joinpath("examples/ESRF_ORM_example/ideal_orm.json").resolve()
27-
),
28-
),
29-
)
30-
3118
## get reference
32-
ref_h, ref_v = orbit.element_holder.get_bpms("BPM").positions.get().T
19+
ref_h, ref_v = ebs.get_bpms("BPM").positions.get().T
3320
reference = np.concat((ref_h, ref_v))
3421
########################################################
3522

@@ -57,9 +44,9 @@
5744
########################################################
5845

5946
## Correct the orbit
60-
orbit.correct(reference=reference)
61-
# orbit.correct(plane="H")
62-
# orbit.correct(plane="V")
47+
ebs.orbit.correct(reference=reference)
48+
# ebs.orbit.correct(plane="H")
49+
# ebs.orbit.correct(plane="V")
6350
########################################################
6451

6552
## inspect orbit correction

examples/ESRF_ORM_example/measure_ideal_ORM_and_disp.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@
4646
)
4747

4848
ideal_ORM_data = {
49+
"type": "pyaml.tuning_tools.response_matrix",
4950
"matrix": orm_data["matrix"],
5051
"input_names": orm_data["input_names"],
5152
"output_names": orm_data["output_names"],
5253
"rf_response": rf_response,
5354
}
5455

55-
json.dump(ideal_ORM_data, open("ideal_orm_disp.json", "w"), indent=4)
56+
json.dump(ideal_ORM_data, open("ideal_orm_disp.json", "w"))

pyaml/common/element_holder.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Module handling element references for simulators and control system
33
"""
44

5+
from typing import TYPE_CHECKING
6+
57
from ..arrays.bpm_array import BPMArray
68
from ..arrays.cfm_magnet_array import CombinedFunctionMagnetArray
79
from ..arrays.element_array import ElementArray
@@ -13,9 +15,12 @@
1315
from ..magnet.magnet import Magnet
1416
from ..rf.rf_plant import RFPlant
1517
from ..rf.rf_transmitter import RFTransmitter
16-
from ..tuning_tools.tune import Tune
1718
from .element import Element
1819

20+
if TYPE_CHECKING:
21+
from ..tuning_tools.orbit import Orbit
22+
from ..tuning_tools.tune import Tune
23+
1924

2025
class ElementHolder(object):
2126
"""
@@ -189,8 +194,18 @@ def add_betatron_tune_monitor(self, tune_monitor: Element):
189194

190195
# Tuning tools
191196

192-
def get_tune_tuning(self, name: str) -> Tune:
197+
def get_tune_tuning(self, name: str) -> "Tune":
193198
return self.__get("Tune tuning tool", name, self.__TUNING_TOOLS)
194199

195200
def add_tune_tuning(self, tune: Element):
196201
self.__add(self.__TUNING_TOOLS, tune)
202+
203+
def get_orbit_tuning(self, name: str) -> "Orbit":
204+
return self.__get("Orbit tuning tool", name, self.__TUNING_TOOLS)
205+
206+
def add_orbit_tuning(self, orbit: Element):
207+
self.__add(self.__TUNING_TOOLS, orbit)
208+
209+
@property
210+
def orbit(self) -> "Orbit":
211+
return self.get_orbit_tuning("DEFAULT_ORBIT_CORRECTION")

pyaml/control/controlsystem.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ..magnet.magnet import Magnet
2727
from ..rf.rf_plant import RFPlant, RWTotalVoltage
2828
from ..rf.rf_transmitter import RFTransmitter
29+
from ..tuning_tools.orbit import Orbit
2930
from ..tuning_tools.tune import Tune
3031

3132

@@ -166,3 +167,6 @@ def fill_device(self, elements: list[Element]):
166167

167168
elif isinstance(e, Tune):
168169
self.add_tune_tuning(e.attach(self))
170+
171+
elif isinstance(e, Orbit):
172+
self.add_orbit_tuning(e.attach(self))

pyaml/external/pySC_interface.py

Lines changed: 14 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,28 @@
1-
from typing import Dict, List, Tuple
1+
from typing import TYPE_CHECKING, Optional, Tuple
22

33
import numpy as np
44

5-
from ..common.element_holder import ElementHolder
5+
if TYPE_CHECKING:
6+
from ..common.element_holder import ElementHolder
67
from ..common.exception import PyAMLException
78

89

910
class pySCInterface:
1011
def __init__(
1112
self,
12-
element_holder: ElementHolder,
13-
bpm_array_name: str = "BPM",
14-
hcorr_array_name: str = "HCorr",
15-
vcorr_array_name: str = "VCorr",
16-
rf_plant_name: str = "RF",
13+
element_holder: "ElementHolder",
14+
bpm_array_name: str,
15+
rf_plant_name: Optional[str] = None,
1716
):
1817
self.element_holder = element_holder
1918

2019
self.bpm_array = element_holder.get_bpms(bpm_array_name)
2120

22-
# We could generalize to arbitrary arrays.
23-
# Presently, pySC only uses set_many and get_many to set corrector strengths
24-
# when doing orbit correction.
25-
# Technically we don't need to define it, because we can ask for the trims to
26-
# make for an orbit correction and then apply them in pyAML.
27-
# Only get and set are used for other measurements.
28-
self.hcorr_array_name = hcorr_array_name
29-
self.hcorr_array = element_holder.get_magnets(hcorr_array_name)
30-
self.hcorr_names = self.hcorr_array.names()
31-
self.hcorr_name_to_index = {
32-
name: ii for ii, name in enumerate(self.hcorr_names)
33-
}
34-
35-
self.vcorr_array_name = vcorr_array_name
36-
self.vcorr_array = element_holder.get_magnets(vcorr_array_name)
37-
self.vcorr_names = self.vcorr_array.names()
38-
self.vcorr_name_to_index = {
39-
name: ii for ii, name in enumerate(self.vcorr_names)
40-
}
41-
4221
self.rf_plant_name = rf_plant_name
43-
self.rf_plant = element_holder.get_rf_plant(self.rf_plant_name)
22+
if rf_plant_name is not None:
23+
self.rf_plant = element_holder.get_rf_plant(self.rf_plant_name)
24+
else:
25+
self.rf_plant = None
4426

4527
def get_orbit(self) -> Tuple[np.array, np.array]:
4628
# we should wait here somehow according to polling rate
@@ -56,112 +38,13 @@ def set(self, name: str, value: float) -> None:
5638
magnet.strength.set(value=value) # ideally set_and_wait but not implemented
5739
return
5840

59-
def get_many(self, names: List[str]) -> Dict[str, float]:
60-
get_hcorr = False
61-
get_vcorr = False
62-
name_to_array = {}
63-
64-
# check if all names are accounted for and which strength arrays to get.
65-
for name in names:
66-
if name in self.hcorr_names:
67-
get_hcorr = True
68-
name_to_array[name] = "hcorr"
69-
elif name in self.vcorr_names:
70-
get_vcorr = True
71-
name_to_array[name] = "vcorr"
72-
else:
73-
raise PyAMLException(
74-
f"{name} was not found in magnet arrays "
75-
f"{self.hcorr_array_name} and {self.vcorr_array_name}"
76-
)
77-
78-
# do actual get
79-
if get_hcorr:
80-
hcorr_strengths = self.hcorr_array.strengths.get()
81-
else:
82-
hcorr_strengths = []
83-
84-
if get_vcorr:
85-
vcorr_strengths = self.vcorr_array.strengths.get()
86-
else:
87-
vcorr_strengths = []
88-
89-
# prepare data to return
90-
data = {}
91-
for name in names:
92-
if name_to_array[name] == "hcorr":
93-
hcorr_index = self.hcorr_name_to_index[name]
94-
data[name] = hcorr_strengths[hcorr_index]
95-
elif name_to_array[name] == "vcorr":
96-
vcorr_index = self.vcorr_name_to_index[name]
97-
data[name] = vcorr_strengths[vcorr_index]
98-
else:
99-
raise PyAMLException("BUG: This should not happen.")
100-
101-
return data
102-
103-
def set_many(self, names_values: Dict[str, float]) -> None:
104-
set_hcorr = False
105-
set_vcorr = False
106-
name_to_array = {}
107-
108-
names = list(names_values.keys())
109-
# check if all names are accounted for and which strength arrays to get/set.
110-
for name in names:
111-
if name in self.hcorr_names:
112-
set_hcorr = True
113-
name_to_array[name] = "hcorr"
114-
elif name in self.vcorr_names:
115-
set_vcorr = True
116-
name_to_array[name] = "vcorr"
117-
else:
118-
raise PyAMLException(
119-
f"{name} was not found in magnet arrays "
120-
f"{self.hcorr_array_name} and {self.vcorr_array_name}"
121-
)
122-
123-
# first do get
124-
if set_hcorr:
125-
hcorr_strengths = self.hcorr_array.strengths.get()
126-
else:
127-
hcorr_strengths = []
128-
129-
if set_vcorr:
130-
vcorr_strengths = self.vcorr_array.strengths.get()
131-
else:
132-
vcorr_strengths = []
133-
134-
# change hcorr_strengths and vcorr_strengths according to names_values
135-
# (input data). We could check if original setpoint and new setpoint are not
136-
# the same to avoid redundant set.
137-
for name in names:
138-
if name_to_array[name] == "hcorr":
139-
hcorr_index = self.hcorr_name_to_index[name]
140-
hcorr_strengths[hcorr_index] = names_values[name]
141-
elif name_to_array[name] == "vcorr":
142-
vcorr_index = self.vcorr_name_to_index[name]
143-
vcorr_strengths[vcorr_index] = names_values[name]
144-
else:
145-
raise PyAMLException("BUG: This should not happen.")
146-
147-
# TODO: we should check first if any value goes out of range before starting to
148-
# set anything
149-
# TODO: can weset everything together instead of settings the arrays one by one?
150-
151-
if set_hcorr:
152-
self.hcorr_array.strengths.set(
153-
hcorr_strengths
154-
) # ideally set_and_wait but not implemented
155-
if set_vcorr:
156-
self.vcorr_array.strengths.set(
157-
vcorr_strengths
158-
) # ideally set_and_wait but not implemented
159-
160-
return
161-
16241
def get_rf_main_frequency(self) -> float:
42+
if self.rf_plant is None:
43+
raise PyAMLException("RF plant name was not provided.")
16344
return self.rf_plant.frequency.get()
16445

16546
def set_rf_main_frequency(self, value: float) -> None:
47+
if self.rf_plant is None:
48+
raise PyAMLException("RF plant name was not provided.")
16649
self.rf_plant.frequency.set(value)
16750
return

pyaml/lattice/simulator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from ..magnet.magnet import Magnet
3333
from ..rf.rf_plant import RFPlant, RWTotalVoltage
3434
from ..rf.rf_transmitter import RFTransmitter
35+
from ..tuning_tools.orbit import Orbit
3536
from ..tuning_tools.tune import Tune
3637
from .attribute_linker import (
3738
ConfigModel as PyAtAttrLinkerConfigModel,
@@ -207,6 +208,9 @@ def fill_device(self, elements: list[Element]):
207208
elif isinstance(e, Tune):
208209
self.add_tune_tuning(e.attach(self))
209210

211+
elif isinstance(e, Orbit):
212+
self.add_orbit_tuning(e.attach(self))
213+
210214
def get_at_elems(self, element: Element) -> list[at.Element]:
211215
identifier = self._linker.get_element_identifier(element)
212216
element_list = self._linker.get_at_elements(identifier)

0 commit comments

Comments
 (0)