예제 #1
0
def check_charge_conservation(sim, rho_ions):
    """
    Check that the relation div(E) - rho/epsilon_0 is satisfied, with a
    relative precision close to the machine precision (directly in spectral space)

    Parameters
    ----------
    sim: Simulation object
    rho_ions: list of 2d complex arrays (one per mode)
        The density of the ions (which are not explicitly present in the `sim`
        object, since they are motionless)
    """
    # Create a global field object across all subdomains, and copy the fields
    global_Nz, _ = sim.comm.get_Nz_and_iz(local=False,
                                          with_damp=False,
                                          with_guard=False)
    global_zmin, global_zmax = sim.comm.get_zmin_zmax(local=False,
                                                      with_damp=False,
                                                      with_guard=False)
    global_fld = Fields(global_Nz,
                        global_zmax,
                        sim.fld.Nr,
                        sim.fld.rmax,
                        sim.fld.Nm,
                        sim.fld.dt,
                        zmin=global_zmin,
                        n_order=sim.fld.n_order,
                        use_cuda=False)
    # Gather the fields of the interpolation grid
    for m in range(sim.fld.Nm):
        # Gather E
        for field in ['Er', 'Et', 'Ez']:
            local_array = getattr(sim.fld.interp[m], field)
            gathered_array = sim.comm.gather_grid_array(local_array)
            setattr(global_fld.interp[m], field, gathered_array)
        # Gather rho
        global_fld.interp[m].rho = \
            sim.comm.gather_grid_array( sim.fld.interp[m].rho + rho_ions[m] )

    # Loop over modes and check charge conservation in spectral space
    if sim.comm.rank == 0:
        global_fld.interp2spect('E')
        global_fld.interp2spect('rho_prev')
        for m in range(global_fld.Nm):
            spect = global_fld.spect[m]
            # Calculate div(E) in spectral space
            divE = spect.kr * (spect.Ep - spect.Em) + 1.j * spect.kz * spect.Ez
            # Calculate rho/epsilon_0 in spectral space
            rho_eps0 = spect.rho_prev / epsilon_0
            # Calculate relative RMS error
            rel_err = np.sqrt( np.sum(abs(divE - rho_eps0)**2) \
                / np.sum(abs(rho_eps0)**2) )
            print('Relative error on divE in mode %d: %e' % (m, rel_err))
            assert rel_err < 1.e-11
예제 #2
0
파일: bunch.py 프로젝트: zcl-maker/fbpic
def get_space_charge_fields(sim, ptcl, direction='forward'):
    """
    Add the space charge field from `ptcl` the interpolation grid

    This assumes that all the particles being passed have the same gamma.

    Parameters
    ----------
    sim : a Simulation object
        Contains the values of the fields, and the MPI communicator

    ptcl : a Particles object
        The list of the species which are relativistic and
        will produce a space charge field. (Do not pass the
        particles which are at rest.)

    direction : string, optional
        Can be either "forward" or "backward".
        Propagation direction of the beam.
    """
    if sim.comm.rank == 0:
        print("Calculating initial space charge field...")

    # Calculate the mean gamma by computing weighted sum on each subdomain
    w_sum_local = ptcl.w.sum()
    w_gamma_sum_local = (ptcl.w * 1. / ptcl.inv_gamma).sum()
    if sim.comm.mpi_comm is None:
        w_sum = w_sum_local
        w_gamma_sum = w_gamma_sum_local
    else:
        w_sum = sim.comm.mpi_comm.allreduce(w_sum_local)
        w_gamma_sum = sim.comm.mpi_comm.allreduce(w_gamma_sum_local)
    # Check that the number of particles is not 0
    if w_sum == 0:
        warnings.warn(
            "Tried to calculate space charge, but found 0 macroparticles in \n"
            "the corresponding species. Skipping space charge calculation...\n"
        )
        return
    else:
        gamma = w_gamma_sum / w_sum

    # Project the charge and currents onto the local subdomain
    sim.deposit('rho',
                exchange=True,
                species_list=[ptcl],
                update_spectral=False)
    sim.deposit('J', exchange=True, species_list=[ptcl], update_spectral=False)

    # Create a global field object across all subdomains, and copy the sources
    # (Space-charge calculation is a global operation)
    # Note: in the single-proc case, this is also useful in order not to
    # erase the pre-existing E and B field in sim.fld
    global_Nz, _ = sim.comm.get_Nz_and_iz(local=False,
                                          with_damp=True,
                                          with_guard=False)
    global_zmin, global_zmax = sim.comm.get_zmin_zmax(local=False,
                                                      with_damp=True,
                                                      with_guard=False)
    global_fld = Fields(global_Nz,
                        global_zmax,
                        sim.fld.Nr,
                        sim.fld.rmax,
                        sim.fld.Nm,
                        sim.fld.dt,
                        n_order=sim.fld.n_order,
                        smoother=sim.fld.smoother,
                        zmin=global_zmin,
                        use_cuda=False)
    # Gather the sources on the interpolation grid of global_fld
    for m in range(sim.fld.Nm):
        for field in ['Jr', 'Jt', 'Jz', 'rho']:
            local_array = getattr(sim.fld.interp[m], field)
            gathered_array = sim.comm.gather_grid_array(local_array,
                                                        with_damp=True)
            setattr(global_fld.interp[m], field, gathered_array)

    # Calculate the space-charge fields on the global grid
    # (For a multi-proc simulation: only performed by the first proc)
    if sim.comm.rank == 0:
        # - Convert the sources to spectral space
        global_fld.interp2spect('rho_prev')
        global_fld.interp2spect('J')
        if sim.filter_currents:
            global_fld.filter_spect('rho_prev')
            global_fld.filter_spect('J')
        # - Get the space charge fields in spectral space
        for m in range(global_fld.Nm):
            get_space_charge_spect(global_fld.spect[m], gamma, direction)
        # - Convert the fields back to real space
        global_fld.spect2interp('E')
        global_fld.spect2interp('B')

    # Communicate the results from proc 0 to the other procs
    # and add it to the interpolation grid of sim.fld.
    # - First find the indices at which the fields should be added
    Nz_local, iz_start_local_domain = sim.comm.get_Nz_and_iz(
        local=True, with_damp=True, with_guard=False, rank=sim.comm.rank)
    _, iz_start_local_array = sim.comm.get_Nz_and_iz(local=True,
                                                     with_damp=True,
                                                     with_guard=True,
                                                     rank=sim.comm.rank)
    iz_in_array = iz_start_local_domain - iz_start_local_array
    # - Then loop over modes and fields
    for m in range(sim.fld.Nm):
        for field in ['Er', 'Et', 'Ez', 'Br', 'Bt', 'Bz']:
            # Get the local result from proc 0
            global_array = getattr(global_fld.interp[m], field)
            local_array = sim.comm.scatter_grid_array(global_array,
                                                      with_damp=True)
            # Add it to the fields of sim.fld
            local_field = getattr(sim.fld.interp[m], field)
            local_field[iz_in_array:iz_in_array + Nz_local, :] += local_array

    if sim.comm.rank == 0:
        print("Done.\n")
예제 #3
0
def add_laser_direct(sim, laser_profile, boost):
    """
    Add a laser pulse in the simulation, by directly adding it to the mesh

    Note:
    -----
    Arbitrary laser profiles can be passed through `laser_profile`
    (which must provide the *transverse electric field*)
    For any profile:
    - The field is automatically decomposed into azimuthal modes
    - The Ez field is automatically calculated so as to ensure that div(E)=0
    - The B field is automatically calculated so as to ensure propagation
    in the right direction.

    Parameters:
    -----------
    sim: a Simulation object
        The structure that contains the simulation.

    laser_profile: a valid laser profile object
        Laser profiles can be imported from fbpic.lpa_utils.laser

    boost: a BoostConverter object or None
       Contains the information about the boost to be applied
    """
    if sim.comm.rank == 0:
        print("Initializing laser pulse on the mesh...")

    # Get the local azimuthally-decomposed laser fields Er and Et on each proc
    laser_Er, laser_Et = get_laser_Er_Et(sim, laser_profile, boost)
    # Save previous values on the grid, and replace them with the laser fields
    # (This is done in preparation for gathering among procs)
    saved_Er = []
    saved_Et = []
    for m in range(sim.fld.Nm):
        saved_Er.append(sim.fld.interp[m].Er.copy())
        sim.fld.interp[m].Er[:, :] = laser_Er[:, :, m]
        saved_Et.append(sim.fld.interp[m].Et.copy())
        sim.fld.interp[m].Et[:, :] = laser_Et[:, :, m]

    # Create a global field object across all subdomains, and copy the fields
    # (Calculating the self-consistent Ez and B is a global operation)
    global_Nz, _ = sim.comm.get_Nz_and_iz(local=False,
                                          with_damp=True,
                                          with_guard=False)
    global_zmin, global_zmax = sim.comm.get_zmin_zmax(local=False,
                                                      with_damp=True,
                                                      with_guard=False)
    global_fld = Fields(global_Nz,
                        global_zmax,
                        sim.fld.Nr,
                        sim.fld.rmax,
                        sim.fld.Nm,
                        sim.fld.dt,
                        zmin=global_zmin,
                        n_order=sim.fld.n_order,
                        use_cuda=False)
    # Gather the fields of the interpolation grid
    for m in range(sim.fld.Nm):
        for field in ['Er', 'Et']:
            local_array = getattr(sim.fld.interp[m], field)
            gathered_array = sim.comm.gather_grid_array(local_array,
                                                        with_damp=True)
            setattr(global_fld.interp[m], field, gathered_array)

    # Now that the (gathered) laser fields are stored in global_fld,
    # copy the saved field back into the local grid
    for m in range(sim.fld.Nm):
        sim.fld.interp[m].Er[:, :] = saved_Er[m]
        sim.fld.interp[m].Et[:, :] = saved_Et[m]

    # Calculate the Ez and B fields on the global grid
    # (For a multi-proc simulation: only performed by the first proc)
    if sim.comm.rank == 0:
        calculate_laser_fields(global_fld, laser_profile.propag_direction)

    # Communicate the results from proc 0 to the other procs
    # and add it to the interpolation grid of sim.fld.
    # - First find the indices at which the fields should be added
    Nz_local, iz_start_local_domain = sim.comm.get_Nz_and_iz(
        local=True, with_damp=True, with_guard=False, rank=sim.comm.rank)
    _, iz_start_local_array = sim.comm.get_Nz_and_iz(local=True,
                                                     with_damp=True,
                                                     with_guard=True,
                                                     rank=sim.comm.rank)
    iz_in_array = iz_start_local_domain - iz_start_local_array
    # - Then loop over modes and fields
    for m in range(sim.fld.Nm):
        for field in ['Er', 'Et', 'Ez', 'Br', 'Bt', 'Bz']:
            # Get the local result from proc 0
            global_array = getattr(global_fld.interp[m], field)
            local_array = sim.comm.scatter_grid_array(global_array,
                                                      with_damp=True)
            # Add it to the fields of sim.fld
            local_field = getattr(sim.fld.interp[m], field)
            local_field[iz_in_array:iz_in_array + Nz_local, :] += local_array

    if sim.comm.rank == 0:
        print("Done.\n")