/*@T * * The [[place_particle]] routine determines the initial particle * placement, but not the desired mass. We want the fluid in the * initial configuration to exist roughly at the reference density. * One way to do this is to take the volume in the indicated body of * fluid, multiply by the mass density, and divide by the number of * particles; but that requires that we be able to compute the volume * of the fluid region. Alternately, we can simply compute the * average mass density assuming each particle has mass one, then use * that to compute the particle mass necessary in order to achieve the * desired reference density. We do this with [[normalize_mass]]. * * @c*/ void normalize_mass(sim_state_t* s, proc_info* pInfo, sim_param_t* param) { if (pInfo->proc == 0) { s->mass = 1; // Set mass with only one processor //hash_particles_parallel(s, pInfo, param->h); hash_particles(s, param->h); // Hashing with only one processor } float rho0 = param->rho0; float rho2s = 0; float rhos = 0; #pragma omp barrier // Barrier because need hashing information before computing density compute_density(s, pInfo, param); #pragma omp barrier // Want all processor to finish updating their own densities //printf("Starting: %d\n", pInfo->proc); #pragma omp parallel for reduction(+:rhos, rho2s) for (int i = 0; i < s->n; ++i) { rho2s += (s->part[i].rho)*(s->part[i].rho); rhos += s->part[i].rho; } #pragma omp single // Only one processor to update this { s->mass *= ( rho0*rhos / rho2s ); } }
/*@T * * The [[place_particle]] routine determines the initial particle * placement, but not the desired mass. We want the fluid in the * initial configuration to exist roughly at the reference density. * One way to do this is to take the volume in the indicated body of * fluid, multiply by the mass density, and divide by the number of * particles; but that requires that we be able to compute the volume * of the fluid region. Alternately, we can simply compute the * average mass density assuming each particle has mass one, then use * that to compute the particle mass necessary in order to achieve the * desired reference density. We do this with [[normalize_mass]]. * * @c*/ void normalize_mass(sim_state_t* s, sim_param_t* param) { s->mass = 1; hash_particles(s, param->h); compute_density(s, param); float rho0 = param->rho0; float rho2s = 0; float rhos = 0; for (int i = 0; i < s->n; ++i) { rho2s += (s->part[i].rho)*(s->part[i].rho); rhos += s->part[i].rho; } s->mass *= ( rho0*rhos / rho2s ); }
void BaderGrid::construct_bader(const arma::mat & P, double otoler) { // Amount of radial shells on the atoms std::vector<size_t> nrad(basp->get_Nnuc()); Timer t; size_t nd=0, ng=0; // Form radial shells std::vector<angshell_t> grids; for(size_t iat=0;iat<basp->get_Nnuc();iat++) { angshell_t sh; sh.atind=iat; sh.cen=basp->get_nuclear_coords(iat); sh.tol=otoler*PRUNETHR; // Compute necessary number of radial points for atom size_t nr=std::max(20,(int) round(-5*(3*log10(otoler)+8-element_row[basp->get_Z(iat)]))); // Get Chebyshev nodes and weights for radial part std::vector<double> rad, wrad; radial_chebyshev_jac(nr,rad,wrad); nr=rad.size(); // Sanity check nrad[iat]=nr; // Loop over radii for(size_t irad=0;irad<nr;irad++) { sh.R=rad[irad]; sh.w=wrad[irad]; grids.push_back(sh); } } // List of grid points std::vector<gridpoint_t> points; // Initialize list of maxima maxima.clear(); reggrid.clear(); for(size_t i=0;i<basp->get_Nnuc();i++) { nucleus_t nuc(basp->get_nucleus(i)); if(!nuc.bsse) { // Add to list maxima.push_back(nuc.r); std::vector<gridpoint_t> ghlp; reggrid.push_back(ghlp); } } Nnuc=maxima.size(); // Block inside classification? std::vector<bool> block(maxima.size(),false); // Index of last treated atom size_t oldatom=-1; for(size_t ig=0;ig<grids.size();ig++) { // Construct the shell wrk.set_grid(grids[ig]); grids[ig]=wrk.construct_becke(otoler/nrad[grids[ig].atind]); // Form the grid again wrk.form_grid(); // Extract the points on the shell std::vector<gridpoint_t> shellpoints(wrk.get_grid()); if(!shellpoints.size()) continue; // Are we inside an established trust radius, or are we close enough to a real nucleus? bool inside=false; if(grids[ig].R<=TRUSTRAD && !(basp->get_nucleus(grids[ig].atind).bsse)) inside=true; else if(!block[grids[ig].atind] && oldatom==grids[ig].atind) { // Compute projection of density gradient of points on shell arma::vec proj(shellpoints.size()); coords_t nuccoord(basp->get_nuclear_coords(grids[ig].atind)); #ifdef _OPENMP #pragma omp parallel for #endif for(size_t ip=0;ip<shellpoints.size();ip++) { // Compute density gradient double d; arma::vec g; compute_density_gradient(P,*basp,shellpoints[ip].r,d,g); // Vector pointing to nucleus coords_t dRc=nuccoord-shellpoints[ip].r; arma::vec dR(3); dR(0)=dRc.x; dR(1)=dRc.y; dR(2)=dRc.z; // Compute dot product with gradient proj(ip)=arma::norm_dot(dR,g); } // Increment amount of gradient evaluations ng+=shellpoints.size(); // Check if all points are inside const double cthcrit=cos(M_PI/4.0); inside=(arma::min(proj) >= cthcrit); } // If we are not inside, we need to run a point by point classification. if(!inside) { Timer tc; // Reset the trust atom oldatom=-1; // and the current atom block[grids[ig].atind]=true; // Loop over points #ifdef _OPENMP #pragma omp parallel for schedule(dynamic) #endif for(size_t ip=0;ip<shellpoints.size();ip++) { if(compute_density(P,*basp,shellpoints[ip].r)<=SMALLDENSITY) { // Zero density - skip point continue; } // Track the density to its maximum coords_t r=track_to_maximum(*basp,P,shellpoints[ip].r,nd,ng); #ifdef _OPENMP #pragma omp critical #endif { // Now that we have the maximum, check if it is on the list of known maxima bool found=false; for(size_t im=0;im<maxima.size();im++) if(norm(r-maxima[im])<=SAMEMAXIMUM) { found=true; reggrid[im].push_back(shellpoints[ip]); break; } // Maximum was not found, add it to the list if(!found) { maxima.push_back(r); std::vector<gridpoint_t> ghlp; ghlp.push_back(shellpoints[ip]); reggrid.push_back(ghlp); } } } // Continue with the next radial shell continue; } else { // If we are here, then all points belong to this nuclear maximum oldatom=grids[ig].atind; reggrid[ grids[ig].atind ].insert(reggrid[ grids[ig].atind ].end(), shellpoints.begin(), shellpoints.end()); } } if(verbose) { printf("Bader grid constructed in %s, taking %i density and %i gradient evaluations.\n",t.elapsed().c_str(),(int) nd, (int) ng); print_maxima(); // Amount of integration points arma::uvec np(basp->get_Nnuc()); np.zeros(); // Amount of function values arma::uvec nf(basp->get_Nnuc()); nf.zeros(); for(size_t i=0;i<grids.size();i++) { np(grids[i].atind)+=grids[i].np; nf(grids[i].atind)+=grids[i].nfunc; } printf("Composition of atomic integration grid:\n %7s %7s %10s\n","atom","Npoints","Nfuncs"); for(size_t i=0;i<basp->get_Nnuc();i++) printf(" %4i %-2s %7i %10i\n",(int) i+1, basp->get_symbol(i).c_str(), (int) np(i), (int) nf(i)); printf("\nAmount of grid points in the regions:\n %7s %7s\n","region","Npoints"); for(size_t i=0;i<reggrid.size();i++) printf(" %4i %7i\n",(int) i+1, (int) reggrid[i].size()); fflush(stdout); } }
void compute_accel(sim_state_t* state, sim_param_t* params) { // Unpack basic parameters const float h = params->h; const float rho0 = params->rho0; // const float k = params->k; // const float mu = params->mu; const float g = params->g; // const float mass = state->mass; const float h2 = params->h2; // Unpack system state particle_t* p = state->part; particle_t** hash = state->hash; const int n = state->n; // Rehash the particles hash_particles(state, h); // Compute density and color compute_density(state, params); // Constants for interaction term const float C0 = params->C0; const float Cp = params->Cp; const float Cv = params->Cv; // Start with gravity and surface forces for (int i = 0; i < n; ++i) { vec3_set(p[i].a, 0, -g, 0); } // Accumulate forces #ifdef USE_BUCKETING /* BEGIN TASK */ // Start multi-threaded // Iterate over each bucket, and within each bucket each particle #pragma omp parallel { // Get the thread ID and total threads int thread_id = omp_get_thread_num(); int total_threads = omp_get_num_threads(); // Get the appropriate forces vector float* forces = forces_all + thread_id * (state->n * 3); memset(forces, 0, sizeof(float) * state->n * 3); // Get the dedupe buffer for this thread char* usedBinID = used_bin_id_flags + (thread_id * HASH_SIZE); // Create storage for neighbor set unsigned buckets[MAX_NBR_BINS]; unsigned numbins; // Process all buckets that the thread is responsible for for (int iter_bucket = thread_id; iter_bucket < HASH_SIZE; iter_bucket += total_threads) { for (particle_t* pi = hash[iter_bucket]; pi != NULL ; pi = pi->next) { // Get the position of the pi force accumulator unsigned diffPosI = pi - state->part; float* pia = forces + 3 * diffPosI; // Compute equal and opposite forces for the particle, // first get neighbors numbins = particle_neighborhood(buckets, pi, h, usedBinID); for (int j = 0; j < numbins; ++j) { // Get the neighbor particles unsigned bucketid = buckets[j]; for (particle_t* pj = hash[bucketid]; pj != NULL ; pj = pj->next) { // Compute forces only if appropriate if (pi < pj && abs(pi->ix - pj->ix) <= 1 && abs(pi->iy - pj->iy) <= 1 && abs(pi->iz - pj->iz) <= 1) { // Get the position of the pj force accumulator unsigned diffPosJ = pj - state->part; float* pja = forces + 3 * diffPosJ; // Accumulate forces update_forces(pi, pj, h2, rho0, C0, Cp, Cv, pia, pja); } } } } } // Accumulate values in vector for (int iter_particle = 0; iter_particle < state->n; ++iter_particle) { particle_t* cur_particle = state->part + iter_particle; float* pia = forces + 3 * iter_particle; if(pia[0] != 0 || pia[1] != 0 || pia[2] != 0) { omp_set_lock(&cur_particle->lock); vec3_saxpy(cur_particle->a, 1, pia); omp_unset_lock(&cur_particle->lock); } } } /* END TASK */ #else for (int i = 0; i < n; ++i) { particle_t* pi = p+i; for (int j = i+1; j < n; ++j) { particle_t* pj = p+j; update_forces(pi, pj, h2, rho0, C0, Cp, Cv, pi->a, pj->a); } } #endif }
coords_t track_to_maximum(const BasisSet & basis, const arma::mat & P, const coords_t r0, size_t & nd, size_t & ng) { // Track density to maximum. coords_t r(r0); size_t iiter=0; // Amount of density and gradient evaluations size_t ndens=0; size_t ngrad=0; // Nuclear coordinates arma::mat nuccoord=basis.get_nuclear_coords(); // Initial step size to use const double steplen=0.1; double dr(steplen); // Maximum amount of steps to take in line search const size_t nline=5; // Density and gradient double d; arma::vec g; while(true) { // Iteration number iiter++; // Compute density and gradient compute_density_gradient(P,basis,r,d,g); double gnorm=arma::norm(g,2); fflush(stdout); ndens++; ngrad++; // Normalize gradient and perform line search coords_t gn; gn.x=g(0)/gnorm; gn.y=g(1)/gnorm; gn.z=g(2)/gnorm; std::vector<double> len, dens; #ifdef BADERDEBUG printf("Gradient norm %e. Normalized gradient at % f % f % f is % f % f % f\n",gnorm,r.x,r.y,r.z,gn.x,gn.y,gn.z); #endif // Determine step size to use by finding out minimal distance to nuclei arma::rowvec rv(3); rv(0)=r.x; rv(1)=r.y; rv(2)=r.z; // and the closest nucleus double mindist=arma::norm(rv-nuccoord.row(0),2); arma::rowvec closenuc=nuccoord.row(0); for(size_t i=1;i<nuccoord.n_rows;i++) { double t=arma::norm(rv-nuccoord.row(i),2); if(t<mindist) { mindist=t; closenuc=nuccoord.row(i); } } // printf("Minimal distance to nucleus is %e.\n",mindist); // Starting point len.push_back(0.0); dens.push_back(d); #ifdef BADERDEBUG printf("Step length %e: % f % f % f, density %e, difference %e\n",len[0],r.x,r.y,r.z,dens[0],0.0); #endif // Trace until density does not increase any more. do { // Increase step size len.push_back(len.size()*dr); // New point coords_t pt=r+gn*len[len.size()-1]; // and density dens.push_back(compute_density(P,basis,pt)); ndens++; #ifdef BADERDEBUG printf("Step length %e: % f % f % f, density %e, difference %e\n",len[len.size()-1],pt.x,pt.y,pt.z,dens[dens.size()-1],dens[dens.size()-1]-dens[0]); #endif } while(dens[dens.size()-1]>dens[dens.size()-2] && dens.size()<nline); // Optimal line length double optlen=0.0; if(dens[dens.size()-1]>=dens[dens.size()-2]) // Maximum allowed optlen=len[len.size()-1]; else { // Interpolate arma::vec ilen(3), idens(3); if(dens.size()==2) { ilen(0)=len[len.size()-2]; ilen(2)=len[len.size()-1]; ilen(1)=(ilen(0)+ilen(2))/2.0; idens(0)=dens[dens.size()-2]; idens(2)=dens[dens.size()-1]; idens(1)=compute_density(P,basis,r+gn*ilen(1)); ndens++; } else { ilen(0)=len[len.size()-3]; ilen(1)=len[len.size()-2]; ilen(2)=len[len.size()-1]; idens(0)=dens[dens.size()-3]; idens(1)=dens[dens.size()-2]; idens(2)=dens[dens.size()-1]; } #ifdef BADERDEBUG arma::trans(ilen).print("Step lengths"); arma::trans(idens).print("Densities"); #endif // Fit polynomial arma::vec p=fit_polynomial(ilen,idens); // and solve for the roots of its derivative arma::vec roots=solve_roots(derivative_coefficients(p)); // The optimal step length is for(size_t i=0;i<roots.n_elem;i++) if(roots(i)>=ilen(0) && roots(i)<=ilen(2)) { optlen=roots(i); break; } } #ifdef BADERDEBUG printf("Optimal step length is %e.\n",optlen); #endif if(std::min(optlen,mindist)<=CONVTHR) { // Converged at nucleus. r.x=closenuc(0); r.y=closenuc(1); r.z=closenuc(2); } if(optlen==0.0) { if(dr>=CONVTHR) { // Reduce step length. dr/=10.0; continue; } else { // Converged break; } } else if(optlen<=CONVTHR) // Converged break; // Update point r=r+gn*optlen; } nd+=ndens; ng+=ngrad; #ifdef BADERDEBUG printf("Point % .3f % .3f %.3f tracked to maximum at % .3f % .3f % .3f with %s density evaluations.\n",r0.x,r0.y,r0.z,r.x,r.y,r.z,space_number(ndens).c_str()); #endif return r; }