@@ -80,6 +80,7 @@ pub use crate::parry::{ParryBall, ParryBallInput, InputParryBall, ParryTriMesh,
80
80
pub fn pybca ( py : Python , m : & PyModule ) -> PyResult < ( ) > {
81
81
m. add_function ( wrap_pyfunction ! ( simple_bca_py, m) ?) ?;
82
82
m. add_function ( wrap_pyfunction ! ( simple_bca_list_py, m) ?) ?;
83
+ m. add_function ( wrap_pyfunction ! ( compound_bca_list_py, m) ?) ?;
83
84
Ok ( ( ) )
84
85
}
85
86
@@ -980,6 +981,198 @@ pub extern "C" fn simple_bca_c(x: f64, y: f64, z: f64, ux: f64, uy: f64, uz: f64
980
981
}
981
982
}
982
983
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
+
983
1176
#[ cfg( feature = "python" ) ]
984
1177
/// simple_bca_py( x, y, z, ux, uy, uz, energy, Z1, m1, Ec1, Es1, Z2, m2, Ec2, Es2, n2, Eb2)
985
1178
/// --
0 commit comments