diff --git a/pycgtool.py b/pycgtool.py index c30d7150ff40327cfd2beda3e22a2fa349e67a4f..ebd3000544ead28601add8eb21422de170358ef4 100755 --- a/pycgtool.py +++ b/pycgtool.py @@ -2,100 +2,9 @@ import argparse import sys -import logging - -from pycgtool.frame import Frame -from pycgtool.mapping import Mapping -from pycgtool.bondset import BondSet -from pycgtool.forcefield import ForceField -from pycgtool.interface import Progress, Options - -logger = logging.getLogger(__name__) - - -def main(args, config): - """ - Main function of the program PyCGTOOL. - - Performs the complete AA->CG mapping and outputs a files dependent on given input. - - :param args: Arguments from argparse - :param config: Configuration dictionary - """ - frame = Frame(gro=args.gro, xtc=args.xtc, itp=args.itp, frame_start=args.begin, xtc_reader="mdtraj") - - if args.bnd: - bonds = BondSet(args.bnd, config) - logger.info("Bond measurements will be made") - else: - logger.info("Bond measurements will not be made") - - if args.map: - mapping = Mapping(args.map, config, itp=args.itp) - cgframe = mapping.apply(frame, exclude={"SOL"}) - cgframe.output(config.output_name + ".gro", format=config.output) - logger.info("Mapping will be performed") - else: - logger.info("Mapping will not be performed") - - # Main loop - perform mapping and measurement on every frame in XTC - def main_loop(): - nonlocal cgframe - frame.next_frame() - if args.map: - cgframe = mapping.apply(frame, cgframe=cgframe, exclude={"SOL"}) - if config.output_xtc: - cgframe.write_xtc(config.output_name + ".xtc") - else: - cgframe = frame - if args.bnd: - bonds.apply(cgframe) - - numframes = frame.numframes - args.begin if args.end == -1 else args.end - args.begin - logger.info("Beginning analysis of {0} frames".format(numframes)) - Progress(numframes, postwhile=main_loop).run() - - if args.bnd: - if args.map: - logger.info("Beginning Boltzmann inversion") - bonds.boltzmann_invert() - if config.output_forcefield: - logger.info("Creating GROMACS forcefield directory") - ff = ForceField("ff" + config.output_name + ".ff") - ff.write_rtp(config.output_name + ".rtp", mapping, bonds) - logger.info("GROMACS forcefield directory created") - else: - bonds.write_itp(config.output_name + ".itp", mapping=mapping) - - if config.dump_measurements: - logger.info("Dumping bond measurements to file") - bonds.dump_values(config.dump_n_values) - - -def map_only(args, config): - """ - Perform AA->CG mapping and output coordinate file. - - :param args: Program arguments - :param config: Object containing run options - """ - frame = Frame(gro=args.gro, xtc=args.xtc) - mapping = Mapping(args.map, config) - cgframe = mapping.apply(frame, exclude={"SOL"}) - cgframe.output(config.output_name + ".gro", format=config.output) - - if args.xtc and (config.output_xtc or args.outputxtc): - # Main loop - perform mapping and measurement on every frame in XTC - def main_loop(): - nonlocal cgframe - frame.next_frame() - cgframe = mapping.apply(frame, cgframe=cgframe, exclude={"SOL"}) - cgframe.write_xtc(config.output_name + ".xtc") - - numframes = frame.numframes - args.begin if args.end == -1 else args.end - args.begin - logger.info("Beginning analysis of {0} frames".format(numframes)) - Progress(numframes, postwhile=main_loop).run() +from pycgtool.pycgtool import main, map_only +from pycgtool.interface import Options if __name__ == "__main__": parser = argparse.ArgumentParser(description="Perform coarse-grain mapping of atomistic trajectory") @@ -108,6 +17,7 @@ if __name__ == "__main__": parser.add_argument('--interactive', default=False, action='store_true') parser.add_argument('--outputxtc', default=False, action='store_true') + parser.add_argument('--quiet', default=False, action='store_true') input_files.add_argument('--begin', type=int, default=0, help="Frame number to begin") input_files.add_argument('--end', type=int, default=-1, help="Frame number to end") @@ -140,10 +50,6 @@ if __name__ == "__main__": print("Using XTC: {0}".format(args.xtc)) if config.map_only: - logger.info("Starting: mapping only") map_only(args, config) else: - logger.info("Starting: parameter measurement") main(args, config) - - logger.info("Analysis complete") diff --git a/pycgtool/interface.py b/pycgtool/interface.py index d22d3ed2d8b6cf88446870cb41d2fb1d68fecce5..db65c372b3a14fac46ea135873892fc9371883a2 100644 --- a/pycgtool/interface.py +++ b/pycgtool/interface.py @@ -263,14 +263,17 @@ class Progress: """ Iterate through self until stopped by maximum iterations or False condition. """ - collections.deque(self, maxlen=0) + # collections.deque(self, maxlen=0) + for _ in self: + pass return self._its @property def _bar(self): done = int(self._length * (self._its / self._maxits)) left = self._length - done - return "{0} [".format(self._its) + done * "#" + left * "-" + "] {0}".format(self._maxits) + width = len(str(self._maxits)) + return "{0:-{width}} [".format(self._its, width=width) + done * "#" + left * "-" + "] {0}".format(self._maxits) def _stop(self): if not self._quiet: diff --git a/pycgtool/pycgtool.py b/pycgtool/pycgtool.py new file mode 100755 index 0000000000000000000000000000000000000000..c728e9b00310b32446263a0cbe8c3fef61148bf7 --- /dev/null +++ b/pycgtool/pycgtool.py @@ -0,0 +1,98 @@ +import logging + +from .frame import Frame +from .mapping import Mapping +from .bondset import BondSet +from .forcefield import ForceField +from .interface import Progress + +logger = logging.getLogger(__name__) + + +def main(args, config): + """ + Main function of the program PyCGTOOL. + + Performs the complete AA->CG mapping and outputs a files dependent on given input. + + :param args: Arguments from argparse + :param config: Configuration dictionary + """ + frame = Frame(gro=args.gro, xtc=args.xtc, itp=args.itp, frame_start=args.begin, xtc_reader="mdtraj") + + if args.bnd: + bonds = BondSet(args.bnd, config) + logger.info("Bond measurements will be made") + else: + logger.info("Bond measurements will not be made") + + if args.map: + mapping = Mapping(args.map, config, itp=args.itp) + cgframe = mapping.apply(frame, exclude={"SOL"}) + cgframe.output(config.output_name + ".gro", format=config.output) + logger.info("Mapping will be performed") + else: + logger.info("Mapping will not be performed") + + # Main loop - perform mapping and measurement on every frame in XTC + def main_loop(): + nonlocal cgframe + if not frame.next_frame(): + return False + if args.map: + cgframe = mapping.apply(frame, cgframe=cgframe, exclude={"SOL"}) + if config.output_xtc: + cgframe.write_xtc(config.output_name + ".xtc") + else: + cgframe = frame + if args.bnd: + bonds.apply(cgframe) + return True + + numframes = frame.numframes - args.begin if args.end == -1 else args.end - args.begin + logger.info("Beginning analysis of {0} frames".format(numframes)) + Progress(numframes, postwhile=main_loop, quiet=args.quiet).run() + + if args.bnd: + if args.map: + logger.info("Beginning Boltzmann inversion") + bonds.boltzmann_invert() + if config.output_forcefield: + logger.info("Creating GROMACS forcefield directory") + ff = ForceField("ff" + config.output_name + ".ff") + ff.write_rtp(config.output_name + ".rtp", mapping, bonds) + logger.info("GROMACS forcefield directory created") + else: + bonds.write_itp(config.output_name + ".itp", mapping=mapping) + + if config.dump_measurements: + logger.info("Dumping bond measurements to file") + bonds.dump_values(config.dump_n_values) + + +def map_only(args, config): + """ + Perform AA->CG mapping and output coordinate file. + + :param args: Program arguments + :param config: Object containing run options + """ + frame = Frame(gro=args.gro, xtc=args.xtc) + mapping = Mapping(args.map, config) + cgframe = mapping.apply(frame, exclude={"SOL"}) + cgframe.output(config.output_name + ".gro", format=config.output) + + if args.xtc and (config.output_xtc or args.outputxtc): + # Main loop - perform mapping and measurement on every frame in XTC + def main_loop(): + nonlocal cgframe + if not frame.next_frame(): + return False + cgframe = mapping.apply(frame, cgframe=cgframe, exclude={"SOL"}) + cgframe.write_xtc(config.output_name + ".xtc") + return True + + numframes = frame.numframes - args.begin if args.end == -1 else args.end - args.begin + logger.info("Beginning analysis of {0} frames".format(numframes)) + Progress(numframes, postwhile=main_loop, quiet=args.quiet).run() + diff --git a/test/data/sugar_out.xtc b/test/data/sugar_out.xtc new file mode 100644 index 0000000000000000000000000000000000000000..0f9b0822191d84aa887bedd2c1c71978a93fcb20 Binary files /dev/null and b/test/data/sugar_out.xtc differ diff --git a/test/test_pycgtool.py b/test/test_pycgtool.py index 2b1edd275a4bf73771e3cd4c3dc60a797487e987..eb1bf0db8b7f6766b781a46c6509b9caa55d5640 100644 --- a/test/test_pycgtool.py +++ b/test/test_pycgtool.py @@ -2,12 +2,57 @@ import unittest import subprocess import os +import numpy as np + +from simpletraj.trajectory import XtcTrajectory + +from pycgtool.interface import Options +from pycgtool.pycgtool import main, map_only + + +class Args: + def __init__(self, name, map=True, bnd=True): + self.gro = os.path.join("test/data", name+".gro") + self.xtc = os.path.join("test/data", name+".xtc") + self.map = os.path.join("test/data", name+".map") if map else None + self.bnd = os.path.join("test/data", name+".bnd") if bnd else None + self.begin = 0 + self.end = -1 + self.quiet = True + class PycgtoolTest(unittest.TestCase): + config = Options([("output_name", "out"), + ("output", "gro"), + ("output_xtc", True), + ("map_only", False), + ("map_center", "geom"), + ("constr_threshold", 100000), + ("dump_measurements", False), + ("dump_n_values", 100000), + ("output_forcefield", False), + ("temperature", 310), + ("angle_default_fc", True), + ("generate_angles", True), + ("generate_dihedrals", False)]) + def test_run(self): path = os.path.dirname(os.path.dirname(__file__)) self.assertEqual(0, subprocess.check_call([os.path.join(path, "pycgtool.py"), "-h"], stdout=subprocess.PIPE)) + def test_map_only(self): + map_only(Args("sugar"), self.config) + + xtc = XtcTrajectory("out.xtc") + xtc_ref = XtcTrajectory("test/data/sugar_out.xtc") + self.assertEqual(xtc_ref.numframes, xtc.numframes) + + for i in range(xtc_ref.numframes): + xtc.get_frame(i) + xtc_ref.get_frame(i) + np.testing.assert_array_almost_equal(xtc_ref.box, xtc.box, decimal=3) + np.testing.assert_array_almost_equal(xtc_ref.x, xtc.x, decimal=3) + # TODO more tests