Example #1
0
    def compute_repulsion(self):
        'Compute repulsive interaction'
        # Setup list of atoms to sum over

        atom_coord = []    
        v_widths = []
        params = []
        for sys in self.systems:
            atom_coord.append([crd*constants.a2b for crd in sys.coords])
            params.append([self.rep[typ] for typ in sys.atom_types])
            v_widths.append([v for v in sys.valence_widths])
 
        nsys = len(self.systems)
        self.energy = 0.0
        for s1 in range(nsys):
            for s2 in range(s1+1, nsys):
                r = utils.build_r(atom_coord[s1], atom_coord[s2], self.cell)
                ovp = utils.slater_ovp_mat(r,v_widths[s1],v_widths[s2])
                self.energy += np.dot(params[s1], np.matmul(ovp,params[s2]))

                if self.decompose:
                    self.at_exch = np.zeros((len(atom_coord[s1]), len(atom_coord[s2])))
                    for i in range(len(atom_coord[s1])): 
                        for j in range(len(atom_coord[s2])): 
                            self.at_exch[i,j] = ovp[i,j] * params[s1][i] * params[s2][j] * constants.au2kcalmol


        self.energy *= constants.au2kcalmol
        self.logger.debug("Energy: %7.4f kcal/mol" % self.energy)
        return self.energy
Example #2
0
def nuclear_rep(at_elst, coord1, coord2, ele1, ele2, cell):

    r = constants.a2b * utils.build_r(coord1, coord2, cell)
    r = 1.0 / r

    if self.decompose:
        for i, a in ele1:
            for j, b in ele2:
                at_elst[i, j] = r[i, j] * a * b

    return np.dot(ele1, np.matmul(r, ele2))
Example #3
0
    def mtp_energy(self, stone_convention=False):
        'Convert multipole interactions'

        nsys = len(self.systems)
        self.get_mtp_coefficients(stone_convention)
        # Setup list of atoms to sum over
        atom_coord = []
        atom_ele = []
        atom_nums = []
        alphas = []

        for sys in self.systems:
            atom_coord.append([crd for crd in sys.coords])
            atom_ele.append([ele for ele in sys.elements])
            atom_nums.append(
                [constants.atomic_number[ele] for ele in sys.elements])
            alphas.append(
                [self.exp[ele] * constants.b2a for ele in sys.atom_types])

        elst = 0.0
        elst1 = 0.0
        elst2 = 0.0
        elst3 = 0.0
        # Loop over unique interactions
        for s1 in range(nsys):
            # this is a matrix, natom x 13
            # contains ALL multipoles for sys 1
            mi = self.mtps_cart[s1]
            for s2 in range(s1 + 1, nsys):
                mj = self.mtps_cart[s2]

                # 1. nuclear-nuclear int
                r = constants.a2b * utils.build_r(atom_coord[s1],
                                                  atom_coord[s2], self.cell)
                r = 1.0 / r
                if self.decompose:
                    self.at_elst = np.zeros(
                        (len(atom_nums[s1]), len(atom_nums[s2])))
                    for i, a in enumerate(atom_nums[s1]):
                        for j, b in enumerate(atom_nums[s2]):
                            self.at_elst[i, j] = r[i, j] * a * b

                elst0 = np.dot(atom_nums[s1], np.matmul(r, atom_nums[s2]))

                # 2. nuclear-MTP interaction
                ## TODO: avoid this loop over atoms in sys
                for ele, Z in enumerate(atom_nums[s1]):
                    zm_int = charge_mtp_damped_interaction(
                        atom_coord[s1][ele], atom_coord[s2], alphas[s2],
                        self.cell)
                    i1 = Z * np.einsum('ij,ij->i', zm_int, mj)
                    elst1 += np.sum(i1)
                    if self.decompose:
                        for n, value in enumerate(i1):
                            self.at_elst[ele, n] += value

                for ele, Z in enumerate(atom_nums[s2]):
                    zm_int = charge_mtp_damped_interaction(
                        atom_coord[s2][ele], atom_coord[s1], alphas[s1],
                        self.cell)
                    i1 = Z * np.einsum('ij,ij->i', zm_int, mi)
                    elst2 += np.sum(i1)
                    if self.decompose:
                        for n, value in enumerate(i1):
                            self.at_elst[n, ele] += value

                # 3. MTP-MTP
                for atom1 in range(len(atom_nums[s1])):
                    crdi = atom_coord[s1][atom1]
                    alpha1 = alphas[s1][atom1]
                    mi1 = mi[atom1, :]
                    for atom2 in range(len(atom_nums[s2])):
                        crdj = atom_coord[s2][atom2]
                        alpha2 = alphas[s2][atom2]
                        mj1 = mj[atom2, :]
                        d_int = full_damped_interaction(
                            crdi, crdj, alpha1, alpha2, self.cell)
                        value = np.dot(mi1.T, np.dot(d_int, mj1))
                        elst3 += value
                        if self.decompose:
                            self.at_elst[atom1, atom2] += value

        elst += (elst0 + elst1 + elst2 + elst3)

        self.energy_elst = elst * constants.au2kcalmol
        self.at_elst *= constants.au2kcalmol
        return self.energy_elst
Example #4
0
    def polarization_energy(self, smearing_coeff=None, stone_convention=False):
        """Compute induction energy"""
        nsys = len(self.systems)
        atom_coord = []
        atom_ele = []
        atom_typ = []
        v_widths = []
        atom_alpha_iso = []
        ind_params = []
        induced_dip = []

        self.get_mtp_coefficients(stone_convention=False)

        for sys in self.systems:
            atom_coord.append([crd * constants.a2b for crd in sys.coords])
            atom_ele.append([ele for ele in sys.elements])
            atom_typ.append([typ for typ in sys.atom_types])
            v_widths.append([v for v in sys.valence_widths])
            induced_dip.append(np.zeros((len(sys.elements), 3)))

            # Atomic polarizabilities
            atom_alpha_iso.append([
                alpha for alpha in
                Polarizability(self.name, self.logger, self.scs_cutoff,
                               self.pol_exponent, sys).get_pol_scaled()
            ])
            ind_params.append([self.ind_sr[i] for i in sys.atom_types])

        # Compute the short-range correction
        # This is done with U_aU_b*S(a,b,r),
        # which is the same formalism as exchange
        self.energy_shortranged = 0.0
        start_sr = time.time()
        for s1 in range(nsys):
            for s2 in range(s1 + 1, nsys):
                r = utils.build_r(atom_coord[s1], atom_coord[s2], self.cell)
                r1 = utils.build_r(atom_coord[s1], atom_coord[s1], self.cell)
                r2 = utils.build_r(atom_coord[s2], atom_coord[s2], self.cell)
                ovp = utils.slater_ovp_mat(r, v_widths[s1], v_widths[s2])
                self.energy_shortranged = np.dot(
                    ind_params[s1], np.matmul(ovp, ind_params[s2]))

                u = self.build_u(r, atom_alpha_iso[s1], atom_alpha_iso[s2])
                u_1 = self.build_u(r1, atom_alpha_iso[s1], atom_alpha_iso[s1])
                u_2 = self.build_u(r2, atom_alpha_iso[s2], atom_alpha_iso[s2])

                if self.decompose:
                    self.at_ind = np.zeros(
                        (len(atom_coord[s1]), len(atom_coord[s2])))
                    for i in range(len(atom_coord[s1])):
                        for j in range(len(atom_coord[s2])):
                            self.at_ind[i, j] = -1.0 * ovp[
                                i, j] * ind_params[s1][i] * ind_params[s2][
                                    j] * constants.au2kcalmol

        self.energy_shortranged *= constants.au2kcalmol
        end_sr = time.time()

        ###  Compute the induction term using Thole's formalism
        start_pol = time.time()
        if smearing_coeff != None:
            self.smearing_coeff = smearing_coeff

        # Compute the initial induced dipoles for each atom
        # These correspond to: mu_i = alpha_i * sum_j(T^ij_a M_j)
        # where we damp T using Thole's formalism

        # fix the charges
        for s, sys in enumerate(self.systems):
            for i in range(sys.num_atoms):
                self.mtps_cart[s][i][0] += constants.atomic_number[atom_ele[s]
                                                                   [i]]

        # Gather intramonomer dipole interaction tensors  for later too
        T_dd_1_self = []
        T_dd_2_self = []

        # grab the dipole-dipole part for later
        T_dd_1 = []
        T_dd_2 = []

        for s1 in range(nsys):
            mtp1 = self.mtps_cart[s1]
            for s2 in range(s1 + 1, nsys):
                mtp2 = self.mtps_cart[s2]

                for i, atom in enumerate(atom_ele[s1]):
                    T_1 = self.build_int_tensor(atom_coord[s1][i],
                                                atom_coord[s2], u[i, :],
                                                self.smearing_coeff)
                    induced_dip[s1][i] = np.einsum(
                        "ijk,ki->j", T_1, mtp2.T) * atom_alpha_iso[s1][i]
                    T_dd_1.append(T_1[:, :, 1:4])

                    T_dd_1_self.append(
                        self.build_self_dip_int_tensor(atom_coord[s1][i],
                                                       atom_coord[s1],
                                                       u_1[i, :],
                                                       self.smearing_coeff))

                for i, atom in enumerate(atom_ele[s2]):
                    T_2 = self.build_int_tensor(atom_coord[s2][i],
                                                atom_coord[s1], u[:, i],
                                                self.smearing_coeff)
                    induced_dip[s2][i] = np.einsum(
                        "ijk,ki->j", T_2, mtp1.T) * atom_alpha_iso[s2][i]
                    T_dd_2.append(T_2[:, :, 1:4])

                    T_dd_2_self.append(
                        self.build_self_dip_int_tensor(atom_coord[s2][i],
                                                       atom_coord[s2],
                                                       u_2[i, :],
                                                       self.smearing_coeff))

        end_init = time.time()

        #logger.info("init: %6.3f" % (end_init - start_pol))
        # Self-consistent polarization
        #mu_next = np.copy(induced_dip)
        mu_next = []
        #mu_prev = np.zeros(np.shape(mu_next))
        #mu_prev = np.zeros(np.shape(induced_dip))
        mu_prev = []
        for i in induced_dip:
            mu_next.append(i)
            mu_prev.append(np.zeros(np.shape(i)))

    # diff_init = np.linalg.norm(mu_next-mu_prev)
        diff_init = 0.0
        for n in range(nsys):
            diff_init += np.linalg.norm(mu_next[n] - mu_prev[n])

        counter = 0

        # Compute the induced dipoles
        # We already have the interaction tensors
        diff = diff_init
        while diff > self.conv:
            for s in range(nsys):
                mu_prev[s] = np.copy(mu_next[s])
                mu_next[s] = (1.0 - self.omega) * mu_prev[s]
            for s1 in range(nsys):
                mp_1 = mu_prev[s1]
                for s2 in range(s1 + 1, nsys):
                    mp_2 = mu_prev[s2]

                    tmp = np.einsum("nijk,ki,n->nj", T_dd_1, mp_2.T, atom_alpha_iso[s1]) + \
                          np.einsum("nijk,ki,n->nj", T_dd_1_self, mp_1.T, atom_alpha_iso[s1])
                    mu_next[s1] += (tmp + induced_dip[s1]) * self.omega

                    tmp = np.einsum("nijk,ki,n->nj", T_dd_2, mp_1.T, atom_alpha_iso[s2]) + \
                          np.einsum("nijk,ki,n->nj", T_dd_2_self, mp_2.T, atom_alpha_iso[s2])
                    mu_next[s2] += (tmp + induced_dip[s2]) * self.omega

            counter += 1
            diff = 0.0
            for n in range(nsys):
                diff += np.linalg.norm(mu_next[n] - mu_prev[n])
            if diff > diff_init * 10 or counter > 2000:
                self.logger.info(
                    "Can't converge self-consistent equations. Exiting.")
                #exit(1)
                return False
            if counter % 50 == 0 and self.omega > 0.2:
                self.omega *= 0.8

        #self.induced_dip = np.zeros(np.shape(self.mtps_cart))
        self.induced_dip = []
        for i in self.mtps_cart:
            self.induced_dip.append(np.zeros(np.shape(i)))

#        logger.debug("Converged induced dipoles [debye]:")
        for s in range(nsys):
            #           logger.info("Mol %d" % s)
            for n, vec in enumerate(mu_next[s]):
                self.induced_dip[s][n][1:4] = vec
#                tmp_vec = vec * constants.au2debye
#                logger.info("%9.6f  %9.6f  %9.6f" %(tmp_vec[0],tmp_vec[1],tmp_vec[2]))

        self.energy_polarization = 0.0
        for s1 in range(nsys):
            for s2 in range(s1 + 1, nsys):
                for atom1 in range(len(atom_ele[s1])):
                    crdi = atom_coord[s1][atom1]
                    mi1 = self.mtps_cart[s1][atom1, :]
                    mind_1 = self.induced_dip[s1][atom1, :]
                    for atom2 in range(len(atom_ele[s2])):
                        crdj = atom_coord[s2][atom2]
                        mj2 = self.mtps_cart[s2][atom2, :]
                        mind_2 = self.induced_dip[s2][atom2, :]
                        T = interaction_tensor(crdi, crdj, self.cell)

                        en = np.dot(mind_1.T, np.dot(T, mj2)) + np.dot(
                            mi1.T, np.dot(T, mind_2))
                        self.energy_polarization += en
                        if self.decompose:
                            self.at_ind[
                                atom1,
                                atom2] += en * 0.5 * constants.au2kcalmol

        self.energy_polarization *= 0.5 * constants.au2kcalmol

        end_pol = time.time()
        #logger.debug("Polarization energy: %7.4f kcal/mol" % self.energy_polarization)
        #print("Polarization energy: %7.4f kcal/mol" % self.energy_polarization)
        #print "Short range", self.energy_shortranged
        #print(str(self.sys_comb)[:-1],self.energy_polarization , self.energy_shortranged)
        return self.energy_polarization - self.energy_shortranged