Skip to content

Optimization - #6

Open
Ajax23 wants to merge 39 commits into
mainfrom
optimization
Open

Optimization#6
Ajax23 wants to merge 39 commits into
mainfrom
optimization

Conversation

@Ajax23

@Ajax23 Ajax23 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

v1.0.0

Performance

Benchmarked on an Apple Silicon macOS machine (2001-frame cylinder trajectory, benzene,
13 lag times). Timings are the mean of 3 consecutive runs.
Direct comparison against PyPI v0.2.3 is blocked by its CMake/scikit-build dependency,
so microbenchmarks on the core hot paths are provided instead.

Benchmark v1.0.0
Density sampling — serial 0.57 s
Density sampling — parallel 0.10 s
Gyration sampling — serial 0.84 s
Diffusion MC sampling — serial 1.03 s
Diffusion MC sampling — parallel 0.14 s

Parallel sampling is 5–7× faster than the serial equivalent on all platforms.
Earlier builds measured parallel density at 1.33 s and parallel MC at 1.59 s on
macOS because Python 3.12 changed the default multiprocessing start method from
fork to spawn, causing each worker to re-import all heavy dependencies on
every call. The explicit mp.get_context("fork") fix restores expected speedup
(~13× improvement in parallel wall time on macOS versus the spawn baseline).

Hot-path microbenchmarks (2001 frames, isolated from I/O):

Operation Old (Python loops) New (NumPy) Speedup
Centre-of-mass per frame 1.30 s 0.21 s 6.2×
Diffusion-bin merge (parallel combine) 16.3 ms 4.5 ms 3.6×

Key changes driving the speedup:

  • sample.py_sample_helper hot loop: position extraction vectorised as positions[atom_indices] / 10.0 + shift_arr; COM calculated via masses_arr @ pos / sum_masses (NumPy dot product replaces per-atom Python loop); _masses_arr pre-computed once in __init__ as a float64 NumPy array; residue atom index arrays pre-built as np.array([...]) at init time
  • sample.py — parallel diffusion-bin merge: replaced O(bin_num × len_window) triple-nested Python loop with (np.array(a) + np.array(b)).tolist() per key
  • sample.py / mc.py — explicit mp.get_context("fork") on non-Windows platforms; prevents Python 3.12 macOS spawn default from re-importing dependencies per worker (~13× parallel speedup on macOS)
  • density.py — bin volume calculation vectorised: np.pi * plen * (w[1:]**2 - w[:-1]**2) replaces scalar loop; weighted-mean integration uses np.dot + boolean mask
  • diffusion.py — MSD normalisation and bin slope calculation fully vectorised with NumPy arrays; Bessel function series uses np.exp on a coefficient array; removed unused math and itertools imports
  • angle.py / gyration.py — density-weighted normalisation uses np.where(dens != 0, val / dens, 0.0); mean computed with np.mean; mean line rendered via np.full_like
  • adsorption.pynp.sum replaces accumulator loops for reservoir and pore molecule counts
  • mc.pyn_proc parameter no longer shadows the np (NumPy) import; duplicate import numpy as numpy removed; dead commented-out code removed from __init__

Logic fixes

  • mc.pylist_diff_coeff was incorrectly assigned list_diff_profile (per-bin profile data) instead of the actual per-step model coefficients; silently returned wrong values for all MC diffusion runs
  • mc.py / sample.py — function parameter np=0 shadowed the numpy alias, causing AttributeError: 'int' has no attribute 'array' in all parallel and post-merge code paths; renamed to n_proc
  • diffusion.py — residual math.floor / math.ceil calls remained after import math was removed; converted to int(np.floor(...)) / int(np.ceil(...))
  • adsorption.py — reservoir mask ex_width[:-1] (150 elements) applied to ex_bins (151 elements) caused IndexError; mask now uses ex_width directly
  • utils.py / tables.pyfile_to_text and mc_results still accessed the old flat pore structure (pore["box"], pore["diam"], pore["res"], pore["type"]) after the data model was updated to pore["box"]["dimensions"], pore["box"]["res"], pore[shape_id]["diam"]; fixed across all four branches (gyr_bin, diff_bin, dens_bin, mc) and in tables.py
  • utils.pyfile_to_text MC branch used free_energy[0][i][1:] (99 elements) in a DataFrame alongside 100-element arrays; [1:] removed
  • model.py — debug print calls for _d0 and _diff_bin removed
  • model.py — duplicate self._sys_props = {} assignment removed

Tests

  • Converted tests/test_simple.py from unittest to pytest
  • Split into test_unit.py (fast, no trajectory) and test_integration.py (full pipeline)
  • Session-scoped conftest.py fixture runs all trajectory sampling once per session; pre-computes MC output for file_to_text tests
  • New coverage: density.mean() return values; adsorption.calculate() output structure; density/gyration/angle.bins_plot() intent "in"/"ex" and normalised x-axis; diffusion.bins() output structure; MC output key correctness (list_diff_coeff vs list_diff_profile); utils.file_to_text() for all four output types (dens_bin pore, dens_bin box, diff_bin, gyr_bin, mc); YAML round-trip in test_utils
  • bench_compare.py added: standalone script for sampling speed comparison (python tests/bench_compare.py)

Documentation

  • RST source files migrated to MyST Markdown; Sphinx theme updated to furo; API docs via sphinx-autoapi
  • Docs source moved from docsrc/ to docs/; previous built HTML preserved at docs/v_old/
  • Copyright year updated to 2026; DESIGN.md added documenting the red color palette

CI / tooling

  • GitHub Actions: added ruff linting workflow (lint.yml)
  • GitHub Actions: added pip-audit security scan workflow (security.yml)
  • CI matrix updated: Python 3.12–3.13; python_requires bumped to >=3.12
  • Migrated from setup.py + MANIFEST.in to a single pyproject.toml (PEP 517/621); [tool.ruff] config added
  • CI migrated from pip to uv (astral-sh/setup-uv@v5); requirements.txt removed (deps resolved via pyproject.toml)
  • codeql.yml: updated actions from @v2 to @v3; removed irrelevant javascript language scan

Administrative

  • pyproject.toml: version 1.0.0, requires-python = ">=3.12", author email updated, chemfiles unpinned, porems dependency bumped to >=1.0.0; [project.optional-dependencies] dev group added (pytest, pytest-cov)
  • README: updated image paths, Python version, PyPI badge, testing and installation instructions

Ajax23 added 19 commits June 29, 2026 16:23
- Remove old built HTML and RST source tree (docs/ built output, docsrc/)
  replaced by MyST Markdown sources in the new docs/ layout
- Add tests/test_workflow.py: end-to-end PoreMS → PoreSim → PoreAna workflow
  test covering yml structure, PoreSim Box API, and PoreAna density/gyration/
  file_to_text driven by a PoreMS-generated pore.yml
- conftest.py: remove redundant parallel MC run that overwrote the serial
  output; reduce parallel MC comparison to the 7 lag steps actually checked
  by test_parallel_sample; run those serially to avoid spawn-mode pool hangs
  under Python 3.12 macOS pytest
Convert two is_parallel=True calls to is_parallel=False: the angle
sample call in test_sample and the MC().run call in test_diffusion_mc_mc.
Python 3.12 macOS uses spawn-mode multiprocessing which hangs under pytest
for these workloads; serial execution is sufficient for correctness testing.
sample.py and mc.py: replace mp.Pool() with mp.get_context('fork').Pool()
on Linux and macOS. Python 3.12 changed the default start method on macOS
from fork to spawn; spawn causes hang-under-pytest issues due to heavy
re-import overhead per worker. Explicitly requesting fork restores the
original behaviour and is consistent with the Linux cluster environment
where the code primarily runs.

Windows falls back to the default (spawn), which is the only option there.

test_integration.py: MC().run() parallel test kept serial (is_parallel=False)
to avoid fork copy-on-write overhead on large model objects in CI. The
parallel sampling path (sample.sample is_parallel=True) is tested via the
conftest fixture and test_parallel_sample.
Drop Python 3.10 and 3.11 from CI matrix and classifiers; raise
python_requires to >=3.12 in setup.py; update README and changelog.
Replace setup.py and MANIFEST.in with a single pyproject.toml.
Switch all workflows from pip to uv (astral-sh/setup-uv@v5) for
faster installs; remove requirements.txt (deps now declared solely in
pyproject.toml); add explicit [tool.ruff] config to pyproject.toml;
update codeql.yml from @v2 to @V3 and drop irrelevant javascript scan.
Declare pytest and pytest-cov under [project.optional-dependencies] dev
so the full test environment is installed with: pip install -e ".[dev]"
CI updated to use the same extras group.
Remove three commented-out blocks from mc.py: stale save comment,
incomplete txt-export stub, and unfinished radial diffusion algorithm
(including junk debug line). Fix ajax23.github.io URL in further_props.md.
Replace manual venv setup with the dev install step so the README
reflects the current pyproject.toml optional-dependencies setup.
Re-run all sampling benchmarks (3 passes, mean taken); parallel timings
drop from ~1.3-1.6 s to 0.10-0.14 s after the mp.get_context("fork") fix
for Python 3.12 macOS. Document the spawn-vs-fork root cause in changelog.
Administrative section now reflects pyproject.toml and the new
[project.optional-dependencies] dev group.
- Replace all % string formatting with f-strings in diffusion.py print
  statements and utils.py file_to_text output
- Replace range(len(lst)) anti-patterns with direct iteration (len_step,
  lagtime_inverse, D_mean) and zip() for parallel list operations in
  diffusion.py and freeenergy.py
- Add encoding="utf-8" to all text-mode open() calls in utils.py for
  cross-platform correctness
@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

Ajax23 added 6 commits July 11, 2026 06:49
Brings in radial VACF support for cylindrical pores and the mc_profile
is_legend parameter from the vacf branch; resolves all conflicts in
favour of the optimization branch's modernized style.
Remove unused variable assignments from density.py and the VACF
functions in diffusion.py; fix F821 by replacing bare `pa.density`
calls with a local `_density` import; apply ruff format to all three
changed modules; fix invalid LaTeX escape sequence in density ylabel.
Cover _bin_pore radial bins, init_diffusion_vacf guard conditions
(non-trr rejection, wrong direction, mode conflict), and
_diffusion_vacf_data output shape.
- Add _atoms_per_mol to __init__ (total atoms per mol in trajectory)
- Fix _numpy() and VACF _sample_helper block to use
  .reshape(n_res, _atoms_per_mol, 3)[:, self._atoms, :] so partial
  atom selections (e.g. atoms=["C1"]) compute correct CoM instead of
  raising a reshape error
- Fix self._masses → self._masses_arr (ndarray) in _numpy()
- Update benchmark table to Python 3.13 / 16-core measurements
- Add VACF new-feature section to changelog
- sample.py: add self.num_res = n_res in __init__ so VACF and numpy
  sampling paths can reference the residue count without AttributeError
- sample.py: _diffusion_vacf wall mask now uses true radial distance from
  the pore axis for all binning directions; previously it used the binning
  axis coordinate, which is wrong for axial (z) sampling in pore systems
- diffusion.py: integrate_bin_diffusion_vacf guards against zero-density
  bins with np.where; previously divided by zero, producing silent NaN in
  all downstream VACF integrals for unoccupied bins
- mc.py: parallel MC assembly loop now copies fluc_diff_bin and
  fluc_df_bin from each worker; previously only worker-0's values were kept
- tests: add test_vacf_zero_density (unit) and test_diffusion_mc_parallel_keys
  (integration) to cover the fixed paths; extend test_vacf_init to assert
  num_res is set and consistent with the residue map
Ajax23 added 12 commits July 11, 2026 12:20
- sample.py: assign self.num_res in __init__ (AttributeError on all VACF paths)
- sample.py: fix in_wall_mask to use true radial distance for non-radial pore directions
- diffusion.py: prevent NaN in zero-density VACF bins; use np.where guard
- mc.py: assemble fluc_diff_bin/fluc_df_bin from all parallel workers, not just worker 0
- diffusion.py: remove is_error parameter from mc_profile (dead code caused KeyError)
- Remove dead functions: cui, column, num_dens_to_mass_dens, mc_statistics, mc_lag_time
- Restore commented-out radial MC stubs (log_likelihood_radial, setup_bessel_box, mc_fit_radial, mc_profile_radial) for future implementation
- Replace all non-ASCII chars (em dashes, arrows, special symbols) with ASCII equivalents
- Convert docs/diffusion_vacf.rst to MyST Markdown
- Add tests: test_vacf_init num_res check, test_vacf_zero_density, test_diffusion_mc_parallel_keys
- Update benchmark numbers to median of 3 runs
- tables.py: remove unused numpy import (ruff F401)
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