comp_flow/mach_from.rs
1//! Collection of functions for isentropic compressible flow.
2
3use crate::{mach_to_a_ac, mach_to_pm_angle};
4use eqsolver::single_variable::FDNewton;
5use num::Float;
6
7/// Mach number for a given Prandtl-Meyer angle in radians.
8///
9/// <div class="warning">
10///
11/// This function uses Newton's method to solve for the Mach number. If this
12/// function must be called many times, it may be preferable to make a look up
13/// table with `mach_to_pm_angle` and interpolate those values.
14///
15/// </div>
16///
17/// # Examples
18///
19/// ```
20/// use comp_flow::mach_from_pm_angle;
21///
22/// assert_eq!(mach_from_pm_angle(0.4604136818474_f32, 1.4_f32), 2.0);
23/// assert_eq!(mach_from_pm_angle(0.0_f64, 1.4_f64), 1.00000022981460310);
24/// ```
25pub fn mach_from_pm_angle<F: Float>(pm_angle: F, gamma: F) -> F {
26 let f = |m| mach_to_pm_angle(m, gamma) - pm_angle;
27 let x0 = F::from(2.).unwrap();
28 FDNewton::new(f).solve(x0).unwrap()
29}
30
31/// Mach number for a given mach angle in radians.
32///
33/// # Examples
34///
35/// ```
36/// use comp_flow::mach_from_mach_angle;
37///
38/// assert_eq!(mach_from_mach_angle(0.5235988_f32), 2.0);
39/// assert_eq!(mach_from_mach_angle(1.5707963267948966_f64), 1.0);
40/// ```
41pub fn mach_from_mach_angle<F: Float>(mach_angle: F) -> F {
42 // TODO check for invalid input i.e. mach_angle > 90 deg
43 (F::one()) / mach_angle.sin()
44}
45
46/// Mach number for a given total temperature ratio.
47///
48/// # Examples
49///
50/// ```
51/// use comp_flow::mach_from_t_t0;
52///
53/// assert_eq!(mach_from_t_t0(1.0, 1.4), 0.0);
54/// assert_eq!(mach_from_t_t0(0.8333333333333334, 1.4), 1.0);
55/// assert_eq!(mach_from_t_t0(0.55555556_f32, 1.4), 2.0);
56/// ```
57pub fn mach_from_t_t0<F: Float>(t_t0: F, gamma: F) -> F {
58 let two = F::from(2.0).unwrap();
59 (two / (gamma - F::one()) * (F::one() / t_t0 - F::one())).sqrt()
60}
61
62/// Mach number for a given total pressure ratio.
63///
64/// # Examples
65///
66/// ```
67/// use comp_flow::mach_from_p_p0;
68///
69/// assert_eq!(mach_from_p_p0(1.0, 1.4), 0.0);
70/// assert_eq!(mach_from_p_p0(0.5282817877171742, 1.4), 1.0);
71/// assert_eq!(mach_from_p_p0(0.1278045254629509, 1.4), 2.0);
72/// ```
73pub fn mach_from_p_p0<F: Float>(p_p0: F, gamma: F) -> F {
74 let two = F::from(2.0).unwrap();
75 (two / (gamma - F::one()) * (p_p0.powf((F::one() - gamma) / gamma) - F::one())).sqrt()
76}
77
78/// Mach number for a given stagnation density ratio.
79///
80/// # Examples
81///
82/// ```
83/// use comp_flow::mach_from_rho_rho0;
84///
85/// assert_eq!(mach_from_rho_rho0(1.0, 1.4), 0.0);
86/// assert_eq!(mach_from_rho_rho0(0.633938145260609, 1.4), 1.0);
87/// assert_eq!(mach_from_rho_rho0(0.2300481458333117, 1.4), 2.0);
88/// ```
89pub fn mach_from_rho_rho0<F: Float>(rho_rho0: F, gamma: F) -> F {
90 let two = F::from(2.0).unwrap();
91 (two / (gamma - F::one()) * (rho_rho0.powf(F::one() - gamma) - F::one())).sqrt()
92}
93
94/// Mach number for a given critical area ratio.
95///
96/// <div class="warning">
97///
98/// This function uses a bisection algorythm to solve for the Mach number. If this
99/// function must be called many times, it may be preferable to make a look up
100/// table with `mach_to_a_ac` and interpolate those values.
101///
102/// </div>
103///
104/// # Examples
105///
106/// ```
107/// use comp_flow::mach_from_a_ac;
108///
109/// assert_eq!(mach_from_a_ac(1.6875000000000002_f64, 1.4, true), 2.0);
110/// assert_eq!(mach_from_a_ac(5.821828750000001_f64, 1.4, false), 0.1);
111/// assert_eq!(mach_from_a_ac(6.25, 1.4, false), 0.09307469911759117);
112/// assert_eq!(mach_from_a_ac(1.0, 1.4, false), 1.0);
113/// assert_eq!(mach_from_a_ac(1.0, 1.4, true), 1.0);
114/// ```
115pub fn mach_from_a_ac<F: Float>(a_ac: F, gamma: F, supersonic: bool) -> F {
116 if a_ac.is_one() {
117 return F::one();
118 }
119 let mut m_max: F;
120 let mut m_min: F;
121 if supersonic {
122 m_max = F::max_value();
123 m_min = F::one();
124 } else {
125 m_max = F::one();
126 m_min = F::zero();
127 }
128 let mut m_try = (m_max + m_min) / F::from(2.).unwrap();
129 let mut val: F;
130 let mut i: usize = 0;
131 let max_iter: usize = 10000;
132 while i < max_iter {
133 val = mach_to_a_ac(m_try, gamma);
134 if val == a_ac {
135 return m_try;
136 } else if val < a_ac {
137 if supersonic {
138 m_min = m_try;
139 } else {
140 m_max = m_try;
141 }
142 } else if val > a_ac {
143 if supersonic {
144 m_max = m_try;
145 } else {
146 m_min = m_try;
147 }
148 }
149 m_try = (m_max + m_min) / F::from(2.).unwrap();
150 i += 1;
151 }
152 return m_try;
153}