# Bundled basis sets

qc-rs ships **229 named basis sets** as editable Python data files under `python/qc/basis/`. The
[basis chapter](../20-guide/basis-and-ao.md) is the tutorial; this page is the reference for **listing,
loading, and customizing** the bundled data.

## Listing what is available

The names live in `qc.basis.baslib_filename` (a dict of `name → module_stem`):

```python
from qc import basis
len(basis.baslib_filename)                 # 229
"cc-pvtz" in basis.baslib_filename         # True
sorted(n for n in basis.baslib_filename if n.startswith("def2"))
```

Any of these names is a valid `ao=` (or `rijk=`/`ric=`) value in `qc.chk.new(...)`. The main families:

| family | examples | notes |
|---|---|---|
| Pople | `3-21g`, `6-31g`, `6-31g*`, `6-31+g**`, `6-311g**`, `6-311++g**` | split-valence |
| Dunning cc | `cc-pvdz`, `cc-pvtz`, `cc-pvqz`, `aug-cc-pvdz`, `cc-pvdz-pp` | correlation-consistent; `-pp` carries an ECP |
| Karlsruhe def2 | `def2-svp`, `def2-tzvp`, `def2-qzvp`, `def2-tzvpd` | built-in ECP for heavy elements (Rb onward) |
| auxiliary | `cc-pvdz-jkfit`, `cc-pvdz-ri/mp2fit`, `def2-universal/jkfit` | RI fitting sets (`rijk=`/`ric=`) |

## The data files

Each file (except the `_00*` utilities) is a **basis-set data file**: it defines one module-level variable
per element (named after the symbol — `H`, `He`, `Li`, …) as an `nwchem_format(...)` object:

```python
import importlib
cc_pvdz = importlib.import_module("qc.basis.cc_pvdz")
h = cc_pvdz.H
h.description     # "cc-pvdz:H"
h.inpdata         # the NWChem-format orbital-shell text
h.ecp             # None (or the ECP text, for *-pp / def2 heavy elements)
```

### The `nwchem_format` object

```python
class nwchem_format:
    inpdata      # str: NWChem orbital-basis text (one element)
    description  # str or None: a human-readable label
    ecp          # str or None: NWChem ECP text (None for all-electron)
```

`inpdata` is parsed by the Rust `qc_mol::nwchem::parse()` when `qc.chk.new(ao=...)` is called.

### NWChem text format

```text
SYMBOL  SHELL_TYPE
  exponent  coeff1  coeff2  ...
  exponent  coeff1  coeff2  ...
```

- `SHELL_TYPE`: `S`, `P`, `D`, `F`, `G`, `H`, `I`, `K`, or `SP` (auto-split into S and P).
- Multiple coefficient columns → a generally-contracted shell.

```text
H    S
     13.0100000   0.0196850
      1.9620000   0.1379770
      0.4446000   0.4781480
H    S
      0.1220000   1.0000000
H    P
      0.7270000   1.0000000
```

Do **not** apply primitive normalization by hand — `qc-mol` does it once from the raw coefficients.

## Using a custom basis

### Inline, per element

Build an `nwchem_format` object and hand it to `ao=` (as a dict value):

```python
from qc.basis import nwchem_format
import qc

myN = nwchem_format("""
N S
  242.766  0.0598657
   36.485  0.352955
    7.814  0.706513
N P
    5.425  0.237972
    1.149  0.858953
""", description="custom:N")

m = qc.chk.new(atom="N 0 0 0; N 0 0 1.1", ao={"N": myN}, unit="angstrom")
```

### Editing or adding a bundled set

The files are plain Python, so you can edit an element's block in place, or add a new `<name>.py` data file
and register it in `baslib_filename`. After editing, **rebuild** the extension (`make install` /
`maturin develop`) so the change is picked up. Full editing/adding workflow: `docs/qc-basis.md`.

## ECP-bearing bases

The correlation-consistent `-pp` sets and the **def2** sets carry their effective-core potential **inline**
(in the `ecp` field). `qc.chk.new(ao="def2-svp")` on a heavy atom therefore runs valence-only automatically —
no separate `ecp=` argument — matching PySCF's `basis="def2-svp", ecp="def2-svp"`. The def2 JK-fit auxiliary
is the universal `def2-universal/jkfit`.
