Skip to content

Commit 5f849ba

Browse files
authored
Merge pull request #6 from rinikerlab/CPU_MacOS
Publication updates and CPU compatibility
2 parents 11d8679 + c82e7cb commit 5f849ba

File tree

10 files changed

+2092
-582
lines changed

10 files changed

+2092
-582
lines changed

Analysis/Analyse_Speed_of_simulations.ipynb

Lines changed: 759 additions & 0 deletions
Large diffs are not rendered by default.

Analysis/Intra_molecular_HBond.ipynb

Lines changed: 584 additions & 34 deletions
Large diffs are not rendered by default.

ForceField/Forcefield.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,87 @@ def Data(self, Data):
301301
self._Data = Data
302302

303303

304+
class OpenFF_forcefield_OBC(OpenFF_forcefield):
305+
306+
def __init__(
307+
self,
308+
pdb_id,
309+
solvent_model="TIP3P",
310+
SA=None,
311+
cache=None,
312+
solvent_dielectric=78.5,
313+
rdkit_mol=None,
314+
partial_charges=None,
315+
forcefield="openff-2.0.0",
316+
constraints=HBonds,
317+
):
318+
super().__init__(
319+
pdb_id,
320+
solvent_model,
321+
cache=cache,
322+
rdkit_mol=rdkit_mol,
323+
partial_charges=partial_charges,
324+
forcefield=forcefield,
325+
constraints=constraints,
326+
)
327+
self._SA = SA
328+
self._solvent_dielectric = solvent_dielectric
329+
330+
def create_system(
331+
self, topology, nonbondedMethod=PME, nonbondedCutoff=1 * nanometer
332+
):
333+
334+
system = self._openmm_forcefield.createSystem(
335+
topology=topology, nonbondedMethod=NoCutoff, constraints=HBonds
336+
)
337+
charges = np.array(
338+
[
339+
system.getForces()[0].getParticleParameters(i)[0]._value
340+
for i in range(topology._numAtoms)
341+
]
342+
)
343+
344+
force = GBSAOBC2Force(
345+
cutoff=None,
346+
SA=self._SA,
347+
soluteDielectric=1,
348+
solventDielectric=self._solvent_dielectric,
349+
)
350+
obc_parameters = np.empty((topology.getNumAtoms(), 3))
351+
obc_parameters[:, 0] = charges # Charges
352+
obc_parameters[:, 1:] = force.getStandardParameters(
353+
topology
354+
) # GBNeck2 parameters
355+
356+
self._Data = obc_parameters
357+
# Add Particles and finalize force
358+
force.addParticles(obc_parameters)
359+
force.finalize()
360+
361+
# Create System and add force
362+
system.addForce(force)
363+
364+
return system
365+
366+
def __str__(self):
367+
if self._SA is None:
368+
return "openff200_OBC_%.1f" % self._solvent_dielectric
369+
else:
370+
return "openff200_SAOBC_%.1f" % self._solvent_dielectric
371+
372+
@property
373+
def water_model(self):
374+
return "implicit"
375+
376+
@property
377+
def Data(self):
378+
return self._Data
379+
380+
@Data.setter
381+
def Data(self, Data):
382+
self._Data = Data
383+
384+
304385
class OpenFF_forcefield_SAGBNeck2(OpenFF_forcefield_GBNeck2):
305386

306387
def __init__(self, pdb_id, solvent_model="TIP3P", SA="ACE", cache=None):

MachineLearning/GNN_Models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ def _compute_energy_tensors(self, positions, solvent_model, solvent_dielectric):
787787
# Encode solvent
788788

789789
# Get Graph
790-
if positions.device != self._device:
790+
if positions.device != torch.device("cuda"):
791791
positions = positions.float().to(self._device)
792792

793793
# Build Graph

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
## Publications
44

5-
[3] ChemRxiv. 2024; DOI: [https://doi.org/10.26434/chemrxiv-2024-1hb0b](https://doi.org/10.26434/chemrxiv-2024-1hb0b)
5+
[3] _J. Am. Chem. Soc._, **2025**, DOI: [https://doi.org/10.1021/jacs.4c17622](https://doi.org/10.1021/jacs.4c17622)
66

7-
[2] Chem. Sci., 2024, DOI: [https://doi.org/10.1039/D4SC02432J](https://doi.org/10.1039/D4SC02432J)
7+
[2] _Chem. Sci._, **2024**, DOI: [https://doi.org/10.1039/D4SC02432J](https://doi.org/10.1039/D4SC02432J)
88

9-
[1] J. Chem. Phys. 158, 204101 (2023), DOI: [https://doi.org/10.1063/5.0147027](https://doi.org/10.1063/5.0147027)
9+
[1] _J. Chem. Phys._, **2023**, DOI: [https://doi.org/10.1063/5.0147027](https://doi.org/10.1063/5.0147027)
1010

1111
## Abstract
12-
[3] Understanding and manipulating the conformational behavior of a molecule in different solvent environments is of great interest in the fields of drug discovery and organic synthesis. Molecular dynamics (MD) simulations with solvent molecules explicitly present are the gold standard to compute such conformational ensembles (within the accuracy of the underlying force field), complementing experimental findings and supporting their interpretation. However, conventional methods often face challenges related to computational cost (explicit solvent) or accuracy (implicit solvent). Here, we showcase how our graph neural network (GNN)-based implicit solvent (GNNIS) approach can be used to rapidly compute small molecule conformational ensembles in 39 common organic solvents with high accuracy compared to explicit-solvent simulations. We validate this approach using nuclear magnetic resonance (NMR) measurements, thus identifying the conformers contributing most to the experimental observable. The method allows the time required to accurately predict conformational ensembles to be reduced from days to minutes while achieving results within one kBT of the experimental values.
12+
[3] Understanding and manipulating the conformational behavior of a molecule in different solvent environments is of great interest in the fields of drug discovery and organic synthesis. Molecular dynamics (MD) simulations with solvent molecules explicitly present are the gold standard to compute such conformational ensembles (within the accuracy of the underlying force field), complementing experimental findings and supporting their interpretation. However, conventional methods often face challenges related to computational cost (explicit solvent) or accuracy (implicit solvent). Here, we showcase how our graph neural network (GNN)-based implicit solvent (GNNIS) approach can be used to rapidly compute small molecule conformational ensembles in 39 common organic solvents reproducing explicit-solvent simulations with high accuracy. We validate this approach using nuclear magnetic resonance (NMR) measurements, thus identifying the conformers contributing most to the experimental observable. The method allows the time required to accurately predict conformational ensembles to be reduced from days to minutes while achieving results within one kBT of the experimental values.
1313

1414
[2] The dynamical behavior of small molecules in their environment can be studied with classical molecular dynamics (MD) simulations to gain deeper insight on an atomic level and thus complement and rationalize the interpretation of experimental findings. Such approaches are of great value in various areas of research, e.g., in the development of new therapeutics. The accurate description of solvation effects in such simulations is thereby key and has in consequence been an active field of research since the introduction of MD. So far, the most accurate approaches involve computationally expensive explicit solvent simulations, while widely applied models using an implicit solvent description suffer from reduced accuracy. Recently, machine learning (ML) approaches that provide a probabilistic representation of solvation effects have been proposed as potential alternatives. However, the associated computational costs and minimal or lack of transferability render them unusable in practice. Here, we report the first example of a transferable ML-based implicit solvent model trained on a diverse set of 3 000 000 molecular structures that can be applied to organic small molecules for simulations in water. Extensive testing against reference calculations demonstrated that the model delivers on par accuracy with explicit solvent simulations while providing an up to 18-fold increase in sampling rate.
1515

@@ -26,7 +26,7 @@
2626

2727
## Installation
2828

29-
First clone this repository to your work station and install the environmentusing conda or mamba:
29+
First clone this repository to your work station and install the environment using conda or mamba:
3030

3131
```bash
3232
mamba env create -f environment.yml
@@ -57,7 +57,7 @@ from rdkit.Chem import AllChem
5757

5858
mol = Chem.MolFromSmiles('COCCO')
5959
mol = Chem.AddHs(mol)
60-
AllChem.EmbedMultipleConfs(mol, numConfs=128)
60+
AllChem.EmbedMultipleConfs(mol, numConfs=128, useExpTorsionAnglePrefs = False)
6161

6262
minimized_mol, energies = minimize_mol(mol,"DMSO")
6363
entropies, free_energies = calculate_entropy(minimized_mol,"DMSO")
@@ -68,6 +68,7 @@ In addition an example workflow is provided in the [ExampleConformationEnsemble.
6868
## Reproducibility
6969

7070
This section is intended to provide a step-by-step guide to reproduce the results of the paper.
71+
For the exact versions of the packages used in this study please refer to the [reproduction_environment.yml](reproduction_environment.yml) file.
7172

7273
### Data Set Generation
7374

Simulation/helper_functions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ def create_vac_sim(
450450
save_name=save_name,
451451
openff_forcefield=forcefield,
452452
constraints=constraints,
453+
rdkit_mol=mol,
453454
)
454455
vac_sim.forcefield = OpenFF_forcefield_vacuum(
455456
pdb_id,
@@ -1164,7 +1165,7 @@ def get_gnn_sim(
11641165
constraints=constraints,
11651166
)
11661167
else:
1167-
model_dict = torch.load(model_path)["model"]
1168+
model_dict = torch.load(model_path, map_location="cpu")["model"]
11681169
gnn_sim = create_gnn_sim(
11691170
smiles,
11701171
cache=cache,

Simulation/run_simulation_for_small_molecules.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
OpenFF_forcefield_vacuum,
3939
OpenFF_forcefield_SAGBNeck2,
4040
OpenFF_TIP5P_forcefield,
41+
OpenFF_forcefield_OBC,
4142
)
4243
import yaml
4344
from openmm import LangevinMiddleIntegrator, MonteCarloBarostat
@@ -49,6 +50,7 @@
4950
forcefield_dict = DefaultDict(lambda: OpenFF_forcefield)
5051
forcefield_dict["SAGBNeck2"] = OpenFF_forcefield_SAGBNeck2
5152
forcefield_dict["GBNeck2"] = OpenFF_forcefield_GBNeck2
53+
forcefield_dict["OBC"] = OpenFF_forcefield_OBC
5254
forcefield_dict["vac"] = OpenFF_forcefield_vacuum
5355
forcefield_dict["TIP5P"] = OpenFF_TIP5P_forcefield
5456

@@ -59,7 +61,7 @@
5961

6062
def solvent_model_dict(x):
6163

62-
if x in ["GBNeck2", "vac", "SAGBNeck2"]:
64+
if x in ["GBNeck2", "vac", "SAGBNeck2", "OBC"]:
6365
return "v"
6466
if x in solvent_dict.keys():
6567
return solvent_dict[x]["SMILES"]
@@ -97,6 +99,18 @@ def solvent_model_dict(x):
9799
solvent_dielectric=args.solvent_dielectric,
98100
cache="run_caches/" + save_name + ".cache",
99101
)
102+
elif args.solvent == "OBC":
103+
sim.forcefield = forcefield_dict[args.solvent](
104+
pdb_id,
105+
solvent_dielectric=args.solvent_dielectric,
106+
cache="run_caches/" + save_name + ".cache",
107+
)
108+
elif args.solvent == "SAGBNeck2":
109+
sim.forcefield = forcefield_dict[args.solvent](
110+
pdb_id,
111+
solvent_dielectric=args.solvent_dielectric,
112+
cache="run_caches/" + save_name + ".cache",
113+
)
100114
else:
101115
sim.forcefield = forcefield_dict[args.solvent](
102116
pdb_id,

0 commit comments

Comments
 (0)