Statistiques
| Révision :

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()