Rietveld refinement
Stage order
refine_phase follows the McCusker order. Each stage changes only its own parameters and leaves the rest at the value the previous stage reached:
| Stage | Parameters | Method |
|---|---|---|
background_scale | scale and the background polynomial | analytic weighted least squares |
zero_cell | zero shift and the free cell numbers | Nelder-Mead simplex |
width | multiplier on the PXRD FWHM | Nelder-Mead simplex |
structure | Wyckoff coordinates, isotropic ADPs, occupancies, March-Dollase texture | Nelder-Mead simplex |
The scale and a constant, linear or quadratic background are linear, so they are solved directly at every model evaluation. The other parameters move the reflection positions or the relative intensities and are refined by a bounded direct search.
Why a direct search
The zero shift, the cell and the width are strongly correlated: a small cell change can be absorbed by a width change, and a zero shift can be absorbed by the cell. At a displaced start the Gauss-Newton direction can point uphill, while a simplex still finds the valley. Every simplex vertex is projected onto the parameter bounds and the scale/background are refitted before the vertex is scored, so the search compares complete models.
Wyckoff-allowed coordinates
A site may only move along the directions its stabilizer leaves invariant. For every symmetry operation R that maps the site onto itself modulo a lattice translation, a displacement u keeps the site on the same Wyckoff position when R u = u. Intersecting that condition gives:
- three directions on a general position,
- one on a mirror or a rotation axis,
- none at an inversion centre.
free_directions(operations, frac) returns an orthonormal deterministic basis of that subspace. The refinement creates one parameter per basis vector, so a rutile oxygen at 4f (x, x, 0) moves only along [1, 1, 0] / sqrt(2) and a site at the origin does not move at all.
Cell family ties
Only the numbers the metric family leaves free are refined, and the ties are restored after every move: a cubic cell keeps a = b = c, a tetragonal or hexagonal cell keeps a = b, and the angles of the higher-symmetry families stay at their base values. Without the ties, a refined a would no longer equal the untouched b and symmetry-equivalent reflections would split apart.
Texture
The March-Dollase factor for a reflection with unit normal n and a preferred-orientation axis a is
r^2 cos^2(theta) + sin^2(theta) / r, raised to -3/2,with cos(theta) = n . a. r = 1 is random orientation. In this port the axis is fixed by RefineOptions::texture_axis and only r is refined.
Standard uncertainties
At the final values the Jacobian is built by central differences of the complete model, so the analytic scale/background refit is already projected into every column. The covariance is (J^T W J)^-1 scaled by the reduced chi-squared, and the diagonal gives the parameter esds. The scale and the background coefficients are fitted analytically and are reported without esds.
Occupancies and the scale are degenerate: multiplying every occupancy by c and dividing the scale by c leaves the pattern unchanged. Only the occupancy ratios are observable, and the reported absolute values follow the fitted scale. This is why the CLI enables occupancy refinement only with --occupancies.
Validation
crates/xtalforge-analysis/tests/rietveld_golden.rs reads tests/golden/phase-7/rietveld/rutile.xy, generated by the Crystal-Builder _rutile_xy fixture (rutile, P4_2/mnm, a = 4.5940, c = 2.9590, oxygen at x = 0.29, Cu K alpha 1 + K alpha 2, 20-80 degrees, Poisson noise with seed 0). The structure starts at x = 0.26; the McCusker stages must return it to the generating position:
xwithin3e-3of0.29, still on(x, x, 0),aandcwithin2e-3Angstrom, witha = bexactly,- zero shift within
0.02degree, Rwp < 0.25, every requested parameter with a positive esd,- stages exactly
pawley_cell -> background_scale -> zero_cell -> width -> structure.
When the cell is refined, a Pawley fit runs first (pawley_cell) and its cell becomes the starting point when it moves less than 5 %. A Rietveld cell refinement alone is local: from a rutile cell 1 % off it stopped at Rwp 0.47 with c = 2.932 instead of 2.989; with the Pawley start it recovers a = 4.6399 and c = 2.9890 (Rwp 0.12, the same as from the true cell). Added by the functional audit of 2026-11-11.
The synthetic tests in tests/rietveld_stages.rs add exact recoveries of a known zero shift, cell, width multiplier, March-Dollase ratio, ADP multiplier, occupancy ratio and the Wyckoff direction counts.
Checked on a second structure
A noisy, backgrounded NaCl pattern (Poisson noise, 150 counts of background, single wavelength) refined from a starting cell of 5.64 Angstrom returns a = 5.620, width_scale = 0.997 and a zero shift of 0.000 with Rwp 0.094 when the model uses one wavelength (doublet = false, CLI --no-doublet). With the doublet model on single-wavelength data the isotropic ADP multiplier runs to its bound to compensate (Rwp 0.26, a = 5.615), and on a noise-free pattern with zero background the 1 / y weights drive Rwp towards 0.7: match the radiation and give the data a background or an uncertainty column.
Documented deviations
- The profile is the PXRD engine area-normalised Gaussian for every reflection;
RietveldOptions::kindis accepted but does not change this curve. The reference uses a pseudo-Voigt with a Caglioti width. - With
doublet = truethe copper K alpha 1/K alpha 2 pair is placed from the K alpha 1 position with the 0.5 intensity ratio. Other radiations use one line. This is why the goldenRwpis0.15, not the~0.05of the rietx reference, which uses the full instrument profile. - The isotropic ADP is one global multiplier on every site's
U_iso, not one freeU_ijper site. - The energy term of
combined_objectiveneeds a force-field calculator that this signature does not have, so it stays at zero during the fit.