Esempio n. 1
0
def lattice_sumX(func, positions, elem_cell=np.eye(3), periodic_directions=(False, False, False), cutoff_radius=DF_CUTOFF_RADIUS):
    """Computes the lattice sum of interactions within the (0,0,0) copy
    as well as the interactions to all other copies that lie whithin
    a sphere defined by the "cutoff_radius". Thereby the interaction
    is defined by a distance-vector-dependent function "func"
    """

    # Number of atoms within a single copy
    N_atoms = len(positions)

    # Maximum distance of two atoms within a copy
    # Serves as measure for extension of a single copy
    Rij_max = maxdist(positions)

    # use this below as cutoff radius:
    cutoff = cutoff_radius + Rij_max

    #
    # So far we have to provide the meaningfull (non-linearly dependent) cell vectors
    # also for directions that are not periodic in order to be able to invert the 3x3
    # matrix. FIXME: Is there a better way? Also ASE sets the system in that way?
    #

    # compute the size of the box enclosing a sphere of radius |cutoff|
    # in "fractional" coordinates:
    box = minbox(elem_cell, cutoff)

    # round them in very conservative fashion, all box[i] >= 1,
    box = [ int(ceil(k)) for k in box ]

    #
    # This rounding approach appears to never restrict the summation
    # later to a single cell, rather to at least three at
    #
    #   -1, 0 , +1
    #
    # of the cell vector in each direction.
    #

    # reset the values for non-periodic directions to zero:
    for i in range(len(box)):
        if not periodic_directions[i]:
            # in this direction treat only the unit cell itself:
            box[i] = 0

    #
    # Now we are ready to sum over all cells in the box
    #

    # Initialization of output data by call for the 000 cell
    f = func(np.array([0.,0.,0.]))
    #
    # Running over all residual copies in the box
    u = 0
    v = 0
    # u, v = 0 case. Summation over w, with w != 0
    for w in xrange(-box[2], 0):
        t_vec = u * elem_cell[0] + v * elem_cell[1] + w * elem_cell[2]
        t2 = np.dot(t_vec, t_vec)
        if t2 > cutoff**2: continue
        f  += func(t_vec)
    for w in xrange(1, box[2] + 1):
        t_vec = u * elem_cell[0] + v * elem_cell[1] + w * elem_cell[2]
        t2 = np.dot(t_vec, t_vec)
        if t2 > cutoff**2: continue
        f  += func(t_vec)
    #
    # u = 0 case. Summation over v and w, with v != 0
    for v in xrange(-box[1], 0):
        for w in xrange(-box[2], box[2] + 1):
            t_vec = u * elem_cell[0] + v * elem_cell[1] + w * elem_cell[2]
            t2 = np.dot(t_vec, t_vec)
            if t2 > cutoff**2: continue
            f  += func(t_vec)
        #
        for w in xrange(-box[2], box[2] + 1):
            t_vec = u * elem_cell[0] - v * elem_cell[1] + w * elem_cell[2]
            t2 = np.dot(t_vec, t_vec)
            if t2 > cutoff**2: continue
            f  += func(t_vec)
    #
    # General case. Summation over u, v, and w, with u != 0
    for u in xrange(-box[0], 0):
        for v in xrange(-box[1], box[1] + 1):
            for w in xrange(-box[2], box[2] + 1):
                t_vec = u * elem_cell[0] + v * elem_cell[1] + w * elem_cell[2]
                t2 = np.dot(t_vec, t_vec)
                if t2 > cutoff**2: continue
                f  += func(t_vec)
        #
        for v in xrange(-box[1], box[1] + 1):
            for w in xrange(-box[2], box[2] + 1):
                t_vec = -u * elem_cell[0] + v * elem_cell[1] + w * elem_cell[2]
                t2 = np.dot(t_vec, t_vec)
                if t2 > cutoff**2: continue
                f  += func(t_vec)
    #
    return f
Esempio n. 2
0
def lattice_sum(func, f0, f0_prime, positions, elem_cell=np.eye(3), periodic_directions=(False, False, False), cutoff_radius=DF_CUTOFF_RADIUS):
    """Computes the lattice sum of interactions within the (0,0,0) copy
    as well as the interactions to all other copies that lie whithin
    a sphere defined by the "cutoff_radius". Thereby the interaction
    is defined by a distance-vector-dependent function "func"

        >>> from math import pi
        >>> v, g = lattice_sum(lambda x: (1, 1), [[0., 0., 0.]], periodic_directions=3*(True,))
        >>> v / (4. * pi / 3. * DF_CUTOFF_RADIUS**3)
        1.0056889511012566
    """

    # Number of atoms within a single copy
    N_atoms = len(positions)

    # Maximum distance of two atoms within a copy
    # Serves as measure for extension of a single copy
    Rij_max = maxdist(positions)

    # use this below as cutoff radius:
    cutoff = cutoff_radius / AU_TO_ANG + Rij_max

    #
    # So far we have to provide the meaningfull (non-linearly dependent) cell vectors
    # also for directions that are not periodic in order to be able to invert the 3x3
    # matrix. FIXME: Is there a better way? Also ASE sets the system in that way?
    #

    # compute the size of the box enclosing a sphere of radius |cutoff|
    # in "fractional" coordinates:
    box = minbox(elem_cell, cutoff)

    # round them in very conservative fashion, all box[i] >= 1,
    box = [ int(ceil(k)) for k in box ]

    #
    # This rounding approach appears to never restrict the summation
    # later to a single cell, rather to at least three at
    #
    #   -1, 0 , +1
    #
    # of the cell vector in each direction.
    #

    # reset the values for non-periodic directions to zero:
    for i in range(len(box)):
        if not periodic_directions[i]:
            # in this direction treat only the unit cell itself:
            box[i] = 0

    #
    f       = f0
    f_prime = f0_prime
    # Now we are ready to sum over all cells in the box
    #
    # scan over indices u, v, w
    for u in xrange(-box[0], box[0] + 1):
        for v in xrange(-box[1], box[1] + 1):
            for w in xrange(-box[2], box[2] + 1):

                # Calculate translation vector to actual copy
                t_vec = u * elem_cell[0] + v * elem_cell[1] + w * elem_cell[2]

                # Calculate the squared distance to copy, cycle if that is too long:
                t2 = np.dot(t_vec, t_vec)
                if t2 > cutoff**2: continue

                # Calculate the dispersion interaction between
                # the considered pair of copies
                f1, fprime1 = func(t_vec)
                #
                # Actualize dispersion correction and contributions to forces
                f       += f1
                f_prime += fprime1
            #
        #
    #
    return f, f_prime