Skip to content

Commit fd62427

Browse files
Merge pull request #2 from NMRDfromMD/improve-isotropic-tutorial
Improve isotropic tutorial
2 parents 19bb97c + a75fe2d commit fd62427

16 files changed

Lines changed: 408 additions & 78 deletions

.submodules/dataset-LJ-fluid

Submodule dataset-LJ-fluid updated 593 files
-121 KB
Binary file not shown.
-121 KB
Binary file not shown.
-88.2 KB
Binary file not shown.
-88 KB
Binary file not shown.

docs/source/tutorials/isotropic-system.rst

Lines changed: 108 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,28 @@ To follow the tutorial, |MDAnalysis|, |NumPy|, and
1313
MD system
1414
---------
1515

16-
.. image:: ../figures/tutorials/isotropic-systems/snapshot-dark.png
16+
.. image:: isotropic-system/snapshot-dark.png
1717
:class: only-dark
1818
:alt: PEG-water mixture simulated with LAMMPS - Dipolar NMR relaxation time calculation
1919
:width: 250
2020
:align: right
2121

22-
.. image:: ../figures/tutorials/isotropic-systems/snapshot-light.png
22+
.. image:: isotropic-system/snapshot-light.png
2323
:class: only-light
2424
:alt: PEG-water mixture simulated with LAMMPS - Dipolar NMR relaxation time calculation
2525
:width: 250
2626
:align: right
2727

28-
The system consists of a bulk mixture of 320 :math:`\text{H}_2\text{O}` water
29-
molecules and 32 :math:`\text{PEG 300}` polymer molecules. The :math:`\text{TIP4P}-\epsilon`
28+
The system consists of a bulk mixture of 420 :math:`\text{H}_2\text{O}` water
29+
molecules and 30 :math:`\text{PEG 300}` polymer molecules. The :math:`\text{TIP4P}-\epsilon`
3030
is used for the water :cite:`fuentes-azcatlNonPolarizableForceField2014`.
3131
:math:`\text{PEG 300}` refers to polyethylene glycol chains with a molar mass of
3232
:math:`300~\text{g/mol}`. The trajectory was recorded during a
3333
:math:`10~\text{ns}` production run performed using the open-source code
3434
LAMMPS in the :math:`NpT` ensemble with a timestep of :math:`1~\text{fs}`.
3535
The temperature was set to :math:`T = 300~\text{K}` and the pressure to
3636
:math:`p = 1~\text{atm}`. Atomic positions were saved in the **prod.xtc** file
37-
every :math:`1~\text{ps}`.
37+
every :math:`2~\text{ps}`.
3838

3939
.. admonition:: Note
4040
:class: non-title-info
@@ -98,6 +98,7 @@ of molecules (water and PEG):
9898
n_TOT = u.atoms.n_residues
9999
n_H2O = u.select_atoms("type 6 7").n_residues
100100
n_PEG = u.select_atoms("type 1 2 3 4 5").n_residues
101+
101102
print(f"The total number of molecules is {n_TOT} ({n_H2O} H2O, {n_PEG} PEG)")
102103
103104
Executing the script using Python will return:
@@ -135,142 +136,173 @@ Executing the script using Python will return:
135136
Launch the H-NMR analysis
136137
-------------------------
137138

138-
Let us create three atom groups: the hydrogen atoms of the PEG, the hydrogen
139-
atoms of the water, and all hydrogen atoms:
139+
First, we create three atom groups: the hydrogen atoms of the PEG, the
140+
hydrogen atoms of the water, and all hydrogen atoms:
140141

141142
.. code-block:: python
142143
143144
H_PEG = u.select_atoms("type 3 5")
144145
H_H2O = u.select_atoms("type 7")
145146
H_ALL = H_PEG + H_H2O
146147
147-
Then, let us first run NMRDfromMD for all hydrogen atoms:
148+
Next, we run ``NMRDfromMD`` for all hydrogen atoms:
148149

149150
.. code-block:: python
150151
151152
nmr_all = NMRD(
152153
u=u,
153154
atom_group=H_ALL,
154155
neighbor_group = H_ALL,
155-
number_i=40)
156+
number_i=20)
156157
nmr_all.run_analysis()
157158
158-
With ``number_i = 40``, only 40 randomly selected atoms from ``H_ALL`` are
159-
used in the calculation. Increase this number for better statistical resolution,
160-
or set ``number_i = 0`` to include all atoms in the group. Here, ``H_ALL``
161-
is specified as both the ``atom_group`` and ``neighbor_group``.
159+
With ``number_i = 20``, only 20 randomly selected atoms from ``H_ALL`` are
160+
included in the calculation. Increasing this number improves statistical
161+
resolution, while setting ``number_i = 0`` includes all atoms in the group.
162+
Here, ``H_ALL`` is specified as both the ``atom_group`` and the
163+
``neighbor_group``.
162164

163-
Let us access the calculated value of the NMR relaxation time :math:`T_1`
164-
in :math:`f \to 0` by adding the following lines to the Python script:
165+
To access the calculated value of the NMR relaxation time :math:`T_1` in
166+
the limit :math:`f \to 0`, add the following lines to your Python script:
165167

166168
.. code-block:: python
167169
168170
T1 = np.round(nmr_all.T1, 2)
169171
170172
print(f"The NMR relaxation time is T1 = {T1} s")
171173
172-
which should return:
174+
The output should be similar to:
173175

174176
.. code-block:: bw
175177
176178
The NMR relaxation time is T1 = 1.59 s
177179
178-
The exact value you obtain will be different, as it depends on which hydrogen
179-
atoms were randomly selected for the calculation. With the small value
180-
``number_i = 40``, the noise is important. You can increase that number
181-
for more precise result, but this will obviously increase the computation time.
180+
The exact value you obtain may differ, as it depends on the specific hydrogen
181+
atoms that were randomly selected for the calculation. With the relatively
182+
small value of ``number_i = 20``, the uncertainty is significant. Increasing
183+
``number_i`` will yield more precise results but at the cost of increased
184+
computation time.
182185

183186
Extract the NMR spectra
184187
-----------------------
185188

186-
The full :math:`R_1` and :math:`T_2` spectra can be extracted as
187-
``1/nmr_ALL.R1`` and ``1/nmr_ALL.R2``, respectively. The corresponding
188-
frequencies are stored in ``nmr_ALL.f``.
189+
The relaxation rates :math:`R_1 (f) = 1/T_1 (f)` (in units of
190+
:math:`\text{s}^{-1}`) and :math:`R_2 (f) = 1/T_2 (f)` can be extracted for
191+
all frequencies :math:`f` (in MHz) as ``nmr_all.R1`` and ``nmr_all.R2``,
192+
respectively. The corresponding frequencies are stored in ``nmr_all.f``.
189193

190194
.. code-block:: python
191195
192-
R1_spectrum = nmr_ALL.R1
193-
R2_spectrum = nmr_ALL.R2
194-
T1_spectrum = 1 / R1_spectrum
195-
T2_spectrum = 1 / R2_spectrum
196-
f = nmr_ALL.f
196+
R1_spectrum = nmr_all.R1
197+
R2_spectrum = nmr_all.R2
198+
f = nmr_all.f
197199
198-
The spectra :math:`T_1` and :math:`T_2` can then be plotted as a function of
199-
:math:`f` using ``pyplot``:
200+
The spectra :math:`R_1 (f)` and :math:`R_2 (f)` can then be plotted as a
201+
function of :math:`f` using ``pyplot``:
200202

201203
.. code-block:: python
202204
203205
from matplotlib import pyplot as plt
204-
plt.loglog(f, T1_spectrum, 'o', label='T1')
205-
plt.loglog(f, T2_spectrum, 's', label='T2')
206-
plt.xlabel("f (MHz)")
207-
plt.ylabel("T1, T2 (s)")
206+
207+
# Plot settings
208+
plt.figure(figsize=(8, 5))
209+
plt.loglog(f, R1_spectrum, 'o', label='R1', markersize=5)
210+
plt.loglog(f, R2_spectrum, 's', label='R2', markersize=5)
211+
# Labels and Title
212+
plt.xlabel("Frequency (MHz)", fontsize=12)
213+
plt.ylabel("Relaxation Rates (s⁻¹)", fontsize=12)
214+
# Grid and boundaries
215+
plt.grid(True, which="both", linestyle='--', linewidth=0.7)
216+
plt.xlim(80, 1e5)
217+
plt.ylim(0.05, 2)
218+
# Legend
208219
plt.legend()
220+
plt.tight_layout()
209221
plt.show()
210222
211-
.. image:: ../figures/tutorials/isotropic-systems/T1-dark.png
223+
.. image:: isotropic-system/nmr-total-dm.png
212224
:class: only-dark
213225
:alt: NMR results obtained from the LAMMPS simulation of water
214226

215-
.. image:: ../figures/tutorials/isotropic-systems/T1-light.png
227+
.. image:: isotropic-system/nmr-total.png
216228
:class: only-light
217229
:alt: NMR results obtained from the LAMMPS simulation of water
218230

219231
.. container:: figurelegend
220232

221-
Figure: NMR relaxation times :math:`T_1` (disks) and :math:`T_2` (squares)
222-
as a function of the frequency :math:`f` for the
223-
:math:`\text{PEG-H}_2\text{O}` bulk mixture.
233+
Figure: NMR relaxation rates :math:`R_1` (A) and :math:`R_2` (B) as a
234+
function of the frequency :math:`f` for the
235+
:math:`\text{PEG-H}_2\text{O}` bulk mixture. Results are shown for two
236+
different values of ``number_i``, :math:`n_i`.
237+
238+
Separating intra from intermolecular contributions
239+
--------------------------------------------------
224240

225-
Calculate the intra-molecular NMR relaxation
226-
--------------------------------------------
241+
So far, the calculations were performed for the two molecule types, PEG and
242+
:math:`\text{H}_2\text{O}`, without distinguishing between intra and intermolecular
243+
contributions. However, this separation is meaningful and allows for
244+
identifying the primary contributors to the relaxation process.
227245

228-
Let us measure the intra-molecular contribution to the NMR relaxation time.
229-
To simplify the analysis, we will differentiate between PEG and
230-
:math:`\text{H}_2\text{O}` molecules and perform two separate analyses.
246+
Let us extract the intramolecular contributions to the relaxation for
247+
both water and PEG, separately:
231248

232249
.. code-block:: python
233250
234-
nmr_H2O_intra = nmrmd.NMR(u, atom_group = H_H2O, type_analysis="intra_molecular", number_i=40)
235-
nmr_PEG_intra = nmrmd.NMR(u, atom_group = H_PEG, type_analysis="intra_molecular", number_i=40)
251+
nmr_h2o_intra = NMRD(
252+
u=u,
253+
atom_group=H_H2O,
254+
type_analysis="intra_molecular",
255+
number_i=200)
256+
nmr_h2o_intra.run_analysis()
236257
237-
The correlation function :math:`G_{ij}` can be accessed from
238-
``nmr_H2O_intra.gij[0]``, and the corresponding time values from
239-
``nmr_H2O_intra.t``:
258+
nmr_peg_intra = NMRD(
259+
u=u,
260+
atom_group=H_PEG,
261+
type_analysis="intra_molecular",
262+
number_i=200)
263+
nmr_peg_intra.run_analysis()
264+
265+
We can also measure the intermolecular contributions:
240266

241267
.. code-block:: python
242268
243-
t = nmr_PEG_intra.t
244-
G_intra_H2O = nmr_H2O_intra.gij[0]
245-
G_intra_PEG = nmr_PEG_intra.gij[0]
269+
nmr_h2o_inter = NMRD(
270+
u=u,
271+
atom_group=H_H2O,
272+
type_analysis="inter_molecular",
273+
number_i=200)
274+
nmr_h2o_inter.run_analysis()
246275
247-
.. image:: ../figures/tutorials/isotropic-systems/Gintra-dark.png
276+
nmr_peg_inter = NMRD(
277+
u=u,
278+
atom_group=H_PEG,
279+
type_analysis="inter_molecular",
280+
number_i=200)
281+
nmr_peg_inter.run_analysis()
282+
283+
Importantly, when no ``neighbor_group`` is specified, the ``atom_group`` is
284+
used as the neighbor group. Thus, in this case, intermolecular
285+
contributions are calculated between molecules of the same type only.
286+
287+
When comparing the NMR spectra ``nmr_h2o_inter.R1`` and ``nmr_h2o_intra.R1``,
288+
you may observe that the intramolecular contributions to :math:`R_1` are
289+
larger than the intermolecular contributions. Additionally, the
290+
intra- and intermolecular spectra display different scaling with
291+
frequency :math:`f`, reflecting distinct motion types contributing to each
292+
term.
293+
294+
.. image:: isotropic-system/nmr-intra-dm.png
248295
:class: only-dark
249-
:alt: NMR results obtained from the LAMMPS simulation of water-PEG
296+
:alt: NMR results obtained from the LAMMPS simulation of water
250297

251-
.. image:: ../figures/tutorials/isotropic-systems/Gintra-light.png
298+
.. image:: isotropic-system/nmr-intra.png
252299
:class: only-light
253-
:alt: NMR results obtained from the LAMMPS simulation of water-PEG
300+
:alt: NMR results obtained from the LAMMPS simulation of water
254301

255302
.. container:: figurelegend
256303

257-
Figure: Intra-molecular correlation function :math:`G_\text{R}` for both
258-
PEG (squares) and :math:`\text{H}_2\text{O}` (disks).
259-
260-
From the correlation functions, one can obtain the typical rotational
261-
time of the molecules:
262-
263-
.. code-block:: python
264-
265-
tau_rot_H2O = np.round(np.trapz(G_intra_H2O, t)/G_intra_H2O[0],2)
266-
tau_rot_PEG = np.round(np.trapz(G_intra_PEG, t)/G_intra_PEG[0],2)
267-
print(f"The rotational time of H2O is = {tau_rot_H2O} ps")
268-
print(f"The rotational time of PEG is = {tau_rot_PEG} ps")
269-
270-
This should return (again, the exact values will likely differ in your
271-
case):
272-
273-
.. code-block:: bw
274-
275-
>> The rotational time of H2O is = 6.35 ps
276-
>> The rotational time of PEG is = 8.34 ps
304+
Figure: Intramolecular NMR relaxation rates :math:`R_{1 \text{R}}` (A) and
305+
Intermolecular NMR relaxation rates :math:`R_{1 \text{T}}` (B) as a
306+
function of the frequency :math:`f` for the
307+
:math:`\text{PEG-H}_2\text{O}` bulk mixture. Results are shown for
308+
:math:`n_i = 1280`.
130 KB
Loading

docs/source/tutorials/isotropic-system/nmr-intra.ipynb

Lines changed: 156 additions & 0 deletions
Large diffs are not rendered by default.
130 KB
Loading

0 commit comments

Comments
 (0)