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.
Files changed (83) hide show
  1. orGUI-1.0.1.dist-info/LICENSE +21 -0
  2. orGUI-1.0.1.dist-info/METADATA +147 -0
  3. orGUI-1.0.1.dist-info/RECORD +83 -0
  4. orGUI-1.0.1.dist-info/WHEEL +5 -0
  5. orGUI-1.0.1.dist-info/entry_points.txt +2 -0
  6. orGUI-1.0.1.dist-info/top_level.txt +1 -0
  7. orgui/__init__.py +36 -0
  8. orgui/app/ArrayTableDialog.py +433 -0
  9. orgui/app/QReflectionSelector.py +538 -0
  10. orgui/app/QScanSelector.py +692 -0
  11. orgui/app/QUBCalculator.py +1210 -0
  12. orgui/app/__init__.py +36 -0
  13. orgui/app/database.py +487 -0
  14. orgui/app/orGUI.py +2613 -0
  15. orgui/app/qutils.py +51 -0
  16. orgui/backend/__init__.py +32 -0
  17. orgui/backend/backends.py +157 -0
  18. orgui/backend/beamline/ID31DiffractLinTilt.py +77 -0
  19. orgui/backend/beamline/P212_tools.py +577 -0
  20. orgui/backend/beamline/__init__.py +36 -0
  21. orgui/backend/beamline/fio_reader.py +110 -0
  22. orgui/backend/beamline/id31_tools.py +651 -0
  23. orgui/backend/scans.py +95 -0
  24. orgui/backend/udefaults.py +163 -0
  25. orgui/backend/universalScanLoader.py +105 -0
  26. orgui/datautils/__init__.py +32 -0
  27. orgui/datautils/util.py +705 -0
  28. orgui/datautils/xrayutils/CTRcalc.py +3022 -0
  29. orgui/datautils/xrayutils/CTRopt.py +623 -0
  30. orgui/datautils/xrayutils/CTRplotutil.py +904 -0
  31. orgui/datautils/xrayutils/DetectorCalibration.py +685 -0
  32. orgui/datautils/xrayutils/HKLVlieg.py +1360 -0
  33. orgui/datautils/xrayutils/ReciprocalNavigation.py +401 -0
  34. orgui/datautils/xrayutils/_CTRcalc_accel.py +181 -0
  35. orgui/datautils/xrayutils/__init__.py +46 -0
  36. orgui/datautils/xrayutils/element_data.py +213 -0
  37. orgui/datautils/xrayutils/test/__init__.py +57 -0
  38. orgui/datautils/xrayutils/test/test_CTRcalc.py +152 -0
  39. orgui/datautils/xrayutils/test/test_DetectorCalibration.py +336 -0
  40. orgui/datautils/xrayutils/test/test_HKLcalc.py +88 -0
  41. orgui/datautils/xrayutils/unitcells/Fe3O4(100).bul +59 -0
  42. orgui/datautils/xrayutils/unitcells/Pt100.bul +7 -0
  43. orgui/datautils/xrayutils/unitcells/Pt100_small.bul +5 -0
  44. orgui/datautils/xrayutils/unitcells/Pt110.bul +5 -0
  45. orgui/datautils/xrayutils/unitcells/Pt111.bul +6 -0
  46. orgui/datautils/xrayutils/unitcells/Pt310.bul +13 -0
  47. orgui/datautils/xrayutils/unitcells/Pt3O4(100).bul +19 -0
  48. orgui/datautils/xrayutils/unitcells/PtO(001).bul +9 -0
  49. orgui/datautils/xrayutils/unitcells/PtO(010).bul +9 -0
  50. orgui/datautils/xrayutils/unitcells/PtO(100).bul +9 -0
  51. orgui/datautils/xrayutils/unitcells/__init__.py +67 -0
  52. orgui/datautils/xrayutils/unitcells/a-PtO2(0001).bul +6 -0
  53. orgui/main.py +101 -0
  54. orgui/resources/__init__.py +40 -0
  55. orgui/resources/icons/alpha.png +0 -0
  56. orgui/resources/icons/alpha.svg +67 -0
  57. orgui/resources/icons/diffractometer_v3.png +0 -0
  58. orgui/resources/icons/disable-image.png +0 -0
  59. orgui/resources/icons/disable-image.svg +68 -0
  60. orgui/resources/icons/document-nx-open.png +0 -0
  61. orgui/resources/icons/document-nx-open.svg +152 -0
  62. orgui/resources/icons/document-nx-save.png +0 -0
  63. orgui/resources/icons/document-nx-save.svg +73 -0
  64. orgui/resources/icons/logo.png +0 -0
  65. orgui/resources/icons/logo.svg +808 -0
  66. orgui/resources/icons/max_image.png +0 -0
  67. orgui/resources/icons/max_image.svg +77 -0
  68. orgui/resources/icons/max_image2.png +0 -0
  69. orgui/resources/icons/max_image2.svg +83 -0
  70. orgui/resources/icons/search-image.png +0 -0
  71. orgui/resources/icons/search-image.svg +94 -0
  72. orgui/resources/icons/search-reflection.png +0 -0
  73. orgui/resources/icons/search-reflection.svg +126 -0
  74. orgui/resources/icons/search.png +0 -0
  75. orgui/resources/icons/search.svg +91 -0
  76. orgui/resources/icons/select-image.png +0 -0
  77. orgui/resources/icons/select-image.svg +60 -0
  78. orgui/resources/icons/set-reflection.png +0 -0
  79. orgui/resources/icons/set-reflection.svg +91 -0
  80. orgui/resources/icons/sum_image.png +0 -0
  81. orgui/resources/icons/sum_image.svg +63 -0
  82. orgui/resources/icons/sum_image2.png +0 -0
  83. orgui/resources/icons/sum_image2.svg +75 -0
@@ -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