相较上一版 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 通过。
50 lines
2.7 KiB
Python
50 lines
2.7 KiB
Python
"""Pair saved samples by forcing phase, never by the size of output errors.
|
|
|
|
All curves share ONE pair of row indices. No interpolation across jumps, no
|
|
time shifting, and no use of force/pressure agreement to select a sample.
|
|
The reference's nearest saved grid row is authoritative; a different native
|
|
row is allowed only in the tiny output-timestamp roundoff window.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
|
|
def pair_saved_phases(native_times, reference_times, native_signals, reference_signals, grid, step):
|
|
nt, rt, grid = map(np.asarray, (native_times, reference_times, grid))
|
|
ns, rs = map(np.asarray, (native_signals, reference_signals))
|
|
if ns.shape != (len(nt), rs.shape[1]) or len(rs) != len(rt):
|
|
raise ValueError('Phase signatures must be rows by the same signal columns.')
|
|
tolerance = step * 1e-7
|
|
ni = np.full(len(grid), -1, dtype=int)
|
|
ri = np.full(len(grid), -1, dtype=int)
|
|
records = []
|
|
for j, t in enumerate(grid):
|
|
candidates = np.arange(np.searchsorted(nt, t-tolerance, side='left'),
|
|
np.searchsorted(nt, t+tolerance, side='right'))
|
|
references = np.arange(np.searchsorted(rt, t-tolerance, side='left'),
|
|
np.searchsorted(rt, t+tolerance, side='right'))
|
|
if not len(candidates) or not len(references):
|
|
records.append(dict(gridIndex=j, time=float(t), status='missing-saved-sample'))
|
|
continue
|
|
# Prefer the later row on an exact tie / duplicate timestamp, matching
|
|
# the comparison's existing right-side duplicate policy.
|
|
ref = min(references, key=lambda i: (abs(rt[i]-t), -int(i)))
|
|
closest = min(candidates, key=lambda i: (abs(nt[i]-t), -int(i)))
|
|
matches = candidates[np.all(np.isclose(ns[candidates], rs[ref], rtol=1e-12, atol=1e-12), axis=1)]
|
|
ri[j] = ref
|
|
if not len(matches):
|
|
records.append(dict(gridIndex=j, time=float(t), status='unmatched-forcing-phase',
|
|
referenceTime=float(rt[ref]), referenceSignals=rs[ref].tolist(),
|
|
nativeCandidateTimes=nt[candidates].tolist(),
|
|
nativeCandidateSignals=ns[candidates].tolist()))
|
|
continue
|
|
chosen = min(matches, key=lambda i: (abs(nt[i]-t), -int(i)))
|
|
ni[j] = chosen
|
|
if chosen != closest:
|
|
records.append(dict(gridIndex=j, time=float(t), status='matched-other-event-side',
|
|
platformTime=float(nt[chosen]), referenceTime=float(rt[ref]),
|
|
originalPlatformTime=float(nt[closest]),
|
|
signals=ns[chosen].tolist(), referenceSignals=rs[ref].tolist()))
|
|
return ni, ri, records
|