pyemaps 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyemaps/COPYING +798 -0
- pyemaps/CifFile/CifFile_module.py +3117 -0
- pyemaps/CifFile/StarFile.py +2496 -0
- pyemaps/CifFile/TypeContentsParser.py +82 -0
- pyemaps/CifFile/YappsStarParser_1_0.py +289 -0
- pyemaps/CifFile/YappsStarParser_1_1.py +288 -0
- pyemaps/CifFile/YappsStarParser_2_0.py +369 -0
- pyemaps/CifFile/YappsStarParser_STAR2.py +382 -0
- pyemaps/CifFile/__init__.py +7 -0
- pyemaps/CifFile/drel/__init__.py +1 -0
- pyemaps/CifFile/drel/drel_ast_yacc.py +582 -0
- pyemaps/CifFile/drel/drel_lex.py +228 -0
- pyemaps/CifFile/drel/drel_runtime.py +126 -0
- pyemaps/CifFile/drel/parsetab.py +185 -0
- pyemaps/CifFile/drel/py_from_ast.py +628 -0
- pyemaps/CifFile/parsetab.py +185 -0
- pyemaps/CifFile/yapps3_compiled_rt.py +390 -0
- pyemaps/README.md +201 -0
- pyemaps/__config__.py +91 -0
- pyemaps/__init__.py +233 -0
- pyemaps/__main__.py +219 -0
- pyemaps/__version__.py +1 -0
- pyemaps/cdata/Aluminium.xtl +4 -0
- pyemaps/cdata/AluminiumOxide.xtl +5 -0
- pyemaps/cdata/Aluminium_FCC.xtl +4 -0
- pyemaps/cdata/BariumTitanate_180k.xtl +6 -0
- pyemaps/cdata/BariumTitanate_270k.xtl +7 -0
- pyemaps/cdata/BariumTitanate_Tetra.xtl +7 -0
- pyemaps/cdata/BiMnO3.xtl +27 -0
- pyemaps/cdata/Boron_Tetra.xtl +8 -0
- pyemaps/cdata/CadmiumSelenide_Hex.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Cubic.xtl +5 -0
- pyemaps/cdata/CadmiumSulfide_Hex.xtl +5 -0
- pyemaps/cdata/Chromium_BCC.xtl +4 -0
- pyemaps/cdata/CoSb3_Skutterudite.xtl +5 -0
- pyemaps/cdata/CopperOxide.xtl +5 -0
- pyemaps/cdata/Copper_FCC.xtl +4 -0
- pyemaps/cdata/Cu2O_Cuprite.xtl +5 -0
- pyemaps/cdata/Diamond.xtl +4 -0
- pyemaps/cdata/ErbiumPyrogermanate.xtl +9 -0
- pyemaps/cdata/FePd_Tetra.xtl +5 -0
- pyemaps/cdata/FeS2_Pyrite.xtl +15 -0
- pyemaps/cdata/GalliumAntimonide.xtl +5 -0
- pyemaps/cdata/GalliumArsenide.xtl +5 -0
- pyemaps/cdata/GalliumNitride.xtl +5 -0
- pyemaps/cdata/Germanium.xtl +4 -0
- pyemaps/cdata/Gold_FCC.xtl +4 -0
- pyemaps/cdata/IndiumArsenide.xtl +5 -0
- pyemaps/cdata/LaMnO3.xtl +7 -0
- pyemaps/cdata/LeadZirconateTitanate.xtl +8 -0
- pyemaps/cdata/Li2MnO3.xtl +13 -0
- pyemaps/cdata/NaFeO2.xtl +6 -0
- pyemaps/cdata/Nb3Sn.xtl +5 -0
- pyemaps/cdata/Pentacene.xtl +39 -0
- pyemaps/cdata/Silicon.xtl +4 -0
- pyemaps/cdata/StrontiumTitanate.xtl +6 -0
- pyemaps/cdata/TelluriumDioxide.xtl +5 -0
- pyemaps/cdata/TinDioxide_RT.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Anatase.xtl +5 -0
- pyemaps/cdata/TitaniumDioxide_Rutile.xtl +5 -0
- pyemaps/cdata/TungstenDiselenide.xtl +5 -0
- pyemaps/cdata/VanadiumDioxide_RT.xtl +6 -0
- pyemaps/cdata/ZincOxide.xtl +5 -0
- pyemaps/cdata/Zinc_HCP.xtl +4 -0
- pyemaps/cdata/ZirconiumNitride.xtl +5 -0
- pyemaps/cdata/limno2.xtl +6 -0
- pyemaps/crystals.py +1415 -0
- pyemaps/ddiffs.py +147 -0
- pyemaps/diffract/__init__.py +70 -0
- pyemaps/diffract/bloch_dec.py +779 -0
- pyemaps/diffract/csf_dec.py +242 -0
- pyemaps/diffract/dif_dec.py +519 -0
- pyemaps/diffract/dpgen_dec.py +175 -0
- pyemaps/diffract/mxtal_dec.py +201 -0
- pyemaps/diffract/powder_dec.py +137 -0
- pyemaps/diffract/stereo_dec.py +111 -0
- pyemaps/display.py +791 -0
- pyemaps/emcontrols.py +1017 -0
- pyemaps/errors.py +266 -0
- pyemaps/fileutils.py +1001 -0
- pyemaps/kdiffs.py +1221 -0
- pyemaps/license.txt +42 -0
- pyemaps/samples/adf.py +137 -0
- pyemaps/samples/al.img +0 -0
- pyemaps/samples/al_dpgen.py +57 -0
- pyemaps/samples/al_ediom.py +94 -0
- pyemaps/samples/convert_image.py +88 -0
- pyemaps/samples/dm_diff.py +225 -0
- pyemaps/samples/mask.im3 +0 -0
- pyemaps/samples/powder.py +51 -0
- pyemaps/samples/raw.img +0 -0
- pyemaps/samples/si_bloch.py +120 -0
- pyemaps/samples/si_constructor.py +51 -0
- pyemaps/samples/si_csf.py +59 -0
- pyemaps/samples/si_dif.py +134 -0
- pyemaps/samples/si_lacbed.py +59 -0
- pyemaps/samples/si_pyemaps.py +80 -0
- pyemaps/samples/si_scm.py +156 -0
- pyemaps/samples/si_stereo.py +116 -0
- pyemaps/scattering/__init__.py +26 -0
- pyemaps/scattering/sct_dec.py +31 -0
- pyemaps/spg/__init__.py +25 -0
- pyemaps/spg/spg_dec.py +60 -0
- pyemaps/stackimg.py +1031 -0
- pyemaps/xtal.py +222 -0
- pyemaps-2.0.0.dist-info/METADATA +231 -0
- pyemaps-2.0.0.dist-info/RECORD +112 -0
- pyemaps-2.0.0.dist-info/WHEEL +5 -0
- pyemaps-2.0.0.dist-info/entry_points.txt +2 -0
- pyemaps-2.0.0.dist-info/licenses/COPYING +798 -0
- pyemaps-2.0.0.dist-info/licenses/license.txt +42 -0
- pyemaps-2.0.0.dist-info/top_level.txt +1 -0
pyemaps/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
|