def get_potential(V_bias, n_eff): try: return tools.load( os.path.join(DATAFOLDER, 'pot_descr_%d_%d_%d_3D' % (V_bias, n_eff, RESOLUTION))) except IOError: V_readout = 0. V_bi = -silicon.get_diffusion_potential(n_eff / 1e12, temperature=TEMP) mesh = get_mesh() potential = fields.calculate_3D_sensor_potential( mesh, WIDTH_X, WIDTH_Y, NPIXEL_X, NPIXEL_Y, RADIUS, ND, n_eff, V_bias, V_readout, V_bi) min_x, max_x, min_y, max_y = GEOM_DESCR.get_array_corners() nx = WIDTH_X * NPIXEL_X * 4 ny = WIDTH_Y * NPIXEL_Y * 4 pot_descr = fields.Description( potential, min_x=min_x, max_x=max_x, min_y=min_y, max_y=max_y, nx=nx, # um res. ny=ny, # um res. smoothing=SMOOTHING) tools.save( pot_descr, os.path.join(DATAFOLDER, 'pot_descr_%d_%d_%d_3D' % (V_bias, n_eff, RESOLUTION))) return pot_descr
def plot_diffusion_potential(): n_eff = np.linspace(0.1, 100., 1000.) plt.clf() # Plot diffusion potential for temperature in [200, 250, 300, 350]: plt.plot(n_eff, silicon.get_diffusion_potential(n_eff, temperature=temperature), linewidth=2., label='T = %d' % temperature) plt.title('Diffusion potential at thermal equilibrium in silicon') plt.xlabel('Effective doping concentration [$\mathrm{10^{12} / cm^3}}$]') plt.ylabel('Diffusion potential [$\mathrm{V}$]') plt.legend(loc=0) plt.grid() plt.savefig('DiffusionPotential.pdf', layout='tight')
def get_potential(V_bias, n_eff, thickness): try: return tools.load( os.path.join(DATAFOLDER, 'pot_descr_%d_%d_%d' % (V_bias, n_eff, thickness))) except IOError: V_readout = 0. V_bi = -silicon.get_diffusion_potential(n_eff / 1e12, temperature=TEMP) # mesh = MESH_1 if thickness == THICKNESS_1 else MESH_2 mesh = get_mesh(n_pixel=NPIXEL, width=WIDTH, thickness=thickness, resolution=RESOLUTION_1 if thickness == THICKNESS_1 else RESOLUTION_2) potential = fields.calculate_planar_sensor_potential( mesh=mesh, width=WIDTH, pitch=PITCH, n_pixel=NPIXEL, thickness=thickness, n_eff=n_eff, V_bias=V_bias, V_readout=V_readout, V_bi=V_bi) min_x = float(mesh.getFaceCenters()[0, :].min()) max_x = float(mesh.getFaceCenters()[0, :].max()) nx = WIDTH * NPIXEL ny = thickness pot_descr = fields.Description(potential, min_x=min_x, max_x=max_x, min_y=0, max_y=thickness, nx=nx, ny=ny, smoothing=SMOOTHING) tools.save( pot_descr, os.path.join(DATAFOLDER, 'pot_descr_%d_%d_%d' % (V_bias, n_eff, thickness))) return pot_descr
def planar_sensor(n_eff, V_bias, V_readout=0., temperature=300, n_pixel=9, width=50., pitch=45., thickness=200., selection=None, resolution=300., nx=None, ny=None, smoothing=0.05, mesh_file='planar_mesh.msh'): ''' Create a planar_sensor sensor pixel array. Parameters ---------- n_eff : number Effective doping concentration in :math:`\mathrm{\frac{1}{cm^3}}` V_bias : number Bias voltage in Volt V_readout : number Readout voltage in Volt temperature : float Temperature in Kelvin n_pixel : int Number of pixels width : number Width of one pixel in :math:`\mathrm{\mu m}` pitch : number Pitch (redout implant width) of one pixel in :math:`\mathrm{\mu m}` thickness : number Thickness of the sensor in :math:`\mathrm{\mu m}` selection : string Selects if the weighting potential / potentials or both are calculated. If not set: calculate weighting potential and drift potential If drift: calculate drift potential only If weighting: calculate weighting potential only resolution : number Mesh resolution. Should lead to > 200000 mesh points for exact results. nx : number Interpolation points in x for the potentials and fields ny : number Interpolation points in y for the potentials and fields smoothing : number Smoothing parameter for the potential. Higher number leads to more smooth looking potential, but be aware too much smoothing leads to wrong results! mesh_file : str File name of the created mesh file Returns ----- Two scarce.fields.Description objects for the weighting potential and potential if no specified selection. ''' # Create mesh of the sensor and stores the result # The created file can be viewed with any mesh viewer (e.g. gmsh) mesh = geometry.mesh_planar_sensor(n_pixel=n_pixel, width=width, thickness=thickness, resolution=resolution, filename=mesh_file) min_x = float(mesh.getFaceCenters()[0, :].min()) max_x = float(mesh.getFaceCenters()[0, :].max()) # Set um resolution grid if not nx: nx = width * n_pixel if not ny: ny = thickness if not selection or 'drift' in selection: V_bi = -silicon.get_diffusion_potential(n_eff, temperature) # Numerically solve the Laplace equation on the mesh potential = fields.calculate_planar_sensor_potential( mesh=mesh, width=width, pitch=pitch, n_pixel=n_pixel, thickness=thickness, n_eff=n_eff, V_bias=V_bias, V_readout=V_readout, V_bi=V_bi) pot_descr = fields.Description(potential, min_x=min_x, max_x=max_x, min_y=0, max_y=thickness, nx=nx, ny=ny, smoothing=smoothing) if selection and 'drift' in selection: return pot_descr if not selection or 'weighting' in selection: # Numerically solve the Poisson equation on the mesh w_potential = fields.calculate_planar_sensor_w_potential( mesh=mesh, width=width, pitch=pitch, n_pixel=n_pixel, thickness=thickness) pot_w_descr = fields.Description(w_potential, min_x=min_x, max_x=max_x, min_y=0, max_y=thickness, nx=nx, ny=ny, smoothing=smoothing) if selection and 'weighting' in selection: return pot_w_descr return pot_w_descr, pot_descr
def sensor_3D(n_eff, V_bias, V_readout=0., temperature=300, n_pixel_x=3, n_pixel_y=3, width_x=250., width_y=50., radius=6., nD=2, selection=None, resolution=80., nx=None, ny=None, smoothing=0.1, mesh_file='3D_mesh.msh'): ''' Create a 3D sensor pixel array. Parameters ---------- n_eff : number Effective doping concentration in :math:`\mathrm{\frac{1}{cm^3}}` V_bias : number Bias voltage in Volt V_readout : number Readout voltage in Volt temperature : float Temperature in Kelvin n_pixel_x : int Number of pixels in x n_pixel_y : int Number of pixels in y width_x : number Width of one pixel in x in :math:`\mathrm{\mu m}` width_y : number Width of one pixel in y in :math:`\mathrm{\mu m}` radius : number Radius of readout and biasing columns in :math:`\mathrm{\mu m}` nD : int Number of readout columns per pixel selection : string Selects if the weighting potential / potentials or both are calculated. If not set: calculate weighting potential and drift potential If drift: calculate drift potential only If weighting: calculate weighting potential only resolution : number Mesh resolution. Should lead to > 300000 mesh points for exact results. nx : number Interpolation points in x for the potentials and fields ny : number Interpolation points in y for the potentials and fields smoothing : number Smoothing parameter for the potential. Higher number leads to more smooth looking potential, but be aware too much smoothing leads to wrong results! mesh_file : str File name of the created mesh file Returns ----- Two scarce.fields.Description objects for the weighting potential and potential if not specified selection and a geometry desciption object. ''' if not nx: nx = width_x * n_pixel_x * 4 if not ny: ny = width_y * n_pixel_y * 4 mesh = geometry.mesh_3D_sensor(width_x=width_x, width_y=width_y, n_pixel_x=n_pixel_x, n_pixel_y=n_pixel_y, radius=radius, nD=nD, resolution=resolution, filename=mesh_file) # Describe the 3D sensor array geom_descr = geometry.SensorDescription3D(width_x, width_y, n_pixel_x, n_pixel_y, radius, nD) min_x, max_x, min_y, max_y = geom_descr.get_array_corners() if not selection or 'drift' in selection: V_bi = -silicon.get_diffusion_potential(n_eff, temperature) potential = fields.calculate_3D_sensor_potential( mesh, width_x, width_y, n_pixel_x, n_pixel_y, radius, nD, n_eff, V_bias, V_readout, V_bi) pot_descr = fields.Description( potential, min_x=min_x, max_x=max_x, min_y=min_y, max_y=max_y, nx=nx, # um res. ny=ny, # um res. smoothing=smoothing) if selection and 'drift' in selection: return pot_descr, geom_descr if not selection or 'weighting' in selection: w_potential = fields.calculate_3D_sensor_w_potential(mesh, width_x, width_y, n_pixel_x, n_pixel_y, radius, nD=nD) pot_w_descr = fields.Description(w_potential, min_x=min_x, max_x=max_x, min_y=min_y, max_y=max_y, nx=nx, ny=ny, smoothing=smoothing) if selection and 'weighting' in selection: return pot_w_descr, geom_descr return pot_w_descr, pot_descr, geom_descr
def sensor_planar(): # Sensor parameters n_eff = 6.2e12 n_pixel = 9 width = 50. pitch = 30. thickness = 250. smoothing = 0.05 resolution = 287 temperature = 300. V_bias = -300. V_readout = 0. # Create sensor 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, # Calculate drift potential only # to safe time selection='drift', resolution=resolution, # Might have to be adjusted when changing # the geometry smoothing=smoothing) # Build in voltage needed for analytical solution V_bi = -silicon.get_diffusion_potential(n_eff, temperature) # Plot analytical / numerical result with depletion region in 1D y = np.linspace(0, thickness, 1000) x = np.zeros_like(y) plt.plot(y, pot_descr.get_potential(x, y), label='Potential, numerical', linewidth=2) pot_masked = np.ma.masked_array(pot_descr.get_potential(x, y), mask=pot_descr.get_depl_mask(x, y)) plt.plot(y, pot_masked, label='Potential, numerical, depl.', linewidth=2) plt.plot( [pot_descr.get_depletion(x[500]), pot_descr.get_depletion(x[500])], plt.ylim(), label='Depletion, numerical ', linewidth=2) plt.plot(y, fields.get_potential_planar_analytic_1D(y, V_bias=V_bias + V_bi, V_readout=V_readout, n_eff=n_eff, D=thickness), '--', label='Potential, analytical', linewidth=2) plt.plot([ silicon.get_depletion_depth(np.abs(V_bias), n_eff / 1e12, temperature), silicon.get_depletion_depth(np.abs(V_bias), n_eff / 1e12, temperature) ], plt.ylim(), '--', label='Depletion, analytical', linewidth=2) plt.ylabel('Potential [V]') plt.legend(loc=1) plt.ylabel('Potential [V]') ax2 = plt.gca().twinx() ax2.plot(y, pot_descr.get_field(x, y)[1], '--', label='Field, numerical', linewidth=2) ax2.plot(y, fields.get_electric_field_analytic(x, y, V_bias=V_bias, V_readout=V_readout, n_eff=n_eff, D=thickness)[1], '--', label='Field, analytical', linewidth=2) plt.ylabel('Field [V/cm]') plt.legend(loc=4) plt.ylabel('Field [V/cm]') plt.title('Potential in a not fully depleted planar sensor') plt.xlabel('Position [um]') plt.grid() plt.show() # Plot numerical result in 2D plot.plot_planar_sensor( width=width, pitch=pitch, thickness=thickness, n_pixel=n_pixel, # Weighting field = 0 at backplane V_backplane=V_bias, # Weighting field = 1 at readout V_readout=V_readout, pot_func=pot_descr.get_potential, field_func=pot_descr.get_field, depl_func=pot_descr.get_depletion, # Comment in if you want to see the mesh mesh=None, # potential.mesh, title='Planar sensor potential')