@@ -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 ) ]
151205pub 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)
17621816pub 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)
18531913pub 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)
19622028pub 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