291 lines
8.6 KiB
Python
Executable File
291 lines
8.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# ----------------------------------------------------------------------
|
|
# LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
|
# https://www.lammps.org/ Sandia National Laboratories
|
|
# LAMMPS Development team: developers@lammps.org
|
|
#
|
|
# Copyright (2003) Sandia Corporation. Under the terms of Contract
|
|
# DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
|
|
# certain rights in this software. This software is distributed under
|
|
# the GNU General Public License.
|
|
#
|
|
# See the README file in the top-level LAMMPS directory.
|
|
# -------------------------------------------------------------------------
|
|
# Author: Germain Clavier (Unicaen), germain.clavier at unicaen.fr
|
|
|
|
"""
|
|
Plot LAMMPS tabulated forces.
|
|
"""
|
|
|
|
import argparse
|
|
import numpy as np
|
|
import os
|
|
import logging
|
|
import sys
|
|
from matplotlib import pyplot as plt
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
units = {
|
|
"lj": {
|
|
"Distance": "Reduced units",
|
|
"Energy": "Reduced units",
|
|
"Force": "Reduced units",
|
|
"kb": 1,
|
|
},
|
|
"real": {
|
|
"Distance": "[A]",
|
|
"Energy": "[kcal/mol]",
|
|
"Force": "[kcal/mol/A]",
|
|
"kb": 0.001985875,
|
|
},
|
|
"metal": {
|
|
"Distance": "[A]",
|
|
"Energy": "[eV]",
|
|
"Force": "[eV/A]",
|
|
"kb": 8.6173332e-5,
|
|
},
|
|
"si": {"Distance": "[m]", "Energy": "[J]", "Force": "[N]", "kb": 1.380649e-23},
|
|
"cgs": {
|
|
"Distance": "[cm]",
|
|
"Energy": "[ergs]",
|
|
"Force": "[dynes]",
|
|
"kb": 1.3806504e-16,
|
|
},
|
|
"electron": {
|
|
"Distance": "[Bohr]",
|
|
"Energy": "[Hartrees]",
|
|
"Force": "[Hartree/Bohr]",
|
|
"kb": 3.16681534e-6,
|
|
},
|
|
"micro": {
|
|
"Distance": "[um]",
|
|
"Energy": "[pg·um^2/us^2]",
|
|
"Force": "[pg·um/us^2]",
|
|
"kb": 1.3806504e-8,
|
|
},
|
|
"nano": {
|
|
"Distance": "[nm]",
|
|
"Energy": "[ag·nm^2/ns^2]",
|
|
"Force": "[ag·nm/ns^2]",
|
|
"kb": 0.013806504,
|
|
},
|
|
}
|
|
|
|
|
|
def compute_energy(tp):
|
|
r = tp[0]
|
|
fo = tp[2]
|
|
e = np.zeros(r.shape)
|
|
for i, (ri, fi) in enumerate(zip(r, fo)):
|
|
if i == 0:
|
|
continue
|
|
dr = ri - r[i - 1]
|
|
e[i] = e[i - 1] - dr * fo[i - 1]
|
|
e -= e[-1]
|
|
return e
|
|
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="""
|
|
Plots LAMMPS tabulated forces. This script takes a table
|
|
file as an input and plots all the tabulated forces inside with their
|
|
corresponding energy. The forces label is the token used to name the
|
|
force in the file. It can be used to output all the forces in separate
|
|
files and/or recompute the energy from forces through finite difference
|
|
(assuming e(rc)=0). This script requires the matplotlib and numpy
|
|
Python libraries. Bitmap format is not supported.
|
|
"""
|
|
)
|
|
parser.add_argument(
|
|
"-u",
|
|
"--units",
|
|
dest="units",
|
|
default="real",
|
|
help="Units of the file (LAMMPS units system)",
|
|
)
|
|
parser.add_argument(
|
|
"-f",
|
|
"--file",
|
|
dest="infile",
|
|
default="",
|
|
help="File to read",
|
|
)
|
|
parser.add_argument(
|
|
"-x",
|
|
dest="xrange",
|
|
default="",
|
|
help="xrange separated by : (for negative values use the '=' sign: -x=-3:10)",
|
|
)
|
|
parser.add_argument(
|
|
"-y",
|
|
dest="yrange",
|
|
default="",
|
|
help="yrange separated by :",
|
|
)
|
|
parser.add_argument(
|
|
"-t",
|
|
dest="temp",
|
|
default=None,
|
|
type=float,
|
|
help="temperature for KbT plot [default none]",
|
|
)
|
|
parser.add_argument(
|
|
"-d",
|
|
"--diff-num",
|
|
dest="recompute",
|
|
action="store_true",
|
|
help="Recompute the energies from forces and distances through finite differences",
|
|
)
|
|
parser.add_argument(
|
|
"-e",
|
|
dest="extract",
|
|
action="store_true",
|
|
help="Extract the forces in separate files",
|
|
)
|
|
args = parser.parse_args()
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
##########
|
|
# Manage arguments
|
|
|
|
udistance = units[args.units]["Distance"]
|
|
uenergy = units[args.units]["Energy"]
|
|
uforce = units[args.units]["Force"]
|
|
kb = units[args.units]["kb"]
|
|
rlabel = " ".join(["Rij", udistance])
|
|
elabel = " ".join(["E", uenergy])
|
|
flabel = " ".join(["F", uforce])
|
|
etitle = "Energy"
|
|
ftitle = "Force"
|
|
font = "DejaVu Sans"
|
|
fontsize = 30
|
|
|
|
infile = args.infile
|
|
if not os.path.isfile(infile):
|
|
logger.error("Input file not found")
|
|
sys.exit(1)
|
|
|
|
toplot = []
|
|
with open(infile, "r") as f:
|
|
lines = iter(f.readlines())
|
|
while True:
|
|
try:
|
|
r = []
|
|
force = []
|
|
ener = []
|
|
tok = []
|
|
while not tok:
|
|
tok = next(lines).partition("#")[0].rstrip()
|
|
logger.info("Found {} token".format(tok))
|
|
infos = next(lines).split()
|
|
npoints = int(infos[1])
|
|
next(lines)
|
|
if "bitmap" in infos:
|
|
logger.info("Unsupported bitmap format for token {:s}".format(tok))
|
|
for _ in range(npoints):
|
|
continue
|
|
else:
|
|
for i in range(npoints):
|
|
line = next(lines).split()
|
|
r.append(float(line[1]))
|
|
ener.append(float(line[2]))
|
|
force.append(float(line[3]))
|
|
r = np.array(r)
|
|
ener = np.array(ener)
|
|
force = np.array(force)
|
|
toplot.append([r, ener, force, tok])
|
|
tok = []
|
|
next(lines)
|
|
except StopIteration:
|
|
break
|
|
if args.recompute:
|
|
etitle = "Estimated energy"
|
|
for tp in toplot:
|
|
tp[1] = compute_energy(tp)
|
|
|
|
fig, axes = plt.subplots(1, 2)
|
|
|
|
for tp in toplot:
|
|
axes[0].plot(tp[0], tp[1], label=tp[3], linewidth=3)
|
|
axes[1].plot(tp[0], tp[2], label=tp[3], linewidth=3)
|
|
hmin, hmax = axes[1].get_xlim()
|
|
axes[1].hlines(0, hmin, hmax, color="black", linewidth=3, linestyles="dashdot")
|
|
|
|
if args.temp:
|
|
if args.temp > 0:
|
|
hmin, hmax = axes[0].get_xlim()
|
|
axes[0].hlines(
|
|
kb * args.temp,
|
|
hmin,
|
|
hmax,
|
|
color="orange",
|
|
label=r"$k_BT$",
|
|
linewidth=3,
|
|
linestyles="dashdot",
|
|
)
|
|
axes[0].text(hmax / 2.0, kb * args.temp, "KbT", fontsize=0.7 * fontsize)
|
|
logger.info("KbT value= {:e} {:s}".format(kb * args.temp, uenergy))
|
|
else:
|
|
logger.info("Invalid temperature value: {:e}".format(args.temp))
|
|
|
|
if args.xrange:
|
|
xmin, xmax = list(map(float, args.xrange.split(":")))
|
|
axes[0].set_xlim(xmin, xmax)
|
|
axes[1].set_xlim(xmin, xmax)
|
|
if args.yrange:
|
|
ymin, ymax = list(map(float, args.yrange.split(":")))
|
|
axes[0].set_ylim(ymin, ymax)
|
|
axes[1].set_ylim(ymin, ymax)
|
|
|
|
# Setting axes 0
|
|
axes[0].set_title(etitle, fontsize=fontsize)
|
|
axes[0].set_xlabel(
|
|
rlabel, fontname=font, fontsize=fontsize
|
|
) # xlabel name, size 30pts
|
|
axes[0].set_ylabel(
|
|
elabel, fontname=font, fontsize=fontsize
|
|
) # ylabel name, size 30pts
|
|
axes[0].tick_params(
|
|
axis="both", which="major", labelsize=fontsize
|
|
) # Biggers ticks, bigger tick labels!
|
|
|
|
# Setting axes 1
|
|
axes[1].set_title(ftitle, fontsize=fontsize)
|
|
axes[1].legend(frameon=False, fontsize=fontsize) # Fat font, no frame
|
|
axes[1].set_xlabel(
|
|
rlabel, fontname=font, fontsize=fontsize
|
|
)
|
|
axes[1].set_ylabel(
|
|
flabel, fontname=font, fontsize=fontsize
|
|
)
|
|
axes[1].tick_params(
|
|
axis="both", which="major", labelsize=fontsize
|
|
)
|
|
|
|
figManager = plt.get_current_fig_manager()
|
|
figManager.window.showMaximized()
|
|
plt.show()
|
|
|
|
if args.extract:
|
|
for tp in toplot:
|
|
outfile = "".join([tp[3], ".plot"])
|
|
logger.info("Writing file {}".format(outfile))
|
|
with open(outfile, "w") as f:
|
|
f.write("# {} force extracted from {}\n".format(tp[3], infile))
|
|
f.write("# {:^20} {:^20} {:^20}\n".format("r", "energy", "force"))
|
|
for a, b, c in zip(tp[0], tp[1], tp[2]):
|
|
f.write("{:>18.16e} {:>18.16e} {:>18.16e}\n".format(a, b, c))
|
|
return
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
raise SystemExit("User interruption.")
|