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/emcontrols.py ADDED
@@ -0,0 +1,1017 @@
1
+ """
2
+ .. This file is part of pyEMAPS.
3
+
4
+ This class is a helper for handling pyEMAPS microscope controls.
5
+
6
+ There are two controls classes this module defines: `microscope controls <modules.html#pyemaps.emcontrols.EMControl>`_
7
+ and `simulations controls <modules.html#pyemaps.emcontrols.SIMControl>`_.
8
+
9
+ Since the latter changes much less frequently than the former,
10
+ simulation control is embedded as a member of a microscope controls class.
11
+
12
+ Simulation control constants and default values
13
+ -----------------------------------------------
14
+
15
+ .. data:: DEF_EXCITATION
16
+ :value: (0.3, 2.0)
17
+
18
+ .. data:: DEF_GMAX
19
+ :value: 3.5
20
+
21
+ .. data:: DEF_BMIN
22
+ :value: 0.1
23
+
24
+ .. data:: DEF_INTENSITY
25
+ :value: (0.0, 5)
26
+
27
+ .. data:: DEF_GCTL
28
+ :value: 6.0
29
+
30
+ .. data:: DEF_ZCTL
31
+ :value: 5.0
32
+
33
+ .. data:: DEF_OMEGA
34
+ :value: 10
35
+
36
+ .. data:: DEF_SAMPLING
37
+ :value: 8
38
+
39
+ .. data:: SAMPLE_THICKNESS
40
+ :value: (200, 200, 100)
41
+
42
+
43
+ Microscope control constants and default values
44
+ -----------------------------------------------
45
+
46
+ .. data:: DEF_TILT
47
+ :value: (0.0, 0.0)
48
+
49
+ .. data:: DEF_ZONE
50
+ :value: (0, 0, 1)
51
+
52
+ .. data:: DEF_DEFL
53
+ :value: (0.0, 0.0)
54
+
55
+ .. data:: DEF_KV
56
+ :value: 200.0
57
+
58
+ .. data:: DEF_CL
59
+ :value: 1000.0
60
+
61
+ .. data:: DEF_APERTURE
62
+ :value: 1.0
63
+
64
+ .. data:: DEF_XAXIS
65
+ :value: (0, 0, 0)
66
+
67
+ .. data:: DEF_PIXSIZE
68
+ :value: 25
69
+
70
+ .. data:: DEF_DETSIZE
71
+ :value: 512
72
+
73
+ .. data:: DEF_MODE
74
+ :value: 1
75
+
76
+ .. data:: DEF_CBED_DSIZE
77
+ :value: 0.16
78
+
79
+ .. pyEMAPS is free software. You can redistribute it and/or modify
80
+ .. it under the terms of the GNU General Public License as published
81
+ .. by the Free Software Foundation, either version 3 of the License,
82
+ .. or (at your option) any later version..
83
+
84
+ .. pyEMAPS is distributed in the hope that it will be useful,
85
+ .. but WITHOUT ANY WARRANTY; without even the implied warranty of
86
+ .. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
87
+ .. GNU General Public License for more details.
88
+
89
+ .. You should have received a copy of the GNU General Public License
90
+ .. along with pyEMAPS. If not, see `<https://www.gnu.org/licenses/>`_.
91
+
92
+ .. Contact supprort@emlabsoftware.com for any questions and comments.
93
+
94
+ .. ----
95
+
96
+ .. Author: EMLab Solutions, Inc.
97
+ .. Date: May 09, 2022
98
+
99
+ """
100
+
101
+ from . import EMCError
102
+ import math
103
+ EMC_TOLERANCE = 1.0e-07
104
+
105
+ sdefault = ' [**default**]'
106
+ from pyemaps import (
107
+ DEF_EXCITATION,
108
+ DEF_GMAX,
109
+ DEF_BMIN,
110
+ DEF_INTENSITY,
111
+ DEF_GCTL,
112
+ DEF_ZCTL,
113
+ DEF_XAXIS,
114
+ DEF_PIXSIZE,
115
+ DEF_DETSIZE,
116
+ DEF_CBED_DSIZE,
117
+ DEF_DSIZE_LIMITS,
118
+ DEF_MODE)
119
+
120
+ from pyemaps import (DEF_ZONE,
121
+ DEF_DEFL,
122
+ DEF_TILT,
123
+ DEF_CL,
124
+ DEF_KV,
125
+ DEF_APERTURE,
126
+ DEF_OMEGA,
127
+ DEF_SAMPLING,
128
+ DEF_THICKNESS)
129
+
130
+ DEF_SIMC = {
131
+ 'excitation': DEF_EXCITATION,
132
+ 'gmax': DEF_GMAX,
133
+ 'bmin': DEF_BMIN,
134
+ 'intensity': DEF_INTENSITY,
135
+ 'gctl': DEF_GCTL,
136
+ 'zctl': DEF_ZCTL,
137
+ 'omega': DEF_OMEGA,
138
+ 'sampling': DEF_SAMPLING,
139
+ 'sth': DEF_THICKNESS[0]
140
+ }
141
+ SIMC_DESC = {
142
+ 'excitation': 'Excitation error range in (min, max)',
143
+ 'gmax': 'Maximum recipricol vector length',
144
+ 'bmin': 'Beta perturbation cutoff',
145
+ 'intensity': 'Kinematic diffraction intensity cutoff level and scale in (level, scale)',
146
+ 'gctl': 'Maximum index number for g-list',
147
+ 'zctl': 'Maximum zone or Miller index number',
148
+ 'omega': 'Diagnization cutoff value',
149
+ 'sampling': 'Number of sampling points',
150
+ 'sth': 'Samples thickness'
151
+ }
152
+
153
+ def doc_dec_simc(k):
154
+ def doc_str(obj):
155
+ obj.__doc__ = obj.__doc__.format(SIMC_DESC[k])
156
+ return obj
157
+ return doc_str
158
+
159
+
160
+ def _print_default(sdecs, dv):
161
+ return sdecs + ' [default=' + str(dv) + ']: '
162
+
163
+ class SIMControl:
164
+ '''
165
+ Simulation controls, to be embedded in EMControl
166
+
167
+ '''
168
+ def __init__(self, excitation = DEF_SIMC['excitation'], \
169
+ gmax = DEF_SIMC['gmax'], \
170
+ bmin = DEF_SIMC['bmin'], \
171
+ intensity = DEF_SIMC['intensity'], \
172
+ gctl = DEF_SIMC['gctl'], \
173
+ zctl = DEF_SIMC['zctl']
174
+ ):
175
+
176
+ setattr(self, 'excitation', excitation)
177
+ setattr(self, 'gmax', gmax)
178
+ setattr(self, 'bmin', bmin)
179
+ setattr(self, 'intensity', intensity)
180
+ setattr(self, 'gctl', gctl)
181
+ setattr(self, 'zctl', zctl)
182
+ #Setting optional control parameters to defaults if not set
183
+ setattr(self, 'sampling', DEF_SIMC['sampling'])
184
+ setattr(self, 'omega', DEF_SIMC['omega'])
185
+ setattr(self, 'sth', DEF_SIMC['sth'])
186
+
187
+ def __call__(self, **kwargs):
188
+ """
189
+ Adding more simulation control patameters during Bloch
190
+ or other simulations.
191
+
192
+ """
193
+ for k, v in kwargs.items():
194
+ if k not in DEF_SIMC:
195
+ print(f'Key {k} does not belong to supported pyemaps simulation controls, ingored')
196
+
197
+ setattr(self, k, v)
198
+
199
+ @property
200
+ @doc_dec_simc('excitation')
201
+ def excitation(self):
202
+ '''{0}'''
203
+ return self._excitation
204
+
205
+ @property
206
+ @doc_dec_simc('gmax')
207
+ def gmax(self):
208
+ '''{0}'''
209
+ return self._gmax
210
+
211
+ @property
212
+ @doc_dec_simc('bmin')
213
+ def bmin(self):
214
+ '''{0}'''
215
+ return self._bmin
216
+
217
+ @property
218
+ @doc_dec_simc('intensity')
219
+ def intensity(self):
220
+ '''{0}'''
221
+ return self._intensity
222
+
223
+ @property
224
+ @doc_dec_simc('gctl')
225
+ def gctl(self):
226
+ '''{0}'''
227
+
228
+ return self._gctl
229
+
230
+ @property
231
+ @doc_dec_simc('zctl')
232
+ def zctl(self):
233
+ '''{0}'''
234
+ return self._zctl
235
+
236
+ @property
237
+ @doc_dec_simc('omega')
238
+ def omega(self):
239
+ '''{0}'''
240
+ return self._omega
241
+
242
+ @property
243
+ @doc_dec_simc('sampling')
244
+ def sampling(self):
245
+ '''{0}'''
246
+ return self._sampling
247
+
248
+ @property
249
+ @doc_dec_simc('sth')
250
+ def sth(self):
251
+ '''{0}'''
252
+ # '''
253
+ # Samples thickness, must be an integer - optional attributes
254
+ # of the emcontrols, set to default if not present
255
+ # '''
256
+
257
+ return self._sth
258
+
259
+ @excitation.setter
260
+ def excitation(self, excit):
261
+
262
+ if not excit or \
263
+ not isinstance(excit, tuple) or \
264
+ len(excit) != 2 or \
265
+ not isinstance(excit[0], (int, float)) or \
266
+ not isinstance(excit[1], (int, float)) or \
267
+ excit[0] > excit[1]:
268
+ raise EMCError('Excitation values must be a tuple of two numbers in ascending order')
269
+ self._excitation = excit
270
+
271
+ @gmax.setter
272
+ def gmax(self, gm):
273
+
274
+ if not isinstance(gm, (int, float)):
275
+ raise EMCError('gmax values must be a tuple of two numbers')
276
+
277
+ self._gmax = gm
278
+
279
+ @bmin.setter
280
+ def bmin(self, bm):
281
+
282
+ if not isinstance(bm, (int, float)):
283
+ raise EMCError('bmin values must be a tuple of two numbers')
284
+
285
+ self._bmin = bm
286
+
287
+ @intensity.setter
288
+ def intensity(self, intv):
289
+
290
+ if intv is None or \
291
+ not isinstance(intv, tuple) or \
292
+ len(intv) != 2 or \
293
+ not isinstance(intv[0], (int, float)) or \
294
+ not isinstance(intv[1], (int, float)) or \
295
+ intv[0] > intv[1]:
296
+ raise EMCError('kinematic intensity values must be ordered numberals')
297
+
298
+ self._intensity = intv
299
+
300
+ @gctl.setter
301
+ def gctl(self, gv):
302
+
303
+ if not isinstance(gv, (int, float)):
304
+ raise EMCError('gctl values must be a numeral')
305
+
306
+ self._gctl = int(gv)
307
+
308
+ @zctl.setter
309
+ def zctl(self, zv):
310
+ if not isinstance(zv, (int, float)):
311
+ raise EMCError('zctl values must be a numeral')
312
+
313
+ self._zctl = int(zv)
314
+
315
+ @omega.setter
316
+ def omega(self, ov):
317
+ if ov is None or not isinstance(ov, (int,float)):
318
+ raise EMCError("omega must be a numberal")
319
+
320
+ self._omega = ov
321
+
322
+ @sampling.setter
323
+ def sampling(self, sv):
324
+ try:
325
+ v = int(sv)
326
+ except ValueError:
327
+ raise EMCError("Invalid number of sampling points input")
328
+
329
+ self._sampling = v
330
+
331
+ @sth.setter
332
+ def sth(self, sv):
333
+ if sv is None or not isinstance(sv, (int, float)):
334
+ raise EMCError("Sample thickness must be a numberal")
335
+
336
+ self._sth = sv
337
+
338
+ def __lt__(self, other):
339
+ return self.__key__() < other.__key__()
340
+
341
+ def __key__(self):
342
+ data = self.__dict__
343
+ return [data[k] for k in data.keys()]
344
+
345
+ def __eq__(self, other):
346
+
347
+ if not isinstance(other, SIMControl):
348
+ print('Comparing object does not match')
349
+ return False
350
+
351
+ if self._intensity != other.intensity or \
352
+ self._zctl != other.zctl or \
353
+ self._gctl != other.gctl or \
354
+ self._sth != other.sth or \
355
+ self._sampling != other.sampling:
356
+ # print(f'intensity, zctl, gctl sampling and slice numer does not match:')
357
+ return False
358
+
359
+ for a, b in zip(self._excitation, other.excitation):
360
+ if not math.isclose(a, b, abs_tol=EMC_TOLERANCE):
361
+ # print(f'excitation does not match: {self._excitation} != {other.excitation}')
362
+ return False
363
+
364
+ if not math.isclose(self._gmax, other.gmax, abs_tol=EMC_TOLERANCE):
365
+ # print(f'gmax does not match: {self._gmax} != {other.gmax}')
366
+ return False
367
+
368
+ if not math.isclose(self._bmin, other.bmin, abs_tol=EMC_TOLERANCE):
369
+ # print(f'bmin does not match: {self._bmin} != {other.bmin}')
370
+ return False
371
+
372
+ if not math.isclose(self._omega, other.omega, abs_tol=EMC_TOLERANCE):
373
+ # print(f'omega does not match: {self._omega} != {other.omega}')
374
+ return False
375
+
376
+ return True
377
+
378
+ def _str_prop(self, k) -> str:
379
+ '''internal - out formatted string for k attributes'''
380
+
381
+ if k not in SIMC_DESC or not hasattr(self, k):
382
+ return EMCError('internal error!')
383
+
384
+ v = getattr(self, k)
385
+
386
+ return _print_default(SIMC_DESC[k], DEF_SIMC[k]) + str(v)
387
+
388
+ def _check_def(self, k) -> bool:
389
+ '''internal - checking if the attribute k is at default value'''
390
+ if k not in SIMC_DESC or not hasattr(self, k):
391
+ # return EMCError('internal error!')
392
+ print(f'Warning: simulation class does not have {k} attribute')
393
+ return False
394
+
395
+ v = getattr(self, k)
396
+
397
+ return v == DEF_SIMC[k]
398
+
399
+ def __str__(self) -> str:
400
+
401
+ simulation = ['Simulation Controls:']
402
+
403
+ for k in DEF_SIMC:
404
+ simulation.append(self._str_prop(k))
405
+
406
+ return "\n\t".join(simulation)
407
+
408
+ def plot_format(self):
409
+ '''
410
+ Format simulation controls in builtin display functions
411
+ Only plot those parameter that are not defaults
412
+
413
+ '''
414
+ simcstrs = []
415
+
416
+ for k in DEF_SIMC:
417
+
418
+ if self._check_def(k):
419
+ continue
420
+
421
+ format_str = k + '='
422
+ v = getattr(self, k)
423
+
424
+ if k == 'excitation':
425
+ format_str += '({:.2f},{:.2f})'.format(v[0], v[1])
426
+ elif k == 'gmax':
427
+ format_str += '{:.2f}'.format(v)
428
+ elif k == 'bim':
429
+ format_str += '{:.2f}'.format(v)
430
+ elif k == 'intensity':
431
+ format_str += '({:.2f},{:.2f})'.format(self._intensity[0], self._intensity[1])
432
+ elif k == 'gctl':
433
+ format_str += '{:f}'.format(v)
434
+ elif k == 'zctl':
435
+ format_str += '{:f}'.format(v)
436
+ elif k == 'omega':
437
+ format_str += '{:.2f}'.format(v)
438
+ elif k == 'sampling':
439
+ format_str += '{:d}'.format(v)
440
+ elif k == 'sth':
441
+ format_str += '{:.2f}'.format(v)
442
+ else:
443
+ continue
444
+
445
+ simcstrs.append(format_str)
446
+
447
+ return ';'.join(simcstrs)
448
+
449
+ @classmethod
450
+ def from_random(cls):
451
+
452
+ ''' For backward compatibility only'''
453
+ return cls._from_random()
454
+
455
+ @classmethod
456
+ def _from_random(cls):
457
+
458
+ ''' Design for internal testing purpose only'''
459
+
460
+ import random
461
+
462
+ # random excitation
463
+ excit = (random.uniform(0.2, 0.4), random.uniform(1.0, 3.0))
464
+
465
+ # random gcuttoffs
466
+ bmn = random.uniform(0.05, 0.15)
467
+ gmx = random.uniform(2.0, 5.0)
468
+
469
+ # random intensity
470
+ inten = (0.0, random.randint(4,7))
471
+
472
+ # random zone and gcutoffs
473
+ gc = random.uniform(5.0, 7.0)
474
+ zc = random.uniform(4.0, 6.0)
475
+ try:
476
+ sc = cls(excitation = excit, \
477
+ intensity = inten, \
478
+ gmax = gmx, \
479
+ bmin = bmn, \
480
+ gctl = gc, \
481
+ zctl = zc)
482
+
483
+ except Exception as e:
484
+ raise EMCError(str(e))
485
+ else:
486
+ return sc
487
+
488
+ DEF_EMC = {
489
+ 'mode': {'defval': DEF_MODE, 'desc': 'Simulation mode'},
490
+ 'dsize': {'defval': DEF_CBED_DSIZE, 'desc': 'Diffracted beam size'},
491
+ 'zone': {'defval': DEF_ZONE, 'desc': 'Starting zone axis'},
492
+ 'tilt': {'defval': DEF_TILT, 'desc': 'Tilt in x and y directions (x,y)'},
493
+ 'defl': {'defval': DEF_DEFL, 'desc': 'Shifts in x and y directions (x, y)'},
494
+ 'cl': {'defval': DEF_CL, 'desc': 'Camera length'},
495
+ 'vt': {'defval': DEF_KV, 'desc': 'High voltage'},
496
+ 'xaxis': {'defval': DEF_XAXIS, 'desc': 'Crystal horizontal axis in reciprical space'},
497
+ 'pix_size': {'defval': DEF_PIXSIZE, 'desc': 'Detector pixel size in microns'},
498
+ 'det_size': {'defval': DEF_DETSIZE, 'desc': 'Detector size in microns'},
499
+ 'aperture': {'defval': DEF_APERTURE, 'desc': 'Objective lense aperture'},
500
+ 'simc': {'defval': None, 'desc': 'Simulation Controls'}
501
+ }
502
+
503
+ # can we use the above lookup for the property docstring?
504
+ def doc_dec(k):
505
+ def doc_str(obj):
506
+ obj.__doc__ = obj.__doc__.format(DEF_EMC[k]['desc'])
507
+ return obj
508
+ return doc_str
509
+
510
+ class EMControl:
511
+ '''
512
+ Microscope and sample property controls class. Its attributes include:
513
+
514
+ * **tilt**: sample tilt in x and y directory (x,y)
515
+ * **zone**: starting zone axis
516
+ * **defl**: shifts in x and y direction (x, y)
517
+ * **cl**: cameral length
518
+ * **vt**: hight voltage in kilo-volts
519
+ * **simc*: `SIMControl <pyemaps.emcontrols.html#pyemaps.emcontrols.SIMControl>`_ object
520
+
521
+ Other optional emcontrol parameters:
522
+
523
+ * **aperture**: Objective len aperture
524
+ * **pix_size**: detector pixel size
525
+ * **det_size**: detector size
526
+ * **xaxis**: crystal horizontal axis in reciprical space
527
+ * **mode**: simulation mode: 1- normal, 2-CBED
528
+ * **dsize**: diffracted beam size
529
+
530
+ '''
531
+
532
+ def __init__(self, tilt = DEF_TILT,
533
+ zone = DEF_ZONE,
534
+ defl = DEF_DEFL,
535
+ vt = DEF_KV,
536
+ cl = DEF_CL,
537
+ simc = SIMControl()):
538
+
539
+ emc_dict = dict(tilt = tilt,
540
+ zone = zone,
541
+ defl = defl,
542
+ vt = vt,
543
+ cl = cl)
544
+ for k, v in emc_dict.items():
545
+ setattr(self, k, v)
546
+
547
+ setattr(self, 'simc', simc)
548
+
549
+ setattr(self, 'mode', DEF_MODE)
550
+ setattr(self, 'aperture', DEF_APERTURE)
551
+ setattr(self, 'dsize', DEF_CBED_DSIZE) #(not used mode == 1)
552
+ setattr(self, 'pix_size', DEF_PIXSIZE)
553
+ setattr(self, 'det_size', DEF_DETSIZE)
554
+ setattr(self, 'xaxis', DEF_XAXIS)
555
+
556
+ def __call__(self, **kwargs):
557
+ """
558
+ Additional class members added as simulation is run.
559
+
560
+ """
561
+
562
+ for k, v in kwargs.items():
563
+ if k not in DEF_EMC:
564
+ print(f'key {k} is not in pyemaps microscope control keys, ignored')
565
+ continue
566
+ setattr(self, k, v)
567
+
568
+ @classmethod
569
+ def from_dict(cls, emc_dict):
570
+
571
+ '''
572
+ Create an EMControl object from a python dict object
573
+
574
+ :param emc_dict: Microscope control dict object
575
+ :type emc_dict: dict, required
576
+ :raises: EMCError, if validation fails
577
+
578
+ '''
579
+ if not isinstance(emc_dict, dict):
580
+ raise EMCError('invalid data input in constructing EMC from a dictionary')
581
+
582
+ t = z = d = vt = c = None
583
+ for k, v in emc_dict.items():
584
+ if k not in DEF_EMC:
585
+ print(f'Invilid key {k} found in the input, ignored')
586
+ continue
587
+
588
+ if k == 'tilt':
589
+ t = v
590
+ if k == 'zone':
591
+ z = v
592
+ if k == 'defl':
593
+ d = v
594
+ if k == 'vt':
595
+ vt = v
596
+ if k == 'cl':
597
+ c = v
598
+ sc = SIMControl()
599
+
600
+ return cls(tilt = t, zone = z, defl = d, vt = vt, cl = c, simc = sc)
601
+
602
+ @property
603
+ @doc_dec('zone')
604
+ def zone(self):
605
+ """{0}"""
606
+ return self._zone
607
+
608
+ @property
609
+ @doc_dec('tilt')
610
+ def tilt(self):
611
+ '''{0}'''
612
+ return self._tilt
613
+
614
+ @property
615
+ @doc_dec('defl')
616
+ def defl(self):
617
+ '''{0}'''
618
+ return self._defl
619
+
620
+ @property
621
+ @doc_dec('cl')
622
+ def cl(self):
623
+ '''{0}'''
624
+ return self._cl
625
+
626
+ @property
627
+ @doc_dec('vt')
628
+ def vt(self):
629
+ '''{0}'''
630
+ return self._vt
631
+
632
+ @property
633
+ @doc_dec('simc')
634
+ def simc(self):
635
+ '''{0}'''
636
+ return self._simc
637
+
638
+ @property
639
+ @doc_dec('xaxis')
640
+ def xaxis(self):
641
+ '''{0}'''
642
+ return self._xaxis
643
+
644
+ @property
645
+ @doc_dec('aperture')
646
+ def aperture(self):
647
+ '''{0}'''
648
+ return self._aperture
649
+
650
+ @property
651
+ @doc_dec('pix_size')
652
+ def pix_size(self):
653
+ '''{0}'''
654
+ return self._pix_size
655
+
656
+ @property
657
+ @doc_dec('det_size')
658
+ def det_size(self):
659
+ '''{0}'''
660
+ return self._det_size
661
+
662
+ @property
663
+ @doc_dec('mode')
664
+ def mode(self):
665
+ '''{0}'''
666
+ return self._mode
667
+
668
+ @property
669
+ @doc_dec('dsize')
670
+ def dsize(self):
671
+ '''{0}'''
672
+ return self._dsize
673
+
674
+
675
+ @zone.setter
676
+ def zone(self, zv):
677
+ if not isinstance(zv, tuple) or len(zv) != 3 or \
678
+ list(map(type, zv)) != [int, int, int]:
679
+ raise EMCError("Zone axis must be tuple of three intergers")
680
+
681
+ if zv == (0,0,0):
682
+ raise EMCError("Zone axis must not be (0,0,0)")
683
+
684
+ self._zone = zv
685
+
686
+ @tilt.setter
687
+ def tilt(self, tl):
688
+ if not isinstance(tl, tuple) or len(tl) != 2:
689
+ raise EMCError("Tilt must be tuple of two numbers")
690
+
691
+ typelist = list(map(type, tl))
692
+
693
+ for vt in typelist:
694
+ if vt != float and vt != int:
695
+ raise EMCError('Input values for tilt must be nmeric')
696
+
697
+ self._tilt = tl
698
+
699
+ @defl.setter
700
+ def defl(self, df):
701
+ if not isinstance(df, tuple) or len(df) != 2:
702
+ raise EMCError("deflection must be tuple of two numbers")
703
+
704
+ typelist = list(map(type, df))
705
+
706
+ for vt in typelist:
707
+ if vt != float and vt != int:
708
+ raise EMCError('Input values for deflection must be nmeric')
709
+
710
+ self._defl = df
711
+
712
+ @cl.setter
713
+ def cl(self, clen):
714
+ if not isinstance(clen, (int, float)):
715
+ raise ValueError("Camera length must be of number")
716
+
717
+ self._cl = clen
718
+
719
+ @vt.setter
720
+ def vt(self, kv):
721
+ if not isinstance(kv, (int,float)):
722
+ raise EMCError("Voltage must be of integer")
723
+
724
+ self._vt = kv
725
+
726
+ @xaxis.setter
727
+ def xaxis(self, xv):
728
+ if xv is None:
729
+ self._xaxis = DEF_XAXIS
730
+ else:
731
+ if not isinstance(xv, tuple) or \
732
+ len(xv) != 3 or \
733
+ not isinstance(xv[0], int) or \
734
+ not isinstance(xv[1], int) or \
735
+ not isinstance(xv[2], int):
736
+ raise EMCError('Invalid crystal horizon axis')
737
+
738
+ self._xaxis = xv
739
+
740
+
741
+ @simc.setter
742
+ def simc(self, sc):
743
+ if not isinstance(sc, SIMControl):
744
+ raise EMCError("Simulation control invlid")
745
+
746
+ self._simc = sc
747
+
748
+ @aperture.setter
749
+ def aperture(self, av):
750
+ if av is None:
751
+ self._aperture = DEF_APERTURE
752
+ else:
753
+ if not isinstance(av, (int,float)):
754
+ raise EMCError("Voltage must be a numberal")
755
+
756
+ self._aperture = av
757
+
758
+ @pix_size.setter
759
+ def pix_size(self, pv):
760
+ if pv is None:
761
+ self._pix_size = DEF_PIXSIZE
762
+ else:
763
+ if not isinstance(pv, int):
764
+ raise EMCError('Detector pixel size in microns must be an integer')
765
+
766
+ self._pix_size = pv
767
+
768
+ @det_size.setter
769
+ def det_size(self, dv):
770
+ if dv is None:
771
+ self._det_size = DEF_DETSIZE
772
+ else:
773
+ if not isinstance(dv, int):
774
+ raise EMCError('Detector size must be integer')
775
+
776
+ self._det_size = dv
777
+
778
+ @mode.setter
779
+ def mode(self, mv):
780
+ if mv is None:
781
+ self._mode = DEF_MODE
782
+ else:
783
+ if not isinstance(mv, int) or \
784
+ (mv != 1 and mv != 2):
785
+ raise EMCError('mode must be integer of value 1 or 2')
786
+ # in case of disk size is set, validate, warn
787
+ if hasattr(self, 'dsize') and mv == 2:
788
+ ds = self._dsize
789
+ ds_min, ds_max = DEF_DSIZE_LIMITS
790
+
791
+ if ds < ds_min or ds > ds_max:
792
+ print(f'Warning: beam size is out of range {DEF_DSIZE_LIMITS}')
793
+ print(f'Please reset the value of beam size before continuing')
794
+
795
+ self._mode = mv
796
+
797
+ @dsize.setter
798
+ def dsize(self, dv):
799
+ if dv is None:
800
+ self._dsize = DEF_CBED_DSIZE
801
+ else:
802
+ if not isinstance(dv, (int,float)):
803
+ raise EMCError('Detector size must be integer')
804
+
805
+ # in case of CBED mode, the disk size must be in DEF_DSIZE_LIMITS range
806
+ if self._mode == 2:
807
+ ds_min, ds_max = DEF_DSIZE_LIMITS
808
+
809
+ if dv < ds_min or dv > ds_max:
810
+ raise EMCError(f'Beam size is out of range {DEF_DSIZE_LIMITS}')
811
+
812
+
813
+ self._dsize = dv
814
+
815
+ def __lt__(self, other):
816
+ return self.__key__() < other.__key__()
817
+
818
+ def __key__(self):
819
+ data = self.__dict__
820
+
821
+ td = []
822
+ for k in data:
823
+ if 'simc' not in k:
824
+ td.append(data[k])
825
+ else:
826
+ td.append(data[k].__key__())
827
+
828
+ return td
829
+
830
+ def __eq__(self, other):
831
+ if not isinstance(other, EMControl):
832
+ raise EMCError("Comparison must be done with another EMControl object")
833
+
834
+ if self.mode != other.mode or \
835
+ self._pix_size != other.pix_size or \
836
+ self._det_size != other.det_size or \
837
+ self._zone != other.zone or \
838
+ self._xaxis !=other.xaxis:
839
+ # print(f'integer controls: mode pix_size det_size zone or xaxis do not match:')
840
+ return False
841
+
842
+ if not math.isclose(self._aperture, other.aperture, abs_tol=EMC_TOLERANCE):
843
+ # print(f'aperture controls: does not match: {self._aperture} != {other.aperture}')
844
+ return False
845
+
846
+ if not math.isclose(self._dsize, other.dsize, abs_tol=EMC_TOLERANCE):
847
+ # print(f'dsize controls: does not match: {self._dsize} != {other.dsize}')
848
+ return False
849
+
850
+ if not math.isclose(self._vt, other.vt, abs_tol=EMC_TOLERANCE):
851
+ # print(f'vt controls: does not match: {self._vt} != {other.vt}')
852
+ return False
853
+
854
+ if not math.isclose(self._cl, other.cl, abs_tol=EMC_TOLERANCE):
855
+ # print(f'cl controls: does not match: {self._cl} != {other.cl}')
856
+ return False
857
+
858
+ for d1, d2 in zip(self._defl, other.defl):
859
+ if not math.isclose(d1, d2, abs_tol=EMC_TOLERANCE):
860
+ # print(f'defl controls: does not match: {self._defl} != {other.defl}')
861
+ return False
862
+
863
+ for t1, t2 in zip(self._tilt, other.tilt):
864
+ if not math.isclose(t1, t2, abs_tol=EMC_TOLERANCE):
865
+ # print(f'tilt controls: does not match: {self._tilt} != {other.tilt}')
866
+ return False
867
+
868
+ if not self._simc == other.simc:
869
+ return False
870
+
871
+ return True
872
+
873
+ def _str_prop(self, k) -> str:
874
+ '''internal'''
875
+ if k not in DEF_EMC or not hasattr(self, k):
876
+ return EMCError('internal error!')
877
+
878
+ v = getattr(self, k)
879
+ defv = DEF_EMC[k]['defval']
880
+ defdesc = DEF_EMC[k]['desc']
881
+
882
+ if k == 'mode':
883
+ cstr=[]
884
+ ds_def = DEF_EMC['dsize']['defval']
885
+ ds_desc = _print_default(DEF_EMC['dsize']['desc'], ds_def)
886
+ if v == defv:
887
+ cstr.append(defdesc + ' (default=Normal): Normal ')
888
+ cstr.append(ds_desc + ' Undefined, not used')
889
+ elif v == defv + 1:
890
+ cstr.append(defdesc + ': CBED')
891
+ try:
892
+ ds = self._dsize
893
+ except AttributeError as e:
894
+ raise Exception from e
895
+ else:
896
+ if ds <= 0:
897
+ cstr.append(ds_desc + str(ds) + ' ERROR!')
898
+ else:
899
+ cstr.append(ds_desc + str(ds))
900
+ else:
901
+ cstr.append('Mode: Unknown (Error!)')
902
+ cstr.append('Diffraction beam size: Undefined')
903
+ return '\n'.join(cstr)
904
+
905
+ if k == 'dsize':
906
+ return '' # do nothing, already taken care of
907
+
908
+ sdef = _print_default(defdesc, defv)
909
+ return sdef + str(v)
910
+
911
+ def __str__(self):
912
+ '''
913
+ Readable printout of the EMC class object.
914
+ The output will include all class attribute values and annotate
915
+ those with default values with [**default**] string
916
+ '''
917
+
918
+ cstr = ['Electron Microscope Controls:',]
919
+
920
+ for k in DEF_EMC:
921
+ if k != 'dsize' and k !='simc':
922
+ cstr.append(self._str_prop(k))
923
+
924
+ cstr.append(str(self._simc))
925
+
926
+ return '\n'.join(cstr)
927
+
928
+
929
+ def _check_def(self, k) -> bool:
930
+ '''internal - checking if the attribute k is at default value'''
931
+ if k not in DEF_EMC or not hasattr(self, k):
932
+
933
+ print(f'Warning: EMControl class does not have {k} attribute')
934
+ return False
935
+
936
+ v = getattr(self, k)
937
+
938
+ if k == 'dsize':
939
+ if self._mode == 1:
940
+ return True
941
+ elif self._mode == 2 and v == DEF_EMC[k]['defval']:
942
+ return True
943
+ else:
944
+ return False
945
+
946
+ if k == 'simc':
947
+ return v == SIMControl()
948
+
949
+ return v == DEF_EMC[k]['defval']
950
+
951
+ def plot_format(self):
952
+ '''
953
+ Format simulation controls in builtin display functions
954
+ Only plot those parameter that are not defaults
955
+
956
+ '''
957
+ emcstrs = []
958
+
959
+ for k in DEF_EMC:
960
+
961
+ if self._check_def(k):
962
+ continue
963
+
964
+ format_str = k + '='
965
+ v = getattr(self, k)
966
+
967
+ if k == 'mode':
968
+ format_str += 'CBED'
969
+ elif k == 'dszie':
970
+ format_str += '{:.2f}'.format(v)
971
+ elif k == 'zone' or k == 'xaxis':
972
+ format_str += '({:d},{:d},{:d})'.format(v[0],v[1],v[2])
973
+ elif k == 'tilt' or k == 'defl':
974
+ format_str += '({:.2f},{:.2f})'.format(v[0],v[1])
975
+ elif k == 'cl' or k == 'vt':
976
+ format_str += '{:.2f}'.format(v)
977
+ elif k == 'aperture':
978
+ format_str += '{:.2f}'.format(v)
979
+ elif k == 'pix_size' or k == 'det_size':
980
+ format_str += '{:f}'.format(v)
981
+ else:
982
+ continue
983
+
984
+ emcstrs.append(format_str)
985
+
986
+ cstr = ''
987
+
988
+ if len(emcstrs) != 0:
989
+ cstr = cstr + ';'.join(emcstrs)
990
+
991
+ simstr = self._simc.plot_format()
992
+
993
+ if len(simstr) != 0 and len(cstr) != 0:
994
+ return cstr + '\n' + simstr
995
+
996
+ if len(simstr) == 0 and len(cstr) != 0:
997
+ return cstr
998
+
999
+ if len(simstr) != 0 and len(cstr) == 0:
1000
+ return simstr
1001
+
1002
+ return ';'.join(emcstrs)
1003
+
1004
+ @staticmethod
1005
+ def def_dict():
1006
+ retdict={}
1007
+ for k in DEF_EMC:
1008
+
1009
+ if k == 'simc':
1010
+ for sk, sv in DEF_SIMC.items():
1011
+ retdict[sk] = sv
1012
+ continue
1013
+
1014
+ retdict[k] = DEF_EMC[k]['defval']
1015
+
1016
+
1017
+ return retdict