treams_core/lattice/
direct.rs1use super::{
7 cell::BlochLattice,
8 geometry::visit_cube,
9 inputs::Inputs,
10 wave::{Family, solid_jet},
11};
12use crate::{
13 Complex, Error, Result,
14 numerics::Jet,
15 special::{self, Bessel, Radial, harmonic_normalization},
16};
17
18fn direct_term<const N: usize>(wave: Family, k: Jet<N>, r: [Jet<N>; 3]) -> Result<Jet<N>> {
24 let radius = r.into_iter().map(|v| v * v).sum::<Jet<N>>().sqrt();
25 let argument = k * radius;
26 let (degree, spherical) = match wave {
27 Family::Spherical { l, .. } => (l, true),
28 Family::Cylindrical { m } => (m, false),
29 };
30 let radial = if N == 0 {
31 Jet::constant(special::bessel(
32 f64::from(degree),
33 argument.value,
34 Bessel::H1,
35 spherical,
36 0,
37 )?)
38 } else {
39 let radial = if spherical {
40 special::spherical_radial(
41 u32::try_from(degree).map_err(|_| Error::InvalidInput("invalid degree".into()))?,
42 argument.value,
43 Radial::Singular,
44 )?
45 } else {
46 special::cylindrical_radial(degree, argument.value, Radial::Singular)?
47 };
48 argument.chain(radial.value, radial.first)
49 };
50 let angular = match wave {
51 Family::Spherical { l, m } => {
52 harmonic_normalization(l, m) * solid_jet(l, m, r.map(|v| v / radius))
53 }
54 Family::Cylindrical { m } => ((r[0] + Complex::i() * r[1]) / radius).powi(m),
55 };
56 let result = radial * angular;
57 if !result.finite() {
58 return Err(Error::NonFinite("non-finite direct lattice term".into()));
59 }
60 Ok(result)
61}
62
63pub(super) fn direct_shell<const N: usize>(
71 wave: Family,
72 lattice: &BlochLattice,
73 inputs: &Inputs<N>,
74 shell: i64,
75) -> Result<Jet<N>> {
76 let (dim, axis, r) = (inputs.dim, inputs.axes[0], &inputs.r);
77 let half_cell = dim == 1
78 && r.iter()
79 .enumerate()
80 .all(|(j, v)| j == axis || v.value == Complex::default())
81 && (r[axis].value.re / lattice.direct[0][0]).abs() == 0.5;
82 if N > 0 && half_cell {
83 return Err(Error::InvalidInput("direct half-cell shell grouping changes discontinuously; use the Ewald sum for adjoints".into()));
84 }
85 let mut result = Jet::default();
86 let mut term = |n| {
87 let point = inputs.point(&inputs.direct, n);
88 let shift = inputs.image(&point);
89 if shift.iter().all(|v| v.value == Complex::default()) {
90 return Ok(());
91 }
92 result += (Complex::i() * inputs.phase(&point)).exp() * direct_term(wave, inputs.k, shift)?;
93 Ok(())
94 };
95 if half_cell {
96 let direction = if r[axis].value.re / lattice.direct[0][0] > 0.0 {
97 1
98 } else {
99 -1
100 };
101 term([direction * shell, 0, 0])?;
102 term([-direction * (shell + 1), 0, 0])?;
103 } else {
104 visit_cube(dim, shell, true, term)?;
105 }
106 Ok(result)
107}