Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Build and deploy accml_lib docs

on:
push:
branches: [ main ] # build on pushes to main (adjust as needed)
pull_request:
branches: [ main ]

jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: true

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11' # choose your supported version

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python3 -m pip install -e ./[bluesky-epics,docs,pyat-simulator]

- name: Build docs
working-directory: docs
run: |
python3 -m sphinx.ext.apidoc -o src/ ../src/accml_lib/ || echo "failed to create auto/API docs!"
python3 -m sphinx.cmd.build --conf-dir ./ ./ _build/html/

- name: Deploy to gh-pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/_build/html
# optional: cname: docs.example.com
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# emacs autosave files
*~

# doc build files
doc/_build/
# put api auto generated doc here
doc/src/

# files for running fixtures
fixtures/

Expand Down
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# accml: Accelerator middle layer
# accml_lib: Particle accelerator middle layer: library part

`accml` is a software stack designed to facilitate implementing tools
characterising (high) energy charged accelerator.
Expand All @@ -10,6 +10,9 @@ These tools typically address:

For details of its concept see [design.md](https://github.com/python-accelerator-middle-layer/accml/design.md).

Additional [![Documentation](https://github.com/python-accelerator-middle-layer/accml_lib/actions/workflows/docs.yml/badge.svg)](https://python-accelerator-middle-layer.github.io/accml_lib/)


## 🚀 Installation and Running Instructions

### 1. Clone the Repository
Expand All @@ -25,9 +28,31 @@ git checkout dev/main
git submodule update --init --recursive
```
### 3. Install the Package

Please note: typically, especially as a user, you would install
accml, which in turn will install accml_lib. So typically
you want to look to https://github.com/python-accelerator-middle-layer/accml
and install everything there

#### 3.1 Installing only accml_lib for an EPICS facility

For an EPICS facility install

```bash
python3 -m pip install -e .
python3 -m pip install -e \
./[bluesky-epics,pyat-simulator]
```

#### 3.1 Installing only accml_lib for a TANGO facility

**NB** this installation is not yet tested. In case of
experiencing trouble please drop us a line or share your
experience in case of success.
```bash
python3 -m pip install -e \
./[bluesky-tango,pyat-simulator]
```

### 4. Run the Virtual Accelerator (Test bench) --EPICS VERSION
```bash
apptainer run oras://registry.hzdr.de/digital-twins-for-accelerators/containers/pyat-softioc-digital-twin:v0-1-2-bessy.2475331
Expand Down
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
55 changes: 55 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import os
import sys
# so accml_lib is found
sys.path.insert(0, os.path.abspath('../../'))

project = 'accml_lib'
copyright = '2026, Helmholtz Zentrum Berlin'
author = 'Pierre Schnizer, Waheedullah Sulaiman Khail'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.viewcode',
'sphinx.ext.intersphinx',
'sphinx_autodoc_typehints',
'sphinx.ext.autosummary',
"sphinx.ext.todo",
"sphinxcontrib.bibtex",
]
bibtex_bibfiles = ["refs.bib"]

# Optional formatting settings:
bibtex_default_style = "unsrt" # or "alpha", "plain", etc.
bibtex_reference_style = "author_year" # controls :cite: rendering style

autosummary_generate = True
autodoc_typehints = 'description'
html_theme = 'sphinx_rtd_theme'

templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']


# optional: mock imports if your package has heavy optional deps
# autodoc_mock_imports = ['numpy', 'scipy', "lat2db", "acclerator-toolbox"]


# Intersphinx configuration: cross-reference external docs
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"numpy": ("https://numpy.org/doc/stable", None),
"scipy": ("https://docs.scipy.org/doc/scipy", None),
}

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_static_path = ['_static']

# -- Options for todo extension ----------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/extensions/todo.html#configuration

todo_include_todos = True
16 changes: 16 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
accml\_lib: documentation
=========================

accml contains all the modules which are used by
client code or twin.

Its general concepts are explained in
the design document of `accml`.

API
---

.. toctree::
:maxdepth: 4

src/accml_lib
35 changes: 35 additions & 0 deletions docs/make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
12 changes: 11 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,18 @@ transitions = {version ="*", optional = true}
# for testing
pytest-asyncio = {version = "*", optional = true}

# for documentation
sphinx = {version = ">=5", optional = true}
sphinx-rtd-theme = {version = "*", optional = true}
sphinx-autodoc-typehints = {version = "*", optional = true}
sphinxcontrib-napoleon = {version = "*", optional = true}
sphinxcontrib-bibtex = {version = "*", optional = true}


[tool.poetry.extras]
bluesky-epics = ["ophyd-async", "aioca", "p4p", "epics", "bluesky", "databroker"]
bluesky-tango = ["ophyd-async", "pytango", "bluesky", "databroker"]
pyat-simulator = ["accelerator-toolbox", "transitions"]
testing = ["pytest-asyncio", "accelerator-toolbox", "transitions"]
testing = ["pytest-asyncio", "accelerator-toolbox", "transitions"]

docs = ["sphinx", "sphinx-rtd-theme", "sphinx-autodoc-typehints", "sphinxcontrib-napoleon", "sphinxcontrib-bibtex"]
1 change: 1 addition & 0 deletions src/accml_lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__all__ = ["core", "custom"]
18 changes: 18 additions & 0 deletions src/accml_lib/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""accm\_lib core packages

The `core` contains the modules which
are to be independent of any facility
and form the basis of `àccml\_lib`.

It contains the following main parts:

* models: data models used within the package
* bl: business logic or modules that provide
basic functionality
* config: configuration data

Interfaces are used to export the interfaces
used within this package.
"""

__all__ = ["model", "config", "bl", "interfaces"]
8 changes: 8 additions & 0 deletions src/accml_lib/core/bl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__all__ = [
"yellow_pages",
"liaison_manager",
"translator_service",
"command_rewritter",
"unit_conversion",
"delta_backend"
]
61 changes: 42 additions & 19 deletions src/accml_lib/core/bl/command_rewritter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

Please note:
here we have to map (lattice_name, property) -> (device_name, property)

Todo:
Split up content in different modules
"""

from typing import Sequence
Expand All @@ -14,7 +11,11 @@
from ...core.interfaces.utils.liaison_manager import LiaisonManagerBase
from ...core.interfaces.utils.translator_service import TranslatorServiceBase
from ...core.model.utils.command import Command
from ...core.model.utils.identifiers import DevicePropertyID, LatticeElementPropertyID, ConversionID
from ...core.model.utils.identifiers import (
DevicePropertyID,
LatticeElementPropertyID,
ConversionID,
)


class CommandRewriter(CommandRewriterBase):
Expand All @@ -28,7 +29,11 @@ class CommandRewriter(CommandRewriterBase):
to convert the command values between representations.
"""

def __init__(self, liaison_manager: LiaisonManagerBase, translation_service: TranslatorServiceBase):
def __init__(
self,
liaison_manager: LiaisonManagerBase,
translation_service: TranslatorServiceBase,
):
"""
Initialize the CommandRewriter with the required services.

Expand All @@ -49,17 +54,24 @@ def inverse(self, cmd: Command) -> Sequence[Command]:
Returns:
A sequence of commands corresponding to the inverse translations.
"""
dev_prop_id = DevicePropertyID(
device_name=cmd.id, property=cmd.property
)
dev_prop_id = DevicePropertyID(device_name=cmd.id, property=cmd.property)
rcmd = self.inverse_read_command(cmd)
lat_prop_ids = [LatticeElementPropertyID(element_name=r.id, property=r.property) for r in rcmd]

return [self.inverse_translate_one(cmd, dev_prop_id, lat_prop_id) for lat_prop_id in lat_prop_ids]

def inverse_translate_one(self, cmd: Command, dev_prop_id: DevicePropertyID,
lat_prop_id: LatticeElementPropertyID
) -> Command:
lat_prop_ids = [
LatticeElementPropertyID(element_name=r.id, property=r.property)
for r in rcmd
]

return [
self.inverse_translate_one(cmd, dev_prop_id, lat_prop_id)
for lat_prop_id in lat_prop_ids
]

def inverse_translate_one(
self,
cmd: Command,
dev_prop_id: DevicePropertyID,
lat_prop_id: LatticeElementPropertyID,
) -> Command:
"""
Perform a single inverse translation.

Expand All @@ -72,11 +84,15 @@ def inverse_translate_one(self, cmd: Command, dev_prop_id: DevicePropertyID,
A new Command with the value converted to the lattice state.
"""
translation_object = self.translator_service.get(
ConversionID(lattice_property_id=lat_prop_id, device_property_id=dev_prop_id)
ConversionID(
lattice_property_id=lat_prop_id, device_property_id=dev_prop_id
)
)

if dev_prop_id.device_name is None:
raise ValueError("Device name cannot be None in device property identifier.")
raise ValueError(
"Device name cannot be None in device property identifier."
)

ncmd = Command(
id=lat_prop_id.element_name,
Expand All @@ -94,7 +110,9 @@ def forward(self, cmd: Command) -> Command:
dev_prop_id = DevicePropertyID(device_name=rcmd.id, property=rcmd.property)

translation_object = self.translator_service.get(
ConversionID(lattice_property_id=lat_prop_id, device_property_id=dev_prop_id)
ConversionID(
lattice_property_id=lat_prop_id, device_property_id=dev_prop_id
)
)
ncmd = Command(
id=dev_prop_id.device_name,
Expand All @@ -116,4 +134,9 @@ def inverse_read_command(self, command: ReadCommand) -> Sequence[ReadCommand]:
device_name=command.id, property=command.property
)
lat_prop_ids = self.liaison_manager.inverse(dev_prop_id)
return [ReadCommand(id=lp.element_name, property=lp.property) for lp in lat_prop_ids]
return [
ReadCommand(id=lp.element_name, property=lp.property) for lp in lat_prop_ids
]


__all__ = ["CommandRewriter"]
Loading
Loading