/* Updated July 24, 2009 to include hardwall boundary condition option, as well as polydisperse spheres. Packing of hard spheres via molecular dynamics Developed by Monica Skoge, 2006, Princeton University Contact: Monica Skoge (mskoge.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, double bidispersityratio_i, double bidispersityfraction_i, double massratio_i, int hardwallBC_i): r(r_i), N(N_i), growthrate(growthrate_i), h(N_i+1), maxpf(maxpf_i), bidispersityratio(bidispersityratio_i), bidispersityfraction(bidispersityfraction_i), massratio(massratio_i), hardwallBC(hardwallBC_i) { ngrids = Optimalngrids2(r); 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.; collisionrate = 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].r; // read in radius infile >> s[i].gr; // read in growth rate infile >> s[i].m; // read in mass for (int k=0; k> s[i].x[k]; // read in position } 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 radius; double growth_rate; double mass; int species; if (Ncurrent < bidispersityfraction*N) { radius = r; growth_rate = growthrate; mass = 1.; species = 1; } else { radius = r*bidispersityratio; growth_rate = growthrate*bidispersityratio; mass = massratio; species = 2; } while (counter<1000) { keeper = 1; for(int k=0; k 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]); } } if (d <= (radius + s[i].r)*(radius + s[i].r)) // overlapping! { keeper = 0; counter++; break; } } if (hardwallBC) { for (int k=0; k= 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, radius, growth_rate, mass, species); //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; } } //============================================================== // 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; i1)) { event o = event(outgrowtime,i,INF-1); return o; } if ((c.time < t.time)&&(c.j == INF)) // next event is check at DBL infinity return c; else if (c.time < t.time) // next event is collision! { CollisionChecker(c); return c; } else // next event is transfer! return t; } //============================================================== // Checks events of predicted collision partner to keep collisions // symmetric //============================================================== void box::CollisionChecker(event c) { int i = c.i; int j = c.j; event cj(c.time,j,i,c.v*(-1)); // j should have NO event before collision with i! if (!(c.time < s[j].nextevent.time)) std::cout << i << " " << j << " error collchecker, s[j].nextevent.time= " << s[j].nextevent.time << " " << s[j].nextevent.j << ", c.time= " << c.time << std::endl; int k = s[j].nextevent.j; if ((k < N) && (k!=i)) // j's next event was collision so give k a check s[k].nextevent.j = INF; // give collision cj to j s[j].nextevent = cj; h.upheap(h.index[j]); } //============================================================== // Find next transfer for sphere i //============================================================== event box::FindNextTransfer(int i) { double ttime = dblINF; int wallindex = INF; // -(k+1) left wall, (k+1) right wall vector 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, need to check if last wall { if ((hardwallBC)&&(s[i].cell[k] == ngrids - 1)) newtime = ((double)(s[i].cell[k]+1)*SIZE/((double)(ngrids)) - (xi[k]+s[i].r+s[i].gr*gtime))/(vi[k]+s[i].gr); else newtime = ((double)(s[i].cell[k]+1)*SIZE/((double)(ngrids)) - xi[k])/(vi[k]); 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) // out of bounds to right pboffset[k] = 1; else pboffset[k] = 0; } int j = cells.get((cell+offset)%ngrids); 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) { if ((hardwallBC)&&(pboffset.norm_squared() > 1E-12)) { return dblINF; } else { // 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_i = s[i].r + gtime*s[i].gr; double r_now_j = s[j].r + gtime*s[j].gr; double A,B,C; A = vector::norm_squared(vi - vj) - (s[i].gr+s[j].gr)*(s[i].gr+s[j].gr); B = vector::dot(xi - xj, vi - vj) - (r_now_i+r_now_j)*(s[i].gr+s[j].gr); C = vector::norm_squared(xi - xj) - (r_now_i+r_now_j)*(r_now_i+r_now_j); if (C < -1E-12*(r_now_i+r_now_j)) { std::cout << "error, " << i << " and " << j << " are overlapping at time "<< gtime << std::cout; std::cout << "A, B, C = " << A << " " << " " << B << " " << " " << C << std::endl; if (CheckSphereDiameters()>0) std::cout << "a sphere has grown greater than unit cell" << std::endl; else std::cout << "unknown error" << std::cout; 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; } //============================================================== // Returns first event //============================================================== void box::ProcessEvent() { 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 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*(s[i].gr+s[j].gr)*2 + viperp; s[j].v = vipar - dhat*(s[i].gr+s[j].gr)*2 + vjperp; // momentum exchange double xvelocity; // exchanged velocity xvelocity = vector::dot(s[i].v - s[j].v, dhat) - (s[i].gr+s[j].gr); xmomentum += xvelocity*dhatmagnitude*s[i].m*s[j].m*2/(s[i].m+s[j].m); } //============================================================== // 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 (hardwallBC) { // if in right-most cell, reflect back if (s[i].cell[k] == ngrids - 1) s[i].v[k] = -s[i].v[k] - 4.*s[i].gr; else { if (ngrids>1) UpdateCell(i, celli); } } else { // if in right-most cell, translate x and cell if (s[i].cell[k] == ngrids - 1) { s[i].x[k] -= SIZE; celli[k] -= ngrids; } if (ngrids>1) UpdateCell(i, celli); } } else if (j1) UpdateCell(i, celli); } } else { // if in left-most cell, translate x and cell if (s[i].cell[k] == 0) { s[i].x[k] += SIZE; celli[k] += ngrids; } if (ngrids>1) UpdateCell(i, celli); } } else std::cout << "error in Transfer" << std::endl; } //============================================================== // Updates cell of a sphere to time //============================================================= void box::UpdateCell(int i, vector& celli) { 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 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 } } //============================================================== // 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 1./ngrids){ offender = i; break; } } return offender; } //============================================================== // Change ngrids //============================================================== void box::ChangeNgrids(int newngrids) { cells.set_size(newngrids); cells.initialize(-1); // initialize cells to -1 for (int i=0; i