"%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

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

        multipole_to_multipole(dr, L_MAX, M0_l, M_L, Mc0_l, Mc_L)

    # Compute the multipole moment directly
Example #2
0
                      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

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

        multipole_to_multipole(dr, L_MAX, M0_l, M_L, Mc0_l, Mc_L)
Example #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")
Example #4
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')