def simulate_beam_focusing( z_injection_plane, write_dir ): """ Simulate a focusing beam in the boosted frame Parameters ---------- z_injection_plane: float or None when this is not None, the injection through a plane is activated. write_dir: string The directory where the boosted diagnostics are written. """ # Initialize the simulation object sim = Simulation( Nz, zmax, Nr, rmax, Nm, dt, zmin=zmin, gamma_boost=gamma_boost, boundaries='open', use_cuda=use_cuda, v_comoving=v_comoving ) # Note: no macroparticles get created because we do not pass # the density and number of particle per cell # Remove the plasma particles sim.ptcl = [] # Initialize the bunch, along with its space charge add_elec_bunch_gaussian( sim, sigma_r, sigma_z, n_emit, gamma0, sigma_gamma, Q, N, tf=(z_focus-z0)/c, zf=z_focus, boost=boost, z_injection_plane=z_injection_plane ) # Configure the moving window sim.set_moving_window( v=c ) # Add a field diagnostic sim.diags = [ BackTransformedParticleDiagnostic( zmin, zmax, c, dt_snapshot_lab, Ntot_snapshot_lab, gamma_boost, period=100, fldobject=sim.fld, species={'bunch':sim.ptcl[0]}, comm=sim.comm, write_dir=write_dir) ] # Run the simulation sim.step( N_step )
def run_simulation(gamma_boost, show): """ Run a simulation with a relativistic electron bunch crosses a laser Parameters ---------- gamma_boost: float The Lorentz factor of the frame in which the simulation is carried out. show: bool Whether to show a plot of the angular distribution """ # Boosted frame boost = BoostConverter(gamma_boost) # The simulation timestep diag_period = 100 N_step = 101 # Number of iterations to perform # Calculate timestep to resolve the interaction with enough points laser_duration_boosted, = boost.copropag_length([laser_duration], beta_object=-1) bunch_sigma_z_boosted, = boost.copropag_length([bunch_sigma_z], beta_object=1) dt = (4 * laser_duration_boosted + bunch_sigma_z_boosted / c) / N_step # Initialize the simulation object zmax, zmin = boost.copropag_length([zmax_lab, zmin_lab], beta_object=1.) sim = Simulation(Nz, zmax, Nr, rmax, Nm, dt, p_zmin=0, p_zmax=0, p_rmin=0, p_rmax=0, p_nz=1, p_nr=1, p_nt=1, n_e=1, dens_func=None, zmin=zmin, boundaries='periodic', use_cuda=use_cuda) # Remove particles that were previously created sim.ptcl = [] print('Initialized simulation') # Add electron bunch (automatically converted to boosted-frame) add_elec_bunch_gaussian(sim, sig_r=1.e-6, sig_z=bunch_sigma_z, n_emit=0., gamma0=gamma_bunch_mean, sig_gamma=gamma_bunch_rms, Q=Q_bunch, N=N_bunch, tf=0.0, zf=0.5 * (zmax + zmin), boost=boost) elec = sim.ptcl[0] print('Initialized electron bunch') # Add a photon species photons = Particles(q=0, m=0, n=0, Npz=1, zmin=0, zmax=0, Npr=1, rmin=0, rmax=0, Nptheta=1, dt=sim.dt, ux_m=0., uy_m=0., uz_m=0., ux_th=0., uy_th=0., uz_th=0., dens_func=None, continuous_injection=False, grid_shape=sim.fld.interp[0].Ez.shape, particle_shape='linear', use_cuda=sim.use_cuda) sim.ptcl.append(photons) print('Initialized photons') # Activate Compton scattering for electrons of the bunch elec.activate_compton(target_species=photons, laser_energy=laser_energy, laser_wavelength=laser_wavelength, laser_waist=laser_waist, laser_ctau=laser_ctau, laser_initial_z0=laser_initial_z0, ratio_w_electron_photon=50, boost=boost) print('Activated Compton') # Add diagnostics if write_hdf5: sim.diags = [ ParticleDiagnostic(diag_period, species={ 'electrons': elec, 'photons': photons }, comm=sim.comm) ] # Get initial total momentum initial_total_elec_px = (elec.w * elec.ux).sum() * m_e * c initial_total_elec_py = (elec.w * elec.uy).sum() * m_e * c initial_total_elec_pz = (elec.w * elec.uz).sum() * m_e * c ### Run the simulation for species in sim.ptcl: species.send_particles_to_gpu() for i_step in range(N_step): for species in sim.ptcl: species.halfpush_x() elec.handle_elementary_processes(sim.time + 0.5 * sim.dt) for species in sim.ptcl: species.halfpush_x() # Increment time and run diagnostics sim.time += sim.dt sim.iteration += 1 for diag in sim.diags: diag.write(sim.iteration) # Print fraction of photons produced if i_step % 10 == 0: for species in sim.ptcl: species.receive_particles_from_gpu() simulated_frac = photons.w.sum() / elec.w.sum() for species in sim.ptcl: species.send_particles_to_gpu() print( 'Iteration %d: Photon fraction per electron = %f' \ %(i_step, simulated_frac) ) for species in sim.ptcl: species.receive_particles_from_gpu() # Check estimation of photon fraction check_photon_fraction(simulated_frac) # Check conservation of momentum (is only conserved ) if elec.compton_scatterer.ratio_w_electron_photon == 1: check_momentum_conservation(gamma_boost, photons, elec, initial_total_elec_px, initial_total_elec_py, initial_total_elec_pz) # Transform the photon momenta back into the lab frame photon_u = 1. / photons.inv_gamma photon_lab_pz = boost.gamma0 * (photons.uz + boost.beta0 * photon_u) photon_lab_p = boost.gamma0 * (photon_u + boost.beta0 * photons.uz) # Plot the scaled angle and frequency if show: import matplotlib.pyplot as plt # Bin the photons on a grid in frequency and angle freq_min = 0.5 freq_max = 1.2 N_freq = 500 gammatheta_min = 0. gammatheta_max = 1. N_gammatheta = 100 hist_range = [[freq_min, freq_max], [gammatheta_min, gammatheta_max]] extent = [freq_min, freq_max, gammatheta_min, gammatheta_max] fundamental_frequency = 4 * gamma_bunch_mean**2 * c / laser_wavelength photon_scaled_freq = photon_lab_p * c / (h * fundamental_frequency) gamma_theta = gamma_bunch_mean * np.arccos( photon_lab_pz / photon_lab_p) grid, freq_bins, gammatheta_bins = np.histogram2d( photon_scaled_freq, gamma_theta, weights=photons.w, range=hist_range, bins=[N_freq, N_gammatheta]) # Normalize by solid angle, frequency and number of photons dw = (freq_bins[1] - freq_bins[0]) * 2 * np.pi * fundamental_frequency dtheta = (gammatheta_bins[1] - gammatheta_bins[0]) / gamma_bunch_mean domega = 2. * np.pi * np.sin( gammatheta_bins / gamma_bunch_mean) * dtheta grid /= dw * domega[np.newaxis, 1:] * elec.w.sum() grid = np.where(grid == 0, np.nan, grid) plt.imshow(grid.T, origin='lower', extent=extent, cmap='gist_earth', aspect='auto', vmax=1.8e-16) plt.title('Particles, $d^2N/d\omega \,d\Omega$') plt.xlabel('Scaled energy ($\omega/4\gamma^2\omega_\ell$)') plt.ylabel(r'$\gamma \theta$') plt.colorbar() # Plot theory plt.plot(1. / (1 + gammatheta_bins**2), gammatheta_bins, color='r') plt.show() plt.clf()
sim = Simulation(Nz, zmax, Nr, rmax, Nm, dt, 0, 0, 0, 0, 2, 2, 4, 0., zmin=zmin, n_order=n_order, boundaries={ 'z': 'open', 'r': 'reflective' }) # Configure the moving window sim.set_moving_window(v=c) # Suppress the particles that were intialized by default and add the bunch sim.ptcl = [] add_elec_bunch_gaussian(sim, sig_r, sig_z, n_emit, gamma0, sig_gamma, Q, N, tf, zf) # Set the diagnostics sim.diags = [FieldDiagnostic(10, sim.fld, comm=sim.comm)] # Perform one simulation step (essentially in order to write the diags) sim.step(1)
def test_boosted_output(gamma_boost=10.): """ # TODO Parameters ---------- gamma_boost: float The Lorentz factor of the frame in which the simulation is carried out. """ # The simulation box Nz = 500 # Number of gridpoints along z zmax_lab = 0.e-6 # Length of the box along z (meters) zmin_lab = -20.e-6 Nr = 10 # Number of gridpoints along r rmax = 10.e-6 # Length of the box along r (meters) Nm = 2 # Number of modes used # Number of timesteps N_steps = 500 diag_period = 20 # Period of the diagnostics in number of timesteps dt_lab = (zmax_lab - zmin_lab) / Nz * 1. / c T_sim_lab = N_steps * dt_lab # Move into directory `tests` os.chdir('./tests') # Initialize the simulation object sim = Simulation( Nz, zmax_lab, Nr, rmax, Nm, dt_lab, 0, 0, # No electrons get created because we pass p_zmin=p_zmax=0 0, rmax, 1, 1, 4, n_e=0, zmin=zmin_lab, initialize_ions=False, gamma_boost=gamma_boost, v_comoving=-0.9999 * c, boundaries='open', use_cuda=use_cuda) sim.set_moving_window(v=c) # Remove the electron species sim.ptcl = [] # Add a Gaussian electron bunch # Note: the total charge is 0 so all fields should remain 0 # throughout the simulation. As a consequence, the motion of the beam # is a mere translation. N_particles = 3000 add_elec_bunch_gaussian(sim, sig_r=1.e-6, sig_z=1.e-6, n_emit=0., gamma0=100, sig_gamma=0., Q=0., N=N_particles, zf=0.5 * (zmax_lab + zmin_lab), boost=BoostConverter(gamma_boost)) sim.ptcl[0].track(sim.comm) # openPMD diagnostics sim.diags = [ BackTransformedParticleDiagnostic(zmin_lab, zmax_lab, v_lab=c, dt_snapshots_lab=T_sim_lab / 3., Ntot_snapshots_lab=3, gamma_boost=gamma_boost, period=diag_period, fldobject=sim.fld, species={"bunch": sim.ptcl[0]}, comm=sim.comm) ] # Run the simulation sim.step(N_steps) # Check consistency of the back-transformed openPMD diagnostics: # Make sure that all the particles were retrived by checking particle IDs ts = OpenPMDTimeSeries('./lab_diags/hdf5/') ref_pid = np.sort(sim.ptcl[0].tracker.id) for iteration in ts.iterations: pid, = ts.get_particle(['id'], iteration=iteration) pid = np.sort(pid) assert len(pid) == N_particles assert np.all(ref_pid == pid) # Remove openPMD files shutil.rmtree('./lab_diags/') os.chdir('../')