def test_PDB_residue_name(self): """ It tests the PDB residue name and checks for consistency with Molecule tag. """ def check_residue_name(name): """Check if residue names are valid in the output PDB file""" with open('molecule.pdb') as f: for line in f: if line.startswith('HETATM'): assert line[17:20] == name, 'Unexpected residue name' ligand_path = get_data_file_path('ligands/benzene.pdb') # Checking tag assignation from PDB molecule = Molecule(ligand_path) with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): assert molecule.tag == 'BNZ', 'Unexpected molecule tag' molecule.to_pdb_file('molecule.pdb') check_residue_name('BNZ') # Checking set_tag() function molecule = Molecule(ligand_path) with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): molecule.set_tag('TAG') assert molecule.tag == 'TAG', 'Unexpected molecule tag' molecule.to_pdb_file('molecule.pdb') check_residue_name('TAG') # Checking default tag assignment from SMILES molecule = Molecule(smiles='c1ccccc1', hydrogens_are_explicit=False) with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): assert molecule.tag == 'UNK', 'Unexpected molecule tag' molecule.to_pdb_file('molecule.pdb') check_residue_name('UNK') # Checking custom tag assignment from SMILES molecule = Molecule(smiles='c1ccccc1', tag='BEN', hydrogens_are_explicit=False) with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): assert molecule.tag == 'BEN', 'Unexpected molecule tag' molecule.to_pdb_file('molecule.pdb') check_residue_name('BEN') # Checking second custom tag assignment from SMILES molecule = Molecule(smiles='c1ccccc1', hydrogens_are_explicit=False) with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): molecule.set_tag('BNZ') assert molecule.tag == 'BNZ', 'Unexpected molecule tag' molecule.to_pdb_file('molecule.pdb') check_residue_name('BNZ')
def test_peleffy_main(self): """It checks the main function of peleffy.""" from peleffy.main import parse_args, main from peleffy.utils import Logger import logging ligand_path = get_data_file_path('ligands/benzene.pdb') with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): # Test default settings args = parse_args([ligand_path]) main(args) logger = Logger() for handler in logger._logger.handlers: assert handler.level == logging.INFO # Test silent settings args = parse_args([ligand_path, '--silent']) main(args) logger = Logger() for handler in logger._logger.handlers: assert handler.level == logging.CRITICAL # Test silent settings args = parse_args([ligand_path, '--debug']) main(args) logger = Logger() for handler in logger._logger.handlers: assert handler.level == logging.DEBUG
def run_ffld_server(self, molecule): """ It calls Schrodinger's ffld_server to parameterize a molecule with OPLS. .. todo :: * Review PlopRotTemp's atom type fixes. Should we apply them here? Parameters ---------- molecule : an peleffy.topology.Molecule The peleffy's Molecule object Returns ------- ffld_output : str The ffld_server output """ ffld_server_exec = self.path_to_ffld_server() with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): self._rdkit_toolkit_wrapper.to_pdb_file( molecule, tmpdir + '/molecule.pdb') subprocess.check_output([ ffld_server_exec, "-ipdb", "molecule.pdb", "-version", "14", "-print_parameters", "-out_file", "parameters.txt" ]) with open('parameters.txt') as parameters_file: return parameters_file.read()
def test_undefined_stereo(self): """ It checks the behaviour when ignoring the stereochemistry in the Molecule initialization. """ from openff.toolkit.utils.toolkits import UndefinedStereochemistryError from peleffy.forcefield import OpenForceField # This should crash due to an undefined stereochemistry error with pytest.raises(UndefinedStereochemistryError): mol = Molecule(smiles='CN(C)CCC=C1c2ccccc2CCc3c1cccc3', hydrogens_are_explicit=False) # This now should work mol = Molecule(smiles='CN(C)CCC=C1c2ccccc2CCc3c1cccc3', allow_undefined_stereo=True, hydrogens_are_explicit=False) # And we can parameterize it ff = OpenForceField('openff_unconstrained-1.2.1.offxml') ff.parameterize(mol, charge_method='gasteiger') # Save it with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): mol.to_pdb_file('molecule.pdb')
def test_writer_OPLS(self): """ It tests the writer attribute of the Impact class using OPLS to parameterize. """ from .utils import parameterize_opls2005 TEMPLATE_METZ_OPLS = get_data_file_path('tests/OPLS_metz') TEMPLATE_MALZ_OPLS = get_data_file_path('tests/OPLS_malz') TEMPLATE_ETLZ_OPLS = get_data_file_path('tests/OPLS_etlz') with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): # Generates the template for methane using OPLS opls2005 = OPLS2005ForceField() pdb_path = get_data_file_path('ligands/methane.pdb') molecule = Molecule(pdb_path) ffld_file = get_data_file_path('tests/MET_ffld_output.txt') parameters = parameterize_opls2005(opls2005, molecule, ffld_file) topology = Topology(molecule, parameters) # Generates the impact template for methane impact = Impact(topology) impact.to_file('metz') # Compare the reference template and the generated template compare_files(file1=TEMPLATE_METZ_OPLS, file2='metz') # Generates the template for malonate using OPLS opls2005 = OPLS2005ForceField() pdb_path = get_data_file_path('ligands/malonate.pdb') molecule = Molecule(pdb_path) ffld_file = get_data_file_path('tests/MAL_ffld_output.txt') parameters = parameterize_opls2005(opls2005, molecule, ffld_file) topology = Topology(molecule, parameters) # Generates the impact template for malonate impact = Impact(topology) impact.to_file('malz') # Compare the reference template and the generated template compare_files(file1=TEMPLATE_MALZ_OPLS, file2='malz') # Generates the template for ethylene using OPLS opls2005 = OPLS2005ForceField() pdb_path = get_data_file_path('ligands/ethylene.pdb') molecule = Molecule(pdb_path, tag='ETL') ffld_file = get_data_file_path('tests/ETL_ffld_output.txt') parameters = parameterize_opls2005(opls2005, molecule, ffld_file) topology = Topology(molecule, parameters) # Generates the impact template for ethylene impact = Impact(topology) impact.to_file('etlz') # Compare the reference template and the generated template compare_files(file1=TEMPLATE_ETLZ_OPLS, file2='etlz')
def test_smiles_initialization(self): """ It checks the initialization from a SMILES tag. """ molecule = Molecule(smiles='c1ccccc1', hydrogens_are_explicit=False) # Save it with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): molecule.to_pdb_file('molecule.pdb')
def test_writer_OFF(self): """ It tests the writer attribute of the Impact class using OFF to parameterize. """ TEMPLATE_METZ = get_data_file_path('tests/metz') TEMPLATE_MATZ = get_data_file_path('tests/malz') TEMPLATE_ETLZ = get_data_file_path('tests/etlz') with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): # Generates the template for methane pdb_path = get_data_file_path('ligands/methane.pdb') molecule = Molecule(pdb_path) openff = OpenForceField(self.OPENFF_FORCEFIELD) parameters = openff.parameterize(molecule) topology = Topology(molecule, parameters) # Generates the impact template for methane impact = Impact(topology) impact.to_file('metz') # Compare the reference template and the generated template compare_files(file1=TEMPLATE_METZ, file2='metz') # Generates the template for malonate pdb_path = get_data_file_path('ligands/malonate.pdb') molecule = Molecule(pdb_path) openff = OpenForceField(self.OPENFF_FORCEFIELD) parameters = openff.parameterize(molecule) topology = Topology(molecule, parameters) # Generates the impact template for malonate impact = Impact(topology) impact.to_file('malz') # Compare the reference template and the generated template compare_files(file1=TEMPLATE_MATZ, file2='malz') # Generates the template for ethylene pdb_path = get_data_file_path('ligands/ethylene.pdb') molecule = Molecule( pdb_path, tag='ETL' ) # Note that in this case we are assigning a tag to the molecule which will be used in the Impact template openff = OpenForceField(self.OPENFF_FORCEFIELD) parameters = openff.parameterize(molecule) topology = Topology(molecule, parameters) # Generates the impact template for ethylene impact = Impact(topology) impact.to_file('etlz') # Compare the reference template and the generated template compare_files(file1=TEMPLATE_ETLZ, file2='etlz')
def test_peleffy_default_call(self): """ It checks the default call of peleffy's main function. """ from peleffy.main import run_peleffy LIGAND_PATH = 'ligands/benzene.pdb' ligand_path = get_data_file_path(LIGAND_PATH) with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): run_peleffy(ligand_path, output=tmpdir)
def test_pdb_initialization(self): """ It checks the initialization from a PDB file. """ ligand_path = get_data_file_path('ligands/ethylene.pdb') molecule = Molecule(ligand_path) # Save it with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): molecule.to_pdb_file('molecule.pdb')
def test_multiple_topologies_writer(self): """ It tests the class that generates a OpenFFCompatibleSolvent object for multiple topologies. It compares the outcome of the Solvent writer with a reference file. """ from .utils import compare_dicts, parameterize_opls2005 import json TEMPLATE_PARAMS = get_data_file_path('tests/ligandParams.txt') with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): path_OXO = get_data_file_path('tests/MRO_oleic/OXO.pdb') path_OLC = get_data_file_path('tests/MRO_oleic/OLC.pdb') ff = OpenForceField('openff_unconstrained-1.2.1.offxml') opls2005 = OPLS2005ForceField() # Group OXO m_OXO = Molecule(path_OXO) ffld_file = get_data_file_path('tests/OXO_ffld_output.txt') parameters_OXO = parameterize_opls2005(opls2005, m_OXO, ffld_file) topology_OXO = Topology(m_OXO, parameters_OXO) # Acid oleic m_OLC = Molecule(path_OLC) parameters_OLC = ff.parameterize(m_OLC, charge_method='gasteiger') topology_OLC = Topology(m_OLC, parameters_OLC) # Multiple topologies topologies = [topology_OXO, topology_OLC] solvent = OBC2(topologies) solvent.to_file('OBC_parameters.txt') # Loads reference dict from template with open(TEMPLATE_PARAMS, 'r') as f: reference_dict = json.load(f) # Loads the generated template into a dict with open('OBC_parameters.txt', 'r') as f: solvent_dict = json.load(f) # Compare the output parameters dict with the reference parameters compare_dicts(reference_dict, solvent_dict)
def test_peleffy_custom_call(self): """ It checks the custom call of peleffy's main function. """ from peleffy.main import run_peleffy LIGAND_PATH = 'ligands/benzene.pdb' CUSTOM_FORCEFIELD_NAME = 'openff_unconstrained-1.2.0.offxml' ligand_path = get_data_file_path(LIGAND_PATH) with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): run_peleffy(ligand_path, forcefield_name=CUSTOM_FORCEFIELD_NAME, resolution=10, charge_method='gasteiger', output=tmpdir, with_solvent=True, as_datalocal=True)
def test_write_dihedral_library(self): import os import tempfile from peleffy.utils import get_data_file_path, temporary_cd pdb_path = get_data_file_path('ligands/ethylene.pdb') ffld_path = get_data_file_path("tests/ETL_ffld_output.txt") bce_obj = build_mock_BCEConformations(pdb_path, ffld_path) bce_obj.calculate_cluster_offsets(pdb_path) calculated_lines = [] with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): bce_obj.save(os.path.join(tmpdir, "ETH.conformation")) with open(os.path.join(tmpdir, "ETH.conformation")) as f: calculated_lines = f.readlines() calculated_lines = calculated_lines[3:] golden_conformation_library_path = get_data_file_path( 'parameters/ETH.conformation') with open(golden_conformation_library_path) as f: golden_lines = f.readlines()[3:] assert golden_lines == calculated_lines
def test_OBCOPLS_writer_ligand(pdbfile, tag_name, ffld_name, reference_file): """ Given a ligand, it tests that the output parameters file corresponds to the refenrece file. Parameters ---------- pdbfile : str The path to the PDB of the ligand to test ffld_name : str The path to the ffld_server's output file reference_file : str The path to reference TXT file compatible with PELE """ with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): # Loads the molecule molecule = Molecule(get_data_file_path(pdbfile), tag=tag_name) # Sets forcefield and parameterizes it opls2005 = OPLS2005ForceField() ffld_file = get_data_file_path(ffld_name) parameters = parameterize_opls2005(opls2005, molecule, ffld_file) # Initializes topology topology = Topology(molecule, parameters) # Initializes solvent and gets parameters file solvent = OPLSOBC(topology) solvent.to_file('OBC_parameters.txt') # Compare the output file with the reference parameters file compare_files_without_order('OBC_parameters.txt', reference_file)
def compute_partial_charges(self, molecule, method='am1bcc'): """ It computes the partial charges using antechamber. Parameters ---------- molecule : an peleffy.topology.Molecule The peleffy's Molecule object method : str The name of the method to use. One of ['gasteiger', 'am1bcc']. If None, 'am1bcc' will be used Returns ------- charges : simtk.unit.Quantity The array of partial charges Raises ------ ChargeMethodUnavailableError if the requested charge method can not be handled by this toolkit ChargeCalculationError if the charge method is supported by this toolkit, but fails """ SUPPORTED_CHARGE_METHODS = { 'am1bcc': { 'antechamber_keyword': 'bcc' }, 'gasteiger': { 'antechamber_keyword': 'gas' } } if method not in SUPPORTED_CHARGE_METHODS: raise ChargeMethodUnavailableError( 'partial_charge_method ' + '{} is not available from '.format(method) + 'AmberToolsToolkitWrapper. Available charge methods are ' + list(SUPPORTED_CHARGE_METHODS.keys())) off_molecule = molecule.off_molecule with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): net_charge = off_molecule.total_charge / \ unit.elementary_charge self._rdkit_toolkit_wrapper.to_sdf_file( molecule, tmpdir + '/molecule.sdf') subprocess.check_output([ "antechamber", "-i", "molecule.sdf", "-fi", "sdf", "-o", "charged.ac", "-fo", "ac", "-pf", "yes", "-dr", "n", "-c", SUPPORTED_CHARGE_METHODS[method]['antechamber_keyword'], "-nc", str(net_charge) ]) # Write out just charges subprocess.check_output([ "antechamber", "-dr", "n", "-i", "charged.ac", "-fi", "ac", "-o", "charged2.ac", "-fo", "ac", "-c", "wc", "-cf", "charges.txt", "-pf", "yes" ]) if not os.path.exists('charges.txt'): # TODO: copy files into local directory to aid debugging? raise ChargeCalculationError( "Antechamber/sqm partial charge calculation failed on " "molecule {} (SMILES {})".format( off_molecule.name, off_molecule.to_smiles())) # Read the charges with open('charges.txt', 'r') as infile: contents = infile.read() text_charges = contents.split() charges = np.zeros([off_molecule.n_atoms], np.float64) for index, token in enumerate(text_charges): charges[index] = float(token) charges = unit.Quantity(charges, unit.elementary_charge) assert len(charges) == len(molecule.rdkit_molecule.GetAtoms()), \ 'Partial charge computation failed as the length of ' \ + 'resulting partial charges does not match with the ' \ + 'number of atoms in molecule' return charges
def test_folder_creation(self): """ It tests the folder creation of the OutputPathHandler class. """ from peleffy.utils import OutputPathHandler, temporary_cd from peleffy.forcefield import OpenForceField # Load benzene molecule molecule = Molecule(smiles='c1ccccc1', name='benzene', tag='BNZ', hydrogens_are_explicit=False) # Load force field openff = OpenForceField('openff_unconstrained-1.2.1.offxml') # Initialize output handler with an output_path set with tempfile.TemporaryDirectory() as tmpdir: # Initialize output handler without output_path output_handler = OutputPathHandler( molecule, openff, as_datalocal=True, output_path=os.path.join(tmpdir, 'output')) # Test path getter without folder creation path = output_handler.get_rotamer_library_path( create_missing_folders=False) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is False, \ 'This directory should not exist' path = output_handler.get_impact_template_path( create_missing_folders=False) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is False, \ 'This directory should not exist' path = output_handler.get_solvent_template_path( create_missing_folders=False) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is False, \ 'This directory should not exist' path = output_handler.get_conformation_library_path( create_missing_folders=False) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is False, \ 'This directory should not exist' # Initialize output handler with an output_path set with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): # Initialize output handler without output_path output_handler = OutputPathHandler( molecule, openff, as_datalocal=True, output_path=os.path.join(tmpdir, 'output')) # Test path getter with folder creation path = output_handler.get_rotamer_library_path( create_missing_folders=True) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is True, \ 'This directory should exist' path = output_handler.get_impact_template_path( create_missing_folders=True) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is True, \ 'This directory should exist' path = output_handler.get_solvent_template_path( create_missing_folders=True) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is True, \ 'This directory should exist' path = output_handler.get_conformation_library_path( create_missing_folders=True) path_dir = os.path.dirname(path) assert os.path.isdir(path_dir) is True, \ 'This directory should exist'