RUGNUX: a model run also refines the model it placed; the model can be given as an input; only the unmerged MTZ is written by default; the _process.h5 is not a default output; a short "when a run fails" note. TUTORIAL: the .ins, the refined model/maps/blobs, the log and _modules.h5 in the output list; the report is not written with -o ""; stills: -X Auto picks ffbidx with a cell, a run over several files. FORMATS: the XFEL stills formats and model/reference inputs told by content. CALIBRATION: -C and -S as the calibrant. INSTALL: the CPU path runs every indexer and the refinement. MODEL_AND_REFERENCE: the refined model is written as .pdb too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu (cherry picked from commit 0062d72b95e996438ddc65dc343b526c84daa7fb)
99 lines
6.5 KiB
Markdown
99 lines
6.5 KiB
Markdown
# Detector calibration from powder rings (`rugnux --mode calibration`)
|
||
|
||
|
||
The `calibration` mode determines the detector geometry — PONI *x*/*y*, the two tilts
|
||
rot1/rot2 and the distance — from the powder rings of a calibrant, and writes it as a
|
||
pyFAI **`<prefix>.poni`** file and a machine-readable **`<prefix>.json`**, alongside a printed
|
||
report of how far each parameter moved from the header. Bragg data pin the beam centre worst (it is gauge-coupled to the crystal orientation);
|
||
a powder ring has no orientation to be coupled to, so this is the measurement that fixes it.
|
||
|
||
```
|
||
rugnux --mode calibration --calibrant lab6 -o det LaB6_master.h5
|
||
```
|
||
|
||
## What it writes
|
||
|
||
`<prefix>.poni` is for pyFAI and the tools that read its format. `<prefix>.json` is for everything
|
||
else, and has two members:
|
||
|
||
* **`dataset_settings`** — the geometry under the property names
|
||
[`dataset_settings`](https://jungfraujoch.readthedocs.io) gives them in `jfjoch_broker`'s OpenAPI
|
||
schema, and nothing else. It is a valid `dataset_settings` body as it stands, so it can be POSTed
|
||
or merged into one without translating a field:
|
||
|
||
```
|
||
curl -X POST -H 'Content-Type: application/json' \
|
||
-d "$(jq -c .dataset_settings det.json)" http://broker:5232/start
|
||
```
|
||
|
||
`beam_x_pxl`/`beam_y_pxl` is the **PONI**, as everywhere in this system — on a tilted detector it
|
||
is not where the direct beam lands. The three `poni_rot*_rad` are written whenever any of them is
|
||
non-zero, and left out when all are zero: a body that omits them states a *flat* detector, so they
|
||
travel together or not at all.
|
||
|
||
* **`calibration`** — what the run knows about that geometry: the residual, the fit's own sigmas and
|
||
the correlation between the tilt and the beam centre, whether the tilt cleared its significance
|
||
test or was declined and pinned, where the direct beam lands, and where the spots independently
|
||
put the beam. A calibration that has gone wrong looks exactly like one that has not until those
|
||
are read.
|
||
|
||
`--calibrant` takes `lab6`, `agbh` (silver behenate), `ceo2`, `si` or `ice`, case-insensitively.
|
||
**`ice`** calibrates a real experiment against its own ice rings — no calibrant exposure needed —
|
||
and is the reason a calibrant is a list of ring positions rather than a unit cell: hexagonal ice
|
||
is P6<sub>3</sub>/mmc, so rings enumerated from its cell would include systematically absent ones.
|
||
A standard that is not on the list is given by its unit cell instead: with `-C` the cell **is** the
|
||
calibrant, its rings enumerated from the cell, with the absences of `-S` where that is given (taken as
|
||
primitive where it is not). `-S` supplies symmetry absences only, so a standard whose extinctions come
|
||
from its atomic basis — silicon's 222 — is better named with `--calibrant`.
|
||
|
||
`--calibration` picks how the rings are measured, and **both aggregate over every processed image** — `-s`/`-e`/`-t`
|
||
select which images:
|
||
|
||
- **`rings`** (default) sums the (*q* × azimuth) azimuthal profile over every processed image into
|
||
one map and fits the ring arcs in it. A powder ring is an arc, not a set of spots, and the summed
|
||
profile measures it at every azimuth with all the run's counts behind it. It needs the profile to
|
||
be binned in azimuth, so this mode defaults `--azim-phi-bins` to 32.
|
||
- **`spots`** pools the found spots of every processed image and fits those. It determines the
|
||
centre from scratch (a Hough circle vote, which quantises it to a whole pixel) and then refines.
|
||
|
||
Both routes read the ring position out of a binned profile or a spot centroid, so the radial
|
||
sampling matters: at a long detector distance the default 0.01 Å⁻¹ *q* bin is several pixels wide
|
||
and quantises the `rings` route accordingly — pass a finer `--azim-q-spacing` there (the total
|
||
*q* × azimuth bin count must stay under 65534).
|
||
|
||
The report prints the fitted geometry, the scatter of the ring points about the fitted rings and the
|
||
standard error that implies on the centre. That error is *formal*: it measures the scatter of the
|
||
points, not whether the rings themselves are trustworthy, so it stays small when a fit goes wrong
|
||
for a structural reason — one visible ring, or ice that is textured rather than smooth.
|
||
|
||
A calibration is run because the header is in doubt, so a fit that quietly hands part of that header
|
||
back has answered nothing — and it looks exactly like a fit that worked, down to the residual and the
|
||
sigmas around it. **`calibration.converged`** in the JSON says which of the two a file is. It is false
|
||
when the tilt was declined and pinned at a *non-zero* header value — the rings said they could not tell
|
||
a tilt from a shift of the beam centre, and the angle written in its place has no more support than the
|
||
one refused — or when the fit's covariance never conditioned, so it cannot say what it determined. In
|
||
either case Rugnux prints the reason, writes the `.json` with `converged` false and
|
||
`not_converged_reason` beside it, exits non-zero, and writes **no `.poni`**: a PONI file states where
|
||
the detector is and has no field in which to say that it does not know. A declined tilt over a header
|
||
that states *no* tilt is not this — reporting no tilt is then exactly what the fit measured.
|
||
|
||
A `.poni` is refused for a second reason, whatever the fit found: a detector whose **image
|
||
orientation** is not the identity — the stored image mirrored in Y, or turned by a multiple of 90°
|
||
about the beam. That comes either from `--detector-mirror-y` / `--detector-quarter-turns` or from the
|
||
file itself (a PILATUS miniCBF axis table, an NXmx module's pixel directions). A PONI states the
|
||
detector in five numbers — two offsets, a distance and three rotations — and has no field for how the
|
||
image is stored, so one written here would describe a different geometry from the one that was
|
||
measured. The run says so and exits non-zero.
|
||
|
||
`--no-refine-tilt` holds rot1/rot2 where the header put them and fits only the centre and the
|
||
distance. The tilt is real and worth measuring, but a program that has nowhere to put one — XDS
|
||
takes a detector normal to the beam — is better given a geometry that was measured with the tilt
|
||
pinned than one that was measured tilted and then flattened, because in the tilted fit the centre
|
||
and the distance have already absorbed the tilt.
|
||
|
||
Both the PONI (the point of normal incidence, which is what a `.poni` file stores) and the direct
|
||
beam (where the beam lands, which is what most other programs call the beam centre) are printed.
|
||
They differ by *distance* × tan(rot) once the detector is tilted, which on a 0.3° tilt at 300 mm is
|
||
several pixels — enough to look like a disagreement with another program when there is none.
|
||
|