/** * engines/cpp/main.cpp * -------------------- * C++ 动力学模拟引擎。 * 与 Python 版 (compute.py) 算法保持一致。 * * 输入: param.json, /coord.txt, connection.txt, bond.txt * 输出: /trajectory.txt (JSON, 与 Python 版兼容) * * 编译: * g++ -O3 -march=native -std=c++17 -o build/dynamics_cpp main.cpp * * 用法: * ./build/dynamics_cpp */ #include #include #include #include #include #include #include #include #include #include // ======================================================================== // 配置参数(从 param.json 读取) // ======================================================================== struct SimParams { double box_a = 10.0; int NT = 10000; double DT = 0.001; int NSTEP = 100; int warmup_steps = 0; std::string method = "leapfrog"; double G[3] = {0, 0, -9.8}; double B[3] = {0, 0, 0}; int gravity_field = 1; int gravity_interaction = 0; int elastic_force = 1; int damping_force = 0; double gravity_strength = 1.0; }; // ======================================================================== // 原子数据 // ======================================================================== struct AtomData { std::vector ids; std::vector masses; std::vector radii; std::vector pos_0; // (n_atoms * 3) std::vector vel_0; // (n_atoms * 3) std::vector fixed; // (n_atoms * 3), 0/1 flags }; // ======================================================================== // 成键数据 // ======================================================================== struct BondData { std::vector pairs; // (n_bonds * 2) std::vector stiffness; std::vector rest_lengths; }; // ======================================================================== // 辅助函数 // ======================================================================== static void die(const std::string &msg) { std::cerr << "[C++-engine] 错误: " << msg << std::endl; exit(1); } /* 读整个文件为字符串 */ static std::string read_file(const std::string &path) { std::ifstream f(path, std::ios::binary); if (!f) die("无法打开 " + path); std::ostringstream ss; ss << f.rdbuf(); return ss.str(); } /* 从 JSON 中查找 key,返回冒号后的数值 */ static double json_read_double(const std::string &json, const std::string &key) { auto pos = json.find("\"" + key + "\""); if (pos == std::string::npos) return 0.0; pos = json.find(':', pos); if (pos == std::string::npos) return 0.0; while (pos < json.size() && (json[pos] == ':' || json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\n')) pos++; return std::stod(json.substr(pos)); } static int json_read_int(const std::string &json, const std::string &key) { return static_cast(json_read_double(json, key)); } /* 从 JSON 中读取字符串值 */ static std::string json_read_string(const std::string &json, const std::string &key) { auto pos = json.find("\"" + key + "\""); if (pos == std::string::npos) return ""; pos = json.find(':', pos); if (pos == std::string::npos) return ""; while (pos < json.size() && (json[pos] == ':' || json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\n')) pos++; if (pos >= json.size()) return ""; // 找到引号 if (json[pos] != '"') return ""; pos++; std::string result; while (pos < json.size() && json[pos] != '"') { result += json[pos]; pos++; } return result; } /* 读取 JSON 数组 (如 "G": [0, 0, -9.8]) 到 double[3] */ static void json_read_double3(const std::string &json, const std::string &key, double out[3]) { auto pos = json.find("\"" + key + "\""); if (pos == std::string::npos) { out[0] = out[1] = out[2] = 0; return; } pos = json.find('[', pos); if (pos == std::string::npos) { out[0] = out[1] = out[2] = 0; return; } pos++; for (int i = 0; i < 3; i++) { while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' || json[pos] == '\n' || json[pos] == ',')) pos++; char *end; out[i] = std::strtod(json.c_str() + pos, &end); pos = end - json.c_str(); } } /* 解析 param.json */ static SimParams read_params(const std::string &path) { std::string buf = read_file(path); SimParams p; p.box_a = json_read_double(buf, "box_a"); p.NT = json_read_int(buf, "NT"); p.DT = json_read_double(buf, "DT"); p.NSTEP = json_read_int(buf, "NSTEP"); p.warmup_steps = json_read_int(buf, "warmup_steps"); std::string m = json_read_string(buf, "method"); if (!m.empty()) p.method = m; json_read_double3(buf, "G", p.G); json_read_double3(buf, "B", p.B); p.gravity_field = json_read_int(buf, "gravity_field"); p.gravity_interaction = json_read_int(buf, "gravity_interaction"); p.elastic_force = json_read_int(buf, "elastic_force"); p.damping_force = json_read_int(buf, "damping_force"); p.gravity_strength = json_read_double(buf, "gravity_strength"); return p; } /* 读取 coord.txt */ static AtomData read_coord(const std::string &input_dir) { std::string path = input_dir + "/coord.txt"; std::ifstream f(path); if (!f) die("无法打开 " + path); std::string header; std::getline(f, header); // 跳过表头 AtomData a; int id, fx, fy, fz; double mass, rad, px, py, pz, vx, vy, vz; std::string line; while (std::getline(f, line)) { if (line.empty() || line[0] == '#') continue; int n_parsed = std::sscanf(line.c_str(), "%d %lf %lf %lf %lf %lf %lf %lf %lf %d %d %d", &id, &mass, &rad, &px, &py, &pz, &vx, &vy, &vz, &fx, &fy, &fz); if (n_parsed == 9) { fx = fy = fz = 0; } else if (n_parsed != 12) { continue; } a.ids.push_back(id); a.masses.push_back(mass); a.radii.push_back(rad); a.pos_0.push_back(px); a.pos_0.push_back(py); a.pos_0.push_back(pz); a.vel_0.push_back(vx); a.vel_0.push_back(vy); a.vel_0.push_back(vz); a.fixed.push_back(fx); a.fixed.push_back(fy); a.fixed.push_back(fz); } if (a.ids.empty()) die("coord.txt 中没有原子数据"); return a; } /* 读取 connection.txt 和 bond.txt */ static BondData read_bonds(const std::string &input_dir) { BondData b; std::string conn_path = input_dir + "/connection.txt"; std::ifstream f(conn_path); if (!f) return b; // 无成键 std::string header; std::getline(f, header); // 跳过表头 // 先读取 bond.txt 获得键参数映射 std::string bond_path = input_dir + "/bond.txt"; std::ifstream fb(bond_path); int a1, a2; std::string bond_name; std::vector> conn_lines; while (f >> a1 >> a2 >> bond_name) { conn_lines.emplace_back(a1 - 1, a2 - 1, bond_name); } for (auto &[i, j, name] : conn_lines) { double k = 1.0, r0 = 2.0; if (fb) { fb.clear(); fb.seekg(0); std::string bn, header; double bk, br; std::getline(fb, header); // 跳过表头行 while (fb >> bn >> bk >> br) { if (bn == name) { k = bk; r0 = br; break; } } } b.pairs.push_back(i); b.pairs.push_back(j); b.stiffness.push_back(k); b.rest_lengths.push_back(r0); } return b; } // ======================================================================== // 物理核心 // ======================================================================== /* 加速度计算(各力独立开关控制)——与 Python compute_acceleration 一致 */ static void compute_acceleration( int n, const double *x, const double *y, const double *z, const double *vx, const double *vy, const double *vz, const double *m, const double G[3], const double B[3], const BondData &bonds, int gravity_field, int gravity_interaction, int elastic_force, int damping_force, double gravity_strength, double *ax, double *ay, double *az) { // 清零 std::fill(ax, ax + n, 0.0); std::fill(ay, ay + n, 0.0); std::fill(az, az + n, 0.0); // 均匀重力场 if (gravity_field) { for (int i = 0; i < n; i++) { ax[i] += G[0]; ay[i] += G[1]; az[i] += G[2]; } } // 阻尼 if (damping_force) { for (int i = 0; i < n; i++) { ax[i] -= B[0] * vx[i] / m[i]; ay[i] -= B[1] * vy[i] / m[i]; az[i] -= B[2] * vz[i] / m[i]; } } // 弹簧力 if (elastic_force) { int nb = static_cast(bonds.stiffness.size()); for (int b = 0; b < nb; b++) { int i = bonds.pairs[b * 2]; int j = bonds.pairs[b * 2 + 1]; double dx = x[j] - x[i]; double dy = y[j] - y[i]; double dz = z[j] - z[i]; double dist = std::sqrt(dx * dx + dy * dy + dz * dz); if (dist < 1e-12) continue; double stretch = dist - bonds.rest_lengths[b]; double fmag = bonds.stiffness[b] * stretch; double ux = dx / dist, uy = dy / dist, uz = dz / dist; double fx = fmag * ux, fy = fmag * uy, fz = fmag * uz; ax[i] += fx / m[i]; ay[i] += fy / m[i]; az[i] += fz / m[i]; ax[j] -= fx / m[j]; ay[j] -= fy / m[j]; az[j] -= fz / m[j]; } } // 万有引力(所有原子对之间) if (gravity_interaction) { for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { double dx = x[j] - x[i]; double dy = y[j] - y[i]; double dz = z[j] - z[i]; double r2 = dx * dx + dy * dy + dz * dz; if (r2 <= 1e-12) continue; double r = std::sqrt(r2); double f_mag = gravity_strength * m[i] * m[j] / r2; double fx_g = f_mag * dx / r; double fy_g = f_mag * dy / r; double fz_g = f_mag * dz / r; ax[i] += fx_g / m[i]; ay[i] += fy_g / m[i]; az[i] += fz_g / m[i]; ax[j] -= fx_g / m[j]; ay[j] -= fy_g / m[j]; az[j] -= fz_g / m[j]; } } } } /* 边界条件:clamp 位置 + 速度反转 ——与 Python Limit_in_box 一致 */ static void limit_in_box(double &pos, double &vel, double lo, double hi) { if (pos > hi) { pos = hi; vel = -vel; } if (pos < lo) { pos = lo; vel = -vel; } } // ======================================================================== // 四种积分方法(只做位置/速度更新,不含边界条件) // 与 Python: Explicit_Euler_Method / Implicit_Euler_Method / // Midpoint_Method / Leapfrog_Method 保持一致 // ======================================================================== /* ── 显式欧拉法 ──────────── */ static void explicit_euler_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const double G[3], const double B[3], const BondData &bonds, const int *fixed, double dt, int gravity_field, int gravity_interaction, int elastic_force, int damping_force, double gravity_strength) { std::vector ax(n), ay(n), az(n); compute_acceleration(n, x, y, z, vx, vy, vz, m, G, B, bonds, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength, ax.data(), ay.data(), az.data()); for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue; x[i] += vx[i] * dt; y[i] += vy[i] * dt; z[i] += vz[i] * dt; vx[i] += ax[i] * dt; vy[i] += ay[i] * dt; vz[i] += az[i] * dt; } } /* ── 隐式欧拉法 ──────────── */ static void implicit_euler_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const double G[3], const double B[3], const BondData &bonds, const int *fixed, double dt, int gravity_field, int gravity_interaction, int elastic_force, int damping_force, double gravity_strength) { std::vector ax(n), ay(n), az(n); for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) { ax[i] = ay[i] = az[i] = 0; continue; } double gamma_x = B[0] / m[i]; double gamma_y = B[1] / m[i]; double gamma_z = B[2] / m[i]; // 隐式更新速度(重力 + 阻尼) double vxn = (vx[i] + G[0] * dt) / (1.0 + gamma_x * dt); double vyn = (vy[i] + G[1] * dt) / (1.0 + gamma_y * dt); double vzn = (vz[i] + G[2] * dt) / (1.0 + gamma_z * dt); // 用隐式速度 + 当前位置算加速度(包含各力开关) // 注意:Python 中 compute_acceleration(x, y, z, vx_next, ...) 用新速度+旧位置 double tpx = x[i], tpy = y[i], tpz = z[i]; compute_acceleration(1, &tpx, &tpy, &tpz, &vxn, &vyn, &vzn, &m[i], G, B, bonds, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength, &ax[i], &ay[i], &az[i]); vx[i] += ax[i] * dt; vy[i] += ay[i] * dt; vz[i] += az[i] * dt; x[i] += vx[i] * dt; y[i] += vy[i] * dt; z[i] += vz[i] * dt; } } /* ── 中点法 ──────────── */ static void midpoint_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const double G[3], const double B[3], const BondData &bonds, const int *fixed, double dt, int gravity_field, int gravity_interaction, int elastic_force, int damping_force, double gravity_strength) { std::vector ax(n), ay(n), az(n); compute_acceleration(n, x, y, z, vx, vy, vz, m, G, B, bonds, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength, ax.data(), ay.data(), az.data()); for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue; double x_mid = x[i] + 0.5 * vx[i] * dt; double y_mid = y[i] + 0.5 * vy[i] * dt; double z_mid = z[i] + 0.5 * vz[i] * dt; double vx_mid = vx[i] + 0.5 * ax[i] * dt; double vy_mid = vy[i] + 0.5 * ay[i] * dt; double vz_mid = vz[i] + 0.5 * az[i] * dt; x[i] += vx_mid * dt; y[i] += vy_mid * dt; z[i] += vz_mid * dt; double ax_mid, ay_mid, az_mid; compute_acceleration(1, &x_mid, &y_mid, &z_mid, &vx_mid, &vy_mid, &vz_mid, &m[i], G, B, bonds, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength, &ax_mid, &ay_mid, &az_mid); vx[i] += ax_mid * dt; vy[i] += ay_mid * dt; vz[i] += az_mid * dt; } } /* ── 蛙跳法(Velocity-Verlet)——与 Python Leapfrog_Method 一致 ── */ static void leapfrog_full_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const double G[3], const double B[3], const BondData &bonds, const int *fixed, double dt, int gravity_field, int gravity_interaction, int elastic_force, int damping_force, double gravity_strength) { // 第一次加速度 std::vector ax(n), ay(n), az(n); compute_acceleration(n, x, y, z, vx, vy, vz, m, G, B, bonds, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength, ax.data(), ay.data(), az.data()); // 半推速度:v_half = v + 0.5*a*dt (存入 vx, vy, vz) for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue; vx[i] += ax[i] * dt * 0.5; vy[i] += ay[i] * dt * 0.5; vz[i] += az[i] * dt * 0.5; } // 全推位置(不含边界,边界在外层统一处理) for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue; x[i] += vx[i] * dt; // vx 此时是 v_half y[i] += vy[i] * dt; z[i] += vz[i] * dt; } // 显式预测器:v_pred = v_half + 0.5*a_old*dt,用第一次加速度外推半步 // 包含所有力的贡献(标准 Velocity-Verlet 预测步) std::vector pred_vx(n), pred_vy(n), pred_vz(n); for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue; pred_vx[i] = vx[i] + 0.5 * ax[i] * dt; pred_vy[i] = vy[i] + 0.5 * ay[i] * dt; pred_vz[i] = vz[i] + 0.5 * az[i] * dt; } // 用新位置 + 预测速度重算加速度 compute_acceleration(n, x, y, z, pred_vx.data(), pred_vy.data(), pred_vz.data(), m, G, B, bonds, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength, ax.data(), ay.data(), az.data()); // 速度后半步:v = v_half + 0.5*a_next*dt // vx 仍为 v_half(未被覆盖),直接加上 0.5*a_next*dt for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue; vx[i] += ax[i] * dt * 0.5; vy[i] += ay[i] * dt * 0.5; vz[i] += az[i] * dt * 0.5; } } /* ── 分发器:调用对应积分方法 + 边界条件(与 Python apply_motion_update 一致)── */ static void apply_step( const std::string &method, int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const double G[3], const double B[3], const BondData &bonds, const int *fixed, const double *pos_0, double box_a, double dt, int gravity_field, int gravity_interaction, int elastic_force, int damping_force, double gravity_strength) { // 积分 if (method == "explicit_euler") { explicit_euler_step(n, x, y, z, vx, vy, vz, m, G, B, bonds, fixed, dt, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength); } else if (method == "implicit_euler") { implicit_euler_step(n, x, y, z, vx, vy, vz, m, G, B, bonds, fixed, dt, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength); } else if (method == "midpoint") { midpoint_step(n, x, y, z, vx, vy, vz, m, G, B, bonds, fixed, dt, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength); } else if (method == "leapfrog") { leapfrog_full_step(n, x, y, z, vx, vy, vz, m, G, B, bonds, fixed, dt, gravity_field, gravity_interaction, elastic_force, damping_force, gravity_strength); } else { die("未知算法: " + method); } // 边界条件(与 Python Limit_in_box 一致) for (int i = 0; i < n; i++) { if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue; limit_in_box(x[i], vx[i], -box_a, box_a); limit_in_box(y[i], vy[i], -box_a, box_a); limit_in_box(z[i], vz[i], -box_a, box_a); } // 逐自由度固定约束(与 Python apply_fixed_constraints 一致) for (int i = 0; i < n; i++) { if (fixed[i*3+0]) { x[i] = pos_0[i*3]; vx[i] = 0.0; } if (fixed[i*3+1]) { y[i] = pos_0[i*3+1]; vy[i] = 0.0; } if (fixed[i*3+2]) { z[i] = pos_0[i*3+2]; vz[i] = 0.0; } } } // ======================================================================== // JSON 输出 // ======================================================================== static void write_trajectory_json( const std::string &path, const std::vector &x, const std::vector &y, const std::vector &z, const std::vector &vx, const std::vector &vy, const std::vector &vz, int n_steps, int n_atoms, const SimParams ¶ms, const AtomData &atoms, const BondData &bonds) { std::ofstream f(path); if (!f) die("无法写入 " + path); f << std::setprecision(15); f << "{\n"; // 轨迹数组 const std::string names[] = {"traj_x","traj_y","traj_z","traj_vx","traj_vy","traj_vz"}; const std::vector *arrs[] = {&x, &y, &z, &vx, &vy, &vz}; for (int a = 0; a < 6; a++) { f << " \"" << names[a] << "\": [\n"; const auto &data = *arrs[a]; for (int t = 0; t < n_steps; t++) { f << " ["; for (int i = 0; i < n_atoms; i++) { f << data[t * n_atoms + i]; if (i < n_atoms - 1) f << ','; } f << ']'; if (t < n_steps - 1) f << ','; f << '\n'; } f << " ],\n"; } // 标量参数 f << " \"NT\": " << params.NT << ",\n"; f << " \"DT\": " << params.DT << ",\n"; f << " \"NSTEP\": " << params.NSTEP << ",\n"; f << " \"method\": \"" << params.method << "\",\n"; f << " \"warmup_steps\": " << params.warmup_steps << ",\n"; f << " \"G\": [" << params.G[0] << ", " << params.G[1] << ", " << params.G[2] << "],\n"; f << " \"B\": [" << params.B[0] << ", " << params.B[1] << ", " << params.B[2] << "],\n"; // 原子信息 f << " \"atom_ids\": ["; for (size_t i = 0; i < atoms.ids.size(); i++) { if (i > 0) f << ','; f << atoms.ids[i]; } f << "],\n"; f << " \"atom_masses\": ["; for (size_t i = 0; i < atoms.masses.size(); i++) { if (i > 0) f << ','; f << atoms.masses[i]; } f << "],\n"; // 成键 f << " \"bond_pairs\": ["; for (size_t b = 0; b < bonds.stiffness.size(); b++) { if (b > 0) f << ','; f << "[" << bonds.pairs[b * 2] << ", " << bonds.pairs[b * 2 + 1] << "]"; } f << "],\n"; f << " \"bond_stiffness\": ["; for (size_t b = 0; b < bonds.stiffness.size(); b++) { if (b > 0) f << ','; f << bonds.stiffness[b]; } f << "],\n"; f << " \"bond_rest_lengths\": ["; for (size_t b = 0; b < bonds.rest_lengths.size(); b++) { if (b > 0) f << ','; f << bonds.rest_lengths[b]; } f << "]\n"; f << "}\n"; } // ======================================================================== // 主函数 // ======================================================================== int main(int argc, char **argv) { if (argc < 4) { std::cerr << "用法: " << argv[0] << " " << std::endl; return 1; } std::string input_dir = argv[1]; std::string output_dir = argv[2]; std::string param_path = argv[3]; auto t0 = std::chrono::high_resolution_clock::now(); // 读取参数和输入 SimParams params = read_params(param_path); AtomData atoms = read_coord(input_dir); BondData bonds = read_bonds(input_dir); std::cout << "[C++-engine] 原子数=" << atoms.ids.size() << ", 键数=" << bonds.stiffness.size() << ", NT=" << params.NT << ", DT=" << params.DT << ", method=" << params.method << std::endl; int n = static_cast(atoms.ids.size()); // 初始化位置/速度 std::vector x(n), y(n), z(n), vx(n), vy(n), vz(n); for (int i = 0; i < n; i++) { x[i] = atoms.pos_0[i * 3]; y[i] = atoms.pos_0[i * 3 + 1]; z[i] = atoms.pos_0[i * 3 + 2]; vx[i] = atoms.vel_0[i * 3]; vy[i] = atoms.vel_0[i * 3 + 1]; vz[i] = atoms.vel_0[i * 3 + 2]; } // 分配轨迹缓冲区 int record_steps = params.NT - params.warmup_steps; std::vector traj_x(record_steps * n); std::vector traj_y(record_steps * n); std::vector traj_z(record_steps * n); std::vector traj_vx(record_steps * n); std::vector traj_vy(record_steps * n); std::vector traj_vz(record_steps * n); // 预热 for (int s = 0; s < params.warmup_steps; s++) { apply_step(params.method, n, x.data(), y.data(), z.data(), vx.data(), vy.data(), vz.data(), atoms.masses.data(), params.G, params.B, bonds, atoms.fixed.data(), atoms.pos_0.data(), params.box_a, params.DT, params.gravity_field, params.gravity_interaction, params.elastic_force, params.damping_force, params.gravity_strength); } // 记录 for (int s = 0; s < record_steps; s++) { // 保存当前帧 for (int i = 0; i < n; i++) { traj_x[s * n + i] = x[i]; traj_y[s * n + i] = y[i]; traj_z[s * n + i] = z[i]; traj_vx[s * n + i] = vx[i]; traj_vy[s * n + i] = vy[i]; traj_vz[s * n + i] = vz[i]; } apply_step(params.method, n, x.data(), y.data(), z.data(), vx.data(), vy.data(), vz.data(), atoms.masses.data(), params.G, params.B, bonds, atoms.fixed.data(), atoms.pos_0.data(), params.box_a, params.DT, params.gravity_field, params.gravity_interaction, params.elastic_force, params.damping_force, params.gravity_strength); } // 输出轨迹 std::string out_path = output_dir + "/trajectory.txt"; write_trajectory_json(out_path, traj_x, traj_y, traj_z, traj_vx, traj_vy, traj_vz, record_steps, n, params, atoms, bonds); auto t1 = std::chrono::high_resolution_clock::now(); double elapsed = std::chrono::duration(t1 - t0).count(); std::cout << "[C++-engine] 计算完成: " << record_steps << " 步, " << elapsed << " s" << std::endl; return 0; }