Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pyaml/common/element_holder.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

if TYPE_CHECKING:
from ..accelerator import Accelerator
from ..tuning_tools.bba import BBA
from ..tuning_tools.chromaticity import Chromaticity
from ..tuning_tools.chromaticity_response_matrix import ChromaticityResponseMatrix
from ..tuning_tools.dispersion import Dispersion
Expand Down Expand Up @@ -343,6 +344,11 @@ def get_orm_tuning(self, name: str) -> "OrbitResponseMatrix":
def orm(self) -> "OrbitResponseMatrix":
return self.get_orm_tuning("DEFAULT_ORBIT_RESPONSE_MATRIX")

# ---- BBA --------------------------------------------------------

def get_bba(self, name: str) -> "BBA":
return self.__get("BBA tool", name, self.__TUNING_TOOLS)

# ---- Dispersive orbit --------------------------------------------

def get_dispersion_tuning(self, name: str) -> "Dispersion":
Expand Down
3 changes: 2 additions & 1 deletion pyaml/external/pySC_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

class pySCInterface:
set_wait_time: float = 0
read_wait_time: float = 0

def __init__(
self,
Expand All @@ -28,7 +29,7 @@ def __init__(
self.rf_plant = None

def get_orbit(self) -> Tuple[np.array, np.array]:
# we should wait here somehow according to polling rate
time.sleep(self.read_wait_time)
positions = self.bpm_array.positions.get()
return positions[:, 0], positions[:, 1]

Expand Down
188 changes: 188 additions & 0 deletions pyaml/tuning_tools/bba.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import logging
from typing import Callable, Optional

import numpy as np
import pySC
from pydantic import ConfigDict
from pySC.apps import measure_bba
from pySC.apps.bba import BBAAnalysis
from pySC.apps.codes import BBACode

from ..common.constants import Action
from ..external.pySC_interface import pySCInterface
from .measurement_tool import MeasurementTool, MeasurementToolConfigModel

logger = logging.getLogger(__name__)

PYAMLCLASS = "BBA"


class ConfigModel(MeasurementToolConfigModel):
"""
Configuration model for Beam Based Alignment.
BBA finds the magnetic center of a quad (zero crossing).

Parameters
----------
bpm_array_name : str
BPM array name (orbit)
bpm_name : str
BPM to be corrected (close to the quad)
hcorr_name : str
Horizontal corrector used to make a deviation in the quad
vcorr_name : str
Vertical corrector used to make a deviation in the quad
quad_name : str
Quadrupole used to find the center
hcorr_delta : float
Horizontal corrector delta strength
vcorr_delta : float
Vertical corrector delta strength
hquad_delta : float
Quadrupole delta strength (for h search)
vquad_delta : float
Quadrupole delta strength (for v search)

"""

model_config = ConfigDict(arbitrary_types_allowed=True, extra="forbid")

bpm_array_name: str
bpm_name: str
hcorr_name: str
vcorr_name: str
quad_name: str
hcorr_delta: float
vcorr_delta: float
hquad_delta: float
vquad_delta: float


class BBA(MeasurementTool):
def __init__(self, cfg: ConfigModel):
super().__init__(cfg.name)
self._cfg = cfg

def measure(
self,
sleep_between_step: Optional[float] = None,
n_avg_meas: Optional[int] = None,
sleep_between_meas: Optional[float] = None,
callback: Optional[Callable] = None,
):
"""
Measure BBA.

**Example**

.. code-block:: python

sr = Accelerator.load("MyAccelerator.yaml")
TODO

Parameters
----------
sleep_between_step: float
Default time sleep after steerer or quad exitation
Default: from config
n_avg_meas : int, optional
Default number of orbit measurement per step used for averaging
Default from config
sleep_between_meas: float
Default time sleep between two orbit measurment
Default: from config
callback : Callable, optional
example: callback(action:int, callback_data: 'Complicated struct')
callback is executed after each strength setting and after each orbit
reading.
If the callback returns false, then the process is aborted.
"""
nb_meas = n_avg_meas if n_avg_meas is not None else self._cfg.n_avg_meas
sleep_step = sleep_between_step if sleep_between_step is not None else self._cfg.sleep_between_step
sleep_meas = sleep_between_meas if sleep_between_meas is not None else self._cfg.sleep_between_meas

element_holder = self._peer
interface = pySCInterface(
element_holder=element_holder,
bpm_array_name=self._cfg.bpm_array_name,
)
interface.set_wait_time = sleep_step
interface.read_wait_time = sleep_meas

bpms_names = element_holder.get_bpms(self._cfg.bpm_array_name).names()

bba_pySC_config = {
"number": bpms_names.index(self._cfg.bpm_name),
"QUAD": self._cfg.quad_name,
"HCORR": self._cfg.hcorr_name,
"VCORR": self._cfg.vcorr_name,
"HCORR_delta": self._cfg.hcorr_delta,
"QUAD_dk_H": self._cfg.hquad_delta,
"VCORR_delta": self._cfg.vcorr_delta,
"QUAD_dk_V": self._cfg.vquad_delta,
"QUAD_is_skew": False,
}

generator = measure_bba(
interface=interface,
bpm_name=self._cfg.bpm_name,
config=bba_pySC_config,
shots_per_orbit=nb_meas,
n_corr_steps=self._cfg.n_step,
bipolar=False,
skip_save=True,
)

pySC.disable_pySC_rich()
aborted = False
err = None
idx = 0
self._latest_measurement = {"HOffset": np.nan, "VOffset": np.nan, "HOffsetError": np.nan, "VOffsetError": np.nan}

try:
self._register_callback(callback)
self._init_measure()
for code, measurement_object in generator:
# print(f"Got code: {code.name}")
if code == BBACode.HORIZONTAL_DONE:
result = BBAAnalysis.analyze(measurement_object.H_data)
self.latest_measurement["HOffset"] = result.offset
self.latest_measurement["HOffsetError"] = result.offset_error
if code == BBACode.VERTICAL_DONE:
result = BBAAnalysis.analyze(measurement_object.V_data)
self.latest_measurement["VOffset"] = result.offset
self.latest_measurement["VOffsetError"] = result.offset_error
idx += 1
except Exception as ex:
err = ex
except KeyboardInterrupt as ex:
aborted = True
finally:
# Restore steerer/quad strength
# TODO
self.send_callback(
Action.RESTORE,
{"idx": idx},
raiseException=False,
)

if err is not None:
raise (err)

if aborted:
logger.warning(f"{self.get_name()} : measurement aborted (settings not restored)")
return False

return True

def h_offset(self) -> float:
return self.latest_measurement["HOffset"]

def h_offset_error(self) -> float:
return self.latest_measurement["HOffsetError"]

def v_offset(self) -> float:
return self.latest_measurement["VOffset"]

def v_offset_error(self) -> float:
return self.latest_measurement["VOffsetError"]
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dependencies = [
"accelerator-toolbox>=0.6.1",
"PyYAML>=6.0.2",
"pydantic>=2.11.7",
"accelerator-commissioning==1.1.0", #pySC
"accelerator-commissioning==1.3.0", #pySC
"h5py",
"matplotlib"
]
Expand Down
24 changes: 24 additions & 0 deletions tests/config/EBSOrbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ arrays:
elements:
- BPM*
devices:
- type: pyaml.tuning_tools.bba
name: BBA-BPM_C04-03
bpm_array_name: BPM
bpm_name : BPM_C04-03
hcorr_name : SF2E-C02-H
vcorr_name : SD1A-C26-V
quad_name : QF6B-C04
n_step: 7
hcorr_delta : 3.829380038425445e-05
vcorr_delta : 4.198292876029707e-05
hquad_delta : 0.005762885683993067
vquad_delta : 0.009329539724510462
- type: pyaml.diagnostics.tune_monitor
name: BETATRON_TUNE
tune_h: srdiag/beam-tune/main/Qh
Expand Down Expand Up @@ -85,6 +97,18 @@ devices:
harmonic: 1
distribution: 1
voltage: sys/ringsimulator/ebs/RfVoltage
- type: pyaml.magnet.quadrupole
name: QF6B-C04
model:
type: pyaml.magnet.linear_model
calibration_factor: 0.998960997
crosstalk: 0.99918
curve:
type: pyaml.magnet.csvcurve
file: sr/magnet_models/QF6_strength.csv
unit: 1/m
hardware_unit: A
powerconverter: srmag/vps-qf6/c04-b
- type: pyaml.magnet.cfm_magnet
name: SH1A-C04
mapping:
Expand Down
2 changes: 1 addition & 1 deletion tests/config/catalogs/ebs_catalogs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ entries:
- catalogs/ebs_bpm_catalogs.yaml
- catalogs/ebs_rf_catalogs.yaml
- catalogs/ebs_diag_catalogs.yaml
- catalogs/ebs_qf1_qd2_catalogs.yaml
- catalogs/ebs_magnets_catalogs.yaml
- catalogs/ebs_sh_catalogs.yaml
- catalogs/ebs_sext_catalogs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -866,3 +866,10 @@
attribute: srmag/vps-qd2/c03-a/current
range: [10,109]
unit: A
- type: tango.pyaml.static_catalog_entry
key: srmag/vps-qf6/c04-b
device:
type: tango.pyaml.attribute
attribute: srmag/vps-qf6/c04-b
range: [10,109]
unit: A
101 changes: 101 additions & 0 deletions tests/config/sr/magnet_models/QF6_strength.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
0, 0
10, 4.8381057
11.01010101, 5.309790224
12.02020202, 5.781444181
13.03030303, 6.253037002
14.04040404, 6.72453812
15.05050505, 7.195916967
16.06060606, 7.667150484
17.07070707, 8.138241462
18.08080808, 8.609198255
19.09090909, 9.080029216
20.1010101, 9.5507427
21.11111111, 10.02134783
22.12121212, 10.49185598
23.13131313, 10.96227898
24.14141414, 11.43262864
25.15151515, 11.90291672
26.16161616, 12.37313684
27.17171717, 12.84323594
28.18181818, 13.31315356
29.19191919, 13.78282926
30.2020202, 14.25220272
31.21212121, 14.7212447
32.22222222, 15.18999532
33.23232323, 15.65850402
34.24242424, 16.12682031
35.25252525, 16.59499334
36.26262626, 17.06303632
37.27272727, 17.53089254
38.28282828, 17.99849728
39.29292929, 18.46578582
40.3030303, 18.93269377
41.31313131, 19.39918272
42.32323232, 19.86525832
43.33333333, 20.33093051
44.34343434, 20.79620918
45.35353535, 21.26110358
46.36363636, 21.72558595
47.37373737, 22.18957373
48.38383838, 22.65297993
49.39393939, 23.11571754
50.4040404, 23.57770039
51.41414141, 24.03887417
52.42424242, 24.49922597
53.43434343, 24.95874566
54.44444444, 25.4174231
55.45454545, 25.87522826
56.46464646, 26.33154575
57.47474747, 26.7850931
58.48484848, 27.2345516
59.49494949, 27.6786025
60.50505051, 28.11592527
61.51515152, 28.54515768
62.52525253, 28.96489584
63.53535354, 29.37373405
64.54545455, 29.7702666
65.55555556, 30.15310646
66.56565657, 30.52120955
67.57575758, 30.87383285
68.58585859, 31.21024353
69.5959596, 31.52970877
70.60606061, 31.83155581
71.61616162, 32.11601021
72.62626263, 32.38398911
73.63636364, 32.63642744
74.64646465, 32.87426012
75.65656566, 33.09843152
76.66666667, 33.31000256
77.67676768, 33.51011278
78.68686869, 33.69990319
79.6969697, 33.8805148
80.70707071, 34.05306116
81.71717172, 34.21837218
82.72727273, 34.37711048
83.73737374, 34.52993649
84.74747475, 34.67751066
85.75757576, 34.82045497
86.76767677, 34.95905659
87.77777778, 35.09343031
88.78787879, 35.22368951
89.7979798, 35.34994755
90.80808081, 35.47232532
91.81818182, 35.59099919
92.82828283, 35.70617041
93.83838384, 35.81804036
94.84848485, 35.92681041
95.85858586, 36.03267776
96.86868687, 36.13581324
97.87878788, 36.23637744
98.88888889, 36.33453092
99.8989899, 36.43043424
100.9090909, 36.52421516
101.9191919, 36.6158239
102.9292929, 36.70515097
103.9393939, 36.7920868
104.9494949, 36.87652186
105.959596, 36.95843775
106.969697, 37.03823987
107.979798, 37.11645666
108.989899, 37.19361655
110, 37.270248
Loading
Loading