-
Notifications
You must be signed in to change notification settings - Fork 21
compute mu #278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
compute mu #278
Changes from 9 commits
5180eeb
5ff2c23
1826221
e54f9bb
be7c275
9e4bc0d
1879f44
a108205
0bdbbb4
00f36ad
255c603
5492ad8
a4a217d
953e187
dd52ad9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
**Added:** | ||
|
||
* function to compute x-ray attenuation coefficient (mu) using XrayDB | ||
|
||
**Changed:** | ||
|
||
* <news item> | ||
|
||
**Deprecated:** | ||
|
||
* <news item> | ||
|
||
**Removed:** | ||
|
||
* <news item> | ||
|
||
**Fixed:** | ||
|
||
* <news item> | ||
|
||
**Security:** | ||
|
||
* <news item> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
numpy | ||
xraydb |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
numpy | ||
xraydb |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
import importlib.metadata | ||
import json | ||
import warnings | ||
from copy import copy | ||
from pathlib import Path | ||
|
||
from xraydb import material_mu | ||
|
||
|
||
def clean_dict(obj): | ||
"""Remove keys from the dictionary where the corresponding value is None. | ||
|
@@ -206,3 +209,47 @@ def get_package_info(package_names, metadata=None): | |
pkg_info.update({package: importlib.metadata.version(package)}) | ||
metadata["package_info"] = pkg_info | ||
return metadata | ||
|
||
|
||
def compute_mu_using_xraydb(sample_composition, energy, sample_mass_density=None, packing_fraction=None): | ||
"""Compute the attenuation coefficient (mu) using the XrayDB database. | ||
|
||
Computes mu based on the sample composition and energy. | ||
User should provide a sample mass density or a packing fraction. | ||
If neither density nor packing fraction is specified, or if both are specified, a ValueError will be raised. | ||
sbillinge marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Reference: https://xraypy.github.io/XrayDB/python.html#xraydb.material_mu. | ||
|
||
Parameters | ||
---------- | ||
sample_composition : str | ||
The chemical formula or the name of the material. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not the name, just chemical formula. |
||
energy : float | ||
The energy of the incident x-rays in keV. | ||
sample_mass_density : float, optional, Default is None | ||
The mass density of the packed powder/sample in gr/cm^3. | ||
sbillinge marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. g/cm*3, not gr/cm^3 |
||
packing_fraction : float, optional, Default is None | ||
The fraction of sample in the capillary (between 0 and 1). | ||
sbillinge marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Returns | ||
------- | ||
mu : float | ||
The attenuation coefficient mu in mm^{-1}. | ||
""" | ||
if (sample_mass_density is None and packing_fraction is None) or ( | ||
sample_mass_density is not None and packing_fraction is not None | ||
): | ||
raise ValueError( | ||
"You must specify either sample_mass_density or packing_fraction, but not both. " | ||
"Please rerun specifying only one." | ||
sbillinge marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
if sample_mass_density is not None: | ||
mu = material_mu(sample_composition, energy * 1000, density=sample_mass_density, kind="total") / 10 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make it more readable, don't hardcode numbers but rather variables with obvious names. |
||
else: | ||
warnings.warn( | ||
"Warning: Density is set to None if a packing fraction is specified, " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You didn't need the word warning here. Warnings.warn handles that. |
||
"which may cause errors for some materials. " | ||
"We recommend specifying sample mass density for now. " | ||
"Auto-density calculation is coming soon." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a warning message for now. I set density to None if user specifies packing fraction. We can probably replace this with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's easy if we know the structure, but I am pretty sure MP has material density as an attribute so a simple API call will give it. We will have to add users adding their API key to the global config..... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Density is just number of atoms/unit cell volume which we know from composition and any CIF file we find, then converted to mass density with the formula, then multiplied by packing fraction There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code could be pretty reusable so I expect it will end up in diffpy.utils in the end (but in a future release) |
||
) | ||
mu = material_mu(sample_composition, energy * 1000, density=None, kind="total") * packing_fraction / 10 | ||
return mu |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,12 @@ | |
|
||
import pytest | ||
|
||
from diffpy.utils.tools import check_and_build_global_config, get_package_info, get_user_info | ||
from diffpy.utils.tools import ( | ||
check_and_build_global_config, | ||
compute_mu_using_xraydb, | ||
get_package_info, | ||
get_user_info, | ||
) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
|
@@ -160,3 +165,67 @@ def test_get_package_info(monkeypatch, inputs, expected): | |
) | ||
actual_metadata = get_package_info(inputs[0], metadata=inputs[1]) | ||
assert actual_metadata == expected | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"inputs, expected_mu", | ||
[ | ||
# Test whether the function returns the correct mu | ||
sbillinge marked this conversation as resolved.
Show resolved
Hide resolved
|
||
( # C1: Composition, energy, and mass density provided, expect to get mu based on mass density | ||
# 1. Fully dense mass density | ||
{"sample_composition": "quartz", "energy": 10, "sample_mass_density": 2.65}, | ||
5.0368, | ||
), | ||
( # 2. Measured mass density | ||
{ | ||
"sample_composition": "ZrO2", | ||
"energy": 17.445, | ||
"sample_mass_density": 1.009, | ||
}, | ||
1.2522, | ||
), | ||
( # C2: Composition, energy, and packing fraction provided, expect to get mu based on packing fraction | ||
# Reuse pattern from C1.1 here | ||
{ | ||
"sample_composition": "quartz", | ||
"energy": 10, | ||
"packing_fraction": 0.5, | ||
}, | ||
2.5184, | ||
), | ||
], | ||
) | ||
def test_compute_mu_using_xraydb(inputs, expected_mu): | ||
actual_mu = compute_mu_using_xraydb(**inputs) | ||
assert actual_mu == pytest.approx(expected_mu, rel=1e-6, abs=1e-4) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"inputs", | ||
[ | ||
# Test when the function raises ValueError | ||
# C1: Both mass density and packing fraction are provided | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To compactify we want to write this as follows....also, put what you expect, e.g.,
|
||
( | ||
{ | ||
"sample_composition": "quartz", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "quartz" is not a omposition, so we don't want this in the tests. It doesn't matter for the test, but let's not confuse readers. Please also fix this below. You can use SiO2 instead. Also, please can you move the # C2 down a line inside the paren as I tried to illustrate before. thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah gotcha. Will fix that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooops there're conflicts - willl fix it |
||
"energy": 10, | ||
"sample_mass_density": 2.65, | ||
"packing_fraction": 1, | ||
} | ||
), | ||
# C2: None of mass density or packing fraction are provided | ||
( | ||
{ | ||
"sample_composition": "quartz", | ||
"energy": 10, | ||
} | ||
), | ||
], | ||
sbillinge marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
def test_compute_mu_using_xraydb_bad(inputs): | ||
with pytest.raises( | ||
ValueError, | ||
match="You must specify either sample_mass_density or packing_fraction, but not both. " | ||
"Please rerun specifying only one.", | ||
): | ||
compute_mu_using_xraydb(**inputs) |
Uh oh!
There was an error while loading. Please reload this page.