STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
spray.py
Go to the documentation of this file.
1"""Spray and mixing models (J, We, SMD, x*; TMR and theta are diagnostic only)"""
2
3from __future__ import annotations
4
5import math
6import numpy as np
7from typing import Optional
8from typing import Tuple, List
9from engine.pipeline.config_schemas import SprayConfig
10
11
12def momentum_flux_ratio(rho_O: float, u_O: float, rho_F: float, u_F: float) -> float:
13 """
14 Calculate momentum flux ratio J.
15
16 J = (ρ_O × u_O²) / (ρ_F × u_F²)
17
18 Parameters:
19 -----------
20 rho_O : float
21 Oxidizer density [kg/m³]
22 u_O : float
23 Oxidizer velocity [m/s]
24 rho_F : float
25 Fuel density [kg/m³]
26 u_F : float
27 Fuel velocity [m/s]
28
29 Returns:
30 --------
31 J : float
32 """
33 if u_F == 0:
34 return np.inf if u_O > 0 else 0.0
35
36 J = (rho_O * u_O**2) / (rho_F * u_F**2)
37 return float(J)
38
39
40def thrust_momentum_ratio(J: float, MR: float) -> float:
41 """TMR = J/(1 + MR). DIAGNOSTIC ONLY.
42
43 Not a published parameter: the pintle literature's TMR is the radial-to-axial momentum ratio
44 (mdot_r v_r)/(mdot_a v_a), which this is not. Nothing in the model reads it; it is reported
45 for continuity with old runs.
46 """
47 TMR = J / (1 + MR) if MR > 0 else J
48 return float(TMR)
49
50
51def spray_angle_from_J(J: float, k: float, n: float) -> float:
52 """
53 Calculate spray angle from momentum flux ratio J.
54
55 Uses half-angle tangent form: tan(θ/2) = k × J^n
56
57 PHYSICS INTUITION:
58 ------------------
59 Imagine the spray as a cone coming out of the injector:
60
61 1. **Momentum Vector Decomposition**:
62 - The fuel (RP-1) flows RADIALLY outward (perpendicular to axis)
63 - The oxidizer (LOX) flows AXIALLY (along the axis, with some angle)
64 - When they collide, the combined spray has both radial and axial momentum
65
66 2. **Why Half-Angle?**:
67 - The spray is SYMMETRIC about the axis
68 - We measure angles FROM THE AXIS, not from the opposite edge
69 - A particle at the edge of the spray travels at angle θ/2 from the axis
70 - Its momentum vector makes angle θ/2 with the axis (not θ!)
71 - When we decompose momentum: p_radial = p × sin(θ/2), p_axial = p × cos(θ/2)
72 - Full angle θ = 2×(θ/2) is just a convention - we use θ/2 in calculations
73
74 3. **Why Tangent?**:
75 - In a conical spray, momentum vectors decompose as:
76 * Radial component: p_radial = p_total × sin(θ/2)
77 * Axial component: p_axial = p_total × cos(θ/2)
78 - The RATIO of radial to axial momentum is:
79 p_radial / p_axial = sin(θ/2) / cos(θ/2) = tan(θ/2)
80 - This ratio determines how "spread out" the spray is
81
82 4. **Connection to J**:
83 - J = (ρ_O u_O²) / (ρ_F u_F²) measures relative momentum flux
84 - High J → more oxidizer momentum → spray more axial (smaller angle)
85 - Low J → more fuel momentum → spray more radial (larger angle)
86 - The spray angle emerges from balancing these momentum components
87
88 5. **Why Not Direct θ = k×J^n?**:
89 - Direct form treats angle as arbitrary scalar
90 - But spray angle comes from VECTOR balance of momenta
91 - tan(θ/2) captures this vector relationship naturally
92 - Also provides natural bounds: tan(θ/2) → ∞ as θ → 90°
93
94 Parameters:
95 -----------
96 J : float
97 Momentum flux ratio
98 k : float
99 Model coefficient
100 n : float
101 Model exponent
102
103 Returns:
104 --------
105 theta : float [rad]
106 """
107 if J <= 0:
108 return 0.0
109
110 # Half-angle tangent form: tan(θ/2) = k × J^n
111 # This is the physically meaningful form for spray angles
112 tan_half_theta = k * (J ** n)
113 theta = 2 * np.arctan(tan_half_theta)
114
115 # Clamp to reasonable range [0, π/2] (0-90 degrees)
116 # Note: The tan form naturally limits angles, but we clamp for safety
117 return float(np.clip(theta, 0, np.pi / 2))
118
119
120def spray_angle_from_TMR(TMR: float) -> float:
121 """theta = arccos(1/(1 + TMR^0.75)) [rad]. DIAGNOSTIC ONLY.
122
123 An invented law on the invented TMR above; no source gives it, and no model reads the angle
124 (the injectors publish it as ``theta``). The vaporization march takes its axial drop speed
125 from ``spray_axial_velocity``.
126 """
127 if TMR <= 0:
128 return 0.0
129 cos_theta = np.clip(1 / (1 + TMR**0.75), -1, 1)
130 return float(np.arccos(cos_theta))
131
132
133def weber_number(rho: float, u: float, d_char: float, sigma: float) -> float:
134 """
135 Calculate Weber number.
136
137 We = (ρ × u² × d_char) / σ
138
139 Parameters:
140 -----------
141 rho : float
142 Density [kg/m³]
143 u : float
144 Velocity [m/s]
145 d_char : float
146 Characteristic diameter [m] (port diameter or annulus hydraulic diameter)
147 sigma : float
148 Surface tension [N/m]
149
150 Returns:
151 --------
152 We : float
153 """
154 if sigma <= 0:
155 return np.inf
156
157 We = (rho * u**2 * d_char) / sigma
158 return float(We)
159
160
161def ohnesorge_number(mu: float, rho: float, sigma: float, d_or: float) -> float:
162 """
163 Calculate Ohnesorge number.
164
165 Oh = μ / √(ρ × σ × d_or)
166
167 Parameters:
168 -----------
169 mu : float
170 Dynamic viscosity [Pa·s]
171 rho : float
172 Density [kg/m³]
173 sigma : float
174 Surface tension [N/m]
175 d_or : float
176 Orifice diameter [m]
177
178 Returns:
179 --------
180 Oh : float
181 """
182 if rho <= 0 or sigma <= 0 or d_or <= 0:
183 return 0.0
184
185 # FIXED: Ensure sqrt input is positive
186 sqrt_arg = rho * sigma * d_or
187 Oh = mu / np.sqrt(max(sqrt_arg, 1e-12)) if sqrt_arg > 0 else 0.0
188 return float(Oh)
189
190
192 d_or: float,
193 We: float,
194 Oh: float,
195 C: float,
196 m: float,
197 p: float
198) -> float:
199 """
200 Calculate Sauter Mean Diameter (SMD) using Lefebvre correlation.
201
202 D32 = C × d_or × We^(-m) × (1 + Oh)^p
203
204 Raises ValueError when We or d_or is not positive and finite (it used to return d_or).
205
206 Parameters:
207 -----------
208 d_or : float
209 Orifice diameter [m]
210 We : float
211 Weber number
212 Oh : float
213 Ohnesorge number
214 C : float
215 Model constant
216 m : float
217 Weber exponent
218 p : float
219 Ohnesorge exponent
220
221 Returns:
222 --------
223 D32 : float [m]
224 """
225 if not (np.isfinite(We) and We > 0 and np.isfinite(d_or) and d_or > 0):
226 raise ValueError(f"smd_lefebvre: We={We} and d_or={d_or} must be positive and finite")
227
228 # Viscous damping term uses (1 + Oh)^p, NOT Oh^p. Raw Oh^p is unphysical: for a low-viscosity
229 # liquid (Oh << 1) it drives D32 -> 0 as Oh -> 0, whereas vanishing viscosity must approach the
230 # inviscid limit (no viscous correction => factor -> 1). (1 + Oh)^p -> 1 as Oh -> 0 and grows
231 # with viscosity, which is the correct trend (more viscous => larger drops).
232 D32 = C * d_or * (We ** (-m)) * ((1.0 + Oh) ** p)
233 return float(D32)
234
235
236#: NACA TN 4222 (Ingebo 1958) test conditions: n-heptane jet pairs in an airstream at 82 F and
237#: 29.3 in Hg abs. The correlation carries no property terms, so these are its reference state.
238TN4222_RHO_AIR = 29.3 * 3386.389 / (287.05 * ((82.0 - 32.0) * 5.0 / 9.0 + 273.15)) # 1.149 kg/m^3
239TN4222_HEPTANE_SIGMA = 0.0197 # N/m, n-heptane at 28 C
240TN4222_HEPTANE_MU = 3.8e-4 # Pa s
241TN4222_HEPTANE_RHO = 684.0 # kg/m^3
242#: Nukiyama-Tanasawa distribution TN 4222 fits (number ~ D^2 exp(-bD)): D30 = 3.915/b,
243#: D32 = 5/b, so D32/D30 = 5/3.915.
244TN4222_D32_OVER_D30 = 5.0 / 3.915
245
246
247#: How TN 4222's heptane/air D32 is carried to another liquid and gas (SMDConfig.smd_property_transfer).
248TN4222_PROPERTY_TRANSFERS = ("tn4087", "dombrowski_johns", "none")
249
250
251def tn4222_property_factor(transfer: str, *, rho_liq: float, mu_liq: float, sigma: float,
252 rho_gas: float) -> float:
253 """D32(liquid, gas) / D32(n-heptane, air at TN 4222's conditions), same jets and speeds.
254
255 - "tn4087": Ingebo's crossflow exponents (NACA TN 4087: D30 ~ (We Re)^(-1/4)), so
256 (sigma mu_l / rho_l)^(1/4) rho_g^(-1/4).
257 - "dombrowski_johns": the inviscid sheet-breakup length scale of Dombrowski & Johns (Chem. Eng.
258 Sci. 18, 1963), D ~ (sigma^2 / (rho_g rho_l U^4))^(1/6) at fixed sheet geometry and speed, so
259 (sigma^2 / (rho_g rho_l))^(1/6) relative to heptane/air.
260 - "none": 1 (heptane in air as measured).
261 The spread between them is the uncertainty of carrying a single-liquid correlation to
262 LOX/ethanol at chamber density; a measured D32 (smd_scale) replaces it.
263 """
264 if transfer not in TN4222_PROPERTY_TRANSFERS:
265 raise ValueError(f"TN 4222 property transfer {transfer!r} is not one of {TN4222_PROPERTY_TRANSFERS}")
266 if transfer == "none":
267 return 1.0
268 for name, v in (("rho_liq", rho_liq), ("mu_liq", mu_liq), ("sigma", sigma), ("rho_gas", rho_gas)):
269 if not (np.isfinite(v) and v > 0):
270 raise ValueError(f"tn4222_property_factor: {name}={v} must be positive and finite")
271 if transfer == "tn4087":
272 prop = (sigma * mu_liq / rho_liq) / (TN4222_HEPTANE_SIGMA * TN4222_HEPTANE_MU / TN4222_HEPTANE_RHO)
273 return float((rho_gas / TN4222_RHO_AIR) ** -0.25 * prop ** 0.25)
274 ref = TN4222_HEPTANE_SIGMA ** 2 / (TN4222_RHO_AIR * TN4222_HEPTANE_RHO)
275 return float((sigma ** 2 / (rho_gas * rho_liq) / ref) ** (1.0 / 6.0))
276
277
278def tn4222_transfer_model(smd_cfg) -> str:
279 """The property transfer a spray.smd config asks for: 'none' when smd_property_scaling is off,
280 else smd_property_transfer (default 'tn4087')."""
281 if not bool(getattr(smd_cfg, "smd_property_scaling", True)):
282 return "none"
283 return str(getattr(smd_cfg, "smd_property_transfer", "tn4087") or "tn4087")
284
285
287 d_jet: float,
288 v_jet: float,
289 rho_liq: float,
290 mu_liq: float,
291 sigma: float,
292 rho_gas: float,
293 *,
294 dv: Optional[float] = None,
295 scale_properties: bool = True,
296 transfer: Optional[str] = None,
297) -> float:
298 """Sauter mean diameter of an impinging-jet pair, Ingebo, NACA TN 4222 (1958).
299
300 Dj / D30 = 2.64 (Dj Vj)^(1/2) + 0.97 Dj dV (Dj, D30 in inches; Vj, dV in ft/s)
301 D32 = (5 / 3.915) D30 (Nukiyama-Tanasawa, as fitted)
302
303 ``v_jet`` is the stream's own jet velocity and ``dv`` the jet-to-gas velocity difference; a
304 face-injected jet enters gas at rest, dV = Vj (TN 4222 eq. 11), the default.
305
306 TN 4222 measured n-heptane only. ``transfer`` (see ``tn4222_property_factor``) carries it to
307 other liquids and gas densities; None means "tn4087" when ``scale_properties`` else "none".
308 That transfer is an assumption: measure D32 and set ``spray.smd.smd_scale``.
309
310 Raises ValueError on a non-positive or non-finite input (it used to return d_jet, a drop the
311 size of the orifice, without saying so).
312
313 Replaces an earlier function that cited this report but ran TN 4087's crossflow form with the
314 We*Re product on the jets' closing speed, and read its D30 as D32: about half the drop size
315 TN 4222 measured.
316 """
317 for name, v in (("d_jet", d_jet), ("v_jet", v_jet), ("rho_liq", rho_liq), ("mu_liq", mu_liq),
318 ("sigma", sigma), ("rho_gas", rho_gas)):
319 if not (np.isfinite(v) and v > 0):
320 raise ValueError(f"smd_impinging_tn4222: {name}={v} must be positive and finite")
321 if transfer is None:
322 transfer = "tn4087" if scale_properties else "none"
323 dj_in = d_jet / 0.0254
324 vj = v_jet / 0.3048
325 dV = (v_jet if dv is None else abs(float(dv))) / 0.3048
326 d30 = dj_in / (2.64 * math.sqrt(dj_in * vj) + 0.97 * dj_in * dV) * 0.0254
327 d32 = TN4222_D32_OVER_D30 * d30
328 return float(d32 * tn4222_property_factor(transfer, rho_liq=rho_liq, mu_liq=mu_liq,
329 sigma=sigma, rho_gas=rho_gas))
330
331
333 *,
334 Tc: float,
335 Pc: float,
336 rho_g: float,
337 rho_l: float,
338 L_vap: float,
339 T_boil: float,
340 cp_g: float = 2200.0,
341 D0: float = 2.0e-5,
342 C_evap: float = 1.562,
343) -> float:
344 """d²-law evaporation constant k_evap [m²/s], derived from propellant properties.
345
346 Replaces a single hardcoded ``K`` that was identical in all 14 configs and for every
347 propellant. That constant's VALUE was defensible for ethanol at ~3100 K (it implies
348 k_evap = 3.33e-6 m²/s, within 10% of a T^2 extrapolation of measured ethanol data), but its
349 SHAPE was wrong: it could not distinguish ethanol from methane from RP-1, and it did not
350 move with chamber temperature or pressure at all -- so a 200 psi design and a 600 psi design
351 evaporated at identical rates.
352
353 k_evap = C * (8 * rho_g * D_v / rho_l) * ln(1 + B_M)
354 B_M = cp_g * (Tc - T_boil) / L_vap (Spalding mass transfer number)
355 D_v = D0 * (Tc/300)^1.75 * (101325/Pc) (vapor diffusivity)
356
357 Every input except C and D0 already exists in the propellant presets, so this needs no new
358 tables. ``C_evap`` is the one calibration constant, anchored so ethanol at 3094 K / 450 psi
359 reproduces the 3.33e-6 m²/s the old fixed K implied -- i.e. this is a re-shaping of a value
360 that was already right for the propellant it was tuned on, not a change of magnitude.
361
362 The same form is already written (and disabled) in combustion_physics.py under
363 ``SPALDING_DIAGNOSTIC_ENABLED``, where it also hardcodes ``fuel="RP-1"``.
364 """
365 if not all(np.isfinite(v) for v in (Tc, Pc, rho_g, rho_l, L_vap, T_boil)):
366 return float("nan")
367 if Tc <= 0 or Pc <= 0 or rho_g <= 0 or rho_l <= 0 or L_vap <= 0:
368 return float("nan")
369 D_v = float(D0) * (float(Tc) / 300.0) ** 1.75 * (101325.0 / float(Pc))
370 B_M = float(cp_g) * max(0.0, float(Tc) - float(T_boil)) / float(L_vap)
371 k = float(C_evap) * (8.0 * float(rho_g) * D_v / float(rho_l)) * float(np.log1p(B_M))
372 return float(k) if (np.isfinite(k) and k > 0) else float("nan")
373
374
375def tau_evap_from_k(D32: float, k_evap_m2_s: float) -> float:
376 """Standard d²-law evaporation time: τ = D32² / k_evap. Note k is in m²/s, NOT its reciprocal."""
377 if not (np.isfinite(D32) and np.isfinite(k_evap_m2_s)) or k_evap_m2_s <= 0 or D32 <= 0:
378 return float("nan")
379 return float((D32 ** 2) / k_evap_m2_s)
380
381
383 *,
384 mdot_O: float, u_O: float, theta_O_deg: float,
385 mdot_F: float, u_F: float, theta_F_deg: float,
386) -> float:
387 """Axial velocity of the combined spray after the doublet collides [m/s].
388
389 Momentum balance on the pair: the two jets arrive canted at theta_O / theta_F from the
390 chamber axis, and what survives downstream is the momentum-weighted axial component
391
392 u_axial = (mdot_O*u_O*cos(theta_O) + mdot_F*u_F*cos(theta_F)) / (mdot_O + mdot_F)
393
394 This is the velocity that TRANSPORTS droplets down the chamber, and therefore the one that
395 belongs in x* = u * tau_evap. The code previously used ``u_rel``, the jet-to-jet RELATIVE
396 velocity -- correct for atomisation (it is what shears the sheet, and it belongs in the
397 Ingebo Weber number) but wrong for transport: droplets do not travel downstream at the
398 closing speed of two jets. Measured on a converged design, u_rel was 28.4 m/s against a true
399 axial 13.8 m/s, so x* -- and hence the required chamber length -- was over-predicted 2.06x.
400 """
401 mt = float(mdot_O) + float(mdot_F)
402 if not np.isfinite(mt) or mt <= 0:
403 return float("nan")
404 ax = (float(mdot_O) * float(u_O) * np.cos(np.deg2rad(float(theta_O_deg)))
405 + float(mdot_F) * float(u_F) * np.cos(np.deg2rad(float(theta_F_deg))))
406 u = ax / mt
407 return float(u) if (np.isfinite(u) and u > 0) else float("nan")
408
409
410def tau_evap(D32: float, K: float) -> float:
411 """
412 Calculate evaporation time.
413
414 τ_evap = K × D32²
415
416 Parameters:
417 -----------
418 D32 : float
419 Sauter Mean Diameter [m]
420 K : float
421 Evaporation constant [s/m²]
422
423 Returns:
424 --------
425 tau : float [s]
426 """
427 tau = K * (D32 ** 2)
428 return float(tau)
429
430
431def xstar(U_rel: float, tau_evap: float) -> float:
432 """
433 Calculate evaporation length x*.
434
435 x* = U_rel × τ_evap
436
437 Parameters:
438 -----------
439 U_rel : float
440 Relative velocity [m/s]
441 tau_evap : float
442 Evaporation time [s]
443
444 Returns:
445 --------
446 x_star : float [m]
447 """
448 x_star = U_rel * tau_evap
449 return float(x_star)
450
451
453 We_O: float,
454 We_F: float,
455 x_star: float,
456 config: SprayConfig
457) -> Tuple[bool, List[str]]:
458 """
459 Check if spray constraints are satisfied.
460
461 Constraints:
462 - We_i ≥ We_min
463 - x* < x_limit
464
465 Parameters:
466 -----------
467 We_O : float
468 Oxidizer Weber number
469 We_F : float
470 Fuel Weber number
471 x_star : float
472 Evaporation length [m]
473 config : SprayConfig
474 Spray configuration
475
476 Returns:
477 --------
478 constraints_satisfied : bool
479 violations : list[str]
480 List of violated constraint names
481 """
482 violations = []
483
484 # Check Weber number constraints
485 We_min = config.weber.get("We_min", 15.0)
486 if We_O < We_min:
487 violations.append(f"We_O < We_min ({We_O:.2f} < {We_min:.2f})")
488 if We_F < We_min:
489 violations.append(f"We_F < We_min ({We_F:.2f} < {We_min:.2f})")
490
491 # Check evaporation length constraint
492 if config.evaporation.use_constraint:
493 if x_star >= config.evaporation.x_star_limit:
494 violations.append(
495 f"x* >= x_limit ({x_star:.4f} >= {config.evaporation.x_star_limit:.4f} m)"
496 )
497
498
499 constraints_satisfied = len(violations) == 0
500 return constraints_satisfied, violations
501
502
504 L_open: float,
505 V_rel: float,
506 rho_f: float,
507 mu_f: float,
508 sigma_f: float,
509 C: float,
510 B: float,
511 n: float,
512 p: float,
513) -> float:
514 """
515 Calculate Sauter Mean Diameter (SMD) for pintle injector using relative velocity physics.
516
517 Formula: SMD = C * L_open * We_rel^(-n) * (1 + B * Oh_f)^p
518
519 Parameters:
520 -----------
521 L_open : float
522 Characteristic length (Pintle Opening / Gap Height) [m]
523 V_rel : float
524 Relative velocity magnitude between streams [m/s]
525 rho_f : float
526 Fuel (sheet) density [kg/m³]
527 mu_f : float
528 Fuel (sheet) dynamic viscosity [Pa·s]
529 sigma_f : float
530 Fuel surface tension [N/m]
531 C, B, n, p : float
532 Correlation constants
533
534 Returns:
535 --------
536 D32 : float [m]
537 """
538 if L_open <= 0:
539 raise ValueError(f"smd_pintle: L_open must be positive, got {L_open}")
540 if rho_f <= 0:
541 raise ValueError(f"smd_pintle: rho_f must be positive, got {rho_f}")
542 if sigma_f <= 0:
543 raise ValueError(f"smd_pintle: sigma_f must be positive, got {sigma_f}")
544 if mu_f < 0:
545 raise ValueError(f"smd_pintle: mu_f must be non-negative, got {mu_f}")
546
547 if C <= 0:
548 raise ValueError(f"smd_pintle: C must be positive, got {C}")
549 if n <= 0:
550 raise ValueError(f"smd_pintle: n must be positive, got {n}")
551 if B < 0:
552 raise ValueError(f"smd_pintle: B must be non-negative, got {B}")
553 if p < 0:
554 raise ValueError(f"smd_pintle: p must be non-negative, got {p}")
555
556 if V_rel < 0:
557 raise ValueError(f"smd_pintle: V_rel must be non-negative, got {V_rel}")
558
559 if V_rel == 0:
560 import warnings
561 warnings.warn("smd_pintle: V_rel is 0.0, We=0. SMD calculation relying on Ohnesorge term only.")
562
563 # Calculate Weber number based on relative velocity
564 # We = rho * V^2 * L / sigma
565 We_rel = (rho_f * (V_rel ** 2) * L_open) / sigma_f
566
567 # Calculate Ohnesorge number for the liquid sheet
568 # Oh = mu / sqrt(rho * sigma * L)
569 denom = np.sqrt(rho_f * sigma_f * L_open)
570 Oh_f = mu_f / denom if denom > 0 else 0.0
571
572 # Apply correlation
573 # SMD = C * L_open * We^(-n) * (1 + B * Oh)^p
574 factor_we = (We_rel ** (-n)) if We_rel > 0 else 1.0
575 factor_oh = (1.0 + B * Oh_f) ** p
576
577 D32 = C * L_open * factor_we * factor_oh
578
579 return float(D32)
580
#define max(a, b)
Definition Arduino.h:43
float evaporation_constant_m2_s(*, float Tc, float Pc, float rho_g, float rho_l, float L_vap, float T_boil, float cp_g=2200.0, float D0=2.0e-5, float C_evap=1.562)
Definition spray.py:343
float ohnesorge_number(float mu, float rho, float sigma, float d_or)
Definition spray.py:161
float tau_evap(float D32, float K)
Definition spray.py:410
float spray_axial_velocity(*, float mdot_O, float u_O, float theta_O_deg, float mdot_F, float u_F, float theta_F_deg)
Definition spray.py:386
float smd_lefebvre(float d_or, float We, float Oh, float C, float m, float p)
Definition spray.py:198
float spray_angle_from_J(float J, float k, float n)
Definition spray.py:51
float thrust_momentum_ratio(float J, float MR)
Definition spray.py:40
float smd_pintle(float L_open, float V_rel, float rho_f, float mu_f, float sigma_f, float C, float B, float n, float p)
Definition spray.py:513
float smd_impinging_tn4222(float d_jet, float v_jet, float rho_liq, float mu_liq, float sigma, float rho_gas, *, Optional[float] dv=None, bool scale_properties=True, Optional[str] transfer=None)
Definition spray.py:297
float spray_angle_from_TMR(float TMR)
Definition spray.py:120
float tau_evap_from_k(float D32, float k_evap_m2_s)
Definition spray.py:375
float weber_number(float rho, float u, float d_char, float sigma)
Definition spray.py:133
float tn4222_property_factor(str transfer, *, float rho_liq, float mu_liq, float sigma, float rho_gas)
Definition spray.py:252
str tn4222_transfer_model(smd_cfg)
Definition spray.py:278
float momentum_flux_ratio(float rho_O, float u_O, float rho_F, float u_F)
Definition spray.py:12
Tuple[bool, List[str]] check_spray_constraints(float We_O, float We_F, float x_star, SprayConfig config)
Definition spray.py:457
float xstar(float U_rel, float tau_evap)
Definition spray.py:431