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}