1#![allow(clippy::indexing_slicing)] use crate::{
7 Complex, Error, Result,
8 numerics::{Jet, complex_sqrt, finite, ratio},
9 special::{check_pol, polarized_wave},
10};
11
12#[inline]
18fn algebraic_norm(values: &[Complex]) -> Complex {
19 let scale = values
20 .iter()
21 .map(|v| v.re.abs().max(v.im.abs()))
22 .fold(0.0, f64::max);
23 if scale == 0.0 {
24 return Complex::default();
25 }
26 if (1e-150..=1e150).contains(&scale) {
30 return complex_sqrt(values.iter().map(|v| v * v).sum::<Complex>());
31 }
32 complex_sqrt(values.iter().map(|v| (v / scale).powu(2)).sum::<Complex>()) * scale
33}
34#[inline]
40pub(crate) fn transverse_values(vector: [Complex; 2]) -> Result<(Complex, [Complex; 2])> {
41 let scale = vector
42 .iter()
43 .map(|v| v.re.abs().max(v.im.abs()))
44 .fold(0.0, f64::max);
45 Ok(if scale == 0.0 {
46 (Complex::default(), [Complex::default(); 2])
47 } else {
48 let (scaled, factor) = if (1e-150..=1e150).contains(&scale) {
49 ([vector[0], vector[1]], 1.0)
50 } else {
51 ([vector[0] / scale, vector[1] / scale], scale)
52 };
53 let norm = complex_sqrt(scaled[0] * scaled[0] + scaled[1] * scaled[1]);
54 if norm == Complex::default() {
55 return Err(Error::InvalidInput(
56 "undefined polarization for a null transverse vector".into(),
57 ));
58 }
59 (norm * factor, scaled.map(|v| ratio(v, norm)))
60 })
61}
62pub(crate) fn wavenumbers(vector: [Complex; 3]) -> Result<(Complex, Complex, [Complex; 2])> {
65 if vector.iter().any(|&v| !finite(v)) {
66 return Err(Error::InvalidInput("wavevector must be finite".into()));
67 }
68 let (transverse, xy) = transverse_values([vector[0], vector[1]])?;
69 let k = algebraic_norm(&vector);
70 if k == Complex::default() || !finite(k) {
71 return Err(Error::InvalidInput(
72 "wavevector must have nonzero algebraic norm".into(),
73 ));
74 }
75 Ok((k, transverse, xy))
76}
77
78pub(crate) struct Direction<const N: usize> {
82 pub(crate) k: Jet<N>,
84 pub(crate) transverse: Jet<N>,
86 pub(crate) xy: [Jet<N>; 2],
89}
90impl<const N: usize> Direction<N> {
91 pub(crate) fn new(vector: [Complex; 3]) -> Result<Self> {
93 let (k, transverse, xy) = wavenumbers(vector)?;
94 Self::from_parts(vector, k, transverse, xy)
95 }
96 #[inline]
99 pub(crate) fn from_parts(
100 vector: [Complex; 3],
101 k: Complex,
102 transverse: Complex,
103 xy: [Complex; 2],
104 ) -> Result<Self> {
105 if N != 0 && transverse == Complex::default() {
106 return Err(Error::InvalidInput("plane-wave direction derivative is undefined on the polarization axis; fix the wavevectors".into()));
107 }
108 let k_jet = Jet {
109 value: k,
110 derivative: std::array::from_fn(|a| ratio(vector[a], k)),
111 };
112 let transverse_jet = Jet {
113 value: transverse,
114 derivative: std::array::from_fn(|a| if a < 2 { xy[a] } else { Complex::default() }),
115 };
116 let xy: [Jet<N>; 2] = std::array::from_fn(|a| Jet {
117 value: xy[a],
118 derivative: std::array::from_fn(|b| {
119 if b < 2 {
120 ratio(
121 Complex::new(if a == b { 1.0 } else { 0.0 }, 0.0) - xy[a] * xy[b],
122 transverse,
123 )
124 } else {
125 Complex::default()
126 }
127 }),
128 });
129 Ok(Self {
130 k: k_jet,
131 transverse: transverse_jet,
132 xy,
133 })
134 }
135}
136
137#[inline]
145pub(crate) fn polarization_jet<const N: usize>(
146 vector: [Complex; 3],
147 pol: u8,
148 helicity: bool,
149) -> Result<[Jet<N>; 3]> {
150 check_pol(pol)?;
151 let Direction { k, transverse, xy } = Direction::new(vector)?;
152 let z = vector[2];
153 let m = if transverse.value == Complex::default() {
154 [Jet::default(), Jet::constant(-Complex::i()), Jet::default()]
155 } else {
156 [Complex::i() * xy[1], -Complex::i() * xy[0], Jet::default()]
157 };
158 if !helicity && pol == 0 {
160 return Ok(m);
161 }
162 let n = if transverse.value == Complex::default() {
163 [
164 Jet::constant(-ratio(z, k.value)),
165 Jet::default(),
166 Jet::default(),
167 ]
168 } else {
169 let longitudinal = Jet::variable(z, 2) / k;
170 [-xy[0] * longitudinal, -xy[1] * longitudinal, transverse / k]
171 };
172 Ok(polarized_wave(m, n, pol, helicity))
173}
174
175#[inline]
180pub fn polarization(vector: [Complex; 3], pol: u8, helicity: bool) -> Result<[Complex; 3]> {
181 Ok(polarization_jet::<0>(vector, pol, helicity)?.map(|p| p.value))
182}
183
184#[inline]
189pub fn field_value(
190 polarization: [Complex; 3],
191 k: [Complex; 3],
192 position: [Complex; 3],
193) -> Result<[Complex; 3]> {
194 if position.iter().any(|&v| !finite(v)) {
195 return Err(Error::InvalidInput("field position must be finite".into()));
196 }
197 let phase = (Complex::i() * (0..3).map(|a| k[a] * position[a]).sum::<Complex>()).exp();
198 let values = polarization.map(|p| p * phase);
199 if values.iter().any(|&v| !finite(v)) {
200 return Err(Error::NonFinite("non-finite plane-wave field".into()));
201 }
202 Ok(values)
203}
204
205pub(crate) fn polarization_from_inputs<const N: usize>(
207 vector: [Jet<N>; 3],
208 pol: u8,
209) -> Result<[Jet<N>; 3]> {
210 let values = vector.map(|k| k.value);
211 if N == 0
212 || (values[0] == Complex::default()
213 && values[1] == Complex::default()
214 && vector[..2]
215 .iter()
216 .flat_map(|k| k.derivative)
217 .all(|d| d == Complex::default()))
218 {
219 return Ok(polarization(values, pol, true)?.map(Jet::constant));
220 }
221 Ok(polarization_jet::<3>(values, pol, true)?.map(|e| e.compose(vector)))
222}
223
224#[must_use]
229pub fn wave_vector_z(kx: Complex, ky: Complex, k: Complex) -> Complex {
230 let root = complex_sqrt(k * k - kx * kx - ky * ky);
231 if root.im < 0.0 { -root } else { root }
232}