Skip to content

SerializedMagnets: get/set are not consitent and design/live disagree #461

Open
0 of 1 issue completed

Description

@GamelinAl

Serialized magnets (N magnets on one power supply: pyaml.magnet.serialized_magnet + LinearSerializedMagnetModel) do not follow one consistent convention. Depending on the accessor and the mode, a value means one magnet, the sum over the family, or the family total split by magnet length. As a result:

  • x.set(x.get()) changes the machine for several accessors, including writing N × the current to the power supply in live
  • the same call gives different results in design and live, often by a factor N.

This was found during a control-room test at SOLEIL. The S9 family (16 sextupoles, one PS) read family.hardware.get() == -3512 A while the PS showed −219 A.

This issue assume that the #458 issue is fixed by #460

Proposed behavior

We need to agree on what the different accessors should do for serialized magnets.

Serialized magnets have a single degree of freedom, the PS current I. Magnet i has strength k_i = f_i(I), where f_i is sub-model i.

Accessor get() set(x)
family.hardware I write I = x
family.strength total integrated strength S = Σ f_i(I) solve Σ f_i(I) = x for one I, write it
magnet[i].hardware I write I = x (all magnets follow)
magnet[i].strength k_i = f_i(I) write I = f_i⁻¹(x); the others follow as f_j(I). No length weighting.

@simoneliuzzo @gubaidulinvadim @TeresiaOlsson @kparasch @JeanLucPons @gupichon-soleil @Amoutardier
I need your help to see if we agree on this before this can be corrected.

Once we agree I'll make sub issues so we can start to fix this.

Activity

  1. added theissue type on Oct 1, 2026
  2. kparasch commented on Oct 1, 2026

    @kparasch
    Member

    I agree except on the family.strength. I don't see any use-case for the total integrated strength.

    Typically, all magnets that are serialized are the same type of magnets so they share the I = f(x) relation. There it would make sense for family.strength to return the strength that is equal to all magnets.

    For the funky case where the magnets don't have the same length, controlling by strength sounds awkward to me.

    Perhaps a compromise could be to have family.strength equal to average strength S = Σ f_i(I) / N ?
    Then at least in the typical case, family.strength falls back to the strength that is equal to all magnets.
    The same follows for its set logic, solve Σ f_i(I) / N = x for one I, write it

  3. TeresiaOlsson commented on Oct 1, 2026

    @TeresiaOlsson
    Member

    I don't think it is guaranteed that I = f(x) is the same for shared magnets? I think there are labs which measure all magnets and have a separate excitation curve per magnet despite that they are powered in series.

    I like the idea to have family.strength return the average strength. Then if you want to know the strength of the individual magnet you need to do magnet.strength instead.

  4. kparasch commented on Oct 1, 2026

    @kparasch
    Member

    I don't think it is guaranteed that I = f(x) is the same for shared magnets? I think there are labs which measure all magnets and have a separate excitation curve per magnet despite that they are powered in series.

    You are right, then I = f(x) is still approximately correct. I would still think that S = Σ f_i(I) / N is sensible in that case.

  5. TeresiaOlsson commented on Oct 1, 2026

    @TeresiaOlsson
    Member

    I don't think it is guaranteed that I = f(x) is the same for shared magnets? I think there are labs which measure all magnets and have a separate excitation curve per magnet despite that they are powered in series.

    You are right, then I = f(x) is still approximately correct. I would still think that S = Σ f_i(I) / N is sensible in that case.

    I agree. Then you can use get and set on the family level to set the average and if you care about the individual strengths you can still read them on the magnet level. And if you at some point in the future buy more power supplies and start to split up families the values will not change much. You just gain the freedom to being able to control the individual strengths and not just read them.

    Returning the total integrated strength I don't really understand. That doesn't sound to me like a useful parameter?

  6. GamelinAl commented on Oct 1, 2026

    @GamelinAl
    MemberAuthor

    I agree with you both, it make sense to go this way. The table is now:

    Accessor get() set(x)
    family.hardware I write I = x
    family.strength average strength S = Σ f_i(I) / N solve Σ f_i(I) / N = x for one I, write it
    magnet[i].hardware I write I = x (all magnets follow)
    magnet[i].strength k_i = f_i(I) write I = f_i⁻¹(x); the others follow as f_j(I). No length weighting.

    From this definition, do we agree that tuning tools should use the family, not the individual magnets?
    Should we provide an accessor to the total integrated strength? It would avoid having to do family.strength * len(magnets_inside_fam) and would make clear that family.strength is actually the average strength.

  7. kparasch commented on Oct 1, 2026

    @kparasch
    Member

    From this definition, do we agree that tuning tools should use the family, not the individual magnets?

    I think this is important and I agree but it is not a simple matter.

    From the point of view of configuration, the most straightforward way would be to use arrays. I am not fully up to date with how arrays work. Maybe it is already possible, but I believe it may be ok if an array can contain both individually-powered devices and a serialized family. (Here I mean that set/get of strength/hardware can be done on the array.)

    For example, imagine that to control the tune, you are using 8 different power converters attached to 8 quadrupole families. It should be possible to have a configuration that is not more complex than this one:

    - type: pyaml.tuning_tools.tune
      name: DEFAULT_TUNE_CORRECTION
      quad_array_name: QForTune
      betatron_tune_name: BETATRON_TUNE
      response_matrix: ${path:trm.json}
    
  8. TeresiaOlsson commented on Oct 1, 2026

    @TeresiaOlsson
    Member

    From this definition, do we agree that tuning tools should use the family, not the individual magnets?

    I think this is important and I agree but it is not a simple matter.

    From the point of view of configuration, the most straightforward way would be to use arrays. I am not fully up to date with how arrays work. Maybe it is already possible, but I believe it may be ok if an array can contain both individually-powered devices and a serialized family. (Here I mean that set/get of strength/hardware can be done on the array.)

    For example, imagine that to control the tune, you are using 8 different power converters attached to 8 quadrupole families. It should be possible to have a configuration that is not more complex than this one:

    - type: pyaml.tuning_tools.tune
      name: DEFAULT_TUNE_CORRECTION
      quad_array_name: QForTune
      betatron_tune_name: BETATRON_TUNE
      response_matrix: ${path:trm.json}
    

    I thought the specification was that arrays and elements should provide the same interface? So if the tuning tool makes get and set it doesn't matter if it's on an array or an element? It doesn't really make sense to me that you need to make arrays that just contain a single element if all your magnets are individually powered.

  9. kparasch commented on Oct 1, 2026

    @kparasch
    Member

    From this definition, do we agree that tuning tools should use the family, not the individual magnets?

    I think this is important and I agree but it is not a simple matter.
    From the point of view of configuration, the most straightforward way would be to use arrays. I am not fully up to date with how arrays work. Maybe it is already possible, but I believe it may be ok if an array can contain both individually-powered devices and a serialized family. (Here I mean that set/get of strength/hardware can be done on the array.)
    For example, imagine that to control the tune, you are using 8 different power converters attached to 8 quadrupole families. It should be possible to have a configuration that is not more complex than this one:

    - type: pyaml.tuning_tools.tune
      name: DEFAULT_TUNE_CORRECTION
      quad_array_name: QForTune
      betatron_tune_name: BETATRON_TUNE
      response_matrix: ${path:trm.json}
    

    I thought the specification was that arrays and elements should provide the same interface? So if the tuning tool makes get and set it doesn't matter if it's on an array or an element? It doesn't really make sense to me that you need to make arrays that just contain a single element if all your magnets are individually powered.

    If arrays and elements can provide the same interface, then great.

  10. GamelinAl commented on Oct 1, 2026

    @GamelinAl
    MemberAuthor

    Sorry my question was too imprecise. I'll try to make it better and form a proposal.

    Arrays mixing individual magnets and a serialized family

    • pyaml.arrays.magnet can mix individually powered magnets with the individual magnets of a series (N entries for one PS) which are treated as the virtual magnets of CFM magnets.
    • As of today, a pyaml.arrays.magnet cannot hold the serialized magnet family itself.
    • pyaml.arrays.serialized_magnet can hold serialized_magnet families only.
    • The tune tools only accept a magnet array. A serialized-magnet array fails.

    So today the only way to put a serialized family in a tuning tool is through its virtual magnets (i.e. individual component).

    Proposal:

    • allow to have a serialized magnet family in a pyaml.arrays.magnet
    • forbid to have a pyaml.arrays.magnet with both a serialized magnet family and its own individual magnets in the same array.
    • allow the tunning tools to take a pyaml.arrays.serialized_magnet as input

    Tune correction example

    Imagine the following measurement, using the same lattice but ran with different configuration of the quadrupoles:
    Measure the response matrix with tune_response_matrix, then call tune.add([0.01, 0.005]).

    Setup Achieved ΔQ (target [0.01, 0.005])
    8 individually powered quads (reference) ✔
    Series as 4 virtual magnets + 4 individually powered quads ✘
    A single virtual magnet + 4 individually powered quads ✔

    The second case will not work with what we proposed so far:

    • Stepping one virtual magnet moves the whole series, so each of the N columns is the full family response.
    • The pseudo-inverse treats the N columns as independent knobs and gives each 1/N of the family correction.
    • Since there is only one PS, the series moves by only that 1/N.
    • Averaging the requested strengths gives the same result, because the N requests are identical.

    Proposal:
    Tuning tools need one knob per power supply, as in MML, where chromaticity and response matrices work on families:

    • Input arrays to tuning tool can be a mix of individual magnets and serialized family.
    • If the an array that holds both individual magnets and member of serialized families is given to a tuning tool, that array is translated to a new array which contain the individual magnets and the serialized families.
  11. TeresiaOlsson commented on Oct 2, 2026

    @TeresiaOlsson
    Member

    Hmm... I think none of those options are really good but rather a review is needed of how the serialized magnets have been implemented and see if it can be simplified or done in some other way. Maybe by looking at how magnets are linked together in the twin. Last time I tested them it was not possible to configure and use them in the way we need for BESSY II (or at least I couldn't understand how). But after we decided to use our own ophyd-async devices instead of the common pyAML devices I have not looked at it further.

    I have still not understood why it is needed to have separate classes instead of just defining an individual magnet and if it's serialized link it to a power supply object which is the same for several magnets. For me the serialized magnet at the moment is a bit of a weird hybrid of an individual magnet and an array and if feels like it should be possible to make a simplified implementation.

  12. GamelinAl commented on Oct 13, 2026

    @GamelinAl
    MemberAuthor

    I would like to move on with this as at SOLEIL this is preventing us from testing further pyAML on the machine.

    Here is the proposal I would like to implement.

    A serialized family has one degree of freedom, the current I of its power supply. Everything in pyAML (accessors,
    arrays, tuning tools) treats it that way, in design and in live.

    1. Accessors (as agreed)

    Magnet i of the series has strength k_i = f_i(I).

    Accessor get() set(x)
    family.hardware I write I = x
    family.strength average strength Σ f_i(I) / N solve Σ f_i(I) / N = x for I, write it
    magnet[i].hardware I write I = x (all magnets follow)
    magnet[i].strength k_i = f_i(I) write I = f_i⁻¹(x), the others follow as f_j(I). No length weighting.

    Two guarantees follow:

    • x.set(x.get()) never changes the machine, for any of these accessors.
    • design and live return the same values for the same I.

    2. A serialized family behaves like a special magnet array

    A family is a group of magnets, so it behaves like a magnet array. It also has a family-level value:

    fam = sr.live.serialized_magnet.get("S9")
    fam.strength.get()    # scalar: average strength of the family
    fam.hardware.get()    # scalar: I
    fam.strengths.get()   # vector: [k_1, ..., k_N]
    for m in fam:         # the N individual magnets
        print(m.get_name(), m.strength.get())

    A family name can also be used inside a pyaml.arrays.magnet and stands for its magnets.

    3. Magnet arrays can mix everything

    As today, a pyaml.arrays.magnet can hold any mix of:

    • individually powered magnets,
    • individual magnets of a series (N entries for one PS),
    • virtual magnets of combined-function magnets (CFM).

    Writing through an array follows the table above. Changing one magnet of a series moves its power supply, and so the whole
    series. If several magnets of the same series are in the array, the power supply gets the average of the currents they
    request
    . Today, writing to one magnet moves the PS by only 1/N of the request. That is removed.

    4. Tuning tools: one knob per power supply

    Tuning tools (tune, chromaticity, orbit and their response matrices) only care about what can be moved independently.
    Before measuring or correcting, a tool turns its input array into a list of knobs:

    • an individually powered magnet is one knob,
    • a series is one knob, the family, driven through family.strength. It does not matter how it was listed: one of its
      magnets, all of them, or the family name all give the same knob,
    • each virtual magnet of a CFM is its own knob (see 5).

    This happens inside the tools. Users keep configuring and using normal magnet arrays. The only visible effect is that a series
    appears under its family name in the response matrix (variable_names). When a loaded response matrix does not match the knobs
    (for example an old matrix with one column per magnet of a series), the tool raises an error and asks for a new measurement,
    instead of silently correcting wrong.

    Back to the tune correction example (measure the TRM, then tune.add([0.01, 0.005])):

    Setup Today With this proposal
    8 individually powered quads ✔ ✔
    Series listed as 4 magnets + 4 individual quads ✘ (moves 1/N) ✔ (same as the next two rows)
    1 magnet of the series + 4 individual quads ✔ ✔
    Family name + 4 individual quads not possible ✔

    The tune config stays as simple as @kparasch asked:

    - type: pyaml.tuning_tools.tune
      name: DEFAULT_TUNE_CORRECTION
      quad_array_name: QForTune      # any mix of individual magnets, series members or family names
      betatron_tune_name: BETATRON_TUNE
      response_matrix: ${path:trm.json}

    5. Serialized magnets vs combined-function magnets

    Both create "virtual magnets", but in opposite ways:

    CFM Series
    Physical object 1 magnet, M multipoles N magnets, 1 multipole each
    Power supplies K PS mixed by a matrix 1 shared PS
    Degrees of freedom M (when the matrix allows it) 1
    Setting one virtual magnet changes that multipole only, the others stay moves the whole series
    Knob for tuning tools each virtual magnet the family

    Mixing both in one array is fine. Each CFM multipole stays a knob, and each series collapses into one knob.

    Known limitations

    • Series of CFMs is not covered: one power supply shared between several combined-function magnets (e.g. the main coils of a
      sextupole family in series, with individual corrector coils). That needs a model where the power supply is a separate object
      shared between magnets (closer to @TeresiaOlsson's suggestion). I suggest a separate design issue if a lab needs it.
    • A CFM with more multipoles than power supplies stays as today (least-squares compromise).

    Implementation

    Three sub-issues / PRs, each to be merge into a complete feature branch:

    1. Accessors: implement the table in design and live, fix the array write so it no longer moves 1/N. This alone fixes the main bug.
    2. Family as a special magnet array: iteration and per-magnet vectors on a family, and serialized family names allowed in
      pyaml.arrays.magnet.
    3. One knob per power supply in tuning tools: internal knob grouping and tests reproducing the table in §4.

    Do you agree with this direction?
    I'll strat with 1. as this was already agreed.

  13. TeresiaOlsson commented on Oct 13, 2026

    @TeresiaOlsson
    Member

    Known limitations

    * **Series of CFMs is not covered:** one power supply shared between several combined-function magnets (e.g. the main coils of a
      sextupole family in series, with individual corrector coils). That needs a model where the power supply is a separate object
      shared between magnets (closer to [@TeresiaOlsson](https://github.com/TeresiaOlsson)'s suggestion). I suggest a separate design issue if a lab needs it.
    
    * A CFM with more multipoles than power supplies stays as today (least-squares compromise).
    

    @GamelinAl I don't fully understand if this covers the cases I have been struggling with or not:

    1. MAX IV: they have combined function magnets connected in series. It's one power supply controlling for example both quadrupole and sextupole component of several magnets. They currently have no idea how to configure it. I guess that is included in the limitation?

    2. BESSY II and MLS: We have dipoles and sextupoles powered in series with correctors and skew quadrupoles with individual power supplies on top. I don't think of this as combined-function magnets but I have understood that you do. Are these also included in the limitations? I have so far not understood the correct way to configure these in pyAML so I have gone for the approach we currently use in MML and configure them as completely separate magnets.

  14. GamelinAl commented on Oct 13, 2026

    @GamelinAl
    MemberAuthor

    Interesting cases ... I'll try to answer the best I can. Please correct me if my understanding of these particular cases are wrong.

    In pyAML as it is now, a CombinedFunctionMagnet (with linear_cfm_model) is one magnet whose K coils are mixed into M multipoles by a matrix. This is the ESRF type, where each coil current contributes to several multipoles (crosstalk). Extra windings that each produce only their own multipole are not neccesarily a CFM in that sense. They can also be configured as independent magnets that happen to sit on the same lattice element.

    BESSY II / MLS: covered by the proposal, not a limitation

    • Main coils (sextupoles, quadrupoles): one pyaml.magnet.serialized_magnet per power supply, with one member per magnet.

    • Extra windings (H/V correctors, skew quads): keep them as individual magnets (HCorrector, VCorrector, SkewQuad) on
      the same lattice element, using lattice_names.

    This becomes a limitation only if the windings have crosstalk with the main coil, e.g. the main current changes the skew
    or corrector field, or the reverse. That needs a CFM matrix containing a PS shared with other magnets ("series of CFMs"), which
    the proposal does not cover.

    MAX IV: a limitation today, but it fits the same concept

    I understand that here each magnet has several components (e.g. quadrupole and sextupole) and the N magnets are on one PS. There is still one degree of freedom, I, for N × M strengths k_{i,m} = f_{i,m}(I). So this is a series of multi-function magnets, not a CFM with several PS. Is this correct?

    Configuring it today with N CombinedFunctionMagnet that point to the same PS would not work well I think.

    For it to work, we could extend the serialized magnet proposal as:

    • serialized_magnet accepts several functions per member, e.g. functions: [B1, B2], with one curve per (magnet, function).
    • Each (magnet, function) is a virtual magnet. Setting its strength writes I = f_{i,m}⁻¹(x), following the same table as for
      the single-function series.
    • For tuning tools, the whole family is one knob, as for any series.

    It need more thinking but it could probably work.

    How things would be with the proposal

    Case Configuration Status
    Series, one function per magnet (SOLEIL) serialized_magnet sub-issues 1–3 (#473, ...)
    Series main coils + independent individual windings (BESSY II, MLS) serialized_magnet for the main coils + individual magnets on the same lattice element sub-issues 1–3
    Series of multi-function magnets on one PS (MAX IV) serialized_magnet with several functions need an extension be probably ok
    Series coil and individual coils with crosstalk (shared PS inside a CFM matrix) not supported limitation
    One CFM with more multipoles than PS least-squares, as today out of scope

    The last two lines would need another architecture I think, with the power supply as a separate object shared between magnets.

  15. TeresiaOlsson commented on Oct 13, 2026

    @TeresiaOlsson
    Member

    Interesting cases ... I'll try to answer the best I can. Please correct me if my understanding of these particular cases are wrong.

    In pyAML as it is now, a CombinedFunctionMagnet (with linear_cfm_model) is one magnet whose K coils are mixed into M multipoles by a matrix. This is the ESRF type, where each coil current contributes to several multipoles (crosstalk). Extra windings that each produce only their own multipole are not neccesarily a CFM in that sense. They can also be configured as independent magnets that happen to sit on the same lattice element.

    BESSY II / MLS: covered by the proposal, not a limitation

    * **Main coils** (sextupoles, quadrupoles): one `pyaml.magnet.serialized_magnet` per power supply, with one member per magnet.
    
    * **Extra windings** (H/V correctors, skew quads): keep them as individual magnets (`HCorrector`, `VCorrector`, `SkewQuad`) on
      the same lattice element, using `lattice_names`.
    

    This becomes a limitation only if the windings have crosstalk with the main coil, e.g. the main current changes the skew or corrector field, or the reverse. That needs a CFM matrix containing a PS shared with other magnets ("series of CFMs"), which the proposal does not cover.

    This sounds reasonable. I think the situation we have is quite common for third generation light sources. They might be crosstalk but if it exists there is anyway no model of it that can be put into pyAML.

    MAX IV: a limitation today, but it fits the same concept

    I understand that here each magnet has several components (e.g. quadrupole and sextupole) and the N magnets are on one PS. There is still one degree of freedom, I, for N × M strengths k_{i,m} = f_{i,m}(I). So this is a series of multi-function magnets, not a CFM with several PS. Is this correct?

    Yes, one current -> several magnet components that can't be controlled individually per magnet. Then those magnets are connected together in series resulting in one current -> several magnet components on several magnets. Some individual adjustment of the different components and between magnets is possible with pole face strips and resistance shunts but how to implement that I think is a separate topic. They have something for it already in their MML.

    They also for some things have separate "corrector" magnets. For example for the combined function quadrupole-sextupole if you want to change the quadrupole component, the sextupole component will also change but you can compensate it by at the same time change a separate much weaker sextupole. So I think it's kind of the ESRF type where you have several currents mapping to several magnet components except that it's between different physical magnets. As I have understood it they handle this in their MML by not setting some magnets directly but instead have a script that sets several magnets in iterative steps until you reach the situation you wanted.

  16. gubaidulinvadim commented on Oct 13, 2026

    @gubaidulinvadim
    Member

    Known limitations

    * **Series of CFMs is not covered:** one power supply shared between several combined-function magnets (e.g. the main coils of a
      sextupole family in series, with individual corrector coils). That needs a model where the power supply is a separate object
      shared between magnets (closer to [@TeresiaOlsson](https://github.com/TeresiaOlsson)'s suggestion). I suggest a separate design issue if a lab needs it.
    
    * A CFM with more multipoles than power supplies stays as today (least-squares compromise).
    

    @GamelinAl I don't fully understand if this covers the cases I have been struggling with or not:

    1. MAX IV: they have combined function magnets connected in series. It's one power supply controlling for example both quadrupole and sextupole component of several magnets. They currently have no idea how to configure it. I guess that is included in the limitation?
    
    2. BESSY II and MLS: We have dipoles and sextupoles powered in series with correctors and skew quadrupoles with individual power supplies on top. I don't think of this as combined-function magnets but I have understood that you do. Are these also included in the limitations? I have so far not understood the correct way to configure these in pyAML so I have gone for the approach we currently use in MML and configure them as completely separate magnets.
    

    Could you please create corresponding issues? and tag them as related to @GamelinAl issue

  17. JeanLucPons commented on Oct 13, 2026

    @JeanLucPons
    Member

    I think several things are mixed in this discussion.

    You can have:

    • CFM that link N strengths to M currents for one single element with an invertible model. OK.
    • Serialized magnets with n independent models when the serialized magnets are not the same. OK (Except that the average strength might not be very convenient).

    Then you have the case of corrected magnets, for instance quad in series with quadrupole corrector on top of them which needs an additional correction strength field (as discussed here) to get an invertible model. Not OK yet.

    Complex combined function magnets that includes several elements, several multipoles and several power supplies. Not yet OK. The Max IV case enter in that case and an invertible model has to be written.

  18. self-assigned this
    on Oct 13, 2026
  19. linked a pull request that will close this issue461 serializedmagnets #481on Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingenhancementNew feature or request

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions