Skip to content

Commit 130d732

Browse files
Merge pull request #75 from wiederm/absolute_solvation
absolute solvation
2 parents 14b7242 + 66f1b08 commit 130d732

File tree

11 files changed

+697
-137
lines changed

11 files changed

+697
-137
lines changed

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[pytest]
22
markers =
3+
asfe: part of the workflow for absolute solvation free energy calculation.
34
rsfe: part of the workflow for a relative solvation free energy calculation.
45
postprocessing: part of the postprocessing workflow.
56
rbfe: part of the workflow for a relative binding free energy calculation.
67
full_workflow: performs a complete free energy calculation
7-
requires_loeffler_systems: test that requires that the loeffler system are installed.
88
requires_charmm_installation: test requires CHARMM installation.
99
charmm: charmm test
1010
restraints: All tests for restraints.py

transformato/analysis.py

Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import gc
33
import logging
44
import multiprocessing as mp
5-
import os
5+
import os,sys
66
import pickle
77
import subprocess
88
from collections import defaultdict
@@ -21,7 +21,7 @@
2121
from tqdm import tqdm
2222

2323
from transformato.constants import temperature
24-
from transformato.utils import get_structure_name
24+
from transformato.utils import get_structure_name, isnotebook
2525

2626
logger = logging.getLogger(__name__)
2727

@@ -76,7 +76,10 @@ def __init__(self, configuration: dict, structure_name: str):
7676
# decide if the name of the system corresponds to structure1 or structure2
7777
structure = get_structure_name(configuration, structure_name)
7878

79-
if configuration["simulation"]["free-energy-type"] == "rsfe":
79+
if (
80+
configuration["simulation"]["free-energy-type"] == "rsfe"
81+
or configuration["simulation"]["free-energy-type"] == "asfe"
82+
):
8083
self.envs = ("vacuum", "waterbox")
8184
self.mbar_results = {"waterbox": None, "vacuum": None}
8285

@@ -96,15 +99,15 @@ def __init__(self, configuration: dict, structure_name: str):
9699
self.save_results_to_path: str = f"{self.configuration['system_dir']}/results/"
97100
self.traj_files = defaultdict(list)
98101

99-
def load_trajs(self, nr_of_max_snapshots: int = 300):
102+
def load_trajs(self, nr_of_max_snapshots: int = 300, multiple_runs: int = 0):
100103
"""
101104
load trajectories, thin trajs and merge themn.
102105
Also calculate N_k for mbar.
103106
"""
104107

105108
assert type(nr_of_max_snapshots) == int
106109
self.nr_of_max_snapshots = nr_of_max_snapshots
107-
self.snapshots, self.unitcell, self.nr_of_states, self.N_k = self._merge_trajs()
110+
self.snapshots, self.unitcell, self.nr_of_states, self.N_k = self._merge_trajs(multiple_runs)
108111

109112
def _generate_openMM_system(self, env: str, lambda_state: int) -> Simulation:
110113
# read in necessary files
@@ -156,7 +159,7 @@ def _thinning(self, any_list):
156159
further_thinning,
157160
)
158161

159-
def _merge_trajs(self) -> Tuple[dict, dict, int, dict]:
162+
def _merge_trajs(self, multiple_runs: int) -> Tuple[dict, dict, int, dict]:
160163
"""
161164
load trajectories, thin trajs and merge themn.
162165
Also calculate N_k for mbar.
@@ -179,7 +182,11 @@ def _merge_trajs(self) -> Tuple[dict, dict, int, dict]:
179182
unitcell_ = []
180183
conf_sub = self.configuration["system"][self.structure][env]
181184
for lambda_state in tqdm(range(1, nr_of_states + 1)):
182-
dcd_path = f"{self.base_path}/intst{lambda_state}/{conf_sub['intermediate-filename']}.dcd"
185+
if multiple_runs:
186+
dcd_path = f"{self.base_path}/intst{lambda_state}/run_{multiple_runs}/{conf_sub['intermediate-filename']}.dcd"
187+
else:
188+
dcd_path = f"{self.base_path}/intst{lambda_state}/{conf_sub['intermediate-filename']}.dcd"
189+
183190
psf_path = f"{self.base_path}/intst{lambda_state}/{conf_sub['intermediate-filename']}.psf"
184191
if not os.path.isfile(dcd_path):
185192
raise RuntimeError(f"{dcd_path} does not exist.")
@@ -425,9 +432,8 @@ def _evaluate_e_with_openMM(
425432
return energies
426433

427434
def energy_at_lambda(
428-
self, lambda_state: int, env: str, nr_of_max_snapshots: int, in_memory: bool
435+
self, lambda_state: int, env: str, nr_of_max_snapshots: int, in_memory: bool, multiple_runs: int,
429436
) -> Tuple:
430-
431437
gc.enable()
432438
logger.info(f"Analysing lambda state {lambda_state} of {self.nr_of_states}")
433439
conf_sub = self.configuration["system"][self.structure][env]
@@ -440,7 +446,11 @@ def energy_at_lambda(
440446

441447
for lambda_state in range(1, self.nr_of_states + 1):
442448

443-
dcd_path = f"{self.base_path}/intst{lambda_state}/{conf_sub['intermediate-filename']}.dcd"
449+
if not multiple_runs:
450+
dcd_path = f"{self.base_path}/intst{lambda_state}/{conf_sub['intermediate-filename']}.dcd"
451+
else:
452+
dcd_path = f"{self.base_path}/intst{lambda_state}/run_{multiple_runs}/{conf_sub['intermediate-filename']}.dcd"
453+
444454
if not os.path.isfile(dcd_path):
445455
raise RuntimeError(f"{dcd_path} does not exist.")
446456

@@ -497,10 +507,13 @@ def _analyse_results_using_mda(
497507
engine: str,
498508
num_proc: int,
499509
nr_of_max_snapshots: int,
510+
multiple_runs: int,
500511
in_memory: bool = False,
501512
):
502513

503514
logger.info(f"Evaluating with {engine}, using {num_proc} CPUs")
515+
if not os.path.isdir(self.base_path):
516+
sys.exit(f"{self.base_path} does not exist")
504517
self.nr_of_states = len(next(os.walk(f"{self.base_path}"))[1])
505518

506519
if engine == "openMM":
@@ -520,6 +533,7 @@ def _analyse_results_using_mda(
520533
repeat(env),
521534
repeat(nr_of_max_snapshots),
522535
repeat(in_memory),
536+
repeat(multiple_runs),
523537
),
524538
)
525539
)
@@ -535,12 +549,19 @@ def _analyse_results_using_mda(
535549
)
536550

537551
if save_results:
538-
file = f"{self.save_results_to_path}/mbar_data_for_{self.structure_name}_in_{env}.pickle"
552+
if multiple_runs:
553+
file = f"{self.save_results_to_path}/mbar_data_for_{self.structure_name}_in_{env}_run_{multiple_runs}.pickle"
554+
else:
555+
file = f"{self.save_results_to_path}/mbar_data_for_{self.structure_name}_in_{env}.pickle"
556+
539557
logger.info(f"Saving results: {file}")
540558
results = {"u_kn": u_kn, "N_k": N_k}
541559
pickle.dump(results, open(file, "wb+"))
542-
543-
return self.calculate_dG_using_mbar(u_kn, N_k, env)
560+
561+
return self.calculate_dG_using_mbar(u_kn, N_k, env)
562+
563+
else:
564+
return self.calculate_dG_using_mbar(u_kn, N_k, env)
544565

545566
def _analyse_results_using_mdtraj(
546567
self,
@@ -611,6 +632,7 @@ def calculate_dG_to_common_core(
611632
num_proc: int = 1,
612633
in_memory: bool = False,
613634
nr_of_max_snapshots: int = -1,
635+
multiple_runs: int = 0,
614636
):
615637
"""
616638
Calculate mbar results using either the python package mdtraj
@@ -636,13 +658,14 @@ def calculate_dG_to_common_core(
636658
engine,
637659
num_proc,
638660
nr_of_max_snapshots,
661+
multiple_runs,
639662
)
640663
elif analyze_traj_with == "mdtraj":
641664
self.mbar_results[env] = self._analyse_results_using_mdtraj(
642665
env, self.snapshots[env], self.unitcell[env], save_results, engine
643666
)
644667
else:
645-
raise RuntimeError("Either mda or mdtray")
668+
raise RuntimeError("Either mda or mdtraj")
646669

647670
def load_waterbox_results(self, file: str):
648671
self.mbar_results["waterbox"] = self._load_mbar_results(file)
@@ -765,8 +788,8 @@ def plot_free_energy_overlap(self, env: str):
765788
plt.savefig(
766789
f"{self.save_results_to_path}/ddG_to_common_core_overlap_{env}_for_{self.structure_name}.png"
767790
)
768-
769-
plt.show()
791+
if isnotebook():
792+
plt.show()
770793
plt.close()
771794

772795
def plot_free_energy(self, env: str):
@@ -805,31 +828,17 @@ def plot_free_energy(self, env: str):
805828
plt.savefig(
806829
f"{self.save_results_to_path}/ddG_to_common_core_line_plot_{env}_for_{self.structure_name}.png"
807830
)
808-
plt.show()
831+
if isnotebook():
832+
plt.show()
809833
plt.close()
810834

811-
def plot_vacuum_free_energy_overlap(self):
812-
self.plot_free_energy_overlap("vacuum")
813-
814-
def plot_complex_free_energy_overlap(self):
815-
self.plot_free_energy_overlap("complex")
816-
817-
def plot_waterbox_free_energy_overlap(self):
818-
self.plot_free_energy_overlap("waterbox")
819-
820-
def plot_vacuum_free_energy(self):
821-
self.plot_free_energy("vacuum")
822-
823-
def plot_complex_free_energy(self):
824-
self.plot_free_energy("complex")
825-
826-
def plot_waterbox_free_energy(self):
827-
self.plot_free_energy("waterbox")
828-
829835
@property
830836
def end_state_free_energy_difference(self):
831837
"""DeltaF[lambda=1 --> lambda=0]"""
832-
if self.configuration["simulation"]["free-energy-type"] == "rsfe":
838+
if (
839+
self.configuration["simulation"]["free-energy-type"] == "rsfe"
840+
or self.configuration["simulation"]["free-energy-type"] == "asfe"
841+
):
833842
return (
834843
self.waterbox_free_energy_differences[0, -1]
835844
- self.vacuum_free_energy_differences[0, -1],
@@ -848,26 +857,20 @@ def end_state_free_energy_difference(self):
848857
raise RuntimeError()
849858

850859
def show_summary(self):
851-
from transformato.utils import isnotebook
852-
853-
if self.configuration["simulation"]["free-energy-type"] == "rsfe":
854-
if isnotebook:
855-
# only show this if we are in a notebook
856-
self.plot_vacuum_free_energy_overlap()
857-
self.plot_waterbox_free_energy_overlap()
858-
self.plot_vacuum_free_energy()
859-
self.plot_waterbox_free_energy()
860-
self.detailed_overlap("waterbox")
861-
self.detailed_overlap("vacuum")
860+
861+
if (
862+
self.configuration["simulation"]["free-energy-type"] == "rsfe"
863+
or self.configuration["simulation"]["free-energy-type"] == "asfe"
864+
):
865+
self.plot_free_energy_overlap("vacuum")
866+
self.plot_free_energy_overlap("waterbox")
867+
self.plot_free_energy("vacuum")
868+
self.plot_free_energy("waterbox")
862869
else:
863-
if isnotebook:
864-
# only show this if we are in a notebook
865-
self.plot_complex_free_energy_overlap()
866-
self.plot_waterbox_free_energy_overlap()
867-
self.plot_complex_free_energy()
868-
self.plot_waterbox_free_energy()
869-
self.detailed_overlap("complex")
870-
self.detailed_overlap("waterbox")
870+
self.plot_free_energy_overlap("waterbox")
871+
self.plot_free_energy_overlap("complex")
872+
self.plot_free_energy("waterbox")
873+
self.plot_free_energy("complex")
871874

872875
energy_estimate, uncertainty = self.end_state_free_energy_difference
873876
print(

0 commit comments

Comments
 (0)