Ejemplo n.º 1
0
    def deposit(self, fld, fieldtype, comm):
        """
        Deposit the charge or current of the virtual particles onto the grid

        This function closely mirrors the deposit function of the regular
        macroparticles, but also introduces a few specific optimization:
        - use the particle velocities instead of the momenta for J
        - deposit the currents and charge into a small-size array

        Parameter
        ----------
        fld : a Field object
             Contains the list of InterpolationGrid objects with
             the field values as well as the prefix sum.

        fieldtype : string
             Indicates which field to deposit
             Either 'J' or 'rho'

        comm : a BoundaryCommunicator object
             Allows to extract the boundaries of the physical domain
        """
        # Check if baseline_z is in the local physical domain
        # (This prevents out-of-bounds errors, and prevents 2 neighboring
        # processors from simultaneously depositing the laser antenna)
        zmin_local, zmax_local = comm.get_zmin_zmax(local=True,
                                                    with_damp=False,
                                                    with_guard=False,
                                                    rank=comm.rank)
        # Interrupt this function if the antenna is not in the local domain
        z_antenna = self.baseline_z[0]
        if (z_antenna < zmin_local) or (z_antenna >= zmax_local):
            return

        # Shortcut for the list of InterpolationGrid objects
        grid = fld.interp

        # Set the buffers to zero
        if fieldtype == 'rho':
            self.rho_buffer[:, :, :] = 0.
        elif fieldtype == 'J':
            self.Jr_buffer[:, :, :] = 0.
            self.Jt_buffer[:, :, :] = 0.
            self.Jz_buffer[:, :, :] = 0.

        # Indices and weights in z:
        # same for both the negative and positive virtual particles
        iz, Sz = weights(self.baseline_z,
                         grid[0].invdz,
                         grid[0].zmin,
                         grid[0].Nz,
                         direction='z',
                         shape_order=1)
        # Find the z index where the small-size buffers should be added
        # to the large-size arrays rho, Jr, Jt, Jz
        iz_min = iz.min()
        iz_max = iz.max()
        # Since linear shape are used, and since the virtual particles all
        # have the same z position, iz_max is necessarily equal to iz_min+1
        # This is a sanity check, to avoid out-of-bound access later on.
        assert iz_max == iz_min + 1
        # Substract from the array of indices in order to find the particle
        # index within the small-size buffers
        iz = iz - iz_min

        # Deposit the charge/current of positive and negative
        # virtual particles successively, into the small-size buffers
        for q in [-1, 1]:
            self.deposit_virtual_particles(q, fieldtype, grid, iz, Sz)

        # Copy the small-size buffers into the large-size arrays
        # (When running on the GPU, this involves copying the
        # small-size buffers from CPU to GPU)
        if fieldtype == 'rho':
            self.copy_rho_buffer(iz_min, grid)
        elif fieldtype == 'J':
            self.copy_J_buffer(iz_min, grid)
Ejemplo n.º 2
0
    def deposit_virtual_particles(self, q, fieldtype, grid, iz, Sz):
        """
        Deposit the charge/current of the positive (q=+1) or negative
        (q=-1) virtual macroparticles

        Parameters
        ----------
        q: float (either +1 or -1)
            Indicates whether to deposit the charge/current
            of the positive or negative virtual macroparticles

        fieldtype: string (either 'rho' or 'J')
            Indicates whether to deposit the charge or current

        grid: a list of InterpolationGrid object
            The grids on which to the deposit the charge/current

        iz, ir : 2darray of ints
            Arrays of shape (shape_order+1, Ntot)
            where Ntot is the number of macroparticles.
            Contains the index of the cells that each virtual macroparticle
            will deposit to.
            (In the case of the laser antenna, these arrays are constant
            in principle; but they are kept as arrays for compatibility
            with the deposit_field_numba function.)

        Sz, Sr: 2darray of ints
            Arrays of shape (shape_order+1, Ntot)
            where Ntot is the number of macroparticles
            Contains the weight for respective cells from iz and ir,
            for each macroparticle.
        """
        # Position of the particles
        x = self.baseline_x + q * self.excursion_x
        y = self.baseline_y + q * self.excursion_y
        vx = q * self.vx
        vy = q * self.vy
        w = q * self.w

        # Preliminary arrays for the cylindrical conversion
        r = np.sqrt(x**2 + y**2)
        # Avoid division by 0.
        invr = 1. / np.where(r != 0., r, 1.)
        cos = np.where(r != 0., x * invr, 1.)
        sin = np.where(r != 0., y * invr, 0.)

        # Indices and weights in r
        ir, Sr = weights(r,
                         grid[0].invdr,
                         grid[0].rmin,
                         grid[0].Nr,
                         direction='r',
                         shape_order=1)

        if fieldtype == 'rho':
            # ---------------------------------------
            # Deposit the charge density mode by mode
            # ---------------------------------------
            # Prepare auxiliary matrix
            exptheta = np.ones(self.Ntot, dtype='complex')
            # exptheta takes the value exp(im theta) throughout the loop
            for m in range(len(grid)):
                # Increment exptheta (notice the + : forward transform)
                if m == 1:
                    exptheta[:].real = cos
                    exptheta[:].imag = sin
                elif m > 1:
                    exptheta[:] = exptheta * (cos + 1.j * sin)
                # Deposit the fields into small-size buffer arrays
                # (The sign -1 with which the guards are added is not
                # trivial to derive but avoids artifacts on the axis)
                deposit_field_numba(w * exptheta, self.rho_buffer[m, :], iz,
                                    ir, Sz, Sr, -1.)

        elif fieldtype == 'J':
            # ----------------------------------------
            # Deposit the current density mode by mode
            # ----------------------------------------
            # Calculate the currents
            Jr = w * (cos * vx + sin * vy)
            Jt = w * (cos * vy - sin * vx)
            Jz = w * self.vz
            # Prepare auxiliary matrix
            exptheta = np.ones(self.Ntot, dtype='complex')
            # exptheta takes the value exp(im theta) throughout the loop
            for m in range(len(grid)):
                # Increment exptheta (notice the + : forward transform)
                if m == 1:
                    exptheta[:].real = cos
                    exptheta[:].imag = sin
                elif m > 1:
                    exptheta[:] = exptheta * (cos + 1.j * sin)
                # Deposit the fields into small-size buffer arrays
                # (The sign -1 with which the guards are added is not
                # trivial to derive but avoids artifacts on the axis)
                deposit_field_numba(Jr * exptheta, self.Jr_buffer[m, :], iz,
                                    ir, Sz, Sr, -1.)
                deposit_field_numba(Jt * exptheta, self.Jt_buffer[m, :], iz,
                                    ir, Sz, Sr, -1.)
                deposit_field_numba(Jz * exptheta, self.Jz_buffer[m, :], iz,
                                    ir, Sz, Sr, -1.)
Ejemplo n.º 3
0
    def deposit(self, fld, fieldtype):
        """
        Deposit the charge or current of the virtual particles onto the grid

        This function closely mirrors the deposit function of the regular
        macroparticles, but also introduces a few specific optimization:
        - use the particle velocities instead of the momenta for J
        - deposit the currents and charge into a small-size array

        Parameter
        ----------
        fld : a Field object
             Contains the list of InterpolationGrid objects with
             the field values as well as the prefix sum.

        fieldtype : string
             Indicates which field to deposit
             Either 'J' or 'rho'
        """
        # Interrupt this function if the antenna does not currently
        # deposit on the local domain (as determined by `update_current_rank`)
        if not self.deposit_on_this_rank:
            return

        # Shortcut for the list of InterpolationGrid objects
        grid = fld.interp

        # Set the buffers to zero
        if fieldtype == 'rho':
            self.rho_buffer[:, :, :] = 0.
        elif fieldtype == 'J':
            self.Jr_buffer[:, :, :] = 0.
            self.Jt_buffer[:, :, :] = 0.
            self.Jz_buffer[:, :, :] = 0.

        # Indices and weights in z:
        # same for both the negative and positive virtual particles
        iz, Sz = weights(self.baseline_z,
                         grid[0].invdz,
                         grid[0].zmin,
                         grid[0].Nz,
                         direction='z',
                         shape_order=1)
        # Find the z index where the small-size buffers should be added
        # to the large-size arrays rho, Jr, Jt, Jz
        iz_min = iz.min()
        iz_max = iz.max()
        # Since linear shape are used, and since the virtual particles all
        # have the same z position, iz_max is necessarily equal to iz_min+1
        # This is a sanity check, to avoid out-of-bound access later on.
        assert iz_max == iz_min + 1
        # Substract from the array of indices in order to find the particle
        # index within the small-size buffers
        iz = iz - iz_min

        # Deposit the charge/current of positive and negative
        # virtual particles successively, into the small-size buffers
        for q in [-1, 1]:
            self.deposit_virtual_particles(q, fieldtype, grid, iz, Sz)

        # Copy the small-size buffers into the large-size arrays
        # (When running on the GPU, this involves copying the
        # small-size buffers from CPU to GPU)
        if fieldtype == 'rho':
            self.copy_rho_buffer(iz_min, grid)
        elif fieldtype == 'J':
            self.copy_J_buffer(iz_min, grid)