Statistiques
| Révision :

root / ase / calculators / exciting.py @ 1

Historique | Voir | Annoter | Télécharger (4,49 ko)

1
import os
2
import numpy as np
3
from ase.units import Bohr, Hartree
4

    
5

    
6
class Exciting:
7
    """exciting calculator object."""
8
   
9
    def __init__(self, dir='.', template=None, speciespath=None,
10
                 bin='excitingser', kpts=(1, 1, 1), **kwargs):
11
        """Exciting calculator object constructor
12
        
13
        Parameters
14
        ----------
15
        dir: string
16
            directory in which to excecute exciting
17
        template: string
18
            Path to XSLT templat if it schould be used
19
            default: none
20
        bin: string
21
            Path or executable name of exciting 
22
            default: ``excitingser`` 
23
        kpts: integer list length 3
24
            Number of kpoints
25
        kwargs: dictionary like
26
            list of key value pairs to be converted into groundstate attributes
27
        
28
        """
29
        self.dir = dir
30
        self.energy = None
31
        self.template = template
32
        if speciespath is None:
33
            self.speciespath = os.environ.get('EXCITING_SPECIES_PATH', './')
34
        self.converged = False
35
        self.excitingbinary = bin
36
        self.groundstate_attributes = kwargs
37
        if not 'ngridk' in kwargs.keys():
38
            self.groundstate_attributes['ngridk'] = ' '.join(map(str, kpts))
39

    
40
    def update(self, atoms):
41
        if (not self.converged or
42
            len(self.numbers) != len(atoms) or
43
            (self.numbers != atoms.get_atomic_numbers()).any()):
44
            self.initialize(atoms)
45
            self.calculate(atoms)
46
        elif ((self.positions != atoms.get_positions()).any() or
47
              (self.pbc != atoms.get_pbc()).any() or
48
              (self.cell != atoms.get_cell()).any()):
49
            self.calculate(atoms)
50

    
51
    def initialize(self, atoms):
52
        self.numbers = atoms.get_atomic_numbers().copy()
53
        self.write(atoms)
54

    
55
    def get_potential_energy(self, atoms):
56
        self.update(atoms)
57
        return self.energy
58

    
59
    def get_forces(self, atoms):
60
        self.update(atoms)
61
        return self.forces.copy()
62

    
63
    def get_stress(self, atoms):
64
        self.update(atoms)
65
        return self.stress.copy()
66

    
67
    def calculate(self, atoms):
68
        self.positions = atoms.get_positions().copy()
69
        self.cell = atoms.get_cell().copy()
70
        self.pbc = atoms.get_pbc().copy()
71

    
72
        self.initialize(atoms)
73
        syscall = ('cd %(dir)s; %(bin)s;' %
74
                   {'dir': self.dir, 'bin': self.excitingbinary})
75
        print syscall
76
        assert os.system(syscall ) == 0
77
        self.read()
78

    
79
    def write(self, atoms):
80
        from lxml import etree as ET
81
        from ase.io.exciting import  atoms2etree
82
        if not os.path.isdir(self.dir):
83
            os.mkdir(self.dir)
84
        root = atoms2etree(atoms)
85
        root.find('structure').attrib['speciespath'] = self.speciespath
86
        root.find('structure').attrib['autormt'] = 'false'
87
        groundstate = ET.SubElement(root, 'groundstate', tforce='true')
88
        for key, value in self.groundstate_attributes.items():
89
            groundstate.attrib[key] = str(value)
90
        if self.template:
91
            xslf = open(self.template, 'r')
92
            xslt_doc = ET.parse(xslf)
93
            transform = ET.XSLT(xslt_doc)
94
            result = transform(root)
95
            fd = open('%s/input.xml' % self.dir, 'w')
96
            fd.write(ET.tostring(result, method='xml', pretty_print=True,
97
                                 xml_declaration=True, encoding='UTF-8'))
98
            fd.close()
99
        else:
100
            fd = open('%s/input.xml' % self.dir, 'w')
101
            fd.write(ET.tostring(root, method='xml', pretty_print=True,
102
                                 xml_declaration=True, encoding='UTF-8'))
103
            fd.close()
104
        
105
    def read(self):
106
        """ reads Total energy and forces from info.xml
107
        """
108
        from lxml import etree as ET
109
        INFO_file = '%s/info.xml' % self.dir
110
   
111
        try:
112
            fd = open(INFO_file)
113
        except IOError:
114
            print "file doesn't exist"
115
        info = ET.parse(fd)
116
        self.energy = float(info.xpath('//@totalEnergy')[-1]) * Hartree
117
        forces = []
118
        forcesnodes = info.xpath(
119
            '//structure[last()]/species/atom/forces/totalforce/@*')
120
        for force in forcesnodes:
121
            forces.append(np.array(float(force)))
122
        self.forces = np.reshape(forces, (-1, 3))
123
        
124
        if str(info.xpath('//groundstate/@status')[0]) == 'finished':
125
            self.converged = True
126
        else:
127
            raise RuntimeError('calculation did not finish correctly')
128
  
129
        # Stress
130
        self.stress = np.empty((3, 3))