Skip to main content

lindhard/ion/crystal/
mod.rs

1//! Crystalline targets: the cubic and hexagonal lattice models and the
2//! geometry that sets the ion direction in the crystal frame (steps 19a and
3//! 19b of the M2 crystal plan).
4//!
5//! This module is the data model. The binary-collision engine
6//! ([`crate::ion::bca`]) uses it for the regions named in
7//! [`Bca::with_crystal`](crate::ion::bca::Bca::with_crystal) (the crystal
8//! flight model, see [`crate::ion::bca::crystal`]); everywhere else it is
9//! unchanged and amorphous runs are bit-identical. It provides three pieces:
10//!
11//! * [`Lattice`] ([`lattice`]): Bravais (primitive) vectors, a basis of sites
12//!   with their species, and lattice parameters stated with their
13//!   temperature. Constructors exist for the cubic **diamond** (Si, Ge) and
14//!   **zincblende** (GaAs, 3C-SiC) structures and the hexagonal **wurtzite**
15//!   and **polytype** (stacking sequence, e.g. 4H, 6H) structures. Presets
16//!   carry cited lattice parameters ([`LatticeConstant`],
17//!   [`HexagonalConstants`]): Si, Ge, GaAs, 3C-SiC, GaN, 4H-SiC and 6H-SiC.
18//!   Miller-index plane normals and lattice directions are computed from the
19//!   reciprocal lattice; hexagonal lattices also take four-index
20//!   (Miller-Bravais) planes `(hkil)` and directions `[uvtw]`, with the
21//!   convention in the [`lattice`] module docs.
22//! * [`Orientation`] ([`orientation`]): the wafer cut (surface normal and an
23//!   in-plane reference direction, both as indices), and the beam tilt, twist
24//!   and wafer rotation. It gives the lab-to-crystal rotation and the beam
25//!   direction in the crystal frame. The conventions are written out in the
26//!   [`orientation`] module docs and in `docs/crystal-orientation.md`, with a
27//!   figure.
28//! * [`Divergence`] ([`divergence`]): beam divergence sampled on the
29//!   per-particle random stream ([`crate::rng::stream`]): a Gaussian
30//!   (independent normal deviations in two orthogonal angles) or a uniform
31//!   cone.
32//!
33//! # Frames
34//!
35//! * **Crystal frame**: Cartesian axes along the cube edges of the
36//!   conventional cell, so the direction `[100]` is `(1, 0, 0)`. For a
37//!   hexagonal lattice, `z` is the `c` axis `[0001]` and `x` is along
38//!   `[11-20]` (`a1 + a2`). Lengths are metres ([`crate::units`]).
39//! * **Lab frame**: the frame of [`crate::geometry`]: `x` is depth (the inward
40//!   surface normal), `y` and `z` are lateral. A beam direction with polar
41//!   angle `θ` from `+x` and azimuth `φ` from `+y` towards `+z` is
42//!   `(cos θ, sin θ cos φ, sin θ sin φ)`, the same as
43//!   [`crate::ion::bca::Beam::direction`].
44//!
45//! Thermal vibration of lattice atoms is the Debye model in [`debye`]
46//! (amplitude and displacement sampling). The crystal flight model of the
47//! engine uses it when a crystal is given a temperature
48//! ([`CrystalTarget::thermal`](crate::ion::bca::CrystalTarget::thermal)).
49//!
50//! [`search`] finds the lattice sites within an impact parameter of a path
51//! segment by walking rectangular cells ([`Lattice::orthogonal_cell`]: the
52//! cube, or the orthohexagonal cell), ordered along the path (geometry only;
53//! the crystal flight model of the engine calls it for every segment).
54
55pub mod debye;
56pub mod divergence;
57pub mod lattice;
58pub mod orientation;
59pub mod search;
60
61pub use divergence::Divergence;
62pub use lattice::{HexagonalConstants, Lattice, LatticeConstant, Site, Structure};
63pub use orientation::Orientation;
64pub use search::{Candidate, LatticeSearch};
65
66use crate::vec3::{dot, scale};
67
68/// Errors from building lattices, orientations and divergence models.
69#[derive(Debug, Clone, PartialEq, thiserror::Error)]
70pub enum CrystalError {
71    /// The lattice constant was zero, negative or not finite.
72    #[error("invalid lattice constant {0} m: must be finite and positive")]
73    InvalidLatticeConstant(f64),
74    /// The temperature was negative or not finite.
75    #[error("invalid temperature {0} K: must be finite and non-negative")]
76    InvalidTemperature(f64),
77    /// Atomic number outside the element table.
78    #[error("unknown atomic number {0}")]
79    UnknownAtomicNumber(u8),
80    /// A Miller index or direction index triple was all zero.
81    #[error("index triple {0:?} is all zero")]
82    ZeroIndex([i32; 3]),
83    /// A four-index (Miller-Bravais) plane or direction broke its constraint.
84    #[error("invalid Miller-Bravais indices {indices:?}: {why}")]
85    InvalidMillerBravais {
86        /// The four indices as given.
87        indices: [i32; 4],
88        /// The rule they broke.
89        why: &'static str,
90    },
91    /// Four-index input was used with a lattice that is not hexagonal.
92    #[error("Miller-Bravais (four-index) input needs a hexagonal lattice")]
93    NotHexagonal,
94    /// A wurtzite or polytype internal parameter `u` was outside `(0, 1/2)`
95    /// or not finite.
96    #[error("invalid internal parameter u = {0}: must be finite and in (0, 1/2)")]
97    InvalidInternalParameter(f64),
98    /// A polytype stacking sequence was not valid.
99    #[error("invalid stacking sequence {stacking:?}: {why}")]
100    InvalidStacking {
101        /// The sequence as given.
102        stacking: String,
103        /// The rule it broke.
104        why: &'static str,
105    },
106    /// The in-plane reference direction does not lie in the surface plane
107    /// (the zone law `h u + k v + l w = 0` fails).
108    #[error(
109        "reference direction {reference:?} does not lie in the ({normal:?}) plane: \
110         h u + k v + l w = {dot}, must be 0"
111    )]
112    ReferenceNotInPlane {
113        /// Miller indices of the surface plane.
114        normal: [i32; 3],
115        /// Indices of the reference direction.
116        reference: [i32; 3],
117        /// The offending zone-law sum.
118        dot: i64,
119    },
120    /// A lattice-search argument was invalid.
121    #[error("invalid search {name}: {why}")]
122    InvalidSearch {
123        /// Which argument.
124        name: &'static str,
125        /// The rule it broke.
126        why: &'static str,
127    },
128    /// An angle was out of range or not finite.
129    #[error("invalid {name} {value} rad: {why}")]
130    InvalidAngle {
131        /// Which angle.
132        name: &'static str,
133        /// Offending value.
134        value: f64,
135        /// The rule it broke.
136        why: &'static str,
137    },
138}
139
140pub(crate) fn add(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
141    [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
142}
143
144pub(crate) fn unit(v: [f64; 3]) -> [f64; 3] {
145    scale(v, 1.0 / dot(v, v).sqrt())
146}