예제 #1
0
def create_lst_images(freactant, fproduct, nimages=11, mic=False):
    """create images with original LST algorithm without NEB force projection as in IDPP

    Parameters
    ----------
    freactant : path to initial atoms
    fproduct  : path to final atoms
    nimages   : the number of images to be interpolated including two endpoints
    mic       : apply mic or not (PBC)
    """
    from ase.neb import IDPP
    from ase.optimize import BFGS
    from ase.build import minimize_rotation_and_translation

    # create linearly interpolated images
    images = _create_images(freactant, fproduct, nimages)
    neb = NEB(images, remove_rotation_and_translation=True)
    neb.interpolate()

    # refine images with LST algorithm
    d1 = images[0].get_all_distances(mic=mic)
    d2 = images[-1].get_all_distances(mic=mic)
    d = (d2 - d1) / (nimages - 1)
    for i, image in enumerate(images):
        image.set_calculator(IDPP(d1 + i * d, mic=mic))
        qn = BFGS(image)
        qn.run(fmax=0.1)
    # apply optimal translation and rotation
    for i in range(nimages - 1):
        minimize_rotation_and_translation(images[i], images[i + 1])

    return images
예제 #2
0
    def interpolate(self, method='linear', mic=False, apply_constraint=None):
        """Interpolate the positions of the interior images between the
        initial state (image 0) and final state (image -1).

        method: str
            Method by which to interpolate: 'linear' or 'idpp'.
            linear provides a standard straight-line interpolation, while
            idpp uses an image-dependent pair potential.
        mic: bool
            Use the minimum-image convention when interpolating.
        apply_constraint: bool
            Controls if the constraints attached to the images
            are ignored or applied when setting the interpolated positions.
            Default value is None, in this case the resulting constrained
            positions (apply_constraint=True) are compared with unconstrained
            positions (apply_constraint=False),
            if the positions are not the same
            the user is required to specify the desired behaviour
            by setting up apply_constraint keyword argument to False or True.
        """
        if self.remove_rotation_and_translation:
            minimize_rotation_and_translation(self.images[0], self.images[-1])

        interpolate(self.images, mic, apply_constraint=apply_constraint)

        if method == 'idpp':
            idpp_interpolate(images=self, traj=None, log=None, mic=mic)
예제 #3
0
파일: neb.py 프로젝트: yfyh2013/ase
    def interpolate(self, method='linear', mic=False):
        if self.remove_rotation_and_translation:
            minimize_rotation_and_translation(self.images[0], self.images[-1])

        interpolate(self.images, mic)

        if method == 'idpp':
            self.idpp_interpolate(traj=None, log=None, mic=mic)
예제 #4
0
파일: neb.py 프로젝트: rosswhitfield/ase
    def interpolate(self, method='linear', mic=False):
        if self.remove_rotation_and_translation:
            minimize_rotation_and_translation(self.images[0], self.images[-1])

        interpolate(self.images, mic)

        if method == 'idpp':
            self.idpp_interpolate(traj=None, log=None, mic=mic)
예제 #5
0
    def interpolate(self, method='linear', mic=False):
        """Interpolate the positions of the interior images between the
        initial state (image 0) and final state (image -1).

        method: str
            Method by which to interpolate: 'linear' or 'idpp'.
            linear provides a standard straight-line interpolation, while
            idpp uses an image-dependent pair potential.
        mic: bool
            Use the minimum-image convention when interpolating.
        """
        if self.remove_rotation_and_translation:
            minimize_rotation_and_translation(self.images[0], self.images[-1])

        interpolate(self.images, mic)

        if method == 'idpp':
            idpp_interpolate(images=self, traj=None, log=None, mic=mic)
예제 #6
0
 def get_terminations(self, slab, layers):
     '''
     determine how many terminations for a surface index
     for a slab with SrO, TiO2, SrO, TiO2 ... ordering, there will be two terminations
     'SrO' and 'TiO2' 
     Search from the top to the bottom
     '''
     from ase.build import minimize_rotation_and_translation
     from ase.calculators.calculator import compare_atoms, equal
     import pprint
     nlayer = max(layers) + 1
     terminations = {}
     natoms = len(slab)
     for i in range(nlayer - 2, 0, -1):
         atoms = slab[layers == i]
         formula = atoms.get_chemical_formula(mode='hill')
         print(formula)
         inds = [
             j for j in range(natoms)
             if layers[j] in range(i, i - self.nlayer, -1)
         ]
         atoms = slab[inds]
         atoms.wrap()
         if not terminations:
             terminations['%s-%s' % (formula, i)] = atoms.copy()
         else:
             flag = True
             atoms1 = atoms.copy()
             for ter, atoms2 in terminations.items():
                 if len(atoms1) != len(atoms2):
                     flag = False
                 else:
                     minimize_rotation_and_translation(atoms2, atoms1)
                     if equal(atoms1.arrays, atoms2.arrays, tol=0.1):
                         flag = False
             if flag:
                 terminations['%s-%s' % (formula, i)] = atoms.copy()
                 print(formula, ter)
                 # view([atoms1, atoms2])
     pprint.pprint(terminations)
     # view(terminations.values())
     return terminations
예제 #7
0
파일: neb.py 프로젝트: yfyh2013/ase
    def get_forces(self):
        """Evaluate and return the forces."""
        images = self.images

        calculators = [image.calc for image in images
                       if image.calc is not None]
        if len(set(calculators)) != len(calculators):
            msg = ('One or more NEB images share the same calculator.  '
                   'Each image must have its own calculator.  '
                   'You may wish to use the ase.neb.SingleCalculatorNEB '
                   'class instead, although using separate calculators '
                   'is recommended.')
            raise ValueError(msg)

        forces = np.empty(((self.nimages - 2), self.natoms, 3))
        energies = np.empty(self.nimages)

        if self.remove_rotation_and_translation:
            # Remove translation and rotation between
            # images before computing forces:
            for i in range(1, self.nimages):
                minimize_rotation_and_translation(images[i - 1], images[i])

        if self.method != 'aseneb':
            energies[0] = images[0].get_potential_energy()
            energies[-1] = images[-1].get_potential_energy()

        if not self.parallel:
            # Do all images - one at a time:
            for i in range(1, self.nimages - 1):
                energies[i] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
        elif self.world.size == 1:
            def run(image, energies, forces):
                energies[:] = image.get_potential_energy()
                forces[:] = image.get_forces()
            threads = [threading.Thread(target=run,
                                        args=(images[i],
                                              energies[i:i + 1],
                                              forces[i - 1:i]))
                       for i in range(1, self.nimages - 1)]
            for thread in threads:
                thread.start()
            for thread in threads:
                thread.join()
        else:
            # Parallelize over images:
            i = self.world.rank * (self.nimages - 2) // self.world.size + 1
            try:
                energies[i] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
            except:
                # Make sure other images also fail:
                error = self.world.sum(1.0)
                raise
            else:
                error = self.world.sum(0.0)
                if error:
                    raise RuntimeError('Parallel NEB failed!')

            for i in range(1, self.nimages - 1):
                root = (i - 1) * self.world.size // (self.nimages - 2)
                self.world.broadcast(energies[i:i + 1], root)
                self.world.broadcast(forces[i - 1], root)

        imax = 1 + np.argsort(energies[1:-1])[-1]
        self.emax = energies[imax]

        t1 = find_mic(images[1].get_positions() -
                      images[0].get_positions(),
                      images[0].get_cell(), images[0].pbc)[0]

        if self.method == 'eb':
            beeline = (images[self.nimages - 1].get_positions() -
                       images[0].get_positions())
            beelinelength = np.linalg.norm(beeline)
            eqlength = beelinelength / (self.nimages - 1)

        nt1 = np.linalg.norm(t1)

        for i in range(1, self.nimages - 1):
            t2 = find_mic(images[i + 1].get_positions() -
                          images[i].get_positions(),
                          images[i].get_cell(), images[i].pbc)[0]
            nt2 = np.linalg.norm(t2)

            if self.method == 'eb':
                # Tangents are bisections of spring-directions
                # (formula C8 of paper III)
                tangent = t1 / nt1 + t2 / nt2
                # Normalize the tangent vector
                tangent /= np.linalg.norm(tangent)
            elif self.method == 'improvedtangent':
                # Tangents are improved according to formulas 8, 9, 10,
                # and 11 of paper I.
                if energies[i + 1] > energies[i] > energies[i - 1]:
                    tangent = t2.copy()
                elif energies[i + 1] < energies[i] < energies[i - 1]:
                    tangent = t1.copy()
                else:
                    deltavmax = max(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    deltavmin = min(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    if energies[i + 1] > energies[i - 1]:
                        tangent = t2 * deltavmax + t1 * deltavmin
                    else:
                        tangent = t2 * deltavmin + t1 * deltavmax
                # Normalize the tangent vector
                tangent /= np.linalg.norm(tangent)
            else:
                if i < imax:
                    tangent = t2
                elif i > imax:
                    tangent = t1
                else:
                    tangent = t1 + t2
                tt = np.vdot(tangent, tangent)

            f = forces[i - 1]
            ft = np.vdot(f, tangent)

            if i == imax and self.climb:
                # imax not affected by the spring forces. The full force
                # with component along the elestic band converted
                # (formula 5 of Paper II)
                if self.method == 'aseneb':
                    f -= 2 * ft / tt * tangent
                else:
                    f -= 2 * ft * tangent
            elif self.method == 'eb':
                f -= ft * tangent
                # Spring forces
                # (formula C1, C5, C6 and C7 of Paper III)
                f1 = -(nt1 - eqlength) * t1 / nt1 * self.k[i - 1]
                f2 = (nt2 - eqlength) * t2 / nt2 * self.k[i]
                if self.climb and abs(i - imax) == 1:
                    deltavmax = max(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    deltavmin = min(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    f += (f1 + f2) * deltavmin / deltavmax
                else:
                    f += f1 + f2
            elif self.method == 'improvedtangent':
                f -= ft * tangent
                # Improved parallel spring force (formula 12 of paper I)
                f += (nt2 * self.k[i] - nt1 * self.k[i - 1]) * tangent
            else:
                f -= ft / tt * tangent
                f -= np.vdot(t1 * self.k[i - 1] -
                             t2 * self.k[i], tangent) / tt * tangent

            t1 = t2
            nt1 = nt2

        return forces.reshape((-1, 3))
예제 #8
0
    def get_forces(self):
        """Evaluate and return the forces."""
        images = self.images

        if not self.allow_shared_calculator:
            calculators = [
                image.calc for image in images if image.calc is not None
            ]
            if len(set(calculators)) != len(calculators):
                msg = ('One or more NEB images share the same calculator.  '
                       'Each image must have its own calculator.  '
                       'You may wish to use the ase.neb.SingleCalculatorNEB '
                       'class instead, although using separate calculators '
                       'is recommended.')
                raise ValueError(msg)

        forces = np.empty(((self.nimages - 2), self.natoms, 3))
        energies = np.empty(self.nimages)

        if self.remove_rotation_and_translation:
            for i in range(1, self.nimages):
                minimize_rotation_and_translation(images[i - 1], images[i])

        if self.method != 'aseneb':
            energies[0] = images[0].get_potential_energy()
            energies[-1] = images[-1].get_potential_energy()

        if not self.parallel:
            # Do all images - one at a time:
            for i in range(1, self.nimages - 1):
                energies[i] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
        elif self.world.size == 1:

            def run(image, energies, forces):
                energies[:] = image.get_potential_energy()
                forces[:] = image.get_forces()

            threads = [
                threading.Thread(target=run,
                                 args=(images[i], energies[i:i + 1],
                                       forces[i - 1:i]))
                for i in range(1, self.nimages - 1)
            ]
            for thread in threads:
                thread.start()
            for thread in threads:
                thread.join()
        else:
            # Parallelize over images:
            i = self.world.rank * (self.nimages - 2) // self.world.size + 1
            try:
                energies[i] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
            except Exception:
                # Make sure other images also fail:
                error = self.world.sum(1.0)
                raise
            else:
                error = self.world.sum(0.0)
                if error:
                    raise RuntimeError('Parallel NEB failed!')

            for i in range(1, self.nimages - 1):
                root = (i - 1) * self.world.size // (self.nimages - 2)
                self.world.broadcast(energies[i:i + 1], root)
                self.world.broadcast(forces[i - 1], root)

        # Save for later use in iterimages:
        self.energies = energies
        self.real_forces = np.zeros((self.nimages, self.natoms, 3))
        self.real_forces[1:-1] = forces

        state = NEBState(self, images, energies)

        # Can we get rid of self.energies, self.imax, self.emax etc.?
        self.imax = state.imax
        self.emax = state.emax

        spring1 = state.spring(0)

        for i in range(1, self.nimages - 1):
            spring2 = state.spring(i)
            tangent = self.neb_method.get_tangent(state, spring1, spring2, i)

            imgforce = forces[i - 1]
            # Get overlap between PES-derived force and tangent
            tangential_force = np.vdot(imgforce, tangent)

            if i == self.imax and self.climb:
                """The climbing image, imax, is not affected by the spring
                   forces. This image feels the full PES-derived force,
                   but the tangential component is inverted:
                   see Eq. 5 in paper II."""
                if self.method == 'aseneb':
                    tangent_mag = np.vdot(tangent, tangent)  # For normalizing
                    imgforce -= 2 * tangential_force / tangent_mag * tangent
                else:
                    imgforce -= 2 * tangential_force * tangent
            else:
                self.neb_method.add_image_force(state, tangential_force,
                                                tangent, imgforce, spring1,
                                                spring2, i)

            spring1 = spring2
        return forces.reshape((-1, 3))
예제 #9
0
    def get_forces(self):
        """Evaluate and return the forces."""

        print()
        print("Enter MyNEB: get_forces()")
        print()

        images = self.images

        calculators = [
            image.calc for image in images if image.calc is not None
        ]
        if len(set(calculators)) != len(calculators):
            msg = ('One or more NEB images share the same calculator.  '
                   'Each image must have its own calculator.  '
                   'You may wish to use the ase.neb.SingleCalculatorNEB '
                   'class instead, although using separate calculators '
                   'is recommended.')
            raise ValueError(msg)

        forces = np.empty(((self.nimages - 2), self.natoms, 3))
        energies = np.empty(self.nimages)

        if self.remove_rotation_and_translation:
            # Remove translation and rotation between
            # images before computing forces:
            for i in range(1, self.nimages):
                minimize_rotation_and_translation(images[i - 1], images[i])

        if self.method != 'aseneb':
            energies[0] = images[0].get_potential_energy()
            energies[-1] = images[-1].get_potential_energy()

        # Do all images - one at a time:
        for i in range(1, self.nimages - 1):
            energies[i] = images[i].get_potential_energy()
            forces[i - 1] = images[i].get_forces()

        # Save for later use in iterimages:
        self.energies = energies
        self.real_forces = np.zeros((self.nimages, self.natoms, 3))
        self.real_forces[1:-1] = forces

        self.imax = 1 + np.argsort(energies[1:-1])[-1]
        self.emax = energies[self.imax]

        print("imax = ", self.imax + 1)
        print("emax = ", self.emax)

        t1 = find_mic(images[1].get_positions() - images[0].get_positions(),
                      images[0].get_cell(), images[0].pbc)[0]

        if self.method == 'eb':
            beeline = (images[self.nimages - 1].get_positions() -
                       images[0].get_positions())
            beelinelength = np.linalg.norm(beeline)
            eqlength = beelinelength / (self.nimages - 1)

        nt1 = np.linalg.norm(t1)

        for i in range(1, self.nimages - 1):
            t2 = find_mic(
                images[i + 1].get_positions() - images[i].get_positions(),
                images[i].get_cell(), images[i].pbc)[0]
            nt2 = np.linalg.norm(t2)

            if self.method == 'eb':
                # Tangents are bisections of spring-directions
                # (formula C8 of paper III)
                tangent = t1 / nt1 + t2 / nt2
                # Normalize the tangent vector
                tangent /= np.linalg.norm(tangent)
            elif self.method == 'improvedtangent':
                # Tangents are improved according to formulas 8, 9, 10,
                # and 11 of paper I.
                if energies[i + 1] > energies[i] > energies[i - 1]:
                    tangent = t2.copy()
                elif energies[i + 1] < energies[i] < energies[i - 1]:
                    tangent = t1.copy()
                else:
                    deltavmax = max(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    deltavmin = min(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    if energies[i + 1] > energies[i - 1]:
                        tangent = t2 * deltavmax + t1 * deltavmin
                    else:
                        tangent = t2 * deltavmin + t1 * deltavmax
                # Normalize the tangent vector
                tangent /= np.linalg.norm(tangent)
            else:
                if i < self.imax:
                    tangent = t2
                elif i > self.imax:
                    tangent = t1
                else:
                    tangent = t1 + t2
                tt = np.vdot(tangent, tangent)

            f = forces[i - 1]
            ft = np.vdot(f, tangent)

            if i == self.imax and self.climb:
                # imax not affected by the spring forces. The full force
                # with component along the elestic band converted
                # (formula 5 of Paper II)
                if self.method == 'aseneb':
                    f -= 2 * ft / tt * tangent
                else:
                    f -= 2 * ft * tangent
            elif self.method == 'eb':
                f -= ft * tangent
                # Spring forces
                # (formula C1, C5, C6 and C7 of Paper III)
                f1 = -(nt1 - eqlength) * t1 / nt1 * self.k[i - 1]
                f2 = (nt2 - eqlength) * t2 / nt2 * self.k[i]
                if self.climb and abs(i - self.imax) == 1:
                    deltavmax = max(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    deltavmin = min(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    f += (f1 + f2) * deltavmin / deltavmax
                else:
                    f += f1 + f2
            elif self.method == 'improvedtangent':
                f -= ft * tangent
                # Improved parallel spring force (formula 12 of paper I)
                f += (nt2 * self.k[i] - nt1 * self.k[i - 1]) * tangent
            else:
                f -= ft / tt * tangent
                f -= np.vdot(t1 * self.k[i - 1] - t2 * self.k[i],
                             tangent) / tt * tangent

            tx = tangent[0, 0]
            ty = tangent[0, 1]
            fx = forces[i - 1][0, 0]
            fy = forces[i - 1][0, 1]
            print("image = %3d tangent = %18.10f %18.10f" % ((i + 1), tx, ty))
            print("            forces  = %18.10f %18.10f" % (fx, fy))

            t1 = t2
            nt1 = nt2

            if self.dynamic_relaxation:
                n = self.natoms
                k = i - 1
                n1 = n * k
                n2 = n1 + n
                force_i = np.sqrt((forces.reshape(
                    (-1, 3))[n1:n2]**2.).sum(axis=1)).max()

                n1_imax = (self.imax - 1) * n
                positions = self.get_positions()
                pos_imax = positions[n1_imax:n1_imax + n]
                rel_pos = np.sqrt(((positions[n1:n2] - pos_imax)**2).sum())

                if force_i < self.fmax * (1 + rel_pos * self.scale_fmax):
                    if k == self.imax - 1:
                        pass
                    else:
                        forces[k, :, :] = np.zeros((1, self.natoms, 3))
        print()
        print("MyNEB: end of get_forces()")
        print()
        return forces.reshape((-1, 3))
예제 #10
0
    def get_forces(self):
        """Evaluate and return the forces."""
        images = self.images
        forces = np.empty(((self.nimages - 2), self.natoms, 3))
        energies = np.empty(self.nimages - 2)

        if self.remove_rotation_and_translation:
            # Remove translation and rotation between
            # images before computing forces:
            for i in range(1, self.nimages):
                minimize_rotation_and_translation(images[i - 1], images[i])

        if not self.parallel:
            # Do all images - one at a time:
            for i in range(1, self.nimages - 1):
                energies[i - 1] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
        elif self.world.size == 1:
            def run(image, energies, forces):
                energies[:] = image.get_potential_energy()
                forces[:] = image.get_forces()
            threads = [threading.Thread(target=run,
                                        args=(images[i],
                                              energies[i - 1:i],
                                              forces[i - 1:i]))
                       for i in range(1, self.nimages - 1)]
            for thread in threads:
                thread.start()
            for thread in threads:
                thread.join()
        else:
            # Parallelize over images:
            i = self.world.rank * (self.nimages - 2) // self.world.size + 1
            try:
                energies[i - 1] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
            except:
                # Make sure other images also fail:
                error = self.world.sum(1.0)
                raise
            else:
                error = self.world.sum(0.0)
                if error:
                    raise RuntimeError('Parallel NEB failed!')

            for i in range(1, self.nimages - 1):
                root = (i - 1) * self.world.size // (self.nimages - 2)
                self.world.broadcast(energies[i - 1:i], root)
                self.world.broadcast(forces[i - 1], root)

        imax = 1 + np.argsort(energies)[-1]
        self.emax = energies[imax - 1]

        tangent1 = find_mic(images[1].get_positions() -
                            images[0].get_positions(),
                            images[0].get_cell(), images[0].pbc)[0]
        for i in range(1, self.nimages - 1):
            tangent2 = find_mic(images[i + 1].get_positions() -
                                images[i].get_positions(),
                                images[i].get_cell(),
                                images[i].pbc)[0]
            if i < imax:
                tangent = tangent2
            elif i > imax:
                tangent = tangent1
            else:
                tangent = tangent1 + tangent2

            tt = np.vdot(tangent, tangent)
            f = forces[i - 1]
            ft = np.vdot(f, tangent)
            if i == imax and self.climb:
                f -= 2 * ft / tt * tangent
            else:
                f -= ft / tt * tangent
                f -= np.vdot(tangent1 * self.k[i - 1] -
                             tangent2 * self.k[i], tangent) / tt * tangent

            tangent1 = tangent2

        return forces.reshape((-1, 3))
예제 #11
0
def interp_PES(images, flatten=True):
    """Interpolate between an series of images.

    The images should be specified by a dictionary, with keys,

    file

        The name of the file to read the image from.

    format

        The format of the file for the image, according to the requirement of
        the ``read`` function in the ASE IO module.

    atoms

        The ASE atoms object for the image point.  If it is present, the file
        will not be attempted to be read.

    dupl

        The number of duplication of the image.

    interp

        The dictionary of arguments to the function
        :py:func:`interp_by_multiscale_neb`.  For each of the images, it gives
        the method of interpolation between the current image and its
        successor.  No interpolation is performed in the absence of it.

    reorient

        If the image point should be reorientated with respect to the previous
        image point.

    """

    FILE = 'file'
    FORMAT = 'format'
    ATOMS = 'atoms'
    DUPL = 'dupl'
    INTERP = 'interp'
    REORIENT = 'reorient'

    # To hold the duplication of each image as well as the path to its
    # successor.
    FRAMES = '_frames'

    # Read the images if it is needed.
    imgs = []
    for i in images:
        img = dict(i)
        if ATOMS in i:
            atoms = i[ATOMS]
        else:
            if FORMAT not in i:
                atoms = read(i[FILE])
            else:
                atoms = read(i[FILE], format=i[FORMAT])
        if REORIENT in i:
            minimize_rotation_and_translation(imgs[-1][ATOMS], atoms)
        img[ATOMS] = atoms
        img[FRAMES] = [atoms, ]
        imgs.append(img)
        continue

    # Make duplication for the images.
    for i in imgs:
        if DUPL in i:
            i[FRAMES].extend(aug_point(i[ATOMS], i[DUPL]))
        continue

    # Interpolate between the stationary points.
    for curr, succ in zip(imgs, imgs[1:]):
        if INTERP not in curr:
            continue
        else:
            curr[FRAMES].extend(interp_by_multiscale_neb(
                curr[ATOMS], succ[ATOMS], **curr[INTERP]
            ))

    frame_sets = (i[FRAMES] for i in imgs)
    if flatten:
        return list(itertools.chain.from_iterable(frame_sets))
    else:
        return list(frame_sets)
예제 #12
0
파일: neb.py 프로젝트: rosswhitfield/ase
    def get_forces(self):
        """Evaluate and return the forces."""
        images = self.images
        forces = np.empty(((self.nimages - 2), self.natoms, 3))
        energies = np.empty(self.nimages)
        
        if self.remove_rotation_and_translation:
            # Remove translation and rotation between
            # images before computing forces:
            for i in range(1, self.nimages):
                minimize_rotation_and_translation(images[i - 1], images[i])

        if self.method != 'aseneb':
            energies[0] = images[0].get_potential_energy()
            energies[-1] = images[-1].get_potential_energy()

        if not self.parallel:
            # Do all images - one at a time:
            for i in range(1, self.nimages - 1):
                energies[i] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
        elif self.world.size == 1:
            def run(image, energies, forces):
                energies[:] = image.get_potential_energy()
                forces[:] = image.get_forces()
            threads = [threading.Thread(target=run,
                                        args=(images[i],
                                              energies[i:i + 1],
                                              forces[i - 1:i]))
                       for i in range(1, self.nimages - 1)]
            for thread in threads:
                thread.start()
            for thread in threads:
                thread.join()
        else:
            # Parallelize over images:
            i = self.world.rank * (self.nimages - 2) // self.world.size + 1
            try:
                energies[i] = images[i].get_potential_energy()
                forces[i - 1] = images[i].get_forces()
            except:
                # Make sure other images also fail:
                error = self.world.sum(1.0)
                raise
            else:
                error = self.world.sum(0.0)
                if error:
                    raise RuntimeError('Parallel NEB failed!')

            for i in range(1, self.nimages - 1):
                root = (i - 1) * self.world.size // (self.nimages - 2)
                self.world.broadcast(energies[i:i + 1], root)
                self.world.broadcast(forces[i - 1], root)

        imax = 1 + np.argsort(energies[1:-1])[-1]
        self.emax = energies[imax]

        t1 = find_mic(images[1].get_positions() -
                      images[0].get_positions(),
                      images[0].get_cell(), images[0].pbc)[0]

        if self.method == 'eb':
            beeline = (images[self.nimages - 1].get_positions() -
                       images[0].get_positions())
            beelinelength = np.linalg.norm(beeline)
            eqlength = beelinelength / (self.nimages - 1)

        nt1 = np.linalg.norm(t1)

        for i in range(1, self.nimages - 1):
            t2 = find_mic(images[i + 1].get_positions() -
                          images[i].get_positions(),
                          images[i].get_cell(), images[i].pbc)[0]
            nt2 = np.linalg.norm(t2)

            if self.method == 'eb':
                # Tangents are bisections of spring-directions
                # (formula C8 of paper III)
                tangent = t1 / nt1 + t2 / nt2
                # Normalize the tangent vector
                tangent /= np.linalg.norm(tangent)
            elif self.method == 'improvedtangent':
                # Tangents are improved according to formulas 8, 9, 10,
                # and 11 of paper I.
                if energies[i + 1] > energies[i] > energies[i - 1]:
                    tangent = t2.copy()
                elif energies[i + 1] < energies[i] < energies[i - 1]:
                    tangent = t1.copy()
                else:
                    deltavmax = max(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    deltavmin = min(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    if energies[i + 1] > energies[i - 1]:
                        tangent = t2 * deltavmax + t1 * deltavmin
                    else:
                        tangent = t2 * deltavmin + t1 * deltavmax
                # Normalize the tangent vector
                tangent /= np.linalg.norm(tangent)
            else:
                if i < imax:
                    tangent = t2
                elif i > imax:
                    tangent = t1
                else:
                    tangent = t1 + t2
                tt = np.vdot(tangent, tangent)

            f = forces[i - 1]
            ft = np.vdot(f, tangent)
            
            if i == imax and self.climb:
                # imax not affected by the spring forces. The full force
                # with component along the elestic band converted
                # (formula 5 of Paper II)
                if self.method == 'aseneb':
                    f -= 2 * ft / tt * tangent
                else:
                    f -= 2 * ft * tangent
            elif self.method == 'eb':
                f -= ft * tangent
                # Spring forces
                # (formula C1, C5, C6 and C7 of Paper III)
                f1 = -(nt1 - eqlength) * t1 / nt1 * self.k[i - 1]
                f2 = (nt2 - eqlength) * t2 / nt2 * self.k[i]
                if self.climb and abs(i - imax) == 1:
                    deltavmax = max(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    deltavmin = min(abs(energies[i + 1] - energies[i]),
                                    abs(energies[i - 1] - energies[i]))
                    f += (f1 + f2) * deltavmin / deltavmax
                else:
                    f += f1 + f2
            elif self.method == 'improvedtangent':
                f -= ft * tangent
                # Improved parallel spring force (formula 12 of paper I)
                f += (nt2 * self.k[i] - nt1 * self.k[i - 1]) * tangent
            else:
                f -= ft / tt * tangent
                f -= np.vdot(t1 * self.k[i - 1] -
                             t2 * self.k[i], tangent) / tt * tangent

            t1 = t2
            nt1 = nt2

        return forces.reshape((-1, 3))