def test_Impact_writable_parameters(self): """ It checks the Impact writable representation for angles. """ # Load benzene ring molecule = Molecule(smiles='CC=O') # Parameterize molecule.parameterize('openff_unconstrained-1.2.0.offxml', charges_method='gasteiger') expected_parameters = list( [[1, 2, 3, 78.67881492645, 128.2771922378], [1, 2, 7, 34.202963712735, 133.1339832262], [2, 1, 4, 50.00415699765, 110.81136453], [2, 1, 5, 50.00415699765, 110.81136453], [2, 1, 6, 50.00415699765, 110.81136453], [3, 2, 7, 34.202963712735, 133.1339832262], [4, 1, 5, 33.78875634641, 110.2468561538], [4, 1, 6, 33.78875634641, 110.2468561538], [5, 1, 6, 33.78875634641, 110.2468561538]]) # Check resulting parameters for angle in molecule.angles: w_angle = WritableAngle(angle) w_parameters = [attr[1] for attr in list(w_angle)] assert w_parameters in expected_parameters, \ 'Invalid writable angle parameters {}'.format(w_parameters)
def test_gasteiger_method(self): """It tests the gasteiger method""" ligand_path = get_data_file_path(self.LIGAND_PATH) molecule = Molecule(ligand_path) molecule.parameterize(FORCEFIELD_NAME, charges_method='gasteiger') check_CHO_charges_in_molecule(molecule)
def test_OPLS_method(self): """It tests the OPLS method""" ligand_path = get_data_file_path(self.LIGAND_PATH) molecule = Molecule(ligand_path) # To avoid the use of Schrodinger Toolkit charges = [-0.22, 0.7, -0.12, -0.8, -0.8, -0.12, -0.12, -0.12, -0.12, -0.12, -0.115, -0.115, -0.12, -0.12, -0.12, -0.12, -0.12, -0.12, -0.12, -0.18, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.115, 0.115, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06] molecule._OPLS_parameters = SchrodingerToolkitWrapper.OPLSParameters( {'charges': [unit.Quantity(charge, unit.elementary_charge) for charge in charges]}) molecule.parameterize(FORCEFIELD_NAME, charges_method='OPLS') assert len(molecule.off_molecule.partial_charges) == len(charges), \ 'Size of Molecule\'s partial charges is expected to match ' \ + 'with size of reference charges list' for charge, expected_charge in zip( molecule.off_molecule.partial_charges, charges): assert charge == unit.Quantity(expected_charge, unit.elementary_charge), \ 'Unexpected charge {}'.format(charge)
def test_Impact_writable_parameters(self): """ It checks the Impact writable representation for bonds. """ # Load benzene ring molecule = Molecule(smiles='CC=O') # Parameterize molecule.parameterize('openff_unconstrained-1.2.0.offxml', charges_method='gasteiger') expected_parameters = list([[1, 2, 332.5750972667, 1.523640340452], [1, 4, 376.8940758588, 1.094223427522], [1, 5, 376.8940758588, 1.094223427522], [1, 6, 376.8940758588, 1.094223427522], [2, 3, 608.3286693405, 1.225108345696], [2, 7, 404.20804685, 1.085503378387]]) # Check resulting parameters for bond in molecule.bonds: w_bond = WritableBond(bond) w_parameters = [attr[1] for attr in list(w_bond)] assert w_parameters in expected_parameters, \ 'Invalid writable bond parameters {}'.format(w_parameters)
def test_molecule_tag_assignment(self): """ It tests the molecule tag assignment. """ # Look for UNK tag when dummy Molecule is loaded molecule = Molecule() assert molecule.tag == 'UNK', 'Unexpected atom tag' # Look for the PDB residue name as a tag when a Molecule is loaded # from a PDB file ligand_path = get_data_file_path('ligands/BNZ.pdb') molecule = Molecule(ligand_path) assert molecule.tag == 'BNZ', 'Unexpected atom tag' # Look for BEN tag when a Molecule is loaded from a PDB file with # a custom name ligand_path = get_data_file_path('ligands/BNZ.pdb') molecule = Molecule(ligand_path, tag='BEN') assert molecule.tag == 'BEN', 'Unexpected atom tag' # Look for UNK tag when a Molecule is loaded from a SMILES tag molecule = Molecule(smiles='c1ccccc1') assert molecule.tag == 'UNK', 'Unexpected atom tag' # Look for BNZ tag when a Molecule is loaded from a SMILES tag with # a custom tag molecule = Molecule(smiles='c1ccccc1', tag='BNZ') assert molecule.tag == 'BNZ', 'Unexpected atom tag'
def test_molecule_name_assignment(self): """ It tests the molecule name assignment. """ # Look for an empty name when dummy Molecule is loaded molecule = Molecule() assert molecule.name == '', 'Unexpected atom name' # Look for the PDB name when a Molecule is loaded from a PDB file ligand_path = get_data_file_path('ligands/BNZ.pdb') molecule = Molecule(ligand_path) assert molecule.name == 'BNZ', 'Unexpected atom name' # Look for benzene name when a Molecule is loaded from a PDB file # with a custom name ligand_path = get_data_file_path('ligands/BNZ.pdb') molecule = Molecule(ligand_path, name='benzene') assert molecule.name == 'benzene', 'Unexpected atom name' # Look for the SMILES name when a Molecule is loaded from a SMILES tag molecule = Molecule(smiles='c1ccccc1') assert molecule.name == 'c1ccccc1', 'Unexpected atom name' # Look for benzene name when a Molecule is loaded from a SMILES tag # with a custom name molecule = Molecule(smiles='c1ccccc1', name='benzene') assert molecule.name == 'benzene', 'Unexpected atom name'
def test_OFF_parameters(self): """ It checks the standard Open Force Field parameterization for bonds. """ MAX_THRESHOLD = 1e-3 # Load benzene ring molecule = Molecule(smiles='c1ccccc1') # Parameterize molecule.parameterize('openff_unconstrained-1.2.0.offxml', charges_method='gasteiger') # Check resulting parameters expected_lengths = {(0, 1): 1.389761064076, (0, 5): 1.389761064076, (0, 6): 1.085503378387, (1, 2): 1.389761064076, (1, 7): 1.085503378387, (2, 3): 1.389761064076, (2, 8): 1.085503378387, (3, 4): 1.389761064076, (3, 9): 1.085503378387, (4, 5): 1.389761064076, (4, 10): 1.085503378387, (5, 11): 1.085503378387} expected_ks = {(0, 1): 672.0064645264, (0, 5): 672.0064645264, (0, 6): 808.4160937, (1, 2): 672.0064645264, (1, 7): 808.4160937, (2, 3): 672.0064645264, (2, 8): 808.4160937, (3, 4): 672.0064645264, (3, 9): 808.4160937, (4, 5): 672.0064645264, (4, 10): 808.4160937, (5, 11): 808.4160937} for indexes, properties in dict(molecule.parameters['Bonds']).items(): expected_length = unit.Quantity(expected_lengths[indexes], unit.angstrom) expected_k = unit.Quantity(expected_ks[indexes], unit.kilocalorie / (unit.angstrom ** 2 * unit.mole)) assert properties.length - expected_length \ < unit.Quantity(MAX_THRESHOLD, unit.angstrom), \ 'Invalid length for bond {} {}'.format(indexes, properties) assert properties.k - expected_k \ < unit.Quantity(MAX_THRESHOLD, unit.kilocalorie / (unit.angstrom ** 2 * unit.mole)), \ 'Invalid k for bond {} {}'.format(indexes, properties)
def test_good_init_parameterization(self): """ It checks that a call to the parameterize function with a Molecule successfully initialized does not raise any Exception. """ FORCEFIELD_NAME = 'openff_unconstrained-1.1.1.offxml' LIGAND_PATH = SET_OF_LIGAND_PATHS[0] ligand_path = get_data_file_path(LIGAND_PATH) molecule = Molecule(ligand_path) molecule.parameterize(FORCEFIELD_NAME)
def test_Impact_writable_parameters(self): """ It checks the Impact writable representation for dihedrals. """ # Load benzene ring molecule = Molecule(smiles='CC=O') # Parameterize molecule.parameterize('openff_unconstrained-1.2.0.offxml', charges_method='gasteiger') expected_parameters = list( [[3, 2, 1, 4, 0.5107183999341, 1, 1], [3, 2, 1, 5, 0.5107183999341, 1, 1], [3, 2, 1, 6, 0.5107183999341, 1, 1], [4, 1, 2, 7, 0.1493988458474, 1, 3], [5, 1, 2, 7, 0.1493988458474, 1, 3], [6, 1, 2, 7, 0.1493988458474, 1, 3], [3, 2, 1, 4, -0.0275194074427, 1, 2], [3, 2, 1, 5, -0.0275194074427, 1, 2], [3, 2, 1, 6, -0.0275194074427, 1, 2], [3, 2, 1, 4, -0.1057540923121, -1, 3], [3, 2, 1, 5, -0.1057540923121, -1, 3], [3, 2, 1, 6, -0.1057540923121, -1, 3]]) # Check resulting parameters for proper in molecule.propers: w_proper = WritableProper(proper) w_parameters = [attr[1] for attr in list(w_proper)] assert w_parameters in expected_parameters, \ 'Invalid writable proper parameters {}'.format(w_parameters) expected_parameters = list([[1, 2, 3, 7, 1.1, -1, 2]]) # Check resulting parameters for improper in molecule.impropers: w_improper = WritableImproper(improper) w_parameters = [attr[1] for attr in list(w_improper)] assert w_parameters in expected_parameters, \ 'Invalid writable improper parameters {}'.format(w_parameters)
def test_OFF_to_PELE_conversion(self): """ It checks the difference between dihedral equations from PELE and Open Force Field. Their values should match throughout all the domain. """ MAX_THRESHOLD = 1e-10 for ligand_path in SET_OF_LIGAND_PATHS: ligand_path = get_data_file_path(ligand_path) molecule = Molecule(ligand_path) molecule.parameterize(FORCEFIELD_NAME, charges_method='gasteiger') x = unit.Quantity(np.arange(0, np.pi, 0.1), unit=unit.radians) for PELE_proper, OFF_proper in zip(molecule.propers, molecule._OFF_propers): PELE_y = apply_PELE_dihedral_equation(PELE_proper, x) OFF_y = apply_OFF_dihedral_equation(OFF_proper, x) y_diff = PELE_y - OFF_y assert np.linalg.norm(y_diff) < MAX_THRESHOLD
def test_get_Schrodinger_parameters(self): """ It tests the standard methods to obtain Schrodinger parameters from an offpele's Molecule. """ # Load benzene ring molecule = Molecule(smiles='c1ccccc1') molecule.parameterize(FORCEFIELD_NAME, charges_method='gasteiger') with pytest.raises(ToolkitUnavailableException): molecule.get_OPLS_parameters() molecule._OPLS_parameters = METHANE_OPLS_PARAMETERS _ = molecule.get_OPLS_parameters() molecule.add_OPLS_nonbonding_params() molecule.add_OPLS_bonds_and_angles()
def test_excluded_dihedrals_handler(self): """ It checks the exclusion of dihedrals when generating 1-4 lists for PELE. This process is done internally in PELE but we need to mark previously those dihedrals to exclude from 1-4 lists. """ molecule = Molecule() # Add several dummy propers molecule._add_proper( Proper(atom1_idx=0, atom2_idx=1, atom3_idx=2, atom4_idx=3, periodicity=1, prefactor=1, index=0, constant=unit.Quantity(1.0, unit.kilocalorie / unit.mole))) molecule._add_proper( Proper(atom1_idx=0, atom2_idx=1, atom3_idx=2, atom4_idx=3, periodicity=2, prefactor=1, index=1, constant=unit.Quantity(1.0, unit.kilocalorie / unit.mole))) molecule._add_proper( Proper(atom1_idx=0, atom2_idx=1, atom3_idx=5, atom4_idx=3, periodicity=2, prefactor=1, index=2, constant=unit.Quantity(1.0, unit.kilocalorie / unit.mole))) molecule._add_proper( Proper(atom1_idx=0, atom2_idx=1, atom3_idx=2, atom4_idx=4, periodicity=1, prefactor=1, index=3, constant=unit.Quantity(1.0, unit.kilocalorie / unit.mole))) molecule._add_proper( Proper(atom1_idx=0, atom2_idx=1, atom3_idx=2, atom4_idx=5, periodicity=1, prefactor=1, index=4, constant=unit.Quantity(1.0, unit.kilocalorie / unit.mole))) molecule._add_proper( Proper(atom1_idx=0, atom2_idx=1, atom3_idx=2, atom4_idx=6, periodicity=1, prefactor=1, index=5, constant=unit.Quantity(1.0, unit.kilocalorie / unit.mole))) # Add one bond molecule._add_bond( Bond(atom1_idx=0, atom2_idx=4, spring_constant=unit.Quantity(1.0, unit.kilocalorie / (unit.angstrom ** 2 * unit.mole)), eq_dist=unit.Quantity(1.0, unit.angstrom))) # Add one angle molecule._add_angle( Angle(atom1_idx=0, atom2_idx=1, atom3_idx=5, spring_constant=unit.Quantity(1.0, unit.kilocalorie / (unit.radian ** 2 * unit.mole)), eq_angle=unit.Quantity(1.0, unit.degrees))) molecule._handle_excluded_propers() # Assertions for proper in molecule.propers: if proper.index == 0: assert proper.exclude is False, \ 'Proper 0 must be INCLUDED in 1-4 list' elif proper.index == 1: assert proper.exclude is False, \ 'Proper 1 must be INCLUDED in 1-4 list' elif proper.index == 2: assert proper.exclude is True, \ 'Proper 2 must be EXCLUDED in 1-4 list' elif proper.index == 3: assert proper.exclude is True, \ 'Proper 3 must be EXCLUDED in 1-4 list' elif proper.index == 4: assert proper.exclude is True, \ 'Proper 4 must be EXCLUDED in 1-4 list' elif proper.index == 5: assert proper.exclude is False, \ 'Proper 5 must be INCLUDED in 1-4 list'
def test_bad_init_parameterization(self): """ It checks that a call to the parameterize function with a Molecule unsuccessfully initialized raises an Exception. """ FORCEFIELD_NAME = 'openff_unconstrained-1.1.1.offxml' LIGAND_PATH = SET_OF_LIGAND_PATHS[0] ligand_path = get_data_file_path(LIGAND_PATH) molecule = Molecule() with pytest.raises(Exception): molecule.parameterize(FORCEFIELD_NAME) rdkit_toolkit = RDKitToolkitWrapper() molecule._rdkit_molecule = rdkit_toolkit.from_pdb(ligand_path) molecule._off_molecule = None with pytest.raises(Exception): molecule.parameterize(FORCEFIELD_NAME) openforcefield_toolkit = OpenForceFieldToolkitWrapper() molecule._off_molecule = openforcefield_toolkit.from_rdkit(molecule) molecule._rdkit_molecule = None with pytest.raises(Exception): molecule.parameterize(FORCEFIELD_NAME)
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/BNZ.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') 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') 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') 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_PDB_connectivity_template(self): """ It tests the initialization of an offpele's Molecule representation from a PDB file without connectivity and a connectivity template. """ # Initialize an empty Molecule object molecule = Molecule() assert molecule.connectivity_template is None, \ 'Unexpected connectivity template' # Initialize a Molecule from a PDB without connectivity and # without a connectivity template ligand_path = get_data_file_path( 'ligands/BNZ_without_connectivity.pdb') molecule = Molecule(ligand_path) expected_bond_ids = [(1, 0, False), (2, 1, False), (3, 2, False), (4, 3, False), (5, 4, False), (5, 0, False), (6, 0, False), (7, 1, False), (8, 2, False), (9, 3, False), (10, 4, False), (11, 5, False)] for bond in molecule.rdkit_molecule.GetBonds(): bond_id = (bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond.GetIsAromatic()) assert bond_id in expected_bond_ids, 'Unexpected bond id ' \ + '{}'.format(bond_id) # Initialize a Molecule from a PDB without connectivity but with # a connectivity template template_path = get_data_file_path('ligands/BNZ.pdb') template = Molecule(template_path) ligand_path = get_data_file_path( 'ligands/BNZ_without_connectivity.pdb') molecule = Molecule(ligand_path, connectivity_template=template.rdkit_molecule) expected_bond_ids = [(1, 0, True), (2, 1, True), (3, 2, True), (4, 3, True), (5, 4, True), (5, 0, True), (6, 0, False), (7, 1, False), (8, 2, False), (9, 3, False), (10, 4, False), (11, 5, False)] for bond in molecule.rdkit_molecule.GetBonds(): bond_id = (bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond.GetIsAromatic()) assert bond_id in expected_bond_ids, 'Unexpected bond id ' \ + '{}'.format(bond_id) # Initialize a Molecule from a PDB with connectivity and with # a connectivity template template_path = get_data_file_path('ligands/BNZ.pdb') template = Molecule(template_path) ligand_path = get_data_file_path('ligands/BNZ.pdb') molecule = Molecule(ligand_path, connectivity_template=template.rdkit_molecule) expected_bond_ids = [(0, 1, True), (1, 2, True), (2, 3, True), (3, 4, True), (4, 5, True), (0, 5, True), (0, 6, False), (1, 7, False), (2, 8, False), (3, 9, False), (4, 10, False), (5, 11, False)] for bond in molecule.rdkit_molecule.GetBonds(): bond_id = (bond.GetBeginAtomIdx(), bond.GetEndAtomIdx(), bond.GetIsAromatic()) assert bond_id in expected_bond_ids, 'Unexpected bond id ' \ + '{}'.format(bond_id)
def test_assign_Schrodinger_parameters(self): """ It tests the assignment of Schrodinger parameters to offpele's Molecule. """ def check_parameters(): """ It checks the current parameters of the molecule. """ for atom in molecule.atoms: w_atom = WritableAtom(atom) w_parameters = [ w_atom.index, w_atom.parent.index, w_atom.core, w_atom.OPLS_type, w_atom.PDB_name, w_atom.unknown, w_atom.sigma, w_atom.epsilon, w_atom.charge, w_atom.born_radius, w_atom.SASA_radius, w_atom.nonpolar_gamma, w_atom.nonpolar_alpha ] assert w_parameters in expected_nonbonding, \ 'Invalid writable nonbonding parameters {}'.format(w_parameters) for bond in molecule.bonds: w_bond = WritableBond(bond) w_parameters = [attr[1] for attr in list(w_bond)] assert w_parameters in expected_bonds, \ 'Invalid writable bond parameters {}'.format(w_parameters) for angle in molecule.angles: w_angle = WritableAngle(angle) w_parameters = [attr[1] for attr in list(w_angle)] assert w_parameters in expected_angles, \ 'Invalid writable angle parameters {}'.format(w_parameters) # Load benzene ring molecule = Molecule(smiles='C') molecule.parameterize(FORCEFIELD_NAME) # Load OPLS parameters molecule._OPLS_parameters = METHANE_OPLS_PARAMETERS # Check parameters # First check 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_parameters() # Second check molecule.add_OPLS_nonbonding_params() expected_nonbonding = [[ 1, 0, 'M', 'CT', '_C1_', 0, 3.5, 0.066, -0.1088, 1.975, 1.75, 0.005, -0.74168571 ], [ 2, 1, 'M', 'HC', '_H1_', 0, 2.5, 0.03, 0.0267, 1.425, 1.25, 0.00859824, 0.268726247 ], [ 3, 1, 'M', 'HC', '_H2_', 0, 2.5, 0.03, 0.0267, 1.425, 1.25, 0.00859824, 0.268726247 ], [ 4, 1, 'M', 'HC', '_H3_', 0, 2.5, 0.03, 0.0267, 1.425, 1.25, 0.00859824, 0.268726247 ], [ 5, 1, 'M', 'HC', '_H4_', 0, 2.5, 0.03, 0.0267, 1.425, 1.25, 0.00859824, 0.268726247 ]] check_parameters() # Third check molecule.add_OPLS_bonds_and_angles() expected_bonds = [[1, 2, 340.0, 1.09], [1, 3, 340.0, 1.09], [1, 4, 340.0, 1.09], [1, 5, 340.0, 1.09]] expected_angles = [[2, 1, 3, 33.0, 107.8], [2, 1, 4, 33.0, 107.8], [2, 1, 5, 33.0, 107.8], [3, 1, 4, 33.0, 107.8], [3, 1, 5, 33.0, 107.8], [4, 1, 5, 33.0, 107.8]] check_parameters()
def test_OFF_parameters(self): """ It checks the standard Open Force Field parameterization for angles. """ MAX_THRESHOLD = 1e-3 # Load benzene ring molecule = Molecule(smiles='c1ccccc1') # Parameterize molecule.parameterize('openff_unconstrained-1.2.0.offxml', charges_method='gasteiger') # Check resulting parameters expected_angles = {(0, 1, 2): 128.2771922378, (0, 1, 7): 133.1339832262, (0, 5, 4): 128.2771922378, (0, 5, 11): 133.1339832262, (1, 0, 5): 128.2771922378, (1, 0, 6): 133.1339832262, (1, 2, 3): 128.2771922378, (1, 2, 8): 133.1339832262, (2, 1, 7): 133.1339832262, (2, 3, 4): 128.2771922378, (2, 3, 9): 133.1339832262, (3, 2, 8): 133.1339832262, (3, 4, 5): 128.2771922378, (3, 4, 10): 133.1339832262, (4, 3, 9): 133.1339832262, (4, 5, 11): 133.1339832262, (5, 0, 6): 133.1339832262, (5, 4, 10): 133.1339832262} expected_ks = {(0, 1, 2): 157.3576298529, (0, 1, 7): 68.40592742547, (0, 5, 4): 157.3576298529, (0, 5, 11): 68.40592742547, (1, 0, 5): 157.3576298529, (1, 0, 6): 68.40592742547, (1, 2, 3): 157.3576298529, (1, 2, 8): 68.40592742547, (2, 1, 7): 68.40592742547, (2, 3, 4): 157.3576298529, (2, 3, 9): 68.40592742547, (3, 2, 8): 68.40592742547, (3, 4, 5): 157.3576298529, (3, 4, 10): 68.40592742547, (4, 3, 9): 68.40592742547, (4, 5, 11): 68.40592742547, (5, 0, 6): 68.40592742547, (5, 4, 10): 68.40592742547} for indexes, properties in dict(molecule.parameters['Angles']).items(): expected_angle = unit.Quantity(expected_angles[indexes], unit.degree) expected_k = unit.Quantity(expected_ks[indexes], unit.kilocalorie / (unit.radian ** 2 * unit.mole)) assert properties.angle - expected_angle \ < unit.Quantity(MAX_THRESHOLD, unit.degree), \ 'Invalid length for angle {} {}'.format(indexes, properties) assert properties.k - expected_k \ < unit.Quantity(MAX_THRESHOLD, unit.kilocalorie / (unit.radian ** 2 * unit.mole)), \ 'Invalid k for angle {} {}'.format(indexes, properties)
def test_OFF_parameters(self): """ It checks the standard Open Force Field parameterization for dihedrals. """ MAX_THRESHOLD = 1e-3 # Load molecule molecule = Molecule(smiles='C=CC(=O)O') # Parameterize molecule.parameterize('openff_unconstrained-1.2.0.offxml', charges_method='gasteiger') # Check resulting parameters for proper torsions expected_ks = {(0, 1, 2, 3): [0.603518062312, 0.5248455212365], (0, 1, 2, 4): [0.9350453896311], (1, 2, 4, 8): [2.529110648699], (2, 1, 0, 5): [5.376019778605], (2, 1, 0, 6): [5.376019778605], (3, 2, 1, 7): [0.9350453896311], (3, 2, 4, 8): [2.237928151469, 1.23728649144], (4, 2, 1, 7): [0.9350453896311], (5, 0, 1, 7): [5.376019778605], (6, 0, 1, 7): [5.376019778605]} expected_phases = {(0, 1, 2, 3): [180.0, 0.0], (0, 1, 2, 4): [180.0], (1, 2, 4, 8): [180.0], (2, 1, 0, 5): [180.0], (2, 1, 0, 6): [180.0], (3, 2, 1, 7): [180.0], (3, 2, 4, 8): [180.0, 0.0], (4, 2, 1, 7): [180.0], (5, 0, 1, 7): [180.0], (6, 0, 1, 7): [180.0]} expected_periodicities = {(0, 1, 2, 3): [2, 3], (0, 1, 2, 4): [2], (1, 2, 4, 8): [2], (2, 1, 0, 5): [2], (2, 1, 0, 6): [2], (3, 2, 1, 7): [2], (3, 2, 4, 8): [2, 1], (4, 2, 1, 7): [2], (5, 0, 1, 7): [2], (6, 0, 1, 7): [2]} expected_idivfs = {(0, 1, 2, 3): [1.0, 1.0], (0, 1, 2, 4): [1.0], (1, 2, 4, 8): [1.0], (2, 1, 0, 5): [1.0], (2, 1, 0, 6): [1.0], (3, 2, 1, 7): [1.0], (3, 2, 4, 8): [1.0, 1.0], (4, 2, 1, 7): [1.0], (5, 0, 1, 7): [1.0], (6, 0, 1, 7): [1.0]} for indexes, properties in dict( molecule.parameters['ProperTorsions']).items(): for i, (k, phase, periodicity, idivf) in enumerate( zip(properties.k, properties.phase, properties.periodicity, properties.idivf)): expected_k = unit.Quantity(expected_ks[indexes][i], unit.kilocalorie / unit.mole) expected_phase = unit.Quantity(expected_phases[indexes][i], unit.degree) expected_periodicity = expected_periodicities[indexes][i] expected_idivf = expected_idivfs[indexes][i] assert k - expected_k < \ unit.Quantity(MAX_THRESHOLD, unit.kilocalorie / unit.mole), \ 'Invalid k for proper torsion ' \ + '{} {}'.format(indexes, properties) assert phase - expected_phase < \ unit.Quantity(MAX_THRESHOLD, unit.degree), \ 'Invalid phase for proper torsion ' \ + '{} {}'.format(indexes, properties) assert periodicity - expected_periodicity < MAX_THRESHOLD, \ 'Invalid periodicity for proper torsion ' \ + '{} {}'.format(indexes, properties) assert idivf - expected_idivf < MAX_THRESHOLD, \ 'Invalid idivf for proper torsion ' \ + '{} {}'.format(indexes, properties) # Check resulting parameters for improper torsions expected_ks = {(0, 1, 2, 7): [1.1], (1, 0, 5, 6): [1.1], (1, 2, 3, 4): [10.5]} expected_phases = {(0, 1, 2, 7): [180.0], (1, 0, 5, 6): [180.0], (1, 2, 3, 4): [180.0]} expected_periodicities = {(0, 1, 2, 7): [2], (1, 0, 5, 6): [2], (1, 2, 3, 4): [2]} for indexes, properties in dict( molecule.parameters['ImproperTorsions']).items(): for i, (k, phase, periodicity) in enumerate( zip(properties.k, properties.phase, properties.periodicity)): expected_k = unit.Quantity(expected_ks[indexes][i], unit.kilocalorie / unit.mole) expected_phase = unit.Quantity(expected_phases[indexes][i], unit.degree) expected_periodicity = expected_periodicities[indexes][i] assert k - expected_k < \ unit.Quantity(MAX_THRESHOLD, unit.kilocalorie / unit.mole), \ 'Invalid k for improper torsion ' \ + '{} {}'.format(indexes, properties) assert phase - expected_phase < \ unit.Quantity(MAX_THRESHOLD, unit.degree), \ 'Invalid phase for improper torsion ' \ + '{} {}'.format(indexes, properties) assert periodicity - expected_periodicity < MAX_THRESHOLD, \ 'Invalid periodicity for improper torsion ' \ + '{} {}'.format(indexes, properties) assert properties.idivf is None, \ 'Invalid idivf for improper torsion ' \ + '{} {}'.format(indexes, properties)
def test_default_output_paths(self): """ It checks the default output paths that are used for each parameter file from offpele. """ def from_PosixPath_to_string(paths): """ Convert PosixPaths to strings """ return map(str, paths) molecule = Molecule(smiles='c1ccccc1', name='benzene', tag='BNZ') rotlib_path, impact_path, solvent_path = \ handle_output_paths(molecule, '', False) # Convert PosixPaths to strings rotlib_path, impact_path, solvent_path = map( str, [rotlib_path, impact_path, solvent_path]) assert rotlib_path == 'BNZ.rot.assign', 'Unexpected default ' \ + 'rotamer library path' assert impact_path == 'bnzz', 'Unexpected default Impact ' \ + 'template path' assert solvent_path == 'ligandParams.txt', 'Unexpected default ' \ + 'solvent parameters path' with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): # To avoid the complain about unexistent folder os.mkdir('output') rotlib_path, impact_path, solvent_path = \ handle_output_paths(molecule, 'output', False) # Convert PosixPaths to strings rotlib_path, impact_path, solvent_path = map( str, [rotlib_path, impact_path, solvent_path]) assert rotlib_path == 'output/BNZ.rot.assign', 'Unexpected default ' \ + 'rotamer library path' assert impact_path == 'output/bnzz', 'Unexpected default Impact ' \ + 'template path' assert solvent_path == 'output/ligandParams.txt', 'Unexpected ' \ + 'default solvent parameters path' rotlib_path, impact_path, solvent_path = \ handle_output_paths(molecule, '', True) # Convert PosixPaths to strings rotlib_path, impact_path, solvent_path = map( str, [rotlib_path, impact_path, solvent_path]) assert rotlib_path == 'DataLocal/LigandRotamerLibs/' \ + 'BNZ.rot.assign', 'Unexpected default rotamer library path' assert impact_path == 'DataLocal/Templates/OFF/Parsley/' \ + 'HeteroAtoms/bnzz', 'Unexpected default Impact template' assert solvent_path == 'DataLocal/OBC/ligandParams.txt', \ 'Unexpected default solvent parameters path' with tempfile.TemporaryDirectory() as tmpdir: with temporary_cd(tmpdir): # To avoid the complain about unexistent folder os.mkdir('output') rotlib_path, impact_path, solvent_path = \ handle_output_paths(molecule, 'output', True) # Convert PosixPaths to strings rotlib_path, impact_path, solvent_path = map( str, [rotlib_path, impact_path, solvent_path]) assert rotlib_path == 'output/DataLocal/LigandRotamerLibs/' \ + 'BNZ.rot.assign', 'Unexpected default rotamer library path' assert impact_path == 'output/DataLocal/Templates/OFF/Parsley/' \ + 'HeteroAtoms/bnzz', 'Unexpected default Impact template path' assert solvent_path == 'output/DataLocal/OBC/ligandParams.txt', \ 'Unexpected default solvent parameters path'
def run_offpele(pdb_file, forcefield=DEFAULT_OFF_FORCEFIELD, resolution=DEFAULT_RESOLUTION, charges_method=DEFAULT_CHARGES_METHOD, use_OPLS_nb_params=False, use_OPLS_bonds_and_angles=False, exclude_terminal_rotamers=True, output=None, with_solvent=False, as_datalocal=False): """ It runs offpele. Parameters ---------- pdb_file : str The path to the pdb_file to parameterize with offpele forcefield : str The name of an OpenForceField's forcefield resolution : float The resolution in degrees for the rotamer library. Default is 30 charges_method : str The name of the method to use to compute partial charges. Default is 'am1bcc' use_OPLS_nb_params : bool Whether to use Open Force Field or OPLS to obtain the nonbonding parameters. Please, note that this option is only available if a valid Schrodinger installation is found in the current machine. Default is False use_OPLS_bonds_and_angles : bool Whether to use OPLS to obtain the bond and angle parameters or not. Please, note that this option is only available if a valid Schrodinger installation is found in the current machine. Default is False exclude_terminal_rotamers : bool Whether to exclude terminal rotamers or not output : str Path where output files will be saved with_solvent : bool Whether to generate and save the solvent parameters for the input molecule or not as_datalocal : bool Whether to save output files following PELE's DataLocal hierarchy or not """ print('-' * 60) print('Open Force Field parameterizer for PELE', offpele.__version__) print('-' * 60) print(' - General:') print(' - Input PDB:', pdb_file) print(' - Output path:', output) print(' - Write solvent parameters:', with_solvent) print(' - DataLocal-like output:', as_datalocal) print(' - Parameterization:') print(' - Force field:', forcefield) print(' - Charges method:', charges_method) print(' - Use OPLS nonbonding parameters:', use_OPLS_nb_params) print(' - Use OPLS bonds and angles:', use_OPLS_bonds_and_angles) print(' - Rotamer library:') print(' - Resolution:', resolution) print(' - Exclude terminal rotamers:', exclude_terminal_rotamers) print('-' * 60) # Supress OpenForceField toolkit warnings import logging logging.getLogger().setLevel(logging.ERROR) from offpele.topology import Molecule from offpele.template import Impact from offpele.solvent import OBC2 if not output: output = os.getcwd() molecule = Molecule(pdb_file, rotamer_resolution=resolution, exclude_terminal_rotamers=exclude_terminal_rotamers) rotlib_out, impact_out, solvent_out = handle_output_paths( molecule, output, as_datalocal) rotamer_library = offpele.topology.RotamerLibrary(molecule) rotamer_library.to_file(rotlib_out) molecule.parameterize(forcefield, charges_method=charges_method, use_OPLS_nonbonding_params=use_OPLS_nb_params, use_OPLS_bonds_and_angles=use_OPLS_bonds_and_angles) impact = Impact(molecule) impact.write(impact_out) if with_solvent: solvent = OBC2(molecule) solvent.to_json_file(solvent_out) print(' - All files were generated successfully') print('-' * 60)
def test_terminal_rotamer_filtering(self): """ It tests the rotamer library builder when the terminal rotatable bonds are ignored. """ LIGAND_PATH = 'ligands/OLC.pdb' ligand_path = get_data_file_path(LIGAND_PATH) molecule = Molecule(ligand_path, exclude_terminal_rotamers=True) rotamers_per_branch = molecule.rotamers assert len(rotamers_per_branch) == 2, "Found an invalid number " + \ "of branches: {}".format(len(rotamers_per_branch)) atom_list_1 = list() atom_list_2 = list() rotamers = rotamers_per_branch[0] for rotamer in rotamers: atom_list_1.append(set([rotamer.index1, rotamer.index2])) rotamers = rotamers_per_branch[1] for rotamer in rotamers: atom_list_2.append(set([rotamer.index1, rotamer.index2])) EXPECTED_INDICES_1 = [ set([9, 10]), set([8, 9]), set([7, 8]), set([6, 7]), set([5, 6]), set([2, 5]), set([0, 2]), set([0, 1]) ] EXPECTED_INDICES_2 = [ set([12, 11]), set([12, 13]), set([13, 14]), set([14, 15]), set([15, 16]), set([16, 17]), set([17, 18]) ] where_1 = list() for atom_pair in atom_list_1: if atom_pair in EXPECTED_INDICES_1: where_1.append(1) elif atom_pair in EXPECTED_INDICES_2: where_1.append(2) else: where_1.append(0) where_2 = list() for atom_pair in atom_list_2: if atom_pair in EXPECTED_INDICES_1: where_2.append(1) elif atom_pair in EXPECTED_INDICES_2: where_2.append(2) else: where_2.append(0) assert (all(i == 1 for i in where_1) and all(i == 2 for i in where_2)) or \ (all(i == 2 for i in where_1) and all(i == 1 for i in where_2)), "Invalid rotamer library " + \ "{}, {}".format(where_1, where_2) assert (all(i == 1 for i in where_1) and all(i == 2 for i in where_2) and len(where_1) == len(EXPECTED_INDICES_1) and len(where_2) == len(EXPECTED_INDICES_2)) or \ (all(i == 2 for i in where_1) and all(i == 1 for i in where_2) and len(where_1) == len(EXPECTED_INDICES_2) and len(where_2) == len(EXPECTED_INDICES_1)), "Unexpected " + \ "number of rotamers"