Skip to content

Commit 8de6d3b

Browse files
authored
Merge pull request #335 from lcpp-org/compound_dict_macro
Compound dict macro
2 parents fd2d99a + c5db0f7 commit 8de6d3b

1 file changed

Lines changed: 104 additions & 112 deletions

File tree

‎src/lib.rs‎

Lines changed: 104 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,60 @@ mod libRustBCA {
146146
use super::rustbca_local_py;
147147
}
148148

149+
#[cfg(feature = "python")]
150+
macro_rules! geometry_typed_silent_loops {
151+
($geometry_type:ty, $input:expr, $python:expr) => {
152+
{
153+
let input: <$geometry_type as geometry::Geometry>::InputFileFormat = depythonize(&$input)?;
154+
let (particle_input_array, material, options, output_units) = input::process_input_file(input);
155+
let pool = rayon::ThreadPoolBuilder::new().num_threads(options.num_threads).build()
156+
.map_err(|e| PyRuntimeError::new_err(format!("Failed to initialize thread pool.")))?;
157+
let finished_particles = pool.install( ||
158+
physics::silent_physics_loop::<$geometry_type>(particle_input_array, material, options, output_units.clone())
159+
);
160+
let finished_particles_container = physics::process_finished_particles_to_arrays(finished_particles, output_units);
161+
Ok(pythonize($python, &finished_particles_container)?)
162+
}
163+
}
164+
}
165+
166+
#[cfg(feature = "python")]
167+
macro_rules! geometry_typed_loops {
168+
($geometry_type:ty, $input:expr, $python:expr) => {
169+
{
170+
let input: <$geometry_type as geometry::Geometry>::InputFileFormat = depythonize(&$input)?;
171+
let (particle_input_array, material, options, output_units) = input::process_input_file(input);
172+
let pool = rayon::ThreadPoolBuilder::new().num_threads(options.num_threads).build()
173+
.map_err(|e| PyRuntimeError::new_err(format!("Failed to initialize thread pool.")))?;
174+
pool.install( ||
175+
physics::physics_loop::<$geometry_type>(particle_input_array, material, options, output_units)
176+
);
177+
Ok(())
178+
}
179+
}
180+
}
181+
182+
#[cfg(feature = "python")]
183+
macro_rules! get_vec_from_dicts {
184+
($dicts:expr, $key:expr) => {
185+
$dicts.iter()
186+
.enumerate()
187+
.map(|(index, dict)| dict.get_item($key)?
188+
// Error propagation is tricky here, because get_item returns Result<Option<...>>
189+
.ok_or_else(|| PyValueError::new_err(format!("Failed to get key {} from dict at index {}.", $key, index)))?
190+
.extract()).collect::<PyResult<Vec<f64>>>()
191+
}
192+
}
193+
194+
#[cfg(feature = "python")]
195+
macro_rules! get_value_from_dict {
196+
($dict:expr, $key:expr) => {
197+
$dict.get_item($key)?
198+
.ok_or_else(|| PyValueError::new_err(format!("Failed to get key {} from dict.", $key)))?
199+
.extract()
200+
}
201+
}
202+
149203
#[derive(Debug)]
150204
#[repr(C)]
151205
pub struct InputSimpleBCA {
@@ -1761,19 +1815,25 @@ pub fn rotate_back_vec_py<'py>(nx: Vec<f64>, ny: Vec<f64>, nz: Vec<f64>, ux: Vec
17611815
/// num_samples: number of ion trajectories to run; precision will go as 1/sqrt(N)
17621816
pub fn sputtering_yield<'py>(ion: &Bound<'py, PyDict>, target: &Bound<'py, PyDict>, energy: f64, angle: f64, num_samples: usize) -> PyResult<f64> {
17631817

1764-
assert!(angle.abs() <= 90.0, "Incident angle w.r.t. surface normal, {}, cannot exceed 90 degrees.", angle);
1818+
if angle.abs() > 90.0 {
1819+
return Err(PyValueError::new_err(
1820+
format!(
1821+
"Incident angle cannot exceed 90 degrees; {} given.", angle
1822+
)
1823+
))
1824+
}
17651825

1766-
let Z1: f64 = ion.get_item("Z")?.expect("Error: Cannot get key 'Z' from ion dict.").extract()?;
1767-
let m1: f64 = ion.get_item("m")?.expect("Error: Cannot get key 'm' from ion dict.").extract()?;
1768-
let Es1: f64 = ion.get_item("Es")?.expect("Error: Cannot get key 'Es' from ion dict.").extract()?;
1769-
let Ec1: f64 = ion.get_item("Ec")?.expect("Error: Cannot get key 'Ec' from ion dict.").extract()?;
1826+
let Z1: f64 = get_value_from_dict!(ion, "Z")?;
1827+
let m1: f64 = get_value_from_dict!(ion, "m")?;
1828+
let Es1: f64 = get_value_from_dict!(ion, "Es")?;
1829+
let Ec1: f64 = get_value_from_dict!(ion, "Ec")?;
17701830

1771-
let Z2: f64 = target.get_item("Z")?.expect("Error: Cannot get key 'Z' from target dict.").extract()?;
1772-
let m2: f64 = target.get_item("m")?.expect("Error: Cannot get key 'm' from target dict.").extract()?;
1773-
let Es2: f64 = target.get_item("Es")?.expect("Error: Cannot get key 'Es' from target dict.").extract()?;
1774-
let Ec2: f64 = target.get_item("Ec")?.expect("Error: Cannot get key 'Ec' from target dict.").extract()?;
1775-
let Eb2: f64 = target.get_item("Eb")?.expect("Error: Cannot get key 'Eb' from target dict.").extract()?;
1776-
let n2: f64 = target.get_item("n")?.expect("Error: Cannot get key 'n' from target dict.").extract()?;
1831+
let Z2: f64 = get_value_from_dict!(target, "Z")?;
1832+
let m2: f64 = get_value_from_dict!(target, "m")?;
1833+
let Es2: f64 = get_value_from_dict!(target, "Es")?;
1834+
let Ec2: f64 = get_value_from_dict!(target, "Ec")?;
1835+
let Eb2: f64 = get_value_from_dict!(target, "Eb")?;
1836+
let n2: f64 = get_value_from_dict!(target, "n")?;
17771837

17781838
let options = Options::default_options(true);
17791839

@@ -1852,19 +1912,25 @@ pub fn sputtering_yield<'py>(ion: &Bound<'py, PyDict>, target: &Bound<'py, PyDic
18521912
/// R_E (f64): energy reflection coefficient (sum of reflected particle energies / total incident energy)
18531913
pub fn reflection_coefficient<'py>(ion: &Bound<'py, PyDict>, target: &Bound<'py, PyDict>, energy: f64, angle: f64, num_samples: usize) -> PyResult<(f64, f64)> {
18541914

1855-
assert!(angle.abs() <= 90.0, "Incident angle w.r.t. surface normal, {}, cannot exceed 90 degrees.", angle);
1915+
if angle.abs() > 90.0 {
1916+
return Err(PyValueError::new_err(
1917+
format!(
1918+
"Incident angle cannot exceed 90 degrees; {} given.", angle
1919+
)
1920+
))
1921+
}
18561922

1857-
let Z1: f64 = ion.get_item("Z")?.expect("Error: Cannot get key 'Z' from ion dict.").extract()?;
1858-
let m1: f64 = ion.get_item("m")?.expect("Error: Cannot get key 'm' from ion dict.").extract()?;
1859-
let Es1: f64 = ion.get_item("Es")?.expect("Error: Cannot get key 'Es' from ion dict.").extract()?;
1860-
let Ec1: f64 = ion.get_item("Ec")?.expect("Error: Cannot get key 'Ec' from ion dict.").extract()?;
1923+
let Z1: f64 = get_value_from_dict!(ion, "Z")?;
1924+
let m1: f64 = get_value_from_dict!(ion, "m")?;
1925+
let Es1: f64 = get_value_from_dict!(ion, "Es")?;
1926+
let Ec1: f64 = get_value_from_dict!(ion, "Ec")?;
18611927

1862-
let Z2: f64 = target.get_item("Z")?.expect("Error: Cannot get key 'Z' from target dict.").extract()?;
1863-
let m2: f64 = target.get_item("m")?.expect("Error: Cannot get key 'm' from target dict.").extract()?;
1864-
let Es2: f64 = target.get_item("Es")?.expect("Error: Cannot get key 'Es' from target dict.").extract()?;
1865-
let Ec2: f64 = target.get_item("Ec")?.expect("Error: Cannot get key 'Ec' from target dict.").extract()?;
1866-
let Eb2: f64 = target.get_item("Eb")?.expect("Error: Cannot get key 'Eb' from target dict.").extract()?;
1867-
let n2: f64 = target.get_item("n")?.expect("Error: Cannot get key 'n' from target dict.").extract()?;
1928+
let Z2: f64 = get_value_from_dict!(target, "Z")?;
1929+
let m2: f64 = get_value_from_dict!(target, "m")?;
1930+
let Es2: f64 = get_value_from_dict!(target, "Es")?;
1931+
let Ec2: f64 = get_value_from_dict!(target, "Ec")?;
1932+
let Eb2: f64 = get_value_from_dict!(target, "Eb")?;
1933+
let n2: f64 = get_value_from_dict!(target, "n")?;
18681934

18691935
let options = Options::default_options(false);
18701936

@@ -1961,48 +2027,24 @@ fn get_seed() -> Result<u64> {
19612027
/// R_E (f64): energy reflection coefficient (sum of reflected particle energies / total incident energy)
19622028
pub fn compound_reflection_coefficient<'py>(ion: &Bound<'py, PyDict>, targets: Vec<Bound<'py, PyDict>>, target_number_densities: Vec<f64>, energy: f64, angle: f64, num_samples: usize) -> PyResult<(f64, f64)> {
19632029

1964-
assert!(angle.abs() <= 90.0, "Incident angle w.r.t. surface normal, {}, cannot exceed 90 degrees.", angle);
2030+
if angle.abs() > 90.0 {
2031+
return Err(PyValueError::new_err(
2032+
format!(
2033+
"Incident angle cannot exceed 90 degrees; {} given.", angle
2034+
)
2035+
))
2036+
}
19652037

1966-
let Z1: f64 = ion.get_item("Z")?.expect("Error: Cannot get key 'Z' from ion dict.").extract()?;
1967-
let m1: f64 = ion.get_item("m")?.expect("Error: Cannot get key 'm1' from ion dict.").extract()?;
1968-
let Es1: f64 = ion.get_item("Es")?.expect("Error: Cannot get key 'Es' from ion dict.").extract()?;
1969-
let Ec1: f64 = ion.get_item("Ec")?.expect("Error: Cannot get key 'Ec' from ion dict.").extract()?;
2038+
let Z1: f64 = get_value_from_dict!(ion, "Z")?;
2039+
let m1: f64 = get_value_from_dict!(ion, "m")?;
2040+
let Es1: f64 = get_value_from_dict!(ion, "Es")?;
2041+
let Ec1: f64 = get_value_from_dict!(ion, "Ec")?;
19702042

1971-
let Z2: Vec<f64> = targets.iter()
1972-
.enumerate()
1973-
.map(|(index, target)| target.get_item("Z").unwrap()
1974-
.unwrap_or_else(|| panic!(
1975-
"Error: cannot get key 'Z' from target dict at index {}.", index
1976-
))
1977-
.extract().unwrap()).collect::<Vec<f64>>();
1978-
let m2: Vec<f64> = targets.iter()
1979-
.enumerate()
1980-
.map(|(index, target)| target.get_item("m").unwrap()
1981-
.unwrap_or_else(|| panic!(
1982-
"Error: cannot get key 'm' from target dict at index {}.", index
1983-
))
1984-
.extract().unwrap()).collect::<Vec<f64>>();
1985-
let Es2: Vec<f64> = targets.iter()
1986-
.enumerate()
1987-
.map(|(index, target)| target.get_item("Es").unwrap()
1988-
.unwrap_or_else(|| panic!(
1989-
"Error: cannot get key 'Es' from target dict at index {}.", index
1990-
))
1991-
.extract().unwrap()).collect::<Vec<f64>>();
1992-
let Ec2: Vec<f64> = targets.iter()
1993-
.enumerate()
1994-
.map(|(index, target)| target.get_item("Ec").unwrap()
1995-
.unwrap_or_else(|| panic!(
1996-
"Error: cannot get key 'Ec' from target dict at index {}.", index
1997-
))
1998-
.extract().unwrap()).collect::<Vec<f64>>();
1999-
let Eb2: Vec<f64> = targets.iter()
2000-
.enumerate()
2001-
.map(|(index, target)| target.get_item("Eb").unwrap()
2002-
.unwrap_or_else(|| panic!(
2003-
"Error: cannot get key 'Eb' from target dict at index {}.", index
2004-
))
2005-
.extract().unwrap()).collect::<Vec<f64>>();
2043+
let Z2: Vec<f64> = get_vec_from_dicts!(targets, "Z")?;
2044+
let m2: Vec<f64> = get_vec_from_dicts!(targets, "m")?;
2045+
let Es2: Vec<f64> = get_vec_from_dicts!(targets, "Es")?;
2046+
let Ec2: Vec<f64> = get_vec_from_dicts!(targets, "Ec")?;
2047+
let Eb2: Vec<f64> = get_vec_from_dicts!(targets, "Eb")?;
20062048

20072049
let number_target_species = Z2.len();
20082050

@@ -2125,20 +2167,6 @@ fn scattering_integrals(Za: f64, Zb: f64, Ma: f64, Mb: f64, E0: f64, p: f64, n_g
21252167

21262168
Ok((theta_gm, theta_gl, theta_mw, theta_magic))
21272169
}
2128-
#[cfg(feature = "python")]
2129-
macro_rules! geometry_typed_loops {
2130-
($geometry_type:ty, $input:expr, $python:expr) => {
2131-
{
2132-
let input: <$geometry_type as geometry::Geometry>::InputFileFormat = depythonize(&$input).unwrap();
2133-
let (particle_input_array, material, options, output_units) = input::process_input_file(input);
2134-
let pool = rayon::ThreadPoolBuilder::new().num_threads(options.num_threads).build().unwrap();
2135-
pool.install( ||
2136-
physics::physics_loop::<$geometry_type>(particle_input_array, material, options, output_units)
2137-
);
2138-
Ok(())
2139-
}
2140-
}
2141-
}
21422170

21432171
#[cfg(feature = "python")]
21442172
#[pyfunction]
@@ -2158,42 +2186,6 @@ fn rustbca_py<'py>(python: Python<'py>, input: &Bound<'py, PyDict>, geometry_mod
21582186
}
21592187
}
21602188

2161-
/*
2162-
Notes on macros - this is the first I have written, so I'm taking notes here as I go.
2163-
macro_rules! makes a macro - here, the macro is called geometry_types_silent_loops
2164-
macros pattern match an argument and replace it with anything you want
2165-
I want it to take a tuple of a string (e.g., "1D") and a type (e.g., Mesh1D)
2166-
and plop those into corresponding match arms.
2167-
The first line tells the macro to expect an argument with that pattern.
2168-
arguments are $<name>:<designator>. Designators:
2169-
block
2170-
expr is used for expressions
2171-
ident is used for variable/function names
2172-
item
2173-
literal is used for literal constants
2174-
pat (pattern)
2175-
path
2176-
stmt (statement)
2177-
tt (token tree)
2178-
ty (type)
2179-
vis (visibility qualifier)
2180-
*/
2181-
#[cfg(feature = "python")]
2182-
macro_rules! geometry_typed_silent_loops {
2183-
($geometry_type:ty, $input:expr, $python:expr) => {
2184-
{
2185-
let input: <$geometry_type as geometry::Geometry>::InputFileFormat = depythonize(&$input).unwrap();
2186-
let (particle_input_array, material, options, output_units) = input::process_input_file(input);
2187-
let pool = rayon::ThreadPoolBuilder::new().num_threads(options.num_threads).build().unwrap();
2188-
let finished_particles = pool.install( ||
2189-
physics::silent_physics_loop::<$geometry_type>(particle_input_array, material, options, output_units.clone())
2190-
);
2191-
let finished_particles_container = physics::process_finished_particles_to_arrays(finished_particles, output_units);
2192-
Ok(pythonize($python, &finished_particles_container)?)
2193-
}
2194-
}
2195-
}
2196-
21972189
#[cfg(feature = "python")]
21982190
#[pyfunction]
21992191
#[pyo3(signature=(input, geometry_mode="1D"))]

0 commit comments

Comments
 (0)