Improve physical geometry, fluid solvers, audio, and profiling
This commit is contained in:
67
tests/physical_geometry_tests.cpp
Normal file
67
tests/physical_geometry_tests.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "FluidSim/DesktopScenario.hpp"
|
||||
#include "FluidSim/ScenarioGeometry.hpp"
|
||||
#include <toml.hpp>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
using namespace fluidsim::port;
|
||||
void check(bool value,const char* message){if(!value)throw std::runtime_error(message);}
|
||||
int main() try {
|
||||
for(const auto& e:scenario_examples()) {
|
||||
DesktopScenario scenario(e);const auto state=scenario.snapshot();
|
||||
check(bool(state.geometry),"scenario lacks physical geometry");const auto routes=scenario_pipe_routes(state);
|
||||
for(std::size_t p=0;p<state.geometry->pipes.size();++p) {
|
||||
const float mesh=std::accumulate(state.pipe_lengths[p].begin(),state.pipe_lengths[p].end(),0.f);
|
||||
check(std::abs(mesh-state.geometry->pipes[p].length())<1e-5f*std::max(1.f,mesh),"fluid length differs from render centerline");
|
||||
check(routes[p].count==state.geometry->pipes[p].points.size(),"renderer lost a bend");
|
||||
for(unsigned k=0;k<routes[p].count;++k)check(geometry_distance(routes[p].points[k],state.geometry->pipes[p].points[k])<1e-7f,"render route differs from input geometry");
|
||||
}
|
||||
check(!state.storage_volumes.empty(),"storage volumes missing from rendering snapshot");
|
||||
for(const auto& v:state.storage_volumes) {
|
||||
check(v.box.volume()>0&&std::isfinite(v.pressure),"invalid physical storage volume");
|
||||
const auto lo=v.box.minimum(),hi=v.box.maximum();int ports=0;
|
||||
for(const auto& p:state.geometry->pipes)for(auto endpoint:{p.points.front(),p.points.back()}) {
|
||||
constexpr float tolerance=2e-6f;
|
||||
const bool inside=endpoint.x>=lo.x-tolerance&&endpoint.x<=hi.x+tolerance&&endpoint.y>=lo.y-tolerance&&endpoint.y<=hi.y+tolerance&&endpoint.z>=lo.z-tolerance&&endpoint.z<=hi.z+tolerance;
|
||||
const bool surface=std::min({std::abs(endpoint.x-lo.x),std::abs(endpoint.x-hi.x),std::abs(endpoint.y-lo.y),std::abs(endpoint.y-hi.y),std::abs(endpoint.z-lo.z),std::abs(endpoint.z-hi.z)})<tolerance;
|
||||
if(inside&&surface)++ports;
|
||||
}
|
||||
if(ports<2)throw std::runtime_error(e.id+": storage volume has disconnected pipe ports: "+v.id);
|
||||
}
|
||||
for(int i=0;i<state.cylinder_count;++i) {
|
||||
const auto& c=state.geometry->cylinders[i];const float angle=-c.tilt_degrees*pi/180;
|
||||
const auto attached=[&](Vec3 p) {
|
||||
p={p.x-c.origin.x,p.y-c.origin.y,p.z-c.origin.z};
|
||||
const float y=p.y*std::cos(angle)-p.z*std::sin(angle),z=p.y*std::sin(angle)+p.z*std::cos(angle);
|
||||
return std::abs(p.x)<=state.cylinder_bore*.65f&&std::abs(z)<=state.cylinder_bore*.65f&&y>=state.cylinder_rod-state.cylinder_stroke*.5f-.005f&&y<=state.cylinder_rod+state.cylinder_stroke*.5f+state.cylinder_clearance/(pi*state.cylinder_bore*state.cylinder_bore*.25f)+.005f;
|
||||
};
|
||||
const auto& intake=state.geometry->pipes[state.two_stroke?2:1+i];
|
||||
const auto& exhaust=state.geometry->pipes[state.two_stroke?3:1+state.cylinder_count+i];
|
||||
if(!attached(intake.points.back())||!attached(exhaust.points.front()))throw std::runtime_error(e.id+": cylinder port is disconnected from its geometry");
|
||||
}
|
||||
if(e.kind!=ScenarioKind::TwoStroke){FourStrokeScenario four(44100,e);check(std::abs(four.intake_plenum.volume()-e.engine.geometry.volume("intake_plenum")->volume())<1e-8f,"plenum simulation ignores geometry");}
|
||||
}
|
||||
const auto average_primary=[](const ScenarioExample& e){float sum=0;for(int i=0;i<e.engine.cylinders;++i)sum+=e.engine.geometry.pipes[1+e.engine.cylinders+i].length();return sum/e.engine.cylinders;};
|
||||
check(average_primary(scenario_example("v8-short"))<average_primary(scenario_example("v8-h"))&&average_primary(scenario_example("v8-h"))<average_primary(scenario_example("v8-long")),"short and long header variants lost their physical distinction");
|
||||
auto table=toml::parse_file(scenario_example("single-450").source_path.string());
|
||||
auto& volumes=*table["geometry"]["volumes"].as_array();
|
||||
toml::table* plenum_table=nullptr;
|
||||
for(auto& v:volumes)if((*v.as_table())["id"].value_or<std::string>("")=="intake_plenum")plenum_table=v.as_table();
|
||||
check(plenum_table!=nullptr,"test volume not found");auto& plenum=*plenum_table;
|
||||
plenum.erase("size_mm");plenum.erase("center_mm");
|
||||
plenum.insert_or_assign("corners_mm",toml::array{toml::array{100.,200.,300.},toml::array{0.,0.,0.}});
|
||||
auto& pipe=*table["geometry"]["pipes"].as_array()->get(0)->as_table();
|
||||
pipe.insert_or_assign("points_mm",toml::array{toml::array{0.,0.,0.},toml::array{30.,40.,0.},toml::array{30.,40.,120.}});
|
||||
const auto file=std::filesystem::temp_directory_path()/("fluidsim-geometry-"+std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())+".toml");
|
||||
const auto write=[&]{{std::ofstream out(file);out<<table;}};
|
||||
write();const auto loaded=load_engine_file(file);FourStrokeScenario changed(44100,loaded);
|
||||
check(std::abs(changed.intake_plenum.volume()-.006f)<1e-8f,"corner-defined volume conversion is wrong");
|
||||
check(geometry_distance(loaded.engine.geometry.volume("intake_plenum")->center,{.05f,.1f,.15f})<1e-7f,"corner center is wrong");
|
||||
check(std::abs(std::accumulate(changed.pipes[0].cell_lengths.begin(),changed.pipes[0].cell_lengths.end(),0.f)-.17f)<1e-6f,"path length does not drive simulation");
|
||||
pipe.insert_or_assign("points_mm",toml::array{toml::array{0.,0.,0.},toml::array{0.,0.,0.}});write();
|
||||
bool rejected=false;try{load_engine_file(file);}catch(const std::exception& e){rejected=std::string(e.what()).find(file.filename().string())!=std::string::npos;}
|
||||
std::filesystem::remove(file);check(rejected,"degenerate geometry accepted or missing file diagnostic");
|
||||
std::cout<<"All scenario centerlines, storage dimensions, corner volumes and invalid geometry passed\n";
|
||||
}catch(const std::exception& e){std::cerr<<e.what()<<'\n';return 1;}
|
||||
Reference in New Issue
Block a user