"""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