66 lines
3.9 KiB
C++
66 lines
3.9 KiB
C++
#pragma once
|
|
#include "FluidSim/Fluid.hpp"
|
|
#include <span>
|
|
|
|
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<int>(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<const PipeSection> 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<float> 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));
|
|
}
|
|
|
|
// Uniform cells over the full arclength, integrating every profile section that
|
|
// a cell crosses. Route/profile knots do not force short cells; gas volume is
|
|
// still the exact sum of the piecewise frustums at any mesh density.
|
|
inline Pipe1D make_uniform_profiled_pipe(std::span<const PipeSection> 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");
|
|
double total=0;float previous=sections.front().inlet_diameter;
|
|
for(const auto& s:sections){
|
|
if(!std::isfinite(s.length)||!std::isfinite(s.inlet_diameter)||!std::isfinite(s.outlet_diameter)||s.length<=0||s.inlet_diameter<=0||s.outlet_diameter<=0||std::abs(s.inlet_diameter-previous)>1e-6f)throw std::invalid_argument("invalid/discontinuous pipe profile");
|
|
total+=s.length;previous=s.outlet_diameter;
|
|
}
|
|
const int count=profile_section_cells(float(total),maximum_cell_length);const double dx=total/count;
|
|
std::vector<float> areas;areas.reserve(count);std::vector<float> lengths(count,float(dx));
|
|
std::size_t section=0;double section_start=0,section_end=sections.front().length;
|
|
for(int i=0;i<count;++i){
|
|
double cursor=i*dx,volume=0;const double end=i+1==count?total:(i+1)*dx;
|
|
while(cursor<end){
|
|
while(section+1<sections.size()&&cursor>=section_end){section_start=section_end;section_end+=sections[++section].length;}
|
|
const auto& s=sections[section];const double stop=std::min(end,section_end);
|
|
const double d0=std::lerp(double(s.inlet_diameter),double(s.outlet_diameter),std::clamp((cursor-section_start)/s.length,0.,1.));
|
|
const double d1=std::lerp(double(s.inlet_diameter),double(s.outlet_diameter),std::clamp((stop-section_start)/s.length,0.,1.));
|
|
volume+=double(pi)/12*(stop-cursor)*(d0*d0+d0*d1+d1*d1);cursor=stop;
|
|
}
|
|
areas.push_back(float(volume/dx));
|
|
}
|
|
return Pipe1D(std::move(areas),std::move(lengths));
|
|
}
|
|
} // namespace fluidsim::port
|