1use factorial::Factorial;
2use nalgebra::ComplexField;
3use num::{complex::Complex64, Integer};
4use std::f64::consts::PI;
5
6fn alp_pos_m(l: usize, m: usize, x: f64) -> f64 {
7 let mut p = 1.0;
8 if l == 0 && m == 0 {
9 return p;
10 }
11 let y = f64::sqrt(1.0 - f64::powi(x, 2));
12 for m_p in 0..m {
13 p *= -((2 * m_p + 1) as f64) * y;
14 }
15 if l == m {
16 return p;
17 }
18 let mut p_min_2 = p;
19 let mut p_min_1 = (2 * m + 1) as f64 * x * p_min_2;
20 if l == m + 1 {
21 return p_min_1;
22 }
23 for l_p in (m + 1)..l {
24 p = ((2 * l_p + 1) as f64 * x * p_min_1 - (l_p + m) as f64 * p_min_2)
25 / (l_p - m + 1) as f64;
26 p_min_2 = p_min_1;
27 p_min_1 = p;
28 }
29 p
30}
31
32pub fn spherical_harmonic(l: usize, m: isize, costheta: f64, phi: f64) -> Complex64 {
35 let abs_m = isize::abs(m) as usize;
36 let mut res = alp_pos_m(l, abs_m, costheta); res *= f64::sqrt(
38 (2 * l + 1) as f64 / (4.0 * PI) * ((l - abs_m).factorial()) as f64
39 / ((l + abs_m).factorial()) as f64,
40 );
41 if m < 0 {
42 res *= if abs_m.is_even() { 1.0 } else { -1.0 }; }
46 Complex64::new(
47 res * f64::cos(m as f64 * phi),
48 res * f64::sin(m as f64 * phi),
49 )
50}
51
52pub fn chi_plus(s: f64, m1: f64, m2: f64) -> f64 {
54 1.0 - (m1 + m2) * (m1 + m2) / s
55}
56
57pub fn chi_minus(s: f64, m1: f64, m2: f64) -> f64 {
59 1.0 - (m1 - m2) * (m1 - m2) / s
60}
61
62pub fn rho(s: f64, m1: f64, m2: f64) -> Complex64 {
64 let x: Complex64 = (chi_plus(s, m1, m2) * chi_minus(s, m1, m2)).into();
65 x.sqrt()
66}
67
68pub fn breakup_momentum(m0: f64, m1: f64, m2: f64) -> f64 {
73 let s = m0.powi(2);
74 let x: Complex64 = (chi_plus(s, m1, m2) * chi_minus(s, m1, m2)).into();
75 x.abs().sqrt() * m0 / 2.0
76}
77
78pub fn complex_breakup_momentum(m0: f64, m1: f64, m2: f64) -> Complex64 {
81 rho(f64::powi(m0, 2), m1, m2) * m0 / 2.0
82}
83
84pub fn blatt_weisskopf(m0: f64, m1: f64, m2: f64, l: usize) -> f64 {
91 let q = breakup_momentum(m0, m1, m2);
92 let z = q.powi(2) / 0.1973.powi(2);
93 match l {
94 0 => 1.0,
95 1 => ((2.0 * z) / (z + 1.0)).sqrt(),
96 2 => ((13.0 * z.powi(2)) / ((z - 3.0).powi(2) + 9.0 * z)).sqrt(),
97 3 => {
98 ((277.0 * z.powi(3)) / (z * (z - 15.0).powi(2) + 9.0 * (2.0 * z - 5.0).powi(2))).sqrt()
99 }
100 4 => ((12746.0 * z.powi(4))
101 / ((z.powi(2) - 45.0 * z + 105.0).powi(2) + 25.0 * z * (2.0 * z - 21.0).powi(2)))
102 .sqrt(),
103 l => panic!("L = {l} is not yet implemented"),
104 }
105}
106
107pub fn complex_blatt_weisskopf(m0: f64, m1: f64, m2: f64, l: usize) -> Complex64 {
113 let q = complex_breakup_momentum(m0, m1, m2);
114 let z = q.powi(2) / 0.1973.powi(2);
115 match l {
116 0 => Complex64::ONE,
117 1 => ((z * 2.0) / (z + 1.0)).sqrt(),
118 2 => ((z.powi(2) * 13.0) / ((z - 3.0).powi(2) + z * 9.0)).sqrt(),
119 3 => {
120 ((z.powi(3) * 277.0) / (z * (z - 15.0).powi(2) + (z * 2.0 - 5.0).powi(2) * 9.0)).sqrt()
121 }
122 4 => ((z.powi(4) * 12746.0)
123 / ((z.powi(2) - z * 45.0 + 105.0).powi(2) + z * 25.0 * (z * 2.0 - 21.0).powi(2)))
124 .sqrt(),
125 l => panic!("L = {l} is not yet implemented"),
126 }
127}
128
129#[cfg(test)]
130mod test {
131 use approx::assert_relative_eq;
132 use num::complex::Complex64;
133
134 use crate::utils::functions::{
135 blatt_weisskopf, breakup_momentum, chi_minus, complex_breakup_momentum, rho,
136 spherical_harmonic,
137 };
138
139 use super::{chi_plus, complex_blatt_weisskopf};
140
141 #[test]
142 fn test_spherical_harmonics() {
143 use std::f64::consts::PI;
144 let costhetas = [-1.0, -0.8, -0.3, 0.0, 0.3, 0.8, 1.0];
145 let phis = [0.0, 0.3, 0.5, 0.8, 1.0].map(|v| v * PI * 2.0);
146 for costheta in costhetas {
147 for phi in phis {
148 let y00 = spherical_harmonic(0, 0, costheta, phi);
150 let y00_true = Complex64::from(f64::sqrt(1.0 / (4.0 * PI)));
151 assert_relative_eq!(y00.re, y00_true.re);
152 assert_relative_eq!(y00.im, y00_true.im);
153 let y1n1 = spherical_harmonic(1, -1, costheta, phi);
155 let y1n1_true = Complex64::from_polar(
156 f64::sqrt(3.0 / (8.0 * PI)) * f64::sin(f64::acos(costheta)),
157 -phi,
158 );
159 assert_relative_eq!(y1n1.re, y1n1_true.re);
160 assert_relative_eq!(y1n1.im, y1n1_true.im);
161 let y10 = spherical_harmonic(1, 0, costheta, phi);
162 let y10_true = Complex64::from(f64::sqrt(3.0 / (4.0 * PI)) * costheta);
163 assert_relative_eq!(y10.re, y10_true.re);
164 assert_relative_eq!(y10.im, y10_true.im);
165 let y11 = spherical_harmonic(1, 1, costheta, phi);
166 let y11_true = Complex64::from_polar(
167 -f64::sqrt(3.0 / (8.0 * PI)) * f64::sin(f64::acos(costheta)),
168 phi,
169 );
170 assert_relative_eq!(y11.re, y11_true.re);
171 assert_relative_eq!(y11.im, y11_true.im);
172 let y2n2 = spherical_harmonic(2, -2, costheta, phi);
174 let y2n2_true = Complex64::from_polar(
175 f64::sqrt(15.0 / (32.0 * PI)) * f64::sin(f64::acos(costheta)).powi(2),
176 -2.0 * phi,
177 );
178 assert_relative_eq!(y2n2.re, y2n2_true.re);
179 assert_relative_eq!(y2n2.im, y2n2_true.im);
180 let y2n1 = spherical_harmonic(2, -1, costheta, phi);
181 let y2n1_true = Complex64::from_polar(
182 f64::sqrt(15.0 / (8.0 * PI)) * f64::sin(f64::acos(costheta)) * costheta,
183 -phi,
184 );
185 assert_relative_eq!(y2n1.re, y2n1_true.re);
186 assert_relative_eq!(y2n1.im, y2n1_true.im);
187 let y20 = spherical_harmonic(2, 0, costheta, phi);
188 let y20_true =
189 Complex64::from(f64::sqrt(5.0 / (16.0 * PI)) * (3.0 * costheta.powi(2) - 1.0));
190 assert_relative_eq!(y20.re, y20_true.re);
191 assert_relative_eq!(y20.im, y20_true.im);
192 let y21 = spherical_harmonic(2, 1, costheta, phi);
193 let y21_true = Complex64::from_polar(
194 -f64::sqrt(15.0 / (8.0 * PI)) * f64::sin(f64::acos(costheta)) * costheta,
195 phi,
196 );
197 assert_relative_eq!(y21.re, y21_true.re);
198 assert_relative_eq!(y21.im, y21_true.im);
199 let y22 = spherical_harmonic(2, 2, costheta, phi);
200 let y22_true = Complex64::from_polar(
201 f64::sqrt(15.0 / (32.0 * PI)) * f64::sin(f64::acos(costheta)).powi(2),
202 2.0 * phi,
203 );
204 assert_relative_eq!(y22.re, y22_true.re);
205 assert_relative_eq!(y22.im, y22_true.im);
206 }
207 }
208 }
209
210 #[test]
211 fn test_momentum_functions() {
212 assert_relative_eq!(chi_plus(1.3, 0.51, 0.62), 0.01776923076923098,);
213 assert_relative_eq!(chi_minus(1.3, 0.51, 0.62), 0.9906923076923076,);
214 let x0 = rho(1.3, 0.51, 0.62);
215 assert_relative_eq!(x0.re, 0.1326794642613792);
216 assert_relative_eq!(x0.im, 0.0);
217 let x1 = rho(1.3, 1.23, 0.62);
218 assert_relative_eq!(x1.re, 0.0);
219 assert_relative_eq!(x1.im, 1.0795209736472833);
220 let y0 = breakup_momentum(1.2, 0.4, 0.5);
221 assert_relative_eq!(y0, 0.3954823004889093);
222 let y1 = breakup_momentum(1.2, 1.4, 1.5);
223 assert_relative_eq!(y1, 1.3154464282347478);
224 let y2 = complex_breakup_momentum(1.2, 0.4, 0.5);
225 assert_relative_eq!(y2.re, 0.3954823004889093);
226 assert_relative_eq!(y2.im, 0.0);
227 let y3 = complex_breakup_momentum(1.2, 1.4, 1.5);
228 assert_relative_eq!(y3.re, 0.0);
229 assert_relative_eq!(y3.im, 1.3154464282347478);
230 assert_relative_eq!(y0, y2.re);
231 assert_relative_eq!(y1, y3.im);
232
233 let z0 = blatt_weisskopf(1.2, 0.4, 0.5, 0);
234 assert_relative_eq!(z0, 1.0);
235 let z1 = blatt_weisskopf(1.2, 0.4, 0.5, 1);
236 assert_relative_eq!(z1, 1.2654752018685698);
237 let z2 = blatt_weisskopf(1.2, 0.4, 0.5, 2);
238 assert_relative_eq!(z2, 2.375285855793918);
239 let z3 = blatt_weisskopf(1.2, 0.4, 0.5, 3);
240 assert_relative_eq!(z3, 5.6265876867850695);
241 let z4 = blatt_weisskopf(1.2, 0.4, 0.5, 4);
242 assert_relative_eq!(z4, 12.747554064467208);
243 let z0im = blatt_weisskopf(1.2, 1.4, 0.5, 0);
244 assert_relative_eq!(z0im, 1.0);
245 let z1im = blatt_weisskopf(1.2, 1.4, 1.5, 1);
246 assert_relative_eq!(z1im, 1.398569848337654);
247 let z2im = blatt_weisskopf(1.2, 1.4, 1.5, 2);
248 assert_relative_eq!(z2im, 3.482294988252171);
249 let z3im = blatt_weisskopf(1.2, 1.4, 1.5, 3);
250 assert_relative_eq!(z3im, 15.450855647831101);
251 let z4im = blatt_weisskopf(1.2, 1.4, 1.5, 4);
252 assert_relative_eq!(z4im, 98.48799450927207);
253
254 let w0 = complex_blatt_weisskopf(1.2, 0.4, 0.5, 0);
255 assert_relative_eq!(w0.re, 1.0);
256 assert_relative_eq!(w0.im, 0.0);
257 let w1 = complex_blatt_weisskopf(1.2, 0.4, 0.5, 1);
258 assert_relative_eq!(w1.re, 1.2654752018685698);
259 assert_relative_eq!(w1.im, 0.0);
260 let w2 = complex_blatt_weisskopf(1.2, 0.4, 0.5, 2);
261 assert_relative_eq!(w2.re, 2.375285855793918);
262 assert_relative_eq!(w2.im, 0.0);
263 let w3 = complex_blatt_weisskopf(1.2, 0.4, 0.5, 3);
264 assert_relative_eq!(w3.re, 5.62658768678507);
265 assert_relative_eq!(w3.im, 0.0, epsilon = f64::EPSILON.sqrt());
266 let w4 = complex_blatt_weisskopf(1.2, 0.4, 0.5, 4);
267 assert_relative_eq!(w4.re, 12.747554064467208);
268 assert_relative_eq!(w4.im, 0.0, epsilon = f64::EPSILON.sqrt());
269 let w0im = complex_blatt_weisskopf(1.2, 1.4, 1.5, 0);
270 assert_relative_eq!(w0im.re, 1.0);
271 assert_relative_eq!(w0im.im, 0.0);
272 let w1im = complex_blatt_weisskopf(1.2, 1.4, 1.5, 1);
273 assert_relative_eq!(w1im.re, 1.430394249144933);
274 assert_relative_eq!(w1im.im, 0.0);
275 let w2im = complex_blatt_weisskopf(1.2, 1.4, 1.5, 2);
276 assert_relative_eq!(w2im.re, 3.724659004227952);
277 assert_relative_eq!(w2im.im, 0.0, epsilon = f64::EPSILON.sqrt());
278 let w3im = complex_blatt_weisskopf(1.2, 1.4, 1.5, 3);
279 assert_relative_eq!(w3im.re, 17.689297320491015);
280 assert_relative_eq!(w3im.im, 0.0, epsilon = f64::EPSILON.sqrt());
281 let w4im = complex_blatt_weisskopf(1.2, 1.4, 1.5, 4);
282 assert_relative_eq!(w4im.re, 124.0525841825899);
283 assert_relative_eq!(w4im.im, 0.0, epsilon = f64::EPSILON.sqrt());
284
285 assert_relative_eq!(z0, w0.re);
286 assert_relative_eq!(z1, w1.re);
287 assert_relative_eq!(z2, w2.re);
288 assert_relative_eq!(z3, w3.re);
289 assert_relative_eq!(z4, w4.re);
290 }
291 #[test]
292 #[should_panic]
293 fn panicking_blatt_weisskopf() {
294 blatt_weisskopf(1.2, 0.4, 0.5, 5);
295 }
296 #[test]
297 #[should_panic]
298 fn panicking_complex_blatt_weisskopf() {
299 complex_blatt_weisskopf(1.2, 0.4, 0.5, 5);
300 }
301}