From 3e3a0e6b1cee34326e644644bfe182c0e8a81fbd Mon Sep 17 00:00:00 2001 From: bowen zhu <2062141317@qq.com> Date: Wed, 26 Aug 2026 08:02:52 +0800 Subject: [PATCH 1/3] Fix RSPHERE Cartesian particle position conversion --- Examples/Tests/single_particle/CMakeLists.txt | 10 ++++ ..._rsphere_particle_coordinate_conversion.py | 37 +++++++++++++ ...est_rsphere_particle_coordinate_conversion | 54 +++++++++++++++++++ Source/Particles/WarpXParticleContainer.cpp | 7 ++- 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py create mode 100644 Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion diff --git a/Examples/Tests/single_particle/CMakeLists.txt b/Examples/Tests/single_particle/CMakeLists.txt index 4282376a20f..5e315b6c9ba 100644 --- a/Examples/Tests/single_particle/CMakeLists.txt +++ b/Examples/Tests/single_particle/CMakeLists.txt @@ -20,3 +20,13 @@ add_warpx_test( OFF # checksum OFF # dependency ) + +add_warpx_test( + test_rsphere_particle_coordinate_conversion # name + RSPHERE # dims + 1 # nprocs + inputs_test_rsphere_particle_coordinate_conversion # inputs + "analysis_rsphere_particle_coordinate_conversion.py" # analysis + OFF # checksum + OFF # dependency +) diff --git a/Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py b/Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py new file mode 100644 index 00000000000..f4b9ba1040b --- /dev/null +++ b/Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# Copyright 2026 The WarpX Community +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + +import math +from pathlib import Path + + +def read_position(diag_name): + path = Path("diags/reducedfiles") / f"{diag_name}.txt" + rows = [line for line in path.read_text().splitlines() if not line.startswith("#")] + row = [float(value) for value in rows[-1].split()] + + # ParticleExtrema columns are step, time, xmin, xmax, ymin, ymax, zmin, zmax, ... + position_min = tuple(row[index] for index in (2, 4, 6)) + position_max = tuple(row[index] for index in (3, 5, 7)) + for minimum, maximum in zip(position_min, position_max): + assert math.isclose(minimum, maximum, rel_tol=0.0, abs_tol=1.0e-15) + return position_min + + +expected_positions = { + "radial_position": (0.5, 0.0, 0.0), + "off_axis_position": (0.3, 0.4, -1.2), +} + +for diagnostic, expected in expected_positions.items(): + actual = read_position(diagnostic) + print(f"{diagnostic}: expected {expected}, actual {actual}") + for component, value, reference in zip("xyz", actual, expected): + assert math.isclose(value, reference, rel_tol=1.0e-14, abs_tol=1.0e-15), ( + f"{diagnostic} {component}: expected {reference}, got {value}" + ) diff --git a/Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion b/Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion new file mode 100644 index 00000000000..2860a516b1c --- /dev/null +++ b/Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion @@ -0,0 +1,54 @@ +# Copyright 2026 The WarpX Community +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + +max_step = 1 +warpx.const_dt = 1.e-12 + +amr.n_cell = 8 +amr.max_level = 0 +amr.blocking_factor = 8 +amr.max_grid_size = 8 + +geometry.dims = RSPHERE +geometry.prob_lo = 0.0 +geometry.prob_hi = 2.0 + +boundary.field_lo = none +boundary.field_hi = none +boundary.particle_lo = none +boundary.particle_hi = absorbing + +algo.maxwell_solver = none +algo.particle_shape = 1 + +particles.species_names = radial off_axis + +# A ray on the x axis has zero elevation from the x-y plane. +radial.charge = 0.0 +radial.mass = 1.0 +radial.injection_style = SingleParticle +radial.single_particle_pos = 0.5 0.0 0.0 +radial.single_particle_u = 0.0 0.0 0.0 +radial.single_particle_weight = 1.0 + +# This 3-4-12 triangle checks all three reconstructed Cartesian components, +# including the sign of the elevation angle. +off_axis.charge = 0.0 +off_axis.mass = 1.0 +off_axis.injection_style = SingleParticle +off_axis.single_particle_pos = 0.3 0.4 -1.2 +off_axis.single_particle_u = 0.0 0.0 0.0 +off_axis.single_particle_weight = 1.0 + +warpx.reduced_diags_names = radial_position off_axis_position + +radial_position.type = ParticleExtrema +radial_position.intervals = 1 +radial_position.species = radial + +off_axis_position.type = ParticleExtrema +off_axis_position.intervals = 1 +off_axis_position.species = off_axis diff --git a/Source/Particles/WarpXParticleContainer.cpp b/Source/Particles/WarpXParticleContainer.cpp index 2dceb2c01bd..bf42283bed7 100644 --- a/Source/Particles/WarpXParticleContainer.cpp +++ b/Source/Particles/WarpXParticleContainer.cpp @@ -252,7 +252,7 @@ WarpXParticleContainer::AddNParticles (int /*lev*/, long n, r[i-ibegin] = std::sqrt(x[i]*x[i] + y[i]*y[i] + z[i]*z[i]); theta[i-ibegin] = std::atan2(y[i], x[i]); const amrex::ParticleReal rxy = std::sqrt(x[i]*x[i] + y[i]*y[i]); - phi[i-ibegin] = std::atan2(rxy, r[i-ibegin]); + phi[i-ibegin] = std::atan2(z[i], rxy); #endif } @@ -273,9 +273,12 @@ WarpXParticleContainer::AddNParticles (int /*lev*/, long n, #elif defined(WARPX_DIM_1D_Z) amrex::ignore_unused(x,y); pinned_tile.push_back_real(PIdx::z, z.data() + ibegin, z.data() + iend); -#elif defined(WARPX_DIM_RCYLINDER) || defined(WARPX_DIM_RSPHERE) +#elif defined(WARPX_DIM_RCYLINDER) pinned_tile.push_back_real(PIdx::r, x.data() + ibegin, x.data() + iend); amrex::ignore_unused(y,z); +#elif defined(WARPX_DIM_RSPHERE) + pinned_tile.push_back_real(PIdx::r, r.data(), r.data() + np); + amrex::ignore_unused(y,z); #endif pinned_tile.push_back_real(PIdx::w, attr_real[0].data() + ibegin, attr_real[0].data() + iend); From 3d290890f9afe39ef9e6318a4d3d448a21fdc8af Mon Sep 17 00:00:00 2001 From: bowen zhu <2062141317@qq.com> Date: Thu, 27 Aug 2026 08:08:19 +0800 Subject: [PATCH 2/3] Particles: use computed radial coordinate for RCYLINDER and RSPHERE --- Source/Particles/WarpXParticleContainer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/Particles/WarpXParticleContainer.cpp b/Source/Particles/WarpXParticleContainer.cpp index bf42283bed7..4665e18e7ac 100644 --- a/Source/Particles/WarpXParticleContainer.cpp +++ b/Source/Particles/WarpXParticleContainer.cpp @@ -273,10 +273,7 @@ WarpXParticleContainer::AddNParticles (int /*lev*/, long n, #elif defined(WARPX_DIM_1D_Z) amrex::ignore_unused(x,y); pinned_tile.push_back_real(PIdx::z, z.data() + ibegin, z.data() + iend); -#elif defined(WARPX_DIM_RCYLINDER) - pinned_tile.push_back_real(PIdx::r, x.data() + ibegin, x.data() + iend); - amrex::ignore_unused(y,z); -#elif defined(WARPX_DIM_RSPHERE) +#elif defined(WARPX_DIM_RCYLINDER) || defined(WARPX_DIM_RSPHERE) pinned_tile.push_back_real(PIdx::r, r.data(), r.data() + np); amrex::ignore_unused(y,z); #endif From 40cd0175817d7ccf3f90f705c34ab710598ec9bb Mon Sep 17 00:00:00 2001 From: bowen zhu <2062141317@qq.com> Date: Fri, 28 Aug 2026 06:24:09 +0800 Subject: [PATCH 3/3] Tests: remove standalone integration test as requested --- Examples/Tests/single_particle/CMakeLists.txt | 10 ---- ..._rsphere_particle_coordinate_conversion.py | 37 ------------- ...est_rsphere_particle_coordinate_conversion | 54 ------------------- 3 files changed, 101 deletions(-) delete mode 100644 Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py delete mode 100644 Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion diff --git a/Examples/Tests/single_particle/CMakeLists.txt b/Examples/Tests/single_particle/CMakeLists.txt index 5e315b6c9ba..4282376a20f 100644 --- a/Examples/Tests/single_particle/CMakeLists.txt +++ b/Examples/Tests/single_particle/CMakeLists.txt @@ -20,13 +20,3 @@ add_warpx_test( OFF # checksum OFF # dependency ) - -add_warpx_test( - test_rsphere_particle_coordinate_conversion # name - RSPHERE # dims - 1 # nprocs - inputs_test_rsphere_particle_coordinate_conversion # inputs - "analysis_rsphere_particle_coordinate_conversion.py" # analysis - OFF # checksum - OFF # dependency -) diff --git a/Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py b/Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py deleted file mode 100644 index f4b9ba1040b..00000000000 --- a/Examples/Tests/single_particle/analysis_rsphere_particle_coordinate_conversion.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2026 The WarpX Community -# -# This file is part of WarpX. -# -# License: BSD-3-Clause-LBNL - -import math -from pathlib import Path - - -def read_position(diag_name): - path = Path("diags/reducedfiles") / f"{diag_name}.txt" - rows = [line for line in path.read_text().splitlines() if not line.startswith("#")] - row = [float(value) for value in rows[-1].split()] - - # ParticleExtrema columns are step, time, xmin, xmax, ymin, ymax, zmin, zmax, ... - position_min = tuple(row[index] for index in (2, 4, 6)) - position_max = tuple(row[index] for index in (3, 5, 7)) - for minimum, maximum in zip(position_min, position_max): - assert math.isclose(minimum, maximum, rel_tol=0.0, abs_tol=1.0e-15) - return position_min - - -expected_positions = { - "radial_position": (0.5, 0.0, 0.0), - "off_axis_position": (0.3, 0.4, -1.2), -} - -for diagnostic, expected in expected_positions.items(): - actual = read_position(diagnostic) - print(f"{diagnostic}: expected {expected}, actual {actual}") - for component, value, reference in zip("xyz", actual, expected): - assert math.isclose(value, reference, rel_tol=1.0e-14, abs_tol=1.0e-15), ( - f"{diagnostic} {component}: expected {reference}, got {value}" - ) diff --git a/Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion b/Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion deleted file mode 100644 index 2860a516b1c..00000000000 --- a/Examples/Tests/single_particle/inputs_test_rsphere_particle_coordinate_conversion +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2026 The WarpX Community -# -# This file is part of WarpX. -# -# License: BSD-3-Clause-LBNL - -max_step = 1 -warpx.const_dt = 1.e-12 - -amr.n_cell = 8 -amr.max_level = 0 -amr.blocking_factor = 8 -amr.max_grid_size = 8 - -geometry.dims = RSPHERE -geometry.prob_lo = 0.0 -geometry.prob_hi = 2.0 - -boundary.field_lo = none -boundary.field_hi = none -boundary.particle_lo = none -boundary.particle_hi = absorbing - -algo.maxwell_solver = none -algo.particle_shape = 1 - -particles.species_names = radial off_axis - -# A ray on the x axis has zero elevation from the x-y plane. -radial.charge = 0.0 -radial.mass = 1.0 -radial.injection_style = SingleParticle -radial.single_particle_pos = 0.5 0.0 0.0 -radial.single_particle_u = 0.0 0.0 0.0 -radial.single_particle_weight = 1.0 - -# This 3-4-12 triangle checks all three reconstructed Cartesian components, -# including the sign of the elevation angle. -off_axis.charge = 0.0 -off_axis.mass = 1.0 -off_axis.injection_style = SingleParticle -off_axis.single_particle_pos = 0.3 0.4 -1.2 -off_axis.single_particle_u = 0.0 0.0 0.0 -off_axis.single_particle_weight = 1.0 - -warpx.reduced_diags_names = radial_position off_axis_position - -radial_position.type = ParticleExtrema -radial_position.intervals = 1 -radial_position.species = radial - -off_axis_position.type = ParticleExtrema -off_axis_position.intervals = 1 -off_axis_position.species = off_axis