Creating a new beamline#

A beamline is a collection of devices that can be used together to run experiments, they may be read-only or capable of being set. They include motors in the experiment hutch, optical components in the optics hutch, the synchrotron “machine” and more.

Beamline Modules#

Each beamline should have its own file in the doodal.beamlines folder, in which the particular devices for the beamline are instantiated. The file should be named after the colloquial name for the beamline. For example:

  • i03.py

  • i20_1.py

  • vmxi.py

Beamline modules (in dodal.beamlines) are code-as-configuration. They define the set of devices and common device settings needed for a particular beamline or group of similar beamlines (e.g. a beamline and its digital twin). Some of our tooling depends on the convention of only beamline modules going in this package. Common utilities should go somewhere else e.g. dodal.utils or dodal.beamlines.common.

The following example creates a fictitious beamline w41, with a simulated twin s41. w41 needs to monitor the status of the Synchrotron and has an AdAravisDetector. s41 has a simulated clone of the AdAravisDetector, but not of the Synchrotron machine.

from dodal.common.beamlines.beamline_utils import device_instantiation
from dodal.common.beamlines.beamline_utils import set_beamline as set_utils_beamline
from dodal.devices.areadetector.adaravis import AdAravisDetector
from dodal.devices.synchrotron import Synchrotron
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import get_beamline_name, skip_device

BL = get_beamline_name("s41")  # Default used when not on a live beamline
set_log_beamline(BL)  # Configure logging and util functions
set_utils_beamline(BL)


"""
Define device factory functions below this point.
A device factory function is any function that has a return type which conforms
to one or more Bluesky Protocols.
"""


"""
A valid factory function which is:
- instantiated only on the live beamline
- a maximum of once
- can optionally be faked with ophyd simulated axes
- can optionally be connected concurrently by not waiting for connect to complete
- if constructor took a prefix, could optionally exclude the BLIXX prefix
""""
@skip_device(lambda: BL == "s41")  # Conditionally do not instantiate this device
def synchrotron(
    wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False
) -> Synchrotron:
    """Calls the Synchrotron class's constructor with name="synchrotron", prefix=""
    If this is called when already instantiated, it will return the existing object.
    """
    return device_instantiation(
        Synchrotron,
        "synchrotron",
        "",
        wait_for_connection,
        fake_with_ophyd_sim,
        bl_prefix=False,
    )

def d11(name: str = "D11") -> AdAravisDetector:
    """
    Also a valid Device factory function, but as multiple calls would instantiate
    multiple copies of a device, discouraged.
    """
    return AdAravisDetector(name=name, prefix=f"{BL}-DI-DCAM-01:")

w41 should also be added to the list of ALL_BEAMLINES in tests/beamlines/test_device_instantiation. This test checks that the function returns a type that conforms to Bluesky protocols, that it always returns the same instance of the device and that the arguments passed into the Device class constructor are valid.