Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
rkingsbury authored Jul 25, 2024
2 parents d7e72bf + 1bdca30 commit 8119b0e
Show file tree
Hide file tree
Showing 16 changed files with 350 additions and 109 deletions.
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
__pycache__/
.DS_Store
pymatgen.egg-info
dependencies/PyCifRW-3.3/PyCifRW.egg-info
dependencies/spglib*/pyspglib.egg-info
dependencies/spglib*/build
*.o
*.so
*.pyc
Expand All @@ -26,7 +23,6 @@ setuptools*
.cache
.tox
.eggs/
gulptmp_4_1
.coverage
.*_cache
# VS Code
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ci:

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.0
rev: v0.5.4
hooks:
- id: ruff
args: [ --fix, --unsafe-fixes ]
Expand All @@ -22,7 +22,7 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.1
rev: v1.11.0
hooks:
- id: mypy

Expand Down Expand Up @@ -65,6 +65,6 @@ repos:
args: [ --drop-empty-cells, --keep-output ]

- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.369
rev: v1.1.373
hooks:
- id: pyright
2 changes: 1 addition & 1 deletion src/pymatgen/analysis/local_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -3048,7 +3048,7 @@ def get_order_parameters(
norms[idx][j][kc] += 1

for m in range(n_neighbors):
if (m != j) and (m != k) and (not flag_xaxis):
if m not in {j, k} and (not flag_xaxis):
tmp = max(-1.0, min(np.inner(zaxis, rij_norm[m]), 1.0))
thetam = math.acos(tmp)
x_two_axis_tmp = gramschmidt(rij_norm[m], zaxis)
Expand Down
2 changes: 1 addition & 1 deletion src/pymatgen/analysis/magnetism/jahnteller.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ def _get_number_of_d_electrons(species: Species) -> float:

# taken from get_crystal_field_spin
elec = species.element.full_electronic_structure
if len(elec) < 4 or elec[-1][1] != "s" or elec[-2][1] != "d":
if len(elec) < 4 or elec[-2][1] != "s" or elec[-1][1] != "d":
raise AttributeError(f"Invalid element {species.symbol} for crystal field calculation.")
n_electrons = int(elec[-1][2] + elec[-2][2] - species.oxi_state) # type: ignore[operator]
if n_electrons < 0 or n_electrons > 10:
Expand Down
5 changes: 4 additions & 1 deletion src/pymatgen/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@
def _load_pmg_settings() -> dict[str, Any]:
settings: dict[str, Any] = {}

# PMG_CONFIG_FILE takes precedence over default settings location
settings_file = os.getenv("PMG_CONFIG_FILE") or SETTINGS_FILE

# Load .pmgrc.yaml file
yaml = YAML()
for file_path in (SETTINGS_FILE, OLD_SETTINGS_FILE):
for file_path in (settings_file, OLD_SETTINGS_FILE):
try:
with open(file_path, encoding="utf-8") as yml_file:
settings = yaml.load(yml_file) or {}
Expand Down
50 changes: 44 additions & 6 deletions src/pymatgen/core/periodic_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,28 @@

_pt_row_sizes = (2, 8, 8, 18, 18, 32, 32)

_madelung = [
(1, "s"),
(2, "s"),
(2, "p"),
(3, "s"),
(3, "p"),
(4, "s"),
(3, "d"),
(4, "p"),
(5, "s"),
(4, "d"),
(5, "p"),
(6, "s"),
(4, "f"),
(5, "d"),
(6, "p"),
(7, "s"),
(5, "f"),
(6, "d"),
(7, "p"),
]


@functools.total_ordering
@unique
Expand Down Expand Up @@ -422,11 +444,12 @@ def icsd_oxidation_states(self) -> tuple[int, ...]:
@property
def full_electronic_structure(self) -> list[tuple[int, str, int]]:
"""Full electronic structure as list of tuples, in order of increasing
principal (n) and angular momentum (l) quantum numbers.
energy level (according to the Madelung rule). Therefore, the final
element in the list gives the electronic structure of the valence shell.
For example, the electronic structure for Fe is represented as:
[(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
(3, "d", 6), (4, "s", 2)].
(4, "s", 2), (3, "d", 6)].
References:
Kramida, A., Ralchenko, Yu., Reader, J., and NIST ASD Team (2023). NIST
Expand All @@ -445,7 +468,13 @@ def parse_orbital(orb_str):
if data[0][0] == "[":
sym = data[0].replace("[", "").replace("]", "")
data = list(Element(sym).full_electronic_structure) + data[1:]
return data
# sort the final electronic structure by increasing energy level
return sorted(data, key=lambda x: _madelung.index((x[0], x[1])))

@property
def n_electrons(self) -> int:
"""Total number of electrons in the Element."""
return sum([t[-1] for t in self.full_electronic_structure])

@property
def valence(self) -> tuple[int | np.nan, int]:
Expand Down Expand Up @@ -1117,7 +1146,8 @@ def electronic_structure(self) -> str:
@property
def full_electronic_structure(self) -> list[tuple[int, str, int]]:
"""Full electronic structure as list of tuples, in order of increasing
principal (n) and angular momentum (l) quantum numbers.
energy level (according to the Madelung rule). Therefore, the final
element in the list gives the electronic structure of the valence shell.
For example, the electronic structure for Fe+2 is represented as:
[(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
Expand All @@ -1140,7 +1170,15 @@ def parse_orbital(orb_str):
if data[0][0] == "[":
sym = data[0].replace("[", "").replace("]", "")
data = list(Element(sym).full_electronic_structure) + data[1:]
return data
# sort the final electronic structure by increasing energy level
return sorted(data, key=lambda x: _madelung.index((x[0], x[1])))

# NOTE - copied exactly from Element. Refactoring / inheritance may improve
# robustness
@property
def n_electrons(self) -> int:
"""Total number of electrons in the Species."""
return sum([t[-1] for t in self.full_electronic_structure])

# NOTE - copied exactly from Element. Refactoring / inheritance may improve
# robustness
Expand Down Expand Up @@ -1319,7 +1357,7 @@ def get_crystal_field_spin(
raise ValueError("Invalid coordination or spin config")

elec = self.element.full_electronic_structure
if len(elec) < 4 or elec[-1][1] != "s" or elec[-2][1] != "d":
if len(elec) < 4 or elec[-2][1] != "s" or elec[-1][1] != "d":
raise AttributeError(f"Invalid element {self.symbol} for crystal field calculation")

assert self.oxi_state is not None
Expand Down
37 changes: 17 additions & 20 deletions src/pymatgen/electronic_structure/boltztrap.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
"""This module provides classes to run and analyze boltztrap on pymatgen band
structure objects. Boltztrap is a software interpolating band structures and
computing materials properties from this band structure using Boltzmann
semi-classical transport theory.
"""This module provides classes to run and analyze BoltzTraP on pymatgen band
structure objects. BoltzTraP is a software developed by Georg Madsen to
interpolate band structures and compute materials properties from this
band structure using Boltzmann semi-classical transport theory.
Boltztrap has been developed by Georg Madsen.
http://www.icams.de/content/research/software-development/boltztrap/
https://www.tuwien.at/en/tch/tc/theoretical-materials-chemistry/boltztrap
You need version 1.2.3 or higher
References are:
References:
Madsen, G. K. H., and Singh, D. J. (2006).
BoltzTraP. A code for calculating band-structure dependent quantities.
Computer Physics Communications, 175, 67-71
Expand Down Expand Up @@ -60,13 +57,13 @@


class BoltztrapRunner(MSONable):
"""This class is used to run Boltztrap on a band structure object."""
"""This class is used to run BoltzTraP on a band structure object."""

@requires(
which("x_trans"),
"BoltztrapRunner requires the executables 'x_trans' to be in PATH. Please download "
"Boltztrap at http://www.icams.de/content/research/software-development/boltztrap/ "
"and follow the instructions in the README to compile Bolztrap accordingly. "
"BoltzTraP at https://www.tuwien.at/en/tch/tc/theoretical-materials-chemistry/boltztrap "
"and follow the instructions in the README to compile BoltzTraP accordingly. "
"Then add x_trans to your path",
)
def __init__(
Expand Down Expand Up @@ -144,7 +141,7 @@ def __init__(
electron occupations. If the band structure comes from a soc
computation, you should set soc to True (default False)
doping:
the fixed doping levels you want to compute. Boltztrap provides
the fixed doping levels you want to compute. BoltzTraP provides
both transport values depending on electron chemical potential
(fermi energy) and for a series of fixed carrier
concentrations. By default, this is set to 1e16 to 1e22 in
Expand Down Expand Up @@ -734,7 +731,7 @@ def __init__(
bz_kpoints=None,
fermi_surface_data=None,
) -> None:
"""Constructor taking directly all the data generated by Boltztrap. You
"""Constructor taking directly all the data generated by BoltzTraP. You
won't probably use it directly but instead use the from_files and
from_dict methods.
Expand All @@ -760,7 +757,7 @@ def __init__(
each Fermi level in mu_steps]}
The units are m^3/C
doping: The different doping levels that have been given to
Boltztrap. The format is {'p':[],'n':[]} with an array of
BoltzTraP. The format is {'p':[],'n':[]} with an array of
doping levels. The units are cm^-3
mu_doping: Gives the electron chemical potential (or Fermi level)
for a given set of doping.
Expand Down Expand Up @@ -802,7 +799,7 @@ def __init__(
intrans: a dictionary of inputs e.g. {"scissor": 0.0}
carrier_conc: The concentration of carriers in electron (or hole)
per unit cell
dos: The dos computed by Boltztrap given as a pymatgen Dos object
dos: The dos computed by BoltzTraP given as a pymatgen Dos object
dos_partial: Data for the partial DOS projected on sites and
orbitals
vol: Volume of the unit cell in angstrom cube (A^3)
Expand Down Expand Up @@ -837,13 +834,13 @@ def __init__(
self.fermi_surface_data = fermi_surface_data

def get_symm_bands(self, structure: Structure, efermi, kpt_line=None, labels_dict=None):
"""Useful to read bands from Boltztrap output and get a BandStructureSymmLine object
"""Useful to read bands from BoltzTraP output and get a BandStructureSymmLine object
comparable with that one from a DFT calculation (if the same kpt_line is
provided). Default kpt_line and labels_dict is the standard path of high symmetry
k-point for the specified structure. They could be extracted from the
BandStructureSymmLine object that you want to compare with. efermi variable must
be specified to create the BandStructureSymmLine object (usually it comes from DFT
or Boltztrap calc).
or BoltzTraP calc).
"""
try:
if kpt_line is None:
Expand Down Expand Up @@ -1640,7 +1637,7 @@ def get_carrier_concentration(self):

def get_hall_carrier_concentration(self):
"""Get the Hall carrier concentration (in cm^-3). This is the trace of
the Hall tensor (see Boltztrap source code) Hall carrier concentration
the Hall tensor (see BoltzTraP source code) Hall carrier concentration
are not always exactly the same than carrier concentration.
Returns:
Expand Down Expand Up @@ -1770,7 +1767,7 @@ def parse_intrans(path_dir):
path_dir: (str) dir containing the boltztrap.intrans file
Returns:
dict: various inputs that had been used in the Boltztrap run.
dict: various inputs that had been used in the BoltzTraP run.
"""
intrans = {}
with open(f"{path_dir}/boltztrap.intrans") as file:
Expand Down
2 changes: 1 addition & 1 deletion src/pymatgen/io/aims/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def _parse_k_points(self) -> None:

line_start = self.reverse_search_for(["| K-points in task"])
line_end = self.reverse_search_for(["| k-point:"])
if (line_start == LINE_NOT_FOUND) or (line_end == LINE_NOT_FOUND) or (line_end - line_start != n_kpts):
if LINE_NOT_FOUND in {line_start, line_end} or (line_end - line_start != n_kpts):
self._cache.update(
{
"k_points": None,
Expand Down
2 changes: 1 addition & 1 deletion src/pymatgen/io/vasp/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2856,7 +2856,7 @@ def from_directory(
dict of {filename: Object type}. Objects must have
from_file method.
"""
sub_dct = {}
sub_dct: dict[str, Any] = {}
for fname, ftype in (
("INCAR", Incar),
("KPOINTS", Kpoints),
Expand Down
18 changes: 8 additions & 10 deletions src/pymatgen/io/vasp/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1703,10 +1703,8 @@ def __init__(
tag = elem.tag
if event == "start":
# The start event tells us when we have entered blocks
if (
tag == "eigenvalues_kpoints_opt"
or tag == "projected_kpoints_opt"
or (tag == "dos" and elem.attrib.get("comment") == "kpoints_opt")
if tag in {"eigenvalues_kpoints_opt", "projected_kpoints_opt"} or (
tag == "dos" and elem.attrib.get("comment") == "kpoints_opt"
):
in_kpoints_opt = True
elif not parsed_header:
Expand Down Expand Up @@ -3891,31 +3889,31 @@ def __init__(self, filename: PathLike) -> None:
self.data = data
self.phase_factors = phase_factors

def get_projection_on_elements(self, structure: Structure) -> dict[Spin, list]:
def get_projection_on_elements(self, structure: Structure) -> dict[Spin, list[list[dict[str, float]]]]:
"""Get a dict of projections on elements.
Args:
structure (Structure): Input structure.
Returns:
A dict as {Spin.up: [k index][b index][{Element: values}]].
A dict as {Spin: [band index][kpoint index][{Element: values}]].
"""
assert self.data is not None, "Data cannot be None."
assert self.nkpoints is not None
assert self.nbands is not None
assert self.nions is not None

dico: dict[Spin, list] = {}
elem_proj: dict[Spin, list] = {}
for spin in self.data:
dico[spin] = [[defaultdict(float) for _ in range(self.nkpoints)] for _ in range(self.nbands)]
elem_proj[spin] = [[defaultdict(float) for _ in range(self.nkpoints)] for _ in range(self.nbands)]

for iat in range(self.nions):
name = structure.species[iat].symbol
for spin, data in self.data.items():
for kpoint, band in itertools.product(range(self.nkpoints), range(self.nbands)):
dico[spin][band][kpoint][name] += np.sum(data[kpoint, band, iat, :])
elem_proj[spin][band][kpoint][name] += np.sum(data[kpoint, band, iat, :])

return dico
return elem_proj

def get_occupation(self, atom_index: int, orbital: str) -> dict:
"""Get the occupation for a particular orbital of a particular atom.
Expand Down
Loading

0 comments on commit 8119b0e

Please sign in to comment.