Skip to content

Create devices interactively #364

Description

@TeresiaOlsson

I have started to write drafts for the tutorials. In the first one I want to show the difference between creating devices interactively yourself or by loading a configuration file so the users understand the two approaches and better understand what they gain by creating the configuration file.

Problem is that I'm stuck on the correct way to create devices interactively. What I want to do at this point is just to create a single quadrupole using the simulator. This is what I thought I could do:

from pyaml.magnet.quadrupole import Quadrupole
from pyaml.lattice.simulator import Simulator

# First create the quadrupole
quad = Quadrupole(name='QF_001')

# Then create the simulator
simulator = Simulator(name="design", lattice=str(lattice_filepath))

# Attach the quadrupole to the simulator
quad.attach(simulator)

# Now you can read the strength
quad.strength.get()

It gave the error: Magnet.attach() missing 2 required positional arguments: 'strength' and 'hardware' so I change to quad.attach(simulator, strength=None, hardware=None) which instead gave me the error QF_001 is not attachedto a control system or the a simulator and I realised that this is not at all how attach works.

Instead I had to do:

from pyaml.magnet.quadrupole import Quadrupole
from pyaml.lattice.simulator import Simulator

# First create the quadrupole
quad = Quadrupole(name='QF_001')

# Then create the simulator
simulator = Simulator(name="design", lattice=str(lattice_filepath))

# Attach the quadrupole to the simulator
simulator.fill_device([quad])
quad = simulator.magnet.get("QF_001")

# Now you can read the strength
quad.strength.get()

but it still didn't work because 'NoneType' object has no attribute 'has_physics'. This I guess is a bug since it's allowed to create a magnet without a model? Or should it be mandatory to create a model?

In the end I could only get it to work by doing this:

from pyaml.magnet.identity_model import IdentityMagnetModel
from pyaml.magnet.quadrupole import Quadrupole
from pyaml.lattice.simulator import Simulator

# First create a model for the magnet
model = IdentityMagnetModel(physics='')

# Then create the quadrupole
quad = Quadrupole(name='QF_001', model=model)

# Then create the simulator
simulator = Simulator(name="design", lattice=str(lattice_filepath))

# Attach the quadrupole to the simulator
simulator.fill_device([quad])
quad = simulator.magnet.get("QF_001")

# Now you can read the strength
quad.strength.get()

What is the physics parameter and why do I need it if I'm just running the simulator? Looking at the config files it seemed to me like you only need it if using a control system? But the error messages tell me Invalid IdentityMagnetModel configuration,physics or powerconverter device required so I need to either give it or a power converter. So I just put an empty string and the simulator worked.

Is there something we can do to improve this interface before the hackathon because I think this is too complicated for the users?

Activity

  1. TeresiaOlsson commented on Sep 2, 2026

    @TeresiaOlsson
    MemberAuthor

    I also ran into a problem later when trying to do this:

    from pyaml.accelerator import Accelerator
    
    accelerator = Accelerator(
        facility='pyAML_test_facility',
        machine='pyaml_test_machine',
        energy=1e9,
        simulators=[simulator],
        devices=[quad]
    )

    It says Duplicate element Quadrupole name {element.get_name()} because when creating the accelerator the simulator tries to attach the device which has already been attached. I think that will also be confusing for the users.

  2. TeresiaOlsson commented on Sep 2, 2026

    @TeresiaOlsson
    MemberAuthor

    This is the only way I could figure out how to do it. Could be that a function to remove already attached devices is also needed but for this use case it would probably be better to not throw an error if the simulator tries to attach an element which is already attached to it.

    from pyaml.accelerator import Accelerator
    
    simulator._MAGNETS.pop("QF_001", None)
    simulator._ALL.pop("QF_001", None)
    
    accelerator = Accelerator(
        facility='pyAML_test_facility',
        machine='pyaml_test_machine',
        energy=1e9,
        simulators=[simulator],
        devices=[quad]
    )
    
    # Get the quadrupole
    quad = accelerator.design.magnet.get('QF_001')
    
    # Read the strength in the same way as before 
    quad.strength.get()
  3. TeresiaOlsson commented on Sep 2, 2026

    @TeresiaOlsson
    MemberAuthor

    Here is an attachment for the tutorial I'm trying to create using this so you can see what I had in mind.

    documentation.zip

    You should be able to see the page if you open documentation/tutorials/creating_accelerator.html.

  4. TeresiaOlsson commented on Sep 7, 2026

    @TeresiaOlsson
    MemberAuthor

    @JeanLucPons Do you have a better suggestion for how to write what I was trying to do in this tutorial? https://python-accelerator-middle-layer.github.io/documentation/tutorials/01_create_accelerator.html

    I have the feeling that my approach probably wasn't the correct way of doing it and that there might already be a better syntax.

    My idea was to first show how to create the accelerator yourself directly in python and then how to do it by using the configuration file. And the point of it: to show that if you have several control modes, devices etc the configuration file interface is much easier to use so it's worth spending the effort to set it up. My hope was that the new users would then better understand the reason behind it and feel more motivated to learn how to create the configuration.

  5. JeanLucPons commented on Sep 7, 2026

    @JeanLucPons
    Member

    For me it is not so bad.

    May be these 2 lines not really nice.
    It would be nicer to add a clear() method to the holder that clear all internal buffers.

    # Clear devices that will be fill automatically by the accelerator in the next example
    simulator.clear()

    Instead of:

    simulator._MAGNETS.pop("QF_001", None)
    simulator._ALL.pop("QF_001", None)
  6. TeresiaOlsson commented on Sep 7, 2026

    @TeresiaOlsson
    MemberAuthor

    For me it is not so bad.

    May be these 2 lines not really nice. It would be nicer to add a clear() method to the holder that clear all internal buffers.

    Clear devices that will be fill automatically by the accelerator in the next example

    simulator.clear()

    Instead of:

    simulator._MAGNETS.pop("QF_001", None)
    simulator._ALL.pop("QF_001", None)

    Yes, I agree. That feels like an addition which might be possible to do before the hackathon and will immediately make it nicer. I also think the other parts are not so bad. The users will understand them and if they prefer to make some individual devices to test with before they start putting together a yaml file this will show them that they can.

    If we add a clear it will also be easy in the tutorial to explain that this step is necessary to do because the accelerator will handle the attachment. And we can also add that handling the attachment is part of the point of creating an accelerator and not just individual devices.

  7. gubaidulinvadim commented on Sep 18, 2026

    @gubaidulinvadim
    Member

    This can be closed no? simulator.clear() is a separate issue, no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions