diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index c06d3621..c40130af 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -34,8 +34,8 @@ jobs: - name: Print Versions run: | - .github/workflows/pip-versions.sh - + .github/workflows/pip-versions.sh + centos-stream: runs-on: ubuntu-latest @@ -60,8 +60,8 @@ jobs: - name: Print Versions run: | - .github/workflows/pip-versions.sh - + .github/workflows/pip-versions.sh + ubuntu: @@ -88,7 +88,7 @@ jobs: - name: Print Versions run: | - .github/workflows/pip-versions.sh + .github/workflows/pip-versions.sh conda: @@ -120,7 +120,7 @@ jobs: - name: Print Versions run: | - .github/workflows/conda-versions.sh + .github/workflows/conda-versions.sh mpich: runs-on: ubuntu-latest @@ -145,4 +145,7 @@ jobs: - name: Print Versions run: | - .github/workflows/pip-versions.sh \ No newline at end of file + .github/workflows/pip-versions.sh + + build-docs: + uses: ./.github/workflows/docs-build.yml diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml new file mode 100644 index 00000000..85f4143a --- /dev/null +++ b/.github/workflows/docs-build.yml @@ -0,0 +1,42 @@ +on: + workflow_call: + inputs: + upload-artifact: + required: false + type: boolean + default: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: po3 + channels: conda-forge,defaults + auto-update-conda: true + environment-file: ./doc/environment.yml + + - name: Install PyORBIT3 + shell: bash -l {0} + run: | + # @woodtp -- MPI isn't required to build docs and enabling it causes Sphinx to segfault. + conda activate po3 + pip install --config-settings=setup-args="-DUSE_MPI='none'" . + + - name: Build Documentation + shell: bash -l {0} + run: | + conda activate po3 + cd doc + make dirhtml + + - name: Upload artifact + if: ${{ inputs.upload-artifact }} + uses: actions/upload-pages-artifact@v3 + with: + path: './doc/build/dirhtml' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ce5f24dc..f7b27d12 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,15 +1,11 @@ -# Simple workflow for deploying static content to GitHub Pages name: Deploy Documentation to Pages on: - # Runs on pushes targeting the default branch push: branches: ["main"] - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write @@ -22,29 +18,36 @@ concurrency: cancel-in-progress: false jobs: - # Single deploy job since we're just deploying + build: + uses: ./.github/workflows/docs-build.yml + with: + upload-artifact: true + deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + needs: build runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: po3 - environment-file: docs-environment.yml - - uses: ammaraskar/sphinx-action@master - with: - docs-folder: "doc/" - # - name: Setup Pages - # uses: actions/configure-pages@v5 - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - # Upload entire repository - path: './doc/build/html' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + - name: Download Artifact + uses: actions/download-artifact@v5 + with: + name: github-pages + - name: Extract artifact.tar in place + run: | + TARFILE=$(find . -type f -name "*.tar*" | head -n1) + if [ -z "$TARFILE" ]; then + echo "No tar archive found in workspace" + ls -la + exit 1 + fi + mkdir -p ./site + tar -xvf "$TARFILE" -C ./site + + - name: List extracted site + run: ls -R ./site + + - name: Deploy + uses: peaceiris/actions-gh-pages@v4 + with: + deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} + external_repository: pyorbit-collaboration/pyorbit-collaboration.github.io + publish_dir: ./site diff --git a/.gitignore b/.gitignore index 47650c0e..da0ba329 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,9 @@ PyORBIT.egg-info *.so .*.swp .eggs +.venv +_autosummary/ +_api/ +_cpp_api/ +doc/source/reference/*.rst +doc/source/reference/*.rst.include diff --git a/README.md b/README.md index 8e5b4277..b896f29f 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,16 @@ The following commands may require root access.
Click for Ubuntu-based distributions - + ```bash apt-get update apt-get install -y build-essential python3 libfftw3-dev python3-venv libpython3-dev pkg-config git - ``` + ```
Click for Redhat-based distributions - + ```bash dnf group install -y "Development Tools" dnf install -y python3-devel fftw3-devel @@ -47,16 +47,18 @@ The following commands may require root access.
Click for Mac - + Install Homebrew, make sure that homebrew programs are in the **$PATH** (optional step in Homebrew installation) + ```bash brew install pkg-config fftw ```
- + #### Create Python virtual environment Make sure that you have the correct python version installed. We require python>3.9.
Create virtual environment. + ``` python3 -m venv .po3 . .po3/bin/activate @@ -70,6 +72,7 @@ The following commands may require root access. First of all make sure you have conda installed and development packages.
Development packages for Ubuntu: + ``` apt update -y apt install -y curl gpg git build-essential @@ -87,18 +90,18 @@ pip install -U meson-python setuptools setuptools-scm ## 3. Build -If you plan to modify PyORBIT's code, install it in editable mode. +If you plan to modify PyORBIT's code, install it in editable mode. You will NOT need to rebuild after modifications to the code. [Meson](MesonBuild.md) will rebuild as necessary on import. ``` pip install --no-build-isolation --editable . ``` Alternatively if you don't plan to modify PyORBIT's code + ``` pip install . ``` - ## 4. Run full SNS linac example Navigate to your **examples** directory and launch tracking of SNS linac. @@ -116,9 +119,9 @@ pip install --config-settings=setup-args="-DUSE_MPI=none" . Above will build PyORBIT without MPI even if MPI is present. You can change that option to `mpich`, `ompi`, `none` or `auto` (default).
| MPI flavor | Installation command | |---------------|--------------| -| No MPI | `pip install --config-settings=setup-args="-DUSE_MPI=none" .` | -| The first found MPI installation if any | `pip install --config-settings=setup-args="-DUSE_MPI=auto" .` | -| OpenMPI | `pip install --config-settings=setup-args="-DUSE_MPI=ompi" .` | +| No MPI | `pip install --config-settings=setup-args="-DUSE_MPI=none" .` | +| The first found MPI installation if any | `pip install --config-settings=setup-args="-DUSE_MPI=auto" .` | +| OpenMPI | `pip install --config-settings=setup-args="-DUSE_MPI=ompi" .` | | MPICH | `pip install --config-settings=setup-args="-DUSE_MPI=mpich" .` | Meson uses PKG_CONFIG to discover packages. It could be useful to help it to find your MPI installation: diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 00000000..ca5796cb --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,2 @@ +_doxygen +source/api diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..3767ad00 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,24 @@ +# 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 = source +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 clean + +# 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) + +clean: + rm -rf "$(SOURCEDIR)"/_doxygen "$(SOURCEDIR)"/reference/*.rst{,.include} "$(SOURCEDIR)"/_autosummary + @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/environment.yml b/doc/environment.yml new file mode 100644 index 00000000..c7a2e01f --- /dev/null +++ b/doc/environment.yml @@ -0,0 +1,23 @@ +name: docs +channels: + - conda-forge +dependencies: + - doxygen + - exhale + - pydata-sphinx-theme + - python=3.13 + - sphinx + - sphinx-copybutton + - sphinx-autodoc-typehints + - sphinx-automodapi + - myst-parser + - breathe + - standard-imghdr + - fftw + - numpy + - scipy + - matplotlib + - meson + - pkg-config + - ninja + - graphviz diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 00000000..747ffb7b --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +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 diff --git a/doc/placeholder b/doc/source/_static/.gitkeep similarity index 100% rename from doc/placeholder rename to doc/source/_static/.gitkeep diff --git a/doc/source/_templates/.gitkeep b/doc/source/_templates/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100644 index 00000000..b9496fef --- /dev/null +++ b/doc/source/conf.py @@ -0,0 +1,87 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent.parent / "py/")) + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "PyORBIT3" +copyright = "2025, PyORBIT Collaboration" +author = "PyORBIT Collaboration" +release = "v3.0.1" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.coverage", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + # "sphinx.ext.autodoc", + # "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx_autodoc_typehints", + "sphinx_automodapi.automodapi", + "sphinx_automodapi.smart_resolver", + "sphinx_copybutton", + "myst_parser", + "breathe", + "exhale", +] + +autodoc_typehints = "description" + +napoleon_numpy_docstring = True +autosummary_imported_members = True + +numpydoc_show_class_members = False +automodapi_toctreedirnm = "reference" + +templates_path = ["_templates"] +exclude_patterns = [] + +# -- Breathe and Exhale Options --------------------------------------------------------- +breathe_projects = { + "PyORBIT3": "./_doxygen/xml", +} +breathe_default_project = "PyORBIT3" + +doxyfile = "\n".join( + [ + "INPUT = ../../src", + "EXCLUDE_PATTERNS = *wrap* *_init.cc", + "PREDEFINED += DOXYGEN_SHOULD_SKIP_THIS", + 'PREDEFINED += PyObject_HEAD="PyObject ob_base;"', + ] +) + +exhale_args = { + "containmentFolder": "./reference", + "rootFileName": "cpp.rst", + "doxygenStripFromPath": "..", + "rootFileTitle": "C++ API Reference", + "fullApiSubSectionTitle": "", + "createTreeView": True, + "exhaleExecutesDoxygen": True, + "exhaleDoxygenStdin": doxyfile, +} + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "pydata_sphinx_theme" +html_static_path = ["_static"] +html_show_source_link = True +html_theme_options = { + "github_url": "https://github.com/PyORBIT-Collaboration/PyORBIT3", + "logo": { + "text": "PyORBIT3", + }, +} diff --git a/doc/source/contributing.md b/doc/source/contributing.md new file mode 100644 index 00000000..110f6fdb --- /dev/null +++ b/doc/source/contributing.md @@ -0,0 +1,146 @@ +# Contributor Guide + +## Building the Docs + +To build and preview the documentation locally, create a new Conda environment from the `docs-environment.yml` and activate it. +Then, `cd` into the `doc/` directory and run `make html`. + +```sh +conda env create -f docs-environment.yml -n docs +conda activate docs +cd doc +make html +``` + +You can view the documentation in your browser by opening `docs/build/html/index.html`. + +## Creating Static Pages + +Simple pages may be written in Markdown or [reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) and stored in the `doc/source/` directory. +Markdown support is enabled via the MyST parser extension, refer to the [MyST syntax guide](https://myst-parser.readthedocs.io/en/latest/syntax/typography.html) +for more information related to composition of Markdown documents. +Add the file to a `toc` block in the `doc/source/index.rst` in order for it be included in the main documentation. + + +1. Create your document containing useful information under `doc/source/useful_info.md`. + +```md + + +# Useful Information +``` + +2. Add the file to a `toc` block in the `doc/source/index.rst`. + +```rst +.. toctree:: + + useful_info +``` + +:::{tip} +If you want to create a hierarchical structure of pages, add the subdirectory to the `doc/source/` directory and add the file to a `toc` block in the parent file. +::: + +## Documenting Python Code + +### Where to add documentation + +Add or edit docstrings directly in the Python source files following the NumPy docstring style. +For generated API pages, autosummary will produce stub pages from the docstrings and signatures. +Any new top-level `orbit` submodule must be registered by adding it to `docs/source/modules.rst`. + +### Docstring format + +Use the [NumPy docstring convention](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard). +Keep short summary on the first line. +Use Parameters with a table-like list; include types and optional default values. + +```python +def foo(x, y=1): + """ + Short one-line summary. + + Longer description (optional), explaining behavior, side effects, and important details. + + .. math:: + + z = x \cdot y + + Parameters + ---------- + x : int + Description of x. + y : int, optional + Description of y. Defaults to 1. + + Returns + ------- + float + Description of the return value. + + Raises + ------ + ValueError + If x is negative. + + """ + ... +``` + +## Documenting C++ Code + +### Where to add documentation + +Add Doxygen comments directly in the C++ header or source files above the declarations. + +Example documenting a function: + +```cpp +/** + * @brief Compute a simple value. + * + * The relation is given by the LaTeX expression: \f\$ z = x \cdot y \f\$. + * Detailed description if needed. + * + * @param x First input. + * @param y Second input. + * @return Computed value. + * @throws std::invalid\_argument if x <= 0. + * + * Example: + * @code + * double r = foo(x, y); + * @endcode + */ +double foo(double x, double y); +``` + +Example documenting a class: + +```cpp +/** + * @class Bar + * @brief Represents a simple concept. + * + * More detailed class description. + * + * Mathematical note: \f\$E = mc^2\f\$ + */ +class Bar { +public: + /** Construct with a value. */ + Bar(double v); + /** Return the value. */ + double value() const; + // ... +}; +``` + +## For Maintainers + +The docs are deployed to the [main GitHub Pages repository for the PyORBIT Collaboration](https://github.com/pyorbit-collaboration/pyorbit-collaboration.github.io) +via an Action ([peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages)) defined in [PyORBIT-Collaboration/PyORBIT3/.github/workflow/docs.yml](https://github.com/PyORBIT-Collaboration/PyORBIT3/blob/main/.github/workflows/docs.yml). +The action requires a [Deploy Key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys) to be added to the PyORBIT-Collaboration/PyORBIT-Collaboration.github.io repository +as well as a [Secret](https://docs.github.com/en/actions/security-guides/automatic-token-authentication) to be created in the PyORBIT-Collaboration/PyORBIT3 repository. +Refer to the [instructions for deploying to an external repository](https://github.com/peaceiris/actions-gh-pages?tab=readme-ov-file#%EF%B8%8F-deploy-to-external-repository-external_repository) and [Create SSH Deploy Key](https://github.com/peaceiris/actions-gh-pages?tab=readme-ov-file#tips-and-faq) for more details. diff --git a/doc/source/home.md b/doc/source/home.md new file mode 100644 index 00000000..11a94892 --- /dev/null +++ b/doc/source/home.md @@ -0,0 +1 @@ +# PyORBIT3 diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 00000000..55503272 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,28 @@ +.. PyORBIT3 documentation master file, created by + sphinx-quickstart on Mon Dec 15 20:07:23 2025. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +.. include:: ./home.md + :parser: myst_parser.sphinx_ + +.. toctree:: + :maxdepth: 2 + :caption: Getting Started + + install + contributing + +.. toctree:: + :maxdepth: 2 + :caption: API Reference + + reference/py + reference/cpp + + +Indices and tables +================== +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/source/install.md b/doc/source/install.md new file mode 100644 index 00000000..db05c1d2 --- /dev/null +++ b/doc/source/install.md @@ -0,0 +1,211 @@ +# Installation Guide + +```{contents} +:depth: 3 +``` + +This guide provides instructions how to install PyORBIT code.
+This guide doesn't cover MPI enabled installation.
+The following configurations are included in CI testing, versions will change as the runner images progress. + +| HW | Architecture | OS | Python | Compiler | Package | +|---------------|--------------|---------------|---------|--------------|--------------| +| PC | x86_64 | CentOS latest | 3.9.18 | gcc-11.4.1 | pip-24.0 | +| PC | x86_64 | Ubuntu latest | 3.12.3 | gcc-13.2.0 | pip-24.0 | +| Apple Silicon | arm64 | macOS 14 | 3.12.3 | clang-15.0.0 | pip-24.0 | +| PC | x86_64 | Ubuntu latest | 3.10.14 | gcc-13.2.0 | conda-24.5.0 | + + + +## Installation from source + +First step is to clone the source code: + +```bash +git clone https://github.com/PyORBIT-Collaboration/PyORBIT3.git +``` + +### Pip Setup +**PIP** based setup is more involved, we recommend using **conda** if unsure. +#### Prepare OS +The following commands may require root access. + +
+ Click for Ubuntu-based distributions + + ```bash + apt-get update + apt-get install -y build-essential python3 libfftw3-dev python3-venv libpython3-dev pkg-config git + ``` +
+ +
+ Click for Redhat-based distributions + + ```bash + dnf group install -y "Development Tools" + dnf install -y python3-devel fftw3-devel + ``` +
+ +
+ Click for Mac + + Install Homebrew, make sure that homebrew programs are in the **$PATH** (optional step in Homebrew installation) + + ```bash + brew install pkg-config fftw + ``` +
+ + #### Create Python virtual environment + Make sure that you have the correct python version installed. We require python>3.9.
+ Create virtual environment. + + ``` + python3 -m venv .po3 + . .po3/bin/activate + pip install -U pip + pip install -r requirements.txt + pip install -U setuptools + ``` + + +### Conda Setup + +First of all make sure you have conda installed and development packages.
+Development packages for Ubuntu: + +``` +apt update -y +apt install -y curl gpg git build-essential +``` + +Then run the following: + +```bash +cd pyorbit3 +conda env create -n po3 --file environment.yml +conda activate po3 +pip install -U meson-python setuptools setuptools-scm +``` + + +## Build + +If you plan to modify PyORBIT's code, install it in editable mode. +You will NOT need to rebuild after modifications to the code. [Meson](meson-primer) will rebuild as necessary on import. +``` +pip install --no-build-isolation --editable . +``` + +Alternatively if you don't plan to modify PyORBIT's code + +``` +pip install . +``` + +## 4. Run full SNS linac example + +Navigate to your **examples** directory and launch tracking of SNS linac. + +```bash +cd examples/SNS_Linac/pyorbit3_linac_model/ +python pyorbit3_sns_linac_mebt_hebt2.py +``` + +## MPI consideration + +By default, the build system will try to find MPI and compile against it. You can control which MPI to use with command line option when building. +```bash +pip install --config-settings=setup-args="-DUSE_MPI=none" . +``` +Above will build PyORBIT without MPI even if MPI is present. You can change that option to `mpich`, `ompi`, `none` or `auto` (default).
+| MPI flavor | Installation command | +|---------------|--------------| +| No MPI | `pip install --config-settings=setup-args="-DUSE_MPI=none" .` | +| The first found MPI installation if any | `pip install --config-settings=setup-args="-DUSE_MPI=auto" .` | +| OpenMPI | `pip install --config-settings=setup-args="-DUSE_MPI=ompi" .` | +| MPICH | `pip install --config-settings=setup-args="-DUSE_MPI=mpich" .` | + +Meson uses PKG_CONFIG to discover packages. It could be useful to help it to find your MPI installation: + +```bash +PKG_CONFIG_PATH=/opt/lib/pkgconfig pip install --verbose . +``` + +(meson-primer)= +# Meson Primer + +This uses meson-python to build orbit package. + +There is no **setup.py** file, instead we have **meson.build**. +**pyproject.toml** is changed to use meson. + +This is experimental setup that is work in progress. +The pure python part is built with hierarchical **meson.build** files in **py/**. +The C++ setup is combined in one file **src/meson.build**. + +## Main modifications in C++ code +1. **src/libmain/** is not used, still there for reference but will be gone soon. +2. **src/core/** contains one C++ file per module inside _orbit.core_ +3. The files **wrap_XXXX.cc** were modified to correctly reference modules +```cpp +// line +PyObject* mod = PyImport_ImportModule("_bunch"); +// replaced with +PyObject* mod = PyImport_ImportModule("orbit.core.bunch"); +``` + +## Setup + +### 0. Required software + +One needs compilers, python and libfftw (and potentially mpi). +See [PyORBIT3](https://github.com/PyORBIT-Collaboration/PyORBIT3) for external +requirements. + + +### 1. Preparing environment + +First step is to clone the source code from meson branch: + +```bash +git clone -b meson https://github.com/azukov/PyORBIT3.git +``` + +Initialize new virtual environment and install packages + +``` +python -m venv .mes +source .mes/bin/activate +pip install -U pip +pip install -r requirements +``` +Edit **meson.build** and set correct paths/flags for python/fftw3 headers and libraries + +## 2. Build + +To install orbit package in development mode run following: +```bash + pip install --no-build-isolation --editable . +``` +No rebuild is necessary, just edit **py/** or **src/** and meson will rebuild as needed when import happens. + + +### 3. Run examples + +Special examples used for meson testing + +```bash +cd examples/meson +python imports_test.py +python uspas_test.py +``` + +SNS linac example +```bash +cd examples/SNS_Linac/pyorbit3_linac_model/ +python pyorbit3_sns_linac_mebt_hebt2.py +``` + diff --git a/doc/source/reference/orbit.aperture.md b/doc/source/reference/orbit.aperture.md new file mode 100644 index 00000000..7cdfeb25 --- /dev/null +++ b/doc/source/reference/orbit.aperture.md @@ -0,0 +1,6 @@ +# orbit.aperture + +```{eval-rst} +.. automodapi:: orbit.aperture + :no-heading: +``` diff --git a/doc/source/reference/orbit.bumps.md b/doc/source/reference/orbit.bumps.md new file mode 100644 index 00000000..9843c369 --- /dev/null +++ b/doc/source/reference/orbit.bumps.md @@ -0,0 +1,6 @@ +# orbit.bumps + +```{eval-rst} +.. automodapi:: orbit.bumps + :no-heading: +``` diff --git a/doc/source/reference/orbit.bunch_generators.md b/doc/source/reference/orbit.bunch_generators.md new file mode 100644 index 00000000..3cd509c1 --- /dev/null +++ b/doc/source/reference/orbit.bunch_generators.md @@ -0,0 +1,6 @@ +# orbit.bunch_generators + +```{eval-rst} +.. automodapi:: orbit.bunch_generators + :no-heading: +``` diff --git a/doc/source/reference/orbit.bunch_utils.md b/doc/source/reference/orbit.bunch_utils.md new file mode 100644 index 00000000..a13530fb --- /dev/null +++ b/doc/source/reference/orbit.bunch_utils.md @@ -0,0 +1,6 @@ +# orbit.bunch_utils + +```{eval-rst} +.. automodapi:: orbit.bunch_utils + :no-heading: +``` diff --git a/doc/source/reference/orbit.collimation.md b/doc/source/reference/orbit.collimation.md new file mode 100644 index 00000000..49e5aa3f --- /dev/null +++ b/doc/source/reference/orbit.collimation.md @@ -0,0 +1,6 @@ +# orbit.collimation + +```{eval-rst} +.. automodapi:: orbit.collimation + :no-heading: +``` diff --git a/doc/source/reference/orbit.core.md b/doc/source/reference/orbit.core.md new file mode 100644 index 00000000..6cc6c69b --- /dev/null +++ b/doc/source/reference/orbit.core.md @@ -0,0 +1,6 @@ +# orbit.core + +```{eval-rst} +.. automodapi:: orbit.core + :no-heading: +``` diff --git a/doc/source/reference/orbit.diagnostics.md b/doc/source/reference/orbit.diagnostics.md new file mode 100644 index 00000000..5de6a8d6 --- /dev/null +++ b/doc/source/reference/orbit.diagnostics.md @@ -0,0 +1,6 @@ +# orbit.diagnostics + +```{eval-rst} +.. automodapi:: orbit.diagnostics + :no-heading: +``` diff --git a/doc/source/reference/orbit.errors.md b/doc/source/reference/orbit.errors.md new file mode 100644 index 00000000..aa2e69ef --- /dev/null +++ b/doc/source/reference/orbit.errors.md @@ -0,0 +1,7 @@ +# orbit.errors + +```{eval-rst} +.. automodapi:: orbit.errors + :no-heading: +``` + diff --git a/doc/source/reference/orbit.fieldtracker.md b/doc/source/reference/orbit.fieldtracker.md new file mode 100644 index 00000000..b6e3bcdc --- /dev/null +++ b/doc/source/reference/orbit.fieldtracker.md @@ -0,0 +1,7 @@ +# orbit.fieldtracker + +```{eval-rst} +.. automodapi:: orbit.fieldtracker + :no-heading: +``` + diff --git a/doc/source/reference/orbit.foils.md b/doc/source/reference/orbit.foils.md new file mode 100644 index 00000000..184c14f9 --- /dev/null +++ b/doc/source/reference/orbit.foils.md @@ -0,0 +1,7 @@ +# orbit.foils + +```{eval-rst} +.. automodapi:: orbit.foils + :no-heading: +``` + diff --git a/doc/source/reference/orbit.impedances.md b/doc/source/reference/orbit.impedances.md new file mode 100644 index 00000000..570b8b1f --- /dev/null +++ b/doc/source/reference/orbit.impedances.md @@ -0,0 +1,7 @@ +# orbit.impedances + +```{eval-rst} +.. automodapi:: orbit.impedances + :no-heading: +``` + diff --git a/doc/source/reference/orbit.injection.md b/doc/source/reference/orbit.injection.md new file mode 100644 index 00000000..408659bb --- /dev/null +++ b/doc/source/reference/orbit.injection.md @@ -0,0 +1,7 @@ +# orbit.injection + +```{eval-rst} +.. automodapi:: orbit.injection + :no-heading: +``` + diff --git a/doc/source/reference/orbit.kickernodes.md b/doc/source/reference/orbit.kickernodes.md new file mode 100644 index 00000000..a4754587 --- /dev/null +++ b/doc/source/reference/orbit.kickernodes.md @@ -0,0 +1,7 @@ +# orbit.kickernodes + +```{eval-rst} +.. automodapi:: orbit.kickernodes + :no-heading: +``` + diff --git a/doc/source/reference/orbit.lattice.md b/doc/source/reference/orbit.lattice.md new file mode 100644 index 00000000..9964c15a --- /dev/null +++ b/doc/source/reference/orbit.lattice.md @@ -0,0 +1,7 @@ +# orbit.lattice + +```{eval-rst} +.. automodapi:: orbit.lattice + :no-heading: +``` + diff --git a/doc/source/reference/orbit.matching.md b/doc/source/reference/orbit.matching.md new file mode 100644 index 00000000..f62e1f87 --- /dev/null +++ b/doc/source/reference/orbit.matching.md @@ -0,0 +1,7 @@ +# orbit.matching + +```{eval-rst} +.. automodapi:: orbit.matching + :no-heading: +``` + diff --git a/doc/source/reference/orbit.matrix_lattice.md b/doc/source/reference/orbit.matrix_lattice.md new file mode 100644 index 00000000..c8e70bcb --- /dev/null +++ b/doc/source/reference/orbit.matrix_lattice.md @@ -0,0 +1,7 @@ +# orbit.matrix_lattice + +```{eval-rst} +.. automodapi:: orbit.matrix_lattice + :no-heading: +``` + diff --git a/doc/source/reference/orbit.orbit_correction.md b/doc/source/reference/orbit.orbit_correction.md new file mode 100644 index 00000000..0ddec622 --- /dev/null +++ b/doc/source/reference/orbit.orbit_correction.md @@ -0,0 +1,7 @@ +# orbit.orbit_correction + +```{eval-rst} +.. automodapi:: orbit.orbit_correction + :no-heading: +``` + diff --git a/doc/source/reference/orbit.parsers.md b/doc/source/reference/orbit.parsers.md new file mode 100644 index 00000000..ce8b06c0 --- /dev/null +++ b/doc/source/reference/orbit.parsers.md @@ -0,0 +1,7 @@ +# orbit.parsers + +```{eval-rst} +.. automodapi:: orbit.parsers + :no-heading: +``` + diff --git a/doc/source/reference/orbit.py_linac.md b/doc/source/reference/orbit.py_linac.md new file mode 100644 index 00000000..5e00cfb2 --- /dev/null +++ b/doc/source/reference/orbit.py_linac.md @@ -0,0 +1,21 @@ +# orbit.py_linac + +```{eval-rst} +.. automodapi:: orbit.py_linac.errors + :no-heading: +.. automodapi:: orbit.py_linac.lattice + :no-heading: +.. automodapi:: orbit.py_linac.lattice_modifications + :no-heading: +.. automodapi:: orbit.py_linac.linac_parsers + :no-heading: +.. automodapi:: orbit.py_linac.materials + :no-heading: +.. automodapi:: orbit.py_linac.orbit_correction + :no-heading: +.. automodapi:: orbit.py_linac.overlapping_fields + :no-heading: +.. automodapi:: orbit.py_linac.rf_field_readers + :no-heading: +``` + diff --git a/doc/source/reference/orbit.rf_cavities.md b/doc/source/reference/orbit.rf_cavities.md new file mode 100644 index 00000000..64bd5f4e --- /dev/null +++ b/doc/source/reference/orbit.rf_cavities.md @@ -0,0 +1,7 @@ +# orbit.rf_cavities + +```{eval-rst} +.. automodapi:: orbit.rf_cavities + :no-heading: +``` + diff --git a/doc/source/reference/orbit.space_charge.md b/doc/source/reference/orbit.space_charge.md new file mode 100644 index 00000000..6d8c1049 --- /dev/null +++ b/doc/source/reference/orbit.space_charge.md @@ -0,0 +1,7 @@ +# orbit.space_charge + +```{eval-rst} +.. automodapi:: orbit.space_charge + :no-heading: +``` + diff --git a/doc/source/reference/orbit.teapot.md b/doc/source/reference/orbit.teapot.md new file mode 100644 index 00000000..ebe31a31 --- /dev/null +++ b/doc/source/reference/orbit.teapot.md @@ -0,0 +1,7 @@ +# orbit.teapot + +```{eval-rst} +.. automodapi:: orbit.teapot + :no-heading: +``` + diff --git a/doc/source/reference/orbit.teapot_base.md b/doc/source/reference/orbit.teapot_base.md new file mode 100644 index 00000000..b69030ad --- /dev/null +++ b/doc/source/reference/orbit.teapot_base.md @@ -0,0 +1,7 @@ +# orbit.teapot_base + +```{eval-rst} +.. automodapi:: orbit.teapot_base + :no-heading: +``` + diff --git a/doc/source/reference/orbit.time_dep.md b/doc/source/reference/orbit.time_dep.md new file mode 100644 index 00000000..da5972eb --- /dev/null +++ b/doc/source/reference/orbit.time_dep.md @@ -0,0 +1,7 @@ +# orbit.time_dep + +```{eval-rst} +.. automodapi:: orbit.time_dep + :no-heading: +``` + diff --git a/doc/source/reference/orbit.utils.md b/doc/source/reference/orbit.utils.md new file mode 100644 index 00000000..feaf05a0 --- /dev/null +++ b/doc/source/reference/orbit.utils.md @@ -0,0 +1,7 @@ +# orbit.utils + +```{eval-rst} +.. automodapi:: orbit.utils + :no-heading: +``` + diff --git a/doc/source/reference/py.md b/doc/source/reference/py.md new file mode 100644 index 00000000..8e4c7fe5 --- /dev/null +++ b/doc/source/reference/py.md @@ -0,0 +1,32 @@ +# Python API Reference + +```{eval-rst} +.. toctree:: + :maxdepth: 2 + + orbit.aperture + orbit.bumps + orbit.bunch_generators + orbit.bunch_utils + orbit.collimation + orbit.core + orbit.diagnostics + orbit.errors + orbit.fieldtracker + orbit.foils + orbit.impedances + orbit.injection + orbit.kickernodes + orbit.lattice + orbit.matching + orbit.matrix_lattice + orbit.orbit_correction + orbit.parsers + orbit.py_linac + orbit.rf_cavities + orbit.space_charge + orbit.teapot + orbit.teapot_base + orbit.time_dep + orbit.utils +``` diff --git a/py/orbit/aperture/__init__.py b/py/orbit/aperture/__init__.py index cf0f61fa..4289ff27 100644 --- a/py/orbit/aperture/__init__.py +++ b/py/orbit/aperture/__init__.py @@ -8,8 +8,10 @@ from .ApertureLatticeModifications import addTeapotApertureNode from .ApertureLatticeRangeModifications import addCircleApertureSet, addEllipseApertureSet, addRectangleApertureSet +from orbit.core.aperture import Aperture + # from TeapotApertureShapeNode import CircleApertureNode __all__ = [] __all__.append("Aperture") -__all__.append("checkBunch") +# __all__.append("checkBunch") # non-static method of the Aperture Class diff --git a/py/orbit/bunch_utils/__init__.py b/py/orbit/bunch_utils/__init__.py index 88b43d5f..42ea363e 100644 --- a/py/orbit/bunch_utils/__init__.py +++ b/py/orbit/bunch_utils/__init__.py @@ -5,14 +5,25 @@ ## - ParticleIdNumber - Class for adding unique id numbers to particle in a bunch # -from orbit.bunch_utils.particleidnumber import ParticleIdNumber +from .particleidnumber import ParticleIdNumber # This guards against missing numpy. # Should be imporved with some meaningful (and MPI friendly?) warning printed out. try: - from orbit.bunch_utils.serialize import collect_bunch, save_bunch, load_bunch -except: + from .serialize import collect_bunch, save_bunch, load_bunch + from .serialize import BunchDict, SyncPartDict + from .serialize import FileHandler, NumPyHandler +except ImportError: pass __all__ = [] -__all__.append("addParticleIdNumbers") +# __all__.append("addParticleIdNumbers") # doesn't exist +__all__.append("ParticleIdNumber") +__all__.append("collect_bunch") +__all__.append("save_bunch") +__all__.append("load_bunch") +__all__.append("BunchDict") +__all__.append("SyncPartDict") +__all__.append("FileHandler") +__all__.append("NumPyHandler") + diff --git a/py/orbit/bunch_utils/serialize.py b/py/orbit/bunch_utils/serialize.py index 1a8a0e3b..0cbea0f2 100644 --- a/py/orbit/bunch_utils/serialize.py +++ b/py/orbit/bunch_utils/serialize.py @@ -10,6 +10,23 @@ class SyncPartDict(TypedDict): + """A dictionary containing the attributes of the synchronous particle. + + Attributes + ---------- + coords : NDArray[np.float64] + The coordinates (x, px, y, py, z, dE) of the synchronous particle. + kin_energy : float + The kinetic energy of the synchronous particle. + momentum : float + The momentum of the synchronous particle. + beta : float + The beta of the synchronous particle. + gamma : float + The gamma of the synchronous particle. + time : float + The time of the synchronous particle. + """ coords: NDArray[np.float64] kin_energy: np.float64 momentum: np.float64 @@ -19,13 +36,32 @@ class SyncPartDict(TypedDict): class BunchDict(TypedDict): + """A dictionary containing the attributes of a PyOrbit::Bunch object. + + Attributes + ---------- + coords : NDArray[np.float64] + The coordinates (x, px, y, py, z, dE) of the particles in the bunch. + sync_part : SyncPartDict + The attributes of the synchronous particle. + attributes : dict[str, np.float64 | np.int32] + Other attributes of the bunch. + """ coords: NDArray[np.float64] sync_part: SyncPartDict attributes: dict[str, np.float64 | np.int32] class FileHandler(Protocol): - """Protocol for file handlers to read/write bunch data.""" + """Protocol for file handlers to read/write bunch data. + + Methods + _______ + read() -> BunchDict: + Reads the bunch data from the specified directory and returns it as a dictionary. + write(bunch: BunchDict) -> None: + Writes the bunch data to the specified directory. + """ def __init__(self, *args: Any, **kwargs: Any) -> None: ... @@ -36,15 +72,28 @@ def write(self, bunch: BunchDict) -> None: ... class NumPyHandler: """Handler implementing the FileHandler protocol for NumPy binary files. - This handler will create two files in the directory passed to the constructor: - - coords.npy: A memory-mapped NumPy array containing the bunch coordinates. - - attributes.npz: A NumPy archive containing data related to the synchronous particle and other bunch attributes. + + Attributes + ---------- + _coords_fname : str + The name of the file containing the bunch coordinates (default: "coords.npy"). + _attributes_fname : str + The name of the file containing data related to the synchronous particle and other bunch attributes (default: "attributes.npz"). """ _coords_fname = "coords.npy" _attributes_fname = "attributes.npz" - def __init__(self, dir_name: str | pathlib.Path): + def __init__(self, dir_name: str | os.PathLike): + """Constructor for the NumPyHandler class. + + Parameters + ---------- + dir_name + The directory in which to store the bunch data. + + """ + if isinstance(dir_name, str): dir_name = pathlib.Path(dir_name) self._dir_name = dir_name @@ -77,41 +126,29 @@ def write(self, bunch: BunchDict) -> None: def collect_bunch( - bunch: Bunch, output_dir: str | pathlib.Path = "/tmp", return_memmap: bool = True + bunch: Bunch, output_dir: str | os.PathLike = "/tmp", return_memmap: bool = True ) -> BunchDict | None: """Collects attributes from a PyOrbit Bunch across all MPI ranks and returns it as a dictionary. + Parameters ---------- bunch : Bunch The PyOrbit::Bunch object from which to collect attributes. - output_dir : str | pathlib.Path, optional - The director to use for temporary storage of the bunch coordinates on each MPI rank. - If None, the bunch will be stored in "/tmp". - Note: take care that the temporary files are created in a directory where all MPI ranks have write access. + output_dir : str | os.PathLike, optional + The director to use for temporary storage of the bunch coordinates on each MPI rank (default: "/tmp"). return_memmap : bool, optional Return the bunch coordinates as a memory-mapped NumPy array, otherwise the - entire array is copied into RAM and returned as normal NDArray. Default is True. + entire array is copied into RAM and returned as normal NDArray (default: True). + + Note + ---- + Take care that the temporary files are created in a directory which all MPI ranks have write access. + Returns ------- BunchDict | None A dictionary containing the collected bunch attributes. Returns None if not on the root MPI rank or if the global bunch size is 0. - BunchDict structure: - { - "coords": NDArray[np.float64] of shape (N, 6) where N is the total number of macroparticles, - and the 6 columns correspond to [x, xp, y, yp, z, dE] in units of [m, rad, m, rad, m, GeV], respectively. - "sync_part": { - "coords": NDArray[np.float64] of shape (3,), - "kin_energy": np.float64, - "momentum": np.float64, - "beta": np.float64, - "gamma": np.float64, - "time": np.float64 - }, - "attributes": { - : , - ... - } - } + Raises ------ FileNotFoundError @@ -208,21 +245,24 @@ def collect_bunch( def save_bunch( bunch: Bunch | BunchDict, - output_dir: str | pathlib.Path = "bunch_data/", + output_dir: str | os.PathLike = "bunch_data/", Handler: type[FileHandler] = NumPyHandler, ) -> None: """Saves the collected bunch attributes to a specified directory. + Parameters ---------- bunch_dict : Bunch | BunchDict The PyOrbit::Bunch object or the dictionary containing the collected bunch attributes. output_dir : str, optional - The directory where the bunch data files will be saved. Default is "bunch_data/". + The directory where the bunch data files will be saved (default: "bunch_data/"). Handler : FileHandler, optional - The file handler class to use for writing the bunch data. Default is NumPyHandler. + The file handler class to use for writing the bunch data (default: NumPyHandler). + Returns ------- None + Raises ------ ValueError @@ -252,20 +292,23 @@ def save_bunch( def load_bunch( - input_dir: str | pathlib.Path, Handler: type[FileHandler] = NumPyHandler + input_dir: str | os.PathLike, Handler: type[FileHandler] = NumPyHandler ) -> tuple[Bunch, BunchDict]: """Loads the bunch attributes from a specified directory containing NumPy binary files. + Parameters ---------- - input_dir : str | pathlib.Path + input_dir : str | os.PathLike The directory from which to load the bunch data files. Handler : FileHandler, optional - The file handler class to use for reading the bunch data. Default is NumPyHandler. + The file handler class to use for reading the bunch data (default: NumPyHandler). See `orbit.bunch_utils.file_handler` for available handlers. + Returns ------- BunchDict A dictionary containing the loaded bunch attributes. + Raises ------ FileNotFoundError diff --git a/py/orbit/errors/__init__.py b/py/orbit/errors/__init__.py index 92110bcc..9fcf4d63 100644 --- a/py/orbit/errors/__init__.py +++ b/py/orbit/errors/__init__.py @@ -38,7 +38,7 @@ from orbit.errors.ErrorLatticeModifications import addErrorNodeAsChild_F __all__ = [] -__all__.append("") +# __all__.append("") __all__.append("addErrorNode") __all__.append("addErrorNodeAsChild") __all__.append("addErrorNodeAsChild_I") diff --git a/py/orbit/fieldtracker/__init__.py b/py/orbit/fieldtracker/__init__.py index f2b2d390..3a42371f 100644 --- a/py/orbit/fieldtracker/__init__.py +++ b/py/orbit/fieldtracker/__init__.py @@ -3,12 +3,11 @@ ## ## Classes: - -from . import FieldTracker +from orbit.core.fieldtracker import FieldTracker __all__ = [] __all__.append("FieldTracker") -__all__.append("trackBunch") -__all__.append("FieldParser3D") -__all__.append("BGrid3D") -__all__.append("setPathVariable") +# __all__.append("trackBunch") # non-static method of the FieldTracker Class +# __all__.append("FieldParser3D") # doesn't exist +# __all__.append("BGrid3D") # non-static method of the FieldTracker Class +# __all__.append("setPathVariable") # non-static method of the FieldTracker Class diff --git a/py/orbit/orbit_correction/__init__.py b/py/orbit/orbit_correction/__init__.py index 80f56875..22cec1f6 100644 --- a/py/orbit/orbit_correction/__init__.py +++ b/py/orbit/orbit_correction/__init__.py @@ -13,6 +13,6 @@ __all__ = [] -__all__.append("") +# __all__.append("") __all__.append("orbit") __all__.append("correction") diff --git a/py/orbit/py_linac/lattice/__init__.py b/py/orbit/py_linac/lattice/__init__.py index cab3b3a7..85a9df80 100644 --- a/py/orbit/py_linac/lattice/__init__.py +++ b/py/orbit/py_linac/lattice/__init__.py @@ -68,9 +68,9 @@ __all__.append("RF_Cavity") __all__.append("Sequence") -__all__.append("LinacStructureTree") -__all__.append("LinacStructureSeq") -__all__.append("LinacStuctureNode") +# __all__.append("LinacStructureTree") # doesn't exist +# __all__.append("LinacStructureSeq") +# __all__.append("LinacStuctureNode") __all__.append("BaseRF_Gap") __all__.append("AxisFieldRF_Gap") diff --git a/py/orbit/py_linac/overlapping_fields/overlapping_quad_fields_lib.py b/py/orbit/py_linac/overlapping_fields/overlapping_quad_fields_lib.py index 875e5183..747288f6 100644 --- a/py/orbit/py_linac/overlapping_fields/overlapping_quad_fields_lib.py +++ b/py/orbit/py_linac/overlapping_fields/overlapping_quad_fields_lib.py @@ -88,10 +88,12 @@ def getFuncDerivative(self, z): class EngeFunction(AbstractQuadFieldSourceFunction): """ - The Enge function with parameters from Berz's paper - M.Berz, B. Erdelyn, K.Makino - Fringe Field Effects in Small Rings of Large Acceptance - Phys. Rev STAB, V3, 124001(2000) + The Enge function with parameters from Berz's paper [1]_. + + References + ---------- + .. [1] M. Berz, B. Erdélyi, and K. Makino, “Fringe field effects in small rings of large acceptance,” Phys. Rev. ST Accel. Beams, vol. 3, no. 12, p. 124001, Dec. 2000, doi: 10.1103/PhysRevSTAB.3.124001. + """ def __init__(self, length_param, acceptance_diameter_param, cutoff_level=0.01): diff --git a/py/orbit/py_linac/rf_field_readers/SuperFish_3D_RF_FieldReader.py b/py/orbit/py_linac/rf_field_readers/SuperFish_3D_RF_FieldReader.py index 555bfd66..2866d644 100644 --- a/py/orbit/py_linac/rf_field_readers/SuperFish_3D_RF_FieldReader.py +++ b/py/orbit/py_linac/rf_field_readers/SuperFish_3D_RF_FieldReader.py @@ -4,7 +4,7 @@ from orbit.core import orbit_mpi from orbit.core.orbit_mpi import mpi_comm, mpi_datatype, mpi_op -from spacecharge import Grid2D +from orbit.core.spacecharge import Grid2D from orbit.core.orbit_utils import Function, SplineCH, GaussLegendreIntegrator, Polynomial from orbit.utils.fitting import PolynomialFit diff --git a/py/orbit/py_linac/rf_field_readers/__init__.py b/py/orbit/py_linac/rf_field_readers/__init__.py index 47470b31..9921bf1d 100644 --- a/py/orbit/py_linac/rf_field_readers/__init__.py +++ b/py/orbit/py_linac/rf_field_readers/__init__.py @@ -10,12 +10,12 @@ ## 3D filed. It generates the field on the axis for RF_AxisFieldAnalysis # ------------------------------------------------------------------------- -from RF_AxisFieldAnalysis import RF_AxisFieldAnalysis -from SuperFish_3D_RF_FieldReader import SuperFish_3D_RF_FieldReader +from .RF_AxisFieldAnalysis import RF_AxisFieldAnalysis +from .SuperFish_3D_RF_FieldReader import SuperFish_3D_RF_FieldReader __all__ = [] # Classes __all__.append("RF_AxisFieldAnalysis") -__all__.append("SuperFish_3D_RF_FiledReader") +__all__.append("SuperFish_3D_RF_FieldReader") diff --git a/py/orbit/rf_cavities/__init__.py b/py/orbit/rf_cavities/__init__.py index 3bac2839..3c1b3120 100644 --- a/py/orbit/rf_cavities/__init__.py +++ b/py/orbit/rf_cavities/__init__.py @@ -10,6 +10,8 @@ from orbit.rf_cavities.RFNode import Frequency_RFNode from orbit.rf_cavities.RFNode import Harmonic_RFNode from orbit.rf_cavities.RFNode import Barrier_RFNode +from orbit.rf_cavities.RFNode import BRhoDep_Harmonic_RFNode +from orbit.rf_cavities.RFNode import SyncPhaseDep_Harmonic_RFNode from orbit.rf_cavities.RFLatticeModifications import addRFNode __all__ = [] diff --git a/py/orbit/teapot/__init__.py b/py/orbit/teapot/__init__.py index 945450d4..ac2ab3d2 100644 --- a/py/orbit/teapot/__init__.py +++ b/py/orbit/teapot/__init__.py @@ -7,6 +7,7 @@ from .teapot import TEAPOT_Ring from .teapot import BaseTEAPOT from .teapot import BendTEAPOT +from .teapot import BunchWrapTEAPOT from .teapot import DriftTEAPOT from .teapot import FringeFieldTEAPOT from .teapot import KickTEAPOT diff --git a/py/orbit/time_dep/__init__.py b/py/orbit/time_dep/__init__.py index 16afb666..dbf50932 100644 --- a/py/orbit/time_dep/__init__.py +++ b/py/orbit/time_dep/__init__.py @@ -3,5 +3,8 @@ ## ## Classes: ## + +from .time_dep import TIME_DEP_Lattice + __all__ = [] __all__.append("TIME_DEP_Lattice") diff --git a/py/orbit/time_dep/time_dep.py b/py/orbit/time_dep/time_dep.py index 2d8483d3..2bf34260 100644 --- a/py/orbit/time_dep/time_dep.py +++ b/py/orbit/time_dep/time_dep.py @@ -15,8 +15,8 @@ class TIME_DEP_Lattice(TEAPOT_Ring): """ The subclass of the TEAPOT_Lattice. - TIME_DEP_Lattice has the ability to set time-dependent - parameters to the Lattice. + + TIME_DEP_Lattice has the ability to set time-dependent parameters to the Lattice. Multi-turn track also available. """ diff --git a/py/orbit/utils/multiDimArray.py b/py/orbit/utils/multiDimArray.py index 6f73c0c4..9dc80eaf 100644 --- a/py/orbit/utils/multiDimArray.py +++ b/py/orbit/utils/multiDimArray.py @@ -1,11 +1,25 @@ -def multiDimDoubleArray(*dims): +def multiDimDoubleArray(*dims: int) -> list[float]: """ - Method. Creates multi-dimensional arrays with doubles, such as a[i][k][j]. - Some examples of the use of this function: - a = multiDimArray(5,10,2) - a = multiDimArray(*[5,10,2]) - a[1][2][1] = 0. - By default all elements are initialized to 0. + Creates multi-dimensional arrays with doubles, such as a[i][k][j]. + + Parameters + ---------- + dims : int + + Returns + ------- + list[float] + + Note + ---- + All elements are initialized to 0. + + Examples + -------- + >>> a = multiDimArray(5,10,2) + >>> a = multiDimArray(*[5,10,2]) # equivalent + >>> a[1][2][1] + 0.0 """ res = [] if len(dims) == 1: @@ -18,14 +32,24 @@ def multiDimDoubleArray(*dims): return res -def multiDimIntArray(*dims): +def multiDimIntArray(*dims: int) -> list[int]: """ - Method. Creates multi-dimensional arrays with integers, such as a[i][k][j]. - Some examples of the use of this function: - a = multiDimArray(5,10,2) - a = multiDimArray(*[5,10,2]) - a[1][2][1] = 0 - By default all elements are initialized to 0. + Creates multi-dimensional arrays with integers, such as a[i][k][j]. + + Parameters + ---------- + dims : int + + Returns + ------- + list[int] + + Examples + -------- + >>> a = multiDimArray(5,10,2) + >>> a = multiDimArray(*[5,10,2]) # equivalent + >>> a[1][2][1] + 0 """ res = [] if len(dims) == 1: diff --git a/src/mpi/orbit_mpi.cc b/src/mpi/orbit_mpi.cc index 8a36065e..2dc97e71 100644 --- a/src/mpi/orbit_mpi.cc +++ b/src/mpi/orbit_mpi.cc @@ -5,6 +5,7 @@ #include #include +#if !defined(DOXYGEN_SHOULD_SKIP_THIS) /** A C wrapper around MPI_Init. */ int ORBIT_MPI_Init(){ int res = 0; @@ -620,3 +621,4 @@ int ORBIT_MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count){ #endif return res; } +#endif // !defined(DOXYGEN_SHOULD_SKIP_THIS) diff --git a/src/orbit/BunchDiagnostics/BunchTwissAnalysis.cc b/src/orbit/BunchDiagnostics/BunchTwissAnalysis.cc index 2063b6ae..0a361326 100644 --- a/src/orbit/BunchDiagnostics/BunchTwissAnalysis.cc +++ b/src/orbit/BunchDiagnostics/BunchTwissAnalysis.cc @@ -337,10 +337,6 @@ void BunchTwissAnalysis::computeBunchMoments(Bunch* bunch, int order, int disper } - - - -/** Returns the centered correlation <(x-)*(y-)> = - * */ double BunchTwissAnalysis::getCorrelation(int ic, int jc){ if(ic < 0 || ic > 5 || jc < 0 || jc >5) return 0.; return (corr_arr[ic+6*jc] - avg_arr[ic]* avg_arr[jc]); diff --git a/src/orbit/BunchDiagnostics/BunchTwissAnalysis.hh b/src/orbit/BunchDiagnostics/BunchTwissAnalysis.hh index df6e2b4b..deaa20ee 100644 --- a/src/orbit/BunchDiagnostics/BunchTwissAnalysis.hh +++ b/src/orbit/BunchDiagnostics/BunchTwissAnalysis.hh @@ -26,7 +26,16 @@ class BunchTwissAnalysis: public OrbitUtils::CppPyWrapper /** Performs the Twiss analysis of the bunch */ void analyzeBunch(Bunch* bunch); - /** Returns the centered correlation <(x-)*(y-)> = - * */ + /** + * @brief Returns the centered correlation between two components. + * + * \f$\langle (x-\langle x\rangle)(y-\langle y\rangle)\rangle + * = \langle x y\rangle - \langle x\rangle\langle y\rangle\f$ + * + * @param ic Index of the first component (x). + * @param jc Index of the second component (y). + * @return The centered correlation value as a double. + */ double getCorrelation(int ic, int jc); /** Returns the average value for coordinate with index ic */ diff --git a/src/orbit/MaterialInteractions/numrecipes.cc b/src/orbit/MaterialInteractions/numrecipes.cc index eadd37ee..a38dd327 100644 --- a/src/orbit/MaterialInteractions/numrecipes.cc +++ b/src/orbit/MaterialInteractions/numrecipes.cc @@ -29,9 +29,8 @@ namespace OrbitUtils{ return sin(x)*exp(-fac1*pow(2*p*sin(x/2), 2))/pow(sin(x/2),4); } - int zbrak(float (*fx)(float, float, float, float), float x1, float x2, - int n, float xb1[], float xb2[], int &nb, float param1, + int n, float *xb1, float *xb2, int &nb, float param1, float param2, float param3) { int nbb, i; @@ -54,7 +53,9 @@ namespace OrbitUtils{ } #undef JMAX +#if !defined(DOXYGEN_SHOULD_SKIP_THIS) #define JMAX 40 +#endif float rtbis(float (*func)(float, float, float, float), float x1, float x2, float xacc, float param1, float param2, float param3) @@ -134,8 +135,10 @@ namespace OrbitUtils{ #undef EPS #undef JMAX +#if !defined(DOXYGEN_SHOULD_SKIP_THIS) #define EPS 1.0e-6 #define JMAX 20 +#endif float qsimp(float (*func)(float, float, float), float a, float b, float p, float fac1) @@ -164,7 +167,9 @@ namespace OrbitUtils{ /* note #undef's at end of file */ +#if !defined(DOXYGEN_SHOULD_SKIP_THIS) #define FUNC(x, p, fac1) ((*rfunc)(x, p, fac1)) +#endif float trapzd(float (*rfunc)(float, float, float), float a, float b, int n, float p, float fac1) diff --git a/src/orbit/MaterialInteractions/numrecipes.hh b/src/orbit/MaterialInteractions/numrecipes.hh index 25f884e0..8059db80 100644 --- a/src/orbit/MaterialInteractions/numrecipes.hh +++ b/src/orbit/MaterialInteractions/numrecipes.hh @@ -16,7 +16,54 @@ namespace OrbitUtils{ float fstep(float s, float r_o, float pr_o, float theta); float rfunc(float x, float p, float fac1); - int zbrak(float (*fx)(float, float, float, float), float x1, float x2, int n, float xb1[], float xb2[], int &nb, float param1, + + /** + * @brief Bracket subintervals where a function changes sign. + * + * Scans the interval [x1, x2] by dividing it into @p n equal subintervals + * and records subinterval endpoints where the function @p fx changes sign + * (i.e., a root is bracketed). + * + * The function @p fx must have the signature: + * float fx(float x, float p1, float p2, float p3); + * It is evaluated at the grid points x1, x1 + dx, x1 + 2*dx, ..., x2, + * where dx = (x2 - x1) / n. + * + * When an interval [xb1[k], xb2[k]] is found such that fx(xb1[k]) and + * fx(xb2[k]) have opposite signs (fc*fp <= 0), the endpoints are stored + * in the output arrays xb1 and xb2 at the same index k (1-based indexing + * is used internally; the caller should account for this). + * + * @param fx Pointer to the function to examine. Signature: + * float fx(float x, float param1, float param2, float param3). + * @param x1 Left end of the search interval. + * @param x2 Right end of the search interval. + * @param n Number of equal subintervals to split [x1, x2] into. + * @param xb1 Output array to receive left endpoints of bracketing subintervals. + * Must be large enough to hold up to the capacity indicated by the + * integer referenced by @p nb (see @p nb description). + * @param xb2 Output array to receive right endpoints of bracketing subintervals. + * Same required capacity as @p xb1. + * @param nb Reference to an integer indicating the maximum number of bracket + * intervals the caller can accept on input; on return this integer + * is set to the actual number of brackets found (nb <= original nb). + * @param param1 First user parameter passed through to @p fx. + * @param param2 Second user parameter passed through to @p fx. + * @param param3 Third user parameter passed through to @p fx. + * + * @return Returns 0 on completion. If the number of found brackets reaches the + * input capacity (nb), the function returns immediately with xb1/xb2 + * filled up to that capacity and nb set equal to that capacity. + * + * @note + * - The function uses sequential evaluations of @p fx and treats a zero or a + * sign change (fc*fp <= 0.0) as a bracket. Adjacent subintervals that both + * satisfy this condition will each be reported. + * - The implementation uses 1-based indexing internally when filling xb1/xb2. + * The caller should allocate arrays accordingly and interpret the filled + * entries consistent with the calling convention used in the surrounding code. + */ + int zbrak(float (*fx)(float, float, float, float), float x1, float x2, int n, float *xb1, float *xb2, int &nb, float param1, float param2, float param3); float rtbis(float (*func)(float, float, float, float), float x1, float x2, float xacc, float param1, float param2, float param3); float bessj0(float x); diff --git a/src/spacecharge/UniformEllipsoidFieldCalculator.cc b/src/spacecharge/UniformEllipsoidFieldCalculator.cc index 42fc6970..811eb52d 100644 --- a/src/spacecharge/UniformEllipsoidFieldCalculator.cc +++ b/src/spacecharge/UniformEllipsoidFieldCalculator.cc @@ -15,6 +15,7 @@ using namespace OrbitUtils; //macros for max and min +#if !defined(DOXYGEN_SHOULD_SKIP_THIS) #ifndef max #define max( a, b ) ( ((a) > (b)) ? (a) : (b) ) #endif @@ -22,6 +23,7 @@ using namespace OrbitUtils; #ifndef min #define min( a, b ) ( ((a) < (b)) ? (a) : (b) ) #endif +#endif /** Constructor. There is no parameters */ UniformEllipsoidFieldCalculator::UniformEllipsoidFieldCalculator(): CppPyWrapper(NULL) diff --git a/src/utils/Random.cc b/src/utils/Random.cc index c19bafca..4725232a 100644 --- a/src/utils/Random.cc +++ b/src/utils/Random.cc @@ -21,6 +21,7 @@ using namespace OrbitUtils; // By default the random generator is initialized using a random seed (current time) +#if !defined(DOXYGEN_SHOULD_SKIP_THIS) std::mt19937 mt(time(0)); void Random::seed(int seed){ @@ -31,3 +32,4 @@ void Random::seed(int seed){ double Random::ran1(){ return ((double) mt() / (mt.max())); } +#endif