Skip to content

Commit d2ee60f

Browse files
committed
Added layered target version of Python functions.
1 parent c28796c commit d2ee60f

File tree

7 files changed

+282
-0
lines changed

7 files changed

+282
-0
lines changed

examples/test_rustbca.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,46 @@ def main():
8181
print(f'RustBCA R: {len(reflected[:, 0])/number_ions} Thomas R: {thomas}')
8282
print(f'Time per ion: {delta_time/number_ions*1e3} us/{ion["symbol"]}')
8383

84+
#layered target version
85+
86+
#1 keV is above the He on W sputtering threshold of ~150 eV
87+
energies_eV = 1000.0*np.ones(number_ions)
88+
89+
#Working with angles of exactly 0 is problematic due to gimbal lock
90+
angle = 0.0001
91+
92+
#In RustBCA's 0D geometry, +x -> into the surface
93+
ux = np.cos(angle*np.pi/180.)*np.ones(number_ions)
94+
uy = np.sin(angle*np.pi/180.)*np.ones(number_ions)
95+
uz = np.zeros(number_ions)
96+
97+
print(f'Running RustBCA for {number_ions} {ion["symbol"]} ions on {target["symbol"]} with hydrogenated layer at {energies_eV[0]/1000.} keV...')
98+
print(f'This may take several minutes.')
99+
output, incident, stopped = compound_bca_list_1D_py(
100+
ux, uy, uz, energies_eV, [ion['Z']]*number_ions,
101+
[ion['m']]*number_ions, [ion['Ec']]*number_ions, [ion['Es']]*number_ions, [target['Z'], 1.0], [target['m'], 1.008],
102+
[target['Ec'], 1.0], [target['Es'], 1.5], [target['Eb'], 0.0], [[target['n']/10**30, target['n']/10**30], [target['n']/10**30, 0.0]], [50.0, 1e6]
103+
)
104+
105+
output = np.array(output)
106+
107+
Z = output[:, 0]
108+
m = output[:, 1]
109+
E = output[:, 2]
110+
x = output[:, 3]
111+
y = output[:, 4]
112+
z = output[:, 5]
113+
ux = output[:, 6]
114+
uy = output[:, 7]
115+
uz = output[:, 8]
116+
117+
plt.figure(3)
118+
plt.title(f'Implanted {ion["symbol"]} Depth Distribution with 50A {target["symbol"]}-H layer')
119+
plt.xlabel('x [A]')
120+
plt.ylabel('f(x) [A.U.]')
121+
heights, _, _ = plt.hist(x[np.logical_and(incident, stopped)], bins=100, density=True, histtype='step')
122+
plt.plot([50.0, 50.0], [0.0, np.max(heights)*1.1])
123+
plt.gca().set_ylim([0.0, np.max(heights)*1.1])
84124
plt.show()
85125

86126
if __name__ == '__main__':

src/RustBCA.egg-info/PKG-INFO

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Metadata-Version: 2.1
2+
Name: RustBCA
3+
Version: 1.2.0
4+
License-File: LICENSE

src/RustBCA.egg-info/SOURCES.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Cargo.toml
2+
LICENSE
3+
MANIFEST.in
4+
README.md
5+
pyproject.toml
6+
setup.py
7+
src/bca.rs
8+
src/consts.rs
9+
src/enums.rs
10+
src/geometry.rs
11+
src/gui.rs
12+
src/input.rs
13+
src/interactions.rs
14+
src/lib.rs
15+
src/main.rs
16+
src/material.rs
17+
src/output.rs
18+
src/parry.rs
19+
src/particle.rs
20+
src/physics.rs
21+
src/sphere.rs
22+
src/structs.rs
23+
src/tests.rs
24+
src/RustBCA.egg-info/PKG-INFO
25+
src/RustBCA.egg-info/SOURCES.txt
26+
src/RustBCA.egg-info/dependency_links.txt
27+
src/RustBCA.egg-info/not-zip-safe
28+
src/RustBCA.egg-info/top_level.txt
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/RustBCA.egg-info/not-zip-safe

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/RustBCA.egg-info/top_level.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/lib.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ 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)?)?;
8383
m.add_function(wrap_pyfunction!(compound_bca_list_py, m)?)?;
84+
m.add_function(wrap_pyfunction!(compound_bca_list_1D_py, m)?)?;
8485
Ok(())
8586
}
8687

@@ -1173,6 +1174,212 @@ pub fn compound_bca_list_py(energies: Vec<f64>, ux: Vec<f64>, uy: Vec<f64>, uz:
11731174
(total_output, incident)
11741175
}
11751176

1177+
#[cfg(feature = "python")]
1178+
///compound_bca_list_1D_py(ux, uy, uz, energy, Z1, m1, Ec1, Es1, Z2, m2, Ec2, Es2, Eb2 n2, dx)
1179+
/// runs a BCA simulation for a list of particles and outputs a list of sputtered, reflected, and implanted particles.
1180+
/// Args:
1181+
/// energies (list(f64)): initial ion energies in eV.
1182+
/// ux (list(f64)): initial ion directions x. ux != 0.0 to avoid gimbal lock
1183+
/// uy (list(f64)): initial ion directions y.
1184+
/// uz (list(f64)): initial ion directions z.
1185+
/// Z1 (list(f64)): initial ion atomic numbers.
1186+
/// m1 (list(f64)): initial ion masses in amu.
1187+
/// Ec1 (list(f64)): ion cutoff energies in eV. If ion energy < Ec1, it stops in the material.
1188+
/// Es1 (list(f64)): ion surface binding energies. Assumed planar.
1189+
/// Z2 (list(f64)): target material species atomic numbers.
1190+
/// m2 (list(f64)): target material species masses in amu.
1191+
/// Ec2 (list(f64)): target material species cutoff energies in eV. If recoil energy < Ec2, it stops in the material.
1192+
/// Es2 (list(f64)): target species surface binding energies. Assumed planar.
1193+
/// Eb2 (list(f64)): target material species bulk binding energies in eV.
1194+
/// n2 (list(list(f64))): target material species atomic number densities in inverse cubic Angstroms.
1195+
/// dx (list(f64)): target material layer thicknesses starting at surface.
1196+
/// Returns:
1197+
/// output (NX9 list of f64): each row in the list represents an output particle (implanted,
1198+
/// sputtered, or reflected). Each row consists of:
1199+
/// [Z, m (amu), E (eV), x, y, z, (angstrom), ux, uy, uz]
1200+
/// incident (list(bool)): whether each row of output was an incident ion or originated in the target
1201+
/// stopped (list(bool)): whether each row of output is associated with a particle that stopped in the target
1202+
#[pyfunction]
1203+
pub fn compound_bca_list_1D_py(ux: Vec<f64>, uy: Vec<f64>, uz: Vec<f64>, energies: 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>, Eb2: Vec<f64>, n2: Vec<Vec<f64>>, dx: Vec<f64>) -> (Vec<[f64; 9]>, Vec<bool>, Vec<bool>) {
1204+
let mut total_output = vec![];
1205+
let mut incident = vec![];
1206+
let mut stopped = vec![];
1207+
1208+
let num_layers_target = n2.len();
1209+
let num_species = Z2.len();
1210+
let num_incident_ions = energies.len();
1211+
1212+
assert_eq!(ux.len(), num_incident_ions, "Input error: list of x-directions is not the same length as list of incident energies.");
1213+
assert_eq!(uy.len(), num_incident_ions, "Input error: list of y-directions is not the same length as list of incident energies.");
1214+
assert_eq!(uz.len(), num_incident_ions, "Input error: list of z-directions is not the same length as list of incident energies.");
1215+
assert_eq!(Z1.len(), num_incident_ions, "Input error: list of incident atomic numbers is not the same length as list of incident energies.");
1216+
assert_eq!(m1.len(), num_incident_ions, "Input error: list of incident atomic masses is not the same length as list of incident energies.");
1217+
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.");
1218+
assert_eq!(Ec1.len(), num_incident_ions, "Input error: list of incident cutoff energies is not the same length as list of incident energies.");
1219+
1220+
assert_eq!(m2.len(), num_species, "Input error: list of target atomic masses is not the same length as atomic numbers.");
1221+
assert_eq!(Ec2.len(), num_species, "Input error: list of target cutoff energies is not the same length as atomic numbers.");
1222+
assert_eq!(Es2.len(), num_species, "Input error: list of target surface binding energies is not the same length as atomic numbers.");
1223+
assert_eq!(Eb2.len(), num_species, "Input error: list of target bulk binding energies is not the same length as atomic numbers.");
1224+
assert_eq!(n2[0].len(), num_species, "Input error: first layer list of target number densities is not the same length as atomic numbers.");
1225+
1226+
assert_eq!(n2[0].len(), num_species, "Input error: first layer species list of target number densities is not the same length as atomic numbers.");
1227+
assert_eq!(dx.len(), num_layers_target, "Input error: number of layer thicknesses not the same as number of layers in atomic densities list.");
1228+
1229+
#[cfg(feature = "distributions")]
1230+
let options = Options {
1231+
name: "test".to_string(),
1232+
track_trajectories: false,
1233+
track_recoils: true,
1234+
track_recoil_trajectories: false,
1235+
write_buffer_size: 8000,
1236+
weak_collision_order: 3,
1237+
suppress_deep_recoils: true,
1238+
high_energy_free_flight_paths: true,
1239+
electronic_stopping_mode: ElectronicStoppingMode::LOW_ENERGY_NONLOCAL,
1240+
mean_free_path_model: MeanFreePathModel::LIQUID,
1241+
interaction_potential: vec![vec![InteractionPotential::KR_C]],
1242+
scattering_integral: vec![vec![ScatteringIntegral::MENDENHALL_WELLER]],
1243+
num_threads: 1,
1244+
num_chunks: 1,
1245+
use_hdf5: false,
1246+
root_finder: vec![vec![Rootfinder::DEFAULTNEWTON]],
1247+
track_displacements: false,
1248+
track_energy_losses: false,
1249+
energy_min: 0.0,
1250+
energy_max: 10.0,
1251+
energy_num: 11,
1252+
angle_min: 0.0,
1253+
angle_max: 90.0,
1254+
angle_num: 11,
1255+
x_min: 0.0,
1256+
y_min: -10.0,
1257+
z_min: -10.0,
1258+
x_max: 10.0,
1259+
y_max: 10.0,
1260+
z_max: 10.0,
1261+
x_num: 11,
1262+
y_num: 11,
1263+
z_num: 11,
1264+
};
1265+
1266+
#[cfg(not(feature = "distributions"))]
1267+
let options = Options {
1268+
name: "test".to_string(),
1269+
track_trajectories: false,
1270+
track_recoils: true,
1271+
track_recoil_trajectories: false,
1272+
write_buffer_size: 8000,
1273+
weak_collision_order: 3,
1274+
suppress_deep_recoils: true,
1275+
high_energy_free_flight_paths: false,
1276+
electronic_stopping_mode: ElectronicStoppingMode::LOW_ENERGY_NONLOCAL,
1277+
mean_free_path_model: MeanFreePathModel::LIQUID,
1278+
interaction_potential: vec![vec![InteractionPotential::KR_C]],
1279+
scattering_integral: vec![vec![ScatteringIntegral::MENDENHALL_WELLER]],
1280+
num_threads: 1,
1281+
num_chunks: 1,
1282+
use_hdf5: false,
1283+
root_finder: vec![vec![Rootfinder::DEFAULTNEWTON]],
1284+
track_displacements: false,
1285+
track_energy_losses: false,
1286+
};
1287+
1288+
let y = 0.0;
1289+
let z = 0.0;
1290+
1291+
let material_parameters = material::MaterialParameters {
1292+
energy_unit: "EV".to_string(),
1293+
mass_unit: "AMU".to_string(),
1294+
Eb: Eb2,
1295+
Es: Es2,
1296+
Ec: Ec2,
1297+
Z: Z2,
1298+
m: m2,
1299+
interaction_index: vec![0; num_species],
1300+
surface_binding_model: SurfaceBindingModel::INDIVIDUAL,
1301+
bulk_binding_model: BulkBindingModel::INDIVIDUAL,
1302+
};
1303+
1304+
let geometry_input = geometry::Mesh1DInput {
1305+
length_unit: "ANGSTROM".to_string(),
1306+
densities: n2,
1307+
layer_thicknesses: dx,
1308+
electronic_stopping_correction_factors: vec![1.0; num_layers_target]
1309+
};
1310+
1311+
let m = material::Material::<Mesh1D>::new(&material_parameters, &geometry_input);
1312+
1313+
let x = -m.geometry.top_energy_barrier_thickness/2.;
1314+
1315+
let mut index: usize = 0;
1316+
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) {
1317+
1318+
let mut dir = Vector::new(ux_, uy_, uz_);
1319+
dir.normalize();
1320+
1321+
let mut energy_out;
1322+
let p = particle::Particle {
1323+
m: m1_*AMU,
1324+
Z: Z1_,
1325+
E: E1_*EV,
1326+
Ec: Ec1_*EV,
1327+
Es: Es1_*EV,
1328+
pos: Vector::new(x, y, z),
1329+
dir: dir,
1330+
pos_origin: Vector::new(x, y, z),
1331+
pos_old: Vector::new(x, y, z),
1332+
dir_old: dir,
1333+
energy_origin: E1_*EV,
1334+
asymptotic_deflection: 0.0,
1335+
stopped: false,
1336+
left: false,
1337+
incident: true,
1338+
first_step: true,
1339+
trajectory: vec![],
1340+
energies: vec![],
1341+
track_trajectories: false,
1342+
number_collision_events: 0,
1343+
backreflected: false,
1344+
interaction_index : 0,
1345+
weight: 0.0,
1346+
tag: 0,
1347+
tracked_vector: Vector::new(0., 0., 0.),
1348+
};
1349+
1350+
let output = bca::single_ion_bca(p, &m, &options);
1351+
1352+
for particle in output {
1353+
if (particle.left) | (particle.incident) {
1354+
1355+
incident.push(particle.incident);
1356+
stopped.push(particle.stopped);
1357+
1358+
if particle.stopped {
1359+
energy_out = 0.
1360+
} else {
1361+
energy_out = particle.E/EV
1362+
}
1363+
total_output.push(
1364+
[
1365+
particle.Z,
1366+
particle.m/AMU,
1367+
energy_out,
1368+
particle.pos.x/ANGSTROM,
1369+
particle.pos.y/ANGSTROM,
1370+
particle.pos.z/ANGSTROM,
1371+
particle.dir.x,
1372+
particle.dir.y,
1373+
particle.dir.z,
1374+
]
1375+
);
1376+
}
1377+
}
1378+
index += 1;
1379+
}
1380+
(total_output, incident, stopped)
1381+
}
1382+
11761383
#[cfg(feature = "python")]
11771384
/// simple_bca_py( x, y, z, ux, uy, uz, energy, Z1, m1, Ec1, Es1, Z2, m2, Ec2, Es2, n2, Eb2)
11781385
/// --

0 commit comments

Comments
 (0)