# A few notes for Python beginners

This manual is not a Python tutorial, but a handful of Python features come up constantly when driving qc-rs.
This appendix collects the "Python note" callouts scattered through the chapters into one reference. If you
know Python, skip it.

## Keyword arguments

qc-rs calls label each value by name:

```python
qc.chk.new(atom="H 0 0 0; F 0 0 0.9", ao="cc-pvdz", unit="angstrom", charge=0, spin=1)
```

`atom=`, `ao=`, … are **keyword arguments**: the order does not matter, and the call documents itself. You
will see this style everywhere in qc-rs. A `*` in a signature (e.g. `scf(mychk, *, ref="r", …)`) means the
arguments after it are keyword-only — you must name them.

## f-strings

An **f-string** substitutes an expression into text; `:.6f` formats a float with six decimals:

```python
print(f"energy = {mychk.scf.energy:.6f} hartree")   # energy = -76.026772 hartree
```

## Triple-quoted strings

`"""… """` is a **multi-line string** — the natural way to write a geometry, one atom per line:

```python
atom = """
    O   0.000   0.000   0.117
    H   0.000   0.757  -0.469
    H   0.000  -0.757  -0.469
"""
```

Leading/trailing blank lines and indentation are fine; qc-rs ignores them.

## Lists vs dicts

- A **list** `[...]` is an ordered sequence: `ao=["cc-pvtz", "cc-pvdz"]` is *not* how you set per-atom bases.
- A **dict** `{key: value}` maps names to values: `ao={"O": "cc-pvtz", "H": "cc-pvdz"}` gives each element its
  own basis. Many qc-rs options (`ao=`, `pcm=`, `iop=`) accept a dict.

## Method chaining

Each workflow verb returns a new checkpoint, so you can **chain** them left to right:

```python
mychk = qc.chk.new(atom=..., ao="cc-pvdz").scf(ref="r").run()
```

reads as "make a checkpoint → add an SCF step → run it." The functional form
`qc.scf(qc.chk.new(...), ref="r").run()` is identical but reads inside-out.

## Reading arrays (numpy)

Some accessors return NumPy arrays. `np.asarray(...)` makes sure you have one, `.shape` gives its dimensions,
and `np.round(...)` tidies the display:

```python
import numpy as np
g = np.asarray(mychk.scf.gradient)   # shape (natom, 3)
print(g.shape, np.round(g, 4))
```

## Virtual environments and `uv`

A **virtual environment** (venv) is an isolated Python with its own packages, so projects don't clash. qc-rs
uses **`uv`** to manage one. Two things to remember:

- Your notebook/editor **kernel** must be the venv that has qc-rs installed, or `import qc` fails
  ([editor chapter](../00-intro/editor-vscode.md)). Check with `import sys; print(sys.executable)`.
- **`uv add <pkg>` prunes the editable qc-rs extension** — rebuild it afterwards (`make install`).

## Notebooks: `%matplotlib inline`

In a Jupyter notebook, this "magic" makes plots appear in the cell:

```python
%matplotlib inline
mychk.plot_convergence()
```

Set the magic in a cell — do not `pip install` inside a cell (install packages in the environment with `uv`).

## `importlib` for basis data

The bundled basis files are ordinary Python modules; load one with `importlib`:

```python
import importlib
cc_pvdz = importlib.import_module("qc.basis.cc_pvdz")
cc_pvdz.O.description    # "cc-pvdz:O"
```

That is the whole of the Python you need to follow this manual — everything else is qc-rs and chemistry.
