orGUI 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orGUI-1.0.1.dist-info/LICENSE +21 -0
- orGUI-1.0.1.dist-info/METADATA +147 -0
- orGUI-1.0.1.dist-info/RECORD +83 -0
- orGUI-1.0.1.dist-info/WHEEL +5 -0
- orGUI-1.0.1.dist-info/entry_points.txt +2 -0
- orGUI-1.0.1.dist-info/top_level.txt +1 -0
- orgui/__init__.py +36 -0
- orgui/app/ArrayTableDialog.py +433 -0
- orgui/app/QReflectionSelector.py +538 -0
- orgui/app/QScanSelector.py +692 -0
- orgui/app/QUBCalculator.py +1210 -0
- orgui/app/__init__.py +36 -0
- orgui/app/database.py +487 -0
- orgui/app/orGUI.py +2613 -0
- orgui/app/qutils.py +51 -0
- orgui/backend/__init__.py +32 -0
- orgui/backend/backends.py +157 -0
- orgui/backend/beamline/ID31DiffractLinTilt.py +77 -0
- orgui/backend/beamline/P212_tools.py +577 -0
- orgui/backend/beamline/__init__.py +36 -0
- orgui/backend/beamline/fio_reader.py +110 -0
- orgui/backend/beamline/id31_tools.py +651 -0
- orgui/backend/scans.py +95 -0
- orgui/backend/udefaults.py +163 -0
- orgui/backend/universalScanLoader.py +105 -0
- orgui/datautils/__init__.py +32 -0
- orgui/datautils/util.py +705 -0
- orgui/datautils/xrayutils/CTRcalc.py +3022 -0
- orgui/datautils/xrayutils/CTRopt.py +623 -0
- orgui/datautils/xrayutils/CTRplotutil.py +904 -0
- orgui/datautils/xrayutils/DetectorCalibration.py +685 -0
- orgui/datautils/xrayutils/HKLVlieg.py +1360 -0
- orgui/datautils/xrayutils/ReciprocalNavigation.py +401 -0
- orgui/datautils/xrayutils/_CTRcalc_accel.py +181 -0
- orgui/datautils/xrayutils/__init__.py +46 -0
- orgui/datautils/xrayutils/element_data.py +213 -0
- orgui/datautils/xrayutils/test/__init__.py +57 -0
- orgui/datautils/xrayutils/test/test_CTRcalc.py +152 -0
- orgui/datautils/xrayutils/test/test_DetectorCalibration.py +336 -0
- orgui/datautils/xrayutils/test/test_HKLcalc.py +88 -0
- orgui/datautils/xrayutils/unitcells/Fe3O4(100).bul +59 -0
- orgui/datautils/xrayutils/unitcells/Pt100.bul +7 -0
- orgui/datautils/xrayutils/unitcells/Pt100_small.bul +5 -0
- orgui/datautils/xrayutils/unitcells/Pt110.bul +5 -0
- orgui/datautils/xrayutils/unitcells/Pt111.bul +6 -0
- orgui/datautils/xrayutils/unitcells/Pt310.bul +13 -0
- orgui/datautils/xrayutils/unitcells/Pt3O4(100).bul +19 -0
- orgui/datautils/xrayutils/unitcells/PtO(001).bul +9 -0
- orgui/datautils/xrayutils/unitcells/PtO(010).bul +9 -0
- orgui/datautils/xrayutils/unitcells/PtO(100).bul +9 -0
- orgui/datautils/xrayutils/unitcells/__init__.py +67 -0
- orgui/datautils/xrayutils/unitcells/a-PtO2(0001).bul +6 -0
- orgui/main.py +101 -0
- orgui/resources/__init__.py +40 -0
- orgui/resources/icons/alpha.png +0 -0
- orgui/resources/icons/alpha.svg +67 -0
- orgui/resources/icons/diffractometer_v3.png +0 -0
- orgui/resources/icons/disable-image.png +0 -0
- orgui/resources/icons/disable-image.svg +68 -0
- orgui/resources/icons/document-nx-open.png +0 -0
- orgui/resources/icons/document-nx-open.svg +152 -0
- orgui/resources/icons/document-nx-save.png +0 -0
- orgui/resources/icons/document-nx-save.svg +73 -0
- orgui/resources/icons/logo.png +0 -0
- orgui/resources/icons/logo.svg +808 -0
- orgui/resources/icons/max_image.png +0 -0
- orgui/resources/icons/max_image.svg +77 -0
- orgui/resources/icons/max_image2.png +0 -0
- orgui/resources/icons/max_image2.svg +83 -0
- orgui/resources/icons/search-image.png +0 -0
- orgui/resources/icons/search-image.svg +94 -0
- orgui/resources/icons/search-reflection.png +0 -0
- orgui/resources/icons/search-reflection.svg +126 -0
- orgui/resources/icons/search.png +0 -0
- orgui/resources/icons/search.svg +91 -0
- orgui/resources/icons/select-image.png +0 -0
- orgui/resources/icons/select-image.svg +60 -0
- orgui/resources/icons/set-reflection.png +0 -0
- orgui/resources/icons/set-reflection.svg +91 -0
- orgui/resources/icons/sum_image.png +0 -0
- orgui/resources/icons/sum_image.svg +63 -0
- orgui/resources/icons/sum_image2.png +0 -0
- orgui/resources/icons/sum_image2.svg +75 -0
orgui/datautils/util.py
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# /*##########################################################################
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2020-2024 Timo Fuchs
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
|
14
|
+
# all copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
# THE SOFTWARE.
|
|
23
|
+
#
|
|
24
|
+
# ###########################################################################*/
|
|
25
|
+
__author__ = "Timo Fuchs"
|
|
26
|
+
__copyright__ = "Copyright 2020-2024 Timo Fuchs"
|
|
27
|
+
__license__ = "MIT License"
|
|
28
|
+
__version__ = "1.0.0"
|
|
29
|
+
__maintainer__ = "Timo Fuchs"
|
|
30
|
+
__email__ = "fuchs@physik.uni-kiel.de"
|
|
31
|
+
|
|
32
|
+
import numpy as np
|
|
33
|
+
import numpy.linalg as LA
|
|
34
|
+
from scipy.linalg import svd
|
|
35
|
+
import scipy.optimize as opt
|
|
36
|
+
from scipy import special
|
|
37
|
+
from scipy.optimize._numdiff import approx_derivative
|
|
38
|
+
from scipy.stats.mstats import gmean
|
|
39
|
+
from scipy.spatial.transform import Rotation
|
|
40
|
+
|
|
41
|
+
from .xrayutils import HKLVlieg
|
|
42
|
+
import warnings
|
|
43
|
+
|
|
44
|
+
# numdifftools sets TLS to static?
|
|
45
|
+
# causes OSError while hdf5 read
|
|
46
|
+
#import numdifftools as nd
|
|
47
|
+
from math import *
|
|
48
|
+
import os
|
|
49
|
+
import copy
|
|
50
|
+
import matplotlib.pyplot as plt
|
|
51
|
+
from matplotlib import colors as colors
|
|
52
|
+
import configparser
|
|
53
|
+
try:
|
|
54
|
+
from StringIO import StringIO
|
|
55
|
+
except ImportError:
|
|
56
|
+
from io import StringIO
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def atomlist_to_xyzfile(filename,atomlist):
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
filename : str
|
|
66
|
+
file path, .xyz will not be appended.
|
|
67
|
+
atomlist : structured array
|
|
68
|
+
fields: [name, x ,y ,z]
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
None.
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
with open(filename,'w') as f:
|
|
77
|
+
noatoms = atomlist.shape[0]
|
|
78
|
+
f.write("{}\n".format(noatoms))
|
|
79
|
+
f.write(" \n")
|
|
80
|
+
np.savetxt(f,atomlist,fmt=['%s','%.6f','%.6f','%.6f'])
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def formatMeasurement_LatexFloat(x,dx,pad=1,concise=False):
|
|
85
|
+
roundedx, roundederr, prec = roundMeasurement(x,dx)
|
|
86
|
+
if prec < 0:
|
|
87
|
+
prec = 0
|
|
88
|
+
|
|
89
|
+
if concise:
|
|
90
|
+
valstr = "${:{pad}.{prec}f}".format(float(roundedx),prec=prec,pad=pad)
|
|
91
|
+
errstr = "({:d})$".format(int(roundederr*10**prec))
|
|
92
|
+
|
|
93
|
+
else:
|
|
94
|
+
valstr = r"${:{pad}.{prec}f} \pm ".format(float(roundedx),prec=prec,pad=pad)
|
|
95
|
+
errstr = r"{:{pad}.{prec}f}$".format(float(roundederr),prec=prec,pad=pad)
|
|
96
|
+
|
|
97
|
+
return valstr + errstr
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def roundMeasurement(x,dx, fallbackdigits=4):
|
|
101
|
+
if np.abs(dx / x) < 1e-8:
|
|
102
|
+
warnings.warn("Relative error smaller than 1e-8. Use fallback digits!")
|
|
103
|
+
precision = fallbackdigits
|
|
104
|
+
errprec = fallbackdigits
|
|
105
|
+
roundedx = np.round(x/errprec)*errprec
|
|
106
|
+
roundederr = np.ceil(dx/errprec)*errprec
|
|
107
|
+
return roundedx, roundederr, precision
|
|
108
|
+
|
|
109
|
+
firstdigit = 10**np.floor(np.log10(dx)) #mask first digit != 0
|
|
110
|
+
|
|
111
|
+
smaller3 = np.array(dx < 3*firstdigit,dtype=np.float64)
|
|
112
|
+
|
|
113
|
+
errprec = 10**(np.floor(np.log10(dx)) - smaller3)
|
|
114
|
+
|
|
115
|
+
roundederr = np.ceil(dx/errprec)*errprec
|
|
116
|
+
|
|
117
|
+
roundedx = np.round(x/errprec)*errprec
|
|
118
|
+
precision = int(-1*np.log10(errprec))
|
|
119
|
+
|
|
120
|
+
return roundedx, roundederr, precision
|
|
121
|
+
|
|
122
|
+
def F_stat(chi1, chi2, p1, p2, n):
|
|
123
|
+
nom = (chi1 - chi2)/(p2-p1)
|
|
124
|
+
denom = chi2/(n - p2)
|
|
125
|
+
return nom/denom
|
|
126
|
+
|
|
127
|
+
def leastsq_covariance(fun,x0):
|
|
128
|
+
J = approx_derivative(fun, x0, method='3-point')
|
|
129
|
+
|
|
130
|
+
if J.ndim != 2:
|
|
131
|
+
J = np.atleast_2d(J)
|
|
132
|
+
_, s, VT = LA.svd(J, full_matrices=False)
|
|
133
|
+
threshold = np.finfo(float).eps * max(J.shape) * s[0]
|
|
134
|
+
s = s[s > threshold]
|
|
135
|
+
VT = VT[:s.size]
|
|
136
|
+
pcov = np.dot(VT.T / s**2, VT)
|
|
137
|
+
return pcov
|
|
138
|
+
|
|
139
|
+
def leastsq_covariance_unstable(fun,x0):
|
|
140
|
+
Jfun = approx_derivative(fun, x0, method='3-point')
|
|
141
|
+
J = Jfun(x0)
|
|
142
|
+
if J.ndim != 2:
|
|
143
|
+
J = np.atleast_2d(J)
|
|
144
|
+
pcov = LA.pinv(np.dot(J.T ,J))
|
|
145
|
+
|
|
146
|
+
return pcov
|
|
147
|
+
|
|
148
|
+
def leastsq_covariance_lm(fun,x0):
|
|
149
|
+
|
|
150
|
+
res = opt.least_squares(fun,x0,jac='3-point')
|
|
151
|
+
J = res.jac
|
|
152
|
+
if J.ndim != 2:
|
|
153
|
+
J = np.atleast_2d(J)
|
|
154
|
+
_, s, VT = LA.svd(J, full_matrices=False)
|
|
155
|
+
threshold = np.finfo(float).eps * max(J.shape) * s[0]
|
|
156
|
+
s = s[s > threshold]
|
|
157
|
+
VT = VT[:s.size]
|
|
158
|
+
pcov = np.dot(VT.T / s**2, VT)
|
|
159
|
+
return pcov
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_scale_chi2(obs, theo, err=None):
|
|
163
|
+
"""returns scale ``s`` so that chi2 is minimized.
|
|
164
|
+
|
|
165
|
+
chi2 = sum_i ((obs_i - s * theo_i)/err_i)**2
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
if err is None:
|
|
169
|
+
err = 1.
|
|
170
|
+
err2 = err**2
|
|
171
|
+
scale = np.sum((obs*theo)/ err2) / np.sum((theo**2)/ err2)
|
|
172
|
+
return scale
|
|
173
|
+
|
|
174
|
+
def get_scale_logchi2(obs, theo):
|
|
175
|
+
"""returns scale ``s`` so that chi2 is minimized.
|
|
176
|
+
|
|
177
|
+
chi2 = sum_i ((log(obs_i) - log(s * theo_i)))**2
|
|
178
|
+
"""
|
|
179
|
+
return gmean(obs/theo)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def getScalefactor_fitted(ctr,xtal):
|
|
183
|
+
h,k = ctr.hk
|
|
184
|
+
harr = np.full_like(ctr.l,h)
|
|
185
|
+
karr = np.full_like(ctr.l,k)
|
|
186
|
+
logI = np.log( xtal.F(harr,karr,ctr.l)**2 )
|
|
187
|
+
|
|
188
|
+
def minfun(x):
|
|
189
|
+
if x[0] < 0:
|
|
190
|
+
return np.inf
|
|
191
|
+
return np.sum((np.log( (ctr.sfI*x[0])**2 ) - logI )**2)
|
|
192
|
+
res = opt.minimize(minfun,[1.])
|
|
193
|
+
return res.x
|
|
194
|
+
|
|
195
|
+
def getScalefactor(ctr,xtal):
|
|
196
|
+
h,k = ctr.hk
|
|
197
|
+
harr = np.full_like(ctr.l,h)
|
|
198
|
+
karr = np.full_like(ctr.l,k)
|
|
199
|
+
F_cryst = np.abs( xtal.F(harr,karr,ctr.l))
|
|
200
|
+
return np.prod(F_cryst/ctr.sfI)**(1/ctr.l.size)
|
|
201
|
+
|
|
202
|
+
def getScalefactor_collection(coll,xtal):
|
|
203
|
+
scale = []
|
|
204
|
+
for ctr in coll:
|
|
205
|
+
scale.append(getScalefactor(ctr,xtal))
|
|
206
|
+
return scale
|
|
207
|
+
|
|
208
|
+
def stepup(z,A,sigma,mu):
|
|
209
|
+
return A*0.5*(1 + special.erf(np.sqrt(0.5)*((z-mu)/(sigma))))
|
|
210
|
+
|
|
211
|
+
def stepdown(z,A,sigma,mu):
|
|
212
|
+
return A*0.5*(1 - special.erf(np.sqrt(0.5)*((z-mu)/(sigma))))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def averageCTRs_fcc111(CTRcoll, cutoff=2,nosymmetry_factor=3, pclip=0.3, **keyargs):
|
|
216
|
+
fcc111_lattice = HKLVlieg.Crystal([1./np.sqrt(2),1./np.sqrt(2),np.sqrt(3)],[90.,90.,120.])
|
|
217
|
+
B = np.asarray(fcc111_lattice.getB())
|
|
218
|
+
rods = [B @ np.array([*ctr.hk,0.]) for ctr in CTRcoll]
|
|
219
|
+
rotaxis = np.array([0,0,1])
|
|
220
|
+
|
|
221
|
+
Bi = np.linalg.inv(B)
|
|
222
|
+
|
|
223
|
+
equiv = equivalentReflectionsRotation(3,rotaxis,rods)
|
|
224
|
+
|
|
225
|
+
equiv_hkl = [(Bi @ e.T).T for e in equiv]
|
|
226
|
+
|
|
227
|
+
equivalent = [[] for _ in range(len(equiv))]
|
|
228
|
+
|
|
229
|
+
decimals = keyargs.get('decimals', 3)
|
|
230
|
+
|
|
231
|
+
for ctr in CTRcoll:
|
|
232
|
+
ctrhkl = np.array([*ctr.hk,0.])
|
|
233
|
+
for i, ehkl in enumerate(equiv_hkl):
|
|
234
|
+
if np.any(np.isclose(np.around(ehkl,decimals), np.around(ctrhkl,decimals)).all(axis=1)):
|
|
235
|
+
equivalent[i].append(ctr)
|
|
236
|
+
break
|
|
237
|
+
else:
|
|
238
|
+
raise Exception("Did not find the group of symmetry equivalent rods for %s." % ctr)
|
|
239
|
+
|
|
240
|
+
return averageCTRs(equivalent, cutoff,nosymmetry_factor,pclip)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def averageCTRs_fcc100(CTRcoll, cutoff=2,nosymmetry_factor=3, pclip=0.3):
|
|
245
|
+
#temporary fix!!
|
|
246
|
+
#from datautils.xrayutils import CTRplotutil
|
|
247
|
+
#newColl = CTRplotutil.CTRCollection()
|
|
248
|
+
rodnames = []
|
|
249
|
+
for ctr in CTRcoll:
|
|
250
|
+
h, k = ctr.hk
|
|
251
|
+
if abs(h) > abs(k):
|
|
252
|
+
hk = abs(h), abs(k)
|
|
253
|
+
else:
|
|
254
|
+
hk = abs(k), abs(h)
|
|
255
|
+
rodnames.append(hk)
|
|
256
|
+
|
|
257
|
+
#CTRcoll_copy = copy.deepcopy(CTRcoll)
|
|
258
|
+
rodnames = list(sorted(set(rodnames)))
|
|
259
|
+
equivalent = []
|
|
260
|
+
for n in rodnames:
|
|
261
|
+
rodtpe = [c for c in CTRcoll if np.sum(np.array(n)**2) == np.sum(np.array(c.hk)**2) ]
|
|
262
|
+
equivalent.append(rodtpe)
|
|
263
|
+
|
|
264
|
+
return averageCTRs(equivalent, cutoff,nosymmetry_factor,pclip)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# for fcc100
|
|
268
|
+
def averageCTRs_Pt100(CTRcoll, cutoff=2,nosymmetry_factor=3, pclip=0.3):
|
|
269
|
+
return averageCTRs_fcc100(CTRcoll, cutoff,nosymmetry_factor,pclip)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def equivalentReflectionsRotation(numberrot, rotaxis, reflections, decimals=3):
|
|
273
|
+
"""Returns all unique vectors which are generated by rotation
|
|
274
|
+
of the vectors ``reflections`` around the axis ``rotaxis``.
|
|
275
|
+
|
|
276
|
+
The vectors are rotated by ``0``, ``(2pi / numberrot) * 2``,
|
|
277
|
+
``(2pi / numberrot) * 2``... ``(2pi / numberrot) * (numberrot-1)``.
|
|
278
|
+
|
|
279
|
+
All vectors (i.e. reflections) are sorted into groups of
|
|
280
|
+
vectors which can be generated by the symmetry operation.
|
|
281
|
+
The vectors can be provided as a list of 2d arrays. The vectors in
|
|
282
|
+
the 2d arrays will remain part of the same group. This allows
|
|
283
|
+
concatenation of multiple symmetry operations.
|
|
284
|
+
|
|
285
|
+
:param int numberrot: number of rotations (2*pi / n)
|
|
286
|
+
:param np.ndarray rotaxis: rotation axis (shape (3,))
|
|
287
|
+
:param list reflections: vectors to rotate. (List of arrays of shape (n,3))
|
|
288
|
+
:param int decimals: Decimals to round before comparing vectors.
|
|
289
|
+
"""
|
|
290
|
+
rotvec_norm = rotaxis / np.linalg.norm(rotaxis)
|
|
291
|
+
rotation_vectors = [2*np.pi * (i / numberrot) * rotvec_norm for i in range(numberrot)]
|
|
292
|
+
rotations = Rotation.from_rotvec(rotation_vectors)
|
|
293
|
+
|
|
294
|
+
reflections = [np.atleast_2d(r) for r in reflections]
|
|
295
|
+
|
|
296
|
+
all_symmetry = []
|
|
297
|
+
|
|
298
|
+
for refl in reflections:
|
|
299
|
+
symmetry_reflections = np.vstack([rotations.apply(r) for r in refl])
|
|
300
|
+
_, idx = np.unique(np.around(symmetry_reflections,decimals),return_index=True,axis=0)
|
|
301
|
+
all_symmetry.append(symmetry_reflections[idx])
|
|
302
|
+
|
|
303
|
+
reduced_symmety = []
|
|
304
|
+
|
|
305
|
+
# very slow and super ugly code :( !!!
|
|
306
|
+
for i in range(len(all_symmetry)):
|
|
307
|
+
for sym_refl in all_symmetry[i]:
|
|
308
|
+
found = False
|
|
309
|
+
for j in range(i+1,len(all_symmetry)):
|
|
310
|
+
if np.any(np.isclose(np.around(all_symmetry[j],decimals), np.around(sym_refl,decimals)).all(axis=1)):
|
|
311
|
+
symmstack = np.vstack([all_symmetry[j], sym_refl])
|
|
312
|
+
_, idx = np.unique(np.around(symmstack,decimals),return_index=True,axis=0)
|
|
313
|
+
all_symmetry[j] = symmstack[idx]
|
|
314
|
+
found = True
|
|
315
|
+
break
|
|
316
|
+
else:
|
|
317
|
+
continue
|
|
318
|
+
if found:
|
|
319
|
+
break
|
|
320
|
+
else:
|
|
321
|
+
reduced_symmety.append(all_symmetry[i])
|
|
322
|
+
return reduced_symmety
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def averageCTRs(equivalent_list, cutoff=2,nosymmetry_factor=3, pclip=0.3):
|
|
327
|
+
"""Averages symmetry equivalent reflections.
|
|
328
|
+
|
|
329
|
+
:param list equivalent_list: List with lists of symmetry equivalent CTR
|
|
330
|
+
:param float cutoff: reflections with ``sf*cutoff > error`` are merked as\
|
|
331
|
+
sufficient quality
|
|
332
|
+
:param float nosymmetry_factor: If no symmetry equivalent reflection exists\
|
|
333
|
+
will set systematic error to ``meanagreement*sf*nosymmetry_factor``,\
|
|
334
|
+
where meanagreement is either calculated from this rod or uses the\
|
|
335
|
+
agreement of the whole dataset.
|
|
336
|
+
:param float pclip: Sets the minimum systematic error to the ``pclip``\
|
|
337
|
+
-percentile of the agreement of the whole dataset.
|
|
338
|
+
|
|
339
|
+
"""
|
|
340
|
+
|
|
341
|
+
#temporary fix!!
|
|
342
|
+
from datautils.xrayutils import CTRplotutil
|
|
343
|
+
newColl = CTRplotutil.CTRCollection()
|
|
344
|
+
for e in equivalent_list:
|
|
345
|
+
e_curr = list(sorted(e,key=lambda x : x.sfI.size))
|
|
346
|
+
#print(e_curr)
|
|
347
|
+
l = e_curr[0].l
|
|
348
|
+
err = e_curr[0].err**2
|
|
349
|
+
sfI = e_curr[0].sfI
|
|
350
|
+
count = np.ones_like(l)
|
|
351
|
+
#print(e_curr)
|
|
352
|
+
"""
|
|
353
|
+
first calculate the mean structure factor
|
|
354
|
+
and the resulting statistical error
|
|
355
|
+
"""
|
|
356
|
+
for ctrno in range(1,len(e_curr)):
|
|
357
|
+
#print(l)
|
|
358
|
+
ctr = copy.deepcopy(e_curr[ctrno])
|
|
359
|
+
val,i1,i2 = np.intersect1d(l,ctr.l,return_indices=True)
|
|
360
|
+
mask1 = np.zeros_like(l, dtype=np.bool_)
|
|
361
|
+
mask1[i1] = True
|
|
362
|
+
mask2 = np.zeros_like(ctr.l, dtype=np.bool_)
|
|
363
|
+
mask2[i2] = True
|
|
364
|
+
err[mask1] += ctr.err[mask2]**2
|
|
365
|
+
sfI[mask1] += ctr.sfI[mask2]
|
|
366
|
+
count[mask1] += 1
|
|
367
|
+
l = np.concatenate((l,ctr.l[~mask2]))
|
|
368
|
+
err = np.concatenate((err,ctr.err[~mask2]**2))
|
|
369
|
+
sfI = np.concatenate((sfI,ctr.sfI[~mask2]))
|
|
370
|
+
count = np.concatenate((count,np.ones(np.sum(~mask2))))
|
|
371
|
+
|
|
372
|
+
sortarg = np.argsort(l)
|
|
373
|
+
|
|
374
|
+
l = l[sortarg]
|
|
375
|
+
err = err[sortarg]
|
|
376
|
+
sfI = sfI[sortarg]
|
|
377
|
+
count = count[sortarg]
|
|
378
|
+
|
|
379
|
+
sfI /= count # mean structure factor of symmetry equivalent rods
|
|
380
|
+
err = np.sqrt(err) / count # statistical error
|
|
381
|
+
"""
|
|
382
|
+
now calculate the variance of symmetry equivalent reflections
|
|
383
|
+
"""
|
|
384
|
+
varerr = np.zeros_like(err)
|
|
385
|
+
|
|
386
|
+
for ctrno in range(1,len(e_curr)):
|
|
387
|
+
#print(l)
|
|
388
|
+
ctr = copy.deepcopy(e_curr[ctrno])
|
|
389
|
+
val,i1,i2 = np.intersect1d(l,ctr.l,return_indices=True)
|
|
390
|
+
mask1 = np.zeros_like(l, dtype=np.bool_)
|
|
391
|
+
mask1[i1] = True
|
|
392
|
+
mask2 = np.zeros_like(ctr.l, dtype=np.bool_)
|
|
393
|
+
mask2[i2] = True
|
|
394
|
+
varerr[mask1] += (sfI[mask1] - ctr.sfI[mask2])**2
|
|
395
|
+
|
|
396
|
+
varerr = np.sqrt(varerr/count) # systematic error, only averaged, hence the count in the sqrt !
|
|
397
|
+
agreement_factor = varerr/sfI
|
|
398
|
+
no_symmetry_equivalent = varerr == 0.
|
|
399
|
+
if not no_symmetry_equivalent.all():
|
|
400
|
+
meanagreement = np.nanmean((varerr/sfI)[~no_symmetry_equivalent])
|
|
401
|
+
#agreement_factor[:] = meanagreement
|
|
402
|
+
varerr[no_symmetry_equivalent] = meanagreement*sfI[no_symmetry_equivalent]*nosymmetry_factor # no symmetry equivalent reflections available
|
|
403
|
+
else:
|
|
404
|
+
meanagreement = None
|
|
405
|
+
|
|
406
|
+
#sufficient_qualtity_indicator = sfI*cutoff > err
|
|
407
|
+
|
|
408
|
+
final_error = np.sqrt(err**2 + varerr**2)
|
|
409
|
+
|
|
410
|
+
hk = list(reversed(sorted([ctr.hk for ctr in e])))[0]
|
|
411
|
+
newctr = CTRplotutil.CTR(hk,l,sfI,final_error)
|
|
412
|
+
newctr.contributions = count
|
|
413
|
+
newctr.systematicerr = varerr
|
|
414
|
+
newctr.staterr = err
|
|
415
|
+
#newctr.quality_reflection = sufficient_qualtity_indicator
|
|
416
|
+
newctr.agreement_factor = agreement_factor
|
|
417
|
+
newctr.no_symmetry_equivalent = no_symmetry_equivalent
|
|
418
|
+
newctr.meanagreement = meanagreement
|
|
419
|
+
newColl.append(newctr)
|
|
420
|
+
|
|
421
|
+
agreements = np.concatenate([ctr.agreement_factor for ctr in newColl])
|
|
422
|
+
globalagreement = np.nanmean(agreements)
|
|
423
|
+
clipagreement = np.nanpercentile(agreements,pclip*100)
|
|
424
|
+
for ctr in newColl:
|
|
425
|
+
if ctr.no_symmetry_equivalent.all():
|
|
426
|
+
ctr.systematicerr = globalagreement*ctr.sfI*nosymmetry_factor
|
|
427
|
+
ctr.agreement_factor[:] = globalagreement*nosymmetry_factor
|
|
428
|
+
#ctr.err = np.sqrt(ctr.staterr**2 + ctr.systematicerr**2)
|
|
429
|
+
ctr.effagreement = np.clip(ctr.agreement_factor,clipagreement,None)
|
|
430
|
+
ctr.err = np.sqrt((ctr.sfI**2)*(ctr.effagreement)**2 + ctr.staterr**2)
|
|
431
|
+
ctr.quality_reflection = ctr.sfI*cutoff > ctr.err
|
|
432
|
+
|
|
433
|
+
newColl.globalagreement = globalagreement
|
|
434
|
+
newColl.clipagreement = clipagreement
|
|
435
|
+
|
|
436
|
+
return newColl
|
|
437
|
+
|
|
438
|
+
def openRodfile(name):
|
|
439
|
+
with open(name,'r') as ctr:
|
|
440
|
+
names = next(ctr).split('\t')[:-1]
|
|
441
|
+
names[0] = names[0][2:]
|
|
442
|
+
formats = [np.float64 for name in names]
|
|
443
|
+
return np.loadtxt(ctr,dtype={'names': names,'formats': formats} )
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def makeSurePathExists(path):
|
|
447
|
+
if not os.path.exists(path):
|
|
448
|
+
os.makedirs(path)
|
|
449
|
+
|
|
450
|
+
def as_ndarray(obj):
|
|
451
|
+
"""make sure a float, int, list of floats or ints,
|
|
452
|
+
or tuple of floats or ints, acts as a numpy array
|
|
453
|
+
"""
|
|
454
|
+
if isinstance(obj, (float, int)):
|
|
455
|
+
return np.array([obj])
|
|
456
|
+
return np.asarray(obj)
|
|
457
|
+
|
|
458
|
+
def readNDarrayConfig(filename):
|
|
459
|
+
|
|
460
|
+
header = StringIO()
|
|
461
|
+
|
|
462
|
+
with open(filename,'r') as f:
|
|
463
|
+
for line in f:
|
|
464
|
+
if not line.startswith('#'):
|
|
465
|
+
break
|
|
466
|
+
header.write(line[2:])
|
|
467
|
+
config = configparser.ConfigParser()
|
|
468
|
+
config.read_string(header.getvalue())
|
|
469
|
+
|
|
470
|
+
data = np.loadtxt(filename)
|
|
471
|
+
return data.T, config
|
|
472
|
+
|
|
473
|
+
def rotation_matrix_from_vectors(vec1, vec2):
|
|
474
|
+
""" Find the rotation matrix that aligns vec1 to vec2
|
|
475
|
+
:param vec1: A 3d "source" vector
|
|
476
|
+
:param vec2: A 3d "destination" vector
|
|
477
|
+
:return mat: A transform matrix (3x3) which when applied to vec1, aligns it with vec2.
|
|
478
|
+
"""
|
|
479
|
+
a, b = (vec1 / np.linalg.norm(vec1)).reshape(3), (vec2 / np.linalg.norm(vec2)).reshape(3)
|
|
480
|
+
v = np.cross(a, b)
|
|
481
|
+
c = np.dot(a, b)
|
|
482
|
+
s = np.linalg.norm(v)
|
|
483
|
+
kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])
|
|
484
|
+
rotation_matrix = np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))
|
|
485
|
+
return rotation_matrix
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def sumRoi(image,roi):
|
|
489
|
+
xlo,xhi = roi[0]
|
|
490
|
+
ylo,yhi = roi[1]
|
|
491
|
+
image_tmp = image[xlo:xhi,ylo:yhi]
|
|
492
|
+
I = np.nansum(image_tmp)
|
|
493
|
+
pixel = np.count_nonzero(~np.isnan(image_tmp))
|
|
494
|
+
return I, pixel
|
|
495
|
+
|
|
496
|
+
def calcIntersect(g1,g2):
|
|
497
|
+
g1P1 , g1P2 = g1
|
|
498
|
+
g2P1 , g2P2 = g2
|
|
499
|
+
|
|
500
|
+
a = g1P1 - g1P2
|
|
501
|
+
b = g2P1 - g2P2
|
|
502
|
+
|
|
503
|
+
res = LA.solve(np.array([a,-b]),g2P1-g1P1)
|
|
504
|
+
|
|
505
|
+
return a*res[0] + g1P1
|
|
506
|
+
"""
|
|
507
|
+
gives delta in:
|
|
508
|
+
|
|
509
|
+
"""
|
|
510
|
+
|
|
511
|
+
def solveTrigEquation(theta1,theta2,l1,l2):
|
|
512
|
+
|
|
513
|
+
def Chi2(delta):
|
|
514
|
+
res = function(theta1,theta2,l1,l2,delta)
|
|
515
|
+
#print("%s : %s" % (delta,res))
|
|
516
|
+
return res
|
|
517
|
+
|
|
518
|
+
res = opt.minimize(Chi2,np.array([0.0]),bounds=[(-np.pi,np.pi)],method='TNC')
|
|
519
|
+
print(res)
|
|
520
|
+
print(np.rad2deg(res.x))
|
|
521
|
+
return res.x
|
|
522
|
+
|
|
523
|
+
def function(theta1,theta2,l1,l2,delta):
|
|
524
|
+
t1 = cos(theta1 - delta[0])**2 * l1**2
|
|
525
|
+
t2 = cos(theta1 + delta[0])**2 * l2**2
|
|
526
|
+
t3 = 2 * l1 * l2 * cos(theta1 + delta[0]) * cos(theta1 - delta[0]) * cos(2*theta2)
|
|
527
|
+
t4 = ( (l2**2) * cos(theta1 + delta[0])**2 * sin(2*theta2)**2 )/( cos(delta[0] - theta2)**2 )
|
|
528
|
+
return fabs(t1 + t2 - t3 - t4)
|
|
529
|
+
|
|
530
|
+
#solveTrigEquation(np.deg2rad(2.),np.deg2rad(76.),1.,1.1)
|
|
531
|
+
|
|
532
|
+
"""
|
|
533
|
+
only single value of th:
|
|
534
|
+
"""
|
|
535
|
+
def x_rotation(th):
|
|
536
|
+
return np.array(((1., 0., 0.), (0., np.cos(th), -np.sin(th)), (0., np.sin(th), np.cos(th))))
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def y_rotation(th):
|
|
540
|
+
return np.array(((np.cos(th), 0., np.sin(th)), (0, 1., 0.), (-np.sin(th), 0., np.cos(th))))
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def z_rotation(th):
|
|
544
|
+
return np.array(((np.cos(th), -np.sin(th), 0.), (np.sin(th), np.cos(th), 0.), (0., 0., 1.)))
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def rot_trans_matrix(xrot, yrot, zrot, translate):
|
|
548
|
+
rotmat = x_rotation(xrot) @ y_rotation(yrot) @ z_rotation(zrot)
|
|
549
|
+
return np.vstack((rotmat.T,translate)).T
|
|
550
|
+
|
|
551
|
+
"""
|
|
552
|
+
th is now a 1d-array, returns an array of rotation matrices
|
|
553
|
+
"""
|
|
554
|
+
def x_rotationArray(th):
|
|
555
|
+
matrices = np.zeros((th.size,3,3))
|
|
556
|
+
matrices[:,0,0] = 1
|
|
557
|
+
matrices[:,1,1] = np.cos(th)
|
|
558
|
+
matrices[:,1,2] = -np.sin(th)
|
|
559
|
+
matrices[:,2,1] = np.sin(th)
|
|
560
|
+
matrices[:,2,2] = np.cos(th)
|
|
561
|
+
return matrices
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def y_rotationArray(th):
|
|
565
|
+
matrices = np.zeros((th.size,3,3))
|
|
566
|
+
matrices[:,1,1] = 1
|
|
567
|
+
matrices[:,0,0] = np.cos(th)
|
|
568
|
+
matrices[:,0,2] = np.sin(th)
|
|
569
|
+
matrices[:,2,0] = -np.sin(th)
|
|
570
|
+
matrices[:,2,2] = np.cos(th)
|
|
571
|
+
return matrices
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def z_rotationArray(th):
|
|
575
|
+
matrices = np.zeros((th.size,3,3))
|
|
576
|
+
matrices[:,2,2] = 1
|
|
577
|
+
matrices[:,0,0] = np.cos(th)
|
|
578
|
+
matrices[:,0,1] = -np.sin(th)
|
|
579
|
+
matrices[:,1,0] = np.sin(th)
|
|
580
|
+
matrices[:,1,1] = np.cos(th)
|
|
581
|
+
return matrices
|
|
582
|
+
|
|
583
|
+
def orthogonal(matrix):
|
|
584
|
+
matrix = np.array(matrix)
|
|
585
|
+
|
|
586
|
+
SMALL = 1e-4
|
|
587
|
+
|
|
588
|
+
def normalise(m):
|
|
589
|
+
d = LA.norm(m)
|
|
590
|
+
if d < SMALL:
|
|
591
|
+
raise Exception("Error: can't make matrix orthogonal")
|
|
592
|
+
return m / d
|
|
593
|
+
#print(LA.norm(v1))
|
|
594
|
+
v1 = normalise(matrix[:,0])
|
|
595
|
+
print(matrix[:,0])
|
|
596
|
+
print(LA.norm(v1))
|
|
597
|
+
v2 = normalise(matrix[:,1])
|
|
598
|
+
v3 = normalise(matrix[:,2])
|
|
599
|
+
|
|
600
|
+
return np.hstack([v1, v2, v3]).A
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def calcHighPixel(image,threshold):
|
|
604
|
+
return np.argwhere(image > threshold)
|
|
605
|
+
|
|
606
|
+
#old:
|
|
607
|
+
highintensity = image > threshold
|
|
608
|
+
highpixel = []
|
|
609
|
+
|
|
610
|
+
for x,xrow in enumerate(highintensity):
|
|
611
|
+
for y in range(xrow.size):
|
|
612
|
+
if highintensity[x][y]:
|
|
613
|
+
highpixel.append([y,x])
|
|
614
|
+
|
|
615
|
+
highpixel = np.array(highpixel).T
|
|
616
|
+
return highpixel
|
|
617
|
+
|
|
618
|
+
def lines_concatenated(x, m, x0, b0, **kwargs):
|
|
619
|
+
"""Series of concatenated lines
|
|
620
|
+
|
|
621
|
+
y_1 = m_1 * x + b0
|
|
622
|
+
|
|
623
|
+
or (if f1_x_intersect == True):
|
|
624
|
+
y_1 = m_1 * (x - b0)
|
|
625
|
+
|
|
626
|
+
y_n = m_n * x + b_n
|
|
627
|
+
y_n+1 = m_n+1*x + (m_n - m_n+1)*x_n + b_n
|
|
628
|
+
|
|
629
|
+
:param x np.ndarray: datapoints where to calcualte the lines
|
|
630
|
+
:param m np.ndarray: gradients of lines, shape n
|
|
631
|
+
:param x0 np.ndarray: switchover point between lines, shape n-1
|
|
632
|
+
:param b0 float: constant offset or x intersect of first line
|
|
633
|
+
:param kwargs: optional: when f1_x_intersect is set to True, the b0 parameter will be the x intersect of the first line. I.e.
|
|
634
|
+
"""
|
|
635
|
+
y = np.zeros_like(x, dtype=np.float64)
|
|
636
|
+
|
|
637
|
+
line_switch_idx = np.argmin(np.abs(x - x0[0]))
|
|
638
|
+
if x[line_switch_idx] - x0[0] < 0.:
|
|
639
|
+
line_switch_idx += 1
|
|
640
|
+
if kwargs.get('f1_x_intersect', False):
|
|
641
|
+
y[:line_switch_idx] = m[0] * (x[:line_switch_idx] - b0)
|
|
642
|
+
b_n = -b0*m[0]
|
|
643
|
+
else:
|
|
644
|
+
y[:line_switch_idx] = m[0] * x[:line_switch_idx] + b0
|
|
645
|
+
b_n = b0
|
|
646
|
+
|
|
647
|
+
last_idx = line_switch_idx
|
|
648
|
+
m_n = m[0]
|
|
649
|
+
x_n = x0[0]
|
|
650
|
+
|
|
651
|
+
for i, x_np in enumerate(x0[1:], start=1):
|
|
652
|
+
line_switch_idx = np.argmin(np.abs(x - x_np))
|
|
653
|
+
if x[line_switch_idx] - x_np < 0.:
|
|
654
|
+
line_switch_idx += 1
|
|
655
|
+
m_np = m[i]
|
|
656
|
+
b_np = (m_n - m_np) * x_n + b_n
|
|
657
|
+
y[last_idx:line_switch_idx] = m_np * x[last_idx:line_switch_idx] + b_np
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
#print(i, m[i], last_idx, line_switch_idx)
|
|
661
|
+
m_n, b_n, x_n = m_np, b_np, x_np
|
|
662
|
+
last_idx = line_switch_idx
|
|
663
|
+
|
|
664
|
+
m_np = m[-1]
|
|
665
|
+
b_np = (m_n - m_np) * x_n + b_n
|
|
666
|
+
y[last_idx:] = m_np * x[last_idx:] + b_np
|
|
667
|
+
|
|
668
|
+
return y
|
|
669
|
+
|
|
670
|
+
def plotP3Image(image,vmin=0,vmax=None,cmap='jet',thresholdMarker=None,**keyargs):
|
|
671
|
+
if 'figure' in keyargs:
|
|
672
|
+
fig = keyargs['figure']
|
|
673
|
+
ax = fig.add_subplot(111)
|
|
674
|
+
elif 'axis' in keyargs:
|
|
675
|
+
ax = keyargs['axis']
|
|
676
|
+
fig = ax.get_figure()
|
|
677
|
+
else:
|
|
678
|
+
fig = plt.figure(figsize=(12,14))
|
|
679
|
+
ax = fig.add_subplot(111)
|
|
680
|
+
|
|
681
|
+
if not vmax:
|
|
682
|
+
ax.imshow(image,interpolation='none',cmap=plt.get_cmap(cmap),norm=colors.SymLogNorm(linthresh=1,linscale=1,vmin=vmin))
|
|
683
|
+
else:
|
|
684
|
+
ax.imshow(image,interpolation='none',cmap=plt.get_cmap(cmap),norm=colors.SymLogNorm(linthresh=1,linscale=1,vmin=vmin,vmax=vmax))
|
|
685
|
+
|
|
686
|
+
if thresholdMarker is not None:
|
|
687
|
+
highpixel = calcHighPixel(thresholdMarker)
|
|
688
|
+
if highpixel.size > 0:
|
|
689
|
+
ax.plot(highpixel[0],highpixel[1],'ro')
|
|
690
|
+
ax.set_ylim([1700,0])
|
|
691
|
+
ax.set_xlim([0,1475])
|
|
692
|
+
numrows, numcols = image.shape
|
|
693
|
+
def format_coord(x, y):
|
|
694
|
+
col = int(x + 0.5)
|
|
695
|
+
row = int(y + 0.5)
|
|
696
|
+
if col >= 0 and col < numcols and row >= 0 and row < numrows:
|
|
697
|
+
z = image[row, col]
|
|
698
|
+
return 'x=%1.4f, y=%1.4f, z=%1.4f' % (x, y, z)
|
|
699
|
+
else:
|
|
700
|
+
return 'x=%1.4f, y=%1.4f' % (x, y)
|
|
701
|
+
ax.format_coord = format_coord
|
|
702
|
+
return fig, ax
|
|
703
|
+
|
|
704
|
+
#def deltaGamma(drr,gamma):
|
|
705
|
+
# return np.arctan((1 + drr) / np.abs(np.cos(gamma))) - gamma
|