void Points::GenerateKernel(int L, int point_count, std::string title) { int g=point_count, m=0; LEGENDRE P_lm; LEGENDRE Y_P; LEGENDRE dP_lm; std::vector<std::vector<double> > d_kern(g); //kernel for derivative reconstruction std::vector<std::vector<double> > f_kern(g); //kernel for function (test) reconstruction std::vector<std::vector<double> > WT(g); std::complex<double> Y(0,0), Ylm(0,0), dYlm(0,0), Ymp1(0,0), ej(0,0), function(0,0), derivative(0,0); std::complex<double> im(0,1); double th1=0, ph1=0, sign=0; std::cout << title << std::endl; std::ofstream kernel(title); kernel.precision(15); for(int i=0; i<g; i++) { d_kern[i].resize(g); f_kern[i].resize(g); WT[i].resize(g); for(int j=0; j<g; j++) { for(double l=0; l<=L; l++) { for(double m_it=0; m_it<=(2*l); m_it++) { m=0; m = l-m_it; //std::cout << "m = " << m << ", l = " << l << std::endl; ej = m; sign=pow(-1.0,m_it); std::complex<double> exponential_prime(cos( Points::Phi[i]), (-1)*sin(Points::Phi[i])); std::complex<double> exponential(cos(m*Points::Phi[j]), sin(m*Points::Phi[j])); Ylm = P_lm.Yml(m, l, Points::Theta[i], Points::Phi[i]); Y = Y_P.Yml(m, l, Points::Theta[j], Points::Phi[j]); if( Theta[i] != 0 && ((m+1)<=l) ) { Ymp1 = m * (1.0/tan(Points::Theta[i])) * dP_lm.Yml(m, l, Points::Theta[i], Points::Phi[i]) + sqrt( (l-m)*(l+m+1) ) * exponential_prime * dP_lm.Yml(m+1, l, Points::Theta[i], Points::Phi[i]); } ///fill arrays with f=Y*Y for the function kernel and derivative kernel f_kern[i][j] += (conj(Y)*Ylm).real();//Y_real*Y_prime_real; d_kern[i][j] += (conj(Y)*Ymp1).real(); } } ///absorb weights into kernel WT[i][j] = Points::Weight[j]*4.0*PI; kernel << d_kern[i][j]*Points::Weight[j]*4.0*PI << " " << f_kern[i][j]*Points::Weight[j]*4.0*PI << " " << WT[i][j] << std::endl; } } kernel.close(); }
void SaLSA::dumpDensities(const char* filenamePattern) const { PCM::dumpDensities(filenamePattern); //Dump effective site densities: double chiRot = 0., chiPol = 0.; for(const auto& c: fsp.components) { chiRot += c->Nbulk * c->molecule.getDipole().length_squared()/(3.*fsp.T); chiPol += c->Nbulk * c->molecule.getAlphaTot(); } double sqrtCrot = (epsBulk>epsInf && chiRot) ? sqrt((epsBulk-epsInf)/(4.*M_PI*chiRot)) : 1.; const double bessel_jl_by_Gl_zero[4] = {1., 1./3, 1./15, 1./105}; //G->0 limit of j_l(G)/G^l for(const auto& c: fsp.components) { ScalarFieldTildeArray Ntilde(c->molecule.sites.size()); for(int l=0; l<=fsp.lMax; l++) { double prefac = sqrt(4.*M_PI*c->Nbulk/fsp.T) * (l==1 ? sqrtCrot : 1.); for(int m=-l; m<=+l; m++) { const double dG = 0.02, Gmax = gInfo.GmaxGrid; unsigned nGradial = unsigned(ceil(Gmax/dG))+5; std::vector<double> VtotSamples(nGradial); std::vector< std::vector<double> > VsiteSamples(c->molecule.sites.size(), std::vector<double>(nGradial)); for(unsigned iG=0; iG<nGradial; iG++) { double G = iG*dG; for(unsigned iSite=0; iSite<c->molecule.sites.size(); iSite++) { const auto& site = c->molecule.sites[iSite]; for(const vector3<>& r: site->positions) { double rLength = r.length(); double bessel_jl_by_Gl = G ? bessel_jl(l,G*rLength)/pow(G,l) : bessel_jl_by_Gl_zero[l]*pow(rLength,l); vector3<> rHat = (rLength ? 1./rLength : 0.) * r; double Ylm_rHat = Ylm(l, m, rHat); VtotSamples[iG] += prefac * site->chargeKernel(G) * bessel_jl_by_Gl * Ylm_rHat; VsiteSamples[iSite][iG] += prefac * bessel_jl_by_Gl * Ylm_rHat; } } } RadialFunctionG Vtot; Vtot.init(0, VtotSamples, dG); ScalarFieldTilde temp = lDivergence(J(shape * I(lGradient(Vtot * state, l))), l); Vtot.free(); for(unsigned iSite=0; iSite<c->molecule.sites.size(); iSite++) { RadialFunctionG Vsite; Vsite.init(0, VsiteSamples[iSite], dG); Ntilde[iSite] -= (pow(-1,l) * 4*M_PI/(2*l+1)) * (Vsite * temp); Vsite.free(); } } } char filename[256]; for(unsigned j=0; j<c->molecule.sites.size(); j++) { const Molecule::Site& s = *(c->molecule.sites[j]); ScalarField N; N=I(Ntilde[j])+c->Nbulk*siteShape[j]; ostringstream oss; oss << "N_" << c->molecule.name; if(c->molecule.sites.size()>1) oss << "_" << s.name; sprintf(filename, filenamePattern, oss.str().c_str()); logPrintf("Dumping %s... ", filename); logFlush(); if(mpiUtil->isHead()) saveRawBinary(N, filename); { //debug sphericalized site densities ostringstream oss; oss << "Nspherical_" << c->molecule.name; if(c->molecule.sites.size()>1) oss << "_" << s.name; sprintf(filename, filenamePattern, oss.str().c_str()); saveSphericalized(&N,1,filename); } logPrintf("Done.\n"); logFlush(); } } }
SaLSA::SaLSA(const Everything& e, const FluidSolverParams& fsp) : PCM(e, fsp), siteShape(fsp.solvents[0]->molecule.sites.size()) { logPrintf(" Initializing non-local response weight functions:\n"); const double dG = gInfo.dGradial, Gmax = gInfo.GmaxGrid; unsigned nGradial = unsigned(ceil(Gmax/dG))+5; //Initialize fluid molecule's spherically-averaged electron density kernel: const auto& solvent = fsp.solvents[0]; std::vector<double> nFluidSamples(nGradial); for(unsigned i=0; i<nGradial; i++) { double G = i*dG; nFluidSamples[i] = 0.; for(const auto& site: solvent->molecule.sites) { double nTilde = site->elecKernel(G); for(const vector3<>& r: site->positions) nFluidSamples[i] += nTilde * bessel_jl(0, G*r.length()); } } nFluid.init(0, nFluidSamples, dG); //Determine dipole correlation factors: double chiRot = 0., chiPol = 0.; for(const auto& c: fsp.components) { chiRot += c->Nbulk * c->molecule.getDipole().length_squared()/(3.*fsp.T); chiPol += c->Nbulk * c->molecule.getAlphaTot(); } double sqrtCrot = (epsBulk>epsInf && chiRot) ? sqrt((epsBulk-epsInf)/(4.*M_PI*chiRot)) : 1.; double epsInfEff = chiRot ? epsInf : epsBulk; //constrain to epsBulk for molecules with no rotational susceptibility double sqrtCpol = (epsInfEff>1. && chiPol) ? sqrt((epsInfEff-1.)/(4.*M_PI*chiPol)) : 1.; //Rotational and translational response (includes ionic response): const double bessel_jl_by_Gl_zero[4] = {1., 1./3, 1./15, 1./105}; //G->0 limit of j_l(G)/G^l for(const auto& c: fsp.components) for(int l=0; l<=fsp.lMax; l++) { //Calculate radial densities for all m: gsl_matrix* V = gsl_matrix_calloc(nGradial, 2*l+1); //allocate and set to zero double prefac = sqrt(4.*M_PI*c->Nbulk/fsp.T); for(unsigned iG=0; iG<nGradial; iG++) { double G = iG*dG; for(const auto& site: c->molecule.sites) { double Vsite = prefac * site->chargeKernel(G); for(const vector3<>& r: site->positions) { double rLength = r.length(); double bessel_jl_by_Gl = G ? bessel_jl(l,G*rLength)/pow(G,l) : bessel_jl_by_Gl_zero[l]*pow(rLength,l); vector3<> rHat = (rLength ? 1./rLength : 0.) * r; for(int m=-l; m<=+l; m++) *gsl_matrix_ptr(V,iG,l+m) += Vsite * bessel_jl_by_Gl * Ylm(l,m, rHat); } } } //Scale dipole active modes: for(int lm=0; lm<2l+1; lm++) if(l==1 && fabs(gsl_matrix_get(V,0,lm))>1e-6) for(unsigned iG=0; iG<nGradial; iG++) *gsl_matrix_ptr(V,iG,lm) *= sqrtCrot; //Get linearly-independent non-zero modes by performing an SVD: gsl_vector* S = gsl_vector_alloc(2*l+1); gsl_matrix* U = gsl_matrix_alloc(2*l+1, 2*l+1); gsl_matrix* tmpMat = gsl_matrix_alloc(2*l+1, 2*l+1); gsl_vector* tmpVec = gsl_vector_alloc(2*l+1); gsl_linalg_SV_decomp_mod(V, tmpMat, U, S, tmpVec); gsl_vector_free(tmpVec); gsl_matrix_free(tmpMat); gsl_matrix_free(U); //Add response functions for non-singular modes: for(int mode=0; mode<2*l+1; mode++) { double Smode = gsl_vector_get(S, mode); if(Smode*Smode < 1e-3) break; std::vector<double> Vsamples(nGradial); for(unsigned iG=0; iG<nGradial; iG++) Vsamples[iG] = Smode * gsl_matrix_get(V, iG, mode); response.push_back(std::make_shared<MultipoleResponse>(l, -1, 1, Vsamples, dG)); } gsl_vector_free(S); gsl_matrix_free(V); } //Polarizability response: for(unsigned iSite=0; iSite<solvent->molecule.sites.size(); iSite++) { const Molecule::Site& site = *(solvent->molecule.sites[iSite]); if(site.polKernel) { std::vector<double> Vsamples(nGradial); double prefac = sqrtCpol * sqrt(solvent->Nbulk * site.alpha); for(unsigned iG=0; iG<nGradial; iG++) Vsamples[iG] = prefac * site.polKernel(iG*dG); response.push_back(std::make_shared<MultipoleResponse>(1, iSite, site.positions.size(), Vsamples, dG)); } } const double GzeroTol = 1e-12; //Compute bulk properties and print summary: double epsBulk = 1.; double k2factor = 0.; std::map<int,int> lCount; for(const std::shared_ptr<MultipoleResponse>& resp: response) { lCount[resp->l]++; double respGzero = (4*M_PI) * pow(resp->V(0), 2) * resp->siteMultiplicity; if(resp->l==0) k2factor += respGzero; if(resp->l==1) epsBulk += respGzero; } for(auto lInfo: lCount) logPrintf(" l: %d #weight-functions: %d\n", lInfo.first, lInfo.second); logPrintf(" Bulk dielectric-constant: %lg", epsBulk); if(k2factor > GzeroTol) logPrintf(" screening-length: %lg bohrs.\n", sqrt(epsBulk/k2factor)); else logPrintf("\n"); if(fsp.lMax >= 1) myassert(fabs(epsBulk-this->epsBulk) < 1e-3); //verify consistency of correlation factors myassert(fabs(k2factor-this->k2factor) < 1e-3); //verify consistency of site charges //Initialize preconditioner kernel: std::vector<double> KkernelSamples(nGradial); for(unsigned i=0; i<nGradial; i++) { double G = i*dG, G2=G*G; //Compute diagonal part of the hessian ( 4pi(Vc^-1 + chi) ): double diagH = G2; for(const auto& resp: response) diagH += pow(G2,resp->l) * pow(resp->V(G), 2); //Set its inverse square-root as the preconditioner: KkernelSamples[i] = (diagH>GzeroTol) ? 1./sqrt(diagH) : 0.; } Kkernel.init(0, KkernelSamples, dG); //MPI division: TaskDivision(response.size(), mpiUtil).myRange(rStart, rStop); }
/******************************************************************************* ** Compute complex q_lm (sum over eq. 3 from Stukowski paper), for each atom *******************************************************************************/ static void complex_qlm(int NVisibleIn, int *visibleAtoms, struct NeighbourList *nebList, double *pos, double *cellDims, int *PBC, struct AtomStructureResults *results) { int visIndex; /* loop over atoms */ #pragma omp parallel for num_threads(prefs_numThreads) for (visIndex = 0; visIndex < NVisibleIn; visIndex++) { int index, m; double xpos1, ypos1, zpos1; /* atom 1 position */ index = visibleAtoms[visIndex]; xpos1 = pos[3*index]; ypos1 = pos[3*index+1]; zpos1 = pos[3*index+2]; /* loop over m, l = 6 */ for (m = -6; m < 7; m++) { int i; double real_part, img_part; /* loop over neighbours */ real_part = 0.0; img_part = 0.0; for (i = 0; i < nebList[visIndex].neighbourCount; i++) { int visIndex2, index2; double xpos2, ypos2, zpos2, sepVec[3]; double theta, phi, realYlm, complexYlm; /* atom 2 position */ visIndex2 = nebList[visIndex].neighbour[i]; index2 = visibleAtoms[visIndex2]; xpos2 = pos[3*index2]; ypos2 = pos[3*index2+1]; zpos2 = pos[3*index2+2]; /* calculate separation vector between atoms */ atomSeparationVector(sepVec, xpos1, ypos1, zpos1, xpos2, ypos2, zpos2, cellDims[0], cellDims[1], cellDims[2], PBC[0], PBC[1], PBC[2]); /* convert to spherical coordinates */ convertToSphericalCoordinates(sepVec[0], sepVec[1], sepVec[2], nebList[visIndex].neighbourSep[i], &phi, &theta); /* calculate Ylm */ if (m < 0) { Ylm(6, abs(m), theta, phi, &realYlm, &complexYlm); realYlm = pow(-1.0, m) * realYlm; complexYlm = pow(-1.0, m) * complexYlm; } else { Ylm(6, m, theta, phi, &realYlm, &complexYlm); } /* sum */ real_part += realYlm; img_part += complexYlm; } /* divide by number of neighbours */ results[visIndex].realQ6[m+6] = real_part / ((double) nebList[visIndex].neighbourCount); results[visIndex].imgQ6[m+6] = img_part / ((double) nebList[visIndex].neighbourCount); } /* loop over m, l = 4 */ for (m = -4; m < 5; m++) { int i; double real_part, img_part; /* loop over neighbours */ real_part = 0.0; img_part = 0.0; for (i = 0; i < nebList[visIndex].neighbourCount; i++) { int visIndex2, index2; double xpos2, ypos2, zpos2, sepVec[3]; double theta, phi, realYlm, complexYlm; /* atom 2 position */ visIndex2 = nebList[visIndex].neighbour[i]; index2 = visibleAtoms[visIndex2]; xpos2 = pos[3*index2]; ypos2 = pos[3*index2+1]; zpos2 = pos[3*index2+2]; /* calculate separation vector */ atomSeparationVector(sepVec, xpos1, ypos1, zpos1, xpos2, ypos2, zpos2, cellDims[0], cellDims[1], cellDims[2], PBC[0], PBC[1], PBC[2]); /* convert to spherical coordinates */ convertToSphericalCoordinates(sepVec[0], sepVec[1], sepVec[2], nebList[visIndex].neighbourSep[i], &phi, &theta); /* calculate Ylm */ if (m < 0) { Ylm(4, abs(m), theta, phi, &realYlm, &complexYlm); realYlm = pow(-1.0, m) * realYlm; complexYlm = pow(-1.0, m) * complexYlm; } else { Ylm(4, m, theta, phi, &realYlm, &complexYlm); } /* sum */ real_part += realYlm; img_part += complexYlm; } /* divide by number of neighbours */ results[visIndex].realQ4[m+4] = real_part / ((double) nebList[visIndex].neighbourCount); results[visIndex].imgQ4[m+4] = img_part / ((double) nebList[visIndex].neighbourCount); } } }