前端进度条性能优化、仿真结束后后处理优化;后端C代码生成流程优化:先识别来源,再按照已知未知量需求排序,最后局部求解

This commit is contained in:
ljz committed 2026-09-11 11:27:54 +08:00
1 parent 0dcb465d84
commit 91bd9fb252
77 files changed
+11223 -551

No files matched your search

+134
View File
@@ -0,0 +1,134 @@
"""Physical regressions for the corrected native pipe and error-control path."""
import json
import math
from pathlib import Path
import subprocess
import tempfile
import unittest
import numpy as np
from app.main import simulation_event_stream
from app.simulation.core.medium import IdealGasMedium
from app.simulation.native_codegen.build import build_native, toolchain
from app.simulation.native_codegen.extended import compile_extended_program
from tests.test_native_catalog import Circuit
ROOT = Path(__file__).resolve().parents[1]
class NativePipePhysicsTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
try:
cls.compiler = toolchain()[0]
except (OSError, RuntimeError, subprocess.SubprocessError) as exc:
raise unittest.SkipTest(f'Native toolchain unavailable: {exc}')
def test_compressible_pipe_law_is_symmetric_and_regular_near_equilibrium(self):
harness = r'''
#include <math.h>
#include <stdio.h>
#include "kernels.h"
#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %d\n",__LINE__);return 1; } } while(0)
int main(void) {
NativeMedium medium={0,287,1005,300,0,1.8e-5,300,110.4};
for(int helium=0;helium<2;helium++) {
medium.real_helium=helium;
if(helium) { medium.R=2077.26439404998;medium.cp=5193.1609851249505; }
double last=0;
const double dp[]={0,1e-5,1e-4,1e-3,.01,.1,1,100,1e4,1e5};
for(unsigned i=0;i<sizeof(dp)/sizeof(dp[0]);i++) {
double q=native_pipe_flow(&medium,2e5+dp[i],2e5,300,.01,1,1e-5,3);
double reversed=native_pipe_flow(&medium,2e5,2e5+dp[i],300,.01,1,1e-5,3);
CHECK(isfinite(q) && q>=last && reversed==-q);
CHECK(q==native_pipe_flow(&medium,2e5+dp[i],2e5,300,.01,1,1e-5,1));
if(i==0) CHECK(q==0);
if(dp[i]<=.01) CHECK(q<=1e-7);
last=q;
}
for(double T=200;T<=1000;T+=100) {
double mu=native_viscosity(&medium,T,0);
CHECK(mu>0 && mu==native_viscosity(&medium,T,1));
if(!helium && T==300) CHECK(fabs(mu-1.8e-5)<1e-18);
double d[4],q=.0001;
native_pipe_diagnostics(&medium,q,2e5,T,.01,1,1e-5,1,d);
CHECK(fabs(d[0]*mu-4*q/(3.14159265358979323846*.01))<1e-12);
}
}
return 0;
}
'''
with tempfile.TemporaryDirectory(prefix='native-pipe-physics-') as tmp:
directory=Path(tmp); source=directory/'check.c'; exe=directory/'check.exe'
source.write_text(harness)
build=subprocess.run([self.compiler,'-std=c11','-O3','-Wall','-Wextra','-Werror',
'-I',str(ROOT/'native/include'),str(source),str(ROOT/'native/components/kernels.c'),
'-lm','-o',str(exe)],capture_output=True,text=True,timeout=60)
self.assertEqual(build.returncode,0,build.stderr)
run=subprocess.run([str(exe)],capture_output=True,text=True,timeout=15)
self.assertEqual(run.returncode,0,run.stderr)
def test_pnl0001_uses_upstream_temperature_in_both_directions(self):
for reverse in (False,True):
with self.subTest(reverse=reverse):
circuit=Circuit(IdealGasMedium())
left=circuit.chamber('left',p0=1e5 if reverse else 2e5,T0=300,kth=0)
pipe=circuit.add('amesim_pnl0001','pipe',p0=2e5 if reverse else 1e5,
T0=300,diam=.01,le=1,rr=1e-5,kth=0)
circuit.connect(left,'port_1',pipe,'port_1')
program=compile_extended_program(circuit.seal()); build=build_native(program)
initial=json.loads(subprocess.run([str(build.executable),'--init'],
capture_output=True,text=True,check=True,timeout=15).stdout)
states=[initial]
# For ideal gas, at fixed U and volume, halving mass doubles
# temperature while preserving pressure. Vary each side alone.
for name in ('left','pipe'):
state=initial.copy(); state[program.state_keys.index(name+'.m')]/=2
states.append(state)
inputs=''.join('0 '+' '.join(map(str,state))+'\n' for state in states)
run=subprocess.run([str(build.executable),'--probe'],input=inputs,
capture_output=True,text=True,check=True,timeout=15)
rows=[json.loads(line) for line in run.stdout.splitlines()]
outputs=[dict(zip((v.key for v in program.variables),row['outputs'])) for row in rows]
q=[out['pipe.port_1.m_flow'] for out in outputs]
self.assertTrue(all(row['success'] for row in rows))
self.assertTrue(all(flow<0 if reverse else flow>0 for flow in q))
upstream=2 if reverse else 1; downstream=1 if reverse else 2
self.assertLess(abs(q[upstream]),abs(q[0])*.9)
self.assertAlmostEqual(q[downstream],q[0],places=12)
self.assertAlmostEqual(outputs[downstream]['pipe.re'],outputs[0]['pipe.re'],places=8)
for row in rows:
for field in ('m','U'):
rates=[value for key,value in zip(program.state_keys,row['rhs']) if key.endswith('.'+field)]
self.assertLess(abs(sum(rates)),1e-10+sum(map(abs,rates))*1e-12)
def test_web_stream_completes_mql4_and_matches_amesim_reference(self):
reference=json.loads((ROOT/'tests/data/test-mql-4-amesim-reference.json').read_text())
xml=(ROOT/'tests/data/test-mql-4-corrected.xml').read_bytes()
events=[json.loads(line) for line in simulation_event_stream(xml)]
self.assertFalse([e for e in events if e['event']=='error'])
self.assertTrue(any(e['event']=='progress' for e in events))
result=next(e['result'] for e in events if e['event']=='result')
self.assertTrue(result['success'],result['message'])
self.assertEqual(result['simulatedUntil'],10)
self.assertEqual(result['diagnostics']['backend'],'native-c')
self.assertEqual(result['diagnostics']['integration']['rtol'],1e-7)
self.assertEqual(result['diagnostics']['integration']['method'],'BDF')
self.assertEqual(result['diagnostics']['stateCount'],64)
# Bound the formerly stalled tiny-step failure by work, not machine time.
self.assertLess(result['diagnostics']['native']['nfev'],60000)
series=result['series']; times=series['time']
self.assertTrue(all(math.isfinite(v) for values in series.values() for v in values))
limits={'pressure':250,'temperature':.015,'displacement':2e-6,
'velocity':1e-5,'mass_flow':3e-5}
for key,ref in reference['series'].items():
actual=np.interp(reference['times'],times,series[key])
error=float(np.max(np.abs(actual-ref['values'])))
self.assertLessEqual(error,limits[ref['quantity']],(key,error))
masses=[v for k,v in series.items() if k.rsplit('.',1)[-1] in ('m','m1','m2')]
total=np.sum(masses,axis=0)
self.assertLess(float(np.max(abs(total-total[0]))),1e-10)
if __name__=='__main__':
unittest.main()