Ejemplo n.º 1
0
def conf_is_unique_rmsd(conf, conf_list, rmsd_tol=None):
    """
    Determine if a conformer is unique based on an root mean squared
    displacement RMSD threshold based on heavy atoms

    Arguments:
        conf (autode.conformer.Conformer):
        conf_list (list((list(autode.conformer.Conformer)):

    Keyword Arguments:
        rmsd_tol (float): Tolerance for an equivalent structure based on the
                          rmsd in Å. If None then use the default value for
                          autode.Config.rmsd_threshold
    Returns:
        (bool):
    """
    rmsd_tol = Config.rmsd_threshold if rmsd_tol is None else rmsd_tol
    logger.info(f'Removing conformers with RMSD < {rmsd_tol} Å to any other')

    # Calculate the RMSD between this Conformer and the those in conf_list
    # using the Kabsch algorithm
    for other_conf in conf_list:

        if calc_heavy_atom_rmsd(conf.atoms, other_conf.atoms) < rmsd_tol:
            return False

    return True
Ejemplo n.º 2
0
def test_calc_rmsd():

    atoms = [
        Atom('C', 0.0009, 0.0041, -0.0202),
        Atom('H', -0.6577, -0.8481, -0.3214),
        Atom('H', -0.4585, 0.9752, -0.3061),
        Atom('H', 0.0853, -0.0253, 1.0804),
        Atom('H', 1.0300, -0.1058, -0.4327)
    ]

    atoms_rot = [
        Atom('C', -0.0009, -0.0041, -0.0202),
        Atom('H', 0.6577, 0.8481, -0.3214),
        Atom('H', 0.4585, -0.9752, -0.3061),
        Atom('H', -0.0853, 0.0253, 1.0804),
        Atom('H', -1.0300, 0.1058, -0.4327)
    ]

    coords1 = np.array([atom.coord for atom in atoms])
    coords2 = np.array([atom.coord for atom in atoms_rot])

    # Rotated coordinates should have almost 0 RMSD between them
    assert geom.calc_rmsd(coords1, coords2) < 1E-5

    # Coordinates need to have the same shape to calculate the RMSD
    with pytest.raises(AssertionError):
        _ = geom.calc_rmsd(coords1, coords2[1:])

    assert geom.calc_heavy_atom_rmsd(atoms, atoms_rot) < 1E-5

    # Permuting two hydrogens should generate a larger RMSD
    atoms_rot[2], atoms_rot[3] = atoms_rot[3], atoms_rot[2]
    rmsd = geom.calc_rmsd(coords1=np.array([atom.coord for atom in atoms]),
                          coords2=np.array([atom.coord for atom in atoms_rot]))

    assert rmsd > 0.1

    # While the heavy atom RMSD should remain unchanged
    assert geom.calc_heavy_atom_rmsd(atoms, atoms_rot) < 1E-6
Ejemplo n.º 3
0
    def find_lowest_energy_ts_conformer(self, rmsd_threshold=None):
        """Find the lowest energy transition state conformer by performing
        constrained optimisations"""
        logger.info('Finding lowest energy TS conformer')

        atoms, energy = deepcopy(self.atoms), deepcopy(self.energy)
        calc = deepcopy(self.optts_calc)

        hmethod = get_hmethod() if Config.hmethod_conformers else None
        self.find_lowest_energy_conformer(hmethod=hmethod)

        # Remove similar TS conformer that are similar to this TS based on root
        # mean squared differences in their structures
        thresh = Config.rmsd_threshold if rmsd_threshold is None else rmsd_threshold
        self.conformers = [
            conf for conf in self.conformers
            if calc_heavy_atom_rmsd(conf.atoms, atoms) > thresh
        ]

        logger.info(f'Generated {len(self.conformers)} unique (RMSD > '
                    f'{thresh} Å) TS conformer(s)')

        # Optimise the lowest energy conformer to a transition state - will
        # .find_lowest_energy_conformer will have updated self.atoms etc.
        if len(self.conformers) > 0:
            self.optimise(name_ext='optts_conf')

            if self.is_true_ts() and self.energy < energy:
                logger.info('Conformer search successful')
                return None

            # Ensure the energy has a numerical value, so a difference can be
            # evaluated
            self.energy = self.energy if self.energy is not None else 0
            logger.warning(f'Transition state conformer search failed '
                           f'(∆E = {energy - self.energy:.4f} Ha). Reverting')

        logger.info('Reverting to previously found TS')
        self.atoms = atoms
        self.energy = energy
        self.optts_calc = calc
        self.imaginary_frequencies = calc.get_imaginary_freqs()

        return None