@@ -80,6 +80,7 @@ def __init__(self, name: str = 'simulate'):
80
80
"""Simple simulation of the TEM. MUST START IN IMAGE MODE!
81
81
82
82
Consisting of randomly dispersed crystals.
83
+ One crystal is guaranteeed to be at (0, 0) on the stage
83
84
Each crystal is perfectly flat, and perfectly round.
84
85
All crystals have the same unit cell.
85
86
@@ -90,6 +91,7 @@ def __init__(self, name: str = 'simulate'):
90
91
Image mode:
91
92
Crystals have different "thickness", yielding different gray-values proportional to thickness.
92
93
This gray-value never changes, regardless of tilt, orientation ect.
94
+ A cross centered at (0, 0) is added for convenience
93
95
94
96
Diffraction mode:
95
97
All crystals visible in image mode contribute to the diffraction pattern.
@@ -108,7 +110,7 @@ def __init__(self, name: str = 'simulate'):
108
110
self .unit_cell_alpha = 90 # Degrees
109
111
self .unit_cell_beta = 90 # Degrees
110
112
self .unit_cell_gamma = 120 # Degrees
111
- self .max_excitation_error = 0.01
113
+ self .max_excitation_error = 0.005
112
114
self .spot_radius = 3 # pixels
113
115
super ().__init__ (name )
114
116
@@ -126,6 +128,11 @@ def __init__(self, name: str = 'simulate'):
126
128
self .crystal_euler_angle_psi = np .random .uniform (0 , 180 , self .n_crystals )
127
129
self .crystal_euler_angle_phi_2 = np .random .uniform (0 , 360 , self .n_crystals )
128
130
131
+ # Ensure one crystal is always at (0, 0) and 1µm radius
132
+ self .crystal_x [0 ] = 0
133
+ self .crystal_y [0 ] = 0
134
+ self .crystal_r [0 ] = 1000
135
+
129
136
# Reciprocal-space setup
130
137
reciprocal_unit_cell = _get_reciprocal_unit_cell (
131
138
self .unit_cell_a ,
@@ -193,6 +200,11 @@ def actually_establish_connection(self):
193
200
else :
194
201
self .mag = self .tem .getMagnification ()
195
202
203
+ # If the TEM is a simulated one (i.e. random beam shift), reset beam shift
204
+ # Otherwise, reseting the stage will not get you to (0, 0)
205
+ if self .tem .name == 'simulate' :
206
+ self .tem .setBeamShift (0 , 0 )
207
+
196
208
self .ready = True
197
209
198
210
def release_connection (self ):
@@ -237,7 +249,14 @@ def get_realspace_image(
237
249
# thickness multiplied by mask of where the crystal is
238
250
mask = ((xx - x ) ** 2 + (yy - y ) ** 2 ) < r ** 2
239
251
out += t * mask .astype (float )
240
- return out
252
+ # Invert and scale
253
+ out = 0xF000 * (1 - out )
254
+ # Add some noise
255
+ out *= np .random .uniform (0.9 , 1.1 , out .shape )
256
+ # Add cross at (0, 0)
257
+ width = 50 # nm
258
+ out [(np .abs (xx ) < width ) | (np .abs (yy ) < width )] = 0
259
+ return out .astype (int )
241
260
242
261
def get_diffraction_image (
243
262
self , shape : Tuple [int , int ], crystal_indices : np .ndarray
@@ -303,7 +322,9 @@ def get_diffraction_image(
303
322
min_y = round (max (0 , y - self .spot_radius ))
304
323
max_y = round (min (shape [0 ], y + self .spot_radius ))
305
324
out [min_y :max_y , min_x :max_x ] = intensity
306
- return out
325
+ # Scale. Direct beam intensity is 25
326
+ out = out * 0x8000 / 25
327
+ return out .astype (int )
307
328
308
329
def get_image (self , exposure : float = None , binsize : int = None , ** kwargs ) -> np .ndarray :
309
330
self .actually_establish_connection ()
@@ -371,8 +392,7 @@ def get_image(self, exposure: float = None, binsize: int = None, **kwargs) -> np
371
392
c_ind ,
372
393
)
373
394
else :
374
- img = (self .get_realspace_image (xx , yy , c_ind ) * 0xFFFF ).astype (int )
375
- return img
395
+ return self .get_realspace_image (xx , yy , c_ind )
376
396
377
397
def acquire_image (self ) -> int :
378
398
"""For TVIPS compatibility."""
0 commit comments