#include "FluidSim/DesktopScenario.hpp" #include "FluidSim/ScenarioGeometry.hpp" #include #include #include #include #include #include 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;ppipes.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;kpipes[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)})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("")=="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<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<