Capillary sample self-absorption correction#

CapillarySelfAbsorptionCorrection corrects detector-resolved scattering from a sample inside a straight, concentric cylindrical capillary. It supports a two-dimensional detector, a measured or analytic two-dimensional beam profile, separate absorption by the sample and capillary wall, and a horizontal or tilted capillary.

The correction traces rays from illuminated scattering points to the actual detector pixels. It does not reduce the detector to a one-dimensional $2\theta$ curve, so pixels with the same $2\theta$ may receive different corrections when their azimuth or the capillary orientation differs.

MoDaCor provides two process steps over the same numerical model:

  • CapillarySelfAbsorptionCorrection applies the sample-origin correction when wall scattering is negligible or has already been removed consistently;

  • CapillarySampleContainerCorrection jointly treats matched filled and empty measurements, including the different wall-origin attenuation in each.

The wall attenuation of sample scattering is included in both. The second step is required when the wall’s own scattering cannot be neglected.

What the module calculates#

The initial geometry consists of two concentric radial phases:

  1. a central sample of radius sample_radius and linear attenuation coefficient sample_mu;

  2. an optional capillary wall of thickness wall_thickness and coefficient wall_mu.

The radius and coefficient are deliberately separate inputs. Both phases may therefore use independently measured or calculated values, and the code forms products such as $\mu R$ only internally. All configured lengths and coefficients are converted to metres and reciprocal metres before the calculation.

For detector pixel $d$, the absolute sample-origin attenuation is

$$ A_{s,sc}(d) = \frac{\sum_v w_v \exp[-\mu_s L_{s,v,d} - \mu_w L_{w,v,d}]} {\sum_v w_v}. $$

Here $v$ indexes beam-weighted quadrature points in the illuminated sample volume. $L_s$ and $L_w$ are the total incoming-plus-outgoing path lengths in the sample and wall for that point and detector pixel. The calculation uses analytic ray intersections with each cylindrical boundary and Gauss–Legendre quadrature along only the occupied sample chords. It does not construct a three-dimensional voxel box.

The module also predicts the direct-beam transmission

$$ T_{calc} = \frac{\sum_b w_b \exp[-\mu_s L^{through}{s,b} - \mu_w L^{through}{w,b}]} {\sum_b w_b}, $$

where $b$ runs over the complete beam profile. Rays that miss the capillary have zero material path and are included with transmission one.

T_calc is a diagnostic. It is not substituted for an experimentally measured transmission. It is also not generally equal to the forward-scattering limit of $A_{s,sc}$: direct transmission is beam-area weighted, whereas scattering attenuation is illuminated-volume weighted.

Geometry and coordinate convention#

The default MoDaCor laboratory frame is:

  • incident beam direction: $(0,0,1)$;

  • horizontal capillary axis: $(1,0,0)$;

  • vertical direction: $(0,1,0)$.

The default configuration is therefore:

capillary_axis: [1.0, 0.0, 0.0]
incident_direction: [0.0, 0.0, 1.0]
capillary_centre: [0.0, 0.0, 0.0]
capillary_centre_units: m

capillary_centre is a point on the cylinder centreline. The beam-profile plane passes through that point, which expresses the current assumption that the capillary is centred in the beam.

The module reads coord_x, coord_y, and coord_z from each selected DataBundle. These arrays must be length-valued detector-element centres and must broadcast to a two-dimensional detector grid. They are normally produced by PixelCoordinates3D before this correction runs. Tilted or rotated detector panels work because the calculation uses their three-dimensional pixel positions rather than an assumed flat angular grid.

Tilted capillaries#

Any finite, non-zero capillary_axis vector is normalized internally, so a tilted capillary can be configured directly:

capillary_axis: [1.0, 0.15, 0.05]

The implemented cylinder is straight and effectively infinite. A beam or exit ray that is parallel, or numerically too close to parallel, to the capillary axis is rejected. In that geometry the capillary end faces and finite length determine the path, and the infinite-cylinder model is insufficient.

Even for a horizontal capillary, the correction need not be constant along the detector direction parallel to its axis. At finite detector distance, pixels displaced along that direction receive oblique rays with longer physical paths. Mirror symmetry is expected for a centred symmetric setup; strict one-dimensional invariance is not.

Required upstream data#

Each processing key selected through with_processing_keys requires:

Key

Meaning

signal

Detector signal to correct.

coord_x

Lab-frame detector-pixel x coordinate.

coord_y

Lab-frame detector-pixel y coordinate.

coord_z

Lab-frame detector-pixel z coordinate.

mask

Optional boolean mask; True means inactive.

The coordinate key names and mask key are configurable. The default mask name is mask, with Mask accepted as a compatibility alias.

For a scan-shaped mask, a detector pixel is skipped only when it is masked in every leading scan or frame position. A pixel active in at least one frame receives the static geometric correction so that the resulting two-dimensional factor map can broadcast across the scan.

Masked pixels retain an identity factor. They are not sent through the point-ray integrator. Non-finite detector coordinates are accepted only at masked pixels.

Complete pipeline example#

This example uses values supplied directly in the pipeline. Every scalar geometry or attenuation value can instead be loaded through its corresponding *_source and *_units_source setting.

CAPILLARY_ABSORPTION:
  module: CapillarySelfAbsorptionCorrection
  short_title: Correct capillary sample absorption
  requires_steps: [PIXEL_COORDINATES, MERGE_MASKS, TRANSMISSION_NORMALIZATION]
  configuration:
    with_processing_keys: [sample]

    coord_x_key: coord_x
    coord_y_key: coord_y
    coord_z_key: coord_z
    mask_key: mask

    capillary_axis: [1.0, 0.0, 0.0]
    capillary_centre: [0.0, 0.0, 0.0]
    capillary_centre_units: m

    sample_radius: 0.50
    sample_radius_units: mm
    sample_mu: 8.2
    sample_mu_units: 1/mm

    wall_thickness: 0.010
    wall_thickness_units: mm
    wall_mu: 1.7
    wall_mu_units: 1/mm

    beam_profile:
      type: image
      signal_source: measurement::/entry1/instrument/beam/profile
      pixel_pitch: [0.01, 0.01]  # slow, fast
      pixel_pitch_units: mm
      image_centre: [127.5, 127.5]
      downsample: [4, 4]
      relative_weight_cutoff: 1.0e-6

    input_state: transmission_normalized
    transmission_source: measurement::/entry1/sample/transmission
    transmission_units_source: measurement::/entry1/sample/transmission@units
    transmission_uncertainties_sources:
      transmission_SEM: measurement::/entry1/sample/transmission_sem

    evaluation_mode: adaptive
    relative_tolerance: 1.0e-3
    absolute_tolerance: 1.0e-12
    max_depth: 10
    chord_order: 12
    detector_chunk_size: 256

The requires_steps identifiers are illustrative; they must match the step IDs in the actual pipeline.

Supplying sample and wall parameters#

Direct parameters and source-based parameters have the following mapping:

Direct value

Data source

Units source

sample_radius

sample_radius_source

sample_radius_units_source

wall_thickness

wall_thickness_source

wall_thickness_units_source

sample_mu

sample_mu_source

sample_mu_units_source

wall_mu

wall_mu_source

wall_mu_units_source

When a source and direct value are both configured, the source takes precedence. sample_radius must be positive. Wall thickness and both coefficients must be non-negative. A zero wall thickness reduces the geometry to a homogeneous sample cylinder; a zero wall coefficient is allowed for limit checks.

The wall currently requires a linear attenuation coefficient. The sample accepts either a coefficient or the effective-coefficient path described below. The module does not fit a transmission z scan. Composition/density/energy calculations may be performed upstream, including with MoDaCor’s existing xraylib-based material utilities, and the result supplied as sample_mu or wall_mu.

Deriving an effective sample coefficient from measurement metadata#

When the sample coefficient is not otherwise known, sample_mu may instead be derived from either a sample-phase transmission or a sample-phase absorbed fraction and a transmission path thickness:

$$ \mu_{eff} = \frac{-\ln T_s}{t_s}, \qquad A_s=1-T_s. $$

The accepted alternatives are:

Quantity

Direct value

Data source

Units source

Uncertainty sources

Sample-only transmission $T_s$

sample_phase_transmission

sample_phase_transmission_source

dimensionless

sample_phase_transmission_uncertainties_sources

Sample-only absorbed fraction $A_s=1-T_s$

sample_phase_absorption

sample_phase_absorption_source

dimensionless

sample_phase_absorption_uncertainties_sources

Transmission path thickness $t_s$

sample_phase_thickness

sample_phase_thickness_source

sample_phase_thickness_units_source

sample_phase_thickness_uncertainties_sources

Configure exactly one of sample_mu, sample_phase_transmission, or sample_phase_absorption. The two sample-phase factors are mutually exclusive and each requires sample_phase_thickness. Source references take precedence over direct values of the same quantity. Transmission must be in $(0,1]$, absorption in $[0,1)$, and thickness must be positive. Uncertainty values use the same units as their associated quantity and must be scalar or constant-valued.

For example, values already separated from the empty-container absorption can be read directly from a measurement HDF5 file:

sample_mu: null
sample_phase_absorption_source: sample::/entry1/sample/absorption_by_sample
sample_phase_absorption_uncertainties_sources:
  sample_absorption_SEM: sample::/entry1/sample/absorption_by_sample_sem
sample_phase_thickness_source: sample::/entry1/sample/thickness_averaged/mean
sample_phase_thickness_units_source: sample::/entry1/sample/thickness_averaged/mean@units
sample_phase_thickness_uncertainties_sources:
  sample_thickness_SEM: sample::/entry1/sample/thickness_averaged/sem

Equivalently, use sample_phase_transmission_source for a sample-only transmission factor. Here “sample phase” is important: the value must exclude the capillary wall or background contribution. It is used only to derive $\mu_{eff}$; it does not divide or otherwise normalize the scattering signal, so it is compatible with the composite module’s prohibition on a separate measurement-transmission normalization step.

For a capillary illuminated over a finite beam height, the measured transmission averages different chord lengths. Consequently $-\ln(T_s)/t_s$ is generally an effective coefficient unless $t_s$ is a matching effective transmission path. Do not silently substitute the capillary diameter or centreline chord for a whole-beam effective measurement. The calculated whole-beam transmission output provides a useful consistency check.

Uncertainty support#

The current implementation distinguishes measured input uncertainty, derived effective-$\mu$ uncertainty, and unsupported model-parameter uncertainty.

  • Uncertainties already attached to the input signal BaseData propagate through division by the sample factor. In the composite module, filled- and empty-signal uncertainties propagate through attenuation-aware subtraction and the final division. MoDaCor uses first-order, uncorrelated propagation; named uncertainty sources are retained.

  • Covariance is not represented. In particular, a monitor or normalization uncertainty shared by the filled and empty measurements is still treated as uncorrelated by ordinary BaseData arithmetic; the module does not model its cancellation or correlation. The same limitation applies when the sample-phase absorption estimate and empty-capillary scattering originate from a common background measurement.

  • For the sample-only module with input_state: transmission_normalized, measured-transmission uncertainties supplied through transmission_uncertainties_sources propagate through the residual divisor and corrected signal.

  • Uncertainties configured for the sample-phase transmission or absorption and thickness propagate first to the derived coefficient according to

    $$ \sigma_{\mu,T}=\frac{\sigma_T}{t_sT_s},\qquad \sigma_{\mu,A}=\frac{\sigma_A}{t_s(1-A_s)},\qquad \sigma_{\mu,t}=\frac{\mu_{eff}\sigma_t}{t_s}. $$

    Distinct mapping keys remain distinct uncertainty components. Contributions with the same key are combined in quadrature.

  • The derived-$\mu$ terms propagate to capillary_effective_sample_mu, the affected attenuation-factor maps, calculated filled/sample transmission, and corrected signal. The empty-wall factor and empty-capillary calculated transmission do not depend on sample $\mu$ and therefore receive no such component.

  • The composite module differentiates its complete expression. Thus the same physical sample-$\mu$ uncertainty is correlated between $A_{s,sc}$ and $A_{c,sc}$ rather than being incorrectly combined as two independent factor errors.

Factor sensitivities use nominal and perturbed sample coefficients while sharing scattering points, geometric path calculations, and one adaptive detector mesh. A central difference is used except close to $\mu=0$, where a forward difference avoids a negative coefficient. The dimensionless control sample_mu_sensitivity_relative_step defaults to $10^{-2}$; it scales against the larger of $\mu$ and $1/R$. Check stability by changing this value when establishing a high-accuracy pipeline.

Direct sample_mu and wall_mu uncertainties are intentionally not exposed yet because reliable coefficient-uncertainty metadata appears uncommon. Uncertainties in sample radius, wall thickness, capillary orientation, detector geometry, and beam-profile parameters are also not propagated. Those would be geometry/model-parameter uncertainties rather than the implemented measurement-derived effective-$\mu$ path.

Beam profiles#

Beam coordinates use (slow, fast) ordering in the plane normal to the incident direction. All profile weights are non-negative and normalized internally. Profile intensity normalization does not replace the pipeline’s separate incident-flux normalization.

Measured image#

beam_profile:
  type: image
  signal_source: measurement::/entry1/instrument/beam/profile
  pixel_pitch: [0.01, 0.01]
  pixel_pitch_units: mm
  image_centre: [127.5, 127.5]
  rotation: 0.0
  rotation_units: degree
  downsample: [4, 4]
  relative_weight_cutoff: 1.0e-6

Instead of signal_source, a small profile may be supplied inline as signal. image_centre is the fractional (slow, fast) image index aligned with capillary_centre; it defaults to the geometric array centre.

Downsampling groups image blocks without losing their integrated intensity. Each retained block is represented at its intensity-weighted centroid, which preserves the first spatial moment. The relative cutoff removes pixels below a fraction of the image maximum. The retained fraction of the original incident weight is stored as a diagnostic.

Downsampling is a numerical approximation: preserving total weight and the centroid does not guarantee that attenuation is unchanged when a block spans a capillary boundary or a strongly varying chord. Refine it until the resulting correction is stable.

Two-dimensional Gaussian#

beam_profile:
  type: gaussian_2d
  standard_deviations: [0.08, 0.15]  # slow, fast principal widths
  width_units: mm
  rotation: 12.0
  rotation_units: degree
  quadrature_order: [12, 16]
  truncation_sigma: [4.0, 4.0]

The standard deviations are the two principal widths. Rotation permits a correlated Gaussian in the laboratory slow/fast basis. The finite truncation keeps the calculation bounded; the analytically retained Gaussian fraction is recorded.

Two-dimensional trapezoid#

beam_profile:
  type: trapezoid_2d
  plateau_width: [0.10, 0.25]
  ramp_widths:
    - [0.03, 0.04]  # slow negative, slow positive
    - [0.05, 0.06]  # fast negative, fast positive
  width_units: mm
  rotation: 0.0
  rotation_units: degree
  quadrature_order_per_region: [6, 6]

This is a separable product of slow- and fast-axis trapezoids. The four ramp widths are independent, allowing an asymmetric beam. Quadrature nodes are placed separately in each plateau and linear-ramp region, which is more efficient and accurate than sampling the entire bounding rectangle uniformly.

Input-state algebra#

The module always calculates the absolute factor $A_{s,sc}$. What it divides the supplied signal by depends on input_state:

input_state

Meaning of supplied signal

Applied divisor $C(d)$

raw

Uncorrected detector intensity.

$A_{s,sc}(d)$

flux_normalized

Incident-flux normalized, but not transmission normalized.

$A_{s,sc}(d)$

transmission_normalized

Already divided by measured transmission $T_{meas}$.

$A_{s,sc}(d)/T_{meas}$

The operation is

$$ I_{corrected}(d) = \frac{I_{input}(d)}{C(d)}. $$

For transmission-normalized input this is equivalent to

$$ I_{corrected}(d) = \frac{T_{meas} I_{input}(d)}{A_{s,sc}(d)}. $$

transmission_normalized is the default and requires transmission_source. The transmission must be an effective measurement over the same complete beam used for the data; it must not be a centreline Beer–Lambert value. The module does not guess the input state from numeric values.

Measured-transmission uncertainties configured through transmission_uncertainties_sources are propagated through the residual divisor and into the corrected signal. Sample-$\mu$ uncertainty derived from uncertain phase transmission/absorption and thickness is also propagated as described below. Uncertainties on a directly specified $\mu$, radius, wall thickness, detector geometry, and beam-profile calibration are not yet propagated by this process step.

Exact and adaptive detector evaluation#

evaluation_mode: exact traces every active detector pixel from every scattering quadrature point. It is the reference mode and should be used for validation, small regions of interest, and convergence studies.

evaluation_mode: adaptive recursively divides the regular detector-index grid. For each fully active cell it calculates the corners, edge midpoints, centre, and four interior quarter points. Bilinear interpolation is accepted when every non-corner checkpoint meets the configured mixed absolute/relative tolerance. A failed cell subdivides. A cell that reaches max_depth is calculated exactly.

Cells crossing a mask boundary subdivide independently. Fully masked cells are discarded, and unresolved boundary cells evaluate only their active pixels. The output diagnostic distinguishes exactly evaluated pixels from interpolated ones.

The adaptive tolerance is an a posteriori checkpoint criterion, not a formal error bound everywhere between checkpoints. Compare adaptive output with exact pixels or a tighter tolerance for new geometry, very high attenuation, close detectors, or structured beam profiles.

Current performance indication#

Development measurements on an arm64 machine used a 512 by 512 detector, 3,072 illuminated-volume points, and $\mu R=3$:

Geometry

Exact nodes

Fraction of detector

Adaptive time

Maximum error at 1,024 withheld pixels

Horizontal cylinder

4,225

1.61%

1.77 s

$4.5\times10^{-4}$

Axis proportional to $(1,0.3,0.1)$

3,903

1.49%

1.60 s

$8.7\times10^{-4}$

The requested relative tolerance was $10^{-3}$. The same case was estimated at about 110 s when every detector pixel was evaluated exactly. These are development measurements, not guarantees for arbitrary instruments. Peak temporary allocation and runtime scale strongly with detector_chunk_size and the number of illuminated-volume points.

Output data#

The default output keys are:

Key

Contents

capillary_sample_attenuation

Absolute sample-origin attenuation $A_{s,sc}(d)$.

capillary_self_absorption

Residual divisor $C(d)$ actually applied to signal.

capillary_calculated_transmission

Scalar model prediction $T_{calc}$.

capillary_effective_sample_mu

Resolved or measurement-derived effective sample coefficient, in $mathrm{m}^{-1}$.

capillary_attenuation_evaluated

Boolean map: True where an exact detector ray was calculated.

capillary_beam_profile_retained_fraction

Scalar incident-weight fraction retained after profile truncation or thresholding.

Each name can be changed with the corresponding *_key configuration. Masked pixels have attenuation and divisor one, and are false in the evaluated map.

The distinction between the first two outputs matters for transmission-normalized data: the absolute factor remains $A_{s,sc}$ while the applied divisor is $A_{s,sc}/T_{meas}$.

Container scattering is a separate problem#

Absorption of sample scattering by the wall is already included in $A_{s,sc}$. Scattering generated by the wall itself has a different spatial origin and a different attenuation factor. Ordinary empty-capillary subtraction followed by this sample correction is therefore not generally exact.

For raw or flux-normalized measurements, the intended composite relation is

$$ I_s = \frac{I_{sc}^{raw} - (A_{c,sc}/A_{c,c})I_c^{raw}}{A_{s,sc}}, $$

where $A_{c,sc}$ is the wall-origin factor in the filled capillary and $A_{c,c}$ the wall-origin factor in the empty capillary. If both measurements were divided by their own measured transmissions, the equivalent algebra would be

$$ I_s = \frac{T_{sc}N_{sc} - (A_{c,sc}/A_{c,c})T_cN_c}{A_{s,sc}}. $$

CapillarySampleContainerCorrection intentionally implements only the first equation. It does not attempt to undo an earlier transmission normalization, because the result can depend on where that normalization occurred relative to other pipeline operations. Do not emulate this module by applying two independent sample corrections and then subtracting: those operations are not algebraically interchangeable.

Composite pipeline configuration#

The filled and empty signals must have the same shape, compatible units, matching detector geometry, and comparable exposure and incident-flux normalization. If they were measured on different grids, regrid them before this step.

Important

If you use CapillarySampleContainerCorrection, do not include a separate normalization by the measured filled- or empty-capillary transmission in the same correction pipeline. Exposure-time, incident-flux, detector-response, and other common-scale normalizations are allowed, provided the filled and empty signals remain directly comparable.

CAPILLARY_SAMPLE_AND_CONTAINER:
  module: CapillarySampleContainerCorrection
  short_title: Correct sample and capillary wall attenuation
  requires_steps: [PIXEL_COORDINATES, MERGE_MASKS, NORMALIZE_EXPOSURE_AND_FLUX]
  configuration:
    filled_processing_key: sample
    empty_processing_key: background
    mask_key: mask
    empty_mask_key: mask

    capillary_axis: [1.0, 0.0, 0.0]
    sample_radius: 0.50
    sample_radius_units: mm
    sample_mu: 8.2
    sample_mu_units: 1/mm
    wall_thickness: 0.010
    wall_thickness_units: mm
    wall_mu: 1.7
    wall_mu_units: 1/mm

    # Usually zero for an empty capillary; it may represent a gas or other
    # known material in the central region of the background measurement.
    empty_centre_mu: 0.0
    empty_centre_mu_units: 1/mm

    beam_profile:
      type: gaussian_2d
      standard_deviations: [0.08, 0.15]
      width_units: mm
      quadrature_order: [12, 16]
      truncation_sigma: [4.0, 4.0]

    evaluation_mode: adaptive
    relative_tolerance: 1.0e-3
    chord_order: 12
    # Wall-origin quadrature may need more nodes at high sample absorption.
    wall_chord_order: 32

The corrected sample signal replaces signal in the filled DataBundle. The empty bundle remains unchanged. The filled and empty masks are combined with a logical OR and stored on the filled output. At scan dimensions, a detector pixel is ray-traced only if there is at least one paired frame in which both measurements are active. Values at combined-mask positions are preserved from the original filled signal and ignored downstream through the union mask.

The composite step requires a positive wall thickness because wall-origin quadrature is part of the calculation. empty_centre_mu defaults to zero but may be configured as a value or with empty_centre_mu_source and empty_centre_mu_units_source. wall_chord_order controls wall-origin integration independently and defaults to chord_order when omitted.

The filled- and empty-capillary wall factors share the same geometric path calculation and adaptive detector mesh. Only their attenuation coefficients differ. This both reduces runtime and avoids introducing two independently chosen interpolation meshes into their ratio.

Composite outputs#

The filled output bundle receives:

Key

Contents

capillary_sample_attenuation

$A_{s,sc}$, sample-origin attenuation in the filled capillary.

capillary_wall_attenuation_filled

$A_{c,sc}$, wall-origin attenuation with the sample present.

capillary_wall_attenuation_empty

$A_{c,c}$, wall-origin attenuation in the empty capillary.

capillary_wall_subtraction_scale

Pixelwise ratio $A_{c,sc}/A_{c,c}$.

capillary_filled_calculated_transmission

Calculated effective transmission of the filled capillary.

capillary_empty_calculated_transmission

Calculated effective transmission of the empty capillary.

capillary_effective_sample_mu

Resolved or measurement-derived effective sample coefficient.

capillary_beam_profile_retained_fraction

Incident weight retained by profile truncation or thresholding.

capillary_sample_attenuation_evaluated

Exact-evaluation mask for $A_{s,sc}$.

capillary_wall_filled_attenuation_evaluated

Exact-evaluation mask for $A_{c,sc}$.

capillary_wall_empty_attenuation_evaluated

Exact-evaluation mask for $A_{c,c}$.

Filled- and empty-signal uncertainties propagate through the subtraction and sample division. When $\mu_{eff}$ is derived from uncertain sample-phase absorption/transmission and thickness, those uncertainty components also propagate through the correlated sample- and wall-factor dependence described above. Other model-parameter uncertainties are not yet included. The two calculated transmissions are diagnostics only and are not used to rescale the signals. Sample-origin refinement is independent, while the two wall maps use one shared refinement mesh whose acceptance test covers both factors. Validate the composite output and wall-scale ratio against exact mode when establishing settings for a new instrument.

Accuracy controls and convergence#

For a new setup, establish stability in this order:

  1. Run a small detector region or selected frames with evaluation_mode: exact.

  2. Increase chord_order and wall_chord_order independently until the exact correction changes acceptably little.

  3. For analytic profiles, increase their quadrature order; for image profiles, reduce downsampling and cutoff.

  4. Compare the intended adaptive settings against exact values, including high-angle detector regions and mask boundaries.

  5. Compare capillary_calculated_transmission with the measured effective transmission. A discrepancy is a diagnostic for geometry, beam-profile, coefficient, or normalization mismatch; it is not automatically a scale factor to apply.

The factor must remain positive and finite. The module rejects active-pixel values below minimum_attenuation_factor rather than dividing by an unstable number.

Wall-origin quadrature can converge more slowly than sample-origin quadrature when sample absorption is strong, because an outgoing wall ray can become tangent to the inner cylinder within an integration segment. In one deliberately difficult development case with radius ratio 1.1 and sample $\mu R=3$, the maximum relative change against 96 wall nodes was about $6.0\times10^{-3}$ at 12 nodes, $2.9\times10^{-3}$ at 20, $1.2\times10^{-3}$ at 32, and $8.8\times10^{-4}$ at 48. At sample $\mu R=0.5$, 12 nodes were already within about $1.6\times10^{-4}$. These figures are not universal tolerances; they show why the two chord orders are separate controls.

Model limitations#

The implemented model assumes:

  • a straight, circular, concentric and effectively infinite cylinder;

  • a capillary centred in the incident beam;

  • parallel incident rays;

  • single scattering and Beer–Lambert attenuation;

  • one central sample phase and an optional concentric wall;

  • a static detector geometry represented by pixel-centre coordinates.

It does not currently model finite capillary ends, eccentric or non-circular walls, wall texture or crystallographic diffraction, refraction, multiple scattering, or uncertainty in geometry/profile parameters. Detector pixels are treated as point centres; detector-pixel area quadrature is not currently performed.

Further reading#

The implementation and validation plan is documented in Capillary self-absorption and container correction. The principal literature references are:

The one-dimensional homogeneous-cylinder implementation in diffpy.labpdfproc is used as an independent regression reference, not as a runtime dependency. It does not cover the two-phase, arbitrary-axis, beam-profile, or detector-azimuth requirements described here.