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
@@ -0,0 +1,779 @@
1
+ '''
2
+ .. This file is part of pyEMAPS
3
+
4
+ .. ----
5
+
6
+ .. pyEMAPS is free software. You can redistribute it and/or modify
7
+ .. it under the terms of the GNU General Public License as published
8
+ .. by the Free Software Foundation, either version 3 of the License,
9
+ .. or (at your option) any later version..
10
+
11
+ .. pyEMAPS is distributed in the hope that it will be useful,
12
+ .. but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ .. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ .. GNU General Public License for more details.
15
+
16
+ .. You should have received a copy of the GNU General Public License
17
+ .. along with pyEMAPS. If not, see `<https://www.gnu.org/licenses/>`_.
18
+
19
+ .. Contact supprort@emlabsoftware.com for any questions and comments.
20
+
21
+ .. ----
22
+
23
+ .. Author: EMLab Solutions, Inc.
24
+ .. Date Created: May 07, 2022
25
+
26
+ '''
27
+ def add_bloch(target):
28
+ """
29
+
30
+ Dynamic Simulation Specific Controls Default Values:
31
+ ----------------------------------------------------
32
+
33
+ .. data:: DEF_APERTURE
34
+ :value: 1.0
35
+
36
+ .. data:: DEF_OMEGA
37
+ :value: 10
38
+
39
+ .. data:: DEF_SAMPLING
40
+ :value: 8
41
+
42
+ .. data:: DEF_PIXSIZE
43
+ :value: 25
44
+
45
+ .. data:: DEF_DETSIZE
46
+ :value: 512
47
+
48
+ .. data:: DEF_CBED_DSIZE
49
+ :value: 0.16
50
+
51
+ .. data:: DEF_THICKNESS
52
+ :value: (200, 200, 100)
53
+
54
+ .. data:: DEF_DSIZE_LIMITS
55
+ :value: (0.01, 0.5)
56
+
57
+ """
58
+ from . import bloch, dif
59
+ from .. import DEF_APERTURE, \
60
+ DEF_THICKNESS, \
61
+ DEF_SAMPLING, \
62
+ DEF_PIXSIZE, \
63
+ DEF_DETSIZE, \
64
+ MAX_DEPTH, \
65
+ DEF_OMEGA, \
66
+ DEF_CBED_DSIZE, \
67
+ DEF_MODE, \
68
+ DEF_DSIZE_LIMITS, \
69
+ DEF_XAXIS
70
+ from .. import BImgList, EMC, SIMC
71
+
72
+ from ..fileutils import compose_ofn
73
+ from .. import BlochError,BlochListError
74
+ import numpy as np
75
+ from numpy import asfortranarray as farray
76
+ from .. import TY_NORMAL, TY_LACBED
77
+
78
+ BIMG_EXT = '.im3'
79
+ MAX_BIMGFN = 256
80
+ CBED_MODE = DEF_MODE + 1
81
+
82
+ def _getBlochFN(self):
83
+ '''
84
+ Constructs a bloch image output file name.
85
+
86
+ Refer to :ref:`Environment Variables <Environment Variables>`
87
+ for how this file name is constructed.
88
+
89
+ '''
90
+ cfn = compose_ofn(None, self.name, ty='bloch')+BIMG_EXT
91
+
92
+ l = len(cfn)
93
+ if l > MAX_BIMGFN:
94
+ raise BlochError('Bloch image file name too long, it cant excced 256')
95
+
96
+ ffn = farray(np.empty((256), dtype='c'))
97
+ for i in range(l):
98
+ ffn[i] = cfn[i]
99
+
100
+ return cfn, ffn, l
101
+
102
+ def beginBloch(self, aperture = DEF_APERTURE,
103
+ omega = DEF_OMEGA,
104
+ sampling = DEF_SAMPLING,
105
+ dbsize = DEF_CBED_DSIZE,
106
+ em_controls = EMC(cl=200, # set smaller that 1000 default value
107
+ simc = SIMC(gmax=1.0,
108
+ excitation=(0.3,1.0)
109
+ )
110
+ )
111
+ ):
112
+ """
113
+
114
+ Begins a dynamic diffraction (Bloch) simulation session.
115
+ The simulation results are retained in the session between this and
116
+ `endBloch call <pyemaps.crystals.html#pyemaps.crystals.Crystal.endBloch>`_.
117
+
118
+ :param aperture: Optional. Objective aperture
119
+ :type aperture: float
120
+
121
+ :param omega: Optional. Diagnization cutoff value
122
+ :type omega: float
123
+
124
+ :param sampling: Optional. Number of sampling points
125
+ :type sampling: int
126
+
127
+ :param dbsize: Diffracted beams size.
128
+ :type dbsize: float, optional
129
+
130
+ :param em_controls: Optional. electron `microscope control <pyemaps.emcontrols.html#module-pyemaps.emcontrols>`_ object.
131
+ :type em_controls: pyemaps.EMC
132
+
133
+ :return: a tuple (n, ns) where ns is a list of sampling points; n is the number of sampling points
134
+ :rtype: tuple
135
+
136
+ Default values:
137
+
138
+ ::
139
+
140
+ DEF_APERTURE = 1.0
141
+ DEF_OMEGA = 10
142
+ DEF_SAMPLING = 8
143
+ DEF_CBED_DSIZE - 0.16
144
+ DEF_DSIZE_LIMITS =(0.01, 0.5)
145
+
146
+ .. note::
147
+
148
+ During the simulation session, results are retained in pyemaps
149
+ bloch module. The following methods are used to retrieve the
150
+ result before the end of session:
151
+
152
+ 1. `getBlochimages <pyemaps.crystals.html#pyemaps.crystals.Crystal.getBlochImages>`_.
153
+ Retrieve a list of bloch images
154
+
155
+ 2. `getSCMatrix <pyemaps.crystals.html#pyemaps.crystals.Crystal.getSCMatrix>`_.
156
+ Retrieve a scattering matrix at a sampling point
157
+
158
+ .. note::
159
+
160
+ Other information available during the session:
161
+
162
+ a. List of sampling points, diffraction beams tilts etc
163
+ with `getIBDetails <pyemaps.crystals.html#pyemaps.crystals.Crystal.getIBDetails>`_;
164
+
165
+ b. getEigen function is folded into getSCMatrix call starting from Stable verion 1.0.3
166
+
167
+ c. Diagnization Miller indexes at each sampling point:
168
+ `getBeams <pyemaps.crystals.html#pyemaps.crystals.Crystal.getBeams>`_;
169
+
170
+ """
171
+ if aperture is None or not isinstance(aperture, (int, float)):
172
+ raise BlochError('Objective aperture must be valid numberal')
173
+
174
+ if omega is None or not isinstance(omega, int):
175
+ raise BlochError('Diagnization cutoff value must be valid numberal')
176
+
177
+ if dbsize is None or not isinstance(dbsize, (int,float)) or \
178
+ dbsize < DEF_DSIZE_LIMITS[0] or dbsize > DEF_DSIZE_LIMITS[1]:
179
+ raise BlochError(f'Diffracted beam size must be in range {DEF_DSIZE_LIMITS}')
180
+
181
+ if em_controls is None or not isinstance(em_controls, EMC):
182
+ raise BlochError('Control object must be of EMControl type')
183
+
184
+
185
+ dif.initcontrols()
186
+ dif.setmode(CBED_MODE) # alway in CBED mode
187
+
188
+
189
+ # setting default simulation controls
190
+ self.set_sim_controls(em_controls.simc)
191
+
192
+ dif.setdisksize(dbsize)
193
+
194
+ self.load()
195
+
196
+ tx, ty = em_controls.tilt[0], em_controls.tilt[1]
197
+ dx, dy = em_controls.defl[0], em_controls.defl[1]
198
+ z = em_controls.zone
199
+ vt, cl = em_controls.vt, em_controls.cl
200
+
201
+ dif.setsamplecontrols(tx, ty, dx, dy)
202
+ dif.setemcontrols(cl, vt)
203
+ dif.setzone(z[0], z[1], z[2])
204
+
205
+ if em_controls.xaxis != DEF_XAXIS:
206
+ xa = em_controls.xaxis
207
+ dif.set_xaxis(1, xa[0], xa[1], xa[2])
208
+
209
+ ret = dif.diffract(1)
210
+ if ret == 0:
211
+ dif.diff_internaldelete(1)
212
+ raise BlochError('Error bloch runtime1')
213
+
214
+ dif.diff_internaldelete(1)
215
+
216
+ ret = bloch.dobloch(aperture,omega,sampling,0.0)
217
+
218
+ if ret == 2:
219
+ raise BlochError('Predefined Bloch computation resource limit reached')
220
+
221
+ if ret != 0:
222
+ raise BlochError('Error computing dynamic diffraction')
223
+
224
+ nsampling = bloch.get_nsampling()
225
+ sampling_points = farray(np.zeros((2, nsampling)), dtype=int)
226
+ sampling_points, ret = bloch.get_samplingpoints(sampling_points)
227
+
228
+ if ret != 0:
229
+ raise BlochError('Failed to retrive sampling points used in scattering matrix run')
230
+
231
+ spoints = np.transpose(sampling_points)
232
+
233
+ # convert the np array to a array of python native tuple
234
+ sp = [(list(p)[0].item(),list(p)[1].item()) for p in spoints]
235
+
236
+ # updating controls with input params as optional attributes for easy handling
237
+ em_controls(mode = 2,
238
+ dsize = dbsize,
239
+ aperture = aperture)
240
+
241
+ em_controls.simc(omega = omega,
242
+ sampling = sampling
243
+ )
244
+ # update x-axis if it was set to (0,0,0)
245
+ # and new xaxis is calculated by the backend
246
+
247
+ if em_controls.xaxis == DEF_XAXIS:
248
+ xa1 = 0
249
+ xa2 = 2
250
+ xa3 = 0
251
+ xa1, xa2, xa3 = dif.get_xaxis()
252
+ em_controls(xaxis = (xa1,xa2,xa3))
253
+
254
+ self.session_controls=em_controls
255
+ return nsampling, sp
256
+
257
+ def getBlochImages(self,
258
+ sample_thickness = DEF_THICKNESS,
259
+ pix_size = DEF_PIXSIZE,
260
+ det_size = DEF_DETSIZE,
261
+ nType = TY_NORMAL,
262
+ bSave = False):
263
+ """
264
+ Retrieves a set of dynamic diffraction image from the simulation
265
+ sessiom marked by:
266
+
267
+ `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_. and
268
+ `endBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.endBloch>`_.
269
+
270
+ :param thickness: sample thickness range and step in tuple of three integers (th_start, th_end, th_step)
271
+ :type thickness: int, optional
272
+
273
+ :param pix_size: Detector pixel size in microns
274
+ :type pix_size: int, optional
275
+
276
+ :param det_size: Detector size or output image size
277
+ :type det_size: int, optional
278
+
279
+ :param nType: type of bloch images generated. 0 for normal or 1 for large angle CBED images
280
+ :type nType: int, optional. defaults to 0
281
+
282
+ :param bSave: True - save the output to a raw image file with extension of 'im3'
283
+ :type bSave: bool, optional
284
+
285
+ :return: `BImgList <pyemaps.ddiffs.html#pyemaps.ddiffs.BlochImgs>`_ object
286
+ :rtype: BImgList
287
+
288
+ Default values:
289
+
290
+ ::
291
+
292
+ DEF_PIXSIZE = 25
293
+ DEF_DETSIZE = 512
294
+ DEF_THICKNESS = (200, 200, 100)
295
+
296
+ .. warning::
297
+
298
+ Dynamic diffraction pattern generation or Bloch has an extensive memory
299
+ requirement for regular image generation:
300
+
301
+ nType = TY_NORMAL
302
+
303
+ Even more so for a large angle CBED generation when nType = TY_LACBED.
304
+
305
+ To reduce the memory needed, it is recommended to lower input parameters, particularly:
306
+
307
+ - The detector size *det_size*.
308
+ A reduction of the default value of 512 to 218 for example can reduce memory usage in
309
+ pyemaps without losing accuracies of the results.
310
+
311
+ - The number of sampling points in *sampling*.
312
+ With less available memory on the system where pyemaps is running, decreased sampling
313
+ points in *sampling* parameter can make a big difference in pyemaps performance.
314
+ """
315
+ from copy import deepcopy
316
+
317
+ if pix_size is None or not isinstance(pix_size, int):
318
+ raise BlochListError('Pixel size input must be valid integert')
319
+
320
+ if det_size is None or not isinstance(det_size, int):
321
+ raise BlochListError('Pixel size input must be valid integert')
322
+
323
+ if sample_thickness is None or \
324
+ not all(isinstance(v, int) for v in sample_thickness) or \
325
+ len(sample_thickness) !=3:
326
+ raise BlochListError('Sample thickness input must be valid tuple of three integers')
327
+
328
+ th_start, th_end, th_step = sample_thickness
329
+
330
+ if th_start > th_end or th_step <= 0:
331
+ raise BlochListError('Sample thickness input must be valid integers range')
332
+
333
+ thlist = []
334
+ if th_start == th_end:
335
+ thlist.append(th_start)
336
+ else:
337
+ thlist = list(range(th_start, th_end, th_step))
338
+ thlist.append(th_end)
339
+
340
+ ndep = len(thlist)
341
+
342
+ if ndep > MAX_DEPTH:
343
+ raise BlochListError(f'Number of sample thickness cannot exceed {MAX_DEPTH}')
344
+
345
+ # save the input as optional control attributes
346
+ imgfn =''
347
+ if bSave:
348
+ imgfn, bfn, l = self._getBlochFN()
349
+ if l > MAX_BIMGFN-1:
350
+ raise BlochError(f'File name must not exceed {MAX_BIMGFN-1}')
351
+
352
+ if bloch.openimgfile(det_size, ndep, bfn, l) != 0:
353
+ raise BlochError('Error opening file for write, check if you have write permission')
354
+
355
+ isLACBED = (nType == TY_LACBED)
356
+ # for th in thlist:
357
+ nslices = ndep
358
+ nout = 0
359
+ if isLACBED:
360
+ nout = bloch.getncalcbeams()
361
+ nslices = ndep*nout
362
+
363
+ bimg = farray(np.zeros((det_size, det_size, nslices), dtype=np.float32))
364
+ thl = farray(np.array(thlist), dtype=int)
365
+
366
+ bimg, ret = bloch.getimage(thl,
367
+ bimg,
368
+ teta = 0,
369
+ pix = pix_size,
370
+ det = det_size,
371
+ blacbed = isLACBED,
372
+ bsave = bSave)
373
+
374
+ if(ret != 0):
375
+ raise BlochError("bloch image generation failed!")
376
+
377
+ # build a image list
378
+ bimgs = BImgList(self.name)
379
+
380
+ self.session_controls(pix_size=pix_size, det_size=det_size)
381
+
382
+ for i, th in enumerate(thlist):
383
+ emc = deepcopy(self.session_controls)
384
+ emc.simc(sth=th)
385
+ if isLACBED:
386
+ for j in range(nout):
387
+ bimgs.add(emc, bimg[:,:,i*nout + j])
388
+ else:
389
+ bimgs.add(emc, bimg[:,:,i])
390
+
391
+
392
+ if bSave:
393
+ if bloch.closeimgfile() != 0:
394
+ raise BlochError('Error closing file')
395
+
396
+ print(f'The raw bloch image(s) of dimensions {det_size}x{det_size}x{nslices} and an offset of 8 bytes successfully saved to: \n{imgfn}')
397
+ print(f'To view, import the file into ImageJ or other raw image visualization tools')
398
+
399
+ return bimgs
400
+
401
+ def getIBDetails(self, bPrint=True):
402
+ '''
403
+ Retrieve a dynamic diffraction simulation details during a session
404
+ marked by
405
+ `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_. and
406
+ `endBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.endBloch>`_.
407
+
408
+ Information regarding the simulation session include sampling points and their
409
+ associated diffracted beam directions etc.
410
+
411
+ The information is available right after beginBloch call.
412
+
413
+ '''
414
+ nib = bloch.get_nsampling()
415
+ if nib <=0:
416
+ raise BlochError("Failed to retrieve number of incidental beams")
417
+
418
+ if nib == 0:
419
+ raise BlochError("No incidental beams found")
420
+
421
+
422
+ net = farray(np.zeros((2, nib), dtype=int))
423
+ tilt = farray(np.zeros((3, nib), dtype=float))
424
+
425
+ net, tilt, ret = bloch.getibinfo(net, tilt)
426
+ if ret != 0:
427
+ raise BlochError("failed to retrieve incidental beams info")
428
+
429
+ if bPrint:
430
+ print(f'\n-------Dynamic Diffraction Simulation Session for {self._name}---------\n')
431
+ print(f'Total Number of sampling points: {nib}\n')
432
+
433
+ smp = "Sampling"
434
+ stilt = "Beam Tilts In Reciprical Space"
435
+
436
+ print(f"{smp:^11}{stilt:^48}")
437
+ print(f"{'Points':^11}\n")
438
+
439
+ ibnet = np.transpose(net)
440
+ ibtilt = np.transpose(tilt)
441
+
442
+ # if bPrint:
443
+ ibs_list =[]
444
+ for i in range(1, nib+1, 1):
445
+ net1, net2 = ibnet[i-1]
446
+ t1,t2,t3 = ibtilt[i-1]
447
+ ibs_list.append(dict(ib=(net1, net2), tilt=ibtilt[i-1]))
448
+ if bPrint:
449
+ sn1 = '{0: < #06d}'. format(int(net1))
450
+ sn2 = '{0: < #06d}'. format(int(net2))
451
+ st1 = '{0: < #016.10f}'. format(float(t1))
452
+ st2 = '{0: < #016.10f}'. format(float(t2))
453
+ st3 = '{0: < #016.7g}'. format(float(t3))
454
+
455
+ print(f"{sn1}{sn2}{st1}{st2}{st3}")
456
+
457
+ return nib, ibs_list
458
+
459
+ def getCalculatedBeams(self, bPrint=False):
460
+ '''
461
+ Retieves and/or prints calculated beams for current dynamic diffraction simulation
462
+ session marked by
463
+ `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_
464
+ and `endBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.endBloch>`_.
465
+
466
+ This information is available right after beginBloch call.
467
+
468
+ :param bPrint: whether to print selected diffracted beams info on standard output
469
+ :type bPrint: bool, optional, default `False`
470
+
471
+ :return: The number of selected beams and the selected beams list in Miller indexes.
472
+ :rtype: a python tuple.
473
+
474
+ '''
475
+ ncalcbeams = bloch.getncalcbeams()
476
+ if ncalcbeams <= 0:
477
+ raise BlochError("failed to retrieve diffracted beams info")
478
+
479
+ cb = farray(np.zeros((3, ncalcbeams), dtype=int))
480
+
481
+ cb, ret = bloch.getcalcbeams(cb)
482
+
483
+ if ret != 0:
484
+ raise BlochError("failed to retrieve incidental beams info")
485
+
486
+ cbms = np.transpose(cb)
487
+
488
+ if bPrint:
489
+ print('---------Calculated Beams in Miller Indexes---------')
490
+ shkl = "h K l"
491
+ print(f"{shkl:^12}")
492
+
493
+ for cbm in cbms:
494
+ h,k,l = cbm
495
+ sh = '{0: < #04d}'. format(int(h))
496
+ sk = '{0: < #04d}'. format(int(k))
497
+ sl = '{0: < #04d}'. format(int(l))
498
+
499
+ print(f"{sh}{sk}{sl}")
500
+
501
+ return ncalcbeams, cbms
502
+
503
+ # ----------------deprecated and folded into getSCMatrix call----------------
504
+ # def getEigen(self, ib_coords=(0,0)):
505
+ # '''
506
+ # Returns eigen values for given sampling point.
507
+ # It must be called during a dynamic diffraction simulation session
508
+ # marked by
509
+ # `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_ and
510
+ # `endBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.endBloch>`_.
511
+ # :param ib_coords: Sampling point coordinates tuple
512
+ # :type ib_coords: tuple, optional, default (0,0)
513
+ # :return: a list of complex eigen values at sampling point location
514
+ # :rtype: numpy.ndarray
515
+
516
+ # Example of the eigen vales:
517
+ # .. code-block:: console
518
+ # Eigen values at: (0, 0):
519
+ # [ 0.04684002-0.00218389j -0.2064669 -0.00147516j -0.30446348+0.00055009j
520
+ # -0.27657617+0.00023512j -0.2765751 +0.00023515j 0.00539041-0.00382443j
521
+ # -0.535879 -0.00023585j -0.5612881 +0.00045343j -0.55369247+0.00026236j
522
+ # -0.55368818+0.00026249j -0.19093572+0.00066419j -0.1550311 +0.00045471j
523
+ # -0.15503166+0.00045471j -0.58842399-0.00202841j -0.67850191+0.00042728j
524
+ # -0.72713566+0.00060655j -0.70972681+0.00052279j -0.72092338+0.0005903j
525
+ # -0.72093237+0.00059052j -0.64608335-0.0001983j -0.64607544-0.00019853j]
526
+ # '''
527
+ # # validate the input values
528
+
529
+ # if ib_coords is None or not all(isinstance(v, int) for v in ib_coords) or len(ib_coords) != 2:
530
+ # raise BlochError("Invalid incident beam coordinates input, must be tuple of two integers")
531
+
532
+ # scmdim = bloch.get_scmdim(ib_coords)
533
+ # if scmdim <= 0:
534
+ # raise BlochError("Error finding corresponding scattering matrix, use getIBDetails to find potential input for ib_coords")
535
+
536
+ # ev = farray(np.zeros(scmdim, dtype=np.complex64))
537
+ # ev, ret = bloch.geteigenvalues(ib_coords, ev)
538
+ # if ret < 0 or ret > scmdim:
539
+ # raise BlochError("failed to retrieve incidental beams info")
540
+
541
+ # return ev
542
+
543
+ def getSCMatrix(self,
544
+ ib_coords = (0,0),
545
+ sample_thickness = DEF_THICKNESS[0],
546
+ rvec = (0.0,0.0,0.0)
547
+ ):
548
+ '''
549
+
550
+ Obtains scattering matrix at a given sampling point. To get a list of sampling
551
+ points used in this dynamic simulation session, call
552
+ `getIBDetails <pyemaps.crystals.html#pyemaps.crystals.Crystal.getIBDetails>`_
553
+ after `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_.
554
+ In addition to the scattering matrix, it also generates associated eigen values
555
+ and diffracted beams.
556
+
557
+ This call must be made during a dynamic simulation session marked by
558
+ `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_ and
559
+ `endBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.endBloch>`_.
560
+
561
+ :param ib_coords: Sampling point coordinates tuple.
562
+ :type ib_coords: tuple, optional, defaults to (0, 0)
563
+
564
+ :param thickness: Sample thickness.
565
+ :type thickness: int, optional, defaults to 200
566
+
567
+ :param rvec: R vector shifting atom coordinates in crystal, each value between 0.0 and 1.0.
568
+ :type rvec: tuple of 3 floats, optional, defaults to (0.0,0.0,0.0)
569
+
570
+ :return: scattering matrix size; scattering matrix; eigen values; diffracted beams.
571
+ :rtype: a tuple.
572
+
573
+ Default values for sample_thickness:
574
+
575
+ ::
576
+
577
+ DEF_THICKNESS[0] = 200
578
+
579
+ Example of the eigen vales:
580
+
581
+ .. code-block:: console
582
+
583
+ Eigen values at: (0, 0):
584
+ [ 0.04684002-0.00218389j -0.2064669 -0.00147516j -0.30446348+0.00055009j
585
+ -0.27657617+0.00023512j -0.2765751 +0.00023515j 0.00539041-0.00382443j
586
+ -0.535879 -0.00023585j -0.5612881 +0.00045343j -0.55369247+0.00026236j
587
+ -0.55368818+0.00026249j -0.19093572+0.00066419j -0.1550311 +0.00045471j
588
+ -0.15503166+0.00045471j -0.58842399-0.00202841j -0.67850191+0.00042728j
589
+ -0.72713566+0.00060655j -0.70972681+0.00052279j -0.72092338+0.0005903j
590
+ -0.72093237+0.00059052j -0.64608335-0.0001983j -0.64607544-0.00019853j]
591
+
592
+ '''
593
+
594
+ # validate the input values
595
+ if ib_coords is None or len(ib_coords) != 2 or not all(isinstance(v, int) for v in ib_coords):
596
+ raise BlochError("Invalid incident beam coordinates input, must be tuple of two integers2")
597
+
598
+ if sample_thickness is None or not isinstance(sample_thickness, int):
599
+ raise BlochError("Sample thickness ")
600
+
601
+ if rvec is None or not all(isinstance(v, (int,float)) for v in rvec) or len(rvec) != 3:
602
+ raise BlochError("Invalid R-vector input, must be tuple of three floats")
603
+
604
+ # get the dimension of the scattering matrix
605
+ scmdim, RET = bloch.compute_scm([ib_coords[0], ib_coords[1]])
606
+
607
+ if RET != 0 or scmdim <= 0:
608
+ raise BlochError("Error finding corresponding scattering matrix")
609
+
610
+ scm = farray(np.zeros((scmdim, scmdim)), dtype=np.complex64)
611
+ ev = farray(np.zeros(scmdim), dtype=np.complex64)
612
+ bms = farray(np.zeros((3, scmdim), dtype=int))
613
+ scm, ev, bms, ret = bloch.getscm(sample_thickness, rvec, scm, ev, bms)
614
+ if ret != 0:
615
+ raise BlochError('Error retieving scattering matrix, input matrix dimension too small, use getIBDetails to find extact dimentsion')
616
+
617
+ return scmdim, scm, ev, np.transpose(bms)
618
+
619
+ def endBloch(self):
620
+ """
621
+ Clean up Bloch module. This function follows
622
+ `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_
623
+ to mark the end of a dynamic simulation session.
624
+
625
+ """
626
+ dif.diff_delete()
627
+ bloch.free_bloch()
628
+
629
+ def generateBloch(self, aperture = DEF_APERTURE,
630
+ omega = DEF_OMEGA,
631
+ sampling = DEF_SAMPLING,
632
+ pix_size = DEF_PIXSIZE,
633
+ det_size = DEF_DETSIZE,
634
+ disk_size = DEF_CBED_DSIZE,
635
+ sample_thickness = DEF_THICKNESS,
636
+ em_controls = EMC(cl=200, # set smaller that 1000 default value
637
+ simc = SIMC(gmax=1.0, excitation=(0.3,1.0))),
638
+ nType = TY_NORMAL,
639
+ bSave = False):
640
+ """
641
+ Generates dynamic diffraction (Bloch) image(s). This function is equivalent to
642
+ calling :
643
+
644
+ 1. `beginBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.beginBloch>`_
645
+ 2. `getBlockImages <pyemaps.crystals.html#pyemaps.crystals.Crystal.getBlockImages>`_
646
+ 3. `endBloch <pyemaps.crystals.html#pyemaps.crystals.Crystal.endBloch>`_
647
+
648
+ :param aperture: Objective aperture.
649
+ :type aperture: float, optional
650
+
651
+ :param omega: Diagnization cutoff value, defaults to 10.
652
+ :type omega: float, optional
653
+
654
+ :param sampling: Number of sampling points
655
+ :type sampling: int, optional
656
+
657
+ :param pix_size: Detector pixel size in microns
658
+ :type pix_size: int, optional
659
+
660
+ :param det_size: Detector size or output image size
661
+ :type det_size: int, optional
662
+
663
+ :param disk_size: Diffracted beams size in range
664
+ :type disk_size: float, optional
665
+
666
+ :param sample_thickness: Sample thickness in (start, end, step) tuple
667
+ :type sample_thickness: tuple of int, optional
668
+
669
+ :param nType: type of bloch images generated. 0 for normal or 1 for large angle CBED images
670
+ :type nType: int, optional. defaults to 0
671
+
672
+ :param em_controls: Microscope controls object
673
+ :type em_controls: `Microscope control <pyemaps.emcontrols.html#module-pyemaps.emcontrols>`_, optional
674
+
675
+ :param bSave: `True` - save the output to a raw image file (ext: im3)
676
+ :type bSave: bool, optional
677
+
678
+ :return: `BImgList <pyemaps.ddiffs.html#pyemaps.ddiffs.BlochImgs>`_ object
679
+ :rtype: BImgList
680
+
681
+ Default values:
682
+
683
+ ::
684
+
685
+ DEF_APERTURE = 1.0
686
+ DEF_OMEGA = 10
687
+ DEF_SAMPLING = 8
688
+ DEF_CBED_DSIZE = 0.16
689
+ DEF_DSIZE_LIMITS =(0.01, 0.5)
690
+ DEF_PIXSIZE = 25
691
+ DEF_DETSIZE = 512
692
+ DEF_THICKNESS = (200, 200, 100)
693
+
694
+ .. note::
695
+
696
+ There will be one slice of image generated for each sample
697
+ thickness specified by sample_thickness = (start, end, step) arguement:
698
+
699
+ start, start+step ... start+N*step, end
700
+
701
+ .. warning::
702
+
703
+ Dynamic diffraction pattern generation or Bloch has an extensive memory
704
+ requirement for regular image generation:
705
+
706
+ nType = TY_NORMAL
707
+
708
+ Even more so for a large angle CBED generation when nType = TY_LACBED.
709
+
710
+ To reduce the memory needed, it is recommended to lower input parameters, particularly:
711
+
712
+ - The detector size *det_size*.
713
+ A reduction of the default value of 512 to 218 for example can reduce memory usage in
714
+ pyemaps without losing accuracies of the results.
715
+
716
+ - The number of sampling points in *sampling*.
717
+ With less available memory on the system where pyemaps is running, decreased sampling
718
+ points in *sampling* parameter can make a big difference in pyemaps performance.
719
+
720
+ """
721
+ try:
722
+
723
+ _, _ = self.beginBloch(aperture=aperture,
724
+ omega=omega,
725
+ sampling=sampling,
726
+ dbsize = disk_size,
727
+ em_controls=em_controls)
728
+
729
+ except BlochError as e:
730
+ # re-raise the same error, so that the caller can handle it
731
+ raise e
732
+
733
+ except Exception as e:
734
+ # Any other exception will be handled by the callers as BlochError from this point on
735
+ raise BlochError(f'Something went wrong in starting bloch simulation') from e
736
+
737
+ else:
738
+ try:
739
+ bimgs = self.getBlochImages(
740
+ sample_thickness = sample_thickness,
741
+ pix_size = pix_size,
742
+ det_size = det_size,
743
+ nType = nType,
744
+ bSave = bSave)
745
+
746
+ except (BlochError, BlochListError) as e:
747
+ raise
748
+
749
+ except Exception as e:
750
+ raise BlochError(f'Something went wrong when retrieving bloch image {e}') from e
751
+
752
+ else:
753
+ return bimgs
754
+
755
+ finally:
756
+ self.endBloch()
757
+
758
+ finally:
759
+ self.endBloch()
760
+
761
+
762
+
763
+ target.beginBloch = beginBloch
764
+ target._getBlochFN = _getBlochFN
765
+
766
+ # ---These calls must be between beginBloch and endBloch calls
767
+ target.getIBDetails = getIBDetails
768
+ # target.getBeams = getBeams <-------deprecate
769
+ target.getSCMatrix = getSCMatrix
770
+ target.getBlochImages = getBlochImages
771
+ target.getCalculatedBeams = getCalculatedBeams
772
+ # target.getLACBEDImage = getLACBEDImage
773
+ # ---These calls must be between beginBloch and endBloch calls
774
+
775
+ target.endBloch = endBloch
776
+
777
+ target.generateBloch = generateBloch
778
+
779
+ return target