相较上一版 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 通过。
64 lines
4.0 KiB
Python
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()
|