Structure object#

The AbiPy structure inherits from the pymatgen structure. One has therefore access to all the methods and tools already available in pymatgen. In this notebook, we mainly focus on the extensions added by AbiPy. For the features provided by pymatgen, please consult the official pymatgen documentation.

abipy.core.structure.Structure and pymatgen.core.structure.Structure

import warnings
warnings.filterwarnings("ignore") # to get rid of deprecation warnings

# Import abipy modules
from abipy import abilab
from abipy.abilab import Structure
import abipy.data as abidata

# Useful tools we'll need later on.
from pprint import pprint
import numpy as np

# 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

Reading a structure from file#

It is possible to initialize a structure object from different file formats:

  • CIF

  • POSCAR/CONTCAR

  • CHGCAR

  • LOCPOT,

  • vasprun.xml

  • CSSR

  • ABINIT Netcdf files

  • pymatgen’s JSON serialized structures

Note, in particular, that one can initialize the structure from the netcdf files produced by Abinit (GSR.nc, WFK.nc, etc) as well as output files in text format such as the Abinit input/output files or even the DDB file.

To initialize the structure from a CIF file use the from_file method:

structure = Structure.from_file(abidata.cif_file("si.cif"))
print(structure)
Full Formula (Si2)
Reduced Formula: Si
abc   :   3.866975   3.866975   3.866975
angles:  60.000000  60.000000  60.000000
pbc   :       True       True       True
Sites (2)
  #  SP       a     b     c
---  ----  ----  ----  ----
  0  Si    0     0     0
  1  Si    0.25  0.25  0.25

Note

abidata.cif_file, abidata.ref_file and abidata.pseudos are helper function returning the absolute path of one of the reference files shipped with the AbiPy package. In your case, you can directly pass a string or a list of strings with the path to your files.

To read the structure from an Abinit netcdf file, use:

structure = Structure.from_file(abidata.ref_file("si_nscf_GSR.nc"))

print(structure.to_string(verbose=1))  # Use to_string with verbose > 0 to get more info
Full Formula (Si2)
Reduced Formula: Si
abc   :   3.866975   3.866975   3.866975
angles:  60.000000  60.000000  60.000000

Spglib space group info (magnetic symmetries not taken into account).
Spacegroup: Fd-3m (227), Hall: F 4d 2 3 -1d, Abinit spg_number: 227
Crystal_system: cubic, Lattice_type: cubic, Point_group: m-3m

  Idx  Symbol    Reduced_Coords              Wyckoff      EqIdx
-----  --------  --------------------------  ---------  -------
    0  Si        +0.00000 +0.00000 +0.00000  (2a)             0
    1  Si        +0.25000 +0.25000 +0.25000  (2a)             0

Abinit Spacegroup: spgid: 227, num_spatial_symmetries: 48, has_timerev: True, symmorphic: True
/usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/spglib/spglib.py:115: DeprecationWarning: dict interface (SpglibDataset['wyckoffs']) is deprecated.Use attribute interface ({self.__class__.__name__}.{key}) instead
  warnings.warn(
/usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/spglib/spglib.py:115: DeprecationWarning: dict interface (SpglibDataset['equivalent_atoms']) is deprecated.Use attribute interface ({self.__class__.__name__}.{key}) instead
  warnings.warn(

Use to_abivars to get a python dictionary with the list of Abinit variables.

structure.to_abivars()
{'natom': 2,
 'ntypat': 1,
 'typat': array([1, 1]),
 'znucl': [14],
 'xred': array([[0.  , 0.  , 0.  ],
        [0.25, 0.25, 0.25]]),
 'acell': [1.0, 1.0, 1.0],
 'rprim': array([[6.32850055, 0.        , 3.6537615 ],
        [2.10950018, 5.96656754, 3.6537615 ],
        [0.        , 0.        , 7.30752299]])}

and the abi_string property to get a string that can be used directly in the input file:

print(structure.abi_string)
 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.3285005521    0.0000000000    3.6537614973
    2.1095001840    5.9665675402    3.6537614973
    0.0000000000    0.0000000000    7.3075229946

To visualize the structure with matplotlib, use:

structure.plot();
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/IPython/core/formatters.py:343, in BaseFormatter.__call__(self, obj)
    341     pass
    342 else:
--> 343     return printer(obj)
    344 # Finally look for special method names
    345 method = get_real_method(obj, self.print_method)

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/IPython/core/pylabtools.py:170, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
    167     from matplotlib.backend_bases import FigureCanvasBase
    168     FigureCanvasBase(fig)
--> 170 fig.canvas.print_figure(bytes_io, **kw)
    171 data = bytes_io.getvalue()
    172 if fmt == 'svg':

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/matplotlib/backend_bases.py:2175, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2172     # we do this instead of `self.figure.draw_without_rendering`
   2173     # so that we can inject the orientation
   2174     with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2175         self.figure.draw(renderer)
   2176 if bbox_inches:
   2177     if bbox_inches == "tight":

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
     93 @wraps(draw)
     94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95     result = draw(artist, renderer, *args, **kwargs)
     96     if renderer._rasterizing:
     97         renderer.stop_rasterizing()

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     69     if artist.get_agg_filter() is not None:
     70         renderer.start_filter()
---> 72     return draw(artist, renderer)
     73 finally:
     74     if artist.get_agg_filter() is not None:

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/matplotlib/figure.py:3162, in Figure.draw(self, renderer)
   3159             # ValueError can occur when resizing a window.
   3161     self.patch.draw(renderer)
-> 3162     mimage._draw_list_compositing_images(
   3163         renderer, self, artists, self.suppressComposite)
   3165     renderer.close_group('figure')
   3166 finally:

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    130 if not_composite or not has_images:
    131     for a in artists:
--> 132         a.draw(renderer)
    133 else:
    134     # Composite any adjacent images together
    135     image_group = []

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     69     if artist.get_agg_filter() is not None:
     70         renderer.start_filter()
---> 72     return draw(artist, renderer)
     73 finally:
     74     if artist.get_agg_filter() is not None:

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/mpl_toolkits/mplot3d/axes3d.py:441, in Axes3D.draw(self, renderer)
    437 zorder_offset = max(axis.get_zorder()
    438                     for axis in self._axis_map.values()) + 1
    439 collection_zorder = patch_zorder = zorder_offset
--> 441 for artist in sorted(collections_and_patches,
    442                      key=lambda artist: artist.do_3d_projection(),
    443                      reverse=True):
    444     if isinstance(artist, mcoll.Collection):
    445         artist.zorder = collection_zorder

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/mpl_toolkits/mplot3d/axes3d.py:442, in Axes3D.draw.<locals>.<lambda>(artist)
    437 zorder_offset = max(axis.get_zorder()
    438                     for axis in self._axis_map.values()) + 1
    439 collection_zorder = patch_zorder = zorder_offset
    441 for artist in sorted(collections_and_patches,
--> 442                      key=lambda artist: artist.do_3d_projection(),
    443                      reverse=True):
    444     if isinstance(artist, mcoll.Collection):
    445         artist.zorder = collection_zorder

AttributeError: 'Arrow3D' object has no attribute 'do_3d_projection'
<Figure size 640x480 with 1 Axes>

The matplotlib version is minimalistic but it plays well with jupyter notebooks. For a more advanced visualization we suggest using a specialized graphical applications. Fortunately, one can invoke external applications directly from AbiPy with e.g.

# structure.visualize("vesta")

provided VESTA is already installed on your machine and the binary can be found in $PATH.

To get a structure from the materials project database, use:

# Remember to set the env variable PMG_MAPI_KEY in your ~/.pmgrc.yaml files.
si2_mp = Structure.from_mpid("mp-149")
print(si2_mp)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[8], line 2
      1 # Remember to set the env variable PMG_MAPI_KEY in your ~/.pmgrc.yaml files.
----> 2 si2_mp = Structure.from_mpid("mp-149")
      3 print(si2_mp)

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/abipy/core/structure.py:387, in Structure.from_mpid(cls, material_id)
    385 # Get pytmatgen structure and convert it to abipy structure
    386 from abipy.core import restapi
--> 387 with restapi.get_mprester() as rest:
    388     new = rest.get_structure_by_material_id(material_id)
    389     return cls.as_structure(new)

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/abipy/core/restapi.py:33, in get_mprester()
     29 def get_mprester():
     30     """
     31     Args:
     32     """
---> 33     rester = MPRester()
     34     #print(f"{type(rester)=}")
     35     return rester

File /usr/share/miniconda/envs/abipy/lib/python3.12/site-packages/pymatgen/ext/matproj.py:414, in MPRester.__new__(cls, *args, **kwargs)
    411     kwargs["api_key"] = api_key
    413 if not api_key:
--> 414     raise ValueError("Please supply an API key. See https://materialsproject.org/api for details.")
    416 if len(api_key) != 32:
    417     from pymatgen.ext.matproj_legacy import _MPResterLegacy

ValueError: Please supply an API key. See https://materialsproject.org/api for details.

In some cases, we have multiple structures and we need to compare the lattice parameters. Use dataframes_from_structures to build a pandas DataFrame:

dfs = abilab.dataframes_from_structures([structure, si2_mp], index=["CIF", "MP"])

then we can compare the lattice parameters with:

dfs.lattice

Note that all AbiPy robots have this feature built-in. Sometimes it is much easier to build a robot directly from files and then compare the structures with e.g. robot.get_lattice_dataframe().

Converting to other formats#

Use structure.convert(format) to get the string representation in the new format:

for fmt in ("cif", "POSCAR", "qe"):
    print((" Abinit --> %s " % fmt).center(80, "*"))
    print(structure.convert(fmt=fmt))

Getting info on the structure#

print(structure.reciprocal_lattice)
structure.reciprocal_lattice.matrix.T @ structure.lattice.matrix / (2 * np.pi)
# List of high-symmetry k-points.
print(structure.hsym_kpoints)

The method calc_ksampling allows one to get an efficient sampling of the Brillouin zone by just specifying the number of divisions to be used for the smallest lattice vector of the reciprocal lattice:

pprint(structure.calc_ksampling(nksmall=10))

To get the recommended high symmetry \(k\)-path in reduced coordinates:

structure.calc_kptbounds()

The high-symmetry q-path is automatically selected assuming the structure fulfills the convention described in Setyawan2010

To visualize the Brillouin zone with matplotlib, use:

structure.plot_bz();

For the plotly version, use:

structure.plotly_bz();

Note

The name of the plotly method (if implemented) is obtained by replacing the plot verb with plotly.

To get the number of valence electrons for a given set of pseudopotentials:

structure.num_valence_electrons(pseudos=abidata.pseudos("14si.pspnc"))

To visualize the X-ray diffraction plot with pymatgen XRDCalculator, use:

structure.plot_xrd();

The abistruct.py script#

The abistruct.py script provides a handy command line interface to operate on structure objects constructed from external files. There are several options available as well an interface to the and the COD database.

To obtain the list of available commands, use:

!abistruct.py --help

Creating a GUI inside a notebook#

Several AbiPy objects provide a get_panel method that allows one to create a panel GUI exposing some of the underlying AbiPy methods. Similar capabilities are also available via the AbiPy GUI web app.

To build a panel GUI for a given structure use:

abilab.abipanel()
structure.get_panel()