32 """Darcy f from Colebrook-White, 1/sqrt(f) = -2 log10(eD/3.7 + 2.51/(Re sqrt(f))).
34 Solved to machine precision by fixed-point iteration on x = 1/sqrt(f) from the Haaland
35 (1983) explicit start. This is the fallback for when ``fluids`` is not importable; the
36 tests hold it to fluids.friction.Colebrook. Laminar (Re < 2040) returns 64/Re, as
37 fluids.friction.friction_factor does.
41 if Re < LAMINAR_TRANSITION_PIPE:
44 x = -1.8 * math.log10((eD / 3.7) ** 1.11 + 6.9 / Re)
46 x_new = -2.0 * math.log10(eD / 3.7 + 2.51 * x / Re)
47 if abs(x_new - x) <= 1e-15 * abs(x_new):
79 config: FeedSystemConfig,
81 mu: Optional[float] =
None,
84 Calculate feed system pressure loss using generalized K_eff(P) model.
86 Δp_feed = K_eff(P) × (ρ/2) × (ṁ/(ρ×A_hyd))² + K_exit × (ρ/2) × (ṁ/(ρ×A_exit))²
88 where K_eff(P) = K_base + ΣK_fittings + K1 × φ(P), K_base = K0, or (roughness_m set and mu
89 given) K_entrance + f L/d_inlet with Darcy f from Colebrook-White at Re = ρ v d_inlet/μ;
90 and the second term is the exit bore's velocity head
91 dumped into the manifold (Borda-Carnot; K_exit = 1 for a plenum). P_tank minus this is the
92 still-manifold pressure that drives the orifices.
100 config : FeedSystemConfig
101 Feed system configuration
103 Tank pressure [Pa] (used for pressure-dependent K_eff)
105 Liquid dynamic viscosity [Pa s]. Needed only by the friction path (``roughness_m``
106 set): K0 is replaced by K_entrance + f L/d_inlet with Colebrook f at
107 Re = rho v d_inlet / mu. Without mu that path cannot run and the K0 path is used
110 Fittings (``config.fittings``) add to K_eff on either path.
118 roughness = getattr(config,
"roughness_m",
None)
119 use_friction = roughness
is not None
120 if use_friction
and mu
is None:
122 if not _warned_no_mu:
125 "feed_loss: roughness_m is set but the caller passed no viscosity; using K0 "
126 "(the measured-override path) instead of Colebrook friction.")
127 if use_friction
and not (mu > 0):
128 raise ValueError(f
"Invalid viscosity for feed friction: mu={mu!r} Pa s. Must be > 0.")
133 d = float(config.d_inlet)
134 v_line = mdot / (rho * float(config.A_hydraulic))
if rho > 0
else 0.0
135 Re = rho * v_line * d / float(mu)
137 K_base = float(config.K_entrance) + f * float(config.length) / d
140 if getattr(config,
"fittings",
None):
144 if config.phi_type ==
"none":
146 elif config.phi_type ==
"sqrtP":
148 K_eff = K_base + config.K1 * np.sqrt(
max(0, P_tank))
149 elif config.phi_type ==
"logP":
150 K_eff = K_base + config.K1 * np.log(P_tank)
152 raise ValueError(f
"Unknown phi_type: {config.phi_type}")
156 A_area = float(config.A_hydraulic)
157 d_exit = getattr(config,
"d_exit",
None)
158 A_exit = np.pi * (float(d_exit) / 2.0) ** 2
if d_exit
else A_area
159 K_exit = float(getattr(config,
"K_exit", 0.0)
or 0.0)
163 raise ValueError(f
"Invalid feed system area: A_area={A_area:.6e} m². Must be > 0. Check d_inlet or A_hydraulic in config.")
165 raise ValueError(f
"Invalid fluid density: rho={rho:.2f} kg/m³. Must be > 0.")
167 raise ValueError(f
"Invalid mass flow: mdot={mdot:.4f} kg/s. Must be >= 0.")
170 velocity = mdot / (rho * A_area)
171 v_exit = mdot / (rho * A_exit)
174 delta_p = K_eff * (rho / 2) * velocity**2 + K_exit * (rho / 2) * v_exit**2
177 delta_p =
max(0.0, delta_p)
180 if delta_p == 0.0
and mdot > 0.01:
183 f
"Feed system pressure drop is zero with mdot={mdot:.4f} kg/s, rho={rho:.2f} kg/m³, "
184 f
"A_area={A_area:.6e} m², K_eff={K_eff:.2f}, velocity={velocity:.2f} m/s. "
185 f
"Check feed system configuration."
188 return float(delta_p)