Skip to content

Commit

Permalink
Merge pull request #18 from pylhc/doros_lhc
Browse files Browse the repository at this point in the history
Reader/writer for DOROS BPM data
  • Loading branch information
JoschD authored Aug 20, 2024
2 parents c5aca3c + f571a3c commit f75a49c
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ instance/

# Sphinx documentation
docs/_build/
doc/_build/
doc_build/

# PyBuilder
Expand Down
41 changes: 28 additions & 13 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import pathlib
import sys

# ignore numpy warnings, see:
# https://stackoverflow.com/questions/40845304/runtimewarning-numpy-dtype-size-changed-may-indicate-binary-incompatibility
import warnings

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")


TOPLEVEL_DIR = pathlib.Path(__file__).parent.parent.absolute()
ABOUT_FILE = TOPLEVEL_DIR / "turn_by_turn" / "__init__.py"
Expand Down Expand Up @@ -63,6 +68,8 @@ def about_package(init_posixpath: pathlib.Path) -> dict:
"sphinx.ext.githubpages",
"sphinx.ext.napoleon",
]
autosectionlabel_prefix_document = True
autosectionlabel_maxdepth = 2

# Add any paths that contain templates here, relative to this directory.
# templates_path = ['_templates']
Expand All @@ -78,7 +85,7 @@ def about_package(init_posixpath: pathlib.Path) -> dict:

# General information about the project.
project = ABOUT_TBT["__title__"]
copyright_ = "2018-2021, pyLHC/OMC-TEAM"
copyright_ = "2018-2024, pyLHC/OMC-TEAM"
author = ABOUT_TBT["__author__"]

# Override link in 'Edit on Github'
Expand All @@ -100,7 +107,7 @@ def about_package(init_posixpath: pathlib.Path) -> dict:
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = "en"

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
Expand All @@ -120,13 +127,6 @@ def about_package(init_posixpath: pathlib.Path) -> dict:
#
html_theme = "sphinx_rtd_theme"

html_theme_options = {
"collapse_navigation": False,
"display_version": True,
"logo_only": True,
"navigation_depth": 2,
}

html_logo = "_static/img/omc_logo.svg"
html_static_path = ["_static"]
html_context = {
Expand All @@ -147,13 +147,28 @@ def about_package(init_posixpath: pathlib.Path) -> dict:
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
html_theme_options = {
'collapse_navigation': False,
'display_version': True,
'logo_only': True,
'navigation_depth': 1,
}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']

# A dictionary of values to pass into the template engine’s context for all
# pages. Single values can also be put in this dictionary using the
# -A command-line option of sphinx-build.
html_context = {
'display_github': True,
# the following are only needed if :github_url: is not set
'github_user': author,
'github_repo': project,
'github_version': 'master/doc/',
}
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
Expand Down
4 changes: 4 additions & 0 deletions doc/modules/index.rst
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
.. automodule:: turn_by_turn.constants
:members:
:noindex:


.. automodule:: turn_by_turn.io
:members:
:noindex:


.. automodule:: turn_by_turn.structures
:members:
:noindex:


.. automodule:: turn_by_turn.utils
:members:
:noindex:

16 changes: 15 additions & 1 deletion doc/readers/index.rst
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
.. automodule:: turn_by_turn.doros
:members:
:noindex:


.. automodule:: turn_by_turn.esrf
:members:
:noindex:


.. automodule:: turn_by_turn.iota
:members:
:noindex:


.. automodule:: turn_by_turn.lhc
:members:
:noindex:


.. automodule:: turn_by_turn.sps
:members:
:noindex:


.. automodule:: turn_by_turn.ptc
:members:
:noindex:


.. automodule:: turn_by_turn.trackone
:members:

:noindex:
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ exclude = [
packages = ["turn_by_turn"]

[project]
name = "tfs-turn_by_turn"
name = "turn_by_turn"
readme = "README.md"
description = "Read and write turn-by-turn measurement files from different particle accelerator formats."
authors = [
Expand Down
Binary file added tests/inputs/test_doros.h5
Binary file not shown.
115 changes: 115 additions & 0 deletions tests/test_doros.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@

from datetime import datetime
from pathlib import Path

import numpy as np
import pandas as pd
import pytest
import h5py

from turn_by_turn.constants import PRINT_PRECISION
from turn_by_turn.errors import DataTypeError
from turn_by_turn.structures import TbtData, TransverseData
from tests.test_lhc_and_general import create_data, compare_tbt

from turn_by_turn.doros import N_ORBIT_SAMPLES, read_tbt, write_tbt, DEFAULT_BUNCH_ID, POSITIONS

INPUTS_DIR = Path(__file__).parent / "inputs"


def test_read_write_real_data(tmp_path):
tbt = read_tbt(INPUTS_DIR / "test_doros.h5", bunch_id=10)

assert tbt.nbunches == 1
assert len(tbt.matrices) == 1
assert tbt.nturns == 50000
assert tbt.matrices[0].X.shape == (3, tbt.nturns)
assert tbt.matrices[0].Y.shape == (3, tbt.nturns)
assert len(set(tbt.matrices[0].X.index)) == 3
assert np.all(tbt.matrices[0].X.index == tbt.matrices[0].Y.index)

file_path = tmp_path / "test_file.h5"
write_tbt(tbt, file_path)
new = read_tbt(file_path, bunch_id=10)
compare_tbt(tbt, new, no_binary=False)


def test_write_read(tmp_path):
tbt = _tbt_data()
file_path = tmp_path / "test_file.h5"
write_tbt(tbt, file_path)
new = read_tbt(file_path)
compare_tbt(tbt, new, no_binary=False)


def test_read_raises_different_bpm_lengths(tmp_path):
tbt = _tbt_data()
file_path = tmp_path / "test_file.h5"
write_tbt(tbt, file_path)

bpm = tbt.matrices[0].X.index[0]

# modify the BPM lengths in the file
with h5py.File(file_path, "r+") as h5f:
delta = 10
del h5f[bpm][N_ORBIT_SAMPLES]
h5f[bpm][N_ORBIT_SAMPLES] = [tbt.matrices[0].X.shape[1] - delta]
for key in POSITIONS.values():
data = h5f[bpm][key][:-delta]
del h5f[bpm][key]
h5f[bpm][key] = data

with pytest.raises(ValueError) as e:
read_tbt(file_path)
assert "Not all BPMs have the same number of turns!" in str(e)


def test_read_raises_on_different_bpm_lengths_in_data(tmp_path):
tbt = _tbt_data()
file_path = tmp_path / "test_file.h5"
write_tbt(tbt, file_path)

bpms = [tbt.matrices[0].X.index[i] for i in (0, 2)]

# modify the BPM lengths in the file
with h5py.File(file_path, "r+") as h5f:
for bpm in bpms:
del h5f[bpm][N_ORBIT_SAMPLES]
h5f[bpm][N_ORBIT_SAMPLES] = [tbt.matrices[0].X.shape[1] + 10]

with pytest.raises(ValueError) as e:
read_tbt(file_path)
assert "Found BPMs with different data lengths" in str(e)
assert all(bpm in str(e) for bpm in bpms)


def _tbt_data() -> TbtData:
"""TbT data for testing. Adding random noise, so that the data is different per BPM."""
nturns = 2000
bpms = ["TBPM1", "TBPM2", "TBPM3", "TBPM4"]

return TbtData(
matrices=[
TransverseData(
X=pd.DataFrame(
index=bpms,
data=create_data(
np.linspace(-np.pi, np.pi, nturns, endpoint=False),
nbpm=len(bpms), function=np.sin, noise=0.02
),
dtype=float,
),
Y=pd.DataFrame(
index=bpms,
data=create_data(
np.linspace(-np.pi, np.pi, nturns, endpoint=False),
nbpm=len(bpms), function=np.cos, noise=0.015
),
dtype=float,
),
)
],
date=datetime.now(),
bunch_ids=[DEFAULT_BUNCH_ID],
nturns=nturns,
)
4 changes: 2 additions & 2 deletions tests/test_lhc_and_general.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ def compare_tbt(origin: TbtData, new: TbtData, no_binary: bool, max_deviation =
assert np.all(origin_mat == new_mat)


def create_data(phases, nbpm, function) -> np.ndarray:
return np.ones((nbpm, len(phases))) * function(phases)
def create_data(phases, nbpm, function, noise: float = 0) -> np.ndarray:
return np.ones((nbpm, len(phases))) * function(phases) + noise * np.random.randn(nbpm, len(phases))


@pytest.fixture()
Expand Down
2 changes: 1 addition & 1 deletion turn_by_turn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
__title__ = "turn_by_turn"
__description__ = "Read and write turn-by-turn measurement files from different particle accelerator formats."
__url__ = "https://github.com/pylhc/turn_by_turn"
__version__ = "0.6.0"
__version__ = "0.7.0"
__author__ = "pylhc"
__author_email__ = "pylhc@github.com"
__license__ = "MIT"
Expand Down
Loading

0 comments on commit f75a49c

Please sign in to comment.