#include #include #include #include #include #include #include #include #include #include #include using namespace std; using Eigen::Vector2d; namespace plt = matplotlibcpp; #define PI GSL_CONST_NUM_PI #define kb GSL_CONST_MKSA_BOLTZMANN #define sigma 3.405e-10 #define epsilon 1.65e-21 #define mass (6.63382e-26 / 39.95) #define tem (epsilon / kb) #define vel sqrt(epsilon / mass) #define tim (sigma * sqrt(mass / epsilon)) #define M 39.95 bool debug; int N, W, Epoch; double L, T, dt, eps; class Molecule { public: Molecule(){}; Molecule(double m, Vector2d r0, Vector2d v0) { r = r0; v = v0; cnt = 0; length = 0; lastCrash = 0; this->m = m; }; ~Molecule(){}; public: int cnt, lastCrash; double m, length; Vector2d a, aPre, r, v; public: void warp() { r[0] -= L * floor(r[0] / L); r[1] -= L * floor(r[1] / L); } void crash(int index) { cnt += 1; length += v.norm() * (index - lastCrash) * dt; lastCrash = index; } void stretch(double factor) { v *= factor; } }; Vector2d separateDistance(Vector2d ra, Vector2d rb) { double dx = ra[0] - rb[0]; double dy = ra[1] - rb[1]; if (dx > L / 2) dx -= L; else if (dx < -L / 2) dx += L; if (dy > L / 2) dy -= L; else if (dy < -L / 2) dy += L; return Vector2d(dx, dy); } double lennardJones(Vector2d r) { return 4 * (pow(1. / r.norm(), 12) - pow(1. / r.norm(), 6)); } Vector2d lennardJonesFource(Vector2d r) { double fm = 48 * (pow(1. / r.norm(), 14) - 0.5 * pow(1. / r.norm(), 8)); return fm * r; } double getKinetic(vector mol) { double Ek = 0; for (int i = 0; i < N; i++) Ek += 0.5 * mol[i].m * mol[i].v.squaredNorm(); return Ek; } double getPotential(vector mol) { double Vr = 0; for (int i = 0; i < N; i++) for (int j = i + 1; j < N; j++) Vr += lennardJones(separateDistance(mol[i].r, mol[j].r)); return Vr; } double getEnergy(vector mol) { double Ek = getKinetic(mol); double Vr = getPotential(mol); return Ek + Vr; } double getTemperature(vector mol) { double Ek = getKinetic(mol); return Ek / (N - 1); } double getPressure(vector mol) { double Te = getTemperature(mol); double p = N * Te; Vector2d r, f; for (int i = 1; i < N; i++) for (int j = 0; j < i; j++) { r = separateDistance(mol[i].r, mol[j].r); f = lennardJonesFource(r); p += r.dot(f) / 2; } return p / pow(L, 2); } int main(int argc, char const *argv[]) { auto args = util::argparser("Argon MD Simulate"); args.set_program_name("ComSim") .add_help_option() .add_argument("length", "length of box in sigma") .add_argument("number", "number of particles") .add_argument("temperature", "temperature in Kelvin") .add_option("-D", "--debug", "debug mode") .add_option("-E", "--epoch", "maximum number of rounds simulated,\ndefault is 10000", 10000) .add_option("-W", "--width", "width of window to get average,\ndefault is 1000", 1000) .add_option("", "--dt", "delta t to simualte,\ndefault is 0.01", 0.01) .add_option("", "--eps", "error t0 end simualte,\ndefault is 0.01", 0.01) .parse(argc, argv); L = args.get_argument_double("length"); N = args.get_argument_int("number"); T = args.get_argument_double("temperature") / tem; debug = args.get_option_bool("-D"); Epoch = args.get_option_int("-E"); W = min(args.get_option_int("-W"), Epoch); dt = args.get_option_double("--dt"); eps = args.get_option_double("--eps"); int cnt = 0; double fac, eqT, eqE, eqP; vector argon(N); Vector2d Vc = Vector2d(0, 0); vector x(N), y(N); vector Ti, Te, MTe, En, MEn, Pr, MPr; default_random_engine seed(time(NULL)); uniform_real_distribution u01(0, 1); normal_distribution gauss(0, sqrt(T * tem * kb / (M * mass))); int NM = sqrt(N); double H = (L - 1.) / (NM - 1); for (int i = 0; i < N; i++) { double vx = gauss(seed) / vel; double vy = gauss(seed) / vel; argon[i] = Molecule(M, Vector2d((i / NM) * H + 0.5, (i % NM) * H + 0.5), Vector2d(vx, vy)); } for (int i = 0; i < N; i++) Vc += argon[i].v; Vc /= N; for (int i = 0; i < N; i++) argon[i].v -= Vc; eqT = getTemperature(argon); do { fac = sqrt(T / eqT); for (int i = 0; i < N; i++) { argon[i].stretch(fac); argon[i].a = Vector2d(0, 0); for (int j = i + 1; j < N; j++) { Vector2d f = lennardJonesFource(separateDistance(argon[i].r, argon[j].r)); argon[i].a += f; argon[j].a -= f; } argon[i].a /= argon[i].m; } Ti.push_back(Epoch * cnt * dt); Te.push_back(getTemperature(argon)); En.push_back(getEnergy(argon)); Pr.push_back(getPressure(argon)); MTe.push_back(Te[Epoch * cnt]); MEn.push_back(En[Epoch * cnt]); MPr.push_back(Pr[Epoch * cnt]); for (int i = 1; i < Epoch; i++) { for (int j = 0; j < N; j++) { argon[j].r += argon[j].v * dt + argon[j].a * pow(dt, 2) / 2; argon[j].aPre = argon[j].a; argon[j].a = Vector2d(0, 0); } for (int j = 0; j < N; j++) { for (int k = j + 1; k < N; k++) { Vector2d f = lennardJonesFource(separateDistance(argon[j].r, argon[k].r)); argon[j].a += f; argon[k].a -= f; } argon[j].a /= argon[j].m; argon[j].v += (argon[j].aPre + argon[j].a) * dt / 2; argon[j].warp(); } int k = i + Epoch * cnt; Ti.push_back(k * dt); Te.push_back(getTemperature(argon)); En.push_back(getEnergy(argon)); Pr.push_back(getPressure(argon)); if (i >= W) { MTe.push_back((MTe[k - 1] * W + Te[k] - Te[k - W]) / W); MEn.push_back((MEn[k - 1] * W + En[k] - En[k - W]) / W); MPr.push_back((MPr[k - 1] * W + Pr[k] - Pr[k - W]) / W); } else { MTe.push_back((MTe[k - 1] * i + Te[k]) / (i + 1)); MEn.push_back((MEn[k - 1] * i + En[k]) / (i + 1)); MPr.push_back((MPr[k - 1] * i + Pr[k]) / (i + 1)); } } cnt += 1; eqT = average(cutArrs(MTe, Epoch * cnt - W, Epoch * cnt - 1), W); eqE = average(cutArrs(MEn, Epoch * cnt - W, Epoch * cnt - 1), W); eqP = average(cutArrs(MPr, Epoch * cnt - W, Epoch * cnt - 1), W); if (debug) cout << eqT * tem << ", " << abs(eqT - T) / T << ", " << eqE << ", " << eqP << endl; if (abs(eqT - T) / T < eps) break; } while (1); cout << eqT * tem << ", " << abs(eqT - T) / T << ", " << eqE << ", " << eqP << endl; plt::figure_size(900, 600); plt::subplot(3, 1, 1); plt::named_plot("Instant", Ti, Te); plt::named_plot("Average", Ti, MTe); plt::legend(); plt::title("Equilibrium Temperature : " + to_string(eqT)); plt::xlabel("Time / ($\\sigma\\sqrt{m/\\epsilon}$)"); plt::ylabel("Temperature / ($\\epsilon/k_b$)"); plt::subplot(3, 1, 2); plt::named_plot("Instant", Ti, En); plt::named_plot("Average", Ti, MEn); plt::legend(); plt::title("Equilibrium Energy : " + to_string(eqE)); plt::xlabel("Time / ($\\sigma\\sqrt{m/\\epsilon}$)"); plt::ylabel("Energy / ($\\epsilon$)"); plt::subplot(3, 1, 3); plt::named_plot("Instant", Ti, Pr); plt::named_plot("Average", Ti, MPr); plt::legend(); plt::title("Equilibrium Pressure : " + to_string(eqP)); plt::xlabel("Time / ($\\sigma\\sqrt{m/\\epsilon}$)"); plt::ylabel("Pressure / ($\\epsilon/\\sigma^2$)"); plt::suptitle("Argon N = " + to_string(N)); plt::tight_layout(); plt::save("result.png"); plt::show(); return 0; } /* Unit of quantities: Length : σ (Lennard-Jones parameters) Energy : ε (Lennard-Jones parameters) Masses : m (mass of the atom) Velocity : sqrt(ε / m) Time : σ * sqrt(m / ε) Temperature : ε / k (k is Boltzmann's constant) */ /* 2D Maxwell Distribution normal_distribution gauss(0, sqrt(T * kb / m)); vector x(100000); for (int i = 0; i < 100000; i++) x[i] = sqrt(pow(gauss(seed), 2) + pow(gauss(seed), 2)); plt::hist(x, 100); plt::show(); */