treams_core/coeffs/mie/
saved.rs1use super::{Interface, Material, MieResidual, Riccati};
6use crate::{
7 Complex, Error, Result,
8 saved::{Reader, SavedState, Writer},
9};
10
11fn invalid() -> Error {
12 Error::InvalidInput("invalid Mie derivative state".into())
13}
14
15impl MieResidual {
16 pub fn state_size(boundaries: usize) -> Result<usize> {
20 if boundaries == 0 {
21 return Err(invalid());
22 }
23 boundaries
24 .checked_mul(1048)
25 .and_then(|size| size.checked_add(184))
26 .ok_or_else(invalid)
27 }
28
29 pub(crate) fn write_state(&self, writer: &mut Writer) {
30 writer.usize(self.sizes.len());
31 for &size in &self.sizes {
32 writer.f64(size);
33 }
34 for material in &self.materials {
35 writer.complex(material.epsilon);
36 writer.complex(material.mu);
37 writer.complex(material.kappa);
38 }
39 for interface in &self.interfaces {
40 for &value in interface.x.iter().flatten().chain(&interface.z) {
41 writer.complex(value);
42 }
43 for radial in interface.radials.iter().flatten() {
44 for &(value, derivative) in radial.waves.iter().chain(&radial.slopes) {
45 writer.complex(value);
46 writer.complex(derivative);
47 }
48 }
49 for &value in interface.matrix.iter().chain(interface.before.iter()) {
50 writer.complex(value);
51 }
52 }
53 for &value in self.inverse.iter().chain(self.value.iter()) {
54 writer.complex(value);
55 }
56 }
57
58 pub(crate) fn read_state(reader: &mut Reader<'_>) -> Result<Self> {
59 let boundaries = reader.usize()?;
60 let expected = Self::state_size(boundaries)?;
61 if reader.remaining_len() < expected - 8 {
62 return Err(invalid());
63 }
64 let sizes = (0..boundaries)
65 .map(|_| reader.f64())
66 .collect::<Result<Vec<_>>>()?;
67 let materials = (0..=boundaries)
68 .map(|_| {
69 Ok(Material {
70 epsilon: reader.complex()?,
71 mu: reader.complex()?,
72 kappa: reader.complex()?,
73 })
74 })
75 .collect::<Result<Vec<_>>>()?;
76 let mut interfaces = Vec::with_capacity(boundaries);
77 for _ in 0..boundaries {
78 let mut x = [[Complex::default(); 2]; 2];
79 let mut z = [Complex::default(); 2];
80 for value in x.iter_mut().flatten().chain(&mut z) {
81 *value = reader.complex()?;
82 }
83 let mut radials = [[Riccati {
84 waves: [(Complex::default(), Complex::default()); 2],
85 slopes: [(Complex::default(), Complex::default()); 2],
86 }; 2]; 2];
87 for radial in radials.iter_mut().flatten() {
88 for (value, derivative) in radial.waves.iter_mut().chain(&mut radial.slopes) {
89 *value = reader.complex()?;
90 *derivative = reader.complex()?;
91 }
92 }
93 interfaces.push(Interface {
94 x,
95 z,
96 radials,
97 matrix: read_matrix(reader)?,
98 before: read_matrix(reader)?,
99 });
100 }
101 Ok(Self {
102 sizes,
103 materials,
104 interfaces,
105 inverse: read_matrix(reader)?,
106 value: read_matrix(reader)?,
107 })
108 }
109}
110
111fn read_matrix<const R: usize, const C: usize>(
112 reader: &mut Reader<'_>,
113) -> Result<nalgebra::SMatrix<Complex, R, C>> {
114 let mut matrix = nalgebra::SMatrix::<Complex, R, C>::zeros();
115 for value in matrix.iter_mut() {
116 *value = reader.complex()?;
117 }
118 Ok(matrix)
119}
120
121impl SavedState for MieResidual {
122 fn save_state(&self) -> Result<Vec<u8>> {
123 let mut writer = Writer::new(Self::state_size(self.sizes.len())?);
124 self.write_state(&mut writer);
125 Ok(writer.finish())
126 }
127
128 fn from_state(bytes: &[u8]) -> Result<Self> {
129 let mut reader = Reader::new(bytes);
130 let result = Self::read_state(&mut reader)?;
131 reader.finish()?;
132 Ok(result)
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::coeffs::{Matrix2, mie};
140
141 #[test]
142 fn state_roundtrip_preserves_value_and_both_derivatives() {
143 let materials = [
144 Material {
145 epsilon: Complex::new(2.7, 0.2),
146 mu: Complex::new(1.1, 0.03),
147 kappa: Complex::new(0.12, 0.02),
148 },
149 Material {
150 epsilon: Complex::new(1.8, 0.1),
151 ..Material::default()
152 },
153 Material::default(),
154 ];
155 let residual = mie(3, &[0.4, 0.7], &materials).unwrap();
156 let state = residual.save_state().unwrap();
157 assert_eq!(state.len(), MieResidual::state_size(2).unwrap());
158 let restored = MieResidual::from_state(&state).unwrap();
159 assert_eq!(restored.value(), residual.value());
160 let directions = [Material {
161 epsilon: Complex::new(0.1, -0.02),
162 mu: Complex::new(-0.03, 0.02),
163 kappa: Complex::new(0.02, 0.01),
164 }; 3];
165 assert_eq!(
166 restored.pushforward(&[0.1, -0.2], &directions).unwrap(),
167 residual.pushforward(&[0.1, -0.2], &directions).unwrap()
168 );
169 let cotangent = Matrix2::repeat(Complex::new(0.2, -0.3));
170 let actual = restored.pullback(&cotangent).unwrap();
171 let expected = residual.pullback(&cotangent).unwrap();
172 assert_eq!(actual.sizes, expected.sizes);
173 assert_eq!(actual.epsilon, expected.epsilon);
174 assert_eq!(actual.mu, expected.mu);
175 assert_eq!(actual.kappa, expected.kappa);
176 assert_eq!(restored.save_state().unwrap(), state);
177 }
178
179 #[test]
180 fn state_rejects_inconsistent_dimensions_before_allocation() {
181 let residual = mie(1, &[0.5], &[Material::default(); 2]).unwrap();
182 let mut state = residual.save_state().unwrap();
183 assert!(MieResidual::from_state(&state[..state.len() - 1]).is_err());
184 state[..8].copy_from_slice(&u64::MAX.to_le_bytes());
185 assert!(MieResidual::from_state(&state).is_err());
186 assert!(MieResidual::state_size(usize::MAX).is_err());
187 assert!(MieResidual::state_size(0).is_err());
188 }
189}