Skip to content

Fix PersistenceLength normalization for sliced run (Issue #5453) - #5459

Open
srikarjy wants to merge 3 commits into
MDAnalysis:developfrom
srikarjy:fix-persistencelength-normalization-5453
Open

Fix PersistenceLength normalization for sliced run (Issue #5453)#5459
srikarjy wants to merge 3 commits into
MDAnalysis:developfrom
srikarjy:fix-persistencelength-normalization-5453

Conversation

@srikarjy

Copy link
Copy Markdown

Fixes #5453

Changes made in this Pull Request:

  • Fixed PersistenceLength._conclude() normalizing bond_autocorrelation using the total trajectory frame count instead of the number of frames actually analyzed, which gave wrong results when run() was called with start/stop/step. Now uses self.n_frames instead. Added a regression test comparing full-trajectory and sliced runs against manually computed expected normalization values.

LLM / AI generated code disclosure

LLMs or other AI-powered tools (beyond simple IDE use cases) were used in this contribution: minimal (an AI assistant pointed me to the relevant line and explained the concept; I made the edits and wrote/ran the tests myself)

PR Checklist

  • Issue raised/referenced?
  • Tests updated/added?
  • Documentation updated/added?
  • package/CHANGELOG file updated?
  • Is your name in package/AUTHORS?
  • I have read and understand the current AI Policy
  • LLM/AI disclosure was updated.

…5453)

PersistenceLength._conclude() normalized bond_autocorrelation using
the total number of frames in the trajectory instead of the number
of frames actually analyzed. This gave wrong results whenever run()
was called with start, stop, or step. Now uses self.n_frames, which
AnalysisBase already tracks for this purpose. Added a regression
test comparing full trajectory and sliced runs against manually
computed normalization values.
@codecov

codecov Bot commented Aug 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.87%. Comparing base (ceb2e3e) to head (aecf9b2).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #5459   +/-   ##
========================================
  Coverage    93.87%   93.87%           
========================================
  Files          182      182           
  Lines        22522    22524    +2     
  Branches      3206     3207    +1     
========================================
+ Hits         21143    21145    +2     
  Misses         917      917           
  Partials       462      462           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@read-the-docs-community

read-the-docs-community Bot commented Aug 15, 2026

Copy link
Copy Markdown

Documentation build overview

📚 MDAnalysis | 🛠️ Build #34124013 | 📁 Comparing aecf9b2 against latest (c882e5c)

  🔍 Preview build  

3 files changed
± index.html
± documentation_pages/analysis/wbridge_analysis.html
± _modules/MDAnalysis/analysis/polymer.html

Comment thread package/CHANGELOG Outdated
* 0.7.5.1

Fixes
* `PersistenceLength._conclude()` now normalizes `results.bond_autocorrelation` using the number of frames actually analyzed instead of the total trajectory frame count, fixing incorrect results when `run()` is called with `start`/`stop`/`step` (Issue #5453).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this CHANGELOG entry been duplicated across so many old versions? Did you see any previous examples where we did this? Did you read your diff carefully?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, and I apologize. I did not review my diff carefully enough before submitting it, which is how the duplication slipped through. I removed the duplicate entries. The entry now appears only once, under the current unreleased 2.11.0 section, consistent with the rest of the file.

norm *= len(chains) * n_sliced_frames
expected = p.results.raw_bond_autocorr / norm

assert_almost_equal(p.results.bond_autocorrelation, expected, decimal=6)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and above -- I don't like that your expected value is derived from the actual class/function under test. That can be a recipe for suppression of breaking changes if both the actual and expected values are mutated in a similar way.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Reworked this so expected is computed independently, directly from atom positions and trajectory frames, without calling into PersistenceLength or touching any of its results. Also extended the test to cover step=, start/stop/step, and frames=[...], since the original only exercised stop=.

* Remove the CHANGELOG entry that was accidentally duplicated across
  about 25 historical release sections. It now appears only once,
  under the current unreleased 2.11.0 header.
* Replace the tautological regression test, where the expected value
  was derived from the class under test, with an independent reference
  implementation that recomputes the normalized bond autocorrelation
  directly from atom positions, without touching PersistenceLength or
  its results.
* Extend test coverage to step=, start/stop/step, frames=[...], and
  the multiprocessing backend, beyond the original stop= only case.
* Add a guard in _conclude() that raises a clear ValueError when a run
  selects zero frames, instead of silently returning a fabricated all
  zero curve before this fix, or crashing with an opaque scipy NaN
  error after this fix, found while testing the above.
* Document, without fixing since it is out of scope for this issue,
  two existing unrelated edge cases found during testing: a one atom
  chain crashing identically before and after this fix, and a numpy
  boolean frames= mask being misread by AnalysisBase computation
  grouping, pinned as xfail for visibility.
@srikarjy

Copy link
Copy Markdown
Author

Thanks again for the review. While reworking the test above, I also found and fixed something not covered by either of your original comments. An empty frame selection, for example run(start=2, stop=2), used to silently return a fabricated all zero curve. It now raises a clear ValueError instead of crashing later inside scipy.optimize.curve_fit with an opaque NaN error. I also extended the regression test to cover step=, start/stop/step, frames=[...], and the multiprocessing backend, since the original test only exercised stop=.

While testing further I found two more things that already exist and are unrelated to this normalization bug, so I documented them with tests rather than fixing them here: a one atom chain crashes identically before and after this fix, and a numpy boolean frames= mask gets misread by AnalysisBase computation grouping logic. I pinned the second one as an xfail test so it stays visible. Happy to open separate issues for either if that is useful.

Thanks again for catching both of the original issues.

…ength-normalization-5453

# Conflicts:
#	package/CHANGELOG
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.

PersistenceLength.run(start=, stop=) rescales entire results.bond_autocorrelation array by n_frames_processed / n_frames_total_trajectory

2 participants