Skip to content

Commit 69ca523

Browse files
committed
DiffractionDataSingleCrystal: add SetHklIobs, SetIobs, SetSigma, GetSigma, GetChi2, FitScaleFactorForRw and FitScaleFactorForR. Add a unittest.
Fixes #42
1 parent d8fe909 commit 69ca523

File tree

2 files changed

+146
-2
lines changed

2 files changed

+146
-2
lines changed

src/extensions/diffractiondatasinglecrystal_ext.cpp

+85-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@
1313
******************************************************************************
1414
*
1515
* boost::python bindings to ObjCryst::DiffractionDataSingleCrystal.
16-
*
16+
* Changes from ObjCryst::DiffractionDataSingleCrystal
17+
* - SetHklIobs takes float(64) H, K, L arrays rather than long integers - easier
18+
* because passing numpy int array seesm complicated, and more practical anyway
19+
* since GetH() GetK() GetL() functions from ScatteringData natively use floats.
1720
*****************************************************************************/
1821

1922
#include <boost/python/class.hpp>
2023
#include <boost/python/def.hpp>
2124
#include <boost/python/copy_const_reference.hpp>
2225
#include <boost/python/manage_new_object.hpp>
26+
#include <boost/python/stl_iterator.hpp>
2327
#undef B0
2428

2529
#include <string>
@@ -67,6 +71,71 @@ DiffractionDataSingleCrystal* _CreateSingleCrystalDataFromCIF(bp::object input,
6771
return d;
6872
}
6973

74+
void setdiffractiondatasinglecrystal_iobs(DiffractionDataSingleCrystal& diff, bp::object iobs)
75+
{
76+
CrystVector_REAL iiobs;
77+
assignCrystVector(iiobs, iobs);
78+
if(iiobs.size() != diff.GetIobs().size())
79+
throw ObjCryst::ObjCrystException("DiffractionDataSingleCrystal::SetIobs(): "
80+
"number of elements does not match the previous Iobs list. "
81+
"Use SetHklIobs if you want to change the number of reflections.");
82+
MuteObjCrystUserInfo muzzle;
83+
diff.SetIobs(iiobs);
84+
}
85+
86+
void setdiffractiondatasinglecrystal_sigma(DiffractionDataSingleCrystal& diff, bp::object sigma)
87+
{
88+
CrystVector_REAL ssigma;
89+
assignCrystVector(ssigma, sigma);
90+
if(ssigma.size() != diff.GetIobs().size())
91+
throw ObjCryst::ObjCrystException("DiffractionDataSingleCrystal::SetSigma(): "
92+
"number of elements does not match the Iobs list. "
93+
"Use SetHklIobs if you want to change the number of reflections.");
94+
MuteObjCrystUserInfo muzzle;
95+
diff.SetSigma(ssigma);
96+
}
97+
98+
// TODO: For SetHklIobs we should pass directly an integer array but that seems difficult-passed numpy arrays
99+
// are always interpreted as doubles (?). It's more practical this way.
100+
void assignCrystVector(CrystVector<long>& cv, bp::object obj)
101+
{
102+
bp::stl_input_iterator<double> begin(obj), end;
103+
std::list<double> values(begin, end);
104+
cv.resize(values.size());
105+
std::list<double>::const_iterator vv = values.begin();
106+
long* dst = cv.data();
107+
for (; vv != values.end(); ++vv, ++dst) *dst = lround(*vv);
108+
}
109+
110+
void setdiffractiondatasinglecrystal_hkliobs(DiffractionDataSingleCrystal& diff,
111+
bp::object h,bp::object k, bp::object l,
112+
bp::object iobs, bp::object sigma)
113+
{
114+
CrystVector<long> hh;
115+
assignCrystVector(hh, h);
116+
CrystVector<long> kk;
117+
assignCrystVector(kk, k);
118+
CrystVector<long> ll;
119+
assignCrystVector(ll, l);
120+
CrystVector_REAL iiobs;
121+
assignCrystVector(iiobs, iobs);
122+
CrystVector_REAL ssigma;
123+
assignCrystVector(ssigma, sigma);
124+
125+
if(hh.size() != kk.size())
126+
throw ObjCryst::ObjCrystException("DiffractionDataSingleCrystal::SetHklIobs(): h and k array sizes differ");
127+
if(hh.size() != ll.size())
128+
throw ObjCryst::ObjCrystException("DiffractionDataSingleCrystal::SetHklIobs(): h and l array sizes differ");
129+
if(hh.size() != iiobs.size())
130+
throw ObjCryst::ObjCrystException("DiffractionDataSingleCrystal::SetHklIobs(): h and iobs array sizes differ");
131+
if(hh.size() != ssigma.size())
132+
throw ObjCryst::ObjCrystException("DiffractionDataSingleCrystal::SetHklIobs(): h and sigma array sizes differ");
133+
134+
MuteObjCrystUserInfo muzzle;
135+
diff.SetHklIobs(hh, kk, ll, iiobs, ssigma);
136+
}
137+
138+
70139
} // namespace
71140

72141
void wrap_diffractiondatasinglecrystal()
@@ -83,10 +152,24 @@ void wrap_diffractiondatasinglecrystal()
83152
return_value_policy<copy_const_reference>())
84153
.def("GetIobs", &DiffractionDataSingleCrystal::GetIobs,
85154
return_value_policy<copy_const_reference>())
86-
// FIXME ... add SetIobs, SetSigma ....
155+
.def("GetSigma", &DiffractionDataSingleCrystal::GetSigma,
156+
return_value_policy<copy_const_reference>())
157+
.def("SetIobs",
158+
&setdiffractiondatasinglecrystal_iobs,
159+
bp::arg("iobs"))
160+
.def("SetSigma",
161+
&setdiffractiondatasinglecrystal_sigma,
162+
bp::arg("sigma"))
163+
.def("SetHklIobs",
164+
&setdiffractiondatasinglecrystal_hkliobs,
165+
(bp::arg("h"),bp::arg("k"),bp::arg("l"), bp::arg("iobs"), bp::arg("sigma")))
87166
.def("SetIobsToIcalc", &DiffractionDataSingleCrystal::SetIobsToIcalc)
88167
.def("GetRw", &DiffractionDataSingleCrystal::GetRw)
89168
.def("GetR", &DiffractionDataSingleCrystal::GetR)
169+
.def("GetChi2", &DiffractionDataSingleCrystal::GetChi2)
170+
.def("FitScaleFactorForRw", &DiffractionDataSingleCrystal::FitScaleFactorForRw)
171+
.def("FitScaleFactorForR", &DiffractionDataSingleCrystal::FitScaleFactorForR)
172+
// TODO: These functions should print a limited number of reflections - problems otherwise
90173
.def("PrintObsData", &DiffractionDataSingleCrystal::PrintObsData)
91174
.def("PrintObsCalcData", &DiffractionDataSingleCrystal::PrintObsCalcData)
92175
.def("SetUseOnlyLowAngleData", &DiffractionDataSingleCrystal::SetUseOnlyLowAngleData)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python
2+
3+
"""Tests for diffractiondatasinglecrystal module."""
4+
5+
import unittest
6+
import gc
7+
import numpy as np
8+
9+
from pyobjcryst.crystal import CreateCrystalFromCIF, Crystal
10+
from pyobjcryst.diffractiondatasinglecrystal import *
11+
from pyobjcryst.tests.pyobjcrysttestutils import loadcifdata, datafile
12+
13+
14+
class test_single_crystal_data(unittest.TestCase):
15+
16+
def test_create(self):
17+
"""Test creating a DiffractionDataSingleCrystal object"""
18+
c = Crystal(3.52, 3.52, 3.52, "225")
19+
d = DiffractionDataSingleCrystal(c)
20+
21+
def test_create_set_hkliobs(self):
22+
"""test SetHklIobs, SetIobs and SetSigma"""
23+
c = Crystal(3.1, 3.2, 3.3, "Pmmm")
24+
d = DiffractionDataSingleCrystal(c)
25+
n0 = 5
26+
nb = n0 ** 3
27+
r = np.arange(1, nb + 1, dtype=np.float64)
28+
h = r % n0
29+
l = r // n0 ** 2
30+
k = (r - l * n0 ** 2) // n0
31+
iobs = np.random.uniform(0, 100, nb)
32+
sigma = np.sqrt(iobs)
33+
34+
d.SetHklIobs(h, k, l, iobs, sigma)
35+
36+
# SetHklIobs sorts reflecions by sin(theta)/lambda, so do the same for comparison
37+
s = np.sqrt(h ** 2 / 3.1 ** 2 + k ** 2 / 3.2 ** 2 + l ** 2 / 3.3 ** 2) / 2
38+
idx = np.argsort(s)
39+
40+
iobs = np.take(iobs, idx)
41+
sigma = np.take(sigma, idx)
42+
h = np.take(h, idx)
43+
k = np.take(k, idx)
44+
l = np.take(l, idx)
45+
self.assertTrue(np.all(iobs == d.GetIobs()))
46+
self.assertTrue(np.all(sigma == d.GetSigma()))
47+
self.assertTrue(np.all(h == d.GetH()))
48+
self.assertTrue(np.all(k == d.GetK()))
49+
self.assertTrue(np.all(l == d.GetL()))
50+
51+
# Set Iobs and sigma individually
52+
iobs = np.random.uniform(0, 100, nb)
53+
d.SetIobs(iobs)
54+
self.assertTrue(np.all(iobs == d.GetIobs()))
55+
56+
sigma = np.random.uniform(0, 10, nb)
57+
d.SetSigma(sigma)
58+
self.assertTrue(np.all(sigma == d.GetSigma()))
59+
60+
if __name__ == "__main__":
61+
unittest.main()

0 commit comments

Comments
 (0)