#pragma once #include "FluidSim/Fluid.hpp" #include namespace fluidsim::port { struct PipeSection { float length, inlet_diameter, outlet_diameter; }; inline int profile_section_cells(float length,float maximum_cell_length) { const double count=double(length)/maximum_cell_length; return std::max(1,static_cast(std::ceil(count-1e-6*std::max(1.,count)))); } // Linear diameter within each section. Cell area is the exact frustum-volume // average, so refining the mesh preserves the specified chamber volume. inline Pipe1D make_profiled_pipe(std::span sections, float maximum_cell_length = .01f) { if (!std::isfinite(maximum_cell_length) || maximum_cell_length <= 0 || sections.empty()) throw std::invalid_argument("invalid pipe profile mesh"); std::vector areas, lengths; float previous_diameter = sections.front().inlet_diameter; for (const auto& section : sections) { if (!std::isfinite(section.length) || !std::isfinite(section.inlet_diameter) || !std::isfinite(section.outlet_diameter) || section.length <= 0 || section.inlet_diameter <= 0 || section.outlet_diameter <= 0 || std::abs(section.inlet_diameter - previous_diameter) > 1e-6f) throw std::invalid_argument("invalid/discontinuous pipe profile"); const int count = profile_section_cells(section.length,maximum_cell_length); for (int i = 0; i < count; ++i) { const float d0 = std::lerp(section.inlet_diameter, section.outlet_diameter, float(i) / count); const float d1 = std::lerp(section.inlet_diameter, section.outlet_diameter, float(i + 1) / count); areas.push_back(pi / 12 * (d0*d0 + d0*d1 + d1*d1)); lengths.push_back(section.length / count); } previous_diameter = section.outlet_diameter; } return Pipe1D(std::move(areas), std::move(lengths)); } } // namespace fluidsim::port