|
3 | 3 | import unittest |
4 | 4 | from io import TextIOWrapper |
5 | 5 | from pathlib import Path |
6 | | -from typing import Dict, List, Tuple, Optional |
| 6 | +from typing import Dict, List, Tuple, Optional, Any |
7 | 7 |
|
8 | 8 | import numpy as np |
9 | 9 | from ase.atoms import Atoms |
@@ -706,6 +706,63 @@ def read_magmom_from_running_log(src: str | Path | List[str]) \ |
706 | 706 |
|
707 | 707 | return magmom |
708 | 708 |
|
| 709 | +def read_iter_header_from_running_log(src: str | Path | List[str]) \ |
| 710 | + -> List[Tuple[int, int]]: |
| 711 | + ''' |
| 712 | + read the "iteration header" from the running log, useful for determining |
| 713 | + which is the final iteration. The "iteration header" is defined as: |
| 714 | + ``` |
| 715 | + LCAO ALGORITHM --------------- ION= 1 ELEC= 100--------------------- |
| 716 | + ``` |
| 717 | + or for PW: |
| 718 | + ``` |
| 719 | + PW ALGORITHM --------------- ION=2 ELEC=1 ----------------------- |
| 720 | + ``` |
| 721 | + ''' |
| 722 | + if isinstance(src, (str, Path)): |
| 723 | + with open(src) as f: |
| 724 | + raw = f.readlines() |
| 725 | + else: # assume the src is the return of the readlines() |
| 726 | + raw = src |
| 727 | + # with open(fn) as f: |
| 728 | + # raw = f.readlines() |
| 729 | + raw = [l.strip() for l in raw] |
| 730 | + raw = [l for l in raw if l] # remove empty lines |
| 731 | + |
| 732 | + HEADER_PAT = r'(LCAO|PW)\s+ALGORITHM\s+-+\s+ION=\s+([0-9]+)\s+ELEC=\s+([0-9]+)' |
| 733 | + res = [re.findall(HEADER_PAT, l) for l in raw] |
| 734 | + |
| 735 | + return [(int(pack[0][1]), int(pack[0][2])) for pack in res if pack] |
| 736 | + |
| 737 | +def find_final_info_with_iter_header( |
| 738 | + info: List[Any], |
| 739 | + headers: List[Tuple[int, int]] |
| 740 | + ) -> List[Any]: |
| 741 | + '''find the energies of the final iteration |
| 742 | + |
| 743 | + Parameters |
| 744 | + ---------- |
| 745 | + info: List[Dict[str, float]] |
| 746 | + The information that is printed after each iteration. |
| 747 | + headers: List[Tuple[int, int]] |
| 748 | + The "iteration header" returned from the function |
| 749 | + `read_iter_header_from_running_log` |
| 750 | + |
| 751 | + Returns |
| 752 | + ------- |
| 753 | + List[Any] |
| 754 | + The information of the final iteration. |
| 755 | + ''' |
| 756 | + headers = np.array(headers) # because indices should start from 0 |
| 757 | + assert headers.ndim == 2 |
| 758 | + assert headers.shape[1] == 2 |
| 759 | + |
| 760 | + ion = headers[:, 0].flatten() |
| 761 | + ion, nelec = np.unique(ion, return_counts=True) |
| 762 | + ielec = np.cumsum(nelec) - 1 |
| 763 | + |
| 764 | + return [info[i] for i in ielec] |
| 765 | + |
709 | 766 | def is_invalid_arr(arr) -> bool: |
710 | 767 | '''Check if the array is invalid, including the cases of None, |
711 | 768 | empty array, and array with NaN values. |
@@ -797,9 +854,11 @@ def read_abacus_out(fileobj, |
797 | 854 | # the simulation, which is, not exactly to be true for the NPT-MD |
798 | 855 | # runs. |
799 | 856 |
|
800 | | - _, energies = read_energies_from_running_log(abacus_lines) |
801 | | - # only keep the SCF converged energies |
802 | | - energies = [edct for edct in energies if 'E_KS(sigma->0)' in edct] |
| 857 | + # only keep the energies of the final iteration for each ion step |
| 858 | + energies = find_final_info_with_iter_header( |
| 859 | + read_energies_from_running_log(abacus_lines)[1], |
| 860 | + read_iter_header_from_running_log(abacus_lines) |
| 861 | + ) |
803 | 862 |
|
804 | 863 | # read the magmom |
805 | 864 | magmom = read_magmom_from_running_log(abacus_lines) |
@@ -1107,5 +1166,25 @@ def test_read_magmom_from_running_log(self): |
1107 | 1166 | np.array([[0. , 0. , 3.62032142], |
1108 | 1167 | [0. , 0. , 3.62032142]]))) |
1109 | 1168 |
|
| 1169 | + def test_read_iter_header_from_running_log(self): |
| 1170 | + fn = self.testfiles / 'lcao-symm0-nspin2-multik-cellrelax_' |
| 1171 | + header = read_iter_header_from_running_log(fn) |
| 1172 | + self.assertEqual(header[0], (1,1)) |
| 1173 | + self.assertEqual(header[1], (1,2)) |
| 1174 | + self.assertEqual(header[2], (2,1)) |
| 1175 | + fn = self.testfiles / 'pw-symm0-nspin4-gamma-md_' |
| 1176 | + header = read_iter_header_from_running_log(fn) |
| 1177 | + self.assertEqual(header[0], (1,1)) |
| 1178 | + self.assertEqual(header[1], (1,2)) |
| 1179 | + self.assertEqual(header[2], (1,3)) |
| 1180 | + self.assertEqual(header[3], (3,2)) |
| 1181 | + self.assertEqual(header[4], (3,3)) |
| 1182 | + |
| 1183 | + def test_find_final_info_with_iter_header(self): |
| 1184 | + info = [1, 2, 3, 4, 5, 6] |
| 1185 | + header = [[1,1], [1,2], [2,1], [3,1], [3,2], [3,3]] |
| 1186 | + final_info = find_final_info_with_iter_header(info, header) |
| 1187 | + self.assertListEqual(final_info, [info[1], info[2], info[5]]) |
| 1188 | + |
1110 | 1189 | if __name__ == '__main__': |
1111 | 1190 | unittest.main() |
0 commit comments