Skip to content

Commit a8f53ae

Browse files
authored
Rename PDF-specific smear option to --smear-pdf (#220)
* Refactor smear to smear-pdf * Reformat help headers * Move tests to test_morphapp * News
1 parent ea41b24 commit a8f53ae

File tree

3 files changed

+114
-8
lines changed

3 files changed

+114
-8
lines changed

news/smear-update.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* New --smear option applies the smear morph directly to the function (without transforming to RDF).
4+
5+
**Changed:**
6+
7+
* Former --smear option renamed to --smear-pdf (converts PDF to RDF before applying the smear morph).
8+
9+
**Deprecated:**
10+
11+
* <news item>
12+
13+
**Removed:**
14+
15+
* <news item>
16+
17+
**Fixed:**
18+
19+
* <news item>
20+
21+
**Security:**
22+
23+
* <news item>

src/diffpy/morph/morphapp.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,31 @@ def custom_error(self, msg):
196196
type="float",
197197
metavar="SMEAR",
198198
help=(
199-
"Smear peaks with a Gaussian of width SMEAR. "
200-
"This convolves the function with a Gaussian of width SMEAR."
199+
"Smear the peaks with a Gaussian of width SMEAR. "
200+
"This is done by convolving the function with a "
201+
"Gaussian with standard deviation SMEAR. "
202+
"If both --smear and --smear-pdf are enabled, "
203+
"only --smear-pdf will be applied."
204+
),
205+
)
206+
group.add_option(
207+
"--smear-pdf",
208+
type="float",
209+
metavar="SMEAR",
210+
help=(
211+
"Convert PDF to RDF. "
212+
"Then, smear peaks with a Gaussian of width SMEAR. "
213+
"Convert back to PDF. "
214+
"If both --smear and --smear-pdf are enabled, "
215+
"only --smear-pdf will be applied."
201216
),
202217
)
203218
group.add_option(
204219
"--slope",
205220
type="float",
206221
dest="baselineslope",
207-
help="""Slope of the baseline. This is used when applying the smear
208-
factor. It will be estimated if not provided.""",
222+
help="""Slope of the baseline. This is used with the option --smear-pdf
223+
to convert from the PDF to RDF. It will be estimated if not provided.""",
209224
)
210225
group.add_option(
211226
"--hshift",
@@ -520,8 +535,8 @@ def single_morph(parser, opts, pargs, stdout_flag=True):
520535
config["vshift"] = vshift_in
521536
refpars.append("vshift")
522537
# Smear
523-
if opts.smear is not None:
524-
smear_in = opts.smear
538+
if opts.smear_pdf is not None:
539+
smear_in = opts.smear_pdf
525540
chain.append(helpers.TransformXtalPDFtoRDF())
526541
chain.append(morphs.MorphSmear())
527542
chain.append(helpers.TransformXtalRDFtoPDF())
@@ -532,6 +547,11 @@ def single_morph(parser, opts, pargs, stdout_flag=True):
532547
if opts.baselineslope is None:
533548
config["baselineslope"] = -0.5
534549
refpars.append("baselineslope")
550+
elif opts.smear is not None:
551+
smear_in = opts.smear
552+
chain.append(morphs.MorphSmear())
553+
refpars.append("smear")
554+
config["smear"] = smear_in
535555
# Size
536556
radii = [opts.radius, opts.pradius]
537557
nrad = 2 - radii.count(None)
@@ -806,7 +826,7 @@ def multiple_targets(parser, opts, pargs, stdout_flag=True):
806826
morph_inputs = {
807827
"scale": opts.scale,
808828
"stretch": opts.stretch,
809-
"smear": opts.smear,
829+
"smear": opts.smear_pdf,
810830
}
811831
morph_inputs.update({"hshift": opts.hshift, "vshift": opts.vshift})
812832

@@ -989,7 +1009,7 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True):
9891009
morph_inputs = {
9901010
"scale": opts.scale,
9911011
"stretch": opts.stretch,
992-
"smear": opts.smear,
1012+
"smear": opts.smear_pdf,
9931013
}
9941014
morph_inputs.update({"hshift": opts.hshift, "vshift": opts.vshift})
9951015

tests/test_morphapp.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from pathlib import Path
44

5+
import numpy as np
56
import pytest
67

78
from diffpy.morph.morphapp import (
@@ -255,6 +256,68 @@ def test_morphsequence(self, setup_morphsequence):
255256
)
256257
assert s_sequence_results == sequence_results
257258

259+
def test_morphsmear(self, setup_parser, tmp_path):
260+
def gaussian(x, mu, sigma):
261+
return np.exp(-((x - mu) ** 2) / (2 * sigma**2)) / (
262+
sigma * np.sqrt(2 * np.pi)
263+
)
264+
265+
# Generate the test files
266+
x_grid = np.linspace(1, 101, 1001)
267+
# Gaussian with STD 3 (morph)
268+
g2 = gaussian(x_grid, 51, 3)
269+
mf = tmp_path / "morph.txt"
270+
with open(mf, "w") as f:
271+
np.savetxt(f, np.array([x_grid, g2]).T)
272+
# Gaussian with STD 5 (target)
273+
g3 = gaussian(x_grid, 51, 5)
274+
tf = tmp_path / "target.txt"
275+
with open(tf, "w") as f:
276+
np.savetxt(f, np.array([x_grid, g3]).T)
277+
# Gaussian with STD 3 and baseline slope -0.5 (PDF morph)
278+
g2_bl = gaussian(x_grid, 51, 3) / x_grid - 0.5 * x_grid
279+
pmf = tmp_path / "pdf_morph.txt"
280+
with open(pmf, "w") as f:
281+
np.savetxt(f, np.array([x_grid, g2_bl]).T)
282+
# Gaussian with STD 5 with baseline slope -0.5 (PDF target)
283+
g3_bl = gaussian(x_grid, 51, 5) / x_grid - 0.5 * x_grid
284+
ptf = tmp_path / "pdf_target.txt"
285+
with open(ptf, "w") as f:
286+
np.savetxt(f, np.array([x_grid, g3_bl]).T)
287+
288+
# No PDF smear (should not activate baseline slope)
289+
(opts, _) = self.parser.parse_args(
290+
[
291+
"--smear",
292+
"1",
293+
"-n",
294+
]
295+
)
296+
pargs = [mf, tf]
297+
smear_results = single_morph(
298+
self.parser, opts, pargs, stdout_flag=False
299+
)
300+
# Variances add, and 3^2+4^2=5^2
301+
assert pytest.approx(abs(smear_results["smear"])) == 4.0
302+
assert pytest.approx(smear_results["Rw"]) == 0.0
303+
304+
# PDF-specific smear (should activate baseline slope)
305+
(opts, _) = self.parser.parse_args(
306+
[
307+
"--smear",
308+
"100",
309+
"--smear-pdf",
310+
"1",
311+
"-n",
312+
]
313+
)
314+
pargs = [pmf, ptf]
315+
pdf_smear_results = single_morph(
316+
self.parser, opts, pargs, stdout_flag=False
317+
)
318+
assert pytest.approx(abs(pdf_smear_results["smear"])) == 4.0
319+
assert pytest.approx(pdf_smear_results["Rw"]) == 0.0
320+
258321

259322
if __name__ == "__main__":
260323
TestApp()

0 commit comments

Comments
 (0)