Skip to content

Commit d7f2cfb

Browse files
committed
Finish documentation
1 parent eb95c00 commit d7f2cfb

File tree

1 file changed

+124
-1
lines changed

1 file changed

+124
-1
lines changed

docs/source/morphpy.rst

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,4 +402,127 @@ how much the
402402
MorphFuncxy:
403403
^^^^^^^^^^^^
404404
The ``MorphFuncxy`` morph allows users to apply a custom Python function
405-
to a dataset, ***.
405+
to a dataset that modifies both the ``x`` and ``y`` column values.
406+
This is equivalent to applying a ``MorphFuncx`` and ``MorphFuncy``
407+
simultaneously.
408+
409+
This morph is useful when you want to apply operations that modify both
410+
the grid and function value. A PDF-specific example includes computing
411+
PDFs from 1D diffraction data (see paragraph at the end of this section).
412+
413+
For this tutorial, we will go through two examples. One simple one
414+
involving shifting a function in the ``x`` and ``y`` directions, and
415+
another involving a Fourier transform.
416+
417+
1. Let's start by taking a simple ``sine`` function:
418+
.. code-block:: python
419+
420+
import numpy as np
421+
morph_x = np.linspace(0, 10, 101)
422+
morph_y = np.sin(morph_x)
423+
morph_table = np.array([morph_x, morph_y]).T
424+
425+
2. Then, let our target function be that same ``sine`` function shifted
426+
to the right by ``0.3`` and up by ``0.7``:
427+
.. code-block:: python
428+
429+
target_x = morph_x + 0.3
430+
target_y = morph_y + 0.7
431+
target_table = np.array([target_x, target_y]).T
432+
433+
3. While we could use the ``hshift`` and ``vshift`` morphs,
434+
this would require us to refine over two separate morph
435+
operations. We can instead perform these morphs simultaneously
436+
by defining a function:
437+
.. code-block:: python
438+
439+
def shift(x, y, hshift, vshift):
440+
return x + hshift, y + vshift
441+
442+
4. Now, let's try finding the optimal shift parameters using the ``MorphFuncxy`` morph.
443+
We can try an initial guess of ``hshift=0.0`` and ``vshift=0.0``:
444+
.. code-block:: python
445+
446+
from diffpy.morph.morphpy import morph_arrays
447+
initial_guesses = {"hshift": 0.0, "vshift": 0.0}
448+
info, table = morph_arrays(morph_table, target_table, funcxy=(shift, initial_guesses))
449+
450+
5. Finally, to see the refined ``hshift`` and ``vshift`` parameters, we extract them from ``info``:
451+
.. code-block:: python
452+
453+
print(f"Refined hshift: {info["funcxy"]["hshift"]}")
454+
print(f"Refined vshift: {info["funcxy"]["vshift"]}")
455+
456+
Now for an example involving a Fourier transform.
457+
458+
1. Let's say you measured a signal of the form :math:`f(x)=\exp\{\cos(\pi x)\}`.
459+
Unfortunately, your measurement was taken against a noisy sinusoidal
460+
background of the form :math:`n(x)=A\sin(Bx)`, where ``A,B`` are unknown.
461+
For our example, let's say (unknown to us) that ``A=2`` and ``B=1.7``.
462+
.. code-block:: python
463+
464+
import numpy as np
465+
n = 201
466+
dx = 0.01
467+
measured_x = np.linspace(0, 2, n)
468+
469+
def signal(x):
470+
return np.exp(np.cos(np.pi * x))
471+
472+
def noise(x, A, B):
473+
return A * np.sin(B * x)
474+
475+
measured_f = signal(measured_x) + noise(measured_x, 2, 1.7)
476+
morph_table = np.array([measured_x, measured_f]).T
477+
478+
2. Your colleague remembers they previously computed the Fourier transform
479+
of the function and has sent that to you.
480+
.. code-block:: python
481+
482+
# We only consider the region where the grid is positive for simplicity
483+
target_x = np.fft.fftfreq(n, dx)[:n//2]
484+
target_f = np.real(np.fft.fft(signal(measured_x))[:n//2])
485+
target_table = np.array([target_x, target_f]).T
486+
487+
3. We can now write a noise subtraction function that takes in our measured
488+
signal and guesses for parameters ``A,B``, and computes the Fourier
489+
transform post-noise-subtraction.
490+
.. code-block:: python
491+
492+
def noise_subtracted_ft(x, y, A, B):
493+
n = 201
494+
dx = 0.01
495+
background_subtracted_y = y - noise(x, A, B)
496+
497+
ft_x = np.fft.fftfreq(n, dx)[:n//2]
498+
ft_f = np.real(np.fft.fft(background_subtracted_y)[:n//2])
499+
500+
return ft_x, ft_f
501+
502+
4. Finally, we can provide initial guesses of ``A=0`` and ``B=1`` to the
503+
``MorphFuncxy`` morph and see what refined values we get.
504+
.. code-block:: python
505+
506+
from diffpy.morph.morphpy import morph_arrays
507+
initial_guesses = {"A": 0, "B": 1}
508+
info, table = morph_arrays(morph_table, target_table, funcxy=(background_subtracted_ft, initial_guesses))
509+
510+
5. Print these values to see if they match with the true values of
511+
of ``A=2.0`` and ``B=1.7``!
512+
.. code-block:: python
513+
514+
print(f"Refined A: {info["funcxy"]["A"]}")
515+
print(f"Refined B: {info["funcxy"]["B"]}")
516+
517+
You can also use this morph to help find optimal parameters
518+
(e.g. ``rpoly``, ``qmin``, ``qmax``, ``bgscale``) for computing
519+
PDFs of materials with known structures.
520+
One does this by setting the ``MorphFuncxy`` function to a PDF
521+
computing function such as
522+
```PDFgetx3`` <https://www.diffpy.org/products/pdfgetx.html>`_.
523+
The input (morphed) 1D function should be the 1D diffraction data
524+
one wishes to compute the PDF of and the target 1D function
525+
can be the PDF of a target material with similar geometry.
526+
More information about this will be released in the ``diffpy.morph``
527+
manuscript, and we plan to integrate this feature automatically into
528+
``PDFgetx3`` soon.

0 commit comments

Comments
 (0)