44import matplotlib .pyplot as plt
55import numpy as np
66import pySC
7- from pydantic import ConfigDict
87from pySC .apps import measure_bba
98from pySC .apps .bba import BBAAnalysis
109from pySC .apps .codes import BBACode
1110
1211from ..common .constants import Action
1312from ..common .exception import PyAMLException
1413from ..external .pySC_interface import pySCInterface
15- from .measurement_tool import MeasurementTool , MeasurementToolConfigModel
14+ from ..validation import DynamicValidation , register_schema
15+ from .measurement_tool import MeasurementTool
1616
1717logger = logging .getLogger (__name__ )
1818
1919PYAMLCLASS = "BBA"
2020
2121
22- class ConfigModel (MeasurementToolConfigModel ):
22+ @register_schema
23+ class BBA (MeasurementTool , DynamicValidation ):
2324 """
24- Configuration model for Beam Based Alignment.
25- BBA finds the magnetic center of a quad (zero crossing).
25+ Beam-based alignment measurement tool.
26+
27+ This tool determines the magnetic center of a quadrupole by varying its
28+ strength while applying controlled horizontal and vertical orbit offsets.
29+ The quadrupole center is identified from the corresponding zero crossings
30+ in the BPM response.
2631
2732 Parameters
2833 ----------
34+ name : str
35+ Name of the measurement tool.
2936 bpm_array_name : str
30- BPM array name ( orbit)
37+ Name of the BPM array used to measure the orbit.
3138 bpm_name : str
32- BPM to be corrected (close to the quad)
39+ Name of the BPM located near the quadrupole whose center is measured.
3340 hcorr_name : str
34- Horizontal corrector used to make a deviation in the quad
41+ Name of the horizontal corrector used to create horizontal orbit
42+ offsets at the quadrupole.
3543 vcorr_name : str
36- Vertical corrector used to make a deviation in the quad
44+ Name of the vertical corrector used to create vertical orbit offsets
45+ at the quadrupole.
3746 quad_name : str
38- Quadrupole used to find the center
47+ Name of the quadrupole to align.
3948 hcorr_delta : float
40- Horizontal corrector delta strength
49+ Change in horizontal corrector strength used for each horizontal
50+ orbit-offset step.
4151 vcorr_delta : float
42- Vertical corrector delta strength
52+ Change in vertical corrector strength used for each vertical
53+ orbit-offset step.
4354 hquad_delta : float
44- Quadrupole delta strength (for h search)
55+ Change in quadrupole strength used during the horizontal alignment
56+ measurement.
4557 vquad_delta : float
46- Quadrupole delta strength (for v search)
47-
58+ Change in quadrupole strength used during the vertical alignment
59+ measurement.
60+ n_step : int, default=1
61+ Number of orbit-offset steps to perform in each plane.
62+ sleep_between_step : float, default=0
63+ Time in seconds to wait after changing an orbit offset.
64+ n_avg_meas : int, default=1
65+ Number of BPM measurements to average at each step.
66+ sleep_between_meas : float, default=0
67+ Time in seconds to wait between individual BPM measurements.
4868 """
4969
50- model_config = ConfigDict (arbitrary_types_allowed = True , extra = "forbid" )
51-
52- bpm_array_name : str
53- bpm_name : str
54- hcorr_name : str
55- vcorr_name : str
56- quad_name : str
57- hcorr_delta : float
58- vcorr_delta : float
59- hquad_delta : float
60- vquad_delta : float
61-
62-
63- class BBA (MeasurementTool ):
64- def __init__ (self , cfg : ConfigModel ):
65- super ().__init__ (cfg .name )
66- self ._cfg = cfg
70+ def __init__ (
71+ self ,
72+ name : str ,
73+ bpm_array_name : str ,
74+ bpm_name : str ,
75+ hcorr_name : str ,
76+ vcorr_name : str ,
77+ quad_name : str ,
78+ hcorr_delta : float ,
79+ vcorr_delta : float ,
80+ hquad_delta : float ,
81+ vquad_delta : float ,
82+ n_step : int = 1 ,
83+ sleep_between_step : float = 0 ,
84+ n_avg_meas : int = 1 ,
85+ sleep_between_meas : float = 0 ,
86+ ):
87+ super ().__init__ (name )
88+ self .bpm_array_name = bpm_array_name
89+ self .bpm_name = bpm_name
90+ self .hcorr_name = hcorr_name
91+ self .vcorr_name = vcorr_name
92+ self .quad_name = quad_name
93+ self .hcorr_delta = hcorr_delta
94+ self .vcorr_delta = vcorr_delta
95+ self .hquad_delta = hquad_delta
96+ self .vquad_delta = vquad_delta
97+ self .n_step = n_step
98+ self .sleep_between_step = sleep_between_step
99+ self .n_avg_meas = n_avg_meas
100+ self .sleep_between_meas = sleep_between_meas
67101
68102 def measure (
69103 self ,
@@ -111,29 +145,29 @@ def measure(
111145 plane: str, optional
112146 Plane to perform ("H" or "V", None => both plane)
113147 """
114- nb_meas = n_avg_meas if n_avg_meas is not None else self ._cfg . n_avg_meas
115- sleep_step = sleep_between_step if sleep_between_step is not None else self ._cfg . sleep_between_step
116- sleep_meas = sleep_between_meas if sleep_between_meas is not None else self ._cfg . sleep_between_meas
148+ nb_meas = n_avg_meas if n_avg_meas is not None else self .n_avg_meas
149+ sleep_step = sleep_between_step if sleep_between_step is not None else self .sleep_between_step
150+ sleep_meas = sleep_between_meas if sleep_between_meas is not None else self .sleep_between_meas
117151
118152 element_holder = self ._peer
119153 interface = pySCInterface (
120154 element_holder = element_holder ,
121- bpm_array_name = self ._cfg . bpm_array_name ,
155+ bpm_array_name = self .bpm_array_name ,
122156 )
123157 interface .set_wait_time = sleep_step
124158 interface .read_wait_time = sleep_meas
125159
126- bpms_names = element_holder .bpms .get (self ._cfg . bpm_array_name ).names ()
160+ bpms_names = element_holder .bpms .get (self .bpm_array_name ).names ()
127161
128162 bba_pySC_config = {
129- "number" : bpms_names .index (self ._cfg . bpm_name ),
130- "QUAD" : self ._cfg . quad_name ,
131- "HCORR" : self ._cfg . hcorr_name ,
132- "VCORR" : self ._cfg . vcorr_name ,
133- "HCORR_delta" : self ._cfg . hcorr_delta ,
134- "QUAD_dk_H" : self ._cfg . hquad_delta ,
135- "VCORR_delta" : self ._cfg . vcorr_delta ,
136- "QUAD_dk_V" : self ._cfg . vquad_delta ,
163+ "number" : bpms_names .index (self .bpm_name ),
164+ "QUAD" : self .quad_name ,
165+ "HCORR" : self .hcorr_name ,
166+ "VCORR" : self .vcorr_name ,
167+ "HCORR_delta" : self .hcorr_delta ,
168+ "QUAD_dk_H" : self .hquad_delta ,
169+ "VCORR_delta" : self .vcorr_delta ,
170+ "QUAD_dk_V" : self .vquad_delta ,
137171 "magnet_type" : "normal_quadrupole" ,
138172 }
139173
@@ -142,10 +176,10 @@ def measure(
142176
143177 generator = measure_bba (
144178 interface = interface ,
145- bpm_name = self ._cfg . bpm_name ,
179+ bpm_name = self .bpm_name ,
146180 config = bba_pySC_config ,
147181 shots_per_orbit = nb_meas ,
148- n_corr_steps = self ._cfg . n_step ,
182+ n_corr_steps = self .n_step ,
149183 bipolar = False ,
150184 skip_save = True ,
151185 plane = plane ,
@@ -266,7 +300,7 @@ def plot_data(self, plane: str):
266300 axes ["A" ].plot (xx [final_mask ], yy [final_mask ], "." , c = "C0" )
267301 axes ["A" ].plot (xx [~ final_mask ], yy [~ final_mask ], "." , c = "C1" )
268302
269- axes ["A" ].set_xlabel (f"BPM position [μm]\n { self ._cfg . bpm_name } offset = { offset * 1e6 :.3f} [μm]" )
303+ axes ["A" ].set_xlabel (f"BPM position [μm]\n { self .bpm_name } offset = { offset * 1e6 :.3f} [μm]" )
270304 axes ["A" ].set_ylabel ("Modulation [μm]" )
271305 axes ["A" ].grid ()
272306
@@ -285,6 +319,6 @@ def plot_data(self, plane: str):
285319 axes ["C" ].grid ()
286320
287321 fig .tight_layout ()
288- fig .canvas .manager .set_window_title (f"{ plane } BBA { self ._cfg . bpm_name } " )
322+ fig .canvas .manager .set_window_title (f"{ plane } BBA { self .bpm_name } " )
289323
290324 plt .show ()
0 commit comments