@@ -9,10 +9,10 @@ use std::alloc::{dealloc, Layout};
99use std:: mem:: align_of;
1010
1111//Parallelization - currently only used in python library functions
12+ //#[cfg(feature = "python")]
13+ //use rayon::ThreadPoolBuilder;
1214#[ cfg( feature = "python" ) ]
13- use rayon:: prelude:: * ;
14- #[ cfg( feature = "python" ) ]
15- use rayon:: * ;
15+ use rayon:: iter:: { IndexedParallelIterator , ParallelExtend , IntoParallelIterator , ParallelIterator } ;
1616
1717//Error handling crate
1818use anyhow:: { Result , Context , anyhow} ;
@@ -53,6 +53,10 @@ use std::f64::consts::SQRT_2;
5353use pyo3:: prelude:: * ;
5454#[ cfg( feature = "python" ) ]
5555use pyo3:: types:: * ;
56+ #[ cfg( feature = "python" ) ]
57+ use pythonize:: * ;
58+ #[ cfg( feature = "python" ) ]
59+ use pyo3:: exceptions:: { PyValueError , PyRuntimeError } ;
5660
5761//Load internal modules
5862pub mod material;
@@ -68,9 +72,7 @@ pub mod consts;
6872pub mod structs;
6973pub mod sphere;
7074pub mod math;
71-
72- #[ cfg( feature = "parry3d" ) ]
73- pub mod parry;
75+ pub mod physics;
7476
7577pub use crate :: enums:: * ;
7678pub use crate :: consts:: * ;
@@ -81,6 +83,10 @@ pub use crate::geometry::{Geometry, GeometryElement, Mesh0D, Mesh1D, Mesh2D};
8183pub use crate :: sphere:: { Sphere , SphereInput , InputSphere } ;
8284pub use crate :: math:: * ;
8385pub use crate :: material:: * ;
86+ pub use crate :: physics:: * ;
87+
88+ #[ cfg( feature = "parry3d" ) ]
89+ pub mod parry;
8490
8591#[ cfg( feature = "parry3d" ) ]
8692pub use crate :: parry:: { ParryBall , ParryBallInput , InputParryBall , ParryTriMesh , ParryTriMeshInput , InputParryTriMesh } ;
@@ -139,6 +145,12 @@ mod libRustBCA {
139145
140146 #[ pymodule_export]
141147 use super :: scattering_integrals;
148+
149+ #[ pymodule_export]
150+ use super :: rustbca_py;
151+
152+ #[ pymodule_export]
153+ use super :: rustbca_local_py;
142154}
143155
144156#[ derive( Debug ) ]
@@ -2131,7 +2143,6 @@ pub fn compound_reflection_coefficient<'py>(ion: &Bound<'py, PyDict>, targets: V
21312143
21322144 let mut residue = residue. lock ( ) . unwrap ( ) ;
21332145 * residue = * residue + residue_part;
2134-
21352146 }
21362147 }
21372148 } ) ;
@@ -2161,20 +2172,118 @@ fn moller_knuth_two_sum(a: f64, b: f64) -> (f64, f64) {
21612172 let r = delta_a + delta_b;
21622173 ( s, r)
21632174}
2175+
21642176#[ cfg( feature = "python" ) ]
21652177#[ pyfunction]
2166- #[ pyo3( signature = ( Za , Zb , Ma , Mb , E0 , p, n_gl_points=100 ) ) ]
2167- fn scattering_integrals ( Za : f64 , Zb : f64 , Ma : f64 , Mb : f64 , E0 : f64 , p : f64 , n_gl_points : usize ) -> ( f64 , f64 , f64 , f64 ) {
2178+ #[ pyo3( signature = ( Za , Zb , Ma , Mb , E0 , p, n_gl_points=100 , interaction_potential= "KR_C" ) ) ]
2179+ fn scattering_integrals ( Za : f64 , Zb : f64 , Ma : f64 , Mb : f64 , E0 : f64 , p : f64 , n_gl_points : usize , interaction_potential : & str ) -> PyResult < ( f64 , f64 , f64 , f64 ) > {
21682180 let E0 = E0 * EV ;
21692181 let p = p* ANGSTROM ;
21702182
2171- let x0_newton = bca:: newton_rootfinder ( Za , Zb , Ma , Mb , E0 , p, InteractionPotential :: KR_C , 1000 , 1E-12 ) . unwrap ( ) ;
2183+ let potential = match interaction_potential {
2184+ "KR_C" => InteractionPotential :: KR_C ,
2185+ "LENZ_JENSEN" => InteractionPotential :: LENZ_JENSEN ,
2186+ "MOLIERE" => InteractionPotential :: MOLIERE ,
2187+ "ZBL" => InteractionPotential :: ZBL ,
2188+ _ => return Err ( PyValueError :: new_err ( format ! ( "Unimplemented interaction potential {}; try 'KR_C'" , interaction_potential) ) )
2189+ } ;
2190+
2191+ let x0_newton = bca:: newton_rootfinder ( Za , Zb , Ma , Mb , E0 , p, potential, 1000 , 1E-12 ) . map_err (
2192+ |error| PyRuntimeError :: new_err ( format ! ( "Rootfinder failed to find distance of closest approach; check input values." ) )
2193+ ) ?;
21722194
21732195 //Compute center of mass deflection angle with each algorithm
2174- let theta_gm = bca:: gauss_mehler ( Za , Zb , Ma , Mb , E0 , p, x0_newton, InteractionPotential :: KR_C , n_gl_points) ;
2175- let theta_gl = bca:: gauss_legendre ( Za , Zb , Ma , Mb , E0 , p, x0_newton, InteractionPotential :: KR_C ) ;
2176- let theta_mw = bca:: mendenhall_weller ( Za , Zb , Ma , Mb , E0 , p, x0_newton, InteractionPotential :: KR_C ) ;
2177- let theta_magic = bca:: magic ( Za , Zb , Ma , Mb , E0 , p, x0_newton, InteractionPotential :: KR_C ) ;
2196+ let theta_gm = bca:: gauss_mehler ( Za , Zb , Ma , Mb , E0 , p, x0_newton, potential, n_gl_points) ;
2197+ let theta_gl = bca:: gauss_legendre ( Za , Zb , Ma , Mb , E0 , p, x0_newton, potential) ;
2198+ let theta_mw = bca:: mendenhall_weller ( Za , Zb , Ma , Mb , E0 , p, x0_newton, potential) ;
2199+ let theta_magic = bca:: magic ( Za , Zb , Ma , Mb , E0 , p, x0_newton, potential) ;
2200+
2201+ Ok ( ( theta_gm, theta_gl, theta_mw, theta_magic) )
2202+ }
2203+ #[ cfg( feature = "python" ) ]
2204+ macro_rules! geometry_typed_loops {
2205+ ( $geometry_type: ty, $input: expr, $python: expr) => {
2206+ {
2207+ let input: <$geometry_type as geometry:: Geometry >:: InputFileFormat = depythonize( & $input) . unwrap( ) ;
2208+ let ( particle_input_array, material, options, output_units) = input:: process_input_file( input) ;
2209+ let pool = rayon:: ThreadPoolBuilder :: new( ) . num_threads( options. num_threads) . build( ) . unwrap( ) ;
2210+ pool. install( ||
2211+ physics:: physics_loop:: <$geometry_type>( particle_input_array, material, options, output_units)
2212+ ) ;
2213+ Ok ( ( ) )
2214+ }
2215+ }
2216+ }
2217+
2218+ #[ cfg( feature = "python" ) ]
2219+ #[ pyfunction]
2220+ #[ pyo3( signature=( input, geometry_mode="1D" ) ) ]
2221+ fn rustbca_py < ' py > ( python : Python < ' py > , input : & Bound < ' py , PyDict > , geometry_mode : & str ) -> PyResult < ( ) > {
2222+ match geometry_mode {
2223+ "0D" => geometry_typed_loops ! ( Mesh0D , input, python) ,
2224+ "1D" => geometry_typed_loops ! ( Mesh1D , input, python) ,
2225+ "2D" => geometry_typed_loops ! ( Mesh2D , input, python) ,
2226+ "HOMOGENEOUS2D" => geometry_typed_loops ! ( Mesh2D , input, python) ,
2227+ "SPHERE" => geometry_typed_loops ! ( Sphere , input, python) ,
2228+ #[ cfg( feature="parry3d" ) ]
2229+ "BALL" => geometry_typed_loops ! ( ParryBall , input, python) ,
2230+ #[ cfg( feature="parry3d" ) ]
2231+ "TRIMESH" => geometry_typed_loops ! ( ParryTriMesh , input, python) ,
2232+ _ => Err ( PyValueError :: new_err ( format ! ( "Input Error: Unimplemented geometry mode {}; try '1D'" , geometry_mode) ) )
2233+ }
2234+ }
21782235
2179- ( theta_gm, theta_gl, theta_mw, theta_magic)
2236+ /*
2237+ Notes on macros - this is the first I have written, so I'm taking notes here as I go.
2238+ macro_rules! makes a macro - here, the macro is called geometry_types_silent_loops
2239+ macros pattern match an argument and replace it with anything you want
2240+ I want it to take a tuple of a string (e.g., "1D") and a type (e.g., Mesh1D)
2241+ and plop those into corresponding match arms.
2242+ The first line tells the macro to expect an argument with that pattern.
2243+ arguments are $<name>:<designator>. Designators:
2244+ block
2245+ expr is used for expressions
2246+ ident is used for variable/function names
2247+ item
2248+ literal is used for literal constants
2249+ pat (pattern)
2250+ path
2251+ stmt (statement)
2252+ tt (token tree)
2253+ ty (type)
2254+ vis (visibility qualifier)
2255+ */
2256+ #[ cfg( feature = "python" ) ]
2257+ macro_rules! geometry_typed_silent_loops {
2258+ ( $geometry_type: ty, $input: expr, $python: expr) => {
2259+ {
2260+ let input: <$geometry_type as geometry:: Geometry >:: InputFileFormat = depythonize( & $input) . unwrap( ) ;
2261+ let ( particle_input_array, material, options, output_units) = input:: process_input_file( input) ;
2262+ let pool = rayon:: ThreadPoolBuilder :: new( ) . num_threads( options. num_threads) . build( ) . unwrap( ) ;
2263+ let finished_particles = pool. install( ||
2264+ physics:: silent_physics_loop:: <$geometry_type>( particle_input_array, material, options, output_units. clone( ) )
2265+ ) ;
2266+ let finished_particles_container = physics:: process_finished_particles_to_arrays( finished_particles, output_units) ;
2267+ Ok ( pythonize( $python, & finished_particles_container) ?)
2268+ }
2269+ }
2270+ }
2271+
2272+ #[ cfg( feature = "python" ) ]
2273+ #[ pyfunction]
2274+ #[ pyo3( signature=( input, geometry_mode="1D" ) ) ]
2275+ fn rustbca_local_py < ' py > ( python : Python < ' py > , input : & Bound < ' py , PyDict > , geometry_mode : & str ) -> PyResult < Bound < ' py , PyAny > > {
2276+
2277+ match geometry_mode {
2278+ "0D" => geometry_typed_silent_loops ! ( Mesh0D , input, python) ,
2279+ "1D" => geometry_typed_silent_loops ! ( Mesh1D , input, python) ,
2280+ "2D" => geometry_typed_silent_loops ! ( Mesh2D , input, python) ,
2281+ "HOMOGENEOUS2D" => geometry_typed_silent_loops ! ( Mesh2D , input, python) ,
2282+ "SPHERE" => geometry_typed_silent_loops ! ( Sphere , input, python) ,
2283+ #[ cfg( feature="parry3d" ) ]
2284+ "BALL" => geometry_typed_silent_loops ! ( ParryBall , input, python) ,
2285+ #[ cfg( feature="parry3d" ) ]
2286+ "TRIMESH" => geometry_typed_silent_loops ! ( ParryTriMesh , input, python) ,
2287+ _ => Err ( PyValueError :: new_err ( format ! ( "Input Error: Unimplemented geometry mode {}; try '1D'" , geometry_mode) ) )
2288+ }
21802289}
0 commit comments