/** * engines/c/main.c * ----------------- * C 语言动力学模拟引擎。 * 输入: param.json 数值参数(Python 从 YAML 转换得来) * /coord.txt * /connection.txt * /bond.txt * 输出: /trajectory.txt (JSON 格式,与 Python 版兼容) * * 编译: make * 用法: ./build/dynamics_c */ #include #include #include #include #include /* ======================================================================== * 配置参数(从 param.json 读取) * ======================================================================== */ typedef struct { double box_a; /* 盒子半边长 */ int NT; /* 总步数 */ double DT; /* 时间步长 */ int NSTEP; /* 抽帧间隔 */ int warmup_steps; /* 预热步数 */ double G[3]; /* 重力分量 */ double B[3]; /* 阻尼分量 */ } SimParams; /* ======================================================================== * 原子数据 * ======================================================================== */ typedef struct { int n_atoms; int *atom_ids; double *masses; double *radii; double *pos_0; /* 初始位置 (n_atoms*3) */ double *vel_0; /* 初始速度 (n_atoms*3) */ int *fixed; /* 固定约束 (n_atoms*3) */ } AtomData; /* ======================================================================== * 成键数据 * ======================================================================== */ typedef struct { int n_bonds; int *pairs; /* (n_bonds*2) */ double *stiffness; double *rest_lengths; } BondData; /* ======================================================================== * 轨迹缓冲区 * ======================================================================== */ typedef struct { int n_steps; int n_atoms; double *x, *y, *z; double *vx, *vy, *vz; } Trajectory; /* ======================================================================== * 辅助函数 * ======================================================================== */ static void die(const char *msg) { fprintf(stderr, "[C-engine] 错误: %s\n", msg); exit(1); } static void *xmalloc(size_t sz) { void *p = malloc(sz); if (!p) die("内存分配失败"); return p; } /* 从 JSON 中读取 double 值。简单实现:搜索 key 并解析 */ static double json_read_double(const char *json, const char *key) { char search[256]; snprintf(search, sizeof(search), "\"%s\"", key); const char *p = strstr(json, search); if (!p) return 0.0; p = strchr(p, ':'); if (!p) return 0.0; p++; while (*p == ' ' || *p == '\t' || *p == '\n') p++; return strtod(p, NULL); } static int json_read_int(const char *json, const char *key) { return (int)json_read_double(json, key); } /* 读取 JSON 数组 (如 "G": [0, 0, -9.8]) 到 double[3] */ static void json_read_double3(const char *json, const char *key, double out[3]) { char search[256]; snprintf(search, sizeof(search), "\"%s\"", key); const char *p = strstr(json, search); if (!p) { out[0]=out[1]=out[2]=0; return; } p = strchr(p, '['); if (!p) { out[0]=out[1]=out[2]=0; return; } p++; for (int i = 0; i < 3; i++) { while (*p == ' ' || *p == '\t' || *p == '\n' || *p == ',') p++; out[i] = strtod(p, (char**)&p); } } /* 读取 param.json */ static SimParams read_params(const char *path) { FILE *f = fopen(path, "rb"); if (!f) die("无法打开 param.json"); fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET); char *buf = (char*)xmalloc(sz + 1); fread(buf, 1, sz, f); buf[sz] = '\0'; fclose(f); 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"); json_read_double3(buf, "G", p.G); json_read_double3(buf, "B", p.B); free(buf); return p; } /* 读取 coord.txt */ static AtomData read_coord(const char *input_dir) { char path[512]; snprintf(path, sizeof(path), "%s/coord.txt", input_dir); FILE *f = fopen(path, "r"); if (!f) die("无法打开 coord.txt"); /* 跳过第一行表头 */ char line[1024]; if (!fgets(line, sizeof(line), f)) die("coord.txt 为空"); /* 先读入所有行到内存 */ int capacity = 16; AtomData a; a.n_atoms = 0; a.atom_ids = (int*)xmalloc(capacity * sizeof(int)); a.masses = (double*)xmalloc(capacity * sizeof(double)); a.radii = (double*)xmalloc(capacity * sizeof(double)); a.pos_0 = (double*)xmalloc(capacity * 3 * sizeof(double)); a.vel_0 = (double*)xmalloc(capacity * 3 * sizeof(double)); a.fixed = (int*)xmalloc(capacity * 3 * sizeof(int)); while (fgets(line, sizeof(line), f)) { if (a.n_atoms >= capacity) { capacity *= 2; a.atom_ids = realloc(a.atom_ids, capacity * sizeof(int)); a.masses = realloc(a.masses, capacity * sizeof(double)); a.radii = realloc(a.radii, capacity * sizeof(double)); a.pos_0 = realloc(a.pos_0, capacity * 3 * sizeof(double)); a.vel_0 = realloc(a.vel_0, capacity * 3 * sizeof(double)); a.fixed = realloc(a.fixed, capacity * 3 * sizeof(int)); } int id, fx, fy, fz; double mass, rad, px, py, pz, vx, vy, vz; int n_parsed = sscanf(line, "%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; /* 跳过空行/格式不符的行 */ } int i = a.n_atoms; a.atom_ids[i] = id; a.masses[i] = mass; a.radii[i] = rad; a.pos_0[i*3+0] = px; a.pos_0[i*3+1] = py; a.pos_0[i*3+2] = pz; a.vel_0[i*3+0] = vx; a.vel_0[i*3+1] = vy; a.vel_0[i*3+2] = vz; a.fixed[i*3+0] = fx; a.fixed[i*3+1] = fy; a.fixed[i*3+2] = fz; a.n_atoms++; } fclose(f); if (a.n_atoms <= 0) die("coord.txt 原子数无效"); return a; } /* 读取 connection.txt */ static BondData read_bonds(const char *input_dir, const AtomData *atoms) { char path[512]; BondData b; b.n_bonds = 0; b.pairs = NULL; b.stiffness = NULL; b.rest_lengths = NULL; snprintf(path, sizeof(path), "%s/connection.txt", input_dir); FILE *f = fopen(path, "r"); if (!f) return b; /* 无成键 */ /* 跳过第一行表头 */ char line[256]; if (!fgets(line, sizeof(line), f)) { fclose(f); return b; } /* 先数行数 */ int n_lines = 0, tmp_a, tmp_b; char bond_name[256]; while (fscanf(f, "%d %d %s", &tmp_a, &tmp_b, bond_name) == 3) n_lines++; rewind(f); if (n_lines == 0) { fclose(f); return b; } b.n_bonds = n_lines; b.pairs = (int*)xmalloc(n_lines * 2 * sizeof(int)); b.stiffness = (double*)xmalloc(n_lines * sizeof(double)); b.rest_lengths = (double*)xmalloc(n_lines * sizeof(double)); /* 读取 bond.txt 获取劲度系数和平衡长度 */ char bond_path[512]; snprintf(bond_path, sizeof(bond_path), "%s/bond.txt", input_dir); FILE *fb = fopen(bond_path, "r"); /* bond.txt 格式: name stiffness rest_length */ for (int i = 0; i < n_lines; i++) { fscanf(f, "%d %d %s", &tmp_a, &tmp_b, bond_name); /* 转换原子编号为 0-based 索引 */ b.pairs[i*2+0] = tmp_a - 1; b.pairs[i*2+1] = tmp_b - 1; /* 查找 bond.txt 中对应的参数 */ b.stiffness[i] = 1.0; b.rest_lengths[i] = 2.0; /* 默认值 */ if (fb) { char name[256]; double k, r0; rewind(fb); while (fscanf(fb, "%s %lf %lf", name, &k, &r0) == 3) { if (strcmp(name, bond_name) == 0) { b.stiffness[i] = k; b.rest_lengths[i] = r0; break; } } } } fclose(f); if (fb) fclose(fb); return b; } /* ======================================================================== * 物理核心 * ======================================================================== */ 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, double *ax, double *ay, double *az) { for (int i = 0; i < n; i++) { double inv_m = 1.0 / m[i]; ax[i] = (m[i] * G[0] - B[0] * vx[i]) * inv_m; ay[i] = (m[i] * G[1] - B[1] * vy[i]) * inv_m; az[i] = (m[i] * G[2] - B[2] * vz[i]) * inv_m; } /* 弹簧力 */ for (int b = 0; b < bonds->n_bonds; b++) { int i = bonds->pairs[b*2+0]; 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 = 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]; } } /* clamp */ static double clamp(double v, double lo, double hi) { return fmin(fmax(v, lo), hi); } /* 蛙跳法一步 */ static void leapfrog_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 box_a, double dt) { double *ax = (double*)alloca(n * sizeof(double)); double *ay = (double*)alloca(n * sizeof(double)); double *az = (double*)alloca(n * sizeof(double)); compute_acceleration(n, x, y, z, vx, vy, vz, m, G, B, bonds, ax, ay, az); 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; 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; /* wrap */ x[i] = clamp(x[i], -box_a, box_a); y[i] = clamp(y[i], -box_a, box_a); z[i] = clamp(z[i], -box_a, box_a); } } /* ======================================================================== * JSON 输出 * ======================================================================== */ static void write_trajectory_json(const char *path, const Trajectory *traj, const SimParams *params, const AtomData *atoms, const BondData *bonds) { FILE *f = fopen(path, "w"); if (!f) die("无法写入 trajectory.txt"); fprintf(f, "{\n"); /* traj_x, traj_y, ... */ const char *names[] = {"traj_x","traj_y","traj_z","traj_vx","traj_vy","traj_vz"}; double *arrs[] = {traj->x, traj->y, traj->z, traj->vx, traj->vy, traj->vz}; for (int a = 0; a < 6; a++) { fprintf(f, " \"%s\": [\n", names[a]); for (int t = 0; t < traj->n_steps; t++) { fprintf(f, " ["); for (int i = 0; i < traj->n_atoms; i++) { fprintf(f, "%.15g", arrs[a][t * traj->n_atoms + i]); if (i < traj->n_atoms - 1) fputc(',', f); } fprintf(f, "]"); if (t < traj->n_steps - 1) fputc(',', f); fputc('\n', f); } fprintf(f, " ]"); // 每个轨迹数组后加逗号(后面还有标量参数) fputc(',', f); fputc('\n', f); } /* 标量参数 */ fprintf(f, " \"NT\": %d,\n", params->NT); fprintf(f, " \"DT\": %.15g,\n", params->DT); fprintf(f, " \"NSTEP\": %d,\n", params->NSTEP); fprintf(f, " \"method\": \"leapfrog\",\n"); fprintf(f, " \"warmup_steps\": %d,\n", params->warmup_steps); fprintf(f, " \"G\": [%.15g, %.15g, %.15g],\n", params->G[0], params->G[1], params->G[2]); fprintf(f, " \"B\": [%.15g, %.15g, %.15g],\n", params->B[0], params->B[1], params->B[2]); /* 原子信息 */ fprintf(f, " \"atom_ids\": ["); for (int i = 0; i < atoms->n_atoms; i++) { fprintf(f, "%d", atoms->atom_ids[i]); if (i < atoms->n_atoms - 1) fputc(',', f); } fprintf(f, "],\n"); fprintf(f, " \"atom_masses\": ["); for (int i = 0; i < atoms->n_atoms; i++) { fprintf(f, "%.15g", atoms->masses[i]); if (i < atoms->n_atoms - 1) fputc(',', f); } fprintf(f, "],\n"); /* 成键 */ fprintf(f, " \"bond_pairs\": ["); for (int b = 0; b < bonds->n_bonds; b++) { fprintf(f, "[%d, %d]", bonds->pairs[b*2], bonds->pairs[b*2+1]); if (b < bonds->n_bonds - 1) fputc(',', f); } fprintf(f, "],\n"); fprintf(f, " \"bond_stiffness\": ["); for (int b = 0; b < bonds->n_bonds; b++) { fprintf(f, "%.15g", bonds->stiffness[b]); if (b < bonds->n_bonds - 1) fputc(',', f); } fprintf(f, "],\n"); fprintf(f, " \"bond_rest_lengths\": ["); for (int b = 0; b < bonds->n_bonds; b++) { fprintf(f, "%.15g", bonds->rest_lengths[b]); if (b < bonds->n_bonds - 1) fputc(',', f); } fprintf(f, "]\n"); fprintf(f, "}\n"); fclose(f); } /* ======================================================================== * 主函数 * ======================================================================== */ int main(int argc, char **argv) { if (argc < 4) { fprintf(stderr, "用法: %s \n", argv[0]); return 1; } const char *input_dir = argv[1]; const char *output_dir = argv[2]; const char *param_path = argv[3]; clock_t t0 = clock(); /* 读取参数和输入 */ SimParams params = read_params(param_path); AtomData atoms = read_coord(input_dir); BondData bonds = read_bonds(input_dir, &atoms); printf("[C-engine] 原子数=%d, 键数=%d, NT=%d, DT=%.6g\n", atoms.n_atoms, bonds.n_bonds, params.NT, params.DT); /* 初始化位置/速度 */ int n = atoms.n_atoms; double *x = (double*)xmalloc(n * sizeof(double)); double *y = (double*)xmalloc(n * sizeof(double)); double *z = (double*)xmalloc(n * sizeof(double)); double *vx = (double*)xmalloc(n * sizeof(double)); double *vy = (double*)xmalloc(n * sizeof(double)); double *vz = (double*)xmalloc(n * sizeof(double)); for (int i = 0; i < n; i++) { x[i] = atoms.pos_0[i*3+0]; y[i] = atoms.pos_0[i*3+1]; z[i] = atoms.pos_0[i*3+2]; vx[i] = atoms.vel_0[i*3+0]; vy[i] = atoms.vel_0[i*3+1]; vz[i] = atoms.vel_0[i*3+2]; } /* 分配轨迹缓冲区 */ Trajectory traj; traj.n_steps = params.NT; traj.n_atoms = n; traj.x = (double*)xmalloc(params.NT * n * sizeof(double) * 6); traj.y = traj.x + params.NT * n; traj.z = traj.y + params.NT * n; traj.vx = traj.z + params.NT * n; traj.vy = traj.vx + params.NT * n; traj.vz = traj.vy + params.NT * n; /* 预热 */ for (int s = 0; s < params.warmup_steps; s++) { leapfrog_step(n, x, y, z, vx, vy, vz, atoms.masses, params.G, params.B, &bonds, atoms.fixed, params.box_a, params.DT); } /* 记录 */ for (int s = 0; s < params.NT; s++) { leapfrog_step(n, x, y, z, vx, vy, vz, atoms.masses, params.G, params.B, &bonds, atoms.fixed, params.box_a, params.DT); /* 保存这一帧 */ 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]; } } /* 输出轨迹 */ char out_path[512]; snprintf(out_path, sizeof(out_path), "%s/trajectory.txt", output_dir); write_trajectory_json(out_path, &traj, ¶ms, &atoms, &bonds); clock_t t1 = clock(); double elapsed = (double)(t1 - t0) / CLOCKS_PER_SEC; printf("[C-engine] 计算完成: %d 步, %.3f s\n", params.NT, elapsed); /* 清理 */ free(traj.x); free(x); free(y); free(z); free(vx); free(vy); free(vz); free(atoms.atom_ids); free(atoms.masses); free(atoms.radii); free(atoms.pos_0); free(atoms.vel_0); free(atoms.fixed); if (bonds.pairs) { free(bonds.pairs); free(bonds.stiffness); free(bonds.rest_lengths); } return 0; }