mirror of https://github.com/abinit/abipy.git
634 lines
19 KiB
Python
634 lines
19 KiB
Python
# coding: utf-8
|
|
"""This module ..."""
|
|
from __future__ import print_function, division, unicode_literals, absolute_import
|
|
|
|
import abc
|
|
import os
|
|
import six
|
|
import collections
|
|
import tempfile
|
|
import pickle
|
|
|
|
from time import ctime
|
|
from monty.os.path import which
|
|
from monty.termcolor import cprint
|
|
from monty.dev import deprecated
|
|
from monty.string import is_string
|
|
from monty.functools import lazy_property
|
|
from abipy.flowtk.netcdf import NetcdfReader, NO_DEFAULT
|
|
|
|
|
|
__all__ = [
|
|
"AbinitNcFile",
|
|
"Has_Structure",
|
|
"Has_ElectronBands",
|
|
"Has_PhononBands",
|
|
"NotebookWriter",
|
|
"Has_Header",
|
|
]
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class _File(object):
|
|
"""
|
|
Abstract base class defining the methods that must be implemented
|
|
by the concrete classes representing the different files produced by ABINIT.
|
|
"""
|
|
def __init__(self, filepath):
|
|
self._filepath = os.path.abspath(filepath)
|
|
|
|
# Save stat values
|
|
stat = os.stat(filepath)
|
|
self._last_atime = stat.st_atime
|
|
self._last_mtime = stat.st_mtime
|
|
self._last_ctime = stat.st_ctime
|
|
|
|
def __repr__(self):
|
|
return "<%s, %s>" % (self.__class__.__name__, self.relpath)
|
|
|
|
@classmethod
|
|
def from_file(cls, filepath):
|
|
"""Initialize the object from a string."""
|
|
if isinstance(filepath, cls):
|
|
return filepath
|
|
|
|
#print("Perhaps the subclass", cls, "must redefine the classmethod from_file.")
|
|
return cls(filepath)
|
|
|
|
@property
|
|
def filepath(self):
|
|
"""Absolute path of the file."""
|
|
return self._filepath
|
|
|
|
@property
|
|
def relpath(self):
|
|
"""Relative path."""
|
|
try:
|
|
return os.path.relpath(self.filepath)
|
|
except OSError:
|
|
# current working directory may not be defined!
|
|
return self.filepath
|
|
|
|
@property
|
|
def basename(self):
|
|
"""Basename of the file."""
|
|
return os.path.basename(self.filepath)
|
|
|
|
@property
|
|
def filetype(self):
|
|
"""String defining the filetype."""
|
|
return self.__class__.__name__
|
|
|
|
def filestat(self, as_string=False):
|
|
"""
|
|
Dictionary with file metadata
|
|
if ``as_string`` is True, a string is returned.
|
|
"""
|
|
d = get_filestat(self.filepath)
|
|
if not as_string: return d
|
|
return "\n".join("%s: %s" % (k, v) for k, v in d.items())
|
|
|
|
@abc.abstractmethod
|
|
def close(self):
|
|
"""Close file."""
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
"""Activated at the end of the with statement. It automatically closes the file."""
|
|
self.close()
|
|
|
|
#def __del__(self):
|
|
# """
|
|
# Called when the instance is about to be destroyed.
|
|
# """
|
|
# try:
|
|
# self.close()
|
|
# finally:
|
|
# super(_File, self).__close__(self)
|
|
|
|
|
|
class TextFile(_File):
|
|
|
|
#@classmethood
|
|
#def from_string(cls, s):
|
|
# return cls.from_file(filepath)
|
|
|
|
def __enter__(self):
|
|
# Open the file
|
|
self._file
|
|
return self
|
|
|
|
def __iter__(self):
|
|
return iter(self._file)
|
|
|
|
@lazy_property
|
|
def _file(self):
|
|
"""File object open in read-only mode."""
|
|
return open(self.filepath, mode="rt")
|
|
|
|
def close(self):
|
|
"""Close the file."""
|
|
try:
|
|
self._file.close()
|
|
except Exception:
|
|
pass
|
|
|
|
def seek(self, offset, whence=0):
|
|
"""Set the file's current position, like stdio's fseek()."""
|
|
self._file.seek(offset, whence)
|
|
|
|
|
|
@deprecated(message="AbinitOutNcFile is deprecated, use abipy.abio.outputs.OutNcFile")
|
|
class AbinitOutNcFile(NetcdfReader):
|
|
"""
|
|
Class representing the _OUT.nc file.
|
|
"""
|
|
|
|
def get_vars(self, vars, strict=False):
|
|
# TODO: add a check on the variable names ?
|
|
default = NO_DEFAULT if strict else None
|
|
var_values = {}
|
|
for var in vars:
|
|
var_values[var] = self.read_value(varname=var, default=default)
|
|
return var_values
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class AbinitNcFile(_File):
|
|
"""
|
|
Abstract class representing a Netcdf file with data saved
|
|
according to the ETSF-IO specifications (when available).
|
|
An AbinitNcFile has a netcdf reader to read data from file and build objects.
|
|
"""
|
|
def ncdump(self, *nc_args, **nc_kwargs):
|
|
"""Returns a string with the output of ncdump."""
|
|
return NcDumper(*nc_args, **nc_kwargs).dump(self.filepath)
|
|
|
|
@lazy_property
|
|
def abinit_version(self):
|
|
"""String with abinit version: three digits separated by comma."""
|
|
return self.reader.rootgrp.getncattr("abinit_version")
|
|
|
|
@abc.abstractproperty
|
|
def params(self):
|
|
"""
|
|
:class:`OrderedDict` with the convergence parameters
|
|
Used to construct |pandas-DataFrames|.
|
|
"""
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class AbinitFortranFile(_File):
|
|
"""
|
|
Abstract class representing a fortran file containing output data from abinit.
|
|
"""
|
|
def close(self):
|
|
pass
|
|
|
|
|
|
class CubeFile(_File):
|
|
"""
|
|
|
|
.. attribute:: structure
|
|
|
|
|Structure| object
|
|
|
|
.. attribute:: mesh
|
|
|
|
|Mesh3d| object with information on the uniform 3d mesh.
|
|
|
|
.. attribute:: data
|
|
|
|
|numpy-array| of shape [nx, ny, nz] with numerical values on the real-space mesh.
|
|
"""
|
|
def __init__(self, filepath):
|
|
from abipy.iotools.cube import cube_read_structure_mesh_data
|
|
super(CubeFile, self).__init__(filepath)
|
|
self.structure, self.mesh, self.data = cube_read_structure_mesh_data(self.filepath)
|
|
|
|
def close(self):
|
|
"""nop, just to fulfill the abstract interface."""
|
|
|
|
#@classmethod
|
|
#def write_structure_mesh_data(cls, path, structure, mesh, data):
|
|
# with open(path, "wt") as fh:
|
|
# cube_write_structure_mesh(fh, structure, mesh)
|
|
# cube_write_data(fh, data, mesh):
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class Has_Structure(object):
|
|
"""Mixin class for :class:`AbinitNcFile` containing crystallographic data."""
|
|
|
|
@abc.abstractproperty
|
|
def structure(self):
|
|
"""Returns the |Structure| object."""
|
|
|
|
def plot_bz(self, **kwargs):
|
|
"""
|
|
Gives the plot (as a matplotlib object) of the symmetry line path in the Brillouin Zone.
|
|
"""
|
|
return self.structure.plot_bz(**kwargs)
|
|
|
|
# To maintain backward compatbility
|
|
show_bz = plot_bz
|
|
|
|
def export_structure(self, filepath):
|
|
"""
|
|
Export the structure on file.
|
|
|
|
returns: |Visualizer| instance.
|
|
"""
|
|
return self.structure.export(filepath)
|
|
|
|
def visualize_structure_with(self, appname):
|
|
"""
|
|
Visualize the crystalline structure with the specified visualizer.
|
|
|
|
See |Visualizer| for the list of applications and formats supported.
|
|
"""
|
|
from abipy.iotools.visualizer import Visualizer
|
|
visu = Visualizer.from_name(appname)
|
|
|
|
for ext in visu.supported_extensions():
|
|
ext = "." + ext
|
|
try:
|
|
return self.export_structure(ext)
|
|
except visu.Error:
|
|
pass
|
|
else:
|
|
raise visu.Error("Don't know how to export data for appname %s" % appname)
|
|
|
|
def yield_structure_figs(self, **kwargs):
|
|
"""*Generates* a predefined list of matplotlib figures with minimal input from the user."""
|
|
yield self.structure.plot(show=False)
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class Has_ElectronBands(object):
|
|
"""Mixin class for :class:`AbinitNcFile` containing electron data."""
|
|
|
|
@abc.abstractproperty
|
|
def ebands(self):
|
|
"""Returns the |ElectronBands| object."""
|
|
|
|
@property
|
|
def nsppol(self):
|
|
"""Number of spin polarizations"""
|
|
return self.ebands.nsppol
|
|
|
|
@property
|
|
def nspinor(self):
|
|
"""Number of spinors"""
|
|
return self.ebands.nspinor
|
|
|
|
@property
|
|
def nspden(self):
|
|
"""Number of indepedendent spin-density components."""
|
|
return self.ebands.nspden
|
|
|
|
@property
|
|
def mband(self):
|
|
"""Maximum number of bands."""
|
|
return self.ebands.mband
|
|
|
|
@property
|
|
def nband(self):
|
|
"""Maximum number of bands."""
|
|
return self.ebands.nband
|
|
|
|
@property
|
|
def nelect(self):
|
|
"""Number of electrons per unit cell"""
|
|
return self.ebands.nelect
|
|
|
|
@property
|
|
def nkpt(self):
|
|
"""Number of k-points."""
|
|
return self.ebands.nkpt
|
|
|
|
@property
|
|
def kpoints(self):
|
|
"""Iterable with the Kpoints."""
|
|
return self.ebands.kpoints
|
|
|
|
@lazy_property
|
|
def tsmear(self):
|
|
return self.ebands.smearing.tsmear_ev.to("Ha")
|
|
|
|
def get_ebands_params(self):
|
|
""":class:`OrderedDict` with the convergence parameters."""
|
|
return collections.OrderedDict([
|
|
("nsppol", self.nsppol),
|
|
("nspinor", self.nspinor),
|
|
("nspden", self.nspden),
|
|
("nband", self.nband),
|
|
("nkpt", self.nkpt),
|
|
])
|
|
|
|
def plot_ebands(self, **kwargs):
|
|
"""Plot the electron energy bands. See the :func:`ElectronBands.plot` for the signature."""
|
|
return self.ebands.plot(**kwargs)
|
|
|
|
def plot_ebands_with_edos(self, edos, **kwargs):
|
|
"""Plot the electron energy bands with DOS. See the :func:`ElectronBands.plot_with_edos` for the signature."""
|
|
return self.ebands.plot_with_edos(edos, **kwargs)
|
|
|
|
def yield_ebands_figs(self, **kwargs):
|
|
"""*Generates* a predefined list of matplotlib figures with minimal input from the user."""
|
|
if self.ebands.kpoints.is_path:
|
|
yield self.ebands.plot(show=False)
|
|
yield self.ebands.kpoints.plot(show=False)
|
|
else:
|
|
edos = self.ebands.get_edos()
|
|
yield self.ebands.plot_with_edos(edos, show=False)
|
|
yield edos.plot(show=False)
|
|
|
|
def expose_ebands(self, slide_mode=False, slide_timeout=None, **kwargs):
|
|
"""
|
|
Shows a predefined list of matplotlib figures for electron bands with minimal input from the user.
|
|
"""
|
|
from abipy.tools.plotting import MplExpose
|
|
with MplExpose(slide_mode=slide_mode, slide_timeout=slide_mode, verbose=1) as e:
|
|
e(self.yield_ebands_figs(**kwargs))
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class Has_PhononBands(object):
|
|
"""
|
|
Mixin class for :class:`AbinitNcFile` containing phonon data.
|
|
"""
|
|
|
|
@abc.abstractproperty
|
|
def phbands(self):
|
|
"""Returns the |PhononBands| object."""
|
|
|
|
def get_phbands_params(self):
|
|
""":class:`OrderedDict` with the convergence parameters."""
|
|
return collections.OrderedDict([
|
|
("nqpt", len(self.phbands.qpoints)),
|
|
])
|
|
|
|
def plot_phbands(self, **kwargs):
|
|
"""
|
|
Plot the electron energy bands. See the :func:`PhononBands.plot` for the signature.""
|
|
"""
|
|
return self.phbands.plot(**kwargs)
|
|
|
|
#def plot_phbands_with_phdos(self, phdos, **kwargs):
|
|
# return self.phbands.plot_with_phdos(phdos, **kwargs)
|
|
|
|
|
|
def yield_phbands_figs(self, **kwargs): # pragma: no cover
|
|
"""
|
|
This function *generates* a predefined list of matplotlib figures with minimal input from the user.
|
|
Used in abiview.py to get a quick look at the results.
|
|
"""
|
|
units = kwargs.get("units", "mev")
|
|
yield self.phbands.qpoints.plot(show=False)
|
|
yield self.phbands.plot(units=units, show=False)
|
|
yield self.phbands.plot_colored_matched(units=units, show=False)
|
|
|
|
def expose_phbands(self, slide_mode=False, slide_timeout=None, **kwargs):
|
|
"""
|
|
Shows a predefined list of matplotlib figures for phonon bands with minimal input from the user.
|
|
"""
|
|
from abipy.tools.plotting import MplExpose
|
|
with MplExpose(slide_mode=slide_mode, slide_timeout=slide_mode, verbose=1) as e:
|
|
e(self.yield_phbands_figs(**kwargs))
|
|
|
|
|
|
class NcDumper(object):
|
|
"""Wrapper object for the ncdump tool."""
|
|
|
|
def __init__(self, *nc_args, **nc_kwargs):
|
|
"""
|
|
Args:
|
|
nc_args: Arguments passed to ncdump.
|
|
nc_kwargs: Keyword arguments passed to ncdump
|
|
"""
|
|
self.nc_args = nc_args
|
|
self.nc_kwargs = nc_kwargs
|
|
self.ncdump = which("ncdump")
|
|
|
|
def dump(self, filepath):
|
|
"""Returns a string with the output of ncdump."""
|
|
if self.ncdump is None:
|
|
return "Cannot find ncdump tool in $PATH"
|
|
else:
|
|
from subprocess import check_output
|
|
return check_output(["ncdump", filepath])
|
|
|
|
|
|
_ABBREVS = [
|
|
(1 << 50, 'Pb'),
|
|
(1 << 40, 'Tb'),
|
|
(1 << 30, 'Gb'),
|
|
(1 << 20, 'Mb'),
|
|
(1 << 10, 'kb'),
|
|
(1, 'b'),
|
|
]
|
|
|
|
|
|
def size2str(size):
|
|
"""Convert size to string with units."""
|
|
for factor, suffix in _ABBREVS:
|
|
if size > factor:
|
|
break
|
|
return "%.2f " % (size / factor) + suffix
|
|
|
|
|
|
def get_filestat(filepath):
|
|
stat = os.stat(filepath)
|
|
return collections.OrderedDict([
|
|
("Name", os.path.basename(filepath)),
|
|
("Directory", os.path.dirname(filepath)),
|
|
("Size", size2str(stat.st_size)),
|
|
("Access Time", ctime(stat.st_atime)),
|
|
("Modification Time", ctime(stat.st_mtime)),
|
|
("Change Time", ctime(stat.st_ctime)),
|
|
])
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class NotebookWriter(object):
|
|
"""
|
|
Mixin class for objects that are able to generate jupyter_ notebooks.
|
|
Subclasses must provide a concrete implementation of `write_notebook`.
|
|
"""
|
|
def make_and_open_notebook(self, nbpath=None, foreground=False): # pragma: no cover
|
|
"""
|
|
Generate an jupyter_ notebook and open it in the browser.
|
|
|
|
Args:
|
|
nbpath: If nbpath is None, a temporay file is created.
|
|
foreground: By default, jupyter is executed in background and stdout, stderr are redirected
|
|
to devnull. Use foreground to run the process in foreground
|
|
|
|
Return:
|
|
system exit code.
|
|
|
|
Raise:
|
|
`RuntimeError` if jupyter_ is not in $PATH
|
|
"""
|
|
nbpath = self.write_notebook(nbpath=nbpath)
|
|
|
|
if which("jupyter") is None:
|
|
raise RuntimeError("Cannot find jupyter in $PATH. Install it with `conda install jupyter or `pip install jupyter`")
|
|
|
|
if foreground:
|
|
return os.system("jupyter notebook %s" % nbpath)
|
|
else:
|
|
fd, tmpname = tempfile.mkstemp(text=True)
|
|
print(tmpname)
|
|
cmd = "jupyter notebook %s" % nbpath
|
|
print("Executing:", cmd)
|
|
print("stdout and stderr redirected to %s" % tmpname)
|
|
import subprocess
|
|
process = subprocess.Popen(cmd.split(), shell=False, stdout=fd, stderr=fd)
|
|
cprint("pid: %s" % str(process.pid), "yellow")
|
|
return 0
|
|
|
|
@staticmethod
|
|
def get_nbformat_nbv():
|
|
"""Return nbformat module, notebook version module"""
|
|
import nbformat
|
|
nbv = nbformat.v4
|
|
return nbformat, nbv
|
|
|
|
def get_nbformat_nbv_nb(self, title=None):
|
|
"""
|
|
Return ``nbformat`` module, notebook version module
|
|
and new notebook with title and import section
|
|
"""
|
|
nbformat, nbv = self.get_nbformat_nbv()
|
|
nb = nbv.new_notebook()
|
|
|
|
if title is not None:
|
|
nb.cells.append(nbv.new_markdown_cell("## %s" % title))
|
|
|
|
nb.cells.extend([
|
|
nbv.new_code_cell("""\
|
|
from __future__ import print_function, division, unicode_literals, absolute_import
|
|
|
|
import sys, os
|
|
import numpy as np
|
|
|
|
%matplotlib notebook
|
|
from IPython.display import display
|
|
|
|
# This to render pandas DataFrames with https://github.com/quantopian/qgrid
|
|
#import qgrid
|
|
#qgrid.nbinstall(overwrite=True) # copies javascript dependencies to your /nbextensions folder
|
|
|
|
# This to view Mayavi visualizations. See http://docs.enthought.com/mayavi/mayavi/tips.html
|
|
#from mayavi import mlab; mlab.init_notebook(backend='x3d', width=None, height=None, local=True)
|
|
|
|
from abipy import abilab
|
|
|
|
# Tell AbiPy we are inside a notebook and use seaborn settings for plots.
|
|
# See https://seaborn.pydata.org/generated/seaborn.set.html#seaborn.set
|
|
abilab.enable_notebook(with_seaborn=True)
|
|
|
|
# AbiPy widgets for pandas and seaborn plot APIs
|
|
#import abipy.display.seabornw import snw
|
|
#import abipy.display.pandasw import pdw""")
|
|
])
|
|
|
|
return nbformat, nbv, nb
|
|
|
|
@abc.abstractmethod
|
|
def write_notebook(self, nbpath=None):
|
|
"""
|
|
Write a jupyter_ notebook to nbpath. If nbpath is None, a temporay file is created.
|
|
Return path to the notebook. A typical template:
|
|
|
|
.. code-block:: python
|
|
|
|
# Preable.
|
|
nbformat, nbv, nb = self.get_nbformat_nbv_nb(title=None)
|
|
|
|
#####################
|
|
# Put your code here
|
|
nb.cells.extend([
|
|
nbv.new_markdown_cell("# This is a markdown cell"),
|
|
nbv.new_code_cell("a = 1"),
|
|
])
|
|
#####################
|
|
|
|
# Call _write_nb_nbpath
|
|
return self._write_nb_nbpath(nb, nbpath)
|
|
"""
|
|
|
|
@staticmethod
|
|
def _write_nb_nbpath(nb, nbpath):
|
|
"""
|
|
This method must be called at the end of ``write_notebook``.
|
|
nb is the jupyter notebook and nbpath the argument passed to ``write_notebook``.
|
|
"""
|
|
import io, os, tempfile
|
|
if nbpath is None:
|
|
_, nbpath = tempfile.mkstemp(prefix="abinb_", suffix='.ipynb', dir=os.getcwd(), text=True)
|
|
|
|
# Write notebook
|
|
import nbformat
|
|
with io.open(nbpath, 'wt', encoding="utf8") as fh:
|
|
nbformat.write(nb, fh)
|
|
return nbpath
|
|
|
|
@classmethod
|
|
def pickle_load(cls, filepath):
|
|
"""
|
|
Loads the object from a pickle file.
|
|
"""
|
|
with open(filepath, "rb") as fh:
|
|
new = pickle.load(fh)
|
|
#assert cls is new.__class__
|
|
return new
|
|
|
|
def pickle_dump(self, filepath=None):
|
|
"""
|
|
Save the status of the object in pickle format.
|
|
If filepath is None, a temporary file is created.
|
|
|
|
Return:
|
|
name of the pickle file.
|
|
"""
|
|
if filepath is None:
|
|
_, filepath = tempfile.mkstemp(suffix='.pickle')
|
|
|
|
with open(filepath, "wb") as fh:
|
|
pickle.dump(self, fh)
|
|
return filepath
|
|
|
|
#@abc.abstractmethod
|
|
#def yield_figs(self, **kwargs)
|
|
# """
|
|
# This function *generates* a predefined list of matplotlib figures with minimal input from the user.
|
|
# Used in abiview.py to get a quick look at the results.
|
|
# """
|
|
|
|
def expose(self, slide_mode=False, slide_timeout=None, **kwargs):
|
|
"""
|
|
Shows a predefined list of matplotlib figures with minimal input from the user.
|
|
"""
|
|
from abipy.tools.plotting import MplExpose
|
|
with MplExpose(slide_mode=slide_mode, slide_timeout=slide_mode, verbose=1) as e:
|
|
e(self.yield_figs(**kwargs))
|
|
|
|
|
|
class Has_Header(object):
|
|
"""Mixin class for netcdf_ files containing the Abinit header."""
|
|
|
|
@lazy_property
|
|
def hdr(self):
|
|
"""|AttrDict| with the Abinit header e.g. hdr.ecut."""
|
|
return self.reader.read_abinit_hdr()
|
|
|
|
#def get_hdr_params(self):
|
|
# """:class:`OrderedDict` with the convergence parameters."""
|
|
# return collections.OrderedDict([
|
|
|
|
#def compare_hdr(self, other_hdr):
|