Skip to content

Add atoctupole to Matlab version#1077

Open
TeresiaOlsson wants to merge 2 commits into
atcollab:masterfrom
TeresiaOlsson:matlab/add-atoctupole
Open

Add atoctupole to Matlab version#1077
TeresiaOlsson wants to merge 2 commits into
atcollab:masterfrom
TeresiaOlsson:matlab/add-atoctupole

Conversation

@TeresiaOlsson
Copy link
Copy Markdown

When saving a pyAT lattice to a m-file using at.save_m it can currently not be loaded in the Matlab version if the lattice contains octupoles since there is no atoctupole element in the Matlab version.

This is the output of at.save_m:

atoctupole('OMK1RP', 0.1, [0. 0. 0. 0.], [ 0.   0.   0. -50.]);

I have added an atoctupole element to the Matlab version based on the same syntax as atmultipole.

@TeresiaOlsson
Copy link
Copy Markdown
Author

However, the PR is related to issue #465. Is there a reason why the octupole hasn't been implemented similarly to the sextupole? To change that both in the Matlab and Python versions would make the octupole nicer to use.

@swhite2401 swhite2401 requested a review from lfarv May 13, 2026 08:59
@lfarv
Copy link
Copy Markdown
Contributor

lfarv commented May 13, 2026

@TeresiaOlsson : I agree that the Octupole should have been implemented similarly to the Sextupole. Here is a python implementation which does this while keeping the compatibility with the previous syntax:

class Octupole(Multipole):
    """Octupole element, with no changes from multipole at present."""

    _BUILD_ATTRIBUTES = [*LongElement._BUILD_ATTRIBUTES, "_O"]
    _stacklevel = 7  # Stacklevel for warnings

    DefaultOrder = 3

    def __init__(self, family_name: str, length: float, *args, **kwargs):
        """
        Args:
            family_name:    Name of the element
            length:         Element length [m]
            strength:       Octupolar strength [m^-4].

        Keyword arguments:
            PolynomB:           normal multipoles
            PolynomA:           skew multipoles
            MaxOrder:           Number of desired multipoles
            NumIntSteps=10:     Number of integration steps
            KickAngle:          Correction deviation angles (H, V)
            FieldScaling:       Scaling factor applied to the magnetic field
              (*PolynomA* and *PolynomB*)

        Default PassMethod: ``StrMPoleSymplectic4Pass``
        """
        kwargs.setdefault("PassMethod", "StrMPoleSymplectic4Pass")
        match args:
            case [poly_a, poly_b]:
                pass
            case [strength]:
                poly_a = kwargs.pop("PolynomA", [])
                poly_b = [0.0, 0.0, 0.0, strength]
            case []:
                poly_a = kwargs.pop("PolynomA", [])
                poly_b = kwargs.pop("PolynomB", [0.0, 0.0, 0.0, 0.0])
            case _:
                msg = "Octupole strength should be a float."
                raise ValueError(msg)
        super().__init__(family_name, length, poly_a, poly_b, **kwargs)

    # noinspection PyPep8Naming
    @property
    def _O(self) -> float:
        """Octupolar strength [m^-4]."""
        arr = self.PolynomB
        return 0.0 if len(arr) < 4 else float(arr[3])

    # noinspection PyPep8Naming
    @_O.setter
    def _O(self, strength):
        self.PolynomB[3] = strength

Now save_m outputs:

atoctupole('OC', 1.0, 50.0);...

With this you can implement the Matlab octupole in a nicer way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants