def transient_3D(): n_pixel_x, n_pixel_y = 3, 3 width_x = 250. width_y = 50. radius = 6. nD = 2 # Number of columns per pixel n_eff = 1e12 T = 300 V_bias = -20. V_readout = 0. pot_w_descr, pot_descr, geom_descr = sensor.sensor_3D(n_eff=n_eff, V_bias=V_bias, V_readout=V_readout, temperature=T, n_pixel_x=n_pixel_x, n_pixel_y=n_pixel_y, width_x=width_x, width_y=width_y, radius=radius, nD=nD, resolution=80, smoothing=0.1) # Start parameters of e-h pairs # 10 pairs per position xx, yy = np.meshgrid( np.linspace(-width_x / 2., width_x / 2., 25), # x np.repeat(np.linspace(-width_y / 2., width_y / 2., 5), 10), # y sparse=False) # All combinations of x / y p0 = np.array([xx.ravel(), yy.ravel()]) # Initial charge q0 = np.ones(p0.shape[1]) # Time steps dt = 0.001 # [ns] n_steps = 10000 t = np.arange(n_steps) * dt dd = solver.DriftDiffusionSolver(pot_descr, pot_w_descr, geom_descr=geom_descr, T=T, diffusion=True) traj_e, traj_h, I_ind_e, I_ind_h, T, I_ind_tot, Q_ind_e_tot, Q_ind_h_tot = dd.solve( p0, q0, dt, n_steps) I_ind_e[np.isnan(I_ind_e)] = 0. I_ind_h[np.isnan(I_ind_h)] = 0. Q_ind_e = integrate.cumtrapz(I_ind_e, T, axis=0, initial=0) Q_ind_h = integrate.cumtrapz(I_ind_h, T, axis=0, initial=0) for i in (5, 13): plt.plot(T[:, i], Q_ind_e[:, i], color='blue', label='Electrons') plt.plot(T[:, i], Q_ind_h[:, i], color='red', label='Holes') plt.plot(T[:, i], (Q_ind_e + Q_ind_h)[:, i], color='magenta', label='Sum') plt.plot(plt.xlim(), [(Q_ind_e_tot + Q_ind_h_tot)[i]] * 2, '-', color='magenta') plt.legend(loc=0) plt.xlabel('Time [ns]') plt.ylabel('Charge normalized to 1') plt.grid() plt.title('Induced charge of drifting e-h pairs, start pos. %d/%d um' % (p0[0, i], p0[1, i])) plt.show() # Plot numerical result in 2D with particle animation fig = plt.figure() plot.get_3D_sensor_plot(fig, width_x, width_y, radius, nD, n_pixel_x, n_pixel_y, V_bias=V_bias, V_readout=V_readout, pot_func=pot_descr.get_potential_smooth, field_func=pot_descr.get_field, # Comment in if you want to see the mesh mesh=None, # potential.mesh, title='Potential and field of a 3D sensor, '\ '%dx%d pixel matrix, numerical solution' % \ (n_pixel_x, n_pixel_y)) # Create animation frames = 50 init, animate = plot.animate_drift_diffusion(fig, T=T, pe=traj_e, ph=traj_h, dt=t.max() / frames, n_steps=frames) ani = animation.FuncAnimation(fig=fig, func=animate, blit=True, init_func=init, frames=frames, interval=5000 / frames) # ani.save('Example_3D_drift.gif', dpi=80, writer='imagemagick') plt.show()
def get_charge_planar(width, thickness, pot_descr, pot_w_descr, t_e_trapping=0., t_h_trapping=0., t_e_t1=0., t_h_t1=0., t_r=0., grid_x=5, grid_y=5, n_pairs=10, dt=0.001, n_steps=25000, temperature=300, multicore=True): ''' Calculate the collected charge in one planar pixel Charge is given as a 2d map depending on the start postitions of the e-h pairs. Parameters ---------- width: number Pixel width in um thickness: number Pixel thickness in um pot_descr, pot_w_descr: scarce.fields.Description Solution for the drift/weightning potential. grid_x, grid_y: number Grid spacing in um n_pairs: number of pseudo e-h pairs per grid point dt: float Time step in simulation in ns. Should be 1 ps to give reasonable diffusion n_steps: int Time steps to simulate ''' # Number of x/y bins x_bins = int(width / grid_x) y_bins = int(thickness / grid_y) # Bin positions range_x = (-width / 2., width / 2.) range_y = (0, thickness) # Create e-h pairs in the pixel, avoid charge carriers on boundaries # e.g. x = -width / 2 or y = 0 xx, yy = np.meshgrid( np.linspace(range_x[0] + grid_x / 2., range_x[1] - grid_x / 2., x_bins), np.repeat( np.linspace(range_y[0] + grid_y / 2., range_y[1] - grid_y / 2., y_bins), n_pairs), # 10 e-h per position sparse=False) # All combinations of x / y # Start positions p0 = np.array([xx.ravel(), yy.ravel()]) # Position [um] # Initial charge set to 1 q_start = 1. q0 = np.ones(p0.shape[1]) * q_start # Needed for histograming, numerical accuracy demands > 1 q_max = q_start * 1.05 dd = solver.DriftDiffusionSolver(pot_descr, pot_w_descr, T=temperature, diffusion=True, t_e_trapping=t_e_trapping, t_h_trapping=t_h_trapping, t_e_t1=t_e_t1, t_h_t1=t_h_t1, t_r=t_r, save_frac=50) traj_e, traj_h, I_ind_e, I_ind_h, T, _, Q_ind_e_tot, Q_ind_h_tot = dd.solve( p0, q0, dt, n_steps, multicore=multicore) # Trajectory at t=0 is start position pos_0 = traj_e[0] # Interpolate data to fixed time points for easier plotting # I_ind_e = tools.time_data_interpolate(T, I_ind_e, t, axis=0, fill_value=0.) # I_ind_h = tools.time_data_interpolate(T, I_ind_h, t, axis=0, fill_value=0.) # I_ind_e[np.isnan(I_ind_e)] = 0. # I_ind_h[np.isnan(I_ind_h)] = 0. # Q_ind_e = integrate.cumtrapz(I_ind_e, T, axis=0, initial=0) # Q_ind_h = integrate.cumtrapz(I_ind_h, T, axis=0, initial=0) q_ind = Q_ind_e_tot + Q_ind_h_tot # Histogram charge per start position data = np.vstack((pos_0[0], pos_0[1], q_ind)).T n_bins_c = 200 # Number of charge bins H, edges = np.histogramdd(sample=data, bins=(x_bins, y_bins, n_bins_c), range=((range_x[0], range_x[1]), (range_y[0], range_y[1]), (0., q_max))) # Result hist charge_pos = np.zeros(shape=(x_bins, y_bins)) sel = (np.sum(H, axis=2) != 0) weights = (edges[2][:-1] + edges[2][1:]) / 2. charge_pos[sel] = np.average( H, axis=2, weights=weights)[sel] * weights.sum() / np.sum(H, axis=2)[sel] # edges_x = (edges[0][:-1] + edges[0][1:]) / 2. # edges_y = (edges[1][:-1] + edges[1][1:]) / 2. # for xi, yi in zip(*np.where(np.logical_and(charge_pos > 0.1, # charge_pos < 0.9)) # ): # print edges_x[xi], edges_y[yi], charge_pos[xi, yi] # plt.clf() # plt.bar(weights, H[xi, yi], width=np.diff(weights)[0]) # plt.show() # plt.clf() # sel = np.logical_and(pos_0[0] == edges_x[xi], # pos_0[1] == edges_y[yi]) # plt.plot(T[:, sel], Q_ind_e[:, sel] + Q_ind_h[:, sel]) # for c in weights[H[xi, yi].astype(np.bool)]: # plt.plot(plt.xlim(), [c, c]) # plt.show() # plt.clf() # plt.plot( # T[:, sel], traj_e[:, 0, sel], '-.', linewidth=1, label='e_x') # plt.plot( # T[:, sel], traj_e[:, 1, sel], '--', linewidth=1, label='e_y') # plt.plot( # T[:, sel], traj_h[:, 0, sel], '-.', linewidth=1, label='h_x') # plt.plot( # T[:, sel], traj_h[:, 1, sel], '--', linewidth=1, label='h_y') # plt.legend(loc=2) # plt.show() # break del dd return edges[0], edges[1], charge_pos.T
def transient_irrad(): # For CCE important parameters fluence = 5e15 # Neq/cm2 V_bias = -1000. n_eff_0 = 1.7e12 # Calculate effective doping concentration after irradiation n_eff = silicon.get_eff_acceptor_concentration(fluence=fluence, n_eff_0=n_eff_0, is_ntype=True, is_oxygenated=True) # Planar sensor parameters width = 50. thickness = 200. temperature = 300. pitch = 45. n_pixel = 9 V_readout = 0. resolution = 200 # Create sensor pot_w_descr, pot_descr = sensor.planar_sensor( n_eff=n_eff, V_bias=V_bias, V_readout=V_readout, temperature=temperature, n_pixel=n_pixel, width=width, pitch=pitch, thickness=thickness, resolution=resolution, # Might have to be adjusted # when changing the # geometry smoothing=0.01) # Start parameters of e-h pairs # Create 10 e-h pairs every 5 um in y xx, yy = np.meshgrid( np.linspace(0, width, 1), # x np.repeat(np.linspace(0., thickness, thickness / 5.), 10), sparse=False) # All combinations of x / y p0 = np.array([xx.ravel(), yy.ravel()]) # Position [um] # Initial charge set to 1 q0 = np.ones(p0.shape[1]) # Time steps dt = 0.001 # [ns] n_steps = 3000 t = np.arange(n_steps) * dt t_e_trapping = silicon.get_trapping(fluence=fluence, is_electron=True, paper=1) t_h_trapping = silicon.get_trapping(fluence=fluence, is_electron=False, paper=1) dd = solver.DriftDiffusionSolver(pot_descr, pot_w_descr, T=temperature, diffusion=True) dd_irr = solver.DriftDiffusionSolver(pot_descr, pot_w_descr, T=temperature, diffusion=True, t_e_trapping=t_e_trapping, t_h_trapping=t_h_trapping) _, _, I_ind_e, I_ind_h, T, _ = dd.solve(p0, q0, dt, n_steps) _, _, I_ind_e_irr, I_ind_h_irr, T_irr, _ = dd_irr.solve( p0, q0, dt, n_steps) # Interpolate data to fixed time points for easier plotting I_ind_e = tools.time_data_interpolate(T, I_ind_e, t, axis=0, fill_value=0.) I_ind_h = tools.time_data_interpolate(T, I_ind_h, t, axis=0, fill_value=0.) I_ind_e[np.isnan(I_ind_e)] = 0. I_ind_h[np.isnan(I_ind_h)] = 0. I_ind_e_irr = tools.time_data_interpolate(T_irr, I_ind_e_irr, t, axis=0, fill_value=0.) I_ind_h_irr = tools.time_data_interpolate(T_irr, I_ind_h_irr, t, axis=0, fill_value=0.) I_ind_e_irr[np.isnan(I_ind_e_irr)] = 0. I_ind_h_irr[np.isnan(I_ind_h_irr)] = 0. Q_ind_e = integrate.cumtrapz(I_ind_e, t, axis=0, initial=0) Q_ind_h = integrate.cumtrapz(I_ind_h, t, axis=0, initial=0) Q_ind_e_irr = integrate.cumtrapz(I_ind_e_irr, t, axis=0, initial=0) Q_ind_h_irr = integrate.cumtrapz(I_ind_h_irr, t, axis=0, initial=0) plt.plot(t, Q_ind_e.sum(axis=1) / xx.shape[0], color='blue', label='Electrons, depl.') plt.plot(t, Q_ind_h.sum(axis=1) / xx.shape[0], color='red', label='Holes, depl.') plt.plot(t, (Q_ind_e.sum(axis=1) + Q_ind_h.sum(axis=1)) / xx.shape[0], color='magenta', label='Sum, depl.') plt.plot(t, Q_ind_e_irr.sum(axis=1) / xx.shape[0], '--', color='blue', label='Electrons, depl. + trap.') plt.plot(t, Q_ind_h_irr.sum(axis=1) / xx.shape[0], '--', color='red', label='Holes, depl. + trap.') plt.plot(t, (Q_ind_e_irr.sum(axis=1) + Q_ind_h_irr.sum(axis=1)) / xx.shape[0], '--', color='magenta', label='Sum, depl. + trap.') plt.legend(loc=0) plt.xlabel('Time [ns]') plt.ylabel('Total induced charge [a.u.]') plt.grid() plt.title('Induced charge of MIP in planar sensor, readout pixel') plt.show()
def transient_planar(): # Sensor parameters n_eff = 1.45e12 n_pixel = 9 width = 50. pitch = 30. thickness = 200. smoothing = 0.05 resolution = 251 temperature = 300. V_bias = -80. V_readout = 0. # Create sensor pot_w_descr, pot_descr = sensor.planar_sensor( n_eff=n_eff, V_bias=V_bias, V_readout=V_readout, temperature=temperature, n_pixel=n_pixel, width=width, pitch=pitch, thickness=thickness, resolution=resolution, # Might have to be adjusted # when changing the geometry smoothing=smoothing) # Start parameters of e-h pairs # Create 10 e-h pairs every 5 um in y xx, yy = np.meshgrid( np.linspace(0, width, 1), # x np.repeat(np.linspace(0., thickness - 10, 10), 1), sparse=False) # All combinations of x / y p0 = np.array([xx.ravel(), yy.ravel()]) # Position [um] # Initial charge set to 1 q0 = np.ones(p0.shape[1]) # Time steps dt = 0.001 # [ns] n_steps = 20000 t = np.arange(n_steps) * dt dd = solver.DriftDiffusionSolver(pot_descr, pot_w_descr, T=temperature, diffusion=True, t_r=0., save_frac=1) traj_e, traj_h, I_ind_e, I_ind_h, T, I_ind_tot, Q_ind_e_tot, Q_ind_h_tot = dd.solve( p0, q0, dt, n_steps, multicore=True) # Interpolate data to fixed time points for easier plotting # I_ind_e = tools.time_data_interpolate(T, I_ind_e, t, axis=0, fill_value=0.) # I_ind_h = tools.time_data_interpolate(T, I_ind_h, t, axis=0, fill_value=0.) I_ind_e[np.isnan(I_ind_e)] = 0. I_ind_h[np.isnan(I_ind_h)] = 0. Q_ind_e = integrate.cumtrapz(I_ind_e, T, axis=0, initial=0) Q_ind_h = integrate.cumtrapz(I_ind_h, T, axis=0, initial=0) Q_ind_t = integrate.cumtrapz(I_ind_tot, t, axis=0, initial=0) # Induced current of one e-h pair # plt.plot(T[:, 0], I_ind_e[:, 0], # '.-', color='blue', label='e', linewidth=2) # plt.plot(T[:, 0], I_ind_h[:, 0], # '.-', color='red', label='h', linewidth=2) # plt.plot(T[:, 0], I_ind_e[:, 0] + I_ind_h[:, 0], '.-', color='magenta', # label='e+h', linewidth=2) # plt.plot(t, I_ind_tot, '.-', color='black', # label='e+h', linewidth=2) # plt.ylabel('Induced current [a.u.]') # plt.grid() # plt.title('Signal of one drifting e-h pair, readout pixel') # plt.show() # plt.plot(t, I_ind_tot, # color='black', label='Current all charges', linewidth=2) # plt.plot(t, np.cumsum(I_ind_tot) * dt, '--', linewidth=2, # color='black', label='all e+h') # Induced charge of one e-h pair # plt.plot(T[:, 0], Q_ind_e[:, 0], '.-', # color='blue', linewidth=2, label='e') # plt.plot(T[:, 0], Q_ind_h[:, 0], '.-', # color='red', linewidth=2, label='h') Q_ind_tot = Q_ind_e_tot + Q_ind_h_tot for i in range(10): plt.plot(T[:, i], Q_ind_e[:, i] + Q_ind_h[:, i], '-', linewidth=1, label='e+h') plt.plot(plt.xlim(), [Q_ind_tot[i], Q_ind_tot[i]]) print Q_ind_tot[i] # plt.plot(t, Q_ind_t, '.-', color='black', # label='e+h', linewidth=1) plt.legend(loc=0) plt.title('Induced charge of e-h pair, readout pixel') plt.xlabel('Time [ns]') plt.ylabel('Total induced charge [a.u.]') print Q_ind_e_tot, Q_ind_h_tot plt.show() # # Total induced charge of e-h pairs in readout pixel # plt.plot(t, Q_ind_e[:, ::2].sum(axis=1) / xx.shape[0], color='blue', # linewidth=2, label='e') # plt.plot(t, Q_ind_h[:, ::2].sum(axis=1) / xx.shape[0], color='red', # linewidth=2, label='h') # plt.plot(t, (Q_ind_e[:, ::2] + Q_ind_h[:, ::2]).sum(axis=1) / xx.shape[0], # color='magenta', linewidth=2, label='e+h') # plt.legend(loc=0) # plt.xlabel('Time [ns]') # plt.ylabel('Total induced charge [a.u.]') # plt.grid() # plt.title('Induced charge of MIP, readout pixel') # plt.show() # # # Mean induced charge of e-h pairs in readout pixel # plt.plot(t, Q_ind_e[:, 1::2].sum(axis=1) / xx.shape[0], color='blue', # linewidth=2, label='e') # plt.plot(t, Q_ind_h[:, 1::2].sum(axis=1) / xx.shape[0], color='red', # linewidth=2, label='h') # plt.plot(t, (Q_ind_e[:, 1::2] + Q_ind_h[:, 1::2]).sum(axis=1) / xx.shape[0], # color='magenta', linewidth=2, label='e+h') # plt.legend(loc=0) # plt.xlabel('Time [ns]') # plt.ylabel('Total induced charge [a.u.]') # plt.grid() # plt.title('Induced charge of MIP, neighbouring pixel') # plt.show() # Plot numerical result in 2D with particle animation fig = plt.figure() plot.get_planar_sensor_plot(fig=fig, width=width, pitch=pitch, thickness=thickness, n_pixel=n_pixel, V_backplane=V_bias, V_readout=V_readout, pot_func=pot_descr.get_potential, field_func=pot_descr.get_field, depl_func=pot_descr.get_depletion, title='Planar sensor potential') # Create animation frames = 50 init, animate = plot.animate_drift_diffusion(fig, T=T, pe=traj_e, ph=traj_h, dt=t.max() / frames, n_steps=frames) ani = animation.FuncAnimation(fig=fig, func=animate, blit=True, init_func=init, frames=frames, interval=5000 / frames) # ani.save('Example_planar_drift.gif', dpi=80, writer='imagemagick') plt.show()