1use crate::elements::element_by_symbol;
4use crate::geometry::Stack;
5use crate::ion::bca::{BcaConfig, Beam, CrystalTarget, MeanFreePath};
6use crate::ion::potential::{Screening, ScreeningLength};
7use crate::ion::scattering::TableSpec;
8use crate::ion::stopping::bethe::BetheBloch;
9use crate::ion::stopping::lindhard_scharff::LindhardScharff;
10use crate::ion::stopping::none::NoStopping;
11use crate::ion::stopping::table::TableOverride;
12use crate::ion::stopping::ElectronicStopping;
13use crate::material::{Material, MaterialSpec};
14use serde::Serialize;
15use std::borrow::Cow;
16use std::collections::BTreeMap;
17
18use super::schema::*;
19use super::{invalid, Input, InputError};
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
23pub struct ModelInfo {
24 pub role: &'static str,
26 pub name: &'static str,
28 pub citation: Cow<'static, str>,
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub struct ResolvedLayer {
35 pub source: String,
38 pub material: Material,
40}
41
42#[derive(Debug, Clone)]
44pub struct ResolvedCrystal {
45 pub regions: Vec<usize>,
47 pub target: CrystalTarget,
50}
51
52#[derive(Debug, Clone)]
54pub struct Resolved {
55 pub input: Input,
57 pub beam: Beam,
59 pub divergence: crate::ion::crystal::Divergence,
62 pub stack: Stack,
64 pub layers: Vec<ResolvedLayer>,
66 pub config: BcaConfig,
68 pub screening: Screening,
70 pub screening_length: ScreeningLength,
72 pub table_spec: TableSpec,
74 pub stopping_tables: Vec<LoadedStoppingTable>,
76 pub crystals: Vec<ResolvedCrystal>,
78 pub warnings: Vec<String>,
80 pub tuning: Option<TuningReport>,
82}
83
84pub const TABLE_SPEC: TableSpec = TableSpec {
91 eps_min: 1e-6,
92 eps_max: 1e4,
93 beta_min: 1e-5,
94 beta_max: 1e2,
95 per_decade: 32,
96};
97
98pub(crate) fn resolve_material(
103 materials: &BTreeMap<String, MaterialSpec>,
104 field: &str,
105 r: &MaterialRef,
106) -> Result<ResolvedLayer, InputError> {
107 match r {
108 MaterialRef::Inline(spec) => Material::try_from(spec.clone())
109 .map(|material| ResolvedLayer {
110 source: "inline".into(),
111 material,
112 })
113 .map_err(|e| invalid(field, e.to_string())),
114 MaterialRef::Name(name) => {
115 if let Some(spec) = materials.get(name) {
116 let mut material = Material::try_from(spec.clone())
117 .map_err(|e| invalid(format!("materials.{name}"), e.to_string()))?;
118 if material.name().is_none() {
119 material = material.with_name(name.clone());
120 }
121 return Ok(ResolvedLayer {
122 source: name.clone(),
123 material,
124 });
125 }
126 let el = element_by_symbol(name).ok_or_else(|| {
127 invalid(
128 field,
129 format!(
130 "unknown material {name:?}: not a key of [materials] \
131 and not an element symbol"
132 ),
133 )
134 })?;
135 let material = Material::from_atom_fractions(&[(el.z, 1.0)], None)
136 .map_err(|e| {
137 invalid(
138 field,
139 format!("element {name}: {e}; define it in [materials] with a density"),
140 )
141 })?
142 .with_name(name.clone());
143 Ok(ResolvedLayer {
144 source: name.clone(),
145 material,
146 })
147 }
148 }
149}
150
151pub fn stopping_model(choice: StoppingChoice) -> Box<dyn ElectronicStopping + Send + Sync> {
158 match choice {
159 StoppingChoice::LindhardScharff | StoppingChoice::EquipartitionLsOr => {
160 Box::new(LindhardScharff::new())
161 }
162 StoppingChoice::BetheBloch => Box::new(BetheBloch::new()),
163 StoppingChoice::None => Box::new(NoStopping::new()),
164 }
165}
166
167impl Resolved {
168 pub fn stopping_model(&self) -> Box<dyn ElectronicStopping + Send + Sync> {
172 let base = stopping_model(self.input.physics.stopping);
173 if self.stopping_tables.is_empty() {
174 base
175 } else {
176 Box::new(TableOverride::new(
177 self.stopping_tables
178 .iter()
179 .map(|l| l.table.clone())
180 .collect(),
181 base,
182 ))
183 }
184 }
185
186 pub fn models(&self) -> Vec<ModelInfo> {
188 let mut v = vec![ModelInfo {
189 role: "transport",
190 name: "amorphous-bca",
191 citation: Cow::Borrowed(
192 "J. P. Biersack and L. G. Haggmark, Nucl. Instrum. Methods 174 (1980) 257; \
193 M. T. Robinson and I. M. Torrens, Phys. Rev. B 9 (1974) 5008; \
194 W. Eckstein, Computer Simulation of Ion-Solid Interactions (Springer, 1991)",
195 ),
196 }];
197 if !self.crystals.is_empty() {
198 v.push(ModelInfo {
199 role: "crystal transport",
200 name: "lattice-site-bca",
201 citation: Cow::Borrowed(
202 "collision partners from explicit lattice sites after DISPLATH \
203 (https://github.com/permissionx/DISPLATH, MIT); cubic lattices: \
204 M. J. Mehl et al., Comput. Mater. Sci. 136 (2017) S1; thermal \
205 displacements from the Debye model (lindhard::ion::crystal::debye); \
206 the electronic-constants-unverified flag and the known channeling \
207 deviations are stated in docs/cli.md, [[crystal]]",
208 ),
209 });
210 }
211 v.push(match self.screening {
212 Screening::ZblUniversal => ModelInfo {
213 role: "screening function",
214 name: "zbl-universal",
215 citation: Cow::Borrowed(
216 "J. F. Ziegler, J. P. Biersack, U. Littmark, The Stopping and Range \
217 of Ions in Solids (Pergamon, 1985), ch. 2",
218 ),
219 },
220 Screening::KrC => ModelInfo {
221 role: "screening function",
222 name: "kr-c",
223 citation: Cow::Borrowed(
224 "W. D. Wilson, L. G. Haggmark, J. P. Biersack, Phys. Rev. B 15 (1977) 2458",
225 ),
226 },
227 Screening::Moliere => ModelInfo {
228 role: "screening function",
229 name: "moliere",
230 citation: Cow::Borrowed("G. Moliere, Z. Naturforsch. A 2 (1947) 133"),
231 },
232 Screening::LenzJensen => ModelInfo {
233 role: "screening function",
234 name: "lenz-jensen",
235 citation: Cow::Borrowed(
236 "W. Lenz, Z. Phys. 77 (1932) 713; H. Jensen, Z. Phys. 77 (1932) 722",
237 ),
238 },
239 });
240 v.push(match self.screening_length {
241 ScreeningLength::Universal => ModelInfo {
242 role: "screening length",
243 name: "universal",
244 citation: Cow::Borrowed(
245 "J. F. Ziegler, J. P. Biersack, U. Littmark, The Stopping and Range \
246 of Ions in Solids (Pergamon, 1985), ch. 2",
247 ),
248 },
249 ScreeningLength::Firsov => ModelInfo {
250 role: "screening length",
251 name: "firsov",
252 citation: Cow::Borrowed("O. B. Firsov, Sov. Phys. JETP 6 (1958) 534"),
253 },
254 ScreeningLength::Lindhard => ModelInfo {
255 role: "screening length",
256 name: "lindhard",
257 citation: Cow::Borrowed(
258 "J. Lindhard, M. Scharff, H. E. Schiott, Mat. Fys. Medd. Dan. Vid. \
259 Selsk. 33 (14) (1963)",
260 ),
261 },
262 });
263 v.push(ModelInfo {
264 role: "scattering angle",
265 name: "gauss-mehler-quadrature-table",
266 citation: Cow::Borrowed(
267 "scattering integral by Gauss-Mehler quadrature, tabulated in \
268 (reduced energy, reduced impact parameter); see lindhard::ion::scattering",
269 ),
270 });
271 v.extend(match self.input.physics.stopping {
272 StoppingChoice::LindhardScharff => vec![ModelInfo {
273 role: "electronic stopping",
274 name: "lindhard-scharff",
275 citation: Cow::Borrowed("J. Lindhard and M. Scharff, Phys. Rev. 124 (1961) 128"),
276 }],
277 StoppingChoice::BetheBloch => vec![ModelInfo {
278 role: "electronic stopping",
279 name: "bethe-bloch",
280 citation: Cow::Borrowed(
281 "H. Bethe, Ann. Phys. 5 (1930) 325; F. Bloch, Ann. Phys. 408 (1933) 285; \
282 mean excitation energy by the Bloch rule I = 10 eV Z2",
283 ),
284 }],
285 StoppingChoice::EquipartitionLsOr => vec![
286 ModelInfo {
287 role: "electronic stopping",
288 name: "lindhard-scharff",
289 citation: Cow::Borrowed(
290 "J. Lindhard and M. Scharff, Phys. Rev. 124 (1961) 128",
291 ),
292 },
293 ModelInfo {
294 role: "electronic loss partition",
295 name: "equipartition-ls-or",
296 citation: Cow::Borrowed(
297 "half nonlocal Lindhard-Scharff, half local Oen-Robinson: \
298 O. S. Oen and M. T. Robinson, Nucl. Instrum. Methods 132 (1976) 647",
299 ),
300 },
301 ],
302 StoppingChoice::None => vec![ModelInfo {
303 role: "electronic stopping",
304 name: "none",
305 citation: Cow::Borrowed(
306 "no electronic stopping (nuclear loss only; the k = 0 limit of \
307 J. Lindhard, M. Scharff, H. E. Schiott, Mat. Fys. Medd. Dan. Vid. \
308 Selsk. 33 (14) (1963)); for comparisons and nuclear-only studies, \
309 not a physical model",
310 ),
311 }],
312 });
313 for l in &self.stopping_tables {
314 v.push(ModelInfo {
315 role: "electronic stopping (user table)",
316 name: "user-table",
317 citation: Cow::Owned(format!("{}: {}", l.path, l.table.provenance())),
318 });
319 }
320 if self.input.physics.stopping != StoppingChoice::None || !self.stopping_tables.is_empty() {
322 v.push(ModelInfo {
323 role: "compound stopping",
324 name: "bragg-additivity",
325 citation: Cow::Borrowed(
326 "W. H. Bragg and R. Kleeman, Phil. Mag. 10 (1905) 318; no compound correction",
327 ),
328 });
329 }
330 v.push(match self.config.mean_free_path {
331 MeanFreePath::Constant => ModelInfo {
332 role: "free path",
333 name: "constant",
334 citation: Cow::Borrowed(
335 "J. P. Biersack and L. G. Haggmark, Nucl. Instrum. Methods 174 (1980) 257",
336 ),
337 },
338 MeanFreePath::EnergyDependent { .. } => ModelInfo {
339 role: "free path",
340 name: "energy-dependent",
341 citation: Cow::Borrowed(
342 "W. Eckstein, Computer Simulation of Ion-Solid Interactions \
343 (Springer, 1991)",
344 ),
345 },
346 });
347 v.push(ModelInfo {
348 role: "displacement criterion",
349 name: "e_d-e_b",
350 citation: Cow::Borrowed(
351 "J. P. Biersack and L. G. Haggmark, Nucl. Instrum. Methods 174 (1980) 257; \
352 W. Eckstein (1991)",
353 ),
354 });
355 v.push(ModelInfo {
356 role: "surface barrier",
357 name: "planar",
358 citation: Cow::Borrowed(
359 "W. Eckstein, Computer Simulation of Ion-Solid Interactions (Springer, 1991)",
360 ),
361 });
362 v.push(ModelInfo {
363 role: "random numbers",
364 name: "chacha8-per-history-stream",
365 citation: Cow::Borrowed(
366 "D. J. Bernstein, ChaCha, a variant of Salsa20 (2008); \
367 J. K. Salmon et al., Proc. SC'11 (2011)",
368 ),
369 });
370 v
371 }
372}