164 lines
8.1 KiB
C
164 lines
8.1 KiB
C
#include "component_properties_internal.h"
|
|
#include "component_constants_internal.h"
|
|
#include <math.h>
|
|
|
|
static double pipe_rough_limit(double rr) {
|
|
return rr>0 ? 1/pow(-2*log10(rr/3.7),2) : 0;
|
|
}
|
|
static double pipe_friction_prepared(double re, double rr, double rough_limit) {
|
|
if(re<=0) return 64000000;
|
|
double lam=64/re;
|
|
if(re<=89.96829989) return lam;
|
|
double smooth=1/pow(-1.8*log10(6.9/re),2),turb=smooth;
|
|
if(rr>0) { double r=re*rr,weight=r*r/(r*r+180*180);turb+=weight*(rough_limit-smooth); }
|
|
double trans=pow((re-89.96829989)/2741.96700831,8.37293695);
|
|
return lam+trans/(1+trans)*(turb-lam);
|
|
}
|
|
static double pipe_friction(double re, double rr) {
|
|
return pipe_friction_prepared(re,rr,pipe_rough_limit(rr));
|
|
}
|
|
/* Derivative of the existing friction blend, local to this scalar solve. */
|
|
static double pipe_friction_derivative(double re,double rr,double rough,double *df) {
|
|
double lam=64/re,dl=-lam/re;
|
|
if(re<=89.96829989){*df=dl;return lam;}
|
|
double a=-1.8*log10(6.9/re),smooth=1/(a*a);
|
|
double ds=-2*smooth/a*1.8/(log(10.0)*re),turb=smooth,dt=ds;
|
|
if(rr>0){double r=re*rr,w=r*r/(r*r+180*180),dw=2*w*(1-w)/re;
|
|
turb+=w*(rough-smooth);dt=ds*(1-w)+dw*(rough-smooth);}
|
|
double z=pow((re-89.96829989)/2741.96700831,8.37293695),b=z/(1+z);
|
|
double db=8.37293695*b*(1-b)/(re-89.96829989);
|
|
*df=dl*(1-b)+b*dt+db*(turb-lam);return lam+b*(turb-lam);
|
|
}
|
|
static double pipe_checked_solution(double re,double f,double K,double rr,double rough,
|
|
double flow_per_re,NativePipeSolve *status) {
|
|
double target=sqrt(K/f),change=fabs(target-re);
|
|
/* Separate the absolute and relative tests so a large flow scale cannot
|
|
make both sides overflow and accidentally satisfy the stopping test. */
|
|
if(!isfinite(target) || !isfinite(target*flow_per_re) ||
|
|
!(change<=re*1e-10 || change*flow_per_re<=1e-13))return NAN;
|
|
double value=target*target*pipe_friction_prepared(target,rr,rough);
|
|
status->relative_residual=fabs(value/K-1);
|
|
if(isfinite(value) && status->relative_residual<=1e-9) {
|
|
status->converged=1;return target;
|
|
}
|
|
return NAN;
|
|
}
|
|
double native_pipe_resistance(double K,double rr,double flow_per_re,NativePipeSolve *status) {
|
|
NativePipeSolve local={0,0,0,INFINITY};if(!status)status=&local;
|
|
*status=local;
|
|
if(!(K>=0 && rr>=0 && flow_per_re>0) || !isfinite(K) || !isfinite(rr) || !isfinite(flow_per_re))return NAN;
|
|
if(K==0){status->converged=1;status->relative_residual=0;return 0;}
|
|
if(K/64<=89.96829989) {
|
|
double re=K/64;
|
|
status->relative_residual=fabs(64*re/K-1);
|
|
if(re>0 && isfinite(re*flow_per_re) && status->relative_residual<=1e-9) {
|
|
status->converged=1;return re;
|
|
}
|
|
return NAN;
|
|
}
|
|
double rough=pipe_rough_limit(rr),lo=0,hi=fmax(sqrt(K/.02),1),df;
|
|
if(!isfinite(rough) || !isfinite(hi))return NAN;
|
|
int bracketed=0;
|
|
for(int i=0;i<128;i++) {
|
|
double value=hi*hi*pipe_friction_prepared(hi,rr,rough);
|
|
if(!isfinite(value))return NAN;
|
|
if(value>=K){bracketed=1;break;}
|
|
lo=hi;hi*=2;
|
|
if(!isfinite(hi))return NAN;
|
|
}
|
|
/* F(0)=-K; each positive lower endpoint was checked while expanding.
|
|
Never start iteration with an unchecked or non-finite upper residual. */
|
|
if(!bracketed)return NAN;
|
|
double re=fmax(lo,fmin(sqrt(K/.02),hi)),previous_residual=INFINITY;
|
|
int previous_newton=0;
|
|
for(int i=0;i<128;i++) {
|
|
status->iterations=i+1;
|
|
double f=pipe_friction_derivative(re,rr,rough,&df),value=re*re*f,F=value-K;
|
|
if(!(f>0) || !isfinite(f) || !isfinite(value))return NAN;
|
|
status->relative_residual=fabs(value/K-1);
|
|
double target=pipe_checked_solution(re,f,K,rr,rough,flow_per_re,status);
|
|
if(status->converged)return target;
|
|
if(F>0)hi=re;else lo=re;
|
|
double slope=2*re*f+re*re*df;
|
|
double next=slope>0 && isfinite(slope) ? re-F/slope : NAN;
|
|
/* An in-bracket Newton step is useful only if it reduces |F|.
|
|
After a step that fails to halve it, bisect the retained bracket;
|
|
successive Newton steps therefore cannot stagnate near an endpoint. */
|
|
int bisect=(previous_newton && fabs(F)>.5*previous_residual) ||
|
|
!(slope>0) || !isfinite(slope) || !isfinite(next) || next<=lo || next>=hi;
|
|
if(bisect) {
|
|
next=lo+.5*(hi-lo);status->bisections++;
|
|
if(next<=lo || next>=hi) {
|
|
/* Adjacent floating-point endpoints: inspect both actual
|
|
candidates, then fail if neither meets the original tests. */
|
|
double endpoints[]={lo,hi};
|
|
for(int j=0;j<2;j++) {
|
|
double endpoint=endpoints[j];
|
|
target=pipe_checked_solution(endpoint,pipe_friction_prepared(endpoint,rr,rough),
|
|
K,rr,rough,flow_per_re,status);
|
|
if(status->converged)return target;
|
|
}
|
|
return NAN;
|
|
}
|
|
}
|
|
previous_newton=!bisect;previous_residual=fabs(F);
|
|
re=next;
|
|
}
|
|
return NAN;
|
|
}
|
|
double native_pipe_flow(const NativeMedium *m, double p1, double p2, double T,
|
|
double d, double length, double rr, int kind) {
|
|
return native_pipe_flow_context(NULL,m,p1,p2,T,d,length,rr,kind);
|
|
}
|
|
double native_pipe_flow_context(NativePropertyCache *cache,const NativeMedium *m,double p1,double p2,double T,
|
|
double d,double length,double rr,int kind) {
|
|
if(fabs(p1-p2)<=1e-8) return 0;
|
|
double p=fmax(fmax(p1,p2),1),pd=fmin(p1,p2),sign=p1>p2?1:-1;
|
|
T=fmax(T,1);
|
|
NativePropertyState scratch,*up=property_pt(cache,m,p,T,&scratch);
|
|
double area=PI*d*d/4,mu=property_viscosity(up),den=PI*d*mu;
|
|
/* PNL0001/2/3 share compressible flow and its near-equilibrium smoothing.
|
|
PNL0003 differs in storage placement, not in the resistance law. */
|
|
double cm,vel;state_valve(cache,up,pd,&cm,&vel);
|
|
if(kind==0) {
|
|
double lam=pow(area*p*cm,2)/(16*PI*mu*length*T);
|
|
if(4*lam/den<=1000) return sign*lam;
|
|
}
|
|
double base=area*p*cm/sqrt(T),K=pow(4*base/den,2)*d/length;
|
|
return sign*native_pipe_resistance(K,rr,den/4,NULL)*den/4;
|
|
}
|
|
double native_pipe_flow_cached(NativePipeCache *cache, const NativeMedium *m,
|
|
double p1, double p2, double T, double d,
|
|
double length, double rr, int kind) {
|
|
return native_pipe_flow_cached_context(NULL,cache,m,p1,p2,T,d,length,rr,kind);
|
|
}
|
|
double native_pipe_flow_cached_context(NativePropertyCache *properties,NativePipeCache *cache,const NativeMedium *m,
|
|
double p1,double p2,double T,double d,double length,double rr,int kind) {
|
|
if(cache->valid && cache->p1==p1 && cache->p2==p2 && cache->T==T &&
|
|
cache->diameter==d && cache->length==length && cache->roughness==rr && cache->kind==kind &&
|
|
same_medium(&cache->medium,m))
|
|
return cache->flow;
|
|
double result=properties?native_pipe_flow_context(properties,m,p1,p2,T,d,length,rr,kind):
|
|
native_pipe_flow(m,p1,p2,T,d,length,rr,kind);
|
|
cache->valid=0;
|
|
if(isfinite(result)) {
|
|
cache->medium=*m;cache->p1=p1;cache->p2=p2;cache->T=T;
|
|
cache->diameter=d;cache->length=length;cache->roughness=rr;cache->kind=kind;
|
|
cache->flow=result;cache->valid=1;
|
|
}
|
|
return result;
|
|
}
|
|
void native_pipe_diagnostics(const NativeMedium *m, double q, double p, double T,
|
|
double d, double length, double rr, int diagnostic, double *r) {
|
|
native_pipe_diagnostics_context(NULL,m,q,p,T,d,length,rr,diagnostic,r);
|
|
}
|
|
void native_pipe_diagnostics_context(NativePropertyCache *cache,const NativeMedium *m,double q,double p,double T,
|
|
double d,double length,double rr,int diagnostic,double *r) {
|
|
(void)diagnostic;
|
|
NativePropertyState scratch,*state=property_pt(cache,m,p,T,&scratch);
|
|
double area=PI*d*d/4,re=4*fabs(q)/(PI*d*property_viscosity(state)),ff=pipe_friction(re,rr);
|
|
r[0]=re;
|
|
r[1]=fabs(q)*sqrt(T)/fmax(sqrt(d/(length*ff))*area*p,1e-18);
|
|
r[2]=q/(fmax(property_density(state),1e-12)*area);r[3]=ff;
|
|
}
|