# OctaDist Copyright (C) 2019-2024 Rangsiman Ketkaew et al.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import argparse
import os
import sys
import octadist
from .octadist_gui import run_gui
from octadist.src.io import is_xyz, get_coord_xyz, extract_octa
[docs]
def check_file(file):
"""
Check if input file is exist or not.
Parameters
----------
file : str
Input file name.
Returns
-------
file : str
Input file name.
"""
exist = os.path.isfile(file)
if exist:
return file
else:
print(f"File not found: {file}")
sys.exit(1)
[docs]
def find_coord(file):
"""
Find atomic symbols and atomic coordinates of structure.
Parameters
----------
file : str
Input file name.
Returns
-------
atom : list
Atomic symbols.
coord : list
Atomic coordinates.
"""
if file.endswith(".xyz"):
if is_xyz(file):
atom, coord = get_coord_xyz(file)
else:
print(f"File type of input file is not supported: {file}")
sys.exit(1)
else:
print(f"File type of input file is not supported: {file}")
sys.exit(1)
atom = list(filter(None, atom))
return atom, coord
[docs]
def calc_param(coord):
"""
Calculate octahedral distortion parameters.
Parameters
----------
coord : array_like
Atomic coordinates of octahedral structure.
Returns
-------
computed : dict
Computed parameters: zeta, delta, sigma, theta.
"""
dist = octadist.CalcDistortion(coord)
computed = {
"zeta": dist.zeta,
"delta": dist.delta,
"sigma": dist.sigma,
"theta": dist.theta,
}
return computed
[docs]
def run_cli():
"""
OctaDist command-line interface (CLI).
This function has been implemented by entry points function
in setuptools package.
"""
description = """\
Octahedral Distortion Calculator:
A tool for computing octahedral distortion parameters in coordination complex.
For more details, please visit https://octadist.github.io.
"""
epilog = f"Rangsiman Ketkaew\tUpdated on {octadist.__release__}\tE-mail: {octadist.__email__}"
parser = argparse.ArgumentParser(
prog="octadist_cli",
description=description,
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=epilog,
)
# input/output
parser.add_argument(
"-i",
"--inp",
action="store",
type=str,
metavar="INPUT",
help="Input structure in .xyz format",
)
parser.add_argument(
"-f", "--format", action="store_true", help="Show formatted output summary"
)
# octahedron parameters
parser.add_argument(
"-r",
"--ref-index",
type=int,
metavar="REF_CENTER_ATOM",
dest="ref_index",
default=0,
help="Index of the reference center atom. Default to 0",
)
parser.add_argument(
"-c",
"--cutoff",
type=float,
metavar="CUTOFF_DIST",
dest="cutoff",
default=2.8,
help="Cutoff distance (in Angstroms) for determining octahedron. Default to 2.8",
)
parser.add_argument(
"-s",
"--save",
action="store",
type=str,
metavar="OUTPUT",
help="Save formatted output to text file, "
"please specify name of OUTPUT file without '.txt' extension",
)
parser.add_argument(
"-p",
"--par",
type=str,
nargs="+",
choices=["zeta", "delta", "sigma", "theta"],
metavar="PARAMETER",
help="Select which the parameter (zeta, delta, sigma, theta) to show",
)
parser.add_argument(
"--show",
type=str,
nargs="+",
choices=["atom", "coord"],
metavar="MOL",
help="Show atomic symbol (atom) and atomic coordinate (coord) of octahedral structure",
)
parser.add_argument(
"-g",
"--gui",
action="store_true",
help="launch OctaDist GUI (this option is the same as 'octadist' command",
)
parser.add_argument("-a", "--about", action="store_true", help="Show program info")
parser.add_argument(
"-v", "--version", action="version", version=octadist.__version__
)
args = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
if args.about:
print("\nOctaDist Program info")
print("=====================")
print(f"- Name\t\t=\t{octadist.__title__}")
print(f"- Author\t=\t{octadist.__author__}")
print(f"- Version\t=\t{octadist.__version__}")
print(f"- Revision\t=\t{octadist.__revision__}")
print(f"- Release\t=\t{octadist.__release__}")
print(f"- Description\t=\t{octadist.__description__}")
print(f"- E-mail\t=\t{octadist.__email__}")
print(f"- Document\t=\t{octadist.__doc__}")
print(f"- Website\t=\t{octadist.__website__}")
print(f"- Reference\t=\t{octadist.__ref__}. " + f"{octadist.__doi__}")
sys.exit(1)
# in case GUI is requested
if args.gui:
run_gui()
sys.exit(1)
atom_coord = {}
computed = {}
if not args.inp:
print("No input file specified")
sys.exit(1)
# check if file is correct
file = check_file(args.inp)
atom, coord = find_coord(file)
atom, coord = extract_octa(atom, coord, args.ref_index, args.cutoff)
if len(atom) < 7:
print(
"Extracted octahedron is incomplete. Please adjust cutoff distance, e.g., increase the value, to fix the issue."
)
sys.exit(1)
atom_coord = {"atom": atom, "coord": coord}
computed = calc_param(coord)
# get only basename of file from path
basename = os.path.basename(args.inp)
# print unformatted output
if not args.format:
for v in computed.values():
print(f"{v:12.8f}")
# print formatted output
else:
for k, v in computed.items():
print(f"{k}\t=\t{v:12.8f}")
# print atom and coord
if args.show:
for key in args.show:
print(atom_coord[key])
# save result
if args.save:
with open(args.save + ".txt", "w") as f:
f.write("Octahedral distortion parameters\n")
f.write("--------------------------------\n")
f.write(f"File: {basename}\n")
for k, v in computed.items():
f.write(f"{k}\t=\t{v:12.8f}\n")
f.write(f"\nComputed by OctaDist {octadist.__version__}\n")
f.close()
print(f"\nOutput file has been saved to {os.path.realpath(f.name)}")
if __name__ == "__main__":
run_cli()