def test_extract_dummy(): cb6 = pmd.load_file( os.path.join(os.path.dirname(__file__), "../data/cb6-but/vac.pdb") ) aligned_cb6 = zalign(cb6, ":CB6", ":BUT") aligned_cb6 = add_dummy(aligned_cb6, residue_name="DM1", z=-6.0) aligned_cb6 = add_dummy(aligned_cb6, residue_name="DM2", z=-9.0) aligned_cb6 = add_dummy(aligned_cb6, residue_name="DM3", z=-11.2, y=2.2) dm_index = [] for atom in aligned_cb6.topology.atoms(): if atom.residue.name in ["DM1", "DM2", "DM3"]: dm_index.append(atom.index) assert len(dm_index) != 0 dummy_dict = extract_dummy_atoms(aligned_cb6, serial=False) assert all(dummy_dict["DM1"]["pos"] == [0.0, 0.0, -6.0]) assert all(dummy_dict["DM2"]["pos"] == [0.0, 0.0, -9.0]) assert all(dummy_dict["DM3"]["pos"] == [0.0, 2.2, -11.2]) assert dummy_dict["DM1"]["mass"] == 208.0 assert dummy_dict["DM2"]["mass"] == 208.0 assert dummy_dict["DM3"]["mass"] == 208.0 assert dummy_dict["DM1"]["idx"] == dm_index[0] assert dummy_dict["DM2"]["idx"] == dm_index[1] assert dummy_dict["DM3"]["idx"] == dm_index[2]
def test_center_mask(): """ Test that the first mask is centered """ cb6 = pmd.load_file( os.path.join(os.path.dirname(__file__), "../data/cb6-but/vac.pdb")) aligned_cb6 = zalign(cb6, ":CB6", ":BUT") test_coordinates = check_coordinates(aligned_cb6, ":CB6") assert np.allclose(test_coordinates, np.zeros(3))
def test_alignment_after_offset(): """ Test that molecule is properly aligned after random offset. """ cb6 = pmd.load_file( os.path.join(os.path.dirname(__file__), "../data/cb6-but/vac.pdb")) random_coordinates = np.random.randint(10) * np.random.rand(1, 3) cb6_offset = offset_structure(cb6, random_coordinates) aligned_cb6 = zalign(cb6_offset, ":CB6", ":BUT") test_coordinates = check_coordinates(aligned_cb6, ":CB6") assert np.allclose(test_coordinates, np.zeros(3))
def test_theta_after_alignment(): """ Test that molecule is properly aligned after random offset. """ cb6 = pmd.load_file( os.path.join(os.path.dirname(__file__), "../data/cb6-but/vac.pdb")) aligned_cb6 = zalign(cb6, ":CB6", ":BUT") assert get_theta(aligned_cb6, ":CB6", ":BUT", axis="z") == 0 assert (pytest.approx(get_theta(aligned_cb6, ":CB6", ":BUT", axis="x"), abs=1e-3) == 1.5708) assert (pytest.approx(get_theta(aligned_cb6, ":CB6", ":BUT", axis="y"), abs=1e-3) == 1.5708)
def test_alignment_workflow(clean_files): """ Test that we can solvate CB6-BUT after alignment. """ cb6 = pmd.load_file( os.path.join( os.path.dirname(__file__), "../data/cb6-but/cb6-but-notcentered.pdb" ) ) zalign(cb6, ":CB6", ":BUT", save=True, filename="./tmp/tmp.pdb") waters = np.random.randint(1000, 10000) sys = TLeap() sys.template_file = os.path.join( os.path.dirname(__file__), "../data/cb6-but/tleap_solvate.in" ) sys.output_path = "tmp" sys.loadpdb_file = "tmp.pdb" sys.target_waters = waters sys.output_prefix = "solvate" sys.build() logger.debug("Trying {} waters after alignment...".format(waters)) grepped_waters = sp.check_output( ["grep -oh 'WAT' ./tmp/solvate.prmtop | wc -w"], shell=True ) assert int(grepped_waters) == waters
def prepare_host_structure( cls, coordinate_path: str, host_atom_indices: Optional[List[int]] = None ) -> pmd.Structure: """Prepares the coordinates of a host molecule ready for the release phase of an APR calculation. This currently involves aligning the cavity of the host along the z-axis, and positioning the host so that its center of geometry at (0, 0, 0). Notes ----- The hosts cavity axis is for now determined as the vector orthogonal to the two largest principal components of the host. Parameters ---------- coordinate_path The path to the coordinate file which contains the host molecule. host_atom_indices The (0-based) indices of the host atoms in the coordinate file if the file. This may be used if the coordinate file contains more than a single molecule. Returns ------- A ParmEd structure which contains only the aligned and centered host. If ``host_atom_indices`` are provided, the structure will contain only the referenced host atoms. """ # noinspection PyTypeChecker structure = pmd.load_file(coordinate_path, structure=True) # Extract the host from the full structure. if not host_atom_indices: host_atom_indices = range(len(structure.atoms)) host_structure = structure[ "@" + ",".join(map(lambda x: str(x + 1), host_atom_indices)) ] # noinspection PyTypeChecker center_of_mass: np.ndarray = pmd.geometry.center_of_mass( host_structure.coordinates, masses=np.ones(len(host_structure.coordinates)) ) # Remove the COM from the host coordinates to make alignment easier. structure.coordinates -= center_of_mass host_structure.coordinates -= center_of_mass # Find the principal components of the host, take the two largest, and find # the vector orthogonal to that. Use that vector to align with the z-axis. # This may not generalize to non-radially-symmetric host molecules. inertia_tensor = np.dot( host_structure.coordinates.transpose(), host_structure.coordinates ) eigenvalues, eigenvectors = np.linalg.eig(inertia_tensor) order = np.argsort(eigenvalues) _, axis_2, axis_1 = eigenvectors[:, order].transpose() cavity_axis = np.cross(axis_1, axis_2) # Add dummy atoms which will be used to align the structure. cls.add_dummy_atoms_to_structure(structure, [np.array([0, 0, 0]), cavity_axis]) # Give atoms uniform mass so that the align code uses the center # of geometry rather than the center of mass. for atom in structure.atoms: atom.mass = 1.0 aligned_structure = align.zalign(structure, ":DM1", ":DM2") # Return a copy of the original structure where things like the masses # have not been changed and dummy atoms not added. # noinspection PyTypeChecker structure: pmd.Structure = pmd.load_file(coordinate_path, structure=True) structure.coordinates = aligned_structure["!:DM1&!:DM2"].coordinates return structure
def prepare_complex_structure( cls, coordinate_path: str, guest_atom_indices: List[int], guest_orientation_mask: str, pull_distance: float, pull_window_index: int, n_pull_windows: int, ) -> pmd.Structure: """Prepares the coordinates of a host molecule ready for the pull (+ attach) phase of an APR calculation. This currently involves aligning the complex so that the guest molecule (or rather, the guest atoms specified by ``guest_orientation_mask``) is aligned with the z-axis, and the first atom specified by ``guest_orientation_mask`` is positioned at (0, 0, 0). Parameters ---------- coordinate_path The path to the coordinate file which contains the guest molecule bound to the host. guest_atom_indices The (0-based) indices of the atoms in the coordinate file which correspond to the guest molecule. guest_orientation_mask The string mask which describes which guest atoms will be restrained to keep the molecule aligned to the z-axis and at a specific distance from the host. This should be of the form 'X Y' where X Y are ParmEd selectors for the two guest atoms to restrain relative to the dummy atoms. pull_distance The total distance that the guest will be pulled along the z-axis during the pull phase in units of Angstroms. pull_window_index The index of the window to prepare coordinates for. This determines the distance to place the guest from the host in the returned structure. An index of zero corresponds to the guest in its initial position, while an ``index = n_pull_windows - `` corresponds to the guest positioned a distance of ``pull_distance`` away from the host. n_pull_windows The total number of pull windows being used in the calculation. This will determine the distance to move the guest at each window. Returns ------- A ParmEd structure which contains the aligned and positioned host and guest molecules. """ # Align the host-guest complex so the first guest atom is at (0, 0, 0) and the # second guest atom lies along the positive z-axis. # noinspection PyTypeChecker structure: pmd.Structure = pmd.load_file(coordinate_path, structure=True) ( guest_orientation_mask_0, guest_orientation_mask_1, ) = guest_orientation_mask.split(" ") aligned_structure = align.zalign( structure, guest_orientation_mask_0, guest_orientation_mask_1 ) target_distance = np.linspace(0.0, pull_distance, n_pull_windows)[ pull_window_index ] target_difference = target_distance for guest_index in guest_atom_indices: aligned_structure.atoms[guest_index].xz += target_difference return aligned_structure
def test_add_dummy(clean_files): """ Test that dummy atoms get added correctly """ temporary_directory = os.path.join(os.path.dirname(__file__), "tmp") host_guest = pmd.load_file( os.path.join( os.path.dirname(__file__), "../data/cb6-but/cb6-but-notcentered.pdb" ), structure=True, ) host_guest = zalign(host_guest, ":BUT@C", ":BUT@C3", save=False) host_guest = add_dummy(host_guest, residue_name="DM1", z=-11.000, y=2.000, x=-1.500) host_guest.write_pdb( os.path.join(temporary_directory, "cb6-but-dum.pdb"), renumber=False ) with open(os.path.join(temporary_directory, "cb6-but-dum.pdb"), "r") as f: lines = f.readlines() test_line1 = lines[123].rstrip() test_line2 = lines[124].rstrip() ref_line1 = "TER 123 BUT 2" ref_line2 = ( "HETATM 123 DUM DM1 3 -1.500 2.000 -11.000 0.00 0.00 PB" ) assert ref_line1 == test_line1 assert ref_line2 == test_line2 write_dummy_frcmod(path=temporary_directory) write_dummy_mol2(path=temporary_directory, filename="dm1.mol2", residue_name="DM1") sys = TLeap() cb6_frcmod = os.path.abspath( os.path.join(os.path.dirname(__file__), "../data/cb6-but/cb6.frcmod") ) cb6_mol2 = os.path.abspath( os.path.join(os.path.dirname(__file__), "../data/cb6-but/cb6.mol2") ) but_frcmod = os.path.abspath( os.path.join(os.path.dirname(__file__), "../data/cb6-but/but.frcmod") ) but_mol2 = os.path.abspath( os.path.join(os.path.dirname(__file__), "../data/cb6-but/but.mol2") ) sys.template_lines = [ "source leaprc.gaff", f"loadamberparams {cb6_frcmod}", f"CB6 = loadmol2 {cb6_mol2}", f"loadamberparams {but_frcmod}", f"BUT = loadmol2 {but_mol2}", "loadamberparams dummy.frcmod", "DM1 = loadmol2 dm1.mol2", "model = loadpdb cb6-but-dum.pdb", ] sys.output_path = temporary_directory sys.output_prefix = "cb6-but-dum" sys.pbc_type = None sys.neutralize = False sys.build() with open( os.path.join(os.path.dirname(__file__), "../data/cb6-but/REF_cb6-but-dum.rst7"), "r", ) as f: contents = f.read() reference = [float(i) for i in contents.split()[2:]] with open(os.path.join(temporary_directory, "cb6-but-dum.rst7"), "r") as f: contents = f.read() new = [float(i) for i in contents.split()[2:]] assert np.allclose(reference, new)