高温氦气物性补全;三通四通能量计算bug修正

This commit is contained in:
ljz committed 2026-09-15 17:57:20 +08:00
1 parent 6fc9afe41d
commit 2b07d996cf
133 files changed
+5230 -253310

No files matched your search

+55 -8
View File
@@ -9,6 +9,7 @@ import json
import math
import re
from importlib import import_module
from graphlib import TopologicalSorter
from .compiler import NativeCapabilityError, NativeProgram, _Groups, _number as num
from .contracts import SUPPORTED_VERSIONS
@@ -144,6 +145,8 @@ def compile_extended_program(network):
thermal = 'T' if is_polytropic(c) else 'U'
fields = ('m1', thermal+'1', 'm2', thermal+'2') if c.model_type == 'amesim_pnl0003' else ('m', thermal)
add_states(c, fields, [0.0]*len(fields))
if c.model_type == 'amesim_pnch012' and sum(c.external_volume_rates.values()) != 0:
add_states(c, ('_volume_displacement',), (0.0,))
elif c.model_type == 'amesim_mecmas21':
root = groups.find((c.name, 'port_1'))
mass_groups.setdefault(root, []).append(c)
@@ -257,6 +260,30 @@ def compile_extended_program(network):
if temperature and obj.model_type in NODES-{'tee'}:
other = adjacent[(obj.name, 'port_2')]
return f'h[{pi[other]}]'
# Reference nodes aggregate branch quantities towards port 2. Follow this
# directed contract, not the undirected pressure group (which can span
# unrelated volumes). Port-supply validation has already rejected cycles.
reference_nodes = {c.name: c for c in components if c.model_type in NODES-{'tee'}}
node_graph = {c.name: [other[0] for name in pnames(c) if name != 'port_2'
for other in [adjacent[ep(c, name)]]
if other[0] in reference_nodes and other[1] == 'port_2']
for c in reference_nodes.values()}
node_order = [reference_nodes[name] for name in TopologicalSorter(node_graph).static_order()]
volume_sources = {ep(c, 'port_1'): [c] for c in pistons}
node_energy = {ep(c, 'port_2'): f'node_energy[{i}]' for i, c in enumerate(node_order)}
for c in node_order:
volume_sources[ep(c, 'port_2')] = [source for name in pnames(c) if name != 'port_2'
for source in volume_sources.get(adjacent[ep(c, name)], ())]
def energy_into(c, name, outflow_h):
other = adjacent[ep(c, name)]
# Net mass flow can be zero while the sum of branch enthalpy flows
# remains nonzero. Carry that signed energy directly, without H/q.
if other in node_energy:
return node_energy[other]
return f'{q(c,name)}*({q(c,name)}>0?{hin(c,name)}:{outflow_h})'
gases, anchor, port_gas, volume_rate = {}, {}, {}, {}
anchor_partitions = {}
gas_count = 0
@@ -267,11 +294,19 @@ def compile_extended_program(network):
if kind == 'amesim_pnch023':
volume, rate = num(c.cvol), '0.0'
elif kind == 'amesim_pnch012':
attached = [d for d in pistons if adjacent[ep(d,'port_1')][0] == c.name]
attached = [source for name in pnames(c)
for source in volume_sources.get(adjacent[ep(c, name)], ())]
volume = num(c.cvol0+sum(c.external_volumes.values()))+''.join('+'+w(d,'volume') for d in attached)
if (c.name, '_volume_displacement') in states:
volume += '+'+y(c, '_volume_displacement')
target = f'dy[{states[c.name,"_volume_displacement"]}]'
expr = num(sum(c.external_volume_rates.values()))
lines.append(f'{target}={expr};')
dependencies.expression(target, expr)
put(c, 'vol', f'fmax({num(c.cvol0/100)}, {volume})')
rate = num(sum(c.external_volume_rates.values()))+''.join('+'+w(d,'volume_flow') for d in attached)
put(c, 'dvol', f'{w(c,"vol")} <= {num(c.cvol0/100)} ? 0.0 : ({rate})')
limit = num(c.cvol0/100)
put(c, 'dvol', f'({volume}) < {limit} || (({volume}) <= {limit} && ({rate}) < 0) ? 0.0 : ({rate})')
volume, rate = w(c,'vol'), w(c,'dvol')
elif kind in ('cylinder', 'tank'):
volume, rate = num(c.V), '0.0'
@@ -445,7 +480,8 @@ def compile_extended_program(network):
elif kind in NODES:
if kind!='tee':
# Reference copies have no flow dependency. Keep them separate
# from the reference port's returned energy/mixing calculation.
# from the reference port's finite h_outflow diagnostic below;
# actual reference energy is carried by node_energy, not q*h.
for name in names:
if name!='port_2':
operations.append(Computation.assignment(f'alias:{c.name}.{name}',h(c,name),hin(c,'port_2'),'alias'))
@@ -493,6 +529,13 @@ def compile_extended_program(network):
dependencies.expression(target, expr)
schedule_helpers, scheduled_lines=schedule.emit()
lines += scheduled_lines
if node_order:
lines.append(f'double node_energy[{len(node_order)}];')
for c in node_order:
target = node_energy[ep(c, 'port_2')]
expr = ' + '.join(energy_into(c, name, h(c, name)) for name in pnames(c) if name != 'port_2')
lines.append(f'{target}={expr};')
dependencies.expression(target, expr)
for c in components:
for name in pnames(c):
for field,expr in [('p',p(c,name)),('m_flow',q(c,name)),('h_outflow',h(c,name))]:
@@ -612,7 +655,7 @@ def compile_extended_program(network):
gas=gases[c.name,half];suffix=str(half) if half else ''
names=['port_'+suffix] if half else pnames(c)
mass=' + '.join(q(c,name) for name in names)
energy=' + '.join(f'{q(c,name)}*({q(c,name)}>0?{hin(c,name)}:{gas}.h)' for name in names)
energy=' + '.join(energy_into(c, name, gas+'.h') for name in names)
if half:
sign='-' if half==1 else '+'
mass+=sign+center
@@ -682,11 +725,12 @@ def compile_extended_program(network):
*jacobian.source_lines(),
*schedule_helpers,
'int model_init(double *y) {',*[f'y[{i}]={num(v)};' for i,v in enumerate(initial)],*gas_initializers,'return 1;}',
'static int model_eval_internal(double t,const double *y,double *dy,double *w,int canonical,double *friction_drives) {',
'static int model_eval_internal(double t,const double *y,double *dy,double *w,int canonical,double *friction_drives,NativePropertyTemperatures *temperatures) {',
'(void)friction_drives;',
f'NativePropertyState property_states[{min(256,max(16,4*gas_count+2*len(components)))}];',
'NativePropertyCache property_cache, *properties=&property_cache;',
'native_properties_init(properties,property_states,sizeof(property_states)/sizeof(property_states[0]));',
'properties->temperatures=temperatures;',
'NativePropertyCache *gas_properties=canonical?NULL:properties;(void)gas_properties;',
*(['double projected[NSTATES];for(int i=0;i<NSTATES;i++) projected[i]=y[i];',*project,'y=projected;'] if project else []),
f'double p[{np}]={{0}},h[{nq}]={{0}},q[{nq}]={{0}};NativeGas g[{ng}];',
@@ -695,13 +739,16 @@ def compile_extended_program(network):
'(void)t;(void)y;(void)w;(void)p;(void)h;(void)q;(void)g;',*lines,
'for(int i=0;i<NSTATES;i++) if(!isfinite(dy[i])) return 0;',
'for(int i=0;i<NOUTPUTS;i++) if(!isfinite(w[i])) return 0;','return 1;}',
'int model_eval(double t,const double *y,double *dy,double *w) {return model_eval_internal(t,y,dy,w,0,NULL);}',
'int model_eval_jacobian(double t,const double *y,double *dy,double *w) {return model_eval_internal(t,y,dy,w,1,NULL);}',
'int model_friction_drives(double t,const double *y,double *drives) {double dy[NSTATES],w[NOUTPUTS];return model_eval_internal(t,y,dy,w,0,drives);}',
'int model_eval(double t,const double *y,double *dy,double *w) {return model_eval_internal(t,y,dy,w,0,NULL,NULL);}',
'int model_eval_jacobian(double t,const double *y,double *dy,double *w) {return model_eval_internal(t,y,dy,w,1,NULL,NULL);}',
'int model_friction_drives(double t,const double *y,double *drives) {double dy[NSTATES],w[NOUTPUTS];return model_eval_internal(t,y,dy,w,0,drives,NULL);}',
'int model_property_temperatures(double t,const double *y,NativePropertyTemperatures *temperatures) {double dy[NSTATES],w[NOUTPUTS];return model_eval_internal(t,y,dy,w,0,NULL,temperatures);}',
'double model_next_break(double t,double end) { double result=end;(void)t;',*breaks,'return result;}',''])
header=f'''#ifndef GENERATED_NATIVE_MODEL_H
#define GENERATED_NATIVE_MODEL_H
#include "kernels.h"
#define MODEL_PROPERTY_TEMPERATURES {int(any(key[1] == 'peng_robinson' for key in media))}
int model_property_temperatures(double t,const double *y,NativePropertyTemperatures *temperatures);
#define NSTATES {nstates}
#define NOUTPUTS {max(1,len(variables))}
#define NSTOPS {len(stops)}