Skip to content

Commit 59198ce

Browse files
authored
Merge pull request #173 from yucongalicechen/muD-theoretical
feat: options for estimating muD theoretically
2 parents b4aa8bb + f224f3c commit 59198ce

File tree

4 files changed

+197
-25
lines changed

4 files changed

+197
-25
lines changed

news/muD-theoretical.rst

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* Functionalities to estimate mu*D theoretically.
4+
5+
**Changed:**
6+
7+
* <news item>
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/labpdfproc/labpdfprocapp.py

+40-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
from diffpy.utils.diffraction_objects import XQUANTITIES, DiffractionObject
1313
from diffpy.utils.parsers.loaddata import loadData
1414

15+
theoretical_mud_hmsg_suffix = (
16+
"in that exact order, "
17+
"separated by commas (e.g., ZrO2,17.45,0.5). "
18+
"If you add whitespaces, "
19+
"enclose it in quotes (e.g., 'ZrO2, 17.45, 0.5'). "
20+
)
21+
1522

1623
def _define_arguments():
1724
args = [
@@ -21,12 +28,14 @@ def _define_arguments():
2128
"The filename(s) or folder(s) of the datafile(s) to load. "
2229
"Required.\n"
2330
"Supply a space-separated list of files or directories. "
31+
"Avoid spaces in filenames when possible; "
32+
"if present, enclose the name in quotes. "
2433
"Long lists can be supplied, one per line, "
2534
"in a file with name file_list.txt. "
2635
"If one or more directory is provided, all valid "
2736
"data-files in that directory will be processed. "
2837
"Examples of valid inputs are 'file.xy', 'data/file.xy', "
29-
"'file.xy, data/file.xy', "
38+
"'file.xy data/file.xy', "
3039
"'.' (load everything in the current directory), "
3140
"'data' (load everything in the folder ./data), "
3241
"'data/file_list.txt' (load the list of files "
@@ -152,7 +161,11 @@ def _define_arguments():
152161
def _add_mud_selection_group(p, is_gui=False):
153162
"""Current Options:
154163
1. Manually enter muD (`--mud`).
155-
2. Estimate muD from a z-scan file (`-z` or `--z-scan-file`).
164+
2. Estimate from a z-scan file (`-z` or `--z-scan-file`).
165+
3. Estimate theoretically based on sample mass density
166+
(`-d` or `--theoretical-from-density`).
167+
4. Estimate theoretically based on packing fraction
168+
(`-p` or `--theoretical-from-packing`).
156169
"""
157170
g = p.add_argument_group("Options for setting mu*D value (Required)")
158171
g = g.add_mutually_exclusive_group(required=True)
@@ -165,10 +178,32 @@ def _add_mud_selection_group(p, is_gui=False):
165178
g.add_argument(
166179
"-z",
167180
"--z-scan-file",
168-
help="Provide the path to the z-scan file to be loaded "
169-
"to determine the mu*D value.",
181+
help=(
182+
"Estimate mu*D experimentally from a z-scan file. "
183+
"Specify the path to the file "
184+
"used to compute the mu*D value."
185+
),
170186
**({"widget": "FileChooser"} if is_gui else {}),
171187
)
188+
g.add_argument(
189+
"-d",
190+
"--theoretical-from-density",
191+
help=(
192+
"Estimate mu*D theoretically using sample mass density. "
193+
"Specify the chemical formula, incident x-ray energy (in keV), "
194+
"and sample mass density (in g/cm^3), "
195+
+ theoretical_mud_hmsg_suffix
196+
),
197+
)
198+
g.add_argument(
199+
"-p",
200+
"--theoretical-from-packing",
201+
help=(
202+
"Estimate mu*D theoretically using packing fraction. "
203+
"Specify the chemical formula, incident x-ray energy (in keV), "
204+
"and packing fraction (0 to 1), " + theoretical_mud_hmsg_suffix
205+
),
206+
)
172207
return p
173208

174209

@@ -186,7 +221,7 @@ def get_args(override_cli_inputs=None):
186221
return args
187222

188223

189-
@Gooey(required_cols=1, optional_cols=1, program_name="Labpdfproc GUI")
224+
@Gooey(required_cols=1, optional_cols=2, program_name="labpdfproc GUI")
190225
def gooey_parser():
191226
p = GooeyParser()
192227
p = _add_mud_selection_group(p, is_gui=True)

src/diffpy/labpdfproc/tools.py

+72-19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from diffpy.utils.tools import (
1010
_load_config,
1111
check_and_build_global_config,
12+
compute_mu_using_xraydb,
1213
compute_mud,
1314
get_package_info,
1415
get_user_info,
@@ -31,14 +32,19 @@
3132
}
3233
known_sources = [key for key in WAVELENGTHS.keys()]
3334

34-
# Exclude wavelength from metadata to prevent duplication,
35-
# as the dump function in diffpy.utils writes it explicitly.
35+
# Exclude wavelength to avoid duplication,
36+
# as it's written explicitly by diffpy.utils dump function.
37+
# Exclude "theoretical_from_density" and "theoretical_from_packing"
38+
# as they are only used for theoretical mu*D estimation
39+
# and will be written into separate arguments for clarity.
3640
METADATA_KEYS_TO_EXCLUDE = [
3741
"output_correction",
3842
"force_overwrite",
3943
"input",
4044
"input_paths",
4145
"wavelength",
46+
"theoretical_from_density",
47+
"theoretical_from_packing",
4248
]
4349

4450

@@ -298,19 +304,8 @@ def set_xtype(args):
298304
return args
299305

300306

301-
def _estimate_mud_from_zscan(args):
302-
"""Compute mu*D based on the given z-scan file.
303-
304-
Parameters
305-
----------
306-
args : argparse.Namespace
307-
The arguments from the parser.
308-
309-
Returns
310-
-------
311-
args : argparse.Namespace
312-
The updated arguments with mu*D.
313-
"""
307+
def _set_mud_from_zscan(args):
308+
"""Experimental estimation of mu*D from a z-scan file."""
314309
filepath = Path(args.z_scan_file).resolve()
315310
if not filepath.is_file():
316311
raise FileNotFoundError(
@@ -322,10 +317,64 @@ def _estimate_mud_from_zscan(args):
322317
return args
323318

324319

320+
def _parse_theoretical_input(input_str):
321+
"""Helper function to parse and validate the input string."""
322+
parts = [part.strip() for part in input_str.split(",")]
323+
if len(parts) != 3:
324+
raise ValueError(
325+
f"Invalid mu*D input '{input_str}'. "
326+
"Expected format is 'sample composition, energy, "
327+
"sample mass density or packing fraction' "
328+
"(e.g., 'ZrO2,17.45,0.5').",
329+
)
330+
sample_composition = parts[0]
331+
energy = float(parts[1])
332+
mass_density_or_packing_fraction = float(parts[2])
333+
return sample_composition, energy, mass_density_or_packing_fraction
334+
335+
336+
def _set_theoretical_mud_from_density(args):
337+
"""Theoretical estimation of mu*D from
338+
sample composition, energy, and sample mass density."""
339+
sample_composition, energy, sample_mass_density = _parse_theoretical_input(
340+
args.theoretical_from_density
341+
)
342+
args.sample_composition = sample_composition
343+
args.energy = energy
344+
args.sample_mass_density = sample_mass_density
345+
args.mud = compute_mu_using_xraydb(
346+
args.sample_composition,
347+
args.energy,
348+
sample_mass_density=args.sample_mass_density,
349+
)
350+
return args
351+
352+
353+
def _set_theoretical_mud_from_packing(args):
354+
"""Theoretical estimation of mu*D from
355+
sample composition, energy, and packing fraction."""
356+
sample_composition, energy, packing_fraction = _parse_theoretical_input(
357+
args.theoretical_from_packing
358+
)
359+
args.sample_composition = sample_composition
360+
args.energy = energy
361+
args.packing_fraction = packing_fraction
362+
args.mud = compute_mu_using_xraydb(
363+
args.sample_composition,
364+
args.energy,
365+
packing_fraction=args.packing_fraction,
366+
)
367+
return args
368+
369+
325370
def set_mud(args):
326-
"""Compute and set mu*D based on different options.
327-
Current options include manually entering a value,
328-
or estimating from a z-scan file.
371+
"""Compute and set mu*D based on the selected method.
372+
373+
Options include:
374+
1. Manually entering a value.
375+
2. Estimating from a z-scan file.
376+
3. Estimating theoretically based on sample mass density.
377+
4. Estimating theoretically based on packing fraction.
329378
330379
Parameters
331380
----------
@@ -338,7 +387,11 @@ def set_mud(args):
338387
The updated arguments with mu*D.
339388
"""
340389
if args.z_scan_file:
341-
return _estimate_mud_from_zscan(args)
390+
return _set_mud_from_zscan(args)
391+
elif args.theoretical_from_density:
392+
return _set_theoretical_mud_from_density(args)
393+
elif args.theoretical_from_packing:
394+
return _set_theoretical_mud_from_packing(args)
342395
return args
343396

344397

tests/test_tools.py

+62-1
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,15 @@ def test_set_xtype_bad():
460460
(["--mud", "2.5"], 2.5),
461461
# C2: user provides a z-scan file, expect to estimate through the file
462462
(["--z-scan-file", "test_dir/testfile.xy"], 3),
463+
# C3: user specifies sample composition, energy,
464+
# and sample mass density,
465+
# both with and without whitespaces, expect to estimate theoretically
466+
(["--theoretical-from-density", "ZrO2,17.45,1.2"], 1.49),
467+
(["--theoretical-from-density", "ZrO2, 17.45, 1.2"], 1.49),
468+
# C4: user specifies sample composition, energy, and packing fraction
469+
# both with and without whitespaces, expect to estimate theoretically
470+
# (["--theoretical-from-packing", "ZrO2,17.45,0.3"], 1.49),
471+
# (["--theoretical-from-packing", "ZrO2, 17.45, 0.3"], 1.49),
463472
],
464473
)
465474
def test_set_mud(user_filesystem, inputs, expected_mud):
@@ -483,6 +492,58 @@ def test_set_mud(user_filesystem, inputs, expected_mud):
483492
"Cannot find invalid file. Please specify a valid file path.",
484493
],
485494
),
495+
# C2.1: (sample mass density option)
496+
# user provides fewer than three input values
497+
# expect ValueError with a message indicating the correct format
498+
(
499+
["--theoretical-from-density", "ZrO2,0.5"],
500+
[
501+
ValueError,
502+
"Invalid mu*D input 'ZrO2,0.5'. "
503+
"Expected format is 'sample composition, energy, "
504+
"sample mass density or packing fraction' "
505+
"(e.g., 'ZrO2,17.45,0.5').",
506+
],
507+
),
508+
# C2.2: (packing fraction option)
509+
# user provides fewer than three input values
510+
# expect ValueError with a message indicating the correct format
511+
(
512+
["--theoretical-from-packing", "ZrO2,0.5"],
513+
[
514+
ValueError,
515+
"Invalid mu*D input 'ZrO2,0.5'. "
516+
"Expected format is 'sample composition, energy, "
517+
"sample mass density or packing fraction' "
518+
"(e.g., 'ZrO2,17.45,0.5').",
519+
],
520+
),
521+
# C3.1: (sample mass density option)
522+
# user provides more than 3 input values
523+
# expect ValueError with a message indicating the correct format
524+
(
525+
["--theoretical-from-density", "ZrO2,17.45,1.5,0.5"],
526+
[
527+
ValueError,
528+
"Invalid mu*D input 'ZrO2,17.45,1.5,0.5'. "
529+
"Expected format is 'sample composition, energy, "
530+
"sample mass density or packing fraction' "
531+
"(e.g., 'ZrO2,17.45,0.5').",
532+
],
533+
),
534+
# C3.2: (packing fraction option)
535+
# user provides more than 3 input values
536+
# expect ValueError with a message indicating the correct format
537+
(
538+
["--theoretical-from-packing", "ZrO2,17.45,1.5,0.5"],
539+
[
540+
ValueError,
541+
"Invalid mu*D input 'ZrO2,17.45,1.5,0.5'. "
542+
"Expected format is 'sample composition, energy, "
543+
"sample mass density or packing fraction' "
544+
"(e.g., 'ZrO2,17.45,0.5').",
545+
],
546+
),
486547
],
487548
)
488549
def test_set_mud_bad(user_filesystem, inputs, expected):
@@ -491,7 +552,7 @@ def test_set_mud_bad(user_filesystem, inputs, expected):
491552
os.chdir(cwd)
492553
cli_inputs = ["data.xy"] + inputs
493554
actual_args = get_args(cli_inputs)
494-
with pytest.raises(expected_error, match=expected_error_msg):
555+
with pytest.raises(expected_error, match=re.escape(expected_error_msg)):
495556
actual_args = set_mud(actual_args)
496557

497558

0 commit comments

Comments
 (0)