/* Packing of hard spheres via molecular dynamics Developed by Monica Skoge, 2006, Princeton University Contact: Aleksandar Donev (adonev@math.princeton.edu) with questions This code may be used, modified and distributed freely. Please cite: "Packing Hyperspheres in High-Dimensional Euclidean Spaces" M. Skoge, A. Donev, F. H. Stillinger and S. Torquato, 2006 if you use these codes. */ #include "box.h" #include #include #include #include #include //============================================================== //============================================================== // Class Box: Fills box with hardspheres to given packing fraction // and evolves spheres using molecular dynamics! //============================================================== //============================================================== //============================================================== // Constructor //============================================================== box::box(int N_i, double r_i, double growthrate_i, double maxpf_i): r(r_i), N(N_i), growthrate(growthrate_i), h(N_i+1), maxpf(maxpf_i) { ngrids = Optimalngrids(maxpf); cells.set_size(ngrids); s = new sphere[N]; binlist = new int[N]; x = new vector[N]; h.s = s; gtime = 0.; rtime = 0.; ncollisions = 0; ntransfers = 0; nchecks = 0; neventstot = 0; ncycles = 0; xmomentum = 0.; pressure = 0.; cells.set_size(ngrids); cells.initialize(-1); // initialize cells to -1 srandom(::time(0)); // initialize the random number generator for (int i=0; i> s[i].x[k]; infile.close(); } //============================================================== // Recreates all N spheres at random positions //============================================================== void box::RecreateSpheres(const char* filename, double temp) { ReadPositions(filename); // reads in positions of spheres VelocityGiver(temp); // gives spheres initial velocities AssignCells(); // assigns spheres to cells SetInitialEvents(); } //============================================================== // Creates all N spheres at random positions //============================================================== void box::CreateSpheres(double temp) { int Ncurrent = 0; for(int i=0; i xrand; // random new position vector double d = 0.; double rp = 0.; while (counter<1000) { keeper = 1; for(int k=0; k= (RCYL-r)*(RCYL-r)) { keeper = 0; } else { for (int i=0; i SIZE*SIZE/4.) { if (xrand[k] > SIZE/2.) // look at right image d += (xrand[k] - (s[i].x[k]+SIZE))* (xrand[k] - (s[i].x[k]+SIZE)); else // look at left image d += (xrand[k] - (s[i].x[k]-SIZE))* (xrand[k] - (s[i].x[k]-SIZE)); } else d += (xrand[k] - s[i].x[k])*(xrand[k] - s[i].x[k]); } else // lateral dimensions in cylinder d += (xrand[k] - s[i].x[k])*(xrand[k] - s[i].x[k]); } if (d <= 4*r*r) { keeper = 0; counter++; break; } } } if (keeper == 1) break; } if (counter >= 1000) { std::cout << "counter >= 1000" << std::endl; exit(-1); } // now convert xrand into index vector for cells vector cell; cell = vector::integer(xrand*((double)(ngrids))/SIZE); s[Ncurrent] = sphere(Ncurrent, xrand, cell, gtime); //first check to see if entry at cell if (cells.get(cell) == -1) //if yes, add Ncurrent to cells gridfield cells.get(cell) = Ncurrent; else // if no, add i to right place in binlist { int iterater = cells.get(cell); // now iterate through to end and add Ncurrent int pointer = iterater; while (iterater != -1) { pointer = iterater; iterater = binlist[iterater]; } binlist[pointer] = Ncurrent; } Ncurrent++; } //============================================================== // Assign cells to spheres read in from existing configuration //============================================================== void box::AssignCells() { for (int i=0; i cell; cell = vector::integer(s[i].x*((double)(ngrids))/SIZE); s[i].cell = cell; //first check to see if entry at cell if (cells.get(cell) == -1) //if yes, add Ncurrent to cells gridfield cells.get(cell) = i; else // if no, add i to right place in binlist { int iterater = cells.get(cell); // now iterate through to end and add Ncurrent int pointer = iterater; while (iterater != -1) { pointer = iterater; iterater = binlist[iterater]; } binlist[pointer] = i; } } } //============================================================== // Velocity Giver, assigns initial velocities from Max/Boltz dist. //============================================================== void box::VelocityGiver(double T) { for (int i=0; i xi = s[i].x + s[i].v*(gtime - s[i].lutime); vector vi = s[i].v; for (int k=0; k0) // will hit right wall { newtime = ((double)(s[i].cell[k]+1)*SIZE/((double)(ngrids)) - xi[k])/(vi[k]); if (newtime < 0) std::cout << "error in FindNextTransfer right newtime < 0 " << k << std::endl; if (newtime -10.*DBL_EPSILON) // this should happen only when reading in a configuration and spheres is on left boundary moving left newtime = 0.; else std::cout << "error in FindNextTransfer left newtime < 0 " << k << std::endl; } if (newtime vl, vector vr, neighbor& operation) { vector cell = s[i].cell; // now iterate through nearest neighbors vector offset; // nonnegative neighbor offset vector pboffset; // nearest image offset vector grid; int ii ; grid=vl; while(1) { //if (vr[0] > 1) //std::cout << grid << "..." << cell+grid << "\n"; for(int k=0; k=ngrids && k==0) // out of bounds to right (x only) pboffset[k] = 1; else pboffset[k] = 0; } int j = cells.get((cell+offset)%ngrids); if (j > N) { std::cout << "bad j " << j << " argument cells " << (cell+offset)%ngrids << " particle " << i << std::endl; std::cout << "grid " << grid << std::endl; std::cout << "cell " << cell << " offset " << offset << " ngrids " << ngrids << std::endl; exit(-1); } while(j!=-1) { operation.Operation(j,pboffset); j = binlist[j]; } // A. Donev: // This code makes this loop dimension-independent // It is basically a flattened-out loop nest of depth DIM for(ii=0;ii=DIM) break; } } //============================================================== // PredictCollision //============================================================== void box::PredictCollision(int i, int j, vector pboffset, double& ctime, int& cpartner, vector& cpartnerpboffset) { double ctimej; if (i!=j) { ctimej = CalculateCollision(i,j,pboffset.Double())+gtime; if (ctimej < gtime) std::cout << "error in find collision ctimej < 0" << std::endl; if ((ctimej < ctime)&&(ctimej < s[j].nextevent.time)) { ctime = ctimej; cpartner = j; cpartnerpboffset = pboffset; } } } //============================================================== // Find next collision //============================================================== event box::FindNextCollision(int i) { collision cc(i, this); vector vl, vr; for (int k=0; k pboffset) { // calculate updated position and velocity of i and j vector xi = s[i].x + s[i].v*(gtime - s[i].lutime); vector vi = s[i].v; vector xj = s[j].x + pboffset*SIZE + s[j].v*(gtime - s[j].lutime); vector vj = s[j].v; double r_now = r + gtime*growthrate; double A,B,C; A = vector::norm_squared(vi - vj) - 4*growthrate*growthrate; B = vector::dot(xi - xj, vi - vj) - 4*r_now*growthrate; C = vector::norm_squared(xi - xj) - 4*r_now*r_now; if (C < -1E-12*2.*r_now) { std::cout << "error, " << i << " and " << j << " are overlapping at time "<< gtime << " and A, B, C = " << A << " " << " " << B << " " << " " << C << std::endl; std::cout << "velocity i= " << s[i].v << ", velocity j= " << s[j].v << ", gtime= " << gtime << ", det= " << B*B - A*C << std::endl; std::cout << "pos i= " << xi << " pos j = " << xj << std::endl; exit(-1); } return QuadraticFormula(A, B, C); } //============================================================== // Quadratic Formula ax^2 + bx + c = 0 //============================================================== double box::QuadraticFormula(double a, double b, double c) { double x = dblINF; double xpos; double xneg; double det = b*b - a*c; if (c <= 0.) { if(b < 0.) // spheres already overlapping and approaching { //std::cout << "spheres overlapping and approaching" << std::endl; //std::cout << "# events= " << neventstot << std::endl; x = 0.; } } else if (det > -10.*DBL_EPSILON) { if (det < 0.) // determinant can be very small for double roots det = 0.; if (b < 0.) x = c/(-b + sqrt(det)); else if ((a < 0.)&&(b > 0.)) x = -(b + sqrt(det))/a; else x = dblINF; } return x; } double box::QuadraticFormulaWalls(double a, double b, double c) { double x = dblINF; double det = b*b - a*c; /* if (c > -10.*DBL_EPSILON) { if(b > 0.) // sphere overlapping wall and approaching { x = 2.*b/a; x = (-b + sqrt(det))/a; } else // collision just occured and c~0 { x = -2.*b/a; x = (-b + sqrt(det))/a; } } */ if (det > -10.*DBL_EPSILON) { if (det < 0.) // determinant can be very small for double roots det = 0.; if (-b + sqrt(det)>= 0.) // assume a is always positive x = (-b + sqrt(det))/a; else x = (-b - sqrt(det))/a; } if (x < 0) { std::cout << "negative wall collision time: " << x << std::endl; std::cout << "a= " << a << " b= " << b << " c= " << c << std::endl; std::cout << "x1= " << (-b + sqrt(det))/a << " x2= " << (-b - sqrt(det))/a << std::endl; x = 0.; if (c*c<10.*DBL_EPSILON) x = -2.*b/a; // exit(-1); } return x; } //============================================================== // Returns first event //============================================================== void box::ProcessEvent() { vector test; test[0]=3; test[1]=1; test[2]=2; neventstot++; // Extract first event from heap int i = h.extractmax(); event e = s[i].nextevent; // current event event f; // replacement event if ((e.j>=0)&&(e.j v = e.v; // virtual image gtime = ctime; // Update positions and cells of i and j to ctime s[i].x += s[i].v*(gtime-s[i].lutime); s[j].x += s[j].v*(gtime-s[j].lutime); // Check to see if a diameter apart double r_now = r + gtime*growthrate; double distance = vector::norm_squared(s[i].x - s[j].x- v.Double()*SIZE) - 4*r_now*r_now; if (distance*distance > 10.*DBL_EPSILON) std::cout << "overlap " << distance << std::endl; s[i].lutime = gtime; s[j].lutime = gtime; vector vipar; // parallel comp. vi vector vjpar; // parallel comp. vj vector viperp; // perpendicular comp. vi vector vjperp; // perpendicular comp. vj // make unit vector out of displacement vector vector dhat; dhat = s[i].x - s[j].x - v.Double()*SIZE; // using image of j!! double dhatmagnitude = sqrt(dhat.norm_squared()); dhat /= dhatmagnitude; vipar = dhat*vector::dot(s[i].v, dhat); vjpar = dhat*vector::dot(s[j].v, dhat); viperp = s[i].v - vipar; vjperp = s[j].v - vjpar; s[i].v = vjpar + dhat*2.*growthrate + viperp; s[j].v = vipar - dhat*2.*growthrate + vjperp; // momentum exchange double xvelocity; // exchanged velocity xvelocity = vector::dot(s[i].v - s[j].v, dhat) - 4.*growthrate; xmomentum += M*xvelocity*dhatmagnitude; } //============================================================== // Processes a wall collision //============================================================= void box::WallCollision(event e) { double ctime = e.time; double b = dblINF; int i = e.i; gtime = ctime; // Update position of i to ctime s[i].x += s[i].v*(gtime-s[i].lutime); vector xi = s[i].x; xi[0]=0; xi[1]=xi[1]-SIZEH; xi[2]=xi[2]-SIZEH; // Check if at wall double r_now = r + gtime*growthrate; double distance = vector::norm_squared(xi)-(RCYL-r_now)*(RCYL-r_now); if (distance*distance > 10.*DBL_EPSILON) { std::cout << "wall overlap " << distance << std::endl; std::cout << "x= " << xi << " r_now= " << r_now << std::endl; exit(-1); } s[i].lutime = gtime; // construct wall unit normal vector nhat; nhat = xi*(-1.); double nhatmagnitude = sqrt(nhat.norm_squared()); nhat /= nhatmagnitude; vector vnew; vnew = s[i].v - nhat*2.*vector::dot(s[i].v, nhat); std::cout << "vold " << s[i].v << " vnew " << vnew + nhat*growthrate << std::endl; std::cout << "nhat " << nhat << " delta v " << nhat*2.*vector::dot(s[i].v, nhat)*(-1.) + nhat*growthrate << std::endl; s[i].v = vnew + nhat*growthrate; // check if need to increase wall recoil (to prevent additional collisions // due to growthrate) b = vector::dot(xi,s[i].v) + (RCYL-r_now)*growthrate; while (b>=0) { s[i].v += nhat*growthrate; b = vector::dot(xi,s[i].v) + (RCYL-r_now)*growthrate; } } //============================================================== // Transfer, takes care of boundary events too //============================================================= void box::Transfer(event e) { gtime = e.time; int i = e.i; int j = e.j; int k=0; // dimension perpendicular to wall it crosses // update position and lutime (velocity doesn't change) s[i].x += s[i].v*(gtime-s[i].lutime); s[i].lutime = gtime; vector celli; // new cell for i celli = s[i].cell; // this is not redundant // update cell if (j>N+DIM+1) // right wall { k = j-N-DIM-2; celli[k] = s[i].cell[k] + 1; // if in right-most cell, translate x and cell if (s[i].cell[k] == ngrids - 1) { s[i].x[k] -= SIZE; celli[k] -= ngrids; } } else if (j& celli) { std::cout<<"updating cell for " << i << " old cell " << s[i].cell << " get " << cells.get(s[i].cell) << std::endl; if (celli == s[i].cell) std::cout << "error in update cell..shouldn't be the same" << std::endl; // delete i from cell array at cell else if (cells.get(s[i].cell) == i) { if (binlist[i] == -1) cells.get(s[i].cell) = -1; else { cells.get(s[i].cell) = binlist[i]; binlist[i] = -1; } } else if (cells.get(s[i].cell) == -1) { std::cout << "error " << i << " not in claimed cell UpdateCell" << std::endl; OutputCells(); } else // if no, find i in binlist { int iterater = cells.get(s[i].cell); int pointer = iterater; while ((iterater != i)&&(iterater != -1)) { pointer = iterater; iterater = binlist[iterater]; } if (iterater == -1) // got to end of list without finding i { std::cout << "problem " << i << " wasn't in claimed, cell iterater = -1" << std::endl; OutputCells(); } else // we found i! { binlist[pointer] = binlist[i]; binlist[i] = -1; } } // now add i to cell array at celli s[i].cell = celli; //first check to see if entry at celli if (cells.get(celli) == -1) //if yes, add i to cells gridfield cells.get(celli) = i; else // if no, add i to right place in binlist { int iterater = cells.get(celli); // now iterate through to end and add i int pointer = iterater; while (iterater != -1) // find the end of the list { pointer = iterater; iterater = binlist[iterater]; } binlist[pointer] = i; binlist[i] = -1; // redundant } std::cout<< " new cell " << s[i].cell << " get " << cells.get(s[i].cell) << std::endl; } //============================================================== // Output event heap...purely used for debugging //============================================================== void box::OutputEvents() { h.print(); } //============================================================== // Output positions of spheres and their cells...purely used for debugging //============================================================== void box::OutputCells() { for (int i=0; i test; for (ii=0; ii