# Tutorial: from a DFT geometry optimization to properties

This tutorial ties Part I together with one realistic task: start from a rough guess of a molecule, let
DFT **find its equilibrium structure**, and then **analyze** that structure. It is the everyday shape of a
quantum-chemistry study — optimize, then interpret. We use water again so the numbers are easy to check.

By the end you will have used the checkpoint/step/run model from [Core concepts](concepts.md) for a
multi-step calculation.

## Step 1 — build a (deliberately imperfect) molecule

We start from a *bad* guess: an O–H distance and angle that are clearly off. The point of the optimization
is to fix them.

```python
import qc

mol = qc.chk.new(
    atom="O 0.00 0.00 0.00; H 0.00 0.80 0.55; H 0.00 -0.80 0.55",   # rough, not equilibrium
    ao="def2-svp",
    unit="angstrom",
)
```

## Step 2 — optimize the geometry with DFT

Add an SCF step (B3LYP), attach an **`.opt()`** to it, and run. `.opt()` repeatedly computes the energy and
its **gradient** (the force on each atom) and steps the nuclei downhill until the forces vanish — the
equilibrium structure.

```python
mol = mol.scf(ref="r", xc="b3lyp").opt().run()

print("converged:", mol.opt.converged)          # True
print("energy    :", round(mol.opt.energy, 6))   # -76.358316  (hartree)
```

`.run()` is **silent** by default; add **`run(log="stdout")`** to stream the optimizer's progress — a
per-cycle table of the energy and its gradient, ending in **`Converged!`** after a few steps. (The same
`log=` switch drives every run; `log_style="modern"`/`"orca"` restyles it and `plot=True` plots the
trajectory inline — see the [Quickstart](quickstart.md) tip and
[Logging & output](../20-guide/logging-output.md).) The optimized water has

- **O–H bond length ≈ 0.967 Å** (up from our 0.97-ish rough guess, but now consistent both sides), and
- **H–O–H angle ≈ 103.1°** — close to the experimental 104.5°, a typical B3LYP/def2-SVP result.

:::{note} What just happened, in workflow terms
`.scf(...).opt()` added *two* linked pending steps; `.run()` resolved them — each optimization cycle is an
SCF on a slightly moved geometry. The final checkpoint `mol` now holds the **optimized** structure and its
electronic state, ready to analyze.
:::

:::{tip} Use more cores: `nthread=`
An optimization runs *many* SCFs (one per cycle), so it is a natural place to use more of your machine. By
default qc-rs uses a **single CPU core**; add **`run(nthread=8)`** to spread each SCF across 8 cores — the
same shared-memory **thread** parallelism from the [Quickstart](quickstart.md), and the easy speedup on a
laptop or single node. (Spreading a job across *multiple machines* with **MPI process** parallelism,
`nmpi=`, is an advanced topic covered later in [Parallel computing & HPC](../30-hpc/index.md); you do not
need it here.)
:::

## Step 3 — analyze the optimized structure

Now ask the optimized checkpoint for properties. First the **Mulliken atomic charges**:

```python
q = qc.prop.chrg.mulliken(mol)
print(q["charges"])       # [-0.2945, 0.1472, 0.1472]
```

Oxygen is negative, the hydrogens positive — water's familiar polarity. We can quantify that polarity with
the **dipole moment**:

```python
mp = qc.prop.mpol.molecular(mol)
print(round(mp["dipole_magnitude_debye"], 2))    # 2.00  (debye)
```

A dipole of about **2.0 D** is exactly right for water (experiment ≈ 1.85 D; the difference is basis-set
and method, a theme of [Foundations](../10-foundations/index.md)).

## The whole thing

Put together, the study is seven lines:

```python
import qc

mol = qc.chk.new(
    atom="O 0.00 0.00 0.00; H 0.00 0.80 0.55; H 0.00 -0.80 0.55",
    ao="def2-svp", unit="angstrom",
).scf(ref="r", xc="b3lyp").opt().run()

print("E =", round(mol.opt.energy, 6), "hartree, converged:", mol.opt.converged)
print("charges =", qc.prop.chrg.mulliken(mol)["charges"])
print("dipole  =", round(qc.prop.mpol.molecular(mol)["dipole_magnitude_debye"], 2), "D")
```

That is a complete "optimize then analyze" workflow: a molecule in, an equilibrium structure and its
properties out.

## What you have learned, and where to go

You have now:

- built a molecule and chosen a method and basis set;
- chained steps (`scf` → `opt`) and run them;
- read results (`mol.opt.energy`) and computed properties (`mulliken`, the dipole).

That is the core loop of qc-rs. To understand *what Hartree–Fock and DFT actually compute* — and why the
dipole came out a little large — read [Part II — Foundations](../10-foundations/index.md). To go deeper on
any single step, the [User guide](../20-guide/molecular-input.md) has a chapter for each, ending with the
full [molecular-properties suite](../20-guide/properties/index.md).
