lindhard/electron/data/
optical.rs1use super::{
2 check_energy_grid, check_in_range, check_nonnegative, checked_text, invalid, lerp_on_grid,
3 parse_error, read_text, ElectronDataError, Result,
4};
5use serde::{Deserialize, Serialize};
6use std::path::Path;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36#[serde(try_from = "RawOpticalElf", into = "RawOpticalElf")]
37pub struct OpticalElf {
38 material: String,
39 provenance: String,
40 energy_ev: Vec<f64>,
41 elf: Vec<f64>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45#[serde(deny_unknown_fields)]
46struct RawOpticalElf {
47 material: Option<String>,
48 provenance: Option<String>,
49 energy_ev: Vec<f64>,
50 elf: Vec<f64>,
51}
52
53impl TryFrom<RawOpticalElf> for OpticalElf {
54 type Error = ElectronDataError;
55 fn try_from(raw: RawOpticalElf) -> Result<Self> {
56 let provenance = checked_text("provenance", raw.provenance.as_deref())?;
57 let material = checked_text("material", raw.material.as_deref())?;
58 Self::new(material, provenance, raw.energy_ev, raw.elf)
59 }
60}
61
62impl From<OpticalElf> for RawOpticalElf {
63 fn from(e: OpticalElf) -> Self {
64 Self {
65 material: Some(e.material),
66 provenance: Some(e.provenance),
67 energy_ev: e.energy_ev,
68 elf: e.elf,
69 }
70 }
71}
72
73impl OpticalElf {
74 pub fn new(
76 material: impl Into<String>,
77 provenance: impl Into<String>,
78 energy_ev: Vec<f64>,
79 elf: Vec<f64>,
80 ) -> Result<Self> {
81 let provenance = checked_text("provenance", Some(&provenance.into()))?;
82 let material = checked_text("material", Some(&material.into()))?;
83 check_energy_grid("photon energy grid", &energy_ev)?;
84 if elf.len() != energy_ev.len() {
85 return Err(invalid(
86 "ELF values",
87 format!(
88 "need one value per energy: {} energies, {} values",
89 energy_ev.len(),
90 elf.len()
91 ),
92 ));
93 }
94 check_nonnegative("ELF values", &elf)?;
95 Ok(Self {
96 material,
97 provenance,
98 energy_ev,
99 elf,
100 })
101 }
102
103 pub fn from_toml_str(text: &str) -> Result<Self> {
105 let raw: RawOpticalElf = toml::from_str(text).map_err(parse_error)?;
106 Self::try_from(raw)
107 }
108
109 pub fn from_toml_file(path: impl AsRef<Path>) -> Result<Self> {
111 Self::from_toml_str(&read_text(path.as_ref())?)
112 }
113
114 pub fn material(&self) -> &str {
116 &self.material
117 }
118
119 pub fn provenance(&self) -> &str {
121 &self.provenance
122 }
123
124 pub fn energy_ev(&self) -> &[f64] {
126 &self.energy_ev
127 }
128
129 pub fn elf_values(&self) -> &[f64] {
131 &self.elf
132 }
133
134 pub fn energy_range_ev(&self) -> (f64, f64) {
136 (self.energy_ev[0], self.energy_ev[self.energy_ev.len() - 1])
137 }
138
139 pub fn elf(&self, energy_ev: f64) -> Result<f64> {
142 let (lo, hi) = self.energy_range_ev();
143 check_in_range("photon energy (eV)", energy_ev, lo, hi)?;
144 Ok(lerp_on_grid(&self.energy_ev, &self.elf, energy_ev))
145 }
146}