Skip to content

Commit 169ac1e

Browse files
committed
copy over pdf test
1 parent e8485b9 commit 169ac1e

File tree

6 files changed

+16794
-13
lines changed

6 files changed

+16794
-13
lines changed

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
1-
import json
2-
from pathlib import Path
1+
import importlib.resources
2+
import logging
3+
from functools import lru_cache
34

45
import pytest
56

7+
logger = logging.getLogger(__name__)
68

7-
@pytest.fixture
8-
def user_filesystem(tmp_path):
9-
base_dir = Path(tmp_path)
10-
home_dir = base_dir / "home_dir"
11-
home_dir.mkdir(parents=True, exist_ok=True)
12-
cwd_dir = base_dir / "cwd_dir"
13-
cwd_dir.mkdir(parents=True, exist_ok=True)
149

15-
home_config_data = {"username": "home_username", "email": "[email protected]"}
16-
with open(home_dir / "diffpyconfig.json", "w") as f:
17-
json.dump(home_config_data, f)
10+
# diffpy.structure
11+
@lru_cache()
12+
def has_diffpy_structure():
13+
try:
14+
import diffpy.structure as m
1815

19-
yield tmp_path
16+
del m
17+
return True
18+
except ImportError:
19+
logger.warning(
20+
"Cannot import diffpy.structure, Structure tests skipped."
21+
)
22+
return False
23+
24+
25+
# diffpy.srreal
26+
27+
28+
@lru_cache()
29+
def has_diffpy_srreal():
30+
try:
31+
import diffpy.srreal.pdfcalculator as m
32+
33+
del m
34+
return True
35+
except ImportError:
36+
logger.warning("Cannot import diffpy.srreal, PDF tests skipped.")
37+
return False
38+
39+
40+
@pytest.fixture(scope="session")
41+
def diffpy_structure_available():
42+
return has_diffpy_structure()
43+
44+
45+
@pytest.fixture(scope="session")
46+
def diffpy_srreal_available():
47+
return has_diffpy_srreal()
48+
49+
50+
@pytest.fixture(scope="session")
51+
def datafile():
52+
"""Fixture to load a test data file from the testdata package
53+
directory."""
54+
55+
def _datafile(filename):
56+
return importlib.resources.files("tests.testdata").joinpath(filename)
57+
58+
return _datafile

tests/test_pdf.py

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
#!/usr/bin/env python
2+
##############################################################################
3+
#
4+
# (c) 2025 Simon Billinge.
5+
# All rights reserved.
6+
#
7+
# File coded by: Caden Myers, Simon Billinge, and members of the Billinge
8+
# group.
9+
#
10+
# See GitHub contributions for a more detailed list of contributors.
11+
# https://github.com/diffpy/diffpy.cmipdf/graphs/contributors
12+
#
13+
# See LICENSE.rst for license information.
14+
#
15+
##############################################################################
16+
"""Tests for pdf package."""
17+
18+
import io
19+
import pickle
20+
import unittest
21+
from itertools import chain
22+
23+
import numpy
24+
import pytest
25+
26+
from diffpy.cmipdf import PDFContribution, PDFGenerator, PDFParser
27+
from diffpy.srfit.exceptions import SrFitError
28+
29+
# ----------------------------------------------------------------------------
30+
31+
32+
def testParser1(datafile):
33+
data = datafile("ni-q27r100-neutron.gr")
34+
parser = PDFParser()
35+
parser.parseFile(data)
36+
37+
meta = parser._meta
38+
39+
assert data == meta["filename"]
40+
assert 1 == meta["nbanks"]
41+
assert "N" == meta["stype"]
42+
assert 27 == meta["qmax"]
43+
assert 300 == meta.get("temperature")
44+
assert meta.get("qdamp") is None
45+
assert meta.get("qbroad") is None
46+
assert meta.get("spdiameter") is None
47+
assert meta.get("scale") is None
48+
assert meta.get("doping") is None
49+
50+
x, y, dx, dy = parser.getData()
51+
assert dx is None
52+
assert dy is None
53+
54+
testx = numpy.linspace(0.01, 100, 10000)
55+
diff = testx - x
56+
res = numpy.dot(diff, diff)
57+
assert 0 == pytest.approx(res)
58+
59+
testy = numpy.array(
60+
[
61+
1.144,
62+
2.258,
63+
3.312,
64+
4.279,
65+
5.135,
66+
5.862,
67+
6.445,
68+
6.875,
69+
7.150,
70+
7.272,
71+
]
72+
)
73+
diff = testy - y[:10]
74+
res = numpy.dot(diff, diff)
75+
assert 0 == pytest.approx(res)
76+
77+
return
78+
79+
80+
def testParser2(datafile):
81+
data = datafile("si-q27r60-xray.gr")
82+
parser = PDFParser()
83+
parser.parseFile(data)
84+
85+
meta = parser._meta
86+
87+
assert data == meta["filename"]
88+
assert 1 == meta["nbanks"]
89+
assert "X" == meta["stype"]
90+
assert 27 == meta["qmax"]
91+
assert 300 == meta.get("temperature")
92+
assert meta.get("qdamp") is None
93+
assert meta.get("qbroad") is None
94+
assert meta.get("spdiameter") is None
95+
assert meta.get("scale") is None
96+
assert meta.get("doping") is None
97+
98+
x, y, dx, dy = parser.getData()
99+
testx = numpy.linspace(0.01, 60, 5999, endpoint=False)
100+
diff = testx - x
101+
res = numpy.dot(diff, diff)
102+
assert 0 == pytest.approx(res)
103+
104+
testy = numpy.array(
105+
[
106+
0.1105784,
107+
0.2199684,
108+
0.3270088,
109+
0.4305913,
110+
0.5296853,
111+
0.6233606,
112+
0.7108060,
113+
0.7913456,
114+
0.8644501,
115+
0.9297440,
116+
]
117+
)
118+
diff = testy - y[:10]
119+
res = numpy.dot(diff, diff)
120+
assert 0 == pytest.approx(res)
121+
122+
testdy = numpy.array(
123+
[
124+
0.001802192,
125+
0.003521449,
126+
0.005079115,
127+
0.006404892,
128+
0.007440527,
129+
0.008142955,
130+
0.008486813,
131+
0.008466340,
132+
0.008096858,
133+
0.007416456,
134+
]
135+
)
136+
diff = testdy - dy[:10]
137+
res = numpy.dot(diff, diff)
138+
assert 0 == pytest.approx(res)
139+
140+
assert dx is None
141+
return
142+
143+
144+
def testGenerator(
145+
diffpy_srreal_available, diffpy_structure_available, datafile
146+
):
147+
if not diffpy_structure_available:
148+
pytest.skip("diffpy.structure package not available")
149+
if not diffpy_srreal_available:
150+
pytest.skip("diffpy.srreal package not available")
151+
152+
from diffpy.srreal.pdfcalculator import PDFCalculator
153+
from diffpy.structure import PDFFitStructure
154+
155+
qmax = 27.0
156+
gen = PDFGenerator()
157+
gen.setScatteringType("N")
158+
assert "N" == gen.getScatteringType()
159+
gen.setQmax(qmax)
160+
assert qmax == pytest.approx(gen.getQmax())
161+
162+
stru = PDFFitStructure()
163+
ciffile = datafile("ni.cif")
164+
cif_path = str(ciffile)
165+
stru.read(cif_path)
166+
for i in range(4):
167+
stru[i].Bisoequiv = 1
168+
gen.setStructure(stru)
169+
170+
calc = gen._calc
171+
# Test parameters
172+
for par in gen.iterPars(recurse=False):
173+
pname = par.name
174+
defval = calc._getDoubleAttr(pname)
175+
assert defval == par.getValue()
176+
# Test setting values
177+
par.setValue(1.0)
178+
assert 1.0 == par.getValue()
179+
par.setValue(defval)
180+
assert defval == par.getValue()
181+
182+
r = numpy.arange(0, 10, 0.1)
183+
y = gen(r)
184+
185+
# Now create a reference PDF. Since the calculator is testing its
186+
# output, we just have to make sure we can calculate from the
187+
# PDFGenerator interface.
188+
189+
calc = PDFCalculator()
190+
calc.rstep = r[1] - r[0]
191+
calc.rmin = r[0]
192+
calc.rmax = r[-1] + 0.5 * calc.rstep
193+
calc.qmax = qmax
194+
calc.setScatteringFactorTableByType("N")
195+
calc.eval(stru)
196+
yref = calc.pdf
197+
198+
diff = y - yref
199+
res = numpy.dot(diff, diff)
200+
assert 0 == pytest.approx(res)
201+
return
202+
203+
204+
def test_setQmin(diffpy_structure_available, diffpy_srreal_available):
205+
"""Verify qmin is propagated to the calculator object."""
206+
if not diffpy_srreal_available:
207+
pytest.skip("diffpy.srreal package not available")
208+
209+
gen = PDFGenerator()
210+
assert 0 == gen.getQmin()
211+
assert 0 == gen._calc.qmin
212+
gen.setQmin(0.93)
213+
assert 0.93 == gen.getQmin()
214+
assert 0.93 == gen._calc.qmin
215+
return
216+
217+
218+
def test_setQmax(diffpy_structure_available, diffpy_srreal_available):
219+
"""Check PDFContribution.setQmax()"""
220+
if not diffpy_structure_available:
221+
pytest.skip("diffpy.structure package not available")
222+
from diffpy.structure import Structure
223+
224+
if not diffpy_srreal_available:
225+
pytest.skip("diffpy.srreal package not available")
226+
227+
pc = PDFContribution("pdf")
228+
pc.setQmax(21)
229+
pc.addStructure("empty", Structure())
230+
assert 21 == pc.empty.getQmax()
231+
pc.setQmax(22)
232+
assert 22 == pc.getQmax()
233+
assert 22 == pc.empty.getQmax()
234+
return
235+
236+
237+
def test_getQmax(diffpy_structure_available, diffpy_srreal_available):
238+
"""Check PDFContribution.getQmax()"""
239+
if not diffpy_structure_available:
240+
pytest.skip("diffpy.structure package not available")
241+
from diffpy.structure import Structure
242+
243+
if not diffpy_srreal_available:
244+
pytest.skip("diffpy.srreal package not available")
245+
246+
# cover all code branches in PDFContribution._getMetaValue
247+
# (1) contribution metadata
248+
pc1 = PDFContribution("pdf")
249+
assert pc1.getQmax() is None
250+
pc1.setQmax(17)
251+
assert 17 == pc1.getQmax()
252+
# (2) contribution metadata
253+
pc2 = PDFContribution("pdf")
254+
pc2.addStructure("empty", Structure())
255+
pc2.empty.setQmax(18)
256+
assert 18 == pc2.getQmax()
257+
# (3) profile metadata
258+
pc3 = PDFContribution("pdf")
259+
pc3.profile.meta["qmax"] = 19
260+
assert 19 == pc3.getQmax()
261+
return
262+
263+
264+
def test_savetxt(
265+
diffpy_structure_available, diffpy_srreal_available, datafile
266+
):
267+
"check PDFContribution.savetxt()"
268+
if not diffpy_structure_available:
269+
pytest.skip("diffpy.structure package not available")
270+
from diffpy.structure import Structure
271+
272+
if not diffpy_srreal_available:
273+
pytest.skip("diffpy.srreal package not available")
274+
275+
pc = PDFContribution("pdf")
276+
pc.loadData(datafile("si-q27r60-xray.gr"))
277+
pc.setCalculationRange(0, 10)
278+
pc.addStructure("empty", Structure())
279+
fp = io.BytesIO()
280+
with pytest.raises(SrFitError):
281+
pc.savetxt(fp)
282+
pc.evaluate()
283+
pc.savetxt(fp)
284+
txt = fp.getvalue().decode()
285+
nlines = len(txt.strip().split("\n"))
286+
assert 1001 == nlines
287+
return
288+
289+
290+
def test_pickling(
291+
diffpy_structure_available, diffpy_srreal_available, datafile
292+
):
293+
"validate PDFContribution.residual() after pickling."
294+
if not diffpy_structure_available:
295+
pytest.skip("diffpy.structure package not available")
296+
from diffpy.structure import loadStructure
297+
298+
if not diffpy_srreal_available:
299+
pytest.skip("diffpy.srreal package not available")
300+
301+
pc = PDFContribution("pdf")
302+
pc.loadData(datafile("ni-q27r100-neutron.gr"))
303+
ciffile = datafile("ni.cif")
304+
cif_path = str(ciffile)
305+
ni = loadStructure(cif_path)
306+
ni.Uisoequiv = 0.003
307+
pc.addStructure("ni", ni)
308+
pc.setCalculationRange(0, 10)
309+
pc2 = pickle.loads(pickle.dumps(pc))
310+
res0 = pc.residual()
311+
assert numpy.array_equal(res0, pc2.residual())
312+
for p in chain(pc.iterPars("Uiso"), pc2.iterPars("Uiso")):
313+
p.value = 0.004
314+
res1 = pc.residual()
315+
assert not numpy.allclose(res0, res1)
316+
assert numpy.array_equal(res1, pc2.residual())
317+
return
318+
319+
320+
if __name__ == "__main__":
321+
unittest.main()

0 commit comments

Comments
 (0)