Skip to content

Commit d42fd1e

Browse files
authored
Merge pull request #336 from lcpp-org/python_improvements
Python improvements and output_dir implementation
2 parents 8de6d3b + 3fd80f2 commit d42fd1e

16 files changed

Lines changed: 171 additions & 138 deletions

‎.github/workflows/rustbca_compile_check.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
python3 examples/make_input_file_and_run.py
4545
python3 examples/test_electronic_stopping.py
4646
python3 examples/test_different_options.py
47+
python3 examples/test_morse.py
4748
- name: Test Fortran and C bindings
4849
run : |
4950
cargo build --release --lib --features parry3d

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ cpr_rootfinder = ["rcpr"]
4444
distributions = ["ndarray"]
4545
no_list_output = []
4646
parry3d = ["parry3d-f64"]
47-
python = ["pyo3", "pythonize"]
47+
python = ["pyo3", "pythonize", "rcpr"]

‎examples/make_input_file_and_run.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,16 +255,22 @@
255255
'geometry_input': geometry_0D
256256
}
257257

258+
# Test rustbca_py and 'output_dir' option
258259
input_data['options']['name'] = 'rustbca_input_file'
260+
if not os.path.exists(r'./outputs'):
261+
os.makedirs('outputs')
262+
input_data['options']['output_dir'] = 'outputs'
259263
rustbca_py(input_data, mode)
260-
s = np.genfromtxt('rustbca_input_filesputtered.output', delimiter=',')
264+
s = np.genfromtxt('outputs/rustbca_input_filesputtered.output', delimiter=',')
261265

262266
arrays = rustbca_local_py(input_data, mode)
263267
sputtered = arrays['sputtered']
264268

265269
np.testing.assert_approx_equal(s[0, 2], np.array(arrays['energy'])[sputtered][0])
266270

271+
# reset these options before running from command line
267272
input_data['options']['name'] = 'input_file'
273+
input_data['options']['output_dir'] = '.'
268274

269275
# Attempt to cleanup line endings
270276
input_string = dumps(input_data).replace('\r', '')

‎examples/test_morse.py‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def run_morse_potential(energy, index, num_samples=10000, run_sim=True):
2525
mean_free_path_model = "LIQUID"
2626
interaction_potential = [[{{"MORSE"={{D=5.4971E-20, r0=2.782E-10, alpha=1.4198E10}}}}]]
2727
scattering_integral = [["GAUSS_LEGENDRE"]]
28-
root_finder = [[{{"CPR"={{n0=2, nmax=100, epsilon=1E-9, complex_threshold=1E-3, truncation_threshold=1E-9, far_from_zero=1E9, interval_limit=1E-12, derivative_free=true}}}}]]
28+
root_finder = [[{{"CPR"={{n0=3, nmax=100, epsilon=1E-9, complex_threshold=1E-9, truncation_threshold=1E-9, far_from_zero=1E9, interval_limit=1E-13, derivative_free=true}}}}]]
2929
num_threads = 4
3030
num_chunks = 10
3131
@@ -87,9 +87,9 @@ def run_krc_morse_potential(energy, index, num_samples=10000, run_sim=True):
8787
mean_free_path_model = "LIQUID"
8888
interaction_potential = [[{{"KRC_MORSE"={{D=5.4971E-20, r0=2.782E-10, alpha=1.4198E10, k=7E10, x0=0.75E-10}}}}]]
8989
scattering_integral = [["GAUSS_LEGENDRE"]]
90-
root_finder = [[{{"CPR"={{n0=2, nmax=100, epsilon=1E-9, complex_threshold=1E-3, truncation_threshold=1E-9, far_from_zero=1E9, interval_limit=1E-12, derivative_free=true}}}}]]
91-
num_threads = 4
92-
num_chunks = 10
90+
root_finder = [[{{"CPR"={{n0=2, nmax=200, epsilon=1E-9, complex_threshold=1E-9, truncation_threshold=1E-9, far_from_zero=1E9, interval_limit=1E-13, derivative_free=true}}}}]]
91+
num_threads = 6
92+
num_chunks = 1
9393
9494
[particle_parameters]
9595
length_unit = "ANGSTROM"
@@ -173,15 +173,30 @@ def run_krc_morse_potential(energy, index, num_samples=10000, run_sim=True):
173173
num_energies = 15
174174
energies = np.logspace(-1, 4, num_energies)
175175
run_sim = True
176-
num_samples = 10000
176+
num_samples = 100
177177
R_N = np.zeros(num_energies)
178178
R_E = np.zeros(num_energies)
179179
R_N_2 = np.zeros(num_energies)
180180
R_E_2 = np.zeros(num_energies)
181181

182182
for index, energy in enumerate(energies):
183-
R_N[index], R_E[index] = run_krc_morse_potential(energy, index, num_samples=num_samples, run_sim=True)
184-
R_N_2[index], R_E_2[index] = run_morse_potential(energy, index, num_samples=num_samples, run_sim=True)
183+
R_N[index], R_E[index] = run_krc_morse_potential(energy, index, num_samples=num_samples, run_sim=run_sim)
184+
R_N_2[index], R_E_2[index] = run_morse_potential(energy, index, num_samples=num_samples, run_sim=run_sim)
185+
186+
R_N_test = [
187+
0.00, 0.01, 0.28, 0.60, 0.90,
188+
0.95, 0.88, 0.81, 0.70, 0.49,
189+
0.30, 0.24, 0.17, 0.11, 0.10
190+
]
191+
192+
R_N_2_test = [
193+
0.00, 0.01, 0.28, 0.60, 0.90,
194+
0.95, 0.88, 0.81, 0.74, 0.60,
195+
0.46, 0.23, 0.05, 0.00, 0.00
196+
]
197+
198+
np.testing.assert_allclose(R_N, R_N_test)
199+
np.testing.assert_allclose(R_N_2, R_N_2_test)
185200

186201
plt.semilogx(energies, R_N, label='R_N Morse-Kr-C H-Ni, Es=1.5eV', color='purple')
187202
plt.semilogx(energies, R_N_2, label='R_N Morse H-Ni, Es=1.5eV', color='green')

‎setup.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
RustExtension(
99
"libRustBCA",
1010
binding=Binding.PyO3,
11-
features=["python", "parry3d", "pythonize"],
11+
features=["python", "parry3d", "pythonize", "cpr_rootfinder"],
1212
)
1313
],
1414
# rust extensions are not zip safe, just like C-extensions.

‎src/bca.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -623,21 +623,21 @@ pub fn cpr_rootfinder(Za: f64, Zb: f64, Ma: f64, Mb: f64, E0: f64, impact_parame
623623
interactions::scaling_function(r, impact_parameter, interaction_potential)};
624624

625625
let upper_bound = impact_parameter + interactions::crossing_point_doca(interaction_potential);
626+
let lower_bound = impact_parameter / 1000.0;
626627

627628
let roots = match derivative_free {
628-
true => find_roots_with_secant_polishing(&g, &f, 1e-15, upper_bound,
629+
true => find_roots_with_secant_polishing(&g, &f, lower_bound, upper_bound,
629630
n0, epsilon, nmax, complex_threshold,
630631
truncation_threshold, interval_limit, far_from_zero),
631632

632633
false => {
633634
let df = |r: f64| -> f64 {interactions::diff_distance_of_closest_approach_function(r, a, Za, Zb, relative_energy, impact_parameter, interaction_potential)};
634-
find_roots_with_newton_polishing(&g, &f, &df, 1e-15, upper_bound,
635+
find_roots_with_newton_polishing(&g, &f, &df, lower_bound, upper_bound,
635636
n0, epsilon, nmax, complex_threshold,
636637
truncation_threshold, interval_limit, far_from_zero)
637638
}
638639
}.with_context(|| format!("Numerical error: CPR Rootfinder failed to converge when calculating distance of closest approach for Er = {} eV p = {} A using {}.",
639-
relative_energy/EV, impact_parameter/ANGSTROM, interaction_potential))
640-
.unwrap();
640+
relative_energy/EV, impact_parameter/ANGSTROM, interaction_potential))?;
641641

642642
let max_root = roots.iter().cloned().fold(f64::NAN, f64::max)/a;
643643

‎src/consts.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ pub const TABLE_SIZE: usize = Z_MAX*(Z_MAX + 1)/2;
4444
/// Gauss-Legendre Points
4545
pub const GL_X: [f64; 5] = [0./2. + 1./2., -0.538469/2. + 1./2., 0.538469/2. + 1./2., -0.90618/2. + 1./2., 0.90618/2. + 1./2.];
4646
/// Gauss-Legendre Weights
47-
pub const GL_W: [f64; 5] = [0.568889/2., 0.478629/2., 0.478629/2., 0.236927/2., 0.236927/2.];
47+
pub const GL_W: [f64; 5] = [0.568889/2., 0.478629/2., 0.478629/2., 0.236927/2., 0.236927/2.];
48+
/// Enforced maximum number density
49+
pub const MAX_DENSITY: f64 = 1e32;

‎src/geometry.rs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ impl Geometry for Mesh0D {
6565

6666
let densities: Vec<f64> = input.densities.iter().map(|element| element/(length_unit).powi(3)).collect();
6767
assert!(!densities.is_empty(), "Input Error: density list empty.");
68-
68+
6969
let total_density: f64 = densities.iter().sum();
70+
assert!(total_density < MAX_DENSITY, "Input Error: total density {}/m^3 exceeds realistic values; check values or units.", total_density);
7071

7172
let energy_barrier_thickness = 1./total_density.cbrt()/SQRTPI*2.;
7273

@@ -154,6 +155,7 @@ impl Geometry for Mesh1D {
154155
let densities: Vec<Vec<f64>> = geometry_input.densities
155156
.iter()
156157
.map( |row| row.iter().map(|element| element/(length_unit).powi(3)).collect() ).collect();
158+
157159

158160
//Assert all layer density lists are equal length
159161
assert!(
@@ -170,6 +172,7 @@ impl Geometry for Mesh1D {
170172
layer_bottom += layer_thickness*length_unit;
171173

172174
let total_density: f64 = densities.iter().sum();
175+
assert!(total_density < MAX_DENSITY, "Input Error: total density {}/m^3 exceeds realistic values; check values or units.", total_density);
173176
let concentrations: Vec<f64> = densities.iter().map(|&density| density/total_density).collect::<Vec<f64>>();
174177

175178
layers.push(Layer1D::new(layer_top, layer_bottom, densities, concentrations, ck));
@@ -308,6 +311,7 @@ impl Geometry for HomogeneousMesh2D {
308311
let densities: Vec<f64> = input.densities.iter().map(|element| element/(length_unit).powi(3)).collect();
309312

310313
let total_density: f64 = densities.iter().sum();
314+
assert!(total_density < MAX_DENSITY, "Input Error: total density {}/m^3 exceeds realistic values; check values or units.", total_density);
311315

312316
let energy_barrier_thickness = 1./total_density.cbrt()/SQRTPI*2.;
313317

@@ -476,6 +480,7 @@ impl Geometry for Mesh2D {
476480
y3*length_unit,
477481
);
478482
let total_density: f64 = densities.iter().sum();
483+
assert!(total_density < MAX_DENSITY, "Input Error: total density {}/m^3 exceeds realistic values; check values or units.", total_density);
479484
let concentrations: Vec<f64> = densities.iter().map(|&density| density/total_density).collect::<Vec<f64>>();
480485

481486
cells.push(Cell2D::new(coordinate_set_converted, densities, concentrations, ck));

‎src/input.rs‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ fn default_rootfinder() -> Vec<Vec<Rootfinder>> {
212212
vec![vec![Rootfinder::DEFAULTNEWTON]]
213213
}
214214

215+
fn default_output_dir() -> String {
216+
"./".to_string()
217+
}
218+
215219
/// Rustbca's internal representation of the simulation-level options.
216220
#[cfg(not(feature = "distributions"))]
217221
#[derive(Deserialize, Clone)]
@@ -250,7 +254,9 @@ pub struct Options {
250254
#[serde(default = "default_false")]
251255
pub track_energy_losses: bool,
252256
#[serde(default = "default_seed")]
253-
pub seed: i32
257+
pub seed: i32,
258+
#[serde(default = "default_output_dir")]
259+
pub output_dir: String,
254260
}
255261

256262
#[cfg(not(feature = "distributions"))]
@@ -275,6 +281,7 @@ impl Options {
275281
track_displacements: false,
276282
track_energy_losses: false,
277283
seed: default_seed(),
284+
output_dir: default_output_dir(),
278285
}
279286
}
280287
}
@@ -331,7 +338,9 @@ pub struct Options {
331338
pub y_num: usize,
332339
pub z_num: usize,
333340
#[serde(default = "default_seed")]
334-
pub seed: i32
341+
pub seed: i32,
342+
#[serde(default = "default_output_dir")]
343+
pub output_dir: String,
335344
}
336345

337346
#[cfg(feature = "distributions")]
@@ -370,7 +379,8 @@ impl Options {
370379
x_num: 0,
371380
y_num: 0,
372381
z_num: 0,
373-
seed: default_seed()
382+
seed: default_seed(),
383+
output_dir: default_output_dir(),
374384
}
375385
}
376386
}
@@ -423,7 +433,7 @@ pub fn process_input_file<T: Geometry>(input: <T as Geometry>::InputFileFormat)
423433
assert!(material.m.len() == material.Es.len(), "Input error: material input arrays of unequal length.");
424434

425435
if material.interaction_index.len() <= 1 {
426-
material.interaction_index = vec![0; material.m.len()];
436+
material.interaction_index = vec![material.interaction_index[0]; material.m.len()];
427437
}
428438

429439
if material.Ed.len() <= 1 {

‎src/interactions.rs‎

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub fn crossing_point_doca(interaction_potential: InteractionPotential) -> f64 {
88
match interaction_potential {
99
InteractionPotential::LENNARD_JONES_12_6{sigma, ..} | InteractionPotential::LENNARD_JONES_65_6{sigma, ..} => sigma,
1010
InteractionPotential::MORSE{D, alpha, r0} => (alpha*r0 - (2.0_f64).ln())/alpha,
11-
InteractionPotential::WW => 50.*ANGSTROM,
11+
InteractionPotential::WW => 10.*ANGSTROM,
1212
_ => 10.*ANGSTROM,
1313
}
1414
}
@@ -176,7 +176,7 @@ pub fn scaling_function(r: f64, a: f64, interaction_potential: InteractionPotent
176176
1./(1. + (r*alpha).powi(2))
177177
}
178178
InteractionPotential::WW => {
179-
1.
179+
1./(1. + (r/ANGSTROM).powi(2))
180180
},
181181
InteractionPotential::KRC_MORSE{D, alpha, r0, k, x0} => {
182182
1./(1. + (r*alpha).powi(2))
@@ -465,8 +465,10 @@ pub fn tungsten_tungsten_cubic_spline(r: f64) -> f64 {
465465
let x2 = 2.10004200084;
466466

467467
if x <= x1 {
468-
469-
let a = screening_length(74., 74., InteractionPotential::ZBL);
468+
// This correction factor is necessary to correct for a discrepancy in the
469+
// published interaction potential - it is slightly off from the "correct"
470+
// ZBL screening length. Without this, the doca function is discontinuous.
471+
let a = screening_length(74., 74., InteractionPotential::ZBL)*1.000_250_544_359;
470472
screened_coulomb(r, a, 74., 74., InteractionPotential::ZBL)
471473

472474
} else if x <= x2 {
@@ -512,17 +514,8 @@ pub fn tungsten_tungsten_cubic_spline(r: f64) -> f64 {
512514

513515
/// Distance of closest approach function for the W-W cublic spline potential from Bjorkas et al.
514516
pub fn doca_tungsten_tungsten_cubic_spline(r: f64, p: f64, relative_energy: f64) -> f64 {
515-
516517
let x = r/ANGSTROM;
517-
let x1 = 1.10002200044;
518-
let x2 = 2.10004200084;
519-
520-
if x <= x1 {
521-
let a = screening_length(74., 74., InteractionPotential::ZBL);
522-
distance_of_closest_approach_function_singularity_free(r, a, 74., 74., relative_energy, p, InteractionPotential::ZBL)
523-
} else {
524-
(r/ANGSTROM).powi(2) - (r/ANGSTROM).powi(2)*tungsten_tungsten_cubic_spline(r)/relative_energy - p.powi(2)/ANGSTROM.powi(2)
525-
}
518+
x.powi(2) - x.powi(2)*tungsten_tungsten_cubic_spline(r)/relative_energy - (p/ANGSTROM).powi(2)
526519
}
527520

528521
fn heaviside(x: f64) -> f64 {

0 commit comments

Comments
 (0)