Docs + code cleanup

This commit is contained in:
Matteo Giantomassi 2018-11-08 12:27:12 +01:00
parent 5c614179e7
commit 352a781a0d
11 changed files with 205 additions and 362 deletions

View File

@ -8,214 +8,11 @@ import numpy as np
import sympy as sp
#from monty.functools import lazy_property
from collections import Sequence, OrderedDict
from collections import OrderedDict
from monty.termcolor import cprint
from abipy.core.mixins import Has_Structure
def _validate_params(params, wpos):
"""
Check keys in params. Raises ValueError.
"""
check = params.copy()
for k in params:
if k not in wpos.names:
raise ValueError("Cannot find key: %s in symbol_names: %s" % (k, wpos.names))
check.pop(k)
if check:
raise ValueError("Found unknown symbol names in %s" % check)
class WyckoffPositions(Sequence):
"""
A numpy array with sympy expression.
"""
@classmethod
def from_string(cls, s):
"""
Initialize the object from string `s`.
"""
# Build a matrix of strings with expressions.
s = s.replace("\n", "")
s = s.replace("(", "").replace(")", "")
s = s.replace("[", "").replace("]", "")
try:
matrix_str = np.reshape(s.split(","), (-1, 3))
except ValueError:
raise ValueError("Wrong string in input, perhaps missing comma. Input string:\n %s" % str(s))
return cls(matrix_str)
def __init__(self, matrix_str, letter=None, site_symmetry=None):
"""
Args:
matrix_str:
letter:
site_symmetry:
"""
self.letter, self.site_symmetry = None, None
# Build numpy matrix of sympy expressions from the matrix of strings.
from sympy import Symbol
from sympy.parsing.sympy_parser import parse_expr
exprs = []
for vec in np.reshape(matrix_str, (-1, 3)):
exprs.extend([parse_expr(e) for e in vec])
self._expr_mat = np.reshape(exprs, (-1, 3))
# Get set of sympy symbols.
symbols = []
for expr in self.ravel():
for arg in expr.args:
if isinstance(arg, Symbol): symbols.append(arg)
# SymPy symbols and names.
self.symbols = list(set(symbols))
self.names = [s.name for s in self.symbols]
def __len__(self):
return len(self._expr_mat)
def __getitem__(self, key):
return self._expr_mat.__getitem__(key)
def __str__(self):
"""String representation."""
lines = []
for vec in self:
lines.append("(" + ",".join(str(e) for e in vec) + ")")
return ", ".join(lines)
def ravel(self):
"""Flat list, similar to np.ravel"""
return self._expr_mat.ravel()
@property
def mult(self):
"""Multiplicity"""
return len(self)
@property
def num_params(self):
"""The number of parameters."""
return len(self.symbols)
def params_from_frac_coords(self, frac_coords):
"""
Compute the Wyckoff parameters from the fractional coordinates.
"""
frac_coords = np.reshape(frac_coords, (-1, 3))
assert len(self) == len(frac_coords)
equations = []
for i in range(len(self)):
eqs = self[i, :] - frac_coords[i, :]
equations.extend(eqs)
# Solve the problem.
from sympy.solvers import solve
d = solve(equations, self.symbols)
#print("solution: ", d)
# Return results in a dict with symbol names.
if not d:
return None
return {symbol.name: d[symbol] for symbol in self.symbols}
def error_of_params(self, params, frac_coords):
"""
Return a numpy array with the difference between the
wyckoff positions computed from the sympy expressions and the input frac_coords
"""
_validate_params(params, self)
frac_coords = np.reshape(frac_coords, (-1, 3))
assert len(self) == len(frac_coords)
frac_errors = np.empty((self.mult, 3))
for i in range(len(self)):
for j in range(3):
frac_errors[i,j] = self[i,j].subs(params) - frac_coords[i,j]
# Modulo lattice vector (with tolerance?)
return frac_errors % 1
class Wyckoff(object):
"""
"""
#@classmethod
#def from_site2wpos_ordict(cls, site2wpos):
# return cls(site2wpos)
def __init__(self, site2wpos):
# TODO: Ordered dict.
d = {}
for k, s in site2wpos.items():
d[k] = WyckoffPositions.from_string(s)
self.site2wpos = d
def __str__(self):
"""String representation."""
lines = []
app = lines.append
app("Wyckoff positions:")
for site, wpos in self.site2wpos.items():
app("%s site:\n\t%s" % (site, wpos))
return "\n".join(lines)
def generate_structure(self, lattice, site2params, to_unit_cell=False, struct_cls=None):
"""
Generate structure object from `lattice` and dictionary `site2params`
"""
if not isinstance(site2params, OrderedDict):
raise ValueError("Please use a OrderedDict for site2params so that\n" +
"the algorithm used to build the structure is deterministic.")
# Build species and coords arrays.
species, coords = [], []
for elsym, wpos in self.site2wpos.items():
params = site2params[elsym]
_validate_params(params, wpos)
for vec_expr in wpos:
species.append(elsym)
# Evaluate sympy expression with params.
coords.append([float(expr.subs(params)) for expr in vec_expr])
#for sp, c in zip(species, coords):
# print(sp, c, type(c), type(c[0]))
from abipy.core.structure import Structure
cls = struct_cls if struct_cls is not None else Structure
return cls(lattice, species, coords, validate_proximity=True,
to_unit_cell=to_unit_cell, coords_are_cartesian=False, site_properties=None)
def find_params(self, structure):
"""
Compute the value of the parameter given a Structure object.
"""
frac_coords = structure.get_symbol2coords()
# Solve the problem.
params = {}
for elsym, wpos in self.site2wpos.items():
params[elsym] = wpos.params_from_frac_coords(frac_coords[elsym])
# Return results in a dict with symbol names.
return params
def error_of_params(self, params, structure):
frac_coords = structure.get_symbol2coords()
for elsym, wpos in self.site2wpos.items():
frac_errors = wpos.error_of_params(params[elsym], frac_coords[elsym])
print(frac_errors)
class SiteSymmetries(Has_Structure):
def __init__(self, structure):
@ -265,13 +62,27 @@ class SiteSymmetries(Has_Structure):
if len(rotations) != 0:
herm_symbol, ptg_num, trans_mat = spglib.get_pointgroup(rotations)
self.sitesym_labels.append("%s (#%d) ord: %d" % (herm_symbol.strip(), ptg_num, len(rotations)))
self.sitesym_labels.append("%s (#%d,nsym:%d)" % (herm_symbol.strip(), ptg_num, len(rotations)))
#for irred_isite in self.irred_isites:
# for isite_eq, rm1, tau, l0 in self.eq_sites[irred_isite]:
# # isite_eq = rm1(irred_site - tau) + l0
@property
def structure(self):
"""|Structure| object."""
return self._structure
def __str__(self):
return self.to_string()
def to_string(self, verbose=0):
"""String representation with verbosity level verbose."""
lines = []; app = lines.append
app(self.structure.to_string(verbose=verbose, title="Structure"))
return "\n".join(lines)
def get_wyckoff_dataframe(self, view="all", select_symbols=None, decimals=5, verbose=0):
"""
Find Wyckoff positions.
@ -304,7 +115,13 @@ class SiteSymmetries(Has_Structure):
l0 = indsym[iatom, isym, :3]
l0 = sp.Matrix(l0)
m = rm1 * (vector - tau) - (l0 + vector)
if verbose: sp.pprint(m)
if verbose:
print(92 * "=")
print("System of linear equations for iatom %d, %s" % (iatom, repr(site)))
sp.pprint(m)
print("Rotation:")
sp.pprint(rm1)
system.append(m)
# Solve system of linear equations.
@ -322,17 +139,17 @@ class SiteSymmetries(Has_Structure):
d["wyckoff"] = wlabel
d["site_symmetry"] = sitesym_labels[iatom]
for s in frac_symbols:
d[s] = s
d[str(s)] = str(s)
if len(solutions) > 1:
cprint("Found multiple solutions for iatom %d" % iatom, "red")
d.update(**solutions[0])
d.update({str(k): str(v) for k, v in solutions[0].items()})
rows.append(d)
import pandas as pd
df = pd.DataFrame(rows, index=None, columns=list(rows[0].keys()) if rows else None)
return df
def get_tensor_rank2_dataframe(self, view="all", select_symbols=None, decimal=5, verbose=0):
def get_tensor_rank2_dataframe(self, view="all", select_symbols=None, decimals=5, verbose=0):
"""
Args:
@ -349,9 +166,11 @@ class SiteSymmetries(Has_Structure):
sitesym_labels = self.structure.spget_site_symmetries()
# Symmetric tensor.
axx, ayy, azz, axy, axz, ayz = symbols = sp.symbols('axx, ayy, azz, axy, axz, ayz')
tensor = sp.Matrix(([axx, axy, axz], [axy, ayy, ayz], [axz, ayz, azz]))
# Symmetric tensor in reduced coordinates (direct lattice)
# Operations in reduced coords are given by integer matrices and this facilitates the solution
# of the system of equations with sympy.
Txx, Tyy, Tzz, Txy, Txz, Tyz = symbols = sp.symbols('Txx, Tyy, Tzz, Txy, Txz, Tyz')
tensor = sp.Matrix(([Txx, Txy, Txz], [Txy, Tyy, Tyz], [Txz, Tyz, Tzz]))
indsym = self.structure.indsym
rows = []
@ -361,7 +180,14 @@ class SiteSymmetries(Has_Structure):
for isym, rotf in enumerate(self.sp_symrel):
if indsym[iatom, isym, 3] != iatom: continue
m = rotf * tensor * rotf.T - tensor
if verbose: sp.pprint(m)
if verbose:
print(92 * "=")
print("System of linear equations for iatom %d, %s" % (iatom, repr(site)))
sp.pprint(m)
print("Rotation:")
sp.pprint(rotf)
print(92 * "=")
system.append(m)
# Solve system of linear equations.
@ -379,10 +205,10 @@ class SiteSymmetries(Has_Structure):
d["wyckoff"] = wlabel
d["site_symmetry"] = sitesym_labels[iatom]
for s in symbols:
d[s] = s
d[str(s)] = str(s)
if len(solutions) > 1:
cprint("Found multiple solutions for iatom %d" % iatom, "red")
d.update(**solutions[0])
d.update({str(k): str(v) for k, v in solutions[0].items()})
rows.append(d)
import pandas as pd

View File

@ -604,8 +604,7 @@ class Structure(pymatgen.Structure, NotebookWriter):
return("\n".join(lines))
def get_conventional_standard_structure(self, international_monoclinic=True,
symprec=1e-3, angle_tolerance=5):
def get_conventional_standard_structure(self, international_monoclinic=True, symprec=1e-3, angle_tolerance=5):
"""
Gives a structure with a conventional cell according to certain
standards. The standards are defined in :cite:`Setyawan2010`
@ -638,6 +637,18 @@ class Structure(pymatgen.Structure, NotebookWriter):
return self.__class__.as_structure(standardized_structure)
def refine(self, symprec=1e-3, angle_tolerance=5):
"""
Get the refined structure based on detected symmetry. The refined
structure is a *conventional* cell setting with atoms moved to the
expected symmetry positions.
Returns: Refined structure.
"""
sym_finder = SpacegroupAnalyzer(structure=self, symprec=symprec, angle_tolerance=angle_tolerance)
new = sym_finder.get_refined_structure()
return self.__class__.as_structure(new)
def abi_sanitize(self, symprec=1e-3, angle_tolerance=5, primitive=True, primitive_standard=False):
"""
Returns a new structure in which:
@ -658,8 +669,7 @@ class Structure(pymatgen.Structure, NotebookWriter):
# Refine structure
if symprec is not None and angle_tolerance is not None:
sym_finder = SpacegroupAnalyzer(structure=structure, symprec=symprec, angle_tolerance=angle_tolerance)
structure = sym_finder.get_refined_structure()
structure = structure.refine(symprec=symprec, angle_tolerance=angle_tolerance)
# Convert to primitive structure.
if primitive:
@ -932,13 +942,14 @@ class Structure(pymatgen.Structure, NotebookWriter):
"Use `overwrite=True` to allow modification."))
msg = ("Structure object does not have symmetry operations computed from Abinit.\n"
"Will call spglib to get symmetry operations.")
"Calling spglib to get symmetry operations.")
cprint(msg, "magenta")
spglib_data = SpacegroupAnalyzer(self).get_symmetry_dataset()
spgid = spglib_data["number"]
symrel, tnons = spglib_data["rotations"], spglib_data["translations"]
symafm = [1] * len(symrel) # TODO: Anti-ferromagnetic symmetries are not supported by spglib
# TODO: Anti-ferromagnetic symmetries are not supported by spglib
symafm = [1] * len(symrel)
abispg = AbinitSpaceGroup(spgid, symrel, tnons, symafm, has_timerev, inord="C")
self.set_abi_spacegroup(abispg)
@ -972,7 +983,7 @@ class Structure(pymatgen.Structure, NotebookWriter):
@lazy_property
def site_symmetries(self):
from abipy.core.wyckoff import SiteSymmetries
from abipy.core.site_symmetries import SiteSymmetries
return SiteSymmetries(self)
# TODO: site_symmetry or spget_site_symmetries?
@ -984,13 +995,13 @@ class Structure(pymatgen.Structure, NotebookWriter):
sitesym_labels = []
for iatom, site in enumerate(self):
rotations = [symrel[isym] for isym in range(nsym) if
indsym[iatom, isym, 3] == iatom and symafm[isym] == 1]
indsym[iatom, isym, 3] == iatom and symafm[isym] == +1]
# Passing a 0-length rotations list to spglib can segfault.
herm_symbol, ptg_num = "1", 1
if len(rotations) != 0:
herm_symbol, ptg_num, trans_mat = spglib.get_pointgroup(rotations)
sitesym_labels.append("%s (#%d) nsym:%d" % (herm_symbol.strip(), ptg_num, len(rotations)))
sitesym_labels.append("%s (#%d,nsym:%d)" % (herm_symbol.strip(), ptg_num, len(rotations)))
return sitesym_labels
@ -1857,32 +1868,6 @@ class Structure(pymatgen.Structure, NotebookWriter):
return "\n".join(lines)
#def ksampling_from_jhudb(self, **kwargs):
# from pymatgen.ext.jhu import get_kpoints
# __doc__ = get_kpoints.__doc__
# kpoints = get_kpoints(self, **kwargs)
# print(kpoints)
# print(kpoints.style)
# print("num_kpts", kpoints.num_kpts)
# d = {"kptopt": 0,
# "kpt": kpoints.kpts,
# "nkpt": kpoints.num_kpts,
# #"kptnrm": kptnrm,
# "wtk": kpoints.kpts_weights,
# "shiftk": kpoints.kpts_shift,
# "chksymbreak": 0,
# }
# print(d)
# #from pymatgen.io.abinit.abiobjects import KSampling
# #return KSampling(mode=KSamplingModes.automatic,
# # num_kpts= 0,
# # kpts=((1, 1, 1),),
# # kpt_shifts=(0.5, 0.5, 0.5),
# # kpts_weights=None, use_symmetries=True, use_time_reversal=True, chksymbreak=None,
# # comment=None)
def calc_ksampling(self, nksmall, symprec=0.01, angle_tolerance=5):
"""
Return the k-point sampling from the number of divisions ``nksmall`` to be used for

View File

@ -0,0 +1,56 @@
"""Tests for core.site_symmetries module"""
from __future__ import print_function, division, unicode_literals, absolute_import
import unittest
import os
import numpy as np
from abipy.core.testing import AbipyTest
from abipy.core.structure import Structure
from abipy.core.site_symmetries import SiteSymmetries
import abipy.data as abidata
class TestSiteSymmetries(AbipyTest):
"""Unit tests for SiteSymmetries."""
def test_si(self):
"""Testing wyckoff positions for Si2"""
si = Structure.from_file(abidata.cif_file("si.cif"))
ss = si.site_symmetries
repr(ss); str(ss)
assert ss.to_string(verbose=2)
df = ss.get_wyckoff_dataframe(verbose=2)
self.assert_equal(np.array(df["xfrac"].values, dtype=float), [0, 0.25])
self.assert_equal(np.array(df["yfrac"].values, dtype=float), [0, 0.25])
self.assert_equal(np.array(df["zfrac"].values, dtype=float), [0, 0.25])
#0 -43m (#31) nsym:24 0 0 0
#1 -43m (#31) nsym:24 0.250000000000000 0.250000000000000 0.250000000000000
df = ss.get_tensor_rank2_dataframe(verbose=2)
ref = ["Tzz", "Tzz"]
self.assert_equal(df["Txx"].values, ref)
self.assert_equal(df["Tyy"].values, ref)
ref = ["-Tzz/3", "-Tzz/3"]
self.assert_equal(df["Txy"].values, ref)
self.assert_equal(df["Txz"].values, ref)
self.assert_equal(df["Tyz"].values, ref)
def test_alpha_sio2(self):
"""Testing wyckoff positions for alpha-SiO2"""
asi02 = Structure.from_file(os.path.join(abidata.dirpath, "refs", "mp-7000_DDB.bz2"))
ss = asi02.site_symmetries
df = ss.get_wyckoff_dataframe(verbose=2)
df[df["element"] == "Si"]
self.assert_equal(df["xfrac"].values, ["xfrac", "yfrac", "0.0"])
self.assert_equal(df["yfrac"].values, ["0.0", "yfrac", "yfrac"])
self.assert_equal(np.array(df["zfrac"].values, dtype=float), [0.833335, 0.5, 0.166665])
#df = ss.get_tensor_rank2_dataframe()
#ref = ["Tzz", "Tzz"]
#self.assert_equal(df["Txx"].values, ref)
#self.assert_equal(df["Tyy"].values, ref)
#ref = ["-Tzz/3", "-Tzz/3"]
#self.assert_equal(df["Txy"].values, ref)
#self.assert_equal(df["Txz"].values, ref)
#self.assert_equal(df["Tyz"].values, ref)

View File

@ -1,61 +0,0 @@
"""Tests for core.density module"""
from __future__ import print_function, division, unicode_literals, absolute_import
import unittest
from collections import OrderedDict
from abipy.core.testing import AbipyTest
from abipy.core.structure import Lattice
from abipy.core.wyckoff import Wyckoff
class TestWyckoff(AbipyTest):
"""Unit tests for Wyckoff."""
def test_sio2(self):
"""Testing Wyckoff positions with SiO2 (requires sympy)"""
try:
import sympy
except ImportError:
raise unittest.SkipTest("sympy is not installed")
# http://www.cryst.ehu.es/cgi-bin/cryst/programs/nph-wp-list?gnum=152
site2wpos = {}
#3 b .2.
#site2wpos["Si"] = "(x,0,5/6) (0,x,1/6) (-x,-x,1/2)"
#3 a .2.
site2wpos["Si"] = "[x,0,1/3], (0,x,2/3), (-x,-x,0)"
#6 c 1
site2wpos["O"] = """(x,y,z), (-y,x-y,z+1/3), (-x+y,-x,z+2/3), (y,x,-z),
(x-y,-y,-z+2/3), (-x,-x+y,-z+1/3)"""
wyckoff = Wyckoff(site2wpos)
repr(wyckoff); str(wyckoff)
site2params = OrderedDict([
("Si", dict(x=0.4763)),
("O", dict(x=0.1588, y=0.7439, z=0.4612)),
#("O", dict(x=0.1588, y=0.7439, z=0.4612, yyy=3)),
])
lattice = Lattice.hexagonal(a=4.971, c=5.473)
for to_unit_cell in [False]:
#for to_unit_cell in [False, True]:
structure = wyckoff.generate_structure(lattice, site2params, to_unit_cell=to_unit_cell)
print("to_unit_cell %s\n" % to_unit_cell, structure)
#from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
#spga = SpacegroupAnalyzer(structure)
#structure = spga.get_refined_structure()
#print("Refined:", structure)
wyck_params = wyckoff.find_params(structure)
#structure.perturb(distance=0.01)
#wyck_params = wyckoff.find_params(structure)
print("Error of params")
wyckoff.error_of_params(wyck_params, structure)
for site, params in site2params.items():
print(wyck_params[site])
assert wyck_params[site] == params

View File

@ -46,11 +46,11 @@ class _Component(object):
class MsqDos(Has_Structure):
"""
This object stores the generalized phonon DOS with the mean square displacement tensor in cartesian coords.
This object stores the generalized phonon DOS with the mean square displacement tensor in CARTESIAN coords.
This DOS-like quantity allows one to calculate Debye Waller factors as a function of T
by integration with 1/omega and the Bose-Einstein factor.
See :cite:`Lee1995` for the further details about the internal implementation and
:cite:`Trueblood1996` for the different conventions used by crystallographers
:cite:`Trueblood1996` for the different conventions used by crystallographers.
"""
C = _Component
ALL_COMPS = OrderedDict([
@ -73,7 +73,7 @@ class MsqDos(Has_Structure):
Arg:
structure: |Structure| object.
wmesh: Frequency mesh
values: (natom, 3, 3, nomega) arrays with generalized DOS.
values: (natom, 3, 3, nomega) arrays with generalized DOS in cart coords.
amu_symbol: Dictionary element.symbol -> mass in atomic units.
"""
self._structure = structure
@ -120,7 +120,7 @@ class MsqDos(Has_Structure):
Compute mean square displacement for each atom in `iatom_list` as a function of T (bohr^2).
Args:
tmesh: array-like with temperatures in Kelvin degrees
tmesh: array-like with temperatures in Kelvin.
iatom_list: List of atom sites to comput. None for all.
what_list:
"""
@ -252,7 +252,7 @@ class MsqDos(Has_Structure):
def write_cif_file(self, filepath, temp=300):
"""
Write CIF file with structure info and anisotropic U tensor in CIF format.
Write CIF file with structure and anisotropic U tensor in CIF format.
Args:
filepath: Name of CIF file. If None, a temporary filepath is created.
@ -275,7 +275,7 @@ class MsqDos(Has_Structure):
In the Vesta GUI, select: Properties -> Atoms -> Show as displament ellipsoids.
"""
filepath = self.write_cif_file(filepath=None, temp=temp)
print("Writing structure + Debye-Waller tensor in CIF format for T = %s to file: %s" % (temp, filepath))
cprint("Writing structure + Debye-Waller tensor in CIF format for T = %s to file: %s" % (temp, filepath), "green")
from abipy.iotools import Visualizer
visu = Visualizer.from_name("vesta")
@ -318,15 +318,16 @@ _atom_site_aniso_U_12""".splitlines()
return s + "\n".join(aniso_u)
def check_site_symmetries(self, temp=300, verbose=0):
"""
Check symmetries of the displacement tensor at temperature `temp`.
Return maximum error.
"""
msq = self.get_msq_tmesh([float(temp)], what_list="displ")
values = getattr(msq, "displ")
values_cart = getattr(msq, "displ")
natom = len(self.structure)
values = np.reshape(values, (natom, 3, 3))
values_cart = np.reshape(values_cart, (natom, 3, 3))
from abipy.core.wyckoff import SiteSymmetries
ss = SiteSymmetries(self.structure)
return ss.check_site_symmetries(values, verbose=verbose)
return self.structure.site_symmetries.check_site_symmetries(values_cart, verbose=verbose)
def _get_components(self, components):
"""

View File

@ -2833,7 +2833,7 @@ class PhdosFile(AbinitNcFile, Has_Structure, NotebookWriter):
msqd_dos = None
if msqd_dos is not None:
yield msqd_dos.plot(units=units, show=False)
yield msqd_dos.plot_bfactors(show=False)
yield msqd_dos.plot_tensor(show=False)
def write_notebook(self, nbpath=None):
"""

View File

@ -13,7 +13,6 @@ from abipy.dfpt.phonons import PhononBands, get_dyn_mat_eigenvec, match_eigenvec
from abipy.abio.inputs import AnaddbInput
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt
from pymatgen.core.units import bohr_to_angstrom, eV_to_Ha
from pymatgen.symmetry.bandstructure import HighSymmKpath
class SoundVelocity(Has_Structure, NotebookWriter):
@ -108,8 +107,7 @@ class SoundVelocity(Has_Structure, NotebookWriter):
)
if not directions:
hs = HighSymmKpath(ddb.structure, symprec=1e-2)
hs = ddb.structure.hsym_kpath
kpath = hs.kpath
directions = []

View File

@ -227,9 +227,11 @@ codes), a looser tolerance of 0.1 (the value used in Materials Project) is often
help="Extract/Compute Abinit space group from file with structure.")
p_abispg.add_argument("-t", "--tolsym", type=float, default=None, help="""\
Gives the tolerance on the atomic positions (reduced coordinates), primitive vectors, or magnetization,
to be considered equivalent, thanks to symmetry operations. This is used in the recognition of the set
to be considered equivalent, thanks to symmetry operations. This value is used by ABINIT in the recognition of the set
of symmetries of the system, or the application of the symmetry operations to generate from a reduced set of atoms,
the full set of atoms. Note that a value larger than 0.01 is considered to be unacceptable.""")
The internal default is 1e-8. Setting tolsym to a value larger than 1e-8 will make Abinit detect the spacegroup within
this tolerance and re-symmetrize the input structure. This option is useful if the structure has been taken from a CIF
file that does not have enough significant digits.""")
p_abispg.add_argument("-d", "--diff-mode", type=str, default="table", choices=["table", "diff"],
help="Select diff output format.")
@ -283,12 +285,16 @@ Has to be all integers. Several options are possible:
p_proto.add_argument("--angle-tol", default=5, type=float, help="angle tolerance.")
# Subparser for wyckoff.
p_wyckoff = subparsers.add_parser('wyckoff', parents=[copts_parser, path_selector],
p_wyckoff = subparsers.add_parser('wyckoff', parents=[copts_parser, spgopt_parser, path_selector],
help="Print wyckoff positions. WARNING: still under development!")
p_wyckoff.add_argument("--refine", default=False, action="store_true",
help="Use spglib to refine structure before computation")
# Subparser for tensor_site.
p_tensor_site = subparsers.add_parser('tensor_site', parents=[copts_parser, path_selector],
p_tensor_site = subparsers.add_parser('tensor_site', parents=[copts_parser, spgopt_parser, path_selector],
help="Print symmetry properties of tensors due to site-symmetries. WARNING: still under development!")
p_tensor_site.add_argument("--refine", default=False, action="store_true",
help="Use spglib to refine structure before computation")
# Subparser for neighbors.
p_neighbors = subparsers.add_parser('neighbors', parents=[copts_parser, path_selector],
@ -498,20 +504,25 @@ def main():
elif options.command == "abispg":
structure = abilab.Structure.from_file(options.filepath)
check_ordered_structure(structure)
spgrp = structure.abi_spacegroup
abi_spg = structure.abi_spacegroup
if spgrp is not None:
if abi_spg is not None and options.tolsym is None:
print(structure.spget_summary(verbose=options.verbose))
else:
# Here we compare Abinit wrt spglib. If spgrp is None, we create a temporary
# Here we compare Abinit wrt spglib. If abi_spg is None, we create a temporary
# task to run the code in dry-run mode.
print("FILE does not contain Abinit symmetry operations.")
print("Calling Abinit in --dry-run mode with chkprim = 0 to get space group.")
if abi_spg is None:
print("FILE does not contain Abinit symmetry operations.")
cprint("Calling Abinit in --dry-run mode with chkprim = 0 to get space group.")
if options.tolsym is not None and options.tolsym > 1e-8:
cprint("Crystal structure will be re-symmetrized by Abinit with tolsym: %s" % options.tolsym, "yellow")
from abipy.data.hgh_pseudos import HGH_TABLE
gsinp = factories.gs_input(structure, HGH_TABLE, spin_mode="unpolarized")
gsinp["chkprim"] = 0
abistructure = gsinp.abiget_spacegroup(tolsym=options.tolsym)
print(abistructure.spget_summary(verbose=options.verbose))
print("")
diff_structures([structure, abistructure], mode=options.diff_mode,
headers=["Input structure", "After Abinit symmetrization"], fmt="abivars")
@ -659,17 +670,23 @@ def main():
elif options.command == "wyckoff":
structure = abilab.Structure.from_file(options.filepath)
from abipy.core.wyckoff import SiteSymmetries
ss = SiteSymmetries(structure)
df = ss.get_wyckoff_dataframe(view="all", select_symbols=None, verbose=options.verbose)
abilab.print_dataframe(df)
if options.refine:
print("Refining structure with symprec: %s, angle_tolerance: %s" % (options.symprec, options.angle_tolerance))
structure = structure.refine(symprec=options.symprec, angle_tolerance=options.angle_tolerance)
print(structure.spget_summary(verbose=options.verbose))
ss = structure.site_symmetries
df = ss.get_wyckoff_dataframe(verbose=options.verbose)
abilab.print_dataframe(df, title="\nWyckoff positions in reduced coordinates.")
elif options.command == "tensor_site":
structure = abilab.Structure.from_file(options.filepath)
from abipy.core.wyckoff import SiteSymmetries
ss = SiteSymmetries(structure)
df = ss.get_tensor_rank2_dataframe(view="all", select_symbols=None, verbose=options.verbose)
abilab.print_dataframe(df)
if options.refine:
print("Refining structure with symprec: %s, angle_tolerance: %s" % (options.symprec, options.angle_tolerance))
structure = structure.refine(symprec=options.symprec, angle_tolerance=options.angle_tolerance)
print(structure.spget_summary(verbose=options.verbose))
ss = structure.site_symmetries
df = ss.get_tensor_rank2_dataframe(verbose=options.verbose)
abilab.print_dataframe(df, title="\nTensor components in reduced coordinates (rank 2, symmetric)")
elif options.command == "neighbors":
abilab.Structure.from_file(options.filepath).print_neighbors(radius=options.radius)
@ -757,8 +774,11 @@ def main():
#elif options.command == "kmesh_jhu":
# structure = abilab.Structure.from_file(options.filepath)
# ksampling = structure.ksampling_from_jhudb(kppra=1000)
# #print(ksampling)
# from pymatgen.ext.jhu import get_kpoints
# kpoints = get_kpoints(structure, min_distance=0, min_total_kpoints=1,
# kppra=None, gap_distance=7, remove_symmetry=None,
# include_gamma="auto", header="simple", incar=None)
# #print(kpoints)
elif options.command == "lgk":
structure = abilab.Structure.from_file(options.filepath)

View File

@ -124,10 +124,10 @@ core Package
:undoc-members:
:show-inheritance:
:mod:`wyckoff` Module
---------------------
:mod:`site_symmetries` Module
-----------------------------
.. automodule:: abipy.core.wyckoff
.. automodule:: abipy.core.site_symmetries
:members:
:undoc-members:
:show-inheritance:

View File

@ -72,9 +72,9 @@ By default, the installer creates the ``anaconda`` directory in your home.
Anaconda will add one line to your ``.bashrc`` to enable access to the anaconda executables.
Once the installation is completed, execute::
source ~/anaconda/bin/activate root
source ~/anaconda/bin/activate base
to activate the ``root`` environment.
to activate the ``base`` environment.
The output of ``which python`` should show that you are using the python interpreter provided by anaconda.
Use the conda_ command-line interface to install the packages not included in the official distribution.
@ -86,17 +86,17 @@ Remember that if a package is not available in the official conda repository, yo
download the package from one of the conda channels or use ``pip install`` if no conda package is available.
Fortunately there are conda channels providing all dependencies needed by AbiPy.
To install the pymatgen_ package from the matsci_ channel, use::
Now add ``conda-forge``, ``matsci`` and ``abinit`` to your conda channels with::
conda install pymatgen --channel matsci
conda config --add channels conda-forge
conda config --add channels matsci
conda config --add channels abinit
then install Abipy from the abinit-channel_ with::
These are the channels from which we will download pymatgen, abipy and abinit.
Finally, install AbiPy from the abinit-channel_ with::
conda install abipy --channel abinit
Visit `materials.sh <http://materials.sh>`_ for instructions on how to use the
matsci channel to install pymatgen and other packages.
Once you have completed the installation of AbiPy and pymatgen, open the ipython_ shell and type::
# make sure spglib library works

View File

@ -64,7 +64,7 @@ Use::
to read the structure from ``FILE`` and generate a CIF_ file (default behaviour).
The majority of the netcdf_ files produced by Abinit contain structural information
so this command can be used with netcdf ouput files as well as Abinit input/output
so this command can be used with netcdf output files as well as Abinit input/output
files and all the other formats supported by pymatgen e.g. POSCAR files.
Other formats can be specified with the ``-f`` option.
For example::
@ -101,7 +101,7 @@ to validate the input file with Abinit (requires ``manager.yml`` and, obviously,
The script provides other options to invoke Abinit
to get space group information, the list of k-points in the IBZ.
the list of atomic perturbations for phonons or the list of autoparal configurations.
See ``abinp.py --help`` for futher info.
See ``abinp.py --help`` for further info.
Print the warnings in the log file
----------------------------------
@ -232,6 +232,24 @@ to generate a template with the input variables defining the k-path
+0.62500 +0.25000 +0.62500 # U
+0.50000 +0.00000 +0.50000 # X
Re-symmetrize a structure when Abinit reports less symmetries than expected
---------------------------------------------------------------------------
Crystalline structures saved in text format (e.g. CIF files downloaded from
the Materials Project websites) may not have enough significant digits
and Abinit may not find the same spacegroup as the one reported by the source
as the default tolerance for symmetry detection in Abinit is tight (tolsym = 1e-8).
In this case, one can use the `abispg` option of abistruct.py to compute the spacegroup
with Abinit and a tolerance larger that the default value::
abistruct.py abispg problematic.cif --tolsym=1e-6
Hopefully, the code will detect the correct spacegroup, will re-symmetrize
the initial lattice vectors and atomic positions and print the new symmetrized structure to terminal.
Get neighbors for each atom in the unit cell out to a distance radius
---------------------------------------------------------------------
@ -402,7 +420,7 @@ Use::
abiview.py phbands out_PHBST.nc -web
to start a local webserver and open the HTML page inside the default browser
to start a local web server and open the HTML page inside the default browser
(the browser can be changed with the ``--browser`` option).
It is also possible to visualize the phonon modes starting directly from a DDB_ file with::
@ -410,7 +428,7 @@ It is also possible to visualize the phonon modes starting directly from a DDB_
abiview.py ddb -web
In this case, AbiPy will invoke anaddb to produce the ``PHBST.nc`` file on an automatically
generated q-path and then start the webserver.
generated q-path and then start the web server.
Visualize the results of a structural relaxation
------------------------------------------------
@ -461,7 +479,7 @@ the AbiPy scripts are quite handy for a quick analysis of the results.
Compare multiple files
----------------------
The :ref:`abicomp.py` script is explicitely designed for this kind of task.
The :ref:`abicomp.py` script is explicitly designed for this kind of task.
It operates on multiple files (usually files with the same extension) and
either produces matplotlib_ plots or creates AbiPy robots providing methods
to analyze the results, perform convergence studies and build pandas DataFrames_.