56 lines
3.4 KiB
Python
56 lines
3.4 KiB
Python
"""Native event integration checked against independent Amesim 2404 runs."""
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
import numpy as np
|
|
from app.simulation.native_codegen.compiler import compile_native_program
|
|
from app.simulation.native_codegen.build import build_native
|
|
from app.simulation.native_codegen.result_storage import scan_blocks
|
|
from tests.test_native_catalog import Circuit
|
|
|
|
FIXTURE=Path(__file__).parent/'fixtures/amesim-component-semantics-2404.json'
|
|
|
|
class NativeFrictionReferenceTests(unittest.TestCase):
|
|
def test_amesim_stick_slip_endstops_and_joint_circuits(self):
|
|
for case in json.loads(FIXTURE.read_text(encoding='utf-8'))['cases']:
|
|
b=Circuit()
|
|
for c in case['components']:b.add(c['type'],c['name'],**c['parameters'])
|
|
for l,r in case['connections']:b.net.connect(*l,*r)
|
|
program=compile_native_program(b.net)
|
|
build=build_native(program)
|
|
self.addCleanup(build.close)
|
|
methods=('BDF','RK45') if case['name'] in ('friction_simple','friction_band') else ('BDF',)
|
|
for method in methods:
|
|
with self.subTest(case=case['name'],method=method),tempfile.TemporaryDirectory() as tmp:
|
|
path=Path(tmp)/'result.json'
|
|
state_path=Path(tmp)/'states.bin';output_path=Path(tmp)/'outputs.bin'
|
|
subprocess.run([str(build.executable),'--method',method,'--stop','3','--sample-step','.001',
|
|
'--max-step','.002','--rtol','1e-9','--timeout','30','--output',str(path),
|
|
'--sample-file',str(state_path),'--output-block-file',str(output_path)],
|
|
check=True,capture_output=True,timeout=45)
|
|
result=json.loads(path.read_text())
|
|
self.assertTrue(result['success'],result)
|
|
self.assertEqual(result['simulatedUntil'],3)
|
|
for blocks, columns in ((state_path,len(program.state_keys)+1),
|
|
(output_path,len(program.variables)+1)):
|
|
stored=scan_blocks(blocks,expected_columns=columns)
|
|
self.assertGreater(stored['blocks'],1)
|
|
self.assertEqual(stored['samples'],len(result['series']['time']))
|
|
self.assertFalse(stored['incompleteTail'] or stored['corrupt'])
|
|
for key,expected in case['series'].items():
|
|
actual=np.interp(case['time'],result['series']['time'],result['series'][key])
|
|
field=key.split('.')[-1]
|
|
atol=1 if field.startswith('p') else .001 if field.startswith('T') else .002 if field in ('a','Ffric','Fmin','Fmax','force') else 3e-6
|
|
np.testing.assert_allclose(actual,expected,rtol=0,atol=atol,err_msg=key)
|
|
if case['name'].startswith('friction_'):
|
|
# Stored mode makes result replay independent of query order.
|
|
state=result['finalState']
|
|
inputs=[' '.join(map(str,[t,*state])) for t in (2.8,.1,2.8)]
|
|
rows=subprocess.run([str(build.executable),'--probe'],input='\n'.join(inputs)+'\n',
|
|
text=True,capture_output=True,check=True,timeout=15).stdout.splitlines()
|
|
self.assertEqual(json.loads(rows[0]),json.loads(rows[2]))
|
|
|
|
if __name__=='__main__':unittest.main()
|