Use the Virtual Accelerator#
How to run the Control Assistant tutorial against a Virtual Accelerator — a containerized soft-IOC that serves real EPICS Channel Access, with PyAT physics behind the storage-ring lattice channels, so correctors move and BPMs respond.
What You’ll Learn
What the Virtual Accelerator is (and is not)
The three-state
control_system.typeswitchPointing a project at the soft-IOC the stack already deploys
How
osprey sim applyscenarios behave in Virtual Accelerator modeWrite limits, and the archiver live-vs-history divergence
Prerequisites: Docker (or Podman) installed; the Control Assistant tutorial project (see Production Control Systems Tutorial).
Overview#
The Control Assistant tutorial ships three interchangeable control-system
backends, selected by a single control_system.type value:
|
Backend |
|---|---|
|
The in-process simulation. No container, no network — every channel returns a synthesized value. This is the tutorial’s default and needs no setup. |
|
A containerized PyAT soft-IOC serving real EPICS Channel Access. Storage- ring magnet setpoints drive a live lattice and BPM readbacks respond; every other channel is composed by the same simulation engine the mock uses. Requires the container (below). |
|
Production EPICS, pointed at the facility gateway. Untouched by this guide. |
The Virtual Accelerator is a local physics simulator, not a digital twin — it is not synced to any real machine. The OSPREY agent reads and writes it exactly as it does the mock or a real machine; only the backend changes.
Quickstart#
The Control Assistant stack already deploys the Virtual Accelerator: the
preset’s virtual_accelerator: block renders a compose service, so
osprey deploy up brings the soft-IOC up alongside the rest of the stack.
What ships pointed at mock is the connector, not the container — so
switching backends is a config change, not a new deployment.
# 1. Point the project at the soft-IOC it already deploys
osprey config set-control-system virtual_accelerator
# 2. Re-deploy so the running services pick up the new connector
osprey deploy up
# 3. Run the assistant as usual — the agent now talks to real Channel Access
osprey web
The very first osprey deploy up that includes the Virtual Accelerator
builds its container image from source (compiling PyAT and the soft-IOC), so
expect it to take several minutes — it is building, not hanging. Later deploys
reuse the image.
Switch back to the mock at any time with
osprey config set-control-system mock. The epics block keeps its
production values throughout.
Note
Step 2 is what makes the switch take effect for deployed services.
set-control-system edits the project’s config.yml, but services do
not read that file directly — each gets a copy staged into its own directory
at deploy time. A purely local osprey web run picks the change up
immediately; anything already running in a container does not, until you
re-deploy. No image rebuild is involved either way.
Connecting to the IOC#
The container serves Channel Access on 127.0.0.1:5064 in EPICS name-server
mode — the one host-to-container configuration that works reliably across
container runtimes, since broadcast discovery does not cross the container VM
boundary. The project’s virtual_accelerator connector block is configured to
match and sets EPICS_CA_NAME_SERVERS itself, so no client-side EPICS
environment setup is needed.
Running from a source checkout#
If you are working from an OSPREY source checkout rather than a generated project — developing the IOC itself, or running it without deploying a stack — launch the container directly:
./scripts/va/run_va.sh [DATA_DIR]
The image is defined under docker/virtual-accelerator/; see its
README.md for build details. The script builds the image if it is missing
(OSPREY_VA_REBUILD=1 forces a rebuild) and runs in the foreground.
Warning
DATA_DIR is the data/simulation directory (never a single file)
that the container mounts read-only. It defaults to the packaged preset’s
copy, not your project — so with no argument, osprey sim apply in
your project writes a scenario file the running IOC never sees. Pass your
project’s directory explicitly to use its scenarios:
./scripts/va/run_va.sh ~/my-project/data/simulation
Scenarios#
osprey sim apply <scenario> works in Virtual Accelerator mode exactly as it
does for the mock. Applying a scenario writes the project’s active_scenarios
file; the in-container engine polls it and, within about a second, composed
channel values reflect the new scenario. One behavioral difference from the
mock: in VA mode a scenario switch only refreshes the engine-composed channels
— setpoints you wrote during the session live in the IOC’s own records and
survive the switch. (In mock mode, written values are reset.)
This assumes the IOC is reading the same project’s data/simulation. That is
automatic for the deployed service; if you launched the container by hand, see
the warning under Running from a source checkout.
Write limits#
Channels listed in the project’s channel_limits.json carry a min/max range,
and a write outside that range is rejected before it reaches the IOC; an
in-range write goes through. The mandatory write-approval flow and the
control_system.writes_enabled switch apply unchanged — the Virtual
Accelerator connector inherits the same write-safety wiring as the EPICS
connector.
Note
The tutorial runs the limits checker in permissive mode
(limits_checking.allow_unlisted_channels: true), so a channel absent
from channel_limits.json is not blocked. Range enforcement covers listed
channels; it is not a closed allowlist here.
Archiver: live values vs. history#
The mock archiver synthesizes channel history from machine.json. That
history is independent of the Virtual Accelerator’s live physics:
In mock mode, a channel’s live value and its archived history are both synthesized from
machine.json— they agree by construction.In virtual_accelerator mode, live storage-ring readbacks come from the PyAT lattice (a corrector write recomputes the orbit), while the archiver’s history is still the synthetic
machine.jsonseries.
So in Virtual Accelerator mode a lattice channel’s live value and its archived history can diverge in meaning: the live value reflects real simulated physics, the history does not. This is expected — a VA-backed archiver is a separate, future addition.