def __init__(self, config): self.config = config self.in_frame = Frame( topology_file=self.config.topology, trajectory_file=self.config.trajectory, # May be None frame_start=self.config.begin, frame_end=self.config.end, ) self.mapping = None self.out_frame = self.in_frame if self.config.mapping: self.mapping, self.out_frame = self.apply_mapping(self.in_frame) if self.out_frame.natoms == 0: # The following steps won't work with frames containing no atoms logger.warning( "Mapped trajectory contains no atoms - check your mapping file is correct!" ) return self.bondset = None if self.config.bondset: self.bondset = BondSet(self.config.bondset, self.config) self.measure_bonds() if self.config.output_xtc: self.out_frame.save(self.get_output_filepath("xtc"))
def test_bondset_boltzmann_invert(self): measure = BondSet("test/data/sugar.bnd", DummyOptions) frame = Frame("test/data/sugar.gro", xtc="test/data/sugar.xtc") mapping = Mapping("test/data/sugar.map", DummyOptions) cgframe = mapping.apply(frame) while frame.next_frame(): cgframe = mapping.apply(frame, cgframe=cgframe) measure.apply(cgframe) measure.boltzmann_invert() self.support_check_mean_fc(measure["ALLA"], 1)
def test_bondset_polymer(self): bondset = BondSet("test/data/polyethene.bnd", DummyOptions) frame = Frame("test/data/polyethene.gro") bondset.apply(frame) self.assertEqual(5, len(bondset["ETH"][0].values)) self.assertEqual(4, len(bondset["ETH"][1].values)) self.assertEqual(4, len(bondset["ETH"][2].values)) self.assertEqual(4, len(bondset["ETH"][3].values)) bondset.boltzmann_invert() self.assertAlmostEqual(0.107, bondset["ETH"][0].eqm, delta=0.107 / 500) self.assertAlmostEqual(0.107, bondset["ETH"][1].eqm, delta=0.107 / 500)
def test_full_itp_vsites(self): """Test full operation to output of .itp file for molecule with vsites.""" options = DummyOptions() options.generate_angles = False measure = BondSet(self.data_dir.joinpath("martini3/naphthalene.bnd"), options) frame = Frame(self.data_dir.joinpath("martini3/naphthalene.gro")) mapping = Mapping(self.data_dir.joinpath("martini3/naphthalene.map"), options) cg_frame = mapping.apply(frame) measure.apply(cg_frame) measure.boltzmann_invert() with tempfile.TemporaryDirectory() as tmpdir: tmp_path = pathlib.Path(tmpdir) measure.write_itp(tmp_path.joinpath("naphthalene_out.itp"), mapping) self.assertTrue( cmp_file_whitespace_float( tmp_path.joinpath("naphthalene_out.itp"), self.data_dir.joinpath("martini3/naphthalene_out.itp"), rtol=0.005, verbose=True, ))
def test_bondset_boltzmann_invert_mdtraj(self): logging.disable(logging.WARNING) frame = Frame("test/data/sugar.gro", xtc="test/data/sugar.xtc", xtc_reader="mdtraj") logging.disable(logging.NOTSET) measure = BondSet("test/data/sugar.bnd", DummyOptions) mapping = Mapping("test/data/sugar.map", DummyOptions) cgframe = mapping.apply(frame) while frame.next_frame(): cgframe = mapping.apply(frame, cgframe=cgframe) measure.apply(cgframe) measure.boltzmann_invert() self.support_check_mean_fc(measure["ALLA"], 1)
def test_bondset_apply(self): measure = BondSet("test/data/sugar.bnd", DummyOptions) frame = Frame("test/data/sugar-cg.gro") measure.apply(frame) # First six are bond lengths self.assertEqual(1, len(measure["ALLA"][0].values)) self.assertAlmostEqual(0.2225376, measure["ALLA"][0].values[0], delta=0.2225376 / 500) # Second six are angles self.assertEqual(1, len(measure["ALLA"][6].values)) self.assertAlmostEqual(77.22779289, measure["ALLA"][6].values[0], delta=77.22779289 / 500) # Final six are dihedrals self.assertEqual(1, len(measure["ALLA"][12].values)) self.assertAlmostEqual(-89.5552903, measure["ALLA"][12].values[0], delta=89.552903 / 500)
def test_bondset_boltzmann_invert_func_forms(self): class FuncFormOptions(DummyOptions): length_form = "CosHarmonic" angle_form = "Harmonic" dihedral_form = "MartiniDefaultLength" measure = BondSet("test/data/sugar.bnd", FuncFormOptions) frame = Frame("test/data/sugar.gro", xtc="test/data/sugar.xtc") mapping = Mapping("test/data/sugar.map", DummyOptions) cgframe = mapping.apply(frame) while frame.next_frame(): cgframe = mapping.apply(frame, cgframe=cgframe) measure.apply(cgframe) measure.boltzmann_invert() self.support_check_mean_fc(measure["ALLA"], 3)
def test_bondset_pbc(self): bondset = BondSet("test/data/polyethene.bnd", DummyOptions) frame = Frame("test/data/pbcpolyethene.gro") bondset.apply(frame) bondset.boltzmann_invert() for bond in bondset.get_bond_lengths("ETH", True): self.assertAlmostEqual(1., bond.eqm) self.assertEqual(float("inf"), bond.fconst)
def test_bondset_apply(self): measure = BondSet("test/data/sugar.bnd", DummyOptions) frame = Frame("test/data/sugar-cg.gro") measure.apply(frame) # First six are bond lengths self.assertEqual(1, len(measure["ALLA"][0].values)) self.assertAlmostEqual(0.2225376, measure["ALLA"][0].values[0], delta=0.2225376 / 500) # Second six are angles self.assertEqual(1, len(measure["ALLA"][6].values)) expected = math.radians(77.22779289) self.assertAlmostEqual(expected, measure["ALLA"][6].values[0], delta=expected / 500) # Final six are dihedrals self.assertEqual(1, len(measure["ALLA"][12].values)) expected = math.radians(-89.5552903) self.assertAlmostEqual(expected, measure["ALLA"][12].values[0], delta=abs(expected) / 500)
def test_bondset_pbc(self): bondset = BondSet(self.data_dir.joinpath("polyethene.bnd"), DummyOptions) frame = Frame(self.data_dir.joinpath("pbcpolyethene.gro")) bondset.apply(frame) bondset.boltzmann_invert() for bond in bondset.get_bond_lengths("ETH", True): self.assertAlmostEqual(1.0, bond.eqm) self.assertEqual(math.inf, bond.fconst)
def test_get_lines_for_bond_dump(self): expected = [ " 0.00000 1.00000 2.00000", " 1.00000 2.00000 3.00000", " 2.00000 3.00000 4.00000", " 3.00000 4.00000 5.00000", ] bonds = [ DummyBond(None, None, None, values=[0, 1, 2, 3]), DummyBond(None, None, None, values=[1, 2, 3, 4]), DummyBond(None, None, None, values=[2, 3, 4, 5]), ] output = BondSet._get_lines_for_bond_dump(bonds) self.assertListEqual(expected, output)
def test_bondset_boltzmann_invert(self): measure = BondSet(self.data_dir.joinpath("sugar.bnd"), DummyOptions) frame = Frame(self.data_dir.joinpath("sugar.gro"), self.data_dir.joinpath("sugar.xtc")) mapping = Mapping(self.data_dir.joinpath("sugar.map"), DummyOptions) cg_frame = mapping.apply(frame) measure.apply(cg_frame) measure.boltzmann_invert() self.support_check_mean_fc(measure["ALLA"], 1)
def test_full_itp_sugar(self): measure = BondSet("test/data/sugar.bnd", DummyOptions) frame = Frame("test/data/sugar.gro", xtc="test/data/sugar.xtc") mapping = Mapping("test/data/sugar.map", DummyOptions) cgframe = mapping.apply(frame) while frame.next_frame(): cgframe = mapping.apply(frame, cgframe=cgframe) measure.apply(cgframe) measure.boltzmann_invert() logging.disable(logging.WARNING) measure.write_itp("sugar_out.itp", mapping) logging.disable(logging.NOTSET) self.assertTrue( cmp_whitespace_float("sugar_out.itp", "test/data/sugar_out.itp", float_rel_error=0.001))
def test_dump_bonds(self): measure = BondSet(self.data_dir.joinpath("sugar.bnd"), DummyOptions) frame = Frame(self.data_dir.joinpath("sugar.gro"), self.data_dir.joinpath("sugar.xtc")) mapping = Mapping(self.data_dir.joinpath("sugar.map"), DummyOptions) cg_frame = mapping.apply(frame) measure.apply(cg_frame) measure.boltzmann_invert() measure.dump_values() filenames = ("ALLA_length.dat", "ALLA_angle.dat", "ALLA_dihedral.dat") for filename in filenames: self.assertTrue( cmp_file_whitespace_float(self.data_dir.joinpath(filename), filename, rtol=0.008, verbose=True)) os.remove(filename)
def test_full_itp_sugar(self): measure = BondSet("test/data/sugar.bnd", DummyOptions) frame = Frame("test/data/sugar.gro", xtc="test/data/sugar.xtc") mapping = Mapping("test/data/sugar.map", DummyOptions) cgframe = mapping.apply(frame) while frame.next_frame(): cgframe = mapping.apply(frame, cgframe=cgframe) measure.apply(cgframe) measure.boltzmann_invert() logging.disable(logging.WARNING) measure.write_itp("sugar_out.itp", mapping) logging.disable(logging.NOTSET) self.assertTrue(cmp_whitespace_float("sugar_out.itp", "test/data/sugar_out.itp", float_rel_error=0.001))
def test_full_itp_sugar(self): measure = BondSet(self.data_dir.joinpath("sugar.bnd"), DummyOptions) frame = Frame(self.data_dir.joinpath("sugar.gro"), self.data_dir.joinpath("sugar.xtc")) mapping = Mapping(self.data_dir.joinpath("sugar.map"), DummyOptions) cg_frame = mapping.apply(frame) measure.apply(cg_frame) measure.boltzmann_invert() with tempfile.TemporaryDirectory() as tmpdir: tmp_path = pathlib.Path(tmpdir) measure.write_itp(tmp_path.joinpath("sugar_out.itp"), mapping) self.assertTrue( cmp_file_whitespace_float( tmp_path.joinpath("sugar_out.itp"), self.data_dir.joinpath("sugar_out.itp"), rtol=0.005, verbose=True, ))
def test_bondset_boltzmann_invert_manual_default_fc(self): class FuncFormOptions(DummyOptions): length_form = "MartiniDefaultLength" angle_form = "MartiniDefaultAngle" dihedral_form = "MartiniDefaultDihedral" measure = BondSet(self.data_dir.joinpath("sugar.bnd"), FuncFormOptions) frame = Frame(self.data_dir.joinpath("sugar.gro"), self.data_dir.joinpath("sugar.xtc")) mapping = Mapping(self.data_dir.joinpath("sugar.map"), DummyOptions) cg_frame = mapping.apply(frame) measure.apply(cg_frame) measure.boltzmann_invert() self.support_check_mean_fc(measure["ALLA"], 2)
def test_bondset_boltzmann_invert_manual_default_fc(self): class FuncFormOptions(DummyOptions): length_form = "MartiniDefaultLength" angle_form = "MartiniDefaultAngle" dihedral_form = "MartiniDefaultDihedral" measure = BondSet("test/data/sugar.bnd", FuncFormOptions) frame = Frame("test/data/sugar.gro", xtc="test/data/sugar.xtc") mapping = Mapping("test/data/sugar.map", DummyOptions) cgframe = mapping.apply(frame) while frame.next_frame(): cgframe = mapping.apply(frame, cgframe=cgframe) measure.apply(cgframe) measure.boltzmann_invert() self.support_check_mean_fc(measure["ALLA"], 2)
def test_get_lines_for_bond_dump_sample(self): expected = [ " 0.00000 1.00000 2.00000", " 1.00000 2.00000 3.00000", " 2.00000 3.00000 4.00000", " 3.00000 4.00000 5.00000", ] bonds = [ DummyBond(None, None, None, values=[0, 1, 2, 3]), DummyBond(None, None, None, values=[1, 2, 3, 4]), DummyBond(None, None, None, values=[2, 3, 4, 5]), ] nlines = 2 output = BondSet._get_lines_for_bond_dump(bonds, target_number=nlines) self.assertEqual(nlines, len(output)) seen = set() for line in output: self.assertIn(line, expected) self.assertNotIn(line, seen) seen.add(line)
def test_duplicate_atoms_in_bond(self): with self.assertRaises(ValueError): bondset = BondSet("test/data/duplicate_atoms.bnd", DummyOptions)
def test_bondset_create(self): measure = BondSet("test/data/sugar.bnd", DummyOptions) self.assertEqual(1, len(measure)) self.assertTrue("ALLA" in measure) self.assertEqual(18, len(measure["ALLA"]))
def test_duplicate_atoms_in_bond(self): with self.assertRaises(ValueError): _ = BondSet(self.data_dir.joinpath("duplicate_atoms.bnd"), DummyOptions)
def test_bondset_create(self): measure = BondSet(self.data_dir.joinpath("sugar.bnd"), DummyOptions) self.assertEqual(1, len(measure)) self.assertTrue("ALLA" in measure) self.assertEqual(18, len(measure["ALLA"]))
class PyCGTOOL: def __init__(self, config): self.config = config self.in_frame = Frame( topology_file=self.config.topology, trajectory_file=self.config.trajectory, # May be None frame_start=self.config.begin, frame_end=self.config.end, ) self.mapping = None self.out_frame = self.in_frame if self.config.mapping: self.mapping, self.out_frame = self.apply_mapping(self.in_frame) if self.out_frame.natoms == 0: # The following steps won't work with frames containing no atoms logger.warning( "Mapped trajectory contains no atoms - check your mapping file is correct!" ) return self.bondset = None if self.config.bondset: self.bondset = BondSet(self.config.bondset, self.config) self.measure_bonds() if self.config.output_xtc: self.out_frame.save(self.get_output_filepath("xtc")) def get_output_filepath(self, ext: PathLike) -> pathlib.Path: """Get file path for an output file by extension. Creates the output directory if missing. """ out_dir = pathlib.Path(self.config.out_dir) out_dir.mkdir(parents=True, exist_ok=True) return out_dir.joinpath(self.config.output_name + "." + ext) def apply_mapping(self, in_frame: Frame) -> typing.Tuple[Mapping, Frame]: """Map input frame to output using requested mapping file.""" mapping = Mapping(self.config.mapping, self.config, itp_filename=self.config.itp) out_frame = mapping.apply(in_frame) out_frame.save(self.get_output_filepath("gro"), frame_number=0) if self.config.backmapper_resname and self.out_frame.n_frames > 1: try: self.train_backmapper(self.config.resname) except ImportError: logger.error("MDPlus must be installed to perform backmapping") return mapping, out_frame def measure_bonds(self) -> None: """Measure bonds at the end of a run.""" self.bondset.apply(self.out_frame) if self.mapping is not None and self.out_frame.n_frames > 1: # Only perform Boltzmann Inversion if we have a mapping and a trajectory. # Otherwise we get infinite force constants. logger.info("Starting Boltzmann Inversion") self.bondset.boltzmann_invert() logger.info("Finished Boltzmann Inversion") if self.config.output_forcefield: logger.info("Writing GROMACS forcefield directory") out_dir = pathlib.Path(self.config.out_dir) forcefield = ForceField(self.config.output_name, dir_path=out_dir) forcefield.write(self.config.output_name, self.mapping, self.bondset) logger.info("Finished writing GROMACS forcefield directory") else: self.bondset.write_itp(self.get_output_filepath("itp"), mapping=self.mapping) if self.config.dump_measurements: logger.info("Writing bond measurements to file") self.bondset.dump_values(self.config.dump_n_values, self.config.out_dir) logger.info("Finished writing bond measurements to file") def train_backmapper(self, resname: str): from mdplus.multiscale import GLIMPS sel = f"resname {resname}" aa_subset_traj = self.in_frame._trajectory.atom_slice( self.in_frame._trajectory.topology.select(sel)) cg_subset_traj = self.out_frame._trajectory.atom_slice( self.out_frame._trajectory.topology.select(sel)) logger.info("Training backmapper") # Param x_valence is approximate number of bonds per CG bead # Values greater than 2 fail for small molecules e.g. sugar test case backmapper = GLIMPS(x_valence=2) backmapper.fit(cg_subset_traj.xyz, aa_subset_traj.xyz) logger.info("Finished training backmapper") backmapper.save(self.get_output_filepath("backmapper.pkl"))
def test_bondset_remove_triangles(self): bondset = BondSet(self.data_dir.joinpath("triangle.bnd"), DummyOptions) angles = bondset.get_bond_angles("TRI", exclude_triangle=False) self.assertEqual(3, len(angles)) angles = bondset.get_bond_angles("TRI", exclude_triangle=True) self.assertEqual(0, len(angles))
def test_bondset_remove_triangles(self): bondset = BondSet("test/data/triangle.bnd", DummyOptions) angles = bondset.get_bond_angles("TRI", exclude_triangle=False) self.assertEqual(3, len(angles)) angles = bondset.get_bond_angles("TRI", exclude_triangle=True) self.assertEqual(0, len(angles))