# coding: utf-8
# Distributed under the terms of the MIT License.
""" Run calculations in a folder with BatchRun
such that there are no clashes.
"""
import os
import argparse
from matador import __version__, script_epilog
from matador.utils.print_utils import print_notify
from matador.utils.errors import InputError
from matador.compute import BatchRun
[docs]def main():
"""Parse args and run any remaining jobs."""
parser = argparse.ArgumentParser(
prog="run3",
description='Run multiple calculations from a series of .res \
files and single cell and param files, typically CASTEP geometry \
optimisations. The geometry optimization will \
be split into "--rough" (default: 4) chunks of "--rough_iter" (default: 2) \
iterations, followed by chunks of "--fine_iter" (default: 20) \
iterations, until geom_max_iter is reached in the param file. \
Successful runs will be moved to "completed", crashes/failures will go to \
"bad_castep" and initial res files will go into "input". Running jobs will \
be listed in jobs.txt and those that completed cleanly will be listed \
in finished_cleanly.txt.',
epilog=script_epilog,
)
parser.add_argument(
"--version", action="version", version="matador version " + __version__ + "."
)
parser.add_argument(
"seed",
type=str,
nargs="+",
help="cell and param seed to use as template for CASTEP calculations OR list of files\
to apply run executable on",
)
parser.add_argument(
"-nc",
"--ncores",
type=int,
help="number of cores per node per job [DEFAULT=cpu_count/nprocesses]",
)
parser.add_argument(
"-np",
"--nprocesses",
type=int,
default=1,
help="number of concurrent calculations, i.e. number \
of concurrent mpiruns [DEFAULT=1]",
)
parser.add_argument(
"-nn",
"--nnodes",
type=int,
default=1,
help="number of nodes per job, i.e. number of nodes \
using -nc cores [DEFAULT=1].",
)
parser.add_argument(
"-t",
"--max_walltime",
type=int,
help="maximum walltime in seconds (job will quit early to clean up if specified)",
)
parser.add_argument(
"-exec",
"--executable",
type=str,
help="specify path to or name of executable (DEFAULT: castep)",
)
parser.add_argument(
"--no_reopt",
action="store_true",
default=False,
help="do not run geometry optimisation again after first success",
)
parser.add_argument(
"--redirect",
type=str,
help="filename to redirect output to, can use $seed macro",
)
parser.add_argument(
"--mode", type=str, default="castep", help="either castep or generic"
)
parser.add_argument(
"--noise",
action="store_true",
help=(
"add 0.1 A of random noise to positions on every cell, "
"useful for converging forces (DEFAULT: off)"
),
)
parser.add_argument(
"--squeeze",
action="store_true",
help="add external pressure to the rough steps of geom opts",
)
parser.add_argument(
"--ignore_jobs_file",
action="store_true",
help="whether to use the jobs.txt file to avoid clashes",
)
parser.add_argument(
"-d", "--debug", action="store_true", default=False, help="debug output"
)
parser.add_argument(
"-cust",
"--custom_params",
action="store_true",
default=False,
help="use custom param file per structure",
)
parser.add_argument(
"-v",
"--verbosity",
type=int,
default=2,
help="integer to set level of verbosity",
)
parser.add_argument(
"--archer", action="store_true", default=False, help="use aprun over mpirun"
)
parser.add_argument(
"--slurm", action="store_true", default=False, help="use srun over mpirun"
)
parser.add_argument(
"--intel", action="store_true", default=False, help="use Intel's mpirun"
)
parser.add_argument(
"--conv_cutoff",
action="store_true",
default=False,
help="run all res files at cutoff defined in cutoff.conv file",
)
parser.add_argument(
"--conv_kpt",
action="store_true",
default=False,
help="run all res files at kpoint spacings defined in kpt.conv file",
)
parser.add_argument(
"--memcheck",
action="store_true",
default=False,
help="enable memcheck via castep dryrun",
)
parser.add_argument(
"--scratch_prefix",
type=str,
help="specify absolute path prefix for compute dir e.g. "
"--scratch_prefix /scratch/user/ will set the compute directory to /scratch/user/$hostname. "
"default value is taken from .matadorrc.",
)
parser.add_argument("--maxmem", type=int, help="override max memory for memcheck")
parser.add_argument(
"--killcheck",
action="store_true",
default=True,
help="check for $seed.kill file and quit job if present",
)
parser.add_argument(
"--kpts_1D",
action="store_true",
default=False,
help="recalculate a 1D kpoint mesh of spacing specified in template cell",
)
parser.add_argument(
"--spin",
type=int,
nargs="?",
const=5,
default=None,
help=(
"if not specified in .cell file, break spin symmetry on first atom using the spin specified by "
"the user [DEFAULT: 5]"
),
)
parser.add_argument(
"--rough",
type=int,
default=4,
help="choose how many <rough_iter> geometry optimizations \
to perform, decrease if lattice is nearly correct. [DEFAULT: 4].",
)
parser.add_argument(
"--rough_iter",
type=int,
default=2,
help="choose how many relaxation steps per rough geometry optimization\
to perform, [DEFAULT: 2].",
)
parser.add_argument(
"--fine_iter",
type=int,
default=20,
help="choose how many relaxation steps per fine geometry optimization\
to perform, [DEFAULT: 20].",
)
parser.add_argument(
"-l", "--limit", type=int, default=None, help="limit to n structures per run"
)
parser.add_argument(
"--profile", action="store_true", help="profile code with cProfile"
)
args = parser.parse_args()
seed = vars(args)["seed"]
kwargs = vars(args)
del kwargs["seed"]
from matador.config import load_custom_settings
settings = load_custom_settings(debug=kwargs.get("debug")).get("run3")
if settings is not None:
if kwargs["scratch_prefix"] is None:
kwargs["scratch_prefix"] = settings.get("scratch_prefix")
if kwargs["scratch_prefix"] == ".":
kwargs["scratch_prefix"] = None
if kwargs["executable"] is None:
kwargs["executable"] = settings.get("castep_executable", "castep")
kwargs["run3_settings"] = settings
if sum([vars(args)["slurm"], vars(args)["archer"], vars(args)["intel"]]) > 1:
exit(
"Incompatible MPI arguments specified, please use at most one of --archer/--intel/--slurm."
)
if vars(args).get("profile"):
import cProfile
import pstats
from sys import version_info
hostname = os.uname()[1]
pr = cProfile.Profile()
pr.enable()
try:
runner = BatchRun(seed, **kwargs)
runner.spawn()
except InputError as exc:
print_notify(exc)
except RuntimeError:
print_notify("Some jobs failed, exiting...")
except Exception as exc:
raise exc
if vars(args).get("profile"):
pr.disable()
fname = "run3-{}-{}-{}.{}.{}".format(
__version__,
hostname,
version_info.major,
version_info.minor,
version_info.micro,
)
pr.dump_stats(fname + ".prof")
with open(fname + ".pstats", "w") as s:
sortby = "cumulative"
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
if __name__ == "__main__":
main()