fix: driven atoms now oscillate around their initial equilibrium position
Previously, apply_driving_force set absolute position to A*cos(2π f t + φ), ignoring the atom's initial coordinates. For atoms not at the origin (e.g., atom 120 at x=119), this incorrectly forced them back toward the origin each step, causing severe distortion and numerical explosion. Fix: store each driven atom's initial position as eq_pos/eq_x/eq_y/eq_z at load time; position is now eq + A*cos(2π f t + φ) in all four engines (Python, C, C++, Fortran). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+57
-2
@@ -69,6 +69,12 @@ camera_keyframes_raw = ""
|
||||
camera_distance = 40.0
|
||||
camera_elevation = 0.0
|
||||
camera_azimuth = 0.0
|
||||
camera_center_x = 0.0
|
||||
camera_center_y = 0.0
|
||||
camera_center_z = 0.0
|
||||
camera_pos_x = None
|
||||
camera_pos_y = None
|
||||
camera_pos_z = None
|
||||
FIXED_MASK_X = None
|
||||
FIXED_MASK_Y = None
|
||||
FIXED_MASK_Z = None
|
||||
@@ -649,6 +655,8 @@ def load_driver_file(driver_path, atom_ids):
|
||||
"phi": phi_rad,
|
||||
"period_str": period_str,
|
||||
"period_cycles": None if period_str == "all" else float(period_str),
|
||||
# 平衡位置(原子初始坐标),驱动为相对于此位置的偏移
|
||||
"eq_pos": None,
|
||||
# 在模拟中动态记录:冻结步数索引、冻结位置
|
||||
"freeze_step": None,
|
||||
"freeze_pos": None,
|
||||
@@ -698,8 +706,9 @@ def apply_driving_force(x, y, z, vx, vy, vz, t, step, drivers, dt):
|
||||
else:
|
||||
period_steps = None # 全程驱动
|
||||
|
||||
# 当前驱动力下的位置 / 速度
|
||||
pos_drive = d["amp"] * np.cos(2.0 * np.pi * d["freq"] * t + d["phi"])
|
||||
# 当前驱动力下的位置 / 速度(偏移叠加在初始平衡坐标上)
|
||||
eq = d["eq_pos"] if d["eq_pos"] is not None else np.zeros(3)
|
||||
pos_drive = eq + d["amp"] * np.cos(2.0 * np.pi * d["freq"] * t + d["phi"])
|
||||
vel_drive = -d["amp"] * 2.0 * np.pi * d["freq"] * np.sin(2.0 * np.pi * d["freq"] * t + d["phi"])
|
||||
|
||||
x[idx] = pos_drive[0]
|
||||
@@ -764,6 +773,8 @@ def run_from_config(config, out_dir=None):
|
||||
global ball_radius, ball_color_r, ball_color_g, ball_color_b
|
||||
global box_color_r, box_color_g, box_color_b
|
||||
global use_marker, camera_keyframes_raw, camera_distance, camera_elevation, camera_azimuth
|
||||
global camera_center_x, camera_center_y, camera_center_z
|
||||
global camera_pos_x, camera_pos_y, camera_pos_z
|
||||
global FIXED_MASK_X, FIXED_MASK_Y, FIXED_MASK_Z
|
||||
global warmup_steps, sample_start, sample_end
|
||||
global GRAVITY_FIELD, GRAVITY_INTERACTION, ELASTIC_FORCE, DAMPING_FORCE, GRAVITY_STRENGTH
|
||||
@@ -832,6 +843,12 @@ def run_from_config(config, out_dir=None):
|
||||
camera_distance = float(config.get("camera_distance", 40.0))
|
||||
camera_elevation = float(config.get("camera_elevation", 0.0))
|
||||
camera_azimuth = float(config.get("camera_azimuth", 0.0))
|
||||
camera_center_x = float(config.get("camera_center_x", 0.0))
|
||||
camera_center_y = float(config.get("camera_center_y", 0.0))
|
||||
camera_center_z = float(config.get("camera_center_z", 0.0))
|
||||
camera_pos_x = config.get("camera_pos_x", None)
|
||||
camera_pos_y = config.get("camera_pos_y", None)
|
||||
camera_pos_z = config.get("camera_pos_z", None)
|
||||
|
||||
# 力开关
|
||||
global GRAVITY_FIELD, GRAVITY_INTERACTION, ELASTIC_FORCE, DAMPING_FORCE, GRAVITY_STRENGTH
|
||||
@@ -851,6 +868,15 @@ def run_from_config(config, out_dir=None):
|
||||
if out_dir is not None and not os.path.isabs(driver_rel):
|
||||
driver_path = os.path.join(out_dir, driver_rel)
|
||||
DRIVER_DATA = load_driver_file(driver_path, ATOM_IDS)
|
||||
# 将每个驱动原子的初始坐标作为平衡位置
|
||||
if DRIVER_DATA:
|
||||
for _d in DRIVER_DATA:
|
||||
_i = _d["atom_index"]
|
||||
_d["eq_pos"] = np.array([
|
||||
ATOM_POSITIONS[_i, 0],
|
||||
ATOM_POSITIONS[_i, 1],
|
||||
ATOM_POSITIONS[_i, 2],
|
||||
], dtype=np.float64)
|
||||
|
||||
# 加载运动相机关键帧
|
||||
camera_keyframes_raw = ""
|
||||
@@ -986,6 +1012,9 @@ def run_engine(engine, input_dir, output_dir, config):
|
||||
"camera_distance": float(config.get("camera_distance", 40.0)),
|
||||
"camera_elevation": float(config.get("camera_elevation", 0)),
|
||||
"camera_azimuth": float(config.get("camera_azimuth", 0)),
|
||||
"camera_center_x": float(config.get("camera_center_x", 0.0)),
|
||||
"camera_center_y": float(config.get("camera_center_y", 0.0)),
|
||||
"camera_center_z": float(config.get("camera_center_z", 0.0)),
|
||||
}
|
||||
param_path = os.path.join(script_dir, "engines", engine, "param.json")
|
||||
os.makedirs(os.path.dirname(param_path), exist_ok=True)
|
||||
@@ -1136,12 +1165,27 @@ def run_engine(engine, input_dir, output_dir, config):
|
||||
_npz_path = disp_path.replace("display.txt", "display.npz")
|
||||
try:
|
||||
_d = load_display_txt(disp_path)
|
||||
# 外部引擎不写摄像机参数到 display.txt,在此从全局变量补入
|
||||
_cam_extra = {
|
||||
"camera_distance": str(camera_distance),
|
||||
"camera_elevation": str(camera_elevation),
|
||||
"camera_azimuth": str(camera_azimuth),
|
||||
"camera_center_x": str(camera_center_x),
|
||||
"camera_center_y": str(camera_center_y),
|
||||
"camera_center_z": str(camera_center_z),
|
||||
"camera_keyframes": str(camera_keyframes_raw),
|
||||
}
|
||||
if camera_pos_x is not None:
|
||||
_cam_extra["camera_pos_x"] = str(camera_pos_x)
|
||||
_cam_extra["camera_pos_y"] = str(camera_pos_y)
|
||||
_cam_extra["camera_pos_z"] = str(camera_pos_z)
|
||||
save_display_npz(
|
||||
_npz_path,
|
||||
_d["frames_x"], _d["frames_y"], _d["frames_z"],
|
||||
_d["frames_vx"], _d["frames_vy"], _d["frames_vz"],
|
||||
_d["atom_ids"],
|
||||
header_fields={**_d["header_fields"],
|
||||
**_cam_extra,
|
||||
"number_of_frames": str(_d["n_total_frames"]),
|
||||
"number_of_particles": str(_d["n_total_particles"])})
|
||||
print(f"[compute] display.npz 已生成: {_npz_path}")
|
||||
@@ -1727,6 +1771,14 @@ def run_simulation(save_trajectory=0):
|
||||
"camera_distance": str(camera_distance),
|
||||
"camera_elevation": str(camera_elevation),
|
||||
"camera_azimuth": str(camera_azimuth),
|
||||
"camera_center_x": str(camera_center_x),
|
||||
"camera_center_y": str(camera_center_y),
|
||||
"camera_center_z": str(camera_center_z),
|
||||
**({
|
||||
"camera_pos_x": str(camera_pos_x),
|
||||
"camera_pos_y": str(camera_pos_y),
|
||||
"camera_pos_z": str(camera_pos_z),
|
||||
} if camera_pos_x is not None else {}),
|
||||
"camera_keyframes": str(camera_keyframes_raw)}
|
||||
)
|
||||
print(f"[compute] display.txt 已保存至: {disp_path} ({n_frames_actual} 帧)")
|
||||
@@ -1765,6 +1817,9 @@ def run_simulation(save_trajectory=0):
|
||||
"camera_distance": str(camera_distance),
|
||||
"camera_elevation": str(camera_elevation),
|
||||
"camera_azimuth": str(camera_azimuth),
|
||||
"camera_center_x": str(camera_center_x),
|
||||
"camera_center_y": str(camera_center_y),
|
||||
"camera_center_z": str(camera_center_z),
|
||||
"camera_keyframes": str(camera_keyframes_raw),
|
||||
"number_of_frames": str(record_steps),
|
||||
"number_of_particles": str(n_atoms)}
|
||||
|
||||
Reference in New Issue
Block a user