22from ..common .constants import Action
33from ..common .element import ElementConfigModel
44from ..common .exception import PyAMLException
5- from ..tuning_tools .measurement_tool import MeasurementTool
5+ from ..tuning_tools .measurement_tool import MeasurementTool , MeasurementToolConfigModel
66
77try :
88 from typing import Self # Python 3.11+
2020PYAMLCLASS = "ChomaticityMonitor"
2121
2222
23- class ConfigModel (ElementConfigModel ):
23+ class ConfigModel (MeasurementToolConfigModel ):
2424 """
2525 Configuration model for Chromaticity Monitor.
2626
@@ -32,24 +32,13 @@ class ConfigModel(ElementConfigModel):
3232 Name of main RF frequency plant
3333 bpm_array_name : str,optional
3434 Name of main BPM array used for dispersion fit
35- n_step : int, optional
36- Default number of RF step during chromaticity
37- measurement, by default 5
38- alphac : float or None, optional
39- Momentum compaction factor, by default None
4035 e_delta : float, optional
4136 Default variation of relative energy during chromaticity measurement:
4237 f0 - f0 * E_delta * alphac < f_RF < f0 + f0 * E_delta * alphac,
4338 by default 0.001
4439 max_e_delta : float, optional
4540 Maximum authorized variation of relative energy during chromaticity
4641 measurement, by default 0.004
47- n_tune_meas : int, optional
48- Default number of tune/orbit measurement per RF frequency, by default 1
49- sleep_between_meas : float, optional
50- Default sleep time in [s] between two tune measurements, by default 2.0
51- sleep_between_step : float, optional
52- Default sleep time in [s] after RF frequency variation, by default 5.0
5342 fit_order : int, optional
5443 Chomaticity fitting order, by default 1
5544 fit_disp_order : int, optional
@@ -63,13 +52,8 @@ class ConfigModel(ElementConfigModel):
6352 betatron_tune_name : str
6453 rf_plant_name : str
6554 bpm_array_name : str | None = None
66- n_step : int = 5
67- alphac : float | None = None
6855 e_delta : float = 0.001
6956 max_e_delta : float = 0.004
70- n_tune_meas : int = 1
71- sleep_between_meas : float = 0.0
72- sleep_between_step : float = 0.0
7357 fit_order : int = 1
7458 fit_disp_order : int = 1
7559 fit_dispersion : bool = False
@@ -118,6 +102,7 @@ def __init__(self, cfg: ConfigModel):
118102 self ._cfg = cfg
119103 self ._chromaticity = RChromaDispArray (self , "chromaticity" , "1" )
120104 self ._dipsersion = RChromaDispArray (self , "dispersion" , "m" )
105+ self ._alphac = None
121106
122107 @property
123108 def chromaticity (self ) -> ReadFloatArray :
@@ -127,10 +112,13 @@ def chromaticity(self) -> ReadFloatArray:
127112 Returns
128113 -------
129114 ReadFloatArray
130- Array of chromaticity values [[ q'x, q'y],[q''x, q''y],... ]
115+ chromaticity values [q'x, q'y]
131116 """
132117 return self ._chromaticity
133118
119+ def set_mcf (self , alphac : float ):
120+ self ._alphac = alphac
121+
134122 @property
135123 def dispersion (self ) -> ReadFloatArray :
136124 """
@@ -149,7 +137,7 @@ def measure(
149137 alphac : float = None ,
150138 e_delta : float = None ,
151139 max_e_delta : float = None ,
152- n_tune_meas : int = None ,
140+ n_avg_meas : int = None ,
153141 sleep_between_meas : float = None ,
154142 sleep_between_step : float = None ,
155143 fit_order : int = None ,
@@ -175,7 +163,7 @@ def measure(
175163 max_e_delta: float
176164 Maximum autorized variation of relative energy during chromaticity
177165 measurment [default: from config]
178- n_tune_meas : int
166+ n_avg_meas : int
179167 Default number of tune/orbit measurment per RF frequency [default: from config]
180168 sleep_between_meas: float
181169 Default time sleep between two tune measurment [default: from config]
@@ -194,22 +182,24 @@ def measure(
194182 If the callback return false, then the process is aborted.
195183 """
196184 n_step = n_step if n_step is not None else self ._cfg .n_step
197- alphac = alphac if alphac is not None else self ._cfg . alphac
185+ alphac = alphac if alphac is not None else self ._alphac
198186 e_delta = e_delta if e_delta is not None else self ._cfg .e_delta
199187 max_e_delta = max_e_delta if max_e_delta is not None else self ._cfg .max_e_delta
200- n_tune_meas = n_tune_meas if n_tune_meas is not None else self ._cfg .n_tune_meas
188+ n_avg_meas = n_avg_meas if n_avg_meas is not None else self ._cfg .n_avg_meas
201189 sleep_between_meas = sleep_between_meas if sleep_between_meas is not None else self ._cfg .sleep_between_meas
202190 sleep_between_step = sleep_between_step if sleep_between_step is not None else self ._cfg .sleep_between_step
203191 fit_order = fit_order if fit_order is not None else self ._cfg .fit_order
204192 fit_disp_order = fit_disp_order if fit_disp_order is not None else self ._cfg .fit_disp_order
205193 fit_dispersion = fit_dispersion if fit_dispersion is not None else self ._cfg .fit_dispersion
206194
207195 if abs (e_delta ) > abs (max_e_delta ):
208- logger .warning ("e_delta={e_delta} is greater than max_e_delta={max_e_delta}" )
196+ logger .warning (f "e_delta={ e_delta } is greater than max_e_delta={ max_e_delta } " )
209197
210198 if alphac is None :
211199 raise PyAMLException ("Moment compaction factor is not defined" )
212200
201+ self .register_callback (callback )
202+
213203 # Get devices
214204 self .check_peer ()
215205 tm = self ._peer .get_betatron_tune_monitor (self ._cfg .betatron_tune_name )
@@ -234,55 +224,55 @@ def measure(
234224 # ensure that, even if there is an issus, the script will finish by
235225 # reseting the RF frequency to its original value
236226 err = None
237- ok = True
227+ aborted = False
238228 try :
239229 for i , f in enumerate (delta_frec ):
240230 # TODO : Use set_and_wait once it is implemented !
241231
242232 rf .frequency .set (f0 + f )
243-
244- cb_data = {"step" : i , "rf" : f0 + f }
245- if not self .send_callback (Action .APPLY , callback , cb_data ):
246- # Abort
247- rf .frequency .set (f0 )
248- return False
233+ self .send_callback (Action .APPLY , {"step" : i , "rf" : float (f0 + f )})
249234 sleep (sleep_between_step )
250235
251236 # Averaging
252- for j in range (n_tune_meas ):
237+ for j in range (n_avg_meas ):
253238 tune = tm .tune .get ()
254239 Q [i ] += tune
255- cb_data = {"step" : i , "avg_step" : j , "rf" : f0 + f , "tune" : tune }
240+ cb_data = {"step" : i , "avg_step" : j , "rf" : float ( f0 + f ) , "tune" : tune }
256241 if bpms is not None :
257242 orb = bpms .positions .get ()
258243 orbit [i ] += orb
259244 cb_data ["orbit" ] = orb
260- if not self .send_callback (Action .MEASURE , callback , cb_data ):
261- # Abort
262- rf .frequency .set (f0 )
263- return False
245+ self .send_callback (Action .MEASURE , cb_data )
264246
265- if j < n_tune_meas - 1 :
247+ if j < n_avg_meas - 1 :
266248 sleep (sleep_between_meas )
267249
268- Q /= float (n_tune_meas )
250+ Q /= float (n_avg_meas )
269251 if bpms is not None :
270- orbit /= float (n_tune_meas )
252+ orbit /= float (n_avg_meas )
271253
272254 except Exception as ex :
273255 err = ex
256+ except KeyboardInterrupt as ex :
257+ aborted = True
274258 finally :
275- # TODO : Use set_and_wait once it is implemented !
259+ # Restore
276260 rf .frequency .set (f0 )
277- cb_data = {"step" : i , "rf" : f0 }
278- ok = self .send_callback (Action .RESTORE , callback , cb_data )
261+ self .send_callback (Action .RESTORE , {"step" : i , "rf" : f0 }, raiseException = False )
279262
280- if err :
263+ if err is not None :
281264 raise (err )
282265
283- self .fit (delta , Q , fit_order , orbit = orbit , fit_disp_order = fit_disp_order , do_plot = do_plot )
266+ if aborted :
267+ logger .warning (f"{ self .get_name ()} : measurement aborted" )
268+ return False
269+
270+ if fit_dispersion :
271+ self .fit (delta , Q , fit_order , orbit = orbit , fit_disp_order = fit_disp_order , do_plot = do_plot )
272+ else :
273+ self .fit (delta , Q , fit_order , do_plot = do_plot )
284274
285- return ok
275+ return True
286276
287277 def fit (self , deltas , Q , order , orbit = None , fit_disp_order = None , do_plot = False ):
288278 """
@@ -320,9 +310,15 @@ def fit(self, deltas, Q, order, orbit=None, fit_disp_order=None, do_plot=False):
320310 self .latest_measurement ["dispersion" ] = [dispx [:, 1 ], dispy [:, 1 ]] # First order dispersion
321311
322312 if do_plot :
323- fig = plt .figure ("Chromaticity_measurement" )
313+ if fit_disp_order is None :
314+ fig = plt .figure ("Chromaticity measurement" )
315+ cols = 1
316+ else :
317+ fig = plt .figure ("Chromaticity/Dispersion measurement" )
318+ cols = 2
319+
324320 for i in range (2 ):
325- ax = fig .add_subplot (2 , 1 , 1 + i )
321+ ax = fig .add_subplot (2 , cols , 1 + i )
326322 ax .scatter (deltas * 100 , Q [:, i ])
327323 title = ""
328324 for o in range (order , - 1 , - 1 ):
@@ -338,8 +334,17 @@ def fit(self, deltas, Q, order, orbit=None, fit_disp_order=None, do_plot=False):
338334
339335 ax .plot (deltas * 100 , np .polyval (chroma [i ][::- 1 ], deltas ))
340336 ax .set_title (title )
341- ax .set_xlabel ("Momentum Shift, dp/p [%]" )
342337 ax .set_ylabel ("%s Tune" % ["Horizontal" , "Vertical" ][i ])
343- # ax.legend()
338+ ax .set_xlabel ("Momentum Shift, dp/p [%]" )
339+
340+ if fit_disp_order is not None :
341+ ax = fig .add_subplot (2 , cols , 3 )
342+ ax .plot (dispx [:, 1 ])
343+ ax .set_ylabel ("Dispersion [m]" )
344+ ax = fig .add_subplot (2 , cols , 4 )
345+ ax .plot (dispy [:, 1 ])
346+ ax .set_xlabel ("BPM #" )
347+ ax .set_ylabel ("Dispersion [m]" )
348+
344349 fig .tight_layout ()
345350 plt .show ()
0 commit comments