root / ase / calculators / mopac.py @ 11
Historique | Voir | Annoter | Télécharger (5,68 ko)
1 |
"""
|
---|---|
2 |
This module is the MOPAC module for ASE,
|
3 |
based on the PyMD MOPAC Module by R. Bulo,
|
4 |
implemented by T. Kerber
|
5 |
|
6 |
2011, ENS Lyon
|
7 |
"""
|
8 |
import commands |
9 |
import numpy as np |
10 |
|
11 |
from ase.atoms import Atoms |
12 |
from ase.units import kcal, mol |
13 |
from ase.calculators.general import Calculator |
14 |
|
15 |
kcal_mol = kcal / mol |
16 |
class Mopac(Calculator): |
17 |
def __init__(self, files=None, restart=0, geomopt=False, functional="PM6"): |
18 |
self.restart = restart
|
19 |
self.geomopt = geomopt
|
20 |
self.functional = functional
|
21 |
|
22 |
self.spin = 0 |
23 |
self.occupations = None |
24 |
|
25 |
self.first = True |
26 |
self.stress = None |
27 |
self.energy_zero = None |
28 |
self.energy_free = None |
29 |
self.forces = None |
30 |
|
31 |
def write_moldata(self): |
32 |
"""
|
33 |
Writes the files that have to be written each timestep
|
34 |
"""
|
35 |
# Change labels variable if necessary
|
36 |
# FIXME: what does norderats stands for????
|
37 |
norderats = 0
|
38 |
for el in self.atoms.get_chemical_symbols(): |
39 |
norderats += 1
|
40 |
if norderats != self.atoms.get_number_of_atoms(): |
41 |
self.labels = self.get_labels() |
42 |
|
43 |
input = self.get_input_block()
|
44 |
pyfile = open('pymopac.in','w') |
45 |
pyfile.write(input)
|
46 |
pyfile.close() |
47 |
|
48 |
def run(self): |
49 |
self.write_moldata()
|
50 |
out = commands.getoutput('mopac pymopac.in')
|
51 |
|
52 |
outputfile = open('pymopac.in.out') |
53 |
outfile = open('mopac%02i.out'%(self.restart),'w') |
54 |
for line in outputfile: |
55 |
outfile.write(line) |
56 |
outfile.close() |
57 |
outputfile.close() |
58 |
|
59 |
energy = self.read_energy()
|
60 |
if energy == None: |
61 |
energy = self.rerun()
|
62 |
self.forces = self.read_forces() |
63 |
|
64 |
self.energy_zero = energy
|
65 |
self.energy_free = energy
|
66 |
self.first = False |
67 |
|
68 |
def read_energy(self, charges=True): |
69 |
outfile = open('pymopac.in.out') |
70 |
lines = outfile.readlines() |
71 |
outfile.close() |
72 |
|
73 |
chargelines = 0
|
74 |
if charges:
|
75 |
nats = len(self.atoms) |
76 |
block = ''
|
77 |
for line in lines: |
78 |
if line.find('HEAT OF FORMATION') != -1: |
79 |
words = line.split() |
80 |
energy = float(words[5]) |
81 |
if line.find('H.o.F. per unit cell') != -1: |
82 |
words = line.split() |
83 |
energy = float(words[5]) |
84 |
if line.find('"""""""""""""UNABLE TO ACHIEVE SELF-CONSISTENCE') != -1: |
85 |
energy = None
|
86 |
if charges:
|
87 |
if line.find('NET ATOMIC CHARGES') != -1: |
88 |
chargelines += 1
|
89 |
if chargelines > 0 and chargelines <= nats+3: |
90 |
chargelines += 1
|
91 |
block += line |
92 |
|
93 |
if charges:
|
94 |
chargefile = open('charge%02i.out'%(self.restart),'a') |
95 |
chargefile.write(block) |
96 |
chargefile.close() |
97 |
|
98 |
return energy
|
99 |
|
100 |
def read_forces(self): |
101 |
|
102 |
outfile = open('pymopac.in.aux') |
103 |
lines = outfile.readlines() |
104 |
outfile.close() |
105 |
|
106 |
outputforces = None
|
107 |
nats = len(self.atoms) |
108 |
nx = nats * 3
|
109 |
|
110 |
for i,line in enumerate(lines): |
111 |
if line.find('GRADIENTS:') != -1: |
112 |
forces = [] |
113 |
l = 0
|
114 |
for j in range(nx): |
115 |
if j%10 == 0: |
116 |
l += 1
|
117 |
gline = lines[i+l] |
118 |
k = j -((l-1)*10) |
119 |
try:
|
120 |
forces.append(-float(gline[k*18:(k*18)+18])) |
121 |
except ValueError: |
122 |
if outputforces == None: |
123 |
outputforces = self.read_output_forces()
|
124 |
forces.append(outputforces[j]) |
125 |
break
|
126 |
|
127 |
forces = np.reshape(forces, (nats, 3))
|
128 |
forces *= kcal_mol |
129 |
return forces
|
130 |
|
131 |
def read_output_forces(self): |
132 |
outfile = open('pymopac.in.out') |
133 |
lines = outfile.readlines() |
134 |
outfile.close() |
135 |
|
136 |
nats = len(self.atoms) |
137 |
nx = nats * 3
|
138 |
|
139 |
forces = [] |
140 |
for i,line in enumerate(lines): |
141 |
if line.find('GRADIENT\n') != -1: |
142 |
for j in range(nx): |
143 |
gline = lines[i+j+1]
|
144 |
forces.append(-float(gline[49:62])) |
145 |
break
|
146 |
|
147 |
return forces
|
148 |
|
149 |
def get_input_block(self): |
150 |
# For restarting I can use 'DENOUT' and 'OLDENS' at some point
|
151 |
block = ''
|
152 |
block += '%s '%(self.functional) |
153 |
#if self.functional == 'PM6-DH2':
|
154 |
block += 'NOANCI '
|
155 |
block += '1SCF GRADIENTS '
|
156 |
block += 'AUX(0,PRECISION=9) '
|
157 |
block += 'RELSCF = 0.0001 '
|
158 |
charge = 0
|
159 |
if charge != 0: |
160 |
block += 'CHARGE=%i '%(charge)
|
161 |
if self.spin == 1.: |
162 |
block += 'DOUBLET '
|
163 |
elif self.spin == 2.: |
164 |
block += 'TRIPLET '
|
165 |
block += '\n'
|
166 |
block += 'Title: ASE job\n\n'
|
167 |
|
168 |
constraints = self.atoms._get_constraints()
|
169 |
nconstraints = len(constraints)
|
170 |
for iat in xrange(len(self.atoms)): |
171 |
f = [1, 1, 1] |
172 |
if iat < nconstraints:
|
173 |
if constraints[iat] is not None: |
174 |
f = [0, 0, 0] |
175 |
atom = self.atoms[iat]
|
176 |
xyz = atom.position |
177 |
block += ' %2s'%atom.symbol
|
178 |
block += ' %16.5f %i %16.5f %i %16.5f %i \n'%(xyz[0],f[0],xyz[1],f[1],xyz[2],f[2]) |
179 |
|
180 |
if self.atoms.pbc.any(): |
181 |
for v in self.atoms.get_cell(): |
182 |
block += 'Tv %8.3f %8.3f %8.3f\n'%(v[0],v[1],v[2]) |
183 |
return block
|
184 |
|
185 |
def update(self, atoms): |
186 |
if not (self.atoms.get_positions() == atoms.get_positions()).all() or self.first: |
187 |
self.atoms = atoms.copy()
|
188 |
self.run()
|