Source code for pychemia.code.vasp.incar
from __future__ import unicode_literals
from builtins import str
import math
import os
import re
from collections import MutableMapping
from numbers import Number
from pychemia.utils.computing import deep_unicode
import numpy as np
__author__ = "Guillermo Avendano-Franco"
__copyright__ = "Copyright 2016"
__version__ = "0.1"
__maintainer__ = "Guillermo Avendano-Franco"
__email__ = "gtux.gaf@gmail.com"
__status__ = "Development"
__date__ = "May 13, 2016"
[docs]def read_incar(filename='INCAR'):
"""
Load the file INCAR in the directory 'path' or
read directly the file 'path' and return an object
'inputvars' for pychemia
:param filename: (str) Filename of a INCAR file format
:return:
"""
if os.path.isfile(filename):
filename = filename
elif os.path.isdir(filename) and os.path.isfile(filename + '/INCAR'):
filename += '/INCAR'
else:
raise ValueError('[ERROR] INCAR path not found: %s' % filename)
iv = InputVariables(filename=filename)
return iv
[docs]def write_incar(iv, filepath='INCAR'):
"""
Takes an object inputvars from pychemia and
save the file INCAR in the directory 'path' or
save the file 'path' as a VASP INCAR file
:param iv: (InputVariables) VASP Input variables
:param filepath: (str) File path to write the INCAR file
"""
if os.path.isdir(filepath):
filename = filepath + '/INCAR'
else:
filename = filepath
iv.write(filename)
[docs]class InputVariables(MutableMapping):
"""
VASP INCAR object
It contains:
data:
variables = Dictionary whose keys are ABINIT variable names
and contains the values as numpy arrays
methods:
write = Write the input into as a text file that ABINIT
can use as an input file
get_value = Get the value of a particular variable
set_value = Set the value of a particular variable
"""
def __init__(self, filename=None, variables=None):
if variables is not None:
for i in variables:
self.__dict__[i] = variables[i]
if filename is not None and os.path.isfile(filename):
try:
self.__import_input(filename)
except ValueError:
print('File format not identified')
def _clean_variables(self):
for i in self.__dict__:
value = self.__dict__[i]
if isinstance(value, list):
if len(value) == 1:
self.__dict__[i] = value[0]
def __import_input(self, filename):
rf = open(filename, 'r')
for line in rf.readlines():
line = line.partition('#')[0]
line = line.rstrip()
if '=' in line:
varname = line.split('=')[0].strip().upper()
value = line.split('=')[1].strip()
if value[-1] == ';':
value = value[:-1]
if varname == '$SYSTEM':
self.__dict__[varname] = value
elif value.upper() == '.FALSE.':
self.__dict__[varname] = False
elif value.upper() == '.TRUE.':
self.__dict__[varname] = True
else:
try:
self.__dict__[varname] = int(value)
except ValueError:
try:
self.__dict__[varname] = round(float(value), 10)
except ValueError:
self.__dict__[varname] = self._deep_parsing(value)
rf.close()
self._clean_variables()
@staticmethod
def _deep_parsing(value):
# Try splitting
val_splt = value.split()
ret = []
if len(val_splt) == 1:
ret = value
else:
for i in range(len(val_splt)):
try:
newval = [int(val_splt[i])]
except ValueError:
try:
newval = [round(float(val_splt[i]), 10)]
except ValueError:
if '*' in val_splt[i] and len(val_splt[i].split('*')) == 2:
# print 'Trying this', val_splt[i]
newval = int(val_splt[i].split('*')[0]) * [float(val_splt[i].split('*')[1])]
else:
newval = [val_splt[i]]
ret += newval
return ret
[docs] def write(self, filename='INCAR'):
"""
Write an inputvars object into a text
file that VASP can use as an INCAR
file
Args:
filename:
The path to 'INCAR' filename that will be written
"""
wf = open(filename, 'w')
wf.write(self.__str__())
wf.close()
[docs] def write_key(self, varname):
"""
Receives an input variable and write their contents
properly according with their kind and length
Args:
varname:
The name of the input variable
"""
ret = (varname.ljust(15)) + " = "
if varname not in self.__dict__:
raise ValueError("[ERROR] input variable: '%s' is not declared" % varname)
value = self.__dict__[varname]
value = deep_unicode(value)
if isinstance(value, bool):
if value:
ret += '.TRUE.'
else:
ret += '.FALSE.'
elif isinstance(value, Number):
ret += str(value)
elif isinstance(value, str):
ret += value
else:
# Assume that the variables are integer and test if such assumption
# is true
integer = True
real = False
string = False
compact = True
# Get the general kind of values for the input variable
for j in self.__dict__[varname]:
try:
if not float(j).is_integer():
# This is the case of non integer values
integer = False
real = True
string = False
if len(str(float(j))) > 7:
compact = False
except ValueError:
# This is the case of '*1' that could not
# be converted because we do not know the size
# of the array
integer = False
real = False
string = True
if len(self.__dict__[varname]) > 1:
print(varname)
tmp = [(self.__dict__[varname][0], 1)]
prev = self.__dict__[varname][0]
for i in self.__dict__[varname][1:]:
if i == prev:
tmp[-1] = (i, tmp[-1][1] + 1)
else:
tmp.append((i, 1))
prev = i
counter = 0
for j in tmp:
if j[1] > 3:
if real:
if compact:
ret += (" %d*%g" % (j[1] - j[1] % 3, j[0])).rjust(8)
else:
ret += " %d*%g" % (j[1] - j[1] % 3, j[0])
elif integer:
ret += " %d*%d" % (j[1] - j[1] % 3, j[0])
else:
ret += " %d*%s" % (j[1] - j[1] % 3, j[0])
if j[1] % 3 != 0:
for i in range(j[1] % 3):
if real:
if compact:
ret += (" %g" % j[0]).rjust(8)
else:
ret += " %17.10e" % j[0]
elif integer:
ret += " %d" % j[0]
else:
ret += " %s" % j[0]
counter += j[1]
else:
for i in range(j[1]):
if real:
if compact:
ret += (" %g" % j[0]).rjust(8)
else:
ret += " %17.10e" % j[0]
elif integer:
ret += " %d" % j[0]
elif string:
ret += " %s" % j[0]
counter += 1
ret += ";\n"
return ret
[docs] def set_encut(self, ENCUT=300, POTCAR=None):
self.__dict__['ENCUT'] = ENCUT
if POTCAR is not None and ENCUT < 10:
maxvalue = 0
if not os.path.isfile(POTCAR):
raise ValueError('Not such file', POTCAR)
rf = open(POTCAR)
for line in rf.readlines():
if 'ENMAX' in line:
list4line = line.split()
assert (list4line[0].strip() == 'ENMAX')
value = list4line[2].strip()
if value[-1] == ';':
value = value[:-1]
value = float(value)
if value > maxvalue:
maxvalue = value
rf.close()
if ENCUT < 10:
self.__dict__['ENCUT'] = int(math.ceil(ENCUT * maxvalue))
else:
self.__dict__['ENCUT'] = maxvalue
# pcm_log.debug('ENCUT: %7.3f' % self.variables['ENCUT'])
[docs] def set_ismear(self, kpoints):
if np.prod(kpoints.grid) > 27:
self.__dict__['ISMEAR'] = -5
else:
self.__dict__['ISMEAR'] = 0
[docs] def set_ion_relax(self, NSW=50, ISIF=2, IBRION=2, EDIFFG=-1E-3):
self.__dict__['IBRION'] = IBRION
self.__dict__['NSW'] = NSW
self.__dict__['ISIF'] = ISIF
self.__dict__['EDIFFG'] = EDIFFG
[docs] def set_electron_scf(self, NELM=60, NELMIN=2, EDIFF=1E-4, IALGO=48):
self.__dict__['NELMIN'] = NELMIN
self.__dict__['NELM'] = NELM
self.__dict__['EDIFF'] = EDIFF
self.__dict__['IALGO'] = IALGO
[docs] def set_rough_relaxation(self):
self.set_minimum(PREC='Normal', ISPIN=1, LREAL=False, ISMEAR=0, LORBIT=11)
self.set_electron_scf(NELM=60, NELMIN=2, EDIFF=1E-4, IALGO=48)
self.set_ion_relax(NSW=50, ISIF=2, IBRION=2, EDIFFG=-1E-2)
self.__dict__['NPAR'] = 2
[docs] def set_mit_settings(self):
self.set_minimum(PREC='Accurate', ISPIN=2, LREAL=False, ISMEAR=-5, LORBIT=11)
self.set_electron_scf(NELM=100, NELMIN=6, EDIFF=5E-5, IALGO=48)
self.set_ion_relax(NSW=99, ISIF=3, IBRION=2, EDIFFG=-1E-3)
self.__dict__['LWAVE'] = False
self.__dict__['SIGMA'] = 0.05
self.__dict__['LDAU'] = True
self.__dict__['LDAUTYPE'] = 2
self.__dict__['ICHARG'] = 1
[docs] def set_minimum(self, PREC='Normal', ISPIN=2, LREAL=False, ISMEAR=0, LORBIT=11):
self.__dict__['PREC'] = PREC
if LREAL is not None:
self.__dict__['LREAL'] = LREAL
else:
self.__dict__['LREAL'] = 'Auto'
self.__dict__['ISMEAR'] = ISMEAR
self.__dict__['ISPIN'] = ISPIN
self.__dict__['LORBIT'] = LORBIT
self.__dict__['NPAR'] = 2
self.__dict__['LASPH'] = True
# The next five methods are requirements of the ABC.
def __setitem__(self, key, value):
self.__dict__[key] = value
def __getitem__(self, key):
return self.__dict__[key]
def __delitem__(self, key):
del self.__dict__[key]
def __iter__(self):
return iter(self.__dict__)
def __len__(self):
return len(self.__dict__)
# The final two methods aren't required, but nice for demo purposes:
def __str__(self):
"""
returns simple dict representation of the mapping
"""
ret = ''
for i in sorted(self.__dict__.keys()):
ret += self.write_key(i)
return ret
def __repr__(self):
"""
echoes class, id, & reproducible representation in the REPL
"""
return '{}, {}(variables={})'.format(super(self.__class__, self).__repr__(),
self.__class__.__name__,
self.__dict__)