Skip to content

Instantly share code, notes, and snippets.

@FilipDominec
Last active November 15, 2024 17:55
Show Gist options
  • Save FilipDominec/3ac56bde9c462b76d7a5edcc00e75a30 to your computer and use it in GitHub Desktop.
Save FilipDominec/3ac56bde9c462b76d7a5edcc00e75a30 to your computer and use it in GitHub Desktop.
Streak Camera Analysis python snippet for nihilnovi
SIDEPLOTS = 0.0 # defaults 0.15 of width & height; set to 0 to disable them
FONTSIZE = 20
SIDEPLOTS = 0.15; FONTSIZE = 18 # defaults 0.15 of width & height; set to 0 to disable them
SIDELEGENDS = False # True is useful for small fonts & big plots only
SAVEPLOT = True
SAVEDECAYS, SAVESPECTRA = False, False # for further processing
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': FONTSIZE})
import pathlib
## Data preprocessing
all = np.array(ys)
plot_title = '_'.join(sharedlabels).rsplit('sample')[-1] # all info is in the shared labels
print('plot_title',plot_title)
if 'ns_' in plot_title: t_unit, t_scale_ns = 'ns', 1
elif 'us_' in plot_title: t_unit, t_scale_ns = 'us', 1e3
elif 'ms_' in plot_title: t_unit, t_scale_ns = 'ms', 1e6
if all.shape == (641,481): ## "DAT" renamed from "DAC" format that contains row/column labels (newer)
ys = all[-2:1:-1, 1:] #- 0.02 ## manual subtraction of background
tt = all[0, 1:] * t_scale_ns # time axis (ns)
# wavelength axis (nm) with manual calibration
if '136A' in plot_title:
wl = all[-2:1:-1, 0]+7
elif 'new' in plot_title:
wl = all[-2:1:-1, 0]
else:
wl = all[-2:1:-1, 0]-11
else: # older raw DAT format that only contains data - and axes are to be described elsewhere
wl = np.linspace(313,580,all.shape[0])
t1_from_label = plot_title.split(t_unit+'_')[0].rsplit('_',1)[1]
tt = np.linspace(0, float(t1_from_label) * t_scale_ns, all.shape[1])
ys = all[::-1,:]
timespan = tt[-1]-tt[0] #diag: print(tt.shape, tt); print(wl.shape, wl)
## Normalize and smooth data
if 'ND' in plot_title:
NDfilter = float(plot_title.split('ND')[-1].split('p',1)[0])/100
else: NDfilter = 1
ys /= NDfilter
def convolve_without_edge_artifacts(ar, ker):
# at edges, the curve won't be bent towards 0, but may become more noisy
norm = np.convolve(np.ones_like(ar), ker, mode='same')
return np.convolve(ar, ker, mode='same') / norm
smooth_points = 10 # Select width for the (truncated-Gaussian) smoothing kernel
smooth_kernel = 2**-np.linspace(-3,3,smooth_points)**2 # Or use any function you like
smooth_kernel /= np.sum(smooth_kernel) # Roughly maintain the curve height
for axis in (0,1): # For 3D data, replace with (0,1,2) etc.
ys = np.apply_along_axis(convolve_without_edge_artifacts, axis, np.array(ys),
smooth_kernel) # these *args and **kwargs are passed to the chosen func
## Data calibration
spectral_mask_sync = np.logical_and(wl>300, wl<400) # sync to peak in UV (i.e. NBE or fastest QW)
ssum = ys[spectral_mask_sync,:].sum(axis=0)
tt -= tt[np.argmax(ssum)] # shift zero time to maximum intensity time
print("Time extent", tt[0], tt[-1] )
## Plot preparation
if SIDEPLOTS:
fig.delaxes(ax)
gs = fig.add_gridspec(2, 2, width_ratios=(1-SIDEPLOTS, SIDEPLOTS), height_ratios=(SIDEPLOTS, 1-SIDEPLOTS),
wspace=0., hspace=0., left=0.18, right=.985, bottom=0.1, top=.985)
ax = fig.add_subplot(gs[1, 0])
ax_xdecays = fig.add_subplot(gs[0, 0], sharex=ax)
ax_xdecays.tick_params(labelbottom=False, direction="in", labelsize=FONTSIZE-3)
ax_xdecays.grid()
ax_yspectra = fig.add_subplot(gs[1, 1], sharey=ax)
ax_yspectra.tick_params(labelleft=False, labelsize=FONTSIZE-3)
ax_yspectra.grid()
axtopright = fig.add_axes([1.05-SIDEPLOTS, 1.02-SIDEPLOTS, 0.03, SIDEPLOTS-.04])
#<S-F2>axtopright = fig.add_axes([0.87, 0.89, 0.06, 0.15]) # x0, y0, x-width, y-height (rel to fig)
## Main streak-trace plot
import matplotlib.colors as colors
streak_map = ax.imshow(ys[::-1,:], cmap='turbo',
extent=[tt[0], tt[-1], wl[0], wl[-1],], aspect='auto', # aspect=.003,
norm=colors.PowerNorm(gamma=.5))
ax.set_xlabel('time (ns)')
ax.set_ylabel('wavelength (nm)')
ax.grid(alpha=0)
from matplotlib.lines import Line2D
x0,y0,xw,yw = ax.get_position().bounds # prepare for on-axes coloring lines
## Top-right color bar
if SIDEPLOTS:
axtopright.set_title(f'raw I (a.u.) ' if SIDELEGENDS else '', rotation='vertical', x=-0.24, y=.5,
fontsize=int(FONTSIZE*.8), color='k', verticalalignment='center')
axtopright.tick_params(axis='both', which='major', labelsize=int(FONTSIZE*.8))
cb = fig.colorbar(streak_map, cax=axtopright, shrink=0.7)
cb.ax.minorticks_on()
# Decay curves on top
dc_dir = pathlib.Path('decaycurves/'+plot_title+'_decaycurves')
dc_dir.mkdir(parents=True, exist_ok=True)
dx = -0.003 # spacing from the vert axis
if 'sample857' in sharedlabels or 'sample857new' in sharedlabels:
spectral_bands_defs = ((350,370,'darkviolet','GaN NBE'),
(380, 405,'deepskyblue', 'm-plane QW'), (425, 510,'r', 'polar QW'))
#elif 'sample136A' in sharedlabels:
#spectral_bands_defs = ((350,370,'gray','GaN NBE'),
#(380, 410,'k', 'QW'), (470, 510,'orange', 'DB'))
else:
spectral_bands_defs = ((350,370,'gray','GaN NBE'),
(390, 440,'k', 'QW'), (470, 510,'orange', 'DB'))
ymax = 0
for (l0,l1,c,label) in spectral_bands_defs:
spectral_mask = np.logical_and(wl>l0, wl<l1)
if sum(spectral_mask):
i = ys[spectral_mask,:].mean(axis=0)
if SIDEPLOTS:
ax_xdecays.plot(tt, i, label=f"$\\lambda\\in ({l0:g}; {l1:g})$ nm: {label}", c=c)
if max(i)>ymax: ymax=max(i)
y0i,y1i = np.interp((l0,l1), (min(wl), max(wl)), (y0,y0+yw)) # outline segments along the graph axes
fig.add_artist(Line2D([x0+dx,x0+dx], [y0i,y1i],lw=3., color=c))
if SAVEDECAYS:
np.savetxt(dc_dir/f'band_from_{l0:g}_to_{l1:g}nm.dat', np.vstack([tt,i/timespan]).T, fmt="%.8g", header='#t(ns) intensity(a.u.)')
if SIDEPLOTS:
ax_xdecays.set_ylabel('band $I(t)$')
if SIDELEGENDS: ax_xdecays.legend(loc='upper center')
ax_xdecays.set_yscale('log')
ax_xdecays.set_ylim(ymin=ymax/3.1e3, ymax=ymax*1.2)
# spectral curves on right side
ts_dir = pathlib.Path('timeslices/'+plot_title+'_timeslices')
ts_dir.mkdir(parents=True, exist_ok=True)
dy = -0.005 # spacing from the horiz axis
Imax = 0
t_slice_milestones = [j*10**k for k in range(-1,8) for j in (0.1,0.3)]
for (t0,t1,color) in zip(t_slice_milestones[:-1], t_slice_milestones[1:], 'mrygcb'*10):
temporal_mask = np.logical_and(tt>t0, tt<t1)
if sum(temporal_mask) > 5:
i = ys[:,temporal_mask].mean(axis=1)
if SIDEPLOTS:
ax_yspectra.plot(i, wl, label=f"$t\\in ({t0:g}; {t1:g})$ ns", c=color)
if max(i)>Imax: Imax=max(i)
x0i,x1i = np.interp((t0,t1), (min(tt), max(tt)), (x0,x0+xw))
fig.add_artist(Line2D([x0i,x1i],[y0+dy,y0+dy], lw=3., color=color))
if SAVESPECTRA:
np.savetxt(ts_dir/f'slice_to_{t1:g}ns.dat', np.vstack([wl,i/timespan]).T, fmt="%.8g")
if SIDEPLOTS:
ax_yspectra.set_xlabel('$I(λ)$')
if SIDELEGENDS: ax_yspectra.legend()
ax_yspectra.set_xscale('log')
ax_yspectra.set_xlim(xmin=Imax/1.1e3, xmax=Imax*1.2)
ax_yspectra.xaxis.set_ticks([1e-1,1e1]) # reduced to just two tick labels for a narrow plot
if SAVEPLOT:
tosave.append(plot_title+'_streakcam_small.png') ## whole graph will be saved as PNG
tosave.append(plot_title+'_streakcam_small.pdf') ## whole graph will be saved as PDF
@FilipDominec
Copy link
Author

This is how a sample plot in https://github.com/FilipDominec/nihilnovi/ looks like with SIDEPLOTS = 0.15; FONTSIZE = 18.

857new_1kHz_exc2c5uW_10us_50tacc_mrizka480nm_streakcam_small

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment