root / ase / calculators / exciting.py @ 13
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)) |