Ejemplo n.º 1
0
def ase_popt(embedder,
             coords,
             atomnos,
             constrained_indexes=None,
             steps=500,
             targets=None,
             safe=False,
             safe_mask=None,
             traj=None,
             logfunction=None,
             title='temp'):
    '''
    embedder: TSCoDe embedder object
    coords: 
    atomnos: 
    constrained_indexes:
    safe: if True, adds a potential that prevents atoms from scrambling
    safe_mask: bool array, with False for atoms to be excluded when calculating bonds to preserve
    traj: if set to a string, traj is used as a filename for the bending trajectory.
          not only the atoms will be printed, but also all the orbitals and the active pivot.
    '''
    atoms = Atoms(atomnos, positions=coords)
    atoms.calc = get_ase_calc(embedder)
    constraints = []

    if constrained_indexes is not None:
        for i, c in enumerate(constrained_indexes):
            i1, i2 = c
            tgt_dist = norm_of(coords[i1] -
                               coords[i2]) if targets is None else targets[i]
            constraints.append(Spring(i1, i2, tgt_dist))

    if safe:
        constraints.append(
            PreventScramblingConstraint(
                graphize(coords, atomnos, safe_mask),
                atoms,
                double_bond_protection=embedder.options.double_bond_protection,
                fix_angles=embedder.options.fix_angles_in_deformation))

    atoms.set_constraint(constraints)

    t_start_opt = time.perf_counter()
    with LBFGS(atoms, maxstep=0.1, logfile=None, trajectory=traj) as opt:
        opt.run(fmax=0.05, steps=steps)
        iterations = opt.nsteps

    new_structure = atoms.get_positions()
    success = (iterations < 499)

    if logfunction is not None:
        exit_str = 'REFINED' if success else 'MAX ITER'
        logfunction(
            f'    - {title} {exit_str} ({iterations} iterations, {time_to_string(time.perf_counter()-t_start_opt)})'
        )

    energy = atoms.get_total_energy() * 23.06054194532933  #eV to kcal/mol

    return new_structure, energy, success
Ejemplo n.º 2
0
def confab_operator(filename, options, logfunction=None):
    '''
    '''

    if logfunction is not None:
        logfunction(
            f'--> Performing conformational search and optimization on {filename}'
        )

    data = read_xyz(filename)

    if len(data.atomcoords) > 1:
        raise InputError(
            f'Requested conformational search on file {filename} that already contains more than one structure.'
        )

    if len(
            tuple(
                connected_components(graphize(data.atomcoords[0],
                                              data.atomnos)))) > 1:
        raise InputError((
            f'Requested conformational search on a molecular complex (file {filename}). '
            'Confab is not suited for this task, and using TSCoDe\'s csearch> operator '
            'is a better idea.'))

    if len(set(data.atomnos) - {1, 6, 7, 8, 9, 15, 16, 17, 35, 53}) != 0:
        raise InputError((
            'Requested conformational search on a molecule that contains atoms different '
            'than the ones for which OpenBabel Force Fields are parametrized. Please '
            'perform this conformational search through the more sophisticated and better '
            'integrated csearch> operator, part of the TSCoDe program.'))

    confname = filename[:-4] + '_confab.xyz'

    with suppress_stdout_stderr():
        check_call(
            f'obabel {filename} -O {confname} --confab --rcutoff 0.5 --original'
            .split(),
            stdout=DEVNULL,
            stderr=STDOUT)
        # running Confab through Openbabel

    data = read_xyz(confname)
    conformers = data.atomcoords

    if len(conformers) > 10 and not options.let:
        conformers = conformers[0:10]
        logfunction(
            f'Will use only the best 10 conformers for TSCoDe embed.\n')

    os.remove(confname)
    with open(confname, 'w') as f:
        for i, conformer in enumerate(conformers):
            write_xyz(conformer,
                      data.atomnos,
                      f,
                      title=f'Generated conformer {i}')

    return confname
Ejemplo n.º 3
0
def molecule_check(old_coords, new_coords, atomnos, max_newbonds=0):
    '''
    Checks if two molecules have the same bonds between the same atomic indexes
    '''
    old_bonds = {(a, b)
                 for a, b in list(graphize(old_coords, atomnos).edges)
                 if a != b}
    new_bonds = {(a, b)
                 for a, b in list(graphize(new_coords, atomnos).edges)
                 if a != b}

    delta_bonds = (old_bonds | new_bonds) - (old_bonds & new_bonds)

    if len(delta_bonds) > max_newbonds:
        return False

    return True
Ejemplo n.º 4
0
def scramble_check(TS_structure,
                   TS_atomnos,
                   constrained_indexes,
                   mols_graphs,
                   max_newbonds=0) -> bool:
    '''
    Check if a transition state structure has scrambled during some optimization
    steps. If more than a given number of bonds changed (formed or broke) the
    structure is considered scrambled, and the method returns False.
    '''
    assert len(TS_structure) == sum(
        [len(graph.nodes) for graph in mols_graphs])

    bonds = set()
    for i, graph in enumerate(mols_graphs):

        pos = 0
        while i != 0:
            pos += len(mols_graphs[i - 1].nodes)
            i -= 1

        for bond in [
                tuple(sorted((a + pos, b + pos))) for a, b in list(graph.edges)
                if a != b
        ]:
            bonds.add(bond)
    # creating bond set containing all bonds present in the desired transition state

    new_bonds = {
        tuple(sorted((a, b)))
        for a, b in list(graphize(TS_structure, TS_atomnos).edges) if a != b
    }
    delta_bonds = (bonds | new_bonds) - (bonds & new_bonds)
    # delta_bonds -= {tuple(sorted(pair)) for pair in constrained_indexes}

    for bond in delta_bonds.copy():
        for a1, a2 in constrained_indexes:
            if (a1 in bond) or (a2 in bond):
                delta_bonds -= {bond}
    # removing bonds involving constrained atoms: they are not counted as scrambled bonds

    if len(delta_bonds) > max_newbonds:
        return False

    return True
Ejemplo n.º 5
0
    def __init__(self, filename, reactive_indexes=None, debug=False):
        '''
        Initializing class properties: reading conformational ensemble file, aligning
        conformers to first and centering them in origin.

        :params filename:           Input file name. Can be anything, .xyz preferred
        :params reactive_indexes:     Index of atoms that will link during the desired reaction.
                                    May be either int or list of int.

        :params hyper:              bool, whether to calculate orbitals positions
        '''

        if not os.path.isfile(filename):
            if '.' in filename:
                raise SyntaxError((
                    f'Molecule {filename} cannot be read. Please check your syntax.'
                ))

            raise SyntaxError((
                f'The program is trying to read something that is not a valid molecule input ({filename}). '
                +
                'If this looks like a keyword, it is probably faulted by a syntax error.'
            ))

        self.rootname = filename.split('.')[0]
        self.name = filename
        self.debug = debug

        if isinstance(reactive_indexes, np.ndarray):
            self.reactive_indexes = reactive_indexes
        else:
            self.reactive_indexes = np.array(reactive_indexes) if isinstance(
                reactive_indexes, (tuple, list)) else ()

        ccread_object = read_xyz(filename)
        if ccread_object is None:
            raise CCReadError(f'Cannot read file {filename}')

        coordinates = np.array(ccread_object.atomcoords)

        # if coordinates.shape[0] > 5:
        #     coordinates = coordinates[0:5]
        # # Do not keep more than 5 conformations

        self.atomnos = ccread_object.atomnos
        self.position = np.array([0, 0, 0],
                                 dtype=float)  # used in Embedder class
        self.rotation = np.identity(
            3)  # used in Embedder class - rotation matrix

        assert all([
            len(coordinates[i]) == len(coordinates[0])
            for i in range(1, len(coordinates))
        ]), 'Ensembles must have constant atom number.'
        # Checking that ensemble has constant length
        if self.debug:
            print(
                f'DEBUG--> Initializing object {filename}\nDEBUG--> Found {len(coordinates)} structures with {len(coordinates[0])} atoms'
            )

        self.centroid = np.sum(np.sum(coordinates, axis=0), axis=0) / (
            len(coordinates) * len(coordinates[0]))

        if self.debug:
            print('DEBUG--> Centroid was', self.centroid)

        self.atomcoords = coordinates - self.centroid
        self.graph = graphize(self.atomcoords[0], self.atomnos)
        # show_graph(self)

        self.atoms = np.array([
            atom for structure in self.atomcoords for atom in structure
        ])  # single list with all atomic positions

        if self.debug:
            print(f'DEBUG--> Total of {len(self.atoms)} atoms')
Ejemplo n.º 6
0
def ase_adjust_spacings(embedder,
                        structure,
                        atomnos,
                        constrained_indexes,
                        title=0,
                        traj=None):
    '''
    embedder: TSCoDe embedder object
    structure: TS candidate coordinates to be adjusted
    atomnos: 1-d array with element numbering for the TS
    constrained_indexes: (n,2)-shaped array of indexes to be distance constrained
    mols_graphs: list of NetworkX graphs, ordered as the single molecules in the TS
    title: number to be used for referring to this structure in the embedder log
    traj: if set to a string, traj+'.traj' is used as a filename for the refinement trajectory.
    '''
    atoms = Atoms(atomnos, positions=structure)

    atoms.calc = get_ase_calc(embedder)

    springs = [
        Spring(indexes[0], indexes[1], dist)
        for indexes, dist in embedder.target_distances.items()
    ]
    # adding springs to adjust the pairings for which we have target distances

    nci_indexes = [
        indexes for letter, indexes in embedder.pairings_table.items()
        if letter in ('x', 'y', 'z')
    ]
    halfsprings = [HalfSpring(i1, i2, 2.5) for i1, i2 in nci_indexes]
    # HalfSprings get atoms involved in NCIs together if they are more than 2.5A apart,
    # but lets them achieve their natural equilibrium distance when closer

    psc = PreventScramblingConstraint(
        graphize(structure, atomnos),
        atoms,
        double_bond_protection=embedder.options.double_bond_protection,
        fix_angles=embedder.options.fix_angles_in_deformation)

    atoms.set_constraint(springs + halfsprings + [psc])

    t_start_opt = time.perf_counter()
    try:
        with LBFGS(atoms, maxstep=0.2, logfile=None, trajectory=traj) as opt:

            opt.run(fmax=0.05, steps=500)
            # initial coarse refinement with
            # Springs, Half Springs and PSC

            for spring in springs:
                spring.tighten()
            atoms.set_constraint(springs)
            # Tightening Springs to improve
            # spacings accuracy

            opt.run(fmax=0.05, steps=200)
            # final accurate refinement

            iterations = opt.nsteps

        new_structure = atoms.get_positions()

        success = scramble_check(new_structure, atomnos, constrained_indexes,
                                 embedder.graphs)
        exit_str = 'REFINED' if success else 'SCRAMBLED'

    except PropertyNotImplementedError:
        exit_str = 'CRASHED'

    embedder.log(
        f'    - {embedder.options.calculator} {embedder.options.theory_level} refinement: Structure {title} {exit_str} ({iterations} iterations, {time_to_string(time.perf_counter()-t_start_opt)})',
        p=False)

    if exit_str == 'CRASHED':
        return None, None, False

    energy = atoms.get_total_energy() * 23.06054194532933  #eV to kcal/mol

    return new_structure, energy, success