Files
dynamics/doc/index.html
T
admin 5de80d4f7e modified: CMakeLists.txt
modified:   INSTALL.md
	modified:   README.md
	modified:   build_release_zip.py
	modified:   compute.py
	new file:   doc/index.html
	modified:   dynamics.py
	modified:   engines/c/main.c
	modified:   engines/cpp/main.cpp
	modified:   engines/fortran/main.f90
	modified:   examples/case01/input/coord.txt
	renamed:    examples/case01/input/parameters.yaml -> examples/case01/input/input.txt
	modified:   examples/case01/run_dynamics.py
	new file:   examples/case02/input/bond.txt
	new file:   examples/case02/input/connection.txt
	new file:   examples/case02/input/coord.txt
	new file:   examples/case02/input/input.txt
	new file:   examples/case02/run_dynamics.py
2026-05-20 16:03:59 +08:00

773 lines
43 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamics — 多语言蛙跳法实现对比分析</title>
<style>
:root {
--bg: #f8f9fa; --card: #fff; --text: #1a1a2e;
--accent: #4361ee; --accent2: #3a0ca3;
--good: #06d6a0; --warn: #ffd166; --bad: #ef476f;
--code-bg: #1e1e2e; --code-text: #cdd6f4;
--border: #dee2e6;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, "Noto Sans SC", "Microsoft YaHei", sans-serif;
background: var(--bg); color: var(--text); line-height: 1.7;
padding: 0;
}
header {
background: linear-gradient(135deg, var(--accent2), var(--accent));
color: #fff; padding: 40px 20px; text-align: center;
}
header h1 { font-size: 2em; margin-bottom: 8px; }
header p { opacity: .85; font-size: .95em; }
nav {
background: var(--card); border-bottom: 1px solid var(--border);
padding: 12px 20px; position: sticky; top: 0; z-index: 100;
display: flex; flex-wrap: wrap; gap: 8px;
}
nav a {
color: var(--accent); text-decoration: none; font-size: .88em;
padding: 4px 12px; border-radius: 12px;
border: 1px solid var(--border);
}
nav a:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
.container { max-width: 1000px; margin: 0 auto; padding: 24px 20px; }
section { background: var(--card); border-radius: 12px; padding: 24px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,.08); }
h2 { font-size: 1.4em; margin-bottom: 16px; color: var(--accent); border-left: 4px solid var(--accent); padding-left: 12px; }
h3 { font-size: 1.1em; margin: 20px 0 10px; color: var(--accent2); }
.file-path {
font-family: "JetBrains Mono", "Fira Code", monospace;
font-size: .82em; background: #eef; padding: 2px 8px;
border-radius: 4px; color: var(--accent2);
}
pre {
background: var(--code-bg); color: var(--code-text);
padding: 16px; border-radius: 8px; overflow-x: auto;
font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
font-size: .82em; line-height: 1.6; margin: 10px 0;
position: relative;
}
pre .ln { color: #6c7086; user-select: none; margin-right: 16px; }
.lang-tag {
position: absolute; top: 6px; right: 10px;
font-size: .72em; background: #333; color: #ccc;
padding: 2px 8px; border-radius: 4px;
}
.analysis-box { background: #f0f4ff; border-left: 4px solid var(--accent); padding: 14px 18px; margin: 12px 0; border-radius: 0 8px 8px 0; }
.analysis-box.warn { background: #fff8e1; border-color: var(--warn); }
.analysis-box.bad { background: #ffe8ec; border-color: var(--bad); }
.analysis-box.good { background: #e8faf1; border-color: var(--good); }
.analysis-box h4 { margin-bottom: 6px; font-size: .95em; }
.compare-table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: .9em; }
.compare-table th, .compare-table td { border: 1px solid var(--border); padding: 10px 12px; text-align: left; }
.compare-table th { background: #eef; font-weight: 600; }
.compare-table tr:nth-child(even) { background: #f8f9fa; }
.tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: .78em; font-weight: 600; }
.tag.good { background: #d4edda; color: #155724; }
.tag.warn { background: #fff3cd; color: #856404; }
.tag.bad { background: #f8d7da; color: #721c24; }
footer { text-align: center; padding: 20px; color: #888; font-size: .85em; }
</style>
</head>
<body>
<header>
<h1>Dynamics — 多语言蛙跳法实现对比分析</h1>
<p>Python · C · C++ · Fortran 四个引擎的蛙跳 (Velocity-Verlet) 算法与边界条件实现对比</p>
<p style="margin-top:6px;opacity:.8;font-size:.85em;">最后更新:2026-05-20(力开关系统、万有引力、逐自由度约束)</p>
</header>
<nav>
<a href="#overview">概览</a>
<a href="#py">Python</a>
<a href="#forces">力开关</a>
<a href="#c">C</a>
<a href="#cpp">C++</a>
<a href="#fortran">Fortran</a>
<a href="#diff">版本变更</a>
<a href="#bc">边界条件</a>
<a href="#summary">总结</a>
</nav>
<div class="container">
<!-- ================================================================ -->
<section id="overview">
<h2>1. 概览</h2>
<p>本框架实现了四套语言的计算引擎。各引擎共享相同的物理模型:</p>
<ul>
<li>均匀重力场:<em>F<sub>g</sub></em> = <em>m</em> · <em>g</em>(常数矢量)—— 独立开关 <code>gravity_field</code></li>
<li>线性阻尼:<em>F<sub>d</sub></em> = <em>B</em> · <em>v</em> —— 独立开关 <code>damping_force</code></li>
<li>弹簧键:胡克定律 <em>F</em> = <em>k</em>(<em>r</em> <em>r</em><sub>0</sub>) —— 独立开关 <code>elastic_force</code></li>
<li>万有引力(原子间):<em>F</em> = <em>G</em> · <em>m</em><sub>i</sub> · <em>m</em><sub>j</sub> / <em>r</em>² —— 独立开关 <code>gravity_interaction</code></li>
<li>边界:硬壁反射,粒子限制在 [<em>a</em>, <em>a</em>]<sup>3</sup></li>
<li>逐自由度固定约束:coord.txt 中 <code>fix_x fix_y fix_z</code> 列控制各自由度是否参与迭代</li>
</ul>
<p>Python 引擎支持 4 种积分算法,C/C++/Fortran 引擎于 2026-05-20 重写,现支持全部 4 种算法,且边界条件与 Python 完全一致。经验证,四种引擎对于同一输入(case01, NT=1000, leapfrog, B=0)输出完全一致(至双精度浮点精度)。</p>
<h3>分析范围</h3>
<ul>
<li>蛙跳法(Velocity-Verlet)积分器的四语言实现代码</li>
<li>边界条件(硬壁反射:clamp 位置 + 反转速度)的实现</li>
<li>2026-05-20 重写修复的 Bug 及版本差异</li>
</ul>
<h3>引擎简介</h3>
<table class="compare-table">
<tr><th>引擎</th><th>源文件</th><th>语言标准</th><th>算法支持</th></tr>
<tr><td>Python</td><td><span class="file-path">compute.py</span></td><td>Python 3.8+</td><td>显式欧拉、隐式欧拉、中点法、蛙跳法</td></tr>
<tr><td>C</td><td><span class="file-path">engines/c/main.c</span></td><td>C11</td><td>同上(2026-05-20 新增 3 种)</td></tr>
<tr><td>C++</td><td><span class="file-path">engines/cpp/main.cpp</span></td><td>C++17</td><td>同上(2026-05-20 新增 3 种)</td></tr>
<tr><td>Fortran</td><td><span class="file-path">engines/fortran/main.f90</span></td><td>Fortran 90</td><td>同上(2026-05-20 新增 3 种)</td></tr>
</table>
</section>
<!-- ================================================================ -->
<section id="py">
<h2>2. Python 引擎(标准参考实现)</h2>
<p class="file-path" style="margin-bottom:8px">compute.py</p>
<h3>2.1 蛙跳法积分步骤</h3>
<div class="analysis-box">
<h4>Velocity-Verlet 三步法</h4>
<p>Python 的 <code>Leapfrog_Method</code> 实现了标准 velocity-Verlet 格式。三步法对应于:</p>
<ol>
<li><strong>速度半步推</strong>(第919-921行):<em>v</em>(<em>t</em>+Δt/2) = <em>v</em>(<em>t</em>) + ½ · <em>a</em>(<em>t</em>) · Δt</li>
<li><strong>全推位置</strong>(第923-925行):<em>x</em>(<em>t</em>+Δt) = <em>x</em>(<em>t</em>) + <em>v</em>(<em>t</em>+Δt/2) · Δt</li>
<li><strong>速度后半步</strong>(第930-937行):<em>v</em>(<em>t</em>+Δt) = <em>v</em>(<em>t</em>+Δt/2) + ½ · <em>a</em>(<em>t</em>+Δt) · Δt</li>
</ol>
</div>
<pre><span class="lang-tag">Python</span><code><span class="ln">917</span>def Leapfrog_Method(x, y, z, vx, vy, vz, dt, m, g, b):
<span class="ln">918</span> ax, ay, az = compute_acceleration(x, y, z, vx, vy, vz, m, g, b)
<span class="ln">919</span> vx_half = vx + 0.5 * ax * dt
<span class="ln">920</span> vy_half = vy + 0.5 * ay * dt
<span class="ln">921</span> vz_half = vz + 0.5 * az * dt
<span class="ln">922</span>
<span class="ln">923</span> x = x + vx_half * dt
<span class="ln">924</span> y = y + vy_half * dt
<span class="ln">925</span> z = z + vz_half * dt
<span class="ln">926</span>
<span class="ln">927</span> gamma_x = b[0] / m
<span class="ln">928</span> gamma_y = b[1] / m
<span class="ln">929</span> gamma_z = b[2] / m
<span class="ln">930</span> vx_next = (vx_half + 0.5 * g[0] * dt) / (1.0 + 0.5 * gamma_x * dt)
<span class="ln">931</span> vy_next = (vy_half + 0.5 * g[1] * dt) / (1.0 + 0.5 * gamma_y * dt)
<span class="ln">932</span> vz_next = (vz_half + 0.5 * g[2] * dt) / (1.0 + 0.5 * gamma_z * dt)
<span class="ln">933</span> ax_next, ay_next, az_next = compute_acceleration(
<span class="ln">934</span> x, y, z, vx_next, vy_next, vz_next, m, g, b)
<span class="ln">935</span> vx = vx_half + 0.5 * ax_next * dt
<span class="ln">936</span> vy = vy_half + 0.5 * ay_next * dt
<span class="ln">937</span> vz = vz_half + 0.5 * az_next * dt
<span class="ln">938</span> return x, y, z, vx, vy, vz
</code></pre>
<div class="analysis-box warn">
<h4>⚠️ 阻尼的隐式处理(第927-932行)</h4>
<p>Python 的蛙跳法中,阻尼项用隐式格式处理。<code>gamma = B / m</code>,然后 <code>v_next = (v_half + ½·g·Δt) / (1 + ½·γ·Δt)</code>。这个 <code>v_next</code> 是阻尼修正后的速度估计值,被传给第二次 <code>compute_acceleration</code> 作为入参。当 B = 0 时退化为显式 <code>v_next = v_half + ½·g·Δt</code></p>
<p>注意:<code>vx_next</code><strong>独立变量</strong>,不覆盖 <code>vx_half</code>。最终速度 <code>vx = vx_half + ½·a_next·dt</code> 使用的是原始的半步速度。这是正确的做法。</p>
</div>
<h3>2.2 边界条件</h3>
<pre><span class="lang-tag">Python</span><code><span class="ln">940</span>def Limit_in_box(a, amin, amax, va):
<span class="ln">941</span> """限制物体在边界内,发生碰撞时反弹。"""
<span class="ln">942</span> over = a > amax
<span class="ln">943</span> under = a < amin
<span class="ln">944</span> a = np.where(over, amax, a)
<span class="ln">945</span> a = np.where(under, amin, a)
<span class="ln">946</span> va = np.where(over | under, -va, va)
<span class="ln">947</span> return a, va
</code></pre>
<pre><span class="lang-tag">Python</span><code><span class="ln">949</span>def apply_motion_update(x, y, z, vx, vy, vz, dt, m, g, b):
<span class="ln">957</span> elif METHOD == "leapfrog":
<span class="ln">958</span> x, y, z, vx, vy, vz = Leapfrog_Method(x, y, z, vx, vy, vz, dt, m, g, b)
<span class="ln">962</span> x, vx = Limit_in_box(x, X_MIN, X_MAX, vx)
<span class="ln">963</span> y, vy = Limit_in_box(y, Y_MIN, Y_MAX, vy)
<span class="ln">964</span> z, vz = Limit_in_box(z, Z_MIN, Z_MAX, vz)
<span class="ln">965</span> return x, y, z, vx, vy, vz
</code></pre>
<div class="analysis-box">
<h4>边界处理逻辑</h4>
<ul>
<li>积分方法调用(第957-958行):只做速度/位置更新,不含边界</li>
<li>边界后处理(第962-964行):三个方向各调用一次 <code>Limit_in_box</code></li>
<li><code>Limit_in_box</code>:clamp 位置到边界 + 越界方向速度取反(弹性碰撞模型)</li>
</ul>
</div>
</section>
<!-- ================================================================ -->
<section id="forces">
<h2>3. 力开关与能量分析系统</h2>
<p>2026-05-20 添加了独立的力开关系统,每种力可通过 <span class="file-path">input.txt</span> 中的 0/1 开关独立控制。各引擎的受力计算均基于这些开关条件累加。</p>
<h3>3.1 力开关配置</h3>
<table class="compare-table">
<tr><th>开关名</th><th>默认值</th><th>控制的力</th><th>物理公式</th></tr>
<tr><td><code>gravity_field</code></td><td>1</td><td>均匀重力场(常数矢量 <em>g</em></td><td><em>F</em> = <em>m</em> · <em>g</em></td></tr>
<tr><td><code>gravity_interaction</code></td><td>0</td><td>原子间牛顿万有引力</td><td><em>F</em> = <em>G</em> · <em>m</em><sub>i</sub> · <em>m</em><sub>j</sub> / <em>r</em>²</td></tr>
<tr><td><code>elastic_force</code></td><td>1</td><td>弹簧键胡克力</td><td><em>F</em> = <em>k</em>(<em>r</em> <em>r</em><sub>0</sub>)</td></tr>
<tr><td><code>damping_force</code></td><td>0</td><td>线性阻尼</td><td><em>F</em> = <em>B</em> · <em>v</em></td></tr>
</table>
<h3>3.2 代码实现模式</h3>
<p>Python <code>compute_force()</code> / C/C++ <code>compute_acceleration()</code> / Fortran <code>accel()</code> 统一采用以下模式:</p>
<pre><span class="lang-tag">伪代码</span><code>// 1. 清零
ax = 0; ay = 0; az = 0
// 2. 按开关累加
if (gravity_field): ax += G[0]; ay += G[1]; az += G[2]
if (damping_force): ax -= B[0] * vx / m; ...
if (elastic_force): ax += bond_loop(...)
if (gravity_interaction): ax += pair_gravity_loop(...)
</code></pre>
<div class="analysis-box good">
<h4>✅ 开关关闭时零开销</h4>
<p>每个力都包裹在 <code>if (开关)</code> 条件中。开关关闭时,对应力的计算代码完全跳过,不影响运行效率。</p>
</div>
<h3>3.3 能量图联动</h3>
<p>绘图时 <span class="file-path">dynamics.py</span><code>run_case()</code> 根据力开关状态动态计算对应的势能分量:</p>
<table class="compare-table">
<tr><th>力开关</th><th>势能公式</th><th>关=0 时</th></tr>
<tr><td><code>gravity_field=1</code></td><td><em>U<sub>g</sub></em> = <em>m</em> · <em>G</em> · <em>r</em></td><td>Ug 归零</td></tr>
<tr><td><code>gravity_interaction=1</code></td><td><em>U<sub>gg</sub></em> = −∑<sub>i&lt;j</sub> <em>G</em> · <em>m</em><sub>i</sub> · <em>m</em><sub>j</sub> / <em>r</em><sub>ij</sub></td><td>Ugg 归零</td></tr>
<tr><td><code>elastic_force=1</code></td><td><em>U<sub>s</sub></em> = ½ ∑ <em>k</em>(<em>r</em> <em>r</em><sub>0</sub></td><td>Us 归零</td></tr>
</table>
<div class="analysis-box">
<h4>能量图线颜色</h4>
<p>动能=蓝色、均匀重力势能=绿色、弹性势能=橙色、万有引力势能=<strong style="color:purple;">紫色</strong>、总能量=红色虚线。</p>
</div>
</section>
<!-- ================================================================ -->
<section id="c">
<h2>4. C 引擎</h2>
<p class="file-path" style="margin-bottom:8px">engines/c/main.c</p>
<p style="margin-bottom:10px;font-size:.9em;color:#666;">注意:2026-05-20 重写。原实现为 Symplectic Euler(非 Leapfrog),现已修正为 Velocity-Verlet。</p>
<h3>3.1 蛙跳法积分步骤</h3>
<pre><span class="lang-tag">C</span><code><span class="ln">420</span>/* ── 蛙跳法(Velocity-Verlet)── */
<span class="ln">421</span>static void leapfrog_step(
<span class="ln">422</span> int n, double *x, double *y, double *z,
<span class="ln">423</span> double *vx, double *vy, double *vz,
<span class="ln">424</span> const double *m, const double G[3], const double B[3],
<span class="ln">425</span> const BondData *bonds, const int *fixed, double dt)
<span class="ln">426</span>{
<span class="ln">427</span> double *ax = (double*)alloca(n * sizeof(double));
<span class="ln">428</span> double *ay = (double*)alloca(n * sizeof(double));
<span class="ln">429</span> double *az = (double*)alloca(n * sizeof(double));
<span class="ln">430</span> compute_acceleration(n, x, y, z, vx, vy, vz, m, G, B, bonds, ax, ay, az);
<span class="ln">431</span>
<span class="ln">432</span> /* 半推速度:v_half = v + 0.5*a*dt */
<span class="ln">433</span> for (int i = 0; i < n; i++) {
<span class="ln">434</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">435</span> vx[i] += ax[i] * dt * 0.5;
<span class="ln">436</span> vy[i] += ay[i] * dt * 0.5;
<span class="ln">437</span> vz[i] += az[i] * dt * 0.5;
<span class="ln">438</span> }
<span class="ln">439</span>
<span class="ln">440</span> /* 全推位置(不含边界)*/
<span class="ln">441</span> for (int i = 0; i < n; i++) {
<span class="ln">442</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">443</span> x[i] += vx[i] * dt; /* vx 此时是 v_half */
<span class="ln">444</span> y[i] += vy[i] * dt;
<span class="ln">445</span> z[i] += vz[i] * dt;
<span class="ln">446</span> }
<span class="ln">447</span>
<span class="ln">448</span> /* 隐式阻尼处理:v_next = (v_half + 0.5*g*dt) / (1 + 0.5*gamma*dt)
<span class="ln">449</span> 不覆盖 vx/vy/vz,用临时数组存入 */
<span class="ln">450</span> double *dmp_vx = (double*)alloca(n * sizeof(double));
<span class="ln">451</span> double *dmp_vy = (double*)alloca(n * sizeof(double));
<span class="ln">452</span> double *dmp_vz = (double*)alloca(n * sizeof(double));
<span class="ln">453</span> for (int i = 0; i < n; i++) {
<span class="ln">454</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">455</span> double gx = B[0] / m[i], gy = B[1] / m[i], gz = B[2] / m[i];
<span class="ln">456</span> dmp_vx[i] = (vx[i] + 0.5 * G[0] * dt) / (1.0 + 0.5 * gx * dt);
<span class="ln">457</span> dmp_vy[i] = (vy[i] + 0.5 * G[1] * dt) / (1.0 + 0.5 * gy * dt);
<span class="ln">458</span> dmp_vz[i] = (vz[i] + 0.5 * G[2] * dt) / (1.0 + 0.5 * gz * dt);
<span class="ln">459</span> }
<span class="ln">460</span>
<span class="ln">461</span> /* 用新位置 + 阻尼处理后的速度重算加速度 */
<span class="ln">462</span> compute_acceleration(n, x, y, z, dmp_vx, dmp_vy, dmp_vz, m, G, B, bonds, ax, ay, az);
<span class="ln">463</span>
<span class="ln">464</span> /* 速度后半步:v = v_half + 0.5*a_next*dt
<span class="ln">465</span> vx 仍为 v_half(未被覆盖)*/
<span class="ln">466</span> for (int i = 0; i < n; i++) {
<span class="ln">467</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">468</span> vx[i] += ax[i] * dt * 0.5;
<span class="ln">469</span> vy[i] += ay[i] * dt * 0.5;
<span class="ln">470</span> vz[i] += az[i] * dt * 0.5;
<span class="ln">471</span> }
<span class="ln">472</span>}
</code></pre>
<div class="analysis-box good">
<h4>✅ Velocity-Verlet 标准实现(与 Python 一致)</h4>
<p>C 引擎的 <code>leapfrog_step</code> 与 Python 的 <code>Leapfrog_Method</code> 步骤完全相同:</p>
<ol>
<li>计算加速度 <em>a</em>(<em>t</em>)(第430行)</li>
<li>速度半步推 <em>v</em><sub>½</sub>(第432-438行)</li>
<li>全推位置 <em>x</em>(<em>t</em>+Δt)(第440-446行)</li>
<li>隐式阻尼 → 临时数组 <code>dmp_vx</code>(第448-459行)</li>
<li>用新位置 + 阻尼速度重算加速度(第461-462行)</li>
<li>速度后半步(第464-471行)</li>
</ol>
<p>关键:阻尼值存入 <code>dmp_vx</code> 而不覆盖 <code>vx</code>,与 Python 的 <code>vx_next</code> 独立变量做法一致。</p>
</div>
<h3>3.2 边界条件</h3>
<pre><span class="lang-tag">C</span><code><span class="ln">309</span>/* 边界条件:clamp 位置 + 速度反转 ——与 Python Limit_in_box 一致 */
<span class="ln">310</span>static void limit_in_box(double *pos, double *vel, double lo, double hi) {
<span class="ln">311</span> if (*pos > hi) { *pos = hi; *vel = -*vel; }
<span class="ln">312</span> if (*pos < lo) { *pos = lo; *vel = -*vel; }
<span class="ln">313</span>}
</code></pre>
<pre><span class="lang-tag">C</span><code><span class="ln">473</span>/* ── 分发器:调用对应积分方法 + 边界条件 ── */
<span class="ln">488</span> } else if (strcmp(method, "leapfrog") == 0) {
<span class="ln">489</span> leapfrog_step(n, x, y, z, vx, vy, vz, m, G, B, bonds, fixed, dt);
<span class="ln">490</span> }
<span class="ln">495</span> /* 边界条件(与 Python Limit_in_box 一致) */
<span class="ln">496</span> for (int i = 0; i < n; i++) {
<span class="ln">497</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">498</span> limit_in_box(&x[i], &vx[i], -box_a, box_a);
<span class="ln">499</span> limit_in_box(&y[i], &vy[i], -box_a, box_a);
<span class="ln">500</span> limit_in_box(&z[i], &vz[i], -box_a, box_a);
<span class="ln">501</span> }
<span class="ln">502</span>}
</code></pre>
<div class="analysis-box good">
<h4>✅ 边界条件已修复</h4>
<p>原 C 引擎仅有 <code>clamp</code>(只钳位置不反转速度),现已改为 <code>limit_in_box</code>(钳位置 + 反转速度),与 Python 一致。</p>
</div>
</section>
<!-- ================================================================ -->
<section id="cpp">
<h2>5. C++ 引擎</h2>
<p class="file-path" style="margin-bottom:8px">engines/cpp/main.cpp</p>
<h3>4.1 蛙跳法积分步骤</h3>
<pre><span class="lang-tag">C++</span><code><span class="ln">360</span>/* ── 蛙跳法(Velocity-Verlet)——与 Python Leapfrog_Method 一致 ── */
<span class="ln">361</span>static void leapfrog_full_step(
<span class="ln">362</span> int n, double *x, double *y, double *z,
<span class="ln">363</span> double *vx, double *vy, double *vz,
<span class="ln">364</span> const double *m, const double G[3], const double B[3],
<span class="ln">365</span> const BondData &bonds, const int *fixed, double dt)
<span class="ln">366</span>{
<span class="ln">367</span> // 第一次加速度
<span class="ln">368</span> std::vector<double> ax(n), ay(n), az(n);
<span class="ln">369</span> compute_acceleration(n, x, y, z, vx, vy, vz, m, G, B, bonds, ax.data(), ay.data(), az.data());
<span class="ln">370</span>
<span class="ln">371</span> // 半推速度:v_half = v + 0.5*a*dt (存入 vx, vy, vz)
<span class="ln">372</span> for (int i = 0; i < n; i++) {
<span class="ln">373</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">374</span> vx[i] += ax[i] * dt * 0.5;
<span class="ln">375</span> vy[i] += ay[i] * dt * 0.5;
<span class="ln">376</span> vz[i] += az[i] * dt * 0.5;
<span class="ln">377</span> }
<span class="ln">378</span>
<span class="ln">379</span> // 全推位置(不含边界,边界在外层统一处理)
<span class="ln">380</span> for (int i = 0; i < n; i++) {
<span class="ln">381</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">382</span> x[i] += vx[i] * dt; // vx 此时是 v_half
<span class="ln">383</span> y[i] += vy[i] * dt;
<span class="ln">384</span> z[i] += vz[i] * dt;
<span class="ln">385</span> }
<span class="ln">386</span>
<span class="ln">387</span> // 隐式阻尼处理得到 v_next(不覆盖 vx/vy/vzPython 第929-931行 vx_next
<span class="ln">388</span> std::vector<double> dmp_vx(n), dmp_vy(n), dmp_vz(n);
<span class="ln">389</span> for (int i = 0; i < n; i++) {
<span class="ln">390</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">391</span> double gamma_x = B[0] / m[i];
<span class="ln">392</span> double gamma_y = B[1] / m[i];
<span class="ln">393</span> double gamma_z = B[2] / m[i];
<span class="ln">394</span> dmp_vx[i] = (vx[i] + 0.5 * G[0] * dt) / (1.0 + 0.5 * gamma_x * dt);
<span class="ln">395</span> dmp_vy[i] = (vy[i] + 0.5 * G[1] * dt) / (1.0 + 0.5 * gamma_y * dt);
<span class="ln">396</span> dmp_vz[i] = (vz[i] + 0.5 * G[2] * dt) / (1.0 + 0.5 * gamma_z * dt);
<span class="ln">397</span> }
<span class="ln">398</span>
<span class="ln">399</span> // 用新位置 + 阻尼处理后的速度重算加速度
<span class="ln">400</span> compute_acceleration(n, x, y, z, dmp_vx.data(), dmp_vy.data(), dmp_vz.data(),
<span class="ln">401</span> m, G, B, bonds, ax.data(), ay.data(), az.data());
<span class="ln">402</span>
<span class="ln">403</span> // 速度后半步:v = v_half + 0.5*a_next*dt
<span class="ln">404</span> // vx 仍为 v_half(未被覆盖),直接加上 0.5*a_next*dt
<span class="ln">405</span> for (int i = 0; i < n; i++) {
<span class="ln">406</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">407</span> vx[i] += ax[i] * dt * 0.5;
<span class="ln">408</span> vy[i] += ay[i] * dt * 0.5;
<span class="ln">409</span> vz[i] += az[i] * dt * 0.5;
<span class="ln">410</span> }
<span class="ln">411</span>}
</code></pre>
<h3>4.2 边界条件</h3>
<pre><span class="lang-tag">C++</span><code><span class="ln">265</span>/* 边界条件:clamp 位置 + 速度反转 ——与 Python Limit_in_box 一致 */
<span class="ln">266</span>static void limit_in_box(double &pos, double &vel, double lo, double hi) {
<span class="ln">267</span> if (pos > hi) { pos = hi; vel = -vel; }
<span class="ln">268</span> if (pos < lo) { pos = lo; vel = -vel; }
<span class="ln">269</span>}
</code></pre>
<pre><span class="lang-tag">C++</span><code><span class="ln">413</span>/* ── 分发器:调用对应积分方法 + 边界条件 ── */
<span class="ln">429</span> } else if (method == "leapfrog") {
<span class="ln">430</span> leapfrog_full_step(n, x, y, z, vx, vy, vz, m, G, B, bonds, fixed, dt);
<span class="ln">431</span> }
<span class="ln">435</span> // 边界条件(与 Python Limit_in_box 一致)
<span class="ln">436</span> for (int i = 0; i < n; i++) {
<span class="ln">437</span> if (fixed[i*3+0] && fixed[i*3+1] && fixed[i*3+2]) continue;
<span class="ln">438</span> limit_in_box(x[i], vx[i], -box_a, box_a);
<span class="ln">439</span> limit_in_box(y[i], vy[i], -box_a, box_a);
<span class="ln">440</span> limit_in_box(z[i], vz[i], -box_a, box_a);
<span class="ln">441</span> }
<span class="ln">442</span>}
</code></pre>
<div class="analysis-box good">
<h4>✅ 与 Python 算法一致</h4>
<p>C++ 引擎的 <code>leapfrog_full_step</code> 在重写后已与 Python 完全对齐,阻尼使用 <code>std::vector&lt;double&gt; dmp_vx</code> 临时数组而非原处覆盖。边界条件新增了速度反转。</p>
</div>
</section>
<!-- ================================================================ -->
<section id="fortran">
<h2>6. Fortran 引擎</h2>
<p class="file-path" style="margin-bottom:8px">engines/fortran/main.f90</p>
<h3>5.1 蛙跳法积分步骤</h3>
<pre><span class="lang-tag">Fortran</span><code><span class="ln">498</span>! ── 蛙跳法(Velocity-Verlet)──
<span class="ln">499</span>subroutine leapfrog_full(n, x, y, z, vx, vy, vz, m, G, B, &
<span class="ln">500</span> nb, bp, bk, br, fixed, dt)
<span class="ln">501</span> integer, intent(in) :: n, nb, bp(nb, 2), fixed(n, 3)
<span class="ln">502</span> double precision, intent(inout) :: x(n), y(n), z(n), vx(n), vy(n), vz(n)
<span class="ln">503</span> double precision, intent(in) :: m(n), G(3), B(3), bk(nb), br(nb), dt
<span class="ln">504</span> double precision :: ax(n), ay(n), az(n)
<span class="ln">505</span> double precision :: dmp_vx(n), dmp_vy(n), dmp_vz(n)
<span class="ln">506</span> double precision :: gx, gy, gz
<span class="ln">507</span> integer :: i
<span class="ln">508</span>
<span class="ln">509</span> call accel(n, x, y, z, vx, vy, vz, m, G, B, nb, bp, bk, br, ax, ay, az)
<span class="ln">510</span>
<span class="ln">511</span> ! 速度半步推
<span class="ln">512</span> do i = 1, n
<span class="ln">513</span> if (fixed(i,1) /= 0 .and. fixed(i,2) /= 0 .and. fixed(i,3) /= 0) cycle
<span class="ln">514</span> vx(i) = vx(i) + ax(i) * dt * 0.5d0
<span class="ln">515</span> vy(i) = vy(i) + ay(i) * dt * 0.5d0
<span class="ln">516</span> vz(i) = vz(i) + az(i) * dt * 0.5d0
<span class="ln">517</span> end do
<span class="ln">518</span>
<span class="ln">519</span> ! 全推位置(不含边界)
<span class="ln">520</span> do i = 1, n
<span class="ln">521</span> if (fixed(i,1) /= 0 .and. fixed(i,2) /= 0 .and. fixed(i,3) /= 0) cycle
<span class="ln">522</span> x(i) = x(i) + vx(i) * dt
<span class="ln">523</span> y(i) = y(i) + vy(i) * dt
<span class="ln">524</span> z(i) = z(i) + vz(i) * dt
<span class="ln">525</span> end do
<span class="ln">526</span>
<span class="ln">527</span> ! 隐式阻尼处理(用临时数组 dmp_v,不覆盖 vx/vy/vz
<span class="ln">528</span> do i = 1, n
<span class="ln">529</span> if (fixed(i,1) /= 0 .and. fixed(i,2) /= 0 .and. fixed(i,3) /= 0) then
<span class="ln">530</span> dmp_vx(i) = 0; dmp_vy(i) = 0; dmp_vz(i) = 0; cycle
<span class="ln">531</span> end if
<span class="ln">532</span> gx = B(1) / m(i); gy = B(2) / m(i); gz = B(3) / m(i)
<span class="ln">533</span> dmp_vx(i) = (vx(i) + 0.5d0 * G(1) * dt) / (1.0d0 + 0.5d0 * gx * dt)
<span class="ln">534</span> dmp_vy(i) = (vy(i) + 0.5d0 * G(2) * dt) / (1.0d0 + 0.5d0 * gy * dt)
<span class="ln">535</span> dmp_vz(i) = (vz(i) + 0.5d0 * G(3) * dt) / (1.0d0 + 0.5d0 * gz * dt)
<span class="ln">536</span> end do
<span class="ln">537</span>
<span class="ln">538</span> ! 用新位置 + 阻尼速度重算加速度
<span class="ln">539</span> call accel(n, x, y, z, dmp_vx, dmp_vy, dmp_vz, m, G, B, nb, bp, bk, br, ax, ay, az)
<span class="ln">540</span>
<span class="ln">541</span> ! 速度后半步:v = v_half + 0.5*a_next*dtvx 仍为 v_half
<span class="ln">542</span> do i = 1, n
<span class="ln">543</span> if (fixed(i,1) /= 0 .and. fixed(i,2) /= 0 .and. fixed(i,3) /= 0) cycle
<span class="ln">544</span> vx(i) = vx(i) + ax(i) * dt * 0.5d0
<span class="ln">545</span> vy(i) = vy(i) + ay(i) * dt * 0.5d0
<span class="ln">546</span> vz(i) = vz(i) + az(i) * dt * 0.5d0
<span class="ln">547</span> end do
<span class="ln">548</span>end subroutine leapfrog_full
</code></pre>
<h3>5.2 边界条件</h3>
<pre><span class="lang-tag">Fortran</span><code><span class="ln">395</span>! 边界条件:clamp 位置 + 速度反转
<span class="ln">396</span>subroutine limit_in_box(pos, vel, lo, hi)
<span class="ln">397</span> double precision, intent(inout) :: pos, vel
<span class="ln">398</span> double precision, intent(in) :: lo, hi
<span class="ln">399</span> if (pos > hi) then
<span class="ln">400</span> pos = hi; vel = -vel
<span class="ln">401</span> else if (pos < lo) then
<span class="ln">402</span> pos = lo; vel = -vel
<span class="ln">403</span> end if
<span class="ln">404</span>end subroutine limit_in_box
</code></pre>
<pre><span class="lang-tag">Fortran</span><code><span class="ln">571</span> else if (trim(method) == 'leapfrog') then
<span class="ln">572</span> call leapfrog_full(n, x, y, z, vx, vy, vz, mm, G, B, &
<span class="ln">573</span> nb, bp, bk, br, fixed, dt)
<span class="ln">574</span> end if
<span class="ln">579</span> ! 边界条件
<span class="ln">580</span> do i = 1, n
<span class="ln">581</span> if (fixed(i,1) /= 0 .and. fixed(i,2) /= 0 .and. fixed(i,3) /= 0) cycle
<span class="ln">582</span> call limit_in_box(x(i), vx(i), -box_a, box_a)
<span class="ln">583</span> call limit_in_box(y(i), vy(i), -box_a, box_a)
<span class="ln">584</span> call limit_in_box(z(i), vz(i), -box_a, box_a)
<span class="ln">585</span> end do
<span class="ln">586</span>end subroutine apply_step
</code></pre>
<div class="analysis-box good">
<h4>✅ 与 Python 算法一致</h4>
<p>Fortran 引擎的 <code>leapfrog_full</code> 在重写中修复了阻尼覆盖问题(使用 <code>dmp_vx</code> 临时数组),边界也改为速度反转。</p>
</div>
</section>
<!-- ================================================================ -->
<section id="diff">
<h2>7. 版本变更记录</h2>
<p>以下列出了 2026-05-20 三次迭代中所有修复和改进:</p>
<table class="compare-table">
<thead>
<tr><th>#</th><th>问题描述</th><th>影响引擎</th><th>修复方式</th></tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>C 引擎蛙跳算法错误</strong><br><code>leapfrog_step</code> 实现的是 Symplectic Euler<br><code>v += a·Δt; x += v·Δt</code>(一次加速度,使用新速度推位置)。<br>这比 Velocity-Verlet 的截断误差大一阶(<em>O</em>(Δt) vs <em>O</em>(Δt²)),且只用了一次加速度计算。</td>
<td><span class="tag bad">C</span></td>
<td>重写为 velocity-Verlet 三步法:<br>v<sub>½</sub> → x → a<sub>new</sub> → v</td>
</tr>
<tr>
<td>2</td>
<td><strong>阻尼值覆盖速度变量</strong><br>蛙跳法的隐式阻尼处理直接写回 <code>vx[i]</code>,导致后面的速度后半步 <code>vx[i] += ½·a·dt</code> 在覆盖后的值上叠加。等效于重力加速度被多算了一次,引入 ½·g·Δt 的系统偏差(Fortran)或 g·Δt 的偏差(C)。</td>
<td><span class="tag bad">C, Fortran</span></td>
<td>用临时数组 <code>dmp_vx</code> 存储阻尼估计值,不覆盖原始速度变量</td>
</tr>
<tr>
<td>3</td>
<td><strong><span class="file-path">bond.txt</span> 表头未跳过</strong><br>C/C++ 引擎的 <code>read_bonds</code><code>while (fb &gt;&gt; bn &gt;&gt; bk &gt;&gt; br)</code> 读取 <span class="file-path">bond.txt</span>,但第一行是表头 <code>bond_name k rest_length</code>。解析 <code>k</code> 时尝试将字符串 <code>"k"</code> 转 double 失败,使用默认值 1.0(应为 100.0),弹簧劲度系数差 100 倍,完全改变了轨迹。Python 和 Fortran 正确跳过了表头。</td>
<td><span class="tag bad">C, C++</span></td>
<td>在数据读取循环前用 <code>std::getline(fb, header)</code>C++)或 <code>fgets(header, ...)</code>C)跳过表头行</td>
</tr>
<tr>
<td>4</td>
<td><strong>C++ JSON 输出精度不足</strong><br>C++ 的 JSON 输出用默认 <code>&lt;&lt;</code> 运算符,只有 6 位有效数字,而 C 的 <code>%.15g</code> 和 Fortran 的 <code>g0</code> 都用全精度。导致 C++ 与其他引擎的输出到第 6 位之后截断。</td>
<td><span class="tag warn">C++</span></td>
<td>添加 <code>std::setprecision(15)</code> 到输出流</td>
</tr>
<tr>
<td>5</td>
<td><strong>C 引擎 <code>record_steps</code> 忽略 <code>warmup_steps</code></strong><br>C 引擎的轨迹缓冲区大小写死为 <code>params.NT</code> 而非 <code>params.NT - params.warmup_steps</code>。Python/C++/Fortran 正确。</td>
<td><span class="tag warn">C</span></td>
<td>改为 <code>int record_steps = params.NT - params.warmup_steps;</code></td>
</tr>
<tr>
<td>6</td>
<td><strong>力开关系统</strong><br>新增 4 个独立力开关 <code>gravity_field</code>/<code>gravity_interaction</code>/<code>elastic_force</code>/<code>damping_force</code>,通过 <span class="file-path">input.txt</span> 的 0/1 控制各力是否参与计算。所有引擎统一实现:清零 → 按条件累加。</td>
<td><span class="tag good">四引擎</span></td>
<td>Python: 重写 <code>compute_force()</code>C/C++: 重写 <code>compute_acceleration()</code>Fortran: 重写 <code>accel()</code></td>
</tr>
<tr>
<td>7</td>
<td><strong>万有引力引擎</strong><br>原子间牛顿万有引力 <em>F = G · m<sub>i</sub> · m<sub>j</sub> / r²</em>O(N²) 双循环对计算,<code>gravity_interaction=0</code> 时零开销。</td>
<td><span class="tag good">四引擎</span></td>
<td>所有引擎的受力计算函数中添加多原子对循环</td>
</tr>
<tr>
<td>8</td>
<td><strong>逐自由度固定约束</strong><br><span class="file-path">coord.txt</span> 末尾增加 <code>fix_x fix_y fix_z</code> 三列,每列 0/1 控制该方向是否锁定。锁定后位置回复初始值、速度置零。</td>
<td><span class="tag good">四引擎</span></td>
<td>C/C++/Fortran: <code>apply_step()</code> 新增 <code>pos_0</code> 参数,边界条件后添加约束;Python: 已有 <code>apply_fixed_constraints()</code> 完美支持</td>
</tr>
<tr>
<td>9</td>
<td><strong>能量图随力开关联动</strong><br>绘图时根据力开关状态动态计算势能分量。仅 <code>gravity_interaction=1</code> 时计算万有引力势能并以<strong style="color:purple;">紫色</strong>曲线绘制。</td>
<td><span class="tag good">Python</span></td>
<td><span class="file-path">dynamics.py</span> 能量计算添加 <code>ug_grav</code> 和开关判断</td>
</tr>
</tbody>
</table>
</section>
<!-- ================================================================ -->
<section id="bc">
<h2>8. 边界条件深度分析</h2>
<h3>8.1 硬壁反射实现</h3>
<p>四引擎的边界条件在重写后完全一致:</p>
<table class="compare-table">
<tr><th>引擎</th><th>函数名</th><th>钳位置</th><th>反转速度</th><th>调用时机</th></tr>
<tr><td>Python</td><td><code>Limit_in_box</code></td><td><span class="tag good"></span></td><td><span class="tag good"></span></td><td>蛙跳步之后(后处理)</td></tr>
<tr><td>C</td><td><code>limit_in_box</code></td><td><span class="tag good"></span></td><td><span class="tag good"></span></td><td>积分步之后(<code>apply_step</code></td></tr>
<tr><td>C++</td><td><code>limit_in_box</code></td><td><span class="tag good"></span></td><td><span class="tag good"></span></td><td>积分步之后(<code>apply_step</code></td></tr>
<tr><td>Fortran</td><td><code>limit_in_box</code></td><td><span class="tag good"></span></td><td><span class="tag good"></span></td><td>积分步之后(<code>apply_step</code></td></tr>
</table>
<div class="analysis-box">
<h4>算法合理性</h4>
<p><strong>速度反转是必须的。</strong> 硬壁反射的物理模型要求速度在碰撞时反向以保持运动方向的一致性。没有速度反转时,粒子到达边界后会被持续钳制在边界上("黏壁"现象),这是功能性缺陷。</p>
</div>
<h3>8.2 逐自由度固定约束</h3>
<p>2026-05-20 新增了逐自由度固定约束。在 <span class="file-path">coord.txt</span> 末尾添加 <code>fix_x fix_y fix_z</code> 三列:</p>
<pre><span class="lang-tag">coord.txt 格式</span><code>n mass radius x y z vx vy vz fix_x fix_y fix_z
1 1 0.28 -1 0 0 0 0 0 1 0 0 ← 仅固定 x 方向
2 1 0.28 1 0 1 0 0 0 0 1 1 ← 固定 y 和 z 方向
</code></pre>
<p>Python 的 <code>apply_fixed_constraints()</code> 使用 NumPy 逐元素操作:</p>
<pre><span class="lang-tag">Python</span><code>fixed = ATOM_FIXED != 0 # (n_atoms, 3) 布尔矩阵
positions = np.where(fixed, ATOM_POSITIONS, positions) # 逐度约束
velocities = np.where(fixed, 0.0, velocities)
</code></pre>
<p>C/C++/Fortran 引擎在 <code>apply_step()</code> 的边界条件之后添加了相同的约束逻辑:</p>
<pre><span class="lang-tag">C/C++/Fortran 伪代码</span><code>// 边界条件后,逐自由度固定约束
for i = 1..n:
if fixed_x[i]: x[i] = x0[i]; vx[i] = 0
if fixed_y[i]: y[i] = y0[i]; vy[i] = 0
if fixed_z[i]: z[i] = z0[i]; vz[i] = 0
</code></pre>
<div class="analysis-box good">
<h4>✅ 约束在积分步与边界条件之后,保证正确性</h4>
<p>约束在积分器完成位置/速度更新、边界钳位之后执行,确保固定自由度不参与任何物理演化。</p>
</div>
<h3>8.3 能量局限性</h3>
<div class="analysis-box warn">
<h4>⚠️ 硬壁反射与 Symplectic 积分器的不兼容</h4>
<p>Velocity-Verlet 是 symplectic 积分器,在平滑势场中能量几乎精确守恒(前 20 步漂移仅 -0.000078%)。但硬壁反射是一个非光滑势(无限高势垒),每个碰撞步的工作流程如下:</p>
<ol>
<li>蛙跳步假定粒子在光滑势场中自由运动了 Δt 时间</li>
<li>后处理发现粒子越界,将位置钳回边界 + 反转速度</li>
<li>这个"钳回"动作改变了重力势能(因为 <em>z</em> 位置被改变)但没有相应调整动能</li>
<li>每次碰撞产生约 0.03~0.5 J 的能量变化</li>
</ol>
<p>在典型 case01 参数下(100,000 步,83 次碰底),累积能量漂移约 36%。这是硬壁边界的固有限制,不是代码 Bug。</p>
</div>
<div class="analysis-box">
<h4>✅ 修复建议:软壁势</h4>
<p>用光滑的排斥势替换硬壁 clamp 可保持 symplectic 守恒性:</p>
<ul>
<li><strong>WCA 势</strong>Weeks-Chandler-Andersen):截断平移的 Lennard-Jones</li>
<li><strong>指数排斥</strong><em>U</em>(<em>z</em>) = <em>A</em> · exp(|z|/<em>z</em><sub>0</sub>)</li>
<li><strong>半壁谐振子</strong>|z| > <em>a</em> 时受到线性回复力 <em>F</em> = <em>k</em>(z sign(z)·<em>a</em>)</li>
</ul>
</div>
</section>
<!-- ================================================================ -->
<section id="summary">
<h2>9. 总结</h2>
<table class="compare-table">
<thead>
<tr><th>维度</th><th>Python</th><th>C</th><th>C++</th><th>Fortran</th></tr>
</thead>
<tbody>
<tr>
<td><strong>积分器类型</strong></td>
<td><span class="tag good">Velocity-Verlet</span></td>
<td><span class="tag good">Velocity-Verlet</span><br><small>原为 Symplectic Euler</small></td>
<td><span class="tag good">Velocity-Verlet</span></td>
<td><span class="tag good">Velocity-Verlet</span></td>
</tr>
<tr>
<td><strong>算法数量</strong></td>
<td><span class="tag good">4 种</span></td>
<td><span class="tag good">4 种</span><br><small>新增 3 种</small></td>
<td><span class="tag good">4 种</span><br><small>新增 3 种</small></td>
<td><span class="tag good">4 种</span><br><small>新增 3 种</small></td>
</tr>
<tr>
<td><strong>阻尼处理</strong></td>
<td>隐式 + 显式混合</td>
<td>同 Python</td>
<td>同 Python</td>
<td>同 Python</td>
</tr>
<tr>
<td><strong>边界:钳位置</strong></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
</tr>
<tr>
<td><strong>边界:反转速度</strong></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span><br><small>原缺失</small></td>
<td><span class="tag good"></span><br><small>原缺失</small></td>
<td><span class="tag good"></span><br><small>原缺失</small></td>
</tr>
<tr>
<td><strong>输出精度(JSON</strong></td>
<td><span class="tag good">双精度</span></td>
<td><span class="tag good">%.15g</span></td>
<td><span class="tag good">setprecision(15)</span><br><small>原默认 6 位</small></td>
<td><span class="tag good">g0</span></td>
</tr>
<tr>
<td><strong>算法选择方式</strong></td>
<td>YAML config</td>
<td>param.json</td>
<td>param.json</td>
<td>param.json</td>
</tr>
<tr>
<td><strong>引擎一致性</strong></td>
<td colspan="4" style="text-align:center;"><span class="tag good">四个引擎输出完全一致(双精度验证通过)</span></td>
</tr>
<tr>
<td><strong>力开关系统</strong></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
</tr>
<tr>
<td><strong>万有引力</strong></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
<td><span class="tag good"></span></td>
</tr>
<tr>
<td><strong>逐自由度约束</strong></td>
<td><span class="tag good"></span><br><small>np.where</small></td>
<td><span class="tag good"></span><br><small>pos_0 参数</small></td>
<td><span class="tag good"></span><br><small>pos_0 参数</small></td>
<td><span class="tag good"></span><br><small>pos_0 参数</small></td>
</tr>
</tbody>
</table>
</section>
</div>
<footer>
Dynamics Project — 多语言引擎代码分析 &bull; 更新于 2026-05-20(力开关 · 万有引力 · 逐自由度约束 · 能量图联动)
</footer>
</body>
</html>