1use std::borrow::Cow;
4use std::collections::BTreeMap;
5use std::path::PathBuf;
6
7use serde::Serialize;
8
9use crate::electron::boundary::BandStructure;
10use crate::electron::data::{OpticalElf, Subshell, SubshellBindingTable};
11use crate::electron::elastic::corrections::{
12 Corrections, CorrelationPolarization, PolarizationCutoff, CORRELATION_POLARIZATION_MODEL,
13 EXCHANGE_MODEL,
14};
15use crate::electron::inelastic::{MerminFitOptions, PennAlgorithm};
16use crate::electron::phonon::InsulatorChannels;
17use crate::electron::secondary::SecondaryModel;
18use crate::electron::transport::{BeamSpread, BoundaryModel, Primary, TransportConfig};
19use crate::geometry::Stack;
20use crate::input::{ModelInfo, ResolvedLayer};
21use crate::material::Material;
22use crate::tally::electron::SE_BSE_SPLIT_SOURCE;
23use crate::tally::{ElectronTallyConfig, PsfModel, PsfNormalization, VoxelTallyGrid};
24
25use super::schema::*;
26
27#[derive(Debug, Clone, PartialEq, Serialize)]
29pub struct DataFile {
30 pub path: String,
32 pub resolved_path: PathBuf,
34 pub sha256: String,
36}
37
38#[derive(Debug, Clone, PartialEq)]
40pub struct ElasticChoice {
41 pub model: ElasticModelChoice,
43 pub potential: PotentialChoice,
45 pub exchange: bool,
47 pub correlation_polarization: BTreeMap<u8, CorrelationPolarization>,
49}
50
51impl ElasticChoice {
52 pub fn corrections(&self, z: u8) -> Corrections {
54 Corrections {
55 exchange: self.exchange,
56 correlation_polarization: self.correlation_polarization.get(&z).cloned(),
57 }
58 }
59
60 pub fn any_correction(&self) -> bool {
62 self.exchange || !self.correlation_polarization.is_empty()
63 }
64
65 pub fn corrections_description(&self) -> String {
69 if !self.any_correction() {
70 return String::new();
71 }
72 let mut parts = Vec::new();
73 if self.exchange {
74 parts.push(format!("exchange: {EXCHANGE_MODEL}"));
75 }
76 if !self.correlation_polarization.is_empty() {
77 let each: Vec<String> = self
78 .correlation_polarization
79 .iter()
80 .map(|(z, c)| {
81 let sym = crate::elements::element(*z).map_or("?", |e| e.symbol);
82 let cut = match c.cutoff {
83 PolarizationCutoff::Seltzer => "Seltzer b_pol^2".to_string(),
84 PolarizationCutoff::BPolSquared(b) => format!("b_pol^2 = {b}"),
85 };
86 format!(
87 "{sym}: alpha_p = {} bohr^3 ({}), {cut}, outer radius {} bohr",
88 c.polarizability, c.polarizability_source, c.outer_radius
89 )
90 })
91 .collect();
92 parts.push(format!(
93 "correlation-polarization: {CORRELATION_POLARIZATION_MODEL}; {}",
94 each.join("; ")
95 ));
96 }
97 format!("; corrections: {}", parts.join("; "))
98 }
99}
100
101#[derive(Debug, Clone, PartialEq)]
103pub struct ResolvedInnerShells {
104 pub subshells: Vec<Subshell>,
107 pub binding: SubshellBindingTable,
110 pub binding_file: Option<DataFile>,
112}
113
114#[derive(Debug, Clone, PartialEq)]
116pub struct ResolvedElectronMaterial {
117 pub name: String,
119 pub material: Material,
121 pub optical_elf: OpticalElf,
123 pub optical_elf_file: DataFile,
125 pub band: Option<BandStructure>,
127 pub channels: InsulatorChannels,
129 pub mermin_fit: MerminFitOptions,
132 pub inner_shells: Option<ResolvedInnerShells>,
134 pub e_pair: Option<ResolvedEPair>,
137}
138
139#[derive(Debug, Clone, PartialEq)]
141pub struct ResolvedEPair {
142 pub ev: f64,
144 pub source: String,
146}
147
148#[derive(Debug, Clone)]
150pub struct ResolvedElectron {
151 pub input: ElectronInput,
153 pub stack: Stack,
155 pub layers: Vec<ResolvedLayer>,
157 pub layer_material: Vec<usize>,
159 pub materials: Vec<ResolvedElectronMaterial>,
161 pub config: TransportConfig,
163 pub primary: Primary,
165 pub beam: BeamSpread,
168 pub elastic: ElasticChoice,
170 pub inelastic: PennAlgorithm,
172 pub inelastic_fermi_ev: f64,
175 pub table_energy_ev: Vec<f64>,
177 pub tally: ElectronTallyConfig,
179 pub voxel_grid: Option<VoxelTallyGrid>,
181 pub psf_fits: Vec<PsfModel>,
184 pub psf_normalization: PsfNormalization,
186 pub warnings: Vec<String>,
188}
189
190impl ResolvedElectron {
191 pub fn models(&self) -> Vec<ModelInfo> {
195 let mut m = vec![
196 ModelInfo {
197 role: "electron transport",
198 name: "event-by-event",
199 citation: Cow::Borrowed(
200 "Kieft and Bosch, J. Phys. D: Appl. Phys. 41, 215310 (2008), \
201 doi:10.1088/0022-3727/41/21/215310",
202 ),
203 },
204 ModelInfo {
205 role: "elastic scattering",
206 name: "mott-partial-waves",
207 citation: Cow::Borrowed(
208 "Mott cross section from radial-Dirac phase shifts (electron::elastic), \
209 independent-atom additivity (electron::elastic::table)",
210 ),
211 },
212 ];
213 m.push(match self.elastic.potential {
214 PotentialChoice::ThomasFermiYukawa => ModelInfo {
215 role: "elastic potential",
216 name: "thomas-fermi-yukawa",
217 citation: Cow::Borrowed(
218 "STAND-IN Yukawa potential with the Thomas-Fermi screening length \
219 a = 0.8853 a0 Z^(-1/3) (Firsov 1958; Lindhard, Scharff and Schiott 1963); \
220 not a DHFS atomic potential",
221 ),
222 },
223 PotentialChoice::SalvatDhfs => ModelInfo {
224 role: "elastic potential",
225 name: "salvat-dhfs",
226 citation: Cow::Borrowed(
227 "Salvat, Martinez, Mayol and Parellada, Phys. Rev. A 36, 467 (1987), \
228 doi:10.1103/PhysRevA.36.467",
229 ),
230 },
231 });
232 if self.elastic.exchange {
233 m.push(ModelInfo {
234 role: "elastic exchange correction",
235 name: "furness-mccarthy",
236 citation: Cow::Borrowed(EXCHANGE_MODEL),
237 });
238 }
239 if !self.elastic.correlation_polarization.is_empty() {
240 m.push(ModelInfo {
241 role: "elastic correlation-polarization correction",
242 name: "salvat-2003",
243 citation: Cow::Borrowed(CORRELATION_POLARIZATION_MODEL),
244 });
245 }
246 m.push(ModelInfo {
247 role: "inelastic scattering",
248 name: self.inelastic.label(),
249 citation: Cow::Borrowed(match self.inelastic {
250 PennAlgorithm::SinglePole => {
251 "single-pole Penn algorithm, Penn, Phys. Rev. B 35, 482 (1987), as written \
252 in Shinotsuka et al., Surf. Interface Anal. 49, 238 (2017), \
253 doi:10.1002/sia.6123, eqs. (7)-(13)"
254 }
255 PennAlgorithm::Full => {
256 "full Penn algorithm, Penn, Phys. Rev. B 35, 482 (1987), as written in \
257 Shinotsuka et al., Surf. Interface Anal. 49, 238 (2017), \
258 doi:10.1002/sia.6123, eqs. (4)-(6)"
259 }
260 PennAlgorithm::Mermin => {
261 "Mermin-ELF (MELF-GOS) oscillator fit, Mermin, Phys. Rev. B 1, 2362 (1970), \
262 as written in de Vera et al., Int. J. Mol. Sci. 23, 6121 (2022), \
263 doi:10.3390/ijms23116121, eqs. (3)-(5)"
264 }
265 }),
266 });
267 if self.config.secondaries != SecondaryModel::Off {
268 m.push(ModelInfo {
269 role: "secondary electrons",
270 name: "kieft-bosch",
271 citation: Cow::Borrowed(
272 "Kieft and Bosch, J. Phys. D 41, 215310 (2008), as written out in Verduin, \
273 PhD thesis, TU Delft (2017), doi:10.4233/uuid:f214f594-a21f-4318-9f29-9776d60ab06c, \
274 Eqs. 3.86, 3.100-3.111; ported from Nebula (BSD-3-Clause)",
275 ),
276 });
277 }
278 if self.config.boundary != BoundaryModel::Transparent {
279 m.push(ModelInfo {
280 role: "surface and interface barrier",
281 name: "step-barrier",
282 citation: Cow::Borrowed(
283 "inner-potential step, Verduin, PhD thesis, TU Delft (2017), Eqs. 3.136, \
284 3.139, 3.145; ported from Nebula (BSD-3-Clause)",
285 ),
286 });
287 }
288 if self.materials.iter().any(|x| x.channels.phonon.is_some()) {
289 m.push(ModelInfo {
290 role: "LO-phonon scattering",
291 name: "frohlich",
292 citation: Cow::Borrowed(
293 "Frohlich, Adv. Phys. 3, 325 (1954), as written in Ding, Li, Da, Liu, Sci. \
294 Technol. Adv. Mater. 22, 932 (2021), Eqs. (14)-(16), and Taioli and Dapor, \
295 arXiv:2404.07521v5, Eqs. (33)-(34)",
296 ),
297 });
298 }
299 if self.materials.iter().any(|x| x.channels.polaron.is_some()) {
300 m.push(ModelInfo {
301 role: "polaron trapping",
302 name: "ganachaud-mokrani",
303 citation: Cow::Borrowed(
304 "C exp(-gamma E), Ganachaud and Mokrani, Surf. Sci. 334, 329 (1995), as \
305 written in Taioli and Dapor, arXiv:2404.07521v5, Eq. (37)",
306 ),
307 });
308 }
309 if self.materials.iter().any(|x| x.inner_shells.is_some()) {
310 m.push(ModelInfo {
311 role: "inner-shell ionisation",
312 name: "hydrogenic-shell-single-pole-penn",
313 citation: Cow::Borrowed(
314 "one channel per inner subshell, each from that shell's hydrogenic optical \
315 ELF (electron::inelastic::shell_elf) with single-pole Penn, as the inner \
316 part of the ELF in de Vera et al., Int. J. Mol. Sci. 23, 6121 (2022), \
317 doi:10.3390/ijms23116121, eqs. (1)-(2), (32), (35); secondary energy \
318 E_F + W - B (Verduin 2017, Eq. 3.86); no exchange correction",
319 ),
320 });
321 }
322 m.push(ModelInfo {
323 role: "SE/BSE split",
324 name: "energy-split",
325 citation: Cow::Borrowed(SE_BSE_SPLIT_SOURCE),
326 });
327 m
328 }
329}