Successive Refinement of Pole Figure Reconstructions edit page

ODF Estimation reconstructs an orientation distribution function (ODF) on one orientation grid with one kernel width. A kernel is the smooth component placed at each grid orientation. Its width sets the smallest scale that the reconstruction can represent.

This page refines two different parts of that workflow. First, calcODFIterative keeps the measurements fixed while it successively narrows the kernel. Second, a simulation keeps the reconstruction method fixed while it adds measurements where the current ODF predicts high pole density.

Successive kernel refinement is not the same as asking calcODF for more solver iterations. It changes the representation scale and uses each coarser solution to initialise the next one. Neither kind of refinement removes the non-uniqueness of pole figure inversion; see The Ghost Effect.

Adapting the kernel

The seven measured Dubna pole figures provide a reference case. The ordinary reconstruction solves directly at its target resolution.

plottingConvention.default('y↑→x');
mtexdata dubna silent
odf_naive = calcODF(pf,'silent');

calcError(pf,odf_naive,'silent')
ans =
    0.3957    0.3248    0.2381    0.2886    0.2900    0.3619    0.3222

The seven values are the fit errors for the seven measured pole figures. Their recalculated pole figures are the visual baseline for the iterative result below.

plotPDF(odf_naive,pf.allH,'silent')

The iterative reconstruction starts from a uniform ODF. It solves first with a wide kernel, transfers those weights to a finer grid, narrows the kernel, and solves again. The coarse stages suppress fine-scale variation and provide informed starting weights for the finer stages.

This scale progression acts as a regularisation strategy for irregularly sampled data. It does not replace ghost correction. The 'nothinning' flag retains low-weight grid nodes so that this comparison isolates the effect of the changing scale.

odf_iter = calcODFIterative(pf,'nothinning');

calcError(pf,odf_iter,'silent')
ans =
    0.2896    0.1808    0.2221    0.1732    0.1570    0.2622    0.2722

One fit error is reported per pole figure. All seven are smaller than those from the ordinary reconstruction, in places by nearly a factor of two.

plotPDF(odf_iter,pf.allH,'silent')

The peak positions in the two galleries are similar. The error values show that their intensities are not, so a visual match alone is not enough to compare reconstructions. Their L1 difference measures how much ODF volume is distributed differently.

calcError(odf_iter,odf_naive,'l1')
ans =
    0.1612

Sixteen percent of the volume sits in different places in the two reconstructions. Recalculating pole figures from their signed difference shows where that volume moved. The printed values give the minimum, mean, and maximum difference over all seven pole figures.

pf_difference = calcPoleFigure(pf,odf_naive-odf_iter);
plot(pf_difference)
differenceIntensity = pf_difference.intensities;
fprintf('pole figure difference min / mean / max : %.2f / %.2f / %.2f\n', ...
  min(differenceIntensity(:)),mean(differenceIntensity(:)), ...
  max(differenceIntensity(:)))
pole figure difference min / mean / max : -1.09 / 0.00 / 0.63

The range from -1.09 to 0.63 is centred at 0.00. Broad, smooth differences extend across the sphere instead of concentrating at the texture maxima. Compare the centre of each pole figure with its rim.

This is the appearance of a differently distributed uniform portion. It is the part of an ODF that pole figures constrain least, which is why a smaller fit error does not prove that the reconstructed ODF is closer to the unknown true ODF.

Adapting the measurement

The rest of the page uses a simulation so that the true ODF is known. Simulating Pole Figure Data develops this validation strategy. Here the model has two sharp components, and pole figures can be evaluated at whichever specimen directions are selected.

cs = crystalSymmetry('cubic');
plottingConvention.default('y↑→x');
ss = specimenSymmetry;

q = rotation.byEuler(10*degree,10*degree,10*degree,'ABG');
q2 = rotation.byEuler(10*degree,30*degree,10*degree,'ABG');

odf_true = .6*unimodalODF(q,cs,ss,'halfwidth',5*degree) + ...
            .4*unimodalODF(q2,cs,ss,'halfwidth',4*degree);

Three lattice planes will be measured.

h = [ ...
  Miller(1,1,1,cs), ...
  Miller(1,0,0,cs), ...
  Miller(1,1,0,cs), ...
  ];

plotPDF(odf_true,h,'silent')

The compact maxima reflect the 4 and 5 degree component halfwidths. They are the features that a coarse measurement must first locate and then sample more densely.

The initial measurement grid

The first scan uses a nearly equispaced 15 degree grid out to a specimen tilt of 80 degrees.

r = equispacedS2Grid('resolution',15*degree,'maxTheta',80*degree);

plot(r,'MarkerSize',12,'upper')

The points cover the accessible cap uniformly. The gaps between them are deliberately much wider than the sharp model components.

The refinement loop

Each round measures the current directions and merges them with all earlier measurements. An ordinary reconstruction then predicts the pole density at the directions inserted by refine. Only the quarter with the highest predicted intensity is measured in the next round. Five rounds are used here.

This highest-intensity rule is deliberately naive. It exploits the current estimate but does not account for uncertainty, counting noise, acquisition cost, or the possibility that the current estimate missed a component. It demonstrates adaptive sampling, not a general experimental design prescription.

r = equispacedS2Grid('resolution',15*degree,'maxTheta',80*degree);
r = repcell(r,size(h));
pf_measured = [];
nsteps = 5;

for k = 1:nsteps

  % simulate the new measurements
  pf_simulated = calcPoleFigure(odf_true,h,r,'silent');

  % merge new and previous measurements
  pf_measured = union(pf_simulated,pf_measured);
  plot(pf_measured,'silent')
  drawnow

  meanResolution = mean(cellfun(@(r) r.resolution,pf_measured.allR));
  fprintf('- mean sampling resolution : %f\n',meanResolution/degree);

  if k < nsteps
    % reconstruct from all measurements collected so far
    odf_recalc = calcODF(pf_measured,'zeroRange','silent');
    fprintf('  error true -- estimated odf   : %f\n', ...
      calcError(odf_true,odf_recalc,'silent'))

    % select high-intensity directions from the refined grids
    for l = 1:length(h)
      r_old = pf_measured{l}.r;
      [~,r_new] = refine(r_old(:));
      pf_predicted = calcPoleFigure(odf_recalc,h(l),r_new,'silent');
      threshold = quantile(pf_predicted.intensities,0.75);
      r{l} = pf_predicted.r(pf_predicted.intensities > threshold);
    end
  end
end
- mean sampling resolution : 14.545455
  error true -- estimated odf   : 0.948044
- mean sampling resolution : 13.087864
  error true -- estimated odf   : 0.408412
- mean sampling resolution : 9.449497
  error true -- estimated odf   : 0.236763
- mean sampling resolution : 6.737637
  error true -- estimated odf   : 0.306671
- mean sampling resolution : 3.717936

Every round prints the mean resolution of the three accumulated direction sets. This single value summarises an irregular sampling pattern; it does not mean that neighbouring points are that far apart everywhere.

The mean resolution falls from 14.5 to 3.7 degrees. The four interim ODF errors are 0.95, 0.41, 0.24, and 0.31. A real experiment could not compute these errors because its true ODF is unknown.

That rise is the point of the demonstration. The measurement is now dense where the texture is strong and coarse everywhere else. An ordinary fine-grid reconstruction then puts ODF components at orientations that the sparse regions do not constrain.

What was measured

The object summary gives the number of accumulated directions for each pole figure. The sampling pattern is no longer a regular grid.

pf_measured
plot(pf_measured,'silent')
pf_measured = PoleFigure (y↑→x)
  crystal symmetry : m-3m
 
  h = (111), r = 367 × 1 points
  h = (100), r = 367 × 1 points
  h = (110), r = 367 × 1 points

Dense clusters surround the predicted poles of the two components. The original coarse coverage remains between them. This uneven coverage is exactly the case for which successive kernel refinement is useful. Within each cluster, the colours rise towards a predicted pole-density maximum.

Reconstructing from the irregular measurement

First use an ordinary reconstruction at the 2.5 degree resolution that the dense regions can support.

odf_recalc = calcODF(pf_measured,'zeroRange','resolution',2.5*degree, ...
  'silent');
fprintf('  error true -- estimated odf   : %f\n', ...
  calcError(odf_true,odf_recalc))
error true -- estimated odf   : 0.412129

The iterative reconstruction reaches the same target scale through a sequence of wider kernels. Sparse regions inherit the broad distribution established at coarse scale instead of being determined only at the finest scale.

odf_recalc_iterative = calcODFIterative(pf_measured, ...
  'halfwidth',2.5*degree);
fprintf('  error true -- iter. est. odf  : %f\n', ...
  calcError(odf_true,odf_recalc_iterative))
error true -- iter. est. odf  : 0.109858

The errors are 0.11 and 0.41. The iterative error is less than one third of the direct error from the same measurements. The L1 distance below shows how much the two estimated ODFs distribute differently.

calcError(odf_recalc,odf_recalc_iterative,'l1')
ans =
    0.3305

About a third of the volume is placed differently. On an unevenly sampled measurement, the choice between a direct fine-scale solve and successive refinement is therefore part of the model, not an implementation detail.

Further reading

Next

Simulating Pole Figure Data adds counting noise and asks how many pole figures a reconstruction needs. Then return to The Ghost Effect for the information that no refinement of measurement density can recover.

Citing this page. This page is part of the documentation of MTEX, a free and open source MATLAB toolbox for analyzing and modeling crystallographic textures. It was written by The MTEX Developers and is published at https://mtex-toolbox.github.io/PoleFigureRefinement.html. If you use MTEX, or reuse text or figures from this page, in your research, please cite

F. Bachmann, R. Hielscher, H. Schaeben: Texture Analysis with MTEX - Free and Open Source Software Toolbox, Solid State Phenomena 160 (2010), 63-68. 10.4028/www.scientific.net/SSP.160.63

BibTeX
@article{bachmann2010mtex,
  author  = {F. Bachmann and R. Hielscher and H. Schaeben},
  title   = {Texture Analysis with MTEX - Free and Open Source Software Toolbox},
  journal = {Solid State Phenomena},
  volume  = {160},
  pages   = {63-68},
  year    = {2010},
  doi     = {10.4028/www.scientific.net/SSP.160.63},
  url     = {https://doi.org/10.4028/www.scientific.net/SSP.160.63}
}

Other papers describing specific MTEX methods are listed under Publications — please cite the one that best fits your application. The MTEX source code is licensed under the GNU General Public License v2.0; the text and figures of this documentation are licensed under CC BY 4.0, which permits reuse — including by automated systems — provided The MTEX Developers and this page are credited.