pyemaps 2.0.0__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.
- pyemaps/COPYING +798 -0
- pyemaps/CifFile/CifFile_module.py +3117 -0
- pyemaps/CifFile/StarFile.py +2496 -0
- pyemaps/CifFile/TypeContentsParser.py +82 -0
- pyemaps/CifFile/YappsStarParser_1_0.py +289 -0
- pyemaps/CifFile/YappsStarParser_1_1.py +288 -0
- pyemaps/CifFile/YappsStarParser_2_0.py +369 -0
- pyemaps/CifFile/YappsStarParser_STAR2.py +382 -0
- pyemaps/CifFile/__init__.py +7 -0
- pyemaps/CifFile/drel/__init__.py +1 -0
- pyemaps/CifFile/drel/drel_ast_yacc.py +582 -0
- pyemaps/CifFile/drel/drel_lex.py +228 -0
- pyemaps/CifFile/drel/drel_runtime.py +126 -0
- pyemaps/CifFile/drel/parsetab.py +185 -0
- pyemaps/CifFile/drel/py_from_ast.py +628 -0
- pyemaps/CifFile/parsetab.py +185 -0
- pyemaps/CifFile/yapps3_compiled_rt.py +390 -0
- pyemaps/README.md +201 -0
- pyemaps/__config__.py +91 -0
- pyemaps/__init__.py +233 -0
- pyemaps/__main__.py +219 -0
- pyemaps/__version__.py +1 -0
- pyemaps/cdata/Aluminium.xtl +4 -0
- pyemaps/cdata/AluminiumOxide.xtl +5 -0
- pyemaps/cdata/Aluminium_FCC.xtl +4 -0
- pyemaps/cdata/BariumTitanate_180k.xtl +6 -0
- pyemaps/cdata/BariumTitanate_270k.xtl +7 -0
- pyemaps/cdata/BariumTitanate_Tetra.xtl +7 -0
- pyemaps/cdata/BiMnO3.xtl +27 -0
- pyemaps/cdata/Boron_Tetra.xtl +8 -0
- pyemaps/cdata/CadmiumSelenide_Hex.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Cubic.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Hex.xtl +5 -0
- pyemaps/cdata/Chromium_BCC.xtl +4 -0
- pyemaps/cdata/CoSb3_Skutterudite.xtl +5 -0
- pyemaps/cdata/CopperOxide.xtl +5 -0
- pyemaps/cdata/Copper_FCC.xtl +4 -0
- pyemaps/cdata/Cu2O_Cuprite.xtl +5 -0
- pyemaps/cdata/Diamond.xtl +4 -0
- pyemaps/cdata/ErbiumPyrogermanate.xtl +9 -0
- pyemaps/cdata/FePd_Tetra.xtl +5 -0
- pyemaps/cdata/FeS2_Pyrite.xtl +15 -0
- pyemaps/cdata/GalliumAntimonide.xtl +5 -0
- pyemaps/cdata/GalliumArsenide.xtl +5 -0
- pyemaps/cdata/GalliumNitride.xtl +5 -0
- pyemaps/cdata/Germanium.xtl +4 -0
- pyemaps/cdata/Gold_FCC.xtl +4 -0
- pyemaps/cdata/IndiumArsenide.xtl +5 -0
- pyemaps/cdata/LaMnO3.xtl +7 -0
- pyemaps/cdata/LeadZirconateTitanate.xtl +8 -0
- pyemaps/cdata/Li2MnO3.xtl +13 -0
- pyemaps/cdata/NaFeO2.xtl +6 -0
- pyemaps/cdata/Nb3Sn.xtl +5 -0
- pyemaps/cdata/Pentacene.xtl +39 -0
- pyemaps/cdata/Silicon.xtl +4 -0
- pyemaps/cdata/StrontiumTitanate.xtl +6 -0
- pyemaps/cdata/TelluriumDioxide.xtl +5 -0
- pyemaps/cdata/TinDioxide_RT.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Anatase.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Rutile.xtl +5 -0
- pyemaps/cdata/TungstenDiselenide.xtl +5 -0
- pyemaps/cdata/VanadiumDioxide_RT.xtl +6 -0
- pyemaps/cdata/ZincOxide.xtl +5 -0
- pyemaps/cdata/Zinc_HCP.xtl +4 -0
- pyemaps/cdata/ZirconiumNitride.xtl +5 -0
- pyemaps/cdata/limno2.xtl +6 -0
- pyemaps/crystals.py +1415 -0
- pyemaps/ddiffs.py +147 -0
- pyemaps/diffract/__init__.py +70 -0
- pyemaps/diffract/bloch_dec.py +779 -0
- pyemaps/diffract/csf_dec.py +242 -0
- pyemaps/diffract/dif_dec.py +519 -0
- pyemaps/diffract/dpgen_dec.py +175 -0
- pyemaps/diffract/mxtal_dec.py +201 -0
- pyemaps/diffract/powder_dec.py +137 -0
- pyemaps/diffract/stereo_dec.py +111 -0
- pyemaps/display.py +791 -0
- pyemaps/emcontrols.py +1017 -0
- pyemaps/errors.py +266 -0
- pyemaps/fileutils.py +1001 -0
- pyemaps/kdiffs.py +1221 -0
- pyemaps/license.txt +42 -0
- pyemaps/samples/adf.py +137 -0
- pyemaps/samples/al.img +0 -0
- pyemaps/samples/al_dpgen.py +57 -0
- pyemaps/samples/al_ediom.py +94 -0
- pyemaps/samples/convert_image.py +88 -0
- pyemaps/samples/dm_diff.py +225 -0
- pyemaps/samples/mask.im3 +0 -0
- pyemaps/samples/powder.py +51 -0
- pyemaps/samples/raw.img +0 -0
- pyemaps/samples/si_bloch.py +120 -0
- pyemaps/samples/si_constructor.py +51 -0
- pyemaps/samples/si_csf.py +59 -0
- pyemaps/samples/si_dif.py +134 -0
- pyemaps/samples/si_lacbed.py +59 -0
- pyemaps/samples/si_pyemaps.py +80 -0
- pyemaps/samples/si_scm.py +156 -0
- pyemaps/samples/si_stereo.py +116 -0
- pyemaps/scattering/__init__.py +26 -0
- pyemaps/scattering/sct_dec.py +31 -0
- pyemaps/spg/__init__.py +25 -0
- pyemaps/spg/spg_dec.py +60 -0
- pyemaps/stackimg.py +1031 -0
- pyemaps/xtal.py +222 -0
- pyemaps-2.0.0.dist-info/METADATA +231 -0
- pyemaps-2.0.0.dist-info/RECORD +112 -0
- pyemaps-2.0.0.dist-info/WHEEL +5 -0
- pyemaps-2.0.0.dist-info/entry_points.txt +2 -0
- pyemaps-2.0.0.dist-info/licenses/COPYING +798 -0
- pyemaps-2.0.0.dist-info/licenses/license.txt +42 -0
- pyemaps-2.0.0.dist-info/top_level.txt +1 -0
pyemaps/kdiffs.py
ADDED
|
@@ -0,0 +1,1221 @@
|
|
|
1
|
+
'''
|
|
2
|
+
.. This file is part of pyemaps
|
|
3
|
+
|
|
4
|
+
Kinematic diffraction module is designed to handle kinematic simulation
|
|
5
|
+
data. It is composed of Point, Line and Disk class objects.
|
|
6
|
+
|
|
7
|
+
.. ----
|
|
8
|
+
|
|
9
|
+
.. pyEMAPS is free software. You can redistribute it and/or modify
|
|
10
|
+
.. it under the terms of the GNU General Public License as published
|
|
11
|
+
.. by the Free Software Foundation, either version 3 of the License,
|
|
12
|
+
.. or (at your option) any later version..
|
|
13
|
+
|
|
14
|
+
.. pyEMAPS is distributed in the hope that it will be useful,
|
|
15
|
+
.. but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
.. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
.. GNU General Public License for more details.
|
|
18
|
+
|
|
19
|
+
.. You should have received a copy of the GNU General Public License
|
|
20
|
+
.. along with pyEMAPS. If not, see `<https://www.gnu.org/licenses/>`_.
|
|
21
|
+
|
|
22
|
+
.. Contact supprort@emlabsoftware.com for any questions and comments.
|
|
23
|
+
|
|
24
|
+
.. ----
|
|
25
|
+
|
|
26
|
+
.. Author: EMLab Solutions, Inc.
|
|
27
|
+
.. Date: May 07, 2022
|
|
28
|
+
|
|
29
|
+
'''
|
|
30
|
+
|
|
31
|
+
from . import EMC, SIMC
|
|
32
|
+
from . import DPError, PointError, LineError, PIndexError, \
|
|
33
|
+
DiskError, DPListError
|
|
34
|
+
|
|
35
|
+
# precision digits for comparison purposes
|
|
36
|
+
NDIGITS = 1
|
|
37
|
+
DIFF_PRECISION = 0.95
|
|
38
|
+
MIN_OPACITY = 0.2
|
|
39
|
+
MAX_OPACITY = 0.35
|
|
40
|
+
|
|
41
|
+
from . import XMAX, YMAX
|
|
42
|
+
#int: Diffraction simulation output limits
|
|
43
|
+
|
|
44
|
+
DEF_MODE = 1
|
|
45
|
+
#int: Default diffraction simulation mode, normal
|
|
46
|
+
|
|
47
|
+
def _double_eq(a,b):
|
|
48
|
+
|
|
49
|
+
'''
|
|
50
|
+
|
|
51
|
+
For internal testing only
|
|
52
|
+
|
|
53
|
+
'''
|
|
54
|
+
return abs(a-b) <= DIFF_PRECISION
|
|
55
|
+
|
|
56
|
+
class Point:
|
|
57
|
+
|
|
58
|
+
'''
|
|
59
|
+
|
|
60
|
+
Coordinates of kinematic diffraction pattern object.
|
|
61
|
+
|
|
62
|
+
'''
|
|
63
|
+
def __init__(self, p=(0.0, 0.0)):
|
|
64
|
+
|
|
65
|
+
setattr(self, 'x', p[0])
|
|
66
|
+
setattr(self, 'y', p[1])
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def x(self):
|
|
70
|
+
|
|
71
|
+
'''
|
|
72
|
+
|
|
73
|
+
X coordinate
|
|
74
|
+
|
|
75
|
+
'''
|
|
76
|
+
return self._x
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def y(self):
|
|
80
|
+
|
|
81
|
+
'''
|
|
82
|
+
|
|
83
|
+
Y coordinate
|
|
84
|
+
|
|
85
|
+
'''
|
|
86
|
+
return self._y
|
|
87
|
+
|
|
88
|
+
@x.setter
|
|
89
|
+
def x(self, v):
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
self._x=float(v)
|
|
93
|
+
except ValueError as e:
|
|
94
|
+
raise PointError("Point component must be floating point nuberic type")
|
|
95
|
+
|
|
96
|
+
@y.setter
|
|
97
|
+
def y(self, v):
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
self._y=float(v)
|
|
101
|
+
except ValueError as e:
|
|
102
|
+
raise PointError("Point component must be floating point nuberic type")
|
|
103
|
+
|
|
104
|
+
def __lt__(self, other):
|
|
105
|
+
if isinstance(other, Point):
|
|
106
|
+
x1 = self._x
|
|
107
|
+
x2 = other.x
|
|
108
|
+
|
|
109
|
+
if x1 != x2:
|
|
110
|
+
return x1 < x2
|
|
111
|
+
|
|
112
|
+
y1 = self._y
|
|
113
|
+
y2 = other.y
|
|
114
|
+
|
|
115
|
+
if y1 != y2:
|
|
116
|
+
return y1 < y2
|
|
117
|
+
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
raise PointError("comparison must be of Point objects")
|
|
121
|
+
|
|
122
|
+
def __imul__(self, rhs):
|
|
123
|
+
if not isinstance(rhs, int) and not isinstance(rhs, float):
|
|
124
|
+
raise ValueError('right hand side must be numberic')
|
|
125
|
+
|
|
126
|
+
self._x *= rhs
|
|
127
|
+
self._y *= rhs
|
|
128
|
+
|
|
129
|
+
return self
|
|
130
|
+
|
|
131
|
+
def __iadd__(self, other):
|
|
132
|
+
if isinstance(other, Point):
|
|
133
|
+
self._x += other.x
|
|
134
|
+
self._y += other.y
|
|
135
|
+
return self
|
|
136
|
+
|
|
137
|
+
raise PointError("addition of different type other than Point type not supported")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def __isub__(self, other):
|
|
141
|
+
if isinstance(other, Point):
|
|
142
|
+
self._x -= other.x
|
|
143
|
+
self._y -= other.y
|
|
144
|
+
return self
|
|
145
|
+
|
|
146
|
+
raise PointError("subtraction of different type other than Point type not supported")
|
|
147
|
+
|
|
148
|
+
def __key__(self):
|
|
149
|
+
return (self._x, self._y)
|
|
150
|
+
|
|
151
|
+
def __hash__(self):
|
|
152
|
+
return hash(self.__key__())
|
|
153
|
+
|
|
154
|
+
def __eq__(self, other):
|
|
155
|
+
if isinstance(other, Point):
|
|
156
|
+
return _double_eq(self._x, other.x) and \
|
|
157
|
+
_double_eq(self._y, other.y)
|
|
158
|
+
|
|
159
|
+
raise PointError("comparison must be of Point objects")
|
|
160
|
+
|
|
161
|
+
def __repr__(self):
|
|
162
|
+
return str("({}, {})".format(self._x, self._y))
|
|
163
|
+
|
|
164
|
+
def __iter__(self):
|
|
165
|
+
return iter((self._x, self._y))
|
|
166
|
+
|
|
167
|
+
class Line:
|
|
168
|
+
|
|
169
|
+
'''
|
|
170
|
+
|
|
171
|
+
Kikuchi line representation in kinematic diffraction patterns.
|
|
172
|
+
|
|
173
|
+
'''
|
|
174
|
+
def __init__(self, pt1 = Point(), pt2=Point(), intensity=0, type=1):
|
|
175
|
+
|
|
176
|
+
setattr(self, 'pt1', pt1)
|
|
177
|
+
setattr(self, 'pt2', pt2)
|
|
178
|
+
setattr(self, 'intensity', intensity)
|
|
179
|
+
setattr(self, 'type', type)
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def pt1(self):
|
|
183
|
+
|
|
184
|
+
'''
|
|
185
|
+
|
|
186
|
+
The first end point of a Line
|
|
187
|
+
|
|
188
|
+
'''
|
|
189
|
+
return self._pt1
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def pt2(self):
|
|
193
|
+
|
|
194
|
+
'''
|
|
195
|
+
|
|
196
|
+
The second end point of a Line
|
|
197
|
+
|
|
198
|
+
'''
|
|
199
|
+
return self._pt2
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def type(self):
|
|
203
|
+
|
|
204
|
+
'''
|
|
205
|
+
|
|
206
|
+
The type of a Line: Kikuchi line or HOLZ line
|
|
207
|
+
|
|
208
|
+
'''
|
|
209
|
+
return self._type
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def intensity(self):
|
|
213
|
+
|
|
214
|
+
'''
|
|
215
|
+
Line intensity
|
|
216
|
+
|
|
217
|
+
'''
|
|
218
|
+
return self._intensity
|
|
219
|
+
|
|
220
|
+
@pt1.setter
|
|
221
|
+
def pt1(self, pv):
|
|
222
|
+
if not isinstance(pv, Point):
|
|
223
|
+
raise LineError("end points must be of Point type")
|
|
224
|
+
|
|
225
|
+
self._pt1 = pv
|
|
226
|
+
|
|
227
|
+
@pt2.setter
|
|
228
|
+
def pt2(self, pv):
|
|
229
|
+
if not isinstance(pv, Point):
|
|
230
|
+
raise LineError("end points must be of Point type")
|
|
231
|
+
|
|
232
|
+
self._pt2 = pv
|
|
233
|
+
|
|
234
|
+
@type.setter
|
|
235
|
+
def type(self, ty):
|
|
236
|
+
if not isinstance(ty, int):
|
|
237
|
+
raise LineError("type must be integer")
|
|
238
|
+
|
|
239
|
+
self._type = ty
|
|
240
|
+
|
|
241
|
+
@intensity.setter
|
|
242
|
+
def intensity(self, intense):
|
|
243
|
+
|
|
244
|
+
self._intensity = 0
|
|
245
|
+
try:
|
|
246
|
+
self._intensity = int(intense)
|
|
247
|
+
except ValueError as e:
|
|
248
|
+
raise LineError("intensity must be integer or float") from e
|
|
249
|
+
|
|
250
|
+
def __lt__(self, other):
|
|
251
|
+
|
|
252
|
+
if isinstance(other, Line):
|
|
253
|
+
if self._type == other.type:
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
pt1 = self._pt1
|
|
257
|
+
pt2 = other.pt1
|
|
258
|
+
if not (pt1 == pt2):
|
|
259
|
+
return pt1 < pt2
|
|
260
|
+
|
|
261
|
+
pt1 = self._pt2
|
|
262
|
+
pt2 = other.pt2
|
|
263
|
+
if not (pt1 == pt2):
|
|
264
|
+
return pt1 < pt2
|
|
265
|
+
|
|
266
|
+
if self._intensity != other.intensity:
|
|
267
|
+
return self._intensity < other.intensity
|
|
268
|
+
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
raise LineError("comparison must be between Line objects")
|
|
272
|
+
|
|
273
|
+
def __key__(self):
|
|
274
|
+
return (self._pt1, self._pt2, self._intensity)
|
|
275
|
+
|
|
276
|
+
def __hash__(self):
|
|
277
|
+
return hash(self.__key__())
|
|
278
|
+
|
|
279
|
+
def __isub__(self, other):
|
|
280
|
+
|
|
281
|
+
'''
|
|
282
|
+
|
|
283
|
+
This operator override is for shifting the line by fixed amount
|
|
284
|
+
by 2D vector
|
|
285
|
+
|
|
286
|
+
'''
|
|
287
|
+
if isinstance(other, Point):
|
|
288
|
+
self._pt1 -= other
|
|
289
|
+
self._pt2 -= other
|
|
290
|
+
return self
|
|
291
|
+
|
|
292
|
+
raise LineError("substraction of Line class from non-Point class type not supported")
|
|
293
|
+
|
|
294
|
+
def __iadd__(self, other):
|
|
295
|
+
|
|
296
|
+
'''
|
|
297
|
+
|
|
298
|
+
This operator override is for shifting the line by fixed amount when
|
|
299
|
+
other line object has the same pt1 and ot2
|
|
300
|
+
|
|
301
|
+
'''
|
|
302
|
+
if isinstance(other, Point):
|
|
303
|
+
self._pt1 += other
|
|
304
|
+
self._pt2 += other
|
|
305
|
+
return self
|
|
306
|
+
|
|
307
|
+
raise LineError("addition of different type not supported")
|
|
308
|
+
|
|
309
|
+
def __imul__(self, rhs):
|
|
310
|
+
if not isinstance(rhs, int) and not isinstance(rhs, float):
|
|
311
|
+
raise ValueError('right hand side must be numberic')
|
|
312
|
+
|
|
313
|
+
self._pt1 *= rhs
|
|
314
|
+
self._pt2 *= rhs
|
|
315
|
+
|
|
316
|
+
return self
|
|
317
|
+
|
|
318
|
+
def __eq__(self, other):
|
|
319
|
+
if isinstance(other, Line):
|
|
320
|
+
if self._type != other.type:
|
|
321
|
+
return False
|
|
322
|
+
|
|
323
|
+
if self._intensity != other.intensity:
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
if not (self._pt1 == other.pt1) or \
|
|
327
|
+
not (self._pt2 == other.pt2):
|
|
328
|
+
return False
|
|
329
|
+
|
|
330
|
+
return True
|
|
331
|
+
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
def __repr__(self):
|
|
335
|
+
return str("[{}, {}], {}".format(self._pt1.__repr__(),
|
|
336
|
+
self._pt2.__repr__(),
|
|
337
|
+
self._intensity))
|
|
338
|
+
|
|
339
|
+
def __iter__(self):
|
|
340
|
+
x1, y1 = self._pt1
|
|
341
|
+
x2, y2 = self._pt2
|
|
342
|
+
return iter((x1,y1,x2,y2,self._intensity))
|
|
343
|
+
|
|
344
|
+
def calOpacity(self, l, h):
|
|
345
|
+
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
Calculate the line opacity based on its intensity value
|
|
349
|
+
|
|
350
|
+
0.2 -> lowest intensity
|
|
351
|
+
0.35-> highest intensity
|
|
352
|
+
|
|
353
|
+
"""
|
|
354
|
+
if h==l:
|
|
355
|
+
return 0.35
|
|
356
|
+
|
|
357
|
+
return MIN_OPACITY + ((self._intensity-l)*(MAX_OPACITY-MIN_OPACITY))/(h-l)
|
|
358
|
+
|
|
359
|
+
# def calLineWidth(self, l, h):
|
|
360
|
+
# """
|
|
361
|
+
# Calculate the line width based on its intensity value
|
|
362
|
+
|
|
363
|
+
# 1.0 -> lowest intensity
|
|
364
|
+
# 1.75-> highest intensity
|
|
365
|
+
|
|
366
|
+
# """
|
|
367
|
+
# if h==l:
|
|
368
|
+
# return 1.75
|
|
369
|
+
|
|
370
|
+
# return 1.0 + ((self._intensity-l)*3)/(4.0*(h-l))
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def to_dict(self):
|
|
375
|
+
x1, y1 = self._pt1
|
|
376
|
+
x2, y2 = self._pt2
|
|
377
|
+
return {'pt1': (x1,y1),
|
|
378
|
+
'pt2': (x2,y2),
|
|
379
|
+
'int': self._intensity}
|
|
380
|
+
|
|
381
|
+
class Index:
|
|
382
|
+
|
|
383
|
+
'''
|
|
384
|
+
|
|
385
|
+
Miller Indexes of a diffracted beam representation in kinematic
|
|
386
|
+
diffraction pattern.
|
|
387
|
+
|
|
388
|
+
'''
|
|
389
|
+
def __init__(self, I0=(0,0,0)):
|
|
390
|
+
|
|
391
|
+
setattr(self, 'I1', I0[0])
|
|
392
|
+
setattr(self, 'I2', I0[1])
|
|
393
|
+
setattr(self, 'I3', I0[2])
|
|
394
|
+
|
|
395
|
+
@property
|
|
396
|
+
def I1(self):
|
|
397
|
+
|
|
398
|
+
'''
|
|
399
|
+
|
|
400
|
+
The first element of Miller Index of a diffracted beam
|
|
401
|
+
|
|
402
|
+
'''
|
|
403
|
+
return self._I1
|
|
404
|
+
|
|
405
|
+
@property
|
|
406
|
+
def I2(self):
|
|
407
|
+
|
|
408
|
+
'''
|
|
409
|
+
|
|
410
|
+
The second element of Miller Index of a diffracted beam
|
|
411
|
+
|
|
412
|
+
'''
|
|
413
|
+
return self._I2
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def I3(self):
|
|
417
|
+
|
|
418
|
+
'''
|
|
419
|
+
The third element of Miller Index of a diffracted beam
|
|
420
|
+
|
|
421
|
+
'''
|
|
422
|
+
return self._I3
|
|
423
|
+
|
|
424
|
+
@I1.setter
|
|
425
|
+
def I1(self, iv):
|
|
426
|
+
if not isinstance(iv, int):
|
|
427
|
+
raise PIndexError('index must be integer')
|
|
428
|
+
|
|
429
|
+
self._I1 = iv
|
|
430
|
+
|
|
431
|
+
@I2.setter
|
|
432
|
+
def I2(self, iv):
|
|
433
|
+
if not isinstance(iv, int):
|
|
434
|
+
raise PIndexError('index must be integer')
|
|
435
|
+
|
|
436
|
+
self._I2 = iv
|
|
437
|
+
|
|
438
|
+
@I3.setter
|
|
439
|
+
def I3(self, iv):
|
|
440
|
+
if not isinstance(iv, int):
|
|
441
|
+
raise PIndexError('index must be integer')
|
|
442
|
+
|
|
443
|
+
self._I3 = iv
|
|
444
|
+
|
|
445
|
+
def __lt__(self, other):
|
|
446
|
+
if isinstance(other, Index):
|
|
447
|
+
ii1 = self._I1
|
|
448
|
+
ii2 = other.I1
|
|
449
|
+
if ii1 != ii2:
|
|
450
|
+
return ii1 < ii2
|
|
451
|
+
|
|
452
|
+
ii1 = self._I2
|
|
453
|
+
ii2 = other.I2
|
|
454
|
+
if ii1 != ii2:
|
|
455
|
+
return ii1 < ii2
|
|
456
|
+
|
|
457
|
+
ii1 = self._I3
|
|
458
|
+
ii2 = other.I3
|
|
459
|
+
if ii1 != ii2:
|
|
460
|
+
return ii1 < ii2
|
|
461
|
+
return False
|
|
462
|
+
|
|
463
|
+
raise PIndexError('cannot compare with non-Index type')
|
|
464
|
+
|
|
465
|
+
def __key__(self):
|
|
466
|
+
return (self._I1, self._I2, self._I3)
|
|
467
|
+
|
|
468
|
+
def __hash__(self):
|
|
469
|
+
return hash(self.__key__())
|
|
470
|
+
|
|
471
|
+
def __str__(self):
|
|
472
|
+
return str("{} {} {}".format(self._I1, self._I2, self._I3))
|
|
473
|
+
|
|
474
|
+
def __repr__(self):
|
|
475
|
+
return str("({}, {}, {})".format(self._I1, self._I2, self._I3))
|
|
476
|
+
|
|
477
|
+
def __eq__(self, other):
|
|
478
|
+
if isinstance(other, Index):
|
|
479
|
+
return self.__key__() == other.__key__()
|
|
480
|
+
|
|
481
|
+
raise PIndexError('cannot compare with non-Index type')
|
|
482
|
+
|
|
483
|
+
def __iter__(self):
|
|
484
|
+
return iter((self._I1, self._I2, self._I3))
|
|
485
|
+
|
|
486
|
+
class Disk:
|
|
487
|
+
|
|
488
|
+
'''
|
|
489
|
+
|
|
490
|
+
Diffracted beams representation in kinematic diffraction patterns
|
|
491
|
+
|
|
492
|
+
'''
|
|
493
|
+
def __init__(self, c=Point(), r=0.0, i=Index()):
|
|
494
|
+
|
|
495
|
+
setattr(self, 'c', c)
|
|
496
|
+
setattr(self, 'r', r)
|
|
497
|
+
setattr(self, 'idx', i)
|
|
498
|
+
|
|
499
|
+
@property
|
|
500
|
+
def c(self):
|
|
501
|
+
|
|
502
|
+
'''
|
|
503
|
+
|
|
504
|
+
The center point of a diffracted beam.
|
|
505
|
+
|
|
506
|
+
'''
|
|
507
|
+
return self._c
|
|
508
|
+
|
|
509
|
+
@property
|
|
510
|
+
def r(self):
|
|
511
|
+
|
|
512
|
+
'''
|
|
513
|
+
|
|
514
|
+
The radius of a diffracted beam.
|
|
515
|
+
|
|
516
|
+
'''
|
|
517
|
+
return self._r
|
|
518
|
+
|
|
519
|
+
@property
|
|
520
|
+
def idx(self):
|
|
521
|
+
|
|
522
|
+
'''
|
|
523
|
+
|
|
524
|
+
The Miller index of a diffracted beam.
|
|
525
|
+
|
|
526
|
+
'''
|
|
527
|
+
return self._idx
|
|
528
|
+
|
|
529
|
+
@c.setter
|
|
530
|
+
def c(self, cv):
|
|
531
|
+
|
|
532
|
+
if not isinstance(cv, Point):
|
|
533
|
+
raise DiskError('disk center must be of Point type')
|
|
534
|
+
|
|
535
|
+
self._c = cv
|
|
536
|
+
|
|
537
|
+
@r.setter
|
|
538
|
+
def r(self, rv):
|
|
539
|
+
self._r = 0.0
|
|
540
|
+
try:
|
|
541
|
+
self._r = float(rv)
|
|
542
|
+
except ValueError as e:
|
|
543
|
+
raise DiskError('disk radius must be real number')
|
|
544
|
+
|
|
545
|
+
@idx.setter
|
|
546
|
+
def idx(self, iv):
|
|
547
|
+
|
|
548
|
+
if not isinstance(iv, Index):
|
|
549
|
+
raise DiskError('disk index must be of Index type')
|
|
550
|
+
|
|
551
|
+
self._idx = iv
|
|
552
|
+
|
|
553
|
+
def __eq__(self, other):
|
|
554
|
+
if not isinstance(other, Disk):
|
|
555
|
+
raise DiskError('disk object cannot equal to non disk object')
|
|
556
|
+
|
|
557
|
+
if not (self._idx == other.idx):
|
|
558
|
+
return False
|
|
559
|
+
|
|
560
|
+
if abs(self._r - other.r) > 1.0e-06:
|
|
561
|
+
return False
|
|
562
|
+
|
|
563
|
+
if not (self._c == other.c):
|
|
564
|
+
return False
|
|
565
|
+
|
|
566
|
+
return True
|
|
567
|
+
|
|
568
|
+
def __iadd__(self, other):
|
|
569
|
+
|
|
570
|
+
'''
|
|
571
|
+
|
|
572
|
+
This operator override is for shifting the line when
|
|
573
|
+
other line object has the same pt1 and ot2
|
|
574
|
+
|
|
575
|
+
'''
|
|
576
|
+
if isinstance(other, Point):
|
|
577
|
+
self._c += other
|
|
578
|
+
return self
|
|
579
|
+
|
|
580
|
+
raise DiskError('addition cannot be done with non-Disk type')
|
|
581
|
+
|
|
582
|
+
def __isub__(self, other):
|
|
583
|
+
'''
|
|
584
|
+
|
|
585
|
+
This operator override is for shifting the line when
|
|
586
|
+
other line object has the same pt1 and ot2
|
|
587
|
+
|
|
588
|
+
'''
|
|
589
|
+
if isinstance(other, Point):
|
|
590
|
+
self._c -= other
|
|
591
|
+
return self
|
|
592
|
+
|
|
593
|
+
raise DiskError('substraction cannot be done with non-Point type')
|
|
594
|
+
|
|
595
|
+
def __imul__(self, rhs):
|
|
596
|
+
|
|
597
|
+
if not isinstance(rhs, int) and not isinstance(rhs, float):
|
|
598
|
+
raise ValueError('right hand side must be numberic')
|
|
599
|
+
|
|
600
|
+
self._c *= rhs
|
|
601
|
+
self._r *= rhs
|
|
602
|
+
|
|
603
|
+
return self
|
|
604
|
+
|
|
605
|
+
def __key__(self):
|
|
606
|
+
center = self._c
|
|
607
|
+
r = self._r
|
|
608
|
+
indx = self._idx
|
|
609
|
+
return (indx.I1, indx.I2, indx.I3, center.x, center.y, r)
|
|
610
|
+
|
|
611
|
+
def __hash__(self):
|
|
612
|
+
return hash(self.__key__())
|
|
613
|
+
|
|
614
|
+
def __repr__(self):
|
|
615
|
+
|
|
616
|
+
return "index: " + repr(self._idx) + " " + \
|
|
617
|
+
"center: " + repr(self._c) + " " + \
|
|
618
|
+
str("radius: {}".format(self._r))
|
|
619
|
+
|
|
620
|
+
def to_dict(self):
|
|
621
|
+
|
|
622
|
+
'''
|
|
623
|
+
|
|
624
|
+
Creates a diffracted beam object from a dict pyton object
|
|
625
|
+
|
|
626
|
+
'''
|
|
627
|
+
dd = {}
|
|
628
|
+
dd['c'] = self._c.__key__()
|
|
629
|
+
dd['idx'] = self._idx.__key__()
|
|
630
|
+
dd['r'] = self._r
|
|
631
|
+
return dd
|
|
632
|
+
|
|
633
|
+
def __lt__(self, other):
|
|
634
|
+
if not isinstance(other, Disk):
|
|
635
|
+
raise DiskError('comparison cannot be done with non-Disk type')
|
|
636
|
+
|
|
637
|
+
if not (self._idx == other.idx):
|
|
638
|
+
return self._idx < other.idx
|
|
639
|
+
|
|
640
|
+
if not (self._c == other.c):
|
|
641
|
+
return self._c < other.c
|
|
642
|
+
|
|
643
|
+
if self._r != other.r:
|
|
644
|
+
return self._r < other.r
|
|
645
|
+
|
|
646
|
+
return False
|
|
647
|
+
|
|
648
|
+
def __iter__(self):
|
|
649
|
+
cx, cy = self._c
|
|
650
|
+
i1, i2, i3 = self._idx
|
|
651
|
+
return iter((cx, cy, self._r, i1, i2, i3))
|
|
652
|
+
|
|
653
|
+
class diffPattern:
|
|
654
|
+
|
|
655
|
+
'''
|
|
656
|
+
|
|
657
|
+
Create a kinematic diffraction pattern based on the pyemaps
|
|
658
|
+
kinematic simulation output in python dict object.
|
|
659
|
+
|
|
660
|
+
See :doc:Visualization for how to visualize
|
|
661
|
+
kinematic diffraction patterns using this object.
|
|
662
|
+
|
|
663
|
+
'''
|
|
664
|
+
def __init__(self, diff_dict):
|
|
665
|
+
|
|
666
|
+
'''
|
|
667
|
+
|
|
668
|
+
:param diff_dict: Only accepts output from pyemaps kinematic diffraction run.
|
|
669
|
+
:type diff_dict: dict, required
|
|
670
|
+
|
|
671
|
+
'''
|
|
672
|
+
if not diff_dict or not isinstance(diff_dict, dict):
|
|
673
|
+
raise DPError("failed to construct diffraction pattern object")
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
if 'nums' not in diff_dict or \
|
|
677
|
+
'name' not in diff_dict or \
|
|
678
|
+
'klines' not in diff_dict or \
|
|
679
|
+
'hlines' not in diff_dict or \
|
|
680
|
+
'disks' not in diff_dict:
|
|
681
|
+
raise DPError("Invalid diffraction data")
|
|
682
|
+
|
|
683
|
+
ndiffs = diff_dict['nums']
|
|
684
|
+
if not isinstance(ndiffs, dict) or \
|
|
685
|
+
'nklines' not in ndiffs or \
|
|
686
|
+
'nhlines' not in ndiffs or \
|
|
687
|
+
'ndisks' not in ndiffs:
|
|
688
|
+
raise DPError("Invaild diffraction data")
|
|
689
|
+
|
|
690
|
+
if 'bounds' not in diff_dict:
|
|
691
|
+
setattr(self, 'shift', [0.0,0.0])
|
|
692
|
+
else:
|
|
693
|
+
# set the shifts first
|
|
694
|
+
setattr(self, 'shift', diff_dict['bounds'])
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
for k, v in diff_dict.items():
|
|
698
|
+
if k != 'nums' and k != 'bounds':
|
|
699
|
+
setattr(self, k, v)
|
|
700
|
+
|
|
701
|
+
elif k == 'nums':
|
|
702
|
+
|
|
703
|
+
for k1, v1 in v.items():
|
|
704
|
+
setattr(self, k1, v1)
|
|
705
|
+
|
|
706
|
+
@property
|
|
707
|
+
def klines(self):
|
|
708
|
+
|
|
709
|
+
'''
|
|
710
|
+
|
|
711
|
+
Kikuchi lines array
|
|
712
|
+
|
|
713
|
+
'''
|
|
714
|
+
return self._klines
|
|
715
|
+
|
|
716
|
+
@property
|
|
717
|
+
def hlines(self):
|
|
718
|
+
|
|
719
|
+
'''
|
|
720
|
+
|
|
721
|
+
Holz lines array
|
|
722
|
+
|
|
723
|
+
'''
|
|
724
|
+
return self._hlines
|
|
725
|
+
|
|
726
|
+
@property
|
|
727
|
+
def disks(self):
|
|
728
|
+
|
|
729
|
+
'''
|
|
730
|
+
|
|
731
|
+
Disks array
|
|
732
|
+
|
|
733
|
+
'''
|
|
734
|
+
return self._disks
|
|
735
|
+
|
|
736
|
+
@property
|
|
737
|
+
def nklines(self):
|
|
738
|
+
|
|
739
|
+
'''
|
|
740
|
+
|
|
741
|
+
Number of Kikuchi lines
|
|
742
|
+
|
|
743
|
+
'''
|
|
744
|
+
return self._nklines
|
|
745
|
+
|
|
746
|
+
@property
|
|
747
|
+
def nhlines(self):
|
|
748
|
+
|
|
749
|
+
'''
|
|
750
|
+
|
|
751
|
+
Number of HOLZ lines
|
|
752
|
+
|
|
753
|
+
'''
|
|
754
|
+
return self._nhlines
|
|
755
|
+
|
|
756
|
+
@property
|
|
757
|
+
def ndisks(self):
|
|
758
|
+
|
|
759
|
+
'''
|
|
760
|
+
|
|
761
|
+
Number of Disks
|
|
762
|
+
|
|
763
|
+
'''
|
|
764
|
+
return self._ndisks
|
|
765
|
+
|
|
766
|
+
@property
|
|
767
|
+
def shift(self):
|
|
768
|
+
|
|
769
|
+
'''
|
|
770
|
+
|
|
771
|
+
Shifts of the diffraction pattern
|
|
772
|
+
|
|
773
|
+
'''
|
|
774
|
+
return self._shift
|
|
775
|
+
|
|
776
|
+
@property
|
|
777
|
+
def name(self):
|
|
778
|
+
|
|
779
|
+
'''
|
|
780
|
+
|
|
781
|
+
Crystal name
|
|
782
|
+
|
|
783
|
+
'''
|
|
784
|
+
return self._name
|
|
785
|
+
|
|
786
|
+
@klines.setter
|
|
787
|
+
def klines(self, kls):
|
|
788
|
+
|
|
789
|
+
if not hasattr(kls, "__len__"):
|
|
790
|
+
raise DPError("klines data invalid")
|
|
791
|
+
|
|
792
|
+
self._klines = []
|
|
793
|
+
for k in kls:
|
|
794
|
+
try:
|
|
795
|
+
x1,y1,x2,y2,intensity = k
|
|
796
|
+
|
|
797
|
+
except Exception as e:
|
|
798
|
+
raise DPError(f'kline data invalid: {e}')
|
|
799
|
+
|
|
800
|
+
else:
|
|
801
|
+
pt1 = Point(p=(x1,y1))
|
|
802
|
+
pt2 = Point(p=(x2,y2))
|
|
803
|
+
|
|
804
|
+
pt1 -= self._shift
|
|
805
|
+
pt2 -= self._shift
|
|
806
|
+
|
|
807
|
+
ln = Line(pt1 = pt1,
|
|
808
|
+
pt2 = pt2,
|
|
809
|
+
intensity=intensity)
|
|
810
|
+
|
|
811
|
+
self._klines.append(ln)
|
|
812
|
+
|
|
813
|
+
@hlines.setter
|
|
814
|
+
def hlines(self, hls):
|
|
815
|
+
|
|
816
|
+
if not hasattr(hls, "__len__"):
|
|
817
|
+
raise DPError("hlines data invalid")
|
|
818
|
+
|
|
819
|
+
self._hlines = []
|
|
820
|
+
for h in hls:
|
|
821
|
+
try:
|
|
822
|
+
x1,y1,x2,y2,intensity = h
|
|
823
|
+
|
|
824
|
+
except Exception as e:
|
|
825
|
+
raise DPError(f'hlines data invalid: {e}')
|
|
826
|
+
|
|
827
|
+
else:
|
|
828
|
+
pt1 = Point(p=(x1,y1))
|
|
829
|
+
pt2 = Point(p=(x2,y2))
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
pt1 -= self._shift
|
|
833
|
+
pt2 -= self._shift
|
|
834
|
+
|
|
835
|
+
ln = Line(pt1 = pt1,
|
|
836
|
+
pt2 = pt2,
|
|
837
|
+
type = 2,
|
|
838
|
+
intensity=intensity)
|
|
839
|
+
|
|
840
|
+
self._hlines.append(ln)
|
|
841
|
+
|
|
842
|
+
@disks.setter
|
|
843
|
+
def disks(self, dks):
|
|
844
|
+
|
|
845
|
+
if not hasattr(dks, "__len__"):
|
|
846
|
+
raise DPError("disks must be an array of Disk objects")
|
|
847
|
+
|
|
848
|
+
self._disks =[]
|
|
849
|
+
for d in dks:
|
|
850
|
+
if not "c" in d or len(d['c']) != 2:
|
|
851
|
+
raise DPError("disks must have a center of Point type")
|
|
852
|
+
|
|
853
|
+
if not "r" in d:
|
|
854
|
+
raise DPError("disks must have a radius")
|
|
855
|
+
|
|
856
|
+
if not "idx" in d or len(d['idx']) != 3:
|
|
857
|
+
raise DPError("disks must be an index of three integers")
|
|
858
|
+
|
|
859
|
+
ctr = Point(d['c'])
|
|
860
|
+
ctr -= self._shift
|
|
861
|
+
|
|
862
|
+
r = d['r']
|
|
863
|
+
indx = Index(d['idx'])
|
|
864
|
+
dk = Disk(ctr, r, indx)
|
|
865
|
+
|
|
866
|
+
self._disks.append(dk)
|
|
867
|
+
|
|
868
|
+
@name.setter
|
|
869
|
+
def name(self, name):
|
|
870
|
+
|
|
871
|
+
if not isinstance(name, str):
|
|
872
|
+
raise DPError("name must be of string type")
|
|
873
|
+
|
|
874
|
+
self._name = name
|
|
875
|
+
|
|
876
|
+
@shift.setter
|
|
877
|
+
def shift(self, sft):
|
|
878
|
+
|
|
879
|
+
if isinstance(sft, tuple) and \
|
|
880
|
+
list(map(type, sft)) != [float, float]:
|
|
881
|
+
raise DPError("diffraction pattern shift must be a tuple of two floats")
|
|
882
|
+
|
|
883
|
+
self._shift = Point(sft)
|
|
884
|
+
|
|
885
|
+
@nklines.setter
|
|
886
|
+
def nklines(self, nk):
|
|
887
|
+
|
|
888
|
+
if not isinstance(nk, int):
|
|
889
|
+
raise DPError("input must be integer")
|
|
890
|
+
|
|
891
|
+
self._nklines = nk
|
|
892
|
+
|
|
893
|
+
@nhlines.setter
|
|
894
|
+
def nhlines(self, nh):
|
|
895
|
+
|
|
896
|
+
if not isinstance(nh, int):
|
|
897
|
+
raise DPError("input must be integer")
|
|
898
|
+
|
|
899
|
+
self._nhlines = nh
|
|
900
|
+
|
|
901
|
+
@ndisks.setter
|
|
902
|
+
def ndisks(self, nd):
|
|
903
|
+
|
|
904
|
+
if not isinstance(nd, int):
|
|
905
|
+
raise DPError("input must be integer")
|
|
906
|
+
|
|
907
|
+
self._ndisks = nd
|
|
908
|
+
|
|
909
|
+
def __eq__(self, other):
|
|
910
|
+
|
|
911
|
+
if not isinstance(other, diffPattern):
|
|
912
|
+
raise DPError("DP object does not compare with objects of rother types")
|
|
913
|
+
|
|
914
|
+
if self._name != other.name:
|
|
915
|
+
return False
|
|
916
|
+
|
|
917
|
+
if self._nklines != other.nklines or \
|
|
918
|
+
self._ndisks != other.ndisks or \
|
|
919
|
+
self._nhlines != other.nhlines:
|
|
920
|
+
return False
|
|
921
|
+
|
|
922
|
+
if len(self._klines) != len(other.klines) or \
|
|
923
|
+
len(self._disks) != len(other.disks) or \
|
|
924
|
+
len(self._hlines) != len(other.hlines):
|
|
925
|
+
return False
|
|
926
|
+
|
|
927
|
+
for kl in other.klines:
|
|
928
|
+
if not kl in self._klines:
|
|
929
|
+
return False
|
|
930
|
+
|
|
931
|
+
for hl in other.hlines:
|
|
932
|
+
if not hl in self._hlines:
|
|
933
|
+
return False
|
|
934
|
+
|
|
935
|
+
for ds in other.disks:
|
|
936
|
+
if not ds in self._disks:
|
|
937
|
+
return False
|
|
938
|
+
|
|
939
|
+
return True
|
|
940
|
+
|
|
941
|
+
def __contains__(self, lc):
|
|
942
|
+
|
|
943
|
+
if (not isinstance(lc, Line)) and (not isinstance(lc, Disk)):
|
|
944
|
+
raise DPError("DP object does not contain other types other than defined lines, disks")
|
|
945
|
+
|
|
946
|
+
dlist = []
|
|
947
|
+
if isinstance(lc, Line) and lc.type == 1:
|
|
948
|
+
dlist = self.klines
|
|
949
|
+
|
|
950
|
+
elif isinstance(lc, Line) and lc.type == 2:
|
|
951
|
+
dlist = self.hlines
|
|
952
|
+
|
|
953
|
+
else:
|
|
954
|
+
dlist = self.disks
|
|
955
|
+
|
|
956
|
+
for l in dlist:
|
|
957
|
+
if l == lc:
|
|
958
|
+
return True
|
|
959
|
+
|
|
960
|
+
return False
|
|
961
|
+
|
|
962
|
+
def __str__(self):
|
|
963
|
+
|
|
964
|
+
sDiff=[str(f'# of Kikuchi lines (kline): {self._nklines}')]
|
|
965
|
+
for i, k in enumerate(self._klines):
|
|
966
|
+
sDiff.append(str("kline# {}:".format(i+1)).ljust(10) + repr(k))
|
|
967
|
+
|
|
968
|
+
sDiff.append(str(f'\n# of diffracted beams (disk, index = Miller Index): {self._nklines}'))
|
|
969
|
+
for i, d in enumerate(self._disks):
|
|
970
|
+
sDiff.append(str("disk# {}:".format(i+1)).ljust(10) + repr(d))
|
|
971
|
+
|
|
972
|
+
sDiff.append(str(f'\n# of HOLZ lines (hline): {self._nhlines}'))
|
|
973
|
+
for i, h in enumerate(self._hlines):
|
|
974
|
+
sDiff.append(str("hline# {}:".format(i+1)).ljust(10) + repr(h))
|
|
975
|
+
|
|
976
|
+
return "\n".join(sDiff)
|
|
977
|
+
|
|
978
|
+
def _difference(self, other):
|
|
979
|
+
|
|
980
|
+
'''
|
|
981
|
+
|
|
982
|
+
Internal testing use only
|
|
983
|
+
|
|
984
|
+
'''
|
|
985
|
+
# A - B (self - other)
|
|
986
|
+
if not isinstance(other, diffPattern):
|
|
987
|
+
raise DPError("DP object does not compare with objects of DP types")
|
|
988
|
+
|
|
989
|
+
kdiff = []
|
|
990
|
+
for sk in self._klines:
|
|
991
|
+
if not (sk in other):
|
|
992
|
+
kdiff.append(sk)
|
|
993
|
+
|
|
994
|
+
hdiff = []
|
|
995
|
+
for sh in self._hlines:
|
|
996
|
+
if not (sh in other):
|
|
997
|
+
hdiff.append(sh)
|
|
998
|
+
|
|
999
|
+
ddiff = []
|
|
1000
|
+
for d in self._disks:
|
|
1001
|
+
if not (d in other):
|
|
1002
|
+
ddiff.append(d)
|
|
1003
|
+
|
|
1004
|
+
return (kdiff, hdiff, ddiff)
|
|
1005
|
+
|
|
1006
|
+
def to_dict(self):
|
|
1007
|
+
retdict = {}
|
|
1008
|
+
|
|
1009
|
+
retdict['klines'] = [kl.to_dict() for kl in self._klines]
|
|
1010
|
+
retdict['hlines'] = [hl.to_dict() for hl in self._hlines]
|
|
1011
|
+
retdict['disks'] = [d.to_dict() for d in self._disks]
|
|
1012
|
+
return retdict
|
|
1013
|
+
|
|
1014
|
+
class Diffraction:
|
|
1015
|
+
|
|
1016
|
+
'''
|
|
1017
|
+
|
|
1018
|
+
List of DP objects and its associated EMControl objects.
|
|
1019
|
+
|
|
1020
|
+
'''
|
|
1021
|
+
def __init__(self, name, mode=DEF_MODE):
|
|
1022
|
+
|
|
1023
|
+
setattr(self, 'name', name)
|
|
1024
|
+
setattr(self, 'mode', mode)
|
|
1025
|
+
setattr(self, 'diffList', [])
|
|
1026
|
+
|
|
1027
|
+
@property
|
|
1028
|
+
def name(self):
|
|
1029
|
+
return self._name
|
|
1030
|
+
|
|
1031
|
+
@property
|
|
1032
|
+
def mode(self):
|
|
1033
|
+
return self._mode
|
|
1034
|
+
|
|
1035
|
+
@property
|
|
1036
|
+
def diffList(self):
|
|
1037
|
+
return self._diffList
|
|
1038
|
+
|
|
1039
|
+
@name.setter
|
|
1040
|
+
def name(self, n):
|
|
1041
|
+
if not isinstance(n, str) or len(n) == 0:
|
|
1042
|
+
raise DPListError('crystal name invalid')
|
|
1043
|
+
|
|
1044
|
+
self._name = n
|
|
1045
|
+
|
|
1046
|
+
@mode.setter
|
|
1047
|
+
def mode(self, md):
|
|
1048
|
+
|
|
1049
|
+
if not isinstance(md, int) or (md != 1 and md != 2):
|
|
1050
|
+
raise DPListError('DP mode can only be 1 - normal and 2 - CBED')
|
|
1051
|
+
|
|
1052
|
+
self._mode = md
|
|
1053
|
+
|
|
1054
|
+
@diffList.setter
|
|
1055
|
+
def diffList(self, dpl):
|
|
1056
|
+
if not isinstance(dpl, list):
|
|
1057
|
+
raise DPListError('invalid input DP list')
|
|
1058
|
+
|
|
1059
|
+
for emc, dp in dpl:
|
|
1060
|
+
if not isinstance(dp, diffPattern) or \
|
|
1061
|
+
not isinstance(emc, EMC):
|
|
1062
|
+
raise DPListError('invalid data found in DP list')
|
|
1063
|
+
|
|
1064
|
+
self._diffList = dpl
|
|
1065
|
+
|
|
1066
|
+
# Adding new diffraction patterns
|
|
1067
|
+
def add(self, emc, diffP):
|
|
1068
|
+
|
|
1069
|
+
'''
|
|
1070
|
+
|
|
1071
|
+
Append a new kenematic diffraction pattern with its associated
|
|
1072
|
+
controls
|
|
1073
|
+
|
|
1074
|
+
'''
|
|
1075
|
+
if not isinstance(diffP, diffPattern):
|
|
1076
|
+
raise DPListError('failed to add DP')
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
self.diffList.append((emc, diffP))
|
|
1080
|
+
|
|
1081
|
+
def sort(self):
|
|
1082
|
+
|
|
1083
|
+
'''
|
|
1084
|
+
|
|
1085
|
+
Sorting diffraction list by controls
|
|
1086
|
+
|
|
1087
|
+
'''
|
|
1088
|
+
self._diffList.sort(key=lambda x: x[0])
|
|
1089
|
+
|
|
1090
|
+
def __eq__(self, other):
|
|
1091
|
+
|
|
1092
|
+
'''
|
|
1093
|
+
|
|
1094
|
+
Test if two diffraction list is the same
|
|
1095
|
+
|
|
1096
|
+
'''
|
|
1097
|
+
if not isinstance(other, Diffraction):
|
|
1098
|
+
return False
|
|
1099
|
+
|
|
1100
|
+
if self._name != other.name or self._mode != other.mode:
|
|
1101
|
+
return False
|
|
1102
|
+
|
|
1103
|
+
if len(self._diffList) != len(other.diffList):
|
|
1104
|
+
return False
|
|
1105
|
+
|
|
1106
|
+
found = False
|
|
1107
|
+
for c, d in self:
|
|
1108
|
+
found = False
|
|
1109
|
+
|
|
1110
|
+
for oc, od in other.diffList:
|
|
1111
|
+
if c == oc:
|
|
1112
|
+
found = (d==od)
|
|
1113
|
+
if not found:
|
|
1114
|
+
break
|
|
1115
|
+
if found:
|
|
1116
|
+
continue
|
|
1117
|
+
else:
|
|
1118
|
+
break
|
|
1119
|
+
|
|
1120
|
+
return found
|
|
1121
|
+
|
|
1122
|
+
def __getitem__(self, key):
|
|
1123
|
+
|
|
1124
|
+
'''
|
|
1125
|
+
|
|
1126
|
+
Array like method for retrieving DP
|
|
1127
|
+
|
|
1128
|
+
'''
|
|
1129
|
+
return self._diffList[key]
|
|
1130
|
+
|
|
1131
|
+
def clear(self):
|
|
1132
|
+
|
|
1133
|
+
'''
|
|
1134
|
+
|
|
1135
|
+
Empty the DP list, leaving it as empty.
|
|
1136
|
+
|
|
1137
|
+
'''
|
|
1138
|
+
self._diffList.clear()
|
|
1139
|
+
|
|
1140
|
+
def _report_difference(self, other):
|
|
1141
|
+
"""
|
|
1142
|
+
|
|
1143
|
+
internal testing call
|
|
1144
|
+
|
|
1145
|
+
"""
|
|
1146
|
+
if not isinstance(other, Diffraction):
|
|
1147
|
+
raise DPListError('connt report difference between two different type of objects')
|
|
1148
|
+
|
|
1149
|
+
rep = []
|
|
1150
|
+
if self == other:
|
|
1151
|
+
return rep
|
|
1152
|
+
|
|
1153
|
+
if self._name != other.name or self._mode != other.mode:
|
|
1154
|
+
rep.append(str(f"Diffractions are generated in different mode/name" ))
|
|
1155
|
+
return rep
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
for c, d in self:
|
|
1159
|
+
cl = list(c.values())
|
|
1160
|
+
for oc, od in other:
|
|
1161
|
+
ocl = list(oc.values())
|
|
1162
|
+
|
|
1163
|
+
if cl == ocl:
|
|
1164
|
+
if not (d == od):
|
|
1165
|
+
dk,dh,dd = d.difference(od)
|
|
1166
|
+
dk2,dh2,dd2 = od.difference(d)
|
|
1167
|
+
|
|
1168
|
+
details =[]
|
|
1169
|
+
details.append(str(f"Control parameters: {c}"))
|
|
1170
|
+
klen, hlen, dlen = len(dk), len(dh), len(dd)
|
|
1171
|
+
klen2, hlen2, dlen2 = len(dk2), len(dh2), len(dd2)
|
|
1172
|
+
|
|
1173
|
+
if klen > 0:
|
|
1174
|
+
details.append(str(f"{klen} klines in the new run, not in the baseline:"))
|
|
1175
|
+
# save.writelines(str(f"{klen} klines in the new run, not in the baseline:"))
|
|
1176
|
+
for k in dk:
|
|
1177
|
+
details.append(" " + str(k))
|
|
1178
|
+
if klen2 > 0:
|
|
1179
|
+
details.append(str(f"{klen2} klines in the baseline, not in the new run:"))
|
|
1180
|
+
# save.writelines(str(f"{klen} klines in the new run, not in the baseline:"))
|
|
1181
|
+
for k in dk2:
|
|
1182
|
+
details.append(" " + str(k))
|
|
1183
|
+
|
|
1184
|
+
if dlen > 0:
|
|
1185
|
+
details.append(str(f"{dlen} disks in the new run, not in the baseline:"))
|
|
1186
|
+
# save.writelines(str(f"{klen} klines in the new run, not in the baseline:"))
|
|
1187
|
+
for d in dd:
|
|
1188
|
+
details.append(" " + str(d))
|
|
1189
|
+
if dlen2 > 0:
|
|
1190
|
+
details.append(str(f"{dlen2} disks in the baseline, not in the new run:"))
|
|
1191
|
+
# save.writelines(str(f"{klen} klines in the new run, not in the baseline:"))
|
|
1192
|
+
for d in dd2:
|
|
1193
|
+
details.append(" " + str(d))
|
|
1194
|
+
|
|
1195
|
+
if hlen > 0:
|
|
1196
|
+
details.append(str(f"{hlen} hlines in the new run, not in the baseline:"))
|
|
1197
|
+
# save.writelines(str(f"{klen} klines in the new run, not in the baseline:"))
|
|
1198
|
+
for h in dh:
|
|
1199
|
+
details.append(" " + str(h))
|
|
1200
|
+
if hlen2 > 0:
|
|
1201
|
+
details.append(str(f"{hlen2} hlines in the baseline, not in the new run:"))
|
|
1202
|
+
# save.writelines(str(f"{klen} klines in the new run, not in the baseline:"))
|
|
1203
|
+
for h in dh2:
|
|
1204
|
+
details.append(" " + str(h))
|
|
1205
|
+
|
|
1206
|
+
# for debugging - dont delete!!!!
|
|
1207
|
+
# details.append(str(f"entire new DP:"))
|
|
1208
|
+
# details.append(" " + str(d))
|
|
1209
|
+
|
|
1210
|
+
# details.append(str(f"entire baseline DP:"))
|
|
1211
|
+
# details.append(" " + str(od))
|
|
1212
|
+
rep.extend(details)
|
|
1213
|
+
return rep
|
|
1214
|
+
|
|
1215
|
+
def __str__(self):
|
|
1216
|
+
sdpl=[]
|
|
1217
|
+
for c, d in self:
|
|
1218
|
+
sdpl.append(str(f"*****EM Contols: {c}"))
|
|
1219
|
+
sdpl.append(f"{d}")
|
|
1220
|
+
|
|
1221
|
+
return "\n".join(sdpl)
|