修复循环信号与事件采样并接入 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:
1 parent
1aac220084
commit
7611f13208
85 files changed
+11358
-33
No files matched your search
@@ -0,0 +1,118 @@
|
||||
"""Eight-branch SI mapping, carried forward from the audited 2026-09-14 comparison.
|
||||
|
||||
The caller supplies a fresh topology audit and network; no historical curves are loaded.
|
||||
"""
|
||||
import numpy as np
|
||||
from app.simulation.reporting.amesim_results import _parse_variable_line, parse_amesim_results_bytes
|
||||
|
||||
|
||||
def configure(current_audit, current_network):
|
||||
global audit, network, aliases, nodes, adj
|
||||
audit, network = current_audit, current_network
|
||||
aliases = {row['component']: row['ameAlias'] for row in audit['mapping']}
|
||||
nodes = {c.name: c for c in network.components.values()
|
||||
if c.model_type in ('amesim_pn3node2', 'amesim_p4node2')}
|
||||
adj = {}
|
||||
for edge in network.connections:
|
||||
if edge.kind == 'physical':
|
||||
a, b = (e.key for e in edge.endpoints)
|
||||
adj[a], adj[b] = b, a
|
||||
|
||||
|
||||
def native_curves(series):
|
||||
values = {k: np.asarray(v) for k, v in series.items()}
|
||||
def energy(name):
|
||||
key = name + '.reference_enthalpy_flow'
|
||||
if key not in values:
|
||||
result = np.zeros_like(values['time'])
|
||||
for port in nodes[name].active_port_definitions:
|
||||
if port.name == 'port_2':
|
||||
continue
|
||||
other = adj[name, port.name]
|
||||
if other[0] in nodes and other[1] == 'port_2':
|
||||
result += energy(other[0])
|
||||
else:
|
||||
q = values[name + '.' + port.name + '.m_flow']
|
||||
result += q * np.where(q > 0, values['.'.join(other) + '.h_outflow'],
|
||||
values[name + '.' + port.name + '.h_outflow'])
|
||||
values[key] = result
|
||||
return values[key]
|
||||
for name in nodes:
|
||||
energy(name)
|
||||
values[name + '.reference_mass_flow'] = -values[name + '.port_2.m_flow']
|
||||
for component in network.components.values():
|
||||
if component.model_type == 'amesim_pnch012':
|
||||
name = component.name
|
||||
values[name + '.volume_work'] = -values[name + '.p'] * values[name + '.dvol']
|
||||
return values
|
||||
|
||||
|
||||
def read_ame(directory):
|
||||
variables = tuple(_parse_variable_line(i, line) for i,line in enumerate(
|
||||
(directory/'test_mql_.var').read_text(encoding='latin1').splitlines()))
|
||||
return parse_amesim_results_bytes((directory/'test_mql_.results').read_bytes(), variables)
|
||||
|
||||
def curve_mapping():
|
||||
result = []
|
||||
for row in audit['mapping']:
|
||||
kind, name, alias = row['type'],row['component'],row['ameAlias']
|
||||
fields = []
|
||||
if kind == 'amesim_mecmas21':
|
||||
fields = [('x','x1','displacement','m',1,0),('v','v1','velocity','m/s',1,0)]
|
||||
elif kind == 'amesim_lstp00a':
|
||||
fields = [('force','f1','force','N',1,0),('gap','gap','gap','m',.001,0)]
|
||||
elif kind in ('amesim_pnch023','amesim_pnch012'):
|
||||
fields = [('p','press','pressure','Pa',1,101300),('T','temp','temperature','K',1,0)]
|
||||
if kind == 'amesim_pnch012':
|
||||
fields += [('vol','vol','volume','m3',1e-6,0),('m','mgas1','mass','kg',1e-3,0)]
|
||||
elif kind in ('amesim_pnl0001','amesim_pnl0002'):
|
||||
s = '2' if kind.endswith('1') else 'ctr'
|
||||
fields = [('p','p'+s,'pressure','Pa',1,101300),('T','t'+s,'temperature','K',1,0)]
|
||||
elif kind == 'amesim_pnl0003':
|
||||
fields = [(k+str(i), k.lower()+str(i), 'pressure' if k=='p' else 'temperature',
|
||||
'Pa' if k=='p' else 'K',1,101300 if k=='p' else 0) for k in ('p','T') for i in (1,2)]
|
||||
elif kind in ('amesim_ud00','amesim_step0'):
|
||||
fields = [('out.signal','output' if kind=='amesim_ud00' else 'out','signal','1',1,0)]
|
||||
elif kind in ('amesim_pn3node2','amesim_p4node2'):
|
||||
fields = [('reference_enthalpy_flow','dh2','enthalpy_flow','W',1,0),
|
||||
('reference_mass_flow','dm2','mass_flow','kg/s',1e-3,0)]
|
||||
elif kind == 'amesim_pnrp17':
|
||||
fields = [('volume','vol1','piston_volume','m3',1e-6,0),
|
||||
('volume_flow','vvol1','volume_rate','m3/s',1e-3/60,0)]
|
||||
for prop, field, quantity, unit, scale, offset in fields:
|
||||
result.append(dict(key=name+'.'+prop, amePath=field+'@'+alias,
|
||||
quantity=quantity, unit=unit, scale=scale, offset=offset))
|
||||
if kind == 'amesim_pnch012':
|
||||
sources = []
|
||||
volumes = []
|
||||
for p in network.components[name].active_port_definitions:
|
||||
other = adj[name,p.name]
|
||||
component = network.components[other[0]]
|
||||
if component.model_type == 'amesim_pnrp17':
|
||||
sources.append('vvol1@'+aliases[other[0]])
|
||||
volumes.append('vol1@'+aliases[other[0]])
|
||||
elif other[0] in nodes and other[1] == 'port_2':
|
||||
sources.append('dvol2@'+aliases[other[0]])
|
||||
volumes.append('vol2@'+aliases[other[0]])
|
||||
chamber = network.components[name]
|
||||
effective_volume = dict(volumePaths=volumes, deadVolume=chamber.cvol0,
|
||||
prescribedVolume=sum(chamber.external_volumes.values()))
|
||||
result.append(dict(key=name+'.dvol',amePaths=sources, quantity='chamber_volume_rate',unit='m3/s', **effective_volume))
|
||||
result.append(dict(key=name+'.volume_work',amePaths=sources,pressurePath='press@'+alias,
|
||||
quantity='volume_work',unit='W', **effective_volume))
|
||||
return result
|
||||
|
||||
def ame_curve(ame, m):
|
||||
if 'amePath' in m:
|
||||
return np.array(ame.series(m['amePath']))*m['scale']+m['offset']
|
||||
rate = sum((np.array(ame.series(p)) for p in m['amePaths']), np.zeros(len(ame.times)))*(1e-3/60)
|
||||
volume = sum((np.array(ame.series(p)) for p in m['volumePaths']), np.zeros(len(ame.times)))*1e-6
|
||||
volume += m['deadVolume']+m['prescribedVolume']
|
||||
# PNCH012.c sets its internal dvol to zero while the volume is limited.
|
||||
# Summing raw piston rates without this condition invents enormous work
|
||||
# that the original Amesim chamber does not actually apply.
|
||||
limit = m['deadVolume']/100
|
||||
rate = np.where((volume<limit) | ((volume<=limit) & (rate<0)), 0, rate)
|
||||
if m['quantity'] == 'volume_work':
|
||||
return -(np.array(ame.series(m['pressurePath']))+101300)*rate
|
||||
return rate
|
||||
Reference in new issue
Block a user