修复循环信号与事件采样并接入 LSTP 接触定位,补充八路验证及复用实验

相较上一版 Jacobian 确定性复用更新,本次补齐事件边界一致性、结果两侧采样及接触事件定位;保留已有物性复用和组件力学公式。

- 统一 UD00 信号求值与下一事件查询的绝对时间边界,修复循环边界浮点舍入导致的阶段错位、重复或漏报,并覆盖零时长、多阶段及长周期场景。
- 引入原生输出语义 v2:保留规则网格真实时间,补充内部时间事件和状态事件的左邻及事件后采样,按保存时间、状态和离散模式重放结果。
- 两条代码生成路径均发出 LSTP 接触描述,默认定位间隙过零及非负力模式的力截断;仅在接受事件时更新防重复记录,增加 contactEvents 诊断计数。
- 补充 MASS/LSTP 独立事件实验、八路全曲线与驱动阶段配对评估,以及 Amesim 不连续点输出对照和力差定位报告;MASS 新增释放机制仍保留为独立实验。
- 保存局部 probe、context 访问与回退、shadow replay、R288 real skip/typed replay 及阀门数值尾部诊断工具和报告;未证明净收益的实验不启用为生产默认优化。
- 更新原生运行说明和元件建模规范,补充信号边界、输出语义、接触事件和实验依赖回归测试。

验证:五组专项回归共 34 项全部通过;37 个待提交 Python 文件语法检查通过;git diff --cached --check 通过。
This commit is contained in:
ljz committed 2026-09-17 23:50:13 +08:00
1 parent 1aac220084
commit 7611f13208
85 files changed
+11358 -33

No files matched your search

+112
View File
@@ -0,0 +1,112 @@
"""Create shareable plots from evaluate_mql8_correctness.py evidence (Matplotlib)."""
import argparse
import json
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import font_manager
import numpy as np
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('directory', type=Path)
args = parser.parse_args()
out = args.directory
font = Path('C:/Windows/Fonts/msyh.ttc')
if font.exists():
font_manager.fontManager.addfont(str(font))
plt.rcParams['font.family'] = font_manager.FontProperties(fname=str(font)).get_name()
plt.rcParams.update({'font.size': 9, 'axes.spines.top': False, 'axes.spines.right': False,
'axes.unicode_minus': False, 'svg.fonttype': 'none', 'figure.facecolor': 'white'})
summary = json.loads((out / 'summary.json').read_bytes())
full = np.load(out / 'full/curves.npz')
startup = np.load(out / 'startup/curves.npz')
fig, axes = plt.subplots(4, 2, figsize=(13, 13), constrained_layout=True)
selected = [
(startup, summary['startup']['groups']['pressure']['worstAbsolute']['key'], '绝对压力', 'kPa', 1e-3, .002, 1000),
(startup, summary['startup']['groups']['temperature']['worstAbsolute']['key'], '温度', 'K', 1, .002, 1000),
(full, 'amesim_mecmas21_1.x', '1 号质量块位移', 'mm', 1000, 50, 1),
(full, summary['full']['groups']['force']['worstAbsolute']['key'], '接触力(最差曲线)', 'kN', .001, 50, 1),
]
for (data, key, title, unit, scale, end, ts), (left, right) in zip(selected, axes):
mask = data['time'] <= end
t = data['time'][mask] * ts
native, ame = data['platform|' + key][mask] * scale, data['amesim|' + key][mask] * scale
left.plot(t, native, color='#1368a8', linewidth=1.6, label='当前平台')
left.plot(t, ame, color='#e07832', linestyle='--', linewidth=1.2, label='本次 Amesim')
left.set_title(title + ' · ' + key, fontsize=9)
left.legend(fontsize=8)
right.plot(t, native - ame, color='#925034', linewidth=1.1)
right.axhline(0, color='#888888', linewidth=.6)
right.set_title('平台 − Amesim;切换点保留', fontsize=9)
for ax in (left, right):
ax.set_xlabel('时间 / ' + ('ms' if ts == 1000 else 's'))
ax.set_ylabel(unit)
ax.grid(alpha=.18)
fig.suptitle('八路模型正确性初评 · 当前代码与本次 Amesim 执行\n启动段采样 0.1 ms;循环全程采样 10 ms;曲线重合不等于事件输出一致', fontsize=13)
fig.savefig(out / 'comparison.png', dpi=160)
fig.savefig(out / 'comparison.svg')
plt.close(fig)
fig, axes = plt.subplots(3, 2, figsize=(13, 10), constrained_layout=True)
# Show each of the eight physical contacts; full curve metrics still include
# the large event difference. The second column localizes away from events.
for i in range(1, 9):
key = f'amesim_lstp00a_{i}.force'
t = full['time']
error = full['platform|' + key] - full['amesim|' + key]
axes[0, 0].plot(t, error / 1000, linewidth=.8, label=str(i))
quiet = (t >= .1)
for event in summary['full']['signalEvents']:
quiet &= np.abs(t - event) > .0200001
visible = np.where(quiet, error, np.nan)
axes[0, 1].plot(t, visible, linewidth=.8, label=str(i))
axes[0, 0].set(title='8 路接触力差:全部共同采样点', ylabel='差值 / kN')
axes[0, 1].set(title='诊断视图:t≥0.1 s,避开切换前后 0.02 s', ylabel='差值 / N')
axes[0, 0].legend(title='支路', ncol=4, fontsize=7)
for row, (quantity, label) in enumerate([('enthalpy_flow', '节点焓流 / W'), ('mass_flow', '节点质量流 / kg/s')], 1):
key = summary['default']['groups'][quantity]['worstRelative']['key']
for column in range(2):
ax = axes[row, column]
t = full['time']
mask = (t >= .1) & (t <= .5)
n, a = full['platform|' + key], full['amesim|' + key]
if column == 0:
ax.plot(t[mask], n[mask], color='#1368a8', label='当前平台')
ax.plot(t[mask], a[mask], '--', color='#e07832', label='本次 Amesim')
ax.legend(fontsize=8)
ax.set_ylabel(label)
ax.set_title(key, fontsize=9)
else:
ax.plot(t[mask], (n-a)[mask], color='#925034')
ax.set_ylabel('绝对差,沿用左侧单位')
ax.set_title('小量差异单列,避免峰值归一化掩盖')
for ax in axes.flat:
ax.grid(alpha=.18)
ax.set_xlabel('时间 / s')
fig.suptitle('问题定位 · 切换点误差与小流量差异分开评价\n右上筛选只用于诊断;全部采样点仍进入主报告统计', fontsize=13)
fig.savefig(out / 'diagnostics.png', dpi=160)
fig.savefig(out / 'diagnostics.svg')
plt.close(fig)
data = np.load(out / 'volume-startup/curves.npz')
fig, axes = plt.subplots(1, 2, figsize=(12, 4), constrained_layout=True)
for ax, key, label, scale in [(axes[0], 'amesim_pnch012_15.vol', '有效气室容积 / L', 1000),
(axes[1], 'amesim_pnch012_15.volume_work', '气室体积功率 / TW', 1e-12)]:
mask = data['time'] <= 8e-8
for prefix, name, color, style in [('platform', '当前平台', '#1368a8', '-'), ('amesim', '本次 Amesim', '#e07832', '--')]:
ax.plot(data['time'][mask]*1e9, data[prefix+'|'+key][mask]*scale, style, color=color, label=name)
ax.set(xlabel='时间 / ns', ylabel=label)
ax.grid(alpha=.18)
ax.legend()
fig.suptitle('最初 80 ns:容积下限与体积功率 · 采样间隔 2 ns', fontsize=13)
fig.savefig(out / 'volume-startup.png', dpi=160)
fig.savefig(out / 'volume-startup.svg')
plt.close(fig)
if __name__ == '__main__':
main()