int main(){ cout << "Float size: " << sizeof(flt) << " epsilon: " << std::numeric_limits<flt>::epsilon() << "\n"; assert(std::numeric_limits<flt>::epsilon() < force_max*10); // new random seed each time, for velocities and placement seed(); // Volume of the spheres // Each sphere is σ^d * π/(2d), i.e. π σ^2/4 for 2D, π σ^3/6 for 3D // Total volume of the spheres takes a constant N out front const flt Vs = (Ns * pow(sigma, NDIM) + Nl * pow(sigmal, NDIM)) * M_PI_2 / NDIM; // Initial length of the box is [(volume of spheres) / phi0]^(1/d) const flt L = pow(Vs / phi0, OVERNDIM); cout << "Using L = " << L << "\n"; // Create the bounding box (sides of length L), and a "vector" of Natoms atoms boost::shared_ptr<OriginBox> obox(new OriginBox(L)); // Create a vector of the masses of the atoms // We just use all mass 1, because this is a packing boost::shared_ptr<AtomVec> atomptr(new AtomVec(Nl + Ns, 1.0)); AtomVec & atoms = *atomptr; // Harmonic Interaction // Its called "Repulsion" for historical reasons // It takes a pointer to the box, a pointer to the atoms, and a "skin depth" for the NeighborList boost::shared_ptr<NListed<EpsSigExpAtom, RepulsionPair> > hertzian(new NListed<EpsSigExpAtom, RepulsionPair>(obox, atomptr, 0.1*sigma)); boost::shared_ptr<NeighborList> nl = hertzian->neighbor_list(); // ^ this is the Interaction // Note that NListed is a class template; its an Interaction that // takes various structs as template parameters, and turns them into // a neighbor Interaction // See interaction.hpp for a whole bunch of them // Also note that the NeighborList is not the same as the // "neighborlisted" Interaction; multiple interactions can use the // same NeighborList // Now we run through all the atoms, set their positions / velocities, // and add them to the Repulsion Interaction (i.e., to the neighbor list) for (uint i=0; i < atoms.size(); i++){ atoms[i].x = obox->rand_loc(); // random location in the box atoms[i].v = Vec::Zero(); // A zero-vector atoms[i].f = Vec::Zero(); atoms[i].a = Vec::Zero(); flt sig = i < Ns ? sigma : sigmal; // Add it to the Repulsion potential hertzian->add(EpsSigExpAtom(atoms.get_id(i), epsilon, sig, 2)); // ^ exponent for the harmonic Interaction } // force an update the NeighborList, so we can get an accurate energy nl->update_list(true); cout << "Starting. Neighborlist contains " << nl->numpairs() << " / " << (atoms.size()*(atoms.size()-1))/2 << " pairs\n"; //Now we make our "Collection" CollectionNLCG collec = CollectionNLCG(obox, atomptr, dt, P0); // This is very important! Otherwise the NeighborList won't update! collec.add_tracker(nl); // And add the Interaction collec.add_interaction(hertzian); writefile(atoms, *obox); //Print out total energy, kinetic_energy energy, and potential energy cout << "H: " << collec.hamiltonian() << " K: " << collec.kinetic_energy() << " U: " << hertzian->energy(*obox) << " phi: " << (Vs/obox->V()) << "\n"; // Run the simulation! And every _ steps, write a frame to the .xyz // file and print out the energies again uint i = 0; for(flt curP=startP; curP>P0; curP/=10){ cout << "P: " << curP << "\n"; collec.set_pressure_goal(curP); while (true) { for(uint j=0; j<1000; j++){ collec.timestep(); } i++; writefile(atoms, *obox); flt pdiff = collec.pressure() / curP - 1.0; flt force_err = 0; for(uint k=0; k<atoms.size(); k++){ flt fmag = atoms[k].f.norm(); if(fmag > force_err) force_err = fmag; } cout.precision(sizeof(flt)); cout << i << " H: " << collec.hamiltonian() << " K: " << collec.kinetic_energy() << " U: " << hertzian->energy(*obox) << " phi: " << (Vs/obox->V()) << "\n"; cout.precision(6); cout << " Pdiff: " << pdiff << " force_err: " << force_err << "\n"; if(abs(pdiff) < P_frac and force_err < force_max){ cout << "Done!\n"; break; } } } };
int main(){ // new random seed each time, for velocities and placement seed(); const flt Vs = Natoms * pow(sigma, NDIM) * M_PI_2 / NDIM; const flt L = pow(Vs / phi, OVERNDIM); cout << "Using L = " << L << "\n"; // Create the bounding box (sides of length L), and a "vector" of Natoms atoms boost::shared_ptr<OriginBox> obox(new OriginBox(L)); boost::shared_ptr<AtomVec> atomptr(new AtomVec(Natoms, 1.0)); AtomVec & atoms = *atomptr; // LJ Interaction // We'll cut it off at 2.5σ, and have a NeighborList out to 1.4 times that boost::shared_ptr<NListed<EpsSigCutAtom, LennardJonesCutPair> > LJ(new NListed<EpsSigCutAtom, LennardJonesCutPair>(obox, atomptr, 0.4*(sigcut*sigma))); boost::shared_ptr<NeighborList> nl = LJ->neighbor_list(); // ^ this is the Interaction // Note that NListed is a class template; its an Interaction that // takes various structs as template parameters, and turns them into // a neighbor Interaction // See Interaction.hpp for a whole bunch of them // Also note that the NeighborList is not the same as the // "neighborlisted" Interaction; multiple interactions can use the // same NeighborList // Now we run through all the atoms, set their positions / velocities, // and add them to the LJ Interaction (i.e., to the neighbor list) for (uint i=0; i < atoms.size(); i++){ if((i+1) % 50 == 0) cout << (Natoms - (i+1)) / 50 << ", "; cout.flush(); // we track energy to see if things are overlapping flt E0 = LJ->energy(*obox); atoms[i].x = obox->rand_loc(); // random location in the box atoms[i].v = rand_vec(); // from a Gaussian atoms[i].f = Vec::Zero(); atoms[i].a = Vec::Zero(); // Add it to the Lennard-Jones potential LJ->add(EpsSigCutAtom(atoms.get_id(i), epsilon, sigma, sigcut)); // force an update the NeighborList, so we can get an accurate energy nl->update_list(true); // If we're overlapping too much, try a new location while(LJ->energy(*obox) > E0 + epsilon/2.0){ atoms[i].x = obox->rand_loc(); // random location in the box nl->update_list(true); } } cout << "Starting. Neighborlist contains " << nl->numpairs() << " / " << (atoms.size()*(atoms.size()-1))/2 << " pairs.\n"; //Now we make our "Collection" CollectionVerlet collec = CollectionVerlet(boost::static_pointer_cast<Box>(obox), atomptr, dt); // This is very important! Otherwise the NeighborList won't update! collec.add_tracker(nl); // And add the Interaction collec.add_interaction(LJ); // subtract off center of mass velocity, and set a total energy collec.reset_com_velocity(); collec.scale_velocities_to_energy(Natoms / 4.0); // We'll put the energies to stdout and the coordinates to a new file. // VMD is good for 3D visualization purposes, and it can read .xyz files // see 'writefile' function ofstream outfile; outfile.open("LJatoms.xyz", ios::out); writefile(outfile, atoms, *obox); //Print out total energy, kinetic_energy energy, and potential energy cout << "E: " << collec.energy() << " K: " << collec.kinetic_energy() << " U: " << LJ->energy(*obox) << "\n"; // Run the simulation! And every _ steps, write a frame to the .xyz // file and print out the energies again for(uint i=0; i<500; i++){ for(uint j=0; j<1000; j++){ collec.timestep(); } writefile(outfile, atoms, *obox); cout << (500-i) << " E: " << collec.energy() << " K: " << collec.kinetic_energy() << " U: " << LJ->energy(*obox) << "\n"; } // Unnecessary extra: // Write a "tcl" file with the box boundaries // the "tcl" file is made specifically for VMD ofstream pbcfile; pbcfile.open("LJatoms-pbc.tcl", ios::out); pbcfile << "set cell [pbc set {"; for(uint j=0; j<NDIM; j++){ pbcfile << obox->box_shape()[j] << " "; } pbcfile << "} -all];\n"; pbcfile << "pbc box -toggle -center origin -color red;\n"; // Now you should be able to run "vmd -e LJatoms-pbc.tcl LJatoms.xyz" // and it will show you the movie and also the bounding box // if you have .vmdrc in that same directory, you should also be able // to toggle the box with the "b" button cout << "Done. Neighborlist contains " << nl->numpairs() << " / " << (atoms.size()*(atoms.size()-1))/2 << " pairs.\n"; };
obox obox::transform(const mtx& m) const { return obox(x1y1z1 * m, x2y1z1 * m, x1y2z1 * m, x2y2z1 * m, x1y1z2 * m, x1y1z2 * m, x1y2z2 * m, x2y2z2 * m); }