Skip to content

Commit 06aa267

Browse files
authored
Merge pull request #233 from lcpp-org/4_8_potential
4 8 potential
2 parents 4422ceb + 2361e97 commit 06aa267

File tree

6 files changed

+118
-8
lines changed

6 files changed

+118
-8
lines changed

scripts/materials.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@
1212
's': 2.5
1313
}
1414

15+
carbon = {
16+
'symbol': 'C',
17+
'name': 'carbon',
18+
'Z': 6,
19+
'm': 12.011,
20+
'n': 1.1331E29,
21+
'Es': 7.37,
22+
'Ec': 1.0,
23+
'Eb': 3.0,
24+
}
25+
26+
chromium = {
27+
'symbol': 'Cr',
28+
'name': 'chromium',
29+
'Z': 24,
30+
'm': 51.9961,
31+
'Es': 4.10,
32+
'Ed': 28.0,
33+
'Ec': 3.0,
34+
'Eb': 3.0,
35+
'n': 8.327E28
36+
}
37+
1538
hydrogen = {
1639
'symbol': 'H',
1740
'name': 'hydrogen',

src/bca.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,21 @@ pub fn polynomial_rootfinder(Za: f64, Zb: f64, Ma: f64, Mb: f64, E0: f64, impact
576576

577577
let coefficients = interactions::polynomial_coefficients(relative_energy, impact_parameter, interaction_potential);
578578
let roots = real_polynomial_roots(coefficients.clone(), polynom_complex_threshold).unwrap();
579+
580+
/*
581+
println!("p={}", impact_parameter/ANGSTROM);
582+
583+
for coefficient in &coefficients {
584+
println!("{}", {coefficient});
585+
}
586+
587+
for root in &roots {
588+
println!("{} A", {root});
589+
}
590+
*/
591+
579592
let max_root = roots.iter().cloned().fold(f64::NAN, f64::max);
593+
580594
let inverse_transformed_root = interactions::inverse_transform(max_root, interaction_potential);
581595

582596
if roots.is_empty() || inverse_transformed_root.is_nan() {

src/enums.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,9 @@ pub enum InteractionPotential {
173173
/// Unscreened Coulombic interatomic potential between ions with charges Za and Zb.
174174
COULOMB{Za: f64, Zb: f64},
175175
/// Morse potential that smoothly interpolates to Kr-C at short ranges.
176-
KRC_MORSE{D: f64, alpha: f64, r0: f64, k: f64, x0: f64}
176+
KRC_MORSE{D: f64, alpha: f64, r0: f64, k: f64, x0: f64},
177+
/// Born repulsive potential.
178+
FOUR_EIGHT{alpha: f64, beta: f64}
177179
}
178180

179181
impl fmt::Display for InteractionPotential {
@@ -190,6 +192,7 @@ impl fmt::Display for InteractionPotential {
190192
InteractionPotential::WW => write!(f, "W-W cubic spline interaction potential."),
191193
InteractionPotential::COULOMB{Za, Zb} => write!(f, "Coulombic interaction with Za = {} and Zb = {}", Za, Zb),
192194
InteractionPotential::KRC_MORSE{D, alpha, r0, k, x0} => write!(f, "Morse potential with D = {} eV, alpha = {} 1/A, and r0 = {} A with short-range Kr-C with interpolation width k = {} 1/A and transition point x0 = {} A", D/EV, alpha*ANGSTROM, r0/ANGSTROM, k*ANGSTROM, x0/ANGSTROM),
195+
InteractionPotential::FOUR_EIGHT{alpha, beta} => write!(f, "Inverse polynomial potential with beta/r^8 (beta = {} A) Born repulsive term and alpha/r^4 (alpha = {} A) attractive term. Used to describe hydrogen interactions with transition metals.", alpha/ANGSTROM, beta/ANGSTROM),
193196
}
194197
}
195198
}

src/input.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ where <T as Geometry>::InputFileFormat: Deserialize<'static> + 'static {
450450

451451
assert!(
452452
match (interaction_potential, root_finder) {
453+
(InteractionPotential::FOUR_EIGHT{..}, Rootfinder::POLYNOMIAL{..}) => true,
453454
(InteractionPotential::LENNARD_JONES_12_6{..}, Rootfinder::CPR{..}) => true,
454455
(InteractionPotential::LENNARD_JONES_12_6{..}, Rootfinder::POLYNOMIAL{..}) => true,
455456
(InteractionPotential::LENNARD_JONES_12_6{..}, _) => false,

src/interactions.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ pub fn interaction_potential(r: f64, a: f64, Za: f64, Zb: f64, interaction_poten
4040
InteractionPotential::KRC_MORSE{D, alpha, r0, k, x0} => {
4141
krc_morse(r, a, Za, Zb, D, alpha, r0, k, x0)
4242
}
43+
InteractionPotential::FOUR_EIGHT{alpha, beta} => {
44+
four_eight(r, alpha, beta)
45+
}
46+
4347
}
4448
}
4549

@@ -48,6 +52,7 @@ pub fn energy_threshold_single_root(interaction_potential: InteractionPotential)
4852
match interaction_potential{
4953
InteractionPotential::LENNARD_JONES_12_6{..} | InteractionPotential::LENNARD_JONES_65_6{..} => f64::INFINITY,
5054
InteractionPotential::MORSE{..} | InteractionPotential::KRC_MORSE{..} => f64::INFINITY,
55+
InteractionPotential::FOUR_EIGHT{..} => f64::INFINITY,
5156
InteractionPotential::WW => f64::INFINITY,
5257
InteractionPotential::COULOMB{..} => f64::INFINITY,
5358
InteractionPotential::MOLIERE | InteractionPotential::KR_C | InteractionPotential::LENZ_JENSEN | InteractionPotential::ZBL | InteractionPotential::TRIDYN => 0.,
@@ -103,6 +108,9 @@ pub fn distance_of_closest_approach_function(r: f64, a: f64, Za: f64, Zb: f64, r
103108
InteractionPotential::KRC_MORSE{D, alpha, r0, k, x0} => {
104109
doca_krc_morse(r, impact_parameter, relative_energy, a, Za, Zb, D, alpha, r0, k, x0)
105110
},
111+
InteractionPotential::FOUR_EIGHT{alpha, beta} => {
112+
doca_four_eight(r, impact_parameter, relative_energy, alpha, beta)
113+
}
106114
InteractionPotential::COULOMB{..} => panic!("Coulombic potential cannot be used with rootfinder.")
107115
}
108116
}
@@ -131,6 +139,9 @@ pub fn distance_of_closest_approach_function_singularity_free(r: f64, a: f64, Za
131139
InteractionPotential::KRC_MORSE{D, alpha, r0, k, x0} => {
132140
doca_krc_morse(r, impact_parameter, relative_energy, a, Za, Zb, D, alpha, r0, k, x0)
133141
},
142+
InteractionPotential::FOUR_EIGHT{alpha, beta} => {
143+
doca_four_eight(r, impact_parameter, relative_energy, alpha, beta)
144+
}
134145
InteractionPotential::WW => {
135146
doca_tungsten_tungsten_cubic_spline(r, impact_parameter, relative_energy)
136147
},
@@ -152,6 +163,10 @@ pub fn scaling_function(r: f64, a: f64, interaction_potential: InteractionPotent
152163
let n = 6.;
153164
1./(1. + (r/sigma).powf(n))
154165
},
166+
InteractionPotential::FOUR_EIGHT{alpha, beta} => {
167+
let n = 8.;
168+
1./(1. + r.powi(8)/beta)
169+
}
155170
InteractionPotential::MORSE{D, alpha, r0} => {
156171
1./(1. + (r*alpha).powi(2))
157172
}
@@ -256,35 +271,65 @@ pub fn screening_length(Za: f64, Zb: f64, interaction_potential: InteractionPote
256271
InteractionPotential::MORSE{D, alpha, r0} => alpha,
257272
InteractionPotential::COULOMB{Za: Z1, Zb: Z2} => 0.88534*A0/(Z1.powf(0.23) + Z2.powf(0.23)),
258273
InteractionPotential::KRC_MORSE{..} => 0.8853*A0*(Za.sqrt() + Zb.sqrt()).powf(-2./3.),
274+
InteractionPotential::FOUR_EIGHT{..} =>0.8853*A0*(Za.sqrt() + Zb.sqrt()).powf(-2./3.),
259275
}
260276
}
261277

262278
/// Coefficients of inverse-polynomial interaction potentials.
279+
/// Note: these go in order of decreasing orders of r, e.g., [r^n, r^n-1, r^n-2, ... r, 1].
263280
pub fn polynomial_coefficients(relative_energy: f64, impact_parameter: f64, interaction_potential: InteractionPotential) -> Vec<f64> {
264281
match interaction_potential {
265282
InteractionPotential::LENNARD_JONES_12_6{sigma, epsilon} => {
266-
vec![1., 0., -impact_parameter.powi(2), 0., 0., 0., 4.*epsilon*sigma.powf(6.)/relative_energy, 0., 0., 0., 0., 0., -4.*epsilon*sigma.powf(12.)/relative_energy]
283+
let impact_parameteter_angstroms = impact_parameter/ANGSTROM;
284+
let epsilon_ev = epsilon/EV;
285+
let sigma_angstroms = sigma/ANGSTROM;
286+
let relative_energy_ev = relative_energy/EV;
287+
vec![1., 0., -impact_parameteter_angstroms.powi(2), 0., 0., 0., 4.*epsilon_ev*sigma_angstroms.powf(6.)/relative_energy_ev, 0., 0., 0., 0., 0., -4.*epsilon_ev*sigma_angstroms.powf(12.)/relative_energy_ev]
267288
},
268289
InteractionPotential::LENNARD_JONES_65_6{sigma, epsilon} => {
269-
vec![1., 0., 0., 0., -impact_parameter.powi(2), 0., 0., 0., 0., 0., 0., 0., 4.*epsilon*sigma.powf(6.)/relative_energy, -4.*epsilon*sigma.powf(6.5)/relative_energy]
270-
},
290+
let impact_parameteter_angstroms = impact_parameter/ANGSTROM;
291+
let epsilon_ev = epsilon/EV;
292+
let sigma_angstroms = sigma/ANGSTROM;
293+
let relative_energy_ev = relative_energy/EV;
294+
vec![1., 0., 0., 0., -impact_parameteter_angstroms.powi(2), 0., 0., 0., 0., 0., 0., 0., 4.*epsilon_ev*sigma_angstroms.powf(6.)/relative_energy, -4.*epsilon_ev*sigma_angstroms.powf(6.5)/relative_energy_ev]
295+
},
296+
InteractionPotential::FOUR_EIGHT{alpha, beta} => {
297+
//Note: I've transformed to angstroms here to help the rootfinder with numerical issues.
298+
//The units on alpha [m^4 J] and beta [m^8 J] make them unreasonably small to use numerically.
299+
let alpha_angstroms4_joule = alpha/ANGSTROM.powi(4);
300+
let beta_angstroms8_joule = beta/ANGSTROM.powi(8);
301+
let impact_parameteter_angstroms = impact_parameter/ANGSTROM;
302+
vec![1., 0., -(impact_parameteter_angstroms).powi(2), 0., alpha_angstroms4_joule/relative_energy, 0., 0., 0., -beta_angstroms8_joule/relative_energy,]
303+
}
271304
_ => panic!("Input error: non-polynomial interaction potential used with polynomial root-finder.")
272305
}
273306
}
274307

275-
/// Inverse-polynomial interaction potentials transformation to remove singularities for the CPR root-finder.
308+
/// Inverse-polynomial interaction potentials transformation to reduce numercial issues for the polynomial rootfinder.
309+
/// This is for if, for example, you have non-integer powers and need to multiply by a factor to get to integer powers.
310+
/// E.g., the 6.5-6 potential needs to be squared (x*x) giving you a 13th order polynomial in polynomial_coefficients().
276311
pub fn inverse_transform(x: f64, interaction_potential: InteractionPotential) -> f64 {
277312
match interaction_potential {
278313
InteractionPotential::LENNARD_JONES_12_6{..} => {
279-
x
314+
x*ANGSTROM
280315
},
281316
InteractionPotential::LENNARD_JONES_65_6{..} => {
282317
x*x
283318
},
319+
| InteractionPotential::FOUR_EIGHT{..} => {
320+
x*ANGSTROM
321+
}
284322
_ => panic!("Input error: non-polynomial interaction potential used with polynomial root-finder transformation.")
285323
}
286324
}
287325

326+
/// Four-Eight potential (Born repulsion)
327+
pub fn four_eight(r: f64, alpha: f64, beta: f64) -> f64 {
328+
let a = alpha.powf(1./4.);
329+
let b = beta.powf(1./8.);
330+
-(a/r).powi(4) + (b/r).powi(8)
331+
}
332+
288333
/// Lennard-Jones 12-6
289334
pub fn lennard_jones(r: f64, sigma: f64, epsilon: f64) -> f64 {
290335
4.*epsilon*((sigma/r).powf(12.) - (sigma/r).powf(6.))
@@ -305,6 +350,14 @@ pub fn krc_morse(r: f64, a: f64, Za: f64, Zb: f64, D: f64, alpha: f64, r0: f64,
305350
sigmoid(r, k, x0)*morse(r, D, alpha, r0) + sigmoid(r, -k, x0)*screened_coulomb(r, a, Za, Zb, InteractionPotential::KR_C)
306351
}
307352

353+
/// Distance of closest approach function for four-eight potential (Born repulsion)
354+
pub fn doca_four_eight(r: f64, impact_parameter: f64, relative_energy: f64, alpha: f64, beta: f64) -> f64 {
355+
let a = alpha.powf(1./4.);
356+
let b = beta.powf(1./8.);
357+
let b4 = beta.sqrt();
358+
(r/b).powf(8.) - (-(a*r/b/b) + 1.)/relative_energy - (impact_parameter*r.powf(3.)/b4)
359+
}
360+
308361
/// Distance of closest approach function for Morse potential.
309362
pub fn doca_morse(r: f64, impact_parameter: f64, relative_energy: f64, D: f64, alpha: f64, r0: f64) -> f64 {
310363
(r*alpha).powi(2) - (r*alpha).powi(2)*D/relative_energy*((-2.*alpha*(r - r0)).exp() - 2.*(-alpha*(r - r0)).exp()) - (impact_parameter*alpha).powi(2)
@@ -463,5 +516,6 @@ pub fn first_screening_radius(interaction_potential: InteractionPotential) -> f6
463516
InteractionPotential::MORSE{..} | InteractionPotential::KRC_MORSE{..} => 1.,
464517
InteractionPotential::WW => 1.,
465518
InteractionPotential::COULOMB{..} => 0.3,
519+
InteractionPotential::FOUR_EIGHT{..} => 1.,
466520
}
467521
}

src/tests.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@ use super::*;
33
#[cfg(test)]
44
use float_cmp::*;
55

6+
7+
#[test]
8+
#[cfg(feature = "cpr_rootfinder")]
9+
fn test_polynom() {
10+
use rcpr::chebyshev::*;
11+
let interaction_potential = InteractionPotential::FOUR_EIGHT{alpha: 1., beta: 1.};
12+
let coefficients = interactions::polynomial_coefficients(1., 1., interaction_potential);
13+
println!("{} {} {}", coefficients[0], coefficients[4], coefficients[8]);
14+
let roots = real_polynomial_roots(coefficients.clone(), 1e-9).unwrap();
15+
16+
let max_root = roots.iter().cloned().fold(f64::NAN, f64::max);
17+
println!("{}", max_root);
18+
let inverse_transformed_root = interactions::inverse_transform(max_root, interaction_potential);
19+
}
20+
621
#[test]
722
#[cfg(feature = "parry3d")]
823
fn test_parry_cuboid() {
@@ -1072,7 +1087,7 @@ fn test_quadrature() {
10721087

10731088
//If cpr_rootfinder is enabled, compare Newton to CPR - they should be nearly identical
10741089
#[cfg(feature = "cpr_rootfinder")]
1075-
if let Ok(x0_cpr) = bca::cpr_rootfinder(Za, Zb, Ma, Mb, E0, p, InteractionPotential::KR_C, 2, 10000, 1E-6, 1E-6, 1E-9, 1E9, 1E-13, false) {
1090+
if let Ok(x0_cpr) = bca::cpr_rootfinder(Za, Zb, Ma, Mb, E0, p, InteractionPotential::KR_C, 2, 10000, 1E-6, 1E-6, 1E-9, 1E9, 1E-13, true) {
10761091
println!("CPR: {} Newton: {}", x0_cpr, x0_newton);
10771092
assert!(approx_eq!(f64, x0_newton, x0_cpr, epsilon=1E-3));
10781093
};
@@ -1090,4 +1105,4 @@ fn test_quadrature() {
10901105

10911106
println!("Gauss-Mehler: {} Gauss-Legendre: {} Mendenhall-Weller: {} MAGIC: {}",
10921107
theta_gm, theta_gl, theta_mw, theta_magic);
1093-
}
1108+
}

0 commit comments

Comments
 (0)