Skip to content

Commit 9bcf200

Browse files
authored
Add alchemical groups (#15)
* Add alchemical groups * Update environment * Update README * Fix typos in README * Fix replacement of name in modification_kwargs * Fix setting of MLPotential in strategies * Update tests * Update hooks and formatting * Update environments * Update action version in workflow * Bump Python version from 3.10 to 3.12 * Correct env name * Update pre/post/skp dependencies of MLInterpolationModification * Update dependencies for ml mods * Update order for ml mods * Keep edge/node ordering * Bump to version 0.2.2 * Add Tang-Toennies to LJSoftCore * Updates to OpenFF strategy * Generalise CT
1 parent 35fe2dc commit 9bcf200

37 files changed

+676
-371
lines changed

.github/workflows/main.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
matrix:
1717
os: [ubuntu-latest]
18-
python-version: ["3.10"]
18+
python-version: ["3.12"]
1919

2020
runs-on: ${{ matrix.os }}
2121
steps:
@@ -28,15 +28,15 @@ jobs:
2828
python-version: ${{ matrix.python-version }}
2929

3030
- name: Pre-commit hooks
31-
uses: pre-commit/action@v3.0.0
31+
uses: pre-commit/action@v3.0.1
3232

3333
build:
3434
needs: pre-commit
3535

3636
strategy:
3737
matrix:
3838
os: [ubuntu-latest] #, macos-latest, windows-latest]
39-
python-version: ["3.10"]
39+
python-version: ["3.12"]
4040

4141
defaults:
4242
run:
@@ -48,7 +48,7 @@ jobs:
4848
uses: actions/checkout@v4
4949

5050
- name: Set up environment
51-
uses: mamba-org/setup-micromamba@v1
51+
uses: mamba-org/setup-micromamba@v2
5252
with:
5353
environment-file: environment.yaml
5454
environment-name: gha-test-env
@@ -94,7 +94,7 @@ jobs:
9494
# strategy:
9595
# matrix:
9696
# os: [ubuntu-latest]
97-
# python-version: ["3.10"]
97+
# python-version: ["3.12"]
9898

9999
# runs-on: ${{ matrix.os }}
100100
# steps:

.pre-commit-config.yaml

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,13 @@
1-
# Pre-commit hooks for Python code
2-
# Last revision by: Joao Morado
3-
# Last revision date: 8.01.2023
4-
# See https://pre-commit.com for more information
5-
# See https://pre-commit.com/hooks.html for more hooks
6-
71
repos:
8-
- repo: https://github.com/pre-commit/pre-commit-hooks
9-
rev: v3.2.0
10-
hooks:
11-
- id: trailing-whitespace
12-
- id: end-of-file-fixer
13-
#- id: check-added-large-files
14-
- repo: https://github.com/psf/black
15-
rev: 23.1.0
16-
hooks:
17-
- id: black
18-
- id: black-jupyter
19-
- repo: https://github.com/keewis/blackdoc
20-
rev: v0.3.8
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.12.8
214
hooks:
22-
- id: blackdoc
23-
#- id: blackdoc-autoupdate-black
24-
- repo: https://github.com/PyCQA/isort
25-
rev: 5.12.0
26-
hooks:
27-
- id: isort
28-
args: ["--profile", "black", "--filter-files"]
29-
- repo: https://github.com/PyCQA/flake8
30-
rev: 6.0.0
31-
hooks:
32-
- id: flake8
33-
additional_dependencies: [flake8-docstrings]
34-
args: [--max-line-length=127, --exit-zero]
35-
verbose: True
36-
- repo: https://github.com/pre-commit/mirrors-mypy
37-
rev: v1.1.1
5+
- id: ruff
6+
args: ["--fix"]
7+
8+
- repo: https://github.com/pre-commit/mirrors-mypy
9+
rev: v1.17.1
3810
hooks:
39-
- id: mypy
40-
args: [--no-strict-optional, --ignore-missing-imports, --namespace-packages, --explicit-package-bases]
41-
additional_dependencies: ["types-PyYAML"]
11+
- id: mypy
12+
verbose: true
13+
entry: bash -c 'mypy "$@" || true' --

README.md

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@ A package to run hybrid ML/MM free energy simulations.
1111
1. [Installation](#installation)
1212
2. [Alchemical Modifications](#alchemical-modifications)
1313
3. [Running a Multistate Equilibrium Free Energy Simulation](#running-a-multistate-equilibrium-free-energy-simulation)
14+
1. [Using Multiple Alchemical Groups](#using-multiple-alchemical-groups)
1415
4. [Dynamics and EMLE settings](#dynamics-and-emle-settings)
15-
1616
1. [Sire Strategy](#sire-strategy)
1717
2. [OpenFF Strategy](#openff-strategy)
18-
19-
2018
5. [Log Level](#log-level)
2119

2220
## Installation
@@ -98,6 +96,78 @@ U_kln = fes.run_single_state(1000, 1000, 6)
9896
np.save("U_kln_mm_sol_6.npy", np.asarray(U_kln))
9997
```
10098

99+
### Using Multiple Alchemical Groups
100+
101+
For more complex transformations, you can define multiple alchemical groups that can be transformed independently or simultaneously. This is particularly useful when you want to apply different transformations to different regions of your system or transform multiple ligands separately.
102+
103+
To use multiple alchemical groups, specify the group name as a suffix after a colon in the lambda schedule:
104+
105+
```python
106+
from fes_ml.fes import FES
107+
import numpy as np
108+
109+
# Define lambda schedule for multiple alchemical groups
110+
lambda_schedule = {
111+
# Group 1: Turn off LJ and charges for ligand 1
112+
"LJSoftCore:ligand1": [1.0, 0.8, 0.6, 0.4, 0.2, 0.0, 0.0, 0.0],
113+
"ChargeScaling:ligand1": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.0],
114+
115+
# Group 2: Turn off LJ and charges for ligand 2
116+
"LJSoftCore:ligand2": [1.0, 1.0, 0.8, 0.6, 0.4, 0.2, 0.0, 0.0],
117+
"ChargeScaling:ligand2": [1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.33, 0.0],
118+
119+
# Group 3: Interpolate between MM and ML for the entire system
120+
"MLInterpolation:system": [0.0, 0.0, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
121+
}
122+
123+
# Define atom indices for each alchemical group
124+
ligand1_atoms = [1, 2, 3, 4, 5] # Atoms belonging to first ligand
125+
ligand2_atoms = [20, 21, 22, 23, 24] # Atoms belonging to second ligand
126+
system_atoms = list(range(1, 50)) # All atoms for ML/MM interpolation
127+
128+
# Define per-group alchemical atoms
129+
modifications_kwargs = {
130+
"LJSoftCore:ligand1": {
131+
"alchemical_atoms": ligand1_atoms
132+
},
133+
"ChargeScaling:ligand1": {
134+
"alchemical_atoms": ligand1_atoms
135+
},
136+
"LJSoftCore:ligand2": {
137+
"alchemical_atoms": ligand2_atoms
138+
},
139+
"ChargeScaling:ligand2": {
140+
"alchemical_atoms": ligand2_atoms
141+
},
142+
"MLInterpolation:system": {
143+
"alchemical_atoms": system_atoms
144+
}
145+
}
146+
```
147+
148+
#### Multiple Instances of the Same Modification Type
149+
150+
You can also use multiple instances of the same modification type for the same group of atoms. For example, to interpolate between two sets of `CustomLJ` parameters:
151+
152+
```python
153+
lambda_schedule = {
154+
"LJSoftCore:openff1": [1.0, 0.8, 0.6, 0.4, 0.2, 0.0],
155+
"LJSoftCore:openff2": [0.0, 0.2, 0.4, 0.6, 0.8, 1.0],
156+
"CustomLJ:openff1": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
157+
"CustomLJ:openff2": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
158+
}
159+
160+
# Define different LJ parameters for each region
161+
modifications_kwargs = {
162+
"CustomLJ:openff1": {
163+
"lj_offxml": "openff_unconstrained-1.0.0.offxml",
164+
},
165+
"CustomLJ:openff2": {
166+
"lj_offxml": "openff_unconstrained-2.0.0.offxml",
167+
}
168+
}
169+
```
170+
101171
## Dynamics and EMLE settings
102172

103173
In fes-ml, the default strategy for creating OpenMM systems is through Sire. Additionally, fes-ml offers the OpenFF strategy. You can select the desired creation strategy, either `'sire'` or `'openff'`, using the `strategy_name` argument when calling the `fes.create_alchemical_states` method to create the alchemical systems. Most other simulation configurations can also be set by passing additional arguments to this method. For details on customization, refer to the definitions of the `SireCreationStrategy` and `OpenFFCreationStrategy` classes.

analysis/analyse.py

Lines changed: 0 additions & 59 deletions
This file was deleted.

environment.yaml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,26 @@ channels:
66

77
dependencies:
88
- ambertools
9-
- ase
109
- compilers
11-
- cudatoolkit<11.9
12-
- deepmd-kit
10+
- cudatoolkit=11.8
1311
- eigen
1412
- loguru
1513
- openmm>=8.1
1614
- openmm-torch
15+
- openmm-ml
1716
- openmmforcefields
1817
- openff-toolkit
1918
- openff-interchange
2019
- nnpops
2120
- pip
2221
- pybind11
23-
- pytorch
22+
- pytorch=*=*cuda*
2423
- python
2524
- pyyaml
2625
- sire
2726
- torchani
2827
- pygit2
29-
- xtb-python
3028
- pip:
3129
- git+https://github.com/chemle/emle-engine.git
32-
- git+https://github.com/openmm/openmm-ml.git
3330
- coloredlogs
3431
- mace-torch

environment_rascal.yaml

Lines changed: 0 additions & 34 deletions
This file was deleted.

examples/additional_scripts/analysis.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def water_atom(
125125

126126
# Select atoms for which you want to calculate RDF
127127
water = universe.select_atoms(f"type O and not {ligand_selection}")
128-
ligand = universe.select_atoms(ligand_selection)
128+
# ligand = universe.select_atoms(ligand_selection)
129129
atoms = [
130130
universe.select_atoms(f"index {atom.index} and {ligand_selection}")
131131
for atom in universe.select_atoms(ligand_selection)
@@ -327,10 +327,10 @@ def __init__(self) -> None:
327327
def _plot_stdout_with_time_base(txt_file, save_location, data_name, prop):
328328
df = pd.read_csv(txt_file, sep=",")
329329
data = df[f"{data_name}"]
330-
steps = df[f'#"Step"']
330+
steps = df['#"Step"']
331331

332332
plt.plot(steps, data, "r-", linewidth=2, color="maroon")
333-
plt.xlabel(f"Steps")
333+
plt.xlabel("Steps")
334334
plt.ylabel(f"{data_name}")
335335
plt.title(f"Plot of {prop} for {txt_file}")
336336
# plt.savefig(f"{save_location}")
@@ -378,12 +378,12 @@ def plot_energy_all_windows(
378378
txt_file = f"{folder}/{base_name}{w}.txt"
379379
df = pd.read_csv(txt_file, sep=",")
380380
data = df[f"{data_name}"]
381-
steps = df[f'#"Step"']
381+
steps = df['#"Step"']
382382
x_axis = [r for r in range(1, len(steps) + 1, 1)]
383383

384384
plt.plot(x_axis, data, linewidth=2, alpha=0.3, label=label, color=colors[w])
385385
legend_labels.append(label)
386-
plt.xlabel(f"Steps")
386+
plt.xlabel("Steps")
387387
plt.ylabel(f"{data_name}")
388388
plt.title(f"Plot of {prop} for {txt_file}")
389389
plt.legend(bbox_to_anchor=(1, 0.5), loc="center left")

examples/openff_strategy/mts_benchmark/ml/agg_output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
out = f"OUTPUT_{i}"
1010
try:
1111
f = glob.glob(out + "/*npy")[0]
12-
except:
12+
except Exception:
1313
break
1414
U_kln.append(np.load(f)[:, frames_disc::step])
1515

examples/openff_strategy/mts_benchmark/ml/analysis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import matplotlib.pyplot as plt
44
import numpy as np
55
from openmm import unit
6-
from pymbar import MBAR, timeseries
6+
from pymbar import MBAR
77

88
if len(sys.argv) != 3 or sys.argv[1] in ["-h", "--help"]:
99
print("Usage: python analyse.py <file_name> <temperature>")

examples/openff_strategy/mts_benchmark/ml_mts/agg_output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
out = f"OUTPUT_{i}"
1010
try:
1111
f = glob.glob(out + "/*npy")[0]
12-
except:
12+
except Exception:
1313
break
1414
U_kln.append(np.load(f)[:, frames_disc::step])
1515

0 commit comments

Comments
 (0)