<a id="peaks-indexing-and-a-le-bail-check"></a>

# Peaks, indexing and a Le Bail check

When the phase is unknown there is no CIF to start from, and the cell has to come from the peak positions alone.
We pick the peaks of a synthetic pattern whose answer we know, index them, and read how rietx decides whether to name a single cell.
Then we check the best candidate with a Le Bail fit.
By the end you can pick and fit peaks, run an indexing search, and test a candidate cell against the whole pattern.

*Written by Claude Code, Anthropic’s coding agent, for the rietx project.*

**You need** rietx 1.10 or later, which the next cell installs.
Notebook 01 introduces Le Bail fitting.

**The data** is synthetic aragonite, CaCO₃, made from its published structure: Dal Negro and Ungaretti (1971), *American Mineralogist* 56, 768, as entry 9000229 of the Crystallography Open Database (public domain).
The displacement parameters are the isotropic equivalents of that entry’s anisotropic ones.

**Runtime** is under a minute on a laptop, most of it the indexing search.

```ipython3
%pip install rietx
```

<a id="a-synthetic-pattern"></a>

## A synthetic pattern

Write aragonite’s structure to a CIF and read it back.

```ipython3
import tempfile
from pathlib import Path

import numpy as np

import rietx as rx
from rietx.indexing import SearchSpec, structure_from_candidate

work = Path(tempfile.mkdtemp())
cif = work / "aragonite.cif"
cif.write_text(
    "data_aragonite\n_space_group_name_H-M_alt 'P m c n'\n"
    "_cell_length_a 4.9616\n_cell_length_b 7.9705\n_cell_length_c 5.7394\n"
    "_cell_angle_alpha 90\n_cell_angle_beta 90\n_cell_angle_gamma 90\n"
    "loop_\n_atom_site_label\n_atom_site_type_symbol\n_atom_site_fract_x\n"
    "_atom_site_fract_y\n_atom_site_fract_z\n_atom_site_B_iso_or_equiv\n"
    "Ca1 Ca 0.25 0.4151 0.2403 0.67\n"
    "C1  C  0.25 0.7627 0.0850 0.80\n"
    "O1  O  0.25 0.9231 0.0952 1.25\n"
    "O2  O  0.4729 0.6801 0.0870 1.19\n",
    encoding="utf-8",
)
truth = rx.Structure.from_cif(cif)
```

[`predict`](https://rietx.org/docs/stable/reference/refinement.html#entry-points) computes a pattern from a structure and an instrument without fitting anything.
This instrument sets its own peak widths and a flat background of 200 counts.
We scale the phase so the strongest peak reaches about 20 000 counts above the background, then draw Poisson counts.

```ipython3
true_instrument = rx.Instrument.bragg_brentano(radiation="CuKa")
true_instrument.profile.w.value = 0.004
true_instrument.profile.x.value = 0.05
true_instrument.background = rx.BackgroundChebyshev.with_terms(1)
true_instrument.background.coefficients[0].value = 200.0

two_theta = np.arange(10, 80, 0.02)
y = rx.Refinement(truth, true_instrument).predict(two_theta) - 200.0
truth.phases[0].scale.value *= 20000 / y.max()
y = rx.Refinement(truth, true_instrument).predict(two_theta)
data = rx.PatternData(
    two_theta=two_theta,
    intensity=np.random.default_rng(0).poisson(y).astype(float),
)
data.plot()
```

![](../../_images/twin-ebc81ce53405b8a0.png)

From here on we pretend not to know the structure.
The indexer should find the CIF’s cell, perhaps with its axes in another order.

<a id="find-the-peaks"></a>

## Find the peaks

[`pick_peaks`](https://rietx.org/docs/stable/reference/peaks.html#picking-the-peaks) finds every resolvable peak and fits each one for its position, with an esd.
It needs an instrument for the wavelength and a first guess at the peak width.
Give it a default copper instrument, as you would for an unknown sample.
Its widths do not match these peaks, so expect the picker to say so.

```ipython3
lab = rx.Instrument.bragg_brentano(radiation="CuKa")
peaks = rx.pick_peaks(data, lab)
peaks
```

<pre>PeakList: 100 peaks, 54 usable, 10–79.98° 2θ, λ = 1.54059 Å, source fitted
    #   two_theta  two_theta_esd   intensity    fwhm  origin   flags
    0     21.0733         0.0025        31.5   0.106  fitted
    1     22.2914         0.0056         9.7   0.078  fitted
    2     26.2221         0.0003      2174.3   0.095  fitted   asymmetry_unmodelled
    3     26.6070         0.0104        19.6   0.097  fitted   asymmetry_unmodelled
    4     27.2200         0.0003      1236.9   0.097  fitted   asymmetry_unmodelled
    5     31.1394         0.0015        95.0   0.108  fitted
    6     32.7500         0.0012       169.8   0.097  fitted   asymmetry_unmodelled
    7     32.7501      1045.0429        32.3   0.097  fitted   unresolved_shoulder, not_separable, no_intensity, position_unmeasured, asymmetry_unmodelled
    8     32.7501       246.3679       136.9   0.097  fitted   unresolved_shoulder, no_intensity, position_unmeasured, asymmetry_unmodelled
    9     33.1521         0.0004      1063.5   0.097  fitted   asymmetry_unmodelled
   10     33.1522         0.0004      1065.8   0.097  fitted   asymmetry_unmodelled, duplicate_line
   11     36.1488         0.0006       946.7   0.125  fitted
   12     37.2579         0.0024       323.6   0.098  fitted   duplicate_line
   13     37.2650         0.0011       298.2   0.100  fitted   asymmetry_unmodelled
   14     37.8984         0.0334       958.3   0.117  fitted   duplicate_line
   15     37.9064         0.0041       848.5   0.100  fitted   asymmetry_unmodelled, duplicate_line
   16     37.9071         0.0004       869.0   0.097  fitted   asymmetry_unmodelled
   17     37.9074         0.0005       866.2   0.098  fitted   asymmetry_unmodelled, duplicate_line
   18     38.3949         0.0069       551.9   0.117  fitted   duplicate_line
   19     38.4023         0.0005       618.3   0.097  fitted   asymmetry_unmodelled
   20     38.4033         0.0006       642.5   0.098  fitted   asymmetry_unmodelled, duplicate_line
   21     38.5784         0.0099       377.6   0.117  fitted   position_at_bound, duplicate_line
   22     38.6307         0.0007       402.6   0.097  fitted
   23     41.1956         0.0008       271.1   0.093  fitted   asymmetry_unmodelled
   24     42.9067         0.0008       426.2   0.099  fitted
   25     45.4857         0.0022        71.0   0.097  fitted
   26     45.4858         0.0031        70.5   0.097  fitted   asymmetry_unmodelled, duplicate_line
   27     45.8585         0.0003      1528.8   0.097  fitted
   28     45.8585         0.0004      1529.6   0.097  fitted   asymmetry_unmodelled, duplicate_line
   29     48.3096         0.0007       643.1   0.099  fitted   fit_failed, asymmetry_unmodelled
   30     48.3098         0.0008       633.4   0.099  fitted
   31     48.4600         0.0011       420.1   0.099  fitted   position_at_bound
   32     48.4659         9.7164       264.3   0.099  fitted   fit_failed, no_intensity, asymmetry_unmodelled
   33     48.4659        17.5602       145.6   0.099  fitted   fit_failed, unresolved_split, no_intensity, asymmetry_unmodelled, kalpha2_residual
   34     49.8874         0.0051        33.4   0.099  fitted   asymmetry_unmodelled
   35     49.9000         0.0061        32.3   0.099  fitted   position_at_bound, axial_tail, asymmetry_unmodelled, duplicate_line
   36     50.2395         0.0006       624.5   0.099  fitted
   37     50.2396         0.0007       626.3   0.099  fitted   asymmetry_unmodelled, duplicate_line
   38     51.9124         0.0035        56.9   0.097  fitted   asymmetry_unmodelled
   39     52.4903         0.0012       656.6   0.097  fitted   asymmetry_unmodelled, duplicate_line
   40     52.4910         0.0019       675.9   0.095  fitted   asymmetry_unmodelled, duplicate_line
   41     52.4911         0.0004       665.8   0.097  fitted
   42     52.9157         0.0013       146.2   0.097  fitted
   43     52.9163         0.0062       143.9   0.095  fitted   asymmetry_unmodelled, duplicate_line
   44     52.9872         0.0295       531.5   0.242  fitted   fit_failed, asymmetry_unmodelled
   45     53.0541         0.0043       296.8   0.095  fitted   asymmetry_unmodelled, duplicate_line
   46     53.0548         0.0008       292.0   0.097  fitted
   47     53.9604         0.0025        48.1   0.097  fitted   asymmetry_unmodelled
   48     56.1487         0.0032        58.0   0.100  fitted   duplicate_line
   49     56.1503         0.0018        56.5   0.102  fitted
   50     56.4382         0.0052        17.3   0.102  fitted   asymmetry_unmodelled, duplicate_line
   51     56.4382         0.0045        17.8   0.100  fitted
   52     56.8078         0.0023        34.6   0.100  fitted
   53     56.8427         0.0290        79.3   0.102  fitted   duplicate_line
   54     59.2422         0.0015       146.6   0.101  fitted
   55     59.5416         0.0169         8.3   0.101  fitted   position_at_bound
   56     59.5943         0.0180         9.7   0.117  fitted   duplicate_line
   57     60.1989         0.0035        36.6   0.117  fitted
   58     61.8181         0.0013       120.3   0.110  fitted
   59     62.9099         0.0019        58.1   0.106  fitted   asymmetry_unmodelled
   60     62.9100         0.0020        58.0   0.107  fitted   asymmetry_unmodelled, duplicate_line
   61     63.3339         0.0010       149.2   0.106  fitted   asymmetry_unmodelled, duplicate_line
   62     63.3340         0.0010       148.3   0.107  fitted
   63     65.0380         0.0092         8.6   0.094  fitted
   64     66.1075         0.0030        73.2   0.091  fitted   asymmetry_unmodelled, duplicate_line
   65     66.1127         0.0023        77.8   0.094  fitted
   66     66.1476         0.0041       125.0   0.157  fitted   duplicate_line
   67     66.1985         0.0034        66.1   0.091  fitted   position_at_bound, asymmetry_unmodelled, duplicate_line
   68     66.2091         0.0029        63.9   0.094  fitted
   69     66.5200         0.0074        53.5   0.091  fitted   position_at_bound, asymmetry_unmodelled, duplicate_line
   70     66.5346         0.0062        72.3   0.157  fitted   asymmetry_unmodelled, duplicate_line
   71     66.5420         0.0019        71.8   0.094  fitted
   72     68.6421         0.0015        74.9   0.102  fitted   duplicate_line
   73     68.6424         0.0015        73.9   0.104  fitted
   74     69.0998         0.0012       103.4   0.102  fitted   duplicate_line
   75     69.1002         0.0012       102.0   0.102  fitted
   76     69.1006         0.0013       101.7   0.104  fitted   asymmetry_unmodelled, duplicate_line
   77     69.5884         0.0026        40.0   0.102  fitted
   78     69.5885         0.0028        39.6   0.112  fitted   asymmetry_unmodelled, duplicate_line
   79     69.5898         0.0027        40.8   0.102  fitted   duplicate_line
   80     70.0819         0.0023        50.1   0.112  fitted   asymmetry_unmodelled, duplicate_line
   81     70.0832         0.0023        53.8   0.102  fitted
   82     70.2868         0.0161        12.7   0.115  fitted
   83     70.8746         0.0049        35.6   0.115  fitted
   84     72.2883         0.0038        16.3   0.058  fitted   asymmetry_unmodelled
   85     72.2901         0.0052        12.0   0.090  fitted   duplicate_line
   86     72.4957         0.0118         5.3   0.090  fitted
   87     75.2904         0.0010       150.1   0.107  fitted
   88     76.6592         0.0033        63.4   0.108  fitted   asymmetry_unmodelled
   89     76.6595         0.0034        63.0   0.107  fitted   asymmetry_unmodelled, duplicate_line
   90     76.7800         0.0021       117.0   0.108  fitted
   91     76.7801         0.0021       117.5   0.107  fitted   asymmetry_unmodelled, duplicate_line
   92     77.0994         0.0011       191.9   0.108  fitted
   93     77.0996         0.0012       193.3   0.107  fitted   asymmetry_unmodelled, duplicate_line
   94     78.0359         0.0021       124.3   0.116  fitted   duplicate_line
   95     78.0378         0.0014       122.5   0.116  fitted
   96     78.6934         0.0053        20.8   0.116  fitted
   97     78.7218         0.0458        33.2   0.116  fitted   no_intensity
   98     79.3618         0.0220        65.7   0.116  fitted   duplicate_line
   99     79.4031         0.0012       203.1   0.125  fitted   asymmetry_unmodelled
  info[PEAK_UNRESOLVED_SHOULDER]: 2 line(s) never separated from a neighbour by half a
                                  FWHM.
    at: 32.7501°, 32.7501°
    help: Keep them, since their σ already carries the penalty. Quote neither line of a
          pair as an independent line.
    means: Lines were kept in groups where they stayed within half a FWHM of a
           neighbour. Their positions are correlated with their neighbours&#x27; and are less
           precise than their σ alone suggests.
  warning[PEAK_NOT_SEPARABLE]: 1 component(s) were added by a re-seed pass inside a much
                               stronger neighbour&#x27;s own profile, in a group whose fit is
                               still refuted.
    at: 32.7501°
    help: Leave them in the model, because removing one displaces the real line it sits
          on. If many fire, check the declared instrument profile, since undeclared
          axial divergence reproduces this.
    means: Components improved their group&#x27;s fit but did not survive the nested
           comparison against a fit without them. They stay in the model and are
           withheld from indexing.
  info[PEAK_UNRESOLVED_SPLIT]: 1 component(s) lie within Sparrow&#x27;s limit of a comparable
                               group-mate, so each pair is reported as one line whose
                               σ(2θ) reaches both.
    at: 48.4659°
    help: Collect with a finer step, or declare the instrument profile, to resolve real
          close pairs. If many appear, suspect flat-topped or other non-pseudo-Voigt
          lines.
    means: Pairs of comparable components closer than two lines of the fitted shape can
           be and still show a dip. Each pair is reported as one line whose position
           uncertainty reaches both.
  info[PEAK_AXIAL_TAIL]: 1 weak component(s) sit on the axial-divergence tail side of a
                         much stronger group-mate.
    at: 49.9000°
    help: Check each group&#x27;s fit, then exclude the flagged components with
          use_for_indexing=False on a well-aligned Bragg-Brentano pattern. On SRM 660c,
          five such components carried 125 ppm of cell bias.
    means: Weak components lie within 3.5 FWHM of a stronger group-mate on the side
           axial divergence points. They may be real lines and are reported rather than
           removed.
  info[PEAK_KALPHA2_RESIDUAL]: 1 weak component(s) sit at a strong group-mate&#x27;s
                               predicted Kα2 maximum, at a few per cent of its area.
    at: 48.4659°
    help: Exclude them before indexing, unless the pattern gives you a reason to believe
          a genuine reflection hides under the doublet.
    means: Components sit where the Kα2 partner of a stronger line is already modelled,
           so they are that doublet&#x27;s residual rather than new lines. Reported rather
           than removed.
  warning[PEAK_ASYMMETRY_UNMODELLED]: 47 line(s) leave a significant odd residual after
                                      fitting.
    at: 26.2221°, 26.6070°, 27.2200°, 32.7500°, 32.7501°, 32.7501°, 33.1521°, 33.1522°, 37.2650°, 37.9064°, 37.9071°, 37.9074°, 38.4023°, 38.4033°, 41.1956°, 45.4858°, 45.8585°, 48.3096°, 48.4659°, 48.4659°, 49.8874°, 49.9000°, 50.2396°, 51.9124°, 52.4903°, 52.4910°, 52.9163°, 52.9872°, 53.0541°, 53.9604°, 56.4382°, 62.9099°, 62.9100°, 63.3339°, 66.1075°, 66.1985°, 66.5200°, 66.5346°, 69.1006°, 69.5885°, 70.0819°, 72.2883°, 76.6592°, 76.6595°, 76.7801°, 77.0996°, 79.4031°
    help: Set instrument.geometry.axial_sl and axial_hl to the real aperture ratios,
          then re-pick.
    means: Lines show asymmetric residuals beyond the fitted shape, so their positions
           are pulled towards the tail. Declaring the axial divergence terms is the
           usual fix.
  info[PEAK_EXPECTED_FALSE_LINES]: 54 of 54 fitted component(s) clear the pattern&#x27;s
                                   false-discovery bar (I/σ_I ≥ 3.03, Benjamini–Hochberg
                                   at q = 0.01 over 446 resolution elements). The
                                   expected number of noise lines among those kept is at
                                   least 0.54.
    help: Keep weak_line components unless the list is known to carry flank or tail
          junk. Count longer to tell weak lines from noise.
    means: The pattern&#x27;s own false-discovery bar on each component&#x27;s intensity over its
           esd, and the expected number of noise lines among those kept, a lower bound,
           since detection selected every component first. Components under the bar are
           flagged weak and stay usable. A component that absorbs a shape or background
           misfit carries real intensity and is not reached.
  warning[PEAK_WIDTH_LAW_MISMATCH]: The measured FWHM of 0.1566° differs by a factor of
                                    4.9 from the 0.0322° that the instrument&#x27;s U, V, W,
                                    X and Y predict. Seeds and windows were scaled by
                                    4.87 to match the data.
    at: median of the 12 most prominent lines
    help: Run lab_calibrate on a standard, or set U, V, W, X and Y from one. A factor
          near 13 means the ProfileTCHZ default (W = 1e-3 deg²), a synchrotron line, is
          still in place.
    means: The fitted widths do not follow the declared instrumental width law. Either
           the specimen is broadened, which is a finding, or the declared instrument is
           wrong, which is a setup error. The message carries the ratio so the two can
           be told apart. On a refinement the census is compared with the fitted widths,
           size and strain included, so it fires only when no phase&#x27;s widths came within
           the factor. A phase the data cannot see is left out of that comparison.
  info[PEAK_KALPHA2_ALIAS]: 30 candidate(s) were dropped as the Kα2 maximum of a
                            stronger line, so each reported position is a Kα1 position.
    at: 33.2400°, 38.0200°, 38.5000°, 38.7400°, 41.3000°, 43.0200°, 45.9800°, 48.6000°, 50.3800°, 52.0600°, 52.6400°, 53.2000°, 54.1200°, 56.3000°, 56.9600°, 59.4000°, 60.3400°, 61.9800°, 63.0800°, 63.5200°, 66.7200°, 68.8200°, 69.3000°, 69.7800°, 70.2800°, 71.0800°, 75.5000°, 77.3400°, 78.2600°, 79.6400°
    help: Confirm with an incident-side monochromator (Instrument.bragg_brentano with
          Kα1-only radiation) if one of these lines matters.
    means: Detection found candidates at the Kα2 positions of stronger lines and dropped
           them before any fit. The drop is reported because a genuine weak line can
           alias onto one.</pre>

Each row is one peak: its position, the esd of that position, its area and its width.
[`flags`](https://rietx.org/docs/stable/reference/peaks.html#the-flags) records anything the fit noticed.
Some flags, such as `duplicate_line` and `fit_failed`, take a row out of indexing.
The header line counts the rows left usable.

The diagnostics under the table explain the list.
[`PEAK_WIDTH_LAW_MISMATCH`](https://rietx.org/docs/stable/reference/diagnostics-indexing.html#peak-width-law-mismatch) says the default instrument predicts narrower peaks than these, so the picker scaled its windows to the data.
[`PEAK_KALPHA2_ALIAS`](https://rietx.org/docs/stable/reference/diagnostics-indexing.html#peak-kalpha2-alias) lists candidates dropped as the Kα2 partner of a stronger line, so each reported position is a Kα1 position.
Open a diagnostic to read what its code means.
[Diagnostics: peaks and indexing](https://rietx.org/docs/stable/reference/diagnostics-indexing.html) lists every code.

<a id="measure-lines-you-name"></a>

## Measure lines you name

[`fit_peaks`](https://rietx.org/docs/stable/reference/peaks.html#fitting-peaks-you-name) fits exactly the positions you give it, and nothing else.
Use it to measure lines you have chosen: a few reflections for a size analysis, or one line as a quick check.

```ipython3
chosen = rx.fit_peaks(data, lab, [26.22, 27.22, 33.13])
chosen
```

<pre>PeakList: 3 peaks, 3 usable, 10–79.98° 2θ, λ = 1.54059 Å, source fitted
    #   two_theta  two_theta_esd   intensity    fwhm  origin   flags
    0     26.2221         0.0003      2174.3   0.095  manual   asymmetry_unmodelled
    1     27.2200         0.0006      1241.3   0.096  manual   asymmetry_unmodelled, unnamed_neighbour
    2     33.1512         0.0031      1110.2   0.092  manual   asymmetry_unmodelled, unnamed_neighbour
  warning[PEAK_ASYMMETRY_UNMODELLED]: 3 line(s) leave a significant odd residual after
                                      fitting.
    at: 26.2221°, 27.2200°, 33.1512°
    help: Set instrument.geometry.axial_sl and axial_hl to the real aperture ratios,
          then re-pick.
    means: Lines show asymmetric residuals beyond the fitted shape, so their positions
           are pulled towards the tail. Declaring the axial divergence terms is the
           usual fix.
  warning[PEAK_WIDTH_LAW_MISMATCH]: The measured FWHM of 0.1566° differs by a factor of
                                    4.9 from the 0.0322° that the instrument&#x27;s U, V, W,
                                    X and Y predict. Seeds and windows were scaled by
                                    4.87 to match the data.
    at: median of the 12 most prominent lines
    help: Run lab_calibrate on a standard, or set U, V, W, X and Y from one. A factor
          near 13 means the ProfileTCHZ default (W = 1e-3 deg²), a synchrotron line, is
          still in place.
    means: The fitted widths do not follow the declared instrumental width law. Either
           the specimen is broadened, which is a finding, or the declared instrument is
           wrong, which is a setup error. The message carries the ratio so the two can
           be told apart. On a refinement the census is compared with the fitted widths,
           size and strain included, so it fires only when no phase&#x27;s widths came within
           the factor. A phase the data cannot see is left out of that comparison.
  info[PEAK_KALPHA2_ALIAS]: 30 candidate(s) were dropped as the Kα2 maximum of a
                            stronger line, so each reported position is a Kα1 position.
    at: 33.2400°, 38.0200°, 38.5000°, 38.7400°, 41.3000°, 43.0200°, 45.9800°, 48.6000°, 50.3800°, 52.0600°, 52.6400°, 53.2000°, 54.1200°, 56.3000°, 56.9600°, 59.4000°, 60.3400°, 61.9800°, 63.0800°, 63.5200°, 66.7200°, 68.8200°, 69.3000°, 69.7800°, 70.2800°, 71.0800°, 75.5000°, 77.3400°, 78.2600°, 79.6400°
    help: Confirm with an incident-side monochromator (Instrument.bragg_brentano with
          Kα1-only radiation) if one of these lines matters.
    means: Detection found candidates at the Kα2 positions of stronger lines and dropped
           them before any fit. The drop is reported because a genuine weak line can
           alias onto one.</pre>

Each line is marked `manual`, because its position came from you.
A line flagged [`unnamed_neighbour`](https://rietx.org/docs/stable/reference/peaks.html#the-neighbour-you-did-not-name) shares its fitting window with a line you did not name, and that line’s intensity had nowhere to go but into it.
A whole peak list rebuilt from edited positions in code meets this at every line it leaves out.

<a id="the-same-work-in-the-gui"></a>

## The same work in the GUI

Editing a peak list is easier by hand, and rietx has a graphical mode for it.
Run [`rietx gui`](https://rietx.org/docs/stable/reference/cli.html#rietx-gui) in a terminal, open or create a project with your pattern, and choose the Peaks tab.
The plot then becomes an editing surface:

| Gesture | Does |
|---|---|
| click empty space | add a line, measured by `fit_peaks` |
| drag a marker | move it |
| shift-click | exclude a line from indexing |
| right-click | remove it |

The same tab runs the indexing search, draws a candidate’s predicted lines over the pattern, and adopts a candidate as the starting point of a Le Bail fit.
Every gesture also has a typed equivalent in the panel.
[The GUI panel by panel](https://rietx.org/docs/stable/reference/gui-guide.html#peaks) covers the tab in full.

<a id="index"></a>

## Index

[`index_pattern`](https://rietx.org/docs/stable/reference/indexing.html#running-the-search) searches for cells that index the usable peaks.
A search over every crystal system can take minutes, so we narrow it.
[`SearchSpec`](https://rietx.org/docs/stable/reference/indexing.html#the-search-specification) limits it to orthorhombic cells, with a volume between 200 and 250 Å³ and no axis longer than 8.5 Å.
Those limits come from knowing the answer.
With a real unknown, start from the formula and a wide window, and let the search run longer.

[`budget_seconds`](https://rietx.org/docs/stable/reference/indexing.html#presets-budgets-and-the-three-states-of-a-system) caps each engine’s search of each crystal system, and its default is 30 s.
This search finishes inside that on a quiet laptop.
On a laptop running other work, one engine ran past 30 s and the whole call took 58 s.
A search stopped by its cap cannot rule anything out, so we allow 120 s.

```ipython3
spec = SearchSpec(
    systems=("orthorhombic",),
    min_volume=200,
    max_volume=250,
    max_d_axis=8.5,
    budget_seconds=120,
)
indexing = rx.index_pattern(peaks, data=data, instrument=lab, spec=spec)
indexing
```

<pre>engines: dichotomy, svd, trial_error   systems: orthorhombic   lines: 54   validated: yes

 1. [ high ] orthorhombic P   5.73930 4.96157 7.97039 Å  90.000 90.000 90.000°   V = 226.96 Å³
      found by dichotomy, svd, trial_error;  indexed 51/54 lines;  chi2_red 0.46;  Le Bail Rwp 0.2049, 0 of 105 reflections absent

CELL: orthorhombic P 5.73930 4.96157 7.97039 Å 90.000 90.000 90.000°
  info[PEAK_POSITION_PRECISION]: The median σ(2θ) is 0.0015° and the worst is 0.0169°.
                                 The median σ(Q)/Q is 5.31e-05 and σ(Q) is 0.002 of the
                                 mean line spacing.
    at: 2θ 10.00-79.98°
    means: States the measured position precision against what an indexing search needs.
           A cell search matches within an assumed systematic allowance rather than
           within σ, so this is context for reading a search result, not a refusal.
  info[INDEX_SHIFT_FROM_PAIRS]: A systematic shift of 0.0043° 2θ was measured from 8
                                harmonic reflection pairs agreeing to 0.0030° (of 14
                                admitted, z = 8.3 against 200 structureless replicates,
                                p = 0.005). The cause is not named: no template was
                                refuted.
    at: allowance = 0.0043° 2θ (measured), best template = cos_theta
    help: Extend the 2θ range if the cause matters.
    means: The size of a systematic 2θ shift was measured from pairs of harmonic
           reflections in the peak list, with no reference positions. That size set the
           search window. The pair method fixes the amplitude but cannot name the cause,
           because a zero-point shift and a specimen displacement look alike over a
           short range. A wider 2θ range separates them.
  info[INDEX_SYSTEMS_NOT_COVERED]: 6 crystal system(s) were not searched.
    at: cubic, hexagonal, trigonal, tetragonal, monoclinic, triclinic
    help: Search the missing systems before you conclude anything about the specimen.
    means: The search covered only some crystal systems. A failure to find a cell means
           only that none was found in those systems. It says nothing about the
           specimen, and it is no evidence of a mixture. Search the missing systems
           before you draw a conclusion.
  info[INDEX_CELL_SYSTEMATIC_UNQUANTIFIED]: A Bragg-Brentano cell carries about ±85 ppm
                                            that no esd reports. Sweeping the goniometer
                                            radius over 180-320 mm moves Rwp by only
                                            0.029 points, and specimen displacement
                                            absorbs the change 4.6×.
    at: R = 217.5 mm (declared, not measured by the fit)
    help: Quote the cell to no better than this level, or calibrate against a certified
          standard (lab_calibrate with the certified cell held fixed) and carry the
          correction.
    means: The esd on an indexed cell comes from the line positions alone. It measures
           precision, not accuracy. Instrument geometry adds a systematic error, such as
           the goniometer radius of a Bragg-Brentano diffractometer, that the data
           cannot identify and no esd reports. Quote the cell to a coarser level than
           its esd, or calibrate against a certified standard.
  warning[INDEX_IMPURITY_LINES]: 53 observed peak(s) have no calculated reflection
                                 nearby after a whole-pattern Le Bail fit of this cell.
    on: candidate orthorhombic P V=227.0
    at: orthorhombic P, V = 227.0 Å³, cell 5.7393 4.9616 7.9704 Å, 90.000 90.000 90.000°, strongest region near 10.100°
    help: If a handful of lines remain, subtract the solved phase and index the
          remainder. If most of the pattern is unmatched, treat the cell as wrong.
    means: After a whole-pattern Le Bail fit of this cell, some observed peaks sit where
           it predicts no reflection. A handful of such lines points to a second phase.
           Many of them point to a wrong cell. This package does not index multi-phase
           patterns, so subtract the solved phase before you index the remainder.</pre>

The top candidate is the true cell, with its axes in a different order.
All three [search engines](https://rietx.org/docs/stable/reference/indexing.html#three-engines-and-why-the-default-is-all-of-them) found it.

The verdict line names it.
rietx names a single cell only when exactly one candidate reaches [high confidence](https://rietx.org/docs/stable/reference/indexing.html#the-confidence-gate), and this one does.
Its row indexes 51 of the 54 usable lines, and none of the reflections it predicts is missing from the indexer’s [quick Le Bail fit](https://rietx.org/docs/stable/reference/indexing.html#the-whole-profile-test).
The three lines it leaves are the candidate’s [`unindexed_two_theta`](https://rietx.org/docs/stable/reference/indexing.html#one-candidate), printed below.
The one near 36° is two reflections 0.05° apart, fitted as one line between them.

The verdict depends on every search finishing.
An engine stopped by `budget_seconds` makes the row say [`search_incomplete`](https://rietx.org/docs/stable/reference/indexing.html#presets-budgets-and-the-three-states-of-a-system), and the verdict line then says NO CELL.

The candidate’s [`INDEX_IMPURITY_LINES`](https://rietx.org/docs/stable/reference/diagnostics-indexing.html#index-impurity-lines) warning comes from the indexer’s own quick Le Bail fit, whose Rwp in the candidate’s row is poor.
A poor fit leaves many peaks unmatched, so that count is not evidence of a second phase.
Our own Le Bail fit below settles it.

[`best_or_none()`](https://rietx.org/docs/stable/reference/indexing.html#the-result-object) returns a cell only when the gate passes.
Here it returns the candidate.

```ipython3
print(indexing.best_or_none())
```

```myst-ansi
CellCandidate
  cell:                [5.7392979, 4.9615748, 7.9703933, 90, 90, 90]
  cell_esd:            [0.00020205575, 0.0001722875, 0.00031808872, 0, 0, 0]
  system:              'orthorhombic'
  centring:            'P'
  lattice_group:       'P m m m'
  volume:              226.96457
  volume_esd:          0.019150129
  af:                  [0.030358651, 0.040621964, 0.015741296, 0, 0, 0]
  engine_cell:         [5.7392979, 4.9615748, 7.9703933, 90, 90, 90]
  change_of_basis:     [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
  n_indexed:           51
  n_lines:             54
  n_search_unindexed:  1
  unindexed_two_theta: [26.606972, 36.148784, 70.286849]
  chi2_red:            0.46489036
  shift_template:      'cos_theta'
  shift_coefficient:   -0.00055261417
  shift_esd:           0.002002964
  fom: 7 FigureOfMerit
    [0] FigureOfMerit
      name:             'm20'
      value:            292.49411
      n_lines:          20
      n_possible:       35
      k_sigma:          3
      mean_discrepancy: 1.4639996e-05
      blind_spot:       'counts lattice-possible lines, so it is blind to space-group extinctions; its ⟨ΔQ⟩ is a plain mean, so one impurity line among the first twenty wrecks it; its mean discrepancy is trimmed by 2 line(s) to match what the search was allowed to leave unindexed, so that many badly-fitting lines are invisible to it'
    [1] FigureOfMerit
      name:             'f_n'
      value:            377.71612
      n_lines:          20
      n_possible:       35
      k_sigma:          3
      mean_discrepancy: 0.001512852
      blind_spot:       'lives in 2θ, so a refined zero shift can manufacture a large value by absorbing a mismatch a Q-space figure would show; its mean discrepancy is trimmed by 2 line(s) to match what the search was allowed to leave unindexed, so that many badly-fitting lines are invisible to it'
    [2] FigureOfMerit
      name:             'indexed_fraction'
      value:            0.94444444
      n_lines:          54
      n_possible:       103
      k_sigma:          3
      mean_discrepancy: -1
      blind_spot:       "a big enough cell indexes everything — measured, a wrong phase scored 83.7 % against the truth's 79.2 % with 390 predicted lines to the truth's 23"
    [3] FigureOfMerit
      name:             'indexed_intensity_fraction'
      value:            0.93563866
      n_lines:          54
      n_possible:       103
      k_sigma:          3
      mean_discrepancy: -1
      blind_spot:       "a big enough cell indexes everything — measured, a wrong phase scored 83.7 % against the truth's 79.2 % with 390 predicted lines to the truth's 23"
    [4] FigureOfMerit
      name:             'predicted_seen_fraction'
      value:            0.49514563
      n_lines:          54
      n_possible:       103
      k_sigma:          3
      mean_discrepancy: -1
      blind_spot:       'legitimately absent reflections count against a correct cell: space-group extinctions (unknown until the extinction symbol is determined) and reflections too weak to detect'
    [5] FigureOfMerit
      name:             'm_rev'
      value:            2.8805593
      n_lines:          20
      n_possible:       31
      k_sigma:          3
      mean_discrepancy: 0.0021123377
      blind_spot:       "penalises a correct cell for space-group extinctions, the same blind spot as predicted_seen_fraction and confirmed by the paper that defines it; its δ is floored at the median σ(Q), which is this package's addition and not Oishi-Tomiyasu's, because δ_rev vanishes on fp-exact data exactly as M₂₀'s ⟨ΔQ⟩ does; and its scale is not M₂₀'s — correct cells score ~1.7-15.8 where M̃₂₀ reaches 556, so a de Wolff threshold would reject almost all of them; its mean discrepancy is trimmed by 2 line(s) to match what the search was allowed to leave unindexed, so that many badly-fitting lines are invisible to it"
    [6] FigureOfMerit
      name:             'm_sym'
      value:            772.12419
      n_lines:          20
      n_possible:       31
      k_sigma:          3
      mean_discrepancy: 0.0021123377
      blind_spot:       'the product of the forward and reversed figures over one window, so it inherits both their blind spots and is not independent evidence from m_rev; a candidate can reach it by being mediocre in both directions rather than good in either; its mean discrepancy is trimmed by 2 line(s) to match what the search was allowed to leave unindexed, so that many badly-fitting lines are invisible to it'
  found_by:            ['dichotomy', 'svd', 'trial_error']
  bravais: BravaisOpinion
    system:           'orthorhombic'
    system_loosest:   'orthorhombic'
    system_gemmi:     'orthorhombic'
    system_spglib:    'orthorhombic'
    ambiguous:        False
    methods_disagree: False
    reduced_cell:     [4.9615748, 5.7392979, 7.9703933, 90, 90, 90]
  lebail: LeBailValidation
    rwp:                          0.20490532
    gof:                          4.7776264
    space_group:                  'P m m m'
    n_reflections:                105
    predicted_but_absent:         0
    unmatched_observed:           53
    unmatched_observed_two_theta: <53 floats 10.1…78.48>
    status:                       'converged'
    n_stages:                     4
  confidence:          'high'
  diagnostics: 1 Diagnostic
    [0] warning[INDEX_IMPURITY_LINES]: 53 observed peak(s) have no calculated reflection nearby after a whole-pattern Le Bail fit of this cell.
        at: orthorhombic P, V = 227.0 Å³, cell 5.7393 4.9616 7.9704 Å, 90.000 90.000 90.000°, strongest region near 10.100°
        help: If a handful of lines remain, subtract the solved phase and index the remainder. If most of the pattern is unmatched, treat the cell as wrong.
```

The candidate prints every field, its figures of merit included.
When no candidate reaches high confidence, `best_or_none()` returns `None` and the result carries [`INDEX_ABSTAINED`](https://rietx.org/docs/stable/reference/diagnostics-indexing.html#index-abstained).
[Indexing](https://rietx.org/docs/stable/theory/indexing.html#figures-of-merit-in-both-directions) explains the figures of merit behind the ranking.

<a id="check-the-candidate-with-le-bail"></a>

## Check the candidate with Le Bail

[`structure_from_candidate`](https://rietx.org/docs/stable/reference/indexing-evidence.html#from-a-candidate-to-a-phase) turns a candidate into a structure for Le Bail fitting.
It uses the lattice’s highest-symmetry space group, so the fit tests the lattice and nothing else.
We test the first candidate because it is the true cell.
With a real unknown, test each candidate whose caveats you cannot rule out.

```ipython3
candidate = indexing.candidates[0]
structure = structure_from_candidate(candidate)
structure.phases[0].space_group
```

```myst-ansi
'P m m m'
```

As in notebook 01, start the background’s constant term at the 5th percentile of the counts, and run the plan [twice](https://rietx.org/docs/stable/reference/refinement.html#le-bail-passes).

```ipython3
instrument = rx.Instrument.bragg_brentano(radiation="CuKa")
instrument.background = rx.BackgroundChebyshev.with_terms(4)
c0 = float(np.percentile(data.intensity, 5))
instrument.background.coefficients[0].value = c0

ref = rx.Refinement(structure, instrument)
first = ref.fit(data, mode="lebail", plan="profile_only")
second = ref.fit(data, mode="lebail", plan="profile_only")
ref.history
```

```myst-ansi
t4604ebdd  13 nodes  data=4604ebdd
 n0000  root                   —
 n0001  stage:bkg              Rwp 0.7281
 n0002  stage:zero             Rwp 0.7149
 n0003  stage:cell             Rwp 0.7108
 n0004  stage:profile_w        Rwp 0.1538
 n0005  stage:profile          Rwp 0.0702
 n0006  stage:closing          Rwp 0.0568
 n0007  stage:bkg              Rwp 0.0793
 n0008  stage:zero             Rwp 0.0716
 n0009  stage:cell             Rwp 0.0691
 n0010  stage:profile_w        Rwp 0.0680
 n0011  stage:profile          Rwp 0.0666
*n0012  stage:closing          Rwp 0.0533
```

The first pass starts from the default instrument’s peak widths, so its Rwp falls most at the two width stages.
The second pass starts where the first ended.

Keep the pass with the lower Rwp, and return the working state to its node.

```ipython3
best = min([first, second], key=lambda r: r.statistics.rwp)
ref.checkout(best.node_id)
best
```

<pre>RefinementResult: converged (lebail)
  stage bkg: converged (2 evals, ftol=1e-06)
  stage zero: converged (4 evals, ftol=1e-06)
  stage cell: converged (4 evals, ftol=1e-06)
  stage profile_w: converged (5 evals, ftol=1e-06)
  stage profile: converged (5 evals, ftol=1e-06)
  stage closing: converged (5 evals, ftol=solver default), max|Δθ|/esd=0.000
  diagnostics: none
  provenance: rietx 1.10.0, backend=numpy, solver=lm
  Rwp 0.0533 / Rexp 0.0429 (GoF 1.24), Rp 0.0341, χ² 1.5, DW 1.40
  parameters: 13
    path                      value         at_bound
    phases.0.cell.a           5.739529(82)  false
    phases.0.cell.b           4.961587(71)  false
    phases.0.cell.c           7.97060(12)   false
    instrument.zero_shift     0.00002(52)   false
    instrument.profile.u      0.0086(26)    false
    instrument.profile.v      -0.0077(26)   false
    instrument.profile.w      0.00560(65)   false
    instrument.profile.x      0.0515(20)    false
    instrument.profile.y      -0.0061(58)   false
    instrument.background.c0  195.01(80)    false
    instrument.background.c1  -0.0(14)      false
    instrument.background.c2  -5.6(12)      false
    instrument.background.c3  -0.2(12)      false</pre>

The fit raises no diagnostic.
The profile’s Y term, the Lorentzian width that grows as tan θ, refined to zero within its esd.
The pattern was made with no such term, so zero is the right answer.

The table under the summary starts with the refined lattice parameters.
They match the CIF’s cell, in the candidate’s order of axes.
Now look at the fit.

```ipython3
best.plot()
```

![](../../_images/twin-3052fc78e41884b7.png)

The fit puts a reflection under every peak and leaves only small residuals, so the lattice is right.

Some ticks stand over no peak, such as those near 11° and 15.5°.
They are reflections `P m m m` allows and aragonite’s glide planes remove.

What this does not settle is the space group.
That is a question about [which reflections are absent](https://rietx.org/docs/stable/reference/indexing-evidence.html#the-extinction-symbol), and it comes after the cell is refined.
