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_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_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_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_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_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_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 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_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_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_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()