nanonis-reader 0.0.1__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.1
2
+ Name: nanonis_reader
3
+ Version: 0.0.1
4
+ Summary: A Python package for reading STM experimental data files obtained from Nanonis, based on nanonispy
5
+ Home-page: https://github.com/D-gitt/nanonis_reader
6
+ Author: Dowook Kim
7
+ Author-email: dw.kim@postech.ac.kr
8
+ Keywords: nanonis,reader,nanonispy,STM data,scientific data analysis
9
+ Classifier: Programming Language :: Python :: 3.6
10
+ Classifier: Programming Language :: Python :: 3.7
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.6
16
+ Requires-Dist: numpy
17
+ Requires-Dist: matplotlib
18
+ Requires-Dist: nanonispy
19
+ Requires-Dist: scipy
20
+ Requires-Dist: warning
21
+ Requires-Dist: math
@@ -0,0 +1,7 @@
1
+ __version__ = '0.0.1'
2
+
3
+ # import nanonis_reader 실행 시 자동으로 하위 module 들을 사용할수 있도록 미리 import.
4
+ from nanonis_reader import nanonis_sxm, nanonis_dat, nanonis_3ds, cmap_custom, find_value, schematic, spectral_analysis, atom_analysis
5
+
6
+ # from nanonis_reader import *에서 *에 포함되는 것들. (* : "__all__에 포함된 것을 전부" import)
7
+ __all__ = ['nanonis_sxm', 'nanonis_dat', 'nanonis_3ds', 'cmap_custom', 'find_value', 'schematic', 'spectral_analysis', 'atom_analysis']
@@ -0,0 +1,37 @@
1
+ # Drift correction of STM images obtained on monoclinic Ta2NiSe5.
2
+ def Ta2NiSe5_driftcorr(z, a_image_nm, c_image_nm, scansize_nm, origin_line=0, origin_pixel=0):
3
+ import numpy as np
4
+ lines, pixels = np.shape(z)
5
+ a, c = 0.34916, 1.565 # lattice constants (nm)
6
+ lines_corr = int(round(lines * (a_image_nm / a)))
7
+ pixels_corr = int(round(pixels * (c_image_nm / c)))
8
+ # print (lines_corr, pixels_corr)
9
+ z_corr = np.copy(z)[origin_line:origin_line+lines_corr, origin_pixel:origin_pixel+pixels_corr]
10
+
11
+ # length of scansize must be 2.
12
+ # unitpx in \AA unit.
13
+ a_unitpx = (10*scansize_nm[0])/lines_corr
14
+ c_unitpx = (10*scansize_nm[1])/pixels_corr
15
+
16
+ return z_corr, a_unitpx, c_unitpx
17
+
18
+
19
+ # Extract topograph from a ASCII XYZ file of WSxM.
20
+ def Extract_Z (Path, Search = 'X[nm]'):
21
+ import numpy as np
22
+ # Search = 'X[nm]' # X[nm]가 포함된 줄 찾기
23
+ with open(Path, 'r', encoding = 'ISO-8859-1') as f: # encoding='ISO-8859-1'
24
+ for line_number, line in enumerate(f): # enumerate: 대상의 원소와 index를 묶은 tuple을 반환. / start=1 : index가 1부터 시작. (default==0)
25
+ if Search in line:
26
+ print(f"Word '{Search}' found on line {line_number+1}")
27
+ break # line_number == 2
28
+ XYZ_data = f.readlines() # line_number + 1 번째부터 한줄씩 모든 줄을 읽어들임.
29
+ # print(XYZ_data) # 읽어들인 결과값 출력.
30
+
31
+ # Extract z data and do 3sigma
32
+ Result = []
33
+ for Z in XYZ_data[1:]:
34
+ Result.append(Z.split('\t')[2].strip('\n')) # z data만 추출
35
+ Z_data = np.array([float(Z) for Z in Result]) # String array to float array in pm.
36
+ print ('Finished.')
37
+ return Z_data
@@ -0,0 +1,20 @@
1
+ def nanox():
2
+ import numpy as np
3
+ import matplotlib.colors as clr
4
+ return clr.LinearSegmentedColormap.from_list('nanox', \
5
+ np.array([(0, 0, 0), (115, 33, 0), (180, 119, 0), (255, 255, 255)])/255, N=256)
6
+
7
+ def bwr():
8
+ import matplotlib.colors as clr
9
+ return clr.LinearSegmentedColormap.from_list('bwr_custom', \
10
+ [(0, 0, 0.4), (0, 0, 1), (1, 1, 1), (1, 0, 0), (0.4, 0, 0)], N=256)
11
+
12
+ def conduction_band():
13
+ import numpy as np
14
+ # return '#2A59A0'
15
+ return np.array([42, 89, 160])/255
16
+
17
+ def valence_band():
18
+ import numpy as np
19
+ # return '#DF2B2B'
20
+ return np.array([223, 43, 43])/255
@@ -0,0 +1,19 @@
1
+ def barrier_height (kappa): # Input: kappa in 1/m unit.
2
+ m_eV = 0.51099895e+6 # Electron mass in eV unit. (NOT eV/c^2 unit. Use m_e instead of m_e*c**2.)
3
+ hbar = 6.582119569e-16 # hbar in eV*s unit.
4
+ c = 2.99792458e+8 # Speed of light in m/s unit.
5
+ return ( hbar*c*kappa )**2 / ( 2*m_eV )
6
+
7
+ def kappa (workfunction): # Input: work function in eV unit.
8
+ m_eV = 0.51099895e+6 # Electron mass in eV unit. (NOT eV/c^2 unit. Use m_e instead of m_e*c**2.)
9
+ hbar = 6.582119569e-16 # hbar in eV*s unit.
10
+ c = 2.99792458e+8 # Speed of light.
11
+ import numpy as np
12
+ return np.sqrt(2 * m_eV * workfunction) / (hbar * c)
13
+
14
+ def nearest(array, value):
15
+ import numpy as np
16
+ array = np.asarray(array)
17
+ # idx = (np.abs(array - value)).argmin()
18
+ idx = np.where((np.abs(array - value)) == np.min((np.abs(array - value))))[0]
19
+ return idx, array[idx]
@@ -0,0 +1,441 @@
1
+ class Load:
2
+
3
+ def __init__(self, filepath):
4
+ import nanonispy as nap
5
+ self.fname = nap.read.Grid(filepath).fname.split('\\')[-1]
6
+ self.header = nap.read.Grid(filepath).header
7
+ self.signals = nap.read.Grid(filepath).signals
8
+
9
+
10
+ class Topo:
11
+
12
+ def __init__(self, instance):
13
+ self.fname = instance.fname
14
+ self.header = instance.header
15
+ self.signals = instance.signals
16
+
17
+ def get_z (self, processing = 'raw'):
18
+ if processing == 'raw':
19
+ return self.raw()
20
+ elif processing == 'subtract average':
21
+ return self.subtract_average()
22
+ elif processing == 'subtract linear fit':
23
+ return self.subtract_linear_fit()
24
+ elif processing == 'subtract parabolic fit':
25
+ return self.subtract_parabolic_fit()
26
+ elif processing == 'differentiate':
27
+ return self.differentiate()
28
+
29
+ def raw (self):
30
+ import numpy as np
31
+ tmp = self.signals['topo']
32
+ z = np.where(tmp == 0, np.nan, tmp)
33
+ return z
34
+
35
+ def subtract_average (self):
36
+ import warnings
37
+ import numpy as np
38
+ warnings.filterwarnings(action='ignore')
39
+ z = self.raw()
40
+ z_subav = np.zeros(np.shape(z))
41
+ lines = np.shape(z)[0]
42
+ for i in range(lines):
43
+ z_subav[i] = z[i] - np.nanmean(z[i])
44
+ return z_subav
45
+
46
+ def subtract_linear_fit (self):
47
+ import numpy as np
48
+ from scipy.optimize import curve_fit
49
+ def f_lin(x, a, b): return a*x + b
50
+ xrange = round(self.header['size_xy'][0] * 1e9)*1e-9
51
+ print (xrange)
52
+ z = self.raw()
53
+ z_sublf = np.zeros(np.shape(z))
54
+ lines, pixels = np.shape(z)
55
+ for i in range(lines):
56
+ if np.shape(np.where(np.isnan(z))[0])[0] != 0: # image에 nan값이 포함되어 있을 경우 (== scan을 도중에 멈추었을 경우)
57
+ if i < np.min(np.where(np.isnan(z))[0]):
58
+ x = np.linspace(0, xrange, pixels)
59
+ popt, pcov = curve_fit(f_lin, x, z[i])
60
+ z_sublf[i] = z[i] - f_lin(x, *popt)
61
+ else:
62
+ z_sublf[i] = np.nan
63
+ else:
64
+ x = np.linspace(0, xrange, pixels)
65
+ popt, pcov = curve_fit(f_lin, x, z[i]) # x - ith line: linear fitting
66
+ z_sublf[i] = z[i] - f_lin(x, *popt)
67
+
68
+ return z_sublf
69
+
70
+ def subtract_parabolic_fit (self):
71
+ import numpy as np
72
+ from scipy.optimize import curve_fit
73
+ def f_parab(x, a, b, c): return a*(x**2) + b*x + c
74
+ xrange = round(self.header['size_xy'][0] * 1e9)*1e-9
75
+ print (xrange)
76
+ z = self.raw()
77
+ z_subpf = np.zeros(np.shape(z))
78
+ lines, pixels = np.shape(z)
79
+ for i in range(lines):
80
+ if np.shape(np.where(np.isnan(z))[0])[0] != 0: # image에 nan값이 포함되어 있을 경우 (== scan을 도중에 멈추었을 경우)
81
+ if i < np.min(np.where(np.isnan(z))[0]):
82
+ x = np.linspace(0, xrange, pixels)
83
+ popt, pcov = curve_fit(f_parab, x, z[i])
84
+ z_subpf[i] = z[i] - f_parab(x, *popt)
85
+ else:
86
+ z_subpf[i] = np.nan
87
+ else:
88
+ x = np.linspace(0, xrange, pixels)
89
+ popt, pcov = curve_fit(f_parab, x, z[i]) # x - ith line: linear fitting
90
+ z_subpf[i] = z[i] - f_parab(x, *popt)
91
+
92
+ return z_subpf
93
+
94
+ def differentiate (self):
95
+ import numpy as np
96
+ xrange, pixels = round(self.header['size_xy'][0] * 1e9)*1e-9, int(self.header['dim_px'][0])
97
+ dx = xrange / pixels
98
+ z = self.raw()
99
+ z_deriv = np.zeros(np.shape(z))
100
+ lines = np.shape(z)[0]
101
+ for i in range(lines):
102
+ z_deriv[i] = np.gradient(z[i], dx, edge_order = 2) # dI/dV curve를 직접 미분. --> d^2I/dV^2
103
+
104
+ return z_deriv
105
+
106
+
107
+ class Map: # dIdV, I-z spec, apparent barrier map
108
+
109
+ def __init__(self, instance):
110
+ self.fname = instance.fname
111
+ self.header = instance.header
112
+ self.signals = instance.signals
113
+
114
+ def get_didvmap (self, sweep_idx, channel = 'LI Demod 1 X (A)'):
115
+ didv = self.signals[channel][:, :, sweep_idx]
116
+ return didv
117
+
118
+ def get_currentmap (self, sweep_idx, sweep_direction = 'fwd'):
119
+ if sweep_direction == 'fwd':
120
+ current = self.signals['Current (A)'][:, :, sweep_idx]
121
+ elif sweep_direction == 'bwd':
122
+ current = self.signals['Current [bwd] (A)'][:, :, sweep_idx]
123
+ elif sweep_direction == 'AVG':
124
+ current = np.nanmean ( [self.signals['Current (A)'], self.signals['Current [bwd] (A)']], axis = 2 ) [:, :, sweep_idx]
125
+ return current
126
+
127
+ def get_apparent_barrier_height (self, line, pixel, sweep_direction='fwd', fitting_current_range=(1e-12, 10e-12)):
128
+ # fitting_current_range: current range in A unit.
129
+ import numpy as np
130
+ from scipy.optimize import curve_fit
131
+ def linear(x, barr, b):
132
+ return -2*( np.sqrt(2*0.51099895e+6*barr)/(6.582119569e-16*2.99792458e+8) )*x + b
133
+
134
+ z = self.signals['sweep_signal']
135
+ if sweep_direction == 'fwd':
136
+ I = np.abs(self.signals['Current (A)'][line, pixel])
137
+ elif sweep_direction == 'bwd':
138
+ I = np.abs(self.signals['Current [bwd] (A)'][line, pixel])
139
+ elif sweep_direction == 'AVG':
140
+ I = np.abs(np.nanmean ( [self.signals['Current (A)'][line, pixel], \
141
+ self.signals['Current [bwd] (A)'][line, pixel]], \
142
+ axis = 0))
143
+
144
+ ############################## Set fitting range ##############################
145
+ idx = np.where( (fitting_current_range[0] <= I) & (I <= fitting_current_range[1]) ) # Filter with I
146
+ ############################## Set fitting range ##############################
147
+ popt, pcov = curve_fit (linear, z[idx], np.log(I[idx]), p0 = [1.2, 1.2])
148
+ apparent_barrier_height = popt[0]
149
+ err = np.sqrt(np.diag(pcov))[0]
150
+ # err = np.sqrt(np.diag(pcov))[0]
151
+ # slope = -2*np.sqrt(2*0.51099895e+6*apparent_barrier_height)/(6.582119569e-16*2.99792458e+8)
152
+ # return apparent_barrier_height, err, slope
153
+
154
+ return apparent_barrier_height, err
155
+
156
+ def get_apparent_barrier_height_map (self, sweep_direction='fwd', fitting_current_range=(1e-12, 10e-12)):
157
+ import numpy as np
158
+ lines, pixels = self.header['dim_px'][1], self.header['dim_px'][0]
159
+ arr = np.zeros((lines, pixels))
160
+ err = np.zeros((lines, pixels))
161
+ for i in range (lines):
162
+ for j in range (pixels):
163
+ try:
164
+ arr[i, j] = self.get_apparent_barrier_height (i, j, sweep_direction, fitting_current_range)[0]
165
+ err[i, j] = self.get_apparent_barrier_height (i, j, sweep_direction, fitting_current_range)[1]
166
+ except:
167
+ print (f'Estimation error at: {i, j}. Investigate z-spectrum at {i, j} for detailed info.')
168
+ arr[i, j] = np.nan
169
+ err[i, j] = np.nan
170
+ return arr, err
171
+
172
+ def get_sweepsignal (self, sweep_idx):
173
+ return self.signals['sweep_signal'][sweep_idx]
174
+
175
+
176
+ class PtSpec: # any spectrum (dIdV, Z, I, ...) vs sweep_signal at any point.
177
+
178
+ def __init__(self, instance):
179
+ self.fname = instance.fname
180
+ self.header = instance.header
181
+ self.signals = instance.signals
182
+
183
+ def get_didv_raw (self, line, pixel, channel = 'none', offset = 'none'):
184
+ import numpy as np
185
+ if channel == 'none':
186
+ if 'LI Demod 2 X (A)' in self.signals.keys():
187
+ channel = 'LI Demod 2 X (A)'
188
+ elif 'LI Demod 1 X (A)' in self.signals.keys():
189
+ channel = 'LI Demod 1 X (A)'
190
+ else:
191
+ channel = channel
192
+ if isinstance(offset, np.ndarray):
193
+ didv = self.signals[channel][line, pixel] - offset
194
+ else:
195
+ didv = self.signals[channel][line, pixel]
196
+ return self.signals['sweep_signal'], didv
197
+
198
+
199
+ def get_dzdv_numerical (self, line, pixel):
200
+ import numpy as np
201
+ z = self.signals['Z (m)'][line, pixel]
202
+ dzdv_numerical = np.gradient(z, edge_order=2)
203
+ return self.signals['sweep_signal'], dzdv_numerical
204
+
205
+ def get_apparent_barrier_height (self, line, pixel, sweep_direction='fwd', fitting_current_range=(1e-12, 10e-12)):
206
+ # fitting_current_range: current range in A unit.
207
+ import numpy as np
208
+ from scipy.optimize import curve_fit
209
+ def linear(x, barr, b):
210
+ return -2*( np.sqrt(2*0.51099895e+6*barr)/(6.582119569e-16*2.99792458e+8) )*x + b
211
+
212
+ z = self.signals['sweep_signal']
213
+ if sweep_direction == 'fwd':
214
+ I = np.abs(self.signals['Current (A)'][line, pixel])
215
+ elif sweep_direction == 'bwd':
216
+ I = np.abs(self.signals['Current [bwd] (A)'][line, pixel])
217
+ elif sweep_direction == 'AVG':
218
+ I = np.abs(np.nanmean ( [self.signals['Current (A)'][line, pixel], \
219
+ self.signals['Current [bwd] (A)'][line, pixel]], \
220
+ axis = 0))
221
+
222
+ ############################## Set fitting range ##############################
223
+ idx = np.where( (fitting_current_range[0] <= I) & (I <= fitting_current_range[1]) ) # Filter with I
224
+ ############################## Set fitting range ##############################
225
+ popt, pcov = curve_fit (linear, z[idx], np.log(I[idx]), p0 = [1.2, 1.2])
226
+ apparent_barrier_height = popt[0]
227
+ # err = np.sqrt(np.diag(pcov))[0]
228
+ # slope = -2*np.sqrt(2*0.51099895e+6*apparent_barrier_height)/(6.582119569e-16*2.99792458e+8)
229
+ # return apparent_barrier_height, err, slope
230
+
231
+ return apparent_barrier_height
232
+
233
+ def get_didv_scaled (self, line, pixel, channel = 'LI Demod 2 X (A)', offset = 'none'):
234
+ '''
235
+ Returns
236
+ -------
237
+ tuple
238
+ (Bias (V), dIdV (S))
239
+ '''
240
+ import numpy as np
241
+ # return self.signals['sweep_signal'], np.median(self.get_didv_numerical(line, pixel)[1]/self.signals[channel][line, pixel])*self.signals[channel][line, pixel]
242
+ return self.signals['sweep_signal'], \
243
+ np.median(self.get_didv_numerical(line, pixel)[1]/self.get_didv_raw(line, pixel, channel, offset)[1])\
244
+ *self.get_didv_raw(line, pixel, channel, offset)[1]
245
+
246
+ def get_didv_normalized (self, line, pixel, channel='LI Demod 2 X (A)', factor=0.2, offset='none', delete_zero_bias=False):
247
+ '''
248
+ Returns
249
+ -------
250
+ tuple
251
+ (Bias (V), normalized dIdV)
252
+ '''
253
+ import numpy as np
254
+ from scipy.optimize import curve_fit
255
+ try:
256
+ from scipy.integrate import cumtrapz
257
+ except:
258
+ from scipy.integrate import cumulative_trapezoid
259
+
260
+ # dIdV, V = self.get_didv_scaled(line, pixel, channel)[1], self.signals['sweep_signal']
261
+ V, dIdV = self.get_didv_scaled(line, pixel, channel, offset = 'none')
262
+ try:
263
+ I_cal = cumtrapz(dIdV, V, initial = 0)
264
+ except:
265
+ I_cal = cumulative_trapezoid(dIdV, V, initial = 0)
266
+ zero = np.argwhere ( abs(V) == np.min(abs(V)) )[0, 0] # The index where V = 0 or nearest to 0.
267
+ popt, pcov = curve_fit (lambda x, a, b: a*x + b, V[zero-1:zero+2], I_cal[zero-1:zero+2])
268
+ I_cal -= popt[1]
269
+
270
+ # get total conductance I/V
271
+ with np.errstate(divide='ignore'): # Ignore the warning of 'division by zero'.
272
+ IV_cal = I_cal/V
273
+
274
+ # I_cal/V = 0/0으로 계산되는 경우
275
+ # nan으로 처리됨. 이 값 제외를 위해 nanmedian 사용.
276
+ delta = factor*np.nanmedian(IV_cal)
277
+ Normalized_dIdV = dIdV / np.sqrt(np.square(delta) + np.square(IV_cal))
278
+ if delete_zero_bias == False:
279
+ return V, Normalized_dIdV
280
+ else:
281
+ return np.delete(V, zero), np.delete(Normalized_dIdV, zero)
282
+
283
+
284
+
285
+
286
+ def get_didv_numerical (self, line, pixel):
287
+ '''
288
+ Returns
289
+ -------
290
+ tuple
291
+ (Bias (V), numerical dIdV (S))
292
+ '''
293
+ import numpy as np
294
+ step = self.signals['sweep_signal'][1] - self.signals['sweep_signal'][0]
295
+ didv = np.gradient(self.signals['Current (A)'][line, pixel], step, edge_order=2) # I-V curve를 직접 미분.
296
+ return self.signals['sweep_signal'], didv
297
+
298
+ def get_iv_raw (self, line, pixel):
299
+ '''
300
+ Returns
301
+ -------
302
+ tuple
303
+ (Bias (V), Current (A))
304
+ '''
305
+ return self.signals['sweep_signal'], self.signals['Current (A)']
306
+
307
+
308
+ class LineSpec: # any spectrum (dIdV, Z, I, ...) vs sweep_signal at any point.
309
+ def __init__(self, instance):
310
+ self.fname = instance.fname
311
+ self.header = instance.header
312
+ self.signals = instance.signals
313
+
314
+ # def get (self, line, sts='scaled', channel='LI Demod 2 X (A)', factor=0.2, offset='none', delete_zero_bias=False):
315
+ def get (self, line, processing='scaled', **kwargs):
316
+ import numpy as np
317
+ if processing == 'scaled':
318
+ spec = self.get_didv_scaled
319
+ elif processing == 'raw':
320
+ spec = self.get_didv_raw
321
+ elif processing == 'numerical':
322
+ spec = self.get_didv_numerical
323
+ elif processing == 'normalized':
324
+ spec = self.get_didv_normalized
325
+ linespec = np.array([ spec(line, pixel, **kwargs)[1] for pixel \
326
+ in range (self.header['dim_px'][0]) ]).T
327
+ return linespec
328
+
329
+ def get_didv_raw (self, line, pixel, channel = 'none', offset = 'none'):
330
+ if channel == 'none':
331
+ if 'LI Demod 2 X (A)' in self.signals.keys():
332
+ channel = 'LI Demod 2 X (A)'
333
+ elif 'LI Demod 1 X (A)' in self.signals.keys():
334
+ channel = 'LI Demod 1 X (A)'
335
+ else:
336
+ channel = channel
337
+ if offset != 'none':
338
+ didv = self.signals[channel][line, pixel] - offset
339
+ else:
340
+ didv = self.signals[channel][line, pixel]
341
+ return self.signals['sweep_signal'], didv
342
+
343
+ def get_didv_scaled (self, line, pixel, channel = 'LI Demod 2 X (A)', offset = 'none'):
344
+ '''
345
+ Returns
346
+ -------
347
+ tuple
348
+ (Bias (V), dIdV (S))
349
+ '''
350
+ import numpy as np
351
+ # return self.signals['sweep_signal'], np.median(self.get_didv_numerical(line, pixel)[1]/self.signals[channel][line, pixel])*self.signals[channel][line, pixel]
352
+ return self.signals['sweep_signal'], \
353
+ np.median(self.get_didv_numerical(line, pixel)[1]/self.get_didv_raw(line, pixel, channel, offset)[1])\
354
+ *self.get_didv_raw(line, pixel, channel, offset)[1]
355
+
356
+ def get_didv_normalized (self, line, pixel, channel='LI Demod 2 X (A)', factor=0.2, offset='none', delete_zero_bias=False):
357
+ '''
358
+ Returns
359
+ -------
360
+ tuple
361
+ (Bias (V), normalized dIdV)
362
+ '''
363
+ import numpy as np
364
+ from scipy.optimize import curve_fit
365
+ try:
366
+ from scipy.integrate import cumtrapz
367
+ except:
368
+ from scipy.integrate import cumulative_trapezoid
369
+
370
+ # dIdV, V = self.get_didv_scaled(line, pixel, channel)[1], self.signals['sweep_signal']
371
+ V, dIdV = self.get_didv_scaled(line, pixel, channel, offset = 'none')
372
+ try:
373
+ I_cal = cumtrapz(dIdV, V, initial = 0)
374
+ except:
375
+ I_cal = cumulative_trapezoid(dIdV, V, initial = 0)
376
+ zero = np.argwhere ( abs(V) == np.min(abs(V)) )[0, 0] # The index where V = 0 or nearest to 0.
377
+ popt, pcov = curve_fit (lambda x, a, b: a*x + b, V[zero-1:zero+2], I_cal[zero-1:zero+2])
378
+ I_cal -= popt[1]
379
+
380
+ # get total conductance I/V
381
+ with np.errstate(divide='ignore'): # Ignore the warning of 'division by zero'.
382
+ IV_cal = I_cal/V
383
+
384
+ # I_cal/V = 0/0으로 계산되는 경우
385
+ # nan으로 처리됨. 이 값 제외를 위해 nanmedian 사용.
386
+ delta = factor*np.nanmedian(IV_cal)
387
+ Normalized_dIdV = dIdV / np.sqrt(np.square(delta) + np.square(IV_cal))
388
+ if delete_zero_bias == False:
389
+ return V, Normalized_dIdV
390
+ else:
391
+ return np.delete(V, zero), np.delete(Normalized_dIdV, zero)
392
+
393
+
394
+
395
+
396
+ def get_didv_numerical (self, line, pixel):
397
+ '''
398
+ Returns
399
+ -------
400
+ tuple
401
+ (Bias (V), numerical dIdV (S))
402
+ '''
403
+ import numpy as np
404
+ step = self.signals['sweep_signal'][1] - self.signals['sweep_signal'][0]
405
+ didv = np.gradient(self.signals['Current (A)'][line, pixel], step, edge_order=2) # I-V curve를 직접 미분.
406
+ return self.signals['sweep_signal'], didv
407
+
408
+
409
+
410
+
411
+
412
+
413
+
414
+
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+