1
|
|
|
"""Command line interface for :func:`amd.compare() <.compare.compare>`. |
2
|
|
|
""" |
3
|
|
|
|
4
|
|
|
import argparse |
5
|
|
|
import sys |
6
|
|
|
|
7
|
|
|
from .compare import compare |
8
|
|
|
|
9
|
|
|
|
10
|
|
|
def main(): |
11
|
|
|
"""Entry point for command line interface for |
12
|
|
|
:func:`amd.compare() <.compare.compare>. |
13
|
|
|
""" |
14
|
|
|
|
15
|
|
|
desc = "Compare crystals by PDD or AMD from the command line. Given one " \ |
16
|
|
|
"or two paths to cifs/folders, lists of CSD refcodes or periodic " \ |
17
|
|
|
"sets, compare them and return a DataFrame of the distance " \ |
18
|
|
|
"matrix. Default is to comapre by AMD with k = 100. Accepts most " \ |
19
|
|
|
"keyword arguments accepted by amd's CIF/CSD readers and " \ |
20
|
|
|
"comparison functions." |
21
|
|
|
|
22
|
|
|
parser = argparse.ArgumentParser(description=desc) |
23
|
|
|
|
24
|
|
|
parser.add_argument('paths_or_refcodes', type=str, nargs='+', |
25
|
|
|
help='(str) One or two paths to files or folders, or a collection ' \ |
26
|
|
|
'of CSD refcodes if csd-python-api is installed.') |
27
|
|
|
parser.add_argument('--outpath', '-o', type=str, |
28
|
|
|
help='(str) Path of the output file.') |
29
|
|
|
parser.add_argument('--format', '-f', type=str, default='csv', |
30
|
|
|
help='(str) Format of the output file, default csv.') |
31
|
|
|
|
32
|
|
|
# amd.compare args |
33
|
|
|
parser.add_argument( |
34
|
|
|
'--by', '-b', type=str, default='AMD', choices=['AMD', 'PDD'], |
35
|
|
|
help='(str) Use AMD or PDD to compare crystals.') |
36
|
|
|
parser.add_argument('--k', '-k', type=int, default=100, |
37
|
|
|
help='(int) Number of neighbour atoms to use for AMD/PDD.') |
38
|
|
|
parser.add_argument('--nearest', '-n', type=int, default=None, |
39
|
|
|
help='(int) Find n nearest neighbours instead of a full distance ' \ |
40
|
|
|
'matrix between crystals.') |
41
|
|
|
|
42
|
|
|
# Reading args |
43
|
|
|
parser.add_argument( |
44
|
|
|
'--supress_warnings', default=False, action='store_true', |
45
|
|
|
help='(flag) Do not show warnings encountered during reading.') |
46
|
|
|
parser.add_argument('--reader', '-r', type=str, default='ase', |
47
|
|
|
choices=['ase', 'pycodcif', 'ccdc', 'pymatgen', 'gemmi'], |
48
|
|
|
help='(str) Backend package used to parse files, default ase.') |
49
|
|
|
parser.add_argument('--remove_hydrogens', default=False, |
50
|
|
|
action='store_true', help='(flag) Remove Hydrogen atoms.') |
51
|
|
|
parser.add_argument('--disorder', type=str, default='skip', |
52
|
|
|
choices=['skip', 'ordered_sites', 'all_sites'], |
53
|
|
|
help='(str) Control how disordered structures are handled.') |
54
|
|
|
parser.add_argument( |
55
|
|
|
'--heaviest_component', default=False, action='store_true', |
56
|
|
|
help='(flag) (csd-python-api only) Keep only the heaviest part of ' \ |
57
|
|
|
'the asymmetric unit, intended for removing solvents.') |
58
|
|
|
parser.add_argument( |
59
|
|
|
'--molecular_centres', default=False, action='store_true', |
60
|
|
|
help='(flag) (csd-python-api only) Uses the centres of molecules ' \ |
61
|
|
|
'for comparisons instead of atoms.') |
62
|
|
|
parser.add_argument('--families', default=False, action='store_true', |
63
|
|
|
help='(flag) (csd-python-api only) Interpret path_or_refcodes as ' \ |
64
|
|
|
'refcode families.') |
65
|
|
|
|
66
|
|
|
# PDD args |
67
|
|
|
parser.add_argument('--collapse_tol', type=float, default=1e-4, |
68
|
|
|
help='(float) Tolerance for collapsing rows of PDDs.') |
69
|
|
|
|
70
|
|
|
# compare args |
71
|
|
|
parser.add_argument('--metric', type=str, default='chebyshev', |
72
|
|
|
help='(str) Metric used to compare AMDs/rows of PDDs, default chebyshev.') |
73
|
|
|
parser.add_argument('--n_jobs', type=int, default=1, |
74
|
|
|
help='(int) Number of cores to use for multiprocessing when comparing PDDs.') |
75
|
|
|
parser.add_argument('--verbose', type=int, default=1, |
76
|
|
|
help='(int) Print an ETA to the terminal when comparing PDDs. Passed to joblib.Parallel if using multiprocessing.') |
77
|
|
|
parser.add_argument('--low_memory', default=False, action='store_true', |
78
|
|
|
help='(flag) Use an more memory efficient (but slower) method for AMD comparisons.') |
79
|
|
|
|
80
|
|
|
# Remove some arguments before passing others to amd.compare |
81
|
|
|
kwargs = vars(parser.parse_args()) |
82
|
|
|
path_or_refcodes = kwargs.pop('path_or_refcodes') |
83
|
|
|
outpath = kwargs.pop('outpath', None) |
84
|
|
|
if outpath is None: |
85
|
|
|
outpath = f"{kwargs['by']}_k={kwargs['k']}_dist_matrix" |
86
|
|
|
ext = kwargs.pop('format', 'csv') |
87
|
|
|
|
88
|
|
|
# Parameter is show_warnings, here it's a flag supress_warnings |
89
|
|
|
kwargs['show_warnings'] = not kwargs['supress_warnings'] |
90
|
|
|
kwargs.pop('supress_warnings', None) |
91
|
|
|
|
92
|
|
|
crystals = path_or_refcodes[0] |
93
|
|
|
crystals_ = None |
94
|
|
|
if len(path_or_refcodes) == 2: |
95
|
|
|
crystals_ = path_or_refcodes[1] |
96
|
|
|
elif len(path_or_refcodes) > 2: |
97
|
|
|
msg = 'amd.compare accepts one or two collections of crystals for comparison.' |
98
|
|
|
raise ValueError(msg) |
99
|
|
|
|
100
|
|
|
df = compare(crystals, crystals_, **kwargs) |
101
|
|
|
|
102
|
|
|
if kwargs['verbose']: |
103
|
|
|
sys.stdout.write(df) |
104
|
|
|
|
105
|
|
|
if not outpath.endswith('.' + ext): |
106
|
|
|
outpath += '.' + ext |
107
|
|
|
|
108
|
|
|
try: |
109
|
|
|
output_func = getattr(df, 'to_' + ext) |
110
|
|
|
output_func(outpath) |
111
|
|
|
except AttributeError: |
112
|
|
|
sys.stdout.write(f'Unknown format {ext}, using csv.') |
113
|
|
|
df.to_csv(outpath + '.csv') |
114
|
|
|
|