docs: 更新 examples/Readme.md 并新增 Readme.html
- 覆盖全部 10 个案例(原 Readme 只到 case06) - 新增案例选择指南表格 - Readme.html 为深色主题独立 HTML 页面 (含卡片布局、标签分类、代码高亮、响应式设计) - 各案例详情对齐最新配置参数
This commit is contained in:
+20
-1
@@ -8,6 +8,7 @@ CC = gcc
|
||||
CFLAGS = -O3 -march=native -Wall -Wextra
|
||||
LDFLAGS = -lm
|
||||
SRCS = main.c
|
||||
LIB_SRC = dynamics_lib.c
|
||||
|
||||
# 自动检测系统
|
||||
UNAME_S := $(shell uname -s 2>/dev/null || echo Windows)
|
||||
@@ -15,15 +16,33 @@ UNAME_S := $(shell uname -s 2>/dev/null || echo Windows)
|
||||
# 目标文件名:统一使用 .exe 后缀(方便 Python 跨平台调用)
|
||||
TARGET = build/dynamics_c.exe
|
||||
|
||||
# DLL 目标(平台自动选择后缀)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
DLL_TARGET = build/dynamics_c.so
|
||||
DLL_FLAGS = -shared -fPIC
|
||||
else ifeq ($(UNAME_S),Darwin)
|
||||
DLL_TARGET = build/dynamics_c.dylib
|
||||
DLL_FLAGS = -dynamiclib
|
||||
else
|
||||
DLL_TARGET = build/dynamics_c.dll
|
||||
DLL_FLAGS = -shared
|
||||
endif
|
||||
|
||||
# ── 本地编译 ─────────────────────────────────
|
||||
.PHONY: all clean linux windows macos
|
||||
.PHONY: all dll clean linux windows macos
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
dll: $(DLL_TARGET)
|
||||
|
||||
$(TARGET): $(SRCS) | build
|
||||
$(CC) $(CFLAGS) -o $@ $(SRCS) $(LDFLAGS)
|
||||
@echo " === C engine built: $@ ==="
|
||||
|
||||
$(DLL_TARGET): $(LIB_SRC) | build
|
||||
$(CC) $(CFLAGS) $(DLL_FLAGS) -o $@ $(LIB_SRC) $(LDFLAGS)
|
||||
@echo " === C DLL built: $@ ==="
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"n_atoms": 40, "nt": 200000, "step_time": 2.5352442264556887e-05}
|
||||
@@ -0,0 +1,554 @@
|
||||
/**
|
||||
* engines/c/dynamics_lib.c
|
||||
* -------------------------
|
||||
* 纯计算 DLL:无文件 I/O,所有数据由 Python 以 NumPy 数组传入,
|
||||
* 结果直接写入 Python 预分配的输出数组。
|
||||
* 算法与 main.c 和 compute.py 保持完全一致。
|
||||
*
|
||||
* 编译(Windows DLL):
|
||||
* gcc -O3 -march=native -shared -o build/dynamics_c.dll dynamics_lib.c -lm
|
||||
* 编译(Linux .so):
|
||||
* gcc -O3 -march=native -shared -fPIC -o build/dynamics_c.so dynamics_lib.c -lm
|
||||
* 编译(macOS .dylib):
|
||||
* gcc -O3 -march=native -dynamiclib -o build/dynamics_c.dylib dynamics_lib.c -lm
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
# define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
# define EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ── 驱动力结构体 ─────────────────────────────────────────── */
|
||||
typedef struct {
|
||||
int n_drivers;
|
||||
const int *idx; /* [n_drivers] 0-based local atom index */
|
||||
const double *amp; /* [n_drivers*3] (ax,ay,az) interleaved */
|
||||
const double *freq; /* [n_drivers*3] */
|
||||
const double *phi; /* [n_drivers*3] radians */
|
||||
const double *eq; /* [n_drivers*3] equilibrium positions */
|
||||
const double *ncycles; /* [n_drivers] 0=unlimited */
|
||||
const int *has_period; /* [n_drivers] */
|
||||
/* mutable freeze positions (allocated internally) */
|
||||
double *freeze; /* [n_drivers*3] */
|
||||
} Drivers;
|
||||
|
||||
/* ── 加速度:保守力(弹簧键 + 均匀重力场)────────────────── */
|
||||
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];
|
||||
double dy = y[jj] - y[ii];
|
||||
double dz = z[jj] - z[ii];
|
||||
double dist = sqrt(dx*dx + dy*dy + dz*dz);
|
||||
if (dist < 1e-12) continue;
|
||||
double k = bond_k[b];
|
||||
double r0 = bond_r0[b];
|
||||
double fac = k * (dist - r0) / 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 边界:反弹(与 main.c limit_in_box 一致)────────────── */
|
||||
static inline void _limit1(double *p, double *v, double lo, double hi) {
|
||||
if (*p > hi) { *p = hi; *v = -fabs(*v); }
|
||||
if (*p < lo) { *p = lo; *v = fabs(*v); }
|
||||
}
|
||||
|
||||
/* ── 边界:回绕(与 main.c wrap_position 一致)──────────── */
|
||||
static inline void _wrap1(double *p, double lo, double hi) {
|
||||
if (*p > hi) *p = lo;
|
||||
if (*p < lo) *p = hi;
|
||||
}
|
||||
|
||||
/* ── 边界 + 固定约束(与 main.c apply_step 末尾一致)──────── */
|
||||
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);
|
||||
}
|
||||
|
||||
/* 逐自由度固定约束:与 main.c 和 Python apply_fixed_constraints 一致 */
|
||||
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.c leapfrog_step 完全一致)
|
||||
* x(t), v(t-dt/2) → x(t+dt), v(t+dt/2)
|
||||
* 无阻尼:纯辛积分。有阻尼:半隐式处理 α = B·dt/(2m)
|
||||
* ══════════════════════════════════════════════════════════ */
|
||||
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)
|
||||
{
|
||||
double *ax = (double*)alloca(n*sizeof(double)*3);
|
||||
double *ay = ax+n; double *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);
|
||||
|
||||
int 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
* 显式欧拉法(与 main.c explicit_euler_step 一致)
|
||||
* ══════════════════════════════════════════════════════════ */
|
||||
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)
|
||||
{
|
||||
double *ax = (double*)alloca(n*sizeof(double)*3);
|
||||
double *ay = ax+n; double *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.c implicit_euler_step 完全一致)
|
||||
*
|
||||
* main.c 逻辑:
|
||||
* 1. 用 v_next ≈ (v + G·dt)/(1 + γ·dt) 预测(只含重力+阻尼,不含弹簧)
|
||||
* 2. 用 (x, v_next) 计算完整加速度 a_next
|
||||
* 3. v += a_next·dt; x += v·dt
|
||||
* ══════════════════════════════════════════════════════════ */
|
||||
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)
|
||||
{
|
||||
double *vxn = (double*)alloca(n*sizeof(double)*3);
|
||||
double *vyn = vxn+n; double *vzn = vyn+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);
|
||||
}
|
||||
|
||||
double *ax = (double*)alloca(n*sizeof(double)*3);
|
||||
double *ay = ax+n; double *az = ay+n;
|
||||
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.c midpoint_step 完全一致)
|
||||
*
|
||||
* main.c 逻辑:
|
||||
* 1. a = accel(x, v)
|
||||
* 2. xm = x + 0.5·v·dt; vm = v + 0.5·a·dt
|
||||
* 3. x = x + vm·dt (位置更新用 vm,即中点速度)
|
||||
* 4. am = accel(xm, vm)
|
||||
* 5. v = v + am·dt
|
||||
* ══════════════════════════════════════════════════════════ */
|
||||
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)
|
||||
{
|
||||
/* Allocate in one block for cache locality */
|
||||
double *buf = (double*)alloca(n*sizeof(double)*9);
|
||||
double *ax = buf;
|
||||
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;
|
||||
/* position updated with midpoint velocity (same as main.c) */
|
||||
x[i] = x[i] + vxm[i]*dt;
|
||||
y[i] = y[i] + vym[i]*dt;
|
||||
z[i] = z[i] + vzm[i]*dt;
|
||||
}
|
||||
|
||||
double *axm = (double*)alloca(n*sizeof(double)*3);
|
||||
double *aym = axm+n; double *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;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 驱动力(与 main.c apply_driving_force 一致)────────── */
|
||||
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 || drv->n_drivers == 0) return;
|
||||
const 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 = fabs(fx) > fabs(fy) ? fabs(fx) : fabs(fy);
|
||||
if (fabs(fz) > mf) mf = 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]*cos(TWO_PI*fx*t + drv->phi[d*3+0]);
|
||||
double py = drv->eq[d*3+1] + drv->amp[d*3+1]*cos(TWO_PI*fy*t + drv->phi[d*3+1]);
|
||||
double pz = drv->eq[d*3+2] + drv->amp[d*3+2]*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]*cos(TWO_PI*fx*t + drv->phi[d*3+0]);
|
||||
y[idx] = drv->eq[d*3+1] + drv->amp[d*3+1]*cos(TWO_PI*fy*t + drv->phi[d*3+1]);
|
||||
z[idx] = drv->eq[d*3+2] + drv->amp[d*3+2]*cos(TWO_PI*fz*t + drv->phi[d*3+2]);
|
||||
vx[idx] = -drv->amp[d*3+0]*TWO_PI*fx*sin(TWO_PI*fx*t + drv->phi[d*3+0]);
|
||||
vy[idx] = -drv->amp[d*3+1]*TWO_PI*fy*sin(TWO_PI*fy*t + drv->phi[d*3+1]);
|
||||
vz[idx] = -drv->amp[d*3+2]*TWO_PI*fz*sin(TWO_PI*fz*t + drv->phi[d*3+2]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
* 导出函数:run_dynamics
|
||||
*
|
||||
* 与 main.c 的计算顺序完全一致:
|
||||
* 1. leapfrog 初始化 v(-dt/2)
|
||||
* 2. 初始驱动 t=0
|
||||
* 3. 预热循环(不记录)
|
||||
* 4. 记录循环:drive → record → step → boundary → constraints
|
||||
*
|
||||
* 参数说明(所有数组均为 C-contiguous 行优先 float64/int32):
|
||||
* n_atoms 原子数
|
||||
* pos_init 初始位置 [n_atoms*3] x0,y0,z0, x1,y1,z1, ...
|
||||
* vel_init 初始速度 [n_atoms*3]
|
||||
* masses 质量 [n_atoms]
|
||||
* fixed 自由度约束 [n_atoms*3] int32, 1=固定
|
||||
* n_bonds 键数
|
||||
* bond_pairs 键对 [n_bonds*2] int32, 0-based local index
|
||||
* bond_k 刚度 [n_bonds]
|
||||
* bond_r0 平衡键长 [n_bonds]
|
||||
* box_a 盒子半边长
|
||||
* dt 时间步长
|
||||
* NT 总步数(含预热)
|
||||
* NSTEP 抽帧间隔
|
||||
* warmup_steps 预热步数
|
||||
* method_id 0=euler 1=implicit 2=midpoint 3=leapfrog
|
||||
* Gx/Gy/Gz 均匀重力场加速度分量
|
||||
* Bx/By/Bz 阻尼系数分量
|
||||
* gravity_field / elastic_force / damping_force 力开关
|
||||
* gravity_strength 原子间引力强度(暂未实现,留接口)
|
||||
* n_drivers 驱动原子数
|
||||
* drv_idx 驱动原子局部索引 [n_drivers] int32
|
||||
* drv_amp 振幅 [n_drivers*3]
|
||||
* drv_freq 频率 [n_drivers*3]
|
||||
* drv_phi 初相(弧度)[n_drivers*3]
|
||||
* drv_eq 平衡位置 [n_drivers*3]
|
||||
* drv_ncycles 周期数 [n_drivers] 0=不限
|
||||
* drv_has_period [n_drivers] int32
|
||||
* n_frames 输出帧数(Python 预计算:(NT-warmup)/NSTEP 向上取整)
|
||||
* out_x/y/z/vx/vy/vz 输出数组 [n_frames*n_atoms] 由 Python 预分配
|
||||
* progress_cb 进度回调(可为 NULL)
|
||||
*
|
||||
* 返回:0=成功,负数=错误
|
||||
* ══════════════════════════════════════════════════════════ */
|
||||
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;
|
||||
|
||||
/* ── 工作数组 ── */
|
||||
double *x = (double*)malloc(n*sizeof(double));
|
||||
double *y = (double*)malloc(n*sizeof(double));
|
||||
double *z = (double*)malloc(n*sizeof(double));
|
||||
double *vx = (double*)malloc(n*sizeof(double));
|
||||
double *vy = (double*)malloc(n*sizeof(double));
|
||||
double *vz = (double*)malloc(n*sizeof(double));
|
||||
if (!x||!y||!z||!vx||!vy||!vz) return -1;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
x[i]=pos_init[i*3+0]; y[i]=pos_init[i*3+1]; z[i]=pos_init[i*3+2];
|
||||
vx[i]=vel_init[i*3+0]; vy[i]=vel_init[i*3+1]; vz[i]=vel_init[i*3+2];
|
||||
}
|
||||
|
||||
/* ── 驱动结构 ── */
|
||||
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;
|
||||
drv.freeze = NULL;
|
||||
if (n_drivers > 0) {
|
||||
drv.freeze = (double*)calloc(n_drivers*3, sizeof(double));
|
||||
if (!drv.freeze) { free(x);free(y);free(z);free(vx);free(vy);free(vz); return -2; }
|
||||
}
|
||||
|
||||
/* ── 内联步进宏 ── */
|
||||
#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) = v(0) - 0.5·a_c(0)·dt ── */
|
||||
if (method_id == 3) {
|
||||
double *ax0 = (double*)alloca(n*sizeof(double)*3);
|
||||
double *ay0 = ax0+n; double *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(与 main.c 一致:leapfrog init 之后施加)── */
|
||||
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 = record_steps / 100;
|
||||
if (prog_interval < 1) prog_interval = 1;
|
||||
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);
|
||||
|
||||
/* 抽帧记录(drive 之后,step 之前,与 main.c 一致)*/
|
||||
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
|
||||
|
||||
free(x); free(y); free(z);
|
||||
free(vx); free(vy); free(vz);
|
||||
if (drv.freeze) free(drv.freeze);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# engines/cpp/Makefile
|
||||
|
||||
CXX = g++
|
||||
SRCS = main.cpp
|
||||
LIB_SRC = dynamics_lib.cpp
|
||||
|
||||
UNAME_S := $(shell uname -s 2>/dev/null || echo Windows)
|
||||
|
||||
CXXFLAGS = -O3 -march=native -std=c++17 -Wall -Wextra -D_USE_MATH_DEFINES
|
||||
|
||||
# Windows 下静态链接运行时,避免 libstdc++-6.dll / libgcc_s_seh-1.dll 版本冲突
|
||||
ifeq ($(UNAME_S),Windows)
|
||||
STATIC_FLAGS = -static-libgcc -static-libstdc++
|
||||
else
|
||||
STATIC_FLAGS =
|
||||
endif
|
||||
|
||||
TARGET = build/dynamics_cpp.exe
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
DLL_TARGET = build/dynamics_cpp.so
|
||||
DLL_FLAGS = -shared -fPIC
|
||||
else ifeq ($(UNAME_S),Darwin)
|
||||
DLL_TARGET = build/dynamics_cpp.dylib
|
||||
DLL_FLAGS = -dynamiclib
|
||||
else
|
||||
DLL_TARGET = build/dynamics_cpp.dll
|
||||
DLL_FLAGS = -shared
|
||||
endif
|
||||
|
||||
.PHONY: all dll clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
dll: $(DLL_TARGET)
|
||||
|
||||
$(TARGET): $(SRCS) | build
|
||||
$(CXX) $(CXXFLAGS) $(STATIC_FLAGS) -o $@ $(SRCS)
|
||||
@echo " === C++ engine built: $@ ==="
|
||||
|
||||
$(DLL_TARGET): $(LIB_SRC) | build
|
||||
$(CXX) $(CXXFLAGS) $(STATIC_FLAGS) $(DLL_FLAGS) -o $@ $(LIB_SRC)
|
||||
@echo " === C++ DLL built: $@ ==="
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
clean:
|
||||
rm -rf build *.o
|
||||
@@ -0,0 +1 @@
|
||||
{"n_atoms": 40, "nt": 200000, "step_time": 0.0022148028612136842}
|
||||
@@ -0,0 +1,450 @@
|
||||
/**
|
||||
* 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 <cmath>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
/* ── 驱动力结构体 ─────────────────────────────────────────── */
|
||||
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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> xv(n), yv(n), zv(n);
|
||||
std::vector<double> 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<double> 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;
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
"""
|
||||
engines/engine_dll.py
|
||||
---------------------
|
||||
Python ctypes 包装器:加载 C/C++/Fortran 动态链接库并调用 run_dynamics()。
|
||||
|
||||
用法(由 compute.py 内部调用,不直接运行):
|
||||
|
||||
from engines.engine_dll import load_dll, run_dynamics_dll
|
||||
|
||||
dll = load_dll("c") # 自动查找 engines/c/build/dynamics_c.dll/.so/.dylib
|
||||
arrays = run_dynamics_dll(dll, config, atom_data, bond_data, driver_data)
|
||||
# arrays: dict with keys x, y, z, vx, vy, vz shape=(n_frames, n_atoms)
|
||||
|
||||
DLL 编译(C 版本):
|
||||
Windows: gcc -O3 -shared -o engines/c/build/dynamics_c.dll engines/c/dynamics_lib.c -lm
|
||||
Linux: gcc -O3 -shared -fPIC -o engines/c/build/dynamics_c.so engines/c/dynamics_lib.c -lm
|
||||
macOS: gcc -O3 -dynamiclib -o engines/c/build/dynamics_c.dylib engines/c/dynamics_lib.c -lm
|
||||
"""
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import platform
|
||||
import numpy as np
|
||||
|
||||
# ── DLL 文件名后缀 ─────────────────────────────────────────────
|
||||
_SUFFIX = {
|
||||
"windows": ".dll",
|
||||
"linux": ".so",
|
||||
"darwin": ".dylib",
|
||||
}
|
||||
|
||||
# ── method 字符串 → 整数 ID ────────────────────────────────────
|
||||
_METHOD_ID = {
|
||||
"explicit_euler": 0,
|
||||
"euler": 0,
|
||||
"implicit_euler": 1,
|
||||
"midpoint": 2,
|
||||
"leapfrog": 3,
|
||||
}
|
||||
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
_DLL_NAME = {
|
||||
"c": "dynamics_c",
|
||||
"cpp": "dynamics_cpp",
|
||||
"c++": "dynamics_cpp",
|
||||
"fortran": "dynamics_f90",
|
||||
"f90": "dynamics_f90",
|
||||
# "python" 引擎通过直接 import 调用,不使用 DLL
|
||||
}
|
||||
|
||||
# 引擎名规范化:将别名统一为目录名
|
||||
_ENGINE_DIR = {
|
||||
"c": "c",
|
||||
"cpp": "cpp",
|
||||
"c++": "cpp",
|
||||
"fortran": "fortran",
|
||||
"f90": "fortran",
|
||||
"python": "python",
|
||||
}
|
||||
|
||||
|
||||
def _dll_candidates(engine: str) -> list[str]:
|
||||
"""返回 DLL 候选路径列表(按优先级)。"""
|
||||
sys = platform.system().lower()
|
||||
ext = _SUFFIX.get(sys, ".so")
|
||||
eng_dir = _ENGINE_DIR.get(engine, engine)
|
||||
name = _DLL_NAME.get(engine, f"dynamics_{engine}")
|
||||
base = os.path.join(_HERE, eng_dir, "build", name)
|
||||
return [
|
||||
base + ext,
|
||||
base + ".dll",
|
||||
base + ".so",
|
||||
base + ".dylib",
|
||||
]
|
||||
|
||||
|
||||
def load_dll(engine: str = "c"):
|
||||
"""加载指定引擎。
|
||||
|
||||
- C/C++/Fortran: 返回 ctypes.CDLL 对象
|
||||
- Python: 返回模块对象(直接 import,无需编译)
|
||||
|
||||
Args:
|
||||
engine: "c", "cpp", "fortran", 或 "python"
|
||||
Raises:
|
||||
FileNotFoundError: DLL/模块文件不存在
|
||||
"""
|
||||
if _ENGINE_DIR.get(engine, engine) == "python":
|
||||
import importlib.util, sys as _sys
|
||||
mod_path = os.path.join(_HERE, "python", "dynamics_lib.py")
|
||||
if not os.path.exists(mod_path):
|
||||
raise FileNotFoundError(f"Python 引擎未找到: {mod_path}")
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"engines.python.dynamics_lib", mod_path)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod # 返回模块,不是 CDLL
|
||||
|
||||
for p in _dll_candidates(engine):
|
||||
if os.path.exists(p):
|
||||
lib = ctypes.CDLL(p)
|
||||
_setup_prototype(lib)
|
||||
return lib
|
||||
raise FileNotFoundError(
|
||||
f"DLL 未找到(引擎 {engine}),候选路径:\n" +
|
||||
"\n".join(f" {p}" for p in _dll_candidates(engine)) +
|
||||
f"\n请先编译:cd engines/{engine} && make dll"
|
||||
)
|
||||
|
||||
|
||||
def _setup_prototype(lib: ctypes.CDLL) -> None:
|
||||
"""配置 run_dynamics 的参数类型和返回类型。"""
|
||||
c_dbl_p = ctypes.POINTER(ctypes.c_double)
|
||||
c_int_p = ctypes.POINTER(ctypes.c_int)
|
||||
cb_type = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_int)
|
||||
|
||||
lib.run_dynamics.restype = ctypes.c_int
|
||||
lib.run_dynamics.argtypes = [
|
||||
ctypes.c_int, # n_atoms
|
||||
c_dbl_p, # pos_init [n_atoms*3]
|
||||
c_dbl_p, # vel_init [n_atoms*3]
|
||||
c_dbl_p, # masses [n_atoms]
|
||||
c_int_p, # fixed [n_atoms*3]
|
||||
ctypes.c_int, # n_bonds
|
||||
c_int_p, # bond_pairs [n_bonds*2]
|
||||
c_dbl_p, # bond_k [n_bonds]
|
||||
c_dbl_p, # bond_r0 [n_bonds]
|
||||
ctypes.c_double, # box_a
|
||||
ctypes.c_double, # dt
|
||||
ctypes.c_int, # NT
|
||||
ctypes.c_int, # NSTEP
|
||||
ctypes.c_int, # warmup_steps
|
||||
ctypes.c_int, # method_id
|
||||
ctypes.c_double, # Gx
|
||||
ctypes.c_double, # Gy
|
||||
ctypes.c_double, # Gz
|
||||
ctypes.c_double, # Bx
|
||||
ctypes.c_double, # By
|
||||
ctypes.c_double, # Bz
|
||||
ctypes.c_int, # gravity_field
|
||||
ctypes.c_int, # elastic_force
|
||||
ctypes.c_int, # damping_force
|
||||
ctypes.c_double, # gravity_strength
|
||||
ctypes.c_int, # n_drivers
|
||||
c_int_p, # drv_idx [n_drivers]
|
||||
c_dbl_p, # drv_amp [n_drivers*3]
|
||||
c_dbl_p, # drv_freq [n_drivers*3]
|
||||
c_dbl_p, # drv_phi [n_drivers*3]
|
||||
c_dbl_p, # drv_eq [n_drivers*3]
|
||||
c_dbl_p, # drv_ncycles [n_drivers]
|
||||
c_int_p, # drv_has_period [n_drivers]
|
||||
ctypes.c_int, # n_frames
|
||||
c_dbl_p, # out_x
|
||||
c_dbl_p, # out_y
|
||||
c_dbl_p, # out_z
|
||||
c_dbl_p, # out_vx
|
||||
c_dbl_p, # out_vy
|
||||
c_dbl_p, # out_vz
|
||||
cb_type, # progress_cb (可为 NULL)
|
||||
]
|
||||
|
||||
|
||||
def _c_dbl(arr: np.ndarray):
|
||||
"""返回 float64 C 连续数组的 ctypes 指针。"""
|
||||
a = np.ascontiguousarray(arr, dtype=np.float64)
|
||||
return a.ctypes.data_as(ctypes.POINTER(ctypes.c_double)), a
|
||||
|
||||
|
||||
def _c_int(arr: np.ndarray):
|
||||
"""返回 int32 C 连续数组的 ctypes 指针。"""
|
||||
a = np.ascontiguousarray(arr, dtype=np.int32)
|
||||
return a.ctypes.data_as(ctypes.POINTER(ctypes.c_int)), a
|
||||
|
||||
|
||||
def _is_python_module(lib) -> bool:
|
||||
"""判断 lib 是否为 Python 引擎模块(而非 ctypes.CDLL)。"""
|
||||
return not isinstance(lib, ctypes.CDLL)
|
||||
|
||||
|
||||
def _run_dynamics_python(lib, config, atom_positions, atom_velocities, atom_masses,
|
||||
atom_fixed, bond_pairs, bond_stiffness, bond_rest_lengths,
|
||||
driver_data, atom_ids, progress_cb=None) -> dict:
|
||||
"""调用 Python 引擎的 run_dynamics(),参数/返回值格式与 ctypes 版相同。"""
|
||||
n = len(atom_masses)
|
||||
NT = int(config["NT"])
|
||||
NSTEP = int(config.get("NSTEP", 1))
|
||||
warmup = int(config.get("warmup_steps", 0))
|
||||
dt = float(config["DT"])
|
||||
box_a = float(config.get("box_a", 300.0))
|
||||
method_str = str(config.get("method", "leapfrog")).lower().replace(" ", "_")
|
||||
method_id = _METHOD_ID.get(method_str, 3)
|
||||
|
||||
G = config.get("G", [0.0, 0.0, 0.0])
|
||||
B = config.get("B", [0.0, 0.0, 0.0])
|
||||
if hasattr(G, "tolist"): G = G.tolist()
|
||||
if hasattr(B, "tolist"): B = B.tolist()
|
||||
|
||||
gravity_field = int(config.get("gravity_field", 0))
|
||||
elastic_force = int(config.get("elastic_force", 1))
|
||||
damping_force = int(config.get("damping_force", 0))
|
||||
gravity_strength = float(config.get("gravity_strength", 1.0))
|
||||
|
||||
record_steps = NT - warmup
|
||||
n_frames = max(1, record_steps // NSTEP)
|
||||
|
||||
nd = len(driver_data) if driver_data else 0
|
||||
if nd > 0:
|
||||
drv_idx = np.array([d["local_idx"] for d in driver_data], dtype=np.int64)
|
||||
drv_amp = np.array([d["amp"] for d in driver_data], dtype=np.float64)
|
||||
drv_freq = np.array([d["freq"] for d in driver_data], dtype=np.float64)
|
||||
drv_phi = np.array([d["phi"] for d in driver_data], dtype=np.float64)
|
||||
drv_eq = np.array([d["eq_pos"] for d in driver_data], dtype=np.float64)
|
||||
drv_nc = np.array([d["n_cycles"] for d in driver_data], dtype=np.float64)
|
||||
drv_hp = np.array([d["has_period"]for d in driver_data], dtype=np.int32)
|
||||
else:
|
||||
drv_idx = drv_amp = drv_freq = drv_phi = drv_eq = drv_nc = drv_hp = \
|
||||
np.zeros(0, dtype=np.int64)
|
||||
|
||||
out_x, out_y, out_z, out_vx, out_vy, out_vz = lib.run_dynamics(
|
||||
n_atoms=n,
|
||||
pos_init=atom_positions,
|
||||
vel_init=atom_velocities,
|
||||
masses=atom_masses,
|
||||
fixed=atom_fixed,
|
||||
n_bonds=len(bond_pairs),
|
||||
bond_pairs=bond_pairs,
|
||||
bond_k=bond_stiffness,
|
||||
bond_r0=bond_rest_lengths,
|
||||
box_a=box_a,
|
||||
dt=dt,
|
||||
NT=NT,
|
||||
NSTEP=NSTEP,
|
||||
warmup_steps=warmup,
|
||||
method_id=method_id,
|
||||
Gx=float(G[0]), Gy=float(G[1]), Gz=float(G[2]),
|
||||
Bx=float(B[0]), By=float(B[1]), Bz=float(B[2]),
|
||||
gravity_field=gravity_field,
|
||||
elastic_force=elastic_force,
|
||||
damping_force=damping_force,
|
||||
gravity_strength=gravity_strength,
|
||||
n_drivers=nd,
|
||||
drv_idx=drv_idx,
|
||||
drv_amp=drv_amp,
|
||||
drv_freq=drv_freq,
|
||||
drv_phi=drv_phi,
|
||||
drv_eq=drv_eq,
|
||||
drv_ncycles=drv_nc,
|
||||
drv_has_period=drv_hp,
|
||||
n_frames=n_frames,
|
||||
progress_cb=progress_cb,
|
||||
)
|
||||
|
||||
shape = (n_frames, n)
|
||||
t_arr = np.arange(n_frames) * NSTEP * dt + warmup * dt
|
||||
return {
|
||||
"x": out_x.reshape(shape), "y": out_y.reshape(shape),
|
||||
"z": out_z.reshape(shape), "vx": out_vx.reshape(shape),
|
||||
"vy": out_vy.reshape(shape), "vz": out_vz.reshape(shape),
|
||||
"t": t_arr,
|
||||
}
|
||||
|
||||
|
||||
def run_dynamics_dll(
|
||||
lib,
|
||||
config: dict,
|
||||
atom_positions: np.ndarray, # (n_atoms, 3)
|
||||
atom_velocities: np.ndarray, # (n_atoms, 3)
|
||||
atom_masses: np.ndarray, # (n_atoms,)
|
||||
atom_fixed: np.ndarray, # (n_atoms, 3) int, 1=固定
|
||||
bond_pairs: np.ndarray, # (n_bonds, 2) int 0-based 局部索引
|
||||
bond_stiffness: np.ndarray, # (n_bonds,)
|
||||
bond_rest_lengths: np.ndarray,# (n_bonds,)
|
||||
driver_data: list, # 驱动原子列表(见下文)
|
||||
atom_ids: np.ndarray, # (n_atoms,) 全局 atom id(用于驱动原子查找)
|
||||
progress_cb=None,
|
||||
) -> dict:
|
||||
"""调用 DLL 的 run_dynamics(),返回抽帧后的轨迹数组。
|
||||
|
||||
driver_data 格式(每个元素对应一个驱动原子):
|
||||
{
|
||||
"atom_id": int, # 全局 atom id
|
||||
"local_idx": int, # 在 atom_ids 数组中的位置(0-based)
|
||||
"amp": [ax, ay, az],
|
||||
"freq": [fx, fy, fz],
|
||||
"phi": [px, py, pz],
|
||||
"eq_pos": [ex, ey, ez],
|
||||
"n_cycles": float, # 0=不限
|
||||
"has_period": int, # 0/1
|
||||
}
|
||||
|
||||
返回:
|
||||
{
|
||||
"x": np.ndarray (n_frames, n_atoms),
|
||||
"y": ...,
|
||||
"z": ...,
|
||||
"vx": ..., "vy": ..., "vz": ...,
|
||||
"t": np.ndarray (n_frames,), # 时间轴
|
||||
}
|
||||
"""
|
||||
# Python 引擎:直接调用模块函数,不走 ctypes
|
||||
if _is_python_module(lib):
|
||||
return _run_dynamics_python(
|
||||
lib, config, atom_positions, atom_velocities, atom_masses,
|
||||
atom_fixed, bond_pairs, bond_stiffness, bond_rest_lengths,
|
||||
driver_data, atom_ids, progress_cb)
|
||||
|
||||
n = len(atom_masses)
|
||||
NT = int(config["NT"])
|
||||
NSTEP = int(config.get("NSTEP", 1))
|
||||
warmup = int(config.get("warmup_steps", 0))
|
||||
dt = float(config["DT"])
|
||||
box_a = float(config.get("box_a", 300.0))
|
||||
method_str = str(config.get("method", "leapfrog")).lower().replace(" ", "_")
|
||||
method_id = _METHOD_ID.get(method_str, 3)
|
||||
|
||||
G = config.get("G", [0.0, 0.0, 0.0])
|
||||
B = config.get("B", [0.0, 0.0, 0.0])
|
||||
if hasattr(G, "tolist"): G = G.tolist()
|
||||
if hasattr(B, "tolist"): B = B.tolist()
|
||||
|
||||
gravity_field = int(config.get("gravity_field", 0))
|
||||
elastic_force = int(config.get("elastic_force", 1))
|
||||
damping_force = int(config.get("damping_force", 0))
|
||||
gravity_strength = float(config.get("gravity_strength", 1.0))
|
||||
|
||||
# ── 计算帧数 ──────────────────────────────────────────────
|
||||
record_steps = NT - warmup
|
||||
n_frames = max(1, record_steps // NSTEP)
|
||||
|
||||
# ── 驱动原子数据 ──────────────────────────────────────────
|
||||
nd = len(driver_data) if driver_data else 0
|
||||
if nd > 0:
|
||||
drv_idx_arr = np.array([d["local_idx"] for d in driver_data], dtype=np.int32)
|
||||
drv_amp_arr = np.array([d["amp"] for d in driver_data], dtype=np.float64).ravel()
|
||||
drv_freq_arr = np.array([d["freq"] for d in driver_data], dtype=np.float64).ravel()
|
||||
drv_phi_arr = np.array([d["phi"] for d in driver_data], dtype=np.float64).ravel()
|
||||
drv_eq_arr = np.array([d["eq_pos"] for d in driver_data], dtype=np.float64).ravel()
|
||||
drv_nc_arr = np.array([d["n_cycles"] for d in driver_data], dtype=np.float64)
|
||||
drv_hp_arr = np.array([d["has_period"] for d in driver_data], dtype=np.int32)
|
||||
else:
|
||||
drv_idx_arr = np.zeros(1, dtype=np.int32)
|
||||
drv_amp_arr = np.zeros(3, dtype=np.float64)
|
||||
drv_freq_arr = np.zeros(3, dtype=np.float64)
|
||||
drv_phi_arr = np.zeros(3, dtype=np.float64)
|
||||
drv_eq_arr = np.zeros(3, dtype=np.float64)
|
||||
drv_nc_arr = np.zeros(1, dtype=np.float64)
|
||||
drv_hp_arr = np.zeros(1, dtype=np.int32)
|
||||
|
||||
# ── 输出缓冲区 ────────────────────────────────────────────
|
||||
out_x = np.zeros(n_frames * n, dtype=np.float64)
|
||||
out_y = np.zeros(n_frames * n, dtype=np.float64)
|
||||
out_z = np.zeros(n_frames * n, dtype=np.float64)
|
||||
out_vx = np.zeros(n_frames * n, dtype=np.float64)
|
||||
out_vy = np.zeros(n_frames * n, dtype=np.float64)
|
||||
out_vz = np.zeros(n_frames * n, dtype=np.float64)
|
||||
|
||||
# ── ctypes 指针(保留 arr 引用防止 GC) ──────────────────
|
||||
p_pos, _pos = _c_dbl(atom_positions.ravel())
|
||||
p_vel, _vel = _c_dbl(atom_velocities.ravel())
|
||||
p_mass, _mass = _c_dbl(atom_masses)
|
||||
p_fixed, _fixed = _c_int(atom_fixed.ravel())
|
||||
p_bp, _bp = _c_int(bond_pairs.ravel() if len(bond_pairs) else np.zeros(2, dtype=np.int32))
|
||||
p_bk, _bk = _c_dbl(bond_stiffness if len(bond_stiffness) else np.zeros(1))
|
||||
p_br0, _br0 = _c_dbl(bond_rest_lengths if len(bond_rest_lengths) else np.zeros(1))
|
||||
p_didx, _didx = _c_int(drv_idx_arr)
|
||||
p_damp, _damp = _c_dbl(drv_amp_arr)
|
||||
p_dfrq, _dfrq = _c_dbl(drv_freq_arr)
|
||||
p_dphi, _dphi = _c_dbl(drv_phi_arr)
|
||||
p_deq, _deq = _c_dbl(drv_eq_arr)
|
||||
p_dnc, _dnc = _c_dbl(drv_nc_arr)
|
||||
p_dhp, _dhp = _c_int(drv_hp_arr)
|
||||
|
||||
p_ox = out_x.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
|
||||
p_oy = out_y.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
|
||||
p_oz = out_z.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
|
||||
p_ovx = out_vx.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
|
||||
p_ovy = out_vy.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
|
||||
p_ovz = out_vz.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
|
||||
|
||||
# 进度回调
|
||||
cb_type = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_int)
|
||||
if progress_cb is not None:
|
||||
cb = cb_type(progress_cb)
|
||||
else:
|
||||
cb = ctypes.cast(None, cb_type)
|
||||
|
||||
ret = lib.run_dynamics(
|
||||
n,
|
||||
p_pos, p_vel, p_mass, p_fixed,
|
||||
len(bond_pairs), p_bp, p_bk, p_br0,
|
||||
box_a, dt, NT, NSTEP, warmup, method_id,
|
||||
float(G[0]), float(G[1]), float(G[2]),
|
||||
float(B[0]), float(B[1]), float(B[2]),
|
||||
gravity_field, elastic_force, damping_force, gravity_strength,
|
||||
nd, p_didx, p_damp, p_dfrq, p_dphi, p_deq, p_dnc, p_dhp,
|
||||
n_frames,
|
||||
p_ox, p_oy, p_oz, p_ovx, p_ovy, p_ovz,
|
||||
cb,
|
||||
)
|
||||
|
||||
if ret != 0:
|
||||
raise RuntimeError(f"run_dynamics() returned error code {ret}")
|
||||
|
||||
shape = (n_frames, n)
|
||||
t_arr = np.arange(n_frames) * NSTEP * dt + warmup * dt
|
||||
|
||||
return {
|
||||
"x": out_x.reshape(shape),
|
||||
"y": out_y.reshape(shape),
|
||||
"z": out_z.reshape(shape),
|
||||
"vx": out_vx.reshape(shape),
|
||||
"vy": out_vy.reshape(shape),
|
||||
"vz": out_vz.reshape(shape),
|
||||
"t": t_arr,
|
||||
}
|
||||
|
||||
|
||||
def is_dll_available(engine: str = "c") -> bool:
|
||||
"""检查指定引擎是否可用(DLL 已编译或 Python 模块存在)。"""
|
||||
if _ENGINE_DIR.get(engine, engine) == "python":
|
||||
return os.path.exists(os.path.join(_HERE, "python", "dynamics_lib.py"))
|
||||
return any(os.path.exists(p) for p in _dll_candidates(engine))
|
||||
@@ -0,0 +1,47 @@
|
||||
# engines/fortran/Makefile
|
||||
|
||||
FC = gfortran
|
||||
FFLAGS = -O3 -march=native -Wall -Wextra
|
||||
SRCS = main.f90
|
||||
LIB_SRC = dynamics_lib.f90
|
||||
|
||||
UNAME_S := $(shell uname -s 2>/dev/null || echo Windows)
|
||||
|
||||
ifeq ($(UNAME_S),Windows)
|
||||
STATIC_FLAGS = -static-libgcc -static-libgfortran -static-libquadmath
|
||||
else
|
||||
STATIC_FLAGS =
|
||||
endif
|
||||
|
||||
TARGET = build/dynamics_f90.exe
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
DLL_TARGET = build/dynamics_f90.so
|
||||
DLL_FLAGS = -shared -fPIC
|
||||
else ifeq ($(UNAME_S),Darwin)
|
||||
DLL_TARGET = build/dynamics_f90.dylib
|
||||
DLL_FLAGS = -dynamiclib
|
||||
else
|
||||
DLL_TARGET = build/dynamics_f90.dll
|
||||
DLL_FLAGS = -shared -fPIC
|
||||
endif
|
||||
|
||||
.PHONY: all dll clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
dll: $(DLL_TARGET)
|
||||
|
||||
$(TARGET): $(SRCS) | build
|
||||
$(FC) $(FFLAGS) $(STATIC_FLAGS) -o $@ $(SRCS)
|
||||
@echo " === Fortran engine built: $@ ==="
|
||||
|
||||
$(DLL_TARGET): $(LIB_SRC) | build
|
||||
$(FC) $(FFLAGS) $(STATIC_FLAGS) $(DLL_FLAGS) -o $@ $(LIB_SRC)
|
||||
@echo " === Fortran DLL built: $@ ==="
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
clean:
|
||||
rm -rf build *.o *.mod
|
||||
@@ -0,0 +1 @@
|
||||
{"n_atoms": 40, "nt": 200000, "step_time": 0.005991018545627594}
|
||||
@@ -0,0 +1,483 @@
|
||||
! engines/fortran/dynamics_lib.f90
|
||||
! ---------------------------------
|
||||
! 纯计算 DLL(Fortran 版):无文件 I/O,由 Python ctypes 调用。
|
||||
! 算法与 main.f90 / compute.py 完全一致。
|
||||
! 使用 iso_c_binding 导出 C 兼容接口。
|
||||
!
|
||||
! 编译(Windows):
|
||||
! gfortran -O3 -march=native -shared -fPIC -o build/dynamics_f90.dll dynamics_lib.f90
|
||||
! 编译(Linux):
|
||||
! gfortran -O3 -march=native -shared -fPIC -o build/dynamics_f90.so dynamics_lib.f90
|
||||
! 编译(macOS):
|
||||
! gfortran -O3 -march=native -dynamiclib -o build/dynamics_f90.dylib dynamics_lib.f90
|
||||
|
||||
module dynamics_dll
|
||||
use iso_c_binding, only: c_int, c_double, c_funptr, c_f_procpointer, c_associated
|
||||
implicit none
|
||||
private
|
||||
|
||||
real(c_double), parameter :: TWO_PI = 2.0d0 * 3.14159265358979323846d0
|
||||
|
||||
public :: run_dynamics
|
||||
|
||||
contains
|
||||
|
||||
! ── 保守加速度 ───────────────────────────────────────────────
|
||||
subroutine 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)
|
||||
integer, intent(in) :: n, gravity_field, elastic_force, n_bonds
|
||||
real(c_double), intent(in) :: x(n), y(n), z(n), m(n)
|
||||
real(c_double), intent(in) :: Gx, Gy, Gz
|
||||
integer, intent(in) :: bond_pairs(2, n_bonds)
|
||||
real(c_double), intent(in) :: bond_k(n_bonds), bond_r0(n_bonds)
|
||||
real(c_double), intent(out) :: ax(n), ay(n), az(n)
|
||||
|
||||
integer :: b, ii, jj
|
||||
real(c_double) :: dx, dy, dz, dist, fac, fx, fy, fz_b
|
||||
|
||||
if (gravity_field /= 0) then
|
||||
ax = Gx; ay = Gy; az = Gz
|
||||
else
|
||||
ax = 0.0d0; ay = 0.0d0; az = 0.0d0
|
||||
end if
|
||||
|
||||
if (elastic_force == 0 .or. n_bonds == 0) return
|
||||
|
||||
do b = 1, n_bonds
|
||||
ii = bond_pairs(1, b) + 1 ! 0-based → 1-based
|
||||
jj = bond_pairs(2, b) + 1
|
||||
dx = x(jj)-x(ii); dy = y(jj)-y(ii); dz = z(jj)-z(ii)
|
||||
dist = sqrt(dx*dx + dy*dy + dz*dz)
|
||||
if (dist < 1.0d-12) cycle
|
||||
fac = bond_k(b) * (dist - bond_r0(b)) / dist
|
||||
fx = fac*dx; fy = fac*dy; fz_b = fac*dz
|
||||
ax(ii) = ax(ii) + fx/m(ii); ay(ii) = ay(ii) + fy/m(ii); az(ii) = az(ii) + fz_b/m(ii)
|
||||
ax(jj) = ax(jj) - fx/m(jj); ay(jj) = ay(jj) - fy/m(jj); az(jj) = az(jj) - fz_b/m(jj)
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
! ── 完整加速度(含阻尼)──────────────────────────────────────
|
||||
subroutine accel_full(n, x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, ax, ay, az)
|
||||
integer, intent(in) :: n, gravity_field, elastic_force, damping_force, n_bonds
|
||||
real(c_double), intent(in) :: x(n), y(n), z(n), vx(n), vy(n), vz(n), m(n)
|
||||
real(c_double), intent(in) :: Gx, Gy, Gz, Bx, By, Bz
|
||||
integer, intent(in) :: bond_pairs(2, n_bonds)
|
||||
real(c_double), intent(in) :: bond_k(n_bonds), bond_r0(n_bonds)
|
||||
real(c_double), intent(out) :: ax(n), ay(n), az(n)
|
||||
|
||||
integer :: i
|
||||
|
||||
call 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 /= 0) then
|
||||
do i = 1, n
|
||||
ax(i) = ax(i) - Bx*vx(i)/m(i)
|
||||
ay(i) = ay(i) - By*vy(i)/m(i)
|
||||
az(i) = az(i) - Bz*vz(i)/m(i)
|
||||
end do
|
||||
end if
|
||||
end subroutine
|
||||
|
||||
! ── 边界 + 固定约束 ──────────────────────────────────────────
|
||||
subroutine apply_bc(n, x, y, z, vx, vy, vz, fixed, pos_init, box_a)
|
||||
integer, intent(in) :: n
|
||||
real(c_double), intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
|
||||
integer, intent(in) :: fixed(3, n)
|
||||
real(c_double), intent(in) :: pos_init(3, n), box_a
|
||||
|
||||
integer :: i
|
||||
real(c_double) :: lo, hi
|
||||
|
||||
lo = -box_a; hi = box_a
|
||||
|
||||
! 反弹
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0 .and. fixed(2,i)/=0 .and. fixed(3,i)/=0) cycle
|
||||
if (x(i)>hi) then; x(i)=hi; vx(i)=-abs(vx(i)); end if
|
||||
if (x(i)<lo) then; x(i)=lo; vx(i)= abs(vx(i)); end if
|
||||
if (y(i)>hi) then; y(i)=hi; vy(i)=-abs(vy(i)); end if
|
||||
if (y(i)<lo) then; y(i)=lo; vy(i)= abs(vy(i)); end if
|
||||
if (z(i)>hi) then; z(i)=hi; vz(i)=-abs(vz(i)); end if
|
||||
if (z(i)<lo) then; z(i)=lo; vz(i)= abs(vz(i)); end if
|
||||
end do
|
||||
! 回绕
|
||||
do i = 1, n
|
||||
if (x(i)>hi) x(i)=lo; if (x(i)<lo) x(i)=hi
|
||||
if (y(i)>hi) y(i)=lo; if (y(i)<lo) y(i)=hi
|
||||
if (z(i)>hi) z(i)=lo; if (z(i)<lo) z(i)=hi
|
||||
end do
|
||||
! 逐自由度固定约束
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0) then; x(i)=pos_init(1,i); vx(i)=0.0d0; end if
|
||||
if (fixed(2,i)/=0) then; y(i)=pos_init(2,i); vy(i)=0.0d0; end if
|
||||
if (fixed(3,i)/=0) then; z(i)=pos_init(3,i); vz(i)=0.0d0; end if
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
! ── 蛙跳法 ───────────────────────────────────────────────────
|
||||
subroutine leapfrog_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
integer, intent(in) :: n, gravity_field, elastic_force, damping_force, n_bonds
|
||||
real(c_double), intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
|
||||
real(c_double), intent(in) :: m(n), bond_k(n_bonds), bond_r0(n_bonds)
|
||||
integer, intent(in) :: fixed(3,n), bond_pairs(2,n_bonds)
|
||||
real(c_double), intent(in) :: Gx, Gy, Gz, Bx, By, Bz, dt
|
||||
|
||||
real(c_double) :: ax(n), ay(n), az(n), ax_, ay_, az_
|
||||
logical :: has_damp
|
||||
integer :: i
|
||||
|
||||
call 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)
|
||||
has_damp = (damping_force/=0) .and. (abs(Bx)+abs(By)+abs(Bz) > 0.0d0)
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0 .and. fixed(2,i)/=0 .and. fixed(3,i)/=0) cycle
|
||||
if (has_damp) then
|
||||
ax_ = Bx*dt/(2.0d0*m(i)); ay_ = By*dt/(2.0d0*m(i)); az_ = Bz*dt/(2.0d0*m(i))
|
||||
vx(i) = (vx(i)*(1.0d0-ax_) + ax(i)*dt)/(1.0d0+ax_)
|
||||
vy(i) = (vy(i)*(1.0d0-ay_) + ay(i)*dt)/(1.0d0+ay_)
|
||||
vz(i) = (vz(i)*(1.0d0-az_) + az(i)*dt)/(1.0d0+az_)
|
||||
else
|
||||
vx(i) = vx(i)+ax(i)*dt; vy(i) = vy(i)+ay(i)*dt; vz(i) = vz(i)+az(i)*dt
|
||||
end if
|
||||
x(i) = x(i)+vx(i)*dt; y(i) = y(i)+vy(i)*dt; z(i) = z(i)+vz(i)*dt
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
! ── 显式欧拉法 ───────────────────────────────────────────────
|
||||
subroutine euler_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
integer, intent(in) :: n, gravity_field, elastic_force, damping_force, n_bonds
|
||||
real(c_double), intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
|
||||
real(c_double), intent(in) :: m(n), bond_k(n_bonds), bond_r0(n_bonds)
|
||||
integer, intent(in) :: fixed(3,n), bond_pairs(2,n_bonds)
|
||||
real(c_double), intent(in) :: Gx, Gy, Gz, Bx, By, Bz, dt
|
||||
|
||||
real(c_double) :: ax(n), ay(n), az(n)
|
||||
integer :: i
|
||||
|
||||
call accel_full(n, x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, ax, ay, az)
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0 .and. fixed(2,i)/=0 .and. fixed(3,i)/=0) cycle
|
||||
x(i) = x(i)+vx(i)*dt; y(i) = y(i)+vy(i)*dt; z(i) = z(i)+vz(i)*dt
|
||||
vx(i)= vx(i)+ax(i)*dt; vy(i)= vy(i)+ay(i)*dt; vz(i)= vz(i)+az(i)*dt
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
! ── 隐式欧拉法 ───────────────────────────────────────────────
|
||||
subroutine implicit_euler_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
integer, intent(in) :: n, gravity_field, elastic_force, damping_force, n_bonds
|
||||
real(c_double), intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
|
||||
real(c_double), intent(in) :: m(n), bond_k(n_bonds), bond_r0(n_bonds)
|
||||
integer, intent(in) :: fixed(3,n), bond_pairs(2,n_bonds)
|
||||
real(c_double), intent(in) :: Gx, Gy, Gz, Bx, By, Bz, dt
|
||||
|
||||
real(c_double) :: vxn(n), vyn(n), vzn(n), ax(n), ay(n), az(n)
|
||||
real(c_double) :: gamma_x, gamma_y, gamma_z
|
||||
integer :: i
|
||||
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0 .and. fixed(2,i)/=0 .and. fixed(3,i)/=0) then
|
||||
vxn(i)=0.0d0; vyn(i)=0.0d0; vzn(i)=0.0d0; cycle
|
||||
end if
|
||||
gamma_x = Bx/m(i); gamma_y = By/m(i); gamma_z = Bz/m(i)
|
||||
vxn(i) = (vx(i)+Gx*dt)/(1.0d0+gamma_x*dt)
|
||||
vyn(i) = (vy(i)+Gy*dt)/(1.0d0+gamma_y*dt)
|
||||
vzn(i) = (vz(i)+Gz*dt)/(1.0d0+gamma_z*dt)
|
||||
end do
|
||||
call accel_full(n, x, y, z, vxn, vyn, vzn, m, Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, ax, ay, az)
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0 .and. fixed(2,i)/=0 .and. fixed(3,i)/=0) cycle
|
||||
vx(i)=vx(i)+ax(i)*dt; vy(i)=vy(i)+ay(i)*dt; vz(i)=vz(i)+az(i)*dt
|
||||
x(i) =x(i) +vx(i)*dt; y(i) =y(i) +vy(i)*dt; z(i) =z(i) +vz(i)*dt
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
! ── 中点法 ───────────────────────────────────────────────────
|
||||
subroutine midpoint_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
integer, intent(in) :: n, gravity_field, elastic_force, damping_force, n_bonds
|
||||
real(c_double), intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
|
||||
real(c_double), intent(in) :: m(n), bond_k(n_bonds), bond_r0(n_bonds)
|
||||
integer, intent(in) :: fixed(3,n), bond_pairs(2,n_bonds)
|
||||
real(c_double), intent(in) :: Gx, Gy, Gz, Bx, By, Bz, dt
|
||||
|
||||
real(c_double) :: ax(n), ay(n), az(n)
|
||||
real(c_double) :: xm(n), ym(n), zm(n), vxm(n), vym(n), vzm(n)
|
||||
real(c_double) :: axm(n), aym(n), azm(n)
|
||||
integer :: i
|
||||
|
||||
call accel_full(n, x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, ax, ay, az)
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0 .and. fixed(2,i)/=0 .and. fixed(3,i)/=0) then
|
||||
xm(i)=x(i); ym(i)=y(i); zm(i)=z(i)
|
||||
vxm(i)=0.0d0; vym(i)=0.0d0; vzm(i)=0.0d0; cycle
|
||||
end if
|
||||
xm(i) = x(i) +0.5d0*vx(i)*dt; ym(i) = y(i) +0.5d0*vy(i)*dt; zm(i) = z(i) +0.5d0*vz(i)*dt
|
||||
vxm(i) = vx(i)+0.5d0*ax(i)*dt; vym(i) = vy(i)+0.5d0*ay(i)*dt; vzm(i) = vz(i)+0.5d0*az(i)*dt
|
||||
x(i) = x(i) +vxm(i)*dt; y(i) = y(i) +vym(i)*dt; z(i) = z(i) +vzm(i)*dt
|
||||
end do
|
||||
call accel_full(n, xm, ym, zm, vxm, vym, vzm, m, Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, axm, aym, azm)
|
||||
do i = 1, n
|
||||
if (fixed(1,i)/=0 .and. fixed(2,i)/=0 .and. fixed(3,i)/=0) cycle
|
||||
vx(i)=vx(i)+axm(i)*dt; vy(i)=vy(i)+aym(i)*dt; vz(i)=vz(i)+azm(i)*dt
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
! ── 驱动力施加 ───────────────────────────────────────────────
|
||||
subroutine apply_drive(n, x, y, z, vx, vy, vz, t, step, dt, &
|
||||
nd, drv_idx, drv_amp, drv_freq, drv_phi, &
|
||||
drv_eq, drv_ncycles, drv_has_period, freeze)
|
||||
integer, intent(in) :: n, nd, step
|
||||
real(c_double), intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
|
||||
real(c_double), intent(in) :: t, dt
|
||||
integer, intent(in) :: drv_idx(nd), drv_has_period(nd)
|
||||
real(c_double), intent(in) :: drv_amp(3,nd), drv_freq(3,nd)
|
||||
real(c_double), intent(in) :: drv_phi(3,nd), drv_eq(3,nd)
|
||||
real(c_double), intent(in) :: drv_ncycles(nd)
|
||||
real(c_double), intent(inout) :: freeze(3,nd)
|
||||
|
||||
integer :: d, idx, ps
|
||||
real(c_double) :: fx, fy, fz, mf, px, py, pz
|
||||
|
||||
do d = 1, nd
|
||||
idx = drv_idx(d) + 1 ! 0-based → 1-based
|
||||
fx = drv_freq(1,d); fy = drv_freq(2,d); fz = drv_freq(3,d)
|
||||
|
||||
if (drv_has_period(d) /= 0) then
|
||||
mf = max(abs(fx), max(abs(fy), abs(fz)))
|
||||
ps = 0
|
||||
if (mf > 1.0d-12) ps = int(drv_ncycles(d)/mf/dt)
|
||||
if (step > ps) then
|
||||
x(idx)=freeze(1,d); y(idx)=freeze(2,d); z(idx)=freeze(3,d)
|
||||
vx(idx)=0.0d0; vy(idx)=0.0d0; vz(idx)=0.0d0
|
||||
cycle
|
||||
end if
|
||||
px = drv_eq(1,d)+drv_amp(1,d)*cos(TWO_PI*fx*t+drv_phi(1,d))
|
||||
py = drv_eq(2,d)+drv_amp(2,d)*cos(TWO_PI*fy*t+drv_phi(2,d))
|
||||
pz = drv_eq(3,d)+drv_amp(3,d)*cos(TWO_PI*fz*t+drv_phi(3,d))
|
||||
if (step == ps) then
|
||||
freeze(1,d)=px; freeze(2,d)=py; freeze(3,d)=pz
|
||||
end if
|
||||
end if
|
||||
x(idx) = drv_eq(1,d)+drv_amp(1,d)*cos(TWO_PI*fx*t+drv_phi(1,d))
|
||||
y(idx) = drv_eq(2,d)+drv_amp(2,d)*cos(TWO_PI*fy*t+drv_phi(2,d))
|
||||
z(idx) = drv_eq(3,d)+drv_amp(3,d)*cos(TWO_PI*fz*t+drv_phi(3,d))
|
||||
vx(idx) = -drv_amp(1,d)*TWO_PI*fx*sin(TWO_PI*fx*t+drv_phi(1,d))
|
||||
vy(idx) = -drv_amp(2,d)*TWO_PI*fy*sin(TWO_PI*fy*t+drv_phi(2,d))
|
||||
vz(idx) = -drv_amp(3,d)*TWO_PI*fz*sin(TWO_PI*fz*t+drv_phi(3,d))
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
! ══════════════════════════════════════════════════════════════
|
||||
! 导出函数:run_dynamics(C 兼容接口,bind(C))
|
||||
! 接口与 C/C++ DLL 完全相同(扁平 C-contiguous 数组)。
|
||||
! ══════════════════════════════════════════════════════════════
|
||||
integer(c_int) function run_dynamics( &
|
||||
n_atoms, pos_init, vel_init, masses, fixed, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, &
|
||||
box_a, dt, NT, NSTEP, warmup_steps, method_id, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, gravity_strength, &
|
||||
n_drivers, drv_idx, drv_amp, drv_freq, drv_phi, drv_eq, &
|
||||
drv_ncycles, drv_has_period, &
|
||||
n_frames, out_x, out_y, out_z, out_vx, out_vy, out_vz, &
|
||||
progress_cb) &
|
||||
bind(C, name="run_dynamics")
|
||||
|
||||
integer(c_int), value, intent(in) :: n_atoms, n_bonds, NT, NSTEP
|
||||
integer(c_int), value, intent(in) :: warmup_steps, method_id
|
||||
integer(c_int), value, intent(in) :: gravity_field, elastic_force, damping_force
|
||||
integer(c_int), value, intent(in) :: n_drivers, n_frames
|
||||
real(c_double), value, intent(in) :: box_a, dt
|
||||
real(c_double), value, intent(in) :: Gx, Gy, Gz, Bx, By, Bz
|
||||
real(c_double), value, intent(in) :: gravity_strength
|
||||
|
||||
! 扁平数组:Python 传入 C-contiguous int32/float64
|
||||
! Fortran 以列优先解释,维度反转:(3,n) 对应 C 的 n×3
|
||||
real(c_double), intent(in) :: pos_init(3, n_atoms)
|
||||
real(c_double), intent(in) :: vel_init(3, n_atoms)
|
||||
real(c_double), intent(in) :: masses(n_atoms)
|
||||
integer(c_int), intent(in) :: fixed(3, n_atoms)
|
||||
integer(c_int), intent(in) :: bond_pairs(2, n_bonds)
|
||||
real(c_double), intent(in) :: bond_k(n_bonds), bond_r0(n_bonds)
|
||||
integer(c_int), intent(in) :: drv_idx(n_drivers)
|
||||
real(c_double), intent(in) :: drv_amp(3, n_drivers)
|
||||
real(c_double), intent(in) :: drv_freq(3, n_drivers)
|
||||
real(c_double), intent(in) :: drv_phi(3, n_drivers)
|
||||
real(c_double), intent(in) :: drv_eq(3, n_drivers)
|
||||
real(c_double), intent(in) :: drv_ncycles(n_drivers)
|
||||
integer(c_int), intent(in) :: drv_has_period(n_drivers)
|
||||
|
||||
real(c_double), intent(out) :: out_x(n_atoms, n_frames)
|
||||
real(c_double), intent(out) :: out_y(n_atoms, n_frames)
|
||||
real(c_double), intent(out) :: out_z(n_atoms, n_frames)
|
||||
real(c_double), intent(out) :: out_vx(n_atoms, n_frames)
|
||||
real(c_double), intent(out) :: out_vy(n_atoms, n_frames)
|
||||
real(c_double), intent(out) :: out_vz(n_atoms, n_frames)
|
||||
|
||||
type(c_funptr), value, intent(in) :: progress_cb
|
||||
|
||||
! 进度回调接口
|
||||
abstract interface
|
||||
subroutine cb_iface(step, total) bind(C)
|
||||
use iso_c_binding
|
||||
integer(c_int), value :: step, total
|
||||
end subroutine
|
||||
end interface
|
||||
procedure(cb_iface), pointer :: cb_ptr
|
||||
|
||||
integer :: n, s, frame_idx, record_steps, prog_interval, nd
|
||||
real(c_double) :: t, tw
|
||||
real(c_double), allocatable :: x(:), y(:), z(:), vx(:), vy(:), vz(:)
|
||||
real(c_double), allocatable :: ax0(:), ay0(:), az0(:)
|
||||
real(c_double), allocatable :: freeze(:,:)
|
||||
logical :: has_cb
|
||||
|
||||
n = n_atoms
|
||||
nd = n_drivers
|
||||
|
||||
allocate(x(n), y(n), z(n), vx(n), vy(n), vz(n))
|
||||
do s = 1, n
|
||||
x(s) = pos_init(1,s); y(s) = pos_init(2,s); z(s) = pos_init(3,s)
|
||||
vx(s) = vel_init(1,s); vy(s) = vel_init(2,s); vz(s) = vel_init(3,s)
|
||||
end do
|
||||
|
||||
allocate(freeze(3, max(nd,1)))
|
||||
freeze = 0.0d0
|
||||
|
||||
has_cb = c_associated(progress_cb)
|
||||
if (has_cb) call c_f_procpointer(progress_cb, cb_ptr)
|
||||
|
||||
! ── 蛙跳法:初始化 v(-dt/2) ─────────────────────────────
|
||||
if (method_id == 3) then
|
||||
allocate(ax0(n), ay0(n), az0(n))
|
||||
call 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)
|
||||
do s = 1, n
|
||||
if (fixed(1,s)/=0 .and. fixed(2,s)/=0 .and. fixed(3,s)/=0) cycle
|
||||
vx(s)=vx(s)-0.5d0*ax0(s)*dt
|
||||
vy(s)=vy(s)-0.5d0*ay0(s)*dt
|
||||
vz(s)=vz(s)-0.5d0*az0(s)*dt
|
||||
end do
|
||||
deallocate(ax0, ay0, az0)
|
||||
end if
|
||||
|
||||
! ── 初始驱动 t=0 ─────────────────────────────────────────
|
||||
if (nd > 0) call apply_drive(n, x, y, z, vx, vy, vz, 0.0d0, 0, dt, &
|
||||
nd, drv_idx, drv_amp, drv_freq, drv_phi, &
|
||||
drv_eq, drv_ncycles, drv_has_period, freeze)
|
||||
|
||||
! ── 预热 ─────────────────────────────────────────────────
|
||||
do s = 0, warmup_steps-1
|
||||
tw = (s+1)*dt
|
||||
if (nd>0) call apply_drive(n, x, y, z, vx, vy, vz, tw, s, dt, &
|
||||
nd, drv_idx, drv_amp, drv_freq, drv_phi, &
|
||||
drv_eq, drv_ncycles, drv_has_period, freeze)
|
||||
call do_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, method_id, &
|
||||
pos_init, box_a)
|
||||
end do
|
||||
|
||||
! ── 记录循环 ─────────────────────────────────────────────
|
||||
record_steps = NT - warmup_steps
|
||||
prog_interval = max(1, record_steps/100)
|
||||
frame_idx = 0
|
||||
|
||||
do s = 0, record_steps-1
|
||||
if (has_cb .and. mod(s, prog_interval)==0 .and. s>0) call cb_ptr(s, record_steps)
|
||||
|
||||
t = (s+warmup_steps)*dt
|
||||
if (nd>0) call apply_drive(n, x, y, z, vx, vy, vz, t, s, dt, &
|
||||
nd, drv_idx, drv_amp, drv_freq, drv_phi, &
|
||||
drv_eq, drv_ncycles, drv_has_period, freeze)
|
||||
|
||||
if (mod(s, NSTEP)==0 .and. frame_idx<n_frames) then
|
||||
frame_idx = frame_idx+1
|
||||
out_x(:, frame_idx) = x
|
||||
out_y(:, frame_idx) = y
|
||||
out_z(:, frame_idx) = z
|
||||
out_vx(:, frame_idx) = vx
|
||||
out_vy(:, frame_idx) = vy
|
||||
out_vz(:, frame_idx) = vz
|
||||
end if
|
||||
|
||||
call do_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, method_id, &
|
||||
pos_init, box_a)
|
||||
end do
|
||||
|
||||
deallocate(x, y, z, vx, vy, vz, freeze)
|
||||
run_dynamics = 0
|
||||
|
||||
contains
|
||||
|
||||
subroutine do_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt, method_id, &
|
||||
pos_init, box_a)
|
||||
integer, intent(in) :: n, gravity_field, elastic_force, damping_force
|
||||
integer, intent(in) :: n_bonds, method_id
|
||||
real(c_double), intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
|
||||
real(c_double), intent(in) :: m(n), bond_k(n_bonds), bond_r0(n_bonds)
|
||||
integer, intent(in) :: fixed(3,n), bond_pairs(2,n_bonds)
|
||||
real(c_double), intent(in) :: Gx, Gy, Gz, Bx, By, Bz, dt, box_a
|
||||
real(c_double), intent(in) :: pos_init(3, n)
|
||||
|
||||
select case (method_id)
|
||||
case (0)
|
||||
call euler_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
case (1)
|
||||
call implicit_euler_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
case (2)
|
||||
call midpoint_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
case default
|
||||
call leapfrog_step(n, x, y, z, vx, vy, vz, m, fixed, &
|
||||
Gx, Gy, Gz, Bx, By, Bz, &
|
||||
gravity_field, elastic_force, damping_force, &
|
||||
n_bonds, bond_pairs, bond_k, bond_r0, dt)
|
||||
end select
|
||||
call apply_bc(n, x, y, z, vx, vy, vz, fixed, pos_init, box_a)
|
||||
end subroutine do_step
|
||||
|
||||
end function run_dynamics
|
||||
|
||||
end module dynamics_dll
|
||||
+82
-174
@@ -49,7 +49,7 @@ program dynamics_f90
|
||||
double precision, allocatable :: vx(:), vy(:), vz(:)
|
||||
|
||||
! 轨迹缓冲区
|
||||
integer :: record_steps
|
||||
integer :: record_steps, n_frames, frame_idx
|
||||
double precision, allocatable :: traj_x(:, :), traj_y(:, :), traj_z(:, :)
|
||||
double precision, allocatable :: traj_vx(:, :), traj_vy(:, :), traj_vz(:, :)
|
||||
|
||||
@@ -104,10 +104,11 @@ program dynamics_f90
|
||||
vx(i) = vel_0(i, 1); vy(i) = vel_0(i, 2); vz(i) = vel_0(i, 3)
|
||||
end do
|
||||
|
||||
! 分配轨迹缓冲区
|
||||
! 分配轨迹缓冲区(只保存采样帧,不保存每一步)
|
||||
record_steps = NT - warmup_steps
|
||||
allocate(traj_x(record_steps, n), traj_y(record_steps, n), traj_z(record_steps, n))
|
||||
allocate(traj_vx(record_steps, n), traj_vy(record_steps, n), traj_vz(record_steps, n))
|
||||
n_frames = max(1, record_steps / max(1, NSTEP))
|
||||
allocate(traj_x(n_frames, n), traj_y(n_frames, n), traj_z(n_frames, n))
|
||||
allocate(traj_vx(n_frames, n), traj_vy(n_frames, n), traj_vz(n_frames, n))
|
||||
|
||||
! 真蛙跳初始化:v(0) 反推 v(-dt/2) = v(0) - 0.5*a_c(0)*dt
|
||||
if (trim(method) == 'leapfrog') then
|
||||
@@ -160,12 +161,19 @@ program dynamics_f90
|
||||
pos_0)
|
||||
end do
|
||||
|
||||
! 记录
|
||||
prog_step = record_steps / 100
|
||||
if (prog_step < 1) prog_step = 1
|
||||
! 记录(每 NSTEP 步采一帧)
|
||||
prog_step = max(1, record_steps / 100)
|
||||
frame_idx = 0
|
||||
do s = 1, record_steps
|
||||
if (mod(s, prog_step) == 0 .and. s > 0) then
|
||||
if (mod(s, prog_step) == 0) then
|
||||
write(*, '("[Fortran-engine] progress: ", i0, "/", i0)') s, record_steps
|
||||
flush(6)
|
||||
end if
|
||||
! 采帧:在每个 NSTEP 区间的起始时刻记录
|
||||
if (mod(s-1, max(1, NSTEP)) == 0 .and. frame_idx < n_frames) then
|
||||
frame_idx = frame_idx + 1
|
||||
traj_x(frame_idx, :) = x; traj_y(frame_idx, :) = y; traj_z(frame_idx, :) = z
|
||||
traj_vx(frame_idx, :) = vx; traj_vy(frame_idx, :) = vy; traj_vz(frame_idx, :) = vz
|
||||
end if
|
||||
if (driving_force /= 0 .and. n_drivers > 0) then
|
||||
tw = ((s-1 + warmup_steps) * 1.0d0) * DT
|
||||
@@ -178,8 +186,6 @@ program dynamics_f90
|
||||
drv_eq_x, drv_eq_y, drv_eq_z, &
|
||||
drv_freeze_x, drv_freeze_y, drv_freeze_z)
|
||||
end if
|
||||
traj_x(s, :) = x; traj_y(s, :) = y; traj_z(s, :) = z
|
||||
traj_vx(s, :) = vx; traj_vy(s, :) = vy; traj_vz(s, :) = vz
|
||||
call apply_step(method, n, x, y, z, vx, vy, vz, masses, G, B, &
|
||||
n_bonds, bond_pairs, bond_stiffness, bond_rest_lengths, &
|
||||
fixed, box_a, DT, &
|
||||
@@ -188,13 +194,14 @@ program dynamics_f90
|
||||
pos_0)
|
||||
end do
|
||||
|
||||
! 输出轨迹
|
||||
write(*, '("[Fortran-engine] 正在写入轨迹数据…")')
|
||||
call write_json(output_dir, traj_x, traj_y, traj_z, traj_vx, traj_vy, traj_vz, &
|
||||
record_steps, n_atoms, atom_ids, masses, &
|
||||
NT, DT, NSTEP, warmup_steps, method, G, B, &
|
||||
n_bonds, bond_pairs, bond_stiffness, bond_rest_lengths, &
|
||||
driving_force)
|
||||
! 输出 display.txt
|
||||
write(*, '("[Fortran-engine] 正在写入 display.txt (", i0, " 帧)…")') n_frames
|
||||
flush(6)
|
||||
call write_display_txt(output_dir, n_frames, n_atoms, atom_ids, &
|
||||
traj_x, traj_y, traj_z, traj_vx, traj_vy, traj_vz, &
|
||||
NT, DT, NSTEP, warmup_steps, method, G, B, &
|
||||
n_bonds, gravity_field, elastic_force, damping_force, &
|
||||
driving_force, box_a, gravity_strength)
|
||||
|
||||
call cpu_time(t1)
|
||||
elapsed = t1 - t0
|
||||
@@ -985,179 +992,80 @@ subroutine apply_driving(n, x, y, z, vx, vy, vz, t, step, dt, &
|
||||
end subroutine apply_driving
|
||||
|
||||
! ========================================================================
|
||||
! JSON 输出
|
||||
! display.txt 输出(与 compute.py save_display_txt 格式一致)
|
||||
! ========================================================================
|
||||
|
||||
subroutine write_json(outdir, tx, ty, tz, tvx, tvy, tvz, &
|
||||
nsteps, nat, aid, amass, &
|
||||
NT, DT, NSTEP, warmup, method, G, B, &
|
||||
nb, bp, bk, br, driving_force)
|
||||
subroutine write_display_txt(outdir, n_frames, nat, aid, &
|
||||
tx, ty, tz, tvx, tvy, tvz, &
|
||||
NT, DT, NSTEP, warmup, method, G, B, &
|
||||
nb, gravity_field, elastic_force, damping_force, &
|
||||
driving_force, box_a, gravity_strength)
|
||||
character(len=*), intent(in) :: outdir, method
|
||||
integer, intent(in) :: nsteps, nat, NT, NSTEP, warmup, nb, bp(nb, 2), aid(nat), driving_force
|
||||
double precision, intent(in) :: tx(nsteps, nat), ty(nsteps, nat), tz(nsteps, nat)
|
||||
double precision, intent(in) :: tvx(nsteps, nat), tvy(nsteps, nat), tvz(nsteps, nat)
|
||||
double precision, intent(in) :: DT, G(3), B(3), bk(nb), br(nb), amass(nat)
|
||||
integer, intent(in) :: n_frames, nat, NT, NSTEP, warmup, nb
|
||||
integer, intent(in) :: gravity_field, elastic_force, damping_force, driving_force
|
||||
integer, intent(in) :: aid(nat)
|
||||
double precision, intent(in) :: tx(n_frames, nat), ty(n_frames, nat), tz(n_frames, nat)
|
||||
double precision, intent(in) :: tvx(n_frames, nat), tvy(n_frames, nat), tvz(n_frames, nat)
|
||||
double precision, intent(in) :: DT, G(3), B(3), box_a, gravity_strength
|
||||
|
||||
character(len=512) :: path, buf
|
||||
integer :: u, s, i, ib, ios
|
||||
integer :: u, f, a, ios
|
||||
integer :: dynamic_steps
|
||||
double precision :: T_total
|
||||
|
||||
path = trim(outdir) // '/trajectory.txt'
|
||||
dynamic_steps = NT - warmup
|
||||
T_total = NT * DT
|
||||
|
||||
path = trim(outdir) // '/display.txt'
|
||||
open(newunit=u, file=trim(path), status='replace', action='write', iostat=ios)
|
||||
if (ios /= 0) then
|
||||
write(*, '("[Fortran-engine] 错误: 无法写入 ", a)') trim(path)
|
||||
stop
|
||||
return
|
||||
end if
|
||||
|
||||
write(u, '(a)') '{'
|
||||
|
||||
! traj_x
|
||||
write(u, '(a)') ' "traj_x": ['
|
||||
do s = 1, nsteps
|
||||
call json_arr(u, tx(s, :), nat, s < nsteps, ' ')
|
||||
end do
|
||||
write(u, '(a)') ' ],'
|
||||
|
||||
! traj_y
|
||||
write(u, '(a)') ' "traj_y": ['
|
||||
do s = 1, nsteps
|
||||
call json_arr(u, ty(s, :), nat, s < nsteps, ' ')
|
||||
end do
|
||||
write(u, '(a)') ' ],'
|
||||
|
||||
! traj_z
|
||||
write(u, '(a)') ' "traj_z": ['
|
||||
do s = 1, nsteps
|
||||
call json_arr(u, tz(s, :), nat, s < nsteps, ' ')
|
||||
end do
|
||||
write(u, '(a)') ' ],'
|
||||
|
||||
! traj_vx
|
||||
write(u, '(a)') ' "traj_vx": ['
|
||||
do s = 1, nsteps
|
||||
call json_arr(u, tvx(s, :), nat, s < nsteps, ' ')
|
||||
end do
|
||||
write(u, '(a)') ' ],'
|
||||
|
||||
! traj_vy
|
||||
write(u, '(a)') ' "traj_vy": ['
|
||||
do s = 1, nsteps
|
||||
call json_arr(u, tvy(s, :), nat, s < nsteps, ' ')
|
||||
end do
|
||||
write(u, '(a)') ' ],'
|
||||
|
||||
! traj_vz
|
||||
write(u, '(a)') ' "traj_vz": ['
|
||||
do s = 1, nsteps
|
||||
call json_arr(u, tvz(s, :), nat, s < nsteps, ' ')
|
||||
end do
|
||||
write(u, '(a)') ' ],'
|
||||
|
||||
! 标量参数
|
||||
write(buf, '(a, i0, a)') ' "NT": ', NT, ','
|
||||
! ── header ────────────────────────────────────────────────────────────
|
||||
write(u, '("number of frames: ", i0)') n_frames
|
||||
write(u, '("number of particles: ", i0)') nat
|
||||
write(u, '("DT: ", g0)') DT
|
||||
write(u, '("NSTEP: ", i0)') NSTEP
|
||||
write(u, '("method: ", a)') trim(method)
|
||||
write(u, '("NT: ", i0)') NT
|
||||
write(u, '("warmup_steps: ", i0)') warmup
|
||||
write(u, '("dynamic_steps: ", i0)') dynamic_steps
|
||||
write(u, '("T_total: ", g0)') T_total
|
||||
write(u, '("box_a: ", g0)') box_a
|
||||
write(u, '("gravity_field: ", i0)') gravity_field
|
||||
write(u, '("elastic_force: ", i0)') elastic_force
|
||||
write(u, '("damping_force: ", i0)') damping_force
|
||||
write(u, '("driving_force: ", i0)') driving_force
|
||||
write(u, '("gravity_strength: ", g0)') gravity_strength
|
||||
write(buf, '("G: [", g0, ", ", g0, ", ", g0, "]")') G(1), G(2), G(3)
|
||||
write(u, '(a)') trim(buf)
|
||||
write(buf, '(a, g0, a)') ' "DT": ', DT, ','
|
||||
write(u, '(a)') trim(buf)
|
||||
write(buf, '(a, i0, a)') ' "NSTEP": ', NSTEP, ','
|
||||
write(u, '(a)') trim(buf)
|
||||
write(buf, '(a, a, a)') ' "method": "', trim(method), '",'
|
||||
write(u, '(a)') trim(buf)
|
||||
write(buf, '(a, i0, a)') ' "warmup_steps": ', warmup, ','
|
||||
write(buf, '("B: [", g0, ", ", g0, ", ", g0, "]")') B(1), B(2), B(3)
|
||||
write(u, '(a)') trim(buf)
|
||||
write(u, '("number_of_frames: ", i0)') n_frames
|
||||
write(u, '("number_of_particles: ", i0)') nat
|
||||
write(buf, '("X_MIN: ", g0)') -box_a; write(u, '(a)') trim(buf)
|
||||
write(buf, '("X_MAX: ", g0)') box_a; write(u, '(a)') trim(buf)
|
||||
write(buf, '("Y_MIN: ", g0)') -box_a; write(u, '(a)') trim(buf)
|
||||
write(buf, '("Y_MAX: ", g0)') box_a; write(u, '(a)') trim(buf)
|
||||
write(buf, '("Z_MIN: ", g0)') -box_a; write(u, '(a)') trim(buf)
|
||||
write(buf, '("Z_MAX: ", g0)') box_a; write(u, '(a)') trim(buf)
|
||||
|
||||
write(buf, '(a, g0, a, g0, a, g0, a)') &
|
||||
' "G": [', G(1), ', ', G(2), ', ', G(3), '],'
|
||||
write(u, '(a)') trim(buf)
|
||||
write(buf, '(a, g0, a, g0, a, g0, a)') &
|
||||
' "B": [', B(1), ', ', B(2), ', ', B(3), '],'
|
||||
write(u, '(a)') trim(buf)
|
||||
|
||||
! 原子信息
|
||||
write(u, '(a)', advance='no') ' "atom_ids": ['
|
||||
do i = 1, nat
|
||||
if (i > 1) write(u, '(a)', advance='no') ','
|
||||
write(u, '(i0)', advance='no') aid(i)
|
||||
! ── frame data ────────────────────────────────────────────────────────
|
||||
do f = 1, n_frames
|
||||
write(u, '()') ! 空行
|
||||
write(u, '("frame: ", i0)') f
|
||||
write(u, '("n x y z vx vy vz")')
|
||||
do a = 1, nat
|
||||
write(u, '(i0, 6(f13.6))') aid(a), &
|
||||
tx(f,a), ty(f,a), tz(f,a), tvx(f,a), tvy(f,a), tvz(f,a)
|
||||
end do
|
||||
end do
|
||||
write(u, '(a)') '],'
|
||||
|
||||
write(u, '(a)', advance='no') ' "atom_masses": ['
|
||||
do i = 1, nat
|
||||
if (i > 1) write(u, '(a)', advance='no') ','
|
||||
write(u, '(g0)', advance='no') amass(i)
|
||||
end do
|
||||
write(u, '(a)') '],'
|
||||
|
||||
! 成键
|
||||
if (nb > 0) then
|
||||
call write_int2_arr(u, 'bond_pairs', bp, nb, .true.)
|
||||
call write_dbl_arr(u, 'bond_stiffness', bk, nb, .true.)
|
||||
call write_dbl_arr(u, 'bond_rest_lengths', br, nb, .true.)
|
||||
else
|
||||
write(u, '(a)') ' "bond_pairs": [],'
|
||||
write(u, '(a)') ' "bond_stiffness": [],'
|
||||
write(u, '(a)') ' "bond_rest_lengths": [],'
|
||||
end if
|
||||
|
||||
write(buf, '(a, i0)') ' "driving_force": ', driving_force
|
||||
write(u, '(a)') trim(buf)
|
||||
|
||||
write(u, '(a)') '}'
|
||||
close(u)
|
||||
end subroutine write_json
|
||||
|
||||
! 写出单行 JSON 数组 [v1, v2, ...]
|
||||
subroutine json_arr(u, vals, n, has_next, indent)
|
||||
integer, intent(in) :: u, n
|
||||
double precision, intent(in) :: vals(n)
|
||||
logical, intent(in) :: has_next
|
||||
character(len=*), intent(in) :: indent
|
||||
integer :: i
|
||||
write(u, '(a)', advance='no') indent // '['
|
||||
do i = 1, n
|
||||
if (i > 1) write(u, '(a)', advance='no') ','
|
||||
write(u, '(g0.8)', advance='no') vals(i)
|
||||
end do
|
||||
if (has_next) then
|
||||
write(u, '(a)') '],'
|
||||
else
|
||||
write(u, '(a)') ']'
|
||||
end if
|
||||
end subroutine json_arr
|
||||
|
||||
subroutine write_int2_arr(u, name, arr, n, has_next)
|
||||
integer, intent(in) :: u, n, arr(n, 2)
|
||||
character(len=*), intent(in) :: name
|
||||
logical, intent(in) :: has_next
|
||||
character(len=65536) :: buf
|
||||
integer :: i, pos
|
||||
write(u, '(a)', advance='no') ' "' // trim(name) // '": ['
|
||||
do i = 1, n
|
||||
if (i > 1) write(u, '(a)', advance='no') ','
|
||||
write(buf, '(a, i0, a, i0, a)') '[', arr(i, 1), ',', arr(i, 2), ']'
|
||||
write(u, '(a)', advance='no') trim(buf)
|
||||
end do
|
||||
if (has_next) then
|
||||
write(u, '(a)') '],'
|
||||
else
|
||||
write(u, '(a)') ']'
|
||||
end if
|
||||
end subroutine write_int2_arr
|
||||
|
||||
subroutine write_dbl_arr(u, name, arr, n, has_next)
|
||||
integer, intent(in) :: u, n
|
||||
double precision, intent(in) :: arr(n)
|
||||
character(len=*), intent(in) :: name
|
||||
logical, intent(in) :: has_next
|
||||
integer :: i
|
||||
write(u, '(a)', advance='no') ' "' // trim(name) // '": ['
|
||||
do i = 1, n
|
||||
if (i > 1) write(u, '(a)', advance='no') ','
|
||||
write(u, '(g0.8)', advance='no') arr(i)
|
||||
end do
|
||||
if (has_next) then
|
||||
write(u, '(a)') '],'
|
||||
else
|
||||
write(u, '(a)') ']'
|
||||
end if
|
||||
end subroutine write_dbl_arr
|
||||
write(*, '("[Fortran-engine] display.txt 已保存: ", a)') trim(path)
|
||||
flush(6)
|
||||
end subroutine write_display_txt
|
||||
|
||||
end program dynamics_f90
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
engines/python/dynamics_lib.py
|
||||
-------------------------------
|
||||
纯 NumPy 计算引擎:无文件 I/O,所有数据以 NumPy 数组传入,
|
||||
结果作为 NumPy 数组返回。
|
||||
|
||||
接口与 C/C++/Fortran DLL 的 run_dynamics() 完全一致,
|
||||
算法与 compute.py 的 run_simulation() 保持一致。
|
||||
|
||||
用法(由 engine_dll.py 内部调用):
|
||||
from engines.python.dynamics_lib import run_dynamics
|
||||
out_x, out_y, out_z, out_vx, out_vy, out_vz = run_dynamics(...)
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
TWO_PI = 2.0 * np.pi
|
||||
|
||||
# ── method_id 映射 ──────────────────────────────────────────
|
||||
# 0=euler 1=implicit_euler 2=midpoint 3=leapfrog
|
||||
|
||||
|
||||
# ── 保守加速度(弹簧键 + 均匀重力场,不含阻尼)──────────────
|
||||
def _accel_conservative(x, y, z, m, Gx, Gy, Gz,
|
||||
gravity_field, elastic_force,
|
||||
bond_pairs, bond_k, bond_r0):
|
||||
ax = np.full_like(x, Gx) if gravity_field else np.zeros_like(x)
|
||||
ay = np.full_like(y, Gy) if gravity_field else np.zeros_like(y)
|
||||
az = np.full_like(z, Gz) if gravity_field else np.zeros_like(z)
|
||||
|
||||
if elastic_force and len(bond_pairs) > 0:
|
||||
i1 = bond_pairs[:, 0]
|
||||
i2 = bond_pairs[:, 1]
|
||||
dx = x[i2] - x[i1]
|
||||
dy = y[i2] - y[i1]
|
||||
dz = z[i2] - z[i1]
|
||||
dist = np.sqrt(dx*dx + dy*dy + dz*dz)
|
||||
valid = dist > 1e-12
|
||||
fac = np.where(valid, bond_k * (dist - bond_r0) / dist, 0.0)
|
||||
fx = fac * dx
|
||||
fy = fac * dy
|
||||
fz_b = fac * dz
|
||||
np.add.at(ax, i1, fx / m[i1]); np.add.at(ax, i2, -fx / m[i2])
|
||||
np.add.at(ay, i1, fy / m[i1]); np.add.at(ay, i2, -fy / m[i2])
|
||||
np.add.at(az, i1, fz_b / m[i1]); np.add.at(az, i2, -fz_b / m[i2])
|
||||
|
||||
return ax, ay, az
|
||||
|
||||
|
||||
# ── 完整加速度(含阻尼)──────────────────────────────────────
|
||||
def _accel_full(x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0):
|
||||
ax, ay, az = _accel_conservative(x, y, z, m, Gx, Gy, Gz,
|
||||
gravity_field, elastic_force,
|
||||
bond_pairs, bond_k, bond_r0)
|
||||
if damping_force:
|
||||
ax -= Bx * vx / m
|
||||
ay -= By * vy / m
|
||||
az -= Bz * vz / m
|
||||
return ax, ay, az
|
||||
|
||||
|
||||
# ── 蛙跳法(半隐式阻尼,与 compute.py leapfrog_staggered_step 一致)─
|
||||
def _leapfrog_step(x, y, z, vx, vy, vz, fixed, m,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt):
|
||||
ax, ay, az = _accel_conservative(x, y, z, m, Gx, Gy, Gz,
|
||||
gravity_field, elastic_force,
|
||||
bond_pairs, bond_k, bond_r0)
|
||||
has_damp = damping_force and (Bx != 0.0 or By != 0.0 or Bz != 0.0)
|
||||
if has_damp:
|
||||
alpha_x = Bx * dt / (2.0 * m)
|
||||
alpha_y = By * dt / (2.0 * m)
|
||||
alpha_z = Bz * dt / (2.0 * m)
|
||||
vx_new = (vx * (1.0 - alpha_x) + ax * dt) / (1.0 + alpha_x)
|
||||
vy_new = (vy * (1.0 - alpha_y) + ay * dt) / (1.0 + alpha_y)
|
||||
vz_new = (vz * (1.0 - alpha_z) + az * dt) / (1.0 + alpha_z)
|
||||
else:
|
||||
vx_new = vx + ax * dt
|
||||
vy_new = vy + ay * dt
|
||||
vz_new = vz + az * dt
|
||||
# 全固定原子保持不变
|
||||
all_fixed = np.all(fixed, axis=1)
|
||||
vx_new = np.where(all_fixed, vx, vx_new)
|
||||
vy_new = np.where(all_fixed, vy, vy_new)
|
||||
vz_new = np.where(all_fixed, vz, vz_new)
|
||||
x_new = x + vx_new * dt
|
||||
y_new = y + vy_new * dt
|
||||
z_new = z + vz_new * dt
|
||||
return x_new, y_new, z_new, vx_new, vy_new, vz_new
|
||||
|
||||
|
||||
# ── 显式欧拉法 ───────────────────────────────────────────────
|
||||
def _euler_step(x, y, z, vx, vy, vz, fixed, m,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt):
|
||||
ax, ay, az = _accel_full(x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0)
|
||||
all_fixed = np.all(fixed, axis=1)
|
||||
mask = ~all_fixed
|
||||
x_new = np.where(mask, x + vx * dt, x)
|
||||
y_new = np.where(mask, y + vy * dt, y)
|
||||
z_new = np.where(mask, z + vz * dt, z)
|
||||
vx_new = np.where(mask, vx + ax * dt, vx)
|
||||
vy_new = np.where(mask, vy + ay * dt, vy)
|
||||
vz_new = np.where(mask, vz + az * dt, vz)
|
||||
return x_new, y_new, z_new, vx_new, vy_new, vz_new
|
||||
|
||||
|
||||
# ── 隐式欧拉法(与 compute.py Implicit_Euler_Method 一致)──────
|
||||
def _implicit_euler_step(x, y, z, vx, vy, vz, fixed, m,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt):
|
||||
gamma_x = Bx / m
|
||||
gamma_y = By / m
|
||||
gamma_z = Bz / m
|
||||
vx_next = (vx + Gx * dt) / (1.0 + gamma_x * dt)
|
||||
vy_next = (vy + Gy * dt) / (1.0 + gamma_y * dt)
|
||||
vz_next = (vz + Gz * dt) / (1.0 + gamma_z * dt)
|
||||
ax, ay, az = _accel_full(x, y, z, vx_next, vy_next, vz_next, m,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0)
|
||||
all_fixed = np.all(fixed, axis=1)
|
||||
mask = ~all_fixed
|
||||
vx_new = np.where(mask, vx + ax * dt, vx)
|
||||
vy_new = np.where(mask, vy + ay * dt, vy)
|
||||
vz_new = np.where(mask, vz + az * dt, vz)
|
||||
x_new = np.where(mask, x + vx_new * dt, x)
|
||||
y_new = np.where(mask, y + vy_new * dt, y)
|
||||
z_new = np.where(mask, z + vz_new * dt, z)
|
||||
return x_new, y_new, z_new, vx_new, vy_new, vz_new
|
||||
|
||||
|
||||
# ── 中点法(与 compute.py Midpoint_Method 一致)────────────────
|
||||
def _midpoint_step(x, y, z, vx, vy, vz, fixed, m,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt):
|
||||
ax, ay, az = _accel_full(x, y, z, vx, vy, vz, m, Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0)
|
||||
all_fixed = np.all(fixed, axis=1)
|
||||
mask = ~all_fixed
|
||||
xm = np.where(mask, x + 0.5*vx*dt, x)
|
||||
ym = np.where(mask, y + 0.5*vy*dt, y)
|
||||
zm = np.where(mask, z + 0.5*vz*dt, z)
|
||||
vxm = np.where(mask, vx + 0.5*ax*dt, 0.0)
|
||||
vym = np.where(mask, vy + 0.5*ay*dt, 0.0)
|
||||
vzm = np.where(mask, vz + 0.5*az*dt, 0.0)
|
||||
x_new = np.where(mask, x + vxm * dt, x)
|
||||
y_new = np.where(mask, y + vym * dt, y)
|
||||
z_new = np.where(mask, z + vzm * dt, z)
|
||||
axm, aym, azm = _accel_full(xm, ym, zm, vxm, vym, vzm, m, Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0)
|
||||
vx_new = np.where(mask, vx + axm * dt, vx)
|
||||
vy_new = np.where(mask, vy + aym * dt, vy)
|
||||
vz_new = np.where(mask, vz + azm * dt, vz)
|
||||
return x_new, y_new, z_new, vx_new, vy_new, vz_new
|
||||
|
||||
|
||||
# ── 边界:反弹 + 回绕 + 逐自由度固定约束 ───────────────────────
|
||||
def _apply_bc(x, y, z, vx, vy, vz, fixed, pos_init, box_a):
|
||||
lo, hi = -box_a, box_a
|
||||
|
||||
# 反弹(全固定原子跳过)
|
||||
all_fixed = np.all(fixed, axis=1)
|
||||
do_bc = ~all_fixed
|
||||
over_x = do_bc & (x > hi); under_x = do_bc & (x < lo)
|
||||
over_y = do_bc & (y > hi); under_y = do_bc & (y < lo)
|
||||
over_z = do_bc & (z > hi); under_z = do_bc & (z < lo)
|
||||
x = np.where(over_x, hi, np.where(under_x, lo, x))
|
||||
y = np.where(over_y, hi, np.where(under_y, lo, y))
|
||||
z = np.where(over_z, hi, np.where(under_z, lo, z))
|
||||
vx = np.where(over_x | under_x, -np.abs(vx)*np.sign(np.where(over_x, 1, -1)), vx)
|
||||
vy = np.where(over_y | under_y, -np.abs(vy)*np.sign(np.where(over_y, 1, -1)), vy)
|
||||
vz = np.where(over_z | under_z, -np.abs(vz)*np.sign(np.where(over_z, 1, -1)), vz)
|
||||
# 反弹速度简化:越界则取反绝对值(与 C 版 _limit1 一致)
|
||||
vx = np.where(over_x, -np.abs(vx), np.where(under_x, np.abs(vx), vx))
|
||||
vy = np.where(over_y, -np.abs(vy), np.where(under_y, np.abs(vy), vy))
|
||||
vz = np.where(over_z, -np.abs(vz), np.where(under_z, np.abs(vz), vz))
|
||||
|
||||
# 回绕
|
||||
x = np.where(x > hi, lo, np.where(x < lo, hi, x))
|
||||
y = np.where(y > hi, lo, np.where(y < lo, hi, y))
|
||||
z = np.where(z > hi, lo, np.where(z < lo, hi, z))
|
||||
|
||||
# 逐自由度固定约束
|
||||
fx = fixed[:, 0].astype(bool)
|
||||
fy = fixed[:, 1].astype(bool)
|
||||
fz = fixed[:, 2].astype(bool)
|
||||
x = np.where(fx, pos_init[:, 0], x); vx = np.where(fx, 0.0, vx)
|
||||
y = np.where(fy, pos_init[:, 1], y); vy = np.where(fy, 0.0, vy)
|
||||
z = np.where(fz, pos_init[:, 2], z); vz = np.where(fz, 0.0, vz)
|
||||
return x, y, z, vx, vy, vz
|
||||
|
||||
|
||||
# ── 驱动力(与 compute.py apply_driving_force 逻辑一致)─────────
|
||||
def _apply_driving(x, y, z, vx, vy, vz, t, step, dt,
|
||||
drv_idx, drv_amp, drv_freq, drv_phi, drv_eq,
|
||||
drv_ncycles, drv_has_period, freeze):
|
||||
"""freeze: (n_drivers, 3) mutable array for frozen positions."""
|
||||
nd = len(drv_idx)
|
||||
for d in range(nd):
|
||||
idx = drv_idx[d]
|
||||
fx_ = drv_freq[d, 0]; fy_ = drv_freq[d, 1]; fz_ = drv_freq[d, 2]
|
||||
|
||||
if drv_has_period[d]:
|
||||
mf = max(abs(fx_), abs(fy_), abs(fz_))
|
||||
ps = int(drv_ncycles[d] / mf / dt) if mf > 1e-12 else 0
|
||||
if step > ps:
|
||||
x[idx] = freeze[d, 0]; y[idx] = freeze[d, 1]; z[idx] = freeze[d, 2]
|
||||
vx[idx] = vy[idx] = vz[idx] = 0.0
|
||||
continue
|
||||
px = drv_eq[d,0] + drv_amp[d,0]*np.cos(TWO_PI*fx_*t + drv_phi[d,0])
|
||||
py = drv_eq[d,1] + drv_amp[d,1]*np.cos(TWO_PI*fy_*t + drv_phi[d,1])
|
||||
pz = drv_eq[d,2] + drv_amp[d,2]*np.cos(TWO_PI*fz_*t + drv_phi[d,2])
|
||||
if step == ps:
|
||||
freeze[d, 0] = px; freeze[d, 1] = py; freeze[d, 2] = pz
|
||||
|
||||
x[idx] = drv_eq[d,0] + drv_amp[d,0]*np.cos(TWO_PI*fx_*t + drv_phi[d,0])
|
||||
y[idx] = drv_eq[d,1] + drv_amp[d,1]*np.cos(TWO_PI*fy_*t + drv_phi[d,1])
|
||||
z[idx] = drv_eq[d,2] + drv_amp[d,2]*np.cos(TWO_PI*fz_*t + drv_phi[d,2])
|
||||
vx[idx] = -drv_amp[d,0]*TWO_PI*fx_*np.sin(TWO_PI*fx_*t + drv_phi[d,0])
|
||||
vy[idx] = -drv_amp[d,1]*TWO_PI*fy_*np.sin(TWO_PI*fy_*t + drv_phi[d,1])
|
||||
vz[idx] = -drv_amp[d,2]*TWO_PI*fz_*np.sin(TWO_PI*fz_*t + drv_phi[d,2])
|
||||
|
||||
|
||||
def _do_step(x, y, z, vx, vy, vz, fixed, masses, method_id,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt, pos_init, box_a):
|
||||
if method_id == 0:
|
||||
x, y, z, vx, vy, vz = _euler_step(
|
||||
x, y, z, vx, vy, vz, fixed, masses,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt)
|
||||
elif method_id == 1:
|
||||
x, y, z, vx, vy, vz = _implicit_euler_step(
|
||||
x, y, z, vx, vy, vz, fixed, masses,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt)
|
||||
elif method_id == 2:
|
||||
x, y, z, vx, vy, vz = _midpoint_step(
|
||||
x, y, z, vx, vy, vz, fixed, masses,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt)
|
||||
else:
|
||||
x, y, z, vx, vy, vz = _leapfrog_step(
|
||||
x, y, z, vx, vy, vz, fixed, masses,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt)
|
||||
x, y, z, vx, vy, vz = _apply_bc(x, y, z, vx, vy, vz, fixed, pos_init, box_a)
|
||||
return x, y, z, vx, vy, vz
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 主函数:run_dynamics
|
||||
# 接口与 C/C++/Fortran DLL 的 run_dynamics() 对应,
|
||||
# 参数格式:numpy 数组(替代 ctypes 指针)。
|
||||
#
|
||||
# method_id: 0=euler 1=implicit_euler 2=midpoint 3=leapfrog
|
||||
# drv_amp/freq/phi/eq: (n_drivers, 3) float64
|
||||
# drv_ncycles: (n_drivers,) float64 0=不限
|
||||
# drv_has_period: (n_drivers,) int
|
||||
#
|
||||
# 返回:(out_x, out_y, out_z, out_vx, out_vy, out_vz)
|
||||
# 各 shape=(n_frames, n_atoms)
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
def run_dynamics(
|
||||
n_atoms, pos_init, vel_init, masses, fixed,
|
||||
n_bonds, bond_pairs, bond_k, bond_r0,
|
||||
box_a, dt,
|
||||
NT, NSTEP, warmup_steps, method_id,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force, gravity_strength,
|
||||
n_drivers, drv_idx, drv_amp, drv_freq, drv_phi, drv_eq,
|
||||
drv_ncycles, drv_has_period,
|
||||
n_frames,
|
||||
progress_cb=None,
|
||||
):
|
||||
"""运行动力学模拟,返回抽帧轨迹数组。
|
||||
|
||||
Args:
|
||||
pos_init: (n_atoms, 3) float64
|
||||
vel_init: (n_atoms, 3) float64
|
||||
masses: (n_atoms,) float64
|
||||
fixed: (n_atoms, 3) int — 1=固定
|
||||
bond_pairs: (n_bonds, 2) int — 0-based 局部索引
|
||||
bond_k: (n_bonds,) float64
|
||||
bond_r0: (n_bonds,) float64
|
||||
drv_idx: (n_drivers,) int — 0-based
|
||||
drv_amp/freq/phi/eq: (n_drivers, 3) float64
|
||||
drv_ncycles: (n_drivers,) float64
|
||||
drv_has_period: (n_drivers,) int
|
||||
n_frames: 预分配的输出帧数
|
||||
|
||||
Returns:
|
||||
out_x, out_y, out_z, out_vx, out_vy, out_vz — 各 (n_frames, n_atoms)
|
||||
"""
|
||||
pos_init = np.asarray(pos_init, dtype=np.float64)
|
||||
vel_init = np.asarray(vel_init, dtype=np.float64)
|
||||
masses = np.asarray(masses, dtype=np.float64)
|
||||
fixed = np.asarray(fixed, dtype=np.int32)
|
||||
bond_pairs = np.asarray(bond_pairs, dtype=np.int64).reshape(-1, 2) if n_bonds else np.zeros((0,2), dtype=np.int64)
|
||||
bond_k = np.asarray(bond_k, dtype=np.float64) if n_bonds else np.zeros(0)
|
||||
bond_r0 = np.asarray(bond_r0, dtype=np.float64) if n_bonds else np.zeros(0)
|
||||
|
||||
n = n_atoms
|
||||
x = pos_init[:, 0].copy()
|
||||
y = pos_init[:, 1].copy()
|
||||
z = pos_init[:, 2].copy()
|
||||
vx = vel_init[:, 0].copy()
|
||||
vy = vel_init[:, 1].copy()
|
||||
vz = vel_init[:, 2].copy()
|
||||
|
||||
# 驱动力数据(保证正确形状)
|
||||
nd = n_drivers
|
||||
if nd > 0:
|
||||
drv_idx = np.asarray(drv_idx, dtype=np.int64)
|
||||
drv_amp = np.asarray(drv_amp, dtype=np.float64).reshape(nd, 3)
|
||||
drv_freq = np.asarray(drv_freq, dtype=np.float64).reshape(nd, 3)
|
||||
drv_phi = np.asarray(drv_phi, dtype=np.float64).reshape(nd, 3)
|
||||
drv_eq = np.asarray(drv_eq, dtype=np.float64).reshape(nd, 3)
|
||||
drv_nc = np.asarray(drv_ncycles, dtype=np.float64)
|
||||
drv_hp = np.asarray(drv_has_period, dtype=np.int32)
|
||||
freeze = np.zeros((nd, 3), dtype=np.float64)
|
||||
else:
|
||||
drv_idx = drv_amp = drv_freq = drv_phi = drv_eq = drv_nc = drv_hp = freeze = None
|
||||
|
||||
def _drive(t_, step_):
|
||||
if nd > 0:
|
||||
_apply_driving(x, y, z, vx, vy, vz, t_, step_, dt,
|
||||
drv_idx, drv_amp, drv_freq, drv_phi, drv_eq,
|
||||
drv_nc, drv_hp, freeze)
|
||||
|
||||
# ── 蛙跳法:初始化 v(-dt/2) ─────────────────────────────
|
||||
if method_id == 3:
|
||||
ax0, ay0, az0 = _accel_conservative(x, y, z, masses, Gx, Gy, Gz,
|
||||
gravity_field, elastic_force,
|
||||
bond_pairs, bond_k, bond_r0)
|
||||
all_fixed = np.all(fixed, axis=1)
|
||||
vx = np.where(all_fixed, vx, vx - 0.5 * ax0 * dt)
|
||||
vy = np.where(all_fixed, vy, vy - 0.5 * ay0 * dt)
|
||||
vz = np.where(all_fixed, vz, vz - 0.5 * az0 * dt)
|
||||
|
||||
# ── 初始驱动 t=0 ─────────────────────────────────────────
|
||||
_drive(0.0, 0)
|
||||
|
||||
# ── 预热 ─────────────────────────────────────────────────
|
||||
for s in range(warmup_steps):
|
||||
tw = (s + 1) * dt
|
||||
_drive(tw, s)
|
||||
x, y, z, vx, vy, vz = _do_step(
|
||||
x, y, z, vx, vy, vz, fixed, masses, method_id,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt, pos_init, box_a)
|
||||
|
||||
# ── 记录循环 ─────────────────────────────────────────────
|
||||
record_steps = NT - warmup_steps
|
||||
prog_interval = max(1, record_steps // 100)
|
||||
|
||||
out_x = np.zeros((n_frames, n), dtype=np.float64)
|
||||
out_y = np.zeros((n_frames, n), dtype=np.float64)
|
||||
out_z = np.zeros((n_frames, n), dtype=np.float64)
|
||||
out_vx = np.zeros((n_frames, n), dtype=np.float64)
|
||||
out_vy = np.zeros((n_frames, n), dtype=np.float64)
|
||||
out_vz = np.zeros((n_frames, n), dtype=np.float64)
|
||||
frame_idx = 0
|
||||
|
||||
for s in range(record_steps):
|
||||
if progress_cb is not None and s % prog_interval == 0 and s > 0:
|
||||
progress_cb(s, record_steps)
|
||||
|
||||
t = (s + warmup_steps) * dt
|
||||
_drive(t, s)
|
||||
|
||||
if s % NSTEP == 0 and frame_idx < n_frames:
|
||||
out_x[frame_idx] = x
|
||||
out_y[frame_idx] = y
|
||||
out_z[frame_idx] = z
|
||||
out_vx[frame_idx] = vx
|
||||
out_vy[frame_idx] = vy
|
||||
out_vz[frame_idx] = vz
|
||||
frame_idx += 1
|
||||
|
||||
x, y, z, vx, vy, vz = _do_step(
|
||||
x, y, z, vx, vy, vz, fixed, masses, method_id,
|
||||
Gx, Gy, Gz, Bx, By, Bz,
|
||||
gravity_field, elastic_force, damping_force,
|
||||
bond_pairs, bond_k, bond_r0, dt, pos_init, box_a)
|
||||
|
||||
return out_x, out_y, out_z, out_vx, out_vy, out_vz
|
||||
@@ -0,0 +1,283 @@
|
||||
"""
|
||||
engines/python/main.py
|
||||
-----------------------
|
||||
独立 Python 计算引擎。
|
||||
|
||||
与 main.c / main.cpp / main.f90 结构一致:
|
||||
输入: <input_dir>/coord.txt, connection.txt, bond.txt, [driver.txt]
|
||||
<param_json> (同 engines/c/param.json 格式)
|
||||
输出: <output_dir>/display.txt (+ display.npz)
|
||||
<output_dir>/trajectory.txt (若 save_trajectory=1)
|
||||
|
||||
用法:
|
||||
python main.py <input_dir> <output_dir> <param_json>
|
||||
|
||||
内部调用 dynamics_lib.run_dynamics(),算法与 compute.py 完全一致。
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
# 将父目录(engines/python 的上级 engines)加入 sys.path,
|
||||
# 以便在独立运行时也能找到 dynamics_lib
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, _HERE)
|
||||
|
||||
from dynamics_lib import run_dynamics
|
||||
|
||||
# 为读取 coord/bond/display,复用 compute.py 中的 I/O 函数
|
||||
_COMPUTE = os.path.join(_HERE, "..", "..")
|
||||
sys.path.insert(0, _COMPUTE)
|
||||
import compute as _c
|
||||
|
||||
_METHOD_ID = {
|
||||
"explicit_euler": 0,
|
||||
"euler": 0,
|
||||
"implicit_euler": 1,
|
||||
"midpoint": 2,
|
||||
"leapfrog": 3,
|
||||
}
|
||||
|
||||
|
||||
def _load_params(param_path):
|
||||
"""读取 param.json(与 C 引擎格式相同)."""
|
||||
with open(param_path, "r", encoding="utf-8") as f:
|
||||
p = json.load(f)
|
||||
return p
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4:
|
||||
print("用法: python main.py <input_dir> <output_dir> <param_json>")
|
||||
sys.exit(1)
|
||||
|
||||
input_dir = sys.argv[1]
|
||||
output_dir = sys.argv[2]
|
||||
param_path = sys.argv[3]
|
||||
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# ── 读取参数 ─────────────────────────────────────────────
|
||||
p = _load_params(param_path)
|
||||
box_a = float(p.get("box_a", 10.0))
|
||||
NT = int(p.get("NT", 10000))
|
||||
dt = float(p.get("DT", 0.001))
|
||||
NSTEP = int(p.get("NSTEP", 100))
|
||||
warmup_steps = int(p.get("warmup_steps", 0))
|
||||
method_str = str(p.get("method", "leapfrog")).lower().replace(" ", "_")
|
||||
method_id = _METHOD_ID.get(method_str, 3)
|
||||
G = p.get("G", [0.0, 0.0, -9.8])
|
||||
B = p.get("B", [0.0, 0.0, 0.0])
|
||||
gravity_field = int(p.get("gravity_field", 1))
|
||||
elastic_force = int(p.get("elastic_force", 1))
|
||||
damping_force = int(p.get("damping_force", 0))
|
||||
gravity_strength = float(p.get("gravity_strength", 1.0))
|
||||
driving_force = int(p.get("driving_force", 0))
|
||||
save_traj = int(p.get("save_trajectory", 0))
|
||||
|
||||
# ── 读取原子数据 ──────────────────────────────────────────
|
||||
coord_path = os.path.join(input_dir, "coord.txt")
|
||||
atom_ids, masses, radii, positions, velocities, fixed = _c.load_coord_file(coord_path)
|
||||
|
||||
# ── 读取键数据 ────────────────────────────────────────────
|
||||
conn_path = os.path.join(input_dir, "connection.txt")
|
||||
bond_path = os.path.join(input_dir, "bond.txt")
|
||||
bond_map = _c.load_bond_parameters(bond_path)
|
||||
bond_pairs, bond_names, bond_stiffness, bond_rest_lengths = \
|
||||
_c.load_bond_connections(conn_path, atom_ids, positions, bond_map)
|
||||
n_bonds = len(bond_pairs)
|
||||
|
||||
# ── 读取驱动力 ────────────────────────────────────────────
|
||||
drv_list = []
|
||||
if driving_force:
|
||||
driver_path = os.path.join(input_dir, "driver.txt")
|
||||
raw_drivers = _c.load_driver_file(driver_path, atom_ids)
|
||||
if raw_drivers:
|
||||
atom_id_map = {int(aid): i for i, aid in enumerate(atom_ids)}
|
||||
for d in raw_drivers:
|
||||
aid = int(d["atom_id"])
|
||||
if aid not in atom_id_map:
|
||||
continue
|
||||
lidx = atom_id_map[aid]
|
||||
eq = positions[lidx].tolist()
|
||||
d["eq_pos"] = np.array(eq)
|
||||
pc = d.get("period_cycles")
|
||||
nc = float(pc) if pc is not None else 0.0
|
||||
hp = 1 if nc > 0 else 0
|
||||
drv_list.append({
|
||||
"local_idx": lidx,
|
||||
"amp": d["amp"].tolist(),
|
||||
"freq": d["freq"].tolist(),
|
||||
"phi": d["phi"].tolist(), # radians
|
||||
"eq": eq,
|
||||
"nc": nc,
|
||||
"hp": hp,
|
||||
})
|
||||
|
||||
nd = len(drv_list)
|
||||
if nd > 0:
|
||||
drv_idx = np.array([d["local_idx"] for d in drv_list], dtype=np.int64)
|
||||
drv_amp = np.array([d["amp"] for d in drv_list], dtype=np.float64)
|
||||
drv_freq = np.array([d["freq"] for d in drv_list], dtype=np.float64)
|
||||
drv_phi = np.array([d["phi"] for d in drv_list], dtype=np.float64)
|
||||
drv_eq = np.array([d["eq"] for d in drv_list], dtype=np.float64)
|
||||
drv_nc = np.array([d["nc"] for d in drv_list], dtype=np.float64)
|
||||
drv_hp = np.array([d["hp"] for d in drv_list], dtype=np.int32)
|
||||
else:
|
||||
drv_idx = drv_amp = drv_freq = drv_phi = drv_eq = drv_nc = drv_hp = \
|
||||
np.zeros(0, dtype=np.int64)
|
||||
|
||||
# ── 计算帧数 ──────────────────────────────────────────────
|
||||
record_steps = NT - warmup_steps
|
||||
n_frames = max(1, record_steps // NSTEP)
|
||||
|
||||
# ── 进度回调 ──────────────────────────────────────────────
|
||||
def _progress(step, total):
|
||||
pct = step * 100 // total
|
||||
print(f"[python-engine] progress: {step}/{total} ({pct}%)", flush=True)
|
||||
|
||||
# ── 运行计算 ──────────────────────────────────────────────
|
||||
t0 = time.time()
|
||||
print(f"[python-engine] NT={NT} NSTEP={NSTEP} method={method_str} "
|
||||
f"n_atoms={len(atom_ids)} n_bonds={n_bonds}")
|
||||
|
||||
out_x, out_y, out_z, out_vx, out_vy, out_vz = run_dynamics(
|
||||
n_atoms=len(atom_ids),
|
||||
pos_init=positions,
|
||||
vel_init=velocities,
|
||||
masses=masses,
|
||||
fixed=fixed,
|
||||
n_bonds=n_bonds,
|
||||
bond_pairs=bond_pairs,
|
||||
bond_k=bond_stiffness,
|
||||
bond_r0=bond_rest_lengths,
|
||||
box_a=box_a,
|
||||
dt=dt,
|
||||
NT=NT,
|
||||
NSTEP=NSTEP,
|
||||
warmup_steps=warmup_steps,
|
||||
method_id=method_id,
|
||||
Gx=float(G[0]), Gy=float(G[1]), Gz=float(G[2]),
|
||||
Bx=float(B[0]), By=float(B[1]), Bz=float(B[2]),
|
||||
gravity_field=gravity_field,
|
||||
elastic_force=elastic_force,
|
||||
damping_force=damping_force,
|
||||
gravity_strength=gravity_strength,
|
||||
n_drivers=nd,
|
||||
drv_idx=drv_idx,
|
||||
drv_amp=drv_amp,
|
||||
drv_freq=drv_freq,
|
||||
drv_phi=drv_phi,
|
||||
drv_eq=drv_eq,
|
||||
drv_ncycles=drv_nc,
|
||||
drv_has_period=drv_hp,
|
||||
n_frames=n_frames,
|
||||
progress_cb=_progress,
|
||||
)
|
||||
elapsed = time.time() - t0
|
||||
print(f"[python-engine] 完成: {n_frames} 帧 {elapsed:.3f} s")
|
||||
|
||||
# ── 构建 display header ───────────────────────────────────
|
||||
ball_radius = float(p.get("ball_radius", 0.5))
|
||||
ball_color = p.get("ball_color", [0.9, 0.2, 0.2])
|
||||
box_color = p.get("box_color", [0.8, 0.8, 0.85])
|
||||
use_marker = int(p.get("use_marker", 0))
|
||||
alpha_val = p.get("alpha", 0.2)
|
||||
cam_dist = float(p.get("camera_distance", 40.0))
|
||||
cam_elev = float(p.get("camera_elevation", 0.0))
|
||||
cam_azim = float(p.get("camera_azimuth", 0.0))
|
||||
cam_cx = float(p.get("camera_center_x", 0.0))
|
||||
cam_cy = float(p.get("camera_center_y", 0.0))
|
||||
cam_cz = float(p.get("camera_center_z", 0.0))
|
||||
|
||||
header = {
|
||||
"DT": str(dt),
|
||||
"NSTEP": str(NSTEP),
|
||||
"method": method_str,
|
||||
"NT": str(NT),
|
||||
"warmup_steps": str(warmup_steps),
|
||||
"dynamic_steps": str(record_steps),
|
||||
"T_total": str(NT * dt),
|
||||
"box_a": str(box_a),
|
||||
"gravity_field": str(gravity_field),
|
||||
"elastic_force": str(elastic_force),
|
||||
"damping_force": str(damping_force),
|
||||
"driving_force": str(driving_force),
|
||||
"gravity_strength": str(gravity_strength),
|
||||
"G": json.dumps([float(v) for v in G]),
|
||||
"B": json.dumps([float(v) for v in B]),
|
||||
"number_of_frames": str(n_frames),
|
||||
"number_of_particles": str(len(atom_ids)),
|
||||
"use_marker": str(use_marker),
|
||||
"ball_radius": str(ball_radius),
|
||||
"ball_color_r": str(ball_color[0]),
|
||||
"ball_color_g": str(ball_color[1]),
|
||||
"ball_color_b": str(ball_color[2]),
|
||||
"box_color_r": str(box_color[0]),
|
||||
"box_color_g": str(box_color[1]),
|
||||
"box_color_b": str(box_color[2]),
|
||||
"alpha": str(alpha_val) if not isinstance(alpha_val, list)
|
||||
else ",".join(str(a) for a in alpha_val),
|
||||
"atom_radii": ",".join(str(r) for r in radii),
|
||||
"atom_masses": json.dumps([float(m) for m in masses]),
|
||||
"atom_positions": json.dumps(positions.tolist()),
|
||||
"bond_pairs": json.dumps(bond_pairs.tolist() if n_bonds else []),
|
||||
"bond_stiffness": json.dumps(bond_stiffness.tolist() if n_bonds else []),
|
||||
"bond_rest_lengths": json.dumps(bond_rest_lengths.tolist() if n_bonds else []),
|
||||
"X_MIN": str(-box_a), "X_MAX": str(box_a),
|
||||
"Y_MIN": str(-box_a), "Y_MAX": str(box_a),
|
||||
"Z_MIN": str(-box_a), "Z_MAX": str(box_a),
|
||||
"camera_distance": str(cam_dist),
|
||||
"camera_elevation": str(cam_elev),
|
||||
"camera_azimuth": str(cam_azim),
|
||||
"camera_center_x": str(cam_cx),
|
||||
"camera_center_y": str(cam_cy),
|
||||
"camera_center_z": str(cam_cz),
|
||||
"camera_keyframes": "",
|
||||
}
|
||||
|
||||
# ── 保存 display.txt + display.npz ───────────────────────
|
||||
disp_txt = os.path.join(output_dir, "display.txt")
|
||||
_c.save_display_txt(
|
||||
disp_txt,
|
||||
out_x, out_y, out_z, out_vx, out_vy, out_vz,
|
||||
atom_ids, record_steps, len(atom_ids),
|
||||
header_fields=header,
|
||||
)
|
||||
print(f"[python-engine] display.txt 已保存: {disp_txt}")
|
||||
|
||||
disp_npz = os.path.join(output_dir, "display.npz")
|
||||
_c.save_display_npz(
|
||||
disp_npz,
|
||||
out_x, out_y, out_z, out_vx, out_vy, out_vz,
|
||||
atom_ids, header_fields=header,
|
||||
)
|
||||
print(f"[python-engine] display.npz 已保存: {disp_npz}")
|
||||
|
||||
# ── 可选:保存 trajectory.txt ─────────────────────────────
|
||||
if save_traj:
|
||||
traj_payload = {
|
||||
"traj_x": out_x, "traj_y": out_y, "traj_z": out_z,
|
||||
"traj_vx": out_vx, "traj_vy": out_vy, "traj_vz": out_vz,
|
||||
"NT": record_steps, "DT": dt, "NSTEP": NSTEP,
|
||||
"method": method_str,
|
||||
"atom_ids": atom_ids,
|
||||
"atom_masses": masses,
|
||||
"atom_radii": radii,
|
||||
"atom_positions": positions,
|
||||
"bond_pairs": bond_pairs,
|
||||
"bond_stiffness": bond_stiffness,
|
||||
"bond_rest_lengths": bond_rest_lengths,
|
||||
"G": [float(v) for v in G],
|
||||
"B": [float(v) for v in B],
|
||||
}
|
||||
traj_path = os.path.join(output_dir, "trajectory.txt")
|
||||
_c.save_text_data(traj_path, traj_payload)
|
||||
print(f"[python-engine] trajectory.txt 已保存: {traj_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user