Statistiques
| Révision :

root / ase / calculators / tip3p.py @ 1

Historique | Voir | Annoter | Télécharger (5,83 ko)

1
"""TIP3P potential, constraints and dynamics."""
2
from math import pi, sin, cos
3

    
4
import numpy as np
5

    
6
import ase.units as units
7
from ase.parallel import world
8
from ase.md.md import MolecularDynamics
9

    
10
qH = 0.417
11
sigma0 = 3.15061
12
epsilon0 = 0.1521 * units.kcal / units.mol
13
rOH = 0.9572
14
thetaHOH = 104.52 / 180 * pi
15

    
16

    
17
class TIP3P:
18
    def __init__(self, rc=9.0, width=1.0):
19
        self.energy = None
20
        self.forces = None
21

    
22
        self.rc1 = rc - width
23
        self.rc2 = rc
24
        
25
    def get_spin_polarized(self):
26
        return False
27
    
28
    def update(self, atoms):
29
        if (self.energy is None or
30
            len(self.numbers) != len(atoms) or
31
            (self.numbers != atoms.get_atomic_numbers()).any()):
32
            self.calculate(atoms)
33
        elif ((self.positions != atoms.get_positions()).any() or
34
              (self.pbc != atoms.get_pbc()).any() or
35
              (self.cell != atoms.get_cell()).any()):
36
            self.calculate(atoms)
37

    
38
    def calculation_required(self, atoms, quantities):
39
        if len(quantities) == 0:
40
            return False
41

    
42
        return (self.energy is None or
43
                len(self.numbers) != len(atoms) or
44
                (self.numbers != atoms.get_atomic_numbers()).any() or
45
                (self.positions != atoms.get_positions()).any() or
46
                (self.pbc != atoms.get_pbc()).any() or
47
                (self.cell != atoms.get_cell()).any())
48
                
49
    def get_potential_energy(self, atoms):
50
        self.update(atoms)
51
        return self.energy
52

    
53
    def get_forces(self, atoms):
54
        self.update(atoms)
55
        return self.forces.copy()
56
    
57
    def get_stress(self, atoms):
58
        raise NotImplementedError
59
    
60
    def calculate(self, atoms):
61
        self.positions = atoms.get_positions().copy()
62
        self.cell = atoms.get_cell().copy()
63
        self.pbc = atoms.get_pbc().copy()
64
        natoms = len(atoms)
65
        nH2O = natoms // 3
66

    
67
        assert self.pbc.all()
68
        C = self.cell.diagonal()
69
        assert not (self.cell - np.diag(C)).any()
70
        assert (C >= 2 * self.rc2).all()
71
        self.numbers = atoms.get_atomic_numbers()
72
        Z = self.numbers.reshape((-1, 3))
73
        assert (Z[:, 1:] == 1).all() and (Z[:, 0] == 8).all()
74

    
75
        R = self.positions.reshape((nH2O, 3, 3))
76
        RO = R[:, 0]
77
        
78
        self.energy = 0.0
79
        self.forces = np.zeros((natoms, 3))
80
        
81
        if world is None:
82
            mya = range(nH2O - 1)
83
        else:
84
            rank = world.rank
85
            size = world.size
86
            assert nH2O // (2 * size) == 0
87
            mynH2O = nH2O // 2 // size
88
            mya = (range(rank * n, (rank + 1) * n) +
89
                   range((size - rank - 1) * n, (size - rank) * n))
90

    
91
        q = np.empty(3)
92
        q[:] = qH * (units.Hartree * units.Bohr)**0.5
93
        q[0] *= -2
94
        
95
        for a in mya:
96
            DOO = (RO[a + 1:] - RO[a] + 0.5 * C) % C - 0.5 * C
97
            dOO = (DOO**2).sum(axis=1)**0.5
98
            x1 = dOO > self.rc1
99
            x2 = dOO < self.rc2
100
            f = np.zeros(nH2O - a - 1)
101
            f[x2] = 1.0
102
            dfdd = np.zeros(nH2O - a - 1)
103
            x12 = np.logical_and(x1, x2)
104
            d = (dOO[x12] - self.rc1) / (self.rc2 - self.rc1)
105
            f[x12] -= d**2 * (3.0 - 2.0 * d)
106
            dfdd[x12] -= 6.0 / (self.rc2 - self.rc1) * d * (1.0 - d)
107

    
108
            y = (sigma0 / dOO)**6
109
            y2 = y**2
110
            e = 4 * epsilon0 * (y2 - y)
111
            self.energy += np.dot(e, f)
112
            dedd = 24 * epsilon0 * (2 * y2 - y) / dOO * f - e * dfdd
113
            F = (dedd / dOO)[:, np.newaxis] * DOO
114
            self.forces[(a + 1) * 3::3] += F
115
            self.forces[a * 3] -= F.sum(axis=0)
116

    
117
            for i in range(3):
118
                D = (R[a + 1:] - R[a, i] + 0.5 * C) % C - 0.5 * C
119
                d = (D**2).sum(axis=2)**0.5
120
                e = q[i] * q / d
121
                self.energy += np.dot(f, e).sum()
122
                F = (e / d**2 * f[:, np.newaxis])[:, :, np.newaxis] * D
123
                F[:, 0] -= (e.sum(axis=1) * dfdd / dOO)[:, np.newaxis] * DOO 
124
                self.forces[(a + 1) * 3:] += F.reshape((-1, 3))
125
                self.forces[a * 3 + i] -= F.sum(axis=0).sum(axis=0)
126

    
127
        if world is not None:
128
            self.energy = world.sum(self.energy)
129
            world.sum(self.forces)
130

    
131

    
132
class H2OConstraint:
133
    """Constraint object for a rigid H2O molecule."""
134
    def __init__(self, r=rOH, theta=thetaHOH, iterations=23, masses=None):
135
        self.r = r
136
        self.theta = theta
137
        self.iterations = iterations
138
        self.m = masses
139

    
140
    def set_masses(self, masses):
141
        self.m = masses
142

    
143
    def adjust_positions(self, old, new):
144
        bonds = [(0, 1, self.r), (0, 2, self.r)]
145
        if self.theta:
146
            bonds.append((1, 2, sin(self.theta / 2) * self.r * 2))
147
        for iter in range(self.iterations):
148
            for i, j, r in bonds:
149
                D = old[i::3] - old[j::3]
150
                m1 = self.m[i]
151
                m2 = self.m[j]
152
                a = new[i::3]
153
                b = new[j::3]
154
                B = a - b
155
                x = (D**2).sum(axis=1)
156
                y = (D * B).sum(axis=1)
157
                z = (B**2).sum(axis=1) - r**2
158
                k = m1 * m2 / (m1 + m2) * ((y**2 - x * z)**0.5 - y) / x
159
                k.shape = (-1, 1)
160
                a += k / m1 * D
161
                b -= k / m2 * D
162

    
163
    def adjust_forces(self, positions, forces):
164
        pass
165
        
166
    def copy(self):
167
        return H2OConstraint(self.r, self.theta, self.iterations, self.m)
168

    
169

    
170
class Verlet(MolecularDynamics):
171
    def step(self, f):
172
        atoms = self.atoms
173
        m = atoms.get_masses()[:, np.newaxis]
174
        v = self.atoms.get_velocities()
175
        r0 = atoms.get_positions()
176
        r = r0 + self.dt * v + self.dt**2 * f / m
177
        atoms.set_positions(r)
178
        r = atoms.get_positions()
179
        v = (r - r0) / self.dt
180
        self.atoms.set_velocities(v)
181
        return atoms.get_forces()