Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,35 @@
.. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue
:target: https://github.com/diffpy/diffpy.morph/issues

Python package for manipulating and comparing PDF profiles
Python package for manipulating and comparing diffraction data

``diffpy.morph`` is a Python software package designed to increase the insight
researchers can obtain from measured atomic pair distribution functions
researchers can obtain from measured diffraction data
and atomic pair distribution functions
(PDFs) in a model-independent way. The program was designed to help a
researcher answer the question: "Has my material undergone a phase
transition between these two measurements?"

One approach is to compare the two PDFs in a plot and view the difference
curve underneath. However, significant signal can be seen in the
difference curve from benign effects such as thermal expansion (peak
shifts) and increased thermal motion (peak broadening) or a change in
One approach is to compare the two diffraction patterns in a plot
and view the difference curve underneath. However, significant signal can
be seen in the difference curve from benign effects such as thermal expansion
(peak shifts) and increased thermal motion (peak broadening) or a change in
scale due to differences in incident flux, for example. ``diffpy.morph`` will
do its best to correct for these benign effects before computing and
plotting the difference curve. One measured PDF (typically that collected
at higher temperature) is identified as the target PDF and the second
PDF is then morphed by "stretching" (changing the r-axis to simulate a
plotting the difference curve. One measured function (typically that collected
at higher temperature) is identified as the target function and the second
function is then morphed by "stretching" (changing the r-axis to simulate a
uniform lattice expansion), "smearing" (broadening peaks through a
uniform convolution to simulate increased thermal motion), and "scaling"
(self-explanatory). ``diffpy.morph`` will vary the amplitude of the morphing
transformations to obtain the best fit between the morphed and the target
PDFs, then plot them on top of each other with the difference plotted
functions, then plot them on top of each other with the difference plotted
below.

There are also a few other morphing transformations in the program.

Finally, we note that ``diffpy.morph`` should work on other spectra that are not
PDFs, though it has not been extensively tested beyond the PDF.
Finally, we note that ``diffpy.morph`` should work on other spectra,
though it has not been extensively tested beyond spectral data and the PDF.


For more information about the diffpy.morph library, please consult our `online documentation <https://diffpy.github.io/diffpy.morph>`_.
Expand Down Expand Up @@ -153,9 +154,9 @@ If installed correctly, this last command should return the version
of ``diffpy.morph`` that you have installed on your system. To begin using
``diffpy.morph``, run a command like ::

diffpy.morph <target PDF file> <morphed PDF file>
diffpy.morph <morph file> <target file>

where both PDFs file are text files which contain PDF data, such as ``.gr``
where both files are text files which contain two-column data, such as ``.gr``
or ``.cgr`` files that are produced by ``PDFgetX2``, ``PDFgetX3``,
or ``PDFgui``. File extensions other than ``.gr`` or ``.cgr``,
but with the same content structure, also work with ``diffpy.morph``.
Expand Down
8 changes: 4 additions & 4 deletions TUTORIAL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ Basic diffpy.morph Workflow
superficial and in most cases can be ignored.

We see that this has had hardly any effect on our PDF. To see
an effect, we restrict the ``rmin`` and ``rmax`` values to
an effect, we restrict the ``xmin`` and ``xmax`` values to
reflect relevant data range by typing ::

diffpy.morph --scale=0.8 --smear=0.5 --rmin=1.5 --rmax=30 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr
diffpy.morph --scale=0.8 --smear=0.5 --xmin=1.5 --xmax=30 darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr

Now, we see that the difference Rw = 0.204 and that the optimized
``smear=-0.084138``.
Expand All @@ -151,7 +151,7 @@ Basic diffpy.morph Workflow
8. Finally, we will examine the stretch factor. Provide an initial
guess by typing ::

diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.5 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr
diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.5 --xmin=1.5 --xmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr

And noting that the difference has increased. Before continuing,
see if you can see which direction (higher or lower) our initial
Expand All @@ -160,7 +160,7 @@ Basic diffpy.morph Workflow

If you cannot, type ::

diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.005 --rmin=1.5 --rmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr
diffpy.morph --scale=0.8 --smear=-0.08 --stretch=0.005 --xmin=1.5 --xmax=30 -a darkSub_rh20_C_01.gr darkSub_rh20_C_44.gr

to observe decreased difference and then remove ``-a`` to see
the optimized ``--stretch=0.001762``. We have now reached
Expand Down
25 changes: 25 additions & 0 deletions news/refactor_pdf.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**Added:**

* <news item>

**Changed:**

* Changed tutorial language away from PDF-specific names
* Names of rmin, rmax, rstep renamed to xmin, xmax, xstep
* Removed PDF-specific labels from plotting functions

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* <news item>

**Security:**

* <news item>
30 changes: 15 additions & 15 deletions src/diffpy/morph/morph_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ def morph(
y_morph,
x_target,
y_target,
rmin=None,
rmax=None,
rstep=None,
xmin=None,
xmax=None,
xstep=None,
pearson=False,
add_pearson=False,
fixed_operations=None,
Expand All @@ -112,12 +112,12 @@ def morph(
y_morph: numpy.array
An array of target y values, i.e., those will be kept constant by
morphing.
rmin: float, optional
A value to specify lower r-limit of morph operations.
rmax: float, optional
A value to specify upper r-limit of morph operations.
rstep: float, optional
A value to specify rstep of morph operations.
xmin: float, optional
A value to specify lower x-limit of morph operations.
xmax: float, optional
A value to specify upper x-limit of morph operations.
xstep: float, optional
A value to specify xstep of morph operations.
pearson: Bool, optional
Option to include Pearson coefficient as a minimizing target
during morphing. Default to False.
Expand Down Expand Up @@ -191,9 +191,9 @@ def morph(
for k, v in rv_cfg.items()
if (v is not None) and k in _morph_step_dict
]
rv_cfg["rmin"] = rmin
rv_cfg["rmax"] = rmax
rv_cfg["rstep"] = rstep
rv_cfg["xmin"] = xmin
rv_cfg["xmax"] = xmax
rv_cfg["xstep"] = xstep
# configure smear, guess baselineslope when it is not provided
if rv_cfg.get("smear") is not None and rv_cfg.get("baselineslope") is None:
rv_cfg["baselineslope"] = -0.5
Expand Down Expand Up @@ -295,9 +295,9 @@ def plot_morph(chain, ax=None, **kwargs):
rdat, grdat = chain.xy_target_out
l_list = ax.plot(rfit, grfit, label="morph", **kwargs)
l_list += ax.plot(rdat, grdat, label="target", **kwargs)
ax.set_xlim([chain.config["rmin"], chain.config["rmax"]])
ax.set_xlim([chain.config["xmin"], chain.config["xmax"]])
ax.legend()
ax.set_xlabel(r"r ($\mathrm{\AA}$)")
ax.set_ylabel(r"G ($\mathrm{\AA}^{-2}$)")
# ax.set_xlabel(r"r ($\mathrm{\AA}$)")
# ax.set_ylabel(r"G ($\mathrm{\AA}^{-2}$)")

return l_list
34 changes: 18 additions & 16 deletions src/diffpy/morph/morphapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,16 @@ def custom_error(self, msg):
help="Print additional header details to saved files.",
)
parser.add_option(
"--rmin",
"--xmin",
type="float",
help="Minimum r-value (abscissa) to use for function comparisons.",
metavar="XMIN",
help="Minimum x-value (abscissa) to use for function comparisons.",
)
parser.add_option(
"--rmax",
"--xmax",
type="float",
help="Maximum r-value (abscissa) to use for function comparisons.",
metavar="XMAX",
help="Maximum x-value (abscissa) to use for function comparisons.",
)
parser.add_option(
"--tolerance",
Expand Down Expand Up @@ -343,12 +345,12 @@ def custom_error(self, msg):
group.add_option(
"--pmin",
type="float",
help="Minimum r-value to plot. Defaults to RMIN.",
help="Minimum x-value to plot. Defaults to XMIN.",
)
group.add_option(
"--pmax",
type="float",
help="Maximum r-value to plot. Defaults to RMAX.",
help="Maximum x-value to plot. Defaults to XMAX.",
)
group.add_option(
"--maglim",
Expand Down Expand Up @@ -505,13 +507,13 @@ def single_morph(
smear_in = "None"
hshift_in = "None"
vshift_in = "None"
config = {"rmin": opts.rmin, "rmax": opts.rmax, "rstep": None}
config = {"xmin": opts.xmin, "xmax": opts.xmax, "xstep": None}
if (
opts.rmin is not None
and opts.rmax is not None
and opts.rmax <= opts.rmin
opts.xmin is not None
and opts.xmax is not None
and opts.xmax <= opts.xmin
):
e = "rmin must be less than rmax"
e = "xmin must be less than xmax"
parser.custom_error(e)

# Set up the morphs
Expand Down Expand Up @@ -765,7 +767,7 @@ def single_morph(
xy_save = [chain.x_morph_out, chain.y_morph_out]
if opts.get_diff is not None:
diff_chain = morphs.MorphChain(
{"rmin": None, "rmax": None, "rstep": None}
{"xmin": None, "xmax": None, "xstep": None}
)
diff_chain.append(morphs.MorphRGrid())
diff_chain(
Expand Down Expand Up @@ -804,16 +806,16 @@ def single_morph(
labels[0] = opts.tlabel

# Plot extent defaults to calculation extent
pmin = opts.pmin if opts.pmin is not None else opts.rmin
pmax = opts.pmax if opts.pmax is not None else opts.rmax
pmin = opts.pmin if opts.pmin is not None else opts.xmin
pmax = opts.pmax if opts.pmax is not None else opts.xmax
maglim = opts.maglim
mag = opts.mag
l_width = opts.lwidth
plot.compare_funcs(
pairlist,
labels,
rmin=pmin,
rmax=pmax,
xmin=pmin,
xmax=pmax,
maglim=maglim,
mag=mag,
rw=rw,
Expand Down
66 changes: 33 additions & 33 deletions src/diffpy/morph/morphs/morphrgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ class MorphRGrid(Morph):

Configuration Variables
-----------------------
rmin
The lower-bound on the r-range.
rmax
The upper-bound on the r-range (exclusive within tolerance of 1e-8).
rstep
The r-spacing.
xmin
The lower-bound on the x-range.
xmax
The upper-bound on the x-range (exclusive within tolerance of 1e-8).
xstep
The x-spacing.

Notes
-----
Expand All @@ -49,47 +49,47 @@ class MorphRGrid(Morph):
yinlabel = LABEL_GR
xoutlabel = LABEL_RA
youtlabel = LABEL_GR
parnames = ["rmin", "rmax", "rstep"]
parnames = ["xmin", "xmax", "xstep"]

# Define rmin rmax holders for adaptive x-grid refinement
# Define xmin xmax holders for adaptive x-grid refinement
# Without these, the program r-grid can only decrease in interval size
rmin_origin = None
rmax_origin = None
rstep_origin = None
xmin_origin = None
xmax_origin = None
xstep_origin = None

def morph(self, x_morph, y_morph, x_target, y_target):
"""Resample arrays onto specified grid."""
if self.rmin is not None:
self.rmin_origin = self.rmin
if self.rmax is not None:
self.rmax_origin = self.rmax
if self.rstep is not None:
self.rstep_origin = self.rstep
if self.xmin is not None:
self.xmin_origin = self.xmin
if self.xmax is not None:
self.xmax_origin = self.xmax
if self.xstep is not None:
self.xstep_origin = self.xstep

Morph.morph(self, x_morph, y_morph, x_target, y_target)
rmininc = max(min(self.x_target_in), min(self.x_morph_in))
r_step_target = (max(self.x_target_in) - min(self.x_target_in)) / (
xmininc = max(min(self.x_target_in), min(self.x_morph_in))
x_step_target = (max(self.x_target_in) - min(self.x_target_in)) / (
len(self.x_target_in) - 1
)
r_step_morph = (max(self.x_morph_in) - min(self.x_morph_in)) / (
x_step_morph = (max(self.x_morph_in) - min(self.x_morph_in)) / (
len(self.x_morph_in) - 1
)
rstepinc = max(r_step_target, r_step_morph)
rmaxinc = min(
max(self.x_target_in) + r_step_target,
max(self.x_morph_in) + r_step_morph,
xstepinc = max(x_step_target, x_step_morph)
xmaxinc = min(
max(self.x_target_in) + x_step_target,
max(self.x_morph_in) + x_step_morph,
)
if self.rmin_origin is None or self.rmin_origin < rmininc:
self.rmin = rmininc
if self.rmax_origin is None or self.rmax_origin > rmaxinc:
self.rmax = rmaxinc
if self.rstep_origin is None or self.rstep_origin < rstepinc:
self.rstep = rstepinc
if self.xmin_origin is None or self.xmin_origin < xmininc:
self.xmin = xmininc
if self.xmax_origin is None or self.xmax_origin > xmaxinc:
self.xmax = xmaxinc
if self.xstep_origin is None or self.xstep_origin < xstepinc:
self.xstep = xstepinc
# roundoff tolerance for selecting bounds on arrays.
epsilon = self.rstep / 2
# Make sure that rmax is exclusive
epsilon = self.xstep / 2
# Make sure that xmax is exclusive
self.x_morph_out = numpy.arange(
self.rmin, self.rmax - epsilon, self.rstep
self.xmin, self.xmax - epsilon, self.xstep
)
self.y_morph_out = numpy.interp(
self.x_morph_out, self.x_morph_in, self.y_morph_in
Expand Down
4 changes: 2 additions & 2 deletions src/diffpy/morph/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ def compare_funcs(
plt.ylim(ymin, ymax)

# Make labels and legends
plt.xlabel(r"r ($\mathrm{\AA})$")
plt.ylabel(r"G $(\mathrm{\AA}^{-1})$")
# plt.xlabel(r"r ($\mathrm{\AA})$")
# plt.ylabel(r"G $(\mathrm{\AA}^{-1})$")
if legend:
plt.legend(
bbox_to_anchor=(0.005, 1.02, 0.99, 0.10),
Expand Down
10 changes: 5 additions & 5 deletions tests/test_morphapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def test_parser_numerical(self, setup_parser):

# Check values parsed correctly
n_names = [
"--rmin",
"--rmax",
"--xmin",
"--xmax",
"--scale",
"--smear",
"--stretch",
Expand Down Expand Up @@ -134,14 +134,14 @@ def test_parser_systemexits(self, capsys, setup_parser):
in err
)

# Make sure rmax greater than rmin
# Make sure xmax greater than xmin
(opts, pargs) = self.parser.parse_args(
[f"{nickel_PDF}", f"{nickel_PDF}", "--rmin", "10", "--rmax", "1"]
[f"{nickel_PDF}", f"{nickel_PDF}", "--xmin", "10", "--xmax", "1"]
)
with pytest.raises(SystemExit):
single_morph(self.parser, opts, pargs, stdout_flag=False)
_, err = capsys.readouterr()
assert "rmin must be less than rmax" in err
assert "xmin must be less than xmax" in err

# ###Tests exclusive to multiple morphs###
# Make sure we save to a directory that exists
Expand Down
Loading
Loading