Statistiques
| Révision :

root / ase / calculators / qmx.py @ 16

Historique | Voir | Annoter | Télécharger (6,23 ko)

1
"""
2
This module is the EMBED module for ASE
3
implemented by T. Kerber
4

5
Torsten Kerber, ENS LYON: 2011, 07, 11
6

7
This work is supported by Award No. UK-C0017, made by King Abdullah
8
University of Science and Technology (KAUST)
9
"""
10
import ase
11
import ase.atoms
12
import numpy as np
13
from general import Calculator
14
from ase.embed import Embed
15
from ase.units import Hartree
16

    
17
from copy import deepcopy
18

    
19
import sys, os
20

    
21
class Qmx(Calculator):
22
    def __init__(self, calculator_high_cluster,  calculator_low_cluster,  calculator_low_system=None,  print_forces=False):
23
        self._constraints=None
24
        
25
        self.calculator_low_cluster = calculator_low_cluster
26
        self.calculator_low_system = calculator_low_system
27
        if self.calculator_low_system is None:
28
            self.calculator_low_system = deepcopy(calculator_low_cluster)
29
        self.calculator_high_cluster = calculator_high_cluster
30
        self.print_forces = print_forces
31

    
32
    def get_energy_subsystem(self, path, calculator, atoms, force_consistent):
33
        # writing output
34
        line = "running energy in: " + path + "\n"
35
        sys.stderr.write(line)
36
        # go to directory and calculate energies
37
        os.chdir(path)
38
        atoms.set_calculator(calculator)
39
        energy = atoms.get_potential_energy()
40
        # return path and result
41
        os.chdir("..")
42
        return energy
43

    
44
    def get_forces_subsystem(self, path, calculator, atoms):
45
        # writing output
46
        line = "running forces in: " + path + "\n"
47
        sys.stderr.write(line)
48
        # go to directory and calculate forces
49
        os.chdir(path)
50
        atoms.set_calculator(calculator)
51
        forces = atoms.get_forces()
52
        # return path and result
53
        os.chdir("..")
54
        return forces
55

    
56
    def get_potential_energy(self, embed, force_consistent=False):
57
        # perform energy calculations
58
        e_sys_lo = self.get_energy_subsystem("system.low-level", self.calculator_low_system, embed.get_system(), force_consistent)
59
        e_cl_lo  = self.get_energy_subsystem("cluster.low-level", self.calculator_low_cluster, embed.get_cluster(), force_consistent)
60
        e_cl_hi  = self.get_energy_subsystem("cluster.high-level", self.calculator_high_cluster, embed.get_cluster(), force_consistent)
61
        # calculate energies
62
        energy = e_sys_lo - e_cl_lo + e_cl_hi
63
        # print energies
64
        print "%20s = %15s - %15s + %15s" %("E(C:S)", "E(S,LL)", "E(C,LL)", "E(C,HL)")
65
        print "%20f = %15f - %15f + %15f" %(energy, e_sys_lo, e_cl_lo, e_cl_hi)
66
        # set energies and return
67
        if force_consistent:
68
            self.energy_free = energy
69
            return self.energy_free
70
        else:
71
            self.energy_zero = energy
72
            return self.energy_zero
73

    
74
    def get_forces(self, embed):
75
        atom_map_sys_cl = embed.atom_map_sys_cl
76
        # get forces for the three systems
77
        f_sys_lo = self.get_forces_subsystem("system.low-level", self.calculator_low_system, embed.get_system())
78
        f_cl_lo  = self.get_forces_subsystem("cluster.low-level", self.calculator_low_cluster, embed.get_cluster())
79
        f_cl_hi  = self.get_forces_subsystem("cluster.high-level", self.calculator_high_cluster, embed.get_cluster())
80

    
81
        # forces correction for the atoms
82
        f_cl = f_cl_hi - f_cl_lo
83

    
84
        if self.print_forces:
85
            cluster=embed.get_cluster()
86
            print "Forces: System LOW - Cluster LOW + Cluster HIGH"
87
            for iat_sys in xrange(len(embed)):
88
                print "%-2s (" % embed[iat_sys].get_symbol(),
89
                for idir in xrange(3):
90
                    print "%10.6f" % f_sys_lo[iat_sys][idir], 
91
                print ") <system LOW>"
92

    
93
                iat_cl = atom_map_sys_cl[iat_sys]
94
                if iat_cl > -1:
95
                    print "%s" % "-  (",
96
                    for idir in xrange(3):
97
                        print "%10.6f" % f_cl_lo[iat_cl][idir], 
98
                    print ") <cluster LOW>"
99
                    print "%s" % "+  (",
100
                    for idir in xrange(3):
101
                        print "%10.6f" % f_cl_hi[iat_cl][idir], 
102
                    print ") <cluster HIGH>"
103
                print
104
            print
105
    
106
        # lo-sys + (hi-lo)
107
        for iat_sys in xrange(len(embed)):
108
            iat_cl = atom_map_sys_cl[iat_sys]
109
            if iat_cl > -1:
110
                f_sys_lo[iat_sys] += f_cl[iat_cl]
111
        # some settings for the output
112
        i_change = np.zeros(len(embed), int)
113
        if self.print_forces:
114
            f_sys_lo_orig = f_sys_lo.copy()
115
        # correct gradients
116
        # Reference: Eichler, Koelmel, Sauer, J. of Comput. Chem., 18(4). 1997, 463-477.
117
        for cell_L, iat_cl_sys, iat_sys, r, iat_link in embed.linkatoms:
118
            # calculate the bond distance (r_bond) at the border
119
            xyz = embed[iat_sys].get_position() - embed[iat_cl_sys].get_position() + cell_L
120
            # calculate the bond lenght and the factor f
121
            rbond = np.sqrt(np.dot(xyz, xyz))
122
            f = r / rbond
123
            #normalize xyz
124
            xyz /= rbond
125
            # receive the gradients for the link atom
126
            fL = f_cl[iat_link]
127
            # dot product fL, xyz
128
            fs = np.dot(fL, xyz)
129
            # apply corrections for each direction
130
            i_change[iat_sys] = 1
131
            i_change[iat_cl_sys] = 1
132
            for idir in xrange(3):
133
                # correct the atom in the system
134
                f_sys_lo[iat_sys][idir] = f_sys_lo[iat_sys][idir] + f*fL[idir] - f*fs*xyz[idir]
135
                # correct the atom in the cluster
136
                f_sys_lo[iat_cl_sys][idir] = f_sys_lo[iat_cl_sys][idir] + (1-f)*fL[idir] + f*fs*xyz[idir]
137

    
138
        if self.print_forces:
139
            print " TOTAL FORCE (uncorrected : corrected) for link atoms"
140
            for iat_sys in xrange(len(embed)):
141
                print "%-2s (" % embed[iat_sys].get_symbol(),
142
                for idir in xrange(3):
143
                    print "%10.6f" % f_sys_lo_orig[iat_sys][idir], 
144
                print ") : (", 
145
                for idir in xrange(3):
146
                    print "%10.6f" % f_sys_lo[iat_sys][idir], 
147
                print ")", 
148
                if i_change[iat_sys]:
149
                    print " *", 
150
                print
151
            print
152
        
153
        return f_sys_lo
154

    
155
    def get_stress(self, atoms):
156
            return None