DDB: add support for D1, D3

This commit is contained in:
Matteo Giantomassi 2018-07-30 17:19:14 +02:00
parent 02aa0a9078
commit a1e87694af
3 changed files with 301 additions and 61 deletions

View File

@ -8,6 +8,7 @@ import tempfile
import itertools import itertools
import numpy as np import numpy as np
import pandas as pd import pandas as pd
import abipy.core.abinit_units as abu
from collections import OrderedDict from collections import OrderedDict
from six.moves import map, zip, StringIO from six.moves import map, zip, StringIO
@ -27,8 +28,8 @@ from abipy.dfpt.phonons import PhononDosPlotter, PhononBandsPlotter, Interatomic
from abipy.dfpt.tensors import DielectricTensor from abipy.dfpt.tensors import DielectricTensor
from abipy.dfpt.elastic import ElasticData from abipy.dfpt.elastic import ElasticData
from abipy.core.abinit_units import phfactor_ev2units, phunit_tag #Ha_cmm1, from abipy.core.abinit_units import phfactor_ev2units, phunit_tag #Ha_cmm1,
from pymatgen.analysis.elasticity.elastic import ElasticTensor from pymatgen.analysis.elasticity import Stress, ElasticTensor
from pymatgen.core.units import eV_to_Ha, bohr_to_angstrom from pymatgen.core.units import eV_to_Ha, bohr_to_angstrom, Energy
from abipy.tools.plotting import Marker, add_fig_kwargs, get_ax_fig_plt, set_axlims from abipy.tools.plotting import Marker, add_fig_kwargs, get_ax_fig_plt, set_axlims
from abipy.tools import duck from abipy.tools import duck
from abipy.abio.robots import Robot from abipy.abio.robots import Robot
@ -376,14 +377,30 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
if "List of bloks and their characteristics" in line: if "List of bloks and their characteristics" in line:
# add last block when we reach the last part of the file. # add last block when we reach the last part of the file.
# This line is present only if DDB has been produced by mrgddb # This line is present only if DDB has been produced by mrgddb
blocks.append({"data": block_lines, "qpt": qpt}) if block_lines:
blocks.append({"data": block_lines, "qpt": qpt, "dord": dord})
block_lines = []
qpt = None
break break
# Don't use lstring because we may reuse block_lines to write new DDB.
line = line.rstrip() line = line.rstrip()
# new block
# new block --> detect order
if "# elements" in line: if "# elements" in line:
if block_lines: if block_lines:
blocks.append({"data": block_lines, "qpt": qpt}) blocks.append({"data": block_lines, "qpt": qpt, "dord": dord})
tokens = line.split()
num_elements = int(tokens[-1])
s = " ".join(tokens[:2])
dord = {"Total energy": 0,
"1st derivatives": 1,
"2nd derivatives": 2,
"3rd derivatives": 3}.get(s, None)
if dord is None:
raise RuntimeError("Cannot detect derivative order from string: `%s`" % s)
block_lines = [] block_lines = []
qpt = None qpt = None
@ -391,8 +408,9 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
if "qpt" in line: if "qpt" in line:
qpt = list(map(float, line.split()[1:4])) qpt = list(map(float, line.split()[1:4]))
if block_lines: if block_lines:
blocks.append({"data": block_lines, "qpt": qpt}) blocks.append({"data": block_lines, "qpt": qpt, "dord": dord})
return blocks return blocks
@ -475,12 +493,77 @@ class DdbFile(TextFile, Has_Structure, NotebookWriter):
raise TypeError("object %s does not have `params` attribute" % type(obj)) raise TypeError("object %s does not have `params` attribute" % type(obj))
obj.params.update(self.params) obj.params.update(self.params)
# TODO @lazy_property
#@lru_cache(typed=True) def total_energy(self):
#def has_forces(self, select="at_least_one"): """
Total energy in eV. None if not available.
"""
for block in self.blocks:
if block["dord"] == 0:
ene_ha = float(block["data"][1].split()[0].replace("D", "E"))
return Energy(ene_ha, "Ha").to("eV")
return None
#@lru_cache(typed=True) @lazy_property
#def has_stress(self, select="at_least_one"): def cart_forces(self):
"""
Cartesian forces in eV / Ang
None if not available i.e. if the GS DDB has not been merged.
.. note::
These values correspond to the `fred` array in abinit
this array has *not* been corrected by enforcing
the translational symmetry, namely that the sum of force
on all atoms is not necessarly zero.
So inconsistencies with the results reported in the output file are expected.
"""
for block in self.blocks:
if block["dord"] != 1: continue
natom = len(self.structure)
fred = np.empty((natom, 3))
for line in block["data"][1:]:
idir, ipert, fval = line.split()[:3]
# F --> C
idir, ipert = int(idir) - 1, int(ipert) - 1
if ipert < natom:
fred[ipert, idir] = float(fval.replace("D", "E"))
# FIXME
gprimd = self.structure.reciprocal_lattice.matrix
fcart = - fred
return fcart * abu.Ha_eV / abu.Bohr_Ang
return None
@lazy_property
def cart_stress_tensor(self):
"""
pymatgen Stress tensor in cartesian coordinates (Hartree/Bohr^3)
None if not available.
"""
for block in self.blocks:
if block["dord"] != 1: continue
svoigt = np.empty(6)
# Abinit stress is in cart coords and Ha/Bohr**3
# Map (idir, ipert) --> voigt
uniax, shear = len(self.structure) + 3, len(self.structure) + 4
dirper2voigt = {
(1, uniax): 0,
(2, uniax): 1,
(3, uniax): 2,
(1, shear): 3,
(2, shear): 4,
(3, shear): 5}
for line in block["data"][1:]:
idir, ipert, fval = line.split()[:3]
idp = int(idir), int(ipert)
if idp in dirper2voigt:
svoigt[dirper2voigt[idp]] = float(fval.replace("D", "E"))
return Stress.from_voigt(svoigt * abu.Ha_eV / (abu.Bohr_Ang**3))
return None
def has_lo_to_data(self, select="at_least_one"): def has_lo_to_data(self, select="at_least_one"):
""" """

View File

@ -4,10 +4,12 @@ Objects to analyze elastic and piezoelectric tensors computed by anaddb.
""" """
from __future__ import print_function, division, unicode_literals, absolute_import from __future__ import print_function, division, unicode_literals, absolute_import
import sys
import pandas as pd import pandas as pd
from collections import OrderedDict from collections import OrderedDict
from monty.string import is_string, list_strings, marquee from monty.string import is_string, list_strings, marquee
from monty.collections import AttrDict
from monty.functools import lazy_property from monty.functools import lazy_property
from pymatgen.analysis.elasticity.tensors import Tensor from pymatgen.analysis.elasticity.tensors import Tensor
from pymatgen.analysis.elasticity.elastic import ElasticTensor from pymatgen.analysis.elasticity.elastic import ElasticTensor
@ -16,6 +18,42 @@ from abipy.core.mixins import Has_Structure
from abipy.flowtk.netcdf import ETSF_Reader from abipy.flowtk.netcdf import ETSF_Reader
class MyElasticTensor(ElasticTensor):
def _repr_html_(self):
"""Integration with jupyter notebooks."""
return self.to_voigt_dataframe()._repr_html_()
def to_voigt_dataframe(self, tol=1e-5):
tensor = self.zeroed(tol) if tol else self
columns = ["xx", "yy", "zz", "yz", "xz", "xy"]
#columns = ["1", "2", "3", "4", "5", "6"]
rows = []
for row in tensor.voigt:
rows.append({k: v for k, v in zip(columns, row)})
return pd.DataFrame(rows, index=columns, columns=columns)
class MyPiezoTensor(PiezoTensor):
def _repr_html_(self):
"""Integration with jupyter notebooks."""
return self.to_voigt_dataframe()._repr_html_()
def to_voigt_dataframe(self, tol=1e-5):
tensor = self.zeroed(tol) if tol else self
index = ["Px", "Py", "Pz"]
columns = ["xx", "yy", "zz", "yz", "xz", "xy"]
#index = ["P1", "P2", "P3"]
#columns = ["1", "2", "3", "4", "5", "6"]
rows = []
for irow, row in enumerate(tensor.voigt):
rows.append({k: v for k, v in zip(columns, row)})
return pd.DataFrame(rows, index=index, columns=columns)
class ElasticData(Has_Structure): class ElasticData(Has_Structure):
""" """
Container with the different elastic and piezoelectric tensors Container with the different elastic and piezoelectric tensors
@ -26,7 +64,8 @@ class ElasticData(Has_Structure):
""" """
ALL_ELASTIC_TENSOR_NAMES = ( ALL_ELASTIC_TENSOR_NAMES = (
"elastic_clamped", "elastic_relaxed", "elastic_stress_corr", "elastic_relaxed_fixed_D", "elastic_clamped", "elastic_relaxed",
"elastic_stress_corr", "elastic_relaxed_fixed_D",
) )
ALL_PIEZOELECTRIC_TENSOR_NAMES = ( ALL_PIEZOELECTRIC_TENSOR_NAMES = (
@ -42,12 +81,43 @@ class ElasticData(Has_Structure):
"piezoelectric": ALL_PIEZOELECTRIC_TENSOR_NAMES, "piezoelectric": ALL_PIEZOELECTRIC_TENSOR_NAMES,
} }
def __init__(self, structure, elastic_clamped=None, elastic_relaxed=None, elastic_stress_corr=None, TENSOR_META = {
"elastic_clamped": AttrDict(
info="clamped-ion elastic tensor in Voigt notation (shape: (6, 6))",
units="GPa"),
"elastic_relaxed": AttrDict(
info="relaxed-ion elastic tensor in Voigt notation (shape: (6, 6))",
units="GPa"),
"elastic_stress_corr": AttrDict(
info="relaxed-ion elastic tensor considering the stress left inside cell in Voigt notation (shape: (6, 6))",
units="GPa"),
"elastic_relaxed_fixed_D": AttrDict(
info="relaxed-ion elastic tensor at fixed displacement field in Voigt notation (shape: (6, 6))",
units="GPa"),
"piezo_clamped": AttrDict(
info="clamped-ion piezoelectric tensor in Voigt notation (shape: (3, 6))",
units="c/m^2"),
"piezo_relaxed": AttrDict(
info="relaxed-ion piezoelectric tensor in Voigt notation (shape: (3, 6))",
units="c/m^2"),
"d_piezo_relaxed": AttrDict(
info="relaxed-ion piezoelectric d tensor in Voigt notation (shape: (3, 6))",
units="pc/m^2"),
"g_piezo_relaxed": AttrDict(
info="relaxed-ion piezoelectric g tensor in Voigt notation (shape: (3, 6))",
units="m^2/c"),
"h_piezo_relaxed": AttrDict(
info="relaxed-ion piezoelectric h tensor in Voigt notation (shape: (3, 6))",
units="GN/c"),
}
def __init__(self, structure, params, elastic_clamped=None, elastic_relaxed=None, elastic_stress_corr=None,
elastic_relaxed_fixed_D=None, piezo_clamped=None, piezo_relaxed=None, d_piezo_relaxed=None, elastic_relaxed_fixed_D=None, piezo_clamped=None, piezo_relaxed=None, d_piezo_relaxed=None,
g_piezo_relaxed=None, h_piezo_relaxed=None): g_piezo_relaxed=None, h_piezo_relaxed=None):
""" """
Args: Args:
structure: |Structure| object. structure: |Structure| object.
params: Dictionary with input parameters.
elastic_clamped: clamped-ion elastic tensor in Voigt notation (shape (6,6)) in GPa. elastic_clamped: clamped-ion elastic tensor in Voigt notation (shape (6,6)) in GPa.
elastic_relaxed: relaxed-ion elastic tensor in Voigt notation (shape (6,6)) in GPa. elastic_relaxed: relaxed-ion elastic tensor in Voigt notation (shape (6,6)) in GPa.
elastic_stress_corr: relaxed-ion elastic tensor considering the stress left inside cell elastic_stress_corr: relaxed-ion elastic tensor considering the stress left inside cell
@ -65,15 +135,16 @@ class ElasticData(Has_Structure):
Arguments can be either arrays or Tensor objects. Arguments can be either arrays or Tensor objects.
""" """
self._structure = structure self._structure = structure
self.elastic_clamped = self._define_variable(elastic_clamped, ElasticTensor) self.params = params
self.elastic_relaxed = self._define_variable(elastic_relaxed, ElasticTensor) self.elastic_clamped = self._define_variable(elastic_clamped, MyElasticTensor)
self.elastic_stress_corr = self._define_variable(elastic_stress_corr, ElasticTensor) self.elastic_relaxed = self._define_variable(elastic_relaxed, MyElasticTensor)
self.elastic_relaxed_fixed_D = self._define_variable(elastic_relaxed_fixed_D, ElasticTensor) self.elastic_stress_corr = self._define_variable(elastic_stress_corr, MyElasticTensor)
self.piezo_clamped = self._define_variable(piezo_clamped, PiezoTensor) self.elastic_relaxed_fixed_D = self._define_variable(elastic_relaxed_fixed_D, MyElasticTensor)
self.piezo_relaxed = self._define_variable(piezo_relaxed, PiezoTensor) self.piezo_clamped = self._define_variable(piezo_clamped, MyPiezoTensor)
self.d_piezo_relaxed = self._define_variable(d_piezo_relaxed, PiezoTensor) self.piezo_relaxed = self._define_variable(piezo_relaxed, MyPiezoTensor)
self.g_piezo_relaxed = self._define_variable(g_piezo_relaxed, Tensor) self.d_piezo_relaxed = self._define_variable(d_piezo_relaxed, MyPiezoTensor)
self.h_piezo_relaxed = self._define_variable(h_piezo_relaxed, Tensor) self.g_piezo_relaxed = self._define_variable(g_piezo_relaxed, MyPiezoTensor)
self.h_piezo_relaxed = self._define_variable(h_piezo_relaxed, MyPiezoTensor)
def _define_variable(self, tensor_voigt, tensor_class): def _define_variable(self, tensor_voigt, tensor_class):
""" """
@ -105,51 +176,81 @@ class ElasticData(Has_Structure):
""" """
Builds the object from a ETSF_Reader Builds the object from a ETSF_Reader
""" """
structure = reader.read_structure() structure = reader.read_structure()
# [6, 6] symmetric tensors (written by Fortran)
# Produced in ddb_elast
elastic_clamped = reader.read_value("elastic_constants_clamped_ion", default=None)
elastic_relaxed = reader.read_value("elastic_constants_relaxed_ion", default=None)
elastic_stress_corr = reader.read_value("elastic_constants_relaxed_ion_stress_corrected",default=None)
# ddb_piezo
elastic_relaxed_fixed_D = reader.read_value("elastic_tensor_relaxed_ion_fixed_D", default=None)
# [3, 6] tensors params = AttrDict(
# Produced in ddb_piezo # NB: asr and chneut are always present in the new anaddb.nc file
piezo_clamped = reader.read_value("piezo_clamped_ion", default=None) # Use -666 to support old formats.
piezo_relaxed = reader.read_value("piezo_relaxed_ion", default=None) asr=int(reader.read_value("asr", default=-666)),
d_piezo_relaxed = reader.read_value("d_tensor_relaxed_ion", default=None) chneut= int(reader.read_value("chneut", default=-666)),
elaflag=int(reader.read_value("elaflag", default=0)),
instrflag=int(reader.read_value("instrflag", default=0)),
piezoflag=int(reader.read_value("piezoflag", default=0)),
dieflag=int(reader.read_value("dieflag", default=0)),
)
# These are [6, 3] tensors written by Fortran (need to transpose) ts = AttrDict({n: None for n in cls.ALL_TENSOR_NAMES})
g_piezo_relaxed = reader.read_value("g_tensor_relaxed_ion", default=None)
if g_piezo_relaxed is not None:
g_piezo_relaxed = g_piezo_relaxed.T.copy()
h_piezo_relaxed = reader.read_value("h_tensor_relaxed_ion", default=None)
if h_piezo_relaxed is not None:
h_piezo_relaxed = h_piezo_relaxed.T.copy()
return cls(structure=structure, elastic_clamped=elastic_clamped, elastic_relaxed=elastic_relaxed, # [6, 6] symmetric tensors (written by Fortran, produced in ddb_elast)
elastic_stress_corr=elastic_stress_corr, elastic_relaxed_fixed_D=elastic_relaxed_fixed_D, ts.elastic_clamped = reader.read_value("elastic_constants_clamped_ion", default=None)
piezo_clamped=piezo_clamped, piezo_relaxed=piezo_relaxed, d_piezo_relaxed=d_piezo_relaxed, ts.elastic_relaxed = reader.read_value("elastic_constants_relaxed_ion", default=None)
g_piezo_relaxed=g_piezo_relaxed, h_piezo_relaxed=h_piezo_relaxed) if params.elaflag == 5:
ts.elastic_stress_corr = reader.read_value("elastic_constants_relaxed_ion_stress_corrected")
# Written in ddb_piezo
ts.elastic_relaxed_fixed_D = reader.read_value("elastic_tensor_relaxed_ion_fixed_D", default=None)
# [3, 6] tensors (written by Fortran, produced in ddb_piezo).
ts.piezo_clamped = reader.read_value("piezo_clamped_ion", default=None)
ts.piezo_relaxed = reader.read_value("piezo_relaxed_ion", default=None)
ts.d_piezo_relaxed = reader.read_value("d_tensor_relaxed_ion", default=None)
# These are [6, 3] tensors written by Fortran (need to transpose).
ts.g_piezo_relaxed = reader.read_value("g_tensor_relaxed_ion", default=None)
if ts.g_piezo_relaxed is not None:
ts.g_piezo_relaxed = ts.g_piezo_relaxed.T.copy()
ts.h_piezo_relaxed = reader.read_value("h_tensor_relaxed_ion", default=None)
if ts.h_piezo_relaxed is not None:
ts.h_piezo_relaxed = ts.h_piezo_relaxed.T.copy()
return cls(structure, params, **ts)
#return cls(structure, params, elastic_clamped=elastic_clamped, elastic_relaxed=elastic_relaxed,
# elastic_stress_corr=elastic_stress_corr, elastic_relaxed_fixed_D=elastic_relaxed_fixed_D,
# piezo_clamped=piezo_clamped, piezo_relaxed=piezo_relaxed, d_piezo_relaxed=d_piezo_relaxed,
# g_piezo_relaxed=g_piezo_relaxed, h_piezo_relaxed=h_piezo_relaxed)
def __str__(self): def __str__(self):
return self.to_string() return self.to_string()
def to_string(self, verbose=0): def to_string(self, verbose=0):
"""String representing with verbosity level `verbose`.""" """String represention with verbosity level `verbose`."""
lines = []; app = lines.append lines = []; app = lines.append
app(self.structure.to_string(verbose=verbose, title="Structure")) app(self.structure.to_string(verbose=verbose, title="Structure"))
app("")
app(marquee("Parameters", mark="="))
import json
app(json.dumps(self.params, indent=2, sort_keys=True))
for tensor_type in ["elastic", "piezoelectric"]: for tensor_type in ("elastic", "piezoelectric"):
name_tensor_list = self.name_tensor_list(tensor_type=tensor_type) name_tensor_list = self.name_tensor_list(tensor_type=tensor_type)
if name_tensor_list: if name_tensor_list:
app("") app("")
app(marquee("Available %s tensors" % tensor_type, mark="=")) app(marquee("%s tensors available" % tensor_type, mark="="))
for name, tensor in name_tensor_list: for name, tensor in name_tensor_list:
ans = tensor.is_fit_to_structure(self.structure, tol=1e-2) meta = self.TENSOR_META[name]
app("%s (fit_to_structure: %s)" % (name, ans)) is_fit = tensor.is_fit_to_structure(self.structure, tol=1e-2)
# TODO
tol = dict(elastic=1e-3, piezoelectric=1e-5)[tensor_type]
app("[%s]" % name.upper())
app("%s" % meta.info)
app("Units: %s, set to zero below: %s, fit_to_structure: %s" % (meta.units, tol, is_fit))
app("")
if tensor_type == "elastic":
app(self.get_elastic_tensor_dataframe(tensor_name=name, tol=tol).to_string())
elif tensor_type == "piezoelectric":
app(self.get_piezoelectric_tensor_dataframe(tensor_name=name, tol=tol).to_string())
app("")
return "\n".join(lines) return "\n".join(lines)
@ -168,6 +269,8 @@ class ElasticData(Has_Structure):
if tensor is not None: l.append((name, tensor)) if tensor is not None: l.append((name, tensor))
else: else:
for name in list_strings(tensor_names): for name in list_strings(tensor_names):
if name not in self.TYPE2NAMES[tensor_type]:
raise ValueError("tensor name %s does not belong to type: `%s`" % (name, tensor_type))
tensor = getattr(self, name) tensor = getattr(self, name)
if tensor is not None: l.append((name, tensor)) if tensor is not None: l.append((name, tensor))
@ -188,7 +291,7 @@ class ElasticData(Has_Structure):
kwargs = {name: tensor.fit_to_structure(structure, symprec=symprec) kwargs = {name: tensor.fit_to_structure(structure, symprec=symprec)
for name, tensor in self.name_tensor_list()} for name, tensor in self.name_tensor_list()}
return self.__class__(structure, **kwargs) return self.__class__(structure, self.params, **kwargs)
def convert_to_ieee(self, structure=None, initial_fit=True, refine_rotation=True): def convert_to_ieee(self, structure=None, initial_fit=True, refine_rotation=True):
""" """
@ -214,7 +317,43 @@ class ElasticData(Has_Structure):
kwargs[name] = tensor.convert_to_ieee(structure, kwargs[name] = tensor.convert_to_ieee(structure,
initial_fit=initial_fit, refine_rotation=refine_rotation) initial_fit=initial_fit, refine_rotation=refine_rotation)
return self.__class__(structure, **kwargs) return self.__class__(structure, self.params, **kwargs)
def get_elastic_tensor_dataframe(self, tensor_name="elastic_relaxed", tol=1e-3):
"""
Args:
tol: returns the matrix with all entries below a certain threshold
(i.e. tol) set to zero
"""
tensor = getattr(self, tensor_name)
if tensor is None: return pd.DataFrame()
if tol: tensor = tensor.zeroed(tol)
columns = ["xx", "yy", "zz", "yz", "xz", "xy"]
#columns = ["1", "2", "3", "4", "5", "6"]
rows = []
for row in tensor.voigt:
rows.append({k: v for k, v in zip(columns, row)})
return pd.DataFrame(rows, index=columns, columns=columns)
def get_piezoelectric_tensor_dataframe(self, tensor_name="piezo_relaxed", tol=1e-5):
"""
Args:
tol: returns the matrix with all entries below a certain threshold
(i.e. tol) set to zero
"""
tensor = getattr(self, tensor_name)
if tensor is None: return pd.DataFrame()
if tol: tensor = tensor.zeroed(tol)
index = ["Px", "Py", "Pz"]
columns = ["xx", "yy", "zz", "yz", "xz", "xy"]
#index = ["P1", "P2", "P3"]
#columns = ["1", "2", "3", "4", "5", "6"]
rows = []
for row in tensor.voigt:
rows.append({k: v for k, v in zip(columns, row)})
return pd.DataFrame(rows, index=index, columns=columns)
def get_voigt_dataframe(self, tensor_names): def get_voigt_dataframe(self, tensor_names):
""" """
@ -234,7 +373,6 @@ class ElasticData(Has_Structure):
row["tensor_name"] = name row["tensor_name"] = name
rows.append(row) rows.append(row)
return pd.DataFrame(rows, columns=list(rows[0].keys() if rows else None)) return pd.DataFrame(rows, columns=list(rows[0].keys() if rows else None))
#def get_average_elastic_dataframe(self, tensor_names="elastic_relaxed", fit_to_structure=False, symprec=0.1): #def get_average_elastic_dataframe(self, tensor_names="elastic_relaxed", fit_to_structure=False, symprec=0.1):
@ -287,9 +425,8 @@ class ElasticData(Has_Structure):
d = tensor.get_structure_property_dict(self.structure, d = tensor.get_structure_property_dict(self.structure,
include_base_props=include_base_props, ignore_errors=ignore_errors) include_base_props=include_base_props, ignore_errors=ignore_errors)
d.pop("structure") d.pop("structure")
if len(do_fits) > 1:
# Add column telling whether fit has been performed # Add column telling whether fit has been performed
d["fit_to_structure"] = do_fit if len(do_fits) > 1: d["fit_to_structure"] = do_fit
d["tensor_name"] = name d["tensor_name"] = name
rows.append(d) rows.append(d)

View File

@ -4,6 +4,7 @@ from __future__ import print_function, division, unicode_literals, absolute_impo
import os import os
import numpy as np import numpy as np
import abipy.data as abidata import abipy.data as abidata
import abipy.core.abinit_units as abu
from abipy import abilab from abipy import abilab
from abipy.core.testing import AbipyTest from abipy.core.testing import AbipyTest
@ -36,6 +37,9 @@ class DdbTest(AbipyTest):
assert h.xred.shape == (h.natom, 3) and h.kpt.shape == (h.nkpt, 3) assert h.xred.shape == (h.natom, 3) and h.kpt.shape == (h.nkpt, 3)
self.assert_equal(h.znucl, [13, 33]) self.assert_equal(h.znucl, [13, 33])
assert ddb.version == 100401 assert ddb.version == 100401
assert ddb.total_energy is None
assert ddb.cart_forces is None
assert ddb.cart_stress_tensor is None
assert np.all(h.symrel[1].T.ravel() == [0, -1, 1, 0, -1, 0, 1, -1, 0]) assert np.all(h.symrel[1].T.ravel() == [0, -1, 1, 0, -1, 0, 1, -1, 0])
assert np.all(h.symrel[2].T.ravel() == [-1, 0, 0, -1, 0, 1, -1, 1, 0]) assert np.all(h.symrel[2].T.ravel() == [-1, 0, 0, -1, 0, 1, -1, 1, 0])
@ -77,6 +81,7 @@ class DdbTest(AbipyTest):
blocks = ddb._read_blocks() blocks = ddb._read_blocks()
assert len(blocks) == 1 assert len(blocks) == 1
assert blocks[0]["qpt"] == [0.25, 0, 0] assert blocks[0]["qpt"] == [0.25, 0, 0]
assert blocks[0]["dord"] == 2
lines = blocks[0]["data"] lines = blocks[0]["data"]
assert lines[0].rstrip() == " 2nd derivatives (non-stat.) - # elements : 36" assert lines[0].rstrip() == " 2nd derivatives (non-stat.) - # elements : 36"
@ -182,6 +187,7 @@ class DdbTest(AbipyTest):
# Execute anaddb to compute the interatomic forces. # Execute anaddb to compute the interatomic forces.
ifc = ddb.anaget_ifc() ifc = ddb.anaget_ifc()
str(ifc); repr(ifc) str(ifc); repr(ifc)
#assert ifc.to_string(verbose=2)
assert ifc.structure == ddb.structure assert ifc.structure == ddb.structure
assert ifc.number_of_atoms == len(ddb.structure) assert ifc.number_of_atoms == len(ddb.structure)
@ -324,6 +330,20 @@ class DdbTest(AbipyTest):
Testing DDB containing also third order derivatives. Testing DDB containing also third order derivatives.
""" """
with abilab.abiopen(abidata.ref_file("refs/alas_nl_dfpt/AlAs_nl_dte_DDB")) as ddb: with abilab.abiopen(abidata.ref_file("refs/alas_nl_dfpt/AlAs_nl_dte_DDB")) as ddb:
repr(ddb); str(ddb)
assert ddb.to_string(verbose=2)
self.assert_almost_equal(ddb.total_energy.to("Ha"), -0.10085769246152e+02)
assert ddb.cart_forces is not None
stress = ddb.cart_stress_tensor
ref_voigt = np.array([-0.31110177329142E-05, -0.31110177329142E-05, -0.31110177329146E-05,
0.00000000000000E+00, 0.00000000000000E+00, 0.00000000000000E+00])
self.assert_almost_equal(stress[0, 0], ref_voigt[0] * abu.Ha_eV / abu.Bohr_Ang**3)
self.assert_almost_equal(stress[1, 1], ref_voigt[1] * abu.Ha_eV / abu.Bohr_Ang**3)
self.assert_almost_equal(stress[2, 2], ref_voigt[2] * abu.Ha_eV / abu.Bohr_Ang**3)
self.assert_almost_equal(stress[1, 2], ref_voigt[3] * abu.Ha_eV / abu.Bohr_Ang**3)
self.assert_almost_equal(stress[0, 2], ref_voigt[4] * abu.Ha_eV / abu.Bohr_Ang**3)
self.assert_almost_equal(stress[0, 1], ref_voigt[5] * abu.Ha_eV / abu.Bohr_Ang**3)
for qpoint in ddb.qpoints: for qpoint in ddb.qpoints:
assert qpoint in ddb.computed_dynmat assert qpoint in ddb.computed_dynmat
@ -343,6 +363,8 @@ class DdbTest(AbipyTest):
assert repr(e); assert str(e) assert repr(e); assert str(e)
assert e.to_string(verbose=2) assert e.to_string(verbose=2)
assert e.structure.formula == "Al1 As1" assert e.structure.formula == "Al1 As1"
assert e.elastic_relaxed._repr_html_()
#assert hasattr(e.elastic_relaxed.compliance_tensor, "_repr_html_")
name_tensor_list = e.name_tensor_list(tensor_type="elastic") name_tensor_list = e.name_tensor_list(tensor_type="elastic")
names = [nt[0] for nt in name_tensor_list] names = [nt[0] for nt in name_tensor_list]
@ -353,7 +375,7 @@ class DdbTest(AbipyTest):
edata_fit = e.fit_to_structure() edata_fit = e.fit_to_structure()
edata_ieee = e.convert_to_ieee() edata_ieee = e.convert_to_ieee()
df = e.get_voigt_dataframe("elastic_relaxed") df = e.get_voigt_dataframe("elastic_relaxed")
self.assert_almost_equal(df[(0, 0)], 120.41874336082199) self.assert_almost_equal(df[(0, 0)][0], 120.41874336082199)
df = e.get_elast_properties_dataframe(tensor_names="elastic_relaxed", fit_to_structure=True) df = e.get_elast_properties_dataframe(tensor_names="elastic_relaxed", fit_to_structure=True)
@ -406,7 +428,7 @@ class DdbRobotTest(AbipyTest):
assert r.phdos_plotter.gridplot(show=False) assert r.phdos_plotter.gridplot(show=False)
if self.has_nbformat(): if self.has_nbformat():
robot.write_notebook(nbpath=self.get_tmpname(text=True)) assert robot.write_notebook(nbpath=self.get_tmpname(text=True))
robot.close() robot.close()
@ -418,8 +440,6 @@ class PhononComputationTest(AbipyTest):
path = os.path.join(abidata.dirpath, "refs", "mgb2_phonons_nkpt_tsmear", "mgb2_121212k_0.04tsmear_DDB") path = os.path.join(abidata.dirpath, "refs", "mgb2_phonons_nkpt_tsmear", "mgb2_121212k_0.04tsmear_DDB")
ddb = abilab.abiopen(path) ddb = abilab.abiopen(path)
#for dos_method in ("gaussian",):
#for dos_method in ("tetra",):
for dos_method in ("tetra", "gaussian"): for dos_method in ("tetra", "gaussian"):
# Get phonon bands and Dos with anaddb. # Get phonon bands and Dos with anaddb.
phbands_file, phdos_file = ddb.anaget_phbst_and_phdos_files(nqsmall=4, ndivsm=2, phbands_file, phdos_file = ddb.anaget_phbst_and_phdos_files(nqsmall=4, ndivsm=2,