Skip to content

Commit ef1b580

Browse files
authored
dft implementation
1 parent ed328f3 commit ef1b580

File tree

11 files changed

+6225
-224
lines changed

11 files changed

+6225
-224
lines changed

src/nomad_simulations/schema_packages/model_method.py

Lines changed: 535 additions & 224 deletions
Large diffs are not rendered by default.

src/nomad_simulations/schema_packages/numerical_settings.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections.abc import Sequence
12
from itertools import accumulate, chain, tee
23
from typing import TYPE_CHECKING, Union
34

@@ -972,3 +973,116 @@ class ForceCalculations(NumericalSettings):
972973

973974
def normalize(self, archive, logger) -> None:
974975
super().normalize(archive, logger)
976+
977+
978+
class FrozenCore(NumericalSettings):
979+
"""
980+
Section defining the frozen-core approximation settings for molecular electronic-structure methods.
981+
982+
In the frozen-core approximation, selected inner-shell (core) orbitals are excluded from
983+
the orbital optimization or post-SCF correlation treatment, retaining them at their
984+
reference-determinant (e.g., Hartree-Fock or Kohn-Sham) values. This significantly
985+
reduces the number of 'valence' orbitals, lowering computational cost.
986+
987+
The frozen-core scheme can be specified either by enumerating the exact orbitals to
988+
freeze or by using simple threshold rules based on quantum numbers or atomic numbers.
989+
990+
"""
991+
992+
#### MolecularOrbitalsState has not been implemented yet.
993+
# core_orbitals_ref = Quantity(
994+
# type=MolecularOrbitalsState,
995+
# shape=['n_frozen_core_orbitals'],
996+
# description="""
997+
# References to the atomic OrbitalsState sections to keep frozen.
998+
# """,
999+
# )
1000+
1001+
per_atom_n_threshold = Quantity(
1002+
type=np.int32,
1003+
shape=['*'],
1004+
description="""
1005+
For each atom (in input order), maximum principal quantum number *n*
1006+
that will be **frozen**. -1 means “no freezing for that atom”.
1007+
""",
1008+
)
1009+
1010+
per_atom_z_threshold = Quantity(
1011+
type=np.int32,
1012+
shape=['*'],
1013+
description="""
1014+
Alternative per-atom rule: freeze all core shells with atomic number Z ≤ value.
1015+
Entries of 0 disable the rule for that atom.
1016+
""",
1017+
)
1018+
1019+
1020+
class IntegralDecomposition(ArchiveSection):
1021+
"""
1022+
A general class for integral decomposition techniques that approximate
1023+
Coulomb and/or exchange integrals to reduce computational cost in quantum
1024+
chemistry.
1025+
1026+
Captures common families such as RIJ, RIJK, RIJCOSX, SENEX, and Cholesky
1027+
Decomposition (CD / CD_F12). The `approximated_term` encodes which part of
1028+
the electronic structure workload is approximated. This class self-normalizes
1029+
legacy/narrow settings to lossless semantics:
1030+
• RIJ → J only → 'coulomb'
1031+
• RIJK → J and K together → 'jk'
1032+
• RIJCOSX → J (RI) + K (COSX) → 'jk'
1033+
• CD → ERI-tensor level → 'two_electron'
1034+
• CD_F12 → explicit-correlation terms → 'explicit_correlation'
1035+
1036+
Typical references:
1037+
- F. Weigend, M. Häser, The RI-MP2 method: Algorithmic
1038+
implementation of efficient, approximate MP2 theories,
1039+
Theor. Chem. Acc. 97, 331-340 (1997).
1040+
- S. Hättig, F. Weigend, J. Chem. Phys. 113, 5154 (2000). (RI-J)
1041+
- Neese et al., “Chain-of-spheres algorithms for HF exchange,”
1042+
Chem. Phys. 356 (2008), 98-109.
1043+
"""
1044+
1045+
approximation_type = Quantity(
1046+
type=MEnum('RIJ', 'RIJK', 'RIJCOSX', 'SENEX', 'CD', 'CD_F12'),
1047+
description="""
1048+
RIJ : also known as RIJONX, where only Coulomb integrals are approximated.
1049+
RIJK : both Coulomb and exchange integrals.
1050+
RIJCOSX : RIJ for Coulomb and COSX for HF exchange.
1051+
SENEX : Similar to COSX, relevant for Turbomole.
1052+
CD : Cholesky decomposition of the two-electron integral tensor
1053+
CD_F12 : Cholesky decomposition specialized for F12/explicit-correlation integrals
1054+
""",
1055+
)
1056+
1057+
approximated_term = Quantity(
1058+
type=MEnum(
1059+
'coulomb', # J only
1060+
'exchange', # K only
1061+
'jk', # J and K together (e.g., RIJK/RIJCOSX)
1062+
'two_electron', # ERI-level (e.g., plain CD)
1063+
'mp2',
1064+
'cc',
1065+
'explicit_correlation',
1066+
),
1067+
description="""
1068+
The targeted term(s) being approximated. If not provided, it will be
1069+
inferred from `approximation_type`. For backward compatibility, narrow
1070+
values are safely widened (e.g., RIJK + 'exchange' → 'jk').
1071+
""",
1072+
)
1073+
1074+
def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
1075+
super().normalize(archive, logger)
1076+
1077+
_TERM_BY_TYPE = {
1078+
'RIJ': 'coulomb',
1079+
'RIJK': 'jk',
1080+
'RIJCOSX': 'jk',
1081+
'CD': 'two_electron',
1082+
'CD_F12': 'explicit_correlation',
1083+
}
1084+
1085+
if self.approximated_term is None:
1086+
inferred = _TERM_BY_TYPE.get(self.approximation_type)
1087+
if inferred is not None:
1088+
self.approximated_term = inferred
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# LibXC Functional Canonicalization
2+
3+
This module converts **code-specific exchange–correlation (XC) labels** into a **canonical, LibXC-grounded representation**.
4+
It accepts aliases such as `PBE`, hybrids like `B3LYP`, or composite forms like `SCAN+rVV10`, and produces normalized **LibXC components** (`XCComponent` instances) with consistent metadata (`family`, `kind`, `id`, etc.).
5+
6+
---
7+
8+
## Aim
9+
10+
1. **Unify XC naming** across electronic-structure codes by resolving them to LibXC identifiers.
11+
2. **Make functional components explicit** — making functional components explicit as far as LibXC exposes them.
12+
3. **Preserve hybrid parameters** when available (`α`, `ω`, SR/LR fractions).
13+
4. **Provide a single canonical label** (`functional_key`) for user-facing filtering, while keeping full LibXC detail internally.
14+
15+
> **Background:**
16+
> [LibXC](https://libxc.gitlab.io/) is the reference library defining the mathematical kernels for all standard density functionals (LDA, GGA, meta-GGA, hybrids).
17+
> It distinguishes each kernel by a stable name like `XC_GGA_X_PBE` and a numeric ID.
18+
> This canonicalization follows the same separation principle: each kernel is treated as one building block.
19+
20+
---
21+
22+
## Input Forms Accepted
23+
24+
- **Human-readable aliases:** `LDA`, `PBE`, `PW91`, `TPSS`, `SCAN`, `r2SCAN`, `B3LYP`, etc.
25+
- **Hybrid or range-separated forms:** `PBE0`, `HSE06`, `LC-ωPBE`, `CAM-B3LYP`, etc.
26+
- **Raw LibXC labels:** `XC_GGA_X_PBE`, `XC_GGA_C_PBE`, `XC_HYB_GGA_XC_B3LYP`, etc.
27+
28+
The module is code-agnostic — any parser providing a string can normalize it through this interface.
29+
30+
---
31+
32+
## Canonicalization Pipeline
33+
34+
### 1. Tokenization & Normalization
35+
- Input strings are uppercased, `ω` is normalized to `W`, all text in parentheses is dropped, and separators (`space`, `+`, `/`, `_`, `-`, `,`) are removed. Tokenization splits on `[+/,\s]+`.
36+
- Known suffixes like `-D3` or `+VV10` are recognized but **not yet parsed into separate dispersion or nonlocal correlation models** — they remain part of the functional key.
37+
38+
### 2. Alias Expansion → LibXC Components
39+
Aliases are expanded using a curated lookup table (`expand.py`):
40+
41+
| Input Alias | Expanded Components |
42+
|--------------|---------------------|
43+
| `PBE` | `XC_GGA_X_PBE`, `XC_GGA_C_PBE` |
44+
| `SVWN` | `XC_LDA_X`, `XC_LDA_C_VWN` |
45+
| `TPSS` | `XC_MGGA_X_TPSS`, `XC_MGGA_C_TPSS` |
46+
47+
This ensures consistent resolution against LibXC definitions.
48+
49+
### 3. Registry Lookup (`registry.py`)
50+
Each expanded label is matched against a lightweight internal registry (`xc_registry_min.json`)
51+
to populate standard LibXC metadata:
52+
53+
- **`family`**`LDA`, `GGA`, `meta-GGA`, `hybrid-GGA`, `hybrid-meta-GGA`
54+
- **`kind`**`exchange`, `correlation`, or `xc`
55+
- **`libxc_id`** → stable LibXC numeric identifier
56+
- **`display_name`** → human-readable name (e.g., “Perdew, Burke & Ernzerhof”)
57+
58+
### 4. Component Construction (`build.py`)
59+
Each resolved label becomes an `XCComponent`, carrying:
60+
- canonical label (`XC_GGA_X_PBE`)
61+
- LibXC ID and family/kind metadata
62+
- optional hybrid parameters (`fraction_exact_exchange`, `range_separation_parameter`)
63+
- numerical `weight` (default 1.0)
64+
65+
### 5. Attachment to DFT Section
66+
During `DFT.normalize()`:
67+
- If `xc.components` is empty but a `functional_key` is provided,
68+
it is expanded into canonical LibXC components and attached.
69+
- The **highest Jacob’s ladder family** among components sets `jacobs_ladder`.
70+
- If a unique hybrid fraction (`α`) is found, it is propagated to `exact_exchange_mixing_factor`.
71+
72+
---
73+
74+
## Canonical Functional Key (`functional_key`)
75+
76+
While LibXC defines each **exchange**, **correlation**, and **hybrid** kernel separately
77+
(e.g., `XC_GGA_X_PBE`, `XC_GGA_C_PBE`), users typically refer to these collectively using one name — e.g., “PBE”.
78+
79+
This canonicalization introduces:
80+
81+
- **`functional_key`** → one user-facing alias representing the entire functional
82+
*(e.g., `PBE`, `PBE0`, `B3LYP`, `SCAN+rVV10`)*
83+
- **`components`** → explicit LibXC-resolved kernels underneath
84+
*(e.g., `["XC_GGA_X_PBE", "XC_GGA_C_PBE"]`)*
85+
86+
Example:
87+
```text
88+
PBE → functional_key = "PBE"
89+
components = ["XC_GGA_X_PBE", "XC_GGA_C_PBE"]
90+
91+
B3LYP → functional_key = "B3LYP"
92+
components = ["XC_HYB_GGA_XC_B3LYP"]
93+
94+

src/nomad_simulations/schema_packages/utils/libxc/__init__.py

Whitespace-only changes.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
{
2+
"BASE": {
3+
"LDA": ["XC_LDA_X", "XC_LDA_C_PW"],
4+
"LSDA": ["XC_LDA_X", "XC_LDA_C_VWN"],
5+
"SVWN": ["XC_LDA_X", "XC_LDA_C_VWN"],
6+
"SVWN5": ["XC_LDA_X", "XC_LDA_C_VWN"],
7+
"VWN": ["XC_LDA_X", "XC_LDA_C_VWN"],
8+
"PW92": ["XC_LDA_C_PW"],
9+
"PZ81": ["XC_LDA_X", "XC_LDA_C_PZ"],
10+
"PBE": ["XC_GGA_X_PBE", "XC_GGA_C_PBE"],
11+
"PBESOL": ["XC_GGA_X_PBE_SOL", "XC_GGA_C_PBE_SOL"],
12+
"REVPBE": ["XC_GGA_X_RPBE", "XC_GGA_C_PBE"],
13+
"RPBE": ["XC_GGA_X_RPBE", "XC_GGA_C_PBE"],
14+
"PW91": ["XC_GGA_X_PW91", "XC_GGA_C_PW91"],
15+
"BLYP": ["XC_GGA_X_B88", "XC_GGA_C_LYP"],
16+
"BP86": ["XC_GGA_X_B88", "XC_GGA_C_P86"],
17+
"BOP": ["XC_GGA_X_BOP", "XC_GGA_C_PBE"],
18+
"OLYP": ["XC_GGA_X_OPTX", "XC_GGA_C_LYP"],
19+
"PBEINT": ["XC_GGA_X_PBEINT", "XC_GGA_C_PBEINT"],
20+
"PBEALPHA": ["XC_GGA_X_PBE_A", "XC_GGA_C_PBE"],
21+
"WC": ["XC_GGA_X_WC"],
22+
"HTBS": ["XC_GGA_X_HTBS", "XC_GGA_C_PBE"],
23+
"MPW91": ["XC_GGA_X_MPW91", "XC_GGA_C_PW91"],
24+
"MPWPW": ["XC_GGA_X_MPW91", "XC_GGA_C_PW91"],
25+
"MPWLYP": ["XC_GGA_X_MPW91", "XC_GGA_C_LYP"],
26+
"SOGGA": ["XC_GGA_X_SOGGA", "XC_GGA_C_SOGGA11"],
27+
"SOGGA11": ["XC_GGA_X_SOGGA11", "XC_GGA_C_SOGGA11"],
28+
"SOGGA11-X": ["XC_GGA_X_SOGGA11"],
29+
"REVTCA": ["XC_GGA_C_REVTPSS"],
30+
"TPSS": ["XC_MGGA_X_TPSS", "XC_MGGA_C_TPSS"],
31+
"REVTPSS": ["XC_MGGA_X_REVTPSS", "XC_MGGA_C_TPSS"],
32+
"PKZB": ["XC_MGGA_X_PKZB", "XC_MGGA_C_PKZB"],
33+
"BRX": ["XC_MGGA_X_BR89"],
34+
"SCAN": ["XC_MGGA_X_SCAN", "XC_MGGA_C_SCAN"],
35+
"SCAN-L": ["XC_MGGA_X_SCANL", "XC_MGGA_C_SCAN"],
36+
"R2SCAN": ["XC_MGGA_X_R2SCAN", "XC_MGGA_C_R2SCAN"],
37+
"M06-L": ["XC_MGGA_X_M06_L", "XC_MGGA_C_M06_L"],
38+
"M11-L": ["XC_MGGA_X_M11_L", "XC_MGGA_C_M11_L"],
39+
"MN15-L": ["XC_MGGA_X_MN15_L", "XC_MGGA_C_MN15_L"],
40+
"TPSSLOC": ["XC_MGGA_X_TPSS", "XC_MGGA_C_TPSS"],
41+
"B97M-V": ["XC_MGGA_X_B97M_V", "XC_NLC_XC_VV10"],
42+
"B97M-RV": ["XC_MGGA_X_B97M_RV", "XC_NLC_XC_RVV10"]
43+
},
44+
"HYB": {
45+
"PBE0": ["XC_HYB_GGA_XC_PBEH", "XC_HYB_GGA_XC_PBE0", "XC_HYB_GGA_XC_PBE0_13"],
46+
"PBEH": ["XC_HYB_GGA_XC_PBEH", "XC_HYB_GGA_XC_PBE0", "XC_HYB_GGA_XC_PBE0_13"],
47+
"B3LYP": ["XC_HYB_GGA_XC_B3LYP", "XC_HYB_GGA_XC_B3LYPS"],
48+
"B3PW91": ["XC_HYB_GGA_XC_B3PW91"],
49+
"B3PW": ["XC_HYB_GGA_XC_B3PW91"],
50+
"B97-1": ["XC_HYB_GGA_XC_B97_1"],
51+
"B97-2": ["XC_HYB_GGA_XC_B97_2"],
52+
"O3LYP": ["XC_HYB_GGA_XC_O3LYP"],
53+
"X3LYP": ["XC_HYB_GGA_XC_X3LYP"],
54+
"MPW1PW": ["XC_HYB_GGA_XC_MPW1PW"],
55+
"MPW3PW": ["XC_HYB_GGA_XC_MPW3PW"],
56+
"BHANDH": ["XC_HYB_GGA_XC_BHANDH"],
57+
"BHANDHLYP": ["XC_HYB_GGA_XC_BHANDHLYP"],
58+
"HSE03": ["XC_HYB_GGA_XC_HSE03"],
59+
"HSE06": ["XC_HYB_GGA_XC_HSE06"],
60+
"HSE": ["XC_HYB_GGA_XC_HSE06", "XC_HYB_GGA_XC_HSE03", "XC_HYB_GGA_XC_HSE12", "XC_HYB_GGA_XC_HSE_SOL"],
61+
"CAM-B3LYP": ["XC_HYB_GGA_XC_CAM_B3LYP"],
62+
"LC-ωPBE": ["XC_HYB_GGA_XC_LC_WPBE"],
63+
"WPBEH": ["XC_HYB_GGA_XC_WPBEH"],
64+
"N12-SX": ["XC_HYB_GGA_XC_N12_SX"],
65+
"APFD": ["XC_HYB_GGA_XC_APFD"],
66+
"SCAN0": ["XC_HYB_MGGA_X_SCAN0", "XC_MGGA_C_SCAN"],
67+
"R2SCAN0": ["XC_HYB_MGGA_XC_R2SCAN0", "XC_HYB_MGGA_X_REVSCAN0"],
68+
"M06": ["XC_HYB_MGGA_X_M06", "XC_HYB_MGGA_C_M06"],
69+
"M06-2X": ["XC_HYB_MGGA_X_M06_2X", "XC_HYB_MGGA_C_M06_2X"],
70+
"M08-HX": ["XC_HYB_MGGA_X_M08_HX", "XC_HYB_MGGA_C_M08_HX"],
71+
"M08-SO": ["XC_HYB_MGGA_X_M08_SO", "XC_HYB_MGGA_C_M08_SO"],
72+
"M11": ["XC_HYB_MGGA_X_M11", "XC_HYB_MGGA_C_M11"],
73+
"MN15": ["XC_HYB_MGGA_X_MN15", "XC_HYB_MGGA_C_MN15"],
74+
"ωB97X-D": ["XC_HYB_MGGA_X_WB97X_D"],
75+
"ωB97X-V": ["XC_HYB_MGGA_X_WB97X_V", "XC_NLC_XC_VV10"],
76+
"ωB97M-V": ["XC_HYB_MGGA_X_WB97M_V", "XC_NLC_XC_VV10"]
77+
},
78+
"FALLBACK_BY_RUNG": {
79+
"hybrid-GGA": [
80+
["XC_HYB_GGA_XC_PBE0"],
81+
["XC_HYB_GGA_XC_B3LYP"],
82+
["XC_HYB_GGA_XC_HSE06"]
83+
],
84+
"hybrid-meta-GGA": [
85+
["XC_HYB_MGGA_X_SCAN0", "XC_MGGA_C_SCAN"],
86+
["XC_HYB_MGGA_X_M06", "XC_HYB_MGGA_C_M06"]
87+
],
88+
"meta-GGA": [
89+
["XC_MGGA_X_SCAN", "XC_MGGA_C_SCAN"],
90+
["XC_MGGA_X_TPSS", "XC_MGGA_C_TPSS"]
91+
],
92+
"GGA": [
93+
["XC_GGA_X_PBE", "XC_GGA_C_PBE"],
94+
["XC_GGA_X_B88", "XC_GGA_C_LYP"]
95+
]
96+
},
97+
"RUNG_HINT": {
98+
"revTCA": "GGA",
99+
"B97M-V": "meta-GGA",
100+
"B97M-rV": "meta-GGA",
101+
"ωPBEh": "hybrid-GGA",
102+
"N12-SX": "hybrid-GGA",
103+
"APFD": "hybrid-GGA",
104+
"ωB97X-D": "hybrid-meta-GGA",
105+
"ωB97X-V": "hybrid-meta-GGA",
106+
"ωB97M-V": "hybrid-meta-GGA"
107+
},
108+
"version": 1
109+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from __future__ import annotations
2+
3+
from typing import Optional, TypedDict
4+
5+
from .registry import lookup_by_id, lookup_by_label
6+
7+
8+
class XCComponentSpec(TypedDict):
9+
libxc_id: int
10+
canonical_label: str
11+
display_name: str
12+
family: str
13+
kind: str
14+
weight: float
15+
16+
17+
def spec_from_label(label: str, *, weight: float = 1.0) -> XCComponentSpec | None:
18+
"""
19+
Build a plain-data specification for an XCComponent from a LibXC-style label.
20+
21+
Returns:
22+
XCComponentSpec if found, otherwise None.
23+
"""
24+
rec = lookup_by_label(label)
25+
if not rec:
26+
return None
27+
28+
return XCComponentSpec(
29+
libxc_id=rec['id'],
30+
canonical_label=rec['label'],
31+
display_name=rec['name'],
32+
family=rec['family'],
33+
kind=rec['kind'],
34+
weight=float(weight),
35+
)
36+
37+
38+
def spec_from_id(xc_id: int, *, weight: float = 1.0) -> XCComponentSpec | None:
39+
"""
40+
Build a plain-data specification for an XCComponent from a LibXC integer ID.
41+
42+
Returns:
43+
XCComponentSpec if found, otherwise None.
44+
"""
45+
rec = lookup_by_id(xc_id)
46+
if not rec:
47+
return None
48+
return spec_from_label(rec['label'], weight=weight)

0 commit comments

Comments
 (0)