Skip to content

Commit 435f6ef

Browse files
feat: initial commit on computing density
1 parent a51b553 commit 435f6ef

File tree

4 files changed

+168
-1
lines changed

4 files changed

+168
-1
lines changed

news/compute-density.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* Function that computes theoretical density from a given CIF metadata file.
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/utils/tools.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from pathlib import Path
55

66
import numpy as np
7+
from periodictable import formula
8+
from scipy.constants import Avogadro as AVOGADRO_NUMBER
79
from scipy.optimize import dual_annealing
810
from scipy.signal import convolve
911
from xraydb import material_mu
@@ -190,8 +192,9 @@ def get_package_info(package_names, metadata=None):
190192
Package info stored in metadata as
191193
{'package_info': {'package_name': 'version_number'}}.
192194
195+
Parameters
193196
----------
194-
package_name : str or list
197+
package_names : str or list
195198
The name of the package(s) to retrieve the version number for.
196199
metadata : dict
197200
The dictionary to store the package info. If not provided, a new
@@ -214,6 +217,49 @@ def get_package_info(package_names, metadata=None):
214217
return metadata
215218

216219

220+
def compute_density_from_cif(sample_composition, cif_data):
221+
"""Compute the theoretical density from given CIF metadata.
222+
223+
Parameters
224+
----------
225+
sample_composition : str
226+
The chemical formula of the material, e.g. "NaCl".
227+
cif_data : dict
228+
The dictionary containing CIF metadata,
229+
typically parsed from a JSON file retrieved
230+
from the Crystallography Open Database (COD).
231+
232+
Returns
233+
-------
234+
density : float
235+
The material density in g/cm^3.
236+
"""
237+
molar_mass = formula(sample_composition).mass
238+
a, b, c = (float(cif_data[k]) for k in ("a", "b", "c"))
239+
alpha_deg, beta_deg, gamma_deg = (
240+
float(cif_data[k]) for k in ("alpha", "beta", "gamma")
241+
)
242+
Z = int(float(cif_data["Z"]))
243+
alpha_rad, beta_rad, gamma_rad = map(
244+
np.radians, [alpha_deg, beta_deg, gamma_deg]
245+
)
246+
volume = (
247+
a
248+
* b
249+
* c
250+
* np.sqrt(
251+
1
252+
- np.cos(alpha_rad) ** 2
253+
- np.cos(beta_rad) ** 2
254+
- np.cos(gamma_rad) ** 2
255+
+ 2 * np.cos(alpha_rad) * np.cos(beta_rad) * np.cos(gamma_rad)
256+
)
257+
)
258+
volume_cm3 = volume * 1e-24
259+
density = (Z * molar_mass) / (volume_cm3 * AVOGADRO_NUMBER)
260+
return density
261+
262+
217263
def get_density_from_cloud(sample_composition, mp_token=""):
218264
"""Function to get material density from the MP or COD database.
219265

tests/test_tools.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from diffpy.utils.tools import (
1010
_extend_z_and_convolve,
1111
check_and_build_global_config,
12+
compute_density_from_cif,
1213
compute_mu_using_xraydb,
1314
compute_mud,
1415
get_package_info,
@@ -270,6 +271,28 @@ def test_get_package_info(monkeypatch, inputs, expected):
270271
assert actual_metadata == expected
271272

272273

274+
@pytest.mark.parametrize(
275+
"inputs, expected_density",
276+
[
277+
(
278+
{
279+
"sample_composition": "NaCl",
280+
"cif_data_filename": "cif_data.json",
281+
},
282+
2.187,
283+
),
284+
],
285+
)
286+
def test_compute_density_from_cif(inputs, expected_density):
287+
path = Path("testdata") / inputs["cif_data_filename"]
288+
with open(path) as f:
289+
cif_data = json.load(f)
290+
actual_density = compute_density_from_cif(
291+
inputs["sample_composition"], cif_data
292+
)
293+
assert actual_density == pytest.approx(expected_density, rel=0.01, abs=0.1)
294+
295+
273296
@pytest.mark.parametrize(
274297
"inputs",
275298
[

tests/testdata/cif_data.json

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"file": "1000041",
3+
"a": "5.62",
4+
"siga": null,
5+
"b": "5.62",
6+
"sigb": null,
7+
"c": "5.62",
8+
"sigc": null,
9+
"alpha": "90",
10+
"sigalpha": null,
11+
"beta": "90",
12+
"sigbeta": null,
13+
"gamma": "90",
14+
"siggamma": null,
15+
"vol": "177.5",
16+
"sigvol": null,
17+
"celltemp": null,
18+
"sigcelltemp": null,
19+
"diffrtemp": null,
20+
"sigdiffrtemp": null,
21+
"cellpressure": null,
22+
"sigcellpressure": null,
23+
"diffrpressure": null,
24+
"sigdiffrpressure": null,
25+
"thermalhist": null,
26+
"pressurehist": null,
27+
"compoundsource": null,
28+
"nel": "2",
29+
"sg": "F m -3 m",
30+
"sgHall": "-F 4 2 3",
31+
"sgNumber": "225",
32+
"commonname": null,
33+
"chemname": "Sodium chloride",
34+
"mineral": null,
35+
"formula": "- Cl Na -",
36+
"calcformula": "- Cl Na -",
37+
"cellformula": "- Cl4 Na4 -",
38+
"Z": "4",
39+
"Zprime": "0.0208333",
40+
"acce_code": null,
41+
"authors": "Abrahams, S C; Bernstein, J L",
42+
"title": "Accuracy of an automatic diffractometer. measurement of the sodium chloride structure factors",
43+
"journal": "Acta Crystallographica (1,1948-23,1967)",
44+
"year": "1965",
45+
"volume": "18",
46+
"issue": null,
47+
"firstpage": "926",
48+
"lastpage": "932",
49+
"doi": "10.1107/S0365110X65002244",
50+
"method": null,
51+
"radiation": null,
52+
"wavelength": null,
53+
"radType": null,
54+
"radSymbol": null,
55+
"Rall": "0.022",
56+
"Robs": null,
57+
"Rref": null,
58+
"wRall": null,
59+
"wRobs": null,
60+
"wRref": null,
61+
"RFsqd": null,
62+
"RI": null,
63+
"gofall": null,
64+
"gofobs": null,
65+
"gofgt": null,
66+
"gofref": null,
67+
"duplicateof": null,
68+
"optimal": null,
69+
"status": null,
70+
"flags": "has coordinates",
71+
"svnrevision": "130149",
72+
"date": "2020-10-21",
73+
"time": "18:00:00",
74+
"onhold": null
75+
}

0 commit comments

Comments
 (0)