Scans are needed to construct simulations of types reflectometry, off-specular scattering, depth probe.
There are three types of scan:
All scan types have intensity:
scan.setIntensity(intensity)
Polarization and analyzer can be assigned to the scan when needed (for specular, off-specular, and depth-probe simulations).
scan.setPolarization(polarization) # for specular, off-specular & depth probe
scan.setAnalyzer(analyzer) # for specular & depth probe
To specify a scan with n equidistant points in the grazing angle $\alpha_\text{i}$, use
scan = ba.AlphaScan(n, alpha_start, alpha_stop)
Usage is demonstrated by the Alternating layers example.
The angle $\alpha_\text{i}$ is the actual glancing angle of the wave in the medium of incidence: the fronting medium for $\alpha_\text{i}>0$, the substrate for $\alpha_\text{i}<0$ (beam from below). This holds for all simulation types. It makes a difference only if that medium is not vacuum: then the vertical wavenumber there is $n,k\sin\alpha_\text{i}$, with its refractive index $n$ and the vacuum wavenumber $k=2\pi/\lambda$.
For other sequences of $\alpha_\text{i}$ values, use the more generic
alpha_list = ba.ListScan("alpha_i (rad)", [0*deg, 0.01*deg, ... 1.2*deg]) # all points
scan = ba.AlphaScan(alpha_list)
Negative grazing angles $\alpha_\text{i}<0$ describe a beam that enters the sample from below, through the substrate. In a specular simulation, the reflectivity is then that of the wave reflected back into the substrate. The footprint depends on $|\alpha_\text{i}|$.
After constructing a scan, set the wavelength and optionally the azimuthal angle and a constant offset $\delta\alpha_\text{i}$ using
scan.setWavelength(lambda)
scan.setAzimuthalAngle(phi)
scan.setAlphaOffset(dalpha)
alpha, lambda, phi may also have distributions.
For the default wavelength distribution, the distribution width is absolute, in wavelength units:
scan.setWavelengthDistribution(lambda_distr)
scan.setGrazingAngleDistribution(alpha_distr)
scan.setAzimuthalAngleDistribution(phi_distr)
For relative wavelength resolution, use a dimensionless distribution centered at zero, whose width is the fraction of $\lambda$ at each scan point:
lambda_distr = ba.DistributionGaussian(0, dlambda_rel, n_samples)
scan.setRelativeWavelengthDistribution(lambda_distr)
The Beam full divergence example combines relative wavelength resolution with a grazing-angle distribution. The Distributions example compares several absolute wavelength distributions.
DistributionLogNormal cannot be used in relative mode, as its mean is
strictly positive. Nor is this needed: since the log-normal shape is scale
invariant, the plain setWavelengthDistribution already yields a width
proportional to the wavelength, with relative width given by the scale
parameter.
Footprint can be assigned to the alpha scan (for specular, off-specular, and depth-probe simulations).
scan.setFootprint(footprint) # for specular & off-specular
To specify a scan with n equidistant points in the neutron/X-ray wavelength $\lambda$, use
scan = ba.LambdaScan(n, lambda_start, lambda_stop)
To specify a scan with any other sequence of $\lambda$ values, use
lambda_list = ba.ListScan("lambda (nm)", [0.6*nm, 0.61*nm, ... 0.7*nm]) # all points
scan = ba.LambdaScan(lambda_list)
After constructing a scan, set the grazing angle and optionally the azimuthal angle using
scan.setGrazingAngle(alpha)
scan.setAzimuthalAngle(phi)
alpha, lambda, phi may also have distributions, then create them and assign to the scan:
scan.setWavelengthDistribution(lambda_distr)
scan.setGrazingAngleDistribution(alpha_distr)
scan.setAzimuthalAngleDistribution(phi_distr)
Footprint can be assigned to the lambda scan (for specular, off-specular, and depth-probe simulations).
scan.setFootprint(footprint) # for specular & off-specular
Usage in off-specular scattering is demonstrated by the Offspec lambda example.
See also the off-specular examples.
To specify a scan with n equidistant steps in the normal momentum transfer $q_z$, use
scan = ba.QzScan(n, qz_start, qz_stop)
The value $q_z$ is twice the vertical wavenumber in the medium of incidence (the fronting
medium). In the other layers, the vertical wavenumber is
$\sqrt{q_z^2/4 - 4\pi(\rho - \rho_0)}$, with $\rho$ the scattering-length density (SLD) of the
layer, and $\rho_0$ that of the fronting medium.
Since a qz scan specifies no wavelength, all materials must be defined by their SLD
(ba.SLDMaterial); materials defined by refractive index are rejected.
For a wave with wavelength $\lambda$ at glancing angle $\alpha$ in a nonabsorbing fronting
medium with refractive index $n_0$, $q_z = 4\pi n_0 \sin\alpha/\lambda$.
For other sequences of $q_z$ values, use the more generic
qz_list = ba.ListScan("q_z (1/nm)", [0.01/nm, 0.02/nm, ... 1.2/nm]) # all points
scan = ba.QzScan(qz_list)
A third alternative consists in passing a NumPy array,
import numpy as np
qz_vector = np.linspace(0.01, 1, n)
scan = ba.QzScan(qz_vector)
After constructing a scan, a constant offset $\delta q_z$ can be set using
scan.setOffset(dqz)
To specify q-resolution one needs at first create corresponding
distribution distr, but then there are three ways
to specify a q-resolution. In absolute modes, the width is passed separately;
in relative mode, the distribution width is dimensionless and specifies a
fraction of $q_z$.
1/nm:scan.setAbsoluteQResolution(distr, dq)
The Pt layer fit example uses absolute q resolution together with a fitted q-axis offset.
distr = ba.DistributionGaussian(0, dq_rel, n_samples)
scan.setRelativeQResolution(distr)
See TOF relative resolution for a complete example.
n:dq_vector = 0.03*np.linspace(0.01, 1, n) # dq-values in "1/nm"
scan.setVectorResolution(distr, dq_vector)
As Qz scan does not contain full information about simulation geometry, it is incompatible with footprint.
alpha, lambda, phi and their distributions cannot be set.
Usage is demonstrated in the time-of-flight reflectometry example.
The current API is demonstrated in page time-of-flight reflectometry with resolution.