first commit
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# Ignore everything in the repository root
|
||||||
|
/*
|
||||||
|
|
||||||
|
# Keep the repository metadata files
|
||||||
|
!/.gitignore
|
||||||
|
!/Readme.org
|
||||||
|
|
||||||
|
# Keep the analysis script
|
||||||
|
!/voronoi_count_diff.py*
|
||||||
|
|
||||||
|
# Keep only the selected trajectory files
|
||||||
|
!/050Li3PS4-050LiI_thin1000.lammpstrj
|
||||||
|
!/060Li3PS4-040LiI_thin1000.lammpstrj
|
||||||
|
!/070Li3PS4-030LiI_thin1000.lammpstrj
|
||||||
|
!/080Li3PS4-020LiI_thin1000.lammpstrj
|
||||||
|
!/090Li3PS4-010LiI_thin1000.lammpstrj
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+132
@@ -0,0 +1,132 @@
|
|||||||
|
#+TITLE: Nearest P/I Environment Analysis for Li₃PS₄–LiI Glasses
|
||||||
|
#+AUTHOR: Minami Sakuma
|
||||||
|
#+OPTIONS: toc:2 num:t
|
||||||
|
|
||||||
|
* Overview
|
||||||
|
|
||||||
|
This repository contains a Python program for analyzing the local
|
||||||
|
environments of Li⁺ ions in Li₃PS₄–LiI glass trajectories.
|
||||||
|
|
||||||
|
For each Li⁺ ion, the program calculates the distances to the nearest
|
||||||
|
P atom and I⁻ ion under periodic boundary conditions. It then determines
|
||||||
|
whether the Li⁺ ion is closer to P or I⁻.
|
||||||
|
|
||||||
|
The following two quantities are calculated for each composition:
|
||||||
|
|
||||||
|
- The P fraction in the entire system:
|
||||||
|
P / (P + I)
|
||||||
|
- The fraction of Li⁺ ions whose nearest center is P:
|
||||||
|
N_{Li-near-P} / N_{Li}
|
||||||
|
|
||||||
|
The second quantity is calculated for each molecular-dynamics step and
|
||||||
|
then averaged over all steps.
|
||||||
|
|
||||||
|
* Target System
|
||||||
|
|
||||||
|
The target system is Li₃PS₄–LiI glass.
|
||||||
|
|
||||||
|
The following compositions are included in this repository:
|
||||||
|
|
||||||
|
- 80Li₃PS₄–20LiI
|
||||||
|
- 70Li₃PS₄–30LiI
|
||||||
|
- 60Li₃PS₄–40LiI
|
||||||
|
- 50Li₃PS₄–50LiI
|
||||||
|
|
||||||
|
* Analysis Procedure
|
||||||
|
|
||||||
|
For each molecular-dynamics step, the program performs the following
|
||||||
|
operations:
|
||||||
|
|
||||||
|
1. Read the coordinates of Li⁺, P, S, and I⁻.
|
||||||
|
2. Calculate all Li⁺–P distances under periodic boundary conditions.
|
||||||
|
3. Calculate all Li⁺–I⁻ distances under periodic boundary conditions.
|
||||||
|
4. Identify the nearest P atom and I⁻ ion for each Li⁺ ion.
|
||||||
|
5. Classify each Li⁺ ion according to whether P or I⁻ is closer.
|
||||||
|
6. Calculate the fraction of Li⁺ ions whose nearest center is P.
|
||||||
|
7. Average the fraction over all simulation steps.
|
||||||
|
|
||||||
|
The minimum-image convention is used to calculate distances under
|
||||||
|
periodic boundary conditions.
|
||||||
|
|
||||||
|
* Requirements
|
||||||
|
|
||||||
|
- Python 3
|
||||||
|
- NumPy
|
||||||
|
- Matplotlib
|
||||||
|
|
||||||
|
The required Python packages can be installed using:
|
||||||
|
|
||||||
|
#+BEGIN_SRC shell
|
||||||
|
pip install numpy matplotlib
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
* Repository Contents
|
||||||
|
|
||||||
|
#+BEGIN_EXAMPLE
|
||||||
|
.
|
||||||
|
├── calc_share.py
|
||||||
|
├── 050Li3PS4-050LiI_thin100.lammpstrj
|
||||||
|
├── 060Li3PS4-040LiI_thin100.lammpstrj
|
||||||
|
├── 070Li3PS4-030LiI_thin100.lammpstrj
|
||||||
|
├── 080Li3PS4-020LiI_thin100.lammpstrj
|
||||||
|
├── Readme.org
|
||||||
|
└── .gitignore
|
||||||
|
#+END_EXAMPLE
|
||||||
|
|
||||||
|
* Usage
|
||||||
|
|
||||||
|
Run the program by specifying one or more trajectory files with the
|
||||||
|
=-i= option:
|
||||||
|
|
||||||
|
#+BEGIN_SRC shell
|
||||||
|
python calc_share.py -i \
|
||||||
|
080Li3PS4-020LiI_thin100.lammpstrj \
|
||||||
|
070Li3PS4-030LiI_thin100.lammpstrj \
|
||||||
|
060Li3PS4-040LiI_thin100.lammpstrj \
|
||||||
|
050Li3PS4-050LiI_thin100.lammpstrj
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
A wildcard can also be used:
|
||||||
|
|
||||||
|
#+BEGIN_SRC shell
|
||||||
|
python calc_share.py -i *.lammpstrj
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
* Output
|
||||||
|
|
||||||
|
The program generates a plot containing the following quantities:
|
||||||
|
|
||||||
|
- Blue line: P / (P + I) in the entire system
|
||||||
|
- Green line: fraction of Li⁺ ions whose nearest center is P,
|
||||||
|
averaged over all simulation steps
|
||||||
|
|
||||||
|
The plot is saved as:
|
||||||
|
|
||||||
|
#+BEGIN_EXAMPLE
|
||||||
|
voronoi_count_diff.pdf
|
||||||
|
#+END_EXAMPLE
|
||||||
|
|
||||||
|
* Expected Trajectory Format
|
||||||
|
|
||||||
|
The current parser assumes a specific LAMMPS trajectory format:
|
||||||
|
|
||||||
|
- Each atom record contains six columns.
|
||||||
|
- The third column contains the element name:
|
||||||
|
Li, P, S, or I.
|
||||||
|
- The fourth to sixth columns contain the atomic coordinates.
|
||||||
|
- The simulation cell is orthorhombic.
|
||||||
|
- The number and order of header fields are fixed for every frame.
|
||||||
|
|
||||||
|
If the trajectory format differs from these assumptions, the data-loading
|
||||||
|
section of =calc_share.py= must be modified.
|
||||||
|
|
||||||
|
* Notes
|
||||||
|
|
||||||
|
- Li⁺ ions for which the nearest P and I⁻ distances are exactly equal
|
||||||
|
are excluded from both counts in the current implementation.
|
||||||
|
- This analysis is based on nearest-neighbor distances and is not a
|
||||||
|
rigorous Voronoi tessellation.
|
||||||
|
- The local P fraction around Li⁺ should be interpreted by comparison
|
||||||
|
with the P / (P + I) fraction in the entire system.
|
||||||
|
- The current code assumes that both P and I⁻ are present in every
|
||||||
|
trajectory.
|
||||||
Executable
+145
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# ./voronoi_count_diff.py -i *trj
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
|
||||||
|
"""
|
||||||
|
複数の組成のtrjファイルを読み込み、
|
||||||
|
横軸:組成(系のP/P+I比率)
|
||||||
|
縦軸:
|
||||||
|
青:系のP/P+Iの比率
|
||||||
|
緑:Pに近いLi/Liの総数の比率(step平均)
|
||||||
|
をプロットするプログラム
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class LoadData():
|
||||||
|
def __init__(self, trjfile):
|
||||||
|
self.trjfile = trjfile
|
||||||
|
self.loadtrj()
|
||||||
|
|
||||||
|
def loadtrj(self):
|
||||||
|
print(f"Loading {os.path.basename(self.trjfile)} ...")
|
||||||
|
with open(self.trjfile) as o:
|
||||||
|
d = o.read()
|
||||||
|
lines = d.splitlines()
|
||||||
|
self.atoms = int(lines[3])
|
||||||
|
|
||||||
|
x_bnd = [float(x) for x in lines[5].split()]
|
||||||
|
y_bnd = [float(x) for x in lines[6].split()]
|
||||||
|
z_bnd = [float(x) for x in lines[7].split()]
|
||||||
|
self.box_size = np.array(
|
||||||
|
[x_bnd[1]-x_bnd[0], y_bnd[1]-y_bnd[0], z_bnd[1]-z_bnd[0]])
|
||||||
|
|
||||||
|
data = np.array(" ".join(lines).split())
|
||||||
|
self.data = data.reshape(-1, 28+self.atoms*6)
|
||||||
|
self.step = self.data.shape[0]
|
||||||
|
data = self.data[:, 28:].reshape(self.step, self.atoms, -1)
|
||||||
|
|
||||||
|
self.li_data = data[data[:, :, 2] == "Li"].reshape(self.step, -1, 6)
|
||||||
|
self.li_xyz = self.li_data[:, :, 3:].astype(float)
|
||||||
|
|
||||||
|
self.p_data = data[data[:, :, 2] == "P"].reshape(self.step, -1, 6)
|
||||||
|
self.p_xyz = self.p_data[:, :, 3:].astype(float)
|
||||||
|
|
||||||
|
self.i_data = data[data[:, :, 2] == "I"].reshape(self.step, -1, 6)
|
||||||
|
self.i_xyz = self.i_data[:, :, 3:].astype(float)
|
||||||
|
|
||||||
|
self.s_data = data[data[:, :, 2] == "S"].reshape(self.step, -1, 6)
|
||||||
|
self.s_xyz = self.s_data[:, :, 3:].astype(float)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_pbc_distance(self, xyz1, xyz2):
|
||||||
|
d = xyz1[:, np.newaxis, :] - xyz2[np.newaxis, :, :]
|
||||||
|
d -= self.box_size * np.round(d / self.box_size)
|
||||||
|
return np.linalg.norm(d, axis=-1)
|
||||||
|
|
||||||
|
def calc_ratios(self):
|
||||||
|
p_ratios = []
|
||||||
|
|
||||||
|
print(f"Calculating for {os.path.basename(self.trjfile)} ...")
|
||||||
|
|
||||||
|
# 系のP/P+Iの比率はステップ間で変わらないため、最初のステップで計算
|
||||||
|
p_num = self.p_xyz[0].shape[0]
|
||||||
|
i_num = self.i_xyz[0].shape[0]
|
||||||
|
total_num = p_num + i_num
|
||||||
|
system_p_ratio = p_num / total_num if total_num > 0 else 0.0
|
||||||
|
|
||||||
|
# 各ステップのPに近いLiの比率を計算
|
||||||
|
for step in range(self.step):
|
||||||
|
li_pos = self.li_xyz[step]
|
||||||
|
p_pos = self.p_xyz[step]
|
||||||
|
i_pos = self.i_xyz[step]
|
||||||
|
|
||||||
|
# 周期境界条件を考慮し、距離を計算
|
||||||
|
diff_li_p = self.get_pbc_distance(li_pos, p_pos)
|
||||||
|
diff_li_i = self.get_pbc_distance(li_pos, i_pos)
|
||||||
|
|
||||||
|
# 各Liについて、最も近いPとIまでの距離を取得
|
||||||
|
min_diff_p = np.min(diff_li_p, axis=1)
|
||||||
|
min_diff_i = np.min(diff_li_i, axis=1)
|
||||||
|
|
||||||
|
# Pの方が近いLiの数、Iの方が近いLiの数をカウント
|
||||||
|
count_p = np.sum(min_diff_p < min_diff_i)
|
||||||
|
count_i = np.sum(min_diff_p > min_diff_i)
|
||||||
|
|
||||||
|
# Pが一番近いLiの比率を計算
|
||||||
|
total = count_p + count_i
|
||||||
|
ratio = count_p / total if total > 0 else 0.0
|
||||||
|
p_ratios.append(ratio)
|
||||||
|
|
||||||
|
# ステップ平均を計算
|
||||||
|
avg_near_p_ratio = np.mean(p_ratios)
|
||||||
|
|
||||||
|
return system_p_ratio, avg_near_p_ratio
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
description = """This is a test program"""
|
||||||
|
par = argparse.ArgumentParser(description=description)
|
||||||
|
|
||||||
|
par.add_argument('-i', '--trjfiles', default="", required=True, nargs="+",
|
||||||
|
help='input file')
|
||||||
|
args = par.parse_args()
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for trjfile in args.trjfiles:
|
||||||
|
trj = LoadData(trjfile)
|
||||||
|
sys_ratio, avg_near_ratio = trj.calc_ratios()
|
||||||
|
results.append((sys_ratio, avg_near_ratio))
|
||||||
|
|
||||||
|
comps = [int(r[0]*100) for r in results][::-1]
|
||||||
|
y_sys_ratios = [r[0] for r in results][::-1]
|
||||||
|
y_near_ratios = [r[1] for r in results][::-1]
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# プロット
|
||||||
|
# --------------------------------------------------
|
||||||
|
print("Plotting results...")
|
||||||
|
fig, ax = plt.subplots(figsize=(5, 3.5))
|
||||||
|
|
||||||
|
color1, color2 = "tab:blue", "tab:green"
|
||||||
|
|
||||||
|
ax.set_xlabel("Composition")
|
||||||
|
ax.set_ylabel("Ratio of P")
|
||||||
|
|
||||||
|
xtick_labels = [f"LPSI{comp:02d}" for comp in comps]
|
||||||
|
ax.set_xticks(comps)
|
||||||
|
ax.set_xticklabels(xtick_labels, fontsize=11)
|
||||||
|
|
||||||
|
# マーカーをつけてプロット(複数の組成点が分かりやすいように)
|
||||||
|
ax.plot(comps, y_sys_ratios, color=color1, marker='o', linestyle='-',
|
||||||
|
label="Ratio of P (system)")
|
||||||
|
ax.plot(comps, y_near_ratios, color=color2, marker='s', linestyle='-',
|
||||||
|
label="Ratio of P (near Li, step avg)")
|
||||||
|
|
||||||
|
ax.legend(loc='best')
|
||||||
|
ax.grid(True, linestyle='--', alpha=0.7)
|
||||||
|
|
||||||
|
fig.tight_layout()
|
||||||
|
fig.savefig("voronoi_count_diff.pdf", dpi=300)
|
||||||
|
plt.show()
|
||||||
Reference in New Issue
Block a user