Operational interface

Operational measurements use a small interface object to connect pySC to a machine or control system. The interface owns all hardware-specific details: reading orbits, reading setpoints, writing setpoints, waiting for devices to settle, and triggering turn-by-turn acquisition when needed.

The base methods required by pySC operational tools are:

get_orbit()

Return the current horizontal and vertical orbit arrays.

get(name)

Return one magnet strength in physics units.

set(name, value)

Set one magnet strength in physics units and wait until it is settled.

get_many(names)

Return a dictionary mapping magnet names to strengths.

set_many(data)

Set many magnet strengths from a dictionary and wait until they are settled.

Some workflows need extra methods. Orbit correction against an operational reference uses get_ref_orbit(). RF correction uses get_rf_main_frequency() and set_rf_main_frequency().

For first-turn correction, InterfaceInjection.get_orbit() should return the turn-by-turn trajectory flattened with order="F". Its get_ref_orbit() method should return a reference trajectory with the same shape and flattening.

Download the skeleton: interface.py

examples/operation/interface.py
"""Control-system interface skeleton for operational pySC examples."""

from pathlib import Path

import numpy as np

from pySC.apps.interface import AbstractInterface


DATA_FOLDER = Path("data")


class Interface(AbstractInterface):
    """Implement these methods for the target control system."""

    def get_orbit(self) -> tuple[np.ndarray, np.ndarray]:
        """Return the current horizontal and vertical orbit arrays."""
        raise NotImplementedError("Implement get_orbit() for your control system.")

    def get_ref_orbit(self) -> tuple[np.ndarray, np.ndarray]:
        """Return the horizontal and vertical reference orbit arrays."""
        raise NotImplementedError("Implement get_ref_orbit() for your control system.")

    def get(self, name: str) -> float:
        """Return one magnet strength in physics units."""
        raise NotImplementedError("Implement get() for your control system.")

    def set(self, name: str, value: float) -> None:
        """Set one magnet strength in physics units and wait until it is settled."""
        raise NotImplementedError("Implement set() for your control system.")

    def get_many(self, names: list[str]) -> dict[str, float]:
        """Return magnet strengths as a mapping from control name to value."""
        raise NotImplementedError("Implement get_many() for your control system.")

    def set_many(self, data: dict[str, float]) -> None:
        """Set multiple magnet strengths and wait until they are settled."""
        raise NotImplementedError("Implement set_many() for your control system.")

    def get_rf_main_frequency(self) -> float:
        """Return the main RF frequency in Hz if RF correction is used."""
        raise NotImplementedError("Implement get_rf_main_frequency() if RF correction is used.")

    def set_rf_main_frequency(self, frequency: float) -> None:
        """Set the main RF frequency in Hz if RF correction is used."""
        raise NotImplementedError("Implement set_rf_main_frequency() if RF correction is used.")


class InterfaceInjection(Interface):
    """Interface variant for first-turn or multi-turn trajectory measurements."""

    n_turns: int = 1
    trigger_injection: bool = False

    def get_orbit(self) -> tuple[np.ndarray, np.ndarray]:
        """Return turn-by-turn trajectory arrays flattened with order='F'."""
        raise NotImplementedError("Implement get_orbit() for turn-by-turn data.")

    def get_ref_orbit(self) -> tuple[np.ndarray, np.ndarray]:
        """Return the matching reference trajectory flattened with order='F'."""
        raise NotImplementedError("Implement get_ref_orbit() for turn-by-turn data.")