Skip to content

Commit 0bdbbb4

Browse files
enforce requirement to specify either density or packing fraction
1 parent a108205 commit 0bdbbb4

File tree

2 files changed

+66
-47
lines changed

2 files changed

+66
-47
lines changed

src/diffpy/utils/tools.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import importlib.metadata
22
import json
3+
import warnings
34
from copy import copy
45
from pathlib import Path
56

@@ -210,29 +211,45 @@ def get_package_info(package_names, metadata=None):
210211
return metadata
211212

212213

213-
def compute_mu_using_xraydb(sample_composition, energy, density=None, packing_fraction=1):
214+
def compute_mu_using_xraydb(sample_composition, energy, sample_mass_density=None, packing_fraction=None):
214215
"""Compute the attenuation coefficient (mu) using the XrayDB database.
215216
216217
Computes mu based on the sample composition and energy.
217-
User can provide a measured density or an estimated packing fraction.
218-
Specifying the density is recommended, though not required for some pure or standard materials.
218+
User should provide a sample mass density or a packing fraction.
219+
If neither density nor packing fraction is specified, or if both are specified, a ValueError will be raised.
219220
Reference: https://xraypy.github.io/XrayDB/python.html#xraydb.material_mu.
220221
221222
Parameters
222223
----------
223224
sample_composition : str
224225
The chemical formula or the name of the material.
225226
energy : float
226-
The energy in keV.
227-
density : float, optional, Default is None
227+
The energy of the incident x-rays in keV.
228+
sample_mass_density : float, optional, Default is None
228229
The mass density of the packed powder/sample in gr/cm^3.
229-
packing_fraction : float, optional, Default is 1
230+
packing_fraction : float, optional, Default is None
230231
The fraction of sample in the capillary (between 0 and 1).
231232
232233
Returns
233234
-------
234235
mu : float
235236
The attenuation coefficient mu in mm^{-1}.
236237
"""
237-
mu = material_mu(sample_composition, energy * 1000, density=density, kind="total") * packing_fraction / 10
238+
if (sample_mass_density is None and packing_fraction is None) or (
239+
sample_mass_density is not None and packing_fraction is not None
240+
):
241+
raise ValueError(
242+
"You must specify either sample_mass_density or packing_fraction, but not both. "
243+
"Please rerun specifying only one."
244+
)
245+
if sample_mass_density is not None:
246+
mu = material_mu(sample_composition, energy * 1000, density=sample_mass_density, kind="total") / 10
247+
else:
248+
warnings.warn(
249+
"Warning: Density is set to None if a packing fraction is specified, "
250+
"which may cause errors for some materials. "
251+
"We recommend specifying sample mass density for now. "
252+
"Auto-density calculation is coming soon."
253+
)
254+
mu = material_mu(sample_composition, energy * 1000, density=None, kind="total") * packing_fraction / 10
238255
return mu

tests/test_tools.py

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -171,59 +171,61 @@ def test_get_package_info(monkeypatch, inputs, expected):
171171
"inputs, expected_mu",
172172
[
173173
# Test whether the function returns the correct mu
174-
( # C1: No density or packing fraction (only for known material), expect to get mu from database
174+
( # C1: Composition, energy, and mass density provided, expect to get mu based on mass density
175+
# 1. Fully dense mass density
176+
{"sample_composition": "quartz", "energy": 10, "sample_mass_density": 2.65},
177+
5.0368,
178+
),
179+
( # 2. Measured mass density
175180
{
176-
"sample_composition": "H2O",
177-
"energy": 10,
181+
"sample_composition": "ZrO2",
182+
"energy": 17.445,
183+
"sample_mass_density": 1.009,
178184
},
179-
0.5330,
185+
1.2522,
180186
),
181-
( # C2: Packing fraction (=0.5) provided only (only for known material)
187+
( # C2: Composition, energy, and packing fraction provided, expect to get mu based on packing fraction
188+
# Reuse pattern from C1.1 here
182189
{
183-
"sample_composition": "H2O",
190+
"sample_composition": "quartz",
184191
"energy": 10,
185192
"packing_fraction": 0.5,
186193
},
187-
0.2665,
194+
2.5184,
188195
),
189-
( # C3: Density provided only, expect to compute mu based on it
190-
# 1. Known material
196+
],
197+
)
198+
def test_compute_mu_using_xraydb(inputs, expected_mu):
199+
actual_mu = compute_mu_using_xraydb(**inputs)
200+
assert actual_mu == pytest.approx(expected_mu, rel=1e-6, abs=1e-4)
201+
202+
203+
@pytest.mark.parametrize(
204+
"inputs",
205+
[
206+
# Test when the function raises ValueError
207+
# C1: Both mass density and packing fraction are provided
208+
(
191209
{
192-
"sample_composition": "H2O",
210+
"sample_composition": "quartz",
193211
"energy": 10,
194-
"density": 0.987,
195-
},
196-
0.5330,
212+
"sample_mass_density": 2.65,
213+
"packing_fraction": 1,
214+
}
197215
),
198-
( # 2. Unknown material
199-
{
200-
"sample_composition": "ZrO2",
201-
"energy": 17,
202-
"density": 1.009,
203-
},
204-
1.252,
205-
),
206-
( # C4: Both density and packing fraction are provided, expect to compute mu based on both
207-
# 1. Known material
216+
# C2: None of mass density or packing fraction are provided
217+
(
208218
{
209-
"sample_composition": "H2O",
219+
"sample_composition": "quartz",
210220
"energy": 10,
211-
"density": 0.997,
212-
"packing_fraction": 0.5,
213-
},
214-
0.2665,
215-
),
216-
( # 2. Unknown material
217-
{
218-
"sample_composition": "ZrO2",
219-
"energy": 17,
220-
"density": 1.009,
221-
"packing_fraction": 0.5,
222-
},
223-
0.626,
221+
}
224222
),
225223
],
226224
)
227-
def test_compute_mu_using_xraydb(inputs, expected_mu):
228-
actual_mu = compute_mu_using_xraydb(**inputs)
229-
assert actual_mu == pytest.approx(expected_mu, rel=0.01, abs=0.1)
225+
def test_compute_mu_using_xraydb_bad(inputs):
226+
with pytest.raises(
227+
ValueError,
228+
match="You must specify either sample_mass_density or packing_fraction, but not both. "
229+
"Please rerun specifying only one.",
230+
):
231+
compute_mu_using_xraydb(**inputs)

0 commit comments

Comments
 (0)