Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Restraints API #1043

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open

[WIP] Restraints API #1043

wants to merge 36 commits into from

Conversation

IAlibay
Copy link
Member

@IAlibay IAlibay commented Dec 9, 2024

Checklist

  • Added a news entry

Developers certificate of origin

Copy link

github-actions bot commented Dec 9, 2024

🚨 API breaking changes detected! 🚨

Copy link

codecov bot commented Dec 9, 2024

Codecov Report

Attention: Patch coverage is 49.56268% with 346 lines in your changes missing coverage. Please review.

Project coverage is 90.03%. Comparing base (401ae3d) to head (854d1c6).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...enfe/protocols/restraint_utils/geometry/boresch.py 20.85% 167 Missing ⚠️
...protocols/restraint_utils/openmm/omm_restraints.py 39.09% 81 Missing ⚠️
openfe/protocols/restraint_utils/geometry/utils.py 32.40% 73 Missing ⚠️
...e/protocols/restraint_utils/geometry/flatbottom.py 58.82% 14 Missing ⚠️
...nfe/protocols/restraint_utils/geometry/harmonic.py 52.63% 9 Missing ⚠️
openfe/protocols/restraint_utils/settings.py 96.36% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1043      +/-   ##
==========================================
- Coverage   94.50%   90.03%   -4.47%     
==========================================
  Files         134      147      +13     
  Lines        9986    10689     +703     
==========================================
+ Hits         9437     9624     +187     
- Misses        549     1065     +516     
Flag Coverage Δ
fast-tests 90.03% <49.56%> (?)
slow-tests ?

Flags with carried forward coverage won't be shown. Click here to find out more.

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

Copy link

github-actions bot commented Dec 9, 2024

🚨 API breaking changes detected! 🚨

Copy link

github-actions bot commented Dec 9, 2024

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

Copy link

🚨 API breaking changes detected! 🚨

]
for at3 in atom_pool:
if at3 != atom_idx and at3 in at2_neighbors:
angles.append((atom_idx, at2, at3))
Copy link
Member Author

Choose a reason for hiding this comment

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

From today's discussion: we need to avoid cases where we can cross rings with a non-aromatic bond. Most likely easiest way to do this is to have a special case here that can optionally filter by ring (i.e. only pick angles where you have atoms indices that exist solely within one ring).

raise ValueError(errmsg)

# 2. Get the central atom
center = get_central_atom_idx(rdmol)
Copy link
Member Author

Choose a reason for hiding this comment

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

We may need to use the old approach - need to test this and see if nx is picking long paths when it comes to rings.

bond, ang1, ang2, dih1, dih2, dih3 = _get_restraint_distances(
atomgroup
)

Copy link
Member Author

Choose a reason for hiding this comment

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

TODO: add in checks here so we can warn if it's a bad pick. May push this to a follow-up issue.

topology_format=_get_mda_topology_format(topology),
)

host_ag1 = u.atoms[host_idxs]
Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe find better words for this (or maybe actually explain what the variable means!)

Optional[list[int]]
A list of indices for a selected combination of H0, H1, and H2.
"""
h0_eval = EvaluateHostAtoms1(
Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe add some comments on what is going on here?


Note
----
We assume the temperature to be 298.15 Kelvin.
Copy link
Member Author

Choose a reason for hiding this comment

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

Add acknowledgements to Vytas.

for i, at in enumerate(self.host_atom_pool):
distance_bounds = all(self.results.distances[i] > self.minimum_distance)
mean_angle = circmean(self.results.angles[i], high=np.pi, low=0)
angle_bounds = check_angle_not_flat(
Copy link
Member Author

Choose a reason for hiding this comment

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

Let's try doing this for every frame.

width=1.745 * unit.radians,
)
mean_dihed = circmean(self.results.dihedrals[i], high=np.pi, low=-np.pi)
dihed_bounds = check_dihedral_bounds(mean_dihed)
Copy link
Member Author

Choose a reason for hiding this comment

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

Do this per frame?

not_collinear,
]
):
self.results.valid[i] = True
Copy link
Member Author

Choose a reason for hiding this comment

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

If you do that, then you need to set things to False too!

for i, valid_h0 in enumerate(h0_eval.results.valid):
if valid_h0:
g1g2h0_atoms = guest_atoms.atoms[1:] + host_atom_pool.atoms[i]
h1_eval = EvaluateHostAtoms1(
Copy link
Member Author

Choose a reason for hiding this comment

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

TODO: fix the order, this is incorrect.

h1_eval = EvaluateHostAtoms1(
g1g2h0_atoms,
host_atom_pool,
minimum_distance,
Copy link
Member Author

Choose a reason for hiding this comment

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

Let's double check this is indeed 0.5 nm (or higher)

temperature,
)
for j, valid_h1 in enumerate(h1_eval.results.valid):
g2h0h1_atoms = g1g2h0_atoms.atoms[1:] + host_atom_pool.atoms[j]
Copy link
Member Author

Choose a reason for hiding this comment

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

Also fix this.

distance2_bounds = all(self.results.distances2[i] > self.minimum_distance)
mean_dihed = circmean(self.results.dihedrals[i], high=np.pi, low=-np.pi)
dihed_bounds = check_dihedral_bounds(mean_dihed)
dihed_variance = check_angular_variance(
Copy link
Member Author

Choose a reason for hiding this comment

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

Same here could do per frame.

not_collinear,
]
):
self.results.valid[i] = True
Copy link
Member Author

Choose a reason for hiding this comment

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

Here again, make sure you fill in the False case.

)

if any(h2_eval.ressults.valid):
d1_avgs = np.array([d.mean() for d in h2_eval.results.distances1])
Copy link
Member Author

Choose a reason for hiding this comment

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

This needs to be filtered by True!

raise ValueError(errmsg)

# Set the equilibrium values as those of the final frame
u.trajectory[-1]
Copy link
Member Author

Choose a reason for hiding this comment

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

Raise an issue / todo here to experiment on what frame / values to pick - ideally some kind of mean with the frame closest to the mean values.

@hannahbaumann hannahbaumann self-requested a review December 19, 2024 09:23
Copy link
Contributor

@hannahbaumann hannahbaumann left a comment

Choose a reason for hiding this comment

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

Thanks @IAlibay , just a few small comments from a first quick pass. I left out the boresch.py and the utils script for now as discussed. Will do another pass after the break!

openfe/protocols/restraint_utils/geometry/harmonic.py Outdated Show resolved Hide resolved
openfe/protocols/restraint_utils/geometry/harmonic.py Outdated Show resolved Hide resolved
Partially reproduced from Yank.
"""

lambda_restraints = GlobalParameterState.GlobalParameter(
Copy link
Contributor

Choose a reason for hiding this comment

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

Does 1 here mean that the restraint is fully turned on? Maybe add a comment here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Took me a while to remember, the answer is yes - assuming the underlying Force does it properly ;)

What I mean here is that technically it's up to the underlying for to define how the controlling parameter is applied, so in all the cases below it's "lambda_restraints * E" but nothing really stops it from being "1/lambda_restraints * E" is someone wanted to cause chaos.

That being said, I'm pretty sure we'd do our due dilligence to not let that happen..

This is all to say "Yes I've added the comment and updated the docstring" :)

Copy link

github-actions bot commented Jan 7, 2025

No API break detected ✅

@hannahbaumann hannahbaumann self-assigned this Jan 14, 2025
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