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.
- nanonis_reader-0.0.1/PKG-INFO +21 -0
- nanonis_reader-0.0.1/nanonis_reader/__init__.py +7 -0
- nanonis_reader-0.0.1/nanonis_reader/atom_analysis.py +37 -0
- nanonis_reader-0.0.1/nanonis_reader/cmap_custom.py +20 -0
- nanonis_reader-0.0.1/nanonis_reader/find_value.py +19 -0
- nanonis_reader-0.0.1/nanonis_reader/nanonis_3ds.py +441 -0
- nanonis_reader-0.0.1/nanonis_reader/nanonis_dat.py +265 -0
- nanonis_reader-0.0.1/nanonis_reader/nanonis_sxm.py +354 -0
- nanonis_reader-0.0.1/nanonis_reader/schematic.py +33 -0
- nanonis_reader-0.0.1/nanonis_reader/spectral_analysis.py +152 -0
- nanonis_reader-0.0.1/nanonis_reader.egg-info/PKG-INFO +21 -0
- nanonis_reader-0.0.1/nanonis_reader.egg-info/SOURCES.txt +16 -0
- nanonis_reader-0.0.1/nanonis_reader.egg-info/dependency_links.txt +1 -0
- nanonis_reader-0.0.1/nanonis_reader.egg-info/not-zip-safe +1 -0
- nanonis_reader-0.0.1/nanonis_reader.egg-info/requires.txt +6 -0
- nanonis_reader-0.0.1/nanonis_reader.egg-info/top_level.txt +1 -0
- nanonis_reader-0.0.1/setup.cfg +4 -0
- nanonis_reader-0.0.1/setup.py +31 -0
|
@@ -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
|
+
|