Skip to content

Commit f7fe981

Browse files
committed
Allow choice of plot parameter, some reformatting
1 parent 9ecade1 commit f7fe981

File tree

6 files changed

+105
-67
lines changed

6 files changed

+105
-67
lines changed

.github/workflows/main.yml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,6 @@ jobs:
4646
conda install --file requirements/test.txt
4747
pip install .
4848
49-
# redownload diffpy.utils pre 3.2.0 release, use https since public repo
50-
- name: install diffpy.utils
51-
shell: bash -l {0}
52-
run: |
53-
git clone https://github.com/diffpy/diffpy.utils.git
54-
cd diffpy.utils
55-
rm run_tests.py
56-
rm -r conda-recipe
57-
rm -r src/diffpy/utils/tests
58-
conda activate test
59-
pip install .
60-
6149
- name: Validate pdfmorph
6250
shell: bash -l {0}
6351
run: |

diffpy/pdfmorph/pdfmorph_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ def pdfmorph(
212212
if "scale" in refpars:
213213
rptemp.append("scale")
214214
refiner.refine(*rptemp)
215-
# Refine all params
215+
# Refine all param_list
216216
refiner.refine(*refpars)
217217
else:
218218
# no operation if refine=False or refpars is empty list

diffpy/pdfmorph/pdfmorphapp.py

Lines changed: 76 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,6 @@ def custom_error(self, msg):
6767
help="""Save the manipulated PDF to a file named NAME. Use \'-\' for stdout.
6868
When --multiple is enabled, save each manipulated PDF as a file in a directory named NAME;
6969
you can specify names for each saved PDF file using --save-names-file.""",
70-
)
71-
parser.add_option(
72-
'--snf',
73-
'--save-names-file',
74-
metavar="NAMESFILE",
75-
dest="snamesfile",
76-
help=f"""Used only when both -s and --multiple are enabled.
77-
Specify names for each manipulated PDF when saving (see -s) using a serial file
78-
NAMESFILE. The format of NAMESFILE should be as follows: each target PDF
79-
is an entry in NAMESFILE. For each entry, there should be a key {__save_morph_as__}
80-
whose value specifies the name to save the manipulated PDF as.
81-
(See sample names files in the PDFmorph tutorial).""",
8270
)
8371
parser.add_option(
8472
'-v',
@@ -88,53 +76,82 @@ def custom_error(self, msg):
8876
help="Print additional header details to saved files.",
8977
)
9078
parser.add_option(
79+
'--rmin', type="float", help="Minimum r-value to use for PDF comparisons."
80+
)
81+
parser.add_option(
82+
'--rmax', type="float", help="Maximum r-value to use for PDF comparisons."
83+
)
84+
parser.add_option(
85+
'--pearson',
86+
action="store_true",
87+
dest="pearson",
88+
help="Maximize agreement in the Pearson function. Note that this is insensitive to scale.",
89+
)
90+
parser.add_option(
91+
'--addpearson',
92+
action="store_true",
93+
dest="addpearson",
94+
help="""Maximize agreement in the Pearson function as well as
95+
minimizing the residual.""",
96+
)
97+
98+
group = optparse.OptionGroup(
99+
parser,
100+
"Multiple Morphs",
101+
"""This program can morph a PDF against multiple targets in one command.
102+
See -s and Plot Options for how saving and plotting functionality changes when performing multiple morphs."""
103+
)
104+
parser.add_option_group(group)
105+
group.add_option(
91106
'--multiple',
92107
dest="multiple",
93108
action="store_true",
94109
help=f"""Changes usage to \'{prog_short} [options] FILE DIRECTORY\'. FILE
95110
will be morphed with each file in DIRECTORY as target.
96111
Files in DIRECTORY are sorted by alphabetical order unless a field is
97-
specified by --sort-by. See -s and Plot Options for how saving and
98-
plotting change when this option is enabled.""",
112+
specified by --sort-by.""",
99113
)
100-
parser.add_option(
114+
group.add_option(
101115
'--sort-by',
102116
metavar="FIELD",
103117
dest="field",
104118
help="""Used with --multiple to sort files in DIRECTORY by FIELD from lowest to highest.
105119
FIELD must be included in the header of all the PDF files.""",
106120
)
107-
parser.add_option(
121+
group.add_option(
108122
'--reverse',
109123
dest="reverse",
110124
action="store_true",
111125
help="""Sort from highest to lowest instead.""",
112126
)
113-
parser.add_option(
127+
group.add_option(
114128
'--serial-file',
115-
metavar="SERIAL",
129+
metavar="SERIALFILE",
116130
dest="serfile",
117131
help="""Look for FIELD in a serial file instead.
118-
Must specify name of serial file SERIAL.""",
119-
)
120-
parser.add_option(
121-
'--rmin', type="float", help="Minimum r-value to use for PDF comparisons."
132+
Must specify name of serial file SERIALFILE.""",
122133
)
123-
parser.add_option(
124-
'--rmax', type="float", help="Maximum r-value to use for PDF comparisons."
125-
)
126-
parser.add_option(
127-
'--pearson',
128-
action="store_true",
129-
dest="pearson",
130-
help="Maximize agreement in the Pearson function. Note that this is insensitive to scale.",
134+
group.add_option(
135+
'--save-names-file',
136+
metavar="NAMESFILE",
137+
dest="snamesfile",
138+
help=f"""Used when both -s and --multiple are enabled.
139+
Specify names for each manipulated PDF when saving (see -s) using a serial file
140+
NAMESFILE. The format of NAMESFILE should be as follows: each target PDF
141+
is an entry in NAMESFILE. For each entry, there should be a key {__save_morph_as__}
142+
whose value specifies the name to save the manipulated PDF as.
143+
(See sample names files in the PDFmorph tutorial).""",
131144
)
132-
parser.add_option(
133-
'--addpearson',
134-
action="store_true",
135-
dest="addpearson",
136-
help="""Maximize agreement in the Pearson function as well as
137-
minimizing the residual.""",
145+
group.add_option(
146+
'--plot-parameter',
147+
metavar='PLOTPARAM',
148+
dest='plotparam',
149+
help="""Used when both plotting and --multiple are enabled.
150+
Choose a PLOTPARAM to plot for each morph (i.e. adding --pp=Pearson means the program
151+
will display a plot of the Pearson correlation coefficient for each morph-target pair).
152+
PLOTPARAM is not case sensitive, so both Pearson and pearson indicate the same parameter.
153+
When PLOTPARAM is not specified, Rw values for each morph-target pair will be plotted.
154+
PLOTPARAM will be displayed as the vertical axis label for the plot.""",
138155
)
139156

140157
# Manipulations
@@ -222,8 +239,8 @@ def custom_error(self, msg):
222239
group = optparse.OptionGroup(
223240
parser, "Plot Options", """These options control plotting.
224241
The manipulated and target PDFs will be plotted against each other with a
225-
difference curve below. When --multiple is enabled, a plot of Rw values for
226-
each file will be shown instead."""
242+
difference curve below. When --multiple is enabled, the value of a parameter (specified by
243+
--plot-parameter) will be plotted instead."""
227244
)
228245
parser.add_option_group(group)
229246
group.add_option(
@@ -539,14 +556,30 @@ def multiple_morphs(parser, opts, pargs, stdout_flag=True):
539556
save_fail_message = "Unable to save summary to directory."
540557
parser.custom_error(save_fail_message)
541558

542-
# Plot the rw values for each target if requested
543-
# FIXME: create functionality to plot other data (scale, stretch, smear, etc.)
559+
# Plot the values of some parameter for each target if requested
544560
if plot_opt:
545561
plot_results = io.tabulate_results(morph_results)
546-
if field_list is not None:
547-
pdfplot.plot_rws(field_list, plot_results["Rw"], field)
562+
# Default parameter is Rw
563+
param_name = r"$R_w$"
564+
param_list = plot_results["Rw"]
565+
# Find parameter if specified
566+
if opts.plotparam is not None:
567+
param_name = opts.plotparam
568+
param_list = tools.case_insensitive_dictionary_search(opts.plotparam, plot_results)
569+
# Not an available parameter to plot or no values found for the parameter
570+
if param_list is None:
571+
parser.custom_error("Cannot find specified plot parameter. No plot shown.")
548572
else:
549-
pdfplot.plot_rws(target_file_names, plot_results["Rw"])
573+
try:
574+
if field_list is not None:
575+
pdfplot.plot_param(field_list, param_list, param_name, field)
576+
else:
577+
pdfplot.plot_param(target_file_names, param_list, param_name)
578+
# Can occur for non-refined plotting parameters
579+
# i.e. --smear is not selected as an option, but smear is the plotting parameter
580+
except ValueError:
581+
parser.custom_error("The plot parameter is missing values for at least one morph and target pair. "
582+
"No plot shown.")
550583

551584
return morph_results
552585

diffpy/pdfmorph/pdfplot.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,15 @@ def comparePDFs(
208208
return
209209

210210

211-
def plot_rws(target_labels, rws, field=None):
211+
def plot_param(target_labels, param_list, param_name=None, field=None):
212212
"""
213213
Plot Rw values for multiple morphs.
214214
:param target_labels: Names (or field if --sort-by given) of each file acting as target for the morph.
215215
:type target_labels: list
216-
:param rws: Contains the Rw values corresponding to each file.
217-
:type rws: list
216+
:param param_list: Contains the values of some parameter corresponding to each file.
217+
:type param_list: list
218+
:param param_name: Name of the parameter.
219+
:type param_name: str
218220
:param field: When not None and entries in field are numerical, a line chart of Rw versus field is made.
219221
When None (default) or values are non-numerical, it plots a bar chart of Rw values per file.
220222
:type field: list or None
@@ -245,9 +247,10 @@ def plot_rws(target_labels, rws, field=None):
245247
numerical = False
246248

247249
if numerical:
248-
# Plot Rw vs Temperature
249-
plt.plot(target_labels, rws, linestyle='-', marker='o')
250-
plt.ylabel(r"$R_w$")
250+
# Plot the parameter against a numerical field
251+
plt.plot(target_labels, param_list, linestyle='-', marker='o')
252+
if param_name is not None:
253+
plt.ylabel(rf"{param_name}")
251254
plt.xlabel(rf"{field}")
252255
plt.minorticks_on()
253256

@@ -263,8 +266,9 @@ def plot_rws(target_labels, rws, field=None):
263266
plt.xticks(rotation=angle)
264267

265268
# Plot Rw for each file
266-
plt.bar(target_labels, rws)
267-
plt.ylabel(r"$R_w$")
269+
plt.bar(target_labels, param_list)
270+
if param_name is not None:
271+
plt.ylabel(rf"{param_name}")
268272
if field is None:
269273
plt.xlabel(r"Target File")
270274
else:

diffpy/pdfmorph/tests/test_pdfmorphapp.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def test_parser_numerical(self, setup_parser):
6363
assert len(n_args) == 1 # Check one leftover
6464

6565
def test_parser_systemexits(self, setup_parser):
66+
# ###Basic tests for any variety of morphing###
67+
6668
# Ensure only two pargs given for morphing
6769
(opts, pargs) = self.parser.parse_args(["toofewfiles"])
6870
with pytest.raises(SystemExit):
@@ -80,6 +82,7 @@ def test_parser_systemexits(self, setup_parser):
8082
with pytest.raises(SystemExit):
8183
single_morph(self.parser, opts, pargs, stdout_flag=False)
8284

85+
# ###Tests exclusive to multiple morphs###
8386
# Make sure we save to a directory that exists (user must create the directory if non-existing)
8487
(opts, pargs) = self.parser.parse_args([f"{nickel_PDF}", f"{nickel_PDF}", "-s",
8588
"/nonexisting_directory/no_way_this_exists/nonexisting_path"])
@@ -101,7 +104,17 @@ def test_parser_systemexits(self, setup_parser):
101104
with pytest.raises(SystemExit):
102105
multiple_morphs(self.parser, opts, pargs, stdout_flag=False)
103106
(opts, pargs) = self.parser.parse_args([f"{nickel_PDF}", f"{testsequence_dir}", "--sort-by", "fake_field",
104-
"--serial-file", f"{serial_JSON}"])
107+
"--serial-file", f"{serial_JSON}"])
108+
with pytest.raises(SystemExit):
109+
multiple_morphs(self.parser, opts, pargs, stdout_flag=False)
110+
111+
# Try plotting an unknown parameter
112+
(opts, pargs) = self.parser.parse_args([f"{nickel_PDF}", f"{testsequence_dir}", "--plot-parameter", "unknown"])
113+
with pytest.raises(SystemExit):
114+
multiple_morphs(self.parser, opts, pargs, stdout_flag=False)
115+
116+
# Try plotting an unrefined parameter
117+
(opts, pargs) = self.parser.parse_args([f"{nickel_PDF}", f"{testsequence_dir}", "--plot-parameter", "stretch"])
105118
with pytest.raises(SystemExit):
106119
multiple_morphs(self.parser, opts, pargs, stdout_flag=False)
107120

diffpy/pdfmorph/tests/test_pdfmorphio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def ignore_path(line):
5151
tmp_succinct_name = tmp_succinct.resolve().as_posix()
5252

5353
(opts, pargs) = self.parser.parse_args(["--multiple", "--sort-by", "temperature", "-s", tmp_succinct_name,
54-
"-n", "--snf", tssf])
54+
"-n", "--save-names-file", tssf])
5555
pargs = [morph_file, testsequence_dir]
5656
multiple_morphs(self.parser, opts, pargs, stdout_flag=False)
5757

@@ -79,7 +79,7 @@ def ignore_path(line):
7979
tmp_verbose_name = tmp_verbose.resolve().as_posix()
8080

8181
(opts, pargs) = self.parser.parse_args(["--multiple", "--sort-by", "temperature", "-s", tmp_verbose_name,
82-
"-n", "--snf", tssf, "--verbose"])
82+
"-n", "--save-names-file", tssf, "--verbose"])
8383
pargs = [morph_file, testsequence_dir]
8484
multiple_morphs(self.parser, opts, pargs, stdout_flag=False)
8585

0 commit comments

Comments
 (0)