feat: 真蛙跳法重构(Python/C/C++/Fortran 四引擎统一)
- 新增 compute_accel_conservative / accel_conservative: 保守力加速度(弹簧+重力+原子间引力),不含阻尼,供蛙跳专用 - 重写 leapfrog_step / leapfrog_full: - 无阻尼:纯辛积分器,每步 1 次力计算(原 Velocity-Verlet 需 2 次) - 有阻尼:半隐式处理 v(t+dt/2)=[v(t-dt/2)*(1-α)+a_c*dt]/(1+α),无条件稳定 - 主循环加初始化反向半步 v(-dt/2)=v(0)-0.5*a_c(0)*dt - 修复 C/C++ number of frames 字段写采样帧数而非总积分步数的 bug - Python 引擎:新增 display.npz 二进制格式,draw.py/plot_wave.py 优先读取 - 编译参数统一为 -O3 -march=native -ffast-math
This commit is contained in:
+107
-23
@@ -14,17 +14,101 @@ import matplotlib.pyplot as plt
|
||||
from matplotlib.animation import FuncAnimation
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import compute
|
||||
|
||||
|
||||
def load_disp_data(output_dir):
|
||||
"""加载 display.txt"""
|
||||
disp_path = os.path.join(output_dir, "display.txt")
|
||||
if not os.path.exists(disp_path):
|
||||
raise FileNotFoundError(f"找不到 {disp_path}")
|
||||
return compute.load_text_data(disp_path)
|
||||
"""加载 display.npz(优先)或 display.txt。"""
|
||||
npz_path = os.path.join(output_dir, "display.npz")
|
||||
txt_path = os.path.join(output_dir, "display.txt")
|
||||
if os.path.exists(npz_path):
|
||||
return compute.load_display_npz(npz_path)
|
||||
if os.path.exists(txt_path):
|
||||
return compute.load_display_txt(txt_path)
|
||||
raise FileNotFoundError(f"找不到 display.npz 或 display.txt in {output_dir}")
|
||||
|
||||
|
||||
def _header_json(header_fields, key, default):
|
||||
raw = header_fields.get(key, "")
|
||||
if not raw:
|
||||
return default
|
||||
try:
|
||||
return json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
return default
|
||||
|
||||
|
||||
def _load_wave_dataset(output_dir):
|
||||
"""Load wave/energy plotting data from display metadata or sibling input files."""
|
||||
disp_data = load_disp_data(output_dir)
|
||||
header = disp_data["header_fields"]
|
||||
|
||||
x = disp_data["frames_x"]
|
||||
y = disp_data["frames_y"]
|
||||
z = disp_data["frames_z"]
|
||||
vx = disp_data["frames_vx"]
|
||||
vy = disp_data["frames_vy"]
|
||||
vz = disp_data["frames_vz"]
|
||||
atom_ids = np.array(disp_data["atom_ids"], dtype=np.int64)
|
||||
n_frames = x.shape[0]
|
||||
dt = float(header.get("DT", 0.001))
|
||||
nstep = int(header.get("NSTEP", 1))
|
||||
t = np.arange(n_frames, dtype=np.float64) * dt * nstep
|
||||
|
||||
masses = np.array(_header_json(header, "atom_masses", []), dtype=np.float64)
|
||||
pos_0 = np.array(_header_json(header, "atom_positions", []), dtype=np.float64)
|
||||
bond_pairs = np.array(_header_json(header, "bond_pairs", []), dtype=np.int64)
|
||||
bond_stiffness = np.array(_header_json(header, "bond_stiffness", []), dtype=np.float64)
|
||||
bond_rest_lengths = np.array(_header_json(header, "bond_rest_lengths", []), dtype=np.float64)
|
||||
gravity_vec = _header_json(header, "G", [0.0, 0.0, 0.0])
|
||||
|
||||
# Backward-compatible fallback for older display.txt outputs.
|
||||
if masses.size == 0 or pos_0.size == 0:
|
||||
input_dir = os.path.join(os.path.dirname(output_dir), "input")
|
||||
coord_path = os.path.join(input_dir, "coord.txt")
|
||||
if os.path.exists(coord_path):
|
||||
(_, masses_fb, _, positions_fb, _, _) = compute.load_coord_file(coord_path)
|
||||
masses = np.array(masses_fb, dtype=np.float64)
|
||||
pos_0 = np.array(positions_fb, dtype=np.float64)
|
||||
else:
|
||||
raise ValueError("display.txt 缺少 atom_masses/atom_positions 元数据,且未找到 input/coord.txt")
|
||||
|
||||
if bond_pairs.size == 0:
|
||||
input_dir = os.path.join(os.path.dirname(output_dir), "input")
|
||||
connection_path = os.path.join(input_dir, "connection.txt")
|
||||
bond_path = os.path.join(input_dir, "bond.txt")
|
||||
if os.path.exists(connection_path) and os.path.exists(bond_path):
|
||||
bond_map = compute.load_bond_parameters(bond_path)
|
||||
pairs_fb, _, stiffness_fb, rest_lengths_fb = compute.load_bond_connections(
|
||||
connection_path, atom_ids, pos_0, bond_map)
|
||||
bond_pairs = np.array(pairs_fb, dtype=np.int64)
|
||||
bond_stiffness = np.array(stiffness_fb, dtype=np.float64)
|
||||
bond_rest_lengths = np.array(rest_lengths_fb, dtype=np.float64)
|
||||
|
||||
return {
|
||||
"n_frames": n_frames,
|
||||
"t": t,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"z": z,
|
||||
"vx": vx,
|
||||
"vy": vy,
|
||||
"vz": vz,
|
||||
"pos_0": pos_0,
|
||||
"masses": masses,
|
||||
"atom_ids": atom_ids,
|
||||
"bond_pairs": bond_pairs,
|
||||
"bond_stiffness": bond_stiffness,
|
||||
"bond_rest_lengths": bond_rest_lengths,
|
||||
"gravity_field": int(header.get("gravity_field", 0)),
|
||||
"gravity_interaction": int(header.get("gravity_interaction", 0)),
|
||||
"gravity_strength": float(header.get("gravity_strength", 1.0)),
|
||||
"G": gravity_vec,
|
||||
"driving_force": int(header.get("driving_force", 0)),
|
||||
}
|
||||
|
||||
|
||||
def compute_energy(x, y, z, vx, vy, vz, masses, mass_arr,
|
||||
@@ -91,36 +175,36 @@ def plot_wave(output_dir, save_gif=False, save_mp4=False):
|
||||
save_gif: 是否保存 GIF
|
||||
save_mp4: 是否保存 MP4
|
||||
"""
|
||||
data = load_disp_data(output_dir)
|
||||
data = _load_wave_dataset(output_dir)
|
||||
|
||||
n_frames = int(data["n_frames"])
|
||||
t = np.array(data["disp_t"])
|
||||
t = np.array(data["t"])
|
||||
|
||||
# 位置 / 速度
|
||||
x = np.array(data["disp_all_x"])
|
||||
y = np.array(data["disp_all_y"])
|
||||
z = np.array(data["disp_all_z"])
|
||||
vx = np.array(data["disp_all_vx"])
|
||||
vy = np.array(data["disp_all_vy"])
|
||||
vz = np.array(data["disp_all_vz"])
|
||||
x = np.array(data["x"])
|
||||
y = np.array(data["y"])
|
||||
z = np.array(data["z"])
|
||||
vx = np.array(data["vx"])
|
||||
vy = np.array(data["vy"])
|
||||
vz = np.array(data["vz"])
|
||||
|
||||
# 原子信息
|
||||
pos_0 = np.array(data["atom_positions"]) # (n_atoms, 3)
|
||||
masses = np.array(data["atom_masses"])
|
||||
pos_0 = np.array(data["pos_0"])
|
||||
masses = np.array(data["masses"])
|
||||
atom_ids = np.array(data["atom_ids"])
|
||||
n_atoms = len(atom_ids)
|
||||
n_atoms = len(atom_ids)
|
||||
|
||||
# 成键
|
||||
bond_pairs = data.get("bond_pairs", [])
|
||||
bond_stiffness = np.array(data.get("bond_stiffness", []))
|
||||
bond_rest_lengths = np.array(data.get("bond_rest_lengths", []))
|
||||
bond_pairs = np.array(data.get("bond_pairs", []), dtype=np.int64)
|
||||
bond_stiffness = np.array(data.get("bond_stiffness", []), dtype=np.float64)
|
||||
bond_rest_lengths = np.array(data.get("bond_rest_lengths", []), dtype=np.float64)
|
||||
|
||||
# 物理开关
|
||||
gravity_field = int(data.get("gravity_field", 0))
|
||||
gravity_field = int(data.get("gravity_field", 0))
|
||||
gravity_interaction = int(data.get("gravity_interaction", 0))
|
||||
G = data.get("G", [0, 0, 0])
|
||||
gravity_strength = float(data.get("gravity_strength", 1.0))
|
||||
driving_force = int(data.get("driving_force", 0))
|
||||
G = data.get("G", [0, 0, 0])
|
||||
gravity_strength = float(data.get("gravity_strength", 1.0))
|
||||
driving_force = int(data.get("driving_force", 0))
|
||||
|
||||
# ── 位移(偏离初始平衡位形)──
|
||||
dx = x - pos_0[np.newaxis, :, 0] # 纵波(沿链方向 x)
|
||||
|
||||
Reference in New Issue
Block a user