-
Notifications
You must be signed in to change notification settings - Fork 0
/
nsls_utils_air.py
executable file
·242 lines (172 loc) · 7.71 KB
/
nsls_utils_air.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/usr/bin/env python
# Basic
import numpy as np
from scipy.signal import medfilt
import glob
import os
import pandas as pd
from matplotlib import pyplot as plt
# pyFAI
import pyFAI
import pygix
from pygix import plotting as ppl
import fabio
data_dir = "/Users/nils/CC/CMS Data/Nils/insitu_air"
calib_csv = os.path.join(data_dir, 'calib.csv')
def parse_filename(file):
parts = os.path.basename(file).split('_')
parsed = pd.Series()
try:
# Sample name is the first few things up to -4
parsed['sample'] = '_'.join(parts[0:-5])
# The rest is just acquisition parameters
parsed['t'] = float(parts[-5][:-1])
parsed['theta'] = float(parts[-4][2:])
parsed['exp_time'] = float(parts[-3][:-1])
parsed['stamp'] = int(parts[-2])
parsed['mode'] = parts[-1].split('.')[0]
except:
# Sample name is the first few things up to -4
parsed['sample'] = '_'.join(parts[0:-6])
# The rest is just acquisition parameters
parsed['t'] = float(parts[-6][:-1])
parsed['theta'] = float(parts[-5][2:])
parsed['exp_time'] = float(parts[-4][:-1])
parsed['stamp'] = int(parts[-3])
parsed['burst'] = int(parts[-2])
parsed['mode'] = parts[-1].split('.')[0]
return parsed
def build_master_table(raw_files, im_shape_pix=(195,487)):
# Build a DataFrame from the list of raw file names
df = pd.DataFrame()
df['tiff'] = pd.Series(raw_files)
df = pd.concat([df, df['tiff'].apply(parse_filename)],
axis=1)
return df
def setup_detector(theta, calib_csv=calib_csv):
det_param_df = pd.read_csv(calib_csv)
det_kwargs = dict(zip(det_param_df['param'], det_param_df['val']))
# Detector only holds pixel size for whatever reason
detector = pyFAI.detectors.Detector(det_kwargs['det_pix'], det_kwargs['det_pix'])
# Transform holds everything else
pg = pygix.Transform(incident_angle = theta, detector=detector,
**{k:v for k,v in det_kwargs.items() if k != 'det_pix'})
return pg
def get_data(df, sample):
# Function to automatically load, reshape, offset, and median filter raw .tiff data
# data = fabio.open(df['tiff'].loc[sample]).data.reshape(det_shape) - offset
data = fabio.open(df['tiff'].loc[sample]).data
return data
# return data
def get_blank(dfb, dfw, sample, blank_set='si_pos1'):
""" Given a sample row from a regular df, find blank with corresponding theta and
scale for exposure time"""
dfb_filter = dfb.loc[dfb['sample']==blank_set].loc[dfb['mode']==dfw['mode'].loc[sample]]
dfb_sorted = dfb_filter.iloc[(dfb_filter['theta']-dfw['theta'].loc[sample]).abs().argsort()]
blank_match = dfb_sorted.index.values[0]
blank_raw = get_data(dfb_sorted, blank_match)
blank_scaled = blank_raw * dfw['exp_time'].loc[sample] / dfb['exp_time'].loc[blank_match]
return blank_scaled
def get_sector(df, sample, chi, dark=None,
chi_width=4, radial_range=(0,2)):
data = get_data(df, sample)
theta = df['theta'].loc[sample]
pg = setup_detector(theta)
ii, q = pg.profile_sector(data, npt=1000, chi_pos=chi,
chi_width=chi_width, radial_range=radial_range,
correctSolidAngle=True, dark=dark,
method='lut', unit='q_A^-1')
ii[ii<1]=1
return ii, q
def get_ip_box(df, sample, op_pos, dark=None,
op_width=0.05, ip_range=(-2,0.1)):
data = get_data(df, sample)
theta = df['theta'].loc[sample]
pg = setup_detector(theta)
ii, q = pg.profile_ip_box(data, npt=1000, op_pos=op_pos,
op_width=op_width, ip_range=ip_range,
correctSolidAngle=True, dark=dark,
method='lut', unit='q_A^-1')
ii[ii<1]=1
return np.flip(ii), np.flip(-q)
def get_pole_figure(df, sample, dark=None, chi_range=(-90,0), q_range=(0,2), npt=(180,180)):
data = get_data(df, sample)
theta = df['theta'].loc[sample]
pg = setup_detector(theta)
intensity, q_abs, chi = pg.transform_polar(data, unit='A', npt=npt,
chi_range=chi_range, q_range=q_range,
correctSolidAngle=True, dark=dark,
method='splitpix')
intensity[intensity<1]=1
return intensity, q_abs, chi
def bg_corr_slice(A, x, xb, xbg):
"""
Given 2D array with x scale (for the columns), get
an integrated slice within the x values xb, with
background subtracted from the x locations in xbg
(typically two locations just outside of xb).
3 lines will be sampled at each xbg (+/- 1 pixel)
A: array, shape[1]=len(x)
x: 1-D array, scale for column positions of A (like an x-axis)
xb: tuple (lb, ub)
xbg: list-like of individual x locations to sample
Return: cut (bg-corrected slice of A)
"""
x_cols = np.array([np.where(x>xb[0])[0][0], np.where(x>xb[1])[0][0]])
x_bg_cols = np.array([np.where(x>val)[0][0] for val in xbg])
x_bg_cols = np.hstack([x_bg_cols, x_bg_cols+1, x_bg_cols-1])
A_cut = A[:,x_cols[0]:x_cols[1]]
bg_cut = np.tile(np.mean(A[:,x_bg_cols], axis=1), (A_cut.shape[1], 1)).T
cut = np.trapz(A_cut-bg_cut, x[x_cols[0]:x_cols[1]])
return cut
def Hermans(ii, chi):
"""
This function will sin-weight the intensity... do not pass sin-weighted intensity...
"""
sin_chi = np.sin(np.deg2rad(chi))
cos2chi = np.cos(np.deg2rad(chi)) ** 2
expect = np.sum(ii * cos2chi * sin_chi) / np.sum(ii * sin_chi)
return (3*expect-1)/2
def interp_nans(data):
"Fill NaNs in an array with linear interpolation"
mask = np.isnan(data)
data[mask] = np.interp(np.flatnonzero(mask), np.flatnonzero(~mask), data[~mask])
return data
def show_sample(df,sample, newfig=True, dark=None, log=True, prcs=(2,99.8), figsize=(5,5)):
# Inputs:
# df: a dataframe with parsed filenames
# sample: an integer corresponding to the row of the dataframe you'd like to analyze
# newfig false if you're using this as part of a subplot
# setup_detector defines a transform with the parameters we got from calibration
pg = setup_detector(df['theta'].loc[sample])
# Generate the corrected image
# get_data loads the tif file and 3x3 median-filters it (de-zinger)
imgt, qxy, qz = pg.transform_reciprocal(get_data(df,sample),
method='lut',
correctSolidAngle=True,
unit='A', dark=dark)
imgt[imgt<1]=1
if log:
imgt = np.log(imgt)
# Calculate where the color scale should be bounded
clim = np.percentile(imgt, prcs)
if newfig:
plt.figure(figsize=figsize)
# pygix built-in plotting with nice formatting
ppl.implot(imgt, -qxy, qz, mode='rsma',
cmap="terrain", clim=clim,
xlim=(-0.1,2), ylim=(-0.1,2),
newfig=False, show=False)
def pcolor(img, newfig=True, figsize=(5,4), extent=None,
clip_val=None, prcs=(2,99.5), log=False,
cmap='terrain', aspect='auto', origin=None):
if newfig:
plt.figure(figsize=figsize)
if log:
img = np.log(img)
if clip_val:
cmin, cmax = np.percentile(img[img>clip_val], prcs)
else:
cmin, cmax = np.percentile(img, prcs)
plt.imshow(img, cmap=cmap, vmin=cmin, vmax=cmax,
extent=extent, aspect=aspect, origin=origin)