CTR Resolution Modeling

CTRresolution supplies optional instrumental-resolution broadening for calculated crystal truncation rods. It does not change SXRDCrystal.F. CTRopt can opt into the same sampled broadening for calculated fit amplitudes.

Intensity and output convention

Resolution acts on calculated intensity rather than on the complex amplitude:

\[I_{\mathrm{res}}(L_i) = \frac{\int R_i(L-L_i)\lvert F(H_i,K_i,L)\rvert^2\,\mathrm{d}L} {\int R_i(L-L_i)\,\mathrm{d}L}.\]

The returned CTRCollection follows the existing sfI convention and stores the effective amplitude

\[F_{\mathrm{eff}}(L_i) = \sqrt{I_{\mathrm{res}}(L_i)}.\]

Phase and experimental-error information cannot be retained by this operation and is therefore absent from the result. Input collections are never modified.

Resolution functions

The built-in functions are BoxResolution and GaussianResolution. Their effective width is in reciprocal lattice units:

\[\Delta L = \Delta L_0 + \Delta L_1\lvert\sin(\gamma)\rvert + \Delta L_2\lvert\sin(\delta)\rvert.\]

Here gamma and delta are Vlieg detector angles in radians. For a box, DeltaL is the complete support width. For a Gaussian it is the full width at half maximum (FWHM). Setting both angle-dependent contributions to zero creates a constant function; setting all contributions to zero disables broadening.

Angle-dependent models require angle records on their input collection. They can be calculated for all rods at once:

ub = HKLVlieg.UBCalculator(crystal.reference_uc, energy=70.0)
ub.defaultU_GID()
angle_calculator = HKLVlieg.VliegAngles(ub)
ctrs.calcAnglesZmode(
    angle_calculator,
    fixedangle=np.deg2rad(0.1),
    fixed="in",
)

Fast convolution on existing points

fast_convolve uses only the L coordinates and amplitudes already present in each rod. It does not interpolate and does not require an equidistant grid. Local composite-trapezoidal weights account for unequal point spacing, so a dense part of a scan is not overrepresented merely because it has more points.

At the first and last points the kernel is truncated to the available data and renormalized. This preserves constant intensity without assuming either zero intensity or an extrapolation outside the measured range. L coordinates must be finite and unique within a rod, but they may be in any order.

from orgui.datautils.xrayutils import CTRresolution

model = CTRresolution.GaussianResolution(
    delta_l_0=0.015,
    delta_l_1=0.08,
)
broadened = CTRresolution.fast_convolve(calculated_ctrs, model)

The fast method approximates the convolution represented by the existing sampling. If the kernel is narrower than the local point spacing, it may only contain the central point and consequently cannot reproduce unresolved detail.

Sampled structure factors

sample_structure_factor reevaluates crystal.F around every requested point and is slower but does not depend on the density of the input L samples. It uses Gauss-Legendre quadrature for a box and Gauss-Hermite quadrature for an unbounded Gaussian. The quadrature order must be a positive odd integer and defaults to 25.

sampled = CTRresolution.sample_structure_factor(
    data_positions,
    crystal,
    model,
    quadrature_order=25,
)

The central point’s gamma determines the width of all quadrature samples for that point; the same applies to delta when delta_l_2 is nonzero. H and K remain fixed. This implementation therefore models only out-of-plane L resolution.

Fitting resolution widths

Use CTRopt.CTROptimizer.fit_resolution() to include the three resolution widths directly in an optimizer’s parameter vector. They are always the first three parameters, in delta_l_0, delta_l_1, delta_l_2 order, and use r.l.u. bounds. Angle-dependent widths require cached Z-mode angle records; calculate them once because they depend on the fixed CTR geometry and experimental setup, not on the fitted widths.

from orgui.datautils.xrayutils import CTRopt, CTRresolution

optimizer = CTRopt.CTROptAngleCorrection(crystal, measured_ctrs)
optimizer.calc_resolution_angles_zmode(angle_calculator, fixedangle=0.1)
optimizer.fit_resolution(
    CTRresolution.GaussianResolution(0.015, 0.08, 0.02),
    lower_bounds=(0.0, 0.0, 0.0),
    higher_bounds=(0.1, 0.2, 0.1),
)
optimizer.prepareFit()
parameters = optimizer.get_parameters()

For CTROptAngleCorrection, fit traces label these leading values as resolution_delta_l_0, resolution_delta_l_1, and resolution_delta_l_2. Their estimated standard errors are stored in optimizer.resolution_errors after statistics are evaluated.

fit_resolution(..., calculation="sample") is the default and evaluates the crystal at quadrature points. Use calculation="convolve" to convolve calculated values on the existing L grid instead; this is typically faster for fits but inherits the sampling limitations of fast_convolve(). In both modes, the optimizer retains a separate calculated_CTRs collection whose sfI arrays are updated once per parameter assignment; experimental CTRs are not modified.

A complete runnable comparison using the bundled CTR reference data is in examples/CTR/ctr_resolution_example.ipynb.

API reference

class orgui.datautils.xrayutils.CTRresolution.ResolutionFunction(delta_l_0: float, delta_l_1: float = 0.0, delta_l_2: float = 0.0)[source]

Bases: ABC

Base interface for an L-direction CTR resolution function.

The effective full width is

delta_l_0 + delta_l_1 * abs(sin(gamma)) + delta_l_2 * abs(sin(delta)).

The full central HKL coordinates and angle record are accepted by width() so resolution models can depend on diffractometer angles.

Parameters:
  • delta_l_0 (float) – Constant L-width contribution in r.l.u.

  • delta_l_1 (float) – Gamma-dependent L-width contribution in r.l.u.

  • delta_l_2 (float) – Delta-dependent L-width contribution in r.l.u.

width(h, k, l, angles=None)[source]

Return the effective L width at the supplied CTR points.

Parameters:
  • h (numpy.ndarray) – H coordinates in r.l.u.

  • k (numpy.ndarray) – K coordinates in r.l.u.

  • l (numpy.ndarray) – L coordinates in r.l.u.

  • angles – Optional structured angle array containing the enabled gamma and/or delta fields in rad.

Returns:

Effective L widths in r.l.u., broadcast to the HKL shape.

Return type:

numpy.ndarray

Raises:

ValueError – If HKL shapes are incompatible or gamma is required but missing.

abstractmethod weights(offsets, width)[source]

Return unnormalized kernel weights for L offsets in r.l.u.

abstractmethod quadrature(width, order)[source]

Return normalized L quadrature offsets and weights.

Parameters:
  • width (numpy.ndarray) – Effective widths in r.l.u.

  • order (int) – Quadrature order.

Returns:

Tuple of offsets shaped width.shape + (order,) and normalized one-dimensional quadrature weights.

Return type:

tuple[numpy.ndarray, numpy.ndarray]

class orgui.datautils.xrayutils.CTRresolution.BoxResolution(delta_l_0: float, delta_l_1: float = 0.0, delta_l_2: float = 0.0)[source]

Bases: ResolutionFunction

Box resolution whose DeltaL is the full support width.

weights(offsets, width)[source]

Return one inside [-DeltaL / 2, DeltaL / 2] and zero outside.

quadrature(width, order)[source]

Return Gauss-Legendre quadrature for the normalized box.

class orgui.datautils.xrayutils.CTRresolution.GaussianResolution(delta_l_0: float, delta_l_1: float = 0.0, delta_l_2: float = 0.0)[source]

Bases: ResolutionFunction

Gaussian resolution whose DeltaL is its FWHM.

weights(offsets, width)[source]

Return Gaussian weights with the configured full width at half maximum.

quadrature(width, order)[source]

Return Gauss-Hermite quadrature for the normalized Gaussian.

orgui.datautils.xrayutils.CTRresolution.fast_convolve(ctrs, resolution)[source]

Convolve CTR intensity using only the existing irregular L points.

No interpolation or equidistant resampling is performed. Local composite trapezoidal weights account for unequal L spacing, and each kernel is renormalized over the samples available at the rod boundaries.

Parameters:
  • ctrs (CTRCollection) – CTR amplitudes to convolve. Amplitudes must be finite and nonnegative.

  • resolution (ResolutionFunction) – Box or Gaussian L-resolution function.

Returns:

A new collection containing effective amplitudes sqrt(convolved(abs(F)**2)).

Return type:

CTRCollection

orgui.datautils.xrayutils.CTRresolution.sample_structure_factor(ctrs, crystal, resolution, quadrature_order=25)[source]

Sample crystal intensity around every CTR point along L.

H and K remain fixed. Box functions use Gauss-Legendre quadrature and Gaussian functions use Gauss-Hermite quadrature. The effective width is evaluated from each central point and its central angle record.

Parameters:
  • ctrs (CTRCollection) – Collection supplying the requested HKL points and optional angles.

  • crystal – Crystal-like object providing F(h, k, l).

  • resolution (ResolutionFunction) – Box or Gaussian L-resolution function.

  • quadrature_order (int) – Positive odd number of deterministic quadrature points. Defaults to 25.

Returns:

A new collection containing effective amplitudes sqrt(integrated(abs(F)**2)).

Return type:

CTRCollection