Statistiques
| Branche: | Tag: | Révision :

dockonsurf / modules / isolated.py @ cf980c86

Historique | Voir | Annoter | Télécharger (7,96 ko)

1 4614bb6a Carles
"""Functions to generate the conformers to be adsorbed and the most stable one.
2 4614bb6a Carles

3 5c70d6c6 Carles
functions:
4 5c70d6c6 Carles
remove_C_linked_Hs: Removes hydrogens bonded to a carbon atom from a molecule.
5 92924d24 Carles
gen_confs: Generate a number of conformers in random orientations.
6 92924d24 Carles
get_moments_of_inertia: Computes moments of inertia of the given conformers.
7 4e82c425 Carles Martí
get_moments_of_inertia: Computes the moments of inertia of the given conformers.
8 4e82c425 Carles Martí
pre_opt_confs: Optimizes the geometry of the given conformers and returns the
9 92924d24 Carles
    new mol object and the energies of its conformers.
10 4614bb6a Carles
run_isolated: directs the execution of functions to achieve the goal
11 4614bb6a Carles
"""
12 4614bb6a Carles
import logging
13 4614bb6a Carles
14 92924d24 Carles
import numpy as np
15 fff3aab1 Carles
import rdkit.Chem.AllChem as Chem
16 92924d24 Carles
17 4614bb6a Carles
logger = logging.getLogger('DockOnSurf')
18 4614bb6a Carles
19 4614bb6a Carles
20 92924d24 Carles
def remove_C_linked_Hs(mol: Chem.rdchem.Mol):
21 b1f6e69d Carles
    """Removes hydrogen atoms bonded to a carbon atom from a rdkit mol object.
22 5c70d6c6 Carles

23 92924d24 Carles
    @param mol: rdkit mol object of the molecule with hydrogen atoms.
24 92924d24 Carles
    @return: rdkit mol object of the molecule without hydrogen atoms linked to
25 92924d24 Carles
    a carbon atom.
26 92924d24 Carles

27 92924d24 Carles
    The functions removes the hydrogen atoms bonded to carbon atoms while
28 92924d24 Carles
    keeping the ones bonded to other atoms or non-bonded at all.
29 5c70d6c6 Carles
    """
30 5c70d6c6 Carles
31 5c70d6c6 Carles
    mol = Chem.RWMol(mol)
32 5c70d6c6 Carles
    rev_atm_idxs = [atom.GetIdx() for atom in reversed(mol.GetAtoms())]
33 5c70d6c6 Carles
34 5c70d6c6 Carles
    for atm_idx in rev_atm_idxs:
35 5c70d6c6 Carles
        atom = mol.GetAtomWithIdx(atm_idx)
36 5c70d6c6 Carles
        if atom.GetAtomicNum() != 1:
37 5c70d6c6 Carles
            continue
38 5c70d6c6 Carles
        for neigh in atom.GetNeighbors():
39 5c70d6c6 Carles
            if neigh.GetAtomicNum() == 6:
40 5c70d6c6 Carles
                mol.RemoveAtom(atom.GetIdx())
41 5c70d6c6 Carles
    return mol
42 5c70d6c6 Carles
43 5c70d6c6 Carles
44 3a46e515 Carles Marti
def gen_confs(mol: Chem.rdchem.Mol, num_confs: int):
45 92924d24 Carles
    """Generate conformers in random orientations.
46 4614bb6a Carles

47 4614bb6a Carles
    @param mol: rdkit mol object of the molecule to be adsorbed.
48 92924d24 Carles
    @param num_confs: number of conformers to randomly generate.
49 4614bb6a Carles
    @return: mol: rdkit mol object containing the different conformers.
50 bd573212 Carles
             rmsd_mtx: Matrix with the rmsd values of conformers.
51 b1f6e69d Carles

52 92924d24 Carles
    Using the rdkit library, conformers are randomly generated. If structures 
53 92924d24 Carles
    are required to be local minima, ie. setting the 'local_min' value to 
54 92924d24 Carles
    True, a geometry optimisation using UFF is performed.
55 4614bb6a Carles
    """
56 695dcff8 Carles Marti
    logger.debug('Generating Conformers.')
57 9e83b5a1 Carles
58 9324a38a Carles
    mol = Chem.AddHs(mol)
59 9062afa6 Carles Marti
    Chem.EmbedMultipleConfs(mol, numConfs=num_confs, numThreads=0,
60 9062afa6 Carles Marti
                            randomSeed=5817216)
61 5c70d6c6 Carles
    Chem.AlignMolConformers(mol)
62 695dcff8 Carles Marti
    logger.info(f'Generated {len(mol.GetConformers())} conformers.')
63 92924d24 Carles
    return mol
64 92924d24 Carles
65 62576f52 Carles
66 58ede1f9 Carles Martí
def get_moments_of_inertia(mol: Chem.rdchem.Mol):
67 4e82c425 Carles Martí
    """Computes the moments of inertia of the given conformers.
68 92924d24 Carles

69 92924d24 Carles
    @param mol: rdkit mol object of the relevant molecule.
70 92924d24 Carles
    @return numpy array 2D: The inner array contains the moments of inertia for
71 92924d24 Carles
    the three principal axis of a given conformer. They are ordered by its value
72 92924d24 Carles
    in ascending order. The outer tuple loops over the conformers.
73 92924d24 Carles
    """
74 92924d24 Carles
    from rdkit.Chem.Descriptors3D import PMI1, PMI2, PMI3
75 92924d24 Carles
76 92924d24 Carles
    return np.array([[PMI(mol, confId=conf) for PMI in (PMI1, PMI2, PMI3)]
77 92924d24 Carles
                     for conf in range(mol.GetNumConformers())])
78 92924d24 Carles
79 92924d24 Carles
80 365d5b9a Carles Marti
def pre_opt_confs(mol: Chem.rdchem.Mol, force_field='mmff', max_iters=2000):
81 92924d24 Carles
    """Optimizes the geometry of the given conformers and returns the new mol
82 92924d24 Carles
    object and the energies of its conformers.
83 92924d24 Carles

84 92924d24 Carles
    @param mol: rdkit mol object of the relevant molecule.
85 ad57fd42 Carles Marti
    @param force_field: Force Field to use for the pre-optimization.
86 62576f52 Carles
    @param max_iters: Maximum number of geometry optimization iterations. With 0
87 62576f52 Carles
    a single point energy calculation is performed and only the conformer
88 62576f52 Carles
    energies are returned.
89 92924d24 Carles
    @return mol: rdkit mol object of the optimized molecule.
90 92924d24 Carles
    @return numpy.ndarray: Array with the energies of the optimized conformers.
91 92924d24 Carles

92 ad57fd42 Carles Marti
    The MMFF and UFF force fields can be used for the geometry optimization in
93 4670488d Carles Marti
    their rdkit implementation. With max_iters value set to 0, a single point
94 4670488d Carles Marti
    energy calculation is performed and only the energies are returned. For
95 4670488d Carles Marti
    values larger than 0, if the geometry does not converge for a certain
96 4670488d Carles Marti
    conformer, the latter is removed from the list of conformers and its energy
97 4670488d Carles Marti
    is not included in the returned list.
98 92924d24 Carles
    """
99 4670488d Carles Marti
    from rdkit.Chem.rdForceFieldHelpers import MMFFOptimizeMoleculeConfs, \
100 4670488d Carles Marti
        UFFOptimizeMoleculeConfs
101 92924d24 Carles
102 92924d24 Carles
    init_num_confs = mol.GetNumConformers()
103 ad57fd42 Carles Marti
    if force_field == 'mmff':
104 4670488d Carles Marti
        results = np.array(MMFFOptimizeMoleculeConfs(mol, numThreads=0,
105 4670488d Carles Marti
                                                     maxIters=max_iters,
106 4670488d Carles Marti
                                                     nonBondedThresh=10))
107 ad57fd42 Carles Marti
    elif force_field == 'uff':
108 4670488d Carles Marti
        results = np.array(UFFOptimizeMoleculeConfs(mol, numThreads=0,
109 4670488d Carles Marti
                                                    maxIters=max_iters,
110 4670488d Carles Marti
                                                    vdwThresh=10))
111 4670488d Carles Marti
    else:
112 4670488d Carles Marti
        logger.error("force_field parameter must be 'MMFF' or 'UFF'")
113 4670488d Carles Marti
        raise ValueError("force_field parameter must be 'MMFF' or 'UFF'")
114 62576f52 Carles
115 62576f52 Carles
    # Remove non-converged conformers if optimization is on, ie. maxIters > 0
116 62576f52 Carles
    # return all conformers if optimization is switched off, ie. maxIters = 0
117 62576f52 Carles
    if max_iters > 0:
118 62576f52 Carles
        for i, conv in enumerate(results[:, 0]):
119 62576f52 Carles
            if conv != 0:
120 62576f52 Carles
                mol.RemoveConformer(i)
121 62576f52 Carles
        for i, conf in enumerate(mol.GetConformers()):
122 62576f52 Carles
            conf.SetId(i)
123 62576f52 Carles
        if mol.GetNumConformers() < init_num_confs:
124 ad57fd42 Carles Marti
            logger.warning(f'Geometry optimization did not comverge for at'
125 62576f52 Carles
                           f'least one conformer. Continuing with '
126 695dcff8 Carles Marti
                           f'{mol.GetNumConformers()} converged conformers.')
127 ad57fd42 Carles Marti
        logger.info(f'Pre-optimized conformers with {force_field}.')
128 62576f52 Carles
        return mol, np.array([res[1] for res in results if res[0] == 0])
129 62576f52 Carles
    else:
130 ad57fd42 Carles Marti
        logger.info(f'Computed conformers energy with {force_field}.')
131 62576f52 Carles
        return np.array([res[1] for res in results])
132 4614bb6a Carles
133 4614bb6a Carles
134 4614bb6a Carles
def run_isolated(inp_vars):
135 4614bb6a Carles
    """Directs the execution of functions to obtain the conformers to adsorb
136 4614bb6a Carles

137 a9b058a5 Carles
    @param inp_vars: Calculation parameters from input file.
138 4614bb6a Carles
    @return:
139 4614bb6a Carles
    """
140 f3004731 Carles
    from modules.formats import adapt_format, confs_to_mol_list, \
141 1d8c374e Carles Martí
        rdkit_mol_to_ase_atoms, collect_confs
142 fff3aab1 Carles
    from modules.clustering import clustering, get_rmsd
143 1f778710 Carles Martí
    from modules.calculation import run_calc, check_finished_calcs
144 1f778710 Carles Martí
    from modules.refinement import select_stable_confs
145 54be2a9e Carles
146 695dcff8 Carles Marti
    logger.info('Carrying out procedures for the isolated molecule.')
147 4e82c425 Carles Martí
    # Read the molecule
148 90819cc3 Carles Marti
    rd_mol = adapt_format('rdkit', inp_vars['molec_file'],
149 90819cc3 Carles Marti
                          inp_vars['special_atoms'])
150 4e82c425 Carles Martí
    # Generate conformers
151 92924d24 Carles
    confs = gen_confs(rd_mol, inp_vars['num_conformers'])
152 4e82c425 Carles Martí
    # Pre-optimizes conformers
153 4670488d Carles Marti
    if inp_vars['pre_opt']:
154 4670488d Carles Marti
        confs, confs_ener = pre_opt_confs(confs, inp_vars['pre_opt'])
155 fab7b517 Carles
    else:
156 4670488d Carles Marti
        confs_ener = pre_opt_confs(confs, max_iters=0)
157 fff3aab1 Carles
    conf_list = confs_to_mol_list(confs)
158 4e82c425 Carles Martí
    # Calculates RMSD matrix of the conformers
159 fff3aab1 Carles
    rmsd_mtx = get_rmsd(conf_list)
160 fab7b517 Carles
    confs_moi = get_moments_of_inertia(confs)
161 4e82c425 Carles Martí
    # Clusters the conformers and selects a representative
162 fd33bfdb Carles
    exemplars = clustering(rmsd_mtx)
163 f3004731 Carles
    mol_list = confs_to_mol_list(confs, exemplars)
164 f3004731 Carles
    ase_atms_list = [rdkit_mol_to_ase_atoms(mol) for mol in mol_list]
165 a44ad3c2 Carles Martí
    if len(ase_atms_list) == 0:
166 a44ad3c2 Carles Martí
        err_msg = "No configurations were generated: Check the parameters in" \
167 a44ad3c2 Carles Martí
                  "dockonsurf.inp"
168 a44ad3c2 Carles Martí
        logger.error(err_msg)
169 a44ad3c2 Carles Martí
        raise ValueError(err_msg)
170 4e82c425 Carles Martí
    # Runs the jobs.
171 f3004731 Carles
    run_calc('isolated', inp_vars, ase_atms_list)
172 96d422c7 Carles Martí
    logger.info("Finished the procedures for the isolated molecule section. ")
173 96d422c7 Carles Martí
    if inp_vars["batch_q_sys"]:
174 96d422c7 Carles Martí
        finished_calcs, failed_calcs = check_finished_calcs('isolated',
175 082685ad Carles Martí
                                                            inp_vars['code'])
176 96d422c7 Carles Martí
        conf_list = collect_confs(finished_calcs, inp_vars['code'], 'isolated',
177 1d8c374e Carles Martí
                              inp_vars['special_atoms'])
178 96d422c7 Carles Martí
        most_stable_conf = select_stable_confs(conf_list, 0)[0]
179 96d422c7 Carles Martí
180 96d422c7 Carles Martí
        logger.info(f"Most stable conformers is {most_stable_conf.info['iso']},"
181 96d422c7 Carles Martí
                    f" with a total energy of {most_stable_conf.info['energy']}"
182 96d422c7 Carles Martí
                    f" eV.")