Files
SystemSimulationApp/tests/manual/summarize_mechanical_events.py
T
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

64 lines
4.0 KiB
Python

"""Summarize accuracy, contact phases and repeated cost for isolated variants."""
import argparse
import json
from pathlib import Path
import statistics
import numpy as np
def read(path):return json.loads(path.read_bytes())
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('directory',type=Path)
args=parser.parse_args();out=args.directory
runs=read(out/'summary.json');rows=[];branches=[];groups=[]
for profile,variants in runs.items():
baseline=variants['off']
base_force=baseline['groups']['force']['worstAbsolute']['maxAbsoluteError']
base_time=baseline['timing']['medianSeconds']
base_curves=np.load(out/profile/'off/curves.npz')
for mode,r in variants.items():
data=np.load(out/profile/mode/'curves.npz')
contacts=[]
for key in data.files:
if key.startswith('platform|') and key.endswith('.gap'):
expected='amesim|'+key.split('|',1)[1]
mismatch=data['phaseMatched'] & ((data[key]<0)!=(data[expected]<0))
contacts.extend(dict(key=key.split('|',1)[1],time=float(data['time'][i])) for i in np.flatnonzero(mismatch))
equal=all(np.array_equal(data[k],base_curves[k],equal_nan=True)
for k in data.files if k.startswith('platform|'))
worst=r['groups']['force']['worstAbsolute'];timing=r['timing'];native=r['nativeRun']
report=read(out/profile/mode/'comparison.json')
branches.extend(dict(profile=profile,mode=mode,**c) for c in report['curves'] if c['quantity']=='force')
groups.extend(dict(profile=profile,mode=mode,quantity=q,**g) for q,g in r['groups'].items())
counter_keys=('nfev','acceptedSteps','rejectedSteps','solverStarts','stateTransitions','njev','nlu','experimentalEvents')
counters=[{k:t.get(k) for k in counter_keys} for t in timing['runs']]
row=dict(profile=profile,mode=mode,medianSolveSeconds=timing['medianSeconds'],
minimumSolveSeconds=timing['minimumSeconds'],maximumSolveSeconds=timing['maximumSeconds'],
timingRepeats=timing['repeats'],repeatCountersStable=all(c==counters[0] for c in counters),
medianCpuSeconds=statistics.median(t['solveCpuSeconds'] for t in timing['runs']),
solveChangePercent=100*(timing['medianSeconds']/base_time-1),
forceMaxError=worst['maxAbsoluteError'],forceWorstTime=worst['worstTime'],forceWorstKey=worst['key'],
forceErrorChangePercent=100*(worst['maxAbsoluteError']/base_force-1),
forceQuietMax=max(c['quietMaxAbsolute'] for c in report['curves'] if c['quantity']=='force'),
pressureMaxError=r['groups']['pressure']['worstAbsolute']['maxAbsoluteError'],
temperatureMaxError=r['groups']['temperature']['worstAbsolute']['maxAbsoluteError'],
above5PercentCount=r['above5PercentCount'],phaseUnpairedCount=r['phaseUnpairedGridCount'],
contactPhaseMismatches=contacts,gridTrajectoryEqualsOff=equal,
rawPeakContactForce=r['extraEventPointMaxContactForce'],
acceptedSteps=native['acceptedSteps'],rejectedSteps=native['rejectedSteps'],
nfev=native['nfev'],solverStarts=native['solverStarts'],njev=native['njev'],nlu=native['nlu'],
stateTransitions=native['stateTransitions'],
experimentalEvents=native.get('experimentalEvents',{}),jacobianMode=native['jacobianMode'])
rows.append(row)
print(json.dumps(row,ensure_ascii=False))
(out/'effect-summary.json').write_text(json.dumps(rows,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
(out/'branch-force-summary.json').write_text(json.dumps(branches,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
(out/'group-summary.json').write_text(json.dumps(groups,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
if __name__=='__main__':main()