From 1fcfb64bd9764047f6a6c341d1fc0f2d3855c5f2 Mon Sep 17 00:00:00 2001 From: PONS Date: Thu, 8 Oct 2026 11:07:12 +0200 Subject: [PATCH 1/2] Support for transfer line --- pyaml/lattice/abstract_impl.py | 25 ++- pyaml/lattice/simulator.py | 19 +- pyaml/lattice/twiss_in.py | 52 ++++++ tests/config/tl2/tl2.mat | Bin 0 -> 69648 bytes tests/config/tl2/tl2.yml | 215 +++++++++++++++++++++++ tests/tuning_tools/test_transfer_line.py | 47 +++++ 6 files changed, 348 insertions(+), 10 deletions(-) create mode 100644 pyaml/lattice/twiss_in.py create mode 100644 tests/config/tl2/tl2.mat create mode 100644 tests/config/tl2/tl2.yml create mode 100644 tests/tuning_tools/test_transfer_line.py diff --git a/pyaml/lattice/abstract_impl.py b/pyaml/lattice/abstract_impl.py index 615d7ea0..a734a587 100644 --- a/pyaml/lattice/abstract_impl.py +++ b/pyaml/lattice/abstract_impl.py @@ -708,13 +708,14 @@ class BPMScalarAggregator(ScalarAggregator): Return the value unit. """ - def __init__(self, ring: at.Lattice): + def __init__(self, ring: at.Lattice, twiss_in: dict): """ Initialize the BPMScalarAggregator. """ self._lattice = ring self._refpts = [] self._matrices = [] + self._twiss_in = twiss_in def add_elem(self, elem: at.Element): """ @@ -764,9 +765,15 @@ def unit(self) -> str: def _transform(self) -> np.array: """Transform the configured value for the lattice interface.""" - _, orbit = at.find_orbit(self._lattice, refpts=self._refpts) + if self._twiss_in is None: + _, orbit = at.find_orbit(self._lattice, refpts=self._refpts) + pts = orbit[:, [0, 2]] # Extract x,y + else: + _, _, elemdata = at.get_optics(self._lattice, refpts=self._refpts, twiss_in=self._twiss_in) + pts = [e.closed_orbit[[0, 2]] for e in elemdata] + ones = np.ones(len(self._refpts)) - pts = orbit[:, [0, 2]] # Extract x,y + # Transfom BPM coordinates (offset and tilt) # Batch matrices multiplication (homogeneous coordinates) return np.matmul(self._matrices, np.column_stack([pts, ones])[:, :, None]).squeeze(-1) @@ -856,19 +863,25 @@ class RBpmArray(abstract.ReadFloatArray): Return the value unit. """ - def __init__(self, element: at.Element, lattice: at.Lattice): + def __init__(self, element: at.Element, lattice: at.Lattice, twiss_in: dict): """ Initialize the RBpmArray. """ self._element = element self._lattice = lattice + self._twiss_in = twiss_in # Gets the value def get(self) -> np.array: """Return the current value.""" index = self._lattice.index(self._element) - _, orbit = at.find_orbit(self._lattice, refpts=index) - pts = orbit[0, [0, 2]] + if self._twiss_in is None: + _, orbit = at.find_orbit(self._lattice, refpts=index) + pts = orbit[0, [0, 2]] + else: + _, _, elemdata = at.get_optics(self._lattice, refpts=index, twiss_in=self._twiss_in) + pts = elemdata[0].closed_orbit[[0, 2]] + # Transfom BPM coordinates (offset and tilt) return np.dot(self._element._transform, np.hstack([pts, 1])) # Use homogeneous coordinates # Gets the unit of the value diff --git a/pyaml/lattice/simulator.py b/pyaml/lattice/simulator.py index 738ddaf2..e0922ae3 100644 --- a/pyaml/lattice/simulator.py +++ b/pyaml/lattice/simulator.py @@ -48,6 +48,7 @@ from ..tuning_tools.tuning_tool import TuningTool from ..validation import DynamicValidation, register_schema from .lattice_elements_linker import LatticeElementsLinker +from .twiss_in import TwissIn if TYPE_CHECKING: from ..configuration.unbound_element import UnboundElement @@ -79,6 +80,8 @@ class Simulator(ElementHolder, DynamicValidation): mat_key : str, optional Variable name of the lattice when loading a MATLAB ``.mat`` lattice file. + twiss_in : TwissIn, optional + Describe input beam parameters of a tranfer line, when not specified the lattice is considered as a ring linker : LatticeElementsLinker, optional Custom linker used to associate PyAML elements with PyAT lattice elements. If omitted, elements are matched by name. @@ -123,6 +126,7 @@ def __init__( name: str, lattice: str, mat_key: str | None = None, + twiss_in: TwissIn | None = None, linker: LatticeElementsLinker | None = None, description: str | None = None, ): @@ -135,6 +139,11 @@ def __init__( self._lattice = lattice self._mat_key = mat_key self.description = description + self.twiss_in = twiss_in + if twiss_in is None: + self._at_twiss_in = None + else: + self._at_twiss_in = self.twiss_in._to_at() path: Path = ROOT.get() / self._lattice @@ -226,9 +235,9 @@ def create_bpm_aggregators(self, bpms: list[BPM]) -> list[ScalarAggregator]: list[ScalarAggregator] Aggregators for combined, horizontal, and vertical BPM positions. """ - agg = BPMScalarAggregator(self.get_lattice()) - aggh = BPMHScalarAggregator(self.get_lattice()) - aggv = BPMVScalarAggregator(self.get_lattice()) + agg = BPMScalarAggregator(self.get_lattice(), self._at_twiss_in) + aggh = BPMHScalarAggregator(self.get_lattice(), self._at_twiss_in) + aggv = BPMVScalarAggregator(self.get_lattice(), self._at_twiss_in) for b in bpms: e = self.get_at_elems(b)[0] agg.add_elem(e) @@ -293,7 +302,9 @@ def _fill_bpm(self, bpm: BPM) -> None: raise PyAMLException(f"BPM {bpm.get_name()} offset must be a 2-element array.") update_bpm_transform_matrix(bpm_elt) self.diagnostic.bpm.add( - bpm.attach(self, RBpmArray(bpm_elt, self.ring), RWBpmOffsetArray(bpm_elt), RWBpmTiltScalar(bpm_elt)) + bpm.attach( + self, RBpmArray(bpm_elt, self.ring, self._at_twiss_in), RWBpmOffsetArray(bpm_elt), RWBpmTiltScalar(bpm_elt) + ) ) def _fill_rf_plant(self, rf_plant: RFPlant) -> None: diff --git a/pyaml/lattice/twiss_in.py b/pyaml/lattice/twiss_in.py new file mode 100644 index 00000000..51ae9899 --- /dev/null +++ b/pyaml/lattice/twiss_in.py @@ -0,0 +1,52 @@ +""" +Initial input beam parameters for a transfer line +""" + +import numpy + +from ..validation import DynamicValidation, register_schema + +# Define the main class name for this module +PYAMLCLASS = "TwissIn" + + +@register_schema +class TwissIn(DynamicValidation): + """ + Class that describes input beam parameters of a transfer line + + Attributes + ---------- + alpha : list[float] + Initial alpha parameters (x,y) + beta : list[float] | None + Initial beta parameters (x,y), default [0,0] + position : list[float] | None + Initial position (6D), default [0,0,0,0,0,0] + dispersion : list[float] | None + Initial dispersion (4D), default [0,0,0,0] + """ + + def __init__( + self, + alpha: list[float], + beta: list[float] = None, + position: list[float] = None, + dispersion: list[float] = None, + ): + """ + Create TwissIn object + """ + super().__init__() + self.alpha = alpha + self.beta = beta if beta is not None else [0.0, 0.0] + self.position = position if position is not None else [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + self.dispersion = dispersion if dispersion is not None else [0.0, 0.0, 0.0, 0.0] + + def _to_at(self) -> dict: + return { + "beta": numpy.array(self.alpha, dtype=float), + "alpha": numpy.array(self.beta, dtype=float), + "closed_orbit": numpy.array(self.position, dtype=float), + "dispersion": numpy.array(self.dispersion, dtype=float), + } diff --git a/tests/config/tl2/tl2.mat b/tests/config/tl2/tl2.mat new file mode 100644 index 0000000000000000000000000000000000000000..b871250f4aad43cb45c7b86cf12aea470f06e401 GIT binary patch literal 69648 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 literal 0 HcmV?d00001 diff --git a/tests/config/tl2/tl2.yml b/tests/config/tl2/tl2.yml new file mode 100644 index 00000000..c722ef6e --- /dev/null +++ b/tests/config/tl2/tl2.yml @@ -0,0 +1,215 @@ +type: pyaml.accelerator +facility: ESRF +machine: tl2 +energy: 6e9 + +simulators: + - type: pyaml.lattice.simulator + lattice: tl2.mat + name: design + twiss_in: + class: pyaml.lattice.twiss_in.TwissIn + alpha: [-1.71, 1.47] + beta: [5.9 , 5.65] + dispersion: [-0.145, -0.092, 0, 0] + +data_folder: /data/store + +arrays: + + - type: pyaml.arrays.magnet + name: HCORR + elements: + - CH* + - type: pyaml.arrays.magnet + name: VCORR + elements: + - CV* + - type: pyaml.arrays.bpm + name: BPMS + elements: + - BPM* + +devices: + + - class: pyaml.tuning_tools.orbit.Orbit + bpm_array_name: BPMS + hcorr_array_name: HCORR + vcorr_array_name: VCORR + name: DEFAULT_ORBIT_CORRECTION + singular_values: 5 + response_matrix: ideal_tl2_orm.json + + - class: pyaml.tuning_tools.orbit_response_matrix.OrbitResponseMatrix + bpm_array_name: BPMS + hcorr_array_name: HCORR + vcorr_array_name: VCORR + corrector_delta: 1e-6 + name: DEFAULT_ORBIT_RESPONSE_MATRIX + + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_CV1 + x_pos: tl2/d-bpm/cv1/X_Position + y_pos: tl2/d-bpm/cv1/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_CH3 + x_pos: tl2/d-bpm/d2/X_Position + y_pos: tl2/d-bpm/d2/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_QD5 + x_pos: tl2/d-bpm/q5/X_Position + y_pos: tl2/d-bpm/q5/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_QF6 + x_pos: tl2/d-bpm/q6/X_Position + y_pos: tl2/d-bpm/q6/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_SX + x_pos: tl2/d-bpm/sx/X_Position + y_pos: tl2/d-bpm/sx/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_QD8 + x_pos: tl2/d-bpm/q8/X_Position + y_pos: tl2/d-bpm/q8/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_QD12 + x_pos: tl2/d-bpm/q12/X_Position + y_pos: tl2/d-bpm/q12/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_QF14 + x_pos: tl2/d-bpm/q14/X_Position + y_pos: tl2/d-bpm/q14/Z_Position + - class: pyaml.diagnostics.bpm.bpm.BPM + name: BPM_S1PM + x_pos: tl2/d-bpm/s1pm/X_Position + y_pos: tl2/d-bpm/s1pm/Z_Position + + - class: pyaml.magnet.hcorrector.HCorrector + name: CH1 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch1/Current + - class: pyaml.magnet.hcorrector.HCorrector + name: CH2 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch2/Current + - class: pyaml.magnet.hcorrector.HCorrector + name: CH3 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch3/Current + - class: pyaml.magnet.hcorrector.HCorrector + name: CH4 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch4/Current + - class: pyaml.magnet.hcorrector.HCorrector + name: CH5 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch5/Current + - class: pyaml.magnet.hcorrector.HCorrector + name: CH6 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch6/Current + - class: pyaml.magnet.hcorrector.HCorrector + name: CH7 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch7/Current + - class: pyaml.magnet.hcorrector.HCorrector + name: CH8 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/ch8/Current + + - class: pyaml.magnet.vcorrector.VCorrector + name: CV0 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv0/Current + - class: pyaml.magnet.vcorrector.VCorrector + name: CV1 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv1/Current + - class: pyaml.magnet.vcorrector.VCorrector + name: CV3 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv3/Current + - class: pyaml.magnet.vcorrector.VCorrector + name: CV4 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv4/Current + - class: pyaml.magnet.vcorrector.VCorrector + name: CV5 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv5/Current + - class: pyaml.magnet.vcorrector.VCorrector + name: CV6 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv6/Current + - class: pyaml.magnet.vcorrector.VCorrector + name: CV7 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv7/Current + - class: pyaml.magnet.vcorrector.VCorrector + name: CV8 + model: + class: pyaml.magnet.linear_model.LinearMagnetModel + calibration_factor: 161e-6 + unit: 1/m + hardware_unit: A + powerconverter: tl2/ps-c1/cv8/Current diff --git a/tests/tuning_tools/test_transfer_line.py b/tests/tuning_tools/test_transfer_line.py new file mode 100644 index 00000000..cd4dfe3e --- /dev/null +++ b/tests/tuning_tools/test_transfer_line.py @@ -0,0 +1,47 @@ +import numpy +import pytest + +from pyaml.accelerator import Accelerator + + +def test_transfer_line(): + tl2 = Accelerator.load("tests/config/tl2/tl2.yml") + tl2.design.tool.orm.measure() + orm_data = tl2.design.tool.orm.get() + mat = numpy.array(orm_data["matrix"]) + + orm_hv = mat[:9, 8:] + orm_vh = mat[9:, :8] + assert numpy.allclose(orm_hv, 0) + assert numpy.allclose(orm_vh, 0) + + orm_h = mat[:9, :8] + orm_v = mat[9:, 8:] + + assert numpy.allclose( + orm_h[8, :], [1.49746082, 3.11487458, 10.60522905, -0.84014865, -19.27774502, -14.92314963, 1.29287711, 1.60223207] + ) + assert numpy.allclose( + orm_v[8, :], [10.95115436, 10.56436269, 16.43975007, 32.48697172, 12.7218589, 10.31009581, 10.46821268, 2.60568252] + ) + + tl2.design.tool.orm.save("tl2.json") + tl2.design.tool.orbit.load("tl2.json") + + # Mangle the orbit + hcorr = tl2.design.magnets.get("HCORR") + vcorr = tl2.design.magnets.get("VCORR") + numpy.random.seed(1) + std_kick = 1e-6 # rad + hcorr.strengths.set(hcorr.strengths.get() + std_kick * numpy.random.normal(size=len(hcorr))) + vcorr.strengths.set(vcorr.strengths.get() + std_kick * numpy.random.normal(size=len(vcorr))) + + orbit = tl2.design.diagnostic.bpms.get("BPMS").positions + assert numpy.allclose(numpy.std(orbit.get(), axis=0), [1.78601145e-05, 3.57671504e-05]) + + tl2.design.tool.orbit.correct() + tl2.design.tool.orbit.correct() + + assert numpy.allclose(numpy.std(orbit.get(), axis=0), [7.50786048e-07, 7.26005222e-07]) + + assert numpy.allclose(orbit.get()[2], tl2.design.diagnostic.bpm.get("BPM_QD5").positions.get()) From 1b3c17f750b7ea7d99aaef439acd0555706dcc37 Mon Sep 17 00:00:00 2001 From: PONS Date: Thu, 8 Oct 2026 11:18:33 +0200 Subject: [PATCH 2/2] Fix typing --- pyaml/lattice/twiss_in.py | 6 +++--- tests/config/tl2/tl2.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyaml/lattice/twiss_in.py b/pyaml/lattice/twiss_in.py index 51ae9899..c42ee6c7 100644 --- a/pyaml/lattice/twiss_in.py +++ b/pyaml/lattice/twiss_in.py @@ -30,9 +30,9 @@ class TwissIn(DynamicValidation): def __init__( self, alpha: list[float], - beta: list[float] = None, - position: list[float] = None, - dispersion: list[float] = None, + beta: list[float] | None = None, + position: list[float] | None = None, + dispersion: list[float] | None = None, ): """ Create TwissIn object diff --git a/tests/config/tl2/tl2.yml b/tests/config/tl2/tl2.yml index c722ef6e..8509e34d 100644 --- a/tests/config/tl2/tl2.yml +++ b/tests/config/tl2/tl2.yml @@ -4,7 +4,7 @@ machine: tl2 energy: 6e9 simulators: - - type: pyaml.lattice.simulator + - class: pyaml.lattice.simulator.Simulator lattice: tl2.mat name: design twiss_in: