Repository navigation
SerializedMagnets: get/set are not consitent and design/live disagree #461
Description
Activity
- addedbugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or request
on Oct 1, 2026 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 forfamily.strengthto 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.strengthequal to average strengthS = Σ f_i(I) / N?
Then at least in the typical case,family.strengthfalls back to the strength that is equal to all magnets.
The same follows for itssetlogic, solveΣ f_i(I) / N = xfor oneI, write itI 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.strengthreturn the average strength. Then if you want to know the strength of the individual magnet you need to domagnet.strengthinstead.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 thatS = Σ f_i(I) / Nis sensible in that case.Reacted by Teresia OlssonI 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 thatS = Σ f_i(I) / Nis sensible in that case.I agree. Then you can use
getandseton 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?
I agree with you both, it make sense to go this way. The table is now:
Accessor get()set(x)family.hardwareIwrite I = xfamily.strengthaverage strength S = Σ f_i(I) / Nsolve Σ f_i(I) / N = xfor oneI, write itmagnet[i].hardwareIwrite I = x(all magnets follow)magnet[i].strengthk_i = f_i(I)write I = f_i⁻¹(x); the others follow asf_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 dofamily.strength * len(magnets_inside_fam)and would make clear thatfamily.strengthis actually the average strength.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/getofstrength/hardwarecan 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}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/getofstrength/hardwarecan 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
getandsetit 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.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 thatset/getofstrength/hardwarecan 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
getandsetit 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.
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.magnetcan 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.magnetcannot hold the serialized magnet family itself. pyaml.arrays.serialized_magnetcan 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.magnetwith both a serialized magnet family and its own individual magnets in the same array. - allow the tunning tools to take a
pyaml.arrays.serialized_magnetas input
Tune correction example
Imagine the following measurement, using the same lattice but ran with different configuration of the quadrupoles:
Measure the response matrix withtune_response_matrix, then calltune.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.
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.
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 inlivedesignandlive, 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 Awhile 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. Magnetihas strengthk_i = f_i(I), wheref_iis sub-modeli.get()set(x)family.hardwareII = xfamily.strengthS = Σ f_i(I)Σ f_i(I) = xfor oneI, write itmagnet[i].hardwareII = x(all magnets follow)magnet[i].strengthk_i = f_i(I)I = f_i⁻¹(x); the others follow asf_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.