Skip to content

ElementHolder API refurbishment #199

Description

@JeanLucPons

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

  1. changed the title [-]ElementHolder refurbishmenet[/-] [+]ElementHolder API refurbishment[/+] on Feb 20, 2026
  2. gubaidulinvadim commented on Feb 20, 2026

    @gubaidulinvadim
    Member

    I have a small question. In the code example you have

    sr.rf ...
    sr.tool
    

    It is planned to have a holder at Accelerator level? Or is it a typo and it should've been sr.live.rf?

  3. JeanLucPons commented on Feb 20, 2026

    @JeanLucPons
    MemberAuthor

    typo, you know that I'm the king of the typo :D

  4. gupichon commented on Mar 18, 2026

    @gupichon
    Member

    I have a small question. In the code example you have

    sr.rf ...
    sr.tool
    

    It is planned to have a holder at Accelerator level? Or is it a typo and it should've been sr.live.rf?

    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.

  5. JeanLucPons commented on Mar 18, 2026

    @JeanLucPons
    MemberAuthor

    I updated the first post according to the @TeresiaOlsson suggestion to allow:

    sr.live.bpms["BPM*"] # Return all bpms starting with 'BPM'
    sr.live["BPM*"] # Return all elements starting with 'BPM'  (Array dynamically typed)
  6. TeresiaOlsson commented on Mar 19, 2026

    @TeresiaOlsson
    Member

    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_device to 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 both controlsystem.fill_device and simulation.fill_device every time you add a new type of device.

  7. JeanLucPons commented on Mar 19, 2026

    @JeanLucPons
    MemberAuthor

    @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 while sr.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.

  8. TeresiaOlsson commented on Mar 19, 2026

    @TeresiaOlsson
    Member

    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?

  9. JeanLucPons commented on Mar 19, 2026

    @JeanLucPons
    MemberAuthor

    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.

  10. self-assigned this
    on Jun 30, 2026
  11. gupichon commented on Sep 4, 2026

    @gupichon
    Member

    Current implementation status:

    The typed-holder foundation is now in place:

    • ElementHolder exposes magnet / magnets, bpm / bpms, and rf.
    • Dedicated holders also exist for combined-function and serialized magnets.
    • magnets.get() and bpms.get() return typed arrays containing all elements.
    • Named arrays and individual elements can be retrieved through magnets.get("Family"), bpms.get("Family"), magnet.get("Name"), and bpm.get("Name").
    • Typed arrays support slicing, wildcard selection, field-based filtering, and set-like operations (&, |, -).
    • rf.get(name) is available. rf.frequency and rf.voltage already target DEFAULT_RF_PLANT.

    The following parts of the original proposal are not implemented yet:

    • A generic ElementHolder.get(...), ElementHolder[:], and ElementHolder["pattern"] API.
    • Dynamic holder attributes such as sr.live.magnets.QuadForTune.
    • A unified magnets.get_cfm() API. Combined-function magnets are currently exposed through separate combined_function_magnet(s) holders.
    • Public diagnostic and tool holders, including their get() APIs and default-object properties.
    • rf.masterclock; the equivalent default RF access currently lives directly under rf.frequency and rf.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.

  12. JeanLucPons commented on Sep 7, 2026

    @JeanLucPons
    MemberAuthor

    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.

  13. gupichon commented on Sep 7, 2026

    @gupichon
    Member

    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.

    I'll create the sub-issues. If @theorzr is free, feel free to assign him one. Or he can self-assign.

  14. self-assigned this
    on Sep 16, 2026
  15. linked a pull request that will close this issueElementholder api refurbishment #430on Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions