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.
Files changed (112) hide show
  1. pyemaps/COPYING +798 -0
  2. pyemaps/CifFile/CifFile_module.py +3117 -0
  3. pyemaps/CifFile/StarFile.py +2496 -0
  4. pyemaps/CifFile/TypeContentsParser.py +82 -0
  5. pyemaps/CifFile/YappsStarParser_1_0.py +289 -0
  6. pyemaps/CifFile/YappsStarParser_1_1.py +288 -0
  7. pyemaps/CifFile/YappsStarParser_2_0.py +369 -0
  8. pyemaps/CifFile/YappsStarParser_STAR2.py +382 -0
  9. pyemaps/CifFile/__init__.py +7 -0
  10. pyemaps/CifFile/drel/__init__.py +1 -0
  11. pyemaps/CifFile/drel/drel_ast_yacc.py +582 -0
  12. pyemaps/CifFile/drel/drel_lex.py +228 -0
  13. pyemaps/CifFile/drel/drel_runtime.py +126 -0
  14. pyemaps/CifFile/drel/parsetab.py +185 -0
  15. pyemaps/CifFile/drel/py_from_ast.py +628 -0
  16. pyemaps/CifFile/parsetab.py +185 -0
  17. pyemaps/CifFile/yapps3_compiled_rt.py +390 -0
  18. pyemaps/README.md +201 -0
  19. pyemaps/__config__.py +91 -0
  20. pyemaps/__init__.py +233 -0
  21. pyemaps/__main__.py +219 -0
  22. pyemaps/__version__.py +1 -0
  23. pyemaps/cdata/Aluminium.xtl +4 -0
  24. pyemaps/cdata/AluminiumOxide.xtl +5 -0
  25. pyemaps/cdata/Aluminium_FCC.xtl +4 -0
  26. pyemaps/cdata/BariumTitanate_180k.xtl +6 -0
  27. pyemaps/cdata/BariumTitanate_270k.xtl +7 -0
  28. pyemaps/cdata/BariumTitanate_Tetra.xtl +7 -0
  29. pyemaps/cdata/BiMnO3.xtl +27 -0
  30. pyemaps/cdata/Boron_Tetra.xtl +8 -0
  31. pyemaps/cdata/CadmiumSelenide_Hex.xtl +5 -0
  32. pyemaps/cdata/CadmiumSulfide_Cubic.xtl +5 -0
  33. pyemaps/cdata/CadmiumSulfide_Hex.xtl +5 -0
  34. pyemaps/cdata/Chromium_BCC.xtl +4 -0
  35. pyemaps/cdata/CoSb3_Skutterudite.xtl +5 -0
  36. pyemaps/cdata/CopperOxide.xtl +5 -0
  37. pyemaps/cdata/Copper_FCC.xtl +4 -0
  38. pyemaps/cdata/Cu2O_Cuprite.xtl +5 -0
  39. pyemaps/cdata/Diamond.xtl +4 -0
  40. pyemaps/cdata/ErbiumPyrogermanate.xtl +9 -0
  41. pyemaps/cdata/FePd_Tetra.xtl +5 -0
  42. pyemaps/cdata/FeS2_Pyrite.xtl +15 -0
  43. pyemaps/cdata/GalliumAntimonide.xtl +5 -0
  44. pyemaps/cdata/GalliumArsenide.xtl +5 -0
  45. pyemaps/cdata/GalliumNitride.xtl +5 -0
  46. pyemaps/cdata/Germanium.xtl +4 -0
  47. pyemaps/cdata/Gold_FCC.xtl +4 -0
  48. pyemaps/cdata/IndiumArsenide.xtl +5 -0
  49. pyemaps/cdata/LaMnO3.xtl +7 -0
  50. pyemaps/cdata/LeadZirconateTitanate.xtl +8 -0
  51. pyemaps/cdata/Li2MnO3.xtl +13 -0
  52. pyemaps/cdata/NaFeO2.xtl +6 -0
  53. pyemaps/cdata/Nb3Sn.xtl +5 -0
  54. pyemaps/cdata/Pentacene.xtl +39 -0
  55. pyemaps/cdata/Silicon.xtl +4 -0
  56. pyemaps/cdata/StrontiumTitanate.xtl +6 -0
  57. pyemaps/cdata/TelluriumDioxide.xtl +5 -0
  58. pyemaps/cdata/TinDioxide_RT.xtl +5 -0
  59. pyemaps/cdata/TitaniumDioxide_Anatase.xtl +5 -0
  60. pyemaps/cdata/TitaniumDioxide_Rutile.xtl +5 -0
  61. pyemaps/cdata/TungstenDiselenide.xtl +5 -0
  62. pyemaps/cdata/VanadiumDioxide_RT.xtl +6 -0
  63. pyemaps/cdata/ZincOxide.xtl +5 -0
  64. pyemaps/cdata/Zinc_HCP.xtl +4 -0
  65. pyemaps/cdata/ZirconiumNitride.xtl +5 -0
  66. pyemaps/cdata/limno2.xtl +6 -0
  67. pyemaps/crystals.py +1415 -0
  68. pyemaps/ddiffs.py +147 -0
  69. pyemaps/diffract/__init__.py +70 -0
  70. pyemaps/diffract/bloch_dec.py +779 -0
  71. pyemaps/diffract/csf_dec.py +242 -0
  72. pyemaps/diffract/dif_dec.py +519 -0
  73. pyemaps/diffract/dpgen_dec.py +175 -0
  74. pyemaps/diffract/mxtal_dec.py +201 -0
  75. pyemaps/diffract/powder_dec.py +137 -0
  76. pyemaps/diffract/stereo_dec.py +111 -0
  77. pyemaps/display.py +791 -0
  78. pyemaps/emcontrols.py +1017 -0
  79. pyemaps/errors.py +266 -0
  80. pyemaps/fileutils.py +1001 -0
  81. pyemaps/kdiffs.py +1221 -0
  82. pyemaps/license.txt +42 -0
  83. pyemaps/samples/adf.py +137 -0
  84. pyemaps/samples/al.img +0 -0
  85. pyemaps/samples/al_dpgen.py +57 -0
  86. pyemaps/samples/al_ediom.py +94 -0
  87. pyemaps/samples/convert_image.py +88 -0
  88. pyemaps/samples/dm_diff.py +225 -0
  89. pyemaps/samples/mask.im3 +0 -0
  90. pyemaps/samples/powder.py +51 -0
  91. pyemaps/samples/raw.img +0 -0
  92. pyemaps/samples/si_bloch.py +120 -0
  93. pyemaps/samples/si_constructor.py +51 -0
  94. pyemaps/samples/si_csf.py +59 -0
  95. pyemaps/samples/si_dif.py +134 -0
  96. pyemaps/samples/si_lacbed.py +59 -0
  97. pyemaps/samples/si_pyemaps.py +80 -0
  98. pyemaps/samples/si_scm.py +156 -0
  99. pyemaps/samples/si_stereo.py +116 -0
  100. pyemaps/scattering/__init__.py +26 -0
  101. pyemaps/scattering/sct_dec.py +31 -0
  102. pyemaps/spg/__init__.py +25 -0
  103. pyemaps/spg/spg_dec.py +60 -0
  104. pyemaps/stackimg.py +1031 -0
  105. pyemaps/xtal.py +222 -0
  106. pyemaps-2.0.0.dist-info/METADATA +231 -0
  107. pyemaps-2.0.0.dist-info/RECORD +112 -0
  108. pyemaps-2.0.0.dist-info/WHEEL +5 -0
  109. pyemaps-2.0.0.dist-info/entry_points.txt +2 -0
  110. pyemaps-2.0.0.dist-info/licenses/COPYING +798 -0
  111. pyemaps-2.0.0.dist-info/licenses/license.txt +42 -0
  112. 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)