Exemplo n.º 1
0
/*@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 );
  }
}
Exemplo n.º 2
0
/*@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 );
}
Exemplo n.º 3
0
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
}
Exemplo n.º 5
0
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;
}