Skip to content

Commit 6e28b79

Browse files
Merge pull request #335 from python-accelerator-middle-layer/schema-registry-tuning-tools
Changes for schema registry for tuning tools
2 parents d6055c1 + cc12823 commit 6e28b79

14 files changed

Lines changed: 430 additions & 293 deletions

pyaml/common/holders/element_holder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from ...magnet.serialized_magnet import SerializedMagnets
2020
from ...rf.rf_plant import RFPlant
2121
from ...rf.rf_transmitter import RFTransmitter
22-
from ...tuning_tools.chromaticity_monitor import ChomaticityMonitor
22+
from ...tuning_tools.chromaticity_monitor import ChromaticityMonitor
2323
from ..abstract_aggregator import ScalarAggregator
2424
from ..element import Element
2525
from ..exception import PyAMLException
@@ -254,8 +254,8 @@ def add_tool(self, tool: Element):
254254

255255
# ---- Chromaticity -------------------------------------------------
256256

257-
def get_chromaticity_monitor(self, name: str) -> ChomaticityMonitor:
258-
obj = self._get("Chomaticity monitor", name, self._TOOLS)
257+
def get_chromaticity_monitor(self, name: str) -> ChromaticityMonitor:
258+
obj = self._get("Chromaticity monitor", name, self._TOOLS)
259259
return obj
260260

261261
def get_chromaticity_tuning(self, name: str) -> "Chromaticity":

pyaml/tuning_tools/chromaticity.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from .. import PyAMLException
55
from ..validation import DynamicValidation, register_schema
6-
from .chromaticity_monitor import ChomaticityMonitor
6+
from .chromaticity_monitor import ChromaticityMonitor
77
from .response_matrix_data import ResponseMatrixData
88
from .tuning_tool import TuningTool
99

@@ -61,7 +61,7 @@ def __init__(
6161

6262
# Invert matrix
6363
if self._response_matrix:
64-
self._response_matrix = np.array(self._response_matrix._cfg.matrix)
64+
self._response_matrix = np.array(self._response_matrix.matrix)
6565
self._correctionmat = np.linalg.pinv(self._response_matrix)
6666

6767
# TODO: Initialise first setpoint
@@ -84,11 +84,11 @@ def load(self, load_path: Path):
8484
Filename of the :class:`~.ResponseMatrixData` to load
8585
"""
8686
self._response_matrix = ResponseMatrixData.load(load_path)
87-
self._response_matrix = np.array(self._response_matrix._cfg.matrix)
87+
self._response_matrix = np.array(self._response_matrix.matrix)
8888
self._correctionmat = np.linalg.pinv(self._response_matrix)
8989

9090
@property
91-
def _cm(self) -> "ChomaticityMonitor":
91+
def _cm(self) -> "ChromaticityMonitor":
9292
self.check_peer()
9393
return self.peer.get_chromaticity_monitor(self._chromaticity_monitor_name)
9494

pyaml/tuning_tools/chromaticity_monitor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
logger = logging.getLogger(__name__)
1616

17-
PYAMLCLASS = "ChomaticityMonitor"
17+
PYAMLCLASS = "ChromaticityMonitor"
1818

1919

2020
class RChromaDispArray(ReadFloatArray):
@@ -23,7 +23,7 @@ class RChromaDispArray(ReadFloatArray):
2323
Returns arrays of shape (fit_order,2) or None
2424
"""
2525

26-
def __init__(self, parent: "ChomaticityMonitor", name: str, unit: str):
26+
def __init__(self, parent: "ChromaticityMonitor", name: str, unit: str):
2727
self._parent = parent
2828
self._name = name
2929
self._unit = unit
@@ -40,7 +40,7 @@ def unit(self) -> str:
4040

4141

4242
@register_schema
43-
class ChomaticityMonitor(MeasurementTool, DynamicValidation):
43+
class ChromaticityMonitor(MeasurementTool, DynamicValidation):
4444
"""
4545
Class providing access to a chromaticity monitor
4646
of a physical or simulated lattice. The monitor provides

pyaml/tuning_tools/chromaticity_response_matrix.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import logging
22
import time
3+
from dataclasses import asdict
34
from typing import Callable, Optional
45

56
import numpy as np
67

78
from ..common.constants import Action
89
from ..validation import DynamicValidation, register_schema
910
from .measurement_tool import MeasurementTool
10-
from .response_matrix_data import ConfigModel as ResponseMatrixDataConfigModel
11+
from .response_matrix_data import ResponseMatrixData
1112

1213
logger = logging.getLogger(__name__)
1314

@@ -252,12 +253,12 @@ def callback(action: Action, data:dict):
252253
logger.warning(f"{self.get_name()} : measurement aborted")
253254
return False
254255

255-
mat = ResponseMatrixDataConfigModel(
256+
mat = ResponseMatrixData(
256257
matrix=chromamat.T.tolist(),
257258
variable_names=sextus.names(),
258259
observable_names=[cm.get_name() + ".x", cm.get_name() + ".y"],
259260
)
260-
self.latest_measurement.update(mat.model_dump())
261+
self.latest_measurement.update(asdict(mat))
261262
self.latest_measurement["type"] = "pyaml.tuning_tools.response_matrix_data"
262263

263264
return True

pyaml/tuning_tools/dispersion.py

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
11
import logging
2-
from typing import Callable, Optional, Self
2+
from typing import Callable, Optional
33

4-
from pydantic import ConfigDict
54
from pySC.apps import measure_dispersion
65
from pySC.apps.codes import DispersionCode
76

87
from ..common.constants import Action
9-
from ..common.element import ElementConfigModel
10-
from ..common.holders.element_holder import ElementHolder
118
from ..external.pySC_interface import pySCInterface
9+
from ..validation import DynamicValidation, register_schema
1210
from .measurement_tool import MeasurementTool
1311

1412
logger = logging.getLogger(__name__)
1513

1614
PYAMLCLASS = "Dispersion"
1715

1816

19-
class ConfigModel(ElementConfigModel):
20-
"""
21-
Configuration model for dispersion measurement
17+
@register_schema
18+
class Dispersion(MeasurementTool, DynamicValidation):
19+
"""Measure beam dispersion by changing the RF frequency.
20+
21+
The measurement uses a :class:`pySCInterface` to change the frequency of
22+
an RF plant and acquire orbit data from a BPM array. Progress is reported
23+
through the callback mechanism provided by :class:`MeasurementTool`.
2224
2325
Parameters
2426
----------
27+
name : str
28+
Name of the dispersion measurement tool.
2529
bpm_array_name : str
26-
BPM array name
30+
Name of the BPM array used to measure the orbit.
2731
rf_plant_name : str
28-
RF plant name
32+
Name of the RF plant whose frequency is varied.
2933
frequency_delta : float
30-
Frequency delta for measurement
31-
"""
32-
33-
model_config = ConfigDict(arbitrary_types_allowed=True, extra="forbid")
34-
35-
bpm_array_name: str
36-
rf_plant_name: str
37-
frequency_delta: float
34+
RF-frequency change applied during the measurement.
3835
36+
Attributes
37+
----------
38+
bpm_array_name : str
39+
Name of the BPM array used for the measurement.
40+
rf_plant_name : str
41+
Name of the RF plant used for the measurement.
42+
frequency_delta : float
43+
RF-frequency change applied during the measurement.
44+
"""
3945

40-
class Dispersion(MeasurementTool):
41-
def __init__(self, cfg: ConfigModel):
42-
super().__init__(cfg.name)
43-
self._cfg = cfg
46+
def __init__(self, name: str, bpm_array_name: str, rf_plant_name: str, frequency_delta: float):
47+
super().__init__(name)
4448

45-
self.bpm_array_name = cfg.bpm_array_name
46-
self.rf_plant_name = cfg.rf_plant_name
47-
self.frequency_delta = cfg.frequency_delta
49+
self.bpm_array_name = bpm_array_name
50+
self.rf_plant_name = rf_plant_name
51+
self.frequency_delta = frequency_delta
4852

4953
def measure(
5054
self,

pyaml/tuning_tools/measurement_tool.py

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,12 @@
1717

1818

1919
class MeasurementToolConfigModel(ElementConfigModel):
20-
"""
21-
Measurement tool configuration model
22-
23-
Parameters
24-
----------
25-
n_step: int, optional
26-
Number of measurement step [-delta/n_step..delta/n_step]
27-
Default 1
28-
sleep_between_step: float, optional
29-
Default sleep time after an actuator excitation
30-
Default: 0
31-
n_avg_meas : int, optional
32-
Default number of measurement per step used for averaging
33-
Default 1
34-
sleep_between_meas: float, optional
35-
Default sleep time between two measurments
36-
Default: 0
37-
"""
38-
3920
model_config = ConfigDict(arbitrary_types_allowed=True, extra="forbid")
4021

41-
n_step: Optional[int] = 1
42-
sleep_between_step: Optional[float] = 0
43-
n_avg_meas: Optional[int] = 1
44-
sleep_between_meas: Optional[float] = 0
22+
n_step: int = 10
23+
sleep_between_step: float = 0
24+
n_avg_meas: int = 1
25+
sleep_between_meas: float = 0
4526

4627

4728
class MeasurementTool(Element, metaclass=ABCMeta):

pyaml/tuning_tools/orbit.py

Lines changed: 47 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import logging
2+
from dataclasses import asdict
23
from pathlib import Path
3-
from typing import TYPE_CHECKING, Literal, Optional, Union
4-
5-
try:
6-
from typing import Self # Python 3.11+
7-
except ImportError:
8-
from typing_extensions import Self # Python 3.10 and earlier
4+
from typing import Literal, Optional, Union
95

106
import numpy as np
11-
from pydantic import ConfigDict
12-
13-
if TYPE_CHECKING:
14-
from ..common.holders.element_holder import ElementHolder
157
from pySC import ResponseMatrix as pySC_ResponseMatrix
168
from pySC.apps import orbit_correction
179

1810
from ..arrays.magnet_array import MagnetArray
19-
from ..common.element import Element, ElementConfigModel
2011
from ..common.exception import PyAMLException
2112
from ..external.pySC_interface import pySCInterface
2213
from ..rf.rf_plant import RFPlant
14+
from ..validation import DynamicValidation, register_schema
2315
from .orbit_response_matrix_data import OrbitResponseMatrixData
2416
from .tuning_tool import TuningTool
2517

@@ -29,57 +21,56 @@
2921
PYAMLCLASS = "Orbit"
3022

3123

32-
class ConfigModel(ElementConfigModel):
33-
model_config = ConfigDict(arbitrary_types_allowed=True, extra="forbid")
34-
35-
bpm_array_name: str
36-
hcorr_array_name: str
37-
vcorr_array_name: str
38-
rf_plant_name: Optional[str] = None
39-
singular_values: Optional[int] = None
40-
singular_values_H: Optional[int] = None
41-
singular_values_V: Optional[int] = None
42-
virtual_target: float = 0
43-
response_matrix: Union[str, OrbitResponseMatrixData]
44-
24+
@register_schema
25+
class Orbit(TuningTool, DynamicValidation):
26+
def __init__(
27+
self,
28+
name: str,
29+
bpm_array_name: str,
30+
hcorr_array_name: str,
31+
vcorr_array_name: str,
32+
response_matrix: Union[str, OrbitResponseMatrixData],
33+
rf_plant_name: Optional[str] = None,
34+
singular_values: Optional[int] = None,
35+
singular_values_H: Optional[int] = None,
36+
singular_values_V: Optional[int] = None,
37+
virtual_target: float = 0,
38+
):
39+
super().__init__(name)
4540

46-
class Orbit(TuningTool):
47-
def __init__(self, cfg: ConfigModel):
48-
super().__init__(cfg.name)
49-
self._cfg = cfg
50-
self.bpm_array_name = cfg.bpm_array_name
51-
self.hcorr_array_name = cfg.hcorr_array_name
52-
self.vcorr_array_name = cfg.vcorr_array_name
41+
self.bpm_array_name = bpm_array_name
42+
self.hcorr_array_name = hcorr_array_name
43+
self.vcorr_array_name = vcorr_array_name
5344
self._pySC_response_matrix = None
45+
self.rf_plant_name = rf_plant_name
46+
self.virtual_target = virtual_target
5447

55-
self.virtual_target = cfg.virtual_target
56-
57-
if cfg.singular_values is None:
58-
if cfg.singular_values_H is None or cfg.singular_values_V is None:
48+
if singular_values is None:
49+
if singular_values_H is None or singular_values_V is None:
5950
raise PyAMLException(
6051
"Either `singular_values` or `singular_values_H` and `singular_values_V` must be provided."
6152
)
62-
self.singular_values_H = cfg.singular_values_H
63-
self.singular_values_V = cfg.singular_values_V
53+
self.singular_values_H = singular_values_H
54+
self.singular_values_V = singular_values_V
6455
else:
65-
if cfg.singular_values_H is not None or cfg.singular_values_V is not None:
56+
if singular_values_H is not None or singular_values_V is not None:
6657
raise PyAMLException(
6758
"Either `singular_values` or `singular_values_H` and `singular_values_V` must be provided, not both."
6859
)
69-
self.singular_values_H = cfg.singular_values
70-
self.singular_values_V = cfg.singular_values
60+
self.singular_values_H = singular_values
61+
self.singular_values_V = singular_values
7162

7263
# If the configuration response matrix is a filename, load it
73-
if type(cfg.response_matrix) is str:
64+
if type(response_matrix) is str:
7465
try:
75-
cfg.response_matrix = OrbitResponseMatrixData.load(cfg.response_matrix)
66+
self._response_matrix = OrbitResponseMatrixData.load(response_matrix)
7667
except Exception as e:
77-
logger.warning(f"Loading {cfg.response_matrix} failed {str(e)}")
78-
cfg.response_matrix = None
68+
logger.warning(f"Loading {response_matrix} failed {str(e)}")
69+
self._response_matrix = None
7970

8071
# Converts to self._pySC_response_matrix
81-
if cfg.response_matrix:
82-
self._set_response_matrix(cfg.response_matrix)
72+
if self._response_matrix:
73+
self._set_response_matrix(self._response_matrix)
8374

8475
self._hcorr: MagnetArray = None
8576
self._vcorr: MagnetArray = None
@@ -95,24 +86,25 @@ def load(self, load_path: Path):
9586
load_path : Path
9687
Filename of the :class:`~.OrbitResponseMatrixData` to load
9788
"""
98-
self._cfg.response_matrix = OrbitResponseMatrixData.load(load_path)
99-
self._set_response_matrix(self._cfg.response_matrix)
89+
self._response_matrix = OrbitResponseMatrixData.load(load_path)
90+
self._set_response_matrix(self.response_matrix)
10091

10192
def _set_response_matrix(self, mat):
102-
m = mat._cfg.model_dump()
93+
m = asdict(mat)
10394
m["input_names"] = m.pop("variable_names")
10495
m["output_names"] = m.pop("observable_names")
10596
m["input_planes"] = m.pop("variable_planes")
10697
m["output_planes"] = m.pop("observable_planes")
107-
self._cfg.response_matrix = mat
98+
m.pop("type", None)
99+
self._response_matrix = mat
108100
self._pySC_response_matrix = pySC_ResponseMatrix.model_validate(m)
109101

110102
@property
111103
def response_matrix(self) -> OrbitResponseMatrixData | None:
112104
"""
113105
Return the response matrix if it has been loaded None otherwise
114106
"""
115-
return self._cfg.response_matrix
107+
return self._response_matrix
116108

117109
def correct(
118110
self,
@@ -312,11 +304,11 @@ def get_rf_weight(self) -> float:
312304
return self._pySC_response_matrix.rf_weight
313305

314306
def post_init(self):
315-
self._hcorr = self.peer.magnets.get(self._cfg.hcorr_array_name)
316-
self._vcorr = self.peer.magnets.get(self._cfg.vcorr_array_name)
307+
self._hcorr = self.peer.magnets.get(self.hcorr_array_name)
308+
self._vcorr = self.peer.magnets.get(self.vcorr_array_name)
317309
hvElts = []
318310
hvElts.extend(self._hcorr)
319311
hvElts.extend(self._vcorr)
320312
self._hvcorr = MagnetArray("", hvElts)
321-
if self._cfg.rf_plant_name is not None:
322-
self._rf_plant = self.peer.rf.get(self._cfg.rf_plant_name)
313+
if self.rf_plant_name is not None:
314+
self._rf_plant = self.peer.rf.get(self.rf_plant_name)

0 commit comments

Comments
 (0)