∇-Nabla: Numerical Analysis BAsed LAnguage

∇-Nabla: Numerical Analysis BAsed LAnguage

nablaLulesh-512.png pnnnt-512.png nablaSethi-512.png monai-512.png nablaDDFV-512.png nablaMNLDDFV-512.png nablaCoMD-512.png nablaSPH-512.png

∇ CoMD

This partial ∇ port comes from an implementation of the Los Alamos National Security benchmark: CoMD, A Molecular Dynamics Proxy Applications Suite.

with particles;

// ****************************************************************************
// * DEFINES
// ****************************************************************************
#define amu_to_m_e (1822.83)
#define bohr_per_atu_to_A_per_s (0.529/2.418e-17)

// ****************************************************************************
// * OPTIONS
// ****************************************************************************
options{
  ℕ option_nx     = 20;
  ℕ option_ny     = 20;
  ℕ option_nz     = 20;
  ℝ option_lat       = 2.07514e-317;
  ℝ option_defgrad   = 1.0; // stretch in x direction (deformation gradient)
  ℝ option_cutoff    = 4.59;
  ℝ option_boxfactor = 1.0;
  ℕ option_max_iteration = 20;
};

// ****************************************************************************
// * Déclaration des variables aux mailles (alias 'boxes')
// ****************************************************************************
cells{
  ℝ natom;
  ℝ³ dcenter;
  ℝ cell_energy;
  ℝ cell_stress;
};

// ****************************************************************************
// * Déclaration des variables aux particules
// ****************************************************************************
particles{
  ℕ id;
  ℕ iType;  // the type of the atom
  ℝ³ r;        // position
  ℝ³ μ;        // momenta of the atom
  ℝ³ f;
  ℝ energy;
};

// ****************************************************************************
// * Déclaration des variables globales
// ****************************************************************************
global{
  ℕ xNbx;   // number of boxes in each dimension
  ℕ yNbx;   // number of boxes in each dimension
  ℕ zNbx;   // number of boxes in each dimension
  ℕ nboxes; // total number of boxes
  ℕ ntot;   // total number of atoms
  ℝ xBoxsize;  // size of domains
  ℝ yBoxsize;  // size of domains
  ℝ zBoxsize;  // size of domains
  ℝ rho;       // rhosum for EAM potential
  ℝ fi;        // rhobar for EAM potential
  ℝ e;         // the total energy of the system
  ℝ stress;    // virial stress
  ℝ defgrad;   // deformation gradient
  ℝ lat;       // Lattice constant
  ℝ xBounds;   // x periodic bounds
  ℝ yBounds;   // y periodic bounds
  ℝ zBounds;   // z periodic bounds
  ℝ boxfactor;
  // Lennard-Jones potential (pmd_base_potential_t *pot)
  ℝ σ;       // the finite distance at which the inter-particle potential is zero
  ℝ ε;       // the depth of the potential well
  ℝ cutoff;  // potential cutoff distance in Angstroms 
  ℝ mass;    // mass of the atom in atomic mass units
  // Simulation needs
  ℝ ts;
  ℝ te;
  ℕ nsteps;
  ℝ ns;
  ℝ s6;
  ℝ etot;
  ℝ r2cut;
};

// ********************************************************
// * fonctions outils
// ********************************************************
ℝ usPeratom(ℝ tend, ℝ tstart){
  return (ℝ)((ℕ)((1.0e6*(tend-tstart)/(ℝ)ns/(ℝ)ntot)*1000.0))/1000.0;
}

ℝ timeNow(void){
  return (ℝ)((ℕ)(ElapsedTime*1000.0))/1000.0;
}

// ****************************************************************************
// * Partie d'initialisation ]-∞,-0[
// ****************************************************************************

// ********************************************************
// * récupération des options
// ********************************************************
void iniOptions(void) @ -12.0{
  cutoff    = option_cutoff;
  defgrad   = option_defgrad;
  boxfactor = option_boxfactor;
}

// ********************************************************
// * Initialisation propre au potentiel Lennard-Jones
// ********************************************************
void iniLJPotentialValues(void) @ -11.0{
  σ=1.53;
  ε=0.0085;
  cutoff=3.0*σ;
  mass = 1.0;
  lat=1.122462048*cutoff;
  info()<<"[iniLJPotentialValues] cutoff="<<cutoff<<", lattice="<<lat;
}

// ********************************************************
// * Initialisation propre au potentiel Lennard-Jones
// ********************************************************
void iniLattice(void) @ -10.0{
  xBounds = option_nx * lat * defgrad;
  yBounds = option_ny * lat;
  zBounds = option_nz * lat;
}

// ********************************************************
// * Initialisation du domaine
// ********************************************************
void iniDomain(void) @ -9.0{
  ℝ strain =1.0;
  info() << "bounds = "<<xBounds<<","<<yBounds<<","<<zBounds
         << ", cutoff = "<<cutoff<<", box factor = "<<boxfactor
         << ", strain = "<<strain;
  xNbx = (ℕ)floor(xBounds/(cutoff*boxfactor*strain));
  yNbx = (ℕ)floor(yBounds/(cutoff*boxfactor*strain));
  zNbx = (ℕ)floor(zBounds/(cutoff*boxfactor*strain));
  nboxes = xNbx*yNbx*zNbx;
  xBoxsize=xBounds/(ℝ)xNbx;
  yBoxsize=yBounds/(ℝ)yNbx;
  zBoxsize=zBounds/(ℝ)zNbx;
}

// ********************************************************
// * Initialisation des particules
// ********************************************************
∀ particles void iniParticles(void) out (particle id) @ -9.0{
  f=μ=r=0.0;
  energy=0.0;
  id=iType=0;
}

// ********************************************************
// * Initialisation des mailles et calcul du centre
// ********************************************************
∀ cells void computeDomainCenter(void) @ -8.0{
  natom=0.0;
  dcenter=0.0;
  ∀ node dcenter+=coord;
  dcenter/=nbNode;
}

// given x,y,z in world co-ordinates return the box in which those coordinates fall
ℕ getBoxIDWorldCoords(ℝ x, ℝ y, ℝ z){
  ℕ ibox,ibx[3];
  ibx[0] = (ℕ)(floor(x/xBoxsize));
  ibx[1] = (ℕ)(floor(y/yBoxsize));
  ibx[2] = (ℕ)(floor(z/zBoxsize));
  ibox = ibx[0]+xNbx*ibx[1]+xNbx*yNbx*ibx[2];
  return ibox;
}

// finds an appropriate box for an atom based on the spatial coordinates and puts it in there
void putAtomInBox(const ℕ n, ℝ x, ℝ y, ℝ z) {
  ℝ³ ρ;
  // push atom into primary period
  if (x<0.0) x+=xBounds; else if (x>=xBounds) x-=xBounds;
  if (y<0.0) y+=yBounds; else if (y>=yBounds) y-=yBounds;
  if (z<0.0) z+=zBounds; else if (z>=zBounds) z-=zBounds;
  ρ.x=x; ρ.y=y; ρ.z=z;
  particleAddToCell(n,getBoxIDWorldCoords(x,y,z),ρ);
  ntot=ntot+1;
}

// ********************************************************
// * Remplissage des boîtes
// ********************************************************
void putAtomsInBoxes(void) @ -7.0{
  ℝ x,y,z;
  ℕ i,j,k,n;
  ℝ halflat=lat/2.0;
  for(z=lat/4.0,i=j=k=n=0; z<zBounds; z+=halflat,k++)
    for(y=lat/4.0; y<yBounds; y+=halflat,j++)
      for(x=lat*defgrad/4.0; x<xBounds; x+=halflat*defgrad,i++)
        if ((i+j+k) % 2) putAtomInBox(n++,x,y,z);
  particleSyncToCell();
}


// ********************************************************
// * Initialisation des particules
// ********************************************************
∀ particles void iniAtomTheBox(void) in (cell dcenter)
  out (particle id, particle iType, particle r) @ -6.0{
  id = uid;
  iType = 1;
  r -= dcenter;
}


// ********************************************************
// * Mise à jour du nombre de particules dans chaque maille
// ********************************************************
∀ own cells void iniParticlesInCells(void) in (particle id, particle r)@ -5.9{
  ∀ particle{
    natom+=1.0;
    //cell_energy=0.0;
    //if (!(uid%1024)) info()<<"cell #"<<uid<<" has particle "<< id <<" @ coords="<<r;
  }
}


// ********************************************************
// * Initialisation des variables de la simulation
// ********************************************************
void initSim(void) @ -5.0{
  info() << "total atoms is: "<< ntot;
  info() << "box factor is "<< xBoxsize/cutoff
         << " " << yBoxsize/cutoff << " " << zBoxsize/cutoff;
  nsteps=10;
  info() << "Starting simulation";
  δt = 1.0e-15;
  ts=timeNow();
  ns=(ℝ)(1+nsteps);
}


// ****************************************************************************
// * calculates forces for the 12-6 lennard jones potential
// * Notes on LJ:
// * http://en.wikipedia.org/wiki/Lennard_Jones_potential
// * LJ is a simple potential of the form:
// * e_lj(r) = 4*ε*((σ/r)^12 - (σ/r)^6)
// * F(r) = 4*ε*(12*σ^12/r^13 - 6*σ^6/r^7)
// * ε and σ are the adjustable parameters in the potential.
// *    ε = well depth
// *    σ   = hard sphere diameter
// * You can also adjust the 12 & 6, but few people do.
// * Some typical values for ε and σ:
// *   material            ε            σ   
// *     N                 36.20 K             3.299 A
// *     O                 44.06 K             2.956 A
// ****************************************************************************
void iniComputeForce(void){
  e = 0.0;
  etot = 0.0;
  stress = 0.0;
  s6 = σ*σ*σ*σ*σ*σ;
  r2cut = cutoff*cutoff;
}
∀ particles void particleZeroForcesEnergy(void){
  f=0.0;
  energy=0.0;
}
void cellComputeForce(Cell i, Cell j){
  ℝ etotl=0.0;
  ℝ strss=0.0;
  ℝ r2,r6,rs,fr;
  ℝ³ dr;
  ℝ³ drbox=dcenter[i]-dcenter[j];
  ∀ i particle{
    strss -= - μ[pi].x*μ[pi].x/mass;
    ∀ j particle{
      if (id[pj]<=id[pi]) continue;
      dr = drbox+(r[pi]-r[pj]);
      r2 = dot(dr,dr);
      if (r2>r2cut) continue;
      // Important note: from this point on r actually refers to 1.0/r
      r2 = 1.0/r2;
      r6 = (r2*r2*r2);
      rs = 0.5*r6*(s6*r6 - 1.0);
      energy[pi] += rs; 
      energy[pj] += rs;
      etotl += r6*(s6*r6 - 1.0);
      // different formulation to avoid sqrt computation
      fr = 4.0*ε*s6*r6*r2*(12.0*s6*r6-6.0);
      f[pi] += dr*fr;
      f[pj] -= dr*fr;
      strss += 2.0*fr*dr.x*dr.x;
    } // loop over atoms in j
  } // loop over atoms in i
  cell_energy[i]+=etotl;
  cell_stress[i]+=strss;
}
// loop over all boxes in system via pairs
∀ own cells void cellsComputeForceViaPairs(void) {
  cell_stress=cell_energy=0.0;
  cellComputeForce(*this,*this);            // itself
  foreach cell cellComputeForce(*this,*cc);  // neighbors
}
∀ own cells ℝ computeSumEnergy(ℝ nrj) in (cell cell_energy){
  nrj+=cell_energy;
  return nrj;
}
own cells ℝ computeSumStress(ℝ s) in (cell cell_stress){
  s+=cell_stress;
  return s;
}
void endComputeForce(void) {
  etot = etot*4.0*ε*s6;
  e = etot;
  // renormalize stress
  stress = stress/(xBounds*yBounds*zBounds);
}
void computeForce(void){
  iniComputeForce();
  particleZeroForcesEnergy();
  cellsComputeForceViaPairs();
  etot=computeSumEnergy(etot);
  stress=computeSumStress(stress);
  etot=mpi_reduce(ReduceSum,etot);
  stress=mpi_reduce(ReduceSum,stress);
  endComputeForce();
}

// ********************************************************
// * Initialisation des forces avant la boucle
// ********************************************************
void firstComputeForce(void) @ -0.5{
  computeForce();
}

// ****************************************************************************
// * Partie de calcul ]+0,+oo[ 
// ****************************************************************************

// ****************************************************************************
// * do_compute_work
// ****************************************************************************
void do_compute_work(void) @ 1.0{
  te=timeNow();
  info() << "Iteration #"<<(GlobalIteration-1)*nsteps<<", total system energy e="<< e
         << ", computed in "<<(te-ts)<<"s ("<< usPeratom(te,ts) <<"us/atom for "<<ntot<<" atoms)";
  ts=timeNow();
}

// ****************************************************************************
// * advanceVelocity
// ****************************************************************************
∀ particles void advanceVelocity(ℝ dt) in (particle f) out (particle p){
  μ -= dt*f;
}

// ****************************************************************************
// * All computations are done in atomic units.
// * (http://en.wikipedia.org/wiki/Atomic_units)
// * In these units the mass of an electrion is
// * assumed to be 1.0.  Since our mass is in
// * amu, we need to multiply it by 1822.83 to
// * get the mass in amu.
// ****************************************************************************
∀ particles void advancePositions(ℝ dt) in (particle mass){
  r += dt*μ/(amu_to_m_e*mass);
}

ℕ getIx(ℕ iBox) { return (iBox%xNbx);}
ℕ getIy(ℕ iBox) { return (iBox/xNbx)%(yNbx);}
ℕ getIz(ℕ iBox) { return (iBox/xNbx/yNbx)%zNbx;}
ℕ getIBoxFromIxyz3(ℕ x, ℕ y, ℕ z){
  ℕ rtn;
  info()<<"\33[7m getIBoxFromIxyz3 "<<x<<"x"<<y<<"x"<<z<<"\33[m";
  if (x<0) x+=xNbx; if (x>=xNbx) x-=xNbx;
  if (y<0) y+=yNbx; if (y>=yNbx) y-=yNbx;
  if (z<0) z+=zNbx; if (z>=zNbx) z-=zNbx;
  info()<<"\33[7m getIBoxFromIxyz3 "<<x<<"x"<<y<<"x"<<z<<"\33[m";
  rtn = x + y*xNbx + z*xNbx*yNbx;
  info()<<"\33[7m getIBoxFromIxyz3 rtn="<<rtn<<"\33[m";
  return rtn;
}

// ****************************************************************************
// * reBoxAll
// ****************************************************************************
∀ cells void reBoxAll(void){
  ℕ ibox=uid;
  ℕ iXold=getIx(uid);
  ℕ iYold=getIy(uid);
  ℕ iZold=getIz(uid);
  //info()<<"\33[7mreBoxAll cell #"<<uid<<":"<<iXold<<"x"<<iYold<<"x"<<iZold<<"\33[m";
  foreach particle{
    ℕ jbox;
    ℕ iXnew,iYnew,iZnew;
    ℝ³ rnew=r;

    info()<<"\33[35m In box #"<<uid<<", particle #"<<id<<" r="<<r<<"\33[m";
    info()<<" xBoxsize="<<xBoxsize()<<", yBoxsize="<<yBoxsize()<<", zBoxsize="<<zBoxsize();

    if (r.x < 0.0) { iXnew = iXold-1; rnew.x += xBoxsize; }
    else if (r.x >= xBoxsize) { iXnew = iXold+1; rnew.x -= xBoxsize; }
    else { iXnew = iXold; }

    if (r.y < 0.0) { iYnew = iYold-1; rnew.y += yBoxsize; }
    else if (r.y >= yBoxsize) { iYnew = iYold+1; rnew.y -= yBoxsize; }
    else { iYnew = iYold; }

    if (r.z < 0.0) { iZnew = iZold-1; rnew.z += zBoxsize; }
    else if (r.z >= zBoxsize) { iZnew = iZold+1; rnew.z -= zBoxsize; }
    else { iZnew = iZold; }

    jbox = getIBoxFromIxyz3(iXnew,iYnew,iZnew);

    if((jbox<0)||(jbox==ibox)){
      /* do nothing if same box or non-periodic boundary */
    }else{
      r=rnew;      
      moveAtomInBox(id, jbox);
    }
  }
}

// ****************************************************************************
// * Standard verlet algorithm:
// *   1: advance positions half a timestep using current velocities
// *   2: compute forces
// *   3: advance velocities (momenta) a full timestep
// *   4: advance positions half a timestep to bring in sync with velocities.
// ****************************************************************************
void nTimeSteps(void) @ 2.0{
  ℕ i;
  ℝ dt = δt * bohr_per_atu_to_A_per_s;
  for(i=0;i<nsteps;i++){
    advancePositions(dt/2.);
    computeForce();
    advanceVelocity(dt); 
    advancePositions(dt/2.);    
  }
  // compute force to make consistent
  computeForce();
  te=timeNow();
}

// ****************************************************************************
// * exit_compute_work
// ****************************************************************************
void exit_compute_work(void) @ 3.0{
  if (GlobalIteration > option_max_iteration) exit;
}

camierjs@nabla-lang.org,