Diffusion anisotropy#

Import packages#

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from cage_data import cage1_info
from fishmol.utils import vector, Arrow3D, h_channel, calc_freq
from fishmol import msd
from fishmol import style

Load water CoM data#

water_com = pd.read_excel("test/cage1-500K-water-com.xlsx", header=0, index_col=0, engine = "openpyxl")
water_com.head()
water1_x water1_y water1_z water2_x water2_y water2_z water3_x water3_y water3_z water4_x ... water5_z water6_x water6_y water6_z water7_x water7_y water7_z water8_x water8_y water8_z
0 10.586316 19.902134 10.380009 0.624155 10.923925 8.298079 -1.967392 4.261146 12.241481 4.693672 ... 4.725172 15.090375 8.649502 6.883228 17.759366 15.321196 2.865725 11.019290 3.578953 0.703805
1 10.597616 19.945151 10.393126 0.617881 10.942585 8.312331 -1.961926 4.253910 12.205821 4.689873 ... 4.745156 15.076014 8.640429 6.849210 17.749958 15.278667 2.889409 11.049146 3.587807 0.663123
2 10.610208 20.003821 10.408405 0.610859 10.967922 8.330438 -1.953047 4.243309 12.156421 4.687198 ... 4.767198 15.058083 8.628816 6.804086 17.738720 15.225162 2.920655 11.086905 3.600664 0.608757
3 10.622718 20.062112 10.416761 0.604688 10.993416 8.349495 -1.945208 4.229969 12.106099 4.687512 ... 4.786539 15.040753 8.617851 6.760128 17.728982 15.181476 2.948253 11.122864 3.614650 0.558271
4 10.636664 20.122749 10.416813 0.597829 11.020753 8.373153 -1.941190 4.211549 12.054352 4.691621 ... 4.808036 15.020218 8.605213 6.714651 17.717884 15.151479 2.970079 11.155729 3.629731 0.511643

5 rows × 24 columns

Calculate 1D diffusion coefficient on a spherical meshgrid#

water_num = 8
vecs, ave_d = msd.proj_d(df = water_com, num = water_num)
Progress: [■■■■■■■■■■■■■■■■■■■■] 100.0%
data = np.concatenate((vecs, ave_d.reshape((len(vecs), 1))), axis = 1)
results = pd.DataFrame(columns = ["x", "y", "z", "mean_D"], data = data)
results.head()
x y z mean_D
0 1.224647e-16 -7.498799e-33 -1.000000 0.000017
1 5.277535e-02 -3.231558e-18 -0.998606 0.000018
2 1.054036e-01 -6.454109e-18 -0.994430 0.000019
3 1.577381e-01 -9.658671e-18 -0.987481 0.000020
4 2.096329e-01 -1.283631e-17 -0.977780 0.000022
results.to_excel("test/cage1-500K-aniso.xlsx")
cell= [
    [21.2944000000,        0.0000000000,        0.0000000000],
    [-4.6030371123,       20.7909480472,        0.0000000000],
    [-0.9719093466,       -1.2106211379,       15.1054299403]
]

diags = [[1,0,0],[0,1,0],[0,0,1]]
diags = [ vector(diag, cell = cell, name = "m") for diag in diags]
[diag.to_cart() for diag in diags]
[<fishmol.utils.vector at 0x7fd125c34c10>,
 <fishmol.utils.vector at 0x7fd125c34bb0>,
 <fishmol.utils.vector at 0x7fd125c34d00>]
x = vecs[:,0]*ave_d
y = vecs[:,1]*ave_d
z = vecs[:,2]*ave_d
# ave_d = data.iloc[:,3].to_numpy()
fig = plt.figure(figsize = (5, 4.6))
ax = fig.add_axes([0.0,0.04,0.90,0.96], projection='3d')

zdirs = (None,)*3
labels = ("$a$", "$b$", "$c$")

# ax.plot_trisurf(x, y, z, edgecolor ='none', cmap='PuBu', alpha=0.8)
ax.scatter3D(x, y, z, edgecolor ='none', marker = ".", color = "#08519c", alpha=0.3)

for i, axis in enumerate(diags):
    axis = axis.array/15000
    a = Arrow3D([0, axis[0]], [0, axis[1]], [0, axis[2]], mutation_scale=15, 
            lw=1, arrowstyle="-|>", color="#252525")
    ax.add_artist(a)
    ax.text(*axis, labels[i], zdirs[i])

idx = np.where(ave_d == np.amax(ave_d))
h_path = vecs[idx]*ave_d[idx]

a = Arrow3D(*zip(np.zeros(3), h_path[0]), mutation_scale=15, 
        lw=1, arrowstyle="-|>", color="#b2182b")
ax.add_artist(a)
ax.text(*h_path[0], "D$_{max}$", None)

ax.set_xlabel("$D_x$")
ax.set_ylabel("$D_y$")
ax.set_zlabel("$D_z$")

ax.ticklabel_format(axis='both', style='sci', scilimits=[-4,4], useMathText=True)

plt.savefig("test/cage1-aniso-500K.jpg", dpi = 600)

plt.show()
/tmp/ipykernel_3339671/2059225541.py:31: MatplotlibDeprecationWarning: The 'renderer' parameter of do_3d_projection() was deprecated in Matplotlib 3.4 and will be removed two minor releases later.
  plt.savefig("test/cage1-aniso-500K.jpg", dpi = 600)
_images/75a9cf420215d690ab142f6abcfc023e303408c87f30f62f00a33dd01fd3531d.png

Voroni method to identify the diffusion channel of water molecules#

Cage 1 500 K#

The h-path is almost aligned with [100], and the following points used to devide channels

points = np.asarray([
    (0, -3, -2), (0, -3, -1), (0, -3, 0), (0, -3, 1), (0, -3, 2), (0, -3, 3),
    (0, -2, -3), (0, -2, -2), (0, -2, -1), (0, -2, 0), (0, -2, 1), (0, -2, 2), (0, -2, 3),
    (0, -1, -3), (0, -1, -2), (0, -1, -1), (0, -1, 0), (0, -1, 1), (0, -1, 2), (0, -1, 3),
    (0, 0, -3), (0, 0, -2), (0, 0, -1), (0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3),
    (0, 1, -3), (0, 1, -2), (0, 1, -1), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 1, 3),
    (0, 2, -3), (0, 2, -2), (0, 2, -1), (0, 2, 0), (0, 2, 1), (0, 2, 2), (0, 2, 3),
    (0, 3, -2), (0, 3, -1), (0, 3, 0), (0, 3, 1), (0, 3, 2), (0, 3, 3)
], dtype = np.float64)

cell = cage1_info.cell

# The coordination the points will be projected to new coordinate system, where w3 is the h_path with the highest proton diffusivity
w1 = vector([0, 1, 0], cell = cell, name = "m")
w2 = vector([0, 0, 1], cell = cell, name = "m")
w3 = vector([1, 0, 0], cell = cell, name = "m")

W = np.asarray([globals()[f'w{i + 1}'].array for i in range(3)])
points_arrs = [vector(point, cell = cell) for point in points]
for point_arr in points_arrs:
    point_arr.to_cart(normalise = False)
points = np.asarray([point.array for point in points_arrs])
points
array([[ 15.75293003, -59.95160187, -30.21085988],
       [ 14.78102068, -61.162223  , -15.10542994],
       [ 13.80911134, -62.37284414,   0.        ],
       [ 12.83720199, -63.58346528,  15.10542994],
       [ 11.86529264, -64.79408642,  30.21085988],
       [ 10.8933833 , -66.00470756,  45.31628982],
       [ 12.12180226, -37.95003268, -45.31628982],
       [ 11.14989292, -39.16065382, -30.21085988],
       [ 10.17798357, -40.37127496, -15.10542994],
       [  9.20607422, -41.58189609,   0.        ],
       [  8.23416488, -42.79251723,  15.10542994],
       [  7.26225553, -44.00313837,  30.21085988],
       [  6.29034618, -45.21375951,  45.31628982],
       [  7.51876515, -17.15908463, -45.31628982],
       [  6.54685581, -18.36970577, -30.21085988],
       [  5.57494646, -19.58032691, -15.10542994],
       [  4.60303711, -20.79094805,   0.        ],
       [  3.63112777, -22.00156919,  15.10542994],
       [  2.65921842, -23.21219032,  30.21085988],
       [  1.68730907, -24.42281146,  45.31628982],
       [  2.91572804,   3.63186341, -45.31628982],
       [  1.94381869,   2.42124228, -30.21085988],
       [  0.97190935,   1.21062114, -15.10542994],
       [  0.        ,   0.        ,   0.        ],
       [ -0.97190935,  -1.21062114,  15.10542994],
       [ -1.94381869,  -2.42124228,  30.21085988],
       [ -2.91572804,  -3.63186341,  45.31628982],
       [ -1.68730907,  24.42281146, -45.31628982],
       [ -2.65921842,  23.21219032, -30.21085988],
       [ -3.63112777,  22.00156919, -15.10542994],
       [ -4.60303711,  20.79094805,   0.        ],
       [ -5.57494646,  19.58032691,  15.10542994],
       [ -6.54685581,  18.36970577,  30.21085988],
       [ -7.51876515,  17.15908463,  45.31628982],
       [ -6.29034618,  45.21375951, -45.31628982],
       [ -7.26225553,  44.00313837, -30.21085988],
       [ -8.23416488,  42.79251723, -15.10542994],
       [ -9.20607422,  41.58189609,   0.        ],
       [-10.17798357,  40.37127496,  15.10542994],
       [-11.14989292,  39.16065382,  30.21085988],
       [-12.12180226,  37.95003268,  45.31628982],
       [-11.86529264,  64.79408642, -30.21085988],
       [-12.83720199,  63.58346528, -15.10542994],
       [-13.80911134,  62.37284414,   0.        ],
       [-14.78102068,  61.162223  ,  15.10542994],
       [-15.75293003,  59.95160187,  30.21085988],
       [-16.72483938,  58.74098073,  45.31628982]])
from scipy.spatial import voronoi_plot_2d
vor = h_channel(points[:,1:])
fig = voronoi_plot_2d(vor)
ax = plt.gca()

ax.set_xlim(-40,40)
ax.set_ylim(-40,40)

ax.set_xlabel("$y$ ($\AA$)")
ax.set_ylabel("$z$ ($\AA$)")
plt.show()
_images/f87c562784c3583e8813c78344c35faee446991c633c2dce95e0aabd92c0bc83.png

Load CoM data

water1 = water_com.iloc[:,1:3].to_numpy()
out = np.asarray(vor.sort_points(water1))
for n in vor.point_region:
    idx = np.where(out == n)
    globals()['region%s' % n] = water1[idx]

Calculate the frequency of switch channels#

The water molecule stayed inside the diffusion channel as we used a small part of our simulation data

calc_freq(out)
No timestep specified, defauting to 5 fs!
0.0
fig = voronoi_plot_2d(vor)
ax = plt.gca()

for n in vor.point_region:
    if globals()['region%s' % n].size > 0:
        ax.scatter(globals()['region%s' % n][:,0], globals()['region%s' % n][:,1], s = 30, linewidths=0, alpha = 0.1, zorder = 0)

ax.set_xlim(-40,40)
ax.set_ylim(-40,40)
ax.set_xlabel("$y$ ($\AA$)")
ax.set_ylabel("$z$ ($\AA$)")

# plt.savefig("test/cage1_voroni_500K_w1.jpg", dpi = 600)
plt.show()
_images/54f88788774972951922868549e47273666a77e2d6a96cb447d041af109b7b67.png