Coverage for /builds/hweiske/ase/ase/calculators/morse.py: 100.00%
36 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-22 11:22 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-22 11:22 +0000
1import numpy as np
3from ase.calculators.calculator import Calculator
4from ase.neighborlist import neighbor_list
7def fcut(r, r0, r1):
8 """
9 Piecewise quintic C^{2,1} regular polynomial for use as a smooth cutoff.
10 Ported from JuLIP.jl, https://github.com/JuliaMolSim/JuLIP.jl
12 Parameters
13 ----------
14 r0 - inner cutoff radius
15 r1 - outder cutoff radius
16 """""
17 s = 1.0 - (r - r0) / (r1 - r0)
18 return (s >= 1.0) + ((s > 0.0) & (s < 1.0)) * (
19 6.0 * s**5 - 15.0 * s**4 + 10.0 * s**3
20 )
23def fcut_d(r, r0, r1):
24 """
25 Derivative of fcut() function defined above
26 """
27 s = 1 - (r - r0) / (r1 - r0)
28 return -(
29 ((s > 0.0) & (s < 1.0))
30 * (30 * s**4 - 60 * s**3 + 30 * s**2)
31 / (r1 - r0)
32 )
35class MorsePotential(Calculator):
36 """Morse potential.
38 Default values chosen to be similar as Lennard-Jones.
39 """
41 implemented_properties = ['energy', 'forces']
42 default_parameters = {'epsilon': 1.0,
43 'rho0': 6.0,
44 'r0': 1.0,
45 'rcut1': 1.9,
46 'rcut2': 2.7}
47 nolabel = True
49 def __init__(self, **kwargs):
50 """
51 Parameters
52 ----------
53 epsilon: float
54 Absolute minimum depth, default 1.0
55 r0: float
56 Minimum distance, default 1.0
57 rho0: float
58 Exponential prefactor. The force constant in the potential minimum
59 is k = 2 * epsilon * (rho0 / r0)**2, default 6.0
60 """
61 Calculator.__init__(self, **kwargs)
63 def calculate(self, atoms=None, properties=['energy'],
64 system_changes=['positions', 'numbers', 'cell',
65 'pbc', 'charges', 'magmoms']):
66 Calculator.calculate(self, atoms, properties, system_changes)
67 epsilon = self.parameters.epsilon
68 rho0 = self.parameters.rho0
69 r0 = self.parameters.r0
70 rcut1 = self.parameters.rcut1 * r0
71 rcut2 = self.parameters.rcut2 * r0
73 forces = np.zeros((len(self.atoms), 3))
74 preF = - 2 * epsilon * rho0 / r0
76 i, j, d, D = neighbor_list('ijdD', atoms, rcut2)
77 dhat = (D / d[:, None]).T
79 expf = np.exp(rho0 * (1.0 - d / r0))
80 fc = fcut(d, rcut1, rcut2)
82 E = epsilon * expf * (expf - 2)
83 dE = preF * expf * (expf - 1) * dhat
84 energy = 0.5 * (E * fc).sum()
86 F = (dE * fc + E * fcut_d(d, rcut1, rcut2) * dhat).T
87 for dim in range(3):
88 forces[:, dim] = np.bincount(i, weights=F[:, dim],
89 minlength=len(atoms))
91 self.results['energy'] = energy
92 self.results['forces'] = forces