|
| 1 | +.. _funcxy: |
| 2 | + |
| 3 | +Using funcxy with Commonly-Used Diffraction Software |
| 4 | +#################################################### |
| 5 | + |
| 6 | +The general xy morph ``funcxy`` can be used to tune parameters |
| 7 | +of many popular diffraction software functions. |
| 8 | + |
| 9 | +Below, we give templates for how one can use ``funcxy`` |
| 10 | +with `PDFgetx3 <https://www.diffpy.org/products/pdfgetx.html>`_ |
| 11 | +and `PyFai <https://pyfai.readthedocs.io/en/stable/>`_. |
| 12 | + |
| 13 | +Getting a Better PDF with PDFgetx3 |
| 14 | +================================== |
| 15 | + |
| 16 | +In PDFgetx3, the ``PDFGetter`` takes in a 1D diffraction |
| 17 | +pattern I(Q) and returns a PDF G(r). |
| 18 | + |
| 19 | +There are many parameters you can specify, such as |
| 20 | + - ``qmin``: Lower Q-cutoff for the Fourier transform giving the PDF |
| 21 | + - ``qmax``: Upper Q-cutoff for the Fourier transform giving the PDF |
| 22 | + - ``qmaxinst``: Upper Q-boundary for meaningful signal |
| 23 | + - ``rpoly``: Approximately the low-r bound of meaningful G(r) values |
| 24 | + |
| 25 | +Furthermore, you can supply a background file ``backgroundfile`` |
| 26 | +and subtract a scaled version of the background file by the |
| 27 | +scaling factor ``bgscale``. |
| 28 | + |
| 29 | +We will showcase an example of how one would refine over the |
| 30 | +``PDFGetter`` parameters using ``funcxy`` to obtain a PDF. |
| 31 | + |
| 32 | +Let's say you have a measured I(Q) with Q in angstroms of a lead |
| 33 | +nanoparticle (composition PbS) named ``sample.chi`` taken on a |
| 34 | +glass background. We want to match a target calculated PDF G(r) |
| 35 | +stored in a file named ``target.cgr``. |
| 36 | +Let's also say we have a measured I(Q) of the |
| 37 | +glass background ``background.chi``. |
| 38 | + |
| 39 | +.. code-block:: python |
| 40 | +
|
| 41 | + from diffpy.pdfgetx.pdfgetter import PDFGetter |
| 42 | + from diffpy.morph.morphpy import morph_arrays |
| 43 | + from diffpy.utils.parsers.loaddata import loadData |
| 44 | +
|
| 45 | + pg = PDFGetter() |
| 46 | +
|
| 47 | + backgroundfile = loadData("background.chi") |
| 48 | + composition = "PbS" |
| 49 | +
|
| 50 | +
|
| 51 | + def wrap(x, y, **kwargs): |
| 52 | + xy_out = pg.__call__( |
| 53 | + x=x, y=y, dataformat="QA", |
| 54 | + composition=composition, |
| 55 | + backgroundfile=backgroundfile, |
| 56 | + **kwargs |
| 57 | + ) |
| 58 | + r = xy_out[0] |
| 59 | + gr = xy_out[1] |
| 60 | + return (r, gr) |
| 61 | +
|
| 62 | +
|
| 63 | + sample_iq = loadData("sample.chi") |
| 64 | + target_gr = loadData("target.cgr") |
| 65 | + params_to_morph = { |
| 66 | + "bgscale": 1.0, |
| 67 | + "qmin": 0.0, "qmax": 25.0, |
| 68 | + "qmaxinst": 25.0, "rpoly": 0.9 |
| 69 | + } |
| 70 | +
|
| 71 | + morph_info, morphed_gr = morph_arrays( |
| 72 | + sample_iq, target_gr, |
| 73 | + funcxy=(wrap, params_to_morph) |
| 74 | + ) |
| 75 | +
|
| 76 | +You can now plot ``morphed_gr`` against your ``target_gr`` to see |
| 77 | +how well your morphing refinement of the PDF-getting parameters |
| 78 | +as done! |
| 79 | +To see what the refined values of the parameters are, |
| 80 | +print out ``morph_info``. |
| 81 | +You can freely add and remove entries in |
| 82 | +``params_to_morph`` to include or not include them as |
| 83 | +parameters to refine over. |
| 84 | + |
| 85 | +If you expect to see thermal effect differences between your |
| 86 | +measured PDF and ``target_gr``, you can also include |
| 87 | +the ``stretch``, ``scale``, and ``smear`` morphs in your |
| 88 | +call to ``morph_arrays``. |
| 89 | + |
| 90 | + |
| 91 | +Performing Detector Calibration with PyFai |
| 92 | +========================================== |
| 93 | + |
| 94 | +When performing azimuthal integration, it is important to |
| 95 | +ensure your beam center and detector distances are calibrated. |
| 96 | +However, it is possible that they have shifted |
| 97 | +across measurements. Here, we will use morphing to the rescue! |
| 98 | + |
| 99 | +Let's say we just measured a diffraction pattern stored |
| 100 | +as a NumPy object in ``diffraction_image.npy``, but some |
| 101 | +of the detector geometries are off. |
| 102 | +Our azimuthally integrated ``sample.chi`` looks a bit off. |
| 103 | +Before this measurement, you measured an amazing |
| 104 | +I(Q) pattern ``target.chi`` with a perfectly calibrated |
| 105 | +sample-to-detector distance and beam center. |
| 106 | +We will use morphing to try to match the integration of |
| 107 | +the 2D pattern to the target 1D function. |
| 108 | + |
| 109 | +For the integration, we will need some information, such as |
| 110 | +the wavelength of the beam, |
| 111 | +the size of each pixel in the 2D image |
| 112 | +(``pixel1`` is the horizontal length in meters and |
| 113 | +``pixel2`` is the vertical length in meters), |
| 114 | +and a guess of the beam center. |
| 115 | +This information can be found on the |
| 116 | +`PyFai documentation <https://pyfai.readthedocs.io/en/stable/usage/cookbook/integration_with_python.html>`_. |
| 117 | +For our example, let's say we have a ``1024``x``1024`` pixel image |
| 118 | +where each pixel is a ``100`` micron by ``100`` micron region, and |
| 119 | +our wavelength was ``1.11`` angstroms. |
| 120 | + |
| 121 | +.. code-block:: python |
| 122 | +
|
| 123 | + import numpy as np |
| 124 | + import pyFAI.integrator.azimuthal as pyfai |
| 125 | + import pyFAI.detectors as pfd |
| 126 | + from diffpy.morph.morphpy import morph_arrays |
| 127 | + from diffpy.utils.parsers.loaddata import loadData |
| 128 | +
|
| 129 | + pattern_2d = np.load("diffraction_image.npy") |
| 130 | + wavelength = 0.1110e-9 # in m |
| 131 | + pixel1 = 1e-4 # in m |
| 132 | + pixel2 = 1e-4 # in m |
| 133 | + cent_x = 511 # in number of pixels |
| 134 | + cent_y = 511 # in number of pixels |
| 135 | +
|
| 136 | + ai = pyfai.AzimuthalIntegrator() |
| 137 | + ai.wavelength = wavelength |
| 138 | + detector = pfd.Detector() |
| 139 | + detector.max_shape = pattern_2d.shape |
| 140 | +
|
| 141 | +
|
| 142 | + def wrap(x, y, sample_to_detector_dist, cent_offset_x, cent_offset_y): |
| 143 | + detector.pixel1 = pixel1 |
| 144 | + detector.pixel2 = pixel2 |
| 145 | + ai.detector = detector |
| 146 | +
|
| 147 | + ai.setFit2D( |
| 148 | + directDist=sample_to_detector_dist, |
| 149 | + centerX=cent_x+cent_offset_x, |
| 150 | + centerY=cent_y+cent_offset_y |
| 151 | + ) |
| 152 | +
|
| 153 | + return ai.integrate1D_ng( |
| 154 | + pattern_2d, |
| 155 | + npt=1000, unit="q_A^-1", |
| 156 | + method="mean" |
| 157 | + ) |
| 158 | +
|
| 159 | +
|
| 160 | + params_to_morph = { |
| 161 | + "sample_to_detector_dist": 60, # in mm |
| 162 | + "cent_offset_x": 0, # in number of pixels |
| 163 | + "cent_offset_y": 0 # in number of pixels |
| 164 | + } |
| 165 | +
|
| 166 | + sample_chi = loadData("sample.chi") |
| 167 | + target_chi = loadData("target.chi") |
| 168 | +
|
| 169 | + morph_info, morphed_chi = morph_arrays( |
| 170 | + sample_chi, target_chi, |
| 171 | + funcxy=(wrap, params_to_morph) |
| 172 | + ) |
| 173 | +
|
| 174 | +You can now plot ``morphed_chi`` against your ``target_chi`` |
| 175 | +to see if the refinement has helped in the calibration! |
| 176 | +To see the calibrated values, you can print out ``morph_info``. |
| 177 | + |
| 178 | +If you would like to morph over other PyFai parameters |
| 179 | +(e.g. ``rot1``, ``tilt``, ``wavelength``), |
| 180 | +you can adjust the wrapper function ``wrap`` to take in |
| 181 | +these parameters. |
0 commit comments