Skip to content

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

Merged
merged 15 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions news/mu.rst
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>
1 change: 1 addition & 0 deletions requirements/conda.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
numpy
xraydb
1 change: 1 addition & 0 deletions requirements/pip.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
numpy
xraydb
30 changes: 30 additions & 0 deletions src/diffpy/utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from copy import copy
from pathlib import Path

from xraydb import material_mu


def _stringify(obj):
"""
Expand Down Expand Up @@ -131,3 +133,31 @@ 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, density=None, packing_fraction=1):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added packing fraction. does this make sense or should we use None instead? I think if we set the default to 1 the code can be more concise

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think setting to None is clearer here. The logic from the perspective of the user is that we either have a packing fraction or we don't. If it is None, you can set it to 1 in the code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, didn't we discuss to change the name to sample_mass_density?

"""Compute the attenuation coefficient (mu) using the XrayDB database.

This function calculates mu based on the sample composition and energy.
If density is not provided, a standard reference density (e.g., 0.987 g/cm^3 for H2O) is used.
User can provide either a measured density or an estimated packing fraction (with a standard density).
It is recommended to specify the density, especially for materials like ZrO2, where it can vary.
Reference: https://xraypy.github.io/XrayDB/python.html#xraydb.material_mu

Parameters
----------
sample_composition: str
The chemical formula or the name of the material
energy: float
The energy in eV
density: float, optional, Default is None
The mass density of the packed powder/sample in gr/cm^3. If None, a standard density from XrayDB is used.
packing_fraction: float, optional, Default is 1
The fraction of sample in the capillary (between 0 and 1)

Returns
-------
the attenuation coefficient mu in mm^{-1}
"""
mu = material_mu(sample_composition, energy, density=density, kind="total") * packing_fraction / 10
return mu
26 changes: 25 additions & 1 deletion tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import pytest

from diffpy.utils.tools import get_package_info, get_user_info
from diffpy.utils.tools import compute_mu_using_xraydb, get_package_info, get_user_info

# def _setup_dirs(monkeypatch, user_filesystem):
# home_dir, cwd_dir = user_filesystem.home_dir, user_filesystem.cwd_dir
Expand Down Expand Up @@ -189,3 +189,27 @@ def test_get_package_info(monkeypatch, inputs, expected):
)
actual_metadata = get_package_info(inputs[0], metadata=inputs[1])
assert actual_metadata == expected


params_mu = [
Copy link
Contributor

@bobleesj bobleesj Dec 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please follow the guides provided here? https://gitlab.thebillingegroup.com/resources/group-wiki/-/wikis/Group-standards-for-pytest-and-docstrings

add higher level function purpose, use descriptive yet concise variable names, no need to create extra variable of params_mu, no need to add "user specific, etc."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yucongalicechen

@sbillinge if you have also further suggestions to the gitlab doc, please suggest or modify directly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gitlab doc looks pretty good to me. Lot's of great examples to work from.

# C1: user didn't specify density or packing fraction
({"sample_composition": "H2O", "energy": 10000, "density": None, "packing_fraction": 1}, 0.5330),
# C2: user specified packing fraction only
({"sample_composition": "H2O", "energy": 10000, "density": None, "packing_fraction": 0.5}, 0.2665),
# C3: user specified density only
({"sample_composition": "H2O", "energy": 10000, "density": 0.997, "packing_fraction": 1}, 0.5330),
({"sample_composition": "H2O", "energy": 10000, "density": 0.4985, "packing_fraction": 1}, 0.2665),
# C4: user specified a standard density and a packing fraction
({"sample_composition": "H2O", "energy": 10000, "density": 0.997, "packing_fraction": 0.5}, 0.2665),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added test cases with description. I didn't include the bad cases where the user does not specify a density and xraydb doesn't know the density (this is taken care by xraydb)

]


@pytest.mark.parametrize("inputs, expected", params_mu)
def test_compute_mu_using_xraydb(inputs, expected):
actual_mu = compute_mu_using_xraydb(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you put

def test_compute_mu_using_xraydb(inputs, expected_mu):
    actual_mu = compute_mu_using_xraydb(**inputs)

which will unpack the input dict. You may have to handle the required args differently, but you can then have input_args and input_kwargs in your paramatrize and unpack them differently.

inputs["sample_composition"],
inputs["energy"],
density=inputs["density"],
packing_fraction=inputs["packing_fraction"],
)
assert actual_mu == pytest.approx(expected, rel=0.01, abs=0.1)
Loading