Skip to content

Commit fd2d99a

Browse files
authored
Merge pull request #333 from lcpp-org/rayon_reduce
Remove mutexes from lib.rs
2 parents 5f91a61 + d36b2f2 commit fd2d99a

2 files changed

Lines changed: 87 additions & 121 deletions

File tree

‎README.md‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@ Journal of Open Source Software by clicking the badge below:
2323

2424
[![DOI](https://joss.theoj.org/papers/10.21105/joss.03298/status.svg)](https://doi.org/10.21105/joss.03298)
2525

26-
Selected citations of RustBCA as of 5/24/23:
26+
Selected citations of RustBCA as of 8/10/26:
27+
* [Validation of hybrid-PIC Simulations for Advanced Beam-Driven FRC Modeling](https://iopscience.iop.org/article/10.1088/1741-4326/ae96c0/meta), R. E. Groenewald, et al. (2026)
28+
* [(Preprint): Comparative qualification of advanced plasma-facing materials for fusion pilot plants through public- and private-sector experiments in DIII-D](https://arxiv.org/abs/2607.23400), F. Effenberg, et al. (2026)
29+
* [Understanding carbon sourcing and transport originating from the helicon antenna surfaces during high-power helicon discharge in DIII-D Tokamak](https://iopscience.iop.org/article/10.1088/1741-4326/ae9322/meta), A. Kumarm et al. (2026)
30+
* [Molecular dynamics simulations of reflection and sputtering behavior of boron under deuterium ion irradiation](https://www.sciencedirect.com/science/article/pii/S0022311526001327?casa_token=axebkNZe94gAAAAA:WG47RTEOepmq2gZnvPnpFWGfsLHOY1gibRY-Ed0wLXl6zEyUkQPFZnOGrRJhtR-3SVkh57p62Z4) H. Schamis, et al. (2026)
2731
* [Simulation of liquid lithium divertor geometry using SOLPS-ITER](https://doi.org/10.1109/TPS.2022.3166402), JD Lore et al. (2022)
2832
* [Characterizing W sources in the all-W wall, all-RF WEST tokamak environment](https://doi.org/10.1088/1361-6587/ac8acc), CC Klepper et al. (2022)
2933
* [hPIC2: A hardware-accelerated, hybrid particle-in-cell code for dynamic plasma-material interactions](https://doi.org/10.1016/j.cpc.2022.108569), LT Meredith et al. (2023)
3034
* [Global sensitivity analysis of a coupled multiphysics model to predict surface evolution in fusion plasma–surface interactions](https://doi.org/10.1016/j.commatsci.2023.112229), P. Robbe et al. (2023)
3135
* [Modeling the effect of nitrogen recycling on the erosion and leakage of tungsten impurities from the SAS-VW divertor in DIII-D during nitrogen gas injection](https://doi.org/10.1016/j.nme.2022.101254), MS Parsons et al. (2023)
3236
* [Enabling attractive-repulsive potentials in binary-collision-approximation monte-carlo codes for ion-surface interactions](https://doi.org/10.1088/2053-1591/ad1262), J Drobny and D Curreli (2023)
3337
* [Multi-physics modeling of tungsten collector probe samples during the WEST C4 He campaign](https://doi.org.10.1088/1741-4326/ad6c5b), A. Lasa et al. (2024)
38+
* [Integrated modeling of RF-induced tungsten erosion at ICRH antenna structures in the WEST tokamak*](https://iopscience.iop.org/article/10.1088/1741-4326/ade455/meta), A. Kumar et al., (2025)
3439

3540
## Getting started
3641

@@ -134,6 +139,7 @@ The following features are implemented in `RustBCA`:
134139
* nonlocal (Lindhard-Scharff),
135140
* and equipartition
136141
* Biersack-Varelas interpolation is also included for electronic stopping up to ~1 GeV/nucleon. Note that high energy physics beyond electronic stopping are not included, and that Biersack-Varelas may not be as accurate as other methods.
142+
* A modified Biersack-Varelas form which includes an interpolation parameter, `c_i`, that can be used to better match available stopping data for the Bragg peak.
137143
* Biersack-Haggmark treatment of high-energy free-flight paths between collisions can be included to greatly speed up high-energy simulations (i.e., by neglecting very small angle scattering).
138144
* A wide range of interaction potentials are provided, including:
139145
* the Kr-C, ZBL, Lenz-Jensen, and Moliere universal, screened-Coulomb potentials.
@@ -157,7 +163,8 @@ The following features are implemented in `RustBCA`:
157163
* full trajectory tracking for both the incident ions and target atoms,
158164
* and many other parameters such as position of origin of sputtered particles and energy loss along trajectories.
159165
* Optionally, the code can produce energy-angle and implantation distributions when built with the `--features distributions` flag and disable space-intensive particle list output with `--features no_list_output`.
160-
* Library functions for modeling ion reflection, implantation, and sputtering in C++/C, Python, and Fortran codes.
166+
* Library functions for running RustBCA in-memory in C++/C, Python, and Fortran codes.
167+
* A seeded PRNG such that RustBCA results are exactly reproducible on any machine for any number of threads
161168

162169
## Installation
163170

@@ -170,8 +177,6 @@ cargo build --release
170177

171178
will add an executable at `target/release/`.
172179

173-
[HDF5] for particle list input has been tested on Windows, but version 1.10.6 must be used.
174-
175180
#### Manual Dependences
176181

177182
* [rustup], the [Rust] toolchain (includes `cargo`, the [Rust] package manager, `rustc`, the [Rust] compiler, and more).
@@ -182,7 +187,6 @@ will add an executable at `target/release/`.
182187

183188
#### Optional Dependencies
184189

185-
* [HDF5] libraries
186190
* For manipulating input files and running associated scripts, the following are suggested:
187191
* [Python] 3.6+
188192
* [Python] libraries: `numpy`, `matplotlib`, `toml`, `shapely`, and `scipy`.
@@ -216,9 +220,9 @@ git clone https://github.com/lcpp-org/RustBCA
216220
cd RustBCA
217221
cargo build --release
218222
```
219-
8. (Optional) Build `RustBCA` with optional dependencies, `hdf5` and/or `rcpr`:
223+
8. (Optional) Build `RustBCA` with optional dependencies such as `rcpr`:
220224
```bash
221-
cargo build --release --features cpr_rootfinder,hdf5
225+
cargo build --release --features cpr_rootfinder
222226
```
223227
9. `input.toml` is the input file - see the [Input File](https://github.com/lcpp-org/RustBCA/wiki/Standalone-Code:-Input-File) page for more information
224228
10. Run the required tests using:
@@ -284,7 +288,7 @@ Additionally, `RustBCA` accepts an input file type (one of: `0D`, `1D`, `2D`, `T
284288
```bash
285289
./RustBCA 0D /path/to/input.toml
286290
```
287-
**Warning: RustBCA defaults to the 2D triangular mesh input mode.** For more details, see [Input Files](https://github.com/lcpp-org/RustBCA/wiki/Standalone-Code:-Input-File).
291+
**Note: RustBCA defaults to the 2D triangular mesh input mode.** For more details, see [Input Files](https://github.com/lcpp-org/RustBCA/wiki/Standalone-Code:-Input-File).
288292
Also have a look at the examples on the [Wiki] to see some examples of RustBCA input files.
289293

290294
[BCA]: https://en.wikipedia.org/wiki/Binary_collision_approximation

‎src/lib.rs‎

Lines changed: 75 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ use std::os::raw::c_int;
3030

3131
//standard slice
3232
use std::slice;
33-
//Mutex for multithreading in ergonomic Python library functions
34-
#[cfg(feature = "python")]
35-
use std::sync::Mutex;
3633

3734
//itertools
3835
use itertools::{izip};
@@ -1811,38 +1808,33 @@ pub fn sputtering_yield<'py>(ion: &Bound<'py, PyDict>, target: &Bound<'py, PyDic
18111808

18121809
let x = -m.geometry.energy_barrier_thickness;
18131810

1814-
let num_sputtered = Mutex::new(0);
1815-
18161811
let seed: u64 = get_seed().map_err(|error| PyValueError::new_err(""))?;
18171812

1818-
(0..num_samples as u64).into_par_iter()
1819-
.for_each_init(
1813+
Ok((0..num_samples as u64).into_par_iter()
1814+
.map_init(
18201815
|| ChaCha8Rng::seed_from_u64(seed), |rng, index| {
18211816

1822-
let p = particle::Particle::default_incident(
1823-
m1,
1824-
Z1,
1825-
energy,
1826-
Ec1,
1827-
Es1,
1828-
x,
1829-
ux,
1830-
uy,
1831-
uz
1832-
);
1833-
1834-
rng.set_stream(index);
1835-
let output = bca::single_ion_bca(p, &m, &options, rng);
1817+
let p = particle::Particle::default_incident(
1818+
m1,
1819+
Z1,
1820+
energy,
1821+
Ec1,
1822+
Es1,
1823+
x,
1824+
ux,
1825+
uy,
1826+
uz
1827+
);
1828+
18361829

1837-
for particle in output {
1838-
if particle.E > 0.0 && particle.dir.x < 0.0 && particle.left && (!particle.incident) {
1839-
let mut num_sputtered = num_sputtered.lock().unwrap();
1840-
*num_sputtered += 1;
1841-
}
1830+
rng.set_stream(index);
1831+
let output = bca::single_ion_bca(p, &m, &options, rng);
1832+
1833+
output.iter()
1834+
.filter(|particle| particle.E > 0.0 && particle.dir.x < 0.0 && particle.left && !particle.incident)
1835+
.count()
18421836
}
1843-
});
1844-
let num_sputtered = *num_sputtered.lock().unwrap();
1845-
Ok(num_sputtered as f64 / num_samples as f64)
1837+
).sum::<usize>() as f64 / num_samples as f64)
18461838
}
18471839

18481840
#[cfg(feature = "python")]
@@ -1910,54 +1902,38 @@ pub fn reflection_coefficient<'py>(ion: &Bound<'py, PyDict>, target: &Bound<'py,
19101902

19111903
let x = -m.geometry.energy_barrier_thickness;
19121904

1913-
let num_reflected = Mutex::new(0);
1914-
let energy_reflected = Mutex::new(0.0);
1915-
let residue = Mutex::new(0.0);
1916-
19171905
let seed: u64 = get_seed().map_err(|error| PyValueError::new_err(""))?;
19181906

1919-
(0..num_samples as u64).into_par_iter()
1920-
.for_each_init(
1907+
let (num_reflected, energy_reflected): (Vec<usize>, Vec<f64>) = (0..num_samples as u64).into_par_iter()
1908+
.map_init(
19211909
|| ChaCha8Rng::seed_from_u64(seed), |rng, index| {
1922-
let p = particle::Particle::default_incident(
1923-
m1,
1924-
Z1,
1925-
energy,
1926-
Ec1,
1927-
Es1,
1928-
x,
1929-
ux,
1930-
uy,
1931-
uz
1932-
);
1933-
1934-
rng.set_stream(index);
1935-
let output = bca::single_ion_bca(p, &m, &options, rng);
1936-
1937-
for particle in output {
1938-
if particle.E > 0.0 && particle.dir.x < 0.0 && particle.left && particle.incident {
1939-
let mut num_reflected = num_reflected.lock().unwrap();
1940-
*num_reflected += 1;
1941-
1942-
let mut energy_reflected = energy_reflected.lock().unwrap();
1943-
1944-
let residue_part;
1945-
1946-
// Use Moller-Knuth TwoSum to preserve deterministic fp reduce
1947-
(*energy_reflected, residue_part) = moller_knuth_two_sum(*energy_reflected, particle.E);
1948-
1949-
let mut residue = residue.lock().unwrap();
1950-
*residue = *residue + residue_part;
1910+
let p = particle::Particle::default_incident(
1911+
m1,
1912+
Z1,
1913+
energy,
1914+
Ec1,
1915+
Es1,
1916+
x,
1917+
ux,
1918+
uy,
1919+
uz
1920+
);
1921+
1922+
rng.set_stream(index);
1923+
let output = bca::single_ion_bca(p, &m, &options, rng);
1924+
1925+
let mut count = 0;
1926+
let mut energy = 0.0;
1927+
for particle in output.into_iter().filter(|particle| particle.E > 0.0 && particle.dir.x < 0.0 && particle.left && particle.incident) {
1928+
count += 1;
1929+
energy += particle.E;
19511930
}
1952-
};
1953-
});
1954-
if let (Ok(num_reflected), Ok(energy_reflected), Ok(residue)) = (num_reflected.lock(), energy_reflected.lock(), residue.lock()) {
1955-
return Ok((*num_reflected as f64 / num_samples as f64, (*energy_reflected + *residue) / EV / energy / num_samples as f64))
1956-
} else {
1957-
return Err(PyValueError::new_err("Check input values."))
1958-
}
19591931

1960-
1932+
(count, energy)
1933+
}
1934+
).collect();
1935+
// map_init preserves order - that means we don't have to use TwoSum for reproducibility
1936+
Ok((num_reflected.into_iter().sum::<usize>() as f64 / num_samples as f64, energy_reflected.iter().sum::<f64>() / EV / energy / num_samples as f64))
19611937
}
19621938

19631939
fn get_seed() -> Result<u64> {
@@ -2066,52 +2042,38 @@ pub fn compound_reflection_coefficient<'py>(ion: &Bound<'py, PyDict>, targets: V
20662042

20672043
let x = -m.geometry.energy_barrier_thickness;
20682044

2069-
let num_reflected = Mutex::new(0);
2070-
let energy_reflected = Mutex::new(0.0);
2071-
let residue = Mutex::new(0.0);
2072-
20732045
let seed: u64 = get_seed().map_err(|error| PyValueError::new_err(""))?;
20742046

2075-
(0..num_samples as u64).into_par_iter()
2076-
.for_each_init(
2047+
let (num_reflected, energy_reflected): (Vec<usize>, Vec<f64>) = (0..num_samples as u64).into_par_iter()
2048+
.map_init(
20772049
|| ChaCha8Rng::seed_from_u64(seed), |rng, index| {
2078-
2079-
let p = particle::Particle::default_incident(
2080-
m1,
2081-
Z1,
2082-
energy,
2083-
Ec1,
2084-
Es1,
2085-
x,
2086-
ux,
2087-
uy,
2088-
uz
2089-
);
2090-
2091-
rng.set_stream(index);
2092-
let output = bca::single_ion_bca(p, &m, &options, rng);
2093-
2094-
for particle in output {
2095-
if particle.E > 0.0 && particle.dir.x < 0.0 && particle.left && particle.incident {
2096-
let mut num_reflected = num_reflected.lock().unwrap();
2097-
*num_reflected += 1;
2098-
let mut energy_reflected = energy_reflected.lock().unwrap();
2099-
2100-
let residue_part;
2101-
2102-
// Use Moller-Knuth TwoSum to preserve deterministic fp reduce
2103-
(*energy_reflected, residue_part) = moller_knuth_two_sum(*energy_reflected, particle.E);
2104-
2105-
let mut residue = residue.lock().unwrap();
2106-
*residue = *residue + residue_part;
2050+
let p = particle::Particle::default_incident(
2051+
m1,
2052+
Z1,
2053+
energy,
2054+
Ec1,
2055+
Es1,
2056+
x,
2057+
ux,
2058+
uy,
2059+
uz
2060+
);
2061+
2062+
rng.set_stream(index);
2063+
let output = bca::single_ion_bca(p, &m, &options, rng);
2064+
2065+
let mut count = 0;
2066+
let mut energy = 0.0;
2067+
for particle in output.into_iter().filter(|particle| particle.E > 0.0 && particle.dir.x < 0.0 && particle.left && particle.incident) {
2068+
count += 1;
2069+
energy += particle.E;
21072070
}
2108-
}
2109-
});
2110-
let num_reflected = *num_reflected.lock().unwrap();
2111-
let energy_reflected = *energy_reflected.lock().unwrap();
2112-
let residue = *residue.lock().unwrap();
21132071

2114-
Ok((num_reflected as f64 / num_samples as f64, (energy_reflected + residue) / EV / energy / num_samples as f64))
2072+
(count, energy)
2073+
}
2074+
).collect();
2075+
// map_init preserves order - that means we don't have to use TwoSum for reproducibility
2076+
Ok((num_reflected.into_iter().sum::<usize>() as f64 / num_samples as f64, energy_reflected.iter().sum::<f64>() / EV / energy / num_samples as f64))
21152077
}
21162078

21172079
/// Moller-Knuth TwoSum Floating-Point Adder with Residual (FPAR)

0 commit comments

Comments
 (0)