@@ -80,6 +80,7 @@ pub use crate::parry::{ParryBall, ParryBallInput, InputParryBall, ParryTriMesh,
8080pub fn pybca ( py : Python , m : & PyModule ) -> PyResult < ( ) > {
8181 m. add_function ( wrap_pyfunction ! ( simple_bca_py, m) ?) ?;
8282 m. add_function ( wrap_pyfunction ! ( simple_bca_list_py, m) ?) ?;
83+ m. add_function ( wrap_pyfunction ! ( compound_bca_list_py, m) ?) ?;
8384 Ok ( ( ) )
8485}
8586
@@ -980,6 +981,198 @@ pub extern "C" fn simple_bca_c(x: f64, y: f64, z: f64, ux: f64, uy: f64, uz: f64
980981 }
981982}
982983
984+ #[ cfg( feature = "python" ) ]
985+ ///compound_tagged_bca_list_py(ux, uy, uz, energy, Z1, m1, Ec1, Es1, Z2, m2, Ec2, Es2, n2, Eb2)
986+ /// runs a BCA simulation for a list of particles and outputs a list of sputtered, reflected, and implanted particles.
987+ /// Args:
988+ /// energies (list(f64)): initial ion energies in eV.
989+ /// ux (list(f64)): initial ion directions x. ux != 0.0 to avoid gimbal lock
990+ /// uy (list(f64)): initial ion directions y.
991+ /// uz (list(f64)): initial ion directions z.
992+ /// Z1 (list(f64)): initial ion atomic numbers.
993+ /// m1 (list(f64)): initial ion masses in amu.
994+ /// Ec1 (list(f64)): ion cutoff energies in eV. If ion energy < Ec1, it stops in the material.
995+ /// Es1 (list(f64)): ion surface binding energies. Assumed planar.
996+ /// Z2 (list(f64)): target material species atomic numbers.
997+ /// m2 (list(f64)): target material species masses in amu.
998+ /// Ec2 (list(f64)): target material species cutoff energies in eV. If recoil energy < Ec2, it stops in the material.
999+ /// Es2 (list(f64)): target species surface binding energies. Assumed planar.
1000+ /// n2 (list(f64)): target material species atomic number densities in inverse cubic Angstroms.
1001+ /// Eb2 (list(f64)): target material species bulk binding energies in eV.
1002+ /// Returns:
1003+ /// output (NX9 list of f64): each row in the list represents an output particle (implanted,
1004+ /// sputtered, or reflected). Each row consists of:
1005+ /// [Z, m (amu), E (eV), x, y, z, (angstrom), ux, uy, uz]
1006+ /// incident (list(bool)): whether each row of output was an incident ion or originated in the target
1007+ #[ pyfunction]
1008+ pub fn compound_bca_list_py ( energies : Vec < f64 > , ux : Vec < f64 > , uy : Vec < f64 > , uz : Vec < f64 > , Z1 : Vec < f64 > , m1 : Vec < f64 > , Ec1 : Vec < f64 > , Es1 : Vec < f64 > , Z2 : Vec < f64 > , m2 : Vec < f64 > , Ec2 : Vec < f64 > , Es2 : Vec < f64 > , n2 : Vec < f64 > , Eb2 : Vec < f64 > ) -> ( Vec < [ f64 ; 9 ] > , Vec < bool > ) {
1009+ let mut total_output = vec ! [ ] ;
1010+ let mut incident = vec ! [ ] ;
1011+ let num_species_target = Z2 . len ( ) ;
1012+ let num_incident_ions = energies. len ( ) ;
1013+
1014+ assert_eq ! ( ux. len( ) , num_incident_ions, "Input error: list of x-directions is not the same length as list of incident energies." ) ;
1015+ assert_eq ! ( uy. len( ) , num_incident_ions, "Input error: list of y-directions is not the same length as list of incident energies." ) ;
1016+ assert_eq ! ( uz. len( ) , num_incident_ions, "Input error: list of z-directions is not the same length as list of incident energies." ) ;
1017+ assert_eq ! ( Z1 . len( ) , num_incident_ions, "Input error: list of incident atomic numbers is not the same length as list of incident energies." ) ;
1018+ assert_eq ! ( m1. len( ) , num_incident_ions, "Input error: list of incident atomic masses is not the same length as list of incident energies." ) ;
1019+ assert_eq ! ( Es1 . len( ) , num_incident_ions, "Input error: list of incident surface binding energies is not the same length as list of incident energies." ) ;
1020+ assert_eq ! ( Ec1 . len( ) , num_incident_ions, "Input error: list of incident cutoff energies is not the same length as list of incident energies." ) ;
1021+
1022+ assert_eq ! ( m2. len( ) , num_species_target, "Input error: list of target atomic masses is not the same length as atomic numbers." ) ;
1023+ assert_eq ! ( Ec2 . len( ) , num_species_target, "Input error: list of target cutoff energies is not the same length as atomic numbers." ) ;
1024+ assert_eq ! ( Es2 . len( ) , num_species_target, "Input error: list of target surface binding energies is not the same length as atomic numbers." ) ;
1025+ assert_eq ! ( Eb2 . len( ) , num_species_target, "Input error: list of target bulk binding energies is not the same length as atomic numbers." ) ;
1026+ assert_eq ! ( n2. len( ) , num_species_target, "Input error: list of target number densities is not the same length as atomic numbers." ) ;
1027+
1028+ #[ cfg( feature = "distributions" ) ]
1029+ let options = Options {
1030+ name : "test" . to_string ( ) ,
1031+ track_trajectories : false ,
1032+ track_recoils : true ,
1033+ track_recoil_trajectories : false ,
1034+ write_buffer_size : 8000 ,
1035+ weak_collision_order : 3 ,
1036+ suppress_deep_recoils : true ,
1037+ high_energy_free_flight_paths : true ,
1038+ electronic_stopping_mode : ElectronicStoppingMode :: LOW_ENERGY_NONLOCAL ,
1039+ mean_free_path_model : MeanFreePathModel :: LIQUID ,
1040+ interaction_potential : vec ! [ vec![ InteractionPotential :: KR_C ] ] ,
1041+ scattering_integral : vec ! [ vec![ ScatteringIntegral :: MENDENHALL_WELLER ] ] ,
1042+ num_threads : 1 ,
1043+ num_chunks : 1 ,
1044+ use_hdf5 : false ,
1045+ root_finder : vec ! [ vec![ Rootfinder :: DEFAULTNEWTON ] ] ,
1046+ track_displacements : false ,
1047+ track_energy_losses : false ,
1048+ energy_min : 0.0 ,
1049+ energy_max : 10.0 ,
1050+ energy_num : 11 ,
1051+ angle_min : 0.0 ,
1052+ angle_max : 90.0 ,
1053+ angle_num : 11 ,
1054+ x_min : 0.0 ,
1055+ y_min : -10.0 ,
1056+ z_min : -10.0 ,
1057+ x_max : 10.0 ,
1058+ y_max : 10.0 ,
1059+ z_max : 10.0 ,
1060+ x_num : 11 ,
1061+ y_num : 11 ,
1062+ z_num : 11 ,
1063+ } ;
1064+
1065+ #[ cfg( not( feature = "distributions" ) ) ]
1066+ let options = Options {
1067+ name : "test" . to_string ( ) ,
1068+ track_trajectories : false ,
1069+ track_recoils : true ,
1070+ track_recoil_trajectories : false ,
1071+ write_buffer_size : 8000 ,
1072+ weak_collision_order : 3 ,
1073+ suppress_deep_recoils : true ,
1074+ high_energy_free_flight_paths : false ,
1075+ electronic_stopping_mode : ElectronicStoppingMode :: LOW_ENERGY_NONLOCAL ,
1076+ mean_free_path_model : MeanFreePathModel :: LIQUID ,
1077+ interaction_potential : vec ! [ vec![ InteractionPotential :: KR_C ] ] ,
1078+ scattering_integral : vec ! [ vec![ ScatteringIntegral :: MENDENHALL_WELLER ] ] ,
1079+ num_threads : 1 ,
1080+ num_chunks : 1 ,
1081+ use_hdf5 : false ,
1082+ root_finder : vec ! [ vec![ Rootfinder :: DEFAULTNEWTON ] ] ,
1083+ track_displacements : false ,
1084+ track_energy_losses : false ,
1085+ } ;
1086+
1087+ let x = -2. * ( n2. iter ( ) . sum :: < f64 > ( ) * 10E30 ) . powf ( -1. /3. ) ;
1088+ let y = 0.0 ;
1089+ let z = 0.0 ;
1090+
1091+ let material_parameters = material:: MaterialParameters {
1092+ energy_unit : "EV" . to_string ( ) ,
1093+ mass_unit : "AMU" . to_string ( ) ,
1094+ Eb : Eb2 ,
1095+ Es : Es2 ,
1096+ Ec : Ec2 ,
1097+ Z : Z2 ,
1098+ m : m2,
1099+ interaction_index : vec ! [ 0 ; num_species_target] ,
1100+ surface_binding_model : SurfaceBindingModel :: INDIVIDUAL ,
1101+ bulk_binding_model : BulkBindingModel :: INDIVIDUAL ,
1102+ } ;
1103+
1104+ let geometry_input = geometry:: Mesh0DInput {
1105+ length_unit : "ANGSTROM" . to_string ( ) ,
1106+ densities : n2,
1107+ electronic_stopping_correction_factor : 1.0
1108+ } ;
1109+
1110+ let m = material:: Material :: < Mesh0D > :: new ( & material_parameters, & geometry_input) ;
1111+
1112+ let mut index: usize = 0 ;
1113+ for ( ( ( ( ( ( ( E1_ , ux_) , uy_) , uz_) , Z1_ ) , Ec1_ ) , Es1_ ) , m1_) in energies. iter ( ) . zip ( ux) . zip ( uy) . zip ( uz) . zip ( Z1 ) . zip ( Ec1 ) . zip ( Es1 ) . zip ( m1) {
1114+
1115+ let mut energy_out;
1116+ let p = particle:: Particle {
1117+ m : m1_* AMU ,
1118+ Z : Z1_ ,
1119+ E : E1_ * EV ,
1120+ Ec : Ec1_ * EV ,
1121+ Es : Es1_ * EV ,
1122+ pos : Vector :: new ( x, y, z) ,
1123+ dir : Vector :: new ( ux_, uy_, uz_) ,
1124+ pos_origin : Vector :: new ( x, y, z) ,
1125+ pos_old : Vector :: new ( x, y, z) ,
1126+ dir_old : Vector :: new ( ux_, uy_, uz_) ,
1127+ energy_origin : E1_ * EV ,
1128+ asymptotic_deflection : 0.0 ,
1129+ stopped : false ,
1130+ left : false ,
1131+ incident : true ,
1132+ first_step : true ,
1133+ trajectory : vec ! [ ] ,
1134+ energies : vec ! [ ] ,
1135+ track_trajectories : false ,
1136+ number_collision_events : 0 ,
1137+ backreflected : false ,
1138+ interaction_index : 0 ,
1139+ weight : 0.0 ,
1140+ tag : 0 ,
1141+ tracked_vector : Vector :: new ( 0. , 0. , 0. ) ,
1142+ } ;
1143+
1144+ let output = bca:: single_ion_bca ( p, & m, & options) ;
1145+
1146+ for particle in output {
1147+ if ( particle. left ) | ( particle. incident ) {
1148+
1149+ incident. push ( particle. incident ) ;
1150+
1151+ if particle. stopped {
1152+ energy_out = 0.
1153+ } else {
1154+ energy_out = particle. E /EV
1155+ }
1156+ total_output. push (
1157+ [
1158+ particle. Z ,
1159+ particle. m /AMU ,
1160+ energy_out,
1161+ particle. pos . x ,
1162+ particle. pos . y ,
1163+ particle. pos . z ,
1164+ particle. dir . x ,
1165+ particle. dir . y ,
1166+ particle. dir . z ,
1167+ ]
1168+ ) ;
1169+ }
1170+ }
1171+ index += 1 ;
1172+ }
1173+ ( total_output, incident)
1174+ }
1175+
9831176#[ cfg( feature = "python" ) ]
9841177/// simple_bca_py( x, y, z, ux, uy, uz, energy, Z1, m1, Ec1, Es1, Z2, m2, Ec2, Es2, n2, Eb2)
9851178/// --
0 commit comments