Skip to content

Refactor/ase first - #61

Merged
srmnitc merged 45 commits into
mainfrom
refactor/ase-first
Apr 30, 2026
Merged

Refactor/ase first#61
srmnitc merged 45 commits into
mainfrom
refactor/ase-first

Conversation

@srmnitc

@srmnitc srmnitc commented Feb 28, 2026

Copy link
Copy Markdown
Member

This pull request modernizes and improves the documentation, packaging, and CI workflows for the pyscal3 project. The most significant changes include a complete overhaul of the GitHub Actions workflows for testing and deployment, a reorganization and update of the documentation structure and content, and improvements to packaging and distribution. These changes enhance maintainability, usability, and clarity for both users and developers.

CI/CD Workflow Improvements

  • The .github/workflows/deploy.yml file was refactored to build wheels for all major platforms (including Windows and new macOS runners), use the latest GitHub Actions and cibuildwheel versions, and switch to OIDC-based trusted publishing to PyPI, removing the need for API tokens. Publishing is now triggered only on GitHub Releases.
  • The .github/workflows/testing.yml workflow now tests across more Python versions (3.10–3.13) and platforms, uses modern Python setup actions, and uploads coverage only for a specific matrix configuration. Concurrency controls were added to prevent overlapping runs.

Documentation Restructuring and Updates

  • The documentation navigation (_toc.yml) was reorganized to streamline and clarify example topics, with new ordering and grouping, and an API reference was added.
  • The main documentation index (docs/index.rst) was rewritten to reflect the new pyscal3 branding, update badges, and provide a concise library description.
  • The examples overview (docs/examples.md) was rewritten for clarity, with updated descriptions and links to match the new example structure. [1] [2] [3]
  • A new API reference (docs/api.md) was added, providing documentation for all public functions and classes using Sphinx autodoc directives.
  • The installation guide (docs/gettingstarted.md) was updated to clarify Windows support and simplify conda instructions. [1] [2]
  • Sphinx configuration in _config.yml was updated to enable autodoc, autosummary, and Napoleon extensions for better API documentation.

Packaging and Distribution

  • The MANIFEST.in file was updated to include additional source and header files needed for distribution, including C++ sources and headers.

Repository Metadata

  • The repository URL and book path in _config.yml were updated to point to the correct pyscal3 repository.

atoms_to_dict() now passes numpy arrays directly instead of converting
to Python lists via .tolist(). pybind11 handles the conversion
automatically. This eliminates ~27ms of overhead per descriptor call
for a 4000-atom system.

Before: atoms_to_dict = 27ms, steinhardt q6 FCC 4000 = 36ms
After:  atoms_to_dict = 0.01ms, steinhardt q6 FCC 4000 = 18ms

All descriptors now match or beat the old v3.3.1 API performance.
…order to C++

Moved 4 pure Python descriptor methods to C++ (puremath.cpp):
- chi_params:        22-25x faster (523ms → 23ms at 4000 atoms)
- voronoi_vector:    24-25x faster (468ms → 19ms at 4000 atoms)
- short_range_order: 9x faster (21ms → 2.3ms at 4000 atoms)
- angular_criteria:  2.8x faster (52ms → 18ms at 4000 atoms)

Also fixes a floating-point bug where cosines slightly below -1.0
(from FP rounding) were silently dropped by numpy histogram.
Cosines are now properly clamped to [-1, 1].
- Replace 16 legacy notebooks with 13 focused tutorials
- Notebooks cover full API: neighbors, Steinhardt, CNA, Voronoi,
  disorder, angular/chi, centrosymmetry, entropy, SRO, solid/liquid
  clustering, and trajectory module
- Remove unused data files (Al.eam.fs, Mo.set, conf.bcc, etc.)
- Remove scratch notebooks from repo root
- All 13 notebooks execute cleanly; all 54 tests pass
- Create docs/api.md with autodoc for all 16 public functions + Trajectory
- Enable sphinx.ext.autodoc, autosummary, napoleon in _config.yml
- Fix _config.yml repository URL (was pointing to wrong repo)
- Fix _toc.yml to reference 13 new notebooks (was pointing to 16 deleted ones)
- Rewrite docs/intro.md: ASE-first description, quick start, feature list
- Update docs/index.rst: clean badges, new toctree
- Update docs/examples.md: correct notebook links and descriptions
- Update docs/methods_landing.md: correct example links
- Update docs/gettingstarted.md: Windows support, simplified install
- Add Returns section to find_neighbors docstring (documents stored keys)
- Improve Trajectory/Timeslice class docstrings with full numpydoc
- Remove stale pyscal.rst, modules.rst, prologue/api.md
…-arch tests

- Tests used relative paths (e.g. 'tests/files/...') which fail when
  cibuildwheel runs pytest from a temp directory. Fixed all 4 test files
  to resolve paths relative to __file__.
- Removed deprecated macos-13 runner from deploy.yml matrix.
- Added test-skip for x86_64 on macOS (arm64 runner can't run x86_64)
  and aarch64 on Linux (QEMU too slow for scipy/spglib).
manylinux2014 (CentOS 7) has old pip/SSL that fails to install
dependencies like ase/numpy in cibuildwheel test containers.
manylinux_2_28 (AlmaLinux 8) is the recommended image for new projects
and has a modern toolchain.
Building C++ under QEMU emulation takes 30-60 min. ARM Linux users
can install from the source distribution instead.
Implement W_l, W-hat_l (normalized), and neighbor-averaged variants
(Lechner-Dellago) via Wigner 3j symbols using the Racah formula.

C++ (sh.cpp):
  - factorial_int(), wigner3j(l, m1, m2, m3) via Racah formula
  - calculate_w_single(): per-atom W_l and W-hat_l
  - calculate_aw_single(): neighbor-averaged versions

Python (descriptors.py):
  - wigner_w_parameter(atoms, l, averaged, normalized)

Tests (12 new): FCC/BCC/HCP match Steinhardt 1983 Table I values:
  FCC W-hat_6 = -0.01316, BCC W-hat_6 = +0.01316, HCP = -0.01244

References:
  Steinhardt, Nelson & Ronchetti, Phys. Rev. B 28, 784 (1983)
  Lechner & Dellago, J. Chem. Phys. 129, 114707 (2008)
Expose pyscal3.minkowski_parameter(atoms, l, voroexp=1, averaged=False)
which computes Voronoi face-area weighted Steinhardt parameters.

The existing C++ infrastructure already supports weighted Y_lm sums
via the neighborweight array — this function provides a clean API that
automatically performs Voronoi neighbor finding with the requested
face-area exponent and delegates to steinhardt_parameter.

Validated against Mickel et al. (2013) Table I:
  FCC: q4=0.1909, q6=0.5745  (exact match)
  BCC: q4=0.2240, q6=0.5669  (14-neighbor Voronoi tessellation)
  HCP: q4=0.0972, q6=0.4848

Tests: 11 tests covering reference values, multi-l, averaged mode,
voroexp variation, FCC/BCC discrimination, noise sensitivity.

Example notebook: 15_minkowski_structure_metrics.ipynb

Reference: Mickel, Kapfer, Schröder-Turk & Mecke, J. Chem. Phys. 138,
044501 (2013). doi:10.1063/1.4774084
Implement identify_ackland_jones(atoms) which classifies each atom as
FCC (1), HCP (2), BCC (3), ICO (4), or other (0) using a decision tree
on the chi-parameter histogram.

No new C++ code - classification is a vectorized numpy decision tree
operating on the existing chi-parameter output.

Tests: 10 tests, Example notebook: 16_ackland_jones_classification.ipynb

Reference: Ackland and Jones, Phys. Rev. B 73, 054104 (2006)
Add four coordination descriptors as pure Python functions:

- coordination_number(atoms): simple integer neighbor count
- effective_coordination_number(atoms): distance-weighted ECoN (Hoppe 1979)
  using iterative self-consistent weighting exp(1 - (d/d_avg)^6)
- generalized_coordination_number(atoms, cn_max): GCN accounting for
  neighbor coordination (Calle-Vallejo et al., Science 350, 185, 2015)
- local_density(atoms): local atomic number density from mean neighbor
  distance

All functions operate on pre-computed neighbor data (no new C++ code).
Results stored in atoms.arrays with pyscal_ prefix.

Tests: 13 tests. Notebook: 17_coordination_variants.ipynb
New descriptors:
- angular_distribution_function(): histogram of all bond angles
  theta_jik formed by neighbor pairs around each atom
- bond_length_distribution(): histogram of neighbor distances
  (like RDF but without shell-volume normalisation)

Both are pure-Python wrappers (no C++ changes needed):
- ADF reuses chi_params(angles=True) cosine computation
- BLDF uses existing pyscal_neighbordist neighbor data

Validated: FCC peaks at 60/90/120/180 deg; BCC at ~70.5/109.5 deg
13 tests, example notebook 18_angular_bond_distributions.ipynb
New deformation descriptors (require reference configuration):
- atomic_strain(): Green-Lagrange strain tensor per atom
- von_mises_strain(): scalar shear strain invariant
- d2min(): Falk-Langer non-affine squared displacement
- slip_vector(): average neighbor displacement difference

Pure Python implementation (least-squares deformation gradient fit).
14 tests, example notebook 21_deformation_descriptors.ipynb
New Wigner-Seitz cell defect analysis:
- wigner_seitz_analysis(): assign atoms to nearest reference lattice sites
- identify_defect_atoms(): convenience wrapper with defect masks

Features:
- Vacancy detection (empty sites)
- Interstitial detection (multiply-occupied sites)
- Antisite tracking via per_type_occupancies
- Affine mapping for thermal expansion
- PBC via periodic image expansion

Pure Python with scipy.spatial.cKDTree for O(N log N) performance.
17 tests, example notebook 22_wigner_seitz_defects.ipynb
Implements ACE descriptors following Drautz (2019), providing
a unified framework that includes SOAP (nu=2) and bispectrum (nu=3)
as special cases.

Features:
- A-basis (single-particle) coefficient computation
- B-basis rotation-invariant descriptors for nu=1,2,3
- Chebyshev polynomial radial basis with cosine cutoff
- Spherical harmonics angular basis via scipy
- Configurable nmax, lmax, nu_max parameters
- Optional normalization

The descriptor count scales as:
- nu=1: O(nmax) - radial density
- nu=2: O(nmax² × lmax) - power spectrum
- nu=3: O(nmax³ × lmax³) - triplet correlations (limited)
- Replace skipped rotation test with two working variants:
  1. Cubic symmetry operation (90° z rotation, orthogonal cell)
  2. Arbitrary 37° rotation using cluster-in-large-box technique
- Both pass with machine-precision agreement (~1e-15)
- Root cause of original failure: neighbor finder breaks on non-orthogonal
  cells (not an ACE math issue)
- Add cross-validation script (_validate_ace.py) verifying A-basis, nu1,
  nu2 against independent reference implementation (12/12 tests pass)
fix(_bridge): get_box_params now checks cell matrix diagonality instead
of vector orthogonality. A rotated-but-orthogonal cell has perpendicular
vectors (dot product = 0) but non-zero off-diagonal elements, which
requires the triclinic C++ code path. The old check misclassified these
as orthorhombic, producing spurious ghost neighbors in padded systems.

validation: add ACE cross-validation suites
- _validate_ace.py: 12 tests against independent scipy reference
- _validate_ace_lammps.py: 11 LAMMPS property-based tests
- _validate_ace_pyace.py: 9 tests cross-validated against python-ace
  (dimer/trimer coupling, rotation invariance, convention factors,
  FCC symmetry, analytical identities, LAMMPS energy, pyscal3
  self-consistency and rotation invariance)
srmnitc and others added 18 commits April 30, 2026 20:36
# Conflicts:
#	src/pyscal3/descriptors.py

Co-authored-by: srmnitc <12624457+srmnitc@users.noreply.github.com>
…nkowski-ql

# Conflicts:
#	src/pyscal3/__init__.py

Co-authored-by: srmnitc <12624457+srmnitc@users.noreply.github.com>
feat: add Minkowski structure metrics (Voronoi-weighted q_l)
feat: add Ackland-Jones structure classifier
…oordination variants)

Co-authored-by: srmnitc <12624457+srmnitc@users.noreply.github.com>
feat: add coordination number variants (CN, ECoN, GCN, local density)
…d_jones alongside deformation descriptors)

Co-authored-by: srmnitc <12624457+srmnitc@users.noreply.github.com>
…gner-seitz

# Conflicts:
#	src/pyscal3/__init__.py
#	src/pyscal3/descriptors.py

Co-authored-by: srmnitc <12624457+srmnitc@users.noreply.github.com>
@srmnitc
srmnitc merged commit d3ccefa into main Apr 30, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants