diff --git a/news/getmud.rst b/news/getmud.rst new file mode 100644 index 0000000..ff5330c --- /dev/null +++ b/news/getmud.rst @@ -0,0 +1,23 @@ +**Added:** + +* Functionalities to estimate sample mass density or capillary diameter from target muD and related chemical info. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/labpdfproc/getmud.py b/src/diffpy/labpdfproc/getmud.py new file mode 100644 index 0000000..09956ca --- /dev/null +++ b/src/diffpy/labpdfproc/getmud.py @@ -0,0 +1,75 @@ +import numpy as np +from scipy.optimize import newton + +from diffpy.utils.tools import compute_mu_using_xraydb + + +def estimate_mass_density(mud, diameter, sample_composition, energy): + """Estimate sample mass density (g/cm^3) from + muD, capillary diameter, sample composition, and energy. + + Parameters + ---------- + mud : float + The given product of attenuation coefficient mu + in mm^{-1} and capillary diameter in mm. + diameter : float + The given diameter of the sample capillary in mm. + sample_composition : str + The chemical formula of the material. + energy : float + The energy of the incident x-rays in keV. + + Returns + ------- + estimated_density : float + The estimated mass density of the packed powder/sample in g/cm^3. + """ + mu = mud / diameter + + def residual_density(density): + return np.abs( + compute_mu_using_xraydb( + sample_composition, energy, sample_mass_density=density + ) + - mu + ) + + estimated_density = newton(residual_density, x0=1.0, x1=10.0) + return estimated_density + + +def estimate_diameter( + mud, + sample_composition, + energy, + sample_mass_density=None, + packing_fraction=None, +): + """Estimate capillary diameter (mm) from + muD, sample composition, energy, and mass density/packing fraction. + + Parameters + ---------- + mud : float + The given product of attenuation coefficient mu + in mm^{-1} and capillary diameter in mm. + sample_composition : str + The chemical formula of the material. + energy : float + The energy of the incident x-rays in keV. + sample_mass_density : float + The mass density of the packed powder/sample in g/cm^3. + packing_fraction : float, optional, Default is None + The fraction of sample in the capillary (between 0 and 1). + Specify either sample_mass_density or packing_fraction but not both. + + Returns + ------- + diameter : float + The given diameter of the sample capillary in mm. + """ + mu = compute_mu_using_xraydb( + sample_composition, energy, sample_mass_density, packing_fraction + ) + return mud / mu diff --git a/tests/test_getmud.py b/tests/test_getmud.py new file mode 100644 index 0000000..151e725 --- /dev/null +++ b/tests/test_getmud.py @@ -0,0 +1,59 @@ +import pytest + +from diffpy.labpdfproc.getmud import estimate_diameter, estimate_mass_density + + +@pytest.mark.parametrize( + "inputs, expected_mass_density", + [ + ( + { + "mud": 2.0, + "diameter": 1.5, + "sample_composition": "ZrO2", + "energy": 17.45, # Mo K_alpha source + }, + 1.0751, + ), + ], +) +def test_estimate_mass_density(inputs, expected_mass_density): + actual_mass_density = estimate_mass_density( + inputs["mud"], + inputs["diameter"], + inputs["sample_composition"], + inputs["energy"], + ) + assert actual_mass_density == pytest.approx( + expected_mass_density, rel=0.01, abs=0.1 + ) + + +@pytest.mark.parametrize( + "inputs, expected_diameter", + [ + ( # C1: user specifies a sample mass density + { + "mud": 2.0, + "sample_composition": "ZrO2", + "energy": 17.45, + "sample_mass_density": 1.20, + }, + 1.3439, + ), + # ( # C2: user specifies a packing fraction + # { + # "mud": 2.0, + # "sample_composition": "ZrO2", + # "energy": 17.45, + # "packing_fraction": 0.3 + # }, + # 1.5 + # ), + ], +) +def test_estimate_diameter(inputs, expected_diameter): + actual_diameter = estimate_diameter(**inputs) + assert actual_diameter == pytest.approx( + expected_diameter, rel=0.01, abs=0.1 + )