Files
SystemSimulationApp/tests/manual/plot_mql8_evaluation.py
ljz 7611f13208 修复循环信号与事件采样并接入 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 通过。
2026-09-17 23:50:13 +08:00

113 lines
5.9 KiB
Python
Raw Permalink 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.
"""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()