Skip to content
Snippets Groups Projects
Verified Commit cf304275 authored by James Graham's avatar James Graham
Browse files

refactor: prettify console output

parent d750aa70
No related branches found
No related tags found
No related merge requests found
......@@ -85,6 +85,17 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "commonmark"
version = "0.9.1"
description = "Python parser for the CommonMark Markdown spec"
category = "main"
optional = false
python-versions = "*"
[package.extras]
test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
[[package]]
name = "coverage"
version = "5.3"
......@@ -104,6 +115,14 @@ category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "dataclasses"
version = "0.7"
description = "A backport of the dataclasses module for Python 3.6"
category = "main"
optional = false
python-versions = ">=3.6, <3.7"
[[package]]
name = "docutils"
version = "0.16"
......@@ -376,7 +395,7 @@ name = "pygments"
version = "2.7.1"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = true
optional = false
python-versions = ">=3.5"
[[package]]
......@@ -537,6 +556,24 @@ python-versions = "*"
[package.dependencies]
astroid = ">=1.4"
[[package]]
name = "rich"
version = "9.2.0"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
category = "main"
optional = false
python-versions = ">=3.6,<4.0"
[package.dependencies]
colorama = ">=0.4.0,<0.5.0"
commonmark = ">=0.9.0,<0.10.0"
dataclasses = {version = ">=0.7,<0.8", markers = "python_version >= \"3.6\" and python_version < \"3.7\""}
pygments = ">=2.6.0,<3.0.0"
typing-extensions = ">=3.7.4,<4.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
[[package]]
name = "rstcheck"
version = "3.3.1"
......@@ -737,6 +774,14 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "typing-extensions"
version = "3.7.4.3"
description = "Backported and Experimental Type Hints for Python 3.5+"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "unidecode"
version = "1.1.1"
......@@ -804,7 +849,7 @@ test = []
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
content-hash = "4ec5a3e4bfd4075233d7356350beeb69f09f659a18bb7d1450d209e40f15447a"
content-hash = "684ae060e918da15c709fa66630983cb36502abed30a288f1e015ed337f0c1a0"
[metadata.files]
alabaster = [
......@@ -843,6 +888,10 @@ colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
]
commonmark = [
{file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"},
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
]
coverage = [
{file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"},
{file = "coverage-5.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4"},
......@@ -914,6 +963,10 @@ cython = [
{file = "Cython-0.29.21-py2.py3-none-any.whl", hash = "sha256:5c4276fdcbccdf1e3c1756c7aeb8395e9a36874fa4d30860e7694f43d325ae13"},
{file = "Cython-0.29.21.tar.gz", hash = "sha256:e57acb89bd55943c8d8bf813763d20b9099cc7165c0f16b707631a7654be9cad"},
]
dataclasses = [
{file = "dataclasses-0.7-py3-none-any.whl", hash = "sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836"},
{file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"},
]
docutils = [
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
......@@ -1140,6 +1193,10 @@ requests = [
requirements-detector = [
{file = "requirements-detector-0.7.tar.gz", hash = "sha256:0d1e13e61ed243f9c3c86e6cbb19980bcb3a0e0619cde2ec1f3af70fdbee6f7b"},
]
rich = [
{file = "rich-9.2.0-py3-none-any.whl", hash = "sha256:564fee761e0756c3a4fbe97517166703754e11b4e861983cf184c78c9de8b570"},
{file = "rich-9.2.0.tar.gz", hash = "sha256:7003a1cce3b79bf4d34a26099b00a4b67e208d4a6896a1c25368603d5f49f295"},
]
rstcheck = [
{file = "rstcheck-3.3.1.tar.gz", hash = "sha256:92c4f79256a54270e0402ba16a2f92d0b3c15c8f4410cb9c57127067c215741f"},
]
......@@ -1235,6 +1292,11 @@ typed-ast = [
{file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"},
{file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"},
]
typing-extensions = [
{file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
{file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
]
unidecode = [
{file = "Unidecode-1.1.1-py2.py3-none-any.whl", hash = "sha256:1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a"},
{file = "Unidecode-1.1.1.tar.gz", hash = "sha256:2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"},
......
......
......@@ -5,8 +5,11 @@ import cProfile
import logging
import pathlib
import sys
import textwrap
import typing
from rich.logging import RichHandler
from .frame import Frame
from .mapping import Mapping
from .bondset import BondSet
......@@ -37,31 +40,31 @@ def measure_bonds(frame: Frame, mapping: typing.Optional[Mapping],
:param mapping:
:param config: Program arguments from argparse
"""
bonds = BondSet(config.bnd, config)
logger.info("Bond measurements will be made")
bonds = BondSet(config.bondset, config)
bonds.apply(frame)
if config.map and config.trajectory:
if config.mapping and config.trajectory:
# Only perform Boltzmann Inversion if we have a mapping and a trajectory.
# Otherwise we get infinite force constants.
logger.info("Beginning Boltzmann Inversion")
logger.info('Starting Boltzmann Inversion')
bonds.boltzmann_invert()
logger.info('Finished Boltzmann Inversion')
if config.output_forcefield:
logger.info("Creating GROMACS forcefield directory")
logger.info("Writing GROMACS forcefield directory")
out_dir = pathlib.Path(config.out_dir)
forcefield = ForceField(config.output_name, dir_path=out_dir)
forcefield.write(config.output_name, mapping, bonds)
logger.info("GROMACS forcefield directory created")
logger.info("Finished writing GROMACS forcefield directory")
else:
bonds.write_itp(get_output_filepath('itp', config),
mapping=mapping)
if config.dump_measurements:
logger.info("Dumping bond measurements to file")
logger.info('Writing bond measurements to file')
bonds.dump_values(config.dump_n_values, config.out_dir)
logger.info('Finished writing bond measurements to file')
def mapping_loop(frame: Frame, config) -> typing.Tuple[Frame, Mapping]:
......@@ -70,11 +73,12 @@ def mapping_loop(frame: Frame, config) -> typing.Tuple[Frame, Mapping]:
:param frame:
:param config: Program arguments from argparse
"""
logger.info("Mapping will be performed")
mapping = Mapping(config.map, config, itp_filename=config.itp)
logger.info('Starting AA->CG mapping')
mapping = Mapping(config.mapping, config, itp_filename=config.itp)
cg_frame = mapping.apply(frame)
cg_frame.save(get_output_filepath('gro', config), frame_number=0)
logging.info('Finished AA->CG mapping')
return cg_frame, mapping
......@@ -92,18 +96,18 @@ def full_run(config):
frame_start=config.begin,
frame_end=config.end)
if config.map:
if config.mapping:
cg_frame, mapping = mapping_loop(frame, config)
else:
logger.info("Mapping will not be performed")
logger.info('Skipping AA->CG mapping')
mapping = None
cg_frame = frame
if config.output_xtc:
cg_frame.save(get_output_filepath('xtc', config))
if config.bnd:
if config.bondset:
measure_bonds(cg_frame, mapping, config)
......@@ -134,9 +138,9 @@ def parse_arguments(arg_list):
help="AA simulation topology - e.g. PDB, GRO, etc.")
input_files.add_argument('trajectory', type=str, nargs='?',
help="AA simulation trajectory - e.g. XTC, DCD, etc.")
input_files.add_argument('-m', '--map', type=str,
input_files.add_argument('-m', '--mapping', type=str,
help="Mapping file")
input_files.add_argument('-b', '--bnd', type=str,
input_files.add_argument('-b', '--bondset', type=str,
help="Bonds file")
input_files.add_argument('-i', '--itp', type=str,
help="GROMACS ITP file")
......@@ -200,6 +204,9 @@ def parse_arguments(arg_list):
run_options.add_argument('--profile', '--no-profile', default=False, action=BooleanAction,
help="Profile performance?")
run_options.add_argument('--log-level', default='INFO',
choices=('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'),
help="Which log messages should be shown?")
# yapf: enable
args = parser.parse_args(arg_list)
......@@ -219,12 +226,12 @@ def validate_arguments(args):
:param args: Parsed arguments from ArgumentParser
"""
if not args.dump_measurements:
args.dump_measurements = bool(args.bnd) and not bool(args.map)
args.dump_measurements = bool(args.bondset) and not bool(args.mapping)
if not args.map_only:
args.map_only = not bool(args.bnd)
args.map_only = not bool(args.bondset)
if not args.map and not args.bnd:
if not args.mapping and not args.bondset:
raise ArgumentValidationError("One or both of -m and -b is required.")
return args
......@@ -233,9 +240,34 @@ def validate_arguments(args):
def main():
args = parse_arguments(sys.argv[1:])
print("Using GRO: {0}".format(args.topology))
print("Using XTC: {0}".format(args.trajectory))
logging.basicConfig(level=args.log_level,
format='%(message)s',
datefmt='[%X]',
handlers=[RichHandler()])
banner = """\
_____ _____ _____ _______ ____ ____ _
| __ \ / ____/ ____|__ __/ __ \ / __ \| |
| |__) | _| | | | __ | | | | | | | | | |
| ___/ | | | | | | |_ | | | | | | | | | | |
| | | |_| | |___| |__| | | | | |__| | |__| | |____
|_| \__, |\_____\_____| |_| \____/ \____/|______|
__/ |
|___/
""" # noqa
logger.info('[bold blue]%s[/]',
textwrap.dedent(banner),
extra={'markup': True})
logger.info(30 * '-')
logger.info('Topology:\t%s', args.topology)
logger.info('Trajectory:\t%s', args.trajectory)
logger.info('Mapping:\t%s', args.mapping)
logger.info('Bondset:\t%s', args.bondset)
logger.info(30 * '-')
try:
if args.profile:
with cProfile.Profile() as profiler:
full_run(args)
......@@ -245,6 +277,11 @@ def main():
else:
full_run(args)
logger.info('Finished processing - goodbye!')
except Exception as exc:
logger.error(exc)
if __name__ == "__main__":
main()
......@@ -50,15 +50,18 @@ class Frame:
"""
if topology_file is not None:
try:
logging.info('Loading topology file')
self._trajectory = mdtraj.load(str(topology_file))
self._topology = self._trajectory.topology
logging.info('Finished loading topology file')
if trajectory_file is not None:
try:
logging.info('Loading trajectory file - this may take a while')
self._trajectory = mdtraj.load(str(trajectory_file),
top=self._topology)
self._slice_trajectory(frame_start, frame_end)
logging.info('Finished loading trajectory file')
except ValueError as exc:
raise NonMatchingSystemError from exc
......
......
......@@ -249,7 +249,7 @@ class Mapping:
name, typ, first, *atoms = mol_section[i][1:]
except KeyError as exc:
raise ValueError(f'"{name}" line prefix invalid') from exc
raise ValueError(f'Invalid line prefix "{name}" in mapping file') from exc
try:
# Allow optional charge in mapping file
......@@ -262,8 +262,7 @@ class Mapping:
atoms.insert(0, first)
if not atoms:
# TODO should this stop execution?
logger.warning('Bead %s specification contains no atoms', name)
raise ValueError(f'Bead {name} specification contains no atoms')
mol_map.append(
bead_class(name, i, type=typ, atoms=atoms, charge=charge))
......@@ -421,7 +420,6 @@ class Mapping:
return cg_frame
# TODO: Use MDTraj instead
def calc_coords_weight(ref_coords, coords, weights, box=None):
"""Calculate the coordinates of a single CG bead from weighted component atom coordinates.
......
......
......@@ -31,6 +31,7 @@ wheel = "^0.35.1"
numpy = "^1.19.1"
cython = "^0.29.21"
mdtraj = "^1.9.4"
rich = "^9.2.0"
# Optional extras to enable additional functionality
sphinx-autoapi = { version = "^1.5.0", optional = true }
......
......
......@@ -57,7 +57,7 @@ class PycgtoolTest(unittest.TestCase):
])
self.assertEqual('TOPOLOGY', args.topology)
self.assertEqual('MAP', args.map)
self.assertEqual('MAP', args.mapping)
self.assertEqual(1000, args.begin)
def test_map_only(self):
......@@ -117,7 +117,7 @@ class PycgtoolTest(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmpdir:
tmp_path = pathlib.Path(tmpdir)
args = get_args('sugar', tmp_path, extra={
'map': None,
'mapping': None,
'trajectory': None,
})
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment