Skip to content

Commit 8043af1

Browse files
committed
Developed some unit tests
1 parent 7c795ad commit 8043af1

File tree

20 files changed

+1313
-64
lines changed

20 files changed

+1313
-64
lines changed

.codecov.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ comment:
1313
flags: null
1414
paths: null
1515
ignore:
16-
"ensemble_md.exceptions.py"
17-
"ensemble_md.utils.py"
16+
- "ensemble_md.exceptions.py"
17+
- "ensemble_md.utils.py"
18+
- "ensemble_md.ensemble_md.py"

docs/theory_implementation.rst

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,10 @@ Step 1: Set up parameters
134134
To run an ensemble of expanded ensemble in GROMACS using :code:`ensemble_md`, one at
135135
least needs to following four files:
136136

137-
- One GRO file of the system of interest
138-
- One TOP file of the system of interest
139-
- One MDP template for customizing different MDP files for different replicas.
140-
- One YAML file that specify the EEXE-relevant parameters.
137+
* One GRO file of the system of interest
138+
* One TOP file of the system of interest
139+
* One MDP template for customizing different MDP files for different replicas.
140+
* One YAML file that specify the EEXE-relevant parameters.
141141

142142
Notably, here we are assuming that all replicas start from the same configuration represented
143143
by the single GRO file, but the user should also be able to use the methods defined in
@@ -151,23 +151,23 @@ Importantly, to instantiate the class :class:`.EnsembleEXE`, the input YAML file
151151
In this YAML file, the user needs to specify how the replicas should be set up or interact with each
152152
other during the simulation ensemble. Below we decribe the details of these parameters.
153153

154-
- Required parameters
154+
* Required parameters
155155

156-
- :code:`parallel`: Whether the replicas of EEXE should be run in parallel or not.
157-
- :code:`n_sim`: The number of replica simulations.
158-
- :code:`n_iterations`: The number of iterations.
159-
- :code:`s`: The shift in the alchemical ranges between adjacent replicas (e.g. :math:`s = 2` if :math:`λ_2 = (2, 3, 4)` and :math:`λ_3 = (4, 5, 6)`.
160-
- :code:`mdp`: The MDP template that has the whole range of :math:`λ` values.
156+
* :code:`parallel`: Whether the replicas of EEXE should be run in parallel or not.
157+
* :code:`n_sim`: The number of replica simulations.
158+
* :code:`n_iterations`: The number of iterations.
159+
* :code:`s`: The shift in the alchemical ranges between adjacent replicas (e.g. :math:`s = 2` if :math:`λ_2 = (2, 3, 4)` and :math:`λ_3 = (4, 5, 6)`.
160+
* :code:`mdp`: The MDP template that has the whole range of :math:`λ` values.
161161

162-
- Optional parameters
162+
* Optional parameters
163163

164-
- :code:`nst_sim`: The number of simulation steps, i.e. exchange frequency. This option assumes replicas with homogeneous simulation lengths. If this option is not specified, the number of steps defined in the template MDP file will be used.
165-
- :code:`mc_scheme`: The method for swapping simulations. Choices include :code:`same-state`/:code:`same_state`, :code:`metropolis`, and :code:`metropolis-eq`/:code:`metropolis_eq`. For more details, please refer to :ref:`doc_mc_schemes`. (Default: :code:`metropolis`)
166-
- :code:`w_scheme`: The method for combining weights. Choices include :code:`None` (unspecified), :code:`exp-avg`/:code:`exp_avg`, and :code:`hist-exp-avg`/:code:`hist_exp_avg`. For more details, please refer to :ref:`doc_w_schemes`. (Default: :code:`hist-exp-avg`)
167-
- :code:`N_cutoff`: The histogram cutoff. Only required if :code:`hist-exp-avg` is used. (Default: 0)
168-
- :code:`n_ex`: The number of swaps to be proposed in one attempt. This works basically the same as :code:`-nex` flag in GROMACS. A recommended value is :math:`N^3`, where :math:`N` is the number of replicas. If `n_ex` is unspecified or specified as 0, neighboring swapping will be carried out. For more details, please refer to :ref:`doc_swap_basics`. (Default: 0)
169-
- :code:`outfile`: The output file for logging how replicas interact with each other.
170-
- :code:`verbose`: Whether a verbse log is wanted.
164+
* :code:`nst_sim`: The number of simulation steps, i.e. exchange frequency. This option assumes replicas with homogeneous simulation lengths. If this option is not specified, the number of steps defined in the template MDP file will be used.
165+
* :code:`mc_scheme`: The method for swapping simulations. Choices include :code:`same-state`/:code:`same_state`, :code:`metropolis`, and :code:`metropolis-eq`/:code:`metropolis_eq`. For more details, please refer to :ref:`doc_mc_schemes`. (Default: :code:`metropolis`)
166+
* :code:`w_scheme`: The method for combining weights. Choices include :code:`None` (unspecified), :code:`exp-avg`/:code:`exp_avg`, and :code:`hist-exp-avg`/:code:`hist_exp_avg`. For more details, please refer to :ref:`doc_w_schemes`. (Default: :code:`hist-exp-avg`)
167+
* :code:`N_cutoff`: The histogram cutoff. Only required if :code:`hist-exp-avg` is used. (Default: 0)
168+
* :code:`n_ex`: The number of swaps to be proposed in one attempt. This works basically the same as :code:`-nex` flag in GROMACS. A recommended value is :math:`N^3`, where :math:`N` is the number of replicas. If `n_ex` is unspecified or specified as 0, neighboring swapping will be carried out. For more details, please refer to :ref:`doc_swap_basics`. (Default: 0)
169+
* :code:`outfile`: The output file for logging how replicas interact with each other.
170+
* :code:`verbose`: Whether a verbse log is wanted.
171171

172172
Step 2: Run the 1st iteration
173173
-----------------------------
@@ -186,8 +186,8 @@ Step 3-1: Extract the final status of the previous iteration
186186
To calculate the acceptance ratio and modify the mdp files in later steps, we first need to extract the information
187187
of the final status of the previous iteration. Specifically, for all the replica simulations, we need to
188188

189-
- Find the last sampled state and the corresponding lambda values from the DHDL files
190-
- Find the final Wang-Landau incrementors and weights from the LOG files.
189+
* Find the last sampled state and the corresponding lambda values from the DHDL files
190+
* Find the final Wang-Landau incrementors and weights from the LOG files.
191191

192192
These two tasks are done by :obj:`.extract_final_dhdl_info` and :obj:`.extract_final_log_info`.
193193

@@ -232,8 +232,8 @@ when needed), the user should set up the input files for the next iteration. In
232232
status of the previous iteration.
233233
This means:
234234

235-
- For each replica, the input configuration for initializing a new iterations should be the output configuraiton of the previous iteration. For example, if the final configurations are represented by :code:`[1, 2, 0, 3]` (returned by :obj:`.get_swapped_configs`), then in the next iteration, replica 0 should be initialized by the output configuration of replica 1 in the previous iteration, while replica 3 can just inherit the output configuration from previous iteration of the same replica. Notably, instead of exchanging the MDP files, we recommend swapping out the coordinate files to exchange replicas.
236-
- For each replica, the MDP file for the new iteration should be the same as the one used in the previous iteartion of the same replica except that parameters like :code:`tinit`, :code:`init-lambda-state`, :code:`init-wl-delta`, and :code:`init-lambda-weights` should be modified to the final values in the previous iteration. This can be done by :class:`.gmx_parser.MDP` and :obj:`.update_MDP`.
235+
* For each replica, the input configuration for initializing a new iterations should be the output configuraiton of the previous iteration. For example, if the final configurations are represented by :code:`[1, 2, 0, 3]` (returned by :obj:`.get_swapped_configs`), then in the next iteration, replica 0 should be initialized by the output configuration of replica 1 in the previous iteration, while replica 3 can just inherit the output configuration from previous iteration of the same replica. Notably, instead of exchanging the MDP files, we recommend swapping out the coordinate files to exchange replicas.
236+
* For each replica, the MDP file for the new iteration should be the same as the one used in the previous iteartion of the same replica except that parameters like :code:`tinit`, :code:`init-lambda-state`, :code:`init-wl-delta`, and :code:`init-lambda-weights` should be modified to the final values in the previous iteration. This can be done by :class:`.gmx_parser.MDP` and :obj:`.update_MDP`.
237237

238238
Step 4: Run the new iteration
239239
-----------------------------
@@ -474,9 +474,9 @@ Each of these replicas sample 6 alchemical states. There alchemical ranges are d
474474
Replicas 1 and 2 have overlap at states 2 to 5 and replicas 2 and 3 have overlap at states 4 to 7. Notably, all
475475
three replicas sampled states 4 and 5. Therefore, what we are going to do is
476476

477-
- For states 2 and 3, combine weights across replicas 1 and 2.
478-
- For states 4 and 5, combine weights across replicas 1, 2 and 3.
479-
- For states 6 and 7, combine weights across replicas 2 and 3.
477+
* For states 2 and 3, combine weights across replicas 1 and 2.
478+
* For states 4 and 5, combine weights across replicas 1, 2 and 3.
479+
* For states 6 and 7, combine weights across replicas 2 and 3.
480480

481481
However, before we combine the weights, we should make sure the weights of all replicas have the same reference because now
482482
the references of the 3 replicas are states 0, 2, and 4, respectively. Therefore could be

ensemble_md/ensemble_EXE.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ def __init__(self, yml_file):
4747
outfile : str
4848
The file name of the log file for documenting how different replicas interact
4949
during the process.
50+
51+
Notes
52+
-----
53+
For easier parsing of the mdp template file, please make sure that at least the following GROMACS mdp
54+
parameters specified using dashes instead of underscores: :code:`ref-t`, :code:`vdw-lambdas`,
55+
:code:`coul-lambdas`, :code:`restraint-lambdas`, and :code:`init-lambda-weights`.
5056
"""
5157
# Step 0: Set up constants
5258
k = 1.380649e-23
@@ -87,33 +93,33 @@ def __init__(self, yml_file):
8793
params_int = ['n_sim', 'n_iterations', 's', 'nst_sim', 'N_cutoff', 'n_ex'] # integer parameters
8894
for i in params_int:
8995
if type(getattr(self, i)) != int:
90-
raise ParameterError(f"The parameter {i} should be an integer.")
96+
raise ParameterError(f"The parameter '{i}' should be an integer.")
9197

9298
params_pos = ['n_sim', 'n_iterations', 's', 'nst_sim'] # positive parameters
9399
for i in params_pos:
94100
if getattr(self, i) <= 0:
95-
raise ParameterError(f"The parameter {i} should be positive.")
101+
raise ParameterError(f"The parameter '{i}' should be positive.")
96102

97103
params_non_neg = ['N_cutoff', 'n_ex'] # non-negative parameters
98104
for i in params_non_neg:
99105
if getattr(self, i) < 0:
100-
raise ParameterError(f"The parameter {i} should be non-negative.")
106+
raise ParameterError(f"The parameter '{i}' should be non-negative.")
101107

102108
params_str = ['mdp', 'outfile']
103109
for i in params_str:
104110
if type(getattr(self, i)) != str:
105-
raise ParameterError(f"The parameter {i} should be a string.")
111+
raise ParameterError(f"The parameter '{i}' should be a string.")
106112

107113
params_bool = ['parallel', 'verbose']
108114
for i in params_bool:
109115
if type(getattr(self, i)) != bool:
110-
raise ParameterError(f"The parameter {i} should be a boolean variable.")
116+
raise ParameterError(f"The parameter '{i}' should be a boolean variable.")
111117

112118
# Step 4: Read in parameters from the MDP template
113119
self.template = gmx_parser.MDP(self.mdp)
114120
self.nsteps = self.template["nsteps"] # will be overwritten by self.nst_sim if nst_sim is specified.
115121
self.dt = self.template["dt"] # ps
116-
self.temp = self.template["ref_t"]
122+
self.temp = self.template["ref-t"]
117123
self.kT = k * NA * self.temp / 1000 # 1 kT in kJ/mol
118124

119125
# Total # of states. n_tot = n_sub * n_sim - (n_overlap) * (n_sum - 1), where n_overlap = n_sub - s
@@ -179,7 +185,7 @@ def map_lambda2state(self):
179185
(
180186
self.template["coul-lambdas"][i],
181187
self.template["vdw-lambdas"][i],
182-
self.template["restraint_lambdas"][i],
188+
self.template["restraint-lambdas"][i],
183189
)
184190
] = i
185191
else:
@@ -190,8 +196,9 @@ def map_lambda2state(self):
190196
)
191197
] = i
192198
else:
193-
self.lambda_dict[(self.template["vdw-lambdas"][i])] = i
194-
199+
self.lambda_dict[(self.template["vdw-lambdas"][i],)] = i
200+
print(self.lambda_dict)
201+
print(self.state_ranges)
195202
self.lambda_ranges = [[list(self.lambda_dict.keys())[j] for j in self.state_ranges[i]]for i in range(len(self.state_ranges))] # noqa: E501
196203

197204
def initialize_MDP(self, idx):
@@ -380,12 +387,11 @@ def propose_swaps(self, states):
380387

381388
print(f"Swappable pairs: {swappables}")
382389

383-
for i in range(n_ex):
384-
try:
385-
swap_list = random.choices(swappables, k=n_ex)
386-
except IndexError:
387-
# In the case that swappables is an empty list, i.e. no swappable pairs.
388-
swap_list = None
390+
try:
391+
swap_list = random.choices(swappables, k=n_ex)
392+
except IndexError:
393+
# In the case that swappables is an empty list, i.e. no swappable pairs.
394+
swap_list = None
389395

390396
return swap_list
391397

0 commit comments

Comments
 (0)