Révision 19 ase/calculators/gaussian.py

gaussian.py (revision 19)
1 1
"""
2
This module is the Gaussian module for ASE,  based on the PyMD Gaussian Module by R. Bulo,  
3
implemented by T. Kerber
2
@author:       Torsten Kerber and Paul Fleurat-Lessard
3
@organization: Ecole Normale Superieure de Lyon
4
@contact:      Paul.Fleurat-Lessard@ens-lyon.fr
4 5

  
5
 @author:       Rosa Bulo
6
 @organization: Vrije Universiteit Amsterdam
7
 @contact:      bulo@few.vu.nl
6
@data: June, 6, 2012
8 7

  
9
Torsten Kerber,  ENS LYON: 2011,  07,  11
10

  
11
This work is supported by Award No. UK-C0017,  made by King Abdullah
12
University of Science and Technology(KAUST)
8
This work is supported by Award No. UK-C0017 made by King Abdullah University of Science and Technology(KAUST)
13 9
"""
14
import os,  sys,  string
15

  
10
import os, sys
16 11
import numpy as np
17
from ase.units import Hartree,  Bohr
12

  
13
from ase.units import Hartree, Bohr
18 14
from ase.calculators.general import Calculator
19
import os, sys, re, math, commands, subprocess, time
20 15

  
21
class Gaussian(Calculator):
22
    """
23
    Class for representing the settings of a ReaxffForceJob
24
        
25
    It contains the following variables:
26
                        
27
    basis:  
28
        A string representing the basisset to be used
16
str_keys = [
17
    'functional', # functional
18
    'basis',      # basis set
19
    'flags'       # more options
20
    'memory',     # RAM memory used
21
    'disk',       # disk space available
22
    'filename',   # name of the gaussian files
23
    'command'     # command to run Gaussian
24
    ]
29 25

  
30
    functional:
31
        A string representing the DFT functional or wavefunction
32
        method to be used.
26
int_keys = [
27
    'multiplicity', # multiplicity of the system
28
    'nproc',        # number of processors
29
    ]
33 30

  
34
    """
35

  
36
    def __init__(self, functional='BP86', basis='TZVP', atoms=None, inputfile=None):
37
        self.functional = functional
38
        self.basis = basis
39
        self.restartdata = None
40
        self.forces = None
41
        self.atoms_old = None
42
        if inputfile != None:
43
            self = self.get_settings(inputfile)                
31
class Gaussian(Calculator):
32
    def __init__(self, atoms=None, **kwargs):
33
        self.set_standard()
34
        
35
        self.set(**kwargs)
44 36
        self.atoms = atoms
37
        
38
        self.potential_energy = 0.0
39
        self.forces = None
45 40

  
46
    def set_functional(self, functional):
47
        self.functional = functional
41
        self.new_calculation = True
42
        if atoms != None:
43
            self.forces = np.zeros((len(atoms), 3))
44
        self.stress = None
45
        
46
    def set_standard(self):
47
        self.str_params = {}
48
        self.int_params = {}
49
        
50
        for key in str_keys:
51
            self.str_params['key'] = None
52
        
53
        for key in int_keys:
54
            self.int_params['key'] = None
55
        
56
        self.str_params['functional'] = "B3LYP"
57
        self.str_params['basis'] = '6-31G*'
58
        self.str_params['flags'] = ' Force '
59
        self.str_params['memory'] = '256MB'
60
        self.str_params['disk'] = '16GB'
61
        
62
        self.str_params['command'] = 'g09'
63
        self.str_params['filename'] = 'ASE-gaussian'
48 64

  
49
    def set_basis(self, basis):
50
        self.basis = basis
65
        self.int_params['multiplicity'] = 1
66
        self.int_params['nproc'] = 8
67
        
68
        
69
    def set(self, **kwargs):
70
        for key in kwargs:
71
            if self.str_params.has_key(key):
72
                self.str_params[key] = kwargs[key]
73
            if self.int_params.has_key(key):
74
                self.int_params[key] = kwargs[key]
75
                
76
                
77
    def set_atoms(self, atoms):
78
        if self.atoms != atoms:
79
            self.atoms = atoms.copy()
80
            self.new_calculation = True
81
    
82
    def write_file_line(self, file, line):
83
        file.write(line)
84
        file.write('\n')
85
    
86
    def write_input(self):
87
        name = self.str_params['filename']
88
        
89
        inputfile = open(name + '.com', 'w')
90
        self.write_file_line(inputfile, '%chk=' + name + '.chk')
91
        self.write_file_line(inputfile, '%mem=' + self.str_params['memory'])
92
        self.write_file_line(inputfile, '%nproc=' + ('%d' % self.int_params['nproc']))
93
        self.write_file_line(inputfile, 'MaxDisk=' + self.str_params['disk'])
51 94

  
52
    def setup_job(self,  type='force'):
53
        ForceJob.setup_job(self, type=type)
54
 
55
        os.chdir(self.dirname)
56
        self.myfiles = GaussianFileManager()
57
        # Add the restart restuls to the filemanager. The checksum will be empty('')
58
        if self.restartdata != None:
59
            self.r_m = GaussianResults(self.myfiles)
60
            self.myfiles.add_results(self.r_m, dir=self.restartdata+'/')
95
        line = '#' + self.str_params['functional'] + '/' + self.str_params['basis'] + ' ' + self.str_params['flags']
96
        self.write_file_line(inputfile, line)
97
        
98
        self.write_file_line(inputfile, '')
99
        self.write_file_line(inputfile, 'Gaussian job created by ASE (supported by King Abdullah University of Science and Technology ,KAUST)')
100
        self.write_file_line(inputfile, '')
61 101

  
62
        os.chdir(self.dir)
102
        line = '%d %d' % (sum(self.atoms.get_charges()), self.int_params['multiplicity'])
103
        self.write_file_line(inputfile, line)
63 104

  
64
    def write_parfile(self, filename):
65
        parfile = open(filename, 'w')
66
        parfile.write(self.prm)
67
        parfile.close()
105
        coords = self.atoms.get_positions()
106
        for i, at in enumerate(self.atoms.get_chemical_symbols()):
107
            line = at
108
            coord = coords[i]
109
            line += '  %16.5f %16.5f %16.5f'%(coord[0], coord[1], coord[2])
110
            self.write_file_line(inputfile, line)
68 111

  
69
    def write_moldata(self,  type='force'):
70
        out = commands.getoutput('mkdir SETUP')
71
        out = commands.getoutput('ls SETUP/coord.*').split()
72
        pdb = False
73
        psf = False
74
        prm = False
112
        if self.atoms.get_pbc().any(): 
113
            cell = self.atoms.get_cell()
114
            line = ''
115
            for v in cell: 
116
                line += 'TV %8.3f %8.3f %8.3f\n'%(v[0], v[1], v[2])
117
            self.write_file_line(inputfile, line)
118
            
119
        self.write_file_line(inputfile, '')
120
        
121
    def update(self, atoms):
122
        if (self.atoms is None or self.atoms.positions.shape != atoms.positions.shape):
123
            self.atoms = atoms.copy()
124
            self.calculate(atoms)
75 125

  
76
        # Change labels variable if necessary
77
        norderats = 0
78
#        for el in self.labels:
79
#            norderats += 1
80
#            if norderats != self.atoms.pdb.get_number_of_atoms():
81
#                self.labels = self.get_labels()
126
    def calculate(self, atoms):
127
        self.write_input()
128
        line = self.str_params['command'] + ' ' + self.str_params['filename'] + '.com'
129
        exitcode = os.system(line)
130
        if exitcode != 0:
131
            raise RuntimeError('Gaussian exited with exit code: %d.  ' % exitcode)
132
        self.read_output()
133
        self.new_calculation = False
134
            
135
    def get_potential_energy(self, atoms=None, force_consistent=False):
136
        self.set_atoms(atoms)
137
        if self.new_calculation:
138
            self.calculate(atoms)
139
        return self.potential_energy
82 140

  
83
        self.write_input(type)
141
    def get_forces(self, atoms):
142
        self.set_atoms(atoms)
143
        if self.new_calculation:
144
            self.calculate(atoms)
145
        return self.forces
84 146

  
85
    def write_input(self, type):
86
        input = self.get_input_block(type)
87
        pyfile = open('ASE-gaussian.com', 'w')
88
        pyfile.write(input)
89
        pyfile.close()
90

  
91
    def get_inp(self,  filename):
92
        input = None
93
        lis = commands.getoutput('ls %s'%(filename)).split('\n')
94
        if not re.search('ls:', lis[0]):
95
            infile = open(filename)
96
            input = infile.readlines()
97
            infile.close()
98
        return input
99

  
100
    def run(self, type='force'):
101
        startrun = time.time()
102
        # I have to write the input every timestep,  because that is where the coordinates are
103
        # self.write_input(type)
104
        self.write_moldata(type)
105

  
106
        # Make sure that restart data is copied to the current directory
107
#        if self.r_m != None:
108
#            self.r_m.files.copy_job_result_files(self.r_m.fileid)
109

  
110
        out = commands.getoutput('g09 ASE-gaussian.com')
111
        endrun = time.time()
112
        #print 'timings: ', endrun-startrun
113
        # Write the output
114
#        outputfile = open('ASE-gaussian.log')
115
#        outfile = open('ASE-gaussian.out',  'w')
116
#        for line in outputfile:
117
#            outfile.write(line)
118
#        outputfile.close()
119
#        outfile.close()
120
        # End write output
121
        self.energy = self.read_energy()
122
        if type == 'force':
123
            self.forces = self.read_forces()
124
        self.energy_zero = self.energy
125

  
126
        # Change the results object to the new results
127
        input = self.get_input_block(type)
128

  
129
    def read_energy(self,  charges=True):
130
        hartree2kcal = 627.5095
131

  
132
        outfile = open('ASE-gaussian.log')
147
    def read_output(self):
148
        outfile = open(self.str_params['filename'] + '.log')
133 149
        lines = outfile.readlines()
134 150
        outfile.close()
135 151

  
136
        chargelines = 0
137
        if charges:
138
            nats = len(self.atoms)
139
            block = ''
152
        factor = Hartree
140 153
        for line in lines:
141
            if re.search('SCF Done', line):
142
                words = line.split()
143
                energy = float(words[4]) * hartree2kcal
144
            if charges:
145
                if re.search(' Mulliken atomic charges:', line):
146
                    chargelines += 1
147
            if chargelines > 0 and chargelines <= nats+2:
148
                chargelines += 1
149
                block += line
150
   
151
        if charges:
152
            chargefile = open('charge.out', 'a')
153
            chargefile.write(block)
154
            chargefile.close()
154
            if 'SCF Done' in line:
155
                line = line.split()
156
                self.potential_energy = float(line[4]) * factor
155 157

  
156
        return energy
157

  
158
    def read_forces(self):
159 158
        factor = Hartree / Bohr
160
        factor = 1.0
161

  
162
        outfile = open('ASE-gaussian.log')
163
        lines = outfile.readlines()
164
        outfile.close()
165

  
166
        outputforces = None
167
        forces = None
168 159
        nats = len(self.atoms)
169

  
170 160
        for iline, line in enumerate(lines):
171
            if re.search('Forces \(Ha', line):
172
                forces = np.zeros((nats,  3),  float)
161
            if 'Forces (Hartrees/Bohr)' in line:
162
                self.forces = np.zeros((nats, 3), float)
173 163
                for iat in range(nats):
174
                    forceline = lines[iline+iat+3]
175
                    words = forceline.split()
176
                    for idir,  word in enumerate(words[2:5]):
177
                        forces[iat,  idir] = float(word) * factor
164
                    line = lines[iline+iat+3].split()
165
                    for idir, val in enumerate(line[2:5]):
166
                        self.forces[iat, idir] = float(val) * factor
178 167
                break
179
        return forces
180

  
181
    def get_input_block(self, type):
182
        block = ''
183
        block += '%chk=ASE-gaussian.chk\n'
184
        block += '%Mem=256MB\n'
185
        block += '%nproc=32\n'
186
        block += 'MaxDisk=16gb\n'
187

  
188
        block += '#'
189
        block += '%s'%(self.functional)
190
        block += '/'
191
        block += '%s'%(self.basis)
192

  
193
        if type == 'force':
194
            block += 'Force '
195
#        if self.r_m != None:
196
#            block += 'Guess=Read'
197

  
198
        block += '\n\nGaussian job\n\n'
199

  
200
        charge = sum(self.atoms.get_charges())
201
        block += '%i '%(charge)
202
        block += '%i '%(1)
203
        block += '\n'
204

  
205
        coords = self.atoms.get_positions()
206
        for i, at in enumerate(self.atoms.get_chemical_symbols()):
207
            block += ' %2s'%(at)
208
            coord = coords[i]
209
            block += '  %16.5f %16.5f %16.5f'%(coord[0], coord[1], coord[2])
210
            block += '\n'
211

  
212
        if self.atoms.get_pbc().any(): 
213
            cell = self.atoms.get_cell()
214
           
215
            for v in cell: 
216
                block += 'TV %8.3f %8.3f %8.3f\n'%(v[0], v[1], v[2])
217
        block += '\n'
218

  
219
        return block
220

  
221

  
222
    def get_settings(self,  filename=None):
223
        settings = GaussianSettings()
224

  
225
        if filename != None or filename == '':
226
            input = self.get_inp(filename)
227
        else:
228
            settings.set_functional("")
229
            return settings
230
    
231
        if input == None:
232
            settings.set_functional("")
233
            return settings
234

  
235
        for i, line in enumerate(input):
236
            if len(line.strip()) == 0:
237
                continue
238
            if line[0] == '#':
239
                keyline = i
240
                words = line[1:].split()
241
            else:
242
                settings.functional = words[0].split('/')[0]
243
                settings.basis = words[0].split('/')[1]
244
            break
245
            return settings
246
                
247
    def update(self,  atoms):
248
        if self.atoms_old != atoms:
249
            self.atoms = atoms.copy()
250
            self.atoms_old = atoms.copy()
251
            self.run()

Formats disponibles : Unified diff