Skip to content

Commit 7a7061a

Browse files
authored
Merge pull request #188 from lcpp-org/compound_bca_list_py
Python compound bca; minor refactors
2 parents 92a2de3 + 6632e8b commit 7a7061a

File tree

9 files changed

+267
-87
lines changed

9 files changed

+267
-87
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
name: Benchmark request
3+
about: Suggest a benchmark for validation
4+
title: "[benchmark]"
5+
labels: benchmark
6+
assignees: ''
7+
8+
---
9+
10+
**Description**
11+
A clear and concise description of the requested benchmark.
12+
13+
**Source**
14+
Source of data for benchmark; a paper, code, or database with sputtering yields, reflection coefficients, etc.

src/bca.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,6 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
7575

7676
//Remove particle from top of vector as particle_1
7777
let mut particle_1 = particles.pop().unwrap();
78-
79-
//println!("Particle start Z: {} E: {} ({}, {}, {})", particle_1.Z, particle_1.E/Q, particle_1.pos.x/ANGSTROM, particle_1.pos.y/ANGSTROM, particle_1.pos.z/ANGSTROM);
80-
8178
//BCA loop
8279
while !particle_1.stopped & !particle_1.left {
8380

@@ -114,8 +111,6 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
114111
particle_1.pos.x, particle_2.pos.x, &binary_collision_geometry))
115112
.unwrap();
116113

117-
//println!("{}", binary_collision_result);
118-
119114
//Only use 0th order collision for local electronic stopping
120115
if k == 0 {
121116
normalized_distance_of_closest_approach = binary_collision_result.normalized_distance_of_closest_approach;
@@ -127,17 +122,14 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
127122
particle_2.E = binary_collision_result.recoil_energy - material.average_bulk_binding_energy(particle_2.pos.x, particle_2.pos.y, particle_2.pos.z);
128123
particle_2.energy_origin = particle_2.E;
129124

130-
//Accumulate asymptotic deflections for primary particle
125+
//Accumulate energy losses and asymptotic deflections for primary particle
131126
total_energy_loss += binary_collision_result.recoil_energy;
132-
133-
//total_deflection_angle += psi;
134127
total_asymptotic_deflection += binary_collision_result.asymptotic_deflection;
135128

136-
//Rotate particle 1, 2 by lab frame scattering angles
137-
particle::rotate_particle(&mut particle_1, binary_collision_result.psi,
129+
particle_1.rotate(binary_collision_result.psi,
138130
binary_collision_geometry.phi_azimuthal);
139131

140-
particle::rotate_particle(&mut particle_2, -binary_collision_result.psi_recoil,
132+
particle_2.rotate(-binary_collision_result.psi_recoil,
141133
binary_collision_geometry.phi_azimuthal);
142134

143135
particle_2.dir_old.x = particle_2.dir.x;
@@ -184,28 +176,24 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
184176

185177
//Advance particle in space and track total distance traveled
186178
#[cfg(not(feature = "accelerated_ions"))]
187-
let distance_traveled = particle::particle_advance(&mut particle_1,
179+
let distance_traveled = particle_1.advance(
188180
binary_collision_geometries[0].mfp, total_asymptotic_deflection);
189181

190182
#[cfg(feature = "accelerated_ions")]
191-
let distance_traveled = particle::particle_advance(&mut particle_1,
183+
let distance_traveled = particle_1.advance(
192184
binary_collision_geometries[0].mfp + distance_to_target - material.geometry.get_energy_barrier_thickness(), total_asymptotic_deflection);
193185

194186
//Subtract total energy from all simultaneous collisions and electronic stopping
195187
bca::update_particle_energy(&mut particle_1, &material, distance_traveled,
196188
total_energy_loss, normalized_distance_of_closest_approach, strong_collision_Z,
197189
strong_collision_index, &options);
198-
//println!("Particle finished collision loop Z: {} E: {} ({}, {}, {})", particle_1.Z, particle_1.E/Q, particle_1.pos.x/ANGSTROM, particle_1.pos.y/ANGSTROM, particle_1.pos.z/ANGSTROM);
199-
200-
//println!("{} {} {}", energy_0/EV, energy_1/EV, (energy_1 - energy_0)/EV);
201190

202191
//Check boundary conditions on leaving and stopping
203192
material::boundary_condition_planar(&mut particle_1, &material);
204193

205194
//Set particle index to topmost particle
206195
particle_index = particles.len();
207196
}
208-
//println!("Particle stopped or left Z: {} E: {} ({}, {}, {})", particle_1.Z, particle_1.E/Q, particle_1.pos.x/ANGSTROM, particle_1.pos.y/ANGSTROM, particle_1.pos.z/ANGSTROM);
209197
particle_output.push(particle_1);
210198
}
211199
particle_output

src/geometry.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ impl Geometry for Mesh0D {
9797
}
9898

9999
fn inside_simulation_boundary(&self, x: f64, y: f64, z: f64) -> bool {
100-
//println!("x: {} energy_barrier_thickness: {}", x/ANGSTROM, self.energy_barrier_thickness/ANGSTROM);
101100
x > -10.*self.energy_barrier_thickness
102101
}
103102

src/interactions.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub fn crossing_point_doca(interaction_potential: InteractionPotential) -> f64 {
88
InteractionPotential::MORSE{D, alpha, r0} => (alpha*r0 - (2.0_f64).ln())/alpha,
99
InteractionPotential::WW => 50.*ANGSTROM,
1010
_ => 10.*ANGSTROM,
11-
//_ => panic!("Input error: potential never crosses zero for r > 0. Consider using the Newton rootfinder.")
1211
}
1312

1413
}

src/lib.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub use crate::parry::{ParryBall, ParryBallInput, InputParryBall, ParryTriMesh,
8080
pub 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
/// --

src/material.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,6 @@ pub fn surface_binding_energy<T: Geometry>(particle_1: &mut particle::Particle,
323323

324324
//Actual surface binding energies
325325
let Es = material.actual_surface_binding_energy(particle_1, x_old, y_old, z_old);
326-
//println!("Actual Es: {}", Es);
327326
let Ec = particle_1.Ec;
328327

329328
let inside_now = material.inside_energy_barrier(x, y, z);
@@ -380,7 +379,6 @@ pub fn surface_binding_energy<T: Geometry>(particle_1: &mut particle::Particle,
380379
particle_1.dir.x = -2.*(costheta)*dx/mag + cosx;
381380
particle_1.dir.y = -2.*(costheta)*dy/mag + cosy;
382381
particle_1.dir.z = -2.*(costheta)*dz/mag + cosz;
383-
384382
particle_1.backreflected = true;
385383
}
386384
}

0 commit comments

Comments
 (0)