<a id="guide-blank"></a>

<a id="how-to-use-a-measured-blank-as-the-background"></a>

# How to use a measured blank as the background

Read the blank scan with `rietx.read_pattern`, build the background with `BackgroundFixedPlusChebyshev.from_pattern`, and assign it to `Instrument.background`.

A blank is a scan of everything the specimen is not: an empty capillary or can, a matrix-only scan, an empty furnace.
rietx holds it under the pattern, scaled, with a low-order polynomial on top, and never subtracts it.
Subtracting a curve would change the counting statistics while leaving the pattern’s σ as it was.

In TOPAS this is `bkg_file("blank.xy")`, and with a refined scale `bkg_file("blank.xy", @, s)`.

<a id="fit-the-pattern-over-its-blank"></a>

## Fit the pattern over its blank

This uses APS 11-BM’s scan of NIST SRM 640c silicon in a Kapton capillary and the same capillary scanned empty, from the same beamtime in February 2010.
`rietx.examples.fetch` downloads each file, about 1.5 MB, the first time it is asked for, checks it, and keeps it under `~/.rietx/data`.
Without a network it raises `OSError` naming the path to save the file at by hand.

```python

import rietx as rx
from rietx.examples import fetch

data = rx.read_pattern(fetch("11BM_Si640c.xy"))
blank = rx.read_pattern(fetch("11BM_Kapton.xy"))


def silicon():
    """SRM 640c at its certified lattice parameter, 5.4311946 Å."""
    a = rx.Parameter(value=5.4311946)
    right = rx.Parameter(value=90.0)
    return rx.Structure(phases=[rx.Phase(
        name="Si", space_group="F d -3 m :2",
        cell=rx.Cell(a=a, b=a, c=a, alpha=right, beta=right, gamma=right),
        scale=rx.Parameter(value=3e-4, min=0.0),
        atoms=[rx.Atom(label="Si", species="Si", x=rx.Parameter(value=0.125),
                       y=rx.Parameter(value=0.125), z=rx.Parameter(value=0.125),
                       biso=rx.Parameter(value=0.44, min=0.0))])])


def fit_with(background, hold=()):
    instrument = rx.Instrument.debye_scherrer(wavelength=0.412359)
    instrument.background = background
    ref = rx.Refinement(silicon(), instrument)
    ref.hold(["phases.0.cell.*", *hold])
    return ref, ref.fit(data, plan="mccusker_default", two_theta_limits=(1.997, 49.996))


background = rx.BackgroundFixedPlusChebyshev.from_pattern(
    blank, n_terms=3, vary_scale=True, source="APS 11-BM run 4736, empty Kapton capillary")
ref, result = fit_with(background)

scale = result.parameter("instrument.background.scale")
print(f"blank scale {scale.value:.2f} ± {scale.stderr:.2f}")
```

```text
blank scale 0.84 ± 0.02
```

`from_pattern` takes any pattern `read_pattern` opens.
`n_terms` sets the Chebyshev polynomial refined on top of the blank, `vary_scale` frees the blank’s scale, and `source` records where the curve came from.
The lattice parameter is held at the certificate’s value, because this is a standard.

The blank’s own σ comes with it, as `BackgroundFixedPlusChebyshev.fixed_sigma`.
Each channel’s weight then includes the blank’s counting noise, so a short blank scan weighs less than a long one.

<a id="free-the-scale-against-a-low-order-polynomial"></a>

## Free the scale against a low-order polynomial

A blank is not on the specimen’s scale.
The two scans differ in counting time and monitor, and in the sample scan the specimen attenuates the container’s scattering.
The polynomial cannot correct that, because it adds to the level and never rescales the shape.

The refined scale is a measurement only against a low-order polynomial.
With more terms the polynomial imitates part of the blank, and the scale walks away:

```python
six = rx.BackgroundFixedPlusChebyshev.from_pattern(blank, n_terms=6, vary_scale=True)
_, result6 = fit_with(six)
scale6 = result6.parameter("instrument.background.scale")
print(f"with 6 terms: blank scale {scale6.value:.2f} ± {scale6.stderr:.2f}")
```

```text
with 6 terms: blank scale 0.69 ± 0.03
```

On this pair a scan of fixed scales, scored under one σ, puts the best scale near 0.85, which the three-term fit agrees with.
A lower Rwp with more terms does not make the scale a better measurement.

`rietx.background.select_chebyshev_order(data, fixed=background)` chooses the polynomial order for the part of the background the blank does not describe.

<a id="keep-the-scale-at-1-0"></a>

## Keep the scale at 1.0

Every plan preset except `magnetic_width` frees `instrument.background.*` in its first stage, and that includes the scale.
So a blank built with `vary_scale=False` is refined anyway, by the first stage.
Hold it to keep it fixed:

```python
fixed = rx.BackgroundFixedPlusChebyshev.from_pattern(blank, n_terms=3)
held_ref, _ = fit_with(fixed, hold=["instrument.background.scale"])
print("held scale", held_ref.fitted_instrument.background.scale.value)
```

```text
held scale 1.0
```

[How to keep a parameter fixed whatever the plan frees](hold.html.md) explains why a cleared refine flag is not enough.

<a id="know-the-limits"></a>

## Know the limits

- The blank must cover every fitted channel.
  A fit whose range runs past the blank is refused, naming both ranges.
  Narrow the fit range, or measure the blank over the whole scan.
- Interpolating a noisy blank onto the pattern’s 2θ steps makes neighbouring channels’ errors correlated, so the propagated σ is a lower limit.
  Smooth the blank first if that matters, and record that you did.
- One blank reused across a temperature or atmosphere series carries the same error into every pattern.
  A refined scale that trends along the series is partly that error.
  Refine the scale in each pattern.
- A single scale is the leading-order correction.
  In the sample scan the container’s scattering also passes through the specimen, so the true factor depends on angle.

The full script is `examples/guides/blank.py`.

<a id="see-also"></a>

## See also

- [How to check a pattern before fitting](check.html.md) builds a background from the pattern alone, when no blank exists.
