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
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))
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
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