diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 8480df72..add34a8d 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -1,62 +1,49 @@
-# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Docs
on:
- # Runs on pushes targeting the default branch
push:
- branches:
- - '*'
-
- # Allows you to run this workflow manually from the Actions tab
+ branches: [main, dev]
+ pull_request:
+ branches: [main, dev]
workflow_dispatch:
-# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
- contents: write
- pages: write
-
-# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
-# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
-concurrency:
- group: "pages"
- cancel-in-progress: false
+ contents: read
jobs:
- # Build job
- build:
+ build-docs:
runs-on: ubuntu-latest
steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: Setup Python
- uses: actions/setup-python@v5
+ - uses: actions/checkout@v7
with:
- python-version: 3.12
+ fetch-depth: 0
+ - uses: actions/setup-python@v7
+ with:
+ python-version: "3.12"
cache: pip
-
- - name: Requirements
- run: |
- sudo apt-get update
- sudo apt-get install -y python3-sphinx
- python -m pip install --upgrade pip
- python -m pip install -e ".[docs]"
-
- - name: Build
- run: |
- cd docs
- make clean
- rm -f source/code/*
- bash autodoc.sh
- make html
- cd build/html
- touch .nojekyll
- shell: bash
-
- # Deployment job
- - name: deploy
- uses: JamesIves/github-pages-deploy-action@releases/v4
+ - name: Install the package and documentation dependencies
+ run: python -m pip install -e ".[docs]"
+ - name: Build the documentation
+ run: python -m sphinx -W --keep-going -b html docs/source docs/build/html
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v5
with:
- branch: gh-pages # The branch the action should deploy to.
- folder: docs/build/html # The folder the action should deploy.
- if: github.ref_name == 'main' # Only deploy on pushes to the main branch
+ path: docs/build/html
+
+ deploy-docs:
+ needs: build-docs
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ runs-on: ubuntu-latest
+ concurrency:
+ group: pages
+ cancel-in-progress: false
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v5
diff --git a/PQAnalysis/io/restart_file/api.py b/PQAnalysis/io/restart_file/api.py
index 7d49d9c1..6d8b1e31 100644
--- a/PQAnalysis/io/restart_file/api.py
+++ b/PQAnalysis/io/restart_file/api.py
@@ -62,7 +62,7 @@ def write_restart_file(
mode: FileWritingMode | str = 'w'
) -> None:
"""
- API function for reading a restart file.
+ Write an atomic system to a restart file.
Parameters
----------
diff --git a/README.md b/README.md
index bd4da712..26c851a1 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,14 @@
[](https://codecov.io/gh/MolarVerse/PQAnalysis)
[](https://opensource.org/licenses/MIT)
-The main purpose of this package is to provide useful tools for the analysis of the Molecular Dynamics software package [PQ](https://github.com/MolarVerse/PQ). Furthermore, the intent of this package is to enable straightforward implementations of newly developed analysis tools on top of the provided API.
+PQAnalysis reads structures, trajectories, velocities and Hessians produced by
+[PQ](https://github.com/MolarVerse/PQ). Its command-line and Python interfaces
+share parsers, numerical kernels and schema-defined outputs for RDF, MSD, VACF,
+vibrational, spectral and momentum analyses.
-The future development of this package focuses on two main goals. On the one hand the enhancement of the provided analysis tools and extending its API to be compatible with many other different Molecular Dynamics engines. As this project is only a *hobby* project of the maintainers, any contributions considering enhancement or bug fixes are highly welcomed.
+Development focuses on validated analysis methods and support for additional
+molecular-dynamics engines. The maintainers develop PQAnalysis in their free
+time; focused analysis contributions and bug fixes are welcome.
## Installation
@@ -19,22 +24,29 @@ Install with pip:
## Development
-Clone the PQAnalysis GitHub repository and navigate into the directory:
+Clone the repository and install the development, test and documentation
+dependencies in an isolated environment:
git clone https://github.com/MolarVerse/PQAnalysis.git
cd PQAnalysis
+ python -m venv .venv
+ source .venv/bin/activate
+ python -m pip install -e ".[dev,test,docs]"
-Install in editable mode with test dependencies:
+Run the test suite with both debug and release runtime type checking:
- pip install -e ".[test]"
+ bash pytest.sh
-Run the test suite:
+The [developer documentation](https://molarverse.github.io/PQAnalysis/developerGuide/developerGuide.html)
+covers package architecture, adding an analysis, scientific validation and the
+tag-driven release process. The
+[function index](https://molarverse.github.io/PQAnalysis/reference/functions.html)
+lists the supported Python entry points directly.
- python -m pytest
-
-Use squash merges for pull requests. The pull request title becomes the commit
-message on the target branch, so PR titles must follow
-[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/):
+Pull request titles must follow
+[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/); CI
+validates them. Keep individual commits scoped because multi-commit pull
+requests may retain their commit history:
feat: add a new analysis command
fix(io): handle missing trajectory data
diff --git a/docs/source/_plots/_style.py b/docs/source/_plots/_style.py
new file mode 100644
index 00000000..6c2ebefe
--- /dev/null
+++ b/docs/source/_plots/_style.py
@@ -0,0 +1,48 @@
+"""Shared Matplotlib style for the scientific documentation figures."""
+
+from pathlib import Path
+
+import matplotlib as mpl
+
+
+PROJECT_ROOT = Path(__file__).resolve().parents[3]
+
+COLORS = {
+ "blue": "#176c8c",
+ "green": "#008f72",
+ "orange": "#c7521c",
+ "magenta": "#a84d84",
+ "ink": "#202428",
+ "muted": "#66717a",
+ "grid": "#d7dde1",
+ "shell": "#dcecf2",
+}
+
+
+def apply_style(figsize: tuple[float, float]) -> None:
+ """Apply a restrained, colorblind-safe style to one figure."""
+
+ mpl.rcParams.update({
+ "figure.figsize": figsize,
+ "figure.dpi": 120,
+ "figure.facecolor": "white",
+ "savefig.facecolor": "white",
+ "savefig.bbox": "tight",
+ "font.size": 9.5,
+ "axes.labelsize": 10,
+ "axes.labelcolor": COLORS["ink"],
+ "axes.edgecolor": COLORS["muted"],
+ "axes.linewidth": 0.8,
+ "axes.spines.top": False,
+ "axes.spines.right": False,
+ "axes.axisbelow": True,
+ "axes.grid": True,
+ "grid.color": COLORS["grid"],
+ "grid.linewidth": 0.7,
+ "grid.alpha": 0.8,
+ "xtick.color": COLORS["ink"],
+ "ytick.color": COLORS["ink"],
+ "legend.frameon": False,
+ "legend.fontsize": 8.5,
+ "lines.linewidth": 1.8,
+ })
diff --git a/docs/source/_plots/msd.py b/docs/source/_plots/msd.py
new file mode 100644
index 00000000..a03a7343
--- /dev/null
+++ b/docs/source/_plots/msd.py
@@ -0,0 +1,50 @@
+"""MSD components and diffusion-fit interval from the validation fixture."""
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+from _style import COLORS, PROJECT_ROOT, apply_style
+
+
+apply_style((7.2, 4.3))
+
+data = np.loadtxt(PROJECT_ROOT / "tests/data/msd/msd_ref_O.dat")
+time = data[:, 0] * 0.5
+components = data[:, 1:4]
+total = np.sum(components, axis=1)
+
+fit_start = len(time) - 20
+fit_coefficients = np.polyfit(time[fit_start:], total[fit_start:], 1)
+fit = np.polyval(fit_coefficients, time[fit_start:])
+
+figure, axis = plt.subplots()
+for values, label, color in zip(
+ components.T,
+ (r"$\mathrm{MSD}_x$", r"$\mathrm{MSD}_y$", r"$\mathrm{MSD}_z$"),
+ (COLORS["blue"], COLORS["green"], COLORS["magenta"]),
+):
+ axis.plot(time, values, color=color, linewidth=1.35, label=label)
+
+axis.plot(time, total, color=COLORS["ink"], linewidth=2.2, label="total")
+axis.axvspan(
+ time[fit_start],
+ time[-1],
+ color=COLORS["shell"],
+ label="fit interval",
+)
+axis.plot(
+ time[fit_start:],
+ fit,
+ color=COLORS["orange"],
+ linestyle="--",
+ linewidth=1.7,
+ label="linear fit",
+)
+axis.set_xlabel(r"Lag time $t$ / ps")
+axis.set_ylabel(r"Mean square displacement / $\mathrm{\AA}^2$")
+axis.set_xlim(time[0], time[-1])
+axis.set_ylim(bottom=0.0)
+axis.legend(ncol=3, loc="upper left")
+
+figure.tight_layout()
+plt.show()
diff --git a/docs/source/_plots/rdf.py b/docs/source/_plots/rdf.py
new file mode 100644
index 00000000..f9ce3dee
--- /dev/null
+++ b/docs/source/_plots/rdf.py
@@ -0,0 +1,73 @@
+"""Analytic RDF profile used to explain structural features."""
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+from _style import COLORS, apply_style
+
+
+apply_style((7.2, 5.0))
+
+r = np.linspace(0.02, 8.0, 800)
+excluded_volume = 1.0 - np.exp(-(r / 1.65)**8)
+structure = (
+ 1.0
+ + 2.2 * np.exp(-0.5 * ((r - 2.80) / 0.22)**2)
+ - 0.55 * np.exp(-0.5 * ((r - 3.55) / 0.30)**2)
+ + 0.65 * np.exp(-0.5 * ((r - 4.65) / 0.38)**2)
+ - 0.18 * np.exp(-0.5 * ((r - 5.55) / 0.45)**2)
+)
+g_r = np.clip(excluded_volume * structure, 0.0, None)
+
+number_density = 0.0334
+coordination_integrand = 4.0 * np.pi * number_density * r**2 * g_r
+coordination = np.concatenate((
+ [0.0],
+ np.cumsum(
+ 0.5
+ * (coordination_integrand[1:] + coordination_integrand[:-1])
+ * np.diff(r)
+ ),
+))
+
+first_minimum = 3.55
+figure, (rdf_axis, coordination_axis) = plt.subplots(
+ 2,
+ 1,
+ sharex=True,
+ gridspec_kw={"height_ratios": (2.0, 1.25)},
+)
+
+rdf_axis.axvspan(
+ 0.0,
+ first_minimum,
+ color=COLORS["shell"],
+ label="first coordination shell",
+)
+rdf_axis.plot(r, g_r, color=COLORS["blue"])
+rdf_axis.axhline(1.0, color=COLORS["muted"], linestyle=":", linewidth=1.1)
+rdf_axis.axvline(
+ first_minimum,
+ color=COLORS["orange"],
+ linestyle="--",
+ linewidth=1.2,
+ label="first minimum",
+)
+rdf_axis.set_ylabel(r"$g(r)$")
+rdf_axis.set_ylim(0.0, 3.6)
+rdf_axis.legend(loc="upper right")
+
+coordination_axis.plot(r, coordination, color=COLORS["orange"])
+coordination_axis.axvline(
+ first_minimum,
+ color=COLORS["orange"],
+ linestyle="--",
+ linewidth=1.2,
+)
+coordination_axis.set_xlabel(r"Distance $r$ / $\mathrm{\AA}$")
+coordination_axis.set_ylabel(r"$N(r)$")
+coordination_axis.set_xlim(0.0, 8.0)
+coordination_axis.set_ylim(bottom=0.0)
+
+figure.tight_layout()
+plt.show()
diff --git a/docs/source/_plots/vacf.py b/docs/source/_plots/vacf.py
new file mode 100644
index 00000000..b60a9891
--- /dev/null
+++ b/docs/source/_plots/vacf.py
@@ -0,0 +1,90 @@
+"""Analytical two-band VACF and its PQAnalysis spectrum."""
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+from PQAnalysis.analysis.vacf.spectrum import vacf_spectrum
+
+from _style import COLORS, apply_style
+
+
+apply_style((6.2, 5.5))
+
+time = np.arange(0.0, 0.5005, 0.0005)
+correlation = (
+ 0.70 * np.exp(-(time / 0.22) ** 2)
+ * np.cos(2.0 * np.pi * 9.0 * time)
+ + 0.30 * np.exp(-(time / 0.12) ** 2)
+ * np.cos(2.0 * np.pi * 18.0 * time)
+)
+
+wavenumbers, amplitudes, windowed_correlation = vacf_spectrum(
+ time,
+ correlation,
+ ftsize=5000,
+ window_function="exponential",
+ window_param=4.0,
+)
+display_range = wavenumbers <= 1000.0
+wavenumbers = wavenumbers[display_range]
+amplitudes = amplitudes[display_range]
+amplitudes /= amplitudes.max()
+
+figure, (correlation_axis, spectrum_axis) = plt.subplots(
+ 2,
+ 1,
+ gridspec_kw={"height_ratios": (1.25, 1.0)},
+)
+
+correlation_axis.plot(
+ time,
+ correlation,
+ color=COLORS["blue"],
+ label="Unwindowed",
+)
+correlation_axis.plot(
+ time,
+ windowed_correlation,
+ color=COLORS["green"],
+ linestyle="--",
+ linewidth=1.6,
+ label="Exponential, 4 ps⁻¹",
+)
+correlation_axis.axhline(
+ 0.0,
+ color=COLORS["muted"],
+ linestyle=":",
+ linewidth=1.0,
+)
+correlation_axis.set_title(
+ "(a) Normalized VACF",
+ loc="left",
+ fontsize=9.5,
+ fontweight="bold",
+ pad=8,
+)
+correlation_axis.set_xlabel("Lag time, t / ps")
+correlation_axis.set_ylabel("Cᵥᵥ(t)")
+correlation_axis.set_xlim(time[0], time[-1])
+correlation_axis.set_ylim(-0.65, 1.05)
+correlation_axis.legend(loc="upper right")
+
+spectrum_axis.plot(
+ wavenumbers,
+ amplitudes,
+ color=COLORS["orange"],
+)
+spectrum_axis.set_title(
+ "(b) Exponential-window spectrum",
+ loc="left",
+ fontsize=9.5,
+ fontweight="bold",
+ pad=8,
+)
+spectrum_axis.set_xlabel("Wavenumber, ν̃ / cm⁻¹")
+spectrum_axis.set_ylabel("Relative amplitude")
+spectrum_axis.set_xlim(0.0, 1000.0)
+spectrum_axis.set_ylim(0.0, 1.05)
+
+figure.tight_layout(h_pad=1.4)
+plt.show()
diff --git a/docs/source/_plots/vibrations.py b/docs/source/_plots/vibrations.py
new file mode 100644
index 00000000..7bb50df2
--- /dev/null
+++ b/docs/source/_plots/vibrations.py
@@ -0,0 +1,73 @@
+"""IR stick spectrum calculated from the H2O validation fixture."""
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+from PQAnalysis.analysis.vibrational.vibrational_analysis import (
+ calculate_from_system,
+ read_hessian_file,
+)
+from PQAnalysis.io import MoldescriptorReader, RestartFileReader
+
+from _style import COLORS, PROJECT_ROOT, apply_style
+
+
+apply_style((7.2, 3.8))
+
+fixture = PROJECT_ROOT / "tests/data/vibrational"
+moldescriptor = fixture / "moldescriptor.dat"
+system = RestartFileReader(
+ str(fixture / "h2o.rst"),
+ moldescriptor_filename=str(moldescriptor),
+).read()
+hessian = read_hessian_file(str(fixture / "hessian.dat"))
+charges = np.asarray(
+ MoldescriptorReader(str(moldescriptor)).read()[0].partial_charges,
+ dtype=float,
+)
+result = calculate_from_system(
+ system,
+ hessian,
+ atom_charges=charges,
+)
+
+internal_modes = result.wavenumbers > 100.0
+wavenumbers = result.wavenumbers[internal_modes]
+intensities = result.intensities[internal_modes]
+
+figure, axis = plt.subplots()
+axis.vlines(
+ wavenumbers,
+ 0.0,
+ intensities,
+ color=COLORS["blue"],
+ linewidth=2.0,
+)
+axis.scatter(
+ wavenumbers,
+ intensities,
+ color=COLORS["blue"],
+ marker="_",
+ s=85,
+)
+for index, (wavenumber, intensity) in enumerate(
+ zip(wavenumbers, intensities)
+):
+ axis.annotate(
+ f"{wavenumber:.0f}",
+ (wavenumber, intensity),
+ xytext=(0, 5 + 10 * (index % 2)),
+ textcoords="offset points",
+ ha="center",
+ va="bottom",
+ color=COLORS["ink"],
+ fontsize=8,
+ )
+
+axis.set_xlabel(r"Wavenumber $\tilde{\nu}$ / $\mathrm{cm}^{-1}$")
+axis.set_ylabel(r"IR intensity / $\mathrm{km\ mol}^{-1}$")
+axis.set_xlim(0.0, 4200.0)
+axis.set_ylim(0.0, max(intensities) * 1.22)
+
+figure.tight_layout()
+plt.show()
diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css
index d4bed75f..943b20f4 100644
--- a/docs/source/_static/css/custom.css
+++ b/docs/source/_static/css/custom.css
@@ -1,34 +1,181 @@
-@import url("theme.css");
+:root {
+ --pq-plot-background: #fff;
+}
-.wy-nav-content {
- max-width: 80%;
+.sidebar-brand {
+ flex-direction: row;
+ align-items: center;
+ gap: 0.75rem;
+ padding-block: 0.75rem;
}
-dl.py.class {
- dt.sig.sig-object.py {
- display: block !important;
- }
+.sidebar-logo-container {
+ display: flex;
+ flex: 0 0 4.5rem;
+ align-items: center;
+ margin: 0;
+}
+
+.sidebar-logo {
+ width: 4.5rem;
+ margin: 0;
+}
+
+.sidebar-brand-text {
+ margin: 0;
+ font-size: 1.35rem;
+ font-weight: 700;
+ line-height: 1.1;
+ letter-spacing: 0;
+}
+
+code.literal {
+ border-radius: 2px;
+}
+
+figure:has(> img.plot-directive) {
+ margin-block: 1.5rem 2rem;
+}
+
+img.plot-directive {
+ width: 100%;
+ height: auto;
+ border: 1px solid var(--color-foreground-border);
+ background: var(--pq-plot-background);
+}
+
+img.plot-directive + figcaption {
+ margin-top: 0.65rem;
+ color: var(--color-foreground-secondary);
+ font-size: 0.9rem;
+ line-height: 1.45;
+ text-align: left;
+}
+
+.table-wrapper {
+ overflow-x: auto;
}
-.py.property {
- display: block !important;
+@media (max-width: 44rem), (max-height: 32rem) {
+ .back-to-top {
+ display: none !important;
+ }
}
-.sig.sig-object.py dl {
- margin-block-end: 0.0em;
+@media (max-width: 44rem) {
+ article table.autosummary,
+ article table.pq-record-table {
+ display: block;
+ width: 100%;
+ }
+
+ table.autosummary,
+ table.autosummary tbody,
+ table.autosummary tr,
+ table.autosummary td,
+ table.pq-record-table,
+ table.pq-record-table tbody,
+ table.pq-record-table tr,
+ table.pq-record-table td {
+ display: block;
+ width: 100%;
+ }
+
+ table.autosummary,
+ table.pq-record-table {
+ border: 0;
+ }
+
+ table.autosummary tr,
+ table.pq-record-table tr {
+ padding-block: 0.7rem;
+ border-bottom: 1px solid var(--color-foreground-border);
+ }
+
+ table.autosummary td,
+ table.pq-record-table td {
+ padding: 0.2rem 0;
+ border: 0;
+ }
+
+ table.autosummary td:first-child,
+ table.pq-record-table td:first-child {
+ font-weight: 650;
+ }
+
+ table.autosummary td:last-child {
+ color: var(--color-foreground-secondary);
+ }
+
+ table.pq-record-table thead {
+ display: none;
+ }
+
+ table.pq-record-table td:not(:first-child)::before {
+ display: block;
+ margin-top: 0.35rem;
+ color: var(--color-foreground-secondary);
+ font-size: 0.75rem;
+ font-weight: 650;
+ text-transform: uppercase;
+ }
+
+ table.pq-method-table td:nth-child(2)::before,
+ table.pq-observable-table td:nth-child(2)::before {
+ content: "Required data";
+ }
+
+ table.pq-method-table td:nth-child(3)::before,
+ table.pq-observable-table td:nth-child(3)::before {
+ content: "Reported quantity";
+ }
+
+ table.pq-extension-table td:nth-child(2)::before {
+ content: "Primary location";
+ }
+
+ table.pq-extension-table td:nth-child(3)::before {
+ content: "Contract";
+ }
+
+ table.pq-package-table td:nth-child(2)::before {
+ content: "Responsibility";
+ }
+
+ table.pq-validation-table td:nth-child(2)::before {
+ content: "Purpose";
+ }
+
+ table.pq-validation-table td:nth-child(3)::before {
+ content: "Suitable reference";
+ }
- & dd {
- margin-bottom: 0.0em;
+ table.pq-types-table td:nth-child(2)::before {
+ content: "Role";
+ }
+
+ table.pq-package-reference-table td:nth-child(2)::before {
+ content: "Scope";
}
}
-.wy-table-responsive table.analysis-output-columns {
+@media (max-width: 24rem) {
+ article h1 {
+ font-size: 2rem;
+ }
+}
+
+table.analysis-output-columns {
min-width: 640px;
width: 100%;
}
-.wy-table-responsive table.analysis-output-columns th,
-.wy-table-responsive table.analysis-output-columns td {
+table.analysis-output-columns th,
+table.analysis-output-columns td {
vertical-align: top;
white-space: normal;
}
+
+.pq-command-table td:first-child {
+ white-space: nowrap;
+}
diff --git a/docs/source/_templates/package.rst b/docs/source/_templates/package.rst
index fadf61ee..86c9a1c2 100644
--- a/docs/source/_templates/package.rst
+++ b/docs/source/_templates/package.rst
@@ -1,4 +1,5 @@
-{# The :autogenerated: tag is picked up by breadcrumbs.html to suppress "Edit on Github" link #}
+{# Generated packages stay outside the maintained user-facing navigation. #}
+:orphan:
:autogenerated:
{{ name }}
@@ -116,4 +117,4 @@
Reference
---------
-{%- endif %}
\ No newline at end of file
+{%- endif %}
diff --git a/docs/source/analyses/index.rst b/docs/source/analyses/index.rst
new file mode 100644
index 00000000..6985cfde
--- /dev/null
+++ b/docs/source/analyses/index.rst
@@ -0,0 +1,49 @@
+Analyses
+========
+
+PQAnalysis covers structural organization, translational dynamics,
+time-correlation spectra, molecular normal modes and conservation diagnostics.
+Choose the observable from the physical question and from the data recorded by
+the simulation.
+
+Choose by input data
+--------------------
+
+.. list-table:: Analysis inputs and primary observables
+ :class: pq-record-table pq-observable-table
+ :header-rows: 1
+ :widths: 24 32 44
+
+ * - Analysis
+ - Required physical data
+ - Primary observable
+ * - RDF
+ - Positions and periodic cell
+ - :math:`g_{AB}(r)` and cumulative coordination
+ * - MSD
+ - Positions and periodic cell
+ - :math:`\langle |\mathbf{r}(t)-\mathbf{r}(0)|^2\rangle`
+ * - VACF
+ - Velocities and frame time step
+ - Normalized :math:`C_{vv}(t)` and its spectrum
+ * - Vibrations
+ - Structure, masses and Cartesian Hessian
+ - Normal-mode wavenumbers and force constants
+ * - Momentum
+ - Velocities and atomic masses
+ - :math:`|\sum_i m_i\mathbf{v}_i|` per frame
+
+The method pages define each estimator, its assumptions and its interpretation
+limits. File columns and units are specified once in
+:ref:`analysisOutputFiles`. Programmatic entry points are listed in the
+:doc:`../reference/functions`.
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ rdf
+ msd
+ vacf
+ vibrations
+ momentum
diff --git a/docs/source/analyses/momentum.rst b/docs/source/analyses/momentum.rst
new file mode 100644
index 00000000..06114948
--- /dev/null
+++ b/docs/source/analyses/momentum.rst
@@ -0,0 +1,43 @@
+Total Linear Momentum
+=====================
+
+For every velocity frame, PQAnalysis evaluates the selected atoms' total
+linear momentum,
+
+.. math::
+
+ \mathbf{P}(t) = \sum_i m_i\mathbf{v}_i(t),
+
+and writes its scaled norm. This is a diagnostic for center-of-mass drift and
+momentum conservation, not a substitute for inspecting the thermostat,
+constraints or integration scheme.
+
+Run the diagnostic
+------------------
+
+.. code-block:: console
+
+ $ pqanalysis check_momentum velocity.vel \
+ --selection all \
+ --output momentum.dat
+
+The default scale of ``1e-15`` converts PQ velocity-trajectory values from
+amu·Å·s⁻¹ to amu·Å·fs⁻¹. Use ``--scale`` when the input convention differs.
+
+Interpretation
+--------------
+
+The output contains a one-based frame index and the scaled momentum norm. A
+systematic increase can indicate center-of-mass drift. Oscillatory or noisy
+behavior must be interpreted relative to the total mass, velocity scale and
+numerical precision.
+
+File-backed PQ and QMCFC velocity trajectories are parsed directly as float64.
+The compatibility path multiplies and sums atoms in the same order as the
+legacy ``equipartition.jl`` calculation. Native output uses 17 significant
+digits, so reloading it as float64 preserves each calculated value exactly.
+Trajectory objects use the numerical precision already stored in the object.
+
+See :ref:`analysis-output-momentum` for the output schema. Python workflows
+can call :func:`PQAnalysis.analysis.momentum.api.check_momentum` or use
+:class:`PQAnalysis.analysis.momentum.momentum.Momentum` directly.
diff --git a/docs/source/analyses/msd.rst b/docs/source/analyses/msd.rst
new file mode 100644
index 00000000..5ad28d31
--- /dev/null
+++ b/docs/source/analyses/msd.rst
@@ -0,0 +1,75 @@
+Mean Square Displacement
+========================
+
+The mean square displacement measures translational motion over a lag time.
+For Cartesian component :math:`\alpha`, PQAnalysis evaluates multiple time
+origins according to
+
+.. math::
+
+ \mathrm{MSD}_{\alpha}(\tau) =
+ \left\langle [r_{i\alpha}(t+\tau)-r_{i\alpha}(t)]^2 \right\rangle_{i,t}.
+
+Coordinates are unwrapped with the periodic cell before displacements are
+accumulated.
+
+Estimator and fit interval
+--------------------------
+
+.. plot:: _plots/msd.py
+ :alt: Cartesian and total mean square displacement with a linear fit
+ :caption: Bundled oxygen-atom validation fixture with a 0.5 ps frame
+ interval. The dashed line fits the final 20 total-MSD samples and
+ illustrates fit-window selection; the fixture is not a material
+ diffusion benchmark.
+
+Minimal input
+-------------
+
+.. code-block:: text
+
+ traj_files = trajectory.xyz
+ target_selection = O
+ out_file = msd.dat
+ window = 1000
+ gap = 10
+ time_step = 0.001
+ fit_window = 200
+
+.. code-block:: console
+
+ $ pqanalysis msd msd.in
+
+``window`` is the largest lag in frames and must be divisible by ``gap``.
+``gap`` controls the spacing between time origins. ``time_step`` is expressed
+in ps and enables diffusion fitting; ``fit_window`` selects the trailing
+points used by that fit.
+
+File-backed orthorhombic trajectories use a bounded compatibility path that
+preserves the Diffcalc operation order. Unsupported cells or inputs return to
+the general streaming implementation.
+
+Interpretation
+--------------
+
+The output contains the lag index and the x, y and z components in Ų. Their
+sum is the total three-dimensional MSD. In an
+isotropic diffusive regime,
+
+.. math::
+
+ D = \frac{1}{6}\frac{d}{dt}\mathrm{MSD}_{\mathrm{total}}(t).
+
+PQAnalysis also fits each Cartesian component with the corresponding
+one-dimensional factor. The resulting coefficients, uncertainties and
+:math:`R^2` values are written to the log file in m²·s⁻¹. A fit is
+physically meaningful only over a linear diffusive interval; short-time
+ballistic motion and poorly sampled long lags should not be included blindly.
+
+Output and API
+--------------
+
+See :ref:`analysis-output-msd` for the exact table layout. The input-file entry
+point is :func:`PQAnalysis.analysis.msd.api.msd`; direct workflows can use
+:class:`PQAnalysis.analysis.msd.msd.MSD` and inspect its total MSD and fit
+results.
diff --git a/docs/source/analyses/rdf.rst b/docs/source/analyses/rdf.rst
new file mode 100644
index 00000000..bc09e9fe
--- /dev/null
+++ b/docs/source/analyses/rdf.rst
@@ -0,0 +1,81 @@
+Radial Distribution Function
+============================
+
+The radial distribution function measures the probability of finding a target
+atom at distance :math:`r` from a reference atom relative to an ideal gas at
+the same effective target density. For histogram bin :math:`i`, PQAnalysis
+uses
+
+.. math::
+
+ g_i = \frac{H_i}{\rho_T N_R N_F \Delta V_i},
+
+where :math:`H_i` is the eligible pair count, :math:`\rho_T` the target number
+density, :math:`N_R` the number of reference atoms, :math:`N_F` the number of
+frames and :math:`\Delta V_i` the spherical-shell volume.
+
+Structural interpretation
+-------------------------
+
+.. plot:: _plots/rdf.py
+ :alt: Radial distribution function and cumulative coordination number
+ :caption: Analytic schematic, not simulation output. The shaded interval
+ ends at the first minimum. The lower panel evaluates
+ N(r) = 4πρ∫₀ʳ g(s)s² ds with ρ = 0.0334 Å⁻³.
+
+Minimal input
+-------------
+
+.. code-block:: text
+
+ traj_files = trajectory.xyz
+ reference_selection = O
+ target_selection = H
+ delta_r = 0.05
+ r_max = 8.0
+ out_file = rdf.dat
+
+.. code-block:: console
+
+ $ pqanalysis rdf rdf.in
+
+``restart_file`` and ``moldescriptor_file`` are unnecessary for a basic
+species RDF. They are required when ``no_intra_molecular = True`` is used to
+exclude pairs belonging to the same molecule. PQAnalysis can infer the usual
+PQ companion filenames when they are beside the trajectory.
+
+Legacy-compatible arithmetic
+----------------------------
+
+For a file-backed periodic orthorhombic trajectory, specifying ``delta_r``
+alone with the default ``r_min = 0`` selects the legacy-compatible RDF path.
+Coordinates are parsed as float64; histogram binning and all five output
+columns preserve the corrected legacy C operation order. Explicit ``r_max`` or
+``n_bins``, triclinic or vacuum cells, and intramolecular exclusion use the
+general PQAnalysis path. The minimal example above sets ``r_max`` explicitly
+and therefore uses the general path.
+
+Interpretation
+--------------
+
+* Peaks mark preferred pair separations; minima separate coordination shells.
+* :math:`g(r) \approx 1` indicates bulk-like, uncorrelated pair density at that
+ distance.
+* The cumulative coordination column gives the mean number of eligible target
+ atoms per reference atom inside the current upper bin edge.
+* Self pairs are excluded. Intramolecular pairs are included unless molecular
+ topology is supplied and explicitly excluded.
+
+Normalization, finite-size effects, selection definitions and trajectory
+sampling should be considered before comparing RDFs from different systems.
+
+Output and API
+--------------
+
+See :ref:`analysis-output-rdf` for the five output columns and their exact
+normalization. The main Python entry point is
+:func:`PQAnalysis.analysis.rdf.api.rdf`; lower-level calculations use
+:class:`PQAnalysis.analysis.rdf.rdf.RDF`.
+
+The complete input-key table is documented with
+:class:`PQAnalysis.analysis.rdf.rdf_input_file_reader.RDFInputFileReader`.
diff --git a/docs/source/analyses/vacf.rst b/docs/source/analyses/vacf.rst
new file mode 100644
index 00000000..8f8d5cd4
--- /dev/null
+++ b/docs/source/analyses/vacf.rst
@@ -0,0 +1,95 @@
+VACF and Spectra
+================
+
+The normalized velocity autocorrelation function describes how rapidly atomic
+velocities lose memory of their initial direction:
+
+.. math::
+
+ C_{vv}(t) =
+ \left\langle
+ \frac{\sum_i \mathbf{v}_i(t_0)\cdot\mathbf{v}_i(t_0+t)}
+ {\sum_i \mathbf{v}_i(t_0)\cdot\mathbf{v}_i(t_0)}
+ \right\rangle_{t_0}.
+
+The brackets denote an average over admissible time origins, and the sum runs
+over the selected atoms. This is the default, legacy-compatible estimator and
+gives :math:`C_{vv}(0)=1`. The ``fft`` estimator instead averages the numerator
+and denominator separately over all available origins before normalization.
+
+PQAnalysis can transform the correlation to a wavenumber-domain spectrum. If
+static or time-dependent partial charges are supplied, it correlates
+:math:`q_i\mathbf{v}_i` instead, producing a charge-flux spectrum that
+approximates an infrared spectrum.
+
+The correlation written to ``out_file`` is not apodized. When a spectrum is
+requested, ``window_function`` multiplies a copy of the correlation before the
+cosine transform. The optional ``windowed_out_file`` records that copy.
+
+Correlation and spectrum
+------------------------
+
+.. plot:: _plots/vacf.py
+ :alt: Analytical normalized VACF, its exponentially windowed copy and the
+ resulting spectrum
+ :caption: Analytical normalized VACF for two Gaussian-broadened bands
+ centered at 300 and 600 cm⁻¹, with dephasing times of 0.22 and 0.12 ps.
+ The dashed curve applies an exponential window with a decay coefficient
+ of 4 ps⁻¹ before the PQAnalysis cosine transform. Spectrum amplitudes
+ are scaled to unit maximum.
+
+Minimal input
+-------------
+
+.. code-block:: text
+
+ traj_files = trajectory.vel
+ target_selection = all
+ out_file = vacf.dat
+ time_step = 0.001
+ window = 2500
+ gap = 5
+ spectrum_file = spectrum.dat
+ ftsize = 5000
+ window_function = exponential
+ window_param = 4.0
+
+.. code-block:: console
+
+ $ pqanalysis vacf vacf.in
+
+The time step is specified in ps. ``window`` is the maximum correlation lag in
+frames; it is distinct from the apodization selected by ``window_function``.
+The example multiplies only the spectrum input by
+:math:`\exp[-(4\ \mathrm{ps}^{-1})t]`. ``window_function`` also accepts
+``hann``, ``blackman`` and ``none``; ``none`` is the default. The default
+sliding-origin method matches the legacy calculation. ``gap`` controls the
+spacing between its time origins, not the lag-time spacing in ``out_file``;
+``method = fft`` selects a denser-origin Wiener-Khinchin estimator.
+
+Interpretation
+--------------
+
+* A rapidly decaying VACF indicates fast velocity decorrelation.
+* In liquids, negative regions often indicate backscattering or cage motion;
+ in solids, sign oscillations reflect bound vibrational motion.
+* The sampling interval sets the Nyquist limit, and the correlation length sets
+ the resolving power. Zero-padding provides a denser frequency grid but does
+ not add spectral resolution.
+* Apodization reduces endpoint artifacts but changes band widths and
+ amplitudes; report the selected function and its parameters.
+* Charge-flux spectra require physically meaningful partial charges and should
+ not be interpreted as absolute IR intensities without further calibration.
+
+Output and API
+--------------
+
+See :ref:`analysis-output-vacf` for correlation and spectrum columns. The
+input-file entry point is :func:`PQAnalysis.analysis.vacf.api.vacf`. Direct
+calculations use :class:`PQAnalysis.analysis.vacf.vacf.VACF`, while
+:func:`PQAnalysis.analysis.vacf.spectrum.vacf_spectrum` performs the spectral
+transform.
+
+Discrete line spectra can be broadened independently with
+``pqanalysis build_spectrum``; see :ref:`analysis-output-spectrum` for its
+output convention.
diff --git a/docs/source/analyses/vibrations.rst b/docs/source/analyses/vibrations.rst
new file mode 100644
index 00000000..1421b705
--- /dev/null
+++ b/docs/source/analyses/vibrations.rst
@@ -0,0 +1,64 @@
+Vibrational Analysis
+====================
+
+Vibrational analysis diagonalizes the mass-weighted Cartesian Hessian. Its
+eigenvectors define normal modes and its eigenvalues determine signed
+wavenumbers. Negative wavenumbers represent imaginary modes associated with
+negative curvature of the potential-energy surface.
+
+Internal-mode spectrum
+----------------------
+
+.. plot:: _plots/vibrations.py
+ :alt: Infrared stick spectrum for the water validation fixture
+ :caption: IR stick spectrum calculated by PQAnalysis from the bundled H₂O
+ structure, Hessian and partial-charge fixtures. Only internal modes above
+ 100 cm⁻¹ are shown; translational and rotational modes are omitted.
+
+Minimal input
+-------------
+
+.. code-block:: text
+
+ structure_file = structure.rst
+ hessian_file = hessian.dat
+ out_file = wavenumbers.dat
+ normal_modes_file = normal_modes.dat
+ modes_file = modes.xyz
+ modes = positive
+ unit = kcal
+ hessian_sign = auto
+
+.. code-block:: console
+
+ $ pqanalysis vibrations vibrations.in
+
+``structure_file`` may be a PQ restart or a single-frame XYZ file. ``unit``
+describes the Hessian energy unit and accepts ``kcal``, ``hartree`` or ``ev``.
+``hessian_sign = auto`` evaluates both supported sign conventions and chooses
+the one with more non-negative vibrational modes.
+
+Scientific checks
+-----------------
+
+* A stable, fully optimized minimum should not contain genuine imaginary
+ internal modes. Small values can arise from incomplete optimization or
+ numerical noise.
+* Translational and rotational near-zero modes depend on boundary conditions,
+ molecular geometry and numerical precision.
+* IR intensities require a ``moldescriptor_file`` containing partial charges.
+* The Hessian coordinate order, structure atom order and selected unit must
+ agree exactly.
+
+Mode output
+-----------
+
+``normal_modes_file`` stores the dimensionless Cartesian mode matrix.
+``modes_prefix`` writes sinusoidal multi-frame XYZ animations, while
+``modes_file`` writes one extended-XYZ image per selected mode with vectors and
+metadata. Explicit mode numbers are one-based.
+
+See :ref:`analysis-output-vibrations` for every table and file schema. The main
+entry point is :func:`PQAnalysis.analysis.vibrational.api.vibrations`; direct
+calculations use
+:func:`PQAnalysis.analysis.vibrational.vibrational_analysis.calculate_from_system`.
diff --git a/docs/source/conf.py b/docs/source/conf.py
index d7f93d0e..cb0911fc 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -1,43 +1,44 @@
-# 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
-
-# -- Project information -----------------------------------------------------
-# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+"""Sphinx configuration for the PQAnalysis documentation."""
import sys
-import os
+from pathlib import Path
+
+
+SOURCE_DIR = Path(__file__).resolve().parent
+DOCS_DIR = SOURCE_DIR.parent
+PROJECT_ROOT = DOCS_DIR.parent
-sys.path.insert(0, os.path.abspath('../../'))
+sys.path.insert(0, str(PROJECT_ROOT))
+sys.path.insert(0, str(SOURCE_DIR / "_plots"))
-project = 'PQAnalysis'
-copyright = '2023, Jakob Gamper, Josef M. Gallmetzer, Clarissa A. Seidler'
-author = 'Jakob Gamper, Josef M. Gallmetzer, Clarissa A. Seidler'
+project = "PQAnalysis"
+author = "the PQAnalysis authors"
+copyright = "2023-2026, the PQAnalysis authors"
-# -- General configuration ---------------------------------------------------
-# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+try:
+ from PQAnalysis import __version__ as release
+except Exception: # pragma: no cover - package may be absent in a bare checkout
+ release = ""
+version = release
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.intersphinx',
- 'sphinx.ext.todo',
- 'sphinx.ext.coverage',
- 'sphinx.ext.mathjax',
- 'sphinx.ext.ifconfig',
- 'sphinx.ext.viewcode',
- 'sphinx.ext.napoleon',
- 'sphinx.ext.autosummary',
- 'sphinx_sitemap',
- 'sphinx.ext.inheritance_diagram',
- 'myst_parser',
+ "sphinx.ext.autodoc",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.todo",
+ "sphinx.ext.coverage",
+ "sphinx.ext.mathjax",
+ "sphinx.ext.ifconfig",
+ "sphinx.ext.viewcode",
+ "sphinx.ext.napoleon",
+ "sphinx.ext.autosummary",
+ "sphinx.ext.inheritance_diagram",
+ "sphinx_sitemap",
+ "matplotlib.sphinxext.plot_directive",
+ "myst_parser",
+ "sphinx_copybutton",
]
-# Napoleon settings
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = False
@@ -50,92 +51,103 @@
napoleon_use_param = True
napoleon_use_rtype = True
-autoclass_content = 'both'
-autodoc_class_signature = 'mixed'
-autodoc_typehints_format = 'short'
-autodoc_member_order = 'alphabetical'
+autoclass_content = "both"
+autodoc_class_signature = "mixed"
+autodoc_typehints_format = "short"
+autodoc_member_order = "alphabetical"
maximum_signature_line_length = 50
add_module_names = False
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+copybutton_prompt_text = r">>> |\.\.\. |\$ "
+copybutton_prompt_is_regexp = True
-# The suffix(es) of source filenames.
-# You can specify multiple suffix as a list of string:
-source_suffix = ['.rst', '.md']
+plot_formats = [("svg", 96)]
+plot_html_show_formats = False
+plot_html_show_source_link = False
+plot_include_source = False
-# The master toctree document.
-master_doc = 'index'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-# This patterns also effect to html_static_path and html_extra_path
+templates_path = ["_templates"]
+source_suffix = {
+ ".rst": "restructuredtext",
+ ".md": "markdown",
+}
+master_doc = "index"
exclude_patterns = []
+highlight_language = "python"
-highlight_language = 'python'
+html_theme = "furo"
+html_title = "PQAnalysis"
+html_logo = "logo/PQAnalysis.png"
+html_favicon = "logo/PQAnalysis.png"
+html_static_path = ["_static"]
+html_css_files = ["css/custom.css"]
+html_baseurl = "https://molarverse.github.io/PQAnalysis/"
-# -- Options for HTML output -------------------------------------------------
-# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-#
-html_theme = 'sphinx_rtd_theme'
-html_style = 'css/custom.css'
html_theme_options = {
- 'canonical_url': '',
- 'analytics_id': '', # Provided by Google in your dashboard
- 'prev_next_buttons_location': 'bottom',
- 'style_external_links': False,
-
- 'logo_only': False,
-
- # Toc options
- 'collapse_navigation': True,
- 'sticky_navigation': True,
- 'includehidden': True,
- 'titles_only': True,
- 'globaltoc_maxdepth': -1,
+ "sidebar_hide_name": False,
+ "light_css_variables": {
+ "color-brand-primary": "#1f718f",
+ "color-brand-content": "#176c8c",
+ },
+ "dark_css_variables": {
+ "color-brand-primary": "#65bddb",
+ "color-brand-content": "#65bddb",
+ },
+ "source_repository": "https://github.com/MolarVerse/PQAnalysis/",
+ "source_branch": "main",
+ "source_directory": "docs/source/",
+ "footer_icons": [
+ {
+ "name": "GitHub",
+ "url": "https://github.com/MolarVerse/PQAnalysis",
+ "html": (
+ ''
+ ),
+ "class": "",
+ },
+ ],
}
-html_logo = 'logo/PQAnalysis.png'
-# github_url = ''
-html_baseurl = 'https://molarverse.github.io/PQAnalysis/'
-
-# 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']
-
def show_inherited_mixins(app, what, name, obj, options, lines):
- """Show inherited mixins in the base classes of a class"""
+ """Show inherited mixins in the base classes of a class."""
- if what != 'class' or not hasattr(obj, '__bases__'):
+ if what != "class" or not hasattr(obj, "__bases__"):
return
for base in obj.__bases__:
- if base.__name__.endswith('Mixin'):
- options['inherited-members'] = True
+ if base.__name__.endswith("Mixin"):
+ options["inherited-members"] = True
def run_apidoc(app):
- """Generage API documentation"""
+ """Generate the complete package API reference."""
import better_apidoc
+
better_apidoc.APP = app
better_apidoc.main([
- 'better-apidoc',
- '-t',
- os.path.join('.', 'source', '_templates'),
- '--force',
- '--no-toc',
- '--separate',
- '-o',
- os.path.join('.', 'source', 'code'),
- os.path.join('..', 'PQAnalysis')
+ "better-apidoc",
+ "-t",
+ str(SOURCE_DIR / "_templates"),
+ "--force",
+ "--no-toc",
+ "--separate",
+ "-o",
+ str(SOURCE_DIR / "code"),
+ str(PROJECT_ROOT / "PQAnalysis"),
])
def setup(app):
- app.connect('autodoc-process-docstring', show_inherited_mixins)
- app.connect('builder-inited', run_apidoc)
+ app.connect("autodoc-process-docstring", show_inherited_mixins)
+ app.connect("builder-inited", run_apidoc)
diff --git a/docs/source/data/index.rst b/docs/source/data/index.rst
new file mode 100644
index 00000000..9b5535e5
--- /dev/null
+++ b/docs/source/data/index.rst
@@ -0,0 +1,68 @@
+Files and Formats
+=================
+
+PQAnalysis separates simulation data, analysis configuration and output-table
+serialization. File extensions select output formats, while input content and
+explicit engine options determine how trajectories are read.
+
+Four file contracts govern analysis workflows:
+
+* :ref:`inputFile` defines the key-value grammar.
+* :ref:`analysisOutputFiles` defines table fields, symbols and units.
+* :doc:`../reference/cli` documents table, structure and trajectory conversion.
+* :doc:`../reference/api` identifies readers, writers and trajectory objects.
+
+Analysis configuration
+----------------------
+
+RDF, MSD, VACF and vibrational calculations use key-value input files. Lists
+may be written in brackets or as multiline values according to the
+:ref:`inputFile` grammar. Relative filenames are resolved by the process
+running the command, so reproducible workflows should execute from a known run
+directory.
+
+Trajectories and engines
+------------------------
+
+Analysis commands default to PQ conventions. Use ``--engine`` when reading a
+supported alternative convention. Position analyses require coordinates and a
+consistent atom count; MSD additionally needs periodic cells for unwrapping.
+VACF and momentum analyses require velocity data. Molecular exclusions in RDF
+require topology information from a restart and moldescriptor.
+
+The Python format definitions are documented by
+:class:`PQAnalysis.traj.formats.MDEngineFormat` and
+:class:`PQAnalysis.traj.formats.TrajectoryFormat`.
+
+Selections
+----------
+
+Analysis selections are parsed by :class:`PQAnalysis.topology.selection.Selection`.
+Use elemental or atom-name selections for simple systems and full atom
+information when residue-aware selection is required. Always verify that the
+selection contains the intended atoms; normalization and statistical quality
+depend directly on its population.
+
+Output and conversion
+---------------------
+
+Native, CSV, TSV and PQAnalysis-generated XVG tables are mutually convertible.
+The converter detects input content rather than trusting the extension and can
+write several outputs atomically:
+
+.. code-block:: console
+
+ $ pqanalysis convert rdf.xvg \
+ -o rdf.dat \
+ -o rdf.csv \
+ -o rdf.tsv
+
+No output is written if any requested destination already exists. Use
+``--mode o`` only when intentional replacement is acceptable.
+
+.. toctree::
+ :hidden:
+ :maxdepth: 1
+
+ Analysis input files <../userGuide/inputFile>
+ Analysis output files <../userGuide/analysisOutputFiles>
diff --git a/docs/source/developerGuide/adding-analysis.rst b/docs/source/developerGuide/adding-analysis.rst
new file mode 100644
index 00000000..00426479
--- /dev/null
+++ b/docs/source/developerGuide/adding-analysis.rst
@@ -0,0 +1,103 @@
+Adding an Analysis
+==================
+
+Use an existing complete analysis, such as RDF or MSD, as the structural
+reference. Keep the scientific estimator independent from its CLI and file
+format adapters.
+
+1. Define the scientific contract
+---------------------------------
+
+State the observable, normalization, units, periodic-boundary treatment,
+selection semantics and returned array shapes before implementing the
+calculation. Decide which behavior reproduces a legacy tool and which behavior
+is a corrected or newly defined method.
+
+The analysis guide and tests must use the same definitions. A numerical result
+without its normalization and units is not a complete interface.
+
+2. Implement the analysis package
+---------------------------------
+
+A file-driven analysis typically owns these modules:
+
+.. code-block:: text
+
+ PQAnalysis/analysis//
+ __init__.py
+ api.py
+ .py
+ _input_file_reader.py
+ _output_file_writer.py
+ exceptions.py
+
+The analysis class or numerical function owns the calculation. The input reader
+validates configuration and the writer serializes results. Avoid importing CLI
+code from the analysis package.
+
+If a compiled kernel is required, provide a Python or NumPy fallback with the
+same callable signature. Import the compiled implementation first and fall back
+only when it is unavailable, following the RDF, MSD and VACF packages.
+
+3. Define inputs and outputs
+----------------------------
+
+Add input keys to the analysis input reader with explicit types, defaults and
+validation. Required files should be validated before the trajectory is
+processed. Keep aliases only when they preserve an established input contract.
+
+Define output columns in ``PQAnalysis/analysis/_output_schemas.py`` using
+:class:`~PQAnalysis.analysis.output.AnalysisColumn` and
+:class:`~PQAnalysis.analysis.output.AnalysisSchema`. Field identifiers are ASCII
+programmatic names; symbols and units may use Unicode scientific notation.
+
+The data writer should subclass
+:class:`~PQAnalysis.analysis.output.AnalysisDataWriter`, create an
+:class:`~PQAnalysis.analysis.output.AnalysisTable` from the numerical columns,
+and delegate CSV, TSV and XVG exports to the common writer. Preserve a legacy
+native row format only when compatibility requires it.
+
+4. Add the public API
+---------------------
+
+The function in ``api.py`` is the shared orchestration layer. It should:
+
+1. read and validate the analysis input;
+2. construct trajectory, structure or Hessian readers;
+3. construct every output writer so path conflicts fail early;
+4. instantiate and run the scientific analysis;
+5. write the result and return useful in-memory data where appropriate.
+
+Export the function and supported result types from the analysis package
+``__init__.py`` and, for a general analysis workflow, from
+``PQAnalysis.analysis``. Add the callable to :doc:`../reference/functions`.
+
+5. Add the CLI
+--------------
+
+Implement a ``CLIBase`` subclass in ``PQAnalysis/cli/.py``. Its
+``add_arguments`` method defines only command-line parsing; ``run`` calls the
+public API function. Reuse common arguments from
+``PQAnalysis/cli/_argument_parser.py``, including repeatable ``--export`` for
+analysis tables.
+
+Register the class in the dispatch dictionary in ``PQAnalysis/cli/main.py``.
+Add a ``[project.scripts]`` entry in ``pyproject.toml`` only when a standalone
+executable is part of the supported interface.
+
+6. Add evidence and documentation
+---------------------------------
+
+The minimum complete change includes:
+
+* analytical or independently computed numerical tests;
+* legacy parity tests when compatibility is claimed;
+* compiled-kernel and fallback parity where both exist;
+* input-reader validation and default tests;
+* API and CLI end-to-end tests;
+* native output and CSV, TSV and XVG tests;
+* existing-file failure tests for every output path;
+* an analysis page defining equations, assumptions, inputs and interpretation;
+* entries in the function index, command reference and output-schema page.
+
+Follow :doc:`validation` for reference-data provenance and tolerance rules.
diff --git a/docs/source/developerGuide/architecture.rst b/docs/source/developerGuide/architecture.rst
new file mode 100644
index 00000000..cb616ee6
--- /dev/null
+++ b/docs/source/developerGuide/architecture.rst
@@ -0,0 +1,89 @@
+Architecture
+============
+
+PQAnalysis separates scientific computation from file orchestration. An
+input-file analysis normally follows this path:
+
+.. code-block:: text
+
+ CLI class
+ -> public API function
+ -> analysis input reader
+ -> trajectory, structure or Hessian reader
+ -> analysis object and numerical kernel
+ -> AnalysisTable with an AnalysisSchema
+ -> native writer and optional CSV, TSV or XVG writers
+
+The command line and Python API therefore share the calculation, validation and
+output code. A CLI class should parse arguments and call a public API function;
+it should not contain a second implementation of the scientific method.
+
+Package boundaries
+------------------
+
+.. list-table:: Source ownership
+ :class: pq-record-table pq-package-table
+ :header-rows: 1
+ :widths: 30 70
+
+ * - Path
+ - Responsibility
+ * - ``PQAnalysis/analysis/``
+ - Scientific estimators, spectra, result models and analysis-table output
+ * - ``PQAnalysis/cli/``
+ - Argument definitions and dispatch to public API functions
+ * - ``PQAnalysis/io/``
+ - Simulation-file readers, writers and format conversion
+ * - ``PQAnalysis/traj/``
+ - Trajectory containers, engine formats and trajectory-wide checks
+ * - ``PQAnalysis/atomic_system/`` and ``PQAnalysis/core/``
+ - Atomic coordinates, cells, atoms and residues
+ * - ``PQAnalysis/topology/``
+ - Selections, molecular identity and bonded topology
+
+The :doc:`../reference/functions` page lists callable entry points. The
+:doc:`../reference/api` page exposes the classes and generated modules behind
+them.
+
+Public contracts
+----------------
+
+Treat the following as compatibility surfaces:
+
+* non-underscored functions and classes deliberately imported by a package
+ ``__init__.py``;
+* command names, arguments and input-file keys;
+* analysis result attributes and array shapes;
+* output field identifiers, symbols, units and column order;
+* accepted trajectory and structure formats;
+* exception types raised for invalid user input.
+
+Modules, functions and attributes beginning with an underscore are internal.
+Changing a public contract requires tests, documentation and either backward
+compatibility or an explicit deprecation path.
+
+Numerical kernels
+-----------------
+
+RDF, MSD and VACF use compiled Cython kernels when available and NumPy/Python
+fallbacks otherwise. The compiled and fallback implementations must keep the
+same signature, normalization and edge-case behavior. A kernel change therefore
+requires tests of both implementations and a direct parity test between them.
+
+File parsing and logging belong outside numerical kernels. Kernels should accept
+validated arrays and scalar parameters and return numerical results without
+creating files.
+
+Analysis-table contract
+-----------------------
+
+Scientific columns are defined by
+``PQAnalysis/analysis/_output_schemas.py``. Each
+:class:`~PQAnalysis.analysis.output.AnalysisSchema` records stable ASCII field
+identifiers, display symbols, units and an optional xmgrace projection.
+
+Writers convert numerical results into an
+:class:`~PQAnalysis.analysis.output.AnalysisTable`. Native formatting may retain
+legacy row layout, while CSV, TSV and XVG use the same schema. Construct all
+requested writers before the calculation starts so an existing output path
+fails before expensive work or partial output occurs.
diff --git a/docs/source/developerGuide/developerGuide.rst b/docs/source/developerGuide/developerGuide.rst
index 66c7d397..f40ebec5 100644
--- a/docs/source/developerGuide/developerGuide.rst
+++ b/docs/source/developerGuide/developerGuide.rst
@@ -1,141 +1,123 @@
.. _developerGuide:
-###############
-Developer Guide
-###############
+Development
+===========
-This section includes information for developers who want to contribute to the project. It includes information about the project structure, how to run the tests, and how to build the documentation. It also includes information about the project's coding style and how to contribute to the project.
+PQAnalysis integrates feature and fix branches on ``dev`` and releases from
+``main``. The pages below define package ownership, extension steps,
+validation requirements and release operations.
-*****************
-Coding Guidelines
-*****************
+Extension path
+--------------
-The project follows the `PEP8 `_ coding style. The project uses `setuptools `_ for packaging and distribution. The project uses `Sphinx `_ for documentation. The project uses `pytest `_ for testing. The project uses `Gitflow `_ for branching.
+.. list-table:: Analysis implementation path
+ :class: pq-record-table pq-extension-table
+ :header-rows: 1
+ :widths: 24 38 38
-In order to contribute to the project, it is important to follow the coding style and guidelines used by the project, therefore please read ALL of the following sections carefully.
+ * - Stage
+ - Primary location
+ - Contract
+ * - Scientific method
+ - ``PQAnalysis/analysis//``
+ - Estimator, normalization, units and result shape
+ * - Python interface
+ - ``PQAnalysis/analysis//api.py``
+ - Validated orchestration shared with the CLI
+ * - Command line
+ - ``PQAnalysis/cli/.py``
+ - Arguments and dispatch without duplicate computation
+ * - Scientific output
+ - ``PQAnalysis/analysis/_output_schemas.py``
+ - Stable fields, symbols, units and plot projection
+ * - Evidence
+ - ``tests/analysis//`` and ``tests/data//``
+ - Analytical, independent, parity and end-to-end tests
-*****************
-How to Contribute
-*****************
+.. toctree::
+ :maxdepth: 1
-For any contributor willing to contribute to the project, it is important to understand the branching model used by the project. The project uses the `Gitflow `_ branching model. Pull requests should stay small and reviewer-readable. In order to contribute to the project please follow the following steps:
+ architecture
+ adding-analysis
+ validation
+ release
+Local environment
+-----------------
- #. Fork the project on Github. (not necessary if you are a member of the project)
+Install development, test and documentation dependencies in an isolated
+environment:
- #. Clone your fork locally:
-
- .. code:: bash
+.. code-block:: console
- $ git clone https://github.com/MolarVerse/PQAnalysis.git
+ $ git clone https://github.com/MolarVerse/PQAnalysis.git
+ $ cd PQAnalysis
+ $ python -m venv .venv
+ $ source .venv/bin/activate
+ $ python -m pip install -e ".[dev,test,docs]"
- #. Initialize git flow with the following settings (if not specified default settings are used)
+Quality gates
+-------------
- .. code:: bash
+``pytest.sh`` runs the suite with debug runtime type checking and repeats it
+with release settings:
- [master] main
- [develop] dev
- [version tag prefix] v
+.. code-block:: console
- #. Create a feature branch for your contribution:
-
- .. code:: bash
+ $ bash pytest.sh
+ $ bash pytest.sh tests/analysis/rdf -q
- $ git flow feature start
+Run Pylint against the package and retain a score above the CI threshold of
+9.75:
+.. code-block:: console
- #. Commit your changes to your feature branch and publish your feature branch:
-
- .. code:: bash
+ $ python -m pylint PQAnalysis --persistent n
- $ git add
- $ git commit -m "fix: describe the bug fix"
- $ git flow feature publish
-
- #. Create a pull request on Github.
+Public Python interfaces use NumPy-style docstrings. Document parameters,
+returns, raised exceptions, units and array shapes. Inspect coverage with:
- #. Use a short Conventional Commits title for the pull request, for example ``feat: add a new analysis command`` or ``fix(io): handle missing trajectory data``. This title is validated by CI.
+.. code-block:: console
- #. Once your pull request is approved and all required checks pass, it will be merged into the develop branch. If the pull request is squash merged, use the pull request title as the squash commit message.
+ $ docstr-coverage PQAnalysis
- #. Optional: enable the local commit-message hook for earlier feedback:
-
- .. code:: bash
-
- $ git config core.hooksPath .githooks
-
-*************
Documentation
-*************
-
-Please make sure that all code is well documented. The project uses `Sphinx `_ for documentation. The documentation of this webpage is autogenerated from the docstrings of the implemented code, thus it is important to make sure that all docstrings are correct and informative.
-
-.. attention::
-
- The project uses `numpydoc `_ for docstring formatting. Please make sure that all docstrings are formatted correctly.
-
-In order to install all the dependencies required for building the documentation, use the following command:
-
-.. code:: bash
-
- $ pip install -e ".[docs]" # install the project with the documentation dependencies
-
-To build the documentation, use the following command:
-
-.. code:: bash
-
- $ cd docs
-
- $ make html
-
-In order to view the documentation, open the following file in a web browser:
-
-.. code:: bash
-
- $ open build/html/index.html
-
-For the CI/CD pipeline, the a documentation coverage of 99.9% is required. Please make sure that all implemented features are correctly documented. To evaluate the documentation coverage, use the following command:
-
-.. code:: bash
-
- $ docstr-coverage PQAnalysis
-
-*******
-Testing
-*******
-
-The project uses `pytest `_ for testing. Before creating a pull request, please make sure that all tests pass and ensure a high quality of code coverage. In order to run the tests, use the following command:
+-------------
-.. code:: bash
+Build HTML and check external links with warnings treated as errors:
- $ pip install -e ".[test]" # install the project with the test dependencies
+.. code-block:: console
- $ python -m pytest
+ $ python -m sphinx -E -W --keep-going \
+ -b html docs/source docs/build/html
+ $ python -m sphinx -E -W --keep-going \
+ -b linkcheck docs/source docs/build/linkcheck
-The testing framework will run all tests and provide automatically generated coverage reports. Not only should all tests pass, but the coverage should be as close to 100% as possible. Furthermore, the project automatically uses doctest, so please make sure that all examples included in the doc strings of the implemented features are correct otherwise the tests will fail.
+The API reference is generated from package modules when Sphinx starts. Do not
+hand-edit generated files under ``docs/source/code``. Add public callables to
+:doc:`../reference/functions`, and keep implementation guidance in this
+development section.
-Last, if any additional dependencies are required for testing, please add them to the ``pyproject.toml`` file under the ``[project.optional-dependencies]`` section.
+Executable figures under ``docs/source/_plots`` must be deterministic. Captions
+must identify analytical models, versioned validation fixtures and physical
+benchmark results correctly.
-**********************
-Performance Validation
-**********************
+Pull requests
+-------------
-File-backed VACF, MSD, RDF and momentum analyses use bounded compiled fast
-paths. A batch path must preserve the numeric operation order of its streaming
-fallback and must return to that fallback when the configured memory limit is
-exceeded. Parallel work is restricted to independent lag ranges, frames or
-private integer histograms; floating-point reductions within one legacy result
-must not be reordered.
+Feature and fix pull requests normally target ``dev``. Release pull requests
+merge ``dev`` into ``main``. Use a Conventional Commits title, such as
+``feat: add a new analysis command`` or
+``fix(io): handle missing trajectory data``; CI validates the title. Keep each
+commit scoped and reviewable because multi-commit pull requests may retain
+their individual commits.
-Install the benchmark dependency and run the focused benchmark suite with:
+Enable the optional local commit-message hook with:
-.. code:: bash
+.. code-block:: console
- $ pip install -e ".[test,benchmark]"
- $ pytest -c benchmarks/pytest.ini benchmarks --benchmark-only
+ $ git config core.hooksPath .githooks
-Store a baseline with ``--benchmark-json=baseline.json`` and compare a changed
-branch with ``--benchmark-compare=baseline.json``. Runtime assertions do not
-belong in CI because host load is variable. Every optimization must instead
-pass the compiled and fallback tests plus the relevant fixed-bit legacy oracle
-before its benchmark result is considered.
+Before review, run the focused tests for every modified ownership boundary and
+the relevant strict documentation builds. Pull requests and ``dev`` pushes
+build documentation without deploying it; deployment occurs from ``main``.
diff --git a/docs/source/developerGuide/release.rst b/docs/source/developerGuide/release.rst
new file mode 100644
index 00000000..5000f641
--- /dev/null
+++ b/docs/source/developerGuide/release.rst
@@ -0,0 +1,72 @@
+Release Process
+===============
+
+PQAnalysis uses ``dev`` as the integration branch and releases from ``main``.
+Version strings are derived from Git tags by ``setuptools_scm``; do not edit the
+generated ``PQAnalysis/_version.py`` file.
+
+Release boundary
+----------------
+
+The release workflow matches every pushed tag. A tag push can publish to PyPI
+and TestPyPI, create a GitHub release, sign release artifacts and update
+``CHANGELOG.md`` on ``main``.
+
+.. warning::
+
+ Treat ``git push origin vX.Y.Z`` as the publication action. Deleting a Git
+ tag does not remove an uploaded Python distribution.
+
+Pre-release checks
+------------------
+
+1. Open a release pull request from ``dev`` to ``main``.
+2. Confirm that the release PR contains only reviewed integration changes.
+3. Run both runtime-type-checking configurations with ``bash pytest.sh``.
+4. Build the HTML documentation and link check with warnings as errors.
+5. Confirm the Conventional Commits history produces meaningful release notes.
+6. Verify the intended version is greater than every existing release tag.
+
+The local verification commands are:
+
+.. code-block:: console
+
+ $ git fetch origin --tags
+ $ bash pytest.sh
+ $ python -m sphinx -E -W --keep-going \
+ -b html docs/source docs/build/html
+ $ python -m sphinx -E -W --keep-going \
+ -b linkcheck docs/source docs/build/linkcheck
+
+Tagging
+-------
+
+After the release PR is merged and the ``main`` checks pass, tag the verified
+``main`` commit with the existing ``vMAJOR.MINOR.PATCH`` convention:
+
+.. code-block:: console
+
+ $ git switch main
+ $ git pull --ff-only origin main
+ $ git tag -a vX.Y.Z -m "PQAnalysis vX.Y.Z"
+ $ git push origin vX.Y.Z
+
+Use a major version for incompatible public API or file-contract changes, a
+minor version for backward-compatible functionality and a patch version for
+backward-compatible fixes.
+
+Publication verification
+------------------------
+
+Do not consider the release complete until all of these are confirmed:
+
+* the release workflow succeeded;
+* the new version is available from PyPI;
+* the GitHub release contains the signed distribution artifacts;
+* ``CHANGELOG.md`` was updated on ``main``;
+* the documentation workflow deployed the verified ``main`` build;
+* a clean environment can install the published version and run
+ ``pqanalysis --version``.
+
+Published PyPI files are immutable. Correct a defective release with a new
+patch release rather than moving or reusing its tag.
diff --git a/docs/source/developerGuide/validation.rst b/docs/source/developerGuide/validation.rst
new file mode 100644
index 00000000..2a53a450
--- /dev/null
+++ b/docs/source/developerGuide/validation.rst
@@ -0,0 +1,114 @@
+Scientific Validation
+=====================
+
+Tests should identify what kind of evidence they provide. Agreement with a
+previous implementation establishes compatibility; it does not by itself
+establish physical correctness.
+
+Evidence classes
+----------------
+
+.. list-table:: Validation evidence
+ :class: pq-record-table pq-validation-table
+ :header-rows: 1
+ :widths: 25 35 40
+
+ * - Evidence
+ - Purpose
+ - Suitable reference
+ * - Analytical invariant
+ - Verify definitions and limiting cases
+ - Hand-derived value, conservation law or exactly soluble system
+ * - Independent implementation
+ - Detect shared implementation errors
+ - ASE, a direct NumPy expression or another documented program
+ * - Legacy parity
+ - Preserve established PQ tool behavior
+ - Output generated by the named legacy executable and input
+ * - Kernel parity
+ - Keep optimized and fallback paths equivalent
+ - Direct comparison on identical arrays and parameters
+ * - End-to-end behavior
+ - Verify parsing, orchestration and serialization
+ - CLI/API run with versioned fixtures and expected output
+
+Use more than one evidence class for a new scientific method. A reference file
+produced by the implementation under test is not independent validation.
+
+Reference-data provenance
+-------------------------
+
+Store compact, deterministic fixtures under ``tests/data//``. The test
+module or a README beside the data must record:
+
+* the program and version that generated the reference;
+* the complete source input and relevant options;
+* the physical units and column meanings;
+* any precision loss caused by text serialization;
+* the reason for the selected numerical tolerance.
+
+Do not replace a reference file merely to make a failing test pass. A reference
+change must be reviewable as either a corrected scientific definition, an
+intentional compatibility change or a newly generated independent benchmark.
+
+Numerical tolerances
+--------------------
+
+Prefer exact equality for integer counts, field names and deterministic text.
+For floating-point data, choose ``rtol`` and ``atol`` from the numerical method,
+reference precision and expected magnitude. Record relaxed tolerances next to
+the assertion. Do not use one package-wide tolerance for observables with
+different scales.
+
+Compatibility kernels that claim fixed-bit legacy behavior must preserve the
+legacy operation order and pass the corresponding exact oracle. General
+kernels may accumulate values in a different order from NumPy fallbacks; their
+parity tolerance should cover only the expected summation difference.
+
+Test commands
+-------------
+
+``pytest.sh`` runs the requested tests once with debug runtime type checking and
+once with release settings:
+
+.. code-block:: console
+
+ $ bash pytest.sh tests/analysis/rdf -q
+ $ bash pytest.sh tests/analysis/msd -q
+ $ bash pytest.sh tests/analysis/vacf -q
+
+Run the complete suite before review:
+
+.. code-block:: console
+
+ $ bash pytest.sh
+
+Performance validation
+----------------------
+
+File-backed VACF, MSD, RDF and momentum analyses use bounded compiled paths.
+An optimized path must return to its streaming fallback when its memory or
+input-shape requirements are not met. Parallel work may divide independent lag
+ranges or frames, or use private integer histograms. It must not reorder a
+floating-point reduction covered by a fixed-bit compatibility guarantee.
+
+Install the benchmark dependency and run the focused suite with:
+
+.. code-block:: console
+
+ $ python -m pip install -e ".[test,benchmark]"
+ $ pytest -c benchmarks/pytest.ini benchmarks --benchmark-only
+
+Store the parent result with ``--benchmark-json=baseline.json`` and compare a
+changed branch with ``--benchmark-compare=baseline.json``. Record input size,
+selection, window, gap, host and median wall time. Runtime thresholds do not
+belong in CI because runner load is variable; compiled, fallback and fixed-bit
+tests remain mandatory regardless of benchmark results.
+
+Documentation figures
+---------------------
+
+Executable figures under ``docs/source/_plots`` may use an analytic model or a
+versioned validation fixture. Their captions must state which one. A schematic
+must not be presented as simulation output, and a legacy parity fixture must not
+be described as an independent physical benchmark.
diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst
new file mode 100644
index 00000000..955bd345
--- /dev/null
+++ b/docs/source/getting-started.rst
@@ -0,0 +1,80 @@
+Getting Started
+===============
+
+Install PQAnalysis
+------------------
+
+PQAnalysis supports Python 3.12 and newer. Install the current release from
+PyPI:
+
+.. code-block:: console
+
+ $ python -m pip install pqanalysis
+
+Confirm that the command dispatcher and analysis commands are available:
+
+.. code-block:: console
+
+ $ pqanalysis --help
+ $ pqanalysis rdf --help
+
+Run a first analysis
+--------------------
+
+Create ``rdf.in`` beside a PQ trajectory named ``trajectory.xyz``:
+
+.. code-block:: text
+
+ traj_files = trajectory.xyz
+ reference_selection = O
+ target_selection = H
+ delta_r = 0.05
+ out_file = rdf.dat
+
+Run the calculation:
+
+.. code-block:: console
+
+ $ pqanalysis rdf rdf.in
+
+``rdf.dat`` contains the bin-center distance, radial distribution function,
+cumulative coordination number, density-normalized shell population and
+ideal-gas pair-count residual. Its commented metadata header records the field
+names, scientific symbols and units. See :ref:`analysis-output-rdf` for the
+exact definitions.
+
+Choose output formats
+---------------------
+
+The output filename selects the table format. ``.csv`` and ``.tsv`` open
+directly in spreadsheet software, ``.xvg`` opens in xmgrace, and any other
+extension uses native PQAnalysis text.
+
+Additional outputs do not require another analysis run:
+
+.. code-block:: console
+
+ $ pqanalysis rdf rdf.in \
+ --export rdf.csv \
+ --export rdf.tsv \
+ --export rdf.xvg
+
+Existing analysis tables can be converted later:
+
+.. code-block:: console
+
+ $ pqanalysis convert rdf.dat -o rdf.csv -o rdf.xvg
+
+PQAnalysis refuses to overwrite an existing output unless replacement is
+requested explicitly with ``--mode o``.
+
+Next steps
+----------
+
+* :doc:`analyses/index` compares the physical observables and required data.
+* :doc:`reference/functions` lists public Python workflows and numerical
+ functions.
+* :doc:`reference/cli` lists commands and options.
+* :doc:`reference/api` identifies the Python analysis and I/O entry points.
+* :doc:`developerGuide/developerGuide` documents architecture, extension and
+ validation.
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 1580bbe1..24885938 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -1,23 +1,99 @@
-.. PQAnalysis documentation master file, created by
- sphinx-quickstart on Mon Oct 23 16:52:21 2023.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-##########
PQAnalysis
-##########
+==========
+
+PQAnalysis provides command-line and Python tools for quantitative analysis of
+PQ molecular-dynamics simulations. It reads structures, trajectories,
+velocities and Hessians, then produces documented scientific tables for
+structural, transport and vibrational observables.
+
+:doc:`Get started ` | :doc:`Choose an analysis ` |
+:doc:`Python functions ` | :doc:`Develop PQAnalysis `
+
+Quick start
+-----------
+
+PQAnalysis requires Python 3.12 or newer.
+
+.. code-block:: console
+
+ $ python -m pip install pqanalysis
+ $ pqanalysis rdf rdf.in
+
+The output filename in an analysis input file selects native text, CSV, TSV or
+XVG. Repeat ``--export`` to write several formats in the same run.
+
+.. code-block:: console
+
+ $ pqanalysis rdf rdf.in --export rdf.csv --export rdf.xvg
+
+Analysis methods
+----------------
+
+.. list-table:: Implemented observables
+ :class: pq-record-table pq-method-table
+ :header-rows: 1
+ :widths: 24 38 38
+
+ * - Method
+ - Required data
+ - Reported quantity
+ * - :doc:`Radial distribution `
+ - Positions and periodic cell
+ - :math:`g_{AB}(r)` and cumulative coordination
+ * - :doc:`Mean square displacement `
+ - Positions and periodic cell
+ - Cartesian MSD and diffusion fits
+ * - :doc:`VACF and spectra `
+ - Velocities, sampling interval and optional charges
+ - Normalized correlation and wavenumber spectrum
+ * - :doc:`Vibrational analysis `
+ - Structure, masses and Cartesian Hessian
+ - Normal modes, wavenumbers and optional IR intensities
+ * - :doc:`Momentum diagnostic `
+ - Velocities and atomic masses
+ - Frame-resolved total linear momentum
+
+Python interface
+----------------
+
+The public analysis functions use the same validated input readers and
+scientific kernels as the command line:
+
+.. code-block:: python
+
+ from PQAnalysis.analysis import rdf, read_analysis_table
+
+ rdf("rdf.in", export_files=["rdf.csv"])
+ table = read_analysis_table("rdf.csv")
+
+The :doc:`function index ` groups analysis workflows,
+numerical methods, scientific-table operations and simulation-file I/O by
+task.
+
+Development
+-----------
+
+New methods follow a documented path from estimator and validation evidence to
+the public API, CLI and schema-backed output. See
+:doc:`Adding an Analysis ` for the required
+implementation steps and :doc:`Architecture ` for
+package ownership boundaries.
.. toctree::
:hidden:
- :maxdepth: -1
-
- userGuide/userGuide
- developerGuide/developerGuide
- code/PQAnalysis.rst
+ :maxdepth: 2
+ :caption: Use PQAnalysis
-Welcome to PQAnalysis's documentation!
-======================================
+ getting-started
+ analyses/index
+ Python Functions
+ Command Line
+ Files and Formats
+ Package Reference
-:ref:`userGuide`
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+ :caption: Develop PQAnalysis
-:ref:`developerGuide`
+ developerGuide/developerGuide
diff --git a/docs/source/reference/api.rst b/docs/source/reference/api.rst
new file mode 100644
index 00000000..d5f81ec9
--- /dev/null
+++ b/docs/source/reference/api.rst
@@ -0,0 +1,55 @@
+Package Reference
+=================
+
+The :doc:`functions` page documents callable analysis, numerical and I/O
+interfaces. The tables below map principal data types to the generated module
+reference.
+
+Core types
+----------
+
+.. list-table:: Principal data types
+ :class: pq-record-table pq-types-table
+ :header-rows: 1
+ :widths: 34 66
+
+ * - Type
+ - Role
+ * - :class:`PQAnalysis.atomic_system.AtomicSystem`
+ - One structure with coordinates, cell and topology
+ * - :class:`PQAnalysis.traj.Trajectory`
+ - Ordered atomic-system frames
+ * - :class:`PQAnalysis.topology.Topology`
+ - Atoms, residues, molecular identity and bonded topology
+ * - :class:`PQAnalysis.topology.Selection`
+ - Atom selection parser and index resolution
+ * - :class:`PQAnalysis.analysis.output.AnalysisTable`
+ - Numerical analysis data coupled to scientific column metadata
+ * - :class:`PQAnalysis.analysis.output.AnalysisSchema`
+ - Stable fields, symbols, units and plot defaults
+
+Package areas
+-------------
+
+.. list-table:: Generated package reference
+ :class: pq-record-table pq-package-reference-table
+ :header-rows: 1
+ :widths: 34 66
+
+ * - Package
+ - Scope
+ * - :doc:`Analysis <../code/PQAnalysis.analysis>`
+ - Calculations, input readers, result models and output writers
+ * - :doc:`Input and output <../code/PQAnalysis.io>`
+ - Trajectory, restart, topology and simulation-file readers and writers
+ * - :doc:`Trajectories <../code/PQAnalysis.traj>`
+ - Engine formats, trajectory containers and high-level operations
+ * - :doc:`Atomic systems <../code/PQAnalysis.atomic_system>`
+ - Coordinates, cells and topology-bearing systems
+ * - :doc:`Topology and selection <../code/PQAnalysis.topology>`
+ - Selections, residues, bonded topology and SHAKE definitions
+ * - :doc:`Package index <../code/PQAnalysis>`
+ - Generated module hierarchy
+
+Use the analysis guides for physical conventions, the function index for
+callable workflows and the generated reference for implementation details.
diff --git a/docs/source/reference/cli.rst b/docs/source/reference/cli.rst
new file mode 100644
index 00000000..5fa0688d
--- /dev/null
+++ b/docs/source/reference/cli.rst
@@ -0,0 +1,96 @@
+Command-Line Reference
+======================
+
+``pqanalysis`` dispatches all supported commands from one executable. Every
+subcommand also provides local help:
+
+.. code-block:: console
+
+ $ pqanalysis --help
+ $ pqanalysis rdf --help
+
+Analysis commands
+-----------------
+
+.. list-table:: Analysis commands
+ :class: pq-command-table
+ :header-rows: 1
+ :widths: 24 50 26
+
+ * - Command
+ - Purpose
+ - Primary input
+ * - :ref:`rdf `
+ - Radial distribution and cumulative coordination
+ - Input file
+ * - :ref:`msd `
+ - Mean square displacement and diffusion fits
+ - Input file
+ * - :ref:`vacf `
+ - Velocity or charge-flux correlation and spectra
+ - Input file
+ * - :ref:`vibrations `
+ - Hessian normal modes and optional IR intensities
+ - Input file
+ * - :ref:`check_momentum `
+ - Total linear momentum per velocity frame
+ - Trajectory files
+ * - :ref:`build_spectrum `
+ - Gaussian or Lorentzian broadening of discrete lines
+ - Line table
+
+Analysis commands accept ``--export FILE`` where applicable. Repeat the option
+to produce several output formats without repeating the calculation.
+
+Table conversion
+----------------
+
+``pqanalysis convert`` reads native, CSV, TSV or PQAnalysis-generated XVG
+analysis tables and writes one or more target formats. It preserves complete
+schemas and hidden XVG data sets. See
+:doc:`the generated option reference <../code/PQAnalysis.cli.convert>`.
+
+Structure and trajectory conversion
+-----------------------------------
+
+.. list-table:: Structure and trajectory commands
+ :class: pq-command-table
+ :header-rows: 1
+ :widths: 28 72
+
+ * - Command
+ - Purpose
+ * - :ref:`rst2xyz `
+ - Convert a PQ restart structure to XYZ
+ * - :ref:`xyz2rst `
+ - Convert XYZ coordinates to a PQ restart structure
+ * - :ref:`xyz2gen `
+ - Convert XYZ to DFTB+ GEN
+ * - :ref:`gen2xyz `
+ - Convert DFTB+ GEN to XYZ
+ * - :ref:`traj2box `
+ - Extract periodic box data from trajectories
+ * - :ref:`traj2extxyz `
+ - Write extended XYZ trajectories with selected metadata
+ * - :ref:`traj2qmcfc `
+ - Convert trajectories to QMCFC conventions
+
+Simulation-support commands
+---------------------------
+
+.. list-table:: Simulation-support commands
+ :class: pq-command-table
+ :header-rows: 1
+ :widths: 28 72
+
+ * - Command
+ - Purpose
+ * - :ref:`continue_input `
+ - Continue indexed PQ or QMCFC input/output sequences
+ * - :ref:`add_molecules `
+ - Add molecular structures to an existing system
+ * - :ref:`build_nep_traj `
+ - Assemble a trajectory from nudged-elastic-band data
+
+Commands refuse unsafe output replacement by default. Consult each generated
+reference page for its supported writing modes and format-specific options.
diff --git a/docs/source/reference/functions.rst b/docs/source/reference/functions.rst
new file mode 100644
index 00000000..00d6e8da
--- /dev/null
+++ b/docs/source/reference/functions.rst
@@ -0,0 +1,84 @@
+.. _function-index:
+
+Function Index
+==============
+
+Callable Python interfaces are grouped below by task. Analysis wrappers accept
+the same input files as their command-line counterparts. Lower-level numerical
+functions operate on arrays or PQAnalysis objects and do not parse command-line
+arguments.
+
+Analysis workflows
+------------------
+
+.. autosummary::
+
+ ~PQAnalysis.analysis.rdf.api.rdf
+ ~PQAnalysis.analysis.msd.api.msd
+ ~PQAnalysis.analysis.vacf.api.vacf
+ ~PQAnalysis.analysis.vibrational.api.vibrations
+ ~PQAnalysis.analysis.momentum.api.check_momentum
+ ~PQAnalysis.analysis.spectrum_broadening.api.build_spectrum
+
+Numerical methods
+-----------------
+
+.. autosummary::
+
+ ~PQAnalysis.analysis.vacf.spectrum.apodization_window
+ ~PQAnalysis.analysis.vacf.spectrum.vacf_spectrum
+ ~PQAnalysis.analysis.spectrum_broadening.spectrum_broadening.alpha_from_fwhm
+ ~PQAnalysis.analysis.spectrum_broadening.spectrum_broadening.fwhm_from_alpha
+ ~PQAnalysis.analysis.spectrum_broadening.spectrum_broadening.wavenumber_grid
+ ~PQAnalysis.analysis.spectrum_broadening.spectrum_broadening.broaden
+ ~PQAnalysis.analysis.vibrational.vibrational_analysis.calculate
+ ~PQAnalysis.analysis.vibrational.vibrational_analysis.read_hessian_file
+ ~PQAnalysis.analysis.vibrational.vibrational_analysis.select_mode_indices
+
+Analysis tables
+---------------
+
+.. autosummary::
+
+ ~PQAnalysis.analysis.output.infer_output_format
+ ~PQAnalysis.analysis.output.read_analysis_table
+ ~PQAnalysis.analysis.output.write_analysis_table
+ ~PQAnalysis.analysis.output.convert_analysis_output
+
+Structure and trajectory I/O
+----------------------------
+
+.. autosummary::
+
+ ~PQAnalysis.io.traj_file.api.read_trajectory
+ ~PQAnalysis.io.traj_file.api.read_trajectory_generator
+ ~PQAnalysis.io.traj_file.api.write_trajectory
+ ~PQAnalysis.io.traj_file.api.calculate_frames_of_trajectory_file
+ ~PQAnalysis.io.restart_file.api.read_restart_file
+ ~PQAnalysis.io.restart_file.api.write_restart_file
+ ~PQAnalysis.io.gen_file.api.read_gen_file
+ ~PQAnalysis.io.gen_file.api.write_gen_file
+ ~PQAnalysis.io.topology_file.api.read_topology_file
+ ~PQAnalysis.io.topology_file.api.write_topology_file
+ ~PQAnalysis.io.box_reader.read_box
+ ~PQAnalysis.io.optimizer_file_reader.read_optimizer_file
+ ~PQAnalysis.io.write_api.write
+ ~PQAnalysis.io.write_api.write_box
+ ~PQAnalysis.traj.api.check_trajectory_pbc
+ ~PQAnalysis.traj.api.check_trajectory_vacuum
+
+Format conversion
+-----------------
+
+.. autosummary::
+
+ ~PQAnalysis.io.conversion_api.rst2xyz
+ ~PQAnalysis.io.conversion_api.xyz2rst
+ ~PQAnalysis.io.conversion_api.xyz2gen
+ ~PQAnalysis.io.conversion_api.gen2xyz
+ ~PQAnalysis.io.conversion_api.traj2box
+ ~PQAnalysis.io.conversion_api.traj2extxyz
+ ~PQAnalysis.io.conversion_api.traj2qmcfc
+
+See :doc:`api` for classes, enums, exceptions and the generated module
+hierarchy.
diff --git a/docs/source/reference/index.rst b/docs/source/reference/index.rst
new file mode 100644
index 00000000..3507129c
--- /dev/null
+++ b/docs/source/reference/index.rst
@@ -0,0 +1,14 @@
+:orphan:
+
+Reference
+=========
+
+Use the command reference for shell workflows and the Python API reference for
+library integration. Scientific output definitions remain centralized so CLI
+and API users share the same field names, units and normalization conventions.
+
+* :doc:`functions` lists public Python functions by task.
+* :doc:`cli` covers analysis, conversion and simulation-support commands.
+* :doc:`api` maps core classes and generated package modules.
+* :ref:`analysisOutputFiles` specifies table fields, symbols, units and
+ serialization formats.
diff --git a/docs/source/userGuide/analysisOutputFiles.rst b/docs/source/userGuide/analysisOutputFiles.rst
index 3415a52d..d54bcb00 100644
--- a/docs/source/userGuide/analysisOutputFiles.rst
+++ b/docs/source/userGuide/analysisOutputFiles.rst
@@ -1,8 +1,7 @@
.. _analysisOutputFiles:
-#####################
Analysis Output Files
-#####################
+=====================
PQAnalysis analysis commands can write native text, CSV, TSV or XVG tables. The
output filename selects the format:
@@ -27,7 +26,7 @@ output filename selects the format:
- Native PQAnalysis text
- Self-describing scientific data and legacy workflows
-This means that ``out_file table.csv`` in an RDF, MSD, VACF or vibrations input
+This means that ``out_file = table.csv`` in an RDF, MSD, VACF or vibrations input
file writes CSV directly. Names ending in ``.dat``, ``.out``, ``.txt`` or no
extension retain the native format.
@@ -195,7 +194,7 @@ The ideal-gas pair count for the shell is
* - 1
- Bin-center distance
- :math:`r_i = (r_i^- + r_i^+) / 2`
- - Angstrom
+ - Å
* - 2
- Radial distribution function
- :math:`g_i = H_i / E_i`
@@ -208,7 +207,7 @@ The ideal-gas pair count for the shell is
* - 4
- Density-normalized shell population
- :math:`H_i / (\rho_T N_R N_F) = g_i\Delta V_i`
- - Angstrom\ :sup:`3`
+ - ų
* - 5
- Ideal-gas pair-count residual
- :math:`H_i - E_i`; positive values are an excess and negative values
@@ -242,15 +241,15 @@ The ``msd`` command writes the legacy Diffcalc layout to ``out_file``.
* - 2
- :math:`\mathrm{MSD}_x`
- Mean squared displacement along x
- - Angstrom\ :sup:`2`
+ - Ų
* - 3
- :math:`\mathrm{MSD}_y`
- Mean squared displacement along y
- - Angstrom\ :sup:`2`
+ - Ų
* - 4
- :math:`\mathrm{MSD}_z`
- Mean squared displacement along z
- - Angstrom\ :sup:`2`
+ - Ų
The total MSD is the sum of columns 2 through 4. It is returned by the Python
API but is not repeated in the file. If ``time_step`` is provided, multiply
@@ -295,7 +294,7 @@ normalized by their zero-lag value, including charge-weighted correlations.
* - 1
- Wavenumber
- Legacy cosine-transform frequency axis
- - cm\ :sup:`-1`
+ - cm⁻¹
* - 2
- Spectrum amplitude
- Absolute cosine-transform amplitude of the optionally windowed
@@ -340,7 +339,7 @@ output.
* - 1
- Wavenumber
- Regular output grid from ``--min`` to the exclusive ``--max``
- - cm\ :sup:`-1`
+ - cm⁻¹
* - 2
- Broadened intensity
- Sum of the Gaussian or Lorentzian peak-height profiles
@@ -373,7 +372,7 @@ reading the value as float64 preserves the calculated bit pattern.
* - 2
- Scaled momentum norm
- ``scale`` multiplied by :math:`\left|\sum_i m_i\mathbf{v}_i\right|`
- - Set by ``--scale``; default is amu Angstrom fs\ :sup:`-1`
+ - Set by ``--scale``; default is amu·Å·fs⁻¹
.. _analysis-output-vibrations:
@@ -403,15 +402,15 @@ The accompanying ``SYMBOLS`` line provides the Unicode scientific notation.
* - 1
- Signed wavenumber
- Always; negative values represent imaginary modes
- - cm\ :sup:`-1`
+ - cm⁻¹
* - 2
- IR intensity
- Only with partial charges
- - km mol\ :sup:`-1`
+ - km·mol⁻¹
* - 2 or 3
- Force constant
- Always
- - mdyn Angstrom\ :sup:`-1`
+ - mdyn·Å⁻¹
* - 3 or 4
- Reduced mass
- Always
@@ -430,15 +429,15 @@ components.
----------------
One multi-frame XYZ animation named ``-.xyz`` is written per
-selected mode. Each atom row contains species, x, y and z in Angstrom. The XYZ
-comment records the one-based mode number, wavenumber in cm\ :sup:`-1`, frame
+selected mode. Each atom row contains species, x, y and z in Å. The XYZ
+comment records the one-based mode number, wavenumber in cm⁻¹, frame
number and sinusoidal phase.
``modes_file``
--------------
This extended XYZ file contains one image per selected mode. The atom columns
-are species, equilibrium x/y/z coordinates in Angstrom and normalized mode
+are species, equilibrium x/y/z coordinates in Å and normalized mode
x/y/z components. The comment declares
``Properties=species:S:1:pos:R:3:mode:R:3`` and records the one-based mode
number, wavenumber and optional IR intensity.
diff --git a/docs/source/userGuide/inputFile.rst b/docs/source/userGuide/inputFile.rst
index 3ea461cc..164f8525 100644
--- a/docs/source/userGuide/inputFile.rst
+++ b/docs/source/userGuide/inputFile.rst
@@ -1,50 +1,125 @@
.. _inputFile:
-##########
-Input File
-##########
+Analysis Input Files
+====================
-The general parsing of the input file is based on a Lark grammar implementation (For more details see `Lark Grammar `_). Any input file must be based on the following definitions of input key and value pairs:
+RDF, MSD, VACF and vibrational analyses use a compact key-value format parsed
+with `Lark `_. Each analysis documents its
+required and optional keys in the generated command and input-reader reference.
-.. note::
- There are two different types of input key and value pairs. The first type is the key and value pairs that are defined in line seperated by a :code:`=` e.g:
+Inline statements
+-----------------
- .. code-block:: bash
-
- key = value
+An inline statement assigns one value to one key:
- The second type are so called multiline statements where in the first line the key is defined and in the following lines the values assigned to the key. The multiline statements must be closed by an :code:`END` statement. The following example shows a multiline statement:
+.. code-block:: text
- .. code-block:: bash
+ key = value
- key
- value1
- value2
- END
+Several assignments may share a line when separated by commas:
- It is important to note that multiline statements are always parsed as list/array like values. This means, if the documentation of the key states that the value is not a list or array, an inlined statement must be used.
+.. code-block:: text
-.. note::
- In general, all keys are case-insensitive as well as the closing statement :code:`END` of a multiline statement. The values are case-sensitive. Furthermore, all keys and values are stripped from leading and trailing whitespaces and :code:`#` can be used to include comments (including inline comments). Inline statements using :code:`key = value` can also be used multiple times in one line separated by a :code:`,` to define multiple key and value pairs in one line e.g.:
+ window = 1000, gap = 10, time_step = 0.001
- .. code-block:: bash
+Use separate lines for scientific input files unless a compact generated file
+is required; one assignment per line is easier to review and diff.
- key1 = value1, key2 = value2
+Multiline lists
+---------------
-.. note::
- The values are read as strings and are converted to the correct type based on the documentation of the key (if possible). In general, the user should not worry about the type of the value as the parser will try to convert the value to the correct type. If the conversion fails, an error will be raised. The following examples show the conversion of the values:
+A key followed by values on subsequent lines creates a list. Terminate the
+list with ``END``:
- * :code:`True` and :code:`False` are converted to :code:`bool` (case-insensitive)
- * :code:`1` is converted to :code:`int` following possible conversions to :code:`float`
- * :code:`1.0` is converted to :code:`float`
- * :code:`any-kind-of_string` is converted to :code:`str`
- * :code:`[1, 2, 3]` is converted to :code:`list` following possible (all values have to be of the same type)
- * :code:`1..4` is converted to :code:`range` range(1, 4)
- * :code:`1-4` same as :code:`1..4`
- * :code:`1..3..10` is converted to :code:`range` range(1, 10, 3), please note that the step size is always the middle value in contrast to the python syntax
- * :code:`1-3-10` same as :code:`1..3..10`
- * :code:`file_0*.text` is treated as a list of files matching the pattern (For more details see the `glob package `_)
+.. code-block:: text
+ traj_files
+ trajectory-001.xyz
+ trajectory-002.xyz
+ trajectory-003.xyz
+ END
+Multiline syntax always produces a list-like value. Use inline syntax for keys
+that accept only a scalar.
+Comments and case
+-----------------
+Keys are case-insensitive. The closing ``END`` token must be uppercase. Values
+remain case-sensitive because they may contain filenames or selection
+expressions. Leading and trailing whitespace is ignored. ``#`` starts a
+comment, including at the end of a statement:
+
+.. code-block:: text
+
+ target_selection = O # oxygen atoms
+
+Value conversion
+----------------
+
+The parser converts strings to the type required by each documented key.
+Common forms include:
+
+.. list-table:: Input value forms
+ :header-rows: 1
+ :widths: 30 30 40
+
+ * - Input
+ - Parsed form
+ - Notes
+ * - ``True`` or ``False``
+ - Boolean
+ - Case-insensitive
+ * - ``1``
+ - Integer
+ - May also satisfy a real-valued key
+ * - ``1.0``
+ - Floating-point number
+ - Scientific notation is accepted where numeric keys permit it
+ * - ``[1, 2, 3]``
+ - List
+ - Elements must have compatible types
+ * - ``1..4`` or ``1-4``
+ - ``range(1, 4)``
+ - The stop value follows Python's exclusive convention
+ * - ``1..3..10`` or ``1-3-10``
+ - ``range(1, 10, 3)``
+ - The middle value is the step
+ * - ``frame-*.xyz``
+ - Matching file list
+ - Expanded with Python glob semantics
+
+Filenames
+---------
+
+Relative filenames are interpreted from the command's working directory. The
+ordinary filename grammar accepts letters, digits, ``_``, ``-`` and ``.``;
+``*`` provides glob matching. For a portable analysis directory, keep the
+input file and its referenced data together and run the command from that
+directory.
+
+Complete example
+----------------
+
+.. code-block:: text
+
+ # oxygen-hydrogen radial distribution
+ traj_files
+ run-001.xyz
+ run-002.xyz
+ END
+
+ reference_selection = O
+ target_selection = H
+ delta_r = 0.05
+ r_max = 8.0
+ out_file = rdf.dat
+
+Run it with:
+
+.. code-block:: console
+
+ $ pqanalysis rdf rdf.in
+
+See :doc:`../analyses/index` for analysis-specific examples and
+:doc:`analysisOutputFiles` for output formats and scientific schemas.
diff --git a/docs/source/userGuide/userGuide.rst b/docs/source/userGuide/userGuide.rst
index e3ed1575..bfec87c7 100644
--- a/docs/source/userGuide/userGuide.rst
+++ b/docs/source/userGuide/userGuide.rst
@@ -1,168 +1,16 @@
+:orphan:
+
.. _userGuide:
-##########
User Guide
-##########
-
-.. toctree::
- :hidden:
- :maxdepth: 1
-
- inputFile
- analysisOutputFiles
-
-Command Line Interface
-======================
-
-The PQAnalysis package does not only provide an API but also a number of different command line tools. These tools can be categorized into two groups primary groups: pure command line tools and tools that are based on an input file.
-
-Input file based tools
-----------------------
-
-For more details on the grammar and syntax of the input file see :ref:`inputFile`.
-For the columns, units and normalization conventions of analysis output files,
-see :ref:`analysisOutputFiles`.
-
-- :ref:`rdf`
-- :ref:`msd`
-- :ref:`vacf`
-- :ref:`vibrations`
-
-RDF input files
-^^^^^^^^^^^^^^^
-
-Basic RDF calculations only need a trajectory, selections, bin settings,
-and an output file. A restart file is not required for this case:
-
-.. code-block:: text
-
- reference_selection = H
- target_selection = O
- delta_r = 0.05
- out_file = rdf.out
- traj_files = trajectory.xyz
-
-For a file-backed periodic orthorhombic trajectory, this minimal form with
-``delta_r`` alone and the default ``r_min = 0`` uses the legacy-compatible
-RDF path. Coordinates are parsed directly as float64, while ``delta_r`` is
-represented as float32 as it was by the legacy C input reader. Histogram
-binning and all five output columns preserve the legacy arithmetic order.
-Explicit ``r_max`` or ``n_bins`` values, triclinic cells, vacuum trajectories
-and intra-molecular exclusion use the general PQAnalysis RDF definition.
-
-Restart files and moldescriptor files are only needed when the calculation
-requires molecular topology information. For example,
-:code:`no_intra_molecular = True` excludes pairs from the same molecule.
-If the files are not given explicitly, PQAnalysis tries to infer
-:code:`trajectory.rst` from :code:`trajectory.xyz` and
-:code:`moldescriptor.dat` from the trajectory directory:
-
-.. code-block:: text
-
- reference_selection = H
- target_selection = O
- delta_r = 0.05
- out_file = rdf_inter.out
- traj_files = trajectory.xyz
- no_intra_molecular = True
-
-Explicit :code:`restart_file` and :code:`moldescriptor_file` values are used
-as-is. If both files are given and :code:`no_intra_molecular` is omitted,
-:code:`no_intra_molecular` defaults to :code:`True`. If
-:code:`no_intra_molecular` is set to :code:`False`, intra molecular pairs are
-included. Inferred and defaulted values are written to the normal PQAnalysis
-log output.
-
-Vibrational analysis input files
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Vibrational analyses use a structure file and a Cartesian Hessian matrix. The Hessian can be generated by a PQ ``mm-hessian`` run.
-
-.. code-block:: text
-
- structure_file = structure.rst
- hessian_file = hessian.dat
- moldescriptor_file = moldescriptor.dat
- out_file = wavenumbers.dat
- normal_modes_file = normal_modes.dat
- modes_prefix = mode
- modes_file = modes.xyz
- modes = positive
- modes_frames = 30
- modes_amplitude = 0.25
- modes_threshold = 1.0e-6
- unit = kcal
- hessian_sign = auto
-
-The ``moldescriptor_file`` key is optional, but IR intensities require partial charges. ``unit`` accepts ``kcal``, ``hartree`` and ``ev``. ``hessian_sign = auto`` lets PQAnalysis choose the sign convention that gives the larger number of non-negative vibrational modes.
-
-Mode visualization is optional. ``modes_prefix`` writes one sinusoidal multi-frame XYZ animation per selected mode, for example ``mode-6.xyz``. ``modes_file`` writes one extended XYZ file with mode vectors and metadata, similar to ASE/Jmol vibration output. ``modes`` accepts ``all``, ``nonzero``, ``positive``, one mode number, a list of mode numbers or a range. Explicit mode numbers are one-based. ``modes_frames`` controls animation frames, ``modes_amplitude`` controls fixed-amplitude displacement in Angstrom, and ``modes_threshold`` filters named mode selections in ``cm-1``. ``modes_temperature`` can be used instead for ASE-style energy-scaled animations.
-
-MSD input files
-^^^^^^^^^^^^^^^
-
-Mean square displacement analyses compute the multiple-time-origin MSD of a
-selected atom set with periodic-image unwrapping. If ``time_step`` (in ps) is
-given, the self-diffusion coefficient is obtained from an Einstein-relation
-fit over the trailing ``fit_window`` points and reported in the log output in
-m\ :sup:`2`/s:
-
-.. code-block:: text
-
- traj_files = trajectory.xyz
- target_selection = O
- out_file = msd.dat
- window = 1000
- gap = 10
- time_step = 0.001
- fit_window = 200
-
-The output file contains the frame lag and the per-axis MSD in Angstrom
-squared, matching the format of the legacy Diffcalc tool. ``window`` must be
-divisible by ``gap``.
-
-VACF input files
-^^^^^^^^^^^^^^^^
-
-Velocity autocorrelation analyses read a velocity trajectory (``.vel``) and
-compute the normalized VACF; with ``spectrum_file`` set, the windowed cosine
-transform yields a vibrational power spectrum in cm\ :sup:`-1`:
-
-.. code-block:: text
-
- traj_files = trajectory.vel
- target_selection = all
- out_file = vacf.dat
- time_step = 0.001
- window = 2500
- gap = 5
- spectrum_file = spectrum.dat
- ftsize = 5000
- window_function = exponential
- window_param = 4.0
- window_start = 0.0
- window_stop = 1.0
-
-Setting ``charge_file`` (static charges) or ``charge_files`` (a charge
-trajectory read in lockstep) switches to the charge-flux autocorrelation
-q\ :sub:`i`\ v\ :sub:`i`, whose spectrum approximates an infrared spectrum.
-``window_function`` accepts ``exponential``, ``hann`` and ``blackman``;
-``method = fft`` selects a faster dense-origin estimator instead of the
-legacy-exact sliding-origin one.
-
-Pure command line tools
------------------------
+==========
-- :ref:`build_spectrum`
-- :ref:`check_momentum`
-- :ref:`continue_input`
-- :ref:`rst2xyz`
-- :ref:`traj2extxyz`
-- :ref:`traj2qmcfc`
-- :ref:`traj2box`
+The former user-guide URL is retained for compatibility. Current documentation
+is organized by task:
-:ref:`check_momentum` parses file-backed velocity
-trajectories directly in float64 and preserves the atom-order arithmetic of
-the legacy ``equipartition.jl`` tool. This resolves conserved-momentum
-residuals at the float64 noise floor instead of the former float32 parsing
-floor.
+* :doc:`../getting-started`: installation, first RDF calculation and outputs
+* :doc:`../analyses/index`: estimators, assumptions and interpretation
+* :doc:`../reference/functions`: public Python functions by task
+* :doc:`../reference/cli`: command-line interfaces
+* :doc:`../data/index`: input grammar, trajectories and scientific tables
+* :doc:`../developerGuide/developerGuide`: architecture and contribution work
diff --git a/pyproject.toml b/pyproject.toml
index 797b9539..3990becf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -47,9 +47,11 @@ dev = [
"yapf",
]
docs = [
- "sphinx>=7,<9",
+ "furo>=2024.8.6,<2027",
+ "matplotlib>=3.9,<4",
+ "sphinx>=8,<9",
+ "sphinx-copybutton>=0.5,<1",
"sphinx-sitemap",
- "sphinx-rtd-theme",
"breathe",
"myst-parser",
"better-apidoc",
@@ -98,4 +100,6 @@ gen2xyz = "PQAnalysis.cli.gen2xyz:main"
[project.urls]
"Homepage" = "https://github.com/MolarVerse/PQAnalysis"
+"Documentation" = "https://molarverse.github.io/PQAnalysis/"
+"Repository" = "https://github.com/MolarVerse/PQAnalysis"
"PQ" = "https://github.com/MolarVerse/PQ"