# 28. 1-D resolution functions

## `sasmodels.resolution`

Define the resolution functions for the data.

This defines classes for 1D and 2D resolution calculations.

class sasmodels.resolution.Perfect1D(q)

  Bases: `Resolution`

  Resolution function to use when there is no actual resolution smearing to be applied. It has the same interface as the other resolution functions, but returns the identity function.

  apply(theory)

    Smear theory by the resolution function, returning Iq.

  q: ndarray = None
  q_calc: ndarray = None

class sasmodels.resolution.Pinhole1D(q, q_width, q_calc=None, nsigma=(2.5, 3.0))

  Bases: `Resolution`

  Pinhole aperture with q-dependent gaussian resolution.

  q points at which the data is measured.

  q_width gaussian 1-sigma resolution at each data point.

  q_calc is the list of points to calculate, or None if this should be estimated from the q and q_width.

  nsigma is the width of the resolution function. Should be 2.5. See `pinhole_resolution()` for details.

  apply(theory)

    Smear theory by the resolution function, returning Iq.

  q: ndarray = None
  q_calc: ndarray = None

class sasmodels.resolution.Resolution

  Bases: `object`

  Abstract base class defining a 1D resolution function.

  q is the set of q values at which the data is measured.

  q_calc is the set of q values at which the theory needs to be evaluated. This may extend and interpolate the q values.

  apply is the method to call with I(q_calc) to compute the resolution smeared theory I(q).

  apply(theory)

    Smear theory by the resolution function, returning Iq.

  q: ndarray = None
  q_calc: ndarray = None

class sasmodels.resolution.Slit1D(q, q_length=None, q_width=None, q_calc=None)

  Bases: `Resolution`

  Slit aperture with resolution function.

  q points at which the data is measured.

  q_width slit width (short axis); deprecated nomenclature qy

  q_length slit length (long axis); deprecated nomenclature qx

  q_calc is the list of points to calculate, or None if this should be estimated from the q and q_width.

  The weight_matrix is computed by `slit_resolution()`

  apply(theory)

    Smear theory by the resolution function, returning Iq.

  q: ndarray = None
  q_calc: ndarray = None

sasmodels.resolution.apply_resolution_matrix(weight_matrix, theory)

  Apply the resolution weight matrix to the computed theory function.

sasmodels.resolution.bin_edges(x)

  Determine bin edges from bin centers, assuming that edges are centered between the bins.

  Note: this uses the arithmetic mean, which may not be appropriate for log-scaled data.

sasmodels.resolution.geometric_extrapolation(q, q_min, q_max, points_per_decade=None)

  Extrapolate q to [q_min, q_max] using geometric steps, with the average geometric step size in q as the step size.

  if q_min is zero or less then q[0]/10 is used instead.

  points_per_decade sets the ratio between consecutive steps such that there will be \(n\) points used for every factor of 10 increase in q.

  If points_per_decade is not given, it will be estimated as follows. Starting at \(q_1\) and stepping geometrically by \(\Delta q\) to \(q_n\) in \(n\) points gives a geometric average of:

  \[\log \Delta q = (\log q_n - \log q_1) / (n - 1)\]

  From this we can compute the number of steps required to extend \(q\) from \(q_n\) to \(q_\text{max}\) by \(\Delta q\) as:

  \[n_\text{extend} = (\log q_\text{max} - \log q_n) / \log \Delta q\]

  Substituting:

  \[n_\text{extend} = (n-1) (\log q_\text{max} - \log q_n) / (\log q_n - \log q_1)\]

sasmodels.resolution.interpolate(q, max_step)

  Returns q_calc with points spaced at most max_step apart.

sasmodels.resolution.linear_extrapolation(q, q_min, q_max)

  Extrapolate q out to [q_min, q_max] using the step size in q as a guide. Extrapolation below uses about the same size as the first interval. Extrapolation above uses about the same size as the final interval.

  Note that extrapolated values may be negative.

sasmodels.resolution.pinhole_extend_q(q, q_width, nsigma=(2.5, 3.0))

  Given q and q_width, find a set of sampling points q_calc so that each point \(I(q)\) has sufficient support from the underlying function.

sasmodels.resolution.pinhole_resolution(q_calc, q, q_width, nsigma=(2.5, 3.0))

  Compute the convolution matrix W for pinhole resolution 1-D data.

  Each row W[i] determines the normalized weight that the corresponding points q_calc contribute to the resolution smeared point q[i]. Given W, the resolution smearing can be computed using dot(W,q).

  Note that resolution is limited to \(\pm 2.5 \sigma\).[1] The true resolution function is a broadened triangle, and does not extend over the entire range \((-\infty, +\infty)\). It is important to impose this limitation since some models fall so steeply that the weighted value in gaussian tails would otherwise dominate the integral.

  q_calc must be increasing. q_width must be greater than zero.

  [1] Barker, J. G., and J. S. Pedersen. 1995. Instrumental Smearing Effects in Radially Symmetric Small-Angle Neutron Scattering by Numerical and Analytical Methods. Journal of Applied Crystallography 28 (2): 105–14. https://doi.org/10.1107/S0021889894010095.

sasmodels.resolution.slit_extend_q(q, width, length)

  Given q, width and length, find a set of sampling points q_calc so that each point I(q) has sufficient support from the underlying function.

sasmodels.resolution.slit_resolution(q_calc, q, width, length, n_length=30)

  Build a weight matrix to compute I_s(q) from I(q_calc), given \(q_\perp\) = width (in the high-resolution axis) and \(q_\parallel\) = length (in the low resolution axis). n_length is the number of steps to use in the integration over \(q_\parallel\) when both \(q_\perp\) and \(q_\parallel\) are non-zero.

  Each \(q\) can have an independent width and length value even though current instruments use the same slit setting for all measured points.

  If slit length is large relative to width, use:

  \[I_s(q_i) = \frac{1}{\Delta q_\perp} \int_0^{\Delta q_\perp} I\left(\sqrt{q_i^2 + q_\perp^2}\right) \,dq_\perp\]

  If slit width is large relative to length, use:

  \[I_s(q_i) = \frac{1}{2 \Delta q_\parallel} \int_{-\Delta q_\parallel}^{\Delta q_\parallel} I\left(|q_i + q_\parallel|\right) \,dq_\parallel\]

  For a mixture of slit width and length use:

  \[I_s(q_i) = \frac{1}{2 \Delta q_\parallel \Delta q_\perp} \int_{-\Delta q_\parallel}^{\Delta q_\parallel} \int_0^{\Delta q_\perp} I\left(\sqrt{(q_i + q_\parallel)^2 + q_\perp^2}\right) \,dq_\perp dq_\parallel\]

  Definition

  We are using the mid-point integration rule to assign weights to each element of a weight matrix \(W\) so that

  \[I_s(q) = W\,I(q_\text{calc})\]

  If q_calc is at the mid-point, we can infer the bin edges from the pairwise averages of q_calc, adding the missing edges before q_calc[0] and after q_calc[-1].

  For \(q_\parallel = 0\), the smeared value can be computed numerically using the \(u\) substitution

  \[u_j = \sqrt{q_j^2 - q^2}\]

  This gives

  \[I_s(q) \approx \sum_j I(u_j) \Delta u_j\]

  where \(I(u_j)\) is the value at the mid-point, and \(\Delta u_j\) is the difference between consecutive edges which have been first converted to \(u\). Only \(u_j \in [0, \Delta q_\perp]\) are used, which corresponds to \(q_j \in \left[q, \sqrt{q^2 + \Delta q_\perp}\right]\), so

  \[W_{ij} = \frac{1}{\Delta q_\perp} \Delta u_j = \frac{1}{\Delta q_\perp} \left( \sqrt{q_{j+1}^2 - q_i^2} - \sqrt{q_j^2 - q_i^2} \right) \ \text{if}\ q_j \in \left[q_i, \sqrt{q_i^2 + q_\perp^2}\right]\]

  where \(I_s(q_i)\) is the theory function being computed and \(q_j\) are the mid-points between the calculated values in q_calc. We tweak the edges of the initial and final intervals so that they lie on integration limits.

  (To be precise, the transformed midpoint \(u(q_j)\) is not necessarily the midpoint of the edges \(u((q_{j-1}+q_j)/2)\) and \(u((q_j + q_{j+1})/2)\), but it is at least in the interval, so the approximation is going to be a little better than the left or right Riemann sum, and should be good enough for our purposes.)

  For \(q_\perp = 0\), the \(u\) substitution is simpler:

  \[u_j = \left|q_j - q\right|\]

  so

  \[W_{ij} = \frac{1}{2 \Delta q_\parallel} \Delta u_j = \frac{1}{2 \Delta q_\parallel} (q_{j+1} - q_j) \ \text{if}\ q_j \in \left[q-\Delta q_\parallel, q+\Delta q_\parallel\right]\]

  However, we need to support cases were \(u_j < 0\), which means using \(2 (q_{j+1} - q_j)\) when \(q_j \in \left[0, q_\parallel-q_i\right]\). This is not an issue for \(q_i > q_\parallel\).

  For both \(q_\perp > 0\) and \(q_\parallel > 0\) we perform a 2 dimensional integration with

  \[u_{jk} = \sqrt{q_j^2 - (q + (k\Delta q_\parallel/L))^2} \ \text{for}\ k = -L \ldots L\]

  for \(L\) = n_length. This gives

  \[W_{ij} = \frac{1}{2 \Delta q_\perp q_\parallel} \sum_{k=-L}^L \Delta u_{jk} \left(\frac{\Delta q_\parallel}{2 L + 1}\right)\]