# coding: utf-8
# Distributed under the terms of the MIT License.
""" This file implements the "matador" command line. """
from matador.query import DBQuery
from matador.hull import QueryConvexHull
from matador.utils.print_utils import print_failure, print_warning, print_notify
from matador.db import make_connection_to_collection
from matador.config import load_custom_settings
[docs]class MatadorCommandLine:
"""Class that implements the command-line interface to a MongoDB
structure repository.
"""
def __init__(self, *args, **kwargs):
"""Initialise the query with command line arguments and return
results.
"""
# read args
self.kwargs = kwargs
self.args = vars(args[0])
self.args["no_quickstart"] = self.kwargs.get("no_quickstart")
self.argstr = kwargs.get("argstr")
file_exts = ["cell", "res", "pdb", "markdown", "latex", "param", "xsf"]
self.export = any([self.args.get(ext) for ext in file_exts])
self.subcommand = self.args.pop("subcmd")
if self.subcommand != "import":
self.settings = load_custom_settings(
config_fname=self.args.get("config"),
debug=self.args.get("debug"),
no_quickstart=self.args.get("no_quickstart"),
)
result = make_connection_to_collection(
self.args.get("db"),
check_collection=(self.subcommand != "stats"),
mongo_settings=self.settings,
)
self.client, self.db, self.collections = result
if self.subcommand == "stats":
self.stats()
try:
if self.subcommand == "import":
from matador.db import Spatula
self.importer = Spatula(self.args)
if self.subcommand == "query":
self.query = DBQuery(self.client, self.collections, **self.args)
self.cursor = self.query.cursor
if self.subcommand == "swaps":
from matador.swaps import AtomicSwapper
self.query = DBQuery(self.client, self.collections, **self.args)
if self.args.get("hull_cutoff") is not None:
self.hull = QueryConvexHull(query=self.query, **self.args)
self.swapper = AtomicSwapper(self.hull.hull_cursor, **self.args)
else:
self.swapper = AtomicSwapper(self.query.cursor, **self.args)
self.cursor = self.swapper.cursor
if self.subcommand == "refine":
from matador.db import Refiner
self.query = DBQuery(self.client, self.collections, **self.args)
if self.args.get("hull_cutoff") is not None:
self.hull = QueryConvexHull(self.query, **self.args)
self.refiner = Refiner(
self.hull.cursor, self.query.repo, **self.args
)
else:
self.refiner = Refiner(
self.query.cursor, self.query.repo, **self.args
)
self.cursor = self.refiner.cursor
if self.subcommand == "hull" or self.subcommand == "voltage":
self.hull = QueryConvexHull(
**self.args,
voltage=self.subcommand == "voltage",
client=self.client,
collections=self.collections
)
self.cursor = self.hull.hull_cursor
if self.subcommand == "changes":
from matador.db import DatabaseChanges
if len(self.collections) != 1:
raise SystemExit(
"Cannot view changes of more than one collection at once."
)
if self.args.get("undo"):
action = "undo"
else:
action = "view"
changeset = self.args.get("changeset")
if changeset is None:
changeset = 0
DatabaseChanges(
[key for key in self.collections][0],
changeset_ind=changeset,
action=action,
mongo_settings=self.settings,
override=kwargs.get("no_quickstart"),
)
if self.subcommand == "hulldiff":
from matador.hull.hull_diff import diff_hulls
if self.args.get("compare") is None:
raise SystemExit(
"Please specify which hulls to query with --compare."
)
diff_hulls(self.client, self.collections, **self.args)
if self.export and self.cursor:
from matador.export import query2files
if self.args.get("write_n") is not None:
self.cursor = [
doc
for doc in self.cursor
if len(doc["stoichiometry"]) == self.args.get("write_n")
]
if not self.cursor:
print_failure("No structures left to export.")
query2files(
self.cursor,
**self.args,
argstr=self.argstr,
subcmd=self.subcommand,
hash_dupe=True
)
if self.args.get("view"):
from matador.utils.viz_utils import viz
if self.args.get("top") is None:
self.top = len(self.cursor)
else:
self.top = self.args.get("top")
if len(self.cursor[: self.top]) > 10:
from time import sleep
print_warning(
"WARNING: opening {} files with ase-gui...".format(
len(self.cursor)
)
)
print_warning("Please kill script within 3 seconds if undesired...")
sleep(3)
if len(self.cursor[: self.top]) > 20:
print_failure(
"You will literally be opening that many windows, "
+ "I'll give you another 5 seconds to reconsider..."
)
sleep(5)
print_notify("It's your funeral...")
sleep(1)
for doc in self.cursor[: self.top]:
viz(doc)
if self.subcommand != "import":
self.client.close()
except (RuntimeError, SystemExit, KeyboardInterrupt) as oops:
if isinstance(oops, RuntimeError):
print_failure(oops)
elif isinstance(oops, SystemExit):
print_warning(oops)
try:
self.client.close()
except AttributeError:
pass
raise oops
[docs] def print_report(self):
"""Print spatula report on current database."""
try:
report = self.report.find_one()
print(
"Database last modified on",
report["last_modified"],
"with matador",
report["version"] + ".",
)
except Exception:
print_warning(
"Failed to print database report: spatula is probably running!"
)
[docs] def stats(self):
"""Print some useful stats about the database."""
if self.args.get("list"):
print_notify(
str(len(self.db.list_collection_names()))
+ " collections found in database:\n"
)
collstats_list = []
for name in self.db.list_collection_names():
collstats_list.append(self.db.command("collstats", name))
collstats_list[-1]["name"] = name
collstats_list = sorted(
collstats_list, key=lambda k: k["count"], reverse=True
)
print("\t{:^20}\t{:^20}".format("Name", "Number of structures"))
for collection in collstats_list:
if not collection["name"].startswith("__"):
print(
"\t{:<20}\t{:>20d}".format(
collection["name"], collection["count"]
)
)
print("\n")
elif self.args.get("delete"):
target = self.args.get("db")
if isinstance(target, list) and len(target) == 1:
target = target[0]
else:
raise SystemExit("I will only delete one collection at a time...")
if target is None:
raise SystemExit("Please specify a collection to delete.")
if target not in self.db.list_collection_names():
raise SystemExit("No collection named {} was found".format(target))
from getpass import getuser
user = getuser()
if user not in target:
raise SystemExit(
"I cannot delete a collection that's name does not start with "
"your username, {}".format(user)
)
stats = self.db.command("collstats", target)
if self.args.get("no_quickstart"):
answer = "y"
else:
answer = input(
"Are you sure you want to delete collection {} containing {} "
"structures? [y/n]\n".format(target, stats["count"])
)
if answer.lower() == "y":
if target == "repo":
raise SystemExit("I'm sorry Dave, I'm afraid I can't do that...")
print("Deleting collection {}...".format(target))
self.db[target].drop()
print("and its changelog...")
self.db["__changelog_{}".format(target)].drop()
else:
raise SystemExit("Nevermind then!")
else:
comp_list = dict()
stats_dict = dict()
stats_dict["count"] = 0
stats_dict["avgObjSize"] = 0
stats_dict["storageSize"] = 0
stats_dict["totalIndexSize"] = 0
for collection in self.collections:
db_stats_dict = self.db.command("collstats", collection)
stats_dict["count"] += db_stats_dict["count"]
stats_dict["avgObjSize"] += db_stats_dict["avgObjSize"]
stats_dict["storageSize"] += db_stats_dict["storageSize"]
stats_dict["totalIndexSize"] += db_stats_dict["totalIndexSize"]
print(
(
"The collection(s) queried in {} contain {} structures at {:.1f} kB each "
"totalling {:.1f} MB with a further {:.1f} MB of indexes."
).format(
self.db.name,
stats_dict["count"],
stats_dict["avgObjSize"] / (1024),
stats_dict["storageSize"] / (1024**2),
stats_dict["totalIndexSize"] / (1024**2),
)
)
for collname in self.collections:
cursor = self.collections[collname].find()
for doc in cursor:
temp = ""
for ind, elem in enumerate(sorted(doc["stoichiometry"])):
temp += str(elem[0])
if ind != len(doc["stoichiometry"]) - 1:
temp += "+"
if temp not in comp_list:
comp_list[temp] = 0
comp_list[temp] += 1
keys = list(comp_list.keys())
vals = list(comp_list.values())
comp_list = list(zip(keys, vals))
comp_list.sort(key=lambda t: t[1], reverse=True)
small_count = 0
first_ind = 1000
cutoff = 100
for ind, comp in enumerate(comp_list):
if comp[1] < cutoff:
if ind < first_ind:
first_ind = ind
small_count += comp[1]
comp_list = comp_list[:first_ind]
comp_list.append(["others < " + str(cutoff), small_count])
comp_list.sort(key=lambda t: t[1], reverse=True)
try:
from ascii_graph import Pyasciigraph
from ascii_graph.colors import Gre, Blu, Red
from ascii_graph.colordata import hcolor
except ImportError:
print("ascii_graph dependency not found, not creating histogram.")
else:
graph = Pyasciigraph(line_length=80, multivalue=False)
thresholds = {
int(stats_dict["count"] / 40): Gre,
int(stats_dict["count"] / 10): Blu,
int(stats_dict["count"] / 4): Red,
}
data = hcolor(comp_list, thresholds)
for line in graph.graph(label=None, data=data):
print(line)
print("\n")
for comp in comp_list:
print(comp)
[docs]def main(no_quickstart=False):
"""Parse all user args and construct a MatadorCommandLine object.
Keyword arguments:
no_quickstart: no_quickstart all stdin with sensible defaults.
"""
import argparse
from sys import argv
from matador import __version__, script_epilog
parser = argparse.ArgumentParser(
prog="matador",
description="MATerial and Atomistic Database Of Refined structures.",
epilog=script_epilog,
)
parser.add_argument(
"--version", action="version", version="matador version " + __version__ + "."
)
# define subparsers for self.subcommands
subparsers = parser.add_subparsers(
title="self.subcommands", description="valid sub-commands", dest="subcmd"
)
# define parent parser for global arguments
global_flags = argparse.ArgumentParser(add_help=False)
# common arguments to all self.subcommands
global_flags.add_argument(
"--db", nargs="+", help="choose which collection to query"
)
global_flags.add_argument(
"--debug", action="store_true", help="enable debug printing throughout code."
)
global_flags.add_argument(
"-conf",
"--config",
type=str,
help="specify custom location of matador config file."
"(DEFAULT: $MATADOR_ROOT/config/matador_conf.json)",
)
global_flags.add_argument("--devel", action="store_true", help="test devel code.")
global_flags.add_argument(
"--profile", action="store_true", help="run code profiler."
)
global_flags.add_argument(
"-q", "--quiet", action="store_true", help="redirect most output to /dev/null."
)
# define all other flags by group
structure_flags = argparse.ArgumentParser(add_help=False)
structure_flags.add_argument(
"-c",
"--composition",
type=str,
nargs="+",
help="find all structures containing exclusively the given "
"elements, e.g. LiSi. Macros defined for groups [I]-[VII] "
"[Tran] [Lan] and [Act], used with square brackets.",
)
structure_flags.add_argument(
"-int",
"--intersection",
action="store_true",
help="query the intersection of compositions instead of the union "
"e.g. -c LiSnS -int queries Li, Sn, S, LiSn, LiS & LiSnS.",
)
structure_flags.add_argument(
"-n",
"--num_species",
type=int,
help="find all structures containing a certain number of species.",
)
structure_flags.add_argument(
"-f",
"--formula",
type=str,
nargs="+",
help="query a particular chemical formula, e.g. GeTeSi3",
)
structure_flags.add_argument(
"-i",
"--id",
type=str,
nargs="+",
help="specify a particular structure by its text_id",
)
structure_flags.add_argument(
"-ac",
"--calc_match",
action="store_true",
help="display calculations of the same accuracy as specified id",
)
structure_flags.add_argument(
"-kpttol",
"--kpoint_tolerance",
type=float,
help="kpoint tolerance for calculation matches (DEFAULT: +/- 0.01 1/Å)",
)
structure_flags.add_argument(
"-presstol",
"--pressure_tolerance",
type=float,
help="pressure tolerance for calculation matches (DEFAULT: +/- 0.5 GPa)",
)
structure_flags.add_argument(
"-z",
"--num_fu",
type=int,
help="query a calculations with more than n formula units",
)
structure_flags.add_argument(
"-sg", "--space_group", help="query a particular space group"
)
structure_flags.add_argument(
"-u",
"--uniq",
type=float,
nargs="?",
const=0.1,
help="float, return only unique structures (filtered by PDF "
"overlap), to this tolerance (DEFAULT: 0.1)",
)
structure_flags.add_argument(
"-p",
"--pressure",
type=float,
help="specify an isotropic external pressure to search for, e.g. 10 (GPa)",
)
structure_flags.add_argument(
"-pf",
"--partial-formula",
action="store_true",
help="stoichiometry/composition queries will include other unspecified species, e.g. "
"-pf search for Li will query any structure containing Li, not just pure Li.",
)
structure_flags.add_argument(
"--tags", nargs="+", type=str, help=("search for manual tags")
)
structure_flags.add_argument(
"--doi", type=str, help=("search for DOI in format xxxx/xxxx")
)
structure_flags.add_argument(
"-icsd",
"--icsd",
type=int,
const=0,
nargs="?",
help=("search for an ICSD CollCode"),
)
structure_flags.add_argument(
"-ss",
"--src_str",
type=str,
help=("search for a string inside the structure sources"),
)
structure_flags.add_argument(
"-root",
"--root_src",
type=str,
help=("search for a root_source string of the structure"),
)
structure_flags.add_argument(
"-encap",
"--encapsulated",
action="store_true",
help="query only structures encapsulated in a carbon nanotube.",
)
structure_flags.add_argument(
"-cntr",
"--cnt_radius",
type=float,
help="specify the radius of the encapsulating nanotube to within 0.01 Å",
)
structure_flags.add_argument(
"-cntv",
"--cnt_vector",
type=int,
nargs="+",
help="specify the chiral vector of the encapsulating nanotube",
)
structure_flags.add_argument(
"-ecut",
"--cutoff",
type=float,
nargs="+",
help="specify the min. and optionally max. planewave cutoff.",
)
structure_flags.add_argument(
"-geom",
"--geom_force_tol",
type=float,
nargs="+",
help="force tolerance in eV/Å to query for calc matches.",
)
structure_flags.add_argument(
"-grid",
"--grid_scale",
type=float,
nargs="+",
help="grid scale to query for calc matches.",
)
structure_flags.add_argument(
"-finegrid",
"--fine_grid_scale",
type=float,
nargs="+",
help="fine grid scale to query for calc matches.",
)
structure_flags.add_argument(
"--sedc",
type=str,
help="specify the dispersion correction scheme, e.g. TS or null.",
)
structure_flags.add_argument(
"-xc",
"--xc_functional",
type=str,
help="specify an xc-functional to query (case-insensitive).",
)
structure_flags.add_argument(
"-kpts",
"--mp_spacing",
type=float,
help="specify an MP grid spacing in 2π/Å units, e.g. 0.05, will return all values "
"structures with value within --kpt_tol",
)
structure_flags.add_argument(
"--spin",
type=str,
help="specifiy whether to query non-spin-polarized (0) calcs or spin polarized calcs "
"(!=1), or lump them both together with `any`",
)
structure_flags.add_argument(
"--loose",
action="store_true",
help="loosely matches with calc_match, i.e. only matches pspot and xc_functional",
)
structure_flags.add_argument(
"--ignore_warnings",
action="store_true",
help="includes possibly bad structures",
)
structure_flags.add_argument(
"--field", type=str, action="append", help="name of arbitrary field to query"
)
structure_flags.add_argument(
"--filter",
nargs="+",
action="append",
help="specify either float [min, max] or a string/float value.",
)
material_flags = argparse.ArgumentParser(add_help=False)
material_flags.add_argument(
"-hc",
"--hull_cutoff",
type=float,
help="return only structures within a certain distance from hull in eV/atom",
)
material_flags.add_argument(
"-lc",
"--label_cutoff",
nargs="+",
type=float,
help="label only structures within a certain distance from hull in eV/atom",
)
material_flags.add_argument(
"--biggest",
action="store_true",
help="use the largest subset of structures to create a hull",
)
material_flags.add_argument(
"--volume",
action="store_true",
help="plot a volume curve from convex hull (currently limited to binaries)",
)
material_flags.add_argument(
"--chempots",
type=float,
nargs="+",
help="manually specify chem pots as enthalpy per atom for a rough hull.",
)
plot_flags = argparse.ArgumentParser(add_help=False)
plot_flags.add_argument(
"--pdf", action="store_true", help="save pdf rather than showing plot in X"
)
plot_flags.add_argument(
"--png", action="store_true", help="save png rather than showing plot in X"
)
plot_flags.add_argument(
"--csv", action="store_true", help="save plotting data to separate csv files"
)
plot_flags.add_argument("--labels", action="store_true", help="label hull plots")
plot_flags.add_argument(
"--svg", action="store_true", help="save svg rather than showing plot in X"
)
plot_flags.add_argument(
"--subplot", action="store_true", help="plot combined hull and voltage graph"
)
plot_flags.add_argument("--no_plot", action="store_true", help="suppress plotting")
plot_flags.add_argument(
"--capmap", action="store_true", help="plot heat map of gravimetric capacity"
)
plot_flags.add_argument(
"--sampmap", action="store_true", help="plot heat map of concentration sampling"
)
plot_flags.add_argument(
"--efmap", action="store_true", help="plot heat map of formation energy"
)
plot_flags.add_argument(
"--pathways",
action="store_true",
help="plot line from stable B_x C_y to pure A in ABC ternary.",
)
plot_flags.add_argument(
"--expt",
type=str,
help="enter experimental voltage curve .csv file for plotting.",
)
plot_flags.add_argument(
"--expt_label", type=str, help="label for experimental data on voltage curve."
)
import_flags = argparse.ArgumentParser(add_help=False)
import_flags.add_argument(
"-d",
"--dryrun",
action="store_true",
help="run the importer without connecting to the database",
)
import_flags.add_argument(
"-v", "--verbosity", type=int, help="enable verbose output", default=0
)
import_flags.add_argument(
"-f", "--force", action="store_true", help="override main database protection"
)
import_flags.add_argument(
"-t",
"--tags",
nargs="+",
type=str,
help="set user tags, e.g. nanotube, project name",
)
import_flags.add_argument(
"--recent_only",
action="store_true",
help="sort files by creation date (st_ctime) and "
"stop importing after a duplicate is found in the database.",
)
import_flags.add_argument(
"-s",
"--scan",
action="store_true",
help="only scan the database for new structures, do not import new structures",
)
import_flags.add_argument(
"-p",
"--prototype",
action="store_true",
help="create a database of prototype structures that contain no DFT calculations",
)
changes_flags = argparse.ArgumentParser(add_help=False)
changes_flags.add_argument(
"-c", "--changeset", type=int, help="changeset number to query"
)
changes_flags.add_argument(
"-r", "--revert", type=int, help="revert database to specified changeset"
)
changes_flags.add_argument(
"-u", "--undo", action="store_true", help="undo changeset"
)
collection_flags = argparse.ArgumentParser(add_help=False)
collection_flags.add_argument(
"--to", type=str, help="the text_id of a structure with the desired parameters"
)
collection_flags.add_argument(
"--with",
type=str,
help=(
"the seedname (must be within pwd) of cell and param "
+ "files to use for swaps"
),
)
collection_flags.add_argument(
"--prefix",
type=str,
help="add a prefix to all file names to write out (auto-appended with an underscore",
)
query_flags = argparse.ArgumentParser(add_help=False)
query_flags.add_argument(
"-s",
"--summary",
action="store_true",
help="show only the ground state for each stoichiometry.",
)
query_flags.add_argument(
"-t", "--top", type=int, help="number of structures to show/write (DEFAULT: 10)"
)
query_flags.add_argument(
"-dE",
"--delta_E",
type=float,
help="maximum distance from ground state structure to show/write in eV/atom",
)
query_flags.add_argument(
"-d",
"--details",
action="store_true",
help="show as much detail about calculation as possible",
)
query_flags.add_argument(
"-pa",
"--per_atom",
action="store_true",
help="show quantities per atom not per fu.",
)
query_flags.add_argument(
"-ef",
"--eform",
action="store_true",
help="print formation energy not hull distance.",
)
query_flags.add_argument(
"-dt",
"--time",
type=int,
help="query only structures added before this time in days",
)
query_flags.add_argument(
"-avail",
"--available_values",
type=str,
help="list all values of field in query results",
)
query_flags.add_argument(
"--use_source",
default=False,
action="store_true",
help="show the source rather than database ID",
)
query_flags.add_argument(
"--since",
action="store_true",
help="query only structures added after time specified by --time in days",
)
query_flags.add_argument(
"--source",
action="store_true",
help="print filenames from which structures were wrangled",
)
query_flags.add_argument(
"-v",
"--view",
action="store_true",
help="quickly view a structure/structures with ase-gui",
)
query_flags.add_argument(
"--cell",
action="store_true",
help="export query to .cell files in folder name from query string",
)
query_flags.add_argument(
"--param",
action="store_true",
help="export query to .param files in folder name from query string",
)
query_flags.add_argument(
"--res",
action="store_true",
help="export query to .res files in folder name from query string",
)
query_flags.add_argument(
"--json",
action="store_true",
help="export query to raw json files in folder name from query string",
)
query_flags.add_argument(
"--pdb",
action="store_true",
help="export query to .pdb files in folder name from query string",
)
query_flags.add_argument(
"--xsf",
action="store_true",
help="export query to .xsf files in folder name from query string",
)
query_flags.add_argument(
"--markdown",
action="store_true",
help="export query summary to a markdown file",
)
query_flags.add_argument(
"--latex", action="store_true", help="export query summary to a LaTeX table"
)
query_flags.add_argument(
"--write_n", type=int, help="export only those structures with n species"
)
swap_flags = argparse.ArgumentParser(add_help=False)
swap_flags.add_argument(
"-sw",
"--swap",
type=str,
nargs="+",
help="swap all atoms in structures from a query from the first n-1 species to the nth, "
"e.g. -sw NAs will swap all N to As, -sw NAs:LiNa will swap all N to As, and all Li "
"to Na, and -sw [V]As:[Li,K,Rb]Na will swap all group V elements to As and all of Li,"
"K and Rb to Na.",
)
diff_flags = argparse.ArgumentParser(add_help=False)
diff_flags.add_argument(
"-cmp",
"--compare",
type=str,
nargs="+",
help="diff phase diagrams between two different times, in standard time format, "
"e.g. `--compare 1y2m5d3h` will compare the present hull with that of 1 year, 2 "
"months, 5 days and 3 hours ago, and `--compare 3d 2d` will compare three days ago "
"to two days ago.",
)
refine_flags = argparse.ArgumentParser(add_help=False)
refine_flags.add_argument(
"-task",
"--task",
type=str,
help=(
"refine subtask to perform: options are spg, elem_set, tag, doi, source, "
"pspot or raw or sub"
),
)
refine_flags.add_argument(
"-mode",
"--mode",
type=str,
help="mode of refinement: options are display, set and overwrite",
)
refine_flags.add_argument(
"-symprec",
"--symprec",
type=float,
help="spglib symmetry precision for refinement",
)
refine_flags.add_argument(
"--new_tag", type=str, help="new tag to add to structures in query"
)
refine_flags.add_argument(
"--new_doi", type=str, help="new doi to add to structures in query"
)
stats_flags = argparse.ArgumentParser(add_help=False)
stats_flags.add_argument(
"-l",
"--list",
action="store_true",
help="list all collections, their sizes, and owners",
)
stats_flags.add_argument(
"--delete",
action="store_true",
help="try to delete collection specified by --db",
)
# define subcommand parsers and their arguments
# matador stats
subparsers.add_parser(
"stats",
help="print some stats about the database.",
parents=[global_flags, stats_flags],
)
# matador query
subparsers.add_parser(
"query",
help="query and extract structures from the database",
parents=[global_flags, query_flags, structure_flags],
)
# matador import
subparsers.add_parser(
"import",
help="import new structures in folder into database",
parents=[global_flags, import_flags],
)
# matador hull
subparsers.add_parser(
"hull",
help="create a convex hull from query results (currently limited to binaries and ternaries)",
parents=[
global_flags,
structure_flags,
material_flags,
plot_flags,
query_flags,
],
)
# matador voltage
subparsers.add_parser(
"voltage",
help="plot a voltage curve from query results (currently limited to binaries and ternaries)",
parents=[
global_flags,
structure_flags,
material_flags,
plot_flags,
query_flags,
],
)
# matador changes
subparsers.add_parser(
"changes",
help="view database changelog or undo additions to database (NB: not deletions!)",
parents=[global_flags, changes_flags],
)
# matador hulldiff
subparsers.add_parser(
"hulldiff",
help="diff two convex hulls with the --compare flag.",
parents=[
global_flags,
structure_flags,
material_flags,
plot_flags,
query_flags,
diff_flags,
],
)
# matador swaps
subparsers.add_parser(
"swaps",
help="perform atomic swaps on query results",
parents=[
global_flags,
collection_flags,
query_flags,
structure_flags,
material_flags,
swap_flags,
],
)
# matador refine
subparsers.add_parser(
"refine",
help="update structures in the database according to specified --task",
parents=[
global_flags,
query_flags,
structure_flags,
refine_flags,
material_flags,
],
)
parsed_args = parser.parse_args()
vars_args = vars(parsed_args)
# check for inconsistent argument combinations
if vars_args.get("intersection") and vars_args.get("composition") is None:
raise SystemExit("--intersection requires --composition.")
if (
vars_args.get("subcmd") == "stats"
and vars_args.get("list")
and vars_args.get("delete")
):
raise SystemExit("Cannot use -l/--list and --delete")
if vars_args.get("field") and vars_args.get("filter") is None:
raise SystemExit("--field requires --filter.")
if vars_args.get("subcmd") == "hull" and vars_args.get("composition") is None:
raise SystemExit("hull requires --composition")
if vars_args.get("calc_match") and vars_args.get("id") is None:
raise SystemExit(
"calc_match requires specification of a text_id with -i, exiting..."
)
if vars_args.get("profile"):
import cProfile
import pstats
from sys import version_info
profiler = cProfile.Profile()
profiler.enable()
MatadorCommandLine(parsed_args, argstr=argv[1:], no_quickstart=no_quickstart)
if vars_args.get("profile"):
profiler.disable()
fname = "matador-{}-{}.{}.{}".format(
__version__, version_info.major, version_info.minor, version_info.micro
)
profiler.dump_stats(fname + ".prof")
with open(fname + ".pstats", "w") as fp:
stats = pstats.Stats(profiler, stream=fp).sort_stats("cumulative")
stats.print_stats()
if __name__ == "__main__":
main()