Skip to content

Refactor element attachment with holder-specific dispatch - #363

Open
gupichon wants to merge 1 commit into
mainfrom
361-feature-refactor-element-attachment-using-holder-specific-dispatch
Open

Refactor element attachment with holder-specific dispatch#363
gupichon wants to merge 1 commit into
mainfrom
361-feature-refactor-element-attachment-using-holder-specific-dispatch

Conversation

@gupichon

Copy link
Copy Markdown
Member

Summary

Refactor element attachment to replace the type-based if/elif chains in
Simulator and ControlSystem with holder-specific dispatch.

ElementHolder.fill_device() now owns the common iteration over configured
elements. Each supported element type delegates its attachment to a dedicated
fill_* method implemented by the target holder.

Changes

  • Add explicit attachment delegation to magnets, combined-function magnets,
    serialized magnets, BPMs, RF plants, tune monitors, tuning tools,
    measurement tools, and unbound elements.
  • Add abstract fill_* hooks to ElementHolder.
  • Move the existing simulator and control-system attachment logic into their
    corresponding holder-specific methods.
  • Preserve the existing UnboundElement behaviour:
    • ControlSystem instantiates it only for matching control_modes.
    • Simulator ignores it, as before.
  • Preserve existing configuration formats and runtime attachment behaviour.

Validation

  • Ran the standard test suite.
  • Ran the DT4ACC integration smoke test against a local twin for both
    tango-pyaml and pyaml-cs-oa.

Move element-type dispatch out of Simulator and ControlSystem into explicit
fill_* hooks. ElementHolder now orchestrates attachment by delegating to
each element, while holders retain their backend-specific implementation.

Preserve dynamic UnboundElement handling in control systems and the
simulator's existing no-op behavior.
@gupichon gupichon self-assigned this Aug 27, 2026
@gupichon gupichon linked an issue Aug 27, 2026 that may be closed by this pull request
2 tasks
@TeresiaOlsson

Copy link
Copy Markdown
Member

I think it's better than before but is it possible to move the attachment process completely out of controlsystem and simulator? For example, this I think should not be the responsibility of that level but happen already at the Element level.

    def fill_magnet(self, magnet: Magnet) -> None:
        device = self.get_device_access(magnet.model.get_device_names()[0])
        current = RWHardwareScalar(magnet.model, device) if magnet.model.has_hardware() else None
        strength = RWStrengthScalar(magnet.model, device) if magnet.model.has_physics() else None
        self.magnet.add(magnet.attach(self, strength, current))

If not, every single time someone wants to add a new type of element (or change an existing one) changes are required in both controlsystem and simulator which make the code difficult to maintain and very difficult for users to contribute their own devices. It also requires the peer concept which I personally doubt the users will understand because I also don't understand why it is needed. It's strange to me that after creating the object once it has to be copied and the attributes of it has to be repeated somewhere else in the codebase. That feels error-prone to me.

I think when creating an Element it should be self-contained. You create the object and it knows everything it needs from the start. In ophyd-async this is done by specifying the signal types at the device level and giving it which backend to use as part of the initalization of the object. Wouldn't that be possible to also do for us? So when creating the Element you input the controlsystem/simulator and in that way this attachment can be handled at the Element level?

@gupichon

Copy link
Copy Markdown
Member Author

I think it's better than before but is it possible to move the attachment process completely out of controlsystem and simulator? For example, this I think should not be the responsibility of that level but happen already at the Element level.

    def fill_magnet(self, magnet: Magnet) -> None:
        device = self.get_device_access(magnet.model.get_device_names()[0])
        current = RWHardwareScalar(magnet.model, device) if magnet.model.has_hardware() else None
        strength = RWStrengthScalar(magnet.model, device) if magnet.model.has_physics() else None
        self.magnet.add(magnet.attach(self, strength, current))

If not, every single time someone wants to add a new type of element (or change an existing one) changes are required in both controlsystem and simulator which make the code difficult to maintain and very difficult for users to contribute their own devices. It also requires the peer concept which I personally doubt the users will understand because I also don't understand why it is needed. It's strange to me that after creating the object once it has to be copied and the attributes of it has to be repeated somewhere else in the codebase. That feels error-prone to me.

I think when creating an Element it should be self-contained. You create the object and it knows everything it needs from the start. In ophyd-async this is done by specifying the signal types at the device level and giving it which backend to use as part of the initalization of the object. Wouldn't that be possible to also do for us? So when creating the Element you input the controlsystem/simulator and in that way this attachment can be handled at the Element level?

I completely agree, but I would like to do it step by step. In the end, the control system should not be responsible for building the PyAML object. Defining a customizable factory will be a subsequent step. I’ve never agreed with this peer concept either.

Another potential misuse of the current implementation would be overriding object creation in tango-pyaml. It is actually perfectly doable (as before), but it is not something we want to allow.

PyAML objects should only have a lightweight link with the control system or the lattice. It's not completely the case yet.

We will need to take some time to define a better architecture, especially regarding how PyAML objects are defined and linked to their holders. Unlike in Ophyd, here we have a single definition with multiple holders (multiple lattices and multiple control systems).

Anyway, I hope this current change is a step in the right direction.

@TeresiaOlsson

Copy link
Copy Markdown
Member

I think it's better than before but is it possible to move the attachment process completely out of controlsystem and simulator? For example, this I think should not be the responsibility of that level but happen already at the Element level.

    def fill_magnet(self, magnet: Magnet) -> None:
        device = self.get_device_access(magnet.model.get_device_names()[0])
        current = RWHardwareScalar(magnet.model, device) if magnet.model.has_hardware() else None
        strength = RWStrengthScalar(magnet.model, device) if magnet.model.has_physics() else None
        self.magnet.add(magnet.attach(self, strength, current))

If not, every single time someone wants to add a new type of element (or change an existing one) changes are required in both controlsystem and simulator which make the code difficult to maintain and very difficult for users to contribute their own devices. It also requires the peer concept which I personally doubt the users will understand because I also don't understand why it is needed. It's strange to me that after creating the object once it has to be copied and the attributes of it has to be repeated somewhere else in the codebase. That feels error-prone to me.
I think when creating an Element it should be self-contained. You create the object and it knows everything it needs from the start. In ophyd-async this is done by specifying the signal types at the device level and giving it which backend to use as part of the initalization of the object. Wouldn't that be possible to also do for us? So when creating the Element you input the controlsystem/simulator and in that way this attachment can be handled at the Element level?

I completely agree, but I would like to do it step by step. In the end, the control system should not be responsible for building the PyAML object. Defining a customizable factory will be a subsequent step. I’ve never agreed with this peer concept either.

Another potential misuse of the current implementation would be overriding object creation in tango-pyaml. It is actually perfectly doable (as before), but it is not something we want to allow.

PyAML objects should only have a lightweight link with the control system or the lattice. It's not completely the case yet.

We will need to take some time to define a better architecture, especially regarding how PyAML objects are defined and linked to their holders. Unlike in Ophyd, here we have a single definition with multiple holders (multiple lattices and multiple control systems).

Anyway, I hope this current change is a step in the right direction.

Then I like it :) I just wait with approving a bit to give @JeanLucPons a chance to take a look it too before it can be merged it since he might also have input.

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.

Feature: Refactor element attachment using holder-specific dispatch

3 participants