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" ...@@ -85,6 +85,17 @@ category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 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]] [[package]]
name = "coverage" name = "coverage"
version = "5.3" version = "5.3"
...@@ -104,6 +115,14 @@ category = "main" ...@@ -104,6 +115,14 @@ category = "main"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 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]] [[package]]
name = "docutils" name = "docutils"
version = "0.16" version = "0.16"
...@@ -376,7 +395,7 @@ name = "pygments" ...@@ -376,7 +395,7 @@ name = "pygments"
version = "2.7.1" version = "2.7.1"
description = "Pygments is a syntax highlighting package written in Python." description = "Pygments is a syntax highlighting package written in Python."
category = "main" category = "main"
optional = true optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
[[package]] [[package]]
...@@ -537,6 +556,24 @@ python-versions = "*" ...@@ -537,6 +556,24 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
astroid = ">=1.4" 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]] [[package]]
name = "rstcheck" name = "rstcheck"
version = "3.3.1" version = "3.3.1"
...@@ -737,6 +774,14 @@ category = "main" ...@@ -737,6 +774,14 @@ category = "main"
optional = false optional = false
python-versions = "*" 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]] [[package]]
name = "unidecode" name = "unidecode"
version = "1.1.1" version = "1.1.1"
...@@ -804,7 +849,7 @@ test = [] ...@@ -804,7 +849,7 @@ test = []
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.6" python-versions = "^3.6"
content-hash = "4ec5a3e4bfd4075233d7356350beeb69f09f659a18bb7d1450d209e40f15447a" content-hash = "684ae060e918da15c709fa66630983cb36502abed30a288f1e015ed337f0c1a0"
[metadata.files] [metadata.files]
alabaster = [ alabaster = [
...@@ -843,6 +888,10 @@ colorama = [ ...@@ -843,6 +888,10 @@ colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, {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 = [ coverage = [
{file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"}, {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"}, {file = "coverage-5.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4"},
...@@ -914,6 +963,10 @@ cython = [ ...@@ -914,6 +963,10 @@ cython = [
{file = "Cython-0.29.21-py2.py3-none-any.whl", hash = "sha256:5c4276fdcbccdf1e3c1756c7aeb8395e9a36874fa4d30860e7694f43d325ae13"}, {file = "Cython-0.29.21-py2.py3-none-any.whl", hash = "sha256:5c4276fdcbccdf1e3c1756c7aeb8395e9a36874fa4d30860e7694f43d325ae13"},
{file = "Cython-0.29.21.tar.gz", hash = "sha256:e57acb89bd55943c8d8bf813763d20b9099cc7165c0f16b707631a7654be9cad"}, {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 = [ docutils = [
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
...@@ -1140,6 +1193,10 @@ requests = [ ...@@ -1140,6 +1193,10 @@ requests = [
requirements-detector = [ requirements-detector = [
{file = "requirements-detector-0.7.tar.gz", hash = "sha256:0d1e13e61ed243f9c3c86e6cbb19980bcb3a0e0619cde2ec1f3af70fdbee6f7b"}, {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 = [ rstcheck = [
{file = "rstcheck-3.3.1.tar.gz", hash = "sha256:92c4f79256a54270e0402ba16a2f92d0b3c15c8f4410cb9c57127067c215741f"}, {file = "rstcheck-3.3.1.tar.gz", hash = "sha256:92c4f79256a54270e0402ba16a2f92d0b3c15c8f4410cb9c57127067c215741f"},
] ]
...@@ -1235,6 +1292,11 @@ typed-ast = [ ...@@ -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-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"},
{file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, {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 = [ unidecode = [
{file = "Unidecode-1.1.1-py2.py3-none-any.whl", hash = "sha256:1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a"}, {file = "Unidecode-1.1.1-py2.py3-none-any.whl", hash = "sha256:1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a"},
{file = "Unidecode-1.1.1.tar.gz", hash = "sha256:2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"}, {file = "Unidecode-1.1.1.tar.gz", hash = "sha256:2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"},
......
...@@ -5,8 +5,11 @@ import cProfile ...@@ -5,8 +5,11 @@ import cProfile
import logging import logging
import pathlib import pathlib
import sys import sys
import textwrap
import typing import typing
from rich.logging import RichHandler
from .frame import Frame from .frame import Frame
from .mapping import Mapping from .mapping import Mapping
from .bondset import BondSet from .bondset import BondSet
...@@ -37,31 +40,31 @@ def measure_bonds(frame: Frame, mapping: typing.Optional[Mapping], ...@@ -37,31 +40,31 @@ def measure_bonds(frame: Frame, mapping: typing.Optional[Mapping],
:param mapping: :param mapping:
:param config: Program arguments from argparse :param config: Program arguments from argparse
""" """
bonds = BondSet(config.bnd, config) bonds = BondSet(config.bondset, config)
logger.info("Bond measurements will be made")
bonds.apply(frame) 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. # Only perform Boltzmann Inversion if we have a mapping and a trajectory.
# Otherwise we get infinite force constants. # Otherwise we get infinite force constants.
logger.info("Beginning Boltzmann Inversion") logger.info('Starting Boltzmann Inversion')
bonds.boltzmann_invert() bonds.boltzmann_invert()
logger.info('Finished Boltzmann Inversion')
if config.output_forcefield: if config.output_forcefield:
logger.info("Creating GROMACS forcefield directory") logger.info("Writing GROMACS forcefield directory")
out_dir = pathlib.Path(config.out_dir) out_dir = pathlib.Path(config.out_dir)
forcefield = ForceField(config.output_name, dir_path=out_dir) forcefield = ForceField(config.output_name, dir_path=out_dir)
forcefield.write(config.output_name, mapping, bonds) forcefield.write(config.output_name, mapping, bonds)
logger.info("GROMACS forcefield directory created") logger.info("Finished writing GROMACS forcefield directory")
else: else:
bonds.write_itp(get_output_filepath('itp', config), bonds.write_itp(get_output_filepath('itp', config),
mapping=mapping) mapping=mapping)
if config.dump_measurements: 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) 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]: def mapping_loop(frame: Frame, config) -> typing.Tuple[Frame, Mapping]:
...@@ -70,11 +73,12 @@ 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 frame:
:param config: Program arguments from argparse :param config: Program arguments from argparse
""" """
logger.info("Mapping will be performed") logger.info('Starting AA->CG mapping')
mapping = Mapping(config.map, config, itp_filename=config.itp) mapping = Mapping(config.mapping, config, itp_filename=config.itp)
cg_frame = mapping.apply(frame) cg_frame = mapping.apply(frame)
cg_frame.save(get_output_filepath('gro', config), frame_number=0) cg_frame.save(get_output_filepath('gro', config), frame_number=0)
logging.info('Finished AA->CG mapping')
return cg_frame, mapping return cg_frame, mapping
...@@ -92,18 +96,18 @@ def full_run(config): ...@@ -92,18 +96,18 @@ def full_run(config):
frame_start=config.begin, frame_start=config.begin,
frame_end=config.end) frame_end=config.end)
if config.map: if config.mapping:
cg_frame, mapping = mapping_loop(frame, config) cg_frame, mapping = mapping_loop(frame, config)
else: else:
logger.info("Mapping will not be performed") logger.info('Skipping AA->CG mapping')
mapping = None mapping = None
cg_frame = frame cg_frame = frame
if config.output_xtc: if config.output_xtc:
cg_frame.save(get_output_filepath('xtc', config)) cg_frame.save(get_output_filepath('xtc', config))
if config.bnd: if config.bondset:
measure_bonds(cg_frame, mapping, config) measure_bonds(cg_frame, mapping, config)
...@@ -134,9 +138,9 @@ def parse_arguments(arg_list): ...@@ -134,9 +138,9 @@ def parse_arguments(arg_list):
help="AA simulation topology - e.g. PDB, GRO, etc.") help="AA simulation topology - e.g. PDB, GRO, etc.")
input_files.add_argument('trajectory', type=str, nargs='?', input_files.add_argument('trajectory', type=str, nargs='?',
help="AA simulation trajectory - e.g. XTC, DCD, etc.") 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") help="Mapping file")
input_files.add_argument('-b', '--bnd', type=str, input_files.add_argument('-b', '--bondset', type=str,
help="Bonds file") help="Bonds file")
input_files.add_argument('-i', '--itp', type=str, input_files.add_argument('-i', '--itp', type=str,
help="GROMACS ITP file") help="GROMACS ITP file")
...@@ -200,6 +204,9 @@ def parse_arguments(arg_list): ...@@ -200,6 +204,9 @@ def parse_arguments(arg_list):
run_options.add_argument('--profile', '--no-profile', default=False, action=BooleanAction, run_options.add_argument('--profile', '--no-profile', default=False, action=BooleanAction,
help="Profile performance?") 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 # yapf: enable
args = parser.parse_args(arg_list) args = parser.parse_args(arg_list)
...@@ -219,12 +226,12 @@ def validate_arguments(args): ...@@ -219,12 +226,12 @@ def validate_arguments(args):
:param args: Parsed arguments from ArgumentParser :param args: Parsed arguments from ArgumentParser
""" """
if not args.dump_measurements: 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: 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.") raise ArgumentValidationError("One or both of -m and -b is required.")
return args return args
...@@ -233,9 +240,34 @@ def validate_arguments(args): ...@@ -233,9 +240,34 @@ def validate_arguments(args):
def main(): def main():
args = parse_arguments(sys.argv[1:]) args = parse_arguments(sys.argv[1:])
print("Using GRO: {0}".format(args.topology)) logging.basicConfig(level=args.log_level,
print("Using XTC: {0}".format(args.trajectory)) 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: if args.profile:
with cProfile.Profile() as profiler: with cProfile.Profile() as profiler:
full_run(args) full_run(args)
...@@ -245,6 +277,11 @@ def main(): ...@@ -245,6 +277,11 @@ def main():
else: else:
full_run(args) full_run(args)
logger.info('Finished processing - goodbye!')
except Exception as exc:
logger.error(exc)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
...@@ -50,15 +50,18 @@ class Frame: ...@@ -50,15 +50,18 @@ class Frame:
""" """
if topology_file is not None: if topology_file is not None:
try: try:
logging.info('Loading topology file')
self._trajectory = mdtraj.load(str(topology_file)) self._trajectory = mdtraj.load(str(topology_file))
self._topology = self._trajectory.topology self._topology = self._trajectory.topology
logging.info('Finished loading topology file')
if trajectory_file is not None: if trajectory_file is not None:
try: try:
logging.info('Loading trajectory file - this may take a while')
self._trajectory = mdtraj.load(str(trajectory_file), self._trajectory = mdtraj.load(str(trajectory_file),
top=self._topology) top=self._topology)
self._slice_trajectory(frame_start, frame_end) self._slice_trajectory(frame_start, frame_end)
logging.info('Finished loading trajectory file')
except ValueError as exc: except ValueError as exc:
raise NonMatchingSystemError from exc raise NonMatchingSystemError from exc
......
...@@ -249,7 +249,7 @@ class Mapping: ...@@ -249,7 +249,7 @@ class Mapping:
name, typ, first, *atoms = mol_section[i][1:] name, typ, first, *atoms = mol_section[i][1:]
except KeyError as exc: 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: try:
# Allow optional charge in mapping file # Allow optional charge in mapping file
...@@ -262,8 +262,7 @@ class Mapping: ...@@ -262,8 +262,7 @@ class Mapping:
atoms.insert(0, first) atoms.insert(0, first)
if not atoms: if not atoms:
# TODO should this stop execution? raise ValueError(f'Bead {name} specification contains no atoms')
logger.warning('Bead %s specification contains no atoms', name)
mol_map.append( mol_map.append(
bead_class(name, i, type=typ, atoms=atoms, charge=charge)) bead_class(name, i, type=typ, atoms=atoms, charge=charge))
...@@ -421,7 +420,6 @@ class Mapping: ...@@ -421,7 +420,6 @@ class Mapping:
return cg_frame return cg_frame
# TODO: Use MDTraj instead
def calc_coords_weight(ref_coords, coords, weights, box=None): def calc_coords_weight(ref_coords, coords, weights, box=None):
"""Calculate the coordinates of a single CG bead from weighted component atom coordinates. """Calculate the coordinates of a single CG bead from weighted component atom coordinates.
......
...@@ -31,6 +31,7 @@ wheel = "^0.35.1" ...@@ -31,6 +31,7 @@ wheel = "^0.35.1"
numpy = "^1.19.1" numpy = "^1.19.1"
cython = "^0.29.21" cython = "^0.29.21"
mdtraj = "^1.9.4" mdtraj = "^1.9.4"
rich = "^9.2.0"
# Optional extras to enable additional functionality # Optional extras to enable additional functionality
sphinx-autoapi = { version = "^1.5.0", optional = true } sphinx-autoapi = { version = "^1.5.0", optional = true }
......
...@@ -57,7 +57,7 @@ class PycgtoolTest(unittest.TestCase): ...@@ -57,7 +57,7 @@ class PycgtoolTest(unittest.TestCase):
]) ])
self.assertEqual('TOPOLOGY', args.topology) self.assertEqual('TOPOLOGY', args.topology)
self.assertEqual('MAP', args.map) self.assertEqual('MAP', args.mapping)
self.assertEqual(1000, args.begin) self.assertEqual(1000, args.begin)
def test_map_only(self): def test_map_only(self):
...@@ -117,7 +117,7 @@ class PycgtoolTest(unittest.TestCase): ...@@ -117,7 +117,7 @@ class PycgtoolTest(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
tmp_path = pathlib.Path(tmpdir) tmp_path = pathlib.Path(tmpdir)
args = get_args('sugar', tmp_path, extra={ args = get_args('sugar', tmp_path, extra={
'map': None, 'mapping': None,
'trajectory': None, 'trajectory': None,
}) })
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment