treams_core/sw/
expansion_lattice_state.rs

1//! Saved periodic geometry and static angular-plan inputs, without Ewald reevaluation.
2//! treams-rs extension.
3
4use super::{
5    LatticeExpansionFromTableResidual, LatticeExpansionResidual, blocks, validate_expansion,
6};
7use crate::{
8    Result,
9    lattice::BlochLattice,
10    numerics::finite,
11    saved::{self, Reader, SavedState, Writer},
12};
13
14fn basis_pair_size(dm: usize, dp: usize, sm: usize, sp: usize, extra: usize) -> Result<usize> {
15    saved::sw_basis_size(dm, dp)?
16        .checked_add(saved::sw_basis_size(sm, sp)?)
17        .and_then(|size| size.checked_add(extra))
18        .ok_or_else(saved::invalid)
19}
20
21impl LatticeExpansionResidual {
22    /// Fixed byte count from the destination and source basis shapes.
23    pub fn state_size(
24        destination_modes: usize,
25        destination_positions: usize,
26        source_modes: usize,
27        source_positions: usize,
28    ) -> Result<usize> {
29        basis_pair_size(
30            destination_modes,
31            destination_positions,
32            source_modes,
33            source_positions,
34            BlochLattice::STATE_SIZE + 49,
35        )
36    }
37}
38
39impl SavedState for LatticeExpansionResidual {
40    fn save_state(&self) -> Result<Vec<u8>> {
41        let mut writer = Writer::new(Self::state_size(
42            self.destination.modes.len(),
43            self.destination.positions.len(),
44            self.source.modes.len(),
45            self.source.positions.len(),
46        )?);
47        saved::write_sw_basis(&mut writer, &self.destination);
48        saved::write_sw_basis(&mut writer, &self.source);
49        for k in self.ks {
50            writer.complex(k);
51        }
52        writer.complex(self.eta);
53        writer.byte(u8::from(self.helicity));
54        self.lattice.write_state(&mut writer);
55        Ok(writer.finish())
56    }
57
58    fn from_state(bytes: &[u8]) -> Result<Self> {
59        let mut shape = Reader::new(bytes);
60        let (dm, dp) = saved::read_sw_basis_dimensions(&mut shape)?;
61        let (sm, sp) = saved::read_sw_basis_dimensions(&mut shape)?;
62        if bytes.len() != Self::state_size(dm, dp, sm, sp)? {
63            return Err(saved::invalid());
64        }
65        let mut reader = Reader::new(bytes);
66        let destination = saved::read_sw_basis(&mut reader)?;
67        let source = saved::read_sw_basis(&mut reader)?;
68        let ks = [reader.complex()?, reader.complex()?];
69        let eta = reader.complex()?;
70        let helicity = match reader.byte()? {
71            0 => false,
72            1 => true,
73            _ => return Err(saved::invalid()),
74        };
75        let lattice = BlochLattice::read_state(&mut reader)?;
76        reader.finish()?;
77        validate_expansion(&destination, &source, ks, helicity)?;
78        if !finite(eta) {
79            return Err(saved::invalid());
80        }
81        let blocks = blocks(&destination, &source, helicity, false)?;
82        Ok(Self {
83            destination,
84            source,
85            ks,
86            lattice,
87            eta,
88            helicity,
89            blocks,
90        })
91    }
92}
93
94impl LatticeExpansionFromTableResidual {
95    /// Fixed byte count from the destination and source basis shapes.
96    pub fn state_size(
97        destination_modes: usize,
98        destination_positions: usize,
99        source_modes: usize,
100        source_positions: usize,
101    ) -> Result<usize> {
102        basis_pair_size(
103            destination_modes,
104            destination_positions,
105            source_modes,
106            source_positions,
107            33,
108        )
109    }
110}
111
112impl SavedState for LatticeExpansionFromTableResidual {
113    fn save_state(&self) -> Result<Vec<u8>> {
114        let mut writer = Writer::new(Self::state_size(
115            self.destination.modes.len(),
116            self.destination.positions.len(),
117            self.source.modes.len(),
118            self.source.positions.len(),
119        )?);
120        saved::write_sw_basis(&mut writer, &self.destination);
121        saved::write_sw_basis(&mut writer, &self.source);
122        writer.byte(u8::from(self.helicity));
123        for size in self.table_shape {
124            writer.usize(size);
125        }
126        Ok(writer.finish())
127    }
128
129    fn from_state(bytes: &[u8]) -> Result<Self> {
130        let mut shape = Reader::new(bytes);
131        let (dm, dp) = saved::read_sw_basis_dimensions(&mut shape)?;
132        let (sm, sp) = saved::read_sw_basis_dimensions(&mut shape)?;
133        if bytes.len() != Self::state_size(dm, dp, sm, sp)? {
134            return Err(saved::invalid());
135        }
136        let mut reader = Reader::new(bytes);
137        let destination = saved::read_sw_basis(&mut reader)?;
138        let source = saved::read_sw_basis(&mut reader)?;
139        let helicity = match reader.byte()? {
140            0 => false,
141            1 => true,
142            _ => return Err(saved::invalid()),
143        };
144        let table_shape = [
145            reader.usize()?,
146            reader.usize()?,
147            reader.usize()?,
148            reader.usize()?,
149        ];
150        reader.finish()?;
151        let [destinations, sources, channels, harmonics] = table_shape;
152        let order = destination
153            .modes
154            .iter()
155            .map(|(_, mode)| mode.l)
156            .max()
157            .unwrap_or(0)
158            + source
159                .modes
160                .iter()
161                .map(|(_, mode)| mode.l)
162                .max()
163                .unwrap_or(0);
164        if destinations != dp
165            || sources != sp
166            || !(1..=2).contains(&channels)
167            || (!helicity && channels != 1)
168            || usize::try_from((order + 1).pow(2)) != Ok(harmonics)
169        {
170            return Err(saved::invalid());
171        }
172        let blocks = blocks(&destination, &source, helicity, true)?;
173        Ok(Self {
174            destination,
175            source,
176            helicity,
177            blocks,
178            table_shape,
179            shape: (dm, sm),
180        })
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use crate::{
188        Complex,
189        sw::{Basis, Mode, lattice_expansion},
190    };
191    use nalgebra::DMatrix;
192
193    #[test]
194    fn extreme_saved_split_returns_an_error_without_panicking() {
195        let destination = Basis {
196            modes: vec![(0, Mode { l: 1, m: 0, pol: 0 })],
197            positions: vec![[0.0; 3]],
198        };
199        let source = Basis {
200            positions: vec![[0.1, 0.0, 0.0]],
201            ..destination.clone()
202        };
203        let lattice = BlochLattice::new(&[vec![1.5, 0.0], vec![0.3, 1.4]], &[0.1, 0.2]).unwrap();
204        let (_, mut residual) = lattice_expansion(
205            destination,
206            source,
207            [Complex::new(1.2, 0.1); 2],
208            true,
209            lattice,
210            Complex::new(0.9, 0.1),
211        )
212        .unwrap();
213        for eta in [
214            Complex::new(1e200, 0.0),
215            Complex::new(1e-200, 0.0),
216            Complex::new(0.0, 1e200),
217            Complex::new(1e200, 1e200),
218            Complex::new(f64::MAX, 0.0),
219            Complex::new(0.0, f64::MAX),
220        ] {
221            residual.eta = eta;
222            let restored =
223                LatticeExpansionResidual::from_state(&residual.save_state().unwrap()).unwrap();
224            assert!(
225                restored
226                    .pullback(&DMatrix::from_element(1, 1, Complex::new(1.0, 0.0)))
227                    .is_err(),
228                "eta={eta}"
229            );
230        }
231    }
232}