Repository navigation
ElementHolder API refurbishment #199
Description
Description, motivation and use case
Today we have a single ElementHolder class that holds everything for a given control mode.
The ElementHolder suffers from a large number of methods. There is a need for reorganization and the introduction of other holders. Last but not least, ElementHolder.get_all*() or ElementHolder.get_*s() function return list while typed array would be suitable. The ElementHolder will however hold all elements as before.
Note that the get function from typed holder will return appropriate typed object or arrays and raise an exception in case of wrong type while the equivalent get on the base ElementHolder may return random types.
class MagnetsHolder(Object):
# Return the named array or all magnets when no name specified
# sr.magnets[:] is equivalent to sr.magnets.get()
def get(name:str = None) : "MagnetArray"
...
class MagnetHolder(Object):
# Return the magnet with the specified name
def get(name:str) : "Magnet"
...Proposed solution
Create new holders such as Magnet(s)Holder, BPM(s)Holder, RFHolder, DiagnosticHolder,ToolHolder etc and allow access to them trough python properties to allow following writings:
This is a non exhaustive list.
#Magnet(s)Holder
sr.live.magnets.get() # Return all magnets simple and virtual (no combined function magnet)
sr.live.magnets[:] # Return all magnets simple and virtual (no combined function magnet)
sr.live.magnets.get_cfm() # Return all combined function magnets
sr.live.magnets.get("QuadForTune") # Returns family `MagnetArray`
sr.live.mangets.QuadForTune # If we add dynamic python properties
sr.live.magnets['*QD*'] # Returns subset
sr.live.magnets.get("QuadForTune")['QD1*'] # Returns subset of familly
sr.live.magnets[1:10] # Returns subset
sr.live.magnets.get()[model_name:'*-H'] # Returns subset
sr.live.manget.get("QF1E-C03").strength.set( 0.8 ) # Exmaple with single manget
# BPM(s)Holder (similar to MagnetHolder)
sr.live.bpms.get(...)
sr.live.bpm.get(...)
# RFHolder
sr.live.rf.masterclock.frequency.set() # The `RFHolder` will contain a masterclock object coming from the "DEFAULT_RF_PLANT"
sr.live.rf.get(rfname) # Will return an other RFPlant
# ElementHolder
sr.live.get(...) # Get all elements
sr.live[:] # Get all elements
sr.live['BPM*'] # Get all elements having a name starting with BPM and eventually return a BPMArray
sr.live.mangets[:] & sr.live.get("CELL08") # Example of set operation all magnet of CELL08
# Diagnostic Holder
sr.live.diagnostic.betatron_tune.set(tune) # The `DiagnosticHolder` will contain a betatron_tune object mapped from "DEFAULT_BETATRON_TUNE_MONITOR"
sr.live.diagnostic.get("SPARE_BETATRON_TUNE_MONITOR) # Return an other tune monitor
sr.live.diagnostic.get() # Return a list of all available diagnostic (this will be an untyped list)
# Tool holder
sr.live.tool.tune.set([qx,qy]) # The `ToolHolder` will contain a tune mapped from "DEFAULT_TUNE_CORRECTION"
sr.live.tool.tune.response.measure()
sr.live.tool.get("OTHER_TUNE_CORRECTION") # return an other tune tuning tool.
sr.live.tool.get() # Return a list of all available tool (this will be an untyped list)Feel free to comment and to propose modifications, I will update this top level post following discussions.
Activity
[-]ElementHolder refurbishmenet[/-][+]ElementHolder API refurbishment[/+]gubaidulinvadim commentedon Feb 20, 2026
I have a small question. In the code example you have
It is planned to have a holder at Accelerator level? Or is it a typo and it should've been
sr.live.rf?JeanLucPons commentedon Feb 20, 2026
typo, you know that I'm the king of the typo :D
gupichon commentedon Mar 18, 2026
In the end, we might introduce some kind of holder at the Accelerator level to propagate modifications to all other holders affected by the change. The Yellow Pages might be able to handle this.
JeanLucPons commentedon Mar 18, 2026
I updated the first post according to the @TeresiaOlsson suggestion to allow:
TeresiaOlsson commentedon Mar 19, 2026
Why would you do
sr.live.bpms["BPM*"]? That's the same information twice? Personally, I don't entirely understand why we need all of this various holders? Why can't there just be one and then if you for example want to get all the magnets you can find them by type like in pyAT?Also, to me it looks tricky to get
fill_deviceto work together with third-party devices and applications since all the types are currently hardcoded in there. Also feels like a lot of work to maintain it because you need to change in bothcontrolsystem.fill_deviceandsimulation.fill_deviceevery time you add a new type of device.JeanLucPons commentedon Mar 19, 2026
@TeresiaOlsson
I do not see potential problem with fill_device(). ElementHolder will be simply aggregates other Holders.
sr.live.bpms[...]will always return a BPMArray whilesr.live[...]will return a dynamically typed array.This is important for developers to have the completion working when using IDE that work with not constructed object and need strong typing. This some trade off we have to make otherwise maintaining code with only dynamic typed stuff will be a nightmare.
TeresiaOlsson commentedon Mar 19, 2026
So you mean if I want to change to a facility specific TuneMonitor which includes both transverse and longitudinal tunes like we need for both BESSY II and MLS, I don't need to do any changes in fill_device? If not, what is then the purpose of the
add_betatron_tune_monitor?JeanLucPons commentedon Mar 19, 2026
No change nowhere in pyaml. This is the goal !
You just override the tune monitor with its interface (to do).
Then in the attach method, you can select if you attach to a simulator or to a CS and create locally your own ReadFloatArray for your CS which can be OphydDevice(s) if you want. And you let the super class deal with the simulator attachment.
For the time being I'm working on MeasurementTool. I also test OphydAsynch to get the feature I need for dynamic Catalog. OphydAsynch support seems rather efficient. We will see.
gupichon commentedon Sep 4, 2026
Current implementation status:
The typed-holder foundation is now in place:
ElementHolderexposesmagnet/magnets,bpm/bpms, andrf.magnets.get()andbpms.get()return typed arrays containing all elements.magnets.get("Family"),bpms.get("Family"),magnet.get("Name"), andbpm.get("Name").&,|,-).rf.get(name)is available.rf.frequencyandrf.voltagealready targetDEFAULT_RF_PLANT.The following parts of the original proposal are not implemented yet:
ElementHolder.get(...),ElementHolder[:], andElementHolder["pattern"]API.sr.live.magnets.QuadForTune.magnets.get_cfm()API. Combined-function magnets are currently exposed through separatecombined_function_magnet(s)holders.diagnosticandtoolholders, including theirget()APIs and default-object properties.rf.masterclock; the equivalent default RF access currently lives directly underrf.frequencyandrf.voltage.So the typed collections, typed lookups, filtering, and array operations are implemented. The remaining work is mainly the public ergonomic API proposed in this issue, especially generic element access and the diagnostics/tools namespaces.
JeanLucPons commentedon Sep 7, 2026
Yes this is still in progress. During last maintainer meeting (before my holydays) @theorzr proposed to take it in charge.
But I d'ont know if he has time.
Otherwise I will finish.
gupichon commentedon Sep 7, 2026
I'll create the sub-issues. If @theorzr is free, feel free to assign him one. Or he can self-assign.