qsym2/interfaces/binaries/
mod.rs1use std::path::PathBuf;
4
5use anyhow::{Context, format_err};
6use byteorder::{BigEndian, LittleEndian};
7use derive_builder::Builder;
8use itertools::Itertools;
9use ndarray::{Array1, Array2, Array4, ShapeBuilder};
10use serde::{Deserialize, Serialize};
11
12use crate::angmom::spinor_rotation_3d::SpinConstraint;
13use crate::drivers::QSym2Driver;
14use crate::drivers::representation_analysis::MagneticSymmetryAnalysisKind;
15use crate::drivers::representation_analysis::angular_function::AngularFunctionRepAnalysisParams;
16use crate::drivers::representation_analysis::slater_determinant::{
17 SlaterDeterminantRepAnalysisDriver, SlaterDeterminantRepAnalysisParams,
18};
19use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionDriver;
20use crate::interfaces::input::SymmetryGroupDetectionInputKind;
21use crate::interfaces::input::analysis::SlaterDeterminantSourceHandle;
22use crate::interfaces::input::ao_basis::InputBasisAngularOrder;
23use crate::io::numeric::NumericReader;
24use crate::io::{QSym2FileType, read_qsym2_binary};
25use crate::symmetry::symmetry_group::{
26 MagneticRepresentedSymmetryGroup, UnitaryRepresentedSymmetryGroup,
27};
28use crate::target::determinant::SlaterDeterminant;
29
30#[cfg(test)]
31#[path = "binaries_tests.rs"]
32mod binaries_tests;
33
34#[derive(Clone, Builder, Serialize, Deserialize)]
41pub struct BinariesSlaterDeterminantSource {
42 pub xyz: PathBuf,
44
45 pub sao: PathBuf,
47
48 #[builder(default = "None")]
51 pub sao_4c: Option<PathBuf>,
52
53 pub coefficients: Vec<PathBuf>,
56
57 pub occupations: Vec<PathBuf>,
59
60 pub baos: Vec<InputBasisAngularOrder>,
63
64 pub spin_constraint: SpinConstraint,
66
67 pub matrix_order: MatrixOrder,
69
70 pub byte_order: ByteOrder,
72}
73
74impl BinariesSlaterDeterminantSource {
75 pub fn builder() -> BinariesSlaterDeterminantSourceBuilder {
78 BinariesSlaterDeterminantSourceBuilder::default()
79 }
80}
81
82impl Default for BinariesSlaterDeterminantSource {
83 fn default() -> Self {
84 BinariesSlaterDeterminantSource::builder()
85 .xyz(PathBuf::from("path/to/xyz"))
86 .sao(PathBuf::from("path/to/2c/ao/overlap/matrix"))
87 .sao_4c(None)
88 .coefficients(vec![
89 PathBuf::from("path/to/alpha/coeffs"),
90 PathBuf::from("path/to/beta/coeffs"),
91 ])
92 .occupations(vec![
93 PathBuf::from("path/to/alpha/occupations"),
94 PathBuf::from("path/to/beta/occupations"),
95 ])
96 .baos(vec![InputBasisAngularOrder::default()])
97 .spin_constraint(SpinConstraint::Unrestricted(2, false))
98 .matrix_order(MatrixOrder::default())
99 .byte_order(ByteOrder::default())
100 .build()
101 .expect("Unable to build a default `BinariesSlaterDeterminantSource`.")
102 }
103}
104
105impl SlaterDeterminantSourceHandle for BinariesSlaterDeterminantSource {
106 type Outcome = (String, String);
107
108 fn sd_source_handle(
109 &self,
110 pd_params_inp: &SymmetryGroupDetectionInputKind,
111 afa_params: &AngularFunctionRepAnalysisParams,
112 sda_params: &SlaterDeterminantRepAnalysisParams<f64>,
113 ) -> Result<Self::Outcome, anyhow::Error> {
114 let pd_res = match pd_params_inp {
115 SymmetryGroupDetectionInputKind::Parameters(pd_params) => {
116 let mut pd_driver = SymmetryGroupDetectionDriver::builder()
117 .parameters(pd_params)
118 .xyz(Some(self.xyz.clone()))
119 .build()
120 .with_context(|| "Unable to construct a symmetry-group detection driver when handling custom Slater determinant source")?;
121 pd_driver.run().with_context(|| {
122 "Unable to run the symmetry-group detection driver successfully when handling custom Slater determinant source"
123 })?;
124 pd_driver
125 .result()
126 .with_context(|| "Unable to retrieve the symmetry-group detection result when handling custom Slater determinant source")?
127 .clone()
128 }
129 SymmetryGroupDetectionInputKind::FromFile(pd_res_file) => read_qsym2_binary(
130 pd_res_file,
131 QSym2FileType::Sym,
132 )
133 .with_context(|| {
134 format!(
135 "Unable to read `{}.qsym2.sym` when handling custom Slater determinant source",
136 pd_res_file.display()
137 )
138 })?,
139 };
140 let mol = &pd_res.pre_symmetry.recentred_molecule;
141 let baos = self.baos.iter().map(|bao| bao.to_basis_angular_order(mol)
142 .with_context(|| "Unable to digest the input basis angular order information when handling custom Slater determinant source")).collect::<Result<Vec<_>, _>>()?;
143 let nfuncs_tot = baos.iter().map(|bao| bao.n_funcs()).sum::<usize>();
144
145 let sao_v = match self.byte_order {
146 ByteOrder::LittleEndian => {
147 NumericReader::<_, LittleEndian, f64>::from_file(&self.sao)
148 .with_context(|| {
149 "Unable to read the specified two-centre SAO file when handling custom Slater determinant source"
150 })?.collect::<Vec<_>>()
151 }
152 ByteOrder::BigEndian => {
153 NumericReader::<_, BigEndian, f64>::from_file(&self.sao)
154 .with_context(|| {
155 "Unable to read the specified two-centre SAO file when handling custom Slater determinant source"
156 })?.collect::<Vec<_>>()
157 }
158 };
159 let sao = match self.matrix_order {
160 MatrixOrder::RowMajor => Array2::from_shape_vec((nfuncs_tot, nfuncs_tot), sao_v)
161 .with_context(|| {
162 "Unable to construct an AO overlap matrix from the read-in row-major binary file when handling custom Slater determinant source"
163 })?,
164 MatrixOrder::ColMajor => Array2::from_shape_vec((nfuncs_tot, nfuncs_tot).f(), sao_v)
165 .with_context(|| {
166 "Unable to construct an AO overlap matrix from the read-in column-major binary file when handling custom Slater determinant source"
167 })?,
168 };
169
170 let sao_4c = if let Some(sao_4c_path) = self.sao_4c.as_ref() {
171 let sao_4c_v = match self.byte_order {
172 ByteOrder::LittleEndian => {
173 NumericReader::<_, LittleEndian, f64>::from_file(sao_4c_path)
174 .with_context(|| {
175 "Unable to read the specified four-centre SAO file when handling custom Slater determinant source"
176 })?.collect::<Vec<_>>()
177 }
178 ByteOrder::BigEndian => {
179 NumericReader::<_, BigEndian, f64>::from_file(sao_4c_path)
180 .with_context(|| {
181 "Unable to read the specified four-centre SAO file when handling custom Slater determinant source"
182 })?.collect::<Vec<_>>()
183 }
184 };
185 let sao_4c = match self.matrix_order {
186 MatrixOrder::RowMajor => Array4::from_shape_vec((nfuncs_tot, nfuncs_tot, nfuncs_tot, nfuncs_tot), sao_4c_v)
187 .with_context(|| {
188 "Unable to construct a four-centre AO overlap matrix from the read-in row-major binary file when handling custom Slater determinant source"
189 })?,
190 MatrixOrder::ColMajor => Array4::from_shape_vec((nfuncs_tot, nfuncs_tot, nfuncs_tot, nfuncs_tot).f(), sao_4c_v)
191 .with_context(|| {
192 "Unable to construct a four-centre AO overlap matrix from the read-in column-major binary file when handling custom Slater determinant source"
193 })?,
194 };
195 Some(sao_4c)
196 } else {
197 None
198 };
199
200 let cs_v = match self.byte_order {
201 ByteOrder::LittleEndian => self
202 .coefficients
203 .iter()
204 .map(|c_path| {
205 NumericReader::<_, LittleEndian, f64>::from_file(c_path)
206 .map(|r| r.collect::<Vec<_>>())
207 })
208 .collect::<Result<Vec<_>, _>>()
209 .with_context(|| {
210 "Unable to read the specified coefficient binary file(s) when handling custom Slater determinant source"
211 })?,
212 ByteOrder::BigEndian => self
213 .coefficients
214 .iter()
215 .map(|c_path| {
216 NumericReader::<_, BigEndian, f64>::from_file(c_path)
217 .map(|r| r.collect::<Vec<_>>())
218 })
219 .collect::<Result<Vec<_>, _>>()
220 .with_context(|| {
221 "Unable to read the specified coefficient binary file(s) when handling custom Slater determinant source"
222 })?,
223 };
224 let cs = match self.matrix_order {
225 MatrixOrder::RowMajor => cs_v
226 .into_iter()
227 .map(|c_v| {
228 let nmo = c_v.len().div_euclid(nfuncs_tot);
229 Array2::from_shape_vec((nfuncs_tot, nmo), c_v)
230 })
231 .collect::<Result<Vec<_>, _>>()
232 .with_context(|| {
233 "Unable to construct coefficient matrix (matrices) from the read-in row-major binary file(s) when handling custom Slater determinant source"
234 })?,
235
236 MatrixOrder::ColMajor => cs_v
237 .into_iter()
238 .map(|c_v| {
239 let nmo = c_v.len().div_euclid(nfuncs_tot);
240 Array2::from_shape_vec((nfuncs_tot, nmo).f(), c_v)
241 })
242 .collect::<Result<Vec<_>, _>>()
243 .with_context(|| {
244 "Unable to construct coefficient matrix (matrices) from the read-in column-major binary file(s) when handling custom Slater determinant source"
245 })?,
246 };
247
248 let occs = match self.byte_order {
249 ByteOrder::LittleEndian => self
250 .occupations
251 .iter()
252 .map(|occ_path| {
253 Ok::<_, anyhow::Error>(Array1::from_vec(
254 NumericReader::<_, LittleEndian, f64>::from_file(occ_path)
255 .map(|r| r.collect::<Vec<f64>>())?,
256 ))
257 })
258 .collect::<Result<Vec<_>, _>>()
259 .with_context(|| {
260 "Unable to read the specified occupation binary file(s) when handling custom Slater determinant source"
261 })?,
262 ByteOrder::BigEndian => self
263 .occupations
264 .iter()
265 .map(|occ_path| {
266 Ok::<_, anyhow::Error>(Array1::from_vec(
267 NumericReader::<_, BigEndian, f64>::from_file(occ_path)
268 .map(|r| r.collect::<Vec<f64>>())?,
269 ))
270 })
271 .collect::<Result<Vec<_>, _>>()
272 .with_context(|| {
273 "Unable to read occupation binary file(s) when handling custom Slater determinant source"
274 })?,
275 };
276
277 let det = SlaterDeterminant::<f64, SpinConstraint>::builder()
278 .coefficients(&cs)
279 .occupations(&occs)
280 .baos(baos.iter().collect_vec())
281 .mol(mol)
282 .structure_constraint(self.spin_constraint.clone())
283 .complex_symmetric(false)
284 .threshold(sda_params.linear_independence_threshold)
285 .build()
286 .with_context(|| "Failed to construct a Slater determinant when handling custom Slater determinant source")?;
287
288 match &sda_params.use_magnetic_group {
289 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
290 let mut sda_driver = SlaterDeterminantRepAnalysisDriver::<
291 MagneticRepresentedSymmetryGroup,
292 f64,
293 SpinConstraint,
294 >::builder()
295 .parameters(sda_params)
296 .angular_function_parameters(afa_params)
297 .determinant(&det)
298 .sao(&sao)
299 .sao_spatial_4c(sao_4c.as_ref())
300 .symmetry_group(&pd_res)
301 .build()
302 .with_context(|| {
303 "Failed to construct a Slater determinant corepresentation analysis driver when handling custom Slater determinant source"
304 })?;
305 sda_driver
306 .run()
307 .with_context(|| {
308 "Failed to execute the Slater determinant corepresentation analysis driver successfully when handling custom Slater determinant source"
309 })?;
310 let group_name = pd_res
311 .magnetic_symmetry
312 .as_ref()
313 .and_then(|magsym| magsym.group_name.clone())
314 .ok_or(format_err!("Magnetic group name not found when handling custom Slater determinant source."))?;
315 let sym = sda_driver
316 .result()
317 .with_context(|| {
318 "Failed to obtain corepresentation analysis result when handling custom Slater determinant source"
319 })?
320 .determinant_symmetry()
321 .as_ref()
322 .map_err(|err| format_err!(err.clone()))
323 .with_context(|| {
324 "Failed to obtain determinant symmetry from corepresentation analysis result when handling custom Slater determinant source"
325 })?
326 .to_string();
327 Ok((group_name, sym))
328 }
329 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
330 let mut sda_driver = SlaterDeterminantRepAnalysisDriver::<
331 UnitaryRepresentedSymmetryGroup,
332 f64,
333 SpinConstraint,
334 >::builder()
335 .parameters(sda_params)
336 .angular_function_parameters(afa_params)
337 .determinant(&det)
338 .sao(&sao)
339 .sao_spatial_4c(sao_4c.as_ref())
340 .symmetry_group(&pd_res)
341 .build()
342 .with_context(|| {
343 "Failed to construct a Slater determinant representation analysis driver when handling custom Slater determinant source"
344 })?;
345 sda_driver
346 .run()
347 .with_context(|| {
348 "Failed to execute the Slater determinant representation analysis driver successfully when handling custom Slater determinant source"
349 })?;
350 let group_name = if sda_params.use_magnetic_group.is_none() {
351 pd_res
352 .unitary_symmetry
353 .group_name
354 .as_ref()
355 .ok_or(format_err!("Unitary group name not found when handling custom Slater determinant source."))?.clone()
356 } else {
357 pd_res
358 .magnetic_symmetry
359 .as_ref()
360 .and_then(|magsym| magsym.group_name.clone())
361 .ok_or(format_err!("Magnetic group name not found when handling custom Slater determinant source."))?
362 };
363 let sym = sda_driver
364 .result()
365 .with_context(|| {
366 "Failed to obtain representation analysis result when handling custom Slater determinant source"
367 })?
368 .determinant_symmetry()
369 .as_ref()
370 .map_err(|err| format_err!(err.clone()))
371 .with_context(|| {
372 "Failed to obtain determinant symmetry from representation analysis result when handling custom Slater determinant source"
373 })?
374 .to_string();
375 Ok((group_name, sym))
376 }
377 }
378 }
379}
380
381#[derive(Clone, Serialize, Deserialize, Default)]
384pub enum MatrixOrder {
385 #[default]
386 RowMajor,
387 ColMajor,
388}
389
390#[derive(Clone, Serialize, Deserialize, Default)]
392pub enum ByteOrder {
393 #[default]
394 LittleEndian,
395 BigEndian,
396}