Example #1
0
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()
Example #2
0
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
Example #3
0
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()
Example #4
0
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()