Back to the main Index
The creation of the Abinit input file is one of the most repetive and error-prone operations
we have to perform before running our calculations.
To facilitate the creation of the input files, AbiPy provides the AbinitInput
object,
a dict-like object storing the Abinit variables and providing methods to automate
the specification of multiple parameters.
This notebook discusses how to create an AbinitInput
and how to define the parameters of the calculation.
In the last part, we introduce the MultiDataset
object that is mainly designed for the generation
of multiple inputs sharing the same structure and the same list of pseudopotentials.
In another notebook, we briefly discuss how to use factory functions to generate automatically input objects for typical calculations.
from __future__ import division, print_function, unicode_literals
import os
import warnings
warnings.filterwarnings("ignore") # to get rid of deprecation warnings
import abipy.data as abidata
import abipy.abilab as abilab
abilab.enable_notebook() # This line tells AbiPy we are running inside a notebook
from abipy.abilab import AbinitInput
# This line configures matplotlib to show figures embedded in the notebook.
# Replace `inline` with `notebook` in classic notebook
%matplotlib inline
# Option available in jupyterlab. See https://github.com/matplotlib/jupyter-matplotlib
#%matplotlib widget
To create an Abinit input, we must specify the paths of the pseudopotential files.
In this case, we have a pseudo named 14si.pspnc
located
in the abidata.pseudo_dir
directory.
inp = AbinitInput(structure=abidata.cif_file("si.cif"),
pseudos="14si.pspnc", pseudo_dir=abidata.pseudo_dir)
print(inp)
returns a string with our input.
In this case, the input is almost empty since only the structure and the pseudos have been specified.
print(inp)
############################################################################################ # STRUCTURE ############################################################################################ natom 2 ntypat 1 typat 1 1 znucl 14 xred 0.0000000000 0.0000000000 0.0000000000 0.2500000000 0.2500000000 0.2500000000 acell 1.0 1.0 1.0 rprim 6.3285005272 0.0000000000 3.6537614829 2.1095001757 5.9665675167 3.6537614829 0.0000000000 0.0000000000 7.3075229659 #<JSON> #{ # "pseudos": [ # { # "basename": "14si.pspnc", # "type": "NcAbinitPseudo", # "symbol": "Si", # "Z": 14, # "Z_val": 4.0, # "l_max": 2, # "md5": "3916b143991b1cfa1542b130be320e5e", # "filepath": "/Users/gmatteo/git_repos/abipy/abipy/data/pseudos/14si.pspnc", # "@module": "pymatgen.io.abinit.pseudos", # "@class": "NcAbinitPseudo" # } # ] #} #</JSON>
Inside the jupyter notebook, it is possible to visualize the input in HTML including the links to the official ABINIT documentation:
inp
The input has a structure:
print(inp.structure)
Full Formula (Si2) Reduced Formula: Si abc : 3.866975 3.866975 3.866975 angles: 60.000000 60.000000 60.000000 Sites (2) # SP a b c --- ---- ---- ---- ---- 0 Si 0 0 0 1 Si 0.25 0.25 0.25
and a list of Pseudo
objects:
for pseudo in inp.pseudos:
print(pseudo)
<NcAbinitPseudo: 14si.pspnc> summary: Troullier-Martins psp for element Si Thu Oct 27 17:31:21 EDT 1994 number of valence electrons: 4.0 maximum angular momentum: d angular momentum for local part: d XC correlation: LDA_XC_TETER93 supports spin-orbit: False radius for non-linear core correction: 1.80626423934776 hint for low accuracy: ecut: 0.0, pawecutdg: 0.0 hint for normal accuracy: ecut: 0.0, pawecutdg: 0.0 hint for high accuracy: ecut: 0.0, pawecutdg: 0.0
Use set_vars
to set the value of several variables with a single call:
inp.set_vars(ecut=8, paral_kgb=0)
{'ecut': 8, 'paral_kgb': 0}
AbinitInput
is a dict-like object, hence one can test for the presence of a variable in the input:
"ecut" in inp
True
To list all the variables that have been defined, use:
list(inp.keys())
['ecut', 'paral_kgb']
To access the value of a particular variable use the syntax:
inp["ecut"]
8
To iterate over keywords and values:
for varname, varvalue in inp.items():
print(varname, "-->", varvalue)
ecut --> 8 paral_kgb --> 0
Use lists, tuples or numpy arrays when Abinit expects arrays
inp.set_vars(kptopt=1,
ngkpt=[2, 2, 2],
nshiftk=2,
shiftk=[0.0, 0.0, 0.0, 0.5, 0.5, 0.5] # 2 shifts in one list
)
# It is possible to use strings but use them only for special cases such as:
inp["istwfk"] = "*1"
inp
If you mistype the name of the variable, AbinitInput
raises an error:
try:
inp.set_vars(perl=0)
except Exception as exc:
print(exc)
Cannot find variable `perl` in internal database. If you think this is not a typo, use: input.set_spell_check(False) to disable spell checking. Perhaps the internal database is not in synch with the Abinit version you are using. Please contact the AbiPy developers.
a = {"foo": "bar"}
b = a
c = a.copy()
a["hello"] = "world"
print("a dict:", a)
print("b dict:", b)
print("c dict:", c)
a dict: {'foo': 'bar', 'hello': 'world'} b dict: {'foo': 'bar', 'hello': 'world'} c dict: {'foo': 'bar'}
The set_structure
method sets the value of the ABINIT variables:
It is always a good idea to set the structure immediately after the creation of AbinitInput
because several methods use this information to facilitate the specification of other variables.
For example, the set_kpath
method uses the structure to generate the high-symmetry $k$-path for band structure calculations.
structure = dict(
ntypat=1,
natom=2,
typat=[1, 1],
znucl=14,
acell=3*[10.217],
rprim=[[0.0, 0.5, 0.5],
[0.5, 0.0, 0.5],
[0.5, 0.5, 0.0]],
xred=[[0.0 , 0.0 , 0.0],
[0.25, 0.25, 0.25]]
)
inp = AbinitInput(structure, pseudos=abidata.pseudos("14si.pspnc"))
From a CIF file:
inp.set_structure(abidata.cif_file("si.cif"))
From a Netcdf file produced by ABINIT:
inp.set_structure(abidata.ref_file("si_scf_GSR.nc"))
Supported formats include:
# https://www.materialsproject.org/materials/mp-149/
inp.set_structure(abilab.Structure.from_mpid("mp-149"))
Remember to set the PMG_MAPI_KEY
in ~/.pmgrc.yaml as described
here.
Note that you can avoid the call to set_structure
if the structure
argument is passed to AbiniInput
:
AbinitInput(structure=abidata.cif_file("si.cif"), pseudos=abidata.pseudos("14si.pspnc"))
There are two different types of sampling of the BZ: homogeneous and high-symmetry k-path. The later is mainly used for band structure calculations and requires the specification of:
whereas the homogeneous sampling is needed for all the calculations in which we have to compute integrals in the Brillouin zone e.g. total energy calculations, DOS, etc. The $k$-mesh is usually specified via:
inp = AbinitInput(structure=abidata.cif_file("si.cif"), pseudos=abidata.pseudos("14si.pspnc"))
# Set ngkpt, shiftk explicitly
inp.set_kmesh(ngkpt=(1, 2, 3), shiftk=[0.0, 0.0, 0.0, 0.5, 0.5, 0.5])
{'ngkpt': (1, 2, 3), 'kptopt': 1, 'nshiftk': 2, 'shiftk': array([[0. , 0. , 0. ], [0.5, 0.5, 0.5]])}
# Define a homogeneous k-mesh.
# nksmall is the number of divisions to be used to sample the smallest lattice vector,
# shiftk is automatically selected from an internal database.
inp.set_autokmesh(nksmall=4)
{'ngkpt': array([4, 4, 4]), 'kptopt': 1, 'nshiftk': 4, 'shiftk': array([[0.5, 0.5, 0.5], [0.5, 0. , 0. ], [0. , 0.5, 0. ], [0. , 0. , 0.5]])}
# Generate a high-symmetry k-path (taken from an internal database)
# Ten points are used to sample the smallest segment,
# the other segments are sampled so that proportions are preserved.
# A warning is issued by pymatgen about the structure not being standard.
# Be aware that this might possibly affect the automatic labelling of the boundary k-points on the k-path.
# So, check carefully the k-point labels on the figures that are produced in such case.
inp.set_kpath(ndivsm=10)
{'kptbounds': array([[0. , 0. , 0. ], [0.5 , 0. , 0.5 ], [0.5 , 0.25 , 0.75 ], [0.375, 0.375, 0.75 ], [0. , 0. , 0. ], [0.5 , 0.5 , 0.5 ], [0.625, 0.25 , 0.625], [0.5 , 0.25 , 0.75 ], [0.5 , 0.5 , 0.5 ], [0.375, 0.375, 0.75 ], [0.625, 0.25 , 0.625], [0.5 , 0. , 0.5 ]]), 'kptopt': -11, 'ndivsm': 10, 'iscf': -2}
Once the structure has been defined, one can compute the number of valence electrons with:
print("The number of valence electrons is: ", inp.num_valence_electrons)
The number of valence electrons is: 8
If we need to change a particular (scalar) variable to generate inputs for convergence studies:
# When using a non-integer step, such as 0.1, the results will often not
# be consistent. It is better to use ``linspace`` for these cases.
# See also numpy.arange and numpy.linspace
ecut_inps = inp.arange("ecut", start=2, stop=5, step=2)
print([i["ecut"] for i in ecut_inps])
[2, 4]
tsmear_inps = inp.linspace("tsmear", start=0.001, stop=0.003, num=3)
print([i["tsmear"] for i in tsmear_inps])
[0.001, 0.002, 0.003]
Once you have an AbinitInput
, you can call Abinit to get useful information
or simply to validate the input file before running the calculation.
All the method that invoke Abinit starts with the abi
prefix
followed by a verb e.g. abiget
or abivalidate
.
inp = AbinitInput(structure=abidata.cif_file("si.cif"), pseudos=abidata.pseudos("14si.pspnc"))
inp.set_vars(ecut=-2)
inp.set_autokmesh(nksmall=4)
v = inp.abivalidate()
if v.retcode != 0:
# If there is a mistake in the input, one can acces the log file of the run with the log_file object
print("".join(v.log_file.readlines()[-10:]))
src_file: m_chkinp.F90 src_line: 3736 mpi_rank: 0 message: | Checking consistency of input data against itself gave 2 inconsistencies. The details of the problems can be found above. ... abi_abort: decision taken to exit ...
Let's fix the problem with the negative ecut and rerun abivalidate!
inp["ecut"] = 2
inp["toldfe"] = 1e-10
v = inp.abivalidate()
if v.retcode == 0:
print("All ok")
else:
print(v)
All ok
At this point, we have a valid input file and we can get the k-points in the irreducible zone with:
ibz = inp.abiget_ibz()
print("number of k-points:", len(ibz.points))
print("k-points:", ibz.points)
print("weights:", ibz.weights)
print("weights are normalized to:", ibz.weights.sum())
number of k-points: 10 k-points: [[-0.125 -0.25 0. ] [-0.125 0.5 0. ] [-0.25 -0.375 0. ] [-0.125 -0.375 0.125] [-0.125 0.25 0. ] [-0.25 0.375 0. ] [-0.375 0.5 0. ] [-0.25 0.5 0.125] [-0.125 0. 0. ] [-0.375 0. 0. ]] weights: [0.09375 0.09375 0.09375 0.1875 0.09375 0.09375 0.09375 0.1875 0.03125 0.03125] weights are normalized to: 1.0
We can also call the Abinit spacegroup finder with:
abistruct = inp.abiget_spacegroup()
print("spacegroup found by Abinit:", abistruct.abi_spacegroup)
spacegroup found by Abinit: spgid: 227, num_spatial_symmetries: 48, has_timerev: True, symmorphic: True
To get the list of possible parallel configurations for this input up to 5 max_ncpus
inp["paral_kgb"] = 1
pconfs = inp.abiget_autoparal_pconfs(max_ncpus=5)
print("best efficiency:\n", pconfs.sort_by_efficiency()[0])
print("best speedup:\n", pconfs.sort_by_speedup()[0])
best efficiency: {'efficiency': 0.8525, 'mem_per_cpu': 0.0, 'mpi_ncpus': 5, 'omp_ncpus': 1, 'tot_ncpus': 5, 'vars': {'bandpp': 1, 'npband': 1, 'npfft': 1, 'npimage': 1, 'npkpt': 5, 'npspinor': 1}} best speedup: {'efficiency': 0.8525, 'mem_per_cpu': 0.0, 'mpi_ncpus': 5, 'omp_ncpus': 1, 'tot_ncpus': 5, 'vars': {'bandpp': 1, 'npband': 1, 'npfft': 1, 'npimage': 1, 'npkpt': 5, 'npspinor': 1}}
To get the list of irreducible phonon perturbations at Gamma (Abinit notation)
inp.abiget_irred_phperts(qpt=(0, 0, 0))
[{'qpt': [0.0, 0.0, 0.0], 'ipert': 1, 'idir': 1}]
Multiple datasets are handy when you have to generate several input files sharing several common
variables e.g. the crystalline structure, the value of ecut etc...
In this case, one can use the MultiDataset
object that is essentially
a list of AbinitInput
objects. Note however that Abipy
workflows do not support input files with more than one dataset.
# A MultiDataset object with two datasets (a.k.a. AbinitInput)
multi = abilab.MultiDataset(structure=abidata.cif_file("si.cif"),
pseudos="14si.pspnc", pseudo_dir=abidata.pseudo_dir, ndtset=2)
# A MultiDataset is essentially a list of AbinitInput objects
# with handy methods to perform global modifications.
# i.e. changes that will affect all the inputs in the MultiDataset
# For example:
multi.set_vars(ecut=4)
# is equivalent to
#
# for inp in multi: inp.set_vars(ecut=4)
#
# and indeed:
for inp in multi:
print(inp["ecut"])
4 4
# To change the values in a particular dataset use:
multi[0].set_vars(ngkpt=[2,2,2], tsmear=0.004)
multi[1].set_vars(ngkpt=[4,4,4], tsmear=0.008)
{'ngkpt': [4, 4, 4], 'tsmear': 0.008}
To build a table with the values of "ngkpt" and "tsmear":
multi.get_vars_dataframe("ngkpt", "tsmear")
ngkpt | tsmear | |
---|---|---|
dataset 0 | [2, 2, 2] | 0.004 |
dataset 1 | [4, 4, 4] | 0.008 |
multi
Calling set_structure on MultiDataset
will set the structure of the inputs:
multi.set_structure(abidata.cif_file("si.cif"))
# The structure attribute of a MultiDataset returns a list of structures
# equivalent to [inp.structure for inp in multi]
print(multi.structure)
[Structure Summary Lattice abc : 3.86697462 3.86697462 3.86697462 angles : 59.99999999999999 59.99999999999999 59.99999999999999 volume : 40.88829179346891 A : 3.3488982567096763 0.0 1.9334873100000005 B : 1.1162994189032256 3.1573715557642927 1.9334873100000005 C : 0.0 0.0 3.86697462 PeriodicSite: Si (0.0000, 0.0000, 0.0000) [0.0000, 0.0000, 0.0000] PeriodicSite: Si (1.1163, 0.7893, 1.9335) [0.2500, 0.2500, 0.2500], Structure Summary Lattice abc : 3.86697462 3.86697462 3.86697462 angles : 59.99999999999999 59.99999999999999 59.99999999999999 volume : 40.88829179346891 A : 3.3488982567096763 0.0 1.9334873100000005 B : 1.1162994189032256 3.1573715557642927 1.9334873100000005 C : 0.0 0.0 3.86697462 PeriodicSite: Si (0.0000, 0.0000, 0.0000) [0.0000, 0.0000, 0.0000] PeriodicSite: Si (1.1163, 0.7893, 1.9335) [0.2500, 0.2500, 0.2500]]
The function split_datasets
return the list of AbinitInput
stored in MultiDataset
inp0, inp1 = multi.split_datasets()
inp0
print("Number of datasets:", multi.ndtset)
Number of datasets: 2
# To create and append a new dataset (initialized from dataset number 1)
multi.addnew_from(1)
multi[-1].set_vars(ecut=42)
print("Now multi has", multi.ndtset, "datasets and the ecut in the last dataset is:",
multi[-1]["ecut"])
Now multi has 3 datasets and the ecut in the last dataset is: 42
Back to the main Index