-
Notifications
You must be signed in to change notification settings - Fork 32
feat: add get_fcidump to output #253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
17a2832
feat: add get_fcidump to output
haneug ea5ce74
doc: improve get_fcidump docstring
haneug b050e0d
doc: fix typo
haneug 97f24c8
feat: Small improvements to Fcidump
haneug d06ca8d
Update src/opi/output/core.py
haneug 50cd0f1
Update src/opi/output/fcidump_parser.py
haneug 30a18a4
fix: minor renames
haneug 3323a3b
chore: run nox session
haneug c805f53
feat: improve eri_tensor generation and add more tests for it
haneug be14cb1
test: add unit test for _get_int and _get_int_list helper functions
haneug e4167a3
test: remove dedent from test string
haneug cd36b1b
fix: Several fixes
haneug b009282
Merge branch 'main' into feature/output-fcidump-parser
haneug 8787324
fix: apply code review fixes
haneug File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| """Parse a potential FCIDUMP file""" | ||
|
haneug marked this conversation as resolved.
|
||
|
|
||
| import re | ||
| from dataclasses import dataclass, field | ||
| from functools import cached_property | ||
| from pathlib import Path | ||
| from typing import Self | ||
|
|
||
| import numpy as np | ||
|
|
||
|
|
||
| @dataclass | ||
| class Fcidump: | ||
|
haneug marked this conversation as resolved.
|
||
| """ | ||
| Reads and stores data from a FCIDUMP file. One and two-electrons integrals are stored as dicts and can be | ||
| accessed as numpy arrays via `hcore_matrix` and `eri_tensor`. | ||
|
|
||
| Attributes | ||
| -------- | ||
| norb: int | ||
| Number of active orbitals. | ||
| nelec: int | ||
| Number of active electrons. | ||
| ms2: int | ||
| Twice the total spin projection, i.e. the difference of alpha and beta electrons. | ||
| orbsym: list[int] | ||
| Symmetry labels of the orbitals. | ||
| isym: int | ||
| Overall symmetry of the electronic structure. | ||
| one_electron: dict[tuple[int, int], float] | ||
| Dictionary that contains the one-electron integrals. | ||
| two_electron: dict[tuple[int, int, int, int], float] | ||
| Dictionary that contains the two-electron integrals. | ||
| orbital_energies: dict[int, float] | ||
| Dictionary that contains the orbital energies, if present in the FCIDUMP file. | ||
| e_nuc: float | ||
| Core energy contribution. Contains the contracted energy of the inactive space. | ||
| path: Path | ||
| Path to the FCIDUMP file. | ||
| """ | ||
|
|
||
| norb: int | ||
| nelec: int | ||
| ms2: int | ||
| orbsym: list[int] | ||
| isym: int | ||
| one_electron: dict[tuple[int, int], float] = field(default_factory=dict) | ||
| two_electron: dict[tuple[int, int, int, int], float] = field(default_factory=dict) | ||
| orbital_energies: dict[int, float] = field(default_factory=dict) | ||
| e_nuc: float = 0.0 | ||
| path: Path = field(default_factory=Path) | ||
|
|
||
| @cached_property | ||
| def hcore_matrix(self) -> np.ndarray[tuple[int, int], np.dtype[np.float64]]: | ||
| """Return the one-electron integrals as a symmetric (norb, norb) numpy array.""" | ||
| mat = np.zeros((self.norb, self.norb)) | ||
| for (i, j), val in self.one_electron.items(): | ||
| mat[i - 1, j - 1] = val | ||
| mat[j - 1, i - 1] = val | ||
| return mat | ||
|
|
||
| @cached_property | ||
| def eri_tensor(self) -> np.ndarray[tuple[int, int, int, int], np.dtype[np.float64]]: | ||
| """Return the two-electron integrals as a (norb, norb, norb, norb) numpy array. | ||
|
|
||
| Uses chemist's notation (ij|kl) with 8-fold permutation symmetry applied. | ||
| """ | ||
| tensor = np.zeros((self.norb,) * 4) | ||
| if not self.two_electron: | ||
| return tensor | ||
|
|
||
| # > Pull all stored indices/values into arrays once | ||
| idx = np.array(list(self.two_electron.keys()), dtype=np.int64) - 1 # (n_integrals, 4) | ||
| vals = np.array(list(self.two_electron.values()), dtype=np.float64) # (n_integrals,) | ||
| a, b, c, d = idx[:, 0], idx[:, 1], idx[:, 2], idx[:, 3] | ||
|
|
||
| # > Vectorized assignment for each of the 8 symmetry-equivalent index permutations | ||
| for p, q, r, s in ( | ||
| (a, b, c, d), | ||
| (b, a, c, d), | ||
| (a, b, d, c), | ||
| (b, a, d, c), | ||
| (c, d, a, b), | ||
| (d, c, a, b), | ||
| (c, d, b, a), | ||
| (d, c, b, a), | ||
| ): | ||
| tensor[p, q, r, s] = vals | ||
| return tensor | ||
|
|
||
| @classmethod | ||
| def from_file(cls, path: Path | str) -> Self: | ||
| """ | ||
| Parse a FCIDUMP file and return the populated `Fcidump` object. | ||
| The FCIDUMP file is documented in the paper: | ||
| Knowles, P. J.; Handy, N. C. A Determinant Based Full Configuration Interaction Program. | ||
| Computer Physics Communications 1989, 54, 75–83. https://doi.org/10.1016/0010-4655(89)90033-7 | ||
|
|
||
| Raises | ||
| ------- | ||
| ValueError | ||
| If the FCIDUMP file cannot be parsed. | ||
| FileNotFoundError | ||
| If the FCIDUMP file cannot be found at the given path. | ||
| """ | ||
| path = Path(path) | ||
|
|
||
| if not path.is_file(): | ||
| raise FileNotFoundError(f"{cls.__name__}: FCIDUMP file not found at {path}") | ||
|
|
||
| text = path.read_text() | ||
|
|
||
| # > Split header and body | ||
| end_match = re.search(r"&END|/", text, re.IGNORECASE) | ||
| if end_match is None: | ||
| raise ValueError( | ||
| f"{cls.__name__}: Could not find header terminator (&END or /) in {path}" | ||
| ) | ||
| header = text[: end_match.end()] | ||
| body = text[end_match.end() :] | ||
|
|
||
| # > Parse the header | ||
| dump = cls( | ||
| norb=cls._get_int("NORB", header), | ||
| nelec=cls._get_int("NELEC", header), | ||
| ms2=cls._get_int("MS2", header), | ||
| orbsym=cls._get_int_list("ORBSYM", header), | ||
| isym=cls._get_int("ISYM", header), | ||
| path=Path(path), | ||
| ) | ||
|
|
||
| # > Parse the integrals from the body | ||
| for line in body.splitlines(): | ||
| parts = line.split() | ||
| if not parts: | ||
| continue | ||
| if len(parts) != 5: | ||
| raise ValueError(f"{cls.__name__}: Could not parse {line} in {path}") | ||
| try: | ||
| val, i, j, k, ll = ( | ||
| float(parts[0]), | ||
| int(parts[1]), | ||
| int(parts[2]), | ||
| int(parts[3]), | ||
| int(parts[4]), | ||
| ) | ||
| except ValueError: | ||
| raise ValueError(f"{cls.__name__}: Could not parse {line} in {path}") | ||
| # > Inactive contribution | ||
| if i == 0 and j == 0 and k == 0 and ll == 0: | ||
| dump.e_nuc = val | ||
| # > Orbital energies (not written by ORCA, but by some other programs) | ||
| elif j == 0 and k == 0 and ll == 0: | ||
| dump.orbital_energies[i] = val | ||
| # > One-electron matrix | ||
| elif k == 0 and ll == 0: | ||
| dump.one_electron[(i, j)] = val | ||
| # > Two-electron tensor | ||
| else: | ||
| dump.two_electron[(i, j, k, ll)] = val | ||
|
|
||
| return dump | ||
|
|
||
| @classmethod | ||
| def _get_int(cls, key: str, header: str) -> int: | ||
| """Return the positive integer value of the given key.""" | ||
| m = re.search(rf"{key}\s*=\s*(\d+)", header, re.IGNORECASE) | ||
|
haneug marked this conversation as resolved.
|
||
| if m is None: | ||
| raise ValueError(f"{cls.__name__}: Could not parse {key}") | ||
| return int(m.group(1)) | ||
|
|
||
| @classmethod | ||
| def _get_int_list(cls, key: str, header: str) -> list[int]: | ||
| """Return a list of non-negative integers corresponding to the given key.""" | ||
| m = re.search(rf"{key}\s*=\s*([-\d\s,]+)", header, re.IGNORECASE) | ||
| if m is None: | ||
| raise ValueError(f"{cls.__name__}: Could not parse {key}") | ||
| try: | ||
| values = [int(x) for x in re.split(r"[,\s]+", m.group(1).strip()) if x] | ||
| except ValueError: | ||
| raise ValueError(f"{cls.__name__}: Could not parse {key}") | ||
| if not values or any(v < 0 for v in values): | ||
| raise ValueError(f"{cls.__name__}: Could not parse {key}") | ||
| return values | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.