/** * engines/cpp/dynamics_lib.cpp * ----------------------------- * 纯计算 DLL(C++ 版):无文件 I/O,所有数据由 Python 以 NumPy 数组传入。 * 算法与 main.cpp / compute.py 保持完全一致。 * * 编译(Windows): * g++ -O3 -march=native -std=c++17 -shared -o build/dynamics_cpp.dll dynamics_lib.cpp * 编译(Linux): * g++ -O3 -march=native -std=c++17 -shared -fPIC -o build/dynamics_cpp.so dynamics_lib.cpp * 编译(macOS): * g++ -O3 -march=native -std=c++17 -dynamiclib -o build/dynamics_cpp.dylib dynamics_lib.cpp */ #ifdef _WIN32 # define EXPORT extern "C" __declspec(dllexport) #else # define EXPORT extern "C" __attribute__((visibility("default"))) #endif #include #include #include #include /* ── 驱动力结构体 ─────────────────────────────────────────── */ struct Drivers { int n_drivers = 0; const int *idx = nullptr; const double *amp = nullptr; const double *freq = nullptr; const double *phi = nullptr; const double *eq = nullptr; const double *ncycles = nullptr; const int *has_period = nullptr; std::vector freeze; /* [n_drivers*3] 冻结位置(period 结束时锁定)*/ }; /* ── 加速度:保守力(弹簧键 + 均匀重力场)────────────────── */ static void accel_conservative( int n, const double *x, const double *y, const double *z, const double *m, double Gx, double Gy, double Gz, int gravity_field, int elastic_force, int n_bonds, const int *bond_pairs, const double *bond_k, const double *bond_r0, double *ax, double *ay, double *az) { for (int i = 0; i < n; i++) { ax[i] = gravity_field ? Gx : 0.0; ay[i] = gravity_field ? Gy : 0.0; az[i] = gravity_field ? Gz : 0.0; } if (!elastic_force || n_bonds == 0) return; for (int b = 0; b < n_bonds; b++) { int ii = bond_pairs[b*2]; int jj = bond_pairs[b*2+1]; double dx = x[jj]-x[ii], dy = y[jj]-y[ii], dz = z[jj]-z[ii]; double dist = std::sqrt(dx*dx + dy*dy + dz*dz); if (dist < 1e-12) continue; double fac = bond_k[b] * (dist - bond_r0[b]) / dist; double fx = fac*dx, fy = fac*dy, fz_b = fac*dz; ax[ii] += fx/m[ii]; ay[ii] += fy/m[ii]; az[ii] += fz_b/m[ii]; ax[jj] -= fx/m[jj]; ay[jj] -= fy/m[jj]; az[jj] -= fz_b/m[jj]; } } /* ── 完整加速度(含阻尼)────────────────────────────────── */ static void accel_full( int n, const double *x, const double *y, const double *z, const double *vx, const double *vy, const double *vz, const double *m, double Gx, double Gy, double Gz, double Bx, double By, double Bz, int gravity_field, int elastic_force, int damping_force, int n_bonds, const int *bond_pairs, const double *bond_k, const double *bond_r0, double *ax, double *ay, double *az) { accel_conservative(n, x, y, z, m, Gx, Gy, Gz, gravity_field, elastic_force, n_bonds, bond_pairs, bond_k, bond_r0, ax, ay, az); if (damping_force) { for (int i = 0; i < n; i++) { ax[i] -= Bx * vx[i] / m[i]; ay[i] -= By * vy[i] / m[i]; az[i] -= Bz * vz[i] / m[i]; } } } /* ── 边界:反弹 ──────────────────────────────────────────── */ static inline void _limit1(double &p, double &v, double lo, double hi) { if (p > hi) { p = hi; v = -std::fabs(v); } if (p < lo) { p = lo; v = std::fabs(v); } } /* ── 边界:回绕 ──────────────────────────────────────────── */ static inline void _wrap1(double &p, double lo, double hi) { if (p > hi) p = lo; if (p < lo) p = hi; } /* ── 边界 + 固定约束 ────────────────────────────────────── */ static void apply_boundary_and_constraints( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const int *fixed, const double *pos_init, double box_a) { double lo = -box_a, hi = box_a; for (int i = 0; i < n; i++) { if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue; _limit1(x[i], vx[i], lo, hi); _limit1(y[i], vy[i], lo, hi); _limit1(z[i], vz[i], lo, hi); } for (int i = 0; i < n; i++) { _wrap1(x[i], lo, hi); _wrap1(y[i], lo, hi); _wrap1(z[i], lo, hi); } for (int i = 0; i < n; i++) { if (fixed[i*3+0]) { x[i] = pos_init[i*3+0]; vx[i] = 0.0; } if (fixed[i*3+1]) { y[i] = pos_init[i*3+1]; vy[i] = 0.0; } if (fixed[i*3+2]) { z[i] = pos_init[i*3+2]; vz[i] = 0.0; } } } /* ══════════════════════════════════════════════════════════ * 蛙跳法(与 main.cpp leapfrog_step 完全一致) * ══════════════════════════════════════════════════════════ */ static void leapfrog_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const int *fixed, double Gx, double Gy, double Gz, double Bx, double By, double Bz, int gravity_field, int elastic_force, int damping_force, int n_bonds, const int *bp, const double *bk, const double *br0, double dt) { std::vector buf(n * 3); double *ax = buf.data(), *ay = ax+n, *az = ay+n; accel_conservative(n, x, y, z, m, Gx, Gy, Gz, gravity_field, elastic_force, n_bonds, bp, bk, br0, ax, ay, az); bool has_damp = damping_force && (Bx != 0.0 || By != 0.0 || Bz != 0.0); for (int i = 0; i < n; i++) { if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue; if (has_damp) { double ax_ = Bx*dt/(2.0*m[i]); double ay_ = By*dt/(2.0*m[i]); double az_ = Bz*dt/(2.0*m[i]); vx[i] = (vx[i]*(1.0-ax_) + ax[i]*dt) / (1.0+ax_); vy[i] = (vy[i]*(1.0-ay_) + ay[i]*dt) / (1.0+ay_); vz[i] = (vz[i]*(1.0-az_) + az[i]*dt) / (1.0+az_); } else { 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 euler_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const int *fixed, double Gx, double Gy, double Gz, double Bx, double By, double Bz, int gravity_field, int elastic_force, int damping_force, int n_bonds, const int *bp, const double *bk, const double *br0, double dt) { std::vector buf(n * 3); double *ax = buf.data(), *ay = ax+n, *az = ay+n; accel_full(n, x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz, gravity_field, elastic_force, damping_force, n_bonds, bp, bk, br0, ax, ay, az); for (int i = 0; i < n; i++) { if (fixed[i*3] && 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; } } /* ══════════════════════════════════════════════════════════ * 隐式欧拉法(与 main.cpp implicit_euler_step 完全一致) * ══════════════════════════════════════════════════════════ */ static void implicit_euler_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const int *fixed, double Gx, double Gy, double Gz, double Bx, double By, double Bz, int gravity_field, int elastic_force, int damping_force, int n_bonds, const int *bp, const double *bk, const double *br0, double dt) { std::vector vbuf(n * 3), abuf(n * 3); double *vxn = vbuf.data(), *vyn = vxn+n, *vzn = vyn+n; double *ax = abuf.data(), *ay = ax+n, *az = ay+n; for (int i = 0; i < n; i++) { if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) { vxn[i] = vyn[i] = vzn[i] = 0.0; continue; } double gx = Bx/m[i], gy = By/m[i], gz = Bz/m[i]; vxn[i] = (vx[i] + Gx*dt) / (1.0 + gx*dt); vyn[i] = (vy[i] + Gy*dt) / (1.0 + gy*dt); vzn[i] = (vz[i] + Gz*dt) / (1.0 + gz*dt); } accel_full(n, x, y, z, vxn, vyn, vzn, m, Gx, Gy, Gz, Bx, By, Bz, gravity_field, elastic_force, damping_force, n_bonds, bp, bk, br0, ax, ay, az); for (int i = 0; i < n; i++) { if (fixed[i*3] && 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; } } /* ══════════════════════════════════════════════════════════ * 中点法(与 main.cpp midpoint_step 完全一致) * ══════════════════════════════════════════════════════════ */ static void midpoint_step( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, const double *m, const int *fixed, double Gx, double Gy, double Gz, double Bx, double By, double Bz, int gravity_field, int elastic_force, int damping_force, int n_bonds, const int *bp, const double *bk, const double *br0, double dt) { std::vector buf(n * 9); double *ax = buf.data(); double *ay = ax+n; double *az = ay+n; double *xm = az+n; double *ym = xm+n; double *zm = ym+n; double *vxm = zm+n; double *vym = vxm+n; double *vzm = vym+n; accel_full(n, x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz, gravity_field, elastic_force, damping_force, n_bonds, bp, bk, br0, ax, ay, az); for (int i = 0; i < n; i++) { if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) { xm[i]=x[i]; ym[i]=y[i]; zm[i]=z[i]; vxm[i]=vym[i]=vzm[i]=0.0; continue; } xm[i] = x[i] + 0.5*vx[i]*dt; ym[i] = y[i] + 0.5*vy[i]*dt; zm[i] = z[i] + 0.5*vz[i]*dt; vxm[i] = vx[i] + 0.5*ax[i]*dt; vym[i] = vy[i] + 0.5*ay[i]*dt; vzm[i] = vz[i] + 0.5*az[i]*dt; x[i] = x[i] + vxm[i]*dt; y[i] = y[i] + vym[i]*dt; z[i] = z[i] + vzm[i]*dt; } std::vector abuf(n * 3); double *axm = abuf.data(), *aym = axm+n, *azm = aym+n; accel_full(n, xm, ym, zm, vxm, vym, vzm, m, Gx, Gy, Gz, Bx, By, Bz, gravity_field, elastic_force, damping_force, n_bonds, bp, bk, br0, axm, aym, azm); for (int i = 0; i < n; i++) { if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue; vx[i] += axm[i]*dt; vy[i] += aym[i]*dt; vz[i] += azm[i]*dt; } } /* ── 驱动力 ─────────────────────────────────────────────── */ static void apply_driving( int n, double *x, double *y, double *z, double *vx, double *vy, double *vz, double t, int step, double dt, Drivers &drv) { (void)n; if (drv.n_drivers == 0) return; constexpr double TWO_PI = 2.0 * 3.14159265358979323846; for (int d = 0; d < drv.n_drivers; d++) { int idx = drv.idx[d]; double fx = drv.freq[d*3+0]; double fy = drv.freq[d*3+1]; double fz = drv.freq[d*3+2]; if (drv.has_period[d]) { double mf = std::fabs(fx) > std::fabs(fy) ? std::fabs(fx) : std::fabs(fy); if (std::fabs(fz) > mf) mf = std::fabs(fz); int period_steps = 0; if (mf > 1e-12) period_steps = (int)(drv.ncycles[d] / mf / dt); if (step > period_steps) { x[idx] = drv.freeze[d*3+0]; y[idx] = drv.freeze[d*3+1]; z[idx] = drv.freeze[d*3+2]; vx[idx] = vy[idx] = vz[idx] = 0.0; continue; } double px = drv.eq[d*3+0] + drv.amp[d*3+0]*std::cos(TWO_PI*fx*t + drv.phi[d*3+0]); double py = drv.eq[d*3+1] + drv.amp[d*3+1]*std::cos(TWO_PI*fy*t + drv.phi[d*3+1]); double pz = drv.eq[d*3+2] + drv.amp[d*3+2]*std::cos(TWO_PI*fz*t + drv.phi[d*3+2]); if (step == period_steps) { drv.freeze[d*3+0] = px; drv.freeze[d*3+1] = py; drv.freeze[d*3+2] = pz; } } x[idx] = drv.eq[d*3+0] + drv.amp[d*3+0]*std::cos(TWO_PI*fx*t + drv.phi[d*3+0]); y[idx] = drv.eq[d*3+1] + drv.amp[d*3+1]*std::cos(TWO_PI*fy*t + drv.phi[d*3+1]); z[idx] = drv.eq[d*3+2] + drv.amp[d*3+2]*std::cos(TWO_PI*fz*t + drv.phi[d*3+2]); vx[idx] = -drv.amp[d*3+0]*TWO_PI*fx*std::sin(TWO_PI*fx*t + drv.phi[d*3+0]); vy[idx] = -drv.amp[d*3+1]*TWO_PI*fy*std::sin(TWO_PI*fy*t + drv.phi[d*3+1]); vz[idx] = -drv.amp[d*3+2]*TWO_PI*fz*std::sin(TWO_PI*fz*t + drv.phi[d*3+2]); } } /* ══════════════════════════════════════════════════════════ * 导出函数:run_dynamics(接口与 C 版完全相同) * ══════════════════════════════════════════════════════════ */ EXPORT int run_dynamics( int n_atoms, const double *pos_init, const double *vel_init, const double *masses, const int *fixed, int n_bonds, const int *bond_pairs, const double *bond_k, const double *bond_r0, double box_a, double dt, int NT, int NSTEP, int warmup_steps, int method_id, double Gx, double Gy, double Gz, double Bx, double By, double Bz, int gravity_field, int elastic_force, int damping_force, double gravity_strength, int n_drivers, const int *drv_idx, const double *drv_amp, const double *drv_freq, const double *drv_phi, const double *drv_eq, const double *drv_ncycles, const int *drv_has_period, int n_frames, double *out_x, double *out_y, double *out_z, double *out_vx, double *out_vy, double *out_vz, void (*progress_cb)(int step, int total)) { (void)gravity_strength; int n = n_atoms; std::vector xv(n), yv(n), zv(n); std::vector vxv(n), vyv(n), vzv(n); for (int i = 0; i < n; i++) { xv[i]=pos_init[i*3+0]; yv[i]=pos_init[i*3+1]; zv[i]=pos_init[i*3+2]; vxv[i]=vel_init[i*3+0]; vyv[i]=vel_init[i*3+1]; vzv[i]=vel_init[i*3+2]; } double *x=xv.data(), *y=yv.data(), *z=zv.data(); double *vx=vxv.data(), *vy=vyv.data(), *vz=vzv.data(); Drivers drv; drv.n_drivers = n_drivers; drv.idx = drv_idx; drv.amp = drv_amp; drv.freq = drv_freq; drv.phi = drv_phi; drv.eq = drv_eq; drv.ncycles = drv_ncycles; drv.has_period = drv_has_period; if (n_drivers > 0) drv.freeze.assign(n_drivers * 3, 0.0); #define DO_STEP() do { \ switch (method_id) { \ case 0: euler_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \ gravity_field,elastic_force,damping_force, \ n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \ case 1: implicit_euler_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \ gravity_field,elastic_force,damping_force, \ n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \ case 2: midpoint_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \ gravity_field,elastic_force,damping_force, \ n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \ default: leapfrog_step(n,x,y,z,vx,vy,vz,masses,fixed,Gx,Gy,Gz,Bx,By,Bz, \ gravity_field,elastic_force,damping_force, \ n_bonds,bond_pairs,bond_k,bond_r0,dt); break; \ } \ apply_boundary_and_constraints(n,x,y,z,vx,vy,vz,fixed,pos_init,box_a); \ } while(0) /* 蛙跳法:初始化 v(-dt/2) */ if (method_id == 3) { std::vector ibuf(n * 3); double *ax0=ibuf.data(), *ay0=ax0+n, *az0=ay0+n; accel_conservative(n, x, y, z, masses, Gx, Gy, Gz, gravity_field, elastic_force, n_bonds, bond_pairs, bond_k, bond_r0, ax0, ay0, az0); for (int i = 0; i < n; i++) { if (fixed[i*3] && fixed[i*3+1] && fixed[i*3+2]) continue; vx[i] -= 0.5*ax0[i]*dt; vy[i] -= 0.5*ay0[i]*dt; vz[i] -= 0.5*az0[i]*dt; } } /* 初始驱动 t=0 */ if (n_drivers > 0) apply_driving(n, x, y, z, vx, vy, vz, 0.0, 0, dt, drv); /* 预热 */ for (int s = 0; s < warmup_steps; s++) { double tw = (s + 1) * dt; if (n_drivers > 0) apply_driving(n, x, y, z, vx, vy, vz, tw, s, dt, drv); DO_STEP(); } /* 记录循环 */ int record_steps = NT - warmup_steps; int prog_interval = std::max(1, record_steps / 100); int frame_idx = 0; for (int s = 0; s < record_steps; s++) { if (progress_cb && s % prog_interval == 0 && s > 0) progress_cb(s, record_steps); double t = (s + warmup_steps) * dt; if (n_drivers > 0) apply_driving(n, x, y, z, vx, vy, vz, t, s, dt, drv); if (s % NSTEP == 0 && frame_idx < n_frames) { int base = frame_idx * n; for (int i = 0; i < n; i++) { out_x [base+i] = x[i]; out_y [base+i] = y[i]; out_z [base+i] = z[i]; out_vx[base+i] = vx[i]; out_vy[base+i] = vy[i]; out_vz[base+i] = vz[i]; } frame_idx++; } DO_STEP(); } #undef DO_STEP return 0; }