Compare commits
4 Commits
eea62de479
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e415bf5ba5 | |||
| b50c55d0a3 | |||
| 75d8aae1f2 | |||
| dd6fbdfccf |
+8
-6
@@ -1,7 +1,9 @@
|
|||||||
# 全部使用 LF 换行,仓库内外一致,不随系统自动转换
|
# 引擎 DLL 是二进制文件,禁止行尾转换
|
||||||
* text eol=lf
|
engines/release/*.dll binary
|
||||||
|
|
||||||
# 二进制文件不转换
|
# Python 源码使用 LF
|
||||||
*.png binary
|
*.py text eol=lf
|
||||||
*.jpg binary
|
|
||||||
*.ico binary
|
# Makefile 使用 LF
|
||||||
|
Makefile text eol=lf
|
||||||
|
*.mk text eol=lf
|
||||||
|
|||||||
+18
-6
@@ -668,12 +668,20 @@ def load_driver_file(driver_path, atom_ids):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
print(f"[compute] 已加载驱动力: {len(drivers)} 条定义")
|
print(f"[compute] 已加载驱动力: {len(drivers)} 条定义")
|
||||||
for d in drivers:
|
if len(drivers) <= 20:
|
||||||
print(f" 原子 {d['atom_id']}: "
|
for d in drivers:
|
||||||
f"A=({d['amp'][0]},{d['amp'][1]},{d['amp'][2]}), "
|
print(f" 原子 {d['atom_id']}: "
|
||||||
f"f=({d['freq'][0]},{d['freq'][1]},{d['freq'][2]}), "
|
f"A=({d['amp'][0]},{d['amp'][1]},{d['amp'][2]}), "
|
||||||
f"φ=({phi_deg[d['amp'].tolist().index(max(d['amp']))]}° 等), "
|
f"f=({d['freq'][0]},{d['freq'][1]},{d['freq'][2]}), "
|
||||||
f"period={d['period_str']}")
|
f"φ=({phi_deg[d['amp'].tolist().index(max(d['amp']))]}° 等), "
|
||||||
|
f"period={d['period_str']}")
|
||||||
|
else:
|
||||||
|
_first = drivers[0]
|
||||||
|
_last = drivers[-1]
|
||||||
|
print(f" 原子 {_first['atom_id']}~{_last['atom_id']}: "
|
||||||
|
f"A=({_first['amp'][0]},{_first['amp'][1]},{_first['amp'][2]}), "
|
||||||
|
f"f=({_first['freq'][0]},{_first['freq'][1]},{_first['freq'][2]}), "
|
||||||
|
f"period={_first['period_str']} 等 {len(drivers)} 条")
|
||||||
return drivers
|
return drivers
|
||||||
|
|
||||||
|
|
||||||
@@ -1070,6 +1078,10 @@ def run_engine_dll(engine, output_dir, config):
|
|||||||
"number_of_particles": str(n_atoms),
|
"number_of_particles": str(n_atoms),
|
||||||
# draw.py 需要的渲染参数
|
# draw.py 需要的渲染参数
|
||||||
"use_marker": str(use_marker),
|
"use_marker": str(use_marker),
|
||||||
|
"display_color": json.dumps(config.get("display_color",
|
||||||
|
{"x":[0,[1.0,0.0,0.0]],"y":[0,[0.0,1.0,0.0]],"z":[0,[0.0,0.0,1.0]],
|
||||||
|
"xy":[0,[1.0,1.0,0.0]],"yz":[0,[0.0,1.0,1.0]],"zx":[0,[1.0,0.0,1.0]],
|
||||||
|
"xyz":[1,[1.0,1.0,1.0]]})),
|
||||||
"ball_radius": str(config.get("ball_radius", float(ATOM_RADII[0]) if ATOM_RADII is not None else 0.5)),
|
"ball_radius": str(config.get("ball_radius", float(ATOM_RADII[0]) if ATOM_RADII is not None else 0.5)),
|
||||||
"ball_color_r": str(config.get("ball_color_r", 0.9)),
|
"ball_color_r": str(config.get("ball_color_r", 0.9)),
|
||||||
"ball_color_g": str(config.get("ball_color_g", 0.2)),
|
"ball_color_g": str(config.get("ball_color_g", 0.2)),
|
||||||
|
|||||||
@@ -45,7 +45,65 @@ if os.path.exists(npz_path):
|
|||||||
disp_data = compute.load_display_npz(npz_path)
|
disp_data = compute.load_display_npz(npz_path)
|
||||||
else:
|
else:
|
||||||
disp_data = compute.load_display_txt(disp_path)
|
disp_data = compute.load_display_txt(disp_path)
|
||||||
h = disp_data["header_fields"]
|
|
||||||
|
# ── 从 input.txt 读取参数(替代 display.npz 中的 meta)──
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
_have_yaml = True
|
||||||
|
except ImportError:
|
||||||
|
_have_yaml = False
|
||||||
|
|
||||||
|
input_dir = os.path.join(os.path.dirname(output_dir), "input")
|
||||||
|
input_path = os.path.join(input_dir, "input.txt")
|
||||||
|
|
||||||
|
if _have_yaml and os.path.exists(input_path):
|
||||||
|
try:
|
||||||
|
with open(input_path, "r", encoding="utf-8") as f:
|
||||||
|
config = yaml.safe_load(f)
|
||||||
|
except Exception:
|
||||||
|
config = {}
|
||||||
|
else:
|
||||||
|
config = {}
|
||||||
|
# 兼容旧版:若 input.txt 不存在或解析失败,降级到 display.npz 的 meta
|
||||||
|
if not config:
|
||||||
|
config = disp_data.get("header_fields", {})
|
||||||
|
|
||||||
|
# ── 从 coord.txt 读取平衡位置 ─────────────────
|
||||||
|
# 注意: config 中的 *_file 可能带 "input/" 前缀,但 input_dir 已指向 input/ 目录
|
||||||
|
_coord_file_raw = config.get("coord_file", "coord.txt")
|
||||||
|
coord_path = os.path.join(input_dir, os.path.basename(_coord_file_raw))
|
||||||
|
if os.path.exists(coord_path):
|
||||||
|
try:
|
||||||
|
_ids, _masses, _radii, _pos, _vel, _fixed = compute.load_coord_file(coord_path)
|
||||||
|
EQ_POS = _pos # (n_atoms, 3)
|
||||||
|
ATOM_FIXED = _fixed # (n_atoms, 3)
|
||||||
|
except Exception:
|
||||||
|
EQ_POS = None
|
||||||
|
ATOM_FIXED = None
|
||||||
|
else:
|
||||||
|
EQ_POS = None
|
||||||
|
ATOM_FIXED = None
|
||||||
|
|
||||||
|
# ── 从 connection.txt 读取成键信息(若 meta 中没有)──
|
||||||
|
BOND_PAIRS = disp_data.get("bond_pairs", [])
|
||||||
|
if not BOND_PAIRS and 'bond_pairs' not in disp_data:
|
||||||
|
conn_path = os.path.join(input_dir, os.path.basename(config.get("connection_file", "connection.txt")))
|
||||||
|
if os.path.exists(conn_path):
|
||||||
|
try:
|
||||||
|
_pairs = []
|
||||||
|
with open(conn_path, "r", encoding="utf-8") as _f:
|
||||||
|
_f.readline() # skip header
|
||||||
|
for _line in _f:
|
||||||
|
_line = _line.strip()
|
||||||
|
if not _line or _line.startswith("#"):
|
||||||
|
continue
|
||||||
|
_parts = _line.split()
|
||||||
|
if len(_parts) >= 2:
|
||||||
|
_pairs.append([int(_parts[0]) - 1, int(_parts[1]) - 1])
|
||||||
|
BOND_PAIRS = np.array(_pairs, dtype=np.int32) if _pairs else []
|
||||||
|
except Exception:
|
||||||
|
BOND_PAIRS = []
|
||||||
|
BOND_PAIRS = BOND_PAIRS.tolist() if hasattr(BOND_PAIRS, 'tolist') else BOND_PAIRS
|
||||||
|
|
||||||
# 全原子帧数据
|
# 全原子帧数据
|
||||||
DISP_ALL_X = disp_data["frames_x"] # (n_frames, n_atoms)
|
DISP_ALL_X = disp_data["frames_x"] # (n_frames, n_atoms)
|
||||||
@@ -66,64 +124,220 @@ DISP_VZ = DISP_ALL_VZ[:, 0]
|
|||||||
N_FRAMES = DISP_ALL_X.shape[0]
|
N_FRAMES = DISP_ALL_X.shape[0]
|
||||||
NT = int(disp_data["n_total_frames"])
|
NT = int(disp_data["n_total_frames"])
|
||||||
N_ATOMS = int(disp_data["n_total_particles"])
|
N_ATOMS = int(disp_data["n_total_particles"])
|
||||||
DT = float(h.get("DT", 0.001))
|
DT = float(config.get("DT", 0.001))
|
||||||
|
|
||||||
# 视觉位移放大:display_amp: [ax, ay, az],对偏离第0帧的位移乘以倍数
|
# 视觉位移放大:display_amp: [ax, ay, az],对偏离平衡位置的位移乘以倍数
|
||||||
_damp_raw = h.get("display_amp", "")
|
_damp_raw = config.get("display_amp", "")
|
||||||
if _damp_raw.strip():
|
if isinstance(_damp_raw, (list, tuple)):
|
||||||
|
_damp = np.array(_damp_raw, dtype=np.float64)
|
||||||
|
elif isinstance(_damp_raw, str) and _damp_raw.strip():
|
||||||
import ast as _ast
|
import ast as _ast
|
||||||
_damp_vals = _ast.literal_eval(_damp_raw.strip())
|
_damp = np.array(_ast.literal_eval(_damp_raw.strip()), dtype=np.float64)
|
||||||
_damp = np.array(_damp_vals, dtype=np.float64)
|
else:
|
||||||
if _damp.shape == (3,) and not np.allclose(_damp, 1.0):
|
_damp = None
|
||||||
_eq_x = DISP_ALL_X[0:1, :] # 第0帧作为平衡位置参考
|
if _damp is not None and _damp.shape == (3,) and not np.allclose(_damp, 1.0):
|
||||||
_eq_y = DISP_ALL_Y[0:1, :]
|
if EQ_POS is not None:
|
||||||
_eq_z = DISP_ALL_Z[0:1, :]
|
_eq_x = EQ_POS[None, :, 0] # coord.txt 平衡位置
|
||||||
|
_eq_y = EQ_POS[None, :, 1]
|
||||||
|
_eq_z = EQ_POS[None, :, 2]
|
||||||
|
else:
|
||||||
|
_eq_x = DISP_ALL_X[0:1, :] # 第0帧作为平衡位置参考
|
||||||
|
_eq_y = DISP_ALL_Y[0:1, :]
|
||||||
|
_eq_z = DISP_ALL_Z[0:1, :]
|
||||||
DISP_ALL_X = _eq_x + (DISP_ALL_X - _eq_x) * _damp[0]
|
DISP_ALL_X = _eq_x + (DISP_ALL_X - _eq_x) * _damp[0]
|
||||||
DISP_ALL_Y = _eq_y + (DISP_ALL_Y - _eq_y) * _damp[1]
|
DISP_ALL_Y = _eq_y + (DISP_ALL_Y - _eq_y) * _damp[1]
|
||||||
DISP_ALL_Z = _eq_z + (DISP_ALL_Z - _eq_z) * _damp[2]
|
DISP_ALL_Z = _eq_z + (DISP_ALL_Z - _eq_z) * _damp[2]
|
||||||
NSTEP = int(h.get("NSTEP", 1))
|
|
||||||
|
# ── 位移颜色映射 ──────────────────────────────
|
||||||
|
# display_color: { mode: [enabled, [R,G,B]], ... }
|
||||||
|
# mode: x, y, z, xy, yz, zx, xyz
|
||||||
|
# enabled: 0=off, 1=on
|
||||||
|
# [R,G,B]: 最大位移时的颜色(渐变起点为白色)
|
||||||
|
# 例:xyz: [1, [1.0,1.0,1.0]] → 三方向合成位移 → 白色渐变
|
||||||
|
FRAME_COLORS = None
|
||||||
|
_DC_MODE = None
|
||||||
|
_DC_COLOR = None
|
||||||
|
|
||||||
|
_raw_dc = config.get("display_color", "")
|
||||||
|
if _raw_dc:
|
||||||
|
try:
|
||||||
|
dc = json.loads(_raw_dc) if isinstance(_raw_dc, str) else _raw_dc
|
||||||
|
if isinstance(dc, dict):
|
||||||
|
for mode, (enabled, color) in dc.items():
|
||||||
|
if int(enabled) and mode in ("x","y","z","xy","yz","zx","xyz"):
|
||||||
|
_DC_MODE = mode
|
||||||
|
_DC_COLOR = np.array(color, dtype=np.float32)
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if _DC_MODE is not None and _DC_COLOR is not None:
|
||||||
|
if EQ_POS is not None:
|
||||||
|
eq_x = EQ_POS[None, :, 0] # coord.txt 平衡位置
|
||||||
|
eq_y = EQ_POS[None, :, 1]
|
||||||
|
eq_z = EQ_POS[None, :, 2]
|
||||||
|
else:
|
||||||
|
eq_x = DISP_ALL_X[0:1, :] # 第0帧
|
||||||
|
eq_y = DISP_ALL_Y[0:1, :]
|
||||||
|
eq_z = DISP_ALL_Z[0:1, :]
|
||||||
|
dx = DISP_ALL_X - eq_x
|
||||||
|
dy = DISP_ALL_Y - eq_y
|
||||||
|
dz = DISP_ALL_Z - eq_z
|
||||||
|
|
||||||
|
if _DC_MODE == "x":
|
||||||
|
disp_mag = np.abs(dx)
|
||||||
|
elif _DC_MODE == "y":
|
||||||
|
disp_mag = np.abs(dy)
|
||||||
|
elif _DC_MODE == "z":
|
||||||
|
disp_mag = np.abs(dz)
|
||||||
|
elif _DC_MODE == "xy":
|
||||||
|
disp_mag = np.sqrt(dx**2 + dy**2)
|
||||||
|
elif _DC_MODE == "yz":
|
||||||
|
disp_mag = np.sqrt(dy**2 + dz**2)
|
||||||
|
elif _DC_MODE == "zx":
|
||||||
|
disp_mag = np.sqrt(dz**2 + dx**2)
|
||||||
|
else: # xyz
|
||||||
|
disp_mag = np.sqrt(dx**2 + dy**2 + dz**2)
|
||||||
|
|
||||||
|
# ── color_xrange: 限定归一化基准的原子范围 ───────────
|
||||||
|
# 格式: [['min'|'mid'|'max'|数值, 'min'|'mid'|'max'|数值], ...] 对应 x,y,z
|
||||||
|
# 在此范围内的原子计算最大位移 d_max,所有原子以此基准归一化着色
|
||||||
|
_cxr = config.get("color_xrange", None)
|
||||||
|
_d_max_source = disp_mag # 默认:所有原子
|
||||||
|
_range_label = "全部原子"
|
||||||
|
if _cxr and isinstance(_cxr, (list, tuple)) and len(_cxr) == 3:
|
||||||
|
try:
|
||||||
|
# 获取坐标极值
|
||||||
|
if EQ_POS is not None:
|
||||||
|
_eq_all = EQ_POS
|
||||||
|
else:
|
||||||
|
_eq_all = np.column_stack([
|
||||||
|
DISP_ALL_X[0], DISP_ALL_Y[0], DISP_ALL_Z[0]])
|
||||||
|
_cmin = _eq_all.min(axis=0)
|
||||||
|
_cmax = _eq_all.max(axis=0)
|
||||||
|
_cmid = (_cmin + _cmax) / 2
|
||||||
|
|
||||||
|
_key_map = {"min": _cmin, "mid": _cmid, "max": _cmax}
|
||||||
|
_range_lo = np.zeros(3, dtype=np.float64)
|
||||||
|
_range_hi = np.zeros(3, dtype=np.float64)
|
||||||
|
|
||||||
|
for _i in range(3):
|
||||||
|
_lo = _cxr[_i][0]
|
||||||
|
_hi = _cxr[_i][1]
|
||||||
|
_range_lo[_i] = _key_map[_lo][_i] if _lo in _key_map else float(_lo)
|
||||||
|
_range_hi[_i] = _key_map[_hi][_i] if _hi in _key_map else float(_hi)
|
||||||
|
|
||||||
|
_in_x = (_eq_all[:, 0] >= _range_lo[0]) & (_eq_all[:, 0] <= _range_hi[0])
|
||||||
|
_in_y = (_eq_all[:, 1] >= _range_lo[1]) & (_eq_all[:, 1] <= _range_hi[1])
|
||||||
|
_in_z = (_eq_all[:, 2] >= _range_lo[2]) & (_eq_all[:, 2] <= _range_hi[2])
|
||||||
|
_color_mask = _in_x & _in_y & _in_z
|
||||||
|
|
||||||
|
_n_in_range = _color_mask.sum()
|
||||||
|
if _n_in_range > 0:
|
||||||
|
_d_max_source = disp_mag[:, _color_mask] # 仅在范围内找最大位移
|
||||||
|
_range_label = (f"x[{_range_lo[0]:.1f},{_range_hi[0]:.1f}] "
|
||||||
|
f"y[{_range_lo[1]:.1f},{_range_hi[1]:.1f}] "
|
||||||
|
f"z[{_range_lo[2]:.1f},{_range_hi[2]:.1f}]")
|
||||||
|
except Exception as _e:
|
||||||
|
print(f"[draw] color_xrange 解析失败: {_e}")
|
||||||
|
|
||||||
|
# 用(范围限定的)最大位移归一化,所有原子统一着色
|
||||||
|
d_max = _d_max_source.max()
|
||||||
|
if d_max > 1e-12:
|
||||||
|
disp_norm = disp_mag / d_max
|
||||||
|
else:
|
||||||
|
disp_norm = disp_mag
|
||||||
|
|
||||||
|
FRAME_COLORS = np.ones((N_FRAMES, N_ATOMS, 4), dtype=np.float32)
|
||||||
|
t = disp_norm[..., None]
|
||||||
|
FRAME_COLORS[..., :3] = 1.0 + (_DC_COLOR - 1.0) * t
|
||||||
|
print(f"[draw] 位移颜色映射: mode={_DC_MODE}, color={_DC_COLOR.tolist()}, "
|
||||||
|
f"d_max={d_max:.4f}, 范围: {_range_label}")
|
||||||
|
|
||||||
|
# ── color_driver: 驱动原子 → 统一颜色 ──
|
||||||
|
_cd_raw = config.get("color_driver", None)
|
||||||
|
if _cd_raw is not None:
|
||||||
|
try:
|
||||||
|
_cd = np.array(_cd_raw, dtype=np.float32)
|
||||||
|
# 读取 driver.txt 获取驱动原子 ID
|
||||||
|
_driver_file = config.get("driver_file", "driver.txt")
|
||||||
|
_driver_path = os.path.join(input_dir, os.path.basename(_driver_file))
|
||||||
|
if os.path.exists(_driver_path):
|
||||||
|
_driver_ids = []
|
||||||
|
with open(_driver_path, "r", encoding="utf-8") as _df:
|
||||||
|
_df.readline() # skip header
|
||||||
|
for _line in _df:
|
||||||
|
_line = _line.strip()
|
||||||
|
if not _line or _line.startswith("#"):
|
||||||
|
continue
|
||||||
|
_parts = _line.split()
|
||||||
|
if _parts:
|
||||||
|
_driver_ids.append(int(_parts[0]) - 1) # 1-based → 0-based
|
||||||
|
if _driver_ids:
|
||||||
|
FRAME_COLORS[:, _driver_ids, :3] = _cd
|
||||||
|
print(f"[draw] color_driver: {len(_driver_ids)} 驱动原子 → RGB{_cd_raw}")
|
||||||
|
except Exception as _e:
|
||||||
|
print(f"[draw] color_driver 解析失败: {_e}")
|
||||||
|
|
||||||
|
# ── color_fix: 全固定原子 (fix_x=fix_y=fix_z=1) → 统一颜色 ──
|
||||||
|
_cf_raw = config.get("color_fix", None)
|
||||||
|
if _cf_raw is not None and ATOM_FIXED is not None:
|
||||||
|
try:
|
||||||
|
_cf = np.array(_cf_raw, dtype=np.float32)
|
||||||
|
_full_fixed = (ATOM_FIXED[:, 0] == 1) & (ATOM_FIXED[:, 1] == 1) & (ATOM_FIXED[:, 2] == 1)
|
||||||
|
_n_fix = _full_fixed.sum()
|
||||||
|
if _n_fix > 0:
|
||||||
|
FRAME_COLORS[:, _full_fixed, :3] = _cf
|
||||||
|
print(f"[draw] color_fix: {_n_fix} 全固定原子 → RGB{_cf_raw}")
|
||||||
|
except Exception as _e:
|
||||||
|
print(f"[draw] color_fix 解析失败: {_e}")
|
||||||
|
NSTEP = int(config.get("NSTEP", 1))
|
||||||
DISP_STEP = np.arange(N_FRAMES) * NSTEP
|
DISP_STEP = np.arange(N_FRAMES) * NSTEP
|
||||||
DISP_T = DISP_STEP * DT
|
DISP_T = DISP_STEP * DT
|
||||||
|
|
||||||
# 原子信息
|
# 原子信息
|
||||||
ATOM_IDS = disp_data["atom_ids"]
|
ATOM_IDS = disp_data["atom_ids"]
|
||||||
# 优先使用 per-atom 半径,否则用统一的 ball_radius
|
# 优先使用 per-atom 半径,否则用统一的 ball_radius
|
||||||
_raw_radii = h.get("atom_radii", "")
|
_raw_radii = config.get("atom_radii", "")
|
||||||
if _raw_radii.strip():
|
if _raw_radii.strip():
|
||||||
ATOM_RADII = np.array([float(x) for x in _raw_radii.split(",")])
|
ATOM_RADII = np.array([float(x) for x in _raw_radii.split(",")])
|
||||||
else:
|
else:
|
||||||
ATOM_RADII = np.full(N_ATOMS, float(h.get("ball_radius", 0.5)))
|
ATOM_RADII = np.full(N_ATOMS, float(config.get("ball_radius", 0.5)))
|
||||||
PLOT_ATOM_ROW = 0
|
PLOT_ATOM_ROW = 0
|
||||||
PLOT_ATOM_ID = int(ATOM_IDS[0])
|
PLOT_ATOM_ID = int(ATOM_IDS[0])
|
||||||
BOND_PAIRS = [] # display 格式不含成键信息,从原始数据加载
|
# 成键信息已在上面从 connection.txt 加载
|
||||||
|
|
||||||
# 渲染方式:0=Sphere(网格球体), 1=Marker(GPU点精灵)
|
# 渲染方式:0=Sphere(网格球体), 1=Marker(GPU点精灵)
|
||||||
USE_MARKER = int(h.get("use_marker", 0))
|
USE_MARKER = int(config.get("use_marker", 0))
|
||||||
|
|
||||||
if N_FRAMES <= 0:
|
if N_FRAMES <= 0:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"output/display.txt 中没有可播放的帧,请检查 sample_start/sample_end/NSTEP 配置。")
|
"output/display.txt 中没有可播放的帧,请检查 sample_start/sample_end/NSTEP 配置。")
|
||||||
|
|
||||||
# 保留模拟边界常量(用于场景缩放、相机等),从 output/display.txt 中读取
|
# 模拟边界(从 input.txt 的 box_a 计算)
|
||||||
X_MIN = float(h.get("X_MIN", -10)); X_MAX = float(h.get("X_MAX", 10))
|
_box_a = float(config.get("box_a", 10.0))
|
||||||
Y_MIN = float(h.get("Y_MIN", -10)); Y_MAX = float(h.get("Y_MAX", 10))
|
X_MIN = -_box_a; X_MAX = _box_a
|
||||||
Z_MIN = float(h.get("Z_MIN", -10)); Z_MAX = float(h.get("Z_MAX", 10))
|
Y_MIN = -_box_a; Y_MAX = _box_a
|
||||||
raw_alpha = h.get("alpha", "0.2")
|
Z_MIN = -_box_a; Z_MAX = _box_a
|
||||||
try:
|
raw_alpha = config.get("alpha", "0.2")
|
||||||
alpha_list = [float(x) for x in raw_alpha.split(",")]
|
if isinstance(raw_alpha, (list, tuple)):
|
||||||
if len(alpha_list) != 6:
|
alpha_list = [float(x) for x in raw_alpha]
|
||||||
alpha_list = alpha_list * 6
|
else:
|
||||||
except (ValueError, AttributeError):
|
try:
|
||||||
alpha_list = [float(raw_alpha)] * 6
|
alpha_list = [float(x) for x in raw_alpha.split(",")]
|
||||||
|
except (ValueError, AttributeError):
|
||||||
|
alpha_list = [float(raw_alpha)] * 6
|
||||||
|
if len(alpha_list) != 6:
|
||||||
|
alpha_list = (alpha_list * 6)[:6]
|
||||||
|
|
||||||
# 绘图参数
|
# 绘图参数
|
||||||
ball_radius = float(h.get("ball_radius", 0.5))
|
ball_radius = float(config.get("ball_radius", 0.5))
|
||||||
ball_color_r = float(h.get("ball_color_r", 0.9))
|
ball_color_r = float(config.get("ball_color_r", 0.9))
|
||||||
ball_color_g = float(h.get("ball_color_g", 0.2))
|
ball_color_g = float(config.get("ball_color_g", 0.2))
|
||||||
ball_color_b = float(h.get("ball_color_b", 0.2))
|
ball_color_b = float(config.get("ball_color_b", 0.2))
|
||||||
box_color_r = float(h.get("box_color_r", 0.8))
|
box_color_r = float(config.get("box_color_r", 0.8))
|
||||||
box_color_g = float(h.get("box_color_g", 0.8))
|
box_color_g = float(config.get("box_color_g", 0.8))
|
||||||
box_color_b = float(h.get("box_color_b", 0.85))
|
box_color_b = float(config.get("box_color_b", 0.85))
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
@@ -135,23 +349,23 @@ axis_length = 10.0
|
|||||||
|
|
||||||
import math as _math_cam
|
import math as _math_cam
|
||||||
|
|
||||||
_cx = float(h.get("camera_center_x", 0.0))
|
_cx = float(config.get("camera_center_x", 0.0))
|
||||||
_cy = float(h.get("camera_center_y", 0.0))
|
_cy = float(config.get("camera_center_y", 0.0))
|
||||||
_cz = float(h.get("camera_center_z", 0.0))
|
_cz = float(config.get("camera_center_z", 0.0))
|
||||||
|
|
||||||
# 若 input.txt 指定了摄像机自身坐标,则由坐标反推 distance/elevation/azimuth
|
# 若 input.txt 指定了摄像机自身坐标,则由坐标反推 distance/elevation/azimuth
|
||||||
if h.get("camera_pos_x") is not None:
|
if config.get("camera_pos_x") is not None:
|
||||||
_px = float(h["camera_pos_x"])
|
_px = float(config.get("camera_pos_x"))
|
||||||
_py = float(h["camera_pos_y"])
|
_py = float(config.get("camera_pos_y"))
|
||||||
_pz = float(h["camera_pos_z"])
|
_pz = float(config.get("camera_pos_z"))
|
||||||
_dx, _dy, _dz = _px - _cx, _py - _cy, _pz - _cz
|
_dx, _dy, _dz = _px - _cx, _py - _cy, _pz - _cz
|
||||||
_dist = _math_cam.sqrt(_dx*_dx + _dy*_dy + _dz*_dz) or 1.0
|
_dist = _math_cam.sqrt(_dx*_dx + _dy*_dy + _dz*_dz) or 1.0
|
||||||
_elev = _math_cam.degrees(_math_cam.asin(max(-1.0, min(1.0, _dy / _dist))))
|
_elev = _math_cam.degrees(_math_cam.asin(max(-1.0, min(1.0, _dy / _dist))))
|
||||||
_azim = _math_cam.degrees(_math_cam.atan2(_dx, _dz))
|
_azim = _math_cam.degrees(_math_cam.atan2(_dx, _dz))
|
||||||
else:
|
else:
|
||||||
_dist = float(h.get("camera_distance", 40.0))
|
_dist = float(config.get("camera_distance", 40.0))
|
||||||
_elev = float(h.get("camera_elevation", 0))
|
_elev = float(config.get("camera_elevation", 0))
|
||||||
_azim = float(h.get("camera_azimuth", 0))
|
_azim = float(config.get("camera_azimuth", 0))
|
||||||
|
|
||||||
initial_camera = {
|
initial_camera = {
|
||||||
"distance": _dist,
|
"distance": _dist,
|
||||||
@@ -212,11 +426,11 @@ axes_group.append(scene.visuals.Arrow(
|
|||||||
parent=view.scene,
|
parent=view.scene,
|
||||||
))
|
))
|
||||||
|
|
||||||
axes_group.append(scene.visuals.Text(text="x", color=(1.0, 0.2, 0.2, 1.0), font_size=18,
|
axes_group.append(scene.visuals.Text(text="x", color=(1.0, 0.2, 0.2, 1.0), font_size=14,
|
||||||
pos=(axis_length + 0.2, 0, 0), anchor_x="left", anchor_y="center", parent=view.scene))
|
pos=(axis_length + 0.2, 0, 0), anchor_x="left", anchor_y="center", parent=view.scene))
|
||||||
axes_group.append(scene.visuals.Text(text="y", color=(0.2, 1.0, 0.2, 1.0), font_size=18,
|
axes_group.append(scene.visuals.Text(text="y", color=(0.2, 1.0, 0.2, 1.0), font_size=14,
|
||||||
pos=(0, axis_length + 0.2, 0), anchor_x="left", anchor_y="bottom", parent=view.scene))
|
pos=(0, axis_length + 0.2, 0), anchor_x="left", anchor_y="bottom", parent=view.scene))
|
||||||
axes_group.append(scene.visuals.Text(text="z", color=(0.3, 0.6, 1.0, 1.0), font_size=18,
|
axes_group.append(scene.visuals.Text(text="z", color=(0.3, 0.6, 1.0, 1.0), font_size=14,
|
||||||
pos=(0, 0, axis_length + 0.2), anchor_x="left", anchor_y="bottom", parent=view.scene))
|
pos=(0, 0, axis_length + 0.2), anchor_x="left", anchor_y="bottom", parent=view.scene))
|
||||||
|
|
||||||
# ── 原子渲染 ──────────────────────────────────
|
# ── 原子渲染 ──────────────────────────────────
|
||||||
@@ -235,11 +449,14 @@ TAB10_RGB = np.array([
|
|||||||
[0.7373, 0.7412, 0.1333], # 黄绿
|
[0.7373, 0.7412, 0.1333], # 黄绿
|
||||||
[0.0902, 0.7451, 0.8118], # 青
|
[0.0902, 0.7451, 0.8118], # 青
|
||||||
])
|
])
|
||||||
# 每个原子的颜色(循环使用 tab10 色板)
|
# 每个原子的颜色(循环使用 tab10 色板,或按位移着色)
|
||||||
atom_colors = np.zeros((N_ATOMS, 4), dtype=np.float32)
|
atom_colors = np.zeros((N_ATOMS, 4), dtype=np.float32)
|
||||||
for i in range(N_ATOMS):
|
if FRAME_COLORS is not None:
|
||||||
r, g, b = TAB10_RGB[i % len(TAB10_RGB)]
|
atom_colors[:] = FRAME_COLORS[0] # 初始帧颜色
|
||||||
atom_colors[i] = [r, g, b, 1.0]
|
else:
|
||||||
|
for i in range(N_ATOMS):
|
||||||
|
r, g, b = TAB10_RGB[i % len(TAB10_RGB)]
|
||||||
|
atom_colors[i] = [r, g, b, 1.0]
|
||||||
|
|
||||||
if USE_MARKER:
|
if USE_MARKER:
|
||||||
# ── Marker 模式:GPU 实例化,一次 draw call ──
|
# ── Marker 模式:GPU 实例化,一次 draw call ──
|
||||||
@@ -295,12 +512,12 @@ for f_idx, (pos, direction) in enumerate(faces):
|
|||||||
|
|
||||||
# 右上角:相机信息
|
# 右上角:相机信息
|
||||||
camera_info = scene.visuals.Text(
|
camera_info = scene.visuals.Text(
|
||||||
text="", color="white", font_size=14,
|
text="", color="white", font_size=12,
|
||||||
pos=(0, 0), anchor_x="right", anchor_y="top", parent=canvas.scene)
|
pos=(0, 0), anchor_x="right", anchor_y="top", parent=canvas.scene)
|
||||||
|
|
||||||
# 左上角:小球信息
|
# 左上角:小球信息
|
||||||
ball_info = scene.visuals.Text(
|
ball_info = scene.visuals.Text(
|
||||||
text="", color=(0.2, 1.0, 0.2, 1.0), font_size=18,
|
text="", color=(0.2, 1.0, 0.2, 1.0), font_size=14,
|
||||||
pos=(0, 0), anchor_x="left", anchor_y="top",
|
pos=(0, 0), anchor_x="left", anchor_y="top",
|
||||||
face="黑体", bold=True, parent=canvas.scene)
|
face="黑体", bold=True, parent=canvas.scene)
|
||||||
|
|
||||||
@@ -312,7 +529,7 @@ reset_button = scene.visuals.Rectangle(
|
|||||||
radius=6, color=(0.18, 0.35, 0.65, 0.85),
|
radius=6, color=(0.18, 0.35, 0.65, 0.85),
|
||||||
border_color="white", parent=canvas.scene)
|
border_color="white", parent=canvas.scene)
|
||||||
reset_button_label = scene.visuals.Text(
|
reset_button_label = scene.visuals.Text(
|
||||||
text="reset", color="white", font_size=16,
|
text="reset", color="white", font_size=13,
|
||||||
pos=(reset_btn_size[0] / 2 + 8, reset_btn_size[1] / 2 + 8),
|
pos=(reset_btn_size[0] / 2 + 8, reset_btn_size[1] / 2 + 8),
|
||||||
anchor_x="center", anchor_y="center",
|
anchor_x="center", anchor_y="center",
|
||||||
bold=True, parent=canvas.scene)
|
bold=True, parent=canvas.scene)
|
||||||
@@ -326,7 +543,7 @@ info_button = scene.visuals.Rectangle(
|
|||||||
radius=6, color=(0.9, 0.3, 0.3, 0.9),
|
radius=6, color=(0.9, 0.3, 0.3, 0.9),
|
||||||
border_color="white", parent=canvas.scene)
|
border_color="white", parent=canvas.scene)
|
||||||
info_button_label = scene.visuals.Text(
|
info_button_label = scene.visuals.Text(
|
||||||
text="info", color="white", font_size=16,
|
text="info", color="white", font_size=13,
|
||||||
pos=(info_btn_size[0] / 2 + 8, info_btn_size[1] / 2 + 8),
|
pos=(info_btn_size[0] / 2 + 8, info_btn_size[1] / 2 + 8),
|
||||||
anchor_x="center", anchor_y="center",
|
anchor_x="center", anchor_y="center",
|
||||||
bold=True, parent=canvas.scene)
|
bold=True, parent=canvas.scene)
|
||||||
@@ -346,7 +563,7 @@ axes_button = scene.visuals.Rectangle(
|
|||||||
radius=6, color=(0.3, 0.7, 0.3, 0.9),
|
radius=6, color=(0.3, 0.7, 0.3, 0.9),
|
||||||
border_color="white", parent=canvas.scene)
|
border_color="white", parent=canvas.scene)
|
||||||
axes_button_label = scene.visuals.Text(
|
axes_button_label = scene.visuals.Text(
|
||||||
text="axes", color="white", font_size=16,
|
text="axes", color="white", font_size=13,
|
||||||
pos=(axes_btn_size[0] / 2 + 8, axes_btn_size[1] / 2 + 8),
|
pos=(axes_btn_size[0] / 2 + 8, axes_btn_size[1] / 2 + 8),
|
||||||
anchor_x="center", anchor_y="center",
|
anchor_x="center", anchor_y="center",
|
||||||
bold=True, parent=canvas.scene)
|
bold=True, parent=canvas.scene)
|
||||||
@@ -551,12 +768,15 @@ def handle_mouse_press(event):
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
|
|
||||||
def _update_atom_positions(f_idx):
|
def _update_atom_positions(f_idx):
|
||||||
"""更新所有原子到第 f_idx 帧的位置。"""
|
"""更新所有原子到第 f_idx 帧的位置,必要时更新颜色。"""
|
||||||
if USE_MARKER:
|
if USE_MARKER:
|
||||||
marker_pos[:, 0] = DISP_ALL_X[f_idx]
|
marker_pos[:, 0] = DISP_ALL_X[f_idx]
|
||||||
marker_pos[:, 1] = DISP_ALL_Y[f_idx]
|
marker_pos[:, 1] = DISP_ALL_Y[f_idx]
|
||||||
marker_pos[:, 2] = DISP_ALL_Z[f_idx]
|
marker_pos[:, 2] = DISP_ALL_Z[f_idx]
|
||||||
balls.set_data(pos=marker_pos)
|
if FRAME_COLORS is not None:
|
||||||
|
balls.set_data(pos=marker_pos, face_color=FRAME_COLORS[f_idx])
|
||||||
|
else:
|
||||||
|
balls.set_data(pos=marker_pos)
|
||||||
else:
|
else:
|
||||||
for i in range(N_ATOMS):
|
for i in range(N_ATOMS):
|
||||||
balls[i].transform = STTransform(translate=(
|
balls[i].transform = STTransform(translate=(
|
||||||
@@ -630,10 +850,10 @@ def _load_move_camera_txt():
|
|||||||
|
|
||||||
# 先试 move_camera.txt 直读,没有则用 display.txt 缓存
|
# 先试 move_camera.txt 直读,没有则用 display.txt 缓存
|
||||||
# header 中 camera_keyframes 为空字符串表示 move_camera=0(开关关闭),跳过文件加载
|
# header 中 camera_keyframes 为空字符串表示 move_camera=0(开关关闭),跳过文件加载
|
||||||
_camera_motion_enabled = bool(h.get("camera_keyframes", ""))
|
_camera_motion_enabled = bool(config.get("camera_keyframes", ""))
|
||||||
_CAM_MOTION = _load_move_camera_txt() if _camera_motion_enabled else None
|
_CAM_MOTION = _load_move_camera_txt() if _camera_motion_enabled else None
|
||||||
if not _CAM_MOTION:
|
if not _CAM_MOTION:
|
||||||
_CAM_MOTION = json.loads(h.get("camera_keyframes", "null")) if h.get("camera_keyframes") else None
|
_CAM_MOTION = json.loads(config.get("camera_keyframes", "null")) if config.get("camera_keyframes") else None
|
||||||
if _CAM_MOTION:
|
if _CAM_MOTION:
|
||||||
_cam_center = [0.0, 0.0, 0.0]
|
_cam_center = [0.0, 0.0, 0.0]
|
||||||
_cam_elev = initial_camera["elevation"]
|
_cam_elev = initial_camera["elevation"]
|
||||||
|
|||||||
+8
-9
@@ -187,12 +187,11 @@ def run_case(config_path, runtime_base, input_dir="input", output_dir="output",
|
|||||||
disp_path = os.path.join(output_dir_abs, "display.txt")
|
disp_path = os.path.join(output_dir_abs, "display.txt")
|
||||||
|
|
||||||
# ── 自动缓存检测 ───────────────────────────────────────
|
# ── 自动缓存检测 ───────────────────────────────────────
|
||||||
# force_calc=1: 强制重新计算,忽略缓存
|
# force_calc=1: 强制重新计算,忽略缓存(仅在 step_simulate=1 时生效)
|
||||||
# force_calc=0: 尊重 step_simulate 设置,不自动覆盖
|
# force_calc=0: 尊重 step_simulate 设置
|
||||||
force_calc = int(config.get("force_calc", 0))
|
force_calc = int(config.get("force_calc", 0))
|
||||||
if force_calc:
|
if force_calc and config.get("step_simulate", 1):
|
||||||
print(f"[run] force_calc=1,跳过缓存,强制重新计算")
|
print(f"[run] force_calc=1,跳过缓存,强制重新计算")
|
||||||
config["step_simulate"] = 1
|
|
||||||
config["step_sample"] = 1
|
config["step_sample"] = 1
|
||||||
elif config.get("step_simulate", 1):
|
elif config.get("step_simulate", 1):
|
||||||
# step_simulate=1 且 force_calc=0 → 按用户要求执行计算
|
# step_simulate=1 且 force_calc=0 → 按用户要求执行计算
|
||||||
@@ -225,12 +224,12 @@ def run_case(config_path, runtime_base, input_dir="input", output_dir="output",
|
|||||||
print(f"[run] 没有可用的缓存输出,但 step_simulate=0,将跳过模拟")
|
print(f"[run] 没有可用的缓存输出,但 step_simulate=0,将跳过模拟")
|
||||||
|
|
||||||
# 2. 运行物理模拟 → output/trajectory.txt
|
# 2. 运行物理模拟 → output/trajectory.txt
|
||||||
|
_engine_aliases = {"c++": "cpp", "f90": "fortran", "f": "fortran"}
|
||||||
|
engine = _engine_aliases.get(
|
||||||
|
str(config.get("engine", "python")).lower(),
|
||||||
|
str(config.get("engine", "python")).lower()
|
||||||
|
)
|
||||||
if config.get("step_simulate", 1):
|
if config.get("step_simulate", 1):
|
||||||
_engine_aliases = {"c++": "cpp", "f90": "fortran", "f": "fortran"}
|
|
||||||
engine = _engine_aliases.get(
|
|
||||||
str(config.get("engine", "python")).lower(),
|
|
||||||
str(config.get("engine", "python")).lower()
|
|
||||||
)
|
|
||||||
total_steps = config["NT"]
|
total_steps = config["NT"]
|
||||||
record_steps = total_steps - (config.get("warmup_steps") or 0)
|
record_steps = total_steps - (config.get("warmup_steps") or 0)
|
||||||
print(f"[run] 开始计算 总步数={total_steps} 记录步数={record_steps} DT={config['DT']}")
|
print(f"[run] 开始计算 总步数={total_steps} 记录步数={record_steps} DT={config['DT']}")
|
||||||
|
|||||||
@@ -212,6 +212,10 @@ def main():
|
|||||||
"number_of_frames": str(n_frames),
|
"number_of_frames": str(n_frames),
|
||||||
"number_of_particles": str(len(atom_ids)),
|
"number_of_particles": str(len(atom_ids)),
|
||||||
"use_marker": str(use_marker),
|
"use_marker": str(use_marker),
|
||||||
|
"display_color": json.dumps(p.get("display_color",
|
||||||
|
{"x":[0,[1.0,0.0,0.0]],"y":[0,[0.0,1.0,0.0]],"z":[0,[0.0,0.0,1.0]],
|
||||||
|
"xy":[0,[1.0,1.0,0.0]],"yz":[0,[0.0,1.0,1.0]],"zx":[0,[1.0,0.0,1.0]],
|
||||||
|
"xyz":[1,[1.0,1.0,1.0]]})),
|
||||||
"ball_radius": str(ball_radius),
|
"ball_radius": str(ball_radius),
|
||||||
"ball_color_r": str(ball_color[0]),
|
"ball_color_r": str(ball_color[0]),
|
||||||
"ball_color_g": str(ball_color[1]),
|
"ball_color_g": str(ball_color[1]),
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+17
-4
@@ -169,7 +169,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<h1>Dynamics 示例案例 <small>v2.0</small></h1>
|
<h1>Dynamics 示例案例 <small>v2.1</small></h1>
|
||||||
<p class="subtitle">10 个从简单到复杂的物理模拟案例,展示分子动力学模拟框架的多种应用场景</p>
|
<p class="subtitle">10 个从简单到复杂的物理模拟案例,展示分子动力学模拟框架的多种应用场景</p>
|
||||||
|
|
||||||
<h2>📋 案例总览</h2>
|
<h2>📋 案例总览</h2>
|
||||||
@@ -412,7 +412,12 @@
|
|||||||
<h3>配置</h3>
|
<h3>配置</h3>
|
||||||
<p style="color:var(--text-dim); font-size:0.875rem;">
|
<p style="color:var(--text-dim); font-size:0.875rem;">
|
||||||
每个案例的 <code>input/input.txt</code> 可配置物理参数、力开关、算法、引擎、渲染方式等。
|
每个案例的 <code>input/input.txt</code> 可配置物理参数、力开关、算法、引擎、渲染方式等。
|
||||||
从 case06 起支持 <code>save_trajectory</code> 开关、摄像机初始位置、<code>display_amp</code> 视觉放大等高级功能。
|
</p>
|
||||||
|
|
||||||
|
<h3>引擎架构</h3>
|
||||||
|
<p style="color:var(--text-dim); font-size:0.875rem;">
|
||||||
|
外部引擎(C / C++ / Fortran)以 <strong>DLL 方式</strong> 运行,主程序通过 ctypes 在进程内直接调用,不启动子进程。DLL 预编译在 <code>engines/release/</code> 中,源码位于 <code>engines/src/{c,cpp,fortran}/</code>。重新编译:
|
||||||
|
<code>cd engines/src/c && make dll</code>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -421,10 +426,18 @@
|
|||||||
<pre style="font-size:0.825rem; color:var(--text-dim); line-height:1.5;">
|
<pre style="font-size:0.825rem; color:var(--text-dim); line-height:1.5;">
|
||||||
dynamics/
|
dynamics/
|
||||||
├── dynamics.py # 统一运行入口
|
├── dynamics.py # 统一运行入口
|
||||||
├── compute.py # Python 物理引擎
|
├── compute.py # 物理引擎 + 显示数据生成
|
||||||
├── draw.py # VisPy 3D 动画
|
├── draw.py # VisPy 3D 动画
|
||||||
├── plot_wave.py # 波形能量图
|
├── plot_wave.py # 波形能量图
|
||||||
├── engines/ # C / C++ / Fortran 引擎
|
├── .gitattributes # DLL/二进制文件保护
|
||||||
|
├── engines/
|
||||||
|
│ ├── engine_dll.py # DLL 加载器(ctypes)
|
||||||
|
│ ├── python/ # Python 引擎(dynamics_lib.py)
|
||||||
|
│ ├── release/ # 预编译 DLL(C / C++ / Fortran)
|
||||||
|
│ └── src/ # 引擎源码
|
||||||
|
│ ├── c/ # C 源码 + Makefile → dynamics_c.dll
|
||||||
|
│ ├── cpp/ # C++ 源码 + Makefile → dynamics_cpp.dll
|
||||||
|
│ └── fortran/ # Fortran 源码 + Makefile → dynamics_f90.dll
|
||||||
├── examples/ # 案例目录
|
├── examples/ # 案例目录
|
||||||
│ ├── case01/ ~ case10/
|
│ ├── case01/ ~ case10/
|
||||||
└── output/ # 默认输出目录
|
└── output/ # 默认输出目录
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
"""
|
||||||
|
为指定案例添加次紧邻键 (k2, k=100, r0=1.41421356)。
|
||||||
|
|
||||||
|
用法: python add_k2.py case16
|
||||||
|
python add_k2.py case16 case17 case18
|
||||||
|
python add_k2.py --all
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def add_k2(case_dir):
|
||||||
|
coord_path = os.path.join(case_dir, "input", "coord.txt")
|
||||||
|
conn_path = os.path.join(case_dir, "input", "connection.txt")
|
||||||
|
bond_path = os.path.join(case_dir, "input", "bond.txt")
|
||||||
|
|
||||||
|
if not os.path.exists(coord_path):
|
||||||
|
print(f" [跳过] {case_dir}: 找不到 coord.txt")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 读取 coord.txt 获取网格尺寸
|
||||||
|
with open(coord_path, "r", encoding="utf-8") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
n_atoms = len(lines) - 1 # 去掉表头
|
||||||
|
N = int(n_atoms ** 0.5)
|
||||||
|
if N * N != n_atoms:
|
||||||
|
print(f" [跳过] {case_dir}: 非正方形网格 (n_atoms={n_atoms})")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f" {case_dir}: {N}x{N} 网格")
|
||||||
|
|
||||||
|
# 读取现有 connection.txt,检查是否已有 k2
|
||||||
|
has_k2 = False
|
||||||
|
if os.path.exists(conn_path):
|
||||||
|
with open(conn_path, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
if "k2" in line:
|
||||||
|
has_k2 = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if has_k2:
|
||||||
|
print(f" k2 已存在,跳过 connection.txt")
|
||||||
|
else:
|
||||||
|
# 追加 k2 键到 connection.txt
|
||||||
|
with open(conn_path, "a", encoding="utf-8") as f:
|
||||||
|
cnt = 0
|
||||||
|
for row in range(N):
|
||||||
|
for col in range(N):
|
||||||
|
id1 = row * N + col + 1
|
||||||
|
if col + 1 < N and row + 1 < N:
|
||||||
|
f.write(f"{id1} {(row + 1) * N + (col + 1) + 1} k2\n")
|
||||||
|
cnt += 1
|
||||||
|
if col - 1 >= 0 and row + 1 < N:
|
||||||
|
f.write(f"{id1} {(row + 1) * N + (col - 1) + 1} k2\n")
|
||||||
|
cnt += 1
|
||||||
|
print(f" connection.txt: 追加 {cnt} 条 k2 键")
|
||||||
|
|
||||||
|
# 检查 bond.txt 是否有 k2
|
||||||
|
has_bond = False
|
||||||
|
if os.path.exists(bond_path):
|
||||||
|
with open(bond_path, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith("k2"):
|
||||||
|
has_bond = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if has_bond:
|
||||||
|
print(f" bond.txt: k2 已存在")
|
||||||
|
else:
|
||||||
|
with open(bond_path, "a", encoding="utf-8") as f:
|
||||||
|
f.write("k2 100.0 1.41421356\n")
|
||||||
|
print(f" bond.txt: 追加 k2 定义")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
targets = []
|
||||||
|
if "--all" in sys.argv:
|
||||||
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
for d in sorted(os.listdir(base)):
|
||||||
|
if d.startswith("case") and os.path.isdir(os.path.join(base, d)):
|
||||||
|
targets.append(os.path.join(base, d))
|
||||||
|
else:
|
||||||
|
for arg in sys.argv[1:]:
|
||||||
|
if arg.startswith("--"):
|
||||||
|
continue
|
||||||
|
p = arg if os.path.isabs(arg) else os.path.join(os.path.dirname(os.path.abspath(__file__)), arg)
|
||||||
|
targets.append(p)
|
||||||
|
|
||||||
|
for t in targets:
|
||||||
|
add_k2(t)
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
# ── 流程控制 ──────────────────────────────────
|
# ── 流程控制 ──────────────────────────────────
|
||||||
# 每步用 0/1 单独开关,1=执行,0=跳过
|
# 每步用 0/1 单独开关,1=执行,0=跳过
|
||||||
# 依赖关系:抽帧依赖模拟结果,绘图依赖模拟+抽帧
|
# 依赖关系:抽帧依赖模拟结果,绘图依赖模拟+抽帧
|
||||||
step_simulate: 1 # 运行物理模拟 → output/trajectory.txt
|
step_simulate: 0 # 运行物理模拟 → output/trajectory.txt
|
||||||
step_sample: 0 # 抽帧 → output/display.txt
|
step_sample: 0 # 抽帧 → output/display.txt
|
||||||
step_plot: 0 # 绘制轨迹/能量图 → output/trajectory_plots.png
|
step_plot: 0 # 绘制轨迹/能量图 → output/trajectory_plots.png
|
||||||
step_animation: 1 # 自动播放 VisPy 3D 动画窗口(需安装 vispy)
|
step_animation: 1 # 自动播放 VisPy 3D 动画窗口(需安装 vispy)
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
5101 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# 格式:YAML
|
||||||
|
# 用法:python run_dynamics.py
|
||||||
|
|
||||||
|
# ── 流程控制 ──────────────────────────────────
|
||||||
|
step_simulate: 1 # 运行物理模拟
|
||||||
|
step_sample: 0 # 重新抽帧,默认0=不执行
|
||||||
|
step_plot: 0 # 绘制轨迹/能量图
|
||||||
|
step_animation: 1 # 自动播放 VisPy 3D 动画窗口
|
||||||
|
step_plot_wave: 0 # 绘制波形能量动画
|
||||||
|
force_calc: 1 # 强制重新计算
|
||||||
|
|
||||||
|
# ── 文件保存 ──────────────────────────────────
|
||||||
|
save_trajectory: 0 # 0=不保留完整轨迹文件
|
||||||
|
|
||||||
|
# ── 计算引擎 ──────────────────────────────────
|
||||||
|
engine: c
|
||||||
|
|
||||||
|
# ── 盒子 ──────────────────────────────────────
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
# ── 初始构型 ──────────────────────────────────
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
|
||||||
|
# 绘图/动画展示的原子序号
|
||||||
|
plot_atom: 5101 # 中心原子 (0,0)
|
||||||
|
|
||||||
|
# ── 物理参数 ──────────────────────────────────
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
# ── 力开关 ────────────────────────────────────
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
# ── 数值算法 ──────────────────────────────────
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
# ── 步骤控制 ──────────────────────────────────
|
||||||
|
warmup_steps: 0 # 受迫波动,无需预热
|
||||||
|
T_total: 100.0
|
||||||
|
NSTEP: 10
|
||||||
|
DT: 0.01
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
# ── 渲染方式 ──────────────────────────────────
|
||||||
|
use_marker: 1
|
||||||
|
|
||||||
|
# ── 位移着色 ──────────────────────────────────
|
||||||
|
display_color: {
|
||||||
|
x : [0, [255, 0, 0]],
|
||||||
|
y : [0, [ 0, 255, 0]],
|
||||||
|
z : [1, [ 0, 0, 255]],
|
||||||
|
xy : [0, [255, 255, 0]],
|
||||||
|
yz : [0, [ 0, 255, 255]],
|
||||||
|
zx : [0, [255, 0, 255]],
|
||||||
|
xyz : [0, [ 0, 0, 0]],
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 显示参数 ──────────────────────────────────
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.20
|
||||||
|
ball_color_g: 0.60
|
||||||
|
ball_color_b: 0.90
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
# ── 摄像机 ────────────────────────────────────
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case11 — 2D grid (61x61 atomic mesh).
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case11")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
3081 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7121 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# case12 — 二维网格两点干涉(双点源 z 方向驱动)
|
||||||
|
# 驱动点: (0,-10) 和 (0,10),波从两点向外传播,在中心区域干涉
|
||||||
|
|
||||||
|
# ── 流程控制 ──────────────────────────────────
|
||||||
|
step_simulate: 1 # 运行物理模拟
|
||||||
|
step_sample: 0 # 重新抽帧,默认0=不执行
|
||||||
|
step_plot: 0 # 绘制轨迹/能量图
|
||||||
|
step_animation: 1 # 自动播放 VisPy 3D 动画窗口
|
||||||
|
step_plot_wave: 0 # 绘制波形能量动画
|
||||||
|
force_calc: 1 # 强制重新计算
|
||||||
|
|
||||||
|
# ── 文件保存 ──────────────────────────────────
|
||||||
|
save_trajectory: 0
|
||||||
|
|
||||||
|
# ── 计算引擎 ──────────────────────────────────
|
||||||
|
engine: c
|
||||||
|
|
||||||
|
# ── 盒子 ──────────────────────────────────────
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
# ── 初始构型 ──────────────────────────────────
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
plot_atom: 5101 # 中心区域用于信息显示
|
||||||
|
|
||||||
|
# ── 物理参数 ──────────────────────────────────
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
# ── 步骤控制 ──────────────────────────────────
|
||||||
|
warmup_steps: 0
|
||||||
|
T_total: 100.0
|
||||||
|
NSTEP: 500
|
||||||
|
DT: 0.001
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
# ── 渲染/着色 ─────────────────────────────────
|
||||||
|
use_marker: 1
|
||||||
|
display_color: {
|
||||||
|
x : [0, [255, 0, 0]],
|
||||||
|
y : [0, [ 0, 255, 0]],
|
||||||
|
z : [0, [ 0, 0, 255]],
|
||||||
|
xy : [0, [255, 255, 0]],
|
||||||
|
yz : [0, [ 0, 255, 255]],
|
||||||
|
zx : [0, [255, 0, 255]],
|
||||||
|
xyz : [1, [255, 255, 255]],
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 显示参数 ──────────────────────────────────
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.20
|
||||||
|
ball_color_g: 0.60
|
||||||
|
ball_color_b: 0.90
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
# ── 摄像机 ────────────────────────────────────
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case12 — 2D grid dual source interference.
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case11")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
102 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
203 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
304 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
405 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
506 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
607 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
708 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
809 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
910 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1011 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1112 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1213 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1314 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1415 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1516 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1617 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1718 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1819 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1920 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2021 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2122 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2223 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2324 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2425 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2526 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2627 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2728 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2829 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2930 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3031 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3132 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3233 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3334 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3435 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3536 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3637 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3738 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3839 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3940 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4041 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4142 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4243 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4344 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4445 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4546 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4647 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4748 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4849 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4950 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5051 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5152 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5253 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5354 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5455 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5556 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5657 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5758 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5859 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5960 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6061 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6162 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6263 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6364 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6465 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6566 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6667 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6768 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6869 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6970 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7071 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7172 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7273 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7374 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7475 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7576 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7677 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7778 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7879 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7980 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8081 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8182 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8283 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8384 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8485 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8586 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8687 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8788 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8889 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8990 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9091 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9192 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9293 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9394 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9495 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9596 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9697 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9798 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9899 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10000 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10101 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# case13 — 二维网格平面波(左边界驱动,右边界吸收)
|
||||||
|
# 左边界全部 101 原子齐振驱动 → 波从左向右传播 → 右边界全固定
|
||||||
|
|
||||||
|
step_simulate: 1
|
||||||
|
step_sample: 0
|
||||||
|
step_plot: 0
|
||||||
|
step_animation: 1
|
||||||
|
step_plot_wave: 0
|
||||||
|
force_calc: 1
|
||||||
|
|
||||||
|
save_trajectory: 0
|
||||||
|
|
||||||
|
engine: c
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
plot_atom: 51 # 左边界中间原子用于信息显示
|
||||||
|
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
warmup_steps: 0
|
||||||
|
T_total: 200.0
|
||||||
|
NSTEP: 100
|
||||||
|
DT: 0.01
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
# ── 渲染/着色 ─────────────────────────────────
|
||||||
|
use_marker: 1
|
||||||
|
display_color: {
|
||||||
|
x : [1, [255, 0, 0]],
|
||||||
|
y : [1, [ 0, 255, 0]],
|
||||||
|
z : [1, [ 0, 0, 255]],
|
||||||
|
xy : [0, [255, 255, 0]],
|
||||||
|
yz : [0, [ 0, 255, 255]],
|
||||||
|
zx : [0, [255, 0, 255]],
|
||||||
|
xyz : [0, [255, 255, 255]],
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.20
|
||||||
|
ball_color_g: 0.60
|
||||||
|
ball_color_b: 0.90
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case13 — 2D grid (61x61 atomic mesh).
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case13")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
102 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
203 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
304 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
405 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
506 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
607 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
708 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
809 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
910 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1011 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1112 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1213 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1314 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1415 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1516 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1617 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1718 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1819 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1920 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2021 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2122 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2223 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2324 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2425 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2526 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2627 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2728 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2829 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2930 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3031 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3132 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3233 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3334 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3435 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3536 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3637 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3738 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3839 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3940 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4041 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4142 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4243 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4344 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4445 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4546 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4647 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4748 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4849 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4950 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5051 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5152 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5253 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5354 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5455 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5556 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5657 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5758 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5859 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5960 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6061 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6162 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6263 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6364 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6465 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6566 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6667 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6768 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6869 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6970 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7071 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7172 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7273 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7374 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7475 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7576 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7677 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7778 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7879 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7980 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8081 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8182 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8283 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8384 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8485 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8586 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8687 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8788 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8889 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8990 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9091 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9192 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9293 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9394 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9495 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9596 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9697 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9798 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9899 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10000 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10101 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# case14 — 双缝干涉实验
|
||||||
|
# 左边界平面波 → 双缝势垒 (x=0) → 干涉图案 → 右边界反射
|
||||||
|
|
||||||
|
step_simulate: 1
|
||||||
|
step_sample: 0
|
||||||
|
step_plot: 0
|
||||||
|
step_animation: 1
|
||||||
|
step_plot_wave: 0
|
||||||
|
force_calc: 1
|
||||||
|
|
||||||
|
save_trajectory: 0
|
||||||
|
|
||||||
|
engine: c
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
plot_atom: 51
|
||||||
|
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
warmup_steps: 0
|
||||||
|
T_total: 100.0
|
||||||
|
NSTEP: 100
|
||||||
|
DT: 0.001
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
use_marker: 1
|
||||||
|
display_color: {
|
||||||
|
x : [0, [255, 0, 0]],
|
||||||
|
y : [0, [ 0, 255, 0]],
|
||||||
|
z : [1, [ 0, 0, 255]],
|
||||||
|
xy : [0, [255, 255, 0]],
|
||||||
|
yz : [0, [ 0, 255, 255]],
|
||||||
|
zx : [0, [255, 0, 255]],
|
||||||
|
xyz : [0, [255, 255, 255]],
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.20
|
||||||
|
ball_color_g: 0.60
|
||||||
|
ball_color_b: 0.90
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case14 — 2D grid (61x61 atomic mesh).
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case14")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
102 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
203 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
304 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
405 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
506 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
607 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
708 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
809 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
910 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1011 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1112 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1213 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1314 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1415 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1516 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1617 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1718 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1819 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
1920 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2021 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2122 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2223 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2324 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2425 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2526 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2627 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2728 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2829 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
2930 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3031 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3132 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3233 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3334 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3435 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3536 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3637 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3738 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3839 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
3940 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4041 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4142 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4243 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4344 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4445 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4546 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4647 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4748 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4849 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
4950 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5051 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5152 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5253 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5354 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5455 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5556 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5657 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5758 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5859 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
5960 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6061 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6162 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6263 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6364 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6465 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6566 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6667 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6768 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6869 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
6970 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7071 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7172 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7273 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7374 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7475 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7576 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7677 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7778 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7879 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
7980 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8081 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8182 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8283 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8384 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8485 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8586 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8687 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8788 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8889 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
8990 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9091 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9192 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9293 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9394 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9495 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9596 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9697 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9798 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
9899 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
10000 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
|
10101 0.5 0 0 0.05 0 0 90 90 90 all
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# case15 — 双缝干涉(x方向振动)
|
||||||
|
# 所有粒子 y/z 固定,仅 x 方向振动
|
||||||
|
# 左边界 x 方向驱动 → 双缝势垒 → x方向波干涉
|
||||||
|
|
||||||
|
step_simulate: 1
|
||||||
|
step_sample: 0
|
||||||
|
step_plot: 0
|
||||||
|
step_animation: 1
|
||||||
|
step_plot_wave: 0
|
||||||
|
force_calc: 1
|
||||||
|
|
||||||
|
save_trajectory: 0
|
||||||
|
|
||||||
|
engine: c
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
plot_atom: 51
|
||||||
|
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
warmup_steps: 0
|
||||||
|
T_total: 100.0
|
||||||
|
NSTEP: 100
|
||||||
|
DT: 0.001
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
use_marker: 1
|
||||||
|
display_color: {
|
||||||
|
x : [1, [255, 0, 0]],
|
||||||
|
y : [0, [ 0, 255, 0]],
|
||||||
|
z : [0, [ 0, 0, 255]],
|
||||||
|
xy : [0, [255, 255, 0]],
|
||||||
|
yz : [0, [ 0, 255, 255]],
|
||||||
|
zx : [0, [255, 0, 255]],
|
||||||
|
xyz : [0, [255, 255, 255]],
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.90
|
||||||
|
ball_color_g: 0.20
|
||||||
|
ball_color_b: 0.20
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case15 — 2D grid (61x61 atomic mesh).
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case15")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
|
k2 100.0 1.41421356
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
5101 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# 格式:YAML
|
||||||
|
# 用法:python run_dynamics.py
|
||||||
|
|
||||||
|
# ── 流程控制 ──────────────────────────────────
|
||||||
|
step_simulate: 1 # 运行物理模拟
|
||||||
|
step_sample: 0 # 重新抽帧,默认0=不执行
|
||||||
|
step_plot: 0 # 绘制轨迹/能量图
|
||||||
|
step_animation: 1 # 自动播放 VisPy 3D 动画窗口
|
||||||
|
step_plot_wave: 0 # 绘制波形能量动画
|
||||||
|
force_calc: 1 # 强制重新计算
|
||||||
|
|
||||||
|
# ── 文件保存 ──────────────────────────────────
|
||||||
|
save_trajectory: 0 # 0=不保留完整轨迹文件
|
||||||
|
|
||||||
|
# ── 计算引擎 ──────────────────────────────────
|
||||||
|
engine: c
|
||||||
|
|
||||||
|
# ── 盒子 ──────────────────────────────────────
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
# ── 初始构型 ──────────────────────────────────
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
|
||||||
|
# 绘图/动画展示的原子序号
|
||||||
|
plot_atom: 5101 # 中心原子 (0,0)
|
||||||
|
|
||||||
|
# ── 物理参数 ──────────────────────────────────
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
# ── 力开关 ────────────────────────────────────
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
# ── 数值算法 ──────────────────────────────────
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
# ── 步骤控制 ──────────────────────────────────
|
||||||
|
warmup_steps: 0 # 受迫波动,无需预热
|
||||||
|
T_total: 100.0
|
||||||
|
NSTEP: 10
|
||||||
|
DT: 0.01
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
# ── 渲染方式 ──────────────────────────────────
|
||||||
|
use_marker: 1
|
||||||
|
|
||||||
|
# ── 位移着色 ──────────────────────────────────
|
||||||
|
display_color: {
|
||||||
|
x : [0, [255, 0, 0]],
|
||||||
|
y : [0, [ 0, 255, 0]],
|
||||||
|
z : [1, [ 0, 0, 255]],
|
||||||
|
xy : [0, [255, 255, 0]],
|
||||||
|
yz : [0, [ 0, 255, 255]],
|
||||||
|
zx : [0, [255, 0, 255]],
|
||||||
|
xyz : [0, [ 0, 0, 0]],
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 显示参数 ──────────────────────────────────
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.20
|
||||||
|
ball_color_g: 0.60
|
||||||
|
ball_color_b: 0.90
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
# ── 摄像机 ────────────────────────────────────
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case11 — 2D grid (61x61 atomic mesh).
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case11")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
|
k2 100.0 1.41421356
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
3081 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7121 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# case12 — 二维网格两点干涉(双点源 z 方向驱动)
|
||||||
|
# 驱动点: (0,-10) 和 (0,10),波从两点向外传播,在中心区域干涉
|
||||||
|
|
||||||
|
# ── 流程控制 ──────────────────────────────────
|
||||||
|
step_simulate: 1 # 运行物理模拟
|
||||||
|
step_sample: 0 # 重新抽帧,默认0=不执行
|
||||||
|
step_plot: 0 # 绘制轨迹/能量图
|
||||||
|
step_animation: 1 # 自动播放 VisPy 3D 动画窗口
|
||||||
|
step_plot_wave: 0 # 绘制波形能量动画
|
||||||
|
force_calc: 1 # 强制重新计算
|
||||||
|
|
||||||
|
# ── 文件保存 ──────────────────────────────────
|
||||||
|
save_trajectory: 0
|
||||||
|
|
||||||
|
# ── 计算引擎 ──────────────────────────────────
|
||||||
|
engine: c
|
||||||
|
|
||||||
|
# ── 盒子 ──────────────────────────────────────
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
# ── 初始构型 ──────────────────────────────────
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
plot_atom: 5101 # 中心区域用于信息显示
|
||||||
|
|
||||||
|
# ── 物理参数 ──────────────────────────────────
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
# ── 步骤控制 ──────────────────────────────────
|
||||||
|
warmup_steps: 0
|
||||||
|
T_total: 100.0
|
||||||
|
NSTEP: 500
|
||||||
|
DT: 0.001
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
# ── 渲染/着色 ─────────────────────────────────
|
||||||
|
use_marker: 1
|
||||||
|
display_color: {
|
||||||
|
x : [0, [255, 0, 0]],
|
||||||
|
y : [0, [ 0, 255, 0]],
|
||||||
|
z : [1, [ 0, 0, 255]],
|
||||||
|
xy : [1, [255, 0, 0]],
|
||||||
|
yz : [0, [ 0, 255, 0]],
|
||||||
|
zx : [0, [ 0, 0, 255]],
|
||||||
|
xyz : [0, [255, 255, 255]],
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 显示参数 ──────────────────────────────────
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.20
|
||||||
|
ball_color_g: 0.60
|
||||||
|
ball_color_b: 0.90
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
# ── 摄像机 ────────────────────────────────────
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case12 — 2D grid dual source interference.
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case11")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
|
k2 100.0 1.41421356
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
102 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
203 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
304 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
405 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
506 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
607 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
708 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
809 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
910 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1011 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1112 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1213 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1314 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1415 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1516 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1617 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1718 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1819 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1920 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2021 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2122 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2223 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2324 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2425 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2526 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2627 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2728 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2829 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2930 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3031 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3132 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3233 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3334 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3435 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3536 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3637 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3738 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3839 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3940 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4041 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4142 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4243 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4344 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4445 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4546 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4647 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4748 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4849 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4950 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5051 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5152 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5253 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5354 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5455 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5556 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5657 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5758 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5859 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5960 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6061 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6162 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6263 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6364 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6465 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6566 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6667 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6768 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6869 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6970 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7071 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7172 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7273 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7374 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7475 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7576 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7677 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7778 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7879 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7980 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8081 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8182 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8283 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8384 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8485 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8586 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8687 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8788 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8889 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8990 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9091 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9192 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9293 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9394 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9495 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9596 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9697 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9798 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9899 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10000 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10101 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# case13 — 二维网格平面波(左边界驱动,右边界吸收)
|
||||||
|
# 左边界全部 101 原子齐振驱动 → 波从左向右传播 → 右边界全固定
|
||||||
|
|
||||||
|
step_simulate: 1
|
||||||
|
step_sample: 0
|
||||||
|
step_plot: 0
|
||||||
|
step_animation: 1
|
||||||
|
step_plot_wave: 0
|
||||||
|
force_calc: 1
|
||||||
|
|
||||||
|
save_trajectory: 0
|
||||||
|
|
||||||
|
engine: c
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
plot_atom: 51 # 左边界中间原子用于信息显示
|
||||||
|
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
warmup_steps: 0
|
||||||
|
T_total: 200.0
|
||||||
|
NSTEP: 100
|
||||||
|
DT: 0.01
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
# ── 渲染/着色 ─────────────────────────────────
|
||||||
|
use_marker: 1
|
||||||
|
display_color: {
|
||||||
|
x : [1, [255, 0, 0]],
|
||||||
|
y : [1, [ 0, 255, 0]],
|
||||||
|
z : [1, [ 0, 0, 255]],
|
||||||
|
xy : [0, [255, 255, 0]],
|
||||||
|
yz : [0, [ 0, 255, 255]],
|
||||||
|
zx : [0, [255, 0, 255]],
|
||||||
|
xyz : [0, [255, 255, 255]],
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 0.20
|
||||||
|
ball_color_g: 0.60
|
||||||
|
ball_color_b: 0.90
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case13 — 2D grid (61x61 atomic mesh).
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case13")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# case06: 一维原子链横波模拟
|
||||||
|
|
||||||
|
60 个原子沿 x 轴排列,相邻原子用弹簧连接。原子 1 受 z 方向驱动力作用,产生沿链传播的横波。
|
||||||
|
|
||||||
|
## 物理设定
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 原子数 | 120 |
|
||||||
|
| 排列 | 沿 x 轴等间距排列,间距为 1 |
|
||||||
|
| 约束 | 原子**沿 z 方向自由振动**(fix_x=1, fix_y=1, fix_z=0),x, y 锁定 |
|
||||||
|
| 弹簧 | 劲度系数 k=1.0,原长 L₀=1.0 |
|
||||||
|
| 重力 | 无 |
|
||||||
|
| 万有引力 | 无 |
|
||||||
|
| 阻尼 | 无 |
|
||||||
|
| 驱动力 | 原子 1(z 方向驱动) |
|
||||||
|
| 算法 | leapfrog(蛙跳法,能量守恒) |
|
||||||
|
|
||||||
|
## 驱动力
|
||||||
|
|
||||||
|
原子 1 的位置由 `input/driver.txt` 中的驱动力公式决定:
|
||||||
|
|
||||||
|
```math
|
||||||
|
z(t) = A_z \cdot \cos(2\pi f_z t + \phi_z)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前参数:A_z = 0.5, f_z = 0.1 Hz, φ_z = 90°, period = all(全程驱动)。
|
||||||
|
|
||||||
|
## 动力学行为
|
||||||
|
|
||||||
|
原子 1 沿 z 方向的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的**横波**。由于 z 方向的振动是横向的,弹簧大部分张力在 x 方向,z 方向的有效刚度是非线性的——等效于一个三次方恢复力(FPU 型非线性),因此波速较慢。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples/case06
|
||||||
|
python run_dynamics.py
|
||||||
|
```
|
||||||
|
|
||||||
|
配置参数详见 `input/input.txt`,驱动力定义见 `input/driver.txt`,完整文档见 `doc/index.html`。
|
||||||
@@ -0,0 +1,477 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>case06 — 一维原子链驱动力学模拟 | 物理原理 & 使用文档</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #f8f9fa;
|
||||||
|
--card: #fff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-light: #dbeafe;
|
||||||
|
--code-bg: #1e293b;
|
||||||
|
--code-text: #e2e8f0;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--muted: #64748b;
|
||||||
|
}
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Header ── */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 56px 24px 48px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.hero h1 { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; }
|
||||||
|
.hero .subtitle {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Layout ── */
|
||||||
|
.container { max-width: 820px; margin: 0 auto; padding: 32px 20px; }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { margin-bottom: 10px; }
|
||||||
|
ul, ol { padding-left: 22px; }
|
||||||
|
strong { color: var(--accent); }
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.card {
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Formula / Code blocks ── */
|
||||||
|
.formula {
|
||||||
|
background: var(--card);
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
padding: 14px 20px;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-family: "Times New Roman", "STIX", serif;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: var(--accent-light);
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--code-text);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 14px 0;
|
||||||
|
}
|
||||||
|
pre .cm { color: #94a3b8; font-style: italic; } /* comment */
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 14px 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
th { background: var(--accent-light); font-weight: 600; }
|
||||||
|
|
||||||
|
/* ── TOC ── */
|
||||||
|
.toc { counter-reset: toc; }
|
||||||
|
.toc li { counter-increment: toc; list-style: none; margin-bottom: 6px; }
|
||||||
|
.toc li::before { content: counter(toc) ". "; font-weight: 600; color: var(--accent); }
|
||||||
|
.toc a { color: var(--accent); text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* ── Flow diagram ── */
|
||||||
|
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin: 16px 0; }
|
||||||
|
.flow-step {
|
||||||
|
background: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.flow-arrow { color: var(--muted); font-size: 1.2rem; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.hero h1 { font-size: 1.5rem; }
|
||||||
|
.flow { flex-direction: column; }
|
||||||
|
.flow-arrow { transform: rotate(90deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<header class="hero">
|
||||||
|
<h1>一维原子链驱动力学模拟</h1>
|
||||||
|
<p class="subtitle">120 个原子沿 x 轴排列 · 弹簧连接 · z 方向受迫振动</p>
|
||||||
|
<span class="badge">case06 · examples/case06</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- TOC -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section>
|
||||||
|
<h2>目录</h2>
|
||||||
|
<ol class="toc">
|
||||||
|
<li><a href="#physics">物理原理</a></li>
|
||||||
|
<li><a href="#algorithm">数值算法</a></li>
|
||||||
|
<li><a href="#driver">驱动力模型</a></li>
|
||||||
|
<li><a href="#usage">使用方法</a></li>
|
||||||
|
<li><a href="#params">参数参考</a></li>
|
||||||
|
<li><a href="#files">文件结构</a></li>
|
||||||
|
<li><a href="#troubleshoot">常见问题</a></li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 1. Physics -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="physics">
|
||||||
|
<h2>一、物理原理</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.1 一维原子链</h3>
|
||||||
|
<p>120 个原子沿 <strong>x 轴</strong> 等间距排列,原子间距为 1。相邻原子之间用 <strong>理想弹簧</strong> 连接,弹簧的劲度系数 <em>k</em> = 1.0,原长 <em>L</em>₀ = 1.0(与原子间距一致,初始状态弹簧无拉伸)。</p>
|
||||||
|
<p>每个原子被限制在 <strong>z 方向</strong> 自由振动,x 和 y 方向锁定(<code>fix_x=1, fix_y=1, fix_z=0</code>)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.2 弹簧力(胡克定律)</h3>
|
||||||
|
<p>当原子 <em>i</em> 和 <em>j</em> 之间有弹簧连接时,原子 <em>i</em> 受到的弹簧力为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>F</strong> = −<em>k</em> · (<em>d</em> − <em>L</em>₀) · <strong>u</strong><sub><em>ij</em></sub>
|
||||||
|
</div>
|
||||||
|
<p>其中 <em>d</em> = |<strong>r</strong><sub><em>j</em></sub> − <strong>r</strong><sub><em>i</em></sub>| 为两原子间距离,<strong>u</strong><sub><em>ij</em></sub> 为从 <em>i</em> 指向 <em>j</em> 的单位向量。由于原子只在 z 方向振动,弹簧在 z 方向的分量是 <strong>几何非线性</strong> 的——对于小振幅近似,z 方向等效于一个三次方恢复力(FPU 型非线性)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.3 运动方程</h3>
|
||||||
|
<p>对于第 <em>i</em> 个自由原子(非受驱),牛顿第二定律给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<em>m</em> · <strong>a</strong><sub><em>i</em></sub> = <strong>F</strong><sub><em>i</em></sub><sup>spring</sup> + <strong>F</strong><sub><em>i</em></sub><sup>driving</sup>
|
||||||
|
</div>
|
||||||
|
<p>本案例中 <strong>唯一的外力</strong> 来自驱动力(仅施加于原子 1)。无重力、无万有引力、无阻尼,系统总能量守恒。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>1.4 波传播</h3>
|
||||||
|
<p>原子 1 的受迫振动通过弹簧逐次传递给相邻原子,形成沿链传播的 <strong>横波</strong>。由于横向振动的几何非线性(弹簧大部分张力在 x 方向,z 方向的有效刚度远小于 1),波的传播速度较慢,且高阶频率成分会在链中产生复杂的非线性动力学行为(类似 FPU 回波现象)。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 2. Algorithm -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="algorithm">
|
||||||
|
<h2>二、数值算法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.1 蛙跳法(Leapfrog / Velocity-Verlet)</h3>
|
||||||
|
<p>采用能量守恒特性优异的 <strong>蛙跳法</strong>(二阶辛积分器),更新公式为:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) = <strong>v</strong>(<em>t</em>) + ½ <strong>a</strong>(<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>r</strong>(<em>t</em> + Δ<em>t</em>) = <strong>r</strong>(<em>t</em>) + <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) · Δ<em>t</em><br>
|
||||||
|
<strong>a</strong>(<em>t</em> + Δ<em>t</em>) = <strong>F</strong>(<strong>r</strong>(<em>t</em> + Δ<em>t</em>), <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>)) / <em>m</em><br>
|
||||||
|
<strong>v</strong>(<em>t</em> + Δ<em>t</em>) = <strong>v</strong>(<em>t</em> + ½Δ<em>t</em>) + ½ <strong>a</strong>(<em>t</em> + Δ<em>t</em>) · Δ<em>t</em>
|
||||||
|
</div>
|
||||||
|
<p>蛙跳法在长时间模拟中能量漂移极小(本案例验证 <strong>< 0.004%</strong>),适合无阻尼的保守系统。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.2 时间步长与采样</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>说明</th></tr>
|
||||||
|
<tr><td>DT</td><td>0.01 s</td><td>积分步长(远小于 1/ω ≈ 0.16 s,满足稳定性条件)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100 s</td><td>总模拟时间 → NT = 10000 步</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>每 NSTEP 步取一帧用于动画 → 200 帧</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>蛙跳法(Velocity-Verlet)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2.3 计算流程</h3>
|
||||||
|
<div class="flow">
|
||||||
|
<span class="flow-step">读入 coord.txt<br>connection.txt<br>bond.txt</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">施加驱动力<br>(驱动原子 1)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">记录轨迹</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">蛙跳法<br>更新位置/速度</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step">固定约束<br>(x, y 锁定)</span>
|
||||||
|
<span class="flow-arrow">→</span>
|
||||||
|
<span class="flow-step" style="background:#fef3c7;border-color:#f59e0b;">循环<br>NT 次</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:12px;">注意:驱动力在 <strong>每次积分前</strong> 施加,确保受驱原子的位置正确传递给弹簧力计算。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 3. Driving Force -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="driver">
|
||||||
|
<h2>三、驱动力模型</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.1 定义文件</h3>
|
||||||
|
<p>驱动力由 <code>input/driver.txt</code> 定义,格式如下:</p>
|
||||||
|
<pre>n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 5 0 0 1 0 0 90 all</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.2 数学公式</h3>
|
||||||
|
<p>受驱原子的位置由下式决定(<strong>完全替换</strong> coord.txt 中的初始坐标和固定约束):</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>r</strong>(<em>t</em>) = <strong>A</strong> · cos(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>速度由解析导数给出:</p>
|
||||||
|
<div class="formula">
|
||||||
|
<strong>v</strong>(<em>t</em>) = −<strong>A</strong> · 2π<em>f</em> · sin(2π<em>f</em> · <em>t</em> + <strong>φ</strong>)
|
||||||
|
</div>
|
||||||
|
<p>其中 <strong>A</strong> = (amp_x, amp_y, amp_z),<strong>f</strong> = (freq_x, freq_y, freq_z) 为不同方向的驱动频率,<strong>φ</strong> = (phi_x, phi_y, phi_z) 为相位(<strong>角度制</strong>,代码自动转换为弧度)。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.3 本案例驱动参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>值</th><th>含义</th></tr>
|
||||||
|
<tr><td>amp_z</td><td>5.0</td><td>z 方向驱动振幅</td></tr>
|
||||||
|
<tr><td>freq_z</td><td>1.0 Hz</td><td>驱动频率(周期 1 s)</td></tr>
|
||||||
|
<tr><td>phi_z</td><td>90°</td><td>驱动相位 → z(0) = 5·cos(90°) = 0</td></tr>
|
||||||
|
<tr><td>period</td><td>all</td><td>全程驱动,永不停止</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="formula">
|
||||||
|
<em>z</em>(<em>t</em>) = 5.0 · cos(2π · 1.0 · <em>t</em> + 90°)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.4 有限周期驱动</h3>
|
||||||
|
<p><code>period</code> 参数支持三种模式:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>all</strong> — 全程驱动</li>
|
||||||
|
<li><strong>数值</strong> — 驱动指定周期数后 <strong>静止</strong>(冻结在最终位置,速度归零)。例如 <code>period: 1</code> 表示驱动 1 个完整周期后停止。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>3.5 驱动与固定约束的关系</h3>
|
||||||
|
<p>对于受驱原子(<code>driver.txt</code> 中 <code>n</code> 指定的原子),其在 <code>coord.txt</code> 中的初始坐标和 <code>fix_x/fix_y/fix_z</code> 约束被 <strong>完全忽略</strong>。原子的位置和速度完全由驱动力公式决定。</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 4. Usage -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="usage">
|
||||||
|
<h2>四、使用方法</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.1 完整运行(模拟 + 动画)</h3>
|
||||||
|
<pre>cd examples/case06
|
||||||
|
python run_dynamics.py</pre>
|
||||||
|
<p>这步会依次执行:物理模拟 → 抽帧 → 打开 VisPy 3D 动画窗口。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.2 仅查看已有结果</h3>
|
||||||
|
<p>如果已经跑完模拟且生成了 <code>output/display.txt</code>,可以通过修改 <code>input.txt</code> 跳过计算,只开动画:</p>
|
||||||
|
<pre>step_simulate: 0 # 跳过模拟
|
||||||
|
step_sample: 0 # 跳过抽帧
|
||||||
|
step_animation: 1 # 播放动画</pre>
|
||||||
|
<p>然后运行:<code>python run_dynamics.py</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.3 手动 3D 动画</h3>
|
||||||
|
<p>也可以单独启动 VisPy 窗口:</p>
|
||||||
|
<pre>python ../../draw.py output/</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.4 强制重新计算</h3>
|
||||||
|
<p>修改参数后需要重新运行模拟时,设置:</p>
|
||||||
|
<pre>force_calc: 1 # 忽略缓存,强制重新计算</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>4.5 动画交互</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>操作</th><th>效果</th></tr>
|
||||||
|
<tr><td>鼠标拖动</td><td>旋转视角</td></tr>
|
||||||
|
<tr><td>滚轮</td><td>缩放</td></tr>
|
||||||
|
<tr><td>W / S 键</td><td>相机沿 Z 轴向前 / 向后移动(靠近/远离场景)</td></tr>
|
||||||
|
<tr><td>A / D 键</td><td>视角向右 / 向左平移</td></tr>
|
||||||
|
<tr><td>E / Q 键</td><td>视角上升 / 下降(屏幕方向)</td></tr>
|
||||||
|
<tr><td>C / X 键</td><td>增大 / 减小步长</td></tr>
|
||||||
|
<tr><td>V 键</td><td>切换透视 / 正交投影</td></tr>
|
||||||
|
<tr><td>左上角 <strong>reset</strong> 按钮</td><td>复位视角到初始位置</td></tr>
|
||||||
|
<tr><td>左上角 <strong>info</strong> 按钮</td><td>切换信息面板显示/隐藏</td></tr>
|
||||||
|
<tr><td>左上角 <strong>axes</strong> 按钮</td><td>切换坐标轴显示/隐藏</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 5. Parameters -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="params">
|
||||||
|
<h2>五、参数参考</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.1 input.txt 关键参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||||||
|
<tr><td>gravity_field</td><td>0</td><td>均匀重力场(已关闭)</td></tr>
|
||||||
|
<tr><td>gravity_interaction</td><td>0</td><td>原子间万有引力(已关闭)</td></tr>
|
||||||
|
<tr><td>elastic_force</td><td>1</td><td>弹簧键力(已开启)</td></tr>
|
||||||
|
<tr><td>damping_force</td><td>0</td><td>阻尼(已关闭)</td></tr>
|
||||||
|
<tr><td><strong>driving_force</strong></td><td><strong>1</strong></td><td>驱动力开关(1=开启,需 driver.txt)</td></tr>
|
||||||
|
<tr><td>method</td><td>leapfrog</td><td>数值积分方法</td></tr>
|
||||||
|
<tr><td>DT</td><td>0.01</td><td>积分步长 (s)</td></tr>
|
||||||
|
<tr><td>T_total</td><td>100.0</td><td>总模拟时间 (s)</td></tr>
|
||||||
|
<tr><td>NSTEP</td><td>50</td><td>抽帧步数间隔</td></tr>
|
||||||
|
<tr><td>engine</td><td>python</td><td>计算引擎(python / c / cpp / fortran)</td></tr>
|
||||||
|
<tr><td>use_marker</td><td>1</td><td>渲染模式(0=Sphere 网格, 1=Marker GPU 实例化)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>5.2 流程控制参数</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>参数</th><th>0</th><th>1</th></tr>
|
||||||
|
<tr><td>step_simulate</td><td>跳过模拟(加载已有轨迹)</td><td>运行物理模拟</td></tr>
|
||||||
|
<tr><td>step_sample</td><td>跳过抽帧</td><td>从轨迹抽取显示帧</td></tr>
|
||||||
|
<tr><td>step_plot</td><td>不生成图表</td><td>生成轨迹/能量图</td></tr>
|
||||||
|
<tr><td><strong>step_plot_wave</strong></td><td>不生成波形图</td><td>生成波形能量动画 GIF</td></tr>
|
||||||
|
<tr><td>step_animation</td><td>不启动动画</td><td>自动打开 VisPy 3D 窗口</td></tr>
|
||||||
|
<tr><td>force_calc</td><td>自动检测缓存</td><td>强制重新计算</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 6. File Structure -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="files">
|
||||||
|
<h2>六、文件结构</h2>
|
||||||
|
|
||||||
|
<pre>case06/
|
||||||
|
├── input/
|
||||||
|
│ ├── input.txt # 主配置文件(YAML 格式)
|
||||||
|
│ ├── coord.txt # 原子坐标(120 个原子)
|
||||||
|
│ ├── connection.txt # 弹簧连接关系(59 条键)
|
||||||
|
│ ├── bond.txt # 弹簧参数(k=1.0, L₀=1.0)
|
||||||
|
│ └── <strong>driver.txt</strong> # <span class="cm">驱动力定义(本案例新增)</span>
|
||||||
|
├── output/
|
||||||
|
│ ├── trajectory.txt # 全量轨迹数据(50000 步 × 120 原子)
|
||||||
|
│ ├── display.txt # 抽帧后的动画数据(500 帧 × 120 原子)
|
||||||
|
│ ├── dynamics.log # 计算日志
|
||||||
|
│ ├── animation.log # 动画启动日志(闪退时排查用)
|
||||||
|
│ └── wave_animation.gif # 波形能量动画(step_plot_wave=1 时生成)
|
||||||
|
├── doc/
|
||||||
|
│ └── index.html # <span class="cm">本文档</span>
|
||||||
|
├── Readme.md # 案例简介
|
||||||
|
└── run_dynamics.py # 案例运行入口</pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<!-- 7. Troubleshooting -->
|
||||||
|
<!-- ============================================================ -->
|
||||||
|
<section id="troubleshoot">
|
||||||
|
<h2>七、常见问题</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.1 动画窗口闪退</h3>
|
||||||
|
<p>如果 VisPy 窗口一闪就消失,请检查:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>output/animation.log</code> 中是否有错误信息</li>
|
||||||
|
<li><code>output/display.txt</code> 是否存在(需先跑 <code>step_sample: 1</code>)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.2 原子不振动</h3>
|
||||||
|
<p>可能原因:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>NSTEP 过大</strong>:抽帧间隔大于驱动周期的一半时,动画会丢失振动细节。建议 NSTEP ≤ 1/(freq · DT · 10)</li>
|
||||||
|
<li><strong>相位 φ 使采样点落在零值</strong>:试试 <code>phi_z: 0</code> 让原子在 t=0 处于振幅峰值</li>
|
||||||
|
<li>确认 <code>driving_force: 1</code> 且 <code>driver.txt</code> 中 amp_z 不为 0</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>7.3 渲染性能慢</h3>
|
||||||
|
<p>原子数多时动画卡顿:</p>
|
||||||
|
<ul>
|
||||||
|
<li>设置 <code>use_marker: 1</code>(使用 GPU 实例化渲染替代独立网格球体)</li>
|
||||||
|
<li>增大 <code>NSTEP</code> 减少动画帧数</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<hr style="border:none;border-top:1px solid var(--border);margin:40px 0;">
|
||||||
|
|
||||||
|
<footer style="text-align:center;color:var(--muted);font-size:0.85rem;margin-bottom:40px;">
|
||||||
|
Dynamics Simulation Framework · 生成于 2026-06-10
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
bond_name k rest_length
|
||||||
|
h 100.0 1.0
|
||||||
|
k2 100.0 1.41421356
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
|||||||
|
n amp_x amp_y amp_z freq_x freq_y freq_z phi_x phi_y phi_z period
|
||||||
|
1 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
102 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
203 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
304 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
405 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
506 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
607 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
708 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
809 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
910 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1011 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1112 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1213 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1314 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1415 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1516 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1617 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1718 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1819 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
1920 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2021 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2122 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2223 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2324 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2425 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2526 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2627 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2728 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2829 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
2930 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3031 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3132 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3233 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3334 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3435 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3536 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3637 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3738 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3839 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
3940 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4041 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4142 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4243 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4344 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4445 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4546 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4647 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4748 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4849 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
4950 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5051 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5152 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5253 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5354 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5455 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5556 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5657 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5758 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5859 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
5960 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6061 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6162 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6263 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6364 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6465 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6566 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6667 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6768 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6869 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
6970 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7071 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7172 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7273 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7374 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7475 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7576 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7677 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7778 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7879 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
7980 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8081 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8182 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8283 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8384 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8485 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8586 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8687 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8788 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8889 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
8990 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9091 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9192 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9293 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9394 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9495 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9596 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9697 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9798 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
9899 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10000 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
|
10101 0 0 2.0 0 0 0.05 90 90 90 all
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# 物理模拟参数配置
|
||||||
|
# case14 — 双缝干涉实验
|
||||||
|
# 左边界平面波 → 双缝势垒 (x=0) → 干涉图案 → 右边界反射
|
||||||
|
|
||||||
|
step_simulate: 0
|
||||||
|
step_sample: 0
|
||||||
|
step_plot: 0
|
||||||
|
step_animation: 1
|
||||||
|
step_plot_wave: 0
|
||||||
|
force_calc: 1
|
||||||
|
|
||||||
|
save_trajectory: 0
|
||||||
|
|
||||||
|
engine: c
|
||||||
|
box_a: 120.0
|
||||||
|
|
||||||
|
coord_file: input/coord.txt
|
||||||
|
connection_file: input/connection.txt
|
||||||
|
bond_file: input/bond.txt
|
||||||
|
driver_file: input/driver.txt
|
||||||
|
plot_atom: 51
|
||||||
|
|
||||||
|
G: [0.000, 0.000, 0.000]
|
||||||
|
B: [0.000, 0.000, 0.000]
|
||||||
|
|
||||||
|
gravity_field: 0
|
||||||
|
gravity_interaction: 0
|
||||||
|
elastic_force: 1
|
||||||
|
damping_force: 0
|
||||||
|
driving_force: 1
|
||||||
|
gravity_strength: 1.0
|
||||||
|
|
||||||
|
method: leapfrog
|
||||||
|
|
||||||
|
warmup_steps: 0
|
||||||
|
T_total: 100.0
|
||||||
|
NSTEP: 100
|
||||||
|
DT: 0.001
|
||||||
|
|
||||||
|
sample_start: null
|
||||||
|
sample_end: null
|
||||||
|
|
||||||
|
use_marker: 1
|
||||||
|
display_color: {
|
||||||
|
x : [0, [1.0, 0.0, 0.0]],
|
||||||
|
y : [0, [0.0, 1.0, 0.0]],
|
||||||
|
z : [1, [0.0, 0.0, 1.0]],
|
||||||
|
xy : [0, [1.0, 1.0, 0.0]],
|
||||||
|
yz : [0, [0.0, 1.0, 1.0]],
|
||||||
|
zx : [0, [1.0, 0.0, 1.0]],
|
||||||
|
xyz : [0, [1.0, 1.0, 1.0]]
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
ball_color_r: 1.00
|
||||||
|
ball_color_g: 1.00
|
||||||
|
ball_color_b: 1.00
|
||||||
|
|
||||||
|
box_color_r: 0.80
|
||||||
|
box_color_g: 0.80
|
||||||
|
box_color_b: 0.85
|
||||||
|
|
||||||
|
camera_distance: 120.0
|
||||||
|
camera_elevation: 60.0
|
||||||
|
camera_azimuth: -45.0
|
||||||
|
camera_center_x: 0.0
|
||||||
|
camera_center_y: 0.0
|
||||||
|
camera_center_z: 0.0
|
||||||
|
move_camera: 0
|
||||||
|
|
||||||
|
display_amp: [1.0, 1.0, 1.0]
|
||||||
|
|
||||||
|
color_xrange: [['mid', 'max'], ['min', 'max'], ['min', 'max']]
|
||||||
|
color_fix: [0.0, 0.0, 1.0]
|
||||||
|
color_driver: [1.0, 0.0, 0.0]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
0 0 50
|
||||||
|
0 0 80
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Case runner for Dynamics case14 — 2D grid (61x61 atomic mesh).
|
||||||
|
|
||||||
|
This script keeps program and data separated:
|
||||||
|
- program: ../../dynamics.py
|
||||||
|
- input: ./input
|
||||||
|
- output: ./output
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
CASE_DIR = Path(__file__).resolve().parent
|
||||||
|
DYNAMICS_PATH = Path("..") / ".." / "dynamics.py"
|
||||||
|
INPUT_DIR = Path("input")
|
||||||
|
OUTPUT_DIR = Path("output")
|
||||||
|
CONFIG_FILE = INPUT_DIR / "input.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def load_dynamics_module(module_path: Path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamics_module", module_path)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise ImportError(f"无法加载 dynamics.py: {module_path}")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="运行 Dynamics 示例案例 case14")
|
||||||
|
parser.add_argument("--no-plot", action="store_true", help="跳过 matplotlib 绘图")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
dynamics_path = (CASE_DIR / DYNAMICS_PATH).resolve()
|
||||||
|
input_dir = (CASE_DIR / INPUT_DIR).resolve()
|
||||||
|
output_dir = (CASE_DIR / OUTPUT_DIR).resolve()
|
||||||
|
config_path = (CASE_DIR / CONFIG_FILE).resolve()
|
||||||
|
|
||||||
|
module = load_dynamics_module(dynamics_path)
|
||||||
|
module.run_case(
|
||||||
|
config_path=config_path,
|
||||||
|
runtime_base=CASE_DIR,
|
||||||
|
input_dir=input_dir,
|
||||||
|
output_dir=output_dir,
|
||||||
|
no_plot=args.no_plot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user