Skip to content

Commit 973d99c

Browse files
committed
Compute A2(E) and B2(E) using full BZ instead of IBZ
1 parent c348e26 commit 973d99c

File tree

10 files changed

+291
-130
lines changed

10 files changed

+291
-130
lines changed

abipy/core/kpoints.py

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def kmesh_from_mpdivs(mpdivs, shifts, pbc=False, order="bz"):
176176
mpdivs: The three MP divisions.
177177
shifts: Array-like object with the MP shift.
178178
pbc: If True, periodic images of the k-points will be included i.e. closed mesh.
179-
order: "unit_cell" if the kpoint coordinates must be in [0,1)
179+
order: "unit_cell" if the kpoint coordinates must be in [0, 1)
180180
"bz" if the kpoint coordinates must be in [-1/2, +1/2)
181181
"""
182182
shifts = np.reshape(shifts, (-1, 3))
@@ -220,6 +220,7 @@ def map_grid2ibz(structure, ibz, ngkpt, has_timrev, pbc=False):
220220
if abispg is None:
221221
raise ValueError("Structure does not contain Abinit spacegroup info!")
222222

223+
223224
# Extract rotations in reciprocal space (FM part).
224225
symrec_fm = [o.rot_g for o in abispg.fm_symmops]
225226

@@ -242,27 +243,15 @@ def map_grid2ibz(structure, ibz, ngkpt, has_timrev, pbc=False):
242243

243244
if np.any(bzgrid2ibz == -1):
244245
#for ik_bz, ik_ibz in enumerate(self.bzgrid2ibz): print(ik_bz, ">>>", ik_ibz)
245-
msg = "Found %s/%s invalid entries in bzgrid2ibz array" % ((bzgrid2ibz == -1).sum(), bzgrid2ibz.size)
246-
msg += "This can happen if there an inconsistency between the input IBZ and ngkpt"
247-
msg += "ngkpt: %s, has_timrev: %s" % (str(ngkpt), has_timrev)
246+
msg = " Found %s/%s invalid entries in bzgrid2ibz array\n" % ((bzgrid2ibz == -1).sum(), bzgrid2ibz.size)
247+
msg += " This can happen if there is an inconsistency between the input IBZ and ngkpt\n"
248+
msg += " ngkpt: %s, has_timrev: %s\n" % (str(ngkpt), has_timrev)
249+
msg += f" {abispg=}\n"
248250
raise ValueError(msg)
249251

250252
bz2ibz = bzgrid2ibz.flatten()
251253
return bz2ibz
252254

253-
"""
254-
for ik_bz, kbz in enumerate(bz):
255-
found = False
256-
for ik_ibz, kibz in enumerate(ibz):
257-
if found: break
258-
for symmop in structure.spacegroup:
259-
krot = symmop.rotate_k(kibz)
260-
if issamek(krot, kbz):
261-
bz2ibz[ik_bz] = ik_ibz
262-
found = True
263-
break
264-
"""
265-
266255

267256
def has_timrev_from_kptopt(kptopt):
268257
"""

abipy/core/structure.py

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from monty.functools import lazy_property
2121
from monty.string import is_string, marquee, list_strings
2222
from monty.termcolor import cprint
23-
from monty.dev import deprecated
23+
#from monty.dev import deprecated
2424
from pymatgen.core.structure import Structure as pmg_Structure
2525
from pymatgen.core.sites import PeriodicSite
2626
from pymatgen.core.lattice import Lattice
@@ -29,7 +29,7 @@
2929
from abipy.core.symmetries import AbinitSpaceGroup
3030
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt, add_plotly_fig_kwargs
3131
from abipy.iotools import as_etsfreader, Visualizer
32-
32+
from abipy.tools.typing import Figure
3333

3434

3535
__all__ = [
@@ -170,14 +170,48 @@ def display_structure(obj, **kwargs):
170170
return nbjsmol_display(structure.to(fmt="cif"), ext=".cif", **kwargs)
171171

172172

173-
class Structure(pmg_Structure, NotebookWriter):
173+
def get_structures_from_file(filepath: PathLike, index) -> list[Structure]:
174174
"""
175-
Extends :class:`pymatgen.core.structure.Structure` with Abinit-specific methods.
175+
"""
176+
#if index is None:
177+
# index = -1
178+
179+
if filepath.endswith(".traj"):
180+
# ASE trajectory file.
181+
from ase.atoms import Atoms
182+
from ase.io import read
183+
atoms_list = read(filepath, index=index)
184+
if not isinstance(atoms_list, list):
185+
assert isinstance(atoms_list, Atoms)
186+
atoms_list = [atoms_list]
187+
188+
# TODO: HIST.nc and XDATCAR
189+
#elif filepath.endswith("_HIST.nc"):
190+
# from abipy.dynamics.hist import HistFile
191+
# with HistFile(filepath) as hist:
192+
# return hist.read_structures(index)
193+
194+
#elif filepath.endswith("XDATCAR"):
195+
196+
else:
197+
raise NotImplementedError(f"Don't know how to extract structures with index from {filepath=}")
198+
199+
return [Structure.as_structure(atoms) for atoms in atoms_list]
176200

177-
A jupyter_ notebook documenting the usage of this object is available at
178-
<https://nbviewer.jupyter.org/github/abinit/abitutorials/blob/master/abitutorials/structure.ipynb>
179201

180-
For the pymatgen project see :cite:`Ong2013`.
202+
def get_first_and_last_structure_from_file(filepath: PathLike) -> tuple[Structure]:
203+
"""
204+
Helper function to read the first and the last structure from a trajectory file.
205+
Simplified wrapper around get_structures_from_file.
206+
"""
207+
first_structure = get_structures_from_file(filepath, index=0)[0]
208+
last_structure = get_structures_from_file(filepath, index=-1)[0]
209+
return first_structure, last_structure
210+
211+
212+
class Structure(pmg_Structure, NotebookWriter):
213+
"""
214+
Extends :class:`pymatgen.core.structure.Structure` with Abinit-specific methods.
181215
182216
.. rubric:: Inheritance Diagram
183217
.. inheritance-diagram:: Structure
@@ -277,8 +311,7 @@ def from_file(cls, filepath: str, primitive: bool = False, sort: bool = False) -
277311
if closeit: ncfile.close()
278312

279313
elif filepath.endswith(".abi") or filepath.endswith(".in"):
280-
# Abinit input file.
281-
# Here I assume that the input file contains a single structure.
314+
# Abinit input file. Here I assume that the input file contains a single structure.
282315
from abipy.abio.abivars import AbinitInputFile
283316
return AbinitInputFile.from_file(filepath).structure
284317

@@ -291,6 +324,7 @@ def from_file(cls, filepath: str, primitive: bool = False, sort: bool = False) -
291324
#print("initial_structures:\n", out.initial_structures, "\nfinal_structures:\n", out.final_structures)
292325
if out.final_structures: return out.final_structure
293326
if out.initial_structures: return out.initial_structure
327+
294328
raise ValueError("Cannot find structure in Abinit output file `%s`" % filepath)
295329

296330
elif filepath.endswith(".abivars") or filepath.endswith(".ucell"):
@@ -323,8 +357,7 @@ def from_file(cls, filepath: str, primitive: bool = False, sort: bool = False) -
323357
from ase.io import read
324358
except ImportError as exc:
325359
raise RuntimeError("ase is required to read xyz files. Use `pip install ase`") from exc
326-
atoms = read(filepath)
327-
return cls.as_structure(atoms)
360+
return cls.as_structure(read(filepath))
328361

329362
else:
330363
# Invoke pymatgen and change class. Note that AbinitSpacegroup is missing here.
@@ -389,7 +422,6 @@ def from_ase_atoms(cls, atoms) -> Structure:
389422
@property
390423
def n_elems(self) -> int:
391424
"""Number of types of atoms."""
392-
393425
return len(self.types_of_species)
394426

395427
def to_ase_atoms(self, calc=None):
@@ -508,7 +540,6 @@ def zincblende(cls, a, species, units="ang", **kwargs) -> Structure:
508540
kwargs: All keyword arguments accepted by :class:`pymatgen.Structure`
509541
510542
Example::
511-
512543
Structure.zincblende(a, ["Zn", "S"])
513544
"""
514545
a = pmg_units.Length(a, units).to("ang")
@@ -532,7 +563,6 @@ def rocksalt(cls, a, species, units="ang", **kwargs) -> Structure:
532563
kwargs: All keyword arguments accepted by :class:`pymatgen.Structure`
533564
534565
Example::
535-
536566
Structure.rocksalt(a, ["Na", "Cl"])
537567
"""
538568
a = pmg_units.Length(a, units).to("ang")
@@ -570,10 +600,9 @@ def ABO3(cls, a, species, units="ang", **kwargs) -> Structure:
570600
@classmethod
571601
def from_abistring(cls, string: str) -> Structure:
572602
"""
573-
Initialize Structure from string with Abinit input variables.
603+
Initialize Structure from a string with Abinit input variables.
574604
"""
575605
from abipy.abio.abivars import AbinitInputFile, structure_from_abistruct_fmt
576-
577606
if "xred_symbols" not in string:
578607
# Standard (verbose) input file with znucl, typat etc.
579608
return AbinitInputFile.from_string(string).structure
@@ -679,12 +708,10 @@ def write_cif_with_spglib_symms(self, filename, symprec=1e-3, angle_tolerance=5.
679708
symprec (float): If not none, finds the symmetry of the structure
680709
and writes the cif with symmetry information. Passes symprec
681710
to the SpacegroupAnalyzer.
682-
significant_figures (int): Specifies precision for formatting of floats.
683-
Defaults to 8.
711+
significant_figures (int): Specifies precision for formatting of floats. Defaults to 8.
684712
angle_tolerance (float): Angle tolerance for symmetry finding. Passes
685-
angle_tolerance to the SpacegroupAnalyzer. Used only if symprec
686-
is not None.
687-
ret_string: True to return string
713+
angle_tolerance to the SpacegroupAnalyzer. Used only if symprec is not None.
714+
ret_string: True to return string.
688715
"""
689716
from pymatgen.io.cif import CifWriter
690717
cif_str = str(CifWriter(self,
@@ -722,7 +749,7 @@ def to_abivars(self, enforce_znucl=None, enforce_typat=None, **kwargs) -> dict:
722749

723750
@property
724751
def latex_formula(self) -> str:
725-
"""LaTeX formatted formula. E.g., Fe2O3 is transformed to Fe$_{2}$O$_{3}$."""
752+
"""LaTeX formatted formula: e.g., Fe2O3 is transformed to Fe$_{2}$O$_{3}$."""
726753
from pymatgen.util.string import latexify
727754
return latexify(self.formula)
728755

@@ -743,7 +770,6 @@ def get_abi_string(self, fmt: str = "abinit_input") -> str:
743770
fmt="abicell" is the lightweight format that uses `xred_symbols`
744771
This format can be used to include the structure in the input via the structure "abivars:FILE" syntax.
745772
"""
746-
747773
lines = []
748774
app = lines.append
749775

@@ -811,8 +837,8 @@ def get_panel(self, with_inputs=True, **kwargs):
811837
def get_conventional_standard_structure(self, international_monoclinic=True,
812838
symprec=1e-3, angle_tolerance=5) -> Structure:
813839
"""
814-
Gives a structure with a conventional cell according to certain
815-
standards. The standards are defined in :cite:`Setyawan2010`
840+
Gives a structure with a conventional cell according to certain standards.
841+
The standards are defined in :cite:`Setyawan2010`
816842
They basically enforce as much as possible norm(a1) < norm(a2) < norm(a3)
817843
818844
Returns: The structure in a conventional standardized cell
@@ -822,8 +848,7 @@ def get_conventional_standard_structure(self, international_monoclinic=True,
822848
return self.__class__.as_structure(new)
823849

824850
def abi_primitive(self, symprec=1e-3, angle_tolerance=5, no_idealize=0) -> Structure:
825-
#TODO: this should be moved to pymatgen in the get_refined_structure or so ...
826-
# to be considered in February 2016
851+
#TODO: this should be moved to pymatgen in the get_refined_structure or so ... to be considered in February 2016
827852
import spglib
828853
from pymatgen.io.ase import AseAtomsAdaptor
829854
try:
@@ -943,11 +968,6 @@ def abi_sanitize(self, symprec=1e-3, angle_tolerance=5,
943968

944969
return self.__class__.as_structure(structure)
945970

946-
#def relax(self, **kwargs) -> Structure:
947-
# new = super().relax(**kwargs)
948-
# new.__class__ = self.__class__
949-
# return new
950-
951971
def get_oxi_state_decorated(self, **kwargs) -> Structure:
952972
"""
953973
Use :class:`pymatgen.analysis.bond_valence.BVAnalyzer` to estimate oxidation states
@@ -976,24 +996,10 @@ def reciprocal_lattice(self):
976996
"""
977997
return self._lattice.reciprocal_lattice
978998

979-
@deprecated(message="lattice_vectors is deprecated and will be removed in abipy 1.0")
980-
def lattice_vectors(self, space="r"):
981-
"""
982-
Returns the vectors of the unit cell in Angstrom.
983-
984-
Args:
985-
space: "r" for real space vectors, "g" for reciprocal space basis vectors.
986-
"""
987-
if space.lower() == "r":
988-
return self.lattice.matrix
989-
if space.lower() == "g":
990-
return self.lattice.reciprocal_lattice.matrix
991-
raise ValueError("Wrong value for space: %s " % str(space))
992-
993999
def spget_lattice_type(self, symprec=1e-3, angle_tolerance=5) -> str:
9941000
"""
9951001
Call spglib to get the lattice for the structure, e.g., (triclinic,
996-
orthorhombic, cubic, etc.).This is the same than the
1002+
orthorhombic, cubic, etc.). This is the same than the
9971003
crystal system with the exception of the hexagonal/rhombohedral lattice
9981004
9991005
Args:
@@ -1627,7 +1633,7 @@ def plotly_bz(self, fig=None, pmg_path=True, with_labels=True, **kwargs):
16271633

16281634
@add_fig_kwargs
16291635
def plot_xrd(self, wavelength="CuKa", symprec=0, debye_waller_factors=None,
1630-
two_theta_range=(0, 90), annotate_peaks=True, ax=None, **kwargs):
1636+
two_theta_range=(0, 90), annotate_peaks=True, ax=None, **kwargs) -> Figure:
16311637
"""
16321638
Use pymatgen :class:`XRDCalculator` to show the XRD plot.
16331639

abipy/core/tests/test_structure.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,9 @@ def test_utils(self):
224224

225225
# Temporarily disables ad webserver is down.
226226
#if self.is_url_reachable("www.crystallography.net"):
227-
# mgb2_cod = Structure.from_cod_id(1526507, primitive=True)
228-
# assert mgb2_cod.formula == "Mg1 B2"
229-
# assert mgb2_cod.spget_lattice_type() == "hexagonal"
227+
mgb2_cod = Structure.from_cod_id(1526507, primitive=True)
228+
assert mgb2_cod.formula == "Mg1 B2"
229+
assert mgb2_cod.spget_lattice_type() == "hexagonal"
230230

231231
mgb2 = abidata.structure_from_ucell("MgB2")
232232
if self.has_ase():

abipy/dynamics/hist.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ def params(self) -> dict:
5959
def __str__(self) -> str:
6060
return self.to_string()
6161

62+
#def read_structures(self, index: str):
63+
6264
# TODO: Add more metadata.
6365
#@lazy_property
6466
#def nsppol(self):

abipy/electrons/ebands.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from abipy.core.func1d import Function1D
2929
from abipy.core.mixins import Has_Structure, NotebookWriter
3030
from abipy.core.kpoints import (Kpoint, KpointList, Kpath, IrredZone, KSamplingInfo, KpointsReaderMixin,
31-
Ktables, has_timrev_from_kptopt, map_grid2ibz) #, kmesh_from_mpdivs)
31+
Ktables, has_timrev_from_kptopt, map_grid2ibz, kmesh_from_mpdivs)
3232
from abipy.core.structure import Structure
3333
from abipy.iotools import ETSF_Reader
3434
from abipy.tools import duck
@@ -971,6 +971,29 @@ def has_timrev(self) -> bool:
971971
"""True if time-reversal symmetry is used in the BZ sampling."""
972972
return has_timrev_from_kptopt(self.kptopt)
973973

974+
def get_bz2ibz_bz_points(self, require_gamma_centered=False):
975+
"""
976+
Return mapping bz2ibz and the list of k-points in the BZ.
977+
978+
Args:
979+
require_gamma_centered: True if the k-mesh should be Gamma-centered
980+
"""
981+
err_msg = self.isnot_ibz_sampling(require_gamma_centered=require_gamma_centered)
982+
if err_msg:
983+
raise TypeError(err_msg)
984+
985+
if not self.kpoints.is_mpmesh:
986+
raise ValueError("Only Monkhorst-Pack meshes are supported")
987+
988+
ngkpt, shifts = self.kpoints.mpdivs_shifts
989+
print(f"{ngkpt = }, {shifts =}")
990+
# TODO: Handle shifts
991+
992+
bz2ibz = map_grid2ibz(self.structure, self.kpoints.frac_coords, ngkpt, self.has_timrev)
993+
bz_kpoints = kmesh_from_mpdivs(ngkpt, shifts)
994+
995+
return bz2ibz, bz_kpoints
996+
974997
def isnot_ibz_sampling(self, require_gamma_centered=False) -> str:
975998
"""
976999
Test whether the k-points in the band structure represent an IBZ with an associated k-mesh
@@ -5414,7 +5437,7 @@ def xcrysden_view(self): # pragma: no cover
54145437
from abipy.iotools.visualizer import Xcrysden
54155438
return Xcrysden(tmp_filepath)()
54165439

5417-
def to_bxsf(self, filepath, unit="eV") -> str:
5440+
def to_bxsf(self, filepath: str, unit: str="eV") -> str:
54185441
"""
54195442
Export the full band structure to ``filepath`` in BXSF format
54205443
suitable for the visualization of the Fermi surface with xcrysden_ (use ``xcrysden --bxsf FILE``).

0 commit comments

Comments
 (0)