예제 #1
0
    def test_plot_mesh(self):
        mesh = geometry.mesh_planar_sensor(n_pixel=5,
                                           width=50.,
                                           thickness=100.,
                                           resolution=100.,
                                           filename='planar_mesh_tmp_3.msh')

        plot.plot_mesh(mesh)
예제 #2
0
    def test_mesh_planar(self):
        ''' Check the mesh generation for planar sensor.

        Likely due to random pertubation (on purpose) the result
        is never exactly constant. Thus only numbers of points checked.
        '''

        n_pixel = 5
        width = 50
        thickness = 200

        geometry.mesh_planar_sensor(n_pixel,
                                    width,
                                    thickness=thickness,
                                    resolution=100,
                                    filename='planar_mesh_tmp.msh')

        points, _, _, _, _ = meshio.read('planar_mesh_tmp.msh')

        self.assertGreater(points.shape[0], 14000)
예제 #3
0
    def test_save_and_load(self):
        ''' Check the saving and loading to disk.
        '''

        # Create data
        mesh = geometry.mesh_planar_sensor(n_pixel=9,
                                           width=50.,
                                           thickness=300.,
                                           resolution=50.,
                                           filename='planar_mesh_example.msh')
        potential = fields.calculate_planar_sensor_potential(mesh=mesh,
                                                             width=50.,
                                                             pitch=45.,
                                                             n_pixel=9,
                                                             thickness=300.,
                                                             n_eff=5e12,
                                                             V_bias=-160.,
                                                             V_readout=0.,
                                                             V_bi=1.5)
        min_x = float(mesh.getFaceCenters()[0, :].min())
        max_x = float(mesh.getFaceCenters()[0, :].max())
        description = fields.Description(potential,
                                         min_x=min_x,
                                         max_x=max_x,
                                         min_y=0,
                                         max_y=300.,
                                         nx=202,
                                         ny=200)

        # Force the creation of the potential and field functions
        description.get_field(0, 0)

        # Store and reload object
        tools.save(description, 'tmp.sc')
        description_2 = tools.load('tmp.sc')

        self.assertTrue(np.all(description.pot_data == description_2.pot_data))
        self.assertTrue(
            np.all(description.potential_grid == description_2.potential_grid))
        self.assertTrue(
            np.all(
                np.array(
                    description.get_field(description._xx, description._yy)) ==
                np.array(
                    description_2.get_field(description_2._xx,
                                            description_2._yy))))
예제 #4
0
def get_mesh(n_pixel, width, thickness, resolution):
    try:
        return tools.load(
            os.path.join(
                DATAFOLDER, 'mesh_%d_%d_%d_%d' %
                (n_pixel, int(width), int(thickness), int(resolution))))
    except IOError:
        mesh = geometry.mesh_planar_sensor(n_pixel=n_pixel,
                                           width=width,
                                           thickness=thickness,
                                           resolution=resolution)
        tools.save(
            mesh,
            os.path.join(
                DATAFOLDER, 'mesh_%d_%d_%d_%d' %
                (n_pixel, int(width), int(thickness), int(resolution))))
        return mesh
예제 #5
0
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
예제 #6
0
    def test_weighting_potential_planar(self):
        '''  Compares estimated weighting potential to analytical solution.
        '''

        # Influences how correct the field for the center pixel(s) is
        # due to more far away infinite boundary condition
        n_pixel = 11

        for i, width in enumerate([50., 200.]):
            # FIXME: 50 um thichness does not work
            for j, thickness in enumerate([50., 250.]):
                # Analytical solution only existing for pixel width = readout
                # pitch (100% fill factor)
                pitch = width

                # Tune resolution properly for time/accuracy trade off
                if i == 0 and j == 0:
                    resolution = 200
                    continue
                elif i == 0 and j == 1:
                    resolution = 100
                    continue
                elif i == 1 and j == 0:
                    resolution = 600
                    continue  # FIXME: 50 thichness / 200 width does not work
                elif i == 1 and j == 1:
                    resolution = 200
                else:
                    raise RuntimeError('Loop index unknown')

                mesh = geometry.mesh_planar_sensor(
                    n_pixel=n_pixel,
                    width=width,
                    thickness=thickness,
                    resolution=resolution,
                    filename='planar_mesh_tmp_2.msh')

                potential = fields.calculate_planar_sensor_w_potential(
                    mesh=mesh,
                    width=width,
                    pitch=pitch,
                    n_pixel=n_pixel,
                    thickness=thickness)

                min_x, max_x = -width * float(n_pixel), width * float(n_pixel)
                min_y, max_y = 0., thickness
                nx, ny = 1000, 1000

                potential_description = fields.Description(potential,
                                                           min_x=min_x,
                                                           max_x=max_x,
                                                           min_y=min_y,
                                                           max_y=max_y,
                                                           nx=nx,
                                                           ny=ny,
                                                           smoothing=0.1)

                def potential_analytic(x, y):
                    return fields.get_weighting_potential_analytic(
                        x, y,
                        D=thickness,
                        S=width,
                        is_planar=True)

                # Create x,y grid
                x = np.linspace(min_x, max_x, nx)
                y = np.linspace(min_y, max_y, ny)
                xx, yy = np.meshgrid(x, y, sparse=True)

                # Evaluate potential on a grid
                pot_analytic = potential_analytic(xx, yy)
                pot_numeric = potential_description.get_potential(xx, yy)

#                 import matplotlib.pyplot as plt
#                 for pos_x in [0, 10, 15, 30, 45]:
#                     plt.plot(y, pot_analytic.T[nx / 2 + pos_x, :],
#                              label='Analytic')
#                 for pos_x in [0, 10, 15, 30, 45]:
#                     plt.plot(y, pot_numeric.T[nx / 2 + pos_x, :],
#                              label='Numeric')
#                 plt.legend(loc=0)
#                 plt.show()

                # Check only at center pixel, edge pixel are not interessting
                for pos_x in [-45, -30, -15, -10, 0, 10, 15, 30, 45]:
                    sel = pot_analytic.T[nx / 2 + pos_x, :] > 0.01
                    # Check with very tiny and tuned error allowance
                    self.assertTrue(np.allclose(
                        pot_analytic.T[nx / 2 + pos_x, sel],
                        pot_numeric.T[nx / 2 + pos_x, sel],
                        rtol=0.01, atol=0.005))
예제 #7
0
    def test_weighting_field_planar(self):
        '''  Compare weighting field to numerical solution.
        '''

        width = 50.
        # Analytical solution only existing for pixel width = readout pitch
        # (100 % fill factor)
        pitch = width
        thickness = 200.
        n_pixel = 11

        mesh = geometry.mesh_planar_sensor(
            n_pixel=n_pixel,
            width=width,
            thickness=thickness,
            resolution=200,
            filename='planar_mesh_tmp_2.msh')

        potential = fields.calculate_planar_sensor_w_potential(
            mesh=mesh,
            width=width,
            pitch=pitch,
            n_pixel=n_pixel,
            thickness=thickness)

        # Define field/potential domain
        min_x, max_x = -width * float(n_pixel), width * float(n_pixel)
        min_y, max_y = 0., thickness

        # Create x,y grid
        nx, ny = 1000, 1000
        x = np.linspace(min_x, max_x, nx)
        y = np.linspace(min_y, max_y, ny)
        xx, yy = np.meshgrid(x, y, sparse=True)

        field_description = fields.Description(potential,
                                               min_x=min_x,
                                               max_x=max_x,
                                               min_y=min_y,
                                               max_y=max_y,
                                               nx=nx,
                                               ny=ny,
                                               smoothing=0.2)

        def field_analytic(x, y):
            return fields.get_weighting_field_analytic(x, y, D=thickness,
                                                       S=width, is_planar=True)

        # Evaluate field on a grid
        f_analytic_x, f_analytic_y = field_analytic(xx, yy)
        f_numeric_x, f_numeric_y = field_description.get_field(xx, yy)

        # Check only at center pixel, edge pixel are not interessting
        for pox_x in [-45, -30, -15, -10, 0, 10, 15, 30, 45]:
            self.assertTrue(np.allclose(
                f_analytic_x.T[
                    nx / 2 + pox_x, :], f_numeric_x.T[nx / 2 + pox_x, :],
                rtol=0.01, atol=0.01))
            self.assertTrue(np.allclose(
                f_analytic_y.T[
                    nx / 2 + pox_x, :], f_numeric_y.T[nx / 2 + pox_x, :],
                rtol=0.01, atol=0.01))