first commit

This commit is contained in:
佐久間 美波
2026-09-19 19:22:55 +09:00
commit d8abf110b6
8 changed files with 248783 additions and 0 deletions
+16
View File
@@ -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
View File
@@ -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.
+145
View File
@@ -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()