前端进度条性能优化、仿真结束后后处理优化;后端C代码生成流程优化:先识别来源,再按照已知未知量需求排序,最后局部求解
This commit is contained in:
1 parent
0dcb465d84
commit
91bd9fb252
77 files changed
+11223
-551
No files matched your search
@@ -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()
|
||||
Reference in new issue
Block a user