Skip to content

Prolif class implementation - #88

Open
talagayev wants to merge 43 commits into
mainfrom
prolif_class_implementation
Open

talagayev wants to merge 43 commits into
mainfrom
prolif_class_implementation

Conversation

@talagayev

Copy link
Copy Markdown
Collaborator

First draft of the ProLIF implementation into OpenFE Analysis.

Changes made:

  • creation of ProLIFAnalysis class for calculation of interactions using ProLIF
  • Addition of first tests in test_prolif.py, which mainly look into if interactions were calculated sucessfully and protein/ligand conversion from MDAnalysis into ProLIF worked sucessfully.

Currently that is the first draft and PR is to see that the tests work etc.

Some notes:

  • Currently this is just calculating and returning the results in pd.DataFrame format, since that is easy to see if it worked. We can see what the best way is to present the results, be it 2D visualization of the interactions, dataframes or some other format.
  • As was mentioned by Irfan, the recognition and conversion of protein in MDAnalysis is sometimes tricky, which is the case here. So the code currently needs to guess bonds, which it does here:
    universe.select_atoms("protein").guess_bonds(vdwradii=vdwradii)
    But for the vdwradii it wants some values for Cl, Br etc. that we need to provide, so we need to see what defaults we select there.
  • For interactions we need to check what the default option should be, if default == all interactions or some interactions etc. and if the water-bridge interactions are default and if so, what water-bridge interaction order we make default. the current one is 3, which means it can be something like:
    Ligand -- Water -- Water -- Water -- Protein
  • Same goes with ProLIFAnalysis.run() there we can also look into what the defaults have to be, how many cores and if the interactions should be atom based (Ligand with Protein atom) or residue based (Ligand with Protein residue).

This is the first draft, will be updated during further development :)

PS. A small thing I also noticed, since I tested it on different PCs with one being with Windows currently somehow python 3.14 has module loading Issues with rdkit and thus I had to downgrade there to python 3.13.

@codecov

codecov Bot commented Feb 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.72727% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.31%. Comparing base (86b1a79) to head (6235484).

Files with missing lines Patch % Lines
src/openfe_analysis/prolif.py 90.27% 7 Missing ⚠️
src/openfe_analysis/utils/plotting.py 97.36% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #88      +/-   ##
==========================================
- Coverage   98.35%   97.31%   -1.04%     
==========================================
  Files          10       11       +1     
  Lines         487      597     +110     
==========================================
+ Hits          479      581     +102     
- Misses          8       16       +8     

☔ 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.

import prolif as plf


class ProLIFAnalysis:

@IAlibay IAlibay Feb 18, 2026

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.

Can you subclass AnalysisBase here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yup should be doable, will look into it :)

Wanted to wait and see how RMSD will look like to use an identical structure and have this initial PR to have bulletpoints to discuss tomorrow during the meeting and also have an easier overview on what was changed etc. :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We leave it as is for now and will change this when Prolif implements per frame analysis.

@jthorton

Copy link
Copy Markdown
Contributor

I am not sure if this should be handled in the analysis class or in a protocol before running this class but there is an issue when doing this kind of analysis on trajectories from our hybrid topology protocol, where the connectivity of the end state ligands and identities of the atoms can get confused see this example on end stateB when looking at torsions. Should we add an option to provide and rdkit molecule for the ligand and use this to correct the ligand atom group automatically or should we have users fix this beforehand? This will be needed in the torsion analysis as well so a general function which could fix an atom group might be a good idea?

Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py Outdated

@hannahbaumann hannahbaumann left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @talagayev , this looks good! The main part I'm not sure about is the run function, maybe we can discuss that tomorrow!

Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py
Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py
@hannahbaumann hannahbaumann self-assigned this May 21, 2026
This was linked to issues May 21, 2026
Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/prolif.py Outdated
@talagayev
talagayev requested a review from hannahbaumann August 5, 2026 09:52
@talagayev

Copy link
Copy Markdown
Collaborator Author

@hannahbaumann Code would be ready for another round of review :)

@hannahbaumann

Copy link
Copy Markdown
Contributor

pre-commit.ci autofix

@hannahbaumann hannahbaumann left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @talagayev , this looks great! i finally got around to a full review, please let me know if you have any questions!

water_order: int = 3,
protein_cutoff: float = 12.0,
water_cutoff: float = 8.0,
interactions: Optional[Sequence[str] | str] = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
interactions: Optional[Sequence[str] | str] = None,
interactions: Sequence[str] | Literal["all"] | None,

Comment thread src/openfe_analysis/prolif.py Outdated
RDKit/ProLIF can detect donors/acceptors and bonded hydrogens.
"""
# Protein: guess on the full protein so any pocket residue later has bonds
guess_ligand_bonds(self.universe.select_atoms("protein"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it make sense to rename the function in utils to just guess_bonds or so? since the ligand part may be confusing for the protein and water.

else:
self.times = None
except Exception:
self.frames = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we really need the try/except here? What happens if this was removed?

return self

@property
def ifp(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe add type hint here of what the return would be.

Comment thread src/openfe_analysis/prolif.py Outdated
Comment thread src/openfe_analysis/tests/test_prolif.py Outdated
analysis = ProLIFAnalysis(u, ligand_ag, interactions=["WaterBridge"])

assert analysis._parameters is not None
assert "WaterBridge" in analysis._parameters

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would it make sense to also test that there is a "WaterBridge" column in the interaction output?

Comment thread src/openfe_analysis/tests/test_prolif.py Outdated
calls = {}
traj = {0: None}

def ag(n):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe you could also move some of these things that are repeated across different tests into fixtures?

from openfe_analysis.prolif import ProLIFAnalysis
from openfe_analysis.reader import FEReader


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These tests look great! I think what might be missing is a testing of the start/stop/step in run, and to check the respective times, frames, n_frames when you slice the data like that.

hannahbaumann and others added 7 commits August 13, 2026 11:38
Resolved the test_prolif.py conflict by keeping the refactored version
(universe/ligand_ag fixtures, WaterBridge test renamed to *_warns_and_raises,
with-water test removed, plotting tests moved to tests/utils/test_plotting.py).
prolif.py, plotting.py and conft.py (Zenodo fix #125) auto-merged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hannahbaumann

Copy link
Copy Markdown
Contributor

pre-commit.ci autofix

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.

prolif hook Protein-ligand contact analysis

4 participants