Skip to content

Commit 6340c4b

Browse files
naik-aakashjanosh
andauthored
Add LobsterMatrices parser to lobster.io.outputs (#3361)
* add hamiltonMatrices.lobster parser to lobster.io.outputs * pre-commit auto-fixes * fix ruff error * temp mypy fix * update lobster.io __init__.py : include LobsterMatrices class * rename class and make it general to work for all matrices files of lobster * add tests and small test files * pre-commit auto-fixes * fix class description docstring * snake_case * use pytest.approx in TestLobsterMatrices.test_attributes * format to reduce white space --------- Co-authored-by: anaik <anaik@sv2218.zit.bam.de> Co-authored-by: Janosh Riebesell <janosh.riebesell@gmail.com>
1 parent 2e8515b commit 6340c4b

12 files changed

+457
-401
lines changed

pymatgen/io/lobster/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
Fatband,
1717
Grosspop,
1818
Icohplist,
19+
LobsterMatrices,
1920
Lobsterout,
2021
MadelungEnergies,
2122
NciCobiList,

pymatgen/io/lobster/outputs.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,3 +1688,148 @@ def get_orb_from_str(orbs):
16881688
orbitals = [(int(orb[0]), Orbital(orb_labs.index(orb[1:]))) for orb in orbs]
16891689
orb_label = f"{orbitals[0][0]}{orbitals[0][1].name}-{orbitals[1][0]}{orbitals[1][1].name}" # type: ignore
16901690
return orb_label, orbitals
1691+
1692+
1693+
class LobsterMatrices:
1694+
"""
1695+
Class to read Matrices file generated by LOBSTER (e.g. hamiltonMatrices.lobster).
1696+
1697+
Attributes:
1698+
for filename == "hamiltonMatrices.lobster"
1699+
onsite_energies (list[np.arrays]): List real part of onsite energies from the matrices each k-point.
1700+
average_onsite_energies (dict): dict with average onsite elements energies for all k-points with keys as
1701+
basis used in the LOBSTER computation (uses only real part of matrix).
1702+
hamilton_matrices (dict[np.arrays]) : dict with the complex hamilton matrix
1703+
at each k-point with k-point and spin as keys
1704+
1705+
for filename == "coefficientMatrices.lobster"
1706+
1707+
onsite_coefficients (list[np.arrays]): List real part of onsite coefficients from the matrices each k-point.
1708+
average_onsite_coefficient (dict): dict with average onsite elements coefficients for all k-points with keys as
1709+
basis used in the LOBSTER computation (uses only real part of matrix).
1710+
coefficient_matrices (dict[np.arrays]) : dict with the coefficients matrix
1711+
at each k-point with k-point and spin as keys
1712+
1713+
for filename == "transferMatrices.lobster"
1714+
1715+
onsite_transfer (list[np.arrays]): List real part of onsite transfer coefficients from the matrices at each
1716+
k-point.
1717+
average_onsite_transfer (dict): dict with average onsite elements transfer coefficients for all k-points with
1718+
keys as basis used in the LOBSTER computation (uses only real part of matrix).
1719+
transfer_matrices (dict[np.arrays]) : dict with the coefficients matrix at
1720+
each k-point with k-point and spin as keys
1721+
1722+
for filename == "overlapMatrices.lobster"
1723+
1724+
onsite_overlaps (list[np.arrays]): List real part of onsite overlaps from the matrices each k-point.
1725+
average_onsite_overlaps (dict): dict with average onsite elements overlaps for all k-points with keys as
1726+
basis used in the LOBSTER computation (uses only real part of matrix).
1727+
overlap_matrices (dict[np.arrays]) : dict with the overlap matrix at
1728+
each k-point with k-point as keys
1729+
"""
1730+
1731+
def __init__(self, e_fermi=None, filename: str = "hamiltonMatrices.lobster"):
1732+
"""
1733+
Args:
1734+
filename: filename for the hamiltonMatrices file, typically "hamiltonMatrices.lobster".
1735+
e_fermi: fermi level in eV for the structure only
1736+
relevant if input file contains hamilton matrices data
1737+
"""
1738+
1739+
self._filename = filename
1740+
# hamiltonMatrices
1741+
with zopen(self._filename, "rt") as f:
1742+
file_data = f.readlines()
1743+
if len(file_data) == 0:
1744+
raise OSError("Please check provided input file, it seems to be empty")
1745+
1746+
pattern_coeff_hamil_trans = r"(\d+)\s+kpoint\s+(\d+)" # regex pattern to extract spin and k-point number
1747+
pattern_overlap = r"kpoint\s+(\d+)" # regex pattern to extract k-point number
1748+
1749+
if "hamilton" in self._filename:
1750+
if e_fermi is None:
1751+
raise ValueError("Please provide the fermi energy in eV ")
1752+
self.onsite_energies, self.average_onsite_energies, self.hamilton_matrices = self._parse_matrix(
1753+
file_data=file_data, pattern=pattern_coeff_hamil_trans, e_fermi=e_fermi
1754+
)
1755+
1756+
elif "coefficient" in self._filename:
1757+
self.onsite_coefficients, self.average_onsite_coefficient, self.coefficient_matrices = self._parse_matrix(
1758+
file_data=file_data, pattern=pattern_coeff_hamil_trans, e_fermi=0
1759+
)
1760+
1761+
elif "transfer" in self._filename:
1762+
self.onsite_transfer, self.average_onsite_transfer, self.transfer_matrices = self._parse_matrix(
1763+
file_data=file_data, pattern=pattern_coeff_hamil_trans, e_fermi=0
1764+
)
1765+
1766+
elif "overlap" in self._filename:
1767+
self.onsite_overlaps, self.average_onsite_overlaps, self.overlap_matrices = self._parse_matrix(
1768+
file_data=file_data, pattern=pattern_overlap, e_fermi=0
1769+
)
1770+
1771+
@staticmethod
1772+
def _parse_matrix(file_data, pattern, e_fermi):
1773+
complex_matrices = {}
1774+
matrix_diagonal_values = []
1775+
start_inxs_real = []
1776+
end_inxs_real = []
1777+
start_inxs_imag = []
1778+
end_inxs_imag = []
1779+
# get indices of real and imaginary part of matrix for each k point
1780+
for i, line in enumerate(file_data):
1781+
line = line.strip()
1782+
if "Real parts" in line:
1783+
start_inxs_real.append(i + 1)
1784+
if i == 1: # ignore the first occurrence as files start with real matrices
1785+
pass
1786+
else:
1787+
end_inxs_imag.append(i - 1)
1788+
matches = re.search(pattern, file_data[i - 1])
1789+
if matches and len(matches.groups()) == 2:
1790+
k_point = matches.group(2)
1791+
complex_matrices[k_point] = {}
1792+
if "Imag parts" in line:
1793+
end_inxs_real.append(i - 1)
1794+
start_inxs_imag.append(i + 1)
1795+
# explicitly add the last line as files end with imaginary matrix
1796+
if i == len(file_data) - 1:
1797+
end_inxs_imag.append(len(file_data))
1798+
1799+
# extract matrix data and store diagonal elements
1800+
for start_inx_real, end_inx_real, start_inx_imag, end_inx_imag in zip(
1801+
start_inxs_real, end_inxs_real, start_inxs_imag, end_inxs_imag
1802+
):
1803+
# matrix with text headers
1804+
matrix_real = file_data[start_inx_real:end_inx_real]
1805+
matrix_imag = file_data[start_inx_imag:end_inx_imag]
1806+
1807+
# extract only numerical data and convert to numpy arrays
1808+
matrix_array_real = np.array([line.split()[1:] for line in matrix_real[1:]], dtype=float)
1809+
matrix_array_imag = np.array([line.split()[1:] for line in matrix_imag[1:]], dtype=float)
1810+
1811+
# combine real and imaginary parts to create a complex matrix
1812+
comp_matrix = matrix_array_real + 1j + matrix_array_imag
1813+
1814+
matches = re.search(pattern, file_data[start_inx_real - 2])
1815+
if matches and len(matches.groups()) == 2:
1816+
spin = Spin.up if matches.group(1) == "1" else Spin.down
1817+
k_point = matches.group(2)
1818+
complex_matrices[k_point].update({spin: comp_matrix})
1819+
elif matches and len(matches.groups()) == 1:
1820+
k_point = matches.group(1)
1821+
complex_matrices.update({k_point: comp_matrix})
1822+
matrix_diagonal_values.append(comp_matrix.real.diagonal() - e_fermi)
1823+
1824+
# extract elements basis functions as list
1825+
elements_basis_functions = [
1826+
line.split()[:1][0] for line in matrix_real if line.split()[:1][0] != "basisfunction"
1827+
]
1828+
1829+
# get average row-wise
1830+
average_matrix_diagonal_values = np.array(matrix_diagonal_values, dtype=float).mean(axis=0)
1831+
1832+
# get a dict with basis functions as keys and average values as values
1833+
average_average_matrix_diag_dict = dict(zip(elements_basis_functions, average_matrix_diagonal_values))
1834+
1835+
return matrix_diagonal_values, average_average_matrix_diag_dict, complex_matrices

tests/files/.pytest-split-durations

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,7 @@
16771677
"tests/io/lobster/test_inputs.py::TestBandoverlaps::test_attributes": 0.025641332962550223,
16781678
"tests/io/lobster/test_inputs.py::TestBandoverlaps::test_has_good_quality": 0.029428708949126303,
16791679
"tests/io/lobster/test_inputs.py::TestCharge::test_get_structure_with_charges": 0.0030279159545898438,
1680-
"tests/io/lobster/test_inputs.py::TestCharge::testattributes": 0.0022161250235512853,
1680+
"tests/io/lobster/test_inputs.py::TestCharge::test_attributes": 0.0022161250235512853,
16811681
"tests/io/lobster/test_inputs.py::TestCohpcar::test_attributes": 0.07801850006217137,
16821682
"tests/io/lobster/test_inputs.py::TestCohpcar::test_cohp_data": 0.08135204098653048,
16831683
"tests/io/lobster/test_inputs.py::TestCohpcar::test_energies": 0.07727941608754918,
@@ -1693,7 +1693,7 @@
16931693
"tests/io/lobster/test_inputs.py::TestFatband::test_get_bandstructure": 3.241345082933549,
16941694
"tests/io/lobster/test_inputs.py::TestFatband::test_raises": 2.288895126024727,
16951695
"tests/io/lobster/test_inputs.py::TestGrosspop::test_structure_with_grosspop": 0.0011580409482121468,
1696-
"tests/io/lobster/test_inputs.py::TestGrosspop::testattributes": 0.00040716701187193394,
1696+
"tests/io/lobster/test_inputs.py::TestGrosspop::test_attributes": 0.00040716701187193394,
16971697
"tests/io/lobster/test_inputs.py::TestIcohplist::test_attributes": 0.0028339590062387288,
16981698
"tests/io/lobster/test_inputs.py::TestIcohplist::test_values": 0.001649416983127594,
16991699
"tests/io/lobster/test_inputs.py::TestLobsterin::test_msonable_implementation": 0.0035085839335806668,
@@ -1711,7 +1711,7 @@
17111711
"tests/io/lobster/test_inputs.py::TestLobsterin::test_write_kpoints": 0.037688249023631215,
17121712
"tests/io/lobster/test_inputs.py::TestLobsterin::test_write_lobsterin": 0.030836959020234644,
17131713
"tests/io/lobster/test_inputs.py::TestLobsterout::test_get_doc": 0.004303499008528888,
1714-
"tests/io/lobster/test_inputs.py::TestLobsterout::testattributes": 0.003141166002023965,
1714+
"tests/io/lobster/test_inputs.py::TestLobsterout::test_attributes": 0.003141166002023965,
17151715
"tests/io/lobster/test_inputs.py::TestMadelungEnergies::test_attributes": 0.0023860419751144946,
17161716
"tests/io/lobster/test_inputs.py::TestSitePotentials::test_attributes": 0.002964083047118038,
17171717
"tests/io/lobster/test_inputs.py::TestSitePotentials::test_get_structure": 0.0025429160450585186,
468 Bytes
Binary file not shown.
304 Bytes
Binary file not shown.
Binary file not shown.
208 Bytes
Binary file not shown.

tests/files/cohp/hamiltonMatrices.lobster

Whitespace-only changes.

0 commit comments

Comments
 (0)