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_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_get_all_childs_of_atom(self): """ It tests the _get_all_childs_of_atom method used in the building process of the Impact template. """ LIGAND_PATH = get_data_file_path('ligands/malonate.pdb') FORCEFIELD_NAME = 'openff_unconstrained-1.2.1.offxml' molecule = Molecule(LIGAND_PATH) openff = OpenForceField(FORCEFIELD_NAME) parameters = openff.parameterize(molecule, charge_method='dummy') topology = Topology(molecule, parameters) impact = Impact(topology) absolute_parent = impact._get_absolute_parent_atom() childs = impact._get_all_childs_of_atom(absolute_parent, 'side chain') assert [a.PDB_name for a in childs] == \ ['_C1_', '_C3_'], \ 'Unexpected side-chain-child atoms: {}'.format(childs) childs = impact._get_all_childs_of_atom(absolute_parent, 'core') assert [a.PDB_name for a in childs] == \ ['_H1_', '_H2_'], \ 'Unexpected core-child atoms: {}'.format(childs)
def _initialize_from_topology(self): """ Initializes a SolventWrapper object using a peleffy's molecular Topology. """ logger = Logger() logger.info(' - Generating solvent parameters') from peleffy.utils.toolkits import OpenForceFieldToolkitWrapper off_toolkit = OpenForceFieldToolkitWrapper() GBSA_handler = off_toolkit.get_parameter_handler_from_forcefield( 'GBSA', self._ff_file) self._solvent_dielectric = GBSA_handler.solvent_dielectric self._solute_dielectric = GBSA_handler.solute_dielectric self._surface_area_penalty = GBSA_handler.surface_area_penalty self._solvent_radius = GBSA_handler.solvent_radius from peleffy.forcefield import OpenForceField forcefield = OpenForceField(self._ff_file) for idx, topology in enumerate(self.topologies): parameters = forcefield.parameterize(topology.molecule, charge_method='dummy') self._radii[idx] = parameters['GBSA_radii'] self._scales[idx] = parameters['GBSA_scales']
def test_single_topology(self): """ It tests the class that generates a OpenFFCompatibleSolvent object for a single topology. """ from .utils import compare_dicts import json TEMPLATE_PARAMS_MAL = get_data_file_path('tests/ligandParams_MAL.txt') # Loads the molecule molecule = Molecule(path=get_data_file_path('ligands/malonate.pdb'), tag='MAL') # Sets forcefield and parameterizes it ff = OpenForceField('openff_unconstrained-1.2.1.offxml') parameters = ff.parameterize(molecule, charge_method='gasteiger') # Initializes topology topology = Topology(molecule, parameters) # Initializes solvent and gets parameters file solvent = OBC2(topology) solvent_dict = solvent.to_dict() # Loads reference dict from template with open(TEMPLATE_PARAMS_MAL, 'r') as f: reference_dict = json.load(f) # Compare the output parameters dict with the reference parameters compare_dicts(reference_dict, solvent_dict)
def test_parameterizer(self): """It checks the parameterized method.""" from peleffy.topology import Molecule from peleffy.forcefield import OpenForceField from peleffy.utils import (get_data_file_path, convert_all_quantities_to_string) from .utils import compare_dicts import json # Load molecule 1 molecule = Molecule(get_data_file_path('ligands/methane.pdb')) openff = OpenForceField(self.FORCE_FIELD_NAME) # Obtain force field parameters parameters = openff.parameterize(molecule) writable_parameters = convert_all_quantities_to_string(parameters) reference_file = get_data_file_path( 'tests/MET_openff-1.2.1_parameters.json') with open(reference_file) as f: compare_dicts(writable_parameters, json.load(f)) # Load molecule molecule = Molecule(get_data_file_path('ligands/ethylene.pdb')) openff = OpenForceField(self.FORCE_FIELD_NAME) # Obtain force field parameters parameters = openff.parameterize(molecule) writable_parameters = convert_all_quantities_to_string(parameters) reference_file = get_data_file_path( 'tests/ETL_openff-1.2.1_parameters.json') with open(reference_file) as f: compare_dicts(writable_parameters, json.load(f))
def test_multiple_topologies(self): """ It tests the class that generates a OpenFFCompatibleSolvent object for multiple topologies. """ from .utils import compare_dicts, merge_dicts # Path to multiple non standard residues pdb_path_MAL = get_data_file_path('ligands/malonate.pdb') pdb_path_MET = get_data_file_path('ligands/methane.pdb') # Force Field to parameterize the molecules ff = OpenForceField('openff_unconstrained-1.2.1.offxml') # Topology of malonate mol_MAL = Molecule(path=pdb_path_MAL, tag='MAL') parameters_MAL = ff.parameterize(mol_MAL, charge_method='gasteiger') topology_MAL = Topology(mol_MAL, parameters_MAL) # Topology of methane mol_MET = Molecule(path=pdb_path_MET, tag='MET') parameters_MET = ff.parameterize(mol_MET, charge_method='gasteiger') topology_MET = Topology(mol_MET, parameters_MET) # List containing both topologies topologies = [topology_MAL, topology_MET] # Generate the Solvent parameters dictionaries solvent_MAL_dict = OBC2(topology_MAL).to_dict() solvent_MET_dict = OBC2(topology_MET).to_dict() solvent_dict = OBC2(topologies).to_dict() # Check that merging both single topology dicitionaries we obtain the # same dictionary that using multiple topologies compare_dicts(merge_dicts(solvent_MAL_dict['SolventParameters'], solvent_MET_dict['SolventParameters']), solvent_dict['SolventParameters'])
def test_add_topological_elements(self): """ It tests the addition of topological elements to an empty topology. """ from peleffy.topology import Molecule from peleffy.forcefield import OpenForceField from peleffy.forcefield.parameters import BaseParameterWrapper from peleffy.topology import Topology from peleffy.utils import get_data_file_path # Define molecule1 and its topology pdb_path = get_data_file_path('ligands/ethylene.pdb') molecule1 = Molecule(pdb_path) openff = OpenForceField('openff_unconstrained-1.2.1.offxml') parameters1 = openff.parameterize(molecule1) topology1 = Topology(molecule1, parameters1) # Define empty topology2 molecule2 = Molecule() parameters2 = BaseParameterWrapper() topology2 = Topology(molecule2, parameters2) # Add parameters to topology2 for atom in topology1.atoms: topology2.add_atom(atom) for bond in topology1.bonds: topology2.add_bond(bond) for angle in topology1.angles: topology2.add_angle(angle) for proper in topology1.propers: topology2.add_proper(proper) for improper in topology1.impropers: topology2.add_improper(improper) # Verify content of both topologies assert topology1.atoms == topology2.atoms, \ 'The atoms of boths topologies should match' assert topology1.bonds == topology2.bonds, \ 'The bonds of boths topologies should match' assert topology1.angles == topology2.angles, \ 'The angles of boths topologies should match' assert topology1.propers == topology2.propers, \ 'The propers of boths topologies should match' assert topology1.impropers == topology2.impropers, \ 'The impropers of boths topologies should match'
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 __get_template_and_rot(self, template_path='grw', rot_path='GRW.rot.assign', rot_res=30): os.environ['SCHRODINGER'] = self.sch_path m = Molecule(self.ligand_pdb, core_constraints=[' CA ', ' C ', ' N '], rotamer_resolution=rot_res) if self.__forcefield == 'OPLS2005': ff = OPLS2005ForceField() if self.__forcefield == 'OpenForceField': # Not tested yet ff = OpenForceField('openff_unconstrained-1.2.0.offxml') parameters = ff.parameterize(m) topology = Topology(m, parameters) impact = Impact(topology) impact.to_file(template_path) aa_template = self.__create_aa_template_path() cov.correct_template(template_path, aa_template) print("Template modified in {}.".format(template_path)) rotamer_library = RotamerLibrary(m) rotamer_library.to_file(rot_path) print("Rotamer library stored in {}".format(rot_path))
def test_get_absolute_parent_atom(self): """ It tests the _get_absolute_parent_atom method used in the building process of the Impact template. """ LIGAND_PATH = get_data_file_path('ligands/malonate.pdb') FORCEFIELD_NAME = 'openff_unconstrained-1.2.1.offxml' molecule = Molecule(LIGAND_PATH) openff = OpenForceField(FORCEFIELD_NAME) parameters = openff.parameterize(molecule, charge_method='dummy') topology = Topology(molecule, parameters) impact = Impact(topology) absolute_parent = impact._get_absolute_parent_atom() assert absolute_parent.PDB_name == '_C2_', \ 'Unexpected absolute parent atom: {}'.format(absolute_parent)
def test_get_core_atoms(self): """ It tests the _get_core_atoms method used in the building process of the Impact template. """ LIGAND_PATH = get_data_file_path('ligands/malonate.pdb') FORCEFIELD_NAME = 'openff_unconstrained-1.2.1.offxml' molecule = Molecule(LIGAND_PATH) openff = OpenForceField(FORCEFIELD_NAME) parameters = openff.parameterize(molecule, charge_method='dummy') topology = Topology(molecule, parameters) impact = Impact(topology) core_atoms = impact._get_core_atoms() assert [a.PDB_name for a in core_atoms] == \ ['_C2_', '_H1_', '_H2_'], \ 'Unexpected core atoms: {}'.format(core_atoms)
def test_openff_parameterizer(self): """ It checks the behaviour of the Topology with the OpenFF parameters. """ from peleffy.topology import Molecule from peleffy.forcefield import OpenForceField from peleffy.topology import Topology from peleffy.utils import get_data_file_path from .utils import check_parameters FORCE_FIELD_NAME = 'openff_unconstrained-1.2.1.offxml' # Load molecule molecule = Molecule(get_data_file_path('ligands/methane.pdb')) openff = OpenForceField(FORCE_FIELD_NAME) # Obtain force field parameters parameters = openff.parameterize(molecule) # Generate molecular topology topology = Topology(molecule, parameters) # Define expected parameters expected_nonbonding = [[ 1, 0, 'M', 'OFFT', '_C1_', 0, 3.3996695084235347, 0.1094, -0.1088, 0, 1.6998347542117673, 0, 0 ], [ 2, 1, 'M', 'OFFT', '_H1_', 0, 2.649532787749369, 0.0157, 0.0267, 0, 1.3247663938746845, 0, 0 ], [ 3, 1, 'M', 'OFFT', '_H2_', 0, 2.649532787749369, 0.0157, 0.0267, 0, 1.3247663938746845, 0, 0 ], [ 4, 1, 'M', 'OFFT', '_H3_', 0, 2.649532787749369, 0.0157, 0.0267, 0, 1.3247663938746845, 0, 0 ], [ 5, 1, 'M', 'OFFT', '_H4_', 0, 2.649532787749369, 0.0157, 0.0267, 0, 1.3247663938746845, 0, 0 ]] expected_bonds = [[1, 2, 376.8940758588, 1.094223427522], [1, 3, 376.8940758588, 1.094223427522], [1, 4, 376.8940758588, 1.094223427522], [1, 5, 376.8940758588, 1.094223427522]] expected_angles = [[2, 1, 3, 33.78875634641, 110.2468561538], [2, 1, 4, 33.78875634641, 110.2468561538], [2, 1, 5, 33.78875634641, 110.2468561538], [3, 1, 4, 33.78875634641, 110.2468561538], [3, 1, 5, 33.78875634641, 110.2468561538], [4, 1, 5, 33.78875634641, 110.2468561538]] # Check it up check_parameters(topology, expected_nonbonding=expected_nonbonding, expected_bonds=expected_bonds, expected_angles=expected_angles) # Load molecule molecule = Molecule(get_data_file_path('ligands/ethylene.pdb')) openff = OpenForceField(FORCE_FIELD_NAME) # Obtain force field parameters parameters = openff.parameterize(molecule) # Generate molecular topology topology = Topology(molecule, parameters) # Define expected parameters expected_propers = [[3, 1, 2, 5, 5.376019778605, -1, 2, 0.0], [3, 1, 2, 6, 5.376019778605, -1, 2, 0.0], [4, 1, 2, 5, 5.376019778605, -1, 2, 0.0], [4, 1, 2, 6, 5.376019778605, -1, 2, 0.0]] expected_impropers = [[1, 2, 5, 6, 1.1, -1, 2], [2, 1, 3, 4, 1.1, -1, 2]] # Check it up check_parameters(topology, expected_propers=expected_propers, expected_impropers=expected_impropers)
def test_parse_charges_from_mae(self): """ It tests the MAE parser for assigning partial charges from an external file. """ from peleffy.utils import get_data_file_path, parse_charges_from_mae from peleffy.topology import Molecule from peleffy.forcefield import OpenForceField FORCEFIELD = 'openff_unconstrained-1.2.0.offxml' PATH_MAE_MAT = get_data_file_path('tests/MAT.mae') PATH_PDB_MAT = get_data_file_path('tests/MAT.pdb') PATH_MAE_BHP = get_data_file_path('ligands/BHP.mae') PATH_PDB_BHP = get_data_file_path('ligands/BHP.pdb') PATH_MAE_ETL = get_data_file_path('ligands/ethylene.mae') PATH_PDB_ETL = get_data_file_path('ligands/ethylene.pdb') PATH_PDB_MAL = get_data_file_path('ligands/malonate.pdb') CHARGES_REFERENCE_BHP = [[-0.35703, 'O1'], [-0.59535, 'O2'], [-0.50292, 'O3'], [-0.25243, 'C1'], [0.30438, 'C2'], [0.22092, 'C3'], [0.54336, 'C4'], [-0.20569, 'C5'], [-0.20192, 'C6'], [0.16631, 'C7'], [0.02422, 'C8'], [-0.09115, 'C9'], [-0.09904, 'C10'], [-0.15673, 'C11'], [-0.13245, 'C12'], [-0.17806, 'C13'], [-0.12489, 'C14'], [-0.09307, 'C15'], [-0.08973, 'C16'], [0.05397, 'H1'], [0.07338, 'H2'], [0.04514, 'H3'], [0.12979, 'H4'], [0.11025, 'H5'], [0.04054, 'H6'], [0.04581, 'H7'], [0.11444, 'H8'], [0.11761, 'H9'], [0.37274, 'H10'], [0.11825, 'H11'], [0.12584, 'H12'], [0.1381, 'H13'], [0.11696, 'H14'], [0.11148, 'H15'], [0.10697, 'H16']] CHARGES_REFERENCE_MAT = [[-0.18938, 'F1'], [-0.21715, 'F2'], [-0.21234, 'F3'], [-0.39736, 'O1'], [-0.58890, 'O2'], [-1.00825, 'N1'], [0.72066, 'C1'], [-0.06281, 'C2'], [-0.67474, 'C3'], [0.10391, 'C4'], [0.16293, 'C5'], [-0.61076, 'C6'], [0.78183, 'C7'], [0.27041, 'C8'], [-0.48769, 'C9'], [0.15704, 'C10'], [-0.02646, 'H1'], [-0.08394, 'H2'], [-0.01308, 'H3'], [0.14006, 'H4'], [0.12960, 'H5'], [0.31245, 'H6'], [0.30268, 'H7'], [0.17026, 'H8'], [0.15782, 'H9'], [0.03175, 'C11'], [0.03894, 'H10'], [0.07509, 'H11'], [0.01743, 'H12']] # Check up correct charges for malonate m = Molecule(PATH_PDB_MAT) openff = OpenForceField(FORCEFIELD) parameters = openff.parameterize(m, charge_method='dummy') parameters = parse_charges_from_mae(PATH_MAE_MAT, parameters) for charge, atom_name in zip(parameters['charges'], parameters['atom_names']): assert [charge._value, atom_name.replace('_', '')] in \ CHARGES_REFERENCE_MAT, \ 'Incorrect charge value for {}.'.format(atom_name) # Check up correct charges for BHP m = Molecule(PATH_PDB_BHP) openff = OpenForceField(FORCEFIELD) parameters = openff.parameterize(m, charge_method='dummy') parameters = parse_charges_from_mae(PATH_MAE_BHP, parameters) for charge, atom_name in zip(parameters['charges'], parameters['atom_names']): assert [charge._value, atom_name.replace('_', '')] in \ CHARGES_REFERENCE_BHP, \ 'Incorrect charge value for {}.'.format(atom_name) # Error: MAE file without charges information m = Molecule(PATH_PDB_ETL) openff = OpenForceField(FORCEFIELD) parameters = openff.parameterize(m, charge_method='dummy') with pytest.raises(ValueError): _ = parse_charges_from_mae(PATH_MAE_ETL, parameters) # Error: Inconsistency between Moelcule atom names and MAE atom names m = Molecule(PATH_PDB_MAL) openff = OpenForceField(FORCEFIELD) parameters = openff.parameterize(m, charge_method='dummy') with pytest.raises(ValueError): _ = parse_charges_from_mae(PATH_PDB_BHP, parameters)