H bond recognition and lifetime analysis#

Import packages#

import json
from cage_data import cage1_info
from fishmol import trj, data, dimer, style
from fishmol.utils import retrieve_symbol
def pop_C(TFAs):
    for TFA in TFAs:
        [TFA.pop(key) for key in list(TFA.keys()) if key == "C1" or key == "C2"] # keep F and O in TFAs only for H-bond analysis
    return TFAs
waters = cage1_info.waters
TFAs = cage1_info.TFAs
phenols = cage1_info.phenols
amines = cage1_info.amines
TFA_F_Os = pop_C(TFAs)
atoms_dict = waters + phenols + amines + TFA_F_Os
cell = cage1_info.cell
donors, acceptors = dimer.find_d_a(atoms_dict = atoms_dict)
d_a_pairs, d_h_a_pairs = dimer.pair_d_a(donors = donors, acceptors = acceptors)
Done! 40 donors and 40 acceptors were found!
Acceptors:
 [{'O': 14}, {'O': 17}, {'O': 143}, {'O': 146}, {'O': 272}, {'O': 275}, {'O': 401}, {'O': 404}, {'F1': 0, 'F2': 1, 'F3': 2, 'O1': 3, 'O2': 4}, {'F1': 7, 'F2': 8, 'F3': 9, 'O1': 10, 'O2': 11}, {'F1': 129, 'F2': 130, 'F3': 131, 'O1': 132, 'O2': 133}, {'F1': 136, 'F2': 137, 'F3': 138, 'O1': 139, 'O2': 140}, {'F1': 258, 'F2': 259, 'F3': 260, 'O1': 261, 'O2': 262}, {'F1': 265, 'F2': 266, 'F3': 267, 'O1': 268, 'O2': 269}, {'F1': 387, 'F2': 388, 'F3': 389, 'O1': 390, 'O2': 391}, {'F1': 394, 'F2': 395, 'F3': 396, 'O1': 397, 'O2': 398}] 
 Donors:
 [{'O': 14, 'H1': 15}, {'O': 14, 'H2': 16}, {'O': 17, 'H1': 18}, {'O': 17, 'H2': 19}, {'O': 143, 'H1': 144}, {'O': 143, 'H2': 145}, {'O': 146, 'H1': 147}, {'O': 146, 'H2': 148}, {'O': 272, 'H1': 273}, {'O': 272, 'H2': 274}, {'O': 275, 'H1': 276}, {'O': 275, 'H2': 277}, {'O': 401, 'H1': 402}, {'O': 401, 'H2': 403}, {'O': 404, 'H1': 405}, {'O': 404, 'H2': 406}, {'O': 24, 'H': 25}, {'O': 411, 'H': 412}, {'O': 153, 'H': 154}, {'O': 282, 'H': 283}, {'N': 410, 'H1': 284}, {'N': 410, 'H2': 285}, {'N': 409, 'H': 128}, {'N': 407, 'H1': 28}, {'N': 407, 'H2': 29}, {'N': 152, 'H1': 26}, {'N': 152, 'H2': 27}, {'N': 149, 'H1': 286}, {'N': 149, 'H2': 287}, {'N': 151, 'H': 386}, {'N': 280, 'H': 257}, {'N': 278, 'H1': 157}, {'N': 278, 'H2': 158}, {'N': 281, 'H1': 413}, {'N': 281, 'H2': 414}, {'N': 23, 'H1': 155}, {'N': 23, 'H2': 156}, {'N': 22, 'H1': 515}, {'N': 20, 'H1': 415}, {'N': 20, 'H2': 416}]
Donor and acceptor is the same atom: O 14, skipped.
Donor and acceptor is the same atom: O 14, skipped.
Donor and acceptor is the same atom: O 17, skipped.
Donor and acceptor is the same atom: O 17, skipped.
Donor and acceptor is the same atom: O 143, skipped.
Donor and acceptor is the same atom: O 143, skipped.
Donor and acceptor is the same atom: O 146, skipped.
Donor and acceptor is the same atom: O 146, skipped.
Donor and acceptor is the same atom: O 272, skipped.
Donor and acceptor is the same atom: O 272, skipped.
Donor and acceptor is the same atom: O 275, skipped.
Donor and acceptor is the same atom: O 275, skipped.
Donor and acceptor is the same atom: O 401, skipped.
Donor and acceptor is the same atom: O 401, skipped.
Donor and acceptor is the same atom: O 404, skipped.
Donor and acceptor is the same atom: O 404, skipped.
Unique donoar-acceptor/donoar-hydrogen-acceptor combinations: 1904
Sample donor-acceptor pair: [{'O': 14}, {'O': 17}]
Sample donor-hydrogen-acceptor pair: [{'O': 14, 'H1': 15}, {'O': 17}]
import numpy as np
%%time
traj = trj.Trajectory(timestep = 5, data = "/nobackup/rhtp48/data_ana/cage1-500K.xyz", index = ":", cell = cell)
CPU times: user 2min 12s, sys: 3.19 s, total: 2min 15s
Wall time: 2min 16s
traj.wrap2box()
<fishmol.trj.Trajectory at 0x7f61b03ca4f0>

We simulated a large system with 516 atoms at very long time scale (256.9 ps), and the recognition of H bonds can take hours.

%%time
hbonds = dimer.res_h(d_a_pairs = d_a_pairs, d_h_a_pairs = d_h_a_pairs, frames = traj.frames)
CPU times: user 3h 38min 2s, sys: 3h 59min 28s, total: 7h 37min 30s
Wall time: 2h 35min 1s
with open("/nobackup/rhtp48/data_ana/results/cage1-hbonds-500K.json", "w") as json_file:
    json.dump(hbonds, json_file)
json_file.close()

\(R(\text{D} \cdot \cdot \cdot \text{A}) - \angle \text{D} - \text{H} \cdot \cdot \cdot \text{A}\) heatmap#

All H bonds#

# Run this if you are loading data from saved json files
# Opening JSON file
f = open("/nobackup/rhtp48/data_ana/results/cage1-hbonds-500K.json")
 
# returns JSON object as a dictionary
hbonds = json.load(f)
# Iterating through the json list
nhbonds=[]
d_h_a_ang = []
r_d_a = []
for hbond in hbonds:
    nhbonds.append(hbond["n_hbonds"])
    a = hbond["hbonds"]
    for element in a:
        r_d_a.append(element["R(D-A)"])
        d_h_a_ang.append(element["DHA_ang"])
import matplotlib.pyplot as plt
_, ax =plt.subplots(figsize = (4.6, 3.2), constrained_layout =  True)
heatmap, xedges, yedges = np.histogram2d(np.array(r_d_a, dtype = float), np.array(d_h_a_ang, dtype = float), bins=150)

extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]

im = ax.imshow(heatmap.T, extent=extent, origin='lower', aspect = "auto")
cbar = ax.figure.colorbar(im, ax = ax)
cbar.ax.set_ylabel("Counts", rotation=90, labelpad = 8.5, va="center", ha ="center")
ax.set_xlabel("$R\mathrm{(D\cdot \cdot \cdot A)}$ ($\mathrm{\AA}$)")
ax.set_ylabel("$\mathrm{(D-H\cdot \cdot \cdot A)}$ angle (°)")

plt.savefig("test/cage1-all_hbonds_angle_r_heatmap.jpg", dpi = 600)

plt.show()
_images/69fb9130ca066b5ee777b48989d96b12660744b544755e657066fb9f0b51005c.png

H bonds with the participance of water molecules#

waters
({'O': 14, 'H1': 15, 'H2': 16},
 {'O': 17, 'H1': 18, 'H2': 19},
 {'O': 143, 'H1': 144, 'H2': 145},
 {'O': 146, 'H1': 147, 'H2': 148},
 {'O': 272, 'H1': 273, 'H2': 274},
 {'O': 275, 'H1': 276, 'H2': 277},
 {'O': 401, 'H1': 402, 'H2': 403},
 {'O': 404, 'H1': 405, 'H2': 406})
water_O = [water["O"] for water in waters]
water_O
[14, 17, 143, 146, 272, 275, 401, 404]

Filter the H bonds resolved, keep those with the participance of a water molecules only#

def has_common(a, b):
    a_set = set(a)
    b_set = set(b)
    if (a_set & b_set):
        return True
    else:
        return False
    
has_water_hbonds = []
for i, frame in enumerate(hbonds):
    hbonds = []
    for hbond in frame["hbonds"]:
        d_h_a = [*hbond["donor"].values(), *hbond["acceptor"].values()]
        if has_common(d_h_a, water_O):
            hbonds.append(hbond)
    has_water_hbonds.append({f"frame": i, "n_hbonds": len(hbonds), "hbonds": hbonds})
has_water_hbonds[0]["hbonds"]
[{'donor': {'O': 24, 'H': 25},
  'acceptor': {'O': 401},
  'R(D-A)': 2.726292124271186,
  'DHA_ang': 159.98746612853796,
  'r(D-H)': 0.9967552553430291},
 {'donor': {'O': 411, 'H': 412},
  'acceptor': {'O': 14},
  'R(D-A)': 2.726051523249053,
  'DHA_ang': 160.5043633242915,
  'r(D-H)': 1.001683650666012},
 {'donor': {'O': 153, 'H': 154},
  'acceptor': {'O': 272},
  'R(D-A)': 2.725839022339603,
  'DHA_ang': 160.58162299841513,
  'r(D-H)': 1.0020543270949314},
 {'donor': {'O': 282, 'H': 283},
  'acceptor': {'O': 143},
  'R(D-A)': 2.7011264954331087,
  'DHA_ang': 161.4098213827206,
  'r(D-H)': 0.9996765075384993},
 {'donor': {'N': 410, 'H2': 285},
  'acceptor': {'O': 404},
  'R(D-A)': 2.864580984838505,
  'DHA_ang': 164.3774626679764,
  'r(D-H)': 1.032222888114154},
 {'donor': {'N': 152, 'H2': 27},
  'acceptor': {'O': 146},
  'R(D-A)': 2.8591792887230647,
  'DHA_ang': 163.56178179798138,
  'r(D-H)': 1.032487326611596},
 {'donor': {'N': 281, 'H2': 414},
  'acceptor': {'O': 275},
  'R(D-A)': 2.8287980881769776,
  'DHA_ang': 163.15582670406786,
  'r(D-H)': 1.035511301914456},
 {'donor': {'N': 23, 'H2': 156},
  'acceptor': {'O': 17},
  'R(D-A)': 2.8623624040188216,
  'DHA_ang': 164.02453218550465,
  'r(D-H)': 1.035142855861238}]
with open("/nobackup/rhtp48/data_ana/results/cage1-has-water-hbonds-500K.json", "w") as json_file:
    json.dump(has_water_hbonds, json_file)
json_file.close()
# Iterating through the json list
nhbonds=[]
d_h_a_ang = []
r_d_a = []
for hbond in has_water_hbonds:
    nhbonds.append(hbond["n_hbonds"])
    a = hbond["hbonds"]
    for element in a:
        r_d_a.append(element["R(D-A)"])
        d_h_a_ang.append(element["DHA_ang"])
import matplotlib.pyplot as plt
_, ax =plt.subplots(figsize = (4.6, 3.2), constrained_layout =  True)
heatmap, xedges, yedges = np.histogram2d(np.array(r_d_a, dtype = float), np.array(d_h_a_ang, dtype = float), bins=150)

extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]

im = ax.imshow(heatmap.T, extent=extent, origin='lower', aspect = "auto")
cbar = ax.figure.colorbar(im, ax = ax)
cbar.ax.set_ylabel("Counts", rotation=90, labelpad = 8.5, va="center", ha ="center")
ax.set_xlabel("$R\mathrm{(D\cdot \cdot \cdot A)}$ ($\mathrm{\AA}$)")
ax.set_ylabel("$\mathrm{(D-H\cdot \cdot \cdot A)}$ angle (°)")

plt.savefig("test/cage1-has_water_hbonds_angle_r_heatmap.jpg", dpi = 600)

plt.show()
_images/e6c8d8080459553fe17880af9975feb032e0da2fb6b5ad4ec8bc44fc9fd0de9c.png

H bond lifetime#

All H bonds#

c1_t, c1_C_ts_mean, c1_C_ts_error = dimer.auto_cor(nframes = 5000, skip = 100, data = "/nobackup/rhtp48/data_ana/results/cage1-hbonds-500K.json", index = ":")
c1_C_ts_mean
array([1.        , 0.78365306, 0.64408114, 0.54720111, 0.47499348,
       0.41793104, 0.37138124, 0.33213615, 0.29868346, 0.27004558,
       0.24516531, 0.22360301, 0.20444858, 0.18752604, 0.17225393,
       0.15881247, 0.14681956, 0.13595855, 0.12599585, 0.11686875,
       0.10862518, 0.10111938, 0.09427445, 0.08796273, 0.08226667,
       0.07685842, 0.0719501 , 0.06748487, 0.06337391, 0.05956119,
       0.05602405, 0.05279543, 0.04978264, 0.0470523 , 0.04447146,
       0.04204273, 0.03980548, 0.03775526, 0.0358452 , 0.03403376,
       0.0323862 , 0.03082745, 0.02929539, 0.02784549, 0.02648702,
       0.02520094, 0.02406221, 0.02294833, 0.02187818, 0.02087655])
c1_x_fit, c1_y_fit, c1_params = dimer.bi_exp_fit(c1_t, c1_C_ts_mean, amp1 = 0.35922814, tau1 = 5.98152426, tau2 = 1.22001)
c1_tau = dimer.calc_tau(*c1_params)
The fitted biexponential function paramters are:
A: 0.47960248157210766, tau1: 7.086391337617406, tau2: 1.3125274137234557
The H-bond lifetime is 4.081686879882692 ps.
fig, ax = plt.subplots()

ax.errorbar(c1_t, c1_C_ts_mean, yerr = c1_C_ts_error, fmt='o', color="tab:grey",
                    ecolor='#bdbdbd', elinewidth=3, capsize=0, zorder = 0); 
ax.plot(c1_x_fit, c1_y_fit, color = "#08306b", zorder = 1)

ax.text(15.0, 0.9, r"$\tau$ = %.2f ps"%c1_tau)

# ax.xaxis.set_major_locator(MultipleLocator(5))
ax.set_xlabel("$t$ (ps)")
ax.set_ylabel("$C_{\mathrm{HB}}(t)$")
plt.savefig("test/cage1_all_hbond_lifetime_500K.jpg", dpi = 600)
plt.show()
_images/517acaaae41f346446dcf6ec2ab1add5d1e63cd7d8def6edc2aab344c9888e99.png

H bonds formed with a water molecule#

c1_t, c1_C_ts_mean, c1_C_ts_error = dimer.auto_cor(nframes = 5000, skip = 100, data = "/nobackup/rhtp48/data_ana/results/cage1-has-water-hbonds-500K.json", index = ":")
c1_C_ts_mean
array([1.        , 0.73088204, 0.55191651, 0.43177474, 0.3470527 ,
       0.28622472, 0.23942545, 0.20259308, 0.17290821, 0.14830441,
       0.12817609, 0.11123238, 0.09693985, 0.08468134, 0.0743088 ,
       0.06540281, 0.05769645, 0.05130411, 0.04565903, 0.0402614 ,
       0.03559201, 0.03155476, 0.02801979, 0.02469743, 0.02212224,
       0.019557  , 0.01733431, 0.01537476, 0.01366477, 0.01201793,
       0.01050928, 0.00920126, 0.0080454 , 0.00710689, 0.00624038,
       0.00545512, 0.00482987, 0.00426726, 0.00380215, 0.00347118,
       0.00321754, 0.00294797, 0.00266208, 0.00236212, 0.00215375,
       0.00197273, 0.00181923, 0.00162575, 0.00146898, 0.00135168])
c1_x_fit, c1_y_fit, c1_params = dimer.bi_exp_fit(c1_t, c1_C_ts_mean, amp1 = 0.35922814, tau1 = 5.98152426, tau2 = 1.22001)
c1_tau = dimer.calc_tau(*c1_params)
The fitted biexponential function paramters are:
A: 0.44937016787700423, tau1: 3.9978218254149307, tau2: 1.0343073313291071
The H-bond lifetime is 2.3660223370423887 ps.
fig, ax = plt.subplots()

ax.errorbar(c1_t, c1_C_ts_mean, yerr = c1_C_ts_error, fmt='o', color="tab:grey",
                    ecolor='#bdbdbd', elinewidth=3, capsize=0, zorder = 0); 
ax.plot(c1_x_fit, c1_y_fit, color = "#08306b", zorder = 1)

ax.text(15.0, 0.9, r"$\tau$ = %.2f ps"%c1_tau)

ax.set_xlabel("$t$ (ps)")
ax.set_ylabel("$C_{\mathrm{HB}}(t)$")
plt.savefig("test/cage1_has_water_hbond_lifetime_500K.jpg", dpi = 600)
plt.show()
_images/76176ff03ba1bcbff154101d3c7265ed0ea6009f2590a0cb9201b4e73577bb8f.png