diff --git a/docs/source/funcxy.rst b/docs/source/funcxy.rst new file mode 100644 index 0000000..689ad3d --- /dev/null +++ b/docs/source/funcxy.rst @@ -0,0 +1,181 @@ +.. _funcxy: + +Using funcxy with Commonly-Used Diffraction Software +#################################################### + +The general xy morph ``funcxy`` can be used to tune parameters +of many popular diffraction software functions. + +Below, we give templates for how one can use ``funcxy`` +with `PDFgetx3 `_ +and `PyFai `_. + +Getting a Better PDF with PDFgetx3 +================================== + +In PDFgetx3, the ``PDFGetter`` takes in a 1D diffraction +pattern I(Q) and returns a PDF G(r). + +There are many parameters you can specify, such as + - ``qmin``: Lower Q-cutoff for the Fourier transform giving the PDF + - ``qmax``: Upper Q-cutoff for the Fourier transform giving the PDF + - ``qmaxinst``: Upper Q-boundary for meaningful signal + - ``rpoly``: Approximately the low-r bound of meaningful G(r) values + +Furthermore, you can supply a background file ``backgroundfile`` +and subtract a scaled version of the background file by the +scaling factor ``bgscale``. + +We will showcase an example of how one would refine over the +``PDFGetter`` parameters using ``funcxy`` to obtain a PDF. + +Let's say you have a measured I(Q) with Q in angstroms of a lead +nanoparticle (composition PbS) named ``sample.chi`` taken on a +glass background. We want to match a target calculated PDF G(r) +stored in a file named ``target.cgr``. +Let's also say we have a measured I(Q) of the +glass background ``background.chi``. + +.. code-block:: python + + from diffpy.pdfgetx.pdfgetter import PDFGetter + from diffpy.morph.morphpy import morph_arrays + from diffpy.utils.parsers.loaddata import loadData + + pg = PDFGetter() + + backgroundfile = loadData("background.chi") + composition = "PbS" + + + def wrap(x, y, **kwargs): + xy_out = pg.__call__( + x=x, y=y, dataformat="QA", + composition=composition, + backgroundfile=backgroundfile, + **kwargs + ) + r = xy_out[0] + gr = xy_out[1] + return (r, gr) + + + sample_iq = loadData("sample.chi") + target_gr = loadData("target.cgr") + params_to_morph = { + "bgscale": 1.0, + "qmin": 0.0, "qmax": 25.0, + "qmaxinst": 25.0, "rpoly": 0.9 + } + + morph_info, morphed_gr = morph_arrays( + sample_iq, target_gr, + funcxy=(wrap, params_to_morph) + ) + +You can now plot ``morphed_gr`` against your ``target_gr`` to see +how well your morphing refinement of the PDF-getting parameters +as done! +To see what the refined values of the parameters are, +print out ``morph_info``. +You can freely add and remove entries in +``params_to_morph`` to include or not include them as +parameters to refine over. + +If you expect to see thermal effect differences between your +measured PDF and ``target_gr``, you can also include +the ``stretch``, ``scale``, and ``smear`` morphs in your +call to ``morph_arrays``. + + +Performing Detector Calibration with PyFai +========================================== + +When performing azimuthal integration, it is important to +ensure your beam center and detector distances are calibrated. +However, it is possible that they have shifted +across measurements. Here, we will use morphing to the rescue! + +Let's say we just measured a diffraction pattern stored +as a NumPy object in ``diffraction_image.npy``, but some +of the detector geometries are off. +Our azimuthally integrated ``sample.chi`` looks a bit off. +Before this measurement, you measured an amazing +I(Q) pattern ``target.chi`` with a perfectly calibrated +sample-to-detector distance and beam center. +We will use morphing to try to match the integration of +the 2D pattern to the target 1D function. + +For the integration, we will need some information, such as +the wavelength of the beam, +the size of each pixel in the 2D image +(``pixel1`` is the horizontal length in meters and +``pixel2`` is the vertical length in meters), +and a guess of the beam center. +This information can be found on the +`PyFai documentation `_. +For our example, let's say we have a ``1024``x``1024`` pixel image +where each pixel is a ``100`` micron by ``100`` micron region, and +our wavelength was ``1.11`` angstroms. + +.. code-block:: python + + import numpy as np + import pyFAI.integrator.azimuthal as pyfai + import pyFAI.detectors as pfd + from diffpy.morph.morphpy import morph_arrays + from diffpy.utils.parsers.loaddata import loadData + + pattern_2d = np.load("diffraction_image.npy") + wavelength = 0.1110e-9 # in m + pixel1 = 1e-4 # in m + pixel2 = 1e-4 # in m + cent_x = 511 # in number of pixels + cent_y = 511 # in number of pixels + + ai = pyfai.AzimuthalIntegrator() + ai.wavelength = wavelength + detector = pfd.Detector() + detector.max_shape = pattern_2d.shape + + + def wrap(x, y, sample_to_detector_dist, cent_offset_x, cent_offset_y): + detector.pixel1 = pixel1 + detector.pixel2 = pixel2 + ai.detector = detector + + ai.setFit2D( + directDist=sample_to_detector_dist, + centerX=cent_x+cent_offset_x, + centerY=cent_y+cent_offset_y + ) + + return ai.integrate1D_ng( + pattern_2d, + npt=1000, unit="q_A^-1", + method="mean" + ) + + + params_to_morph = { + "sample_to_detector_dist": 60, # in mm + "cent_offset_x": 0, # in number of pixels + "cent_offset_y": 0 # in number of pixels + } + + sample_chi = loadData("sample.chi") + target_chi = loadData("target.chi") + + morph_info, morphed_chi = morph_arrays( + sample_chi, target_chi, + funcxy=(wrap, params_to_morph) + ) + +You can now plot ``morphed_chi`` against your ``target_chi`` +to see if the refinement has helped in the calibration! +To see the calibrated values, you can print out ``morph_info``. + +If you would like to morph over other PyFai parameters +(e.g. ``rot1``, ``tilt``, ``wavelength``), +you can adjust the wrapper function ``wrap`` to take in +these parameters. diff --git a/docs/source/morphpy.rst b/docs/source/morphpy.rst index 1fd4f1c..54a17ce 100644 --- a/docs/source/morphpy.rst +++ b/docs/source/morphpy.rst @@ -10,6 +10,11 @@ This page is intended for those acquainted with the basic morphs described in the aforementioned quickstart tutorial who want to use ``diffpy.morph`` in their Python scripts. +For those looking to use the Python-specific morph ``MorphFuncxy`` (described below) +with commonly used diffraction software like `PDFgetx3 `_ +and `PyFai `_ are directed to the +`funcxy tutorials `__. + Python Morphing Functions ========================= @@ -408,8 +413,10 @@ This is equivalent to applying a ``MorphFuncx`` and ``MorphFuncy`` simultaneously. This morph is useful when you want to apply operations that modify both -the grid and function value. A PDF-specific example includes computing -PDFs from 1D diffraction data (see paragraph at the end of this section). +the grid and function value. +Examples of using ``MorphFuncxy`` with ``PyFai`` azimuthal integration +and ``PDFgetx3`` PDF calculation are included `here `__. + For this tutorial, we will go through two examples. One simple one involving shifting a function in the ``x`` and ``y`` directions, and diff --git a/docs/source/tutorials.rst b/docs/source/tutorials.rst index 2fccec1..01fac3d 100644 --- a/docs/source/tutorials.rst +++ b/docs/source/tutorials.rst @@ -8,6 +8,14 @@ Tutorials for these are included below. The files required for these tutorials c For a full list of options offered by ``diffpy.morph``, please run ``diffpy.morph --help`` on the command line. +Using MorphFuncxy +================= + +Examples of how to use the general morph ``MorphFuncxy`` with commonly used +diffraction software like `PDFgetx3 `_ +and `PyFai `_ are directed to the +`funcxy tutorials `__. + Performing Multiple Morphs ========================== diff --git a/news/funcxy_tutorial.rst b/news/funcxy_tutorial.rst new file mode 100644 index 0000000..f035aaf --- /dev/null +++ b/news/funcxy_tutorial.rst @@ -0,0 +1,23 @@ +**Added:** + +* Tutorials for wrapping PDFgetx3 and PyFai with funcxy. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +*