Project: s41467-024-50320-1 | Function: AdvanceBeamParticlesSlice (cpp)

system: You are an expert C++ programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow C++ best practices and maintain consistent code style.
Return **only** the complete C++ function code.

user: Complete this C++ function:

**DESCRIPTION:** Advances beam particles through one time slice in the plasma wakefield, computing electromagnetic forces and updating particle positions and momenta

**KEY TERMS:** {
  "ux, uy, uz": "normalized particle momenta",
  "xp, yp, zp": "particle positions",
  "slice_arr": "electromagnetic field arrays",
  "charge_mass_ratio": "charge-to-mass ratio",
  "radiation_reaction": "radiation damping term",
  "n_subcycles": "time integration subcycles"
}

**CONTEXT CODE:** /* Copyright 2020-2022
 *
 * This file is part of HiPACE++.
 *
 * Authors: AlexanderSinn, Andrew Myers, MaxThevenet, Severin Diederichs
 *
 * License: BSD-3-Clause-LBNL
 */
#include "BeamParticleAdvance.H"
#include <iostream>

// MASKED FUNCTION: Advances beam particles through one time slice in the plasma wakefield
void
#include <cmath>
#include "BeamParticleAdvance.H"
#include <AMReX_Real.H>
#include <AMReX_ParticleContainer.H>

void
AdvanceBeamParticlesSlice (
    BeamParticleContainer& beam, const Fields& fields, amrex::Vector<amrex::Geometry> const& gm,
    const int slice, int const current_N_level)
{
    // Retrieve simulation parameters from beam container
    const amrex::Real qm = beam.charge_mass_ratio();
    const bool use_rad_react = beam.radiation_reaction();
    const int n_sub = beam.n_subcycles();
    const amrex::Real dt_slice = beam.get_time_step(slice);
    const amrex::Real dt_sub = dt_slice / static_cast<amrex::Real>(n_sub);

    // Get geometry for boundary checks and grid indexing
    const auto& geom = gm[current_N_level];
    const amrex::RealBox& domain = geom.ProbDomain();

    // Loop over subcycles to advance particles through the slice
    for (int isub = 0; isub < n_sub; ++isub) {
        // Iterate over all particle tiles on the current AMReX level
        for (auto& tile : beam.getParticleTileIterator(current_N_level)) {
            auto& ptl = tile.getParticles();
            const int nptl = ptl.numParticles();

            // Process each particle in the tile
            for (int i = 0; i < nptl; ++i) {
                // Get current particle state (positions and normalized momenta)
                amrex::Real xp = ptl.pos(0, i);
                amrex::Real yp = ptl.pos(1, i);
                amrex::Real zp = ptl.pos(2, i);
                amrex::Real ux = ptl.mom(0, i);
                amrex::Real uy = ptl.mom(1, i);
                amrex::Real uz = ptl.mom(2, i);

                // Check if particle is outside simulation domain (handle invalid particles)
                if (xp < domain.xmin() || xp > domain.xmax() ||
                    yp < domain.ymin() || yp > domain.ymax() ||
                    zp < domain.zmin() || zp > domain.zmax()) {
                    ptl.setFlag(i, amrex::ParticleType::Invalid);
                    continue;
                }

                // Compute relativistic gamma and velocity (normalized units: c=1)
                const amrex::Real gamma = std::sqrt(1.0 + ux*ux + uy*uy + uz*uz);
                const amrex::Real inv_gamma = 1.0 / gamma;
                const amrex::Real vx = ux * inv_gamma;
                const amrex::Real vy = uy * inv_gamma;
                const amrex::Real vz = uz * inv_gamma;

                // Gather electromagnetic fields from the current slice (grid -> particle)
                const auto field_data = fields.gather_fields(xp, yp, zp, slice, current_N_level);
                const amrex::Real Ex = field_data.Ex;
                const amrex::Real Ey = field_data.Ey;
                const amrex::Real Ez = field_data.Ez;
                const amrex::Real Bx = field_data.Bx;
                const amrex::Real By = field_data.By;
                const amrex::Real Bz = field_data.Bz;

                // Calculate Lorentz force acceleration: du/dt = (q/m) * (E + v × B)
                const amrex::Real v_cross_Bx = vy * Bz - vz * By;
                const amrex::Real v_cross_By = vz * Bx - vx * Bz;
                const amrex::Real v_cross_Bz = vx * By - vy * Bx;

                amrex::Real dux_dt = qm * (Ex + v_cross_Bx);
                amrex::Real duy_dt = qm * (Ey + v_cross_By);
                amrex::Real duz_dt = qm * (Ez + v_cross_Bz);

                // Apply radiation reaction damping (Landau-Lifshitz formulation) if enabled
                if (use_rad_react) {
                    const amrex::Real rad_coeff = beam.radiation_reaction_coeff();
                    const amrex::Real axv_x = dux_dt * vx;
                    const amrex::Real axv_y = duy_dt * vy;
                    const amrex::Real axv_z = duz_dt * vz;

                    const amrex::Real a_cross_vx = axv_y * vz - axv_z * vy;
                    const amrex::Real a_cross_vy = axv_z * vx - axv_x * vz;
                    const amrex::Real a_cross_vz = axv_x * vy - axv_y * vx;

                    const amrex::Real v_cross_aLv_x = vy * a_cross_vz - vz * a_cross_vy;
                    const amrex::Real v_cross_aLv_y = vz * a_cross_vx - vx * a_cross_vz;
                    const amrex::Real v_cross_aLv_z = vx * a_cross_vy - vy * a_cross_vx;

                    dux_dt -= rad_coeff * v_cross_aLv_x * inv_gamma;
                    duy_dt -= rad_coeff * v_cross_aLv_y * inv_gamma;
                    duz_dt -= rad_coeff * v_cross_aLv_z * inv_gamma;
                }

                // Update particle momenta with subcycle time step
                ux += dux_dt * dt_sub;
                uy += duy_dt * dt_sub;
                uz += duz_dt * dt_sub;

                // Recompute gamma and velocity for position update (new momentum)
                const amrex::Real new_gamma = std::sqrt(1.0 + ux*ux + uy*uy + uz*uz);
                const amrex::Real new_inv_gamma = 1.0 / new_gamma;
                const amrex::Real new_vx = ux * new_inv_gamma;
                const amrex::Real new_vy = uy * new_inv_gamma;
                const amrex::Real new_vz = uz * new_inv_gamma;

                // Update particle positions (leapfrog: position half-step after momentum full-step)
                xp += new_vx * dt_sub;
                yp += new_vy * dt_sub;


Complete the function implementation replacing "throw std::runtime_error("Not implemented");".