示例#1
0
def electrostatics_test(b, r=3, r0=None):
    if r0 is None:
        r0 = np.sum(b.get_positions(), axis=0) / len(a)

    k1 = [(1, 1)[i] for i in b.get_pbc()]
    k3 = [(1, 3)[i] for i in b.get_pbc()]

    # Check multipole moments
    mp = MultipoleExpansion(L_MAX, r, k3, r0)
    mp.update(b, b.get_initial_charges())

    # Store moments and field for later
    moments = mp.get_moments()
    M0_l_mp, M_L_mp = moments[1]

    phi_mp, E_mp = mp.get_potential_and_field(b)

    # Check if the field is the derivative of the potential
    b.set_calculator(mp)
    ffd, f0, err = check_forces(b)
    if debug:
        print("Finite differences forces:")
        print(ffd)
        print("Analytical forces:")
        print(f0)
        print("Error:")
        print(err)

    assert err < TOL_FOR

    #    ffd, f0, err = check_field(b, mp)
    #    if debug:
    #        print "Finite differences field:"
    #        print ffd
    #        print "Analytical field:"
    #        print f0
    #        print "Error:"
    #        print err
    #
    #    assert err < TOL_FIELD

    # Next neighbor shell by direct summation
    mp = MultipoleExpansion(L_MAX, r * r * r, k1, r0)
    mp.update(b, b.get_initial_charges())

    phi_dir, E_dir = mp.get_potential_and_field(b)

    # Multipole moments from large cell,
    # transform symmetrically around the origin
    #rep  = [ (r-1)/2 if i else 0 for i in b.get_pbc() ]
    #rep  = [ (-(r-1)/2, (r-1)/2) if i else (0, 0) for i in b.get_pbc() ]
    rep = [(0, (r - 1) / 2)[i] for i in b.get_pbc()]
    rep = [((0, 0), (-(r - 1) / 2, (r - 1) / 2))[i] for i in b.get_pbc()]
    c = b.extended_copy(tuple(rep))

    M0_l, M_L = get_moments(c.get_positions(), c.get_initial_charges(), L_MAX,
                            r0)

    #print M_L
    #print M_L_mp

    err_mom0 = np.max(np.abs(M0_l - M0_l_mp))
    err_mom = np.max(np.abs(M_L - M_L_mp))

    if debug:
        print("error(mom)  = ", err_mom0, err_mom)

    assert err_mom0 < TOL_MOM[r]
    assert err_mom < TOL_MOM[r]

    # Compare fields and potentials obtained by the multipole expansion
    # and from direct summation

    err_phi = np.max(np.abs(phi_mp - phi_dir))
    err_E = np.max(np.abs(E_mp - E_dir))

    if debug:
        print("error(phi)  = ", err_phi)
        print("error(E)    = ", err_E)

    assert err_phi < TOL_PHI[r]
    assert err_E < TOL_E[r]
示例#2
0
    r0c = np.array( [ SX, SY, SZ ] )

    # Random atoms and charges (charges between -1 and 1)
    a = [ ]
    for i in range(8):
        a += [ ase.Atoms(
                "%iH" % NAT,
                positions  = np.random.random([NAT,3])*SX,
                charges    = (2*np.random.random([NAT])-1)*CHARGE,
                cell       = [ SX, SY, SZ ]
                ) ]

    # Compute moments
    M = [ ]
    for i in range(8):
        M += [ get_moments(a[i].get_positions(), a[i].get_initial_charges(), L_MAX, r0) ]

    # Construct a composite atoms object
    # and compute the corresponding multipole
    # expansion
    b = ase.Atoms(cell=[ 2*SX, 2*SY, 2*SZ ])
    Mc0_l, Mc_L = zero_moments(L_MAX)
    for i, ( ca, ( M0_l, M_L ) ) in enumerate(zip(a, M)):
        x = i % 2
        y = (i/2) % 2
        z = (i/4) % 2

        dr = np.array([x*SX, y*SY, z*SZ])

        ca.translate(dr)
        b += ca
示例#3
0
    def _update(self, a, q):
        """
        Compute multipoles, do the transformations, and compute the
        electrostatic potential and field on each atom in a.

        Parameters:
        -----------
        a:   Hotbit Atoms object, or atoms object that implements the transform
             and rotation interface.
        q:   Charges
        """
        self.timer.start("multipole_to_multipole")

        self.r_av = a.get_positions().copy()
        self.q_a = q.copy()

        nat = len(a)
        r = a.get_positions()

        if self.r0_v is None:
            r0_v = np.sum(r, axis=0) / len(a)
        else:
            r0_v = self.r0_v

        T0_l, T_L = get_moments(r, q, self.l_max, r0_v)

        self.M = [(T0_l.copy(), T_L.copy())]
        self.r0 = [r0_v]

        sym_ranges = a.get_symmetry_operation_ranges()
        for (s1, s2), k in zip(sym_ranges, self.k):
            if s2 != np.Inf and k != 1:
                print sym_ranges
                print self.k
                raise ValueError("For non-periodic symmetries the k-value must " "be 1.")
        n1, n2, n3 = n_from_ranges(sym_ranges, self.n1, self.n2)

        # Compute telescoped multipoles
        level = np.ones(3, dtype=int)
        for k in range(np.max(self.k) - 2):
            M0_l = T0_l
            M_L = T_L

            T0_l = np.zeros_like(M0_l)
            T_L = np.zeros_like(M_L)

            if k >= self.k[0] - 2:
                _n1 = [0, 1]
            else:
                _n1 = n1

            if k >= self.k[1] - 2:
                _n2 = [0, 1]
            else:
                _n2 = n2

            if k >= self.k[2] - 2:
                _n3 = [0, 1]
            else:
                _n3 = n3

            r0_v = np.zeros(3, dtype=float)
            n = 0
            # Determine center of gravity
            for x1 in range(*_n1):
                for x2 in range(*_n2):
                    for x3 in range(*_n3):
                        x = np.array([x1, x2, x3])
                        r0_v += a.transform(self.r0[k], x * level)
                        n += 1
            r0_v /= n
            self.r0 += [r0_v]
            # self.r0 += [ self.r0[0] ]

            # Transform multipoles
            for x1 in range(*_n1):
                for x2 in range(*_n2):
                    for x3 in range(*_n3):
                        # Loop over all symmetry operations and compute
                        # telescoped multipoles
                        # FIXME!!! Currently only supports continuous
                        # symmetries, think about discrete/recurrent ones.
                        x = np.array([x1, x2, x3])  # + self.dx

                        # The origin is already okay, skip it
                        # if np.any(np.abs(x) > self._TOL):
                        r1 = a.transform(self.r0[k], x * level)
                        T = a.rotation(x * level)
                        S0_l, S_L = transform_multipole(T, self.l_max, M0_l, M_L)
                        multipole_to_multipole(r1 - self.r0[k], self.l_max, S0_l, S_L, T0_l, T_L)

            self.M += [(T0_l.copy(), T_L.copy())]

            level *= self.n2 - self.n1 + 1

        self.timer.stop("multipole_to_multipole")

        ###

        self.timer.start("multipole_to_local")

        # Compute the local expansion from telescoped multipoles
        L0_l, L_L = zero_moments(self.l_max)
        m1, m2, m3 = n_from_ranges(sym_ranges, self.m1, self.m2)
        Mi = len(self.M) - 1
        for k in range(np.max(self.k) - 1):
            M0_l, M_L = self.M[Mi]

            if k >= self.k[0] - 1:
                _m1 = [0, 1]
            else:
                _m1 = m1

            if k >= self.k[1] - 1:
                _m2 = [0, 1]
            else:
                _m2 = m2

            if k >= self.k[2] - 1:
                _m3 = [0, 1]
            else:
                _m3 = m3

            for x1 in range(*_m1):
                for x2 in range(*_m2):
                    for x3 in range(*_m3):
                        # Loop over all symmetry operations and compute the
                        # local expansion from the telescoped multipoles
                        x = np.array([x1, x2, x3])  # + self.dx

                        # No local expansion in the inner region
                        if np.any(x < self.n1) or np.any(x > self.n2):
                            r1 = a.transform(self.r0[Mi], x * level)
                            T = a.rotation(x * level)
                            S0_l, S_L = transform_multipole(T, self.l_max, M0_l, M_L)
                            multipole_to_local(-r1 + self.r0[Mi], self.l_max, S0_l, S_L, L0_l, L_L)

            level /= self.n2 - self.n1 + 1
            Mi -= 1

        self.L = (L0_l, L_L)

        self.timer.stop("multipole_to_local")

        ###

        self.phi_a = np.zeros(nat, dtype=float)
        self.E_av = np.zeros([nat, 3], dtype=float)

        ###

        self.timer.start("local_to_local")

        for i in a:
            loc0_l, loc_L = local_to_local(i.position - self.r0[0], self.l_max, L0_l, L_L, 1)
            self.phi_a[i.index] = loc0_l[0]
            self.E_av[i.index, :] = [-loc_L[0].real, -loc_L[0].imag, loc0_l[1]]

        self.timer.stop("local_to_local")

        ###

        self.timer.start("near_field")

        # Contribution of neighboring boxes
        for x1 in range(*n1):
            for x2 in range(*n2):
                for x3 in range(*n3):
                    # self-interaction needs to be treated separately
                    if x1 != 0 or x2 != 0 or x3 != 0:
                        x = np.array([x1, x2, x3])

                        # construct a matrix with distances
                        r1 = a.transform(self.r0[0], x)
                        T = a.rotation(x)

                        rT = np.dot(r - self.r0[0], np.transpose(T))

                        dr = r.reshape(nat, 1, 3) - (r1 + rT).reshape(1, nat, 3)
                        abs_dr = np.sqrt(np.sum(dr * dr, axis=2))
                        phi = q / abs_dr
                        E = q.reshape(1, nat, 1) * dr / (abs_dr ** 3).reshape(nat, nat, 1)

                        self.phi_a += np.sum(phi, axis=1)
                        self.E_av += np.sum(E, axis=1)

        # Self-contribution
        dr = r.reshape(nat, 1, 3) - r.reshape(1, nat, 3)
        abs_dr = np.sqrt(np.sum(dr * dr, axis=2))

        # Avoid divide by zero
        abs_dr[diag_indices_from(abs_dr)] = 1.0

        phi = q / abs_dr
        E = q.reshape(1, nat, 1) * dr / (abs_dr ** 3).reshape(nat, nat, 1)

        phi[diag_indices_from(phi)] = 0.0
        E[diag_indices_from(phi)] = 0.0

        self.phi_a += np.sum(phi, axis=1)
        self.E_av += np.sum(E, axis=1)

        # Dipole correction for 3D sum
        s1, s2, s3 = sym_ranges
        if s1[1] == np.Inf and s2[1] == np.Inf and s3[1] == np.Inf:
            Ml0, Mlm = self.M[0]

            dip = np.array([-2 * Mlm[0].real, 2 * Mlm[0].imag, Ml0[1]])
            dip *= 4 * pi / (3 * a.get_volume())

            self.phi_a -= np.dot(r - self.r0[0], dip)
            self.E_av += dip

        self.timer.stop("near_field")
示例#4
0
    # Random atoms and charges (charges between -1 and 1)
    a = []
    for i in range(8):
        a += [
            ase.Atoms("%iH" % NAT,
                      positions=np.random.random([NAT, 3]) * SX,
                      charges=(2 * np.random.random([NAT]) - 1) * CHARGE,
                      cell=[SX, SY, SZ])
        ]

    # Compute moments
    M = []
    for i in range(8):
        M += [
            get_moments(a[i].get_positions(), a[i].get_initial_charges(),
                        L_MAX, r0)
        ]

    # Construct a composite atoms object
    # and compute the corresponding multipole
    # expansion
    b = ase.Atoms(cell=[2 * SX, 2 * SY, 2 * SZ])
    Mc0_l, Mc_L = zero_moments(L_MAX)
    for i, (ca, (M0_l, M_L)) in enumerate(zip(a, M)):
        x = i % 2
        y = (i / 2) % 2
        z = (i / 4) % 2

        dr = np.array([x * SX, y * SY, z * SZ])

        ca.translate(dr)
示例#5
0
    def _update(self, a, q):
        """
        Compute multipoles, do the transformations, and compute the
        electrostatic potential and field on each atom in a.

        Parameters:
        -----------
        a:   Hotbit Atoms object, or atoms object that implements the transform
             and rotation interface.
        q:   Charges
        """
        self.timer.start('multipole_to_multipole')

        self.r_av = a.get_positions().copy()
        self.q_a = q.copy()

        nat = len(a)
        r = a.get_positions()

        if self.r0_v is None:
            r0_v = np.sum(r, axis=0) / len(a)
        else:
            r0_v = self.r0_v

        T0_l, T_L = get_moments(r, q, self.l_max, r0_v)

        self.M = [(T0_l.copy(), T_L.copy())]
        self.r0 = [r0_v]

        sym_ranges = a.get_symmetry_operation_ranges()
        for (s1, s2), k in zip(sym_ranges, self.k):
            if s2 != np.Inf and k != 1:
                print(sym_ranges)
                print(self.k)
                raise ValueError(
                    'For non-periodic symmetries the k-value must '
                    'be 1.')
        n1, n2, n3 = n_from_ranges(sym_ranges, self.n1, self.n2)

        # Compute telescoped multipoles
        level = np.ones(3, dtype=int)
        for k in range(np.max(self.k) - 2):
            M0_l = T0_l
            M_L = T_L

            T0_l = np.zeros_like(M0_l)
            T_L = np.zeros_like(M_L)

            if k >= self.k[0] - 2:
                _n1 = [0, 1]
            else:
                _n1 = n1

            if k >= self.k[1] - 2:
                _n2 = [0, 1]
            else:
                _n2 = n2

            if k >= self.k[2] - 2:
                _n3 = [0, 1]
            else:
                _n3 = n3

            r0_v = np.zeros(3, dtype=float)
            n = 0
            # Determine center of gravity
            for x1 in range(*_n1):
                for x2 in range(*_n2):
                    for x3 in range(*_n3):
                        x = np.array([x1, x2, x3])
                        r0_v += a.transform(self.r0[k], x * level)
                        n += 1
            r0_v /= n
            self.r0 += [r0_v]
            #self.r0 += [ self.r0[0] ]

            # Transform multipoles
            for x1 in range(*_n1):
                for x2 in range(*_n2):
                    for x3 in range(*_n3):
                        # Loop over all symmetry operations and compute
                        # telescoped multipoles
                        # FIXME!!! Currently only supports continuous
                        # symmetries, think about discrete/recurrent ones.
                        x = np.array([x1, x2, x3])  #+ self.dx

                        # The origin is already okay, skip it
                        #if np.any(np.abs(x) > self._TOL):
                        r1 = a.transform(self.r0[k], x * level)
                        T = a.rotation(x * level)
                        S0_l, S_L = transform_multipole(
                            T, self.l_max, M0_l, M_L)
                        multipole_to_multipole(r1 - self.r0[k], self.l_max,
                                               S0_l, S_L, T0_l, T_L)

            self.M += [(T0_l.copy(), T_L.copy())]

            level *= self.n2 - self.n1 + 1

        self.timer.stop('multipole_to_multipole')

        ###

        self.timer.start('multipole_to_local')

        # Compute the local expansion from telescoped multipoles
        L0_l, L_L = zero_moments(self.l_max)
        m1, m2, m3 = n_from_ranges(sym_ranges, self.m1, self.m2)
        Mi = len(self.M) - 1
        for k in range(np.max(self.k) - 1):
            M0_l, M_L = self.M[Mi]

            if k >= self.k[0] - 1:
                _m1 = [0, 1]
            else:
                _m1 = m1

            if k >= self.k[1] - 1:
                _m2 = [0, 1]
            else:
                _m2 = m2

            if k >= self.k[2] - 1:
                _m3 = [0, 1]
            else:
                _m3 = m3

            for x1 in range(*_m1):
                for x2 in range(*_m2):
                    for x3 in range(*_m3):
                        # Loop over all symmetry operations and compute the
                        # local expansion from the telescoped multipoles
                        x = np.array([x1, x2, x3])  #+ self.dx

                        # No local expansion in the inner region
                        if np.any(x < self.n1) or np.any(x > self.n2):
                            r1 = a.transform(self.r0[Mi], x * level)
                            T = a.rotation(x * level)
                            S0_l, S_L = transform_multipole(
                                T, self.l_max, M0_l, M_L)
                            multipole_to_local(-r1 + self.r0[Mi], self.l_max,
                                               S0_l, S_L, L0_l, L_L)

            level //= self.n2 - self.n1 + 1
            Mi -= 1

        self.L = (L0_l, L_L)

        self.timer.stop('multipole_to_local')

        ###

        self.phi_a = np.zeros(nat, dtype=float)
        self.E_av = np.zeros([nat, 3], dtype=float)

        ###

        self.timer.start('local_to_local')

        for i in a:
            loc0_l, loc_L = local_to_local(i.position - self.r0[0], self.l_max,
                                           L0_l, L_L, 1)
            self.phi_a[i.index] = loc0_l[0]
            self.E_av[i.index, :] = [-loc_L[0].real, -loc_L[0].imag, loc0_l[1]]

        self.timer.stop('local_to_local')

        ###

        self.timer.start('near_field')

        # Contribution of neighboring boxes
        for x1 in range(*n1):
            for x2 in range(*n2):
                for x3 in range(*n3):
                    # self-interaction needs to be treated separately
                    if x1 != 0 or x2 != 0 or x3 != 0:
                        x = np.array([x1, x2, x3])

                        # construct a matrix with distances
                        r1 = a.transform(self.r0[0], x)
                        T = a.rotation(x)

                        rT = np.dot(r - self.r0[0], np.transpose(T))

                        dr      = r.reshape(nat, 1, 3) - \
                            (r1+rT).reshape(1, nat, 3)
                        abs_dr = np.sqrt(np.sum(dr * dr, axis=2))
                        phi = q / abs_dr
                        E       = q.reshape(1, nat, 1)*dr/ \
                            (abs_dr**3).reshape(nat, nat, 1)

                        self.phi_a += np.sum(phi, axis=1)
                        self.E_av += np.sum(E, axis=1)

        # Self-contribution
        dr = r.reshape(nat, 1, 3) - r.reshape(1, nat, 3)
        abs_dr = np.sqrt(np.sum(dr * dr, axis=2))

        # Avoid divide by zero
        abs_dr[diag_indices_from(abs_dr)] = 1.0

        phi = q / abs_dr
        E = q.reshape(1, nat, 1) * dr / (abs_dr**3).reshape(nat, nat, 1)

        phi[diag_indices_from(phi)] = 0.0
        E[diag_indices_from(phi)] = 0.0

        self.phi_a += np.sum(phi, axis=1)
        self.E_av += np.sum(E, axis=1)

        # Dipole correction for 3D sum
        s1, s2, s3 = sym_ranges
        if s1[1] == np.Inf and s2[1] == np.Inf and s3[1] == np.Inf:
            Ml0, Mlm = self.M[0]

            dip = np.array([-2 * Mlm[0].real, 2 * Mlm[0].imag, Ml0[1]])
            dip *= 4 * pi / (3 * a.get_volume())

            self.phi_a -= np.dot(r - self.r0[0], dip)
            self.E_av += dip

        self.timer.stop('near_field')
示例#6
0
def electrostatics_test(b, r=3, r0=None):
    if r0 is None:
        r0 = np.sum(b.get_positions(), axis=0)/len(a)

    k1 = [ (1,1)[i] for i in b.get_pbc() ]
    k3 = [ (1,3)[i] for i in b.get_pbc() ] 

    # Check multipole moments
    mp  = MultipoleExpansion(L_MAX, r, k3, r0)
    mp.update(b, b.get_initial_charges())

    # Store moments and field for later
    moments          = mp.get_moments()
    M0_l_mp, M_L_mp  = moments[1]

    phi_mp, E_mp     = mp.get_potential_and_field(b)

    # Check if the field is the derivative of the potential
    b.set_calculator(mp)
    ffd, f0, err = check_forces(b)
    if debug:
        print "Finite differences forces:"
        print ffd
        print "Analytical forces:"
        print f0
        print "Error:"
        print err

    assert err < TOL_FOR

#    ffd, f0, err = check_field(b, mp)
#    if debug:
#        print "Finite differences field:"
#        print ffd
#        print "Analytical field:"
#        print f0
#        print "Error:"
#        print err
#
#    assert err < TOL_FIELD

    # Next neighbor shell by direct summation
    mp  = MultipoleExpansion(L_MAX, r*r*r, k1, r0)
    mp.update(b, b.get_initial_charges())

    phi_dir, E_dir  = mp.get_potential_and_field(b)

    # Multipole moments from large cell,
    # transform symmetrically around the origin
    #rep  = [ (r-1)/2 if i else 0 for i in b.get_pbc() ]
    #rep  = [ (-(r-1)/2, (r-1)/2) if i else (0, 0) for i in b.get_pbc() ]
    rep  = [ (0,(r-1)/2)[i] for i in b.get_pbc() ]
    rep  = [ ((0,0),(-(r-1)/2, (r-1)/2))[i] for i in b.get_pbc() ]
    c    = b.extended_copy(tuple(rep))

    M0_l, M_L  = get_moments(c.get_positions(), c.get_initial_charges(), L_MAX, r0)

    #print M_L
    #print M_L_mp

    err_mom0  = np.max(np.abs(M0_l-M0_l_mp))
    err_mom   = np.max(np.abs(M_L-M_L_mp))

    if debug:
        print "error(mom)  = ", err_mom0, err_mom

    assert err_mom0 < TOL_MOM[r]
    assert err_mom < TOL_MOM[r]

    # Compare fields and potentials obtained by the multipole expansion
    # and from direct summation

    err_phi  = np.max(np.abs(phi_mp-phi_dir))
    err_E    = np.max(np.abs(E_mp-E_dir))

    if debug:
        print "error(phi)  = ", err_phi
        print "error(E)    = ", err_E

    assert err_phi < TOL_PHI[r]
    assert err_E < TOL_E[r]