From 42c56d034a7817b0c0a9d1e00dba1cbe345ab901 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 6 May 2026 11:08:24 -0400 Subject: [PATCH 001/106] fields3d.inl; test*: mv begin/endStep to make read_checkpoint work again --- src/include/fields3d.inl | 3 --- src/libpsc/tests/test_mfields_io.cxx | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/include/fields3d.inl b/src/include/fields3d.inl index 42e32514b..5e73dc3db 100644 --- a/src/include/fields3d.inl +++ b/src/include/fields3d.inl @@ -39,8 +39,6 @@ public: // FIXME, should just check for consistency? (# ghosts might differ, too) // reader.get("ib", mflds.ib, launch); // reader.get("im", mflds.im, launch); - reader.beginStep(kg::io::StepMode::Read); - auto n_comps = mflds.n_comps(); auto shape = makeDims(n_comps, mflds.gdims()); assert(reader.variableShape() == shape); @@ -55,7 +53,6 @@ public: {}); //{ib, im}); } reader.performGets(); - reader.endStep(); for (int p = 0; p < mflds.n_patches(); p++) { auto h_flds = make_Fields3d(h_mflds[p]); diff --git a/src/libpsc/tests/test_mfields_io.cxx b/src/libpsc/tests/test_mfields_io.cxx index cd62490c6..e1c32c7d0 100644 --- a/src/libpsc/tests/test_mfields_io.cxx +++ b/src/libpsc/tests/test_mfields_io.cxx @@ -75,7 +75,9 @@ TYPED_TEST(MfieldsTest, WriteRead) auto mflds2 = Mfields{grid, NR_FIELDS, {}}; { auto reader = io.open("test.bp", kg::io::Mode::Read); + reader.beginStep(kg::io::StepMode::Read); reader.get("mflds", mflds2); + reader.endStep(); reader.close(); } @@ -106,7 +108,9 @@ TYPED_TEST(MfieldsTest, WriteWithGhostsRead) auto mflds2 = Mfields{grid, NR_FIELDS, {}}; { auto reader = io.open("test.bp", kg::io::Mode::Read); + reader.beginStep(kg::io::StepMode::Read); reader.get("mflds", mflds2); + reader.endStep(); reader.close(); } @@ -137,7 +141,9 @@ TYPED_TEST(MfieldsTest, WriteReadWithGhosts) auto mflds2 = Mfields{grid, NR_FIELDS, {2, 2, 2}}; { auto reader = io.open("test.bp", kg::io::Mode::Read); + reader.beginStep(kg::io::StepMode::Read); reader.get("mflds", mflds2); + reader.endStep(); reader.close(); } From 41be1f02d1d2491d86aeb60ca1233503c4a58a91 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 22 May 2026 15:27:44 -0400 Subject: [PATCH 002/106] output_fields: fix div by 0 --- src/include/output_fields.hxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/include/output_fields.hxx b/src/include/output_fields.hxx index 1a39679d4..09c66a60a 100644 --- a/src/include/output_fields.hxx +++ b/src/include/output_fields.hxx @@ -92,13 +92,17 @@ struct OutputTfieldItemParams : BaseOutputFieldItemParams // Returns whether to accumulate on this timestep. bool do_accum(int timestep) { + if (!enabled()) { + return false; + } + // next_out could be this timestep int n_intervals_elapsed = (timestep - 1) / out_interval; int next_out = out_interval * (n_intervals_elapsed + 1); bool in_averaging_range = next_out - timestep < average_length; bool on_averaging_step = (next_out - timestep) % sample_interval == 0; - return enabled() && in_averaging_range && on_averaging_step; + return in_averaging_range && on_averaging_step; } }; From 32bfb7d0f48b9c47d91624a44a1928786fde74d6 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 11:29:44 -0400 Subject: [PATCH 003/106] push_particles_1vb: fix Grid_t type --- src/libpsc/psc_push_particles/push_particles_1vb.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libpsc/psc_push_particles/push_particles_1vb.hxx b/src/libpsc/psc_push_particles/push_particles_1vb.hxx index 4f5c005b6..59e217665 100644 --- a/src/libpsc/psc_push_particles/push_particles_1vb.hxx +++ b/src/libpsc/psc_push_particles/push_particles_1vb.hxx @@ -26,7 +26,7 @@ struct PushParticlesVb static void push_mprts(Mparticles& mprts, MfieldsState& mflds) { - const auto& grid = mprts.grid(); + const Grid_t& grid = mprts.grid(); Real3 dxi = Real3(grid.domain.dx).inv(); real_t dq_kind[MAX_NR_KINDS]; auto& kinds = grid.kinds; From 683dd344eb1d49d5ac141e291efb60776a6939f7 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 13 May 2026 14:07:12 -0400 Subject: [PATCH 004/106] psc: allow stats_every < 1 --- src/include/psc.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/psc.hxx b/src/include/psc.hxx index 364f7d9cf..a6e7f243f 100644 --- a/src/include/psc.hxx +++ b/src/include/psc.hxx @@ -286,7 +286,7 @@ struct Psc psc_stats_val[st_nr_particles] = mprts_.size(); - if (grid().timestep() % p_.stats_every == 0) { + if (p_.stats_every > 0 && grid().timestep() % p_.stats_every == 0) { print_status(); } From c46c8796fe85f261c79f72fd3430251eef3da81b Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 22 Apr 2026 17:31:42 -0400 Subject: [PATCH 005/106] boundary_injector: take density --- src/include/boundary_injector.hxx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 69cf4fea4..3e56fcc7a 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -82,16 +82,17 @@ public: using real_t = typename PushParticles::real_t; using Real3 = Vec3; - BoundaryInjector(ParticleGenerator particle_generator, Grid_t& grid) + BoundaryInjector(ParticleGenerator particle_generator, Grid_t& grid, + real_t density = 1.0) : particle_generator_{particle_generator}, advance_{grid.dt}, - prts_per_unit_density_{grid.norm.prts_per_unit_density} + prts_per_unit_density_{grid.norm.prts_per_unit_density}, + density_{density} {} /// Injects particles at the lower y-bound as if there were a population of - /// particles just beyond the edge. The imaginary particle population has unit - /// density, and individual particles from that population are sampled using - /// the given ParticleGenerator. + /// particles just beyond the edge. The imaginary particle population is + /// sampled using the given ParticleGenerator. /// /// Some of these limitations may be removed in the future. void inject(Mparticles& mprts, MfieldsState& mflds) override @@ -123,7 +124,7 @@ public: for (Int3 initial_idx : VecRange(ilo, ihi)) { Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; int n_prts_to_try_inject = - get_n_in_cell(1.0, prts_per_unit_density_, true); + get_n_in_cell(density_, prts_per_unit_density_, true); for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { psc::particle::Inject prt = @@ -165,4 +166,5 @@ private: // can't move along x or z, or else might leave patch AdvanceParticle advance_; real_t prts_per_unit_density_; + real_t density_; }; From ded9f606073a9b197f1186e282104e1c3ae5e0a0 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 23 Apr 2026 10:00:19 -0400 Subject: [PATCH 006/106] boundary_injector: +inject_lo --- src/include/boundary_injector.hxx | 96 +++++++++++++++---------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 3e56fcc7a..8d852f935 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -107,55 +107,54 @@ public: Current current(grid); for (int p = 0; p < grid.n_patches(); p++) { - if (!grid.atBoundaryLo(p, INJECT_DIM_IDX_)) { - continue; - } - - Int3 ilo = {0, 0, 0}; - Int3 ihi = grid.ldims; - - ilo[INJECT_DIM_IDX_] = -1; - ihi[INJECT_DIM_IDX_] = 0; - - auto&& injector = injectors_by_patch[p]; - auto flds = mflds[p]; - typename Current::fields_t J(flds); - - for (Int3 initial_idx : VecRange(ilo, ihi)) { - Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; - int n_prts_to_try_inject = - get_n_in_cell(density_, prts_per_unit_density_, true); - - for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { - psc::particle::Inject prt = - particle_generator_.get(cell_corner, grid.domain.dx); - - Real3 v = advance_.calc_v(prt.u); - Real3 initial_x = prt.x; - advance_.push_x(prt.x, v); - - if (prt.x[INJECT_DIM_IDX_] < 0.0) { - // don't inject a particle that fails to enter the patch - continue; + if (inject_lo && grid.atBoundaryLo(p, INJECT_DIM_IDX_)) { + Int3 ilo = {0, 0, 0}; + Int3 ihi = grid.ldims; + + ilo[INJECT_DIM_IDX_] = -1; + ihi[INJECT_DIM_IDX_] = 0; + + auto&& injector = injectors_by_patch[p]; + auto flds = mflds[p]; + typename Current::fields_t J(flds); + + for (Int3 initial_idx : VecRange(ilo, ihi)) { + Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; + int n_prts_to_try_inject = + get_n_in_cell(density_, prts_per_unit_density_, true); + + for (int prt_count = 0; prt_count < n_prts_to_try_inject; + prt_count++) { + psc::particle::Inject prt = + particle_generator_.get(cell_corner, grid.domain.dx); + + Real3 v = advance_.calc_v(prt.u); + Real3 initial_x = prt.x; + advance_.push_x(prt.x, v); + + if (prt.x[INJECT_DIM_IDX_] < 0.0) { + // don't inject a particle that fails to enter the patch + continue; + } + + // GOTCHA: currently, injectors expect particle positions to be + // global, but current deposition expects patch-local + psc::particle::Inject prt_with_global_x = prt; + prt_with_global_x.x += grid.patches[p].xb; + injector(prt_with_global_x); + + // Update currents + // Taken from push_particles_1vb.hxx PushParticlesVb::push_mprts() + + Real3 initial_normalized_pos = initial_x * dxi; + Real3 final_normalized_pos = prt.x * dxi; + Int3 final_idx = final_normalized_pos.fint(); + + // CURRENT DENSITY BETWEEN (n+.5)*dt and (n+1.5)*dt + real_t qni_wni = grid.kinds[prt.kind].q * prt.w; + current.calc_j(J, initial_normalized_pos, final_normalized_pos, + final_idx, initial_idx, qni_wni, v); } - - // GOTCHA: currently, injectors expect particle positions to be - // global, but current deposition expects patch-local - psc::particle::Inject prt_with_global_x = prt; - prt_with_global_x.x += grid.patches[p].xb; - injector(prt_with_global_x); - - // Update currents - // Taken from push_particles_1vb.hxx PushParticlesVb::push_mprts() - - Real3 initial_normalized_pos = initial_x * dxi; - Real3 final_normalized_pos = prt.x * dxi; - Int3 final_idx = final_normalized_pos.fint(); - - // CURRENT DENSITY BETWEEN (n+.5)*dt and (n+1.5)*dt - real_t qni_wni = grid.kinds[prt.kind].q * prt.w; - current.calc_j(J, initial_normalized_pos, final_normalized_pos, - final_idx, initial_idx, qni_wni, v); } } } @@ -167,4 +166,5 @@ private: AdvanceParticle advance_; real_t prts_per_unit_density_; real_t density_; + bool inject_lo = true; }; From 26f89bcd45623d5f7dd07c000ddb5c157435d3ff Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 23 Apr 2026 11:17:36 -0400 Subject: [PATCH 007/106] boundary_injector: -prts_per_unit_density_ --- src/include/boundary_injector.hxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 8d852f935..7930556f6 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -86,7 +86,6 @@ public: real_t density = 1.0) : particle_generator_{particle_generator}, advance_{grid.dt}, - prts_per_unit_density_{grid.norm.prts_per_unit_density}, density_{density} {} @@ -121,7 +120,7 @@ public: for (Int3 initial_idx : VecRange(ilo, ihi)) { Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; int n_prts_to_try_inject = - get_n_in_cell(density_, prts_per_unit_density_, true); + get_n_in_cell(density_, grid.norm.prts_per_unit_density, true); for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { @@ -164,7 +163,6 @@ private: ParticleGenerator particle_generator_; // can't move along x or z, or else might leave patch AdvanceParticle advance_; - real_t prts_per_unit_density_; real_t density_; bool inject_lo = true; }; From 2e444d85612db1c94a45d0c630190c4d67794784 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 23 Apr 2026 11:18:50 -0400 Subject: [PATCH 008/106] boundary_injector: -advance_ --- src/include/boundary_injector.hxx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 7930556f6..feae13067 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -84,9 +84,7 @@ public: BoundaryInjector(ParticleGenerator particle_generator, Grid_t& grid, real_t density = 1.0) - : particle_generator_{particle_generator}, - advance_{grid.dt}, - density_{density} + : particle_generator_{particle_generator}, density_{density} {} /// Injects particles at the lower y-bound as if there were a population of @@ -127,9 +125,10 @@ public: psc::particle::Inject prt = particle_generator_.get(cell_corner, grid.domain.dx); - Real3 v = advance_.calc_v(prt.u); + AdvanceParticle advance{grid.dt}; + Real3 v = advance.calc_v(prt.u); Real3 initial_x = prt.x; - advance_.push_x(prt.x, v); + advance.push_x(prt.x, v); if (prt.x[INJECT_DIM_IDX_] < 0.0) { // don't inject a particle that fails to enter the patch @@ -161,8 +160,6 @@ public: private: ParticleGenerator particle_generator_; - // can't move along x or z, or else might leave patch - AdvanceParticle advance_; real_t density_; bool inject_lo = true; }; From 00dbea0654901a5b97e513b465568915d615978f Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 13:09:13 -0400 Subject: [PATCH 009/106] boundary_injector; *: don't pass grid --- src/include/boundary_injector.hxx | 3 +-- src/libpsc/tests/test_boundary_injector.cxx | 8 ++++---- src/psc_shock.cxx | 6 ++---- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index feae13067..8cf50bbb4 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -82,8 +82,7 @@ public: using real_t = typename PushParticles::real_t; using Real3 = Vec3; - BoundaryInjector(ParticleGenerator particle_generator, Grid_t& grid, - real_t density = 1.0) + BoundaryInjector(ParticleGenerator particle_generator, real_t density = 1.0) : particle_generator_{particle_generator}, density_{density} {} diff --git a/src/libpsc/tests/test_boundary_injector.cxx b/src/libpsc/tests/test_boundary_injector.cxx index d8a679f95..a1f3b5777 100644 --- a/src/libpsc/tests/test_boundary_injector.cxx +++ b/src/libpsc/tests/test_boundary_injector.cxx @@ -132,7 +132,7 @@ TEST(BoundaryInjectorTest, Integration1Particle) psc.add_injector( new BoundaryInjector( - ParticleGenerator(1, 1), grid)); + ParticleGenerator(1, 1))); // ---------------------------------------------------------------------- // set up initial conditions @@ -189,7 +189,7 @@ TEST(BoundaryInjectorTest, IntegrationManyParticles) psc.add_injector( new BoundaryInjector( - ParticleGenerator(-1, 1), grid)); + ParticleGenerator(-1, 1))); // ---------------------------------------------------------------------- // set up initial conditions @@ -243,10 +243,10 @@ TEST(BoundaryInjectorTest, IntegrationManySpecies) auto inject_electrons = BoundaryInjector{ - ParticleGenerator(-1, 0), grid}; + ParticleGenerator(-1, 0)}; auto inject_ions = BoundaryInjector{ - ParticleGenerator(-1, 1), grid}; + ParticleGenerator(-1, 1)}; auto psc = makePscIntegrator(psc_params, grid, mflds, mprts, balance, collision, checks); diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 2ff164d5f..3827bd833 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -810,15 +810,13 @@ static void run(int argc, char** argv) BoundaryInjector( ParticleGeneratorMaxwellian( KIND_ION, grid.kinds[KIND_ION], v_upstream, - {ion_temperature, ion_temperature, ion_temperature}, true), - grid); + {ion_temperature, ion_temperature, ion_temperature}, true)); auto electron_injector = BoundaryInjector( ParticleGeneratorMaxwellian( KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_upstream, {electron_temperature, electron_temperature, electron_temperature}, - true), - grid); + true)); // ---------------------------------------------------------------------- // set up initial conditions From 7c05e00a6506ef405637f259434eda419531f41f Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 23 Apr 2026 11:22:44 -0400 Subject: [PATCH 010/106] boundary_injector: public density, inject_lo --- src/include/boundary_injector.hxx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 8cf50bbb4..9ab7f9049 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -83,7 +83,7 @@ public: using Real3 = Vec3; BoundaryInjector(ParticleGenerator particle_generator, real_t density = 1.0) - : particle_generator_{particle_generator}, density_{density} + : particle_generator_{particle_generator}, density{density} {} /// Injects particles at the lower y-bound as if there were a population of @@ -117,7 +117,7 @@ public: for (Int3 initial_idx : VecRange(ilo, ihi)) { Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; int n_prts_to_try_inject = - get_n_in_cell(density_, grid.norm.prts_per_unit_density, true); + get_n_in_cell(density, grid.norm.prts_per_unit_density, true); for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { @@ -157,8 +157,10 @@ public: } } +public: + real_t density; + bool inject_lo = true; + private: ParticleGenerator particle_generator_; - real_t density_; - bool inject_lo = true; }; From e1a37e5619c4c95331eb1a0063160e4c6390d899 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 13:11:07 -0400 Subject: [PATCH 011/106] boundary_injector: set density after ctor --- src/include/boundary_injector.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 9ab7f9049..6f2c2b3fb 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -82,8 +82,8 @@ public: using real_t = typename PushParticles::real_t; using Real3 = Vec3; - BoundaryInjector(ParticleGenerator particle_generator, real_t density = 1.0) - : particle_generator_{particle_generator}, density{density} + BoundaryInjector(ParticleGenerator particle_generator) + : particle_generator_{particle_generator} {} /// Injects particles at the lower y-bound as if there were a population of From 9a02f912d9ed753c7b1a7b03e532052b54b3093f Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 23 Apr 2026 11:38:04 -0400 Subject: [PATCH 012/106] boundary_injector: support upper inj --- src/include/boundary_injector.hxx | 59 +++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 6f2c2b3fb..1adf9166f 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -86,11 +86,11 @@ public: : particle_generator_{particle_generator} {} - /// Injects particles at the lower y-bound as if there were a population of + /// Injects particles at specified y-bounds as if there were a population of /// particles just beyond the edge. The imaginary particle population is /// sampled using the given ParticleGenerator. /// - /// Some of these limitations may be removed in the future. + /// The dimensional limitations may be removed in the future. void inject(Mparticles& mprts, MfieldsState& mflds) override { static_assert(INJECT_DIM_IDX_ == 1, @@ -103,6 +103,7 @@ public: Current current(grid); for (int p = 0; p < grid.n_patches(); p++) { + // TODO: combine paths as much as possible if (inject_lo && grid.atBoundaryLo(p, INJECT_DIM_IDX_)) { Int3 ilo = {0, 0, 0}; Int3 ihi = grid.ldims; @@ -154,12 +155,66 @@ public: } } } + + if (inject_hi && grid.atBoundaryHi(p, INJECT_DIM_IDX_)) { + Int3 ilo = {0, 0, 0}; + Int3 ihi = grid.ldims; + + ilo[INJECT_DIM_IDX_] = grid.ldims[INJECT_DIM_IDX_]; + ihi[INJECT_DIM_IDX_] = grid.ldims[INJECT_DIM_IDX_] + 1; + + auto&& injector = injectors_by_patch[p]; + auto flds = mflds[p]; + typename Current::fields_t J(flds); + + for (Int3 initial_idx : VecRange(ilo, ihi)) { + Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; + int n_prts_to_try_inject = + get_n_in_cell(density, grid.norm.prts_per_unit_density, true); + + for (int prt_count = 0; prt_count < n_prts_to_try_inject; + prt_count++) { + psc::particle::Inject prt = + particle_generator_.get(cell_corner, grid.domain.dx); + + AdvanceParticle advance{grid.dt}; + Real3 v = advance.calc_v(prt.u); + Real3 initial_x = prt.x; + advance.push_x(prt.x, v); + + if (prt.x[INJECT_DIM_IDX_] > + grid.domain.dx[INJECT_DIM_IDX_] * grid.ldims[INJECT_DIM_IDX_]) { + // don't inject a particle that fails to enter the patch + continue; + } + + // GOTCHA: currently, injectors expect particle positions to be + // global, but current deposition expects patch-local + psc::particle::Inject prt_with_global_x = prt; + prt_with_global_x.x += grid.patches[p].xb; + injector(prt_with_global_x); + + // Update currents + // Taken from push_particles_1vb.hxx PushParticlesVb::push_mprts() + + Real3 initial_normalized_pos = initial_x * dxi; + Real3 final_normalized_pos = prt.x * dxi; + Int3 final_idx = final_normalized_pos.fint(); + + // CURRENT DENSITY BETWEEN (n+.5)*dt and (n+1.5)*dt + real_t qni_wni = grid.kinds[prt.kind].q * prt.w; + current.calc_j(J, initial_normalized_pos, final_normalized_pos, + final_idx, initial_idx, qni_wni, v); + } + } + } } } public: real_t density; bool inject_lo = true; + bool inject_hi = false; private: ParticleGenerator particle_generator_; From 93e00d245366bda7e84f1b9862792fb031585431 Mon Sep 17 00:00:00 2001 From: James McClung Date: Mon, 27 Apr 2026 12:39:27 -0400 Subject: [PATCH 013/106] injector_simple: +inject_local --- src/include/injector_simple.hxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/include/injector_simple.hxx b/src/include/injector_simple.hxx index 5d0b3dce1..b31b1c7de 100644 --- a/src/include/injector_simple.hxx +++ b/src/include/injector_simple.hxx @@ -36,6 +36,18 @@ struct InjectorSimple mprts_.push_back(p_, prt); } + void inject_local(const psc::particle::Inject& new_prt) + { + auto prt = + Particle{Real3(new_prt.x), + Real3(new_prt.u), + real_t(new_prt.w * mprts_.grid().kinds[new_prt.kind].q), + new_prt.kind, + mprts_.uid_gen(), + new_prt.tag}; + mprts_.push_back(p_, prt); + } + void reweight(const psc::particle::Inject& new_prt) { auto& grid = mprts_.grid(); From 6b00edf3fb59ca1f96644b11aa17f005f76efafa Mon Sep 17 00:00:00 2001 From: James McClung Date: Mon, 27 Apr 2026 13:36:22 -0400 Subject: [PATCH 014/106] boundary_injector: default density of 1 --- src/include/boundary_injector.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 1adf9166f..106a6f2cc 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -212,7 +212,7 @@ public: } public: - real_t density; + real_t density = 1.0; bool inject_lo = true; bool inject_hi = false; From 0c5beed3835fbe3b728241cbf149c998c1227e80 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 13:12:25 -0400 Subject: [PATCH 015/106] boundary_injector; *: fix u gen --- src/include/boundary_injector.hxx | 26 +++++++++++++++++--------- src/psc_shock.cxx | 5 ++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 106a6f2cc..06e90f834 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -22,13 +22,18 @@ public: using Real3 = psc::particle::Inject::Real3; // FIXME would be nice to just pass 1 thing for kind-related info - ParticleGeneratorMaxwellian(int kind_idx, Grid_t::Kind kind, Real3 mean_u, - Real3 temperature, bool correct_gamma = false) - : kind_idx{kind_idx}, correct_gamma{correct_gamma} + ParticleGeneratorMaxwellian(int kind_idx, Grid_t::Kind kind, Real3 mean_v, + Real3 temperature) + : kind_idx{kind_idx}, + gamma{1.0 / sqrt(1.0 - mean_v.mag2())}, + mean_u{mean_v * gamma}, + mean_u_hat{mean_u / mean_u.mag()} { for (int d = 0; d < 3; d++) { - Real stdev_u = sqrt(temperature[d] / kind.m); - vdfs[d] = VelocityDistributionFunction{mean_u[d], stdev_u}; + // temperature is interpreted as velocity temperature in bulk frame + Real stdev_v = sqrt(temperature[d] / kind.m); + // multiply by gamma to get proper velocity in bulk frame + vdfs[d] = VelocityDistributionFunction{0.0, stdev_v * gamma}; } } @@ -39,10 +44,11 @@ public: x[d] = min_pos[d] + uniform_dist.get() * pos_range[d]; } + // generate proper velocity in bulk frame Real3 u{vdfs[0].get(), vdfs[1].get(), vdfs[2].get()}; - if (correct_gamma) { - u = vel_to_4vel(u); - } + // Lorentz boost + u += (gamma - 1.0) * u.dot(mean_u_hat) * mean_u_hat + mean_u; + Real w = 1.0; psc::particle::Tag tag = 0; @@ -53,7 +59,9 @@ private: using VelocityDistributionFunction = rng::Normal; Vec3 vdfs; int kind_idx; - bool correct_gamma; + Real gamma; + Real3 mean_u; + Real3 mean_u_hat; rng::Uniform uniform_dist{0.0, 1.0}; }; diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 3827bd833..f25768a19 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -810,13 +810,12 @@ static void run(int argc, char** argv) BoundaryInjector( ParticleGeneratorMaxwellian( KIND_ION, grid.kinds[KIND_ION], v_upstream, - {ion_temperature, ion_temperature, ion_temperature}, true)); + {ion_temperature, ion_temperature, ion_temperature})); auto electron_injector = BoundaryInjector( ParticleGeneratorMaxwellian( KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_upstream, - {electron_temperature, electron_temperature, electron_temperature}, - true)); + {electron_temperature, electron_temperature, electron_temperature})); // ---------------------------------------------------------------------- // set up initial conditions From 93e9c6ada6646b6a5e67f94828e29afb7f28ad4d Mon Sep 17 00:00:00 2001 From: James McClung Date: Mon, 27 Apr 2026 13:39:40 -0400 Subject: [PATCH 016/106] boundary_injector: slight cleanup --- src/include/boundary_injector.hxx | 36 +++++++------------------------ 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 06e90f834..9ed203f43 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -135,7 +135,7 @@ public: AdvanceParticle advance{grid.dt}; Real3 v = advance.calc_v(prt.u); - Real3 initial_x = prt.x; + Real3 initial_normalized_pos = prt.x * dxi; advance.push_x(prt.x, v); if (prt.x[INJECT_DIM_IDX_] < 0.0) { @@ -143,20 +143,11 @@ public: continue; } - // GOTCHA: currently, injectors expect particle positions to be - // global, but current deposition expects patch-local - psc::particle::Inject prt_with_global_x = prt; - prt_with_global_x.x += grid.patches[p].xb; - injector(prt_with_global_x); - - // Update currents - // Taken from push_particles_1vb.hxx PushParticlesVb::push_mprts() - - Real3 initial_normalized_pos = initial_x * dxi; Real3 final_normalized_pos = prt.x * dxi; Int3 final_idx = final_normalized_pos.fint(); - // CURRENT DENSITY BETWEEN (n+.5)*dt and (n+1.5)*dt + injector.inject_local(prt); + real_t qni_wni = grid.kinds[prt.kind].q * prt.w; current.calc_j(J, initial_normalized_pos, final_normalized_pos, final_idx, initial_idx, qni_wni, v); @@ -187,29 +178,18 @@ public: AdvanceParticle advance{grid.dt}; Real3 v = advance.calc_v(prt.u); - Real3 initial_x = prt.x; + Real3 initial_normalized_pos = prt.x * dxi; advance.push_x(prt.x, v); + Real3 final_normalized_pos = prt.x * dxi; + Int3 final_idx = final_normalized_pos.fint(); - if (prt.x[INJECT_DIM_IDX_] > - grid.domain.dx[INJECT_DIM_IDX_] * grid.ldims[INJECT_DIM_IDX_]) { + if (final_idx[INJECT_DIM_IDX_] >= initial_idx[INJECT_DIM_IDX_]) { // don't inject a particle that fails to enter the patch continue; } - // GOTCHA: currently, injectors expect particle positions to be - // global, but current deposition expects patch-local - psc::particle::Inject prt_with_global_x = prt; - prt_with_global_x.x += grid.patches[p].xb; - injector(prt_with_global_x); - - // Update currents - // Taken from push_particles_1vb.hxx PushParticlesVb::push_mprts() - - Real3 initial_normalized_pos = initial_x * dxi; - Real3 final_normalized_pos = prt.x * dxi; - Int3 final_idx = final_normalized_pos.fint(); + injector.inject_local(prt); - // CURRENT DENSITY BETWEEN (n+.5)*dt and (n+1.5)*dt real_t qni_wni = grid.kinds[prt.kind].q * prt.w; current.calc_j(J, initial_normalized_pos, final_normalized_pos, final_idx, initial_idx, qni_wni, v); From b2db1576c9ec299c9585e54bd8a331653c4e05ff Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 13:08:21 -0400 Subject: [PATCH 017/106] psc_bnd_fields_impl; *: hacky upper background --- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 35 ++++++++++--------- src/psc_shock.cxx | 4 +-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 7697893c7..3bbdda209 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -41,7 +41,7 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - set_lower_ghosts(mflds, p, d, EX, background_e, false); + set_lower_ghosts(mflds, p, d, EX, background_e_lo, false); break; } default: { @@ -63,7 +63,7 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - set_upper_ghosts(mflds, p, d, EX, background_e, false); + set_upper_ghosts(mflds, p, d, EX, background_e_hi, false); break; } default: { @@ -566,19 +566,19 @@ struct BndFields_ : BndFieldsBase } F(H2, i3) = - (4.f * s - 2.f * (F(E1, edge_idx) - background_e[d1]) - + (4.f * s - 2.f * (F(E1, edge_idx) - background_e_lo[d1]) - dtdx[d2] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d2))) - - (1.f - dtdx[d0]) * (F(H2, edge_idx) - background_h[d2]) + + (1.f - dtdx[d0]) * (F(H2, edge_idx) - background_h_lo[d2]) + dt * F(J1, edge_idx)) / (1.f + dtdx[d0]) + - background_h[d2]; + background_h_lo[d2]; F(H1, i3) = - (-4.f * p + 2.f * (F(E2, edge_idx) - background_e[d2]) - + (-4.f * p + 2.f * (F(E2, edge_idx) - background_e_lo[d2]) - dtdx[d1] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d1))) - - (1.f - dtdx[d0]) * (F(H1, edge_idx) - background_h[d1]) + + (1.f - dtdx[d0]) * (F(H1, edge_idx) - background_h_lo[d1]) + dt * F(J2, edge_idx)) / (1.f + dtdx[d0]) + - background_h[d1]; + background_h_lo[d1]; } } @@ -616,23 +616,26 @@ struct BndFields_ : BndFieldsBase p = radiation->pulse_p_upper(grid.time(), d0, p, x3_p); } - F(H2, i3) = (-4.f * s + 2.f * (F(E1, i3) - background_e[d1]) + + F(H2, i3) = (-4.f * s + 2.f * (F(E1, i3) - background_e_hi[d1]) + dtdx[d2] * (F(H0, i3) - F(H0, i3 - Int3::unit(d2))) - - (1.f - dtdx[d0]) * (F(H2, edge_idx) - background_h[d2]) - + (1.f - dtdx[d0]) * (F(H2, edge_idx) - background_h_hi[d2]) - dt * F(J1, i3)) / (1.f + dtdx[d0]) + - background_h[d2]; - F(H1, i3) = (4.f * p - 2.f * (F(E2, i3) - background_e[d2]) + + background_h_hi[d2]; + F(H1, i3) = (4.f * p - 2.f * (F(E2, i3) - background_e_hi[d2]) + dtdx[d1] * (F(H0, i3) - F(H0, i3 - Int3::unit(d1))) - - (1.f - dtdx[d0]) * (F(H1, edge_idx) - background_h[d1]) - + (1.f - dtdx[d0]) * (F(H1, edge_idx) - background_h_hi[d1]) - dt * F(J2, i3)) / (1.f + dtdx[d0]) + - background_h[d1]; + background_h_hi[d1]; } } - Vec3 background_e = {0.0, 0.0, 0.0}; - Vec3 background_h = {0.0, 0.0, 0.0}; + Vec3 background_e_lo = {0.0, 0.0, 0.0}; + Vec3 background_e_hi = {0.0, 0.0, 0.0}; + + Vec3 background_h_lo = {0.0, 0.0, 0.0}; + Vec3 background_h_hi = {0.0, 0.0, 0.0}; RadiatingBoundary* radiation = nullptr; }; diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index f25768a19..dd352fe48 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -831,8 +831,8 @@ static void run(int argc, char** argv) psc.add_gauss_corrector(&marder); - psc.bndf.background_e = background_e; - psc.bndf.background_h = background_h; + psc.bndf.background_e_lo = background_e; + psc.bndf.background_h_lo = background_h; psc.bndf.radiation = new AdvectedPeriodicFields{mflds, v_upstream[1], background_e, background_h}; From 855de845bde53bc1759d7199ff14af765c41c1ac Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Apr 2026 15:40:49 -0400 Subject: [PATCH 018/106] psc_shock: fix h_y --- src/psc_shock.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index dd352fe48..9ef607872 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -100,7 +100,7 @@ void setupParameters(int argc, char** argv) double gamma = 1 / sqrt(1 - v_upstream.mag2()); background_e = -gamma * v_upstream.cross(background_h_upstream); // note: this only holds for vx=vz=0 - background_h = background_h_upstream * Real3{gamma, 0.0, gamma}; + background_h = background_h_upstream * Real3{gamma, 1.0, gamma}; gdims[0] = inputParams.get("nx"); gdims[1] = inputParams.get("ny"); From cb9d6fb5e305ba589213a76a581f167f0f951a91 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 13:13:20 -0400 Subject: [PATCH 019/106] psc_shock: fix shift_to_patch_local --- src/psc_shock.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 9ef607872..f4d07ed05 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -628,7 +628,9 @@ struct AdvectedPeriodicFields : RadiatingBoundary { real_t patch_size = grid.domain.length[DIM_Y] / grid.domain.np[DIM_Y]; int n_patches_to_the_left = 0; - while (x3_advected[DIM_Y] < grid.domain.corner[DIM_Y]) { + // note: input x3 is already patch-local; we are just shifting to a + // *different* patch + while (x3_advected[DIM_Y] < 0.0) { x3_advected[DIM_Y] += patch_size; n_patches_to_the_left += 1; } From 5ac7fa8f1a8758d169c1850fa1fe67c1a38cd47d Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 13:13:49 -0400 Subject: [PATCH 020/106] psc_shock: fix advection of nonuniform bg fields --- src/psc_shock.cxx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index f4d07ed05..084050a04 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -581,7 +581,7 @@ void inject_turbulence_dense(MfieldsState& mflds) set_mean_b2(mflds, turb_db2); } -void initializeFields(MfieldsState& mflds) +void initialize_turbulence(MfieldsState& mflds) { if (turb_db2 > 0.0) { if (turb_method == "alfven_dense") { @@ -593,8 +593,6 @@ void initializeFields(MfieldsState& mflds) LOG_ERROR("Unrecognized turbulence method: %s\n", turb_method.c_str()); } } - - add_background_fields(mflds); } struct AdvectedPeriodicFields : RadiatingBoundary @@ -605,18 +603,12 @@ struct AdvectedPeriodicFields : RadiatingBoundary Real3 background_e, Real3 background_h) : v_advect(v_advect), grid(mflds.grid()) { + // mflds must NOT include background fields at this point // FIXME would be better to exclude J, but the interpolator uses EX, etc. auto&& e_b_fields = mflds.storage().view(_all, _all, _all, _all, _all); // FIXME this probably isn't the best way to copy a gtensor array cycled_fields = gt::zeros_like(e_b_fields); cycled_fields.view(_all, _all, _all, _all, _all) = e_b_fields; - - for (int d = 0; d < 3; d++) { - cycled_fields.view(_all, _all, _all, EX + d, _all) = - cycled_fields.view(_all, _all, _all, EX + d, _all) - background_e[d]; - cycled_fields.view(_all, _all, _all, HX + d, _all) = - cycled_fields.view(_all, _all, _all, HX + d, _all) - background_h[d]; - } } Real3 advect_x3(Real3 x3, double t) @@ -823,7 +815,7 @@ static void run(int argc, char** argv) // set up initial conditions initializeParticles(balance, grid_ptr, mprts); - initializeFields(mflds); + initialize_turbulence(mflds); // ---------------------------------------------------------------------- // run the simulation @@ -838,6 +830,10 @@ static void run(int argc, char** argv) psc.bndf.radiation = new AdvectedPeriodicFields{mflds, v_upstream[1], background_e, background_h}; + // add background after initializing radiation inflow, which only wants the + // perturbations to B + add_background_fields(mflds); + psc.add_diagnostic(&out_fields); psc.add_diagnostic(&out_moments); psc.add_diagnostic(&outp); From 99e64ec73cede903d7274e62420dd8a4e190f517 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 13:14:14 -0400 Subject: [PATCH 021/106] psc_shock: conditional set radiation --- src/psc_shock.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 084050a04..7fe1692b4 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -827,8 +827,10 @@ static void run(int argc, char** argv) psc.bndf.background_e_lo = background_e; psc.bndf.background_h_lo = background_h; - psc.bndf.radiation = new AdvectedPeriodicFields{mflds, v_upstream[1], - background_e, background_h}; + if (turb_db2 > 0.0) { + psc.bndf.radiation = new AdvectedPeriodicFields{mflds, v_upstream[1], + background_e, background_h}; + } // add background after initializing radiation inflow, which only wants the // perturbations to B From a273d98311922caacbc9fcbf89fca030d9016db9 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 6 May 2026 10:56:39 -0400 Subject: [PATCH 022/106] psc_shock: +checkpoint support for laminar case --- src/psc_shock.cxx | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 7fe1692b4..9b7b36f62 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -68,6 +68,8 @@ std::string turb_method; int nicell; int seed; +std::string checkpoint_filename; + // ====================================================================== // setupParameters @@ -81,7 +83,10 @@ void setupParameters(int argc, char** argv) psc_params.stats_every = 1000; psc_params.cfl = inputParams.getOrDefault("cfl", .75); - psc_params.write_checkpoint_every_step = 0; + checkpoint_filename = + inputParams.getOrDefault("checkpoint_filename", ""); + psc_params.write_checkpoint_every_step = + inputParams.getOrDefault("checkpoint_interval", 0); electron_temperature = inputParams.get("electron_temperature"); ion_temperature = inputParams.get("ion_temperature"); @@ -814,8 +819,16 @@ static void run(int argc, char** argv) // ---------------------------------------------------------------------- // set up initial conditions - initializeParticles(balance, grid_ptr, mprts); - initialize_turbulence(mflds); + if (checkpoint_filename.empty()) { + initializeParticles(balance, grid_ptr, mprts); + initialize_turbulence(mflds); + } else { + if (turb_db2 > 0.0) { + LOG_ERROR("resuming from checkpoint for nonzero turbulence not yet " + "supported (need to regenerate the advected turbulence)"); + } + read_checkpoint(checkpoint_filename, *grid_ptr, mprts, mflds); + } // ---------------------------------------------------------------------- // run the simulation @@ -832,9 +845,11 @@ static void run(int argc, char** argv) background_e, background_h}; } - // add background after initializing radiation inflow, which only wants the - // perturbations to B - add_background_fields(mflds); + if (checkpoint_filename.empty()) { + // add background after initializing radiation inflow, which only wants the + // perturbations to B + add_background_fields(mflds); + } psc.add_diagnostic(&out_fields); psc.add_diagnostic(&out_moments); From fa43eae3130cba15a35f838d9f6d9762ad30e986 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 6 May 2026 11:13:59 -0400 Subject: [PATCH 023/106] psc_shock: +stats_interval param --- src/psc_shock.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 9b7b36f62..41fec0d81 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -81,7 +81,8 @@ void setupParameters(int argc, char** argv) std::string path_to_params(argv[1]); InputParams inputParams(path_to_params); - psc_params.stats_every = 1000; + psc_params.stats_every = + inputParams.getOrDefault("stats_interval", 1000); psc_params.cfl = inputParams.getOrDefault("cfl", .75); checkpoint_filename = inputParams.getOrDefault("checkpoint_filename", ""); From a0fcca5af5f82fc91b0d491d3d25f2a97bb62f6b Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 6 May 2026 11:51:54 -0400 Subject: [PATCH 024/106] psc_shock: +cycle_turbulence notably, shift arbitrary number of patches --- src/psc_shock.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 41fec0d81..cf7c9384f 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -710,15 +710,20 @@ struct AdvectedPeriodicFields : RadiatingBoundary return; } + cycle_turbulence(n_patches_to_the_left - n_patch_cycles); + } + + void cycle_turbulence(int n_patches) + { LOG_INFO("cycling turbulence...\n"); // hack: guess the rank based on how mrc does it for simple domains // (can't use mrc, because it wouldn't apply periodicity) Int3 np = grid.domain.np; Int3 proc = grid.localPatchInfo(0).idx3; - Int3 dest_proc = (proc + Int3::unit(DIM_Y)) % np; + Int3 dest_proc = (proc + Int3::unit(DIM_Y) * n_patches) % np; int dest_rank = flatten_index(dest_proc.reverse(), np.reverse()); - Int3 source_proc = (proc - Int3::unit(DIM_Y) + np) % np; + Int3 source_proc = ((proc - Int3::unit(DIM_Y) * n_patches) % np + np) % np; int source_rank = flatten_index(source_proc.reverse(), np.reverse()); MPI_Status status; @@ -726,7 +731,7 @@ struct AdvectedPeriodicFields : RadiatingBoundary MpiDtypeTraits::value(), dest_rank, 0, source_rank, 0, grid.comm(), &status); - n_patch_cycles += 1; + n_patch_cycles += n_patches; } const Grid_t& grid; From 37b8496a482ce00ef9039a903e23ae62c2c8510e Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 6 May 2026 11:53:21 -0400 Subject: [PATCH 025/106] psc_shock: support checkpoint for turb case --- src/psc_shock.cxx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index cf7c9384f..438956d1f 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -829,10 +829,6 @@ static void run(int argc, char** argv) initializeParticles(balance, grid_ptr, mprts); initialize_turbulence(mflds); } else { - if (turb_db2 > 0.0) { - LOG_ERROR("resuming from checkpoint for nonzero turbulence not yet " - "supported (need to regenerate the advected turbulence)"); - } read_checkpoint(checkpoint_filename, *grid_ptr, mprts, mflds); } @@ -847,8 +843,17 @@ static void run(int argc, char** argv) psc.bndf.background_e_lo = background_e; psc.bndf.background_h_lo = background_h; if (turb_db2 > 0.0) { - psc.bndf.radiation = new AdvectedPeriodicFields{mflds, v_upstream[1], - background_e, background_h}; + if (checkpoint_filename.empty()) { + // mflds is currently just the pure, initial turbulence + psc.bndf.radiation = new AdvectedPeriodicFields{ + mflds, v_upstream[1], background_e, background_h}; + } else { + // mflds is completely unrelated; need to re-initialize turbulence + MfieldsState mflds2{grid}; + initialize_turbulence(mflds2); + psc.bndf.radiation = new AdvectedPeriodicFields{ + mflds2, v_upstream[1], background_e, background_h}; + } } if (checkpoint_filename.empty()) { From 724a313e84f825fb286aa8a0b2bc510dc26522b9 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 12 May 2026 13:28:08 -0400 Subject: [PATCH 026/106] psc_shock: +shock_method --- src/psc_shock.cxx | 243 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 58 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 438956d1f..e7d2af553 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -41,17 +41,27 @@ using Real3 = Vec3; PscParams psc_params; -double electron_temperature; -double ion_temperature; double electron_mass; double ion_mass; +double n_upstream; Double3 v_upstream; - -Real3 background_h_upstream; - -Real3 background_e; -Real3 background_h; +double te_upstream; +double ti_upstream; +Real3 h0_upstream; +Real3 e0; // e0 is constant + +// ---------------------------- +// standing shock params +double n_downstream; +Double3 v_downstream; +double te_downstream; +double ti_downstream; +Real3 h0_downstream; + +double transition_half_width = 1.0; +double transition_steepness = 2.0; // at least sqrt(3)~1.74 +// ---------------------------- Int3 gdims; Double3 lengths; @@ -64,6 +74,7 @@ int out_interval; int marder_interval; std::string turb_method; +std::string shock_method; int nicell; int seed; @@ -81,6 +92,8 @@ void setupParameters(int argc, char** argv) std::string path_to_params(argv[1]); InputParams inputParams(path_to_params); + shock_method = inputParams.getOrDefault("shock_method", "wall"); + psc_params.stats_every = inputParams.getOrDefault("stats_interval", 1000); psc_params.cfl = inputParams.getOrDefault("cfl", .75); @@ -89,24 +102,63 @@ void setupParameters(int argc, char** argv) psc_params.write_checkpoint_every_step = inputParams.getOrDefault("checkpoint_interval", 0); - electron_temperature = inputParams.get("electron_temperature"); - ion_temperature = inputParams.get("ion_temperature"); electron_mass = inputParams.get("electron_mass"); ion_mass = inputParams.get("ion_mass"); + n_upstream = 1.0; + v_upstream = {0.0, inputParams.get("v_upstream_y"), 0.0}; + te_upstream = inputParams.get("electron_temperature"); + ti_upstream = inputParams.get("ion_temperature"); + inputParams.errIfPresentAndNotEqual("v_upstream_x", 0.0, ""); v_upstream = {0.0, inputParams.get("v_upstream_y"), 0.0}; inputParams.errIfPresentAndNotEqual("v_upstream_z", 0.0, ""); - double b_angle_y_to_x_rad = inputParams.get("b_angle_y_to_x_rad"); - double b_mag = inputParams.get("b_mag"); - background_h_upstream = - b_mag * Real3{sin(b_angle_y_to_x_rad), cos(b_angle_y_to_x_rad), 0.0}; + if (shock_method == "wall") { + double b_angle_y_to_x_rad = inputParams.get("b_angle_y_to_x_rad"); + h0_upstream = Real3{sin(b_angle_y_to_x_rad), cos(b_angle_y_to_x_rad), 0.0} * + inputParams.get("b_mag"); + e0 = -v_upstream.cross(h0_upstream); + + // relativistic correction + double gamma = 1 / sqrt(1 - v_upstream.mag2()); + e0 *= gamma; + h0_upstream *= Real3{gamma, 1.0, gamma}; + } + + if (shock_method == "standing") { + inputParams.errIfPresent("b_angle_y_to_x_rad", + "only perpendicular shocks are supported"); + h0_upstream = {inputParams.get("b_mag"), 0.0, 0.0}; + e0 = -v_upstream.cross(h0_upstream); + + // no relativistic correction, since these aren't relativistic RH conditions - double gamma = 1 / sqrt(1 - v_upstream.mag2()); - background_e = -gamma * v_upstream.cross(background_h_upstream); - // note: this only holds for vx=vz=0 - background_h = background_h_upstream * Real3{gamma, 1.0, gamma}; + // for perpendicular shock (2013 Balogh eq.3.36 and normalization in + // sec.3.3.1) + double b_norm = sqrt(ion_mass * n_upstream * v_upstream.mag2()); + double t_norm = 0.5 * ion_mass * v_upstream.mag2(); + double beta1 = + 2.0 * n_upstream * (te_upstream + ti_upstream) / h0_upstream.mag2(); + Double3 B1 = h0_upstream / b_norm; + double T1 = (te_upstream + ti_upstream) / t_norm; + + double MA_sq = + v_upstream.mag2() * ion_mass * n_upstream / h0_upstream.mag2(); + + double tmp = 1.0 + (1.0 + 2.5 * beta1) * B1.mag2(); + double r = 8.0 / (tmp + sqrt(sqr(tmp) + 2 * B1.mag2())); + + double heating_factor = + 1.0 + + 4.0 / (5.0 * T1) * ((sqr(r) - 1.0) / (2.0 * sqr(r)) + (1.0 - r) / MA_sq); + + n_downstream = n_upstream * r; + v_downstream = v_upstream / r; + te_downstream = te_upstream * heating_factor; + ti_downstream = ti_upstream * heating_factor; + h0_downstream = h0_upstream * r; + } gdims[0] = inputParams.get("nx"); gdims[1] = inputParams.get("ny"); @@ -147,6 +199,23 @@ void setupParameters(int argc, char** argv) dst << src.rdbuf(); } +template +T interpolate_across_shock(T upstream, T downstream, double y) +{ + if (transition_half_width == 0.0) { + return y > 0.0 ? downstream : upstream; + } else { + // smooth transition function + y /= transition_half_width; + double sigmoid_val = abs(y) >= 1.0 + ? (y > 0.0 ? 1.0 : -1.0) + : tanh(transition_steepness * y / (1.0 - y * y)); + double weight_downstream = (sigmoid_val + 1.0) / 2.0; + double weight_upstream = 1.0 - weight_downstream; + return upstream * weight_upstream + downstream * weight_downstream; + } +} + // ====================================================================== // setupGrid // @@ -158,14 +227,27 @@ void setupParameters(int argc, char** argv) Grid_t* setupGrid() { // FIXME add a check to catch mismatch between Dim and n grid points early - Double3 corner = {0.0, 0.0, 0.0}; + + Double3 corner; + int bnd_prt_upper; + int bnd_fld_upper; + + if (shock_method == "wall") { + corner = {0.0, 0.0, 0.0}; + bnd_fld_upper = BND_FLD_CONDUCTING_WALL; + bnd_prt_upper = BND_PRT_REFLECTING; + } else if (shock_method == "standing") { + corner = {0.0, -lengths[1] / 2.0, 0.0}; + bnd_fld_upper = BND_FLD_OPEN; + bnd_prt_upper = BND_PRT_OPEN; + } + auto domain = Grid_t::Domain{gdims, lengths, corner, n_patches}; - auto bc = - psc::grid::BC{{BND_FLD_PERIODIC, BND_FLD_OPEN, BND_FLD_PERIODIC}, - {BND_FLD_PERIODIC, BND_FLD_CONDUCTING_WALL, BND_FLD_PERIODIC}, - {BND_PRT_PERIODIC, BND_PRT_OPEN, BND_PRT_PERIODIC}, - {BND_PRT_PERIODIC, BND_PRT_REFLECTING, BND_PRT_PERIODIC}}; + auto bc = psc::grid::BC{{BND_FLD_PERIODIC, BND_FLD_OPEN, BND_FLD_PERIODIC}, + {BND_FLD_PERIODIC, bnd_fld_upper, BND_FLD_PERIODIC}, + {BND_PRT_PERIODIC, BND_PRT_OPEN, BND_PRT_PERIODIC}, + {BND_PRT_PERIODIC, bnd_prt_upper, BND_PRT_PERIODIC}}; auto kinds = Grid_t::Kinds(NR_KINDS); kinds[KIND_ELECTRON] = {-1.0, electron_mass, "e"}; @@ -194,21 +276,32 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts) setup_particles.random_offsets = true; setup_particles.initial_momentum_gamma_correction = true; - auto init_np = [&](int kind, Double3 crd, int p, Int3 idx, - psc_particle_np& np) { - double temperature = - np.kind == KIND_ION ? ion_temperature : electron_temperature; - np.n = 1.0; - np.p = - setup_particles.createMaxwellian({np.kind, - np.n, - v_upstream, - {temperature, temperature, temperature}, - np.tag}); - }; - - partitionAndSetupParticles(setup_particles, balance, grid_ptr, mprts, - init_np); + if (shock_method == "wall") { + auto init_np = [&](int kind, Double3 pos, int p, Int3 idx, + psc_particle_np& np) { + double t = np.kind == KIND_ION ? ti_upstream : te_upstream; + np.n = 1.0; + np.p = setup_particles.createMaxwellian( + {np.kind, np.n, v_upstream, {t, t, t}, np.tag}); + }; + + partitionAndSetupParticles(setup_particles, balance, grid_ptr, mprts, + init_np); + } else if (shock_method == "standing") { + auto init_np = [&](int kind, Double3 pos, int p, Int3 idx, + psc_particle_np& np) { + np.n = interpolate_across_shock(n_upstream, n_downstream, pos[1]); + double t = interpolate_across_shock( + np.kind == KIND_ION ? ti_upstream : te_upstream, + np.kind == KIND_ION ? ti_downstream : te_downstream, pos[1]); + Double3 v = interpolate_across_shock(v_upstream, v_downstream, pos[1]); + np.p = + setup_particles.createMaxwellian({np.kind, np.n, v, {t, t, t}, np.tag}); + }; + + partitionAndSetupParticles(setup_particles, balance, grid_ptr, mprts, + init_np); + } } // ====================================================================== @@ -224,13 +317,21 @@ void add_background_fields(MfieldsState& mflds) int n_ghosts = mflds.ibn().max(); grid.Foreach_3d(n_ghosts, n_ghosts, [&](int jx, int jy, int jz) { - field_patch(HX, jx, jy, jz) += background_h[0]; - field_patch(HY, jx, jy, jz) += background_h[1]; - field_patch(HZ, jx, jy, jz) += background_h[2]; + Real3 h0; + if (shock_method == "wall") { + h0 = h0_upstream; + } else if (shock_method == "standing") { + Double3 pos = centering::get_pos(patch, {jx, jy, jz}, centering::NC, 0); + h0 = interpolate_across_shock(h0_upstream, h0_downstream, pos[1]); + } + + field_patch(HX, jx, jy, jz) += h0[0]; + field_patch(HY, jx, jy, jz) += h0[1]; + field_patch(HZ, jx, jy, jz) += h0[2]; - field_patch(EX, jx, jy, jz) += background_e[0]; - field_patch(EY, jx, jy, jz) += background_e[1]; - field_patch(EZ, jx, jy, jz) += background_e[2]; + field_patch(EX, jx, jy, jz) += e0[0]; + field_patch(EY, jx, jy, jz) += e0[1]; + field_patch(EZ, jx, jy, jz) += e0[2]; }); } } @@ -811,16 +912,34 @@ static void run(int argc, char** argv) int oute_interval = -100; DiagEnergies oute{grid.comm(), oute_interval}; - auto ion_injector = + auto ion_injector_lo = + BoundaryInjector( + ParticleGeneratorMaxwellian(KIND_ION, grid.kinds[KIND_ION], v_upstream, + {ti_upstream, ti_upstream, ti_upstream})); + ion_injector_lo.density = n_upstream; + auto electron_injector_lo = + BoundaryInjector( + ParticleGeneratorMaxwellian(KIND_ELECTRON, grid.kinds[KIND_ELECTRON], + v_upstream, + {te_upstream, te_upstream, te_upstream})); + electron_injector_lo.density = n_upstream; + + auto ion_injector_hi = BoundaryInjector( ParticleGeneratorMaxwellian( - KIND_ION, grid.kinds[KIND_ION], v_upstream, - {ion_temperature, ion_temperature, ion_temperature})); - auto electron_injector = + KIND_ION, grid.kinds[KIND_ION], v_downstream, + {ti_downstream, ti_downstream, ti_downstream})); + ion_injector_hi.density = n_downstream; + ion_injector_hi.inject_lo = false; + ion_injector_hi.inject_hi = true; + auto electron_injector_hi = BoundaryInjector( ParticleGeneratorMaxwellian( - KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_upstream, - {electron_temperature, electron_temperature, electron_temperature})); + KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_downstream, + {te_downstream, te_downstream, te_downstream})); + electron_injector_hi.density = n_downstream; + electron_injector_hi.inject_lo = false; + electron_injector_hi.inject_hi = true; // ---------------------------------------------------------------------- // set up initial conditions @@ -840,19 +959,22 @@ static void run(int argc, char** argv) psc.add_gauss_corrector(&marder); - psc.bndf.background_e_lo = background_e; - psc.bndf.background_h_lo = background_h; + psc.bndf.background_e_lo = e0; + psc.bndf.background_h_lo = h0_upstream; + psc.bndf.background_e_hi = e0; + psc.bndf.background_h_hi = h0_downstream; + if (turb_db2 > 0.0) { if (checkpoint_filename.empty()) { // mflds is currently just the pure, initial turbulence - psc.bndf.radiation = new AdvectedPeriodicFields{ - mflds, v_upstream[1], background_e, background_h}; + psc.bndf.radiation = + new AdvectedPeriodicFields{mflds, v_upstream[1], e0, h0_upstream}; } else { // mflds is completely unrelated; need to re-initialize turbulence MfieldsState mflds2{grid}; initialize_turbulence(mflds2); - psc.bndf.radiation = new AdvectedPeriodicFields{ - mflds2, v_upstream[1], background_e, background_h}; + psc.bndf.radiation = + new AdvectedPeriodicFields{mflds2, v_upstream[1], e0, h0_upstream}; } } @@ -867,8 +989,13 @@ static void run(int argc, char** argv) psc.add_diagnostic(&outp); psc.add_diagnostic(&oute); - psc.add_injector(&ion_injector); - psc.add_injector(&electron_injector); + psc.add_injector(&ion_injector_lo); + psc.add_injector(&electron_injector_lo); + + if (shock_method == "standing") { + psc.add_injector(&ion_injector_hi); + psc.add_injector(&electron_injector_hi); + } psc.integrate(); } From ef363c67e3605bd681e9e209e56f1af29f2870fc Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 21 May 2026 15:42:11 -0400 Subject: [PATCH 027/106] psc_shock: +n_checkpoints --- src/psc_shock.cxx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index e7d2af553..88fa6a08f 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -99,8 +99,6 @@ void setupParameters(int argc, char** argv) psc_params.cfl = inputParams.getOrDefault("cfl", .75); checkpoint_filename = inputParams.getOrDefault("checkpoint_filename", ""); - psc_params.write_checkpoint_every_step = - inputParams.getOrDefault("checkpoint_interval", 0); electron_mass = inputParams.get("electron_mass"); ion_mass = inputParams.get("ion_mass"); @@ -181,6 +179,19 @@ void setupParameters(int argc, char** argv) } turb_correlation_length = inputParams.get("turb_correlation_length"); + if (inputParams.has("checkpoint_interval")) { + psc_params.write_checkpoint_every_step = + inputParams.get("checkpoint_interval"); + inputParams.errIfPresent( + "n_checkpoints", + "n_checkpoints is mutually exclusive with checkpoint_interval"); + } else if (inputParams.has("n_checkpoints")) { + int n_checkpoints = inputParams.get("n_checkpoints"); + if (n_checkpoints > 0) { + psc_params.write_checkpoint_every_step = psc_params.nmax / n_checkpoints; + } + } + int n_writes = inputParams.getOrDefault("n_writes", 100); out_interval = psc_params.nmax / n_writes; marder_interval = inputParams.getOrDefault("marder_interval", -1); From 81a3eb9bd5e3460997f5f94147ed30dd5e7dabc8 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 13 May 2026 16:02:12 -0400 Subject: [PATCH 028/106] setup_particles: fix message --- src/include/setup_particles.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/setup_particles.hxx b/src/include/setup_particles.hxx index f335c289a..fc5246ede 100644 --- a/src/include/setup_particles.hxx +++ b/src/include/setup_particles.hxx @@ -287,8 +287,8 @@ struct SetupParticles "have the exact same initial position distribution. This results in " "a charge density of 0 if there are two species with opposite " "charges, but the resulting charge density is nonzero in general. In " - "the latter, case, take special care to ensure Gauss' law isn't " - "violated."); + "the latter case, take special care to ensure Gauss' law isn't " + "violated.\n"); } int seed = rng::detail::get_process_seed(); From 7e9cc9b0077c8b6ec6f3e59a00b90a7a55e5efc0 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 2 Jun 2026 16:14:39 -0400 Subject: [PATCH 029/106] setup_particles: +VelocityBooster --- src/include/setup_particles.hxx | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/include/setup_particles.hxx b/src/include/setup_particles.hxx index fc5246ede..a110d8077 100644 --- a/src/include/setup_particles.hxx +++ b/src/include/setup_particles.hxx @@ -121,6 +121,40 @@ int get_n_in_cell(real_t density, real_t prts_per_unit_density, return std::max(1, int(density * prts_per_unit_density + .5)); } +/** + * @brief Boosts velocities using cached intermediate values for a particular + * Lorentz frame. + */ +struct VelocityBooster +{ + VelocityBooster(Double3 frame_v) + : frame_gamma(1.0 / std::sqrt(1.0 - frame_v.mag2())), + frame_u(frame_v * frame_gamma), + frame_dir(frame_v / frame_v.mag()) + { + // FIXME kind of hacky + if (frame_v.mag2() == 0.0) { + frame_dir = {1, 0, 0}; + } + } + + /** + * @param prt_v a particle's "unprimed" non-proper velocity + * @return its "primed" proper velocity + */ + Double3 boost_and_make_proper(Double3 prt_v) + { + double prt_gamma = 1.0 / std::sqrt(1.0 - prt_v.mag2()); + Double3 prt_u = prt_v * prt_gamma; + return prt_u + (frame_gamma - 1.0) * prt_u.dot(frame_dir) * frame_dir - + frame_u * prt_gamma; + } + + double frame_gamma; + Double3 frame_u; + Double3 frame_dir; +}; + // ====================================================================== // SetupParticles From ccd98fd3ede574072f6c399119577ac97648aff1 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 2 Jun 2026 16:15:10 -0400 Subject: [PATCH 030/106] setup_particles: sample v, then boost --- src/include/setup_particles.hxx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/include/setup_particles.hxx b/src/include/setup_particles.hxx index a110d8077..2fd5033d3 100644 --- a/src/include/setup_particles.hxx +++ b/src/include/setup_particles.hxx @@ -247,13 +247,23 @@ struct SetupParticles return [=]() { static rng::Normal dist; + if (initial_momentum_gamma_correction) { + static VelocityBooster booster{-npt.p}; + + Double3 prt_v; + for (int d = 0; d < 3; d++) { + // sample velocity in plasma frame + prt_v[d] = dist.get(0.0, std::sqrt(npt.T[d] / m)); + } + + // boost to lab frame + return booster.boost_and_make_proper(prt_v); + } + Double3 p; for (int i = 0; i < 3; i++) p[i] = dist.get(npt.p[i], beta * std::sqrt(npt.T[i] / m)); - if (initial_momentum_gamma_correction) { - p = vel_to_4vel(p); - } return p; }; } From 3d9f38ccebad85505a2938107fc0a3f241ac0e47 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 2 Jun 2026 16:15:38 -0400 Subject: [PATCH 031/106] boundary_injector: use VelocityBooster I think the original impl was wrong, actually... --- src/include/boundary_injector.hxx | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 9ed203f43..f47465b4d 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -24,16 +24,10 @@ public: // FIXME would be nice to just pass 1 thing for kind-related info ParticleGeneratorMaxwellian(int kind_idx, Grid_t::Kind kind, Real3 mean_v, Real3 temperature) - : kind_idx{kind_idx}, - gamma{1.0 / sqrt(1.0 - mean_v.mag2())}, - mean_u{mean_v * gamma}, - mean_u_hat{mean_u / mean_u.mag()} + : kind_idx{kind_idx}, prt_booster{-mean_v} { for (int d = 0; d < 3; d++) { - // temperature is interpreted as velocity temperature in bulk frame - Real stdev_v = sqrt(temperature[d] / kind.m); - // multiply by gamma to get proper velocity in bulk frame - vdfs[d] = VelocityDistributionFunction{0.0, stdev_v * gamma}; + vdfs[d] = VDF{0.0, sqrt(temperature[d] / kind.m)}; } } @@ -44,10 +38,8 @@ public: x[d] = min_pos[d] + uniform_dist.get() * pos_range[d]; } - // generate proper velocity in bulk frame - Real3 u{vdfs[0].get(), vdfs[1].get(), vdfs[2].get()}; - // Lorentz boost - u += (gamma - 1.0) * u.dot(mean_u_hat) * mean_u_hat + mean_u; + Real3 v{vdfs[0].get(), vdfs[1].get(), vdfs[2].get()}; + Real3 u = prt_booster.boost_and_make_proper(v); Real w = 1.0; psc::particle::Tag tag = 0; @@ -56,12 +48,10 @@ public: } private: - using VelocityDistributionFunction = rng::Normal; - Vec3 vdfs; + using VDF = rng::Normal; + Vec3 vdfs; + VelocityBooster prt_booster; int kind_idx; - Real gamma; - Real3 mean_u; - Real3 mean_u_hat; rng::Uniform uniform_dist{0.0, 1.0}; }; From df1f779cfdd10f346b276aea421e681a765c2f4a Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 2 Jun 2026 16:44:28 -0400 Subject: [PATCH 032/106] setup_particles: +boost() --- src/include/setup_particles.hxx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/include/setup_particles.hxx b/src/include/setup_particles.hxx index 2fd5033d3..272972798 100644 --- a/src/include/setup_particles.hxx +++ b/src/include/setup_particles.hxx @@ -138,6 +138,17 @@ struct VelocityBooster } } + /** + * @param prt_v a particle's "unprimed" proper velocity + * @return its "primed" proper velocity + */ + Double3 boost(Double3 prt_u) + { + double prt_gamma = std::sqrt(1.0 + prt_u.mag2()); + return prt_u + (frame_gamma - 1.0) * prt_u.dot(frame_dir) * frame_dir - + frame_u * prt_gamma; + } + /** * @param prt_v a particle's "unprimed" non-proper velocity * @return its "primed" proper velocity From 15c4f2a4f5222ebb9b22d4ee216d092df37cb302 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 3 Jun 2026 09:22:36 -0400 Subject: [PATCH 033/106] setup_particles: don't have static booster --- src/include/setup_particles.hxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/include/setup_particles.hxx b/src/include/setup_particles.hxx index 272972798..3cf55888d 100644 --- a/src/include/setup_particles.hxx +++ b/src/include/setup_particles.hxx @@ -259,7 +259,8 @@ struct SetupParticles static rng::Normal dist; if (initial_momentum_gamma_correction) { - static VelocityBooster booster{-npt.p}; + // FIXME cache this (static doesn't work) + VelocityBooster booster{-npt.p}; Double3 prt_v; for (int d = 0; d < 3; d++) { From a79e93a1be69a42a32326e183b81337600e41230 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 3 Jun 2026 11:33:42 -0400 Subject: [PATCH 034/106] setup_particles: -vel_to_4vel --- src/include/setup_particles.hxx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/include/setup_particles.hxx b/src/include/setup_particles.hxx index 3cf55888d..d703dd4eb 100644 --- a/src/include/setup_particles.hxx +++ b/src/include/setup_particles.hxx @@ -29,19 +29,6 @@ struct psc_particle_np psc::particle::Tag tag; }; -/** - * @brief Calculates gamma * v for the given velocity v. - * @tparam Real real type - * @param v the actual velocity - * @return the spatial components of the corresponding 4-velocity - */ -template -Vec3 vel_to_4vel(Vec3 v) -{ - Real gamma = 1.0 / sqrt(1.0 - v.mag2()); - return v * gamma; -} - struct InitNptFunc { // Initialize particles according to a Maxwellian. From b2636272c76d119d11652370e725e3b27c954035 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 3 Jun 2026 13:57:53 -0400 Subject: [PATCH 035/106] *: hack pretend v is u --- src/include/boundary_injector.hxx | 5 ++++- src/include/setup_particles.hxx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index f47465b4d..8a703dbf8 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -39,7 +39,10 @@ public: } Real3 v{vdfs[0].get(), vdfs[1].get(), vdfs[2].get()}; - Real3 u = prt_booster.boost_and_make_proper(v); + // FIXME should really sample from Maxwell-Juttner + // this hack interprests v as u to handle rare case when v>1 + // v<<1 => v~= u anyways + Real3 u = prt_booster.boost(v); Real w = 1.0; psc::particle::Tag tag = 0; diff --git a/src/include/setup_particles.hxx b/src/include/setup_particles.hxx index d703dd4eb..0dd10b22d 100644 --- a/src/include/setup_particles.hxx +++ b/src/include/setup_particles.hxx @@ -256,7 +256,10 @@ struct SetupParticles } // boost to lab frame - return booster.boost_and_make_proper(prt_v); + // FIXME should really sample from Maxwell-Juttner + // this hack interprests v as u to handle rare case when v>1 + // v<<1 => v~= u anyways + return booster.boost(prt_v); } Double3 p; From c65fd55870bfaa10a48bf08472cb90e9b7636ca8 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 5 Jun 2026 09:34:41 -0400 Subject: [PATCH 036/106] psc_shock: cleanup params --- src/psc_shock.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 88fa6a08f..7d9f475db 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -104,7 +104,6 @@ void setupParameters(int argc, char** argv) ion_mass = inputParams.get("ion_mass"); n_upstream = 1.0; - v_upstream = {0.0, inputParams.get("v_upstream_y"), 0.0}; te_upstream = inputParams.get("electron_temperature"); ti_upstream = inputParams.get("ion_temperature"); @@ -122,11 +121,10 @@ void setupParameters(int argc, char** argv) double gamma = 1 / sqrt(1 - v_upstream.mag2()); e0 *= gamma; h0_upstream *= Real3{gamma, 1.0, gamma}; - } - - if (shock_method == "standing") { - inputParams.errIfPresent("b_angle_y_to_x_rad", - "only perpendicular shocks are supported"); + } else if (shock_method == "standing") { + inputParams.warnIfPresent( + "b_angle_y_to_x_rad", + "only perpendicular shocks are supported for wall case; angle ignored"); h0_upstream = {inputParams.get("b_mag"), 0.0, 0.0}; e0 = -v_upstream.cross(h0_upstream); @@ -287,6 +285,8 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts) setup_particles.random_offsets = true; setup_particles.initial_momentum_gamma_correction = true; + // std::cout << v_upstream << "=vupstream\n"; + if (shock_method == "wall") { auto init_np = [&](int kind, Double3 pos, int p, Int3 idx, psc_particle_np& np) { From 64328b8a1ca3e2f3717ba13ba2e0d5227ebf4351 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 12 Jun 2026 15:55:22 -0400 Subject: [PATCH 037/106] psc_shock: breaking changes to params --- src/psc_shock.cxx | 75 ++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 7d9f475db..9e0f412a1 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -100,38 +100,44 @@ void setupParameters(int argc, char** argv) checkpoint_filename = inputParams.getOrDefault("checkpoint_filename", ""); - electron_mass = inputParams.get("electron_mass"); - ion_mass = inputParams.get("ion_mass"); + electron_mass = inputParams.get("m_e"); + ion_mass = inputParams.get("m_i"); n_upstream = 1.0; - te_upstream = inputParams.get("electron_temperature"); - ti_upstream = inputParams.get("ion_temperature"); + te_upstream = inputParams.get("T_e"); + ti_upstream = inputParams.get("T_i"); - inputParams.errIfPresentAndNotEqual("v_upstream_x", 0.0, ""); - v_upstream = {0.0, inputParams.get("v_upstream_y"), 0.0}; - inputParams.errIfPresentAndNotEqual("v_upstream_z", 0.0, ""); + double v_shock = inputParams.get("v_shock"); + double theta_bn_deg = inputParams.get("θ_Bn_deg"); + double b0 = inputParams.get("B_0"); + + double theta_bn = theta_bn_deg * M_PI / 180.0; + double theta_xz = inputParams.get("θ_xz_deg") * M_PI / 180.0; + h0_upstream = Real3{sin(theta_bn) * cos(theta_xz), cos(theta_bn), + sin(theta_bn) * sin(theta_xz)} * + b0; if (shock_method == "wall") { - double b_angle_y_to_x_rad = inputParams.get("b_angle_y_to_x_rad"); - h0_upstream = Real3{sin(b_angle_y_to_x_rad), cos(b_angle_y_to_x_rad), 0.0} * - inputParams.get("b_mag"); + v_upstream = {0.0, v_shock / 1.5, 0.0}; // factor is empirical e0 = -v_upstream.cross(h0_upstream); // relativistic correction double gamma = 1 / sqrt(1 - v_upstream.mag2()); e0 *= gamma; h0_upstream *= Real3{gamma, 1.0, gamma}; - } else if (shock_method == "standing") { - inputParams.warnIfPresent( - "b_angle_y_to_x_rad", - "only perpendicular shocks are supported for wall case; angle ignored"); - h0_upstream = {inputParams.get("b_mag"), 0.0, 0.0}; - e0 = -v_upstream.cross(h0_upstream); - - // no relativistic correction, since these aren't relativistic RH conditions + } else if (shock_method == "relaxation") { + v_upstream = {0.0, v_shock, 0.0}; + e0 = -v_upstream.cross(h0_upstream); // upstream and downstream are the same // for perpendicular shock (2013 Balogh eq.3.36 and normalization in // sec.3.3.1) + if (theta_bn_deg != 90.0) { + LOG_ERROR("θ_Bn must be 90° for relaxation method; got %f°\n", + theta_bn_deg); + } + + // no relativistic correction, since these aren't relativistic RH conditions + double b_norm = sqrt(ion_mass * n_upstream * v_upstream.mag2()); double t_norm = 0.5 * ion_mass * v_upstream.mag2(); double beta1 = @@ -161,21 +167,15 @@ void setupParameters(int argc, char** argv) gdims[2] = inputParams.get("nz"); psc_params.nmax = inputParams.get("nt"); - n_patches[0] = inputParams.get("n_patches_x"); - n_patches[1] = inputParams.get("n_patches_y"); - n_patches[2] = inputParams.get("n_patches_z"); - - Double3 dx = {inputParams.get("dx"), inputParams.get("dy"), - inputParams.get("dz")}; + n_patches[0] = inputParams.get("npx"); + n_patches[1] = inputParams.get("npy"); + n_patches[2] = inputParams.get("npz"); - lengths = Double3(gdims) * dx; + lengths = {inputParams.get("lx"), inputParams.get("ly"), + inputParams.get("lz")}; - if (inputParams.warnIfPresent("turb_dB^2", "set turb_dB instead")) { - turb_db2 = inputParams.get("turb_dB^2"); - } else { - turb_db2 = sqr(inputParams.get("turb_dB")); - } - turb_correlation_length = inputParams.get("turb_correlation_length"); + turb_db2 = sqr(inputParams.get("dB")); + turb_correlation_length = inputParams.get("L_c"); if (inputParams.has("checkpoint_interval")) { psc_params.write_checkpoint_every_step = @@ -194,9 +194,6 @@ void setupParameters(int argc, char** argv) out_interval = psc_params.nmax / n_writes; marder_interval = inputParams.getOrDefault("marder_interval", -1); - inputParams.errIfPresentAndEqual("mirror_domain", true, - "only 'false' is permitted"); - turb_method = inputParams.getOrDefault("turb_method", "alfven_dense"); @@ -245,7 +242,7 @@ Grid_t* setupGrid() corner = {0.0, 0.0, 0.0}; bnd_fld_upper = BND_FLD_CONDUCTING_WALL; bnd_prt_upper = BND_PRT_REFLECTING; - } else if (shock_method == "standing") { + } else if (shock_method == "relaxation") { corner = {0.0, -lengths[1] / 2.0, 0.0}; bnd_fld_upper = BND_FLD_OPEN; bnd_prt_upper = BND_PRT_OPEN; @@ -285,8 +282,6 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts) setup_particles.random_offsets = true; setup_particles.initial_momentum_gamma_correction = true; - // std::cout << v_upstream << "=vupstream\n"; - if (shock_method == "wall") { auto init_np = [&](int kind, Double3 pos, int p, Int3 idx, psc_particle_np& np) { @@ -298,7 +293,7 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts) partitionAndSetupParticles(setup_particles, balance, grid_ptr, mprts, init_np); - } else if (shock_method == "standing") { + } else if (shock_method == "relaxation") { auto init_np = [&](int kind, Double3 pos, int p, Int3 idx, psc_particle_np& np) { np.n = interpolate_across_shock(n_upstream, n_downstream, pos[1]); @@ -331,7 +326,7 @@ void add_background_fields(MfieldsState& mflds) Real3 h0; if (shock_method == "wall") { h0 = h0_upstream; - } else if (shock_method == "standing") { + } else if (shock_method == "relaxation") { Double3 pos = centering::get_pos(patch, {jx, jy, jz}, centering::NC, 0); h0 = interpolate_across_shock(h0_upstream, h0_downstream, pos[1]); } @@ -1003,7 +998,7 @@ static void run(int argc, char** argv) psc.add_injector(&ion_injector_lo); psc.add_injector(&electron_injector_lo); - if (shock_method == "standing") { + if (shock_method == "relaxation") { psc.add_injector(&ion_injector_hi); psc.add_injector(&electron_injector_hi); } From 8a27687ac3a204b772e7d82df389df7440b65532 Mon Sep 17 00:00:00 2001 From: James McClung Date: Mon, 15 Jun 2026 15:56:02 -0400 Subject: [PATCH 038/106] psc_shock: override downstream vals --- src/psc_shock.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 9e0f412a1..3f455b9d1 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -150,6 +150,7 @@ void setupParameters(int argc, char** argv) double tmp = 1.0 + (1.0 + 2.5 * beta1) * B1.mag2(); double r = 8.0 / (tmp + sqrt(sqr(tmp) + 2 * B1.mag2())); + r = inputParams.getOrDefault("r", r); double heating_factor = 1.0 + @@ -157,9 +158,12 @@ void setupParameters(int argc, char** argv) n_downstream = n_upstream * r; v_downstream = v_upstream / r; - te_downstream = te_upstream * heating_factor; - ti_downstream = ti_upstream * heating_factor; h0_downstream = h0_upstream * r; + + te_downstream = + inputParams.getOrDefault("T_e2", te_upstream * heating_factor); + ti_downstream = + inputParams.getOrDefault("T_i2", ti_upstream * heating_factor); } gdims[0] = inputParams.get("nx"); From 788b97a864bf0ef740289abdfe8c62959c52c504 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 16 Jun 2026 09:08:35 -0400 Subject: [PATCH 039/106] boundary_injector: preacceleration v1 --- src/include/boundary_injector.hxx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 8a703dbf8..bb1733d57 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -121,11 +121,20 @@ public: int n_prts_to_try_inject = get_n_in_cell(density, grid.norm.prts_per_unit_density, true); + Int3 inner_idx = initial_idx + Int3::unit(INJECT_DIM_IDX_); + real_t inner_e = mflds(EX + INJECT_DIM_IDX_, inner_idx[0], + inner_idx[1], inner_idx[2], p); + for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { psc::particle::Inject prt = particle_generator_.get(cell_corner, grid.domain.dx); + // FIXME no factor of dt - why does this work? can it be better? + real_t du_from_e = + inner_e * grid.kinds[prt.kind].q / grid.kinds[prt.kind].m; + prt.u[INJECT_DIM_IDX_] += du_from_e; + AdvanceParticle advance{grid.dt}; Real3 v = advance.calc_v(prt.u); Real3 initial_normalized_pos = prt.x * dxi; @@ -164,11 +173,20 @@ public: int n_prts_to_try_inject = get_n_in_cell(density, grid.norm.prts_per_unit_density, true); + Int3 inner_idx = initial_idx - Int3::unit(INJECT_DIM_IDX_); + real_t inner_e = mflds(EX + INJECT_DIM_IDX_, inner_idx[0], + inner_idx[1], inner_idx[2], p); + for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { psc::particle::Inject prt = particle_generator_.get(cell_corner, grid.domain.dx); + // FIXME no factor of dt - why does this work? can it be better? + real_t du_from_e = + inner_e * grid.kinds[prt.kind].q / grid.kinds[prt.kind].m; + prt.u[INJECT_DIM_IDX_] += du_from_e; + AdvanceParticle advance{grid.dt}; Real3 v = advance.calc_v(prt.u); Real3 initial_normalized_pos = prt.x * dxi; From 87f474f8bd1cce15dffdf35f27b01cef4f62f9c5 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 30 Jun 2026 19:25:46 +0200 Subject: [PATCH 040/106] psc_shock: +none shock case --- src/psc_shock.cxx | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 3f455b9d1..c416b3781 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -117,7 +117,7 @@ void setupParameters(int argc, char** argv) sin(theta_bn) * sin(theta_xz)} * b0; - if (shock_method == "wall") { + if (shock_method == "wall" || shock_method == "none") { v_upstream = {0.0, v_shock / 1.5, 0.0}; // factor is empirical e0 = -v_upstream.cross(h0_upstream); @@ -239,24 +239,36 @@ Grid_t* setupGrid() // FIXME add a check to catch mismatch between Dim and n grid points early Double3 corner; - int bnd_prt_upper; + int bnd_fld_lower; int bnd_fld_upper; + int bnd_prt_lower; + int bnd_prt_upper; if (shock_method == "wall") { corner = {0.0, 0.0, 0.0}; + bnd_fld_lower = BND_FLD_OPEN; bnd_fld_upper = BND_FLD_CONDUCTING_WALL; + bnd_prt_lower = BND_PRT_OPEN; bnd_prt_upper = BND_PRT_REFLECTING; + } else if (shock_method == "none") { + corner = {0.0, 0.0, 0.0}; + bnd_fld_lower = BND_FLD_PERIODIC; + bnd_fld_upper = BND_FLD_PERIODIC; + bnd_prt_lower = BND_PRT_PERIODIC; + bnd_prt_upper = BND_PRT_PERIODIC; } else if (shock_method == "relaxation") { corner = {0.0, -lengths[1] / 2.0, 0.0}; + bnd_fld_lower = BND_FLD_OPEN; bnd_fld_upper = BND_FLD_OPEN; + bnd_prt_lower = BND_PRT_OPEN; bnd_prt_upper = BND_PRT_OPEN; } auto domain = Grid_t::Domain{gdims, lengths, corner, n_patches}; - auto bc = psc::grid::BC{{BND_FLD_PERIODIC, BND_FLD_OPEN, BND_FLD_PERIODIC}, + auto bc = psc::grid::BC{{BND_FLD_PERIODIC, bnd_fld_lower, BND_FLD_PERIODIC}, {BND_FLD_PERIODIC, bnd_fld_upper, BND_FLD_PERIODIC}, - {BND_PRT_PERIODIC, BND_PRT_OPEN, BND_PRT_PERIODIC}, + {BND_PRT_PERIODIC, bnd_prt_lower, BND_PRT_PERIODIC}, {BND_PRT_PERIODIC, bnd_prt_upper, BND_PRT_PERIODIC}}; auto kinds = Grid_t::Kinds(NR_KINDS); @@ -286,7 +298,7 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts) setup_particles.random_offsets = true; setup_particles.initial_momentum_gamma_correction = true; - if (shock_method == "wall") { + if (shock_method == "wall" || shock_method == "none") { auto init_np = [&](int kind, Double3 pos, int p, Int3 idx, psc_particle_np& np) { double t = np.kind == KIND_ION ? ti_upstream : te_upstream; @@ -328,7 +340,7 @@ void add_background_fields(MfieldsState& mflds) int n_ghosts = mflds.ibn().max(); grid.Foreach_3d(n_ghosts, n_ghosts, [&](int jx, int jy, int jz) { Real3 h0; - if (shock_method == "wall") { + if (shock_method == "wall" || shock_method == "none") { h0 = h0_upstream; } else if (shock_method == "relaxation") { Double3 pos = centering::get_pos(patch, {jx, jy, jz}, centering::NC, 0); @@ -612,7 +624,8 @@ void inject_turbulence_dense(MfieldsState& mflds) Int3 i3_min = (1 - gdims) / 2; Int3 i3_max = gdims / 2; - // inject in only half of k-space, since +k and -k modes are indistinguishable + // inject in only half of k-space, since +k and -k modes are + // indistinguishable for (int d = 0; d < 3; d++) { if (gdims[d] > 2) { i3_min[d] = 0; @@ -974,7 +987,7 @@ static void run(int argc, char** argv) psc.bndf.background_e_hi = e0; psc.bndf.background_h_hi = h0_downstream; - if (turb_db2 > 0.0) { + if (turb_db2 > 0.0 && v_upstream[1] > 0.0) { if (checkpoint_filename.empty()) { // mflds is currently just the pure, initial turbulence psc.bndf.radiation = @@ -989,8 +1002,8 @@ static void run(int argc, char** argv) } if (checkpoint_filename.empty()) { - // add background after initializing radiation inflow, which only wants the - // perturbations to B + // add background after initializing radiation inflow, which only wants + // the perturbations to B add_background_fields(mflds); } @@ -999,8 +1012,10 @@ static void run(int argc, char** argv) psc.add_diagnostic(&outp); psc.add_diagnostic(&oute); - psc.add_injector(&ion_injector_lo); - psc.add_injector(&electron_injector_lo); + if (shock_method != "none") { + psc.add_injector(&ion_injector_lo); + psc.add_injector(&electron_injector_lo); + } if (shock_method == "relaxation") { psc.add_injector(&ion_injector_hi); From b5ac08c935fb7770082d8f1b1364ab593c749b1f Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 1 Jul 2026 23:13:04 +0200 Subject: [PATCH 041/106] psc_shock: technically correct curl didn't really matter on square cells, because dx should just be a scale factor, and we rescale dB at the end --- src/psc_shock.cxx | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index c416b3781..b4eac730e 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -404,24 +404,34 @@ void inject_b_from_potential(MfieldsState& mflds, PscConfig::Mfields& vector_potential) { const auto& grid = mflds.grid(); + Real3 dx = grid.domain.dx; for (int p = 0; p < mflds.n_patches(); ++p) { auto field_patch = make_Fields3d(mflds[p]); auto vector_potential_patch = make_Fields3d(vector_potential[p]); grid.Foreach_3d(2, 1, [&](int jx, int jy, int jz) { - field_patch(HX, jx, jy, jz) = vector_potential_patch(AZ, jx, jy + 1, jz) - - vector_potential_patch(AZ, jx, jy, jz) - - vector_potential_patch(AY, jx, jy, jz + 1) + - vector_potential_patch(AY, jx, jy, jz); - field_patch(HY, jx, jy, jz) = vector_potential_patch(AX, jx, jy, jz + 1) - - vector_potential_patch(AX, jx, jy, jz) - - vector_potential_patch(AZ, jx + 1, jy, jz) + - vector_potential_patch(AZ, jx, jy, jz); - field_patch(HZ, jx, jy, jz) = vector_potential_patch(AY, jx + 1, jy, jz) - - vector_potential_patch(AY, jx, jy, jz) - - vector_potential_patch(AX, jx, jy + 1, jz) + - vector_potential_patch(AX, jx, jy, jz); + field_patch(HX, jx, jy, jz) = + (vector_potential_patch(AZ, jx, jy + 1, jz) - + vector_potential_patch(AZ, jx, jy, jz)) / + dx[1] - + (vector_potential_patch(AY, jx, jy, jz + 1) - + vector_potential_patch(AY, jx, jy, jz)) / + dx[2]; + field_patch(HY, jx, jy, jz) = + (vector_potential_patch(AX, jx, jy, jz + 1) - + vector_potential_patch(AX, jx, jy, jz)) / + dx[2] - + (vector_potential_patch(AZ, jx + 1, jy, jz) - + vector_potential_patch(AZ, jx, jy, jz)) / + dx[0]; + field_patch(HZ, jx, jy, jz) = + (vector_potential_patch(AY, jx + 1, jy, jz) - + vector_potential_patch(AY, jx, jy, jz)) / + dx[0] - + (vector_potential_patch(AX, jx, jy + 1, jz) - + vector_potential_patch(AX, jx, jy, jz)) / + dx[1]; }); } } From 75252a0cc7a5440712f70bb887918937bc704437 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 8 Jul 2026 11:45:53 -0400 Subject: [PATCH 042/106] psc_shock: fix curl for invar dims --- src/psc_shock.cxx | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index b4eac730e..7d668deac 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -412,25 +412,31 @@ void inject_b_from_potential(MfieldsState& mflds, grid.Foreach_3d(2, 1, [&](int jx, int jy, int jz) { field_patch(HX, jx, jy, jz) = - (vector_potential_patch(AZ, jx, jy + 1, jz) - - vector_potential_patch(AZ, jx, jy, jz)) / + (Dim::is_invar(1) ? 0 + : vector_potential_patch(AZ, jx, jy + 1, jz) - + vector_potential_patch(AZ, jx, jy, jz)) / dx[1] - - (vector_potential_patch(AY, jx, jy, jz + 1) - - vector_potential_patch(AY, jx, jy, jz)) / + (Dim::is_invar(2) ? 0 + : vector_potential_patch(AY, jx, jy, jz + 1) - + vector_potential_patch(AY, jx, jy, jz)) / dx[2]; field_patch(HY, jx, jy, jz) = - (vector_potential_patch(AX, jx, jy, jz + 1) - - vector_potential_patch(AX, jx, jy, jz)) / + (Dim::is_invar(2) ? 0 + : vector_potential_patch(AX, jx, jy, jz + 1) - + vector_potential_patch(AX, jx, jy, jz)) / dx[2] - - (vector_potential_patch(AZ, jx + 1, jy, jz) - - vector_potential_patch(AZ, jx, jy, jz)) / + (Dim::is_invar(0) ? 0 + : vector_potential_patch(AZ, jx + 1, jy, jz) - + vector_potential_patch(AZ, jx, jy, jz)) / dx[0]; field_patch(HZ, jx, jy, jz) = - (vector_potential_patch(AY, jx + 1, jy, jz) - - vector_potential_patch(AY, jx, jy, jz)) / + (Dim::is_invar(0) ? 0 + : vector_potential_patch(AY, jx + 1, jy, jz) - + vector_potential_patch(AY, jx, jy, jz)) / dx[0] - - (vector_potential_patch(AX, jx, jy + 1, jz) - - vector_potential_patch(AX, jx, jy, jz)) / + (Dim::is_invar(1) ? 0 + : vector_potential_patch(AX, jx, jy + 1, jz) - + vector_potential_patch(AX, jx, jy, jz)) / dx[1]; }); } From f2edf2264ace4c917d8572e65c8682f2fe83d260 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 8 Jul 2026 11:02:47 -0400 Subject: [PATCH 043/106] psc_shock: flip yp to make xp,yp,k right-handed --- src/psc_shock.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 7d668deac..2a78981b2 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -465,7 +465,7 @@ void inject_plane_alfven_wave(PscConfig::Mfields& vector_potential, double db, } Double3 xp_hat{cos_theta * cos_phi, cos_theta * sin_phi, -sin_theta}; - Double3 yp_hat{sin_phi, -cos_phi, 0}; + Double3 yp_hat{-sin_phi, cos_phi, 0}; Double3 a_vec = db * cos(polarization) / k2 * xp_hat.cross(k_vec); Double3 b_vec = db * sin(polarization) / k2 * yp_hat.cross(k_vec); From 21f8dd06cd91100070022193bb74fc9697e2b974 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 8 Jul 2026 09:21:49 -0400 Subject: [PATCH 044/106] psc_shock: support dx etc --- src/psc_shock.cxx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 2a78981b2..28e408c8f 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -175,8 +175,21 @@ void setupParameters(int argc, char** argv) n_patches[1] = inputParams.get("npy"); n_patches[2] = inputParams.get("npz"); - lengths = {inputParams.get("lx"), inputParams.get("ly"), - inputParams.get("lz")}; + if (inputParams.has("lx")) { + lengths[0] = inputParams.get("lx"); + } else { + lengths[0] = inputParams.get("dx") * gdims[0]; + } + if (inputParams.has("ly")) { + lengths[1] = inputParams.get("ly"); + } else { + lengths[1] = inputParams.get("dy") * gdims[1]; + } + if (inputParams.has("lz")) { + lengths[2] = inputParams.get("lz"); + } else { + lengths[2] = inputParams.get("dz") * gdims[2]; + } turb_db2 = sqr(inputParams.get("dB")); turb_correlation_length = inputParams.get("L_c"); From 4254541a133400b1d66e6333b9a390bf2bacf656 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 27 May 2026 15:35:49 -0400 Subject: [PATCH 045/106] +field_bc_base --- src/libpsc/psc_bnd_fields/field_bc_base.hxx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/libpsc/psc_bnd_fields/field_bc_base.hxx diff --git a/src/libpsc/psc_bnd_fields/field_bc_base.hxx b/src/libpsc/psc_bnd_fields/field_bc_base.hxx new file mode 100644 index 000000000..8f5d2e7e9 --- /dev/null +++ b/src/libpsc/psc_bnd_fields/field_bc_base.hxx @@ -0,0 +1,11 @@ +#pragma once + +template +struct FieldBcBase +{ + virtual ~FieldBcBase() {} + + virtual void apply_j_bcs(MfieldsState& mflds) = 0; + virtual void apply_e_bcs(MfieldsState& mflds) = 0; + virtual void apply_h_bcs(MfieldsState& mflds) = 0; +}; From fe70e9654fa61694dc4b53ab964b0ad209b628fe Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 5 Jun 2026 12:02:35 -0400 Subject: [PATCH 046/106] psc: +field_bcs_ --- src/include/psc.hxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/include/psc.hxx b/src/include/psc.hxx index a6e7f243f..8796e5e71 100644 --- a/src/include/psc.hxx +++ b/src/include/psc.hxx @@ -7,6 +7,7 @@ #include #include +#include "../libpsc/psc_bnd_fields/field_bc_base.hxx" #include "gauss_corrector_base.hxx" #include "diagnostic_base.hxx" #include "injector_base.hxx" @@ -117,6 +118,7 @@ struct Psc using BndFields = typename PscConfig::BndFields; using BndParticles = typename PscConfig::BndParticles; using Dim = typename PscConfig::Dim; + using FieldBcBaseT = FieldBcBase; using GaussCorrectorBaseT = GaussCorrectorBase; using DiagnosticBaseT = DiagnosticBase; using InjectorBaseT = InjectorBase; @@ -162,6 +164,13 @@ struct Psc // TODO: improve ownership model: we should own these objects (i.e., use // unique_ptr), but don't want to burden the user with C++ boilerplate. + void add_field_bc(FieldBcBaseT* field_bc) + { + if (field_bc) { + field_bcs_.push_back(field_bc); + } + } + void add_gauss_corrector(GaussCorrectorBaseT* corrector) { if (corrector) { @@ -555,6 +564,7 @@ protected: Balance& balance_; Collision& collision_; Checks& checks_; + std::vector field_bcs_; std::vector gauss_correctors_; std::vector diagnostics_; std::vector injectors_; From b35432a85a5a8b584f31bc12db016c538d0ca71f Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 8 Jul 2026 14:53:25 -0400 Subject: [PATCH 047/106] +field_bc_util; psc_bnd_fields_impl: mv setters --- src/libpsc/psc_bnd_fields/field_bc_util.hxx | 136 +++++++++++++++++ .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 137 +++--------------- 2 files changed, 154 insertions(+), 119 deletions(-) create mode 100644 src/libpsc/psc_bnd_fields/field_bc_util.hxx diff --git a/src/libpsc/psc_bnd_fields/field_bc_util.hxx b/src/libpsc/psc_bnd_fields/field_bc_util.hxx new file mode 100644 index 000000000..c1701e260 --- /dev/null +++ b/src/libpsc/psc_bnd_fields/field_bc_util.hxx @@ -0,0 +1,136 @@ +#pragma once + +#include + +#include "kg/VecRange.hxx" + +namespace psc +{ +namespace bnd +{ +namespace field +{ +namespace detail +{ + +/** + * @brief Set E or B lower ghosts to the given constants (each component has + * its own constant). + * @param mflds mflds + * @param p patch index + * @param d which dimension to set the ghosts of + * @param mb `EX` or `HX`; note that `mb+1` and `mb+2` are also set + * @param val the constants + * @param include_edge whether or not values located on exact domain edges + * should be considered "ghosts" + */ +template +void set_lower_ghosts(MfieldsState& mflds, int p, int d, int mb, + typename MfieldsState::Real3 val, bool include_edge) +{ + auto F = make_Fields3d(mflds[p]); + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + stop[d] = 0; + + // TODO use gtensor views instead of VecRange + + for (int m = mb; m < mb + 3; m++) { + for (Int3 i3 : VecRange(start, stop)) { + F(m, i3) = val[m - mb]; + } + } + + if (!include_edge) { + return; + } + + Int3 edge_start = mflds.ib(); + Int3 edge_stop = mflds.ib() + mflds.im(); + edge_start[d] = 0; + edge_stop[d] = 1; + + for (int m = mb; m < mb + 3; m++) { + bool edge_ec = mb == EX && m - mb != d; + bool edge_fc = mb == HX && m - mb == d; + + if (edge_ec || edge_fc) { + for (Int3 i3 : VecRange(edge_start, edge_stop)) { + F(m, i3) = val[m - mb]; + } + } + } +} + +/** + * @brief Set E or B upper ghosts to the given constants (each component has + * its own constant). + * @param mflds mflds + * @param p patch index + * @param d which dimension to set the ghosts of + * @param mb `EX` or `HX`; note that `mb+1` and `mb+2` are also set + * @param val the constants + * @param include_edge whether or not values located on exact domain edges + * should be considered "ghosts" + */ +template +void set_upper_ghosts(MfieldsState& mflds, int p, int d, int mb, + typename MfieldsState::Real3 val, bool include_edge) +{ + auto F = make_Fields3d(mflds[p]); + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d] = mflds.grid().ldims[d] + 1; + + // TODO use gtensor views instead of VecRange + + for (int m = mb; m < mb + 3; m++) { + for (Int3 i3 : VecRange(start, stop)) { + F(m, i3) = val[m - mb]; + } + } + + Int3 edge_start = mflds.ib(); + Int3 edge_stop = mflds.ib() + mflds.im(); + edge_start[d] = mflds.grid().ldims[d]; + edge_stop[d] = mflds.grid().ldims[d] + 1; + + for (int m = mb; m < mb + 3; m++) { + bool not_edge_ec = mb == EX && m - mb == d; + bool not_edge_fc = mb == HX && m - mb != d; + + if (not_edge_ec || not_edge_fc || include_edge) + for (Int3 i3 : VecRange(edge_start, edge_stop)) { + F(m, i3) = val[m - mb]; + } + } +} + +template +void set_lower_ghosts_to_nan(MfieldsState& mflds, int p, int d, int mb, + bool include_edge) +{ +#ifndef DEBUG + return; +#endif + using real_t = typename MfieldsState::real_t; + real_t nan = std::numeric_limits::quiet_NaN(); + set_lower_ghosts(mflds, p, d, mb, {nan, nan, nan}, include_edge); +} + +template +void set_upper_ghosts_to_nan(MfieldsState& mflds, int p, int d, int mb, + bool include_edge) +{ +#ifndef DEBUG + return; +#endif + using real_t = typename MfieldsState::real_t; + real_t nan = std::numeric_limits::quiet_NaN(); + set_upper_ghosts(mflds, p, d, mb, {nan, nan, nan}, include_edge); +} + +} // namespace detail +} // namespace field +} // namespace bnd +} // namespace psc \ No newline at end of file diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 3bbdda209..a6e033ee0 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -1,9 +1,11 @@ +#pragma once #include "psc.h" #include "kg/VecRange.hxx" #include "fields.hxx" #include "bnd_fields.hxx" #include "radiating_bnd.hxx" +#include "field_bc_util.hxx" #include @@ -41,7 +43,8 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - set_lower_ghosts(mflds, p, d, EX, background_e_lo, false); + psc::bnd::field::detail::set_lower_ghosts( + mflds, p, d, EX, background_e_lo, false); break; } default: { @@ -63,7 +66,8 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - set_upper_ghosts(mflds, p, d, EX, background_e_hi, false); + psc::bnd::field::detail::set_upper_ghosts( + mflds, p, d, EX, background_e_hi, false); break; } default: { @@ -187,120 +191,10 @@ struct BndFields_ : BndFieldsBase } } - static void set_lower_ghosts_to_nan(MfieldsState& mflds, int p, int d, int mb, - bool include_edge) - { -#ifndef DEBUG - return; -#endif - real_t nan = std::numeric_limits::quiet_NaN(); - set_lower_ghosts(mflds, p, d, mb, {nan, nan, nan}, include_edge); - } - - static void set_upper_ghosts_to_nan(MfieldsState& mflds, int p, int d, int mb, - bool include_edge) - { -#ifndef DEBUG - return; -#endif - real_t nan = std::numeric_limits::quiet_NaN(); - set_upper_ghosts(mflds, p, d, mb, {nan, nan, nan}, include_edge); - } - - /** - * @brief Set E or B lower ghosts to the given constants (each component has - * its own constant). - * @param mflds mflds - * @param p patch index - * @param d which dimension to set the ghosts of - * @param mb `EX` or `HX`; note that `mb+1` and `mb+2` are also set - * @param val the constants - * @param include_edge whether or not values located on exact domain edges - * should be considered "ghosts" - */ - static void set_lower_ghosts(MfieldsState& mflds, int p, int d, int mb, - Real3 val, bool include_edge) - { - auto F = make_Fields3d(mflds[p]); - Int3 start = mflds.ib(); - Int3 stop = mflds.ib() + mflds.im(); - stop[d] = 0; - - // TODO use gtensor views instead of VecRange - - for (int m = mb; m < mb + 3; m++) { - for (Int3 i3 : VecRange(start, stop)) { - F(m, i3) = val[m - mb]; - } - } - - if (!include_edge) { - return; - } - - Int3 edge_start = mflds.ib(); - Int3 edge_stop = mflds.ib() + mflds.im(); - edge_start[d] = 0; - edge_stop[d] = 1; - - for (int m = mb; m < mb + 3; m++) { - bool edge_ec = mb == EX && m - mb != d; - bool edge_fc = mb == HX && m - mb == d; - - if (edge_ec || edge_fc) { - for (Int3 i3 : VecRange(edge_start, edge_stop)) { - F(m, i3) = val[m - mb]; - } - } - } - } - - /** - * @brief Set E or B upper ghosts to the given constants (each component has - * its own constant). - * @param mflds mflds - * @param p patch index - * @param d which dimension to set the ghosts of - * @param mb `EX` or `HX`; note that `mb+1` and `mb+2` are also set - * @param val the constants - * @param include_edge whether or not values located on exact domain edges - * should be considered "ghosts" - */ - static void set_upper_ghosts(MfieldsState& mflds, int p, int d, int mb, - Real3 val, bool include_edge) - { - auto F = make_Fields3d(mflds[p]); - Int3 start = mflds.ib(); - Int3 stop = mflds.ib() + mflds.im(); - start[d] = mflds.grid().ldims[d] + 1; - - // TODO use gtensor views instead of VecRange - - for (int m = mb; m < mb + 3; m++) { - for (Int3 i3 : VecRange(start, stop)) { - F(m, i3) = val[m - mb]; - } - } - - Int3 edge_start = mflds.ib(); - Int3 edge_stop = mflds.ib() + mflds.im(); - edge_start[d] = mflds.grid().ldims[d]; - edge_stop[d] = mflds.grid().ldims[d] + 1; - - for (int m = mb; m < mb + 3; m++) { - bool not_edge_ec = mb == EX && m - mb == d; - bool not_edge_fc = mb == HX && m - mb != d; - - if (not_edge_ec || not_edge_fc || include_edge) - for (Int3 i3 : VecRange(edge_start, edge_stop)) { - F(m, i3) = val[m - mb]; - } - } - } - void conducting_wall_E_lo(MfieldsState& mflds, int p, int d) { - set_lower_ghosts_to_nan(mflds, p, d, EX, true); + psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, EX, + true); auto F = make_Fields3d(mflds[p]); const int* ldims = mflds.grid().ldims; @@ -340,7 +234,8 @@ struct BndFields_ : BndFieldsBase void conducting_wall_E_hi(MfieldsState& mflds, int p, int d) { - set_upper_ghosts_to_nan(mflds, p, d, EX, true); + psc::bnd::field::detail::set_upper_ghosts_to_nan(mflds, p, d, EX, + true); auto F = make_Fields3d(mflds[p]); const int* ldims = mflds.grid().ldims; @@ -381,7 +276,8 @@ struct BndFields_ : BndFieldsBase void conducting_wall_H_lo(MfieldsState& mflds, int p, int d) { - set_lower_ghosts_to_nan(mflds, p, d, HX, false); + psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, + false); auto F = make_Fields3d(mflds[p]); const int* ldims = mflds.grid().ldims; @@ -416,7 +312,8 @@ struct BndFields_ : BndFieldsBase void conducting_wall_H_hi(MfieldsState& mflds, int p, int d) { - set_upper_ghosts_to_nan(mflds, p, d, HX, false); + psc::bnd::field::detail::set_upper_ghosts_to_nan(mflds, p, d, HX, + false); auto F = make_Fields3d(mflds[p]); @@ -534,7 +431,8 @@ struct BndFields_ : BndFieldsBase void radiative_H_lo(MfieldsState& mflds, int p, int d) { - set_lower_ghosts_to_nan(mflds, p, d, HX, false); + psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, + false); auto F = make_Fields3d(mflds[p]); const Grid_t& grid = mflds.grid(); @@ -584,7 +482,8 @@ struct BndFields_ : BndFieldsBase void radiative_H_hi(MfieldsState& mflds, int p, int d) { - set_upper_ghosts_to_nan(mflds, p, d, HX, false); + psc::bnd::field::detail::set_upper_ghosts_to_nan(mflds, p, d, HX, + false); auto F = make_Fields3d(mflds[p]); const Grid_t& grid = mflds.grid(); From 9a188c722544da44331cd99e4fcbb08f97375fde Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 8 Jul 2026 15:06:33 -0400 Subject: [PATCH 048/106] +psc_bnd_util --- src/libpsc/psc_bnd/psc_bnd_util.hxx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/libpsc/psc_bnd/psc_bnd_util.hxx diff --git a/src/libpsc/psc_bnd/psc_bnd_util.hxx b/src/libpsc/psc_bnd/psc_bnd_util.hxx new file mode 100644 index 000000000..67a4862c9 --- /dev/null +++ b/src/libpsc/psc_bnd/psc_bnd_util.hxx @@ -0,0 +1,15 @@ +#pragma once + +namespace psc +{ +namespace bnd +{ + +enum LoHi +{ + Lo, + Hi, +}; + +} // namespace bnd +} // namespace psc \ No newline at end of file From e882df336b8638790f22ebf7f6c765db8bf14e79 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 10:02:35 -0400 Subject: [PATCH 049/106] fields3d: using Real3 --- src/include/fields3d.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/include/fields3d.hxx b/src/include/fields3d.hxx index 69e3b7d15..c6cfb1ef4 100644 --- a/src/include/fields3d.hxx +++ b/src/include/fields3d.hxx @@ -417,6 +417,7 @@ struct MfieldsStateFromMfields : MfieldsStateBase { using fields_view_t = typename Mfields::fields_view_t; using real_t = typename Mfields::real_t; + using Real3 = Vec3; using Real = typename Mfields::Real; using Storage = typename Mfields::Storage; using space = gt::space::host; From 7b6c9332c4231ddc45825019fc9df2183a267ab6 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 10:04:02 -0400 Subject: [PATCH 050/106] psc: call field_bc --- src/include/psc.hxx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/include/psc.hxx b/src/include/psc.hxx index 8796e5e71..9ff0f4ebf 100644 --- a/src/include/psc.hxx +++ b/src/include/psc.hxx @@ -228,11 +228,17 @@ struct Psc void pre_first_step() { + for (auto field_bc : field_bcs_) { + field_bc->apply_h_bcs(mflds_); + } bndf.fill_ghosts_H(mflds_); bnd_.fill_ghosts(mflds_, HX, HX + 3); bnd_.fill_ghosts(mflds_, JXI, JXI + 3); + for (auto field_bc : field_bcs_) { + field_bc->apply_e_bcs(mflds_); + } bndf.fill_ghosts_E(mflds_); bnd_.fill_ghosts(mflds_, EX, EX + 3); @@ -423,6 +429,9 @@ struct Psc mpi_printf(comm, "***** Bnd fields J...\n"); prof_start(pr_bndf); + for (auto field_bc : field_bcs_) { + field_bc->apply_j_bcs(mflds_); + } bndf.add_ghosts_J(mflds_); bnd_.add_ghosts(mflds_, JXI, JXI + 3); bnd_.fill_ghosts(mflds_, JXI, JXI + 3); @@ -437,6 +446,9 @@ struct Psc mpi_printf(comm, "***** Bnd fields B (1 of 2)...\n"); prof_restart(pr_bndf); + for (auto field_bc : field_bcs_) { + field_bc->apply_h_bcs(mflds_); + } bndf.fill_ghosts_H(mflds_); bnd_.fill_ghosts(mflds_, HX, HX + 3); prof_stop(pr_bndf); @@ -450,6 +462,9 @@ struct Psc mpi_printf(comm, "***** Bnd fields E...\n"); prof_restart(pr_bndf); + for (auto field_bc : field_bcs_) { + field_bc->apply_e_bcs(mflds_); + } bndf.fill_ghosts_E(mflds_); bnd_.fill_ghosts(mflds_, EX, EX + 3); prof_stop(pr_bndf); @@ -472,6 +487,9 @@ struct Psc mpi_printf(comm, "***** Bnd fields B (2 of 2)...\n"); prof_restart(pr_bndf); + for (auto field_bc : field_bcs_) { + field_bc->apply_h_bcs(mflds_); + } bndf.fill_ghosts_H(mflds_); bnd_.fill_ghosts(mflds_, HX, HX + 3); prof_stop(pr_bndf); From f702e02fb1a3280c7c69dde860e95848cee11ee6 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 10:41:41 -0400 Subject: [PATCH 051/106] +axis --- src/libpsc/axis.hxx | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/libpsc/axis.hxx diff --git a/src/libpsc/axis.hxx b/src/libpsc/axis.hxx new file mode 100644 index 000000000..403b3ed74 --- /dev/null +++ b/src/libpsc/axis.hxx @@ -0,0 +1,36 @@ +#pragma once + +namespace psc +{ + +struct Axis +{ + static Axis X; + static Axis Y; + static Axis Z; + + Axis(int axis) : axis{axis} {} + + operator int() const { return axis; } + + Axis& operator++() + { + this->axis += 1; + return *this; + } + + Axis operator++(int) + { + Axis temp = *this; + ++(*this); + return temp; + } + + int axis; +}; + +Axis Axis::X = Axis(0); +Axis Axis::Y = Axis(1); +Axis Axis::Z = Axis(2); + +} // namespace psc \ No newline at end of file From 6361948a767d7b780aec3efbc0a3d189e4bfa6bb Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 10:18:55 -0400 Subject: [PATCH 052/106] +conducting_wall --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/libpsc/psc_bnd_fields/conducting_wall.hxx diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx new file mode 100644 index 000000000..ab57bf825 --- /dev/null +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -0,0 +1,44 @@ +#pragma once + +#include "psc.h" +#include "../axis.hxx" +#include "kg/Vec3.h" +#include "field_bc_base.hxx" +#include "../psc_bnd/psc_bnd_util.hxx" + +namespace psc +{ +namespace bnd +{ +namespace field +{ + +template +struct ConductingWall : FieldBcBase +{ + using dim_t = Dim; + + ConductingWall(Axis d, LoHi lohi) : d{d}, lohi{lohi} {} + + void apply_j_bcs(MfieldsState& mflds) override + { + // todo + } + + void apply_e_bcs(MfieldsState& mflds) override + { + // todo + } + + void apply_h_bcs(MfieldsState& mflds) override + { + // todo + } + + Axis d; + LoHi lohi; +}; + +} // namespace field +} // namespace bnd +} // namespace psc From cc281ede61a967179d9db9b5c3bd5091897f203c Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 12:39:58 -0400 Subject: [PATCH 053/106] psc: automatically add conducting wall bc --- src/include/psc.hxx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/include/psc.hxx b/src/include/psc.hxx index 9ff0f4ebf..e3650e204 100644 --- a/src/include/psc.hxx +++ b/src/include/psc.hxx @@ -8,6 +8,7 @@ #include #include "../libpsc/psc_bnd_fields/field_bc_base.hxx" +#include "../libpsc/psc_bnd_fields/conducting_wall.hxx" #include "gauss_corrector_base.hxx" #include "diagnostic_base.hxx" #include "injector_base.hxx" @@ -148,6 +149,18 @@ struct Psc } } + for (int d = 0; d < 3; d++) { + using psc::bnd::LoHi; + using psc::bnd::field::ConductingWall; + + if (grid.bc.fld_lo[d] == BND_FLD_CONDUCTING_WALL) { + add_field_bc(new ConductingWall{d, LoHi::Lo}); + } + if (grid.bc.fld_hi[d] == BND_FLD_CONDUCTING_WALL) { + add_field_bc(new ConductingWall{d, LoHi::Hi}); + } + } + int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); log_.open("mem-" + std::to_string(rank) + ".log"); From cff9b972af64364dfcdde600ca8f0547d7dfac64 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 12:40:57 -0400 Subject: [PATCH 054/106] conducting_wall, psc_bnd_fields_impl: mv E impl --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 84 +++++++++++++++++- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 85 ------------------- 2 files changed, 83 insertions(+), 86 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index ab57bf825..c8e0c8557 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -4,6 +4,7 @@ #include "../axis.hxx" #include "kg/Vec3.h" #include "field_bc_base.hxx" +#include "field_bc_util.hxx" #include "../psc_bnd/psc_bnd_util.hxx" namespace psc @@ -27,7 +28,88 @@ struct ConductingWall : FieldBcBase void apply_e_bcs(MfieldsState& mflds) override { - // todo + const auto& grid = mflds.grid(); + + for (int p = 0; p < mflds.n_patches(); p++) { + if (lohi == Lo && grid.atBoundaryLo(p, d)) { + detail::set_lower_ghosts_to_nan(mflds, p, d, EX, true); + + auto F = make_Fields3d(mflds[p]); + const int* ldims = mflds.grid().ldims; + Int3 ib = mflds.ib(), im = mflds.im(); + + if (d == Axis::Y) { + for (int iz = -2; iz < ldims[2] + 2; iz++) { + // FIXME, needs to be for other dir, too, and it's ugly + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(EX, ix, 0, iz) = 0.; + F(EX, ix, -1, iz) = F(EX, ix, 1, iz); + + F(EY, ix, -1, iz) = -F(EY, ix, 0, iz); + + F(EZ, ix, 0, iz) = 0.; + F(EZ, ix, -1, iz) = F(EZ, ix, 1, iz); + } + } + } else if (d == Axis::Z) { + for (int iy = -2; iy < ldims[1] + 2; iy++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(EX, ix, iy, 0) = 0.; + F(EX, ix, iy, -1) = F(EX, ix, iy, 1); + + F(EY, ix, iy, 0) = 0.; + F(EY, ix, iy, -1) = F(EY, ix, iy, 1); + + F(EZ, ix, iy, -1) = -F(EZ, ix, iy, 0); + } + } + } else { + assert(0); + } + } + + if (lohi == Hi && grid.atBoundaryHi(p, d)) { + detail::set_upper_ghosts_to_nan(mflds, p, d, EX, true); + + auto F = make_Fields3d(mflds[p]); + const int* ldims = mflds.grid().ldims; + Int3 ib = mflds.ib(), im = mflds.im(); + + if (d == Axis::Y) { + int my _mrc_unused = ldims[1]; + for (int iz = -2; iz < ldims[2] + 2; iz++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(EX, ix, my, iz) = 0.; + F(EX, ix, my + 1, iz) = F(EX, ix, my - 1, iz); + + F(EY, ix, my, iz) = -F(EY, ix, my - 1, iz); + + F(EZ, ix, my, iz) = 0.; + F(EZ, ix, my + 1, iz) = F(EZ, ix, my - 1, iz); + } + } + } else if (d == Axis::Z) { + int mz = ldims[2]; + for (int iy = -2; iy < ldims[1] + 2; iy++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(EX, ix, iy, mz) = 0.; + F(EX, ix, iy, mz + 1) = F(EX, ix, iy, mz - 1); + + F(EY, ix, iy, mz) = 0.; + F(EY, ix, iy, mz + 1) = F(EY, ix, iy, mz - 1); + + F(EZ, ix, iy, mz) = -F(EZ, ix, iy, mz - 1); + } + } + } else { + assert(0); + } + } + } } void apply_h_bcs(MfieldsState& mflds) override diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index a6e033ee0..4f5ed796f 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -39,7 +39,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_CONDUCTING_WALL: { - conducting_wall_E_lo(mflds, p, d); break; } case BND_FLD_OPEN: { @@ -62,7 +61,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_CONDUCTING_WALL: { - conducting_wall_E_hi(mflds, p, d); break; } case BND_FLD_OPEN: { @@ -191,89 +189,6 @@ struct BndFields_ : BndFieldsBase } } - void conducting_wall_E_lo(MfieldsState& mflds, int p, int d) - { - psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, EX, - true); - - auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - for (int iz = -2; iz < ldims[2] + 2; iz++) { - // FIXME, needs to be for other dir, too, and it's ugly - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, 0, iz) = 0.; - F(EX, ix, -1, iz) = F(EX, ix, 1, iz); - - F(EY, ix, -1, iz) = -F(EY, ix, 0, iz); - - F(EZ, ix, 0, iz) = 0.; - F(EZ, ix, -1, iz) = F(EZ, ix, 1, iz); - } - } - } else if (d == 2) { - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, iy, 0) = 0.; - F(EX, ix, iy, -1) = F(EX, ix, iy, 1); - - F(EY, ix, iy, 0) = 0.; - F(EY, ix, iy, -1) = F(EY, ix, iy, 1); - - F(EZ, ix, iy, -1) = -F(EZ, ix, iy, 0); - } - } - } else { - assert(0); - } - } - - void conducting_wall_E_hi(MfieldsState& mflds, int p, int d) - { - psc::bnd::field::detail::set_upper_ghosts_to_nan(mflds, p, d, EX, - true); - - auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - int my _mrc_unused = ldims[1]; - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, my, iz) = 0.; - F(EX, ix, my + 1, iz) = F(EX, ix, my - 1, iz); - - F(EY, ix, my, iz) = -F(EY, ix, my - 1, iz); - - F(EZ, ix, my, iz) = 0.; - F(EZ, ix, my + 1, iz) = F(EZ, ix, my - 1, iz); - } - } - } else if (d == 2) { - int mz = ldims[2]; - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, iy, mz) = 0.; - F(EX, ix, iy, mz + 1) = F(EX, ix, iy, mz - 1); - - F(EY, ix, iy, mz) = 0.; - F(EY, ix, iy, mz + 1) = F(EY, ix, iy, mz - 1); - - F(EZ, ix, iy, mz) = -F(EZ, ix, iy, mz - 1); - } - } - } else { - assert(0); - } - } - void conducting_wall_H_lo(MfieldsState& mflds, int p, int d) { psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, From a322babe7568a940a48fb67eb20ae4e287e5cb38 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 15:45:48 -0400 Subject: [PATCH 055/106] axis: +prev, next --- src/libpsc/axis.hxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libpsc/axis.hxx b/src/libpsc/axis.hxx index 403b3ed74..88200b643 100644 --- a/src/libpsc/axis.hxx +++ b/src/libpsc/axis.hxx @@ -26,6 +26,18 @@ struct Axis return temp; } + Axis next() + { + int next = (axis + 1) % 3; + return next; + } + + Axis prev() + { + int next = (axis + 2) % 3; + return next; + } + int axis; }; From d60732242091723861fa96aef96b968954ff9bcc Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 15:20:33 -0400 Subject: [PATCH 056/106] conducting_wall: generalize axes of E impl --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 105 ++++++++---------- 1 file changed, 44 insertions(+), 61 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index c8e0c8557..ea34cfe6c 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -3,6 +3,7 @@ #include "psc.h" #include "../axis.hxx" #include "kg/Vec3.h" +#include "kg/VecRange.hxx" #include "field_bc_base.hxx" #include "field_bc_util.hxx" #include "../psc_bnd/psc_bnd_util.hxx" @@ -35,38 +36,29 @@ struct ConductingWall : FieldBcBase detail::set_lower_ghosts_to_nan(mflds, p, d, EX, true); auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == Axis::Y) { - for (int iz = -2; iz < ldims[2] + 2; iz++) { - // FIXME, needs to be for other dir, too, and it's ugly - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, 0, iz) = 0.; - F(EX, ix, -1, iz) = F(EX, ix, 1, iz); - - F(EY, ix, -1, iz) = -F(EY, ix, 0, iz); - - F(EZ, ix, 0, iz) = 0.; - F(EZ, ix, -1, iz) = F(EZ, ix, 1, iz); - } + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d] = 0; + stop[d] = start[d] + 1; + + int E0 = EX + d; + int E1 = EX + d.next(); + int E2 = EX + d.prev(); + + for (Int3 i3 : VecRange(start, stop)) { + // 1. transverse components: wall is at relative index 0 + F(E1, i3) = 0.0; + F(E2, i3) = 0.0; + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(E1, i3 - j3) = F(E1, i3 + j3); + F(E2, i3 - j3) = F(E2, i3 + j3); } - } else if (d == Axis::Z) { - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, iy, 0) = 0.; - F(EX, ix, iy, -1) = F(EX, ix, iy, 1); - - F(EY, ix, iy, 0) = 0.; - F(EY, ix, iy, -1) = F(EY, ix, iy, 1); - - F(EZ, ix, iy, -1) = -F(EZ, ix, iy, 0); - } + + // 2. normal component: wall is at relative index -1/2 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(E0, i3 - j3) = -F(E0, i3 + j3 - Int3::unit(d)); } - } else { - assert(0); } } @@ -74,39 +66,30 @@ struct ConductingWall : FieldBcBase detail::set_upper_ghosts_to_nan(mflds, p, d, EX, true); auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == Axis::Y) { - int my _mrc_unused = ldims[1]; - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, my, iz) = 0.; - F(EX, ix, my + 1, iz) = F(EX, ix, my - 1, iz); - - F(EY, ix, my, iz) = -F(EY, ix, my - 1, iz); - - F(EZ, ix, my, iz) = 0.; - F(EZ, ix, my + 1, iz) = F(EZ, ix, my - 1, iz); - } + Int3 ldims = mflds.grid().ldims; + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d] = ldims[d]; + stop[d] = start[d] + 1; + + int E0 = EX + d; + int E1 = EX + d.next(); + int E2 = EX + d.prev(); + + for (Int3 i3 : VecRange(start, stop)) { + // 1. transverse components: wall is at relative index n=ldims[d] + F(E1, i3) = 0.0; + F(E2, i3) = 0.0; + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(E1, i3 + j3) = F(E1, i3 - j3); + F(E2, i3 + j3) = F(E2, i3 - j3); } - } else if (d == Axis::Z) { - int mz = ldims[2]; - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(EX, ix, iy, mz) = 0.; - F(EX, ix, iy, mz + 1) = F(EX, ix, iy, mz - 1); - - F(EY, ix, iy, mz) = 0.; - F(EY, ix, iy, mz + 1) = F(EY, ix, iy, mz - 1); - - F(EZ, ix, iy, mz) = -F(EZ, ix, iy, mz - 1); - } + + // 2. normal component: wall is at relative index n-1/2 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(E0, i3 + j3) = -F(E0, i3 - j3 + Int3::unit(d)); } - } else { - assert(0); } } } From dd10c976f1ea9b0f02372c9fa3c389efb9d23a29 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 15:28:04 -0400 Subject: [PATCH 057/106] conducting_wall: add some comments --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index ea34cfe6c..970fbd48e 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -15,6 +15,12 @@ namespace bnd namespace field { +/** + * @brief A perfect electrical conductor. This implementation assumes particles + * are specularly reflected. + * @tparam Dim dimension type + * @tparam MfieldsState fields type + */ template struct ConductingWall : FieldBcBase { @@ -27,6 +33,19 @@ struct ConductingWall : FieldBcBase // todo } + /** + * @brief Set transverse E at the wall's surface to 0. Nominally, E would be 0 + * at interior points too, but instead we mirror E so that reflecting + * particles feel the "right" electric force. + * + * Note that the normal force is reflected in the interior. To see why this is + * necessary, consider a particle with v=0 at the surface. If the normal E + * force on it is nonzero and towards the wall, the particle will + * spontaneously bounce away from the wall. It will then return to the + * wall—with more kinetic energy than before—and bounce again. Flipping normal + * E avoids this runaway effect. + * @param mflds fields + */ void apply_e_bcs(MfieldsState& mflds) override { const auto& grid = mflds.grid(); From 059523b703d180842d9873e6911adba24c44cc1d Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 16:08:37 -0400 Subject: [PATCH 058/106] conducting_wall: cleanup --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index 970fbd48e..03e2982cb 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -48,7 +48,7 @@ struct ConductingWall : FieldBcBase */ void apply_e_bcs(MfieldsState& mflds) override { - const auto& grid = mflds.grid(); + const Grid_t& grid = mflds.grid(); for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { @@ -85,11 +85,10 @@ struct ConductingWall : FieldBcBase detail::set_upper_ghosts_to_nan(mflds, p, d, EX, true); auto F = make_Fields3d(mflds[p]); - Int3 ldims = mflds.grid().ldims; Int3 start = mflds.ib(); Int3 stop = mflds.ib() + mflds.im(); - start[d] = ldims[d]; + start[d] = grid.ldims[d]; stop[d] = start[d] + 1; int E0 = EX + d; From c34687956644ac9206a792209367d1161c5ee4f7 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 16:15:58 -0400 Subject: [PATCH 059/106] conducting_wall; psc_bnd_fields_impl: mv H impl --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 75 +++++++++++++++++- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 77 ------------------- 2 files changed, 74 insertions(+), 78 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index 03e2982cb..ce31d9ad7 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -115,7 +115,80 @@ struct ConductingWall : FieldBcBase void apply_h_bcs(MfieldsState& mflds) override { - // todo + const Grid_t& grid = mflds.grid(); + + for (int p = 0; p < mflds.n_patches(); p++) { + if (lohi == Lo && grid.atBoundaryLo(p, d)) { + detail::set_lower_ghosts_to_nan(mflds, p, d, HX, false); + + auto F = make_Fields3d(mflds[p]); + const int* ldims = mflds.grid().ldims; + Int3 ib = mflds.ib(), im = mflds.im(); + + if (d == 1) { + for (int iz = -1; iz < ldims[2] + 2; iz++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(HX, ix, -1, iz) = -F(HX, ix, 0, iz); + + F(HY, ix, -1, iz) = F(HY, ix, 1, iz); + + F(HZ, ix, -1, iz) = -F(HZ, ix, 0, iz); + } + } + } else if (d == 2) { + for (int iy = -2; iy < ldims[1] + 2; iy++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(HX, ix, iy, -1) = -F(HX, ix, iy, 0); + + F(HY, ix, iy, -1) = -F(HY, ix, iy, 0); + + F(HZ, ix, iy, -1) = F(HZ, ix, iy, 1); + } + } + } else { + assert(0); + } + } + + if (lohi == Hi && grid.atBoundaryHi(p, d)) { + detail::set_upper_ghosts_to_nan(mflds, p, d, HX, false); + + auto F = make_Fields3d(mflds[p]); + + const int* ldims = mflds.grid().ldims; + Int3 ib = mflds.ib(), im = mflds.im(); + + if (d == 1) { + int my _mrc_unused = ldims[1]; + for (int iz = -2; iz < ldims[2] + 2; iz++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(HX, ix, my, iz) = -F(HX, ix, my - 1, iz); + + F(HY, ix, my + 1, iz) = F(HY, ix, my - 1, iz); + + F(HZ, ix, my, iz) = -F(HZ, ix, my - 1, iz); + } + } + } else if (d == 2) { + int mz = ldims[2]; + for (int iy = -2; iy < ldims[1] + 2; iy++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(HX, ix, iy, mz) = -F(HX, ix, iy, mz - 1); + + F(HY, ix, iy, mz) = -F(HY, ix, iy, mz - 1); + + F(HZ, ix, iy, mz + 1) = F(HZ, ix, iy, mz - 1); + } + } + } else { + assert(0); + } + } + } } Axis d; diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 4f5ed796f..0559074a7 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -97,7 +97,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_CONDUCTING_WALL: { - conducting_wall_H_lo(mflds, p, d); break; } case BND_FLD_OPEN: { @@ -122,7 +121,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_CONDUCTING_WALL: { - conducting_wall_H_hi(mflds, p, d); break; } case BND_FLD_OPEN: { @@ -189,81 +187,6 @@ struct BndFields_ : BndFieldsBase } } - void conducting_wall_H_lo(MfieldsState& mflds, int p, int d) - { - psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, - false); - - auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - for (int iz = -1; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, -1, iz) = -F(HX, ix, 0, iz); - - F(HY, ix, -1, iz) = F(HY, ix, 1, iz); - - F(HZ, ix, -1, iz) = -F(HZ, ix, 0, iz); - } - } - } else if (d == 2) { - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, iy, -1) = -F(HX, ix, iy, 0); - - F(HY, ix, iy, -1) = -F(HY, ix, iy, 0); - - F(HZ, ix, iy, -1) = F(HZ, ix, iy, 1); - } - } - } else { - assert(0); - } - } - - void conducting_wall_H_hi(MfieldsState& mflds, int p, int d) - { - psc::bnd::field::detail::set_upper_ghosts_to_nan(mflds, p, d, HX, - false); - - auto F = make_Fields3d(mflds[p]); - - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - int my _mrc_unused = ldims[1]; - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, my, iz) = -F(HX, ix, my - 1, iz); - - F(HY, ix, my + 1, iz) = F(HY, ix, my - 1, iz); - - F(HZ, ix, my, iz) = -F(HZ, ix, my - 1, iz); - } - } - } else if (d == 2) { - int mz = ldims[2]; - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, iy, mz) = -F(HX, ix, iy, mz - 1); - - F(HY, ix, iy, mz) = -F(HY, ix, iy, mz - 1); - - F(HZ, ix, iy, mz + 1) = F(HZ, ix, iy, mz - 1); - } - } - } else { - assert(0); - } - } - void conducting_wall_J_lo(MfieldsState& mflds, int p, int d) { auto F = make_Fields3d(mflds[p]); From ba8b7a3bf7bbc005ce5a148f6a3c3f0365f5f48e Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 16:30:08 -0400 Subject: [PATCH 060/106] conducting_wall: generalize H impl --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 75 ++++++++----------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index ce31d9ad7..9d7d5b995 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -122,33 +122,27 @@ struct ConductingWall : FieldBcBase detail::set_lower_ghosts_to_nan(mflds, p, d, HX, false); auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - if (d == 1) { - for (int iz = -1; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, -1, iz) = -F(HX, ix, 0, iz); + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d] = 0; + stop[d] = start[d] + 1; - F(HY, ix, -1, iz) = F(HY, ix, 1, iz); + int H0 = HX + d; + int H1 = HX + d.next(); + int H2 = HX + d.prev(); - F(HZ, ix, -1, iz) = -F(HZ, ix, 0, iz); - } + for (Int3 i3 : VecRange(start, stop)) { + // 1. transverse components: wall is at relative index -1/2 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(H1, i3 - j3) = -F(H1, i3 + j3 - Int3::unit(d)); + F(H2, i3 - j3) = -F(H2, i3 + j3 - Int3::unit(d)); } - } else if (d == 2) { - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, iy, -1) = -F(HX, ix, iy, 0); - - F(HY, ix, iy, -1) = -F(HY, ix, iy, 0); - F(HZ, ix, iy, -1) = F(HZ, ix, iy, 1); - } + // 2. normal component: wall is at relative index 0 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(H0, i3 - j3) = F(H0, i3 + j3); } - } else { - assert(0); } } @@ -157,35 +151,26 @@ struct ConductingWall : FieldBcBase auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - int my _mrc_unused = ldims[1]; - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, my, iz) = -F(HX, ix, my - 1, iz); + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d] = grid.ldims[d]; + stop[d] = start[d] + 1; - F(HY, ix, my + 1, iz) = F(HY, ix, my - 1, iz); + int H0 = HX + d; + int H1 = HX + d.next(); + int H2 = HX + d.prev(); - F(HZ, ix, my, iz) = -F(HZ, ix, my - 1, iz); - } + for (Int3 i3 : VecRange(start, stop)) { + // 1. transverse components: wall is at relative index n-1/2 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(H1, i3 + j3) = -F(H1, i3 - j3 + Int3::unit(d)); + F(H2, i3 + j3) = -F(H2, i3 - j3 + Int3::unit(d)); } - } else if (d == 2) { - int mz = ldims[2]; - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(HX, ix, iy, mz) = -F(HX, ix, iy, mz - 1); - F(HY, ix, iy, mz) = -F(HY, ix, iy, mz - 1); - - F(HZ, ix, iy, mz + 1) = F(HZ, ix, iy, mz - 1); - } + // 2. normal component: wall is at relative index n=ldims[d] + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(H0, i3 + j3) = F(H0, i3 - j3); } - } else { - assert(0); } } } From c8359f33330be5d9106a765398e0704bfcbf50bd Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 9 Jul 2026 16:36:45 -0400 Subject: [PATCH 061/106] conducting_wall: +H comment --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index 9d7d5b995..893f2d4d8 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -113,6 +113,16 @@ struct ConductingWall : FieldBcBase } } + /** + * @brief Normal H at the wall's surface is a no-op, but set interior H such + * that reflecting particles feel the "right" magnetic force. + * + * Transverse H is flipped to ensure that a particle at the wall's surface + * with nominally-nonzero normal velocity—and thus, actually zero average + * normal velocity, since half of the cloud is reflected—experiences no + * magnetic force. + * @param mflds fields + */ void apply_h_bcs(MfieldsState& mflds) override { const Grid_t& grid = mflds.grid(); From 8698b918c6426fd6168fa76672051a7cfe2d343a Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 08:21:23 -0400 Subject: [PATCH 062/106] conducting_wall; psc_bnd_fields_impl: mv j impl --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 82 ++++++++++++++++++- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 82 ------------------- 2 files changed, 81 insertions(+), 83 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index 893f2d4d8..1ff46738f 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -30,7 +30,87 @@ struct ConductingWall : FieldBcBase void apply_j_bcs(MfieldsState& mflds) override { - // todo + const Grid_t& grid = mflds.grid(); + + for (int p = 0; p < mflds.n_patches(); p++) { + if (lohi == Lo && grid.atBoundaryLo(p, d)) { + auto F = make_Fields3d(mflds[p]); + const int* ldims = mflds.grid().ldims; + Int3 ib = mflds.ib(), im = mflds.im(); + + if (d == 1) { + for (int iz = -2; iz < ldims[2] + 2; iz++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(JXI, ix, 1, iz) += F(JXI, ix, -1, iz); + F(JXI, ix, -1, iz) = 0.; + + F(JYI, ix, 0, iz) -= F(JYI, ix, -1, iz); + F(JYI, ix, -1, iz) = 0.; + + F(JZI, ix, 1, iz) += F(JZI, ix, -1, iz); + F(JZI, ix, -1, iz) = 0.; + } + } + } else if (d == 2) { + for (int iy = -2; iy < ldims[1] + 2; iy++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(JXI, ix, iy, 1) += F(JXI, ix, iy, -1); + F(JXI, ix, iy, -1) = 0.; + + F(JYI, ix, iy, 1) += F(JYI, ix, iy, -1); + F(JYI, ix, iy, -1) = 0.; + + F(JZI, ix, iy, 0) -= F(JZI, ix, iy, -1); + F(JZI, ix, iy, -1) = 0.; + } + } + } else { + assert(0); + } + } + + if (lohi == Hi && grid.atBoundaryHi(p, d)) { + auto F = make_Fields3d(mflds[p]); + const int* ldims = mflds.grid().ldims; + Int3 ib = mflds.ib(), im = mflds.im(); + + if (d == 1) { + int my _mrc_unused = ldims[1]; + for (int iz = -2; iz < ldims[2] + 2; iz++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(JXI, ix, my - 1, iz) += F(JXI, ix, my + 1, iz); + F(JXI, ix, my + 1, iz) = 0.; + + F(JYI, ix, my - 1, iz) -= F(JYI, ix, my, iz); + F(JYI, ix, my, iz) = 0.; + + F(JZI, ix, my - 1, iz) += F(JZI, ix, my + 1, iz); + F(JZI, ix, my + 1, iz) = 0.; + } + } + } else if (d == 2) { + int mz = ldims[2]; + for (int iy = -2; iy < ldims[1] + 2; iy++) { + for (int ix = std::max(-2, ib[0]); + ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { + F(JXI, ix, iy, mz - 1) += F(JXI, ix, iy, mz + 1); + F(JXI, ix, iy, mz + 1) = 0.; + + F(JYI, ix, iy, mz - 1) += F(JYI, ix, iy, mz + 1); + F(JYI, ix, iy, mz + 1) = 0.; + + F(JZI, ix, iy, mz - 1) -= F(JZI, ix, iy, mz); + F(JZI, ix, iy, mz) = 0.; + } + } + } else { + assert(0); + } + } + } } /** diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 0559074a7..cb46c396f 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -152,7 +152,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_CONDUCTING_WALL: { - conducting_wall_J_lo(mflds, p, d); break; } case BND_FLD_OPEN: { @@ -172,7 +171,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_CONDUCTING_WALL: { - conducting_wall_J_hi(mflds, p, d); break; } case BND_FLD_OPEN: { @@ -187,86 +185,6 @@ struct BndFields_ : BndFieldsBase } } - void conducting_wall_J_lo(MfieldsState& mflds, int p, int d) - { - auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, 1, iz) += F(JXI, ix, -1, iz); - F(JXI, ix, -1, iz) = 0.; - - F(JYI, ix, 0, iz) -= F(JYI, ix, -1, iz); - F(JYI, ix, -1, iz) = 0.; - - F(JZI, ix, 1, iz) += F(JZI, ix, -1, iz); - F(JZI, ix, -1, iz) = 0.; - } - } - } else if (d == 2) { - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, iy, 1) += F(JXI, ix, iy, -1); - F(JXI, ix, iy, -1) = 0.; - - F(JYI, ix, iy, 1) += F(JYI, ix, iy, -1); - F(JYI, ix, iy, -1) = 0.; - - F(JZI, ix, iy, 0) -= F(JZI, ix, iy, -1); - F(JZI, ix, iy, -1) = 0.; - } - } - } else { - assert(0); - } - } - - void conducting_wall_J_hi(MfieldsState& mflds, int p, int d) - { - auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - int my _mrc_unused = ldims[1]; - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, my - 1, iz) += F(JXI, ix, my + 1, iz); - F(JXI, ix, my + 1, iz) = 0.; - - F(JYI, ix, my - 1, iz) -= F(JYI, ix, my, iz); - F(JYI, ix, my, iz) = 0.; - - F(JZI, ix, my - 1, iz) += F(JZI, ix, my + 1, iz); - F(JZI, ix, my + 1, iz) = 0.; - } - } - } else if (d == 2) { - int mz = ldims[2]; - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, iy, mz - 1) += F(JXI, ix, iy, mz + 1); - F(JXI, ix, iy, mz + 1) = 0.; - - F(JYI, ix, iy, mz - 1) += F(JYI, ix, iy, mz + 1); - F(JYI, ix, iy, mz + 1) = 0.; - - F(JZI, ix, iy, mz - 1) -= F(JZI, ix, iy, mz); - F(JZI, ix, iy, mz) = 0.; - } - } - } else { - assert(0); - } - } - void radiative_H_lo(MfieldsState& mflds, int p, int d) { psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, From 90c7f7405b6acb7ab148875ca3778959aaa87cd7 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 08:31:10 -0400 Subject: [PATCH 063/106] conducting_wall: generalize j impl --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 108 +++++++----------- 1 file changed, 44 insertions(+), 64 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index 1ff46738f..843e9c8e8 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -35,79 +35,59 @@ struct ConductingWall : FieldBcBase for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, 1, iz) += F(JXI, ix, -1, iz); - F(JXI, ix, -1, iz) = 0.; - - F(JYI, ix, 0, iz) -= F(JYI, ix, -1, iz); - F(JYI, ix, -1, iz) = 0.; - - F(JZI, ix, 1, iz) += F(JZI, ix, -1, iz); - F(JZI, ix, -1, iz) = 0.; - } + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d] = 0; + stop[d] = start[d] + 1; + + int J0 = JXI + d; + int J1 = JXI + d.next(); + int J2 = JXI + d.prev(); + + for (Int3 i3 : VecRange(start, stop)) { + // 1. transverse components: wall is at relative index 0 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(J1, i3 + j3) += F(J1, i3 - j3); + F(J1, i3 - j3) = 0.0; + F(J2, i3 + j3) += F(J2, i3 - j3); + F(J2, i3 - j3) = 0.0; } - } else if (d == 2) { - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, iy, 1) += F(JXI, ix, iy, -1); - F(JXI, ix, iy, -1) = 0.; - - F(JYI, ix, iy, 1) += F(JYI, ix, iy, -1); - F(JYI, ix, iy, -1) = 0.; - - F(JZI, ix, iy, 0) -= F(JZI, ix, iy, -1); - F(JZI, ix, iy, -1) = 0.; - } + + // 2. normal component: wall is at relative index -1/2 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(J0, i3 + j3 - Int3::unit(d)) -= F(J0, i3 - j3); + F(J0, i3 - j3) = 0.0; } - } else { - assert(0); } } if (lohi == Hi && grid.atBoundaryHi(p, d)) { auto F = make_Fields3d(mflds[p]); - const int* ldims = mflds.grid().ldims; - Int3 ib = mflds.ib(), im = mflds.im(); - - if (d == 1) { - int my _mrc_unused = ldims[1]; - for (int iz = -2; iz < ldims[2] + 2; iz++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, my - 1, iz) += F(JXI, ix, my + 1, iz); - F(JXI, ix, my + 1, iz) = 0.; - - F(JYI, ix, my - 1, iz) -= F(JYI, ix, my, iz); - F(JYI, ix, my, iz) = 0.; - - F(JZI, ix, my - 1, iz) += F(JZI, ix, my + 1, iz); - F(JZI, ix, my + 1, iz) = 0.; - } + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d] = grid.ldims[d]; + stop[d] = start[d] + 1; + + int J0 = JXI + d; + int J1 = JXI + d.next(); + int J2 = JXI + d.prev(); + + for (Int3 i3 : VecRange(start, stop)) { + // 1. transverse components: wall is at relative index n=ldims[d] + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(J1, i3 - j3) += F(J1, i3 + j3); + F(J1, i3 + j3) = 0.0; + F(J2, i3 - j3) += F(J2, i3 + j3); + F(J2, i3 + j3) = 0.0; } - } else if (d == 2) { - int mz = ldims[2]; - for (int iy = -2; iy < ldims[1] + 2; iy++) { - for (int ix = std::max(-2, ib[0]); - ix < std::min(ldims[0] + 2, ib[0] + im[0]); ix++) { - F(JXI, ix, iy, mz - 1) += F(JXI, ix, iy, mz + 1); - F(JXI, ix, iy, mz + 1) = 0.; - - F(JYI, ix, iy, mz - 1) += F(JYI, ix, iy, mz + 1); - F(JYI, ix, iy, mz + 1) = 0.; - - F(JZI, ix, iy, mz - 1) -= F(JZI, ix, iy, mz); - F(JZI, ix, iy, mz) = 0.; - } + + // 2. normal component: wall is at relative index n-1/2 + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(J0, i3 - j3) -= F(J0, i3 + j3 - Int3::unit(d)); + F(J0, i3 + j3 - Int3::unit(d)) = 0.0; } - } else { - assert(0); } } } From 3cfedb7ed2a0422c7e68a0990d99a87966d45864 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 08:32:40 -0400 Subject: [PATCH 064/106] conducting_wall: +j comment --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index 843e9c8e8..deb200461 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -28,6 +28,11 @@ struct ConductingWall : FieldBcBase ConductingWall(Axis d, LoHi lohi) : d{d}, lohi{lohi} {} + /** + * @brief Move currents deposited within the wall onto the domain. The normal + * component is flipped in accordance with specular reflection. + * @param mflds fields + */ void apply_j_bcs(MfieldsState& mflds) override { const Grid_t& grid = mflds.grid(); From ff677be5b95fa717867f0f10db1868866e0120e0 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 09:04:48 -0400 Subject: [PATCH 065/106] conducting_wall: fix n ghosts for hi --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index deb200461..8a62ec5e4 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -80,8 +80,9 @@ struct ConductingWall : FieldBcBase int J2 = JXI + d.prev(); for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index n=ldims[d] - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + // 1. transverse components: wall is at relative index n=ldims[d], + // and there's one less ghost + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { F(J1, i3 - j3) += F(J1, i3 + j3); F(J1, i3 + j3) = 0.0; F(J2, i3 - j3) += F(J2, i3 + j3); @@ -161,10 +162,11 @@ struct ConductingWall : FieldBcBase int E2 = EX + d.prev(); for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index n=ldims[d] + // 1. transverse components: wall is at relative index n=ldims[d], + // and there's one less ghost F(E1, i3) = 0.0; F(E2, i3) = 0.0; - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { F(E1, i3 + j3) = F(E1, i3 - j3); F(E2, i3 + j3) = F(E2, i3 - j3); } @@ -242,8 +244,9 @@ struct ConductingWall : FieldBcBase F(H2, i3 + j3) = -F(H2, i3 - j3 + Int3::unit(d)); } - // 2. normal component: wall is at relative index n=ldims[d] - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + // 2. normal component: wall is at relative index n=ldims[d], + // and there's one less ghost + for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { F(H0, i3 + j3) = F(H0, i3 - j3); } } From d1c984b306bbacfa4efc321ad244c37c2da0c7da Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 09:21:04 -0400 Subject: [PATCH 066/106] conducting_wall: cleanup --- src/libpsc/psc_bnd_fields/conducting_wall.hxx | 111 ++++++++---------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/conducting_wall.hxx b/src/libpsc/psc_bnd_fields/conducting_wall.hxx index 8a62ec5e4..46444a89e 100644 --- a/src/libpsc/psc_bnd_fields/conducting_wall.hxx +++ b/src/libpsc/psc_bnd_fields/conducting_wall.hxx @@ -37,6 +37,12 @@ struct ConductingWall : FieldBcBase { const Grid_t& grid = mflds.grid(); + const Int3 dhat = Int3::unit(d); + + const int J0 = JXI + d; + const int J1 = JXI + d.next(); + const int J2 = JXI + d.prev(); + for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { auto F = make_Fields3d(mflds[p]); @@ -46,22 +52,18 @@ struct ConductingWall : FieldBcBase start[d] = 0; stop[d] = start[d] + 1; - int J0 = JXI + d; - int J1 = JXI + d.next(); - int J2 = JXI + d.prev(); - for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index 0 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + // 1. transverse components: wall is at i3 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { F(J1, i3 + j3) += F(J1, i3 - j3); F(J1, i3 - j3) = 0.0; F(J2, i3 + j3) += F(J2, i3 - j3); F(J2, i3 - j3) = 0.0; } - // 2. normal component: wall is at relative index -1/2 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { - F(J0, i3 + j3 - Int3::unit(d)) -= F(J0, i3 - j3); + // 2. normal component: wall is at i3-dhat/2 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(J0, i3 + j3 - dhat) -= F(J0, i3 - j3); F(J0, i3 - j3) = 0.0; } } @@ -75,24 +77,19 @@ struct ConductingWall : FieldBcBase start[d] = grid.ldims[d]; stop[d] = start[d] + 1; - int J0 = JXI + d; - int J1 = JXI + d.next(); - int J2 = JXI + d.prev(); - for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index n=ldims[d], - // and there's one less ghost - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { + // 1. transverse components: wall is at i3, and there's one less ghost + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { F(J1, i3 - j3) += F(J1, i3 + j3); F(J1, i3 + j3) = 0.0; F(J2, i3 - j3) += F(J2, i3 + j3); F(J2, i3 + j3) = 0.0; } - // 2. normal component: wall is at relative index n-1/2 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { - F(J0, i3 - j3) -= F(J0, i3 + j3 - Int3::unit(d)); - F(J0, i3 + j3 - Int3::unit(d)) = 0.0; + // 2. normal component: wall is at i3-dhat/2 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(J0, i3 - j3) -= F(J0, i3 + j3 - dhat); + F(J0, i3 + j3 - dhat) = 0.0; } } } @@ -116,6 +113,12 @@ struct ConductingWall : FieldBcBase { const Grid_t& grid = mflds.grid(); + const Int3 dhat = Int3::unit(d); + + const int E0 = EX + d; + const int E1 = EX + d.next(); + const int E2 = EX + d.prev(); + for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { detail::set_lower_ghosts_to_nan(mflds, p, d, EX, true); @@ -127,22 +130,18 @@ struct ConductingWall : FieldBcBase start[d] = 0; stop[d] = start[d] + 1; - int E0 = EX + d; - int E1 = EX + d.next(); - int E2 = EX + d.prev(); - for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index 0 + // 1. transverse components: wall is at i3 F(E1, i3) = 0.0; F(E2, i3) = 0.0; - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { F(E1, i3 - j3) = F(E1, i3 + j3); F(E2, i3 - j3) = F(E2, i3 + j3); } - // 2. normal component: wall is at relative index -1/2 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { - F(E0, i3 - j3) = -F(E0, i3 + j3 - Int3::unit(d)); + // 2. normal component: wall is at i3-dhat/2 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(E0, i3 - j3) = -F(E0, i3 + j3 - dhat); } } } @@ -157,23 +156,18 @@ struct ConductingWall : FieldBcBase start[d] = grid.ldims[d]; stop[d] = start[d] + 1; - int E0 = EX + d; - int E1 = EX + d.next(); - int E2 = EX + d.prev(); - for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index n=ldims[d], - // and there's one less ghost + // 1. transverse components: wall is at i3, and there's one less ghost F(E1, i3) = 0.0; F(E2, i3) = 0.0; - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { F(E1, i3 + j3) = F(E1, i3 - j3); F(E2, i3 + j3) = F(E2, i3 - j3); } - // 2. normal component: wall is at relative index n-1/2 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { - F(E0, i3 + j3) = -F(E0, i3 - j3 + Int3::unit(d)); + // 2. normal component: wall is at i3-dhat/2 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(E0, i3 + j3) = -F(E0, i3 - j3 + dhat); } } } @@ -194,6 +188,12 @@ struct ConductingWall : FieldBcBase { const Grid_t& grid = mflds.grid(); + const Int3 dhat = Int3::unit(d); + + const int H0 = HX + d; + const int H1 = HX + d.next(); + const int H2 = HX + d.prev(); + for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { detail::set_lower_ghosts_to_nan(mflds, p, d, HX, false); @@ -205,19 +205,15 @@ struct ConductingWall : FieldBcBase start[d] = 0; stop[d] = start[d] + 1; - int H0 = HX + d; - int H1 = HX + d.next(); - int H2 = HX + d.prev(); - for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index -1/2 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { - F(H1, i3 - j3) = -F(H1, i3 + j3 - Int3::unit(d)); - F(H2, i3 - j3) = -F(H2, i3 + j3 - Int3::unit(d)); + // 1. transverse components: wall is at i3-dhat/2 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(H1, i3 - j3) = -F(H1, i3 + j3 - dhat); + F(H2, i3 - j3) = -F(H2, i3 + j3 - dhat); } - // 2. normal component: wall is at relative index 0 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { + // 2. normal component: wall is at i3 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { F(H0, i3 - j3) = F(H0, i3 + j3); } } @@ -233,20 +229,15 @@ struct ConductingWall : FieldBcBase start[d] = grid.ldims[d]; stop[d] = start[d] + 1; - int H0 = HX + d; - int H1 = HX + d.next(); - int H2 = HX + d.prev(); - for (Int3 i3 : VecRange(start, stop)) { - // 1. transverse components: wall is at relative index n-1/2 - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d]; j3[d]++) { - F(H1, i3 + j3) = -F(H1, i3 - j3 + Int3::unit(d)); - F(H2, i3 + j3) = -F(H2, i3 - j3 + Int3::unit(d)); + // 1. transverse components: wall is at i3-dhat/2 + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d]; j3[d]++) { + F(H1, i3 + j3) = -F(H1, i3 - j3 + dhat); + F(H2, i3 + j3) = -F(H2, i3 - j3 + dhat); } - // 2. normal component: wall is at relative index n=ldims[d], - // and there's one less ghost - for (Int3 j3 = Int3::unit(d); j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { + // 2. normal component: wall is at i3, and there's one less ghost + for (Int3 j3 = dhat; j3[d] <= mflds.ibn()[d] - 1; j3[d]++) { F(H0, i3 + j3) = F(H0, i3 - j3); } } From cec3985df56a6c44b5de3be829d95f3435002b32 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 09:45:20 -0400 Subject: [PATCH 067/106] psc_bnd_fields_impl: add_ghosts_j is noop --- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 46 +------------------ 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index cb46c396f..18c04a09d 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -139,51 +139,7 @@ struct BndFields_ : BndFieldsBase // ---------------------------------------------------------------------- // add_ghosts_J - void add_ghosts_J(MfieldsState& mflds) - { - const auto& grid = mflds.grid(); - - for (int p = 0; p < mflds.n_patches(); p++) { - // lo - for (int d = 0; d < 3; d++) { - if (grid.atBoundaryLo(p, d)) { - switch (grid.bc.fld_lo[d]) { - case BND_FLD_PERIODIC: { - break; - } - case BND_FLD_CONDUCTING_WALL: { - break; - } - case BND_FLD_OPEN: { - break; - } - default: { - assert(0); - } - } - } - } - // hi - for (int d = 0; d < 3; d++) { - if (grid.atBoundaryHi(p, d)) { - switch (grid.bc.fld_hi[d]) { - case BND_FLD_PERIODIC: { - break; - } - case BND_FLD_CONDUCTING_WALL: { - break; - } - case BND_FLD_OPEN: { - break; - } - default: { - assert(0); - } - } - } - } - } - } + void add_ghosts_J(MfieldsState& mflds) {} void radiative_H_lo(MfieldsState& mflds, int p, int d) { From 098e51166754352556b23caed5f271df12090df3 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 11:03:50 -0400 Subject: [PATCH 068/106] psc_shock: half width is ly/8 --- src/psc_shock.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 28e408c8f..aa677cfe2 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -59,7 +59,7 @@ double te_downstream; double ti_downstream; Real3 h0_downstream; -double transition_half_width = 1.0; +double transition_half_width; double transition_steepness = 2.0; // at least sqrt(3)~1.74 // ---------------------------- @@ -191,6 +191,8 @@ void setupParameters(int argc, char** argv) lengths[2] = inputParams.get("dz") * gdims[2]; } + transition_half_width = lengths[1] / 8.0; + turb_db2 = sqr(inputParams.get("dB")); turb_correlation_length = inputParams.get("L_c"); From a3bb77d80c1b577a5f40f97f5af0d6360925a9c5 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 11:05:22 -0400 Subject: [PATCH 069/106] psc_shock: don't compress B_par --- src/psc_shock.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index aa677cfe2..2f5a2288e 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -360,6 +360,7 @@ void add_background_fields(MfieldsState& mflds) } else if (shock_method == "relaxation") { Double3 pos = centering::get_pos(patch, {jx, jy, jz}, centering::NC, 0); h0 = interpolate_across_shock(h0_upstream, h0_downstream, pos[1]); + h0[1] = h0_upstream[1]; // parallel B isn't compressed } field_patch(HX, jx, jy, jz) += h0[0]; From daf06b10b9b8b30efdd4e78242d9f26b023bdb10 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 11:14:45 -0400 Subject: [PATCH 070/106] psc_shock: interpolate v_thermal, not T --- src/psc_shock.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 2f5a2288e..cb520903e 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -328,9 +328,10 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts) auto init_np = [&](int kind, Double3 pos, int p, Int3 idx, psc_particle_np& np) { np.n = interpolate_across_shock(n_upstream, n_downstream, pos[1]); - double t = interpolate_across_shock( - np.kind == KIND_ION ? ti_upstream : te_upstream, - np.kind == KIND_ION ? ti_downstream : te_downstream, pos[1]); + // interpolate v_thermal, not T itself + double t = sqr(interpolate_across_shock( + sqrt(np.kind == KIND_ION ? ti_upstream : te_upstream), + sqrt(np.kind == KIND_ION ? ti_downstream : te_downstream), pos[1])); Double3 v = interpolate_across_shock(v_upstream, v_downstream, pos[1]); np.p = setup_particles.createMaxwellian({np.kind, np.n, v, {t, t, t}, np.tag}); From f5f7aa397cb4c4193595e7345593a30e28625bb6 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 09:55:54 -0400 Subject: [PATCH 071/106] +radiating --- src/libpsc/psc_bnd_fields/radiating.hxx | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/libpsc/psc_bnd_fields/radiating.hxx diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx new file mode 100644 index 000000000..8160ad573 --- /dev/null +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -0,0 +1,41 @@ +#pragma once + +#include "psc.h" +#include "../axis.hxx" +#include "kg/Vec3.h" +#include "kg/VecRange.hxx" +#include "field_bc_base.hxx" +#include "field_bc_util.hxx" +#include "../psc_bnd/psc_bnd_util.hxx" + +namespace psc +{ +namespace bnd +{ +namespace field +{ + +template +struct Radiating : FieldBcBase +{ + using dim_t = Dim; + using Pulse = P; + + Radiating(Pulse pulse, Axis d, LoHi lohi) : pulse{pulse}, d{d}, lohi{lohi} {} + + void apply_j_bcs(MfieldsState& mflds) override {} + + void apply_e_bcs(MfieldsState& mflds) override {} + + void apply_h_bcs(MfieldsState& mflds) override {} + + Axis d; + LoHi lohi; + +private: + Pulse pulse; +}; + +} // namespace field +} // namespace bnd +} // namespace psc From a1d92bea60d840bfbf7e9b14b828e0fb82159b7d Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 12:17:07 -0400 Subject: [PATCH 072/106] radiating_bnd; *: ->tick --- src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx | 12 ++++-------- src/libpsc/psc_bnd_fields/radiating_bnd.hxx | 4 +--- src/psc_shock.cxx | 6 +----- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 18c04a09d..3e91105db 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -84,13 +84,13 @@ struct BndFields_ : BndFieldsBase { const auto& grid = mflds.grid(); + if (radiation) { + radiation->tick(grid.time()); + } + for (int p = 0; p < mflds.n_patches(); p++) { // lo for (int d = 0; d < 3; d++) { - if (grid.bc.fld_lo[d] == BND_FLD_OPEN && radiation) { - radiation->update_cache_lower(grid.time(), d); - } - if (grid.atBoundaryLo(p, d)) { switch (grid.bc.fld_lo[d]) { case BND_FLD_PERIODIC: { @@ -111,10 +111,6 @@ struct BndFields_ : BndFieldsBase } // hi for (int d = 0; d < 3; d++) { - if (grid.bc.fld_hi[d] == BND_FLD_OPEN && radiation) { - radiation->update_cache_upper(grid.time(), d); - } - if (grid.atBoundaryHi(p, d)) { switch (grid.bc.fld_hi[d]) { case BND_FLD_PERIODIC: { diff --git a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx index c6a40de9f..f5e0ed548 100644 --- a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx +++ b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx @@ -11,7 +11,5 @@ struct RadiatingBoundary virtual real_t pulse_s_upper(double t, int d, int p, Real3 x3) = 0; virtual real_t pulse_p_upper(double t, int d, int p, Real3 x3) = 0; - // FIXME these are a hack for a specific subclass to work - virtual void update_cache_lower(double t, int d) {} - virtual void update_cache_upper(double t, int d) {} + virtual void tick(double t) {} }; \ No newline at end of file diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index cb520903e..37bd8eacb 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -850,12 +850,8 @@ struct AdvectedPeriodicFields : RadiatingBoundary return 0.0; } - void update_cache_lower(double t, int d) override + void tick(double t) override { - if (d != DIM_Y) { - return; - } - // TODO make this work with >1 patch per process? assert(grid.n_patches() == 1); From 9169349700d4d63b55ffd092c63b576a08f49c74 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 12:39:24 -0400 Subject: [PATCH 073/106] radiating_bnd; psc_shock: +sample_exterior_field_\* --- src/libpsc/psc_bnd_fields/radiating_bnd.hxx | 3 ++ src/psc_shock.cxx | 41 +++++++++++---------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx index f5e0ed548..98eb24e2c 100644 --- a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx +++ b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx @@ -5,6 +5,9 @@ struct RadiatingBoundary { using Real3 = Vec3; + virtual real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) = 0; + virtual real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) = 0; + virtual real_t pulse_s_lower(double t, int d, int p, Real3 x3) = 0; virtual real_t pulse_p_lower(double t, int d, int p, Real3 x3) = 0; diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 37bd8eacb..8736f5ca9 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -792,8 +792,7 @@ struct AdvectedPeriodicFields : RadiatingBoundary return n_patches_to_the_left; } - void calc_e_h(double t, int p, Real3 x3, int d_e, real_t& e, int d_h, - real_t& h) + real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) override { Real3 x3_advected = advect_x3(x3, t); int n_patches_to_the_left = shift_to_patch_local(x3_advected); @@ -806,36 +805,40 @@ struct AdvectedPeriodicFields : RadiatingBoundary auto em = decltype(ip)::fields_t( cycled_fields.view(_all, _all, _all, _all, p), -grid.ibn); - switch (d_e) { - case 0: e = ip.ex(em); break; - case 1: e = ip.ey(em); break; - case 2: e = ip.ez(em); break; - } - switch (d_h) { - case 0: h = ip.hx(em); break; - case 1: h = ip.hy(em); break; - case 2: h = ip.hz(em); break; + switch (m) { + case EX: return ip.ex(em); + case EY: return ip.ey(em); + case EZ: return ip.ez(em); + case HX: return ip.hx(em); + case HY: return ip.hy(em); + case HZ: return ip.hz(em); + default: return 0.0; } } + real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) override + { + return 0.0; + } + real_t pulse_s_lower(double t, int d, int p, Real3 x3) override { - int d1 = (d + 1) % 3; - int d2 = (d + 2) % 3; + int E1 = EX + (d + 1) % 3; + int H2 = HX + (d + 2) % 3; - real_t e, h; - calc_e_h(t, p, x3, d1, e, d2, h); + real_t e = sample_exterior_field_lo(E1, t, p, x3); + real_t h = sample_exterior_field_lo(H2, t, p, x3); return (e + h) / 2.0; } real_t pulse_p_lower(double t, int d, int p, Real3 x3) override { - int d1 = (d + 1) % 3; - int d2 = (d + 2) % 3; + int E2 = EX + (d + 2) % 3; + int H1 = HX + (d + 1) % 3; - real_t e, h; - calc_e_h(t, p, x3, d2, e, d1, h); + real_t e = sample_exterior_field_lo(E2, t, p, x3); + real_t h = sample_exterior_field_lo(H1, t, p, x3); return (e - h) / 2.0; } From 6a8dbb0a82df6901317d2e9c98c69b643563371a Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 12:57:05 -0400 Subject: [PATCH 074/106] radiating_bnd; *: -pulse lower --- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 11 ++++++++-- src/libpsc/psc_bnd_fields/radiating_bnd.hxx | 3 --- src/psc_shock.cxx | 22 ------------------- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 3e91105db..ab5720e47 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -167,8 +167,15 @@ struct BndFields_ : BndFieldsBase Real3(grid.domain.dx); Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * Real3(grid.domain.dx); - s = radiation->pulse_s_lower(grid.time(), d0, p, x3_s); - p = radiation->pulse_p_lower(grid.time(), d0, p, x3_p); + + s = + 0.5 * + (radiation->sample_exterior_field_lo(EX + d1, grid.time(), p, x3_s) + + radiation->sample_exterior_field_lo(HX + d2, grid.time(), p, x3_s)); + p = + 0.5 * + (radiation->sample_exterior_field_lo(EX + d2, grid.time(), p, x3_p) - + radiation->sample_exterior_field_lo(HX + d1, grid.time(), p, x3_p)); } F(H2, i3) = diff --git a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx index 98eb24e2c..c0ab12579 100644 --- a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx +++ b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx @@ -8,9 +8,6 @@ struct RadiatingBoundary virtual real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) = 0; virtual real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) = 0; - virtual real_t pulse_s_lower(double t, int d, int p, Real3 x3) = 0; - virtual real_t pulse_p_lower(double t, int d, int p, Real3 x3) = 0; - virtual real_t pulse_s_upper(double t, int d, int p, Real3 x3) = 0; virtual real_t pulse_p_upper(double t, int d, int p, Real3 x3) = 0; diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 8736f5ca9..2bb518056 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -821,28 +821,6 @@ struct AdvectedPeriodicFields : RadiatingBoundary return 0.0; } - real_t pulse_s_lower(double t, int d, int p, Real3 x3) override - { - int E1 = EX + (d + 1) % 3; - int H2 = HX + (d + 2) % 3; - - real_t e = sample_exterior_field_lo(E1, t, p, x3); - real_t h = sample_exterior_field_lo(H2, t, p, x3); - - return (e + h) / 2.0; - } - - real_t pulse_p_lower(double t, int d, int p, Real3 x3) override - { - int E2 = EX + (d + 2) % 3; - int H1 = HX + (d + 1) % 3; - - real_t e = sample_exterior_field_lo(E2, t, p, x3); - real_t h = sample_exterior_field_lo(H1, t, p, x3); - - return (e - h) / 2.0; - } - real_t pulse_s_upper(double t, int d, int p, Real3 x3) override { return 0.0; From 3969c6c33637d9c299a44802c847a916af801d0e Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 13:10:30 -0400 Subject: [PATCH 075/106] radiating_bnd; *: -pulse upper --- src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx | 11 +++++++++-- src/libpsc/psc_bnd_fields/radiating_bnd.hxx | 3 --- src/psc_shock.cxx | 10 ---------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index ab5720e47..4d654856a 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -226,8 +226,15 @@ struct BndFields_ : BndFieldsBase Real3(grid.domain.dx); Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * Real3(grid.domain.dx); - s = radiation->pulse_s_upper(grid.time(), d0, p, x3_s); - p = radiation->pulse_p_upper(grid.time(), d0, p, x3_p); + + s = + 0.5 * + (radiation->sample_exterior_field_hi(EX + d1, grid.time(), p, x3_s) - + radiation->sample_exterior_field_hi(HX + d2, grid.time(), p, x3_s)); + p = + 0.5 * + (radiation->sample_exterior_field_hi(EX + d2, grid.time(), p, x3_p) + + radiation->sample_exterior_field_hi(HX + d1, grid.time(), p, x3_p)); } F(H2, i3) = (-4.f * s + 2.f * (F(E1, i3) - background_e_hi[d1]) + diff --git a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx index c0ab12579..5b29cb35c 100644 --- a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx +++ b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx @@ -8,8 +8,5 @@ struct RadiatingBoundary virtual real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) = 0; virtual real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) = 0; - virtual real_t pulse_s_upper(double t, int d, int p, Real3 x3) = 0; - virtual real_t pulse_p_upper(double t, int d, int p, Real3 x3) = 0; - virtual void tick(double t) {} }; \ No newline at end of file diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 2bb518056..c21ab2213 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -821,16 +821,6 @@ struct AdvectedPeriodicFields : RadiatingBoundary return 0.0; } - real_t pulse_s_upper(double t, int d, int p, Real3 x3) override - { - return 0.0; - } - - real_t pulse_p_upper(double t, int d, int p, Real3 x3) override - { - return 0.0; - } - void tick(double t) override { // TODO make this work with >1 patch per process? From 3ef9137faa05f9b9007497d9b450c72df2c8ef8c Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 13:21:10 -0400 Subject: [PATCH 076/106] psc_bnd_fields_impl; psc_shock: include background in sample --- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 32 +++++++------------ src/psc_shock.cxx | 22 +++++++++---- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 4d654856a..83dc1fe8b 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -179,19 +179,15 @@ struct BndFields_ : BndFieldsBase } F(H2, i3) = - (4.f * s - 2.f * (F(E1, edge_idx) - background_e_lo[d1]) - + (4.f * s - 2.f * F(E1, edge_idx) - dtdx[d2] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d2))) - - (1.f - dtdx[d0]) * (F(H2, edge_idx) - background_h_lo[d2]) + - dt * F(J1, edge_idx)) / - (1.f + dtdx[d0]) + - background_h_lo[d2]; + (1.f - dtdx[d0]) * F(H2, edge_idx) + dt * F(J1, edge_idx)) / + (1.f + dtdx[d0]); F(H1, i3) = - (-4.f * p + 2.f * (F(E2, edge_idx) - background_e_lo[d2]) - + (-4.f * p + 2.f * F(E2, edge_idx) - dtdx[d1] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d1))) - - (1.f - dtdx[d0]) * (F(H1, edge_idx) - background_h_lo[d1]) + - dt * F(J2, edge_idx)) / - (1.f + dtdx[d0]) + - background_h_lo[d1]; + (1.f - dtdx[d0]) * F(H1, edge_idx) + dt * F(J2, edge_idx)) / + (1.f + dtdx[d0]); } } @@ -237,18 +233,14 @@ struct BndFields_ : BndFieldsBase radiation->sample_exterior_field_hi(HX + d1, grid.time(), p, x3_p)); } - F(H2, i3) = (-4.f * s + 2.f * (F(E1, i3) - background_e_hi[d1]) + + F(H2, i3) = (-4.f * s + 2.f * F(E1, i3) + dtdx[d2] * (F(H0, i3) - F(H0, i3 - Int3::unit(d2))) - - (1.f - dtdx[d0]) * (F(H2, edge_idx) - background_h_hi[d2]) - - dt * F(J1, i3)) / - (1.f + dtdx[d0]) + - background_h_hi[d2]; - F(H1, i3) = (4.f * p - 2.f * (F(E2, i3) - background_e_hi[d2]) + + (1.f - dtdx[d0]) * F(H2, edge_idx) - dt * F(J1, i3)) / + (1.f + dtdx[d0]); + F(H1, i3) = (4.f * p - 2.f * F(E2, i3) + dtdx[d1] * (F(H0, i3) - F(H0, i3 - Int3::unit(d1))) - - (1.f - dtdx[d0]) * (F(H1, edge_idx) - background_h_hi[d1]) - - dt * F(J2, i3)) / - (1.f + dtdx[d0]) + - background_h_hi[d1]; + (1.f - dtdx[d0]) * F(H1, edge_idx) - dt * F(J2, i3)) / + (1.f + dtdx[d0]); } } diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index c21ab2213..57af72de9 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -806,19 +806,27 @@ struct AdvectedPeriodicFields : RadiatingBoundary cycled_fields.view(_all, _all, _all, _all, p), -grid.ibn); switch (m) { - case EX: return ip.ex(em); - case EY: return ip.ey(em); - case EZ: return ip.ez(em); - case HX: return ip.hx(em); - case HY: return ip.hy(em); - case HZ: return ip.hz(em); + case EX: return ip.ex(em) + e0[0]; + case EY: return ip.ey(em) + e0[1]; + case EZ: return ip.ez(em) + e0[2]; + case HX: return ip.hx(em) + h0_upstream[0]; + case HY: return ip.hy(em) + h0_upstream[1]; + case HZ: return ip.hz(em) + h0_upstream[2]; default: return 0.0; } } real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) override { - return 0.0; + switch (m) { + case EX: return e0[0]; + case EY: return e0[1]; + case EZ: return e0[2]; + case HX: return h0_downstream[0]; + case HY: return h0_downstream[1]; + case HZ: return h0_downstream[2]; + default: return 0.0; + } } void tick(double t) override From 32e9c39a8782cbab2ba27b1313f49f04b9a42e78 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 13:31:06 -0400 Subject: [PATCH 077/106] psc_bnd_fields_impl: don't set E for open bcs For 1st order prts, only normal E matters. This now evolves it from H + currents. Currents might not be deposited right, and idk if this is correct either way. --- src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 83dc1fe8b..4c092d798 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -42,8 +42,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - psc::bnd::field::detail::set_lower_ghosts( - mflds, p, d, EX, background_e_lo, false); break; } default: { @@ -64,8 +62,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - psc::bnd::field::detail::set_upper_ghosts( - mflds, p, d, EX, background_e_hi, false); break; } default: { From aea7d062ee05ac4cd6b68570fc397f3af34fc7b3 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 13:41:47 -0400 Subject: [PATCH 078/106] psc_bnd_fields_impl; *: -background flds --- src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx | 6 ------ src/psc_shock.cxx | 5 ----- 2 files changed, 11 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 4c092d798..bcf2b1d33 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -240,12 +240,6 @@ struct BndFields_ : BndFieldsBase } } - Vec3 background_e_lo = {0.0, 0.0, 0.0}; - Vec3 background_e_hi = {0.0, 0.0, 0.0}; - - Vec3 background_h_lo = {0.0, 0.0, 0.0}; - Vec3 background_h_hi = {0.0, 0.0, 0.0}; - RadiatingBoundary* radiation = nullptr; }; diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 57af72de9..382a63d2c 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -990,11 +990,6 @@ static void run(int argc, char** argv) psc.add_gauss_corrector(&marder); - psc.bndf.background_e_lo = e0; - psc.bndf.background_h_lo = h0_upstream; - psc.bndf.background_e_hi = e0; - psc.bndf.background_h_hi = h0_downstream; - if (turb_db2 > 0.0 && v_upstream[1] > 0.0) { if (checkpoint_filename.empty()) { // mflds is currently just the pure, initial turbulence From 8044e5f3601f861a3739eb998de5f37a60d992d2 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 13:42:31 -0400 Subject: [PATCH 079/106] psc_bnd_fields_impl: redefine s, p --- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index bcf2b1d33..8f88e1a57 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -164,23 +164,19 @@ struct BndFields_ : BndFieldsBase Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * Real3(grid.domain.dx); - s = - 0.5 * - (radiation->sample_exterior_field_lo(EX + d1, grid.time(), p, x3_s) + - radiation->sample_exterior_field_lo(HX + d2, grid.time(), p, x3_s)); - p = - 0.5 * - (radiation->sample_exterior_field_lo(EX + d2, grid.time(), p, x3_p) - - radiation->sample_exterior_field_lo(HX + d1, grid.time(), p, x3_p)); + s = radiation->sample_exterior_field_lo(EX + d1, grid.time(), p, x3_s) + + radiation->sample_exterior_field_lo(HX + d2, grid.time(), p, x3_s); + p = radiation->sample_exterior_field_lo(EX + d2, grid.time(), p, x3_p) - + radiation->sample_exterior_field_lo(HX + d1, grid.time(), p, x3_p); } F(H2, i3) = - (4.f * s - 2.f * F(E1, edge_idx) - + (2.f * s - 2.f * F(E1, edge_idx) - dtdx[d2] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d2))) - (1.f - dtdx[d0]) * F(H2, edge_idx) + dt * F(J1, edge_idx)) / (1.f + dtdx[d0]); F(H1, i3) = - (-4.f * p + 2.f * F(E2, edge_idx) - + (-2.f * p + 2.f * F(E2, edge_idx) - dtdx[d1] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d1))) - (1.f - dtdx[d0]) * F(H1, edge_idx) + dt * F(J2, edge_idx)) / (1.f + dtdx[d0]); @@ -219,21 +215,17 @@ struct BndFields_ : BndFieldsBase Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * Real3(grid.domain.dx); - s = - 0.5 * - (radiation->sample_exterior_field_hi(EX + d1, grid.time(), p, x3_s) - - radiation->sample_exterior_field_hi(HX + d2, grid.time(), p, x3_s)); - p = - 0.5 * - (radiation->sample_exterior_field_hi(EX + d2, grid.time(), p, x3_p) + - radiation->sample_exterior_field_hi(HX + d1, grid.time(), p, x3_p)); + s = radiation->sample_exterior_field_hi(EX + d1, grid.time(), p, x3_s) - + radiation->sample_exterior_field_hi(HX + d2, grid.time(), p, x3_s); + p = radiation->sample_exterior_field_hi(EX + d2, grid.time(), p, x3_p) + + radiation->sample_exterior_field_hi(HX + d1, grid.time(), p, x3_p); } - F(H2, i3) = (-4.f * s + 2.f * F(E1, i3) + + F(H2, i3) = (-2.f * s + 2.f * F(E1, i3) + dtdx[d2] * (F(H0, i3) - F(H0, i3 - Int3::unit(d2))) - (1.f - dtdx[d0]) * F(H2, edge_idx) - dt * F(J1, i3)) / (1.f + dtdx[d0]); - F(H1, i3) = (4.f * p - 2.f * F(E2, i3) + + F(H1, i3) = (2.f * p - 2.f * F(E2, i3) + dtdx[d1] * (F(H0, i3) - F(H0, i3 - Int3::unit(d1))) - (1.f - dtdx[d0]) * F(H1, edge_idx) - dt * F(J2, i3)) / (1.f + dtdx[d0]); From 5359e2cb505c97f0e79cef077aecf91ea613a3de Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 14:39:08 -0400 Subject: [PATCH 080/106] psc_bnd_fields_impl: fix sign on j --- src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 8f88e1a57..e96ae63f5 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -178,7 +178,7 @@ struct BndFields_ : BndFieldsBase F(H1, i3) = (-2.f * p + 2.f * F(E2, edge_idx) - dtdx[d1] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d1))) - - (1.f - dtdx[d0]) * F(H1, edge_idx) + dt * F(J2, edge_idx)) / + (1.f - dtdx[d0]) * F(H1, edge_idx) - dt * F(J2, edge_idx)) / (1.f + dtdx[d0]); } } @@ -227,7 +227,7 @@ struct BndFields_ : BndFieldsBase (1.f + dtdx[d0]); F(H1, i3) = (2.f * p - 2.f * F(E2, i3) + dtdx[d1] * (F(H0, i3) - F(H0, i3 - Int3::unit(d1))) - - (1.f - dtdx[d0]) * F(H1, edge_idx) - dt * F(J2, i3)) / + (1.f - dtdx[d0]) * F(H1, edge_idx) + dt * F(J2, i3)) / (1.f + dtdx[d0]); } } From 224c039109aa0366a09aee48660d4dc9a66adb90 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 15:21:25 -0400 Subject: [PATCH 081/106] radiating, psc_bnd_fields_impl; *: mv rad H impl --- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 102 ----------------- src/libpsc/psc_bnd_fields/radiating.hxx | 106 +++++++++++++++++- src/psc_shock.cxx | 83 ++++++++++---- 3 files changed, 168 insertions(+), 123 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index e96ae63f5..377e8b26c 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -81,7 +81,6 @@ struct BndFields_ : BndFieldsBase const auto& grid = mflds.grid(); if (radiation) { - radiation->tick(grid.time()); } for (int p = 0; p < mflds.n_patches(); p++) { @@ -96,7 +95,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - radiative_H_lo(mflds, p, d); break; } default: { @@ -116,7 +114,6 @@ struct BndFields_ : BndFieldsBase break; } case BND_FLD_OPEN: { - radiative_H_hi(mflds, p, d); break; } default: { @@ -133,105 +130,6 @@ struct BndFields_ : BndFieldsBase void add_ghosts_J(MfieldsState& mflds) {} - void radiative_H_lo(MfieldsState& mflds, int p, int d) - { - psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, - false); - - auto F = make_Fields3d(mflds[p]); - const Grid_t& grid = mflds.grid(); - real_t dt = grid.dt; - Real3 dtdx = dt * Real3(grid.domain.dx_inv); - - int d0 = (d + 0) % 3, d1 = (d + 1) % 3, d2 = (d + 2) % 3; - int H0 = HX + d0, H1 = HX + d1, H2 = HX + d2; - int E1 = EX + d1, E2 = EX + d2; - int J1 = JXI + d1, J2 = JXI + d2; - - Int3 start = mflds.ib(); - Int3 stop = mflds.ib() + mflds.im(); - start[d0] = -1; - stop[d0] = 0; - - for (Int3 i3 : VecRange(start, stop)) { - Int3 edge_idx = i3 + Int3::unit(d0); - - real_t s = 0.0; - real_t p = 0.0; - if (radiation) { - Real3 x3_s = (Real3(edge_idx) + Real3::unit(d1) * real_t(0.5)) * - Real3(grid.domain.dx); - Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * - Real3(grid.domain.dx); - - s = radiation->sample_exterior_field_lo(EX + d1, grid.time(), p, x3_s) + - radiation->sample_exterior_field_lo(HX + d2, grid.time(), p, x3_s); - p = radiation->sample_exterior_field_lo(EX + d2, grid.time(), p, x3_p) - - radiation->sample_exterior_field_lo(HX + d1, grid.time(), p, x3_p); - } - - F(H2, i3) = - (2.f * s - 2.f * F(E1, edge_idx) - - dtdx[d2] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d2))) - - (1.f - dtdx[d0]) * F(H2, edge_idx) + dt * F(J1, edge_idx)) / - (1.f + dtdx[d0]); - F(H1, i3) = - (-2.f * p + 2.f * F(E2, edge_idx) - - dtdx[d1] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d1))) - - (1.f - dtdx[d0]) * F(H1, edge_idx) - dt * F(J2, edge_idx)) / - (1.f + dtdx[d0]); - } - } - - void radiative_H_hi(MfieldsState& mflds, int p, int d) - { - psc::bnd::field::detail::set_upper_ghosts_to_nan(mflds, p, d, HX, - false); - - auto F = make_Fields3d(mflds[p]); - const Grid_t& grid = mflds.grid(); - Int3 ldims = grid.ldims; - real_t dt = grid.dt; - Real3 dtdx = dt * Real3(grid.domain.dx_inv); - - int d0 = (d + 0) % 3, d1 = (d + 1) % 3, d2 = (d + 2) % 3; - int H0 = HX + d0, H1 = HX + d1, H2 = HX + d2; - int E1 = EX + d1, E2 = EX + d2; - int J1 = JXI + d1, J2 = JXI + d2; - - Int3 start = mflds.ib(); - Int3 stop = mflds.ib() + mflds.im(); - start[d0] = grid.ldims[d0]; - stop[d0] = grid.ldims[d0] + 1; - - for (Int3 i3 : VecRange(start, stop)) { - Int3 edge_idx = i3 - Int3::unit(d0); - - real_t s = 0.0; - real_t p = 0.0; - if (radiation) { - Real3 x3_s = (Real3(edge_idx) + Real3::unit(d1) * real_t(0.5)) * - Real3(grid.domain.dx); - Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * - Real3(grid.domain.dx); - - s = radiation->sample_exterior_field_hi(EX + d1, grid.time(), p, x3_s) - - radiation->sample_exterior_field_hi(HX + d2, grid.time(), p, x3_s); - p = radiation->sample_exterior_field_hi(EX + d2, grid.time(), p, x3_p) + - radiation->sample_exterior_field_hi(HX + d1, grid.time(), p, x3_p); - } - - F(H2, i3) = (-2.f * s + 2.f * F(E1, i3) + - dtdx[d2] * (F(H0, i3) - F(H0, i3 - Int3::unit(d2))) - - (1.f - dtdx[d0]) * F(H2, edge_idx) - dt * F(J1, i3)) / - (1.f + dtdx[d0]); - F(H1, i3) = (2.f * p - 2.f * F(E2, i3) + - dtdx[d1] * (F(H0, i3) - F(H0, i3 - Int3::unit(d1))) - - (1.f - dtdx[d0]) * F(H1, edge_idx) + dt * F(J2, i3)) / - (1.f + dtdx[d0]); - } - } - RadiatingBoundary* radiation = nullptr; }; diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index 8160ad573..f4394865f 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -20,6 +20,8 @@ struct Radiating : FieldBcBase { using dim_t = Dim; using Pulse = P; + using real_t = typename MfieldsState::real_t; + using Real3 = typename MfieldsState::Real3; Radiating(Pulse pulse, Axis d, LoHi lohi) : pulse{pulse}, d{d}, lohi{lohi} {} @@ -27,7 +29,109 @@ struct Radiating : FieldBcBase void apply_e_bcs(MfieldsState& mflds) override {} - void apply_h_bcs(MfieldsState& mflds) override {} + void apply_h_bcs(MfieldsState& mflds) override + { + const Grid_t& grid = mflds.grid(); + + pulse.tick(grid.time()); + + for (int p = 0; p < mflds.n_patches(); p++) { + if (lohi == Lo && grid.atBoundaryLo(p, d)) { + psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, + false); + + auto F = make_Fields3d(mflds[p]); + const Grid_t& grid = mflds.grid(); + real_t dt = grid.dt; + Real3 dtdx = dt * Real3(grid.domain.dx_inv); + + int d0 = (d + 0) % 3, d1 = (d + 1) % 3, d2 = (d + 2) % 3; + int H0 = HX + d0, H1 = HX + d1, H2 = HX + d2; + int E1 = EX + d1, E2 = EX + d2; + int J1 = JXI + d1, J2 = JXI + d2; + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d0] = -1; + stop[d0] = 0; + + for (Int3 i3 : VecRange(start, stop)) { + Int3 edge_idx = i3 + Int3::unit(d0); + + real_t s = 0.0; + real_t p = 0.0; + + Real3 x3_s = (Real3(edge_idx) + Real3::unit(d1) * real_t(0.5)) * + Real3(grid.domain.dx); + Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * + Real3(grid.domain.dx); + + s = pulse.sample_exterior_field_lo(EX + d1, grid.time(), p, x3_s) + + pulse.sample_exterior_field_lo(HX + d2, grid.time(), p, x3_s); + p = pulse.sample_exterior_field_lo(EX + d2, grid.time(), p, x3_p) - + pulse.sample_exterior_field_lo(HX + d1, grid.time(), p, x3_p); + + F(H2, i3) = + (2.f * s - 2.f * F(E1, edge_idx) - + dtdx[d2] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d2))) - + (1.f - dtdx[d0]) * F(H2, edge_idx) + dt * F(J1, edge_idx)) / + (1.f + dtdx[d0]); + F(H1, i3) = + (-2.f * p + 2.f * F(E2, edge_idx) - + dtdx[d1] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d1))) - + (1.f - dtdx[d0]) * F(H1, edge_idx) - dt * F(J2, edge_idx)) / + (1.f + dtdx[d0]); + } + } + + if (lohi == Hi && grid.atBoundaryHi(p, d)) { + psc::bnd::field::detail::set_upper_ghosts_to_nan(mflds, p, d, HX, + false); + + auto F = make_Fields3d(mflds[p]); + const Grid_t& grid = mflds.grid(); + Int3 ldims = grid.ldims; + real_t dt = grid.dt; + Real3 dtdx = dt * Real3(grid.domain.dx_inv); + + int d0 = (d + 0) % 3, d1 = (d + 1) % 3, d2 = (d + 2) % 3; + int H0 = HX + d0, H1 = HX + d1, H2 = HX + d2; + int E1 = EX + d1, E2 = EX + d2; + int J1 = JXI + d1, J2 = JXI + d2; + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d0] = grid.ldims[d0]; + stop[d0] = grid.ldims[d0] + 1; + + for (Int3 i3 : VecRange(start, stop)) { + Int3 edge_idx = i3 - Int3::unit(d0); + + real_t s = 0.0; + real_t p = 0.0; + + Real3 x3_s = (Real3(edge_idx) + Real3::unit(d1) * real_t(0.5)) * + Real3(grid.domain.dx); + Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * + Real3(grid.domain.dx); + + s = pulse.sample_exterior_field_hi(EX + d1, grid.time(), p, x3_s) - + pulse.sample_exterior_field_hi(HX + d2, grid.time(), p, x3_s); + p = pulse.sample_exterior_field_hi(EX + d2, grid.time(), p, x3_p) + + pulse.sample_exterior_field_hi(HX + d1, grid.time(), p, x3_p); + + F(H2, i3) = (-2.f * s + 2.f * F(E1, i3) + + dtdx[d2] * (F(H0, i3) - F(H0, i3 - Int3::unit(d2))) - + (1.f - dtdx[d0]) * F(H2, edge_idx) - dt * F(J1, i3)) / + (1.f + dtdx[d0]); + F(H1, i3) = (2.f * p - 2.f * F(E2, i3) + + dtdx[d1] * (F(H0, i3) - F(H0, i3 - Int3::unit(d1))) - + (1.f - dtdx[d0]) * F(H1, edge_idx) + dt * F(J2, i3)) / + (1.f + dtdx[d0]); + } + } + } + } Axis d; LoHi lohi; diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 382a63d2c..09ea70621 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -8,6 +8,8 @@ #include "input_params.hxx" #include "kg/include/kg/VecRange.hxx" #include "libpsc/psc_output_particles/output_particles_adios2_impl.hxx" +#include "libpsc/psc_bnd_fields/radiating.hxx" +#include "libpsc/axis.hxx" // ====================================================================== // PSC configuration @@ -876,6 +878,32 @@ struct AdvectedPeriodicFields : RadiatingBoundary ip; }; +struct ConstantFields : RadiatingBoundary +{ + ConstantFields(Real3 e, Real3 h) : e{e}, h{h} {} + + real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) override + { + switch (m) { + case EX: return e[0]; + case EY: return e[1]; + case EZ: return e[2]; + case HX: return h[0]; + case HY: return h[1]; + case HZ: return h[2]; + default: return 0.0; + } + } + + real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) override + { + return sample_exterior_field_lo(m, t, p, x3); + } + + Real3 e; + Real3 h; +}; + // ====================================================================== // run @@ -990,39 +1018,54 @@ static void run(int argc, char** argv) psc.add_gauss_corrector(&marder); - if (turb_db2 > 0.0 && v_upstream[1] > 0.0) { - if (checkpoint_filename.empty()) { - // mflds is currently just the pure, initial turbulence - psc.bndf.radiation = - new AdvectedPeriodicFields{mflds, v_upstream[1], e0, h0_upstream}; - } else { - // mflds is completely unrelated; need to re-initialize turbulence - MfieldsState mflds2{grid}; - initialize_turbulence(mflds2); - psc.bndf.radiation = - new AdvectedPeriodicFields{mflds2, v_upstream[1], e0, h0_upstream}; - } - } - - if (checkpoint_filename.empty()) { - // add background after initializing radiation inflow, which only wants - // the perturbations to B - add_background_fields(mflds); - } - psc.add_diagnostic(&out_fields); psc.add_diagnostic(&out_moments); psc.add_diagnostic(&outp); psc.add_diagnostic(&oute); + using psc::Axis; + using psc::bnd::LoHi; + if (shock_method != "none") { psc.add_injector(&ion_injector_lo); psc.add_injector(&electron_injector_lo); + + if (turb_db2 > 0.0 && v_upstream[1] > 0.0) { + if (checkpoint_filename.empty()) { + // mflds is currently just the pure, initial turbulence + psc.add_field_bc(new psc::bnd::field::Radiating( + AdvectedPeriodicFields{mflds, v_upstream[1], e0, h0_upstream}, + Axis::Y, LoHi::Lo)); + } else { + // mflds is completely unrelated; need to re-initialize turbulence + MfieldsState mflds2{grid}; + initialize_turbulence(mflds2); + psc.add_field_bc(new psc::bnd::field::Radiating( + AdvectedPeriodicFields{mflds2, v_upstream[1], e0, h0_upstream}, + Axis::Y, LoHi::Lo)); + } + } else { + psc.add_field_bc( + new psc::bnd::field::Radiating( + ConstantFields{e0, h0_upstream}, Axis::Y, LoHi::Lo)); + } } if (shock_method == "relaxation") { psc.add_injector(&ion_injector_hi); psc.add_injector(&electron_injector_hi); + + psc.add_field_bc( + new psc::bnd::field::Radiating( + ConstantFields{e0, h0_downstream}, Axis::Y, LoHi::Hi)); + } + + if (checkpoint_filename.empty()) { + // add background after initializing radiation inflow, which only wants + // the perturbations to B + add_background_fields(mflds); } psc.integrate(); From bfb74fd9ce019d1e018b31b7e1b3234b9c92cca6 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 15:23:59 -0400 Subject: [PATCH 082/106] psc_bnd_fields_impl: rm E, H loops --- .../psc_bnd_fields/psc_bnd_fields_impl.hxx | 98 +------------------ 1 file changed, 2 insertions(+), 96 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 377e8b26c..41fc3d0b5 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -26,111 +26,17 @@ struct BndFields_ : BndFieldsBase // ---------------------------------------------------------------------- // fill_ghosts_E - void fill_ghosts_E(MfieldsState& mflds) - { - const auto& grid = mflds.grid(); - - for (int p = 0; p < mflds.n_patches(); p++) { - // lo - for (int d = 0; d < 3; d++) { - if (grid.atBoundaryLo(p, d)) { - switch (grid.bc.fld_lo[d]) { - case BND_FLD_PERIODIC: { - break; - } - case BND_FLD_CONDUCTING_WALL: { - break; - } - case BND_FLD_OPEN: { - break; - } - default: { - assert(0); - } - } - } - } - - // hi - for (int d = 0; d < 3; d++) { - if (grid.atBoundaryHi(p, d)) { - switch (grid.bc.fld_hi[d]) { - case BND_FLD_PERIODIC: { - break; - } - case BND_FLD_CONDUCTING_WALL: { - break; - } - case BND_FLD_OPEN: { - break; - } - default: { - assert(0); - } - } - } - } - } - } + void fill_ghosts_E(MfieldsState& mflds) {} // ---------------------------------------------------------------------- // fill_ghosts_H - void fill_ghosts_H(MfieldsState& mflds) - { - const auto& grid = mflds.grid(); - - if (radiation) { - } - - for (int p = 0; p < mflds.n_patches(); p++) { - // lo - for (int d = 0; d < 3; d++) { - if (grid.atBoundaryLo(p, d)) { - switch (grid.bc.fld_lo[d]) { - case BND_FLD_PERIODIC: { - break; - } - case BND_FLD_CONDUCTING_WALL: { - break; - } - case BND_FLD_OPEN: { - break; - } - default: { - assert(0); - } - } - } - } - // hi - for (int d = 0; d < 3; d++) { - if (grid.atBoundaryHi(p, d)) { - switch (grid.bc.fld_hi[d]) { - case BND_FLD_PERIODIC: { - break; - } - case BND_FLD_CONDUCTING_WALL: { - break; - } - case BND_FLD_OPEN: { - break; - } - default: { - assert(0); - } - } - } - } - } - } + void fill_ghosts_H(MfieldsState& mflds) {} // ---------------------------------------------------------------------- // add_ghosts_J void add_ghosts_J(MfieldsState& mflds) {} - - RadiatingBoundary* radiation = nullptr; }; // ====================================================================== From 65e630b78414589f9b84e1157c2a8c583a48ac14 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 15:27:34 -0400 Subject: [PATCH 083/106] radiating: cleanup --- src/libpsc/psc_bnd_fields/radiating.hxx | 40 ++++++++++--------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index f4394865f..998bc847c 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -32,23 +32,22 @@ struct Radiating : FieldBcBase void apply_h_bcs(MfieldsState& mflds) override { const Grid_t& grid = mflds.grid(); + real_t dt = grid.dt; + Real3 dtdx = dt * Real3(grid.domain.dx_inv); pulse.tick(grid.time()); + int d0 = d, d1 = d.next(), d2 = d.prev(); + int H0 = HX + d0, H1 = HX + d1, H2 = HX + d2; + int E1 = EX + d1, E2 = EX + d2; + int J1 = JXI + d1, J2 = JXI + d2; + for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, false); auto F = make_Fields3d(mflds[p]); - const Grid_t& grid = mflds.grid(); - real_t dt = grid.dt; - Real3 dtdx = dt * Real3(grid.domain.dx_inv); - - int d0 = (d + 0) % 3, d1 = (d + 1) % 3, d2 = (d + 2) % 3; - int H0 = HX + d0, H1 = HX + d1, H2 = HX + d2; - int E1 = EX + d1, E2 = EX + d2; - int J1 = JXI + d1, J2 = JXI + d2; Int3 start = mflds.ib(); Int3 stop = mflds.ib() + mflds.im(); @@ -66,10 +65,10 @@ struct Radiating : FieldBcBase Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * Real3(grid.domain.dx); - s = pulse.sample_exterior_field_lo(EX + d1, grid.time(), p, x3_s) + - pulse.sample_exterior_field_lo(HX + d2, grid.time(), p, x3_s); - p = pulse.sample_exterior_field_lo(EX + d2, grid.time(), p, x3_p) - - pulse.sample_exterior_field_lo(HX + d1, grid.time(), p, x3_p); + s = pulse.sample_exterior_field_lo(E1, grid.time(), p, x3_s) + + pulse.sample_exterior_field_lo(H2, grid.time(), p, x3_s); + p = pulse.sample_exterior_field_lo(E2, grid.time(), p, x3_p) - + pulse.sample_exterior_field_lo(H1, grid.time(), p, x3_p); F(H2, i3) = (2.f * s - 2.f * F(E1, edge_idx) - @@ -89,15 +88,6 @@ struct Radiating : FieldBcBase false); auto F = make_Fields3d(mflds[p]); - const Grid_t& grid = mflds.grid(); - Int3 ldims = grid.ldims; - real_t dt = grid.dt; - Real3 dtdx = dt * Real3(grid.domain.dx_inv); - - int d0 = (d + 0) % 3, d1 = (d + 1) % 3, d2 = (d + 2) % 3; - int H0 = HX + d0, H1 = HX + d1, H2 = HX + d2; - int E1 = EX + d1, E2 = EX + d2; - int J1 = JXI + d1, J2 = JXI + d2; Int3 start = mflds.ib(); Int3 stop = mflds.ib() + mflds.im(); @@ -115,10 +105,10 @@ struct Radiating : FieldBcBase Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * Real3(grid.domain.dx); - s = pulse.sample_exterior_field_hi(EX + d1, grid.time(), p, x3_s) - - pulse.sample_exterior_field_hi(HX + d2, grid.time(), p, x3_s); - p = pulse.sample_exterior_field_hi(EX + d2, grid.time(), p, x3_p) + - pulse.sample_exterior_field_hi(HX + d1, grid.time(), p, x3_p); + s = pulse.sample_exterior_field_hi(E1, grid.time(), p, x3_s) - + pulse.sample_exterior_field_hi(H2, grid.time(), p, x3_s); + p = pulse.sample_exterior_field_hi(E2, grid.time(), p, x3_p) + + pulse.sample_exterior_field_hi(H1, grid.time(), p, x3_p); F(H2, i3) = (-2.f * s + 2.f * F(E1, i3) + dtdx[d2] * (F(H0, i3) - F(H0, i3 - Int3::unit(d2))) - From 9eb7d6f8388b8b77cda8c1230a59b598fadefff5 Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 15:46:30 -0400 Subject: [PATCH 084/106] radiating: reindex, more hats --- src/libpsc/psc_bnd_fields/radiating.hxx | 56 ++++++++++++------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index 998bc847c..ba0fe8773 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -42,6 +42,10 @@ struct Radiating : FieldBcBase int E1 = EX + d1, E2 = EX + d2; int J1 = JXI + d1, J2 = JXI + d2; + Int3 d0hat = Int3::unit(d0); + Int3 d1hat = Int3::unit(d1); + Int3 d2hat = Int3::unit(d2); + for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { psc::bnd::field::detail::set_lower_ghosts_to_nan(mflds, p, d, HX, @@ -51,35 +55,31 @@ struct Radiating : FieldBcBase Int3 start = mflds.ib(); Int3 stop = mflds.ib() + mflds.im(); - start[d0] = -1; - stop[d0] = 0; + start[d0] = 0; + stop[d0] = start[d0] + 1; for (Int3 i3 : VecRange(start, stop)) { - Int3 edge_idx = i3 + Int3::unit(d0); - real_t s = 0.0; real_t p = 0.0; - Real3 x3_s = (Real3(edge_idx) + Real3::unit(d1) * real_t(0.5)) * - Real3(grid.domain.dx); - Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * - Real3(grid.domain.dx); + Real3 x3_s = + (Real3(i3) + Real3(d1hat) * real_t(0.5)) * Real3(grid.domain.dx); + Real3 x3_p = + (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); s = pulse.sample_exterior_field_lo(E1, grid.time(), p, x3_s) + pulse.sample_exterior_field_lo(H2, grid.time(), p, x3_s); p = pulse.sample_exterior_field_lo(E2, grid.time(), p, x3_p) - pulse.sample_exterior_field_lo(H1, grid.time(), p, x3_p); - F(H2, i3) = - (2.f * s - 2.f * F(E1, edge_idx) - - dtdx[d2] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d2))) - - (1.f - dtdx[d0]) * F(H2, edge_idx) + dt * F(J1, edge_idx)) / - (1.f + dtdx[d0]); - F(H1, i3) = - (-2.f * p + 2.f * F(E2, edge_idx) - - dtdx[d1] * (F(H0, edge_idx) - F(H0, edge_idx - Int3::unit(d1))) - - (1.f - dtdx[d0]) * F(H1, edge_idx) - dt * F(J2, edge_idx)) / - (1.f + dtdx[d0]); + F(H2, i3 - d0hat) = (2.f * s - 2.f * F(E1, i3) - + dtdx[d2] * (F(H0, i3) - F(H0, i3 - d2hat)) - + (1.f - dtdx[d0]) * F(H2, i3) + dt * F(J1, i3)) / + (1.f + dtdx[d0]); + F(H1, i3 - d0hat) = (-2.f * p + 2.f * F(E2, i3) - + dtdx[d1] * (F(H0, i3) - F(H0, i3 - d1hat)) - + (1.f - dtdx[d0]) * F(H1, i3) - dt * F(J2, i3)) / + (1.f + dtdx[d0]); } } @@ -92,18 +92,16 @@ struct Radiating : FieldBcBase Int3 start = mflds.ib(); Int3 stop = mflds.ib() + mflds.im(); start[d0] = grid.ldims[d0]; - stop[d0] = grid.ldims[d0] + 1; + stop[d0] = start[d0] + 1; for (Int3 i3 : VecRange(start, stop)) { - Int3 edge_idx = i3 - Int3::unit(d0); - real_t s = 0.0; real_t p = 0.0; - Real3 x3_s = (Real3(edge_idx) + Real3::unit(d1) * real_t(0.5)) * - Real3(grid.domain.dx); - Real3 x3_p = (Real3(edge_idx) + Real3::unit(d2) * real_t(0.5)) * - Real3(grid.domain.dx); + Real3 x3_s = + (Real3(i3) + Real3(d1hat) * real_t(0.5)) * Real3(grid.domain.dx); + Real3 x3_p = + (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); s = pulse.sample_exterior_field_hi(E1, grid.time(), p, x3_s) - pulse.sample_exterior_field_hi(H2, grid.time(), p, x3_s); @@ -111,12 +109,12 @@ struct Radiating : FieldBcBase pulse.sample_exterior_field_hi(H1, grid.time(), p, x3_p); F(H2, i3) = (-2.f * s + 2.f * F(E1, i3) + - dtdx[d2] * (F(H0, i3) - F(H0, i3 - Int3::unit(d2))) - - (1.f - dtdx[d0]) * F(H2, edge_idx) - dt * F(J1, i3)) / + dtdx[d2] * (F(H0, i3) - F(H0, i3 - d2hat)) - + (1.f - dtdx[d0]) * F(H2, i3 - d0hat) - dt * F(J1, i3)) / (1.f + dtdx[d0]); F(H1, i3) = (2.f * p - 2.f * F(E2, i3) + - dtdx[d1] * (F(H0, i3) - F(H0, i3 - Int3::unit(d1))) - - (1.f - dtdx[d0]) * F(H1, edge_idx) + dt * F(J2, i3)) / + dtdx[d1] * (F(H0, i3) - F(H0, i3 - d1hat)) - + (1.f - dtdx[d0]) * F(H1, i3 - d0hat) + dt * F(J2, i3)) / (1.f + dtdx[d0]); } } From 464fc62072954158e35f38fe53bd4c1c9a79bb2d Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 15:46:57 -0400 Subject: [PATCH 085/106] radiating: more cleanup --- src/libpsc/psc_bnd_fields/radiating.hxx | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index ba0fe8773..d4e2763c2 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -59,18 +59,15 @@ struct Radiating : FieldBcBase stop[d0] = start[d0] + 1; for (Int3 i3 : VecRange(start, stop)) { - real_t s = 0.0; - real_t p = 0.0; - Real3 x3_s = (Real3(i3) + Real3(d1hat) * real_t(0.5)) * Real3(grid.domain.dx); Real3 x3_p = (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); - s = pulse.sample_exterior_field_lo(E1, grid.time(), p, x3_s) + - pulse.sample_exterior_field_lo(H2, grid.time(), p, x3_s); - p = pulse.sample_exterior_field_lo(E2, grid.time(), p, x3_p) - - pulse.sample_exterior_field_lo(H1, grid.time(), p, x3_p); + real_t s = pulse.sample_exterior_field_lo(E1, grid.time(), p, x3_s) + + pulse.sample_exterior_field_lo(H2, grid.time(), p, x3_s); + real_t p = pulse.sample_exterior_field_lo(E2, grid.time(), p, x3_p) - + pulse.sample_exterior_field_lo(H1, grid.time(), p, x3_p); F(H2, i3 - d0hat) = (2.f * s - 2.f * F(E1, i3) - dtdx[d2] * (F(H0, i3) - F(H0, i3 - d2hat)) - @@ -95,18 +92,15 @@ struct Radiating : FieldBcBase stop[d0] = start[d0] + 1; for (Int3 i3 : VecRange(start, stop)) { - real_t s = 0.0; - real_t p = 0.0; - Real3 x3_s = (Real3(i3) + Real3(d1hat) * real_t(0.5)) * Real3(grid.domain.dx); Real3 x3_p = (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); - s = pulse.sample_exterior_field_hi(E1, grid.time(), p, x3_s) - - pulse.sample_exterior_field_hi(H2, grid.time(), p, x3_s); - p = pulse.sample_exterior_field_hi(E2, grid.time(), p, x3_p) + - pulse.sample_exterior_field_hi(H1, grid.time(), p, x3_p); + real_t s = pulse.sample_exterior_field_hi(E1, grid.time(), p, x3_s) - + pulse.sample_exterior_field_hi(H2, grid.time(), p, x3_s); + real_t p = pulse.sample_exterior_field_hi(E2, grid.time(), p, x3_p) + + pulse.sample_exterior_field_hi(H1, grid.time(), p, x3_p); F(H2, i3) = (-2.f * s + 2.f * F(E1, i3) + dtdx[d2] * (F(H0, i3) - F(H0, i3 - d2hat)) - From 72c20f82cdfd8b3506dc6035515a37ecf657fadf Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 15:50:05 -0400 Subject: [PATCH 086/106] radiating_bnd; *: combine samples --- src/libpsc/psc_bnd_fields/radiating.hxx | 16 +++++++-------- src/libpsc/psc_bnd_fields/radiating_bnd.hxx | 3 +-- src/psc_shock.cxx | 22 ++------------------- 3 files changed, 11 insertions(+), 30 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index d4e2763c2..6de2b6394 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -64,10 +64,10 @@ struct Radiating : FieldBcBase Real3 x3_p = (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); - real_t s = pulse.sample_exterior_field_lo(E1, grid.time(), p, x3_s) + - pulse.sample_exterior_field_lo(H2, grid.time(), p, x3_s); - real_t p = pulse.sample_exterior_field_lo(E2, grid.time(), p, x3_p) - - pulse.sample_exterior_field_lo(H1, grid.time(), p, x3_p); + real_t s = pulse.sample_exterior_field(E1, grid.time(), p, x3_s) + + pulse.sample_exterior_field(H2, grid.time(), p, x3_s); + real_t p = pulse.sample_exterior_field(E2, grid.time(), p, x3_p) - + pulse.sample_exterior_field(H1, grid.time(), p, x3_p); F(H2, i3 - d0hat) = (2.f * s - 2.f * F(E1, i3) - dtdx[d2] * (F(H0, i3) - F(H0, i3 - d2hat)) - @@ -97,10 +97,10 @@ struct Radiating : FieldBcBase Real3 x3_p = (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); - real_t s = pulse.sample_exterior_field_hi(E1, grid.time(), p, x3_s) - - pulse.sample_exterior_field_hi(H2, grid.time(), p, x3_s); - real_t p = pulse.sample_exterior_field_hi(E2, grid.time(), p, x3_p) + - pulse.sample_exterior_field_hi(H1, grid.time(), p, x3_p); + real_t s = pulse.sample_exterior_field(E1, grid.time(), p, x3_s) - + pulse.sample_exterior_field(H2, grid.time(), p, x3_s); + real_t p = pulse.sample_exterior_field(E2, grid.time(), p, x3_p) + + pulse.sample_exterior_field(H1, grid.time(), p, x3_p); F(H2, i3) = (-2.f * s + 2.f * F(E1, i3) + dtdx[d2] * (F(H0, i3) - F(H0, i3 - d2hat)) - diff --git a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx index 5b29cb35c..46bf27172 100644 --- a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx +++ b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx @@ -5,8 +5,7 @@ struct RadiatingBoundary { using Real3 = Vec3; - virtual real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) = 0; - virtual real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) = 0; + virtual real_t sample_exterior_field(int m, double t, int p, Real3 x3) = 0; virtual void tick(double t) {} }; \ No newline at end of file diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 09ea70621..7baf9c729 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -794,7 +794,7 @@ struct AdvectedPeriodicFields : RadiatingBoundary return n_patches_to_the_left; } - real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) override + real_t sample_exterior_field(int m, double t, int p, Real3 x3) override { Real3 x3_advected = advect_x3(x3, t); int n_patches_to_the_left = shift_to_patch_local(x3_advected); @@ -818,19 +818,6 @@ struct AdvectedPeriodicFields : RadiatingBoundary } } - real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) override - { - switch (m) { - case EX: return e0[0]; - case EY: return e0[1]; - case EZ: return e0[2]; - case HX: return h0_downstream[0]; - case HY: return h0_downstream[1]; - case HZ: return h0_downstream[2]; - default: return 0.0; - } - } - void tick(double t) override { // TODO make this work with >1 patch per process? @@ -882,7 +869,7 @@ struct ConstantFields : RadiatingBoundary { ConstantFields(Real3 e, Real3 h) : e{e}, h{h} {} - real_t sample_exterior_field_lo(int m, double t, int p, Real3 x3) override + real_t sample_exterior_field(int m, double t, int p, Real3 x3) override { switch (m) { case EX: return e[0]; @@ -895,11 +882,6 @@ struct ConstantFields : RadiatingBoundary } } - real_t sample_exterior_field_hi(int m, double t, int p, Real3 x3) override - { - return sample_exterior_field_lo(m, t, p, x3); - } - Real3 e; Real3 h; }; From 04d3f9c15f1d3646c3aa578b72e0b5ed8423403f Mon Sep 17 00:00:00 2001 From: James McClung Date: Fri, 10 Jul 2026 15:56:29 -0400 Subject: [PATCH 087/106] -radiating_bnd, radiating; *: ->PulseBase --- src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx | 1 - src/libpsc/psc_bnd_fields/radiating.hxx | 10 ++++++++++ src/libpsc/psc_bnd_fields/radiating_bnd.hxx | 11 ----------- src/psc_shock.cxx | 4 ++-- 4 files changed, 12 insertions(+), 14 deletions(-) delete mode 100644 src/libpsc/psc_bnd_fields/radiating_bnd.hxx diff --git a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx index 41fc3d0b5..bf226e9e4 100644 --- a/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx +++ b/src/libpsc/psc_bnd_fields/psc_bnd_fields_impl.hxx @@ -4,7 +4,6 @@ #include "kg/VecRange.hxx" #include "fields.hxx" #include "bnd_fields.hxx" -#include "radiating_bnd.hxx" #include "field_bc_util.hxx" #include diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index 6de2b6394..3faa24a06 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -122,6 +122,16 @@ private: Pulse pulse; }; +template +struct PulseBase +{ + using Real3 = Vec3; + + virtual real_t sample_exterior_field(int m, double t, int p, Real3 x3) = 0; + + virtual void tick(double t) {} +}; + } // namespace field } // namespace bnd } // namespace psc diff --git a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx b/src/libpsc/psc_bnd_fields/radiating_bnd.hxx deleted file mode 100644 index 46bf27172..000000000 --- a/src/libpsc/psc_bnd_fields/radiating_bnd.hxx +++ /dev/null @@ -1,11 +0,0 @@ -#include "kg/Vec3.h" - -template -struct RadiatingBoundary -{ - using Real3 = Vec3; - - virtual real_t sample_exterior_field(int m, double t, int p, Real3 x3) = 0; - - virtual void tick(double t) {} -}; \ No newline at end of file diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 7baf9c729..4e19bb691 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -760,7 +760,7 @@ void initialize_turbulence(MfieldsState& mflds) } } -struct AdvectedPeriodicFields : RadiatingBoundary +struct AdvectedPeriodicFields : psc::bnd::field::PulseBase { static const int DIM_Y = 1; @@ -865,7 +865,7 @@ struct AdvectedPeriodicFields : RadiatingBoundary ip; }; -struct ConstantFields : RadiatingBoundary +struct ConstantFields : psc::bnd::field::PulseBase { ConstantFields(Real3 e, Real3 h) : e{e}, h{h} {} From ba7bb6d2ff0c4adfe18a8a0dc74635011a286491 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 14 Jul 2026 09:00:41 -0400 Subject: [PATCH 088/106] radiating: +ConstantPulse --- src/libpsc/psc_bnd_fields/radiating.hxx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index 3faa24a06..f61fc931d 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -132,6 +132,30 @@ struct PulseBase virtual void tick(double t) {} }; +template +struct ConstantPulse : PulseBase +{ + using Real3 = Vec3; + + ConstantPulse(Real3 e, Real3 h) : e{e}, h{h} {} + + real_t sample_exterior_field(int m, double t, int p, Real3 x3) override + { + switch (m) { + case EX: return e[0]; + case EY: return e[1]; + case EZ: return e[2]; + case HX: return h[0]; + case HY: return h[1]; + case HZ: return h[2]; + default: return 0.0; + } + } + + Real3 e; + Real3 h; +}; + } // namespace field } // namespace bnd } // namespace psc From 2099d06aec21f1fea0cee6b0af08359d3b7c5e80 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 14 Jul 2026 09:00:52 -0400 Subject: [PATCH 089/106] psc_shock: use ConstantPulse --- src/psc_shock.cxx | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 4e19bb691..39c5c779c 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -865,27 +865,6 @@ struct AdvectedPeriodicFields : psc::bnd::field::PulseBase ip; }; -struct ConstantFields : psc::bnd::field::PulseBase -{ - ConstantFields(Real3 e, Real3 h) : e{e}, h{h} {} - - real_t sample_exterior_field(int m, double t, int p, Real3 x3) override - { - switch (m) { - case EX: return e[0]; - case EY: return e[1]; - case EZ: return e[2]; - case HX: return h[0]; - case HY: return h[1]; - case HZ: return h[2]; - default: return 0.0; - } - } - - Real3 e; - Real3 h; -}; - // ====================================================================== // run @@ -1007,6 +986,7 @@ static void run(int argc, char** argv) using psc::Axis; using psc::bnd::LoHi; + using ConstantPulse = psc::bnd::field::ConstantPulse; if (shock_method != "none") { psc.add_injector(&ion_injector_lo); @@ -1030,8 +1010,8 @@ static void run(int argc, char** argv) } } else { psc.add_field_bc( - new psc::bnd::field::Radiating( - ConstantFields{e0, h0_upstream}, Axis::Y, LoHi::Lo)); + new psc::bnd::field::Radiating( + ConstantPulse{e0, h0_upstream}, Axis::Y, LoHi::Lo)); } } @@ -1040,8 +1020,8 @@ static void run(int argc, char** argv) psc.add_injector(&electron_injector_hi); psc.add_field_bc( - new psc::bnd::field::Radiating( - ConstantFields{e0, h0_downstream}, Axis::Y, LoHi::Hi)); + new psc::bnd::field::Radiating( + ConstantPulse{e0, h0_downstream}, Axis::Y, LoHi::Hi)); } if (checkpoint_filename.empty()) { From 54f3d7d49c432b948b11fbac8d03854f41450ef6 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 22 Jul 2026 12:35:16 -0400 Subject: [PATCH 090/106] Vec3: +with_component --- src/kg/include/kg/Vec3.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/kg/include/kg/Vec3.h b/src/kg/include/kg/Vec3.h index b74c7db0c..31b028cb5 100644 --- a/src/kg/include/kg/Vec3.h +++ b/src/kg/include/kg/Vec3.h @@ -295,6 +295,19 @@ struct Vec : gt::sarray return res; } + /** + * @brief Returns a copy of this vec but with one component value replaced. + * @param d component index + * @param val new component value + * @return the copy + */ + KG_INLINE Vec with_component(int d, T val) const + { + Vec res = *this; + res[d] = val; + return res; + } + // conversion to pointer KG_INLINE operator const T*() const { return this->data(); } From c2ed0cbad62137061e1a3faf21b4a2054ca03c68 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 14 Jul 2026 09:47:42 -0400 Subject: [PATCH 091/106] radiating: +comments --- src/libpsc/psc_bnd_fields/radiating.hxx | 51 +++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index f61fc931d..13318e32c 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -15,6 +15,13 @@ namespace bnd namespace field { +/** + * @brief "Radiating" open boundary. Prescribe an arbitrary inflowing + * electromagnetic pulse. See Ruhl 2006 for details. + * @tparam Dim dimension type + * @tparam MfieldsState fields type + * @tparam P pulse type (i.e. a type that implements `PulseBase`) + */ template struct Radiating : FieldBcBase { @@ -25,10 +32,30 @@ struct Radiating : FieldBcBase Radiating(Pulse pulse, Axis d, LoHi lohi) : pulse{pulse}, d{d}, lohi{lohi} {} + /** + * @brief Don't do anything to the current. + * @param mflds fields + */ void apply_j_bcs(MfieldsState& mflds) override {} + /** + * @brief Don't do anything to the electric field. This is technically wrong + * for higher-order particles, which can feel deeper into the ghost region. + * Even for 1st-order particles, which can feel the first layer of the + * transverse E ghosts, it's unclear if doing nothing is correct. Those ghosts + * are self-consistently evolved from the transverse H ghosts and interior + * particle motion, at least. + * @param mflds fields + */ void apply_e_bcs(MfieldsState& mflds) override {} + /** + * @brief Set the first layer of transverse H ghosts such that the inflowing S + * and P waves are prescribed at the boundary. The definitions of S and P + * differ from Ruhl 2006 by a factor of 2, and Ruhl's definitions aren't + * invariant under an x->y-z->x rotation. + * @param mflds fields + */ void apply_h_bcs(MfieldsState& mflds) override { const Grid_t& grid = mflds.grid(); @@ -122,16 +149,40 @@ private: Pulse pulse; }; +/** + * @brief The archetypal Pulse type used by the `Radiating` boundary condition. + * This type isn't used polymorphically, so extending it isn't strictly + * required. + * @tparam real_t real type + */ template struct PulseBase { using Real3 = Vec3; + /** + * @brief Sample a component of an out-of-domain field at a location and time. + * The field value will be used as part of a boundary condition calculation. + * @param m field component (e.g. `EX`) + * @param t time + * @param p patch index + * @param x3 cell-normalized location within the patch + * @return the field value + */ virtual real_t sample_exterior_field(int m, double t, int p, Real3 x3) = 0; + /** + * @brief Perform any operations that occur once per time step. + * @param t time + */ virtual void tick(double t) {} }; +/** + * @brief A constant pulse. Use this for open boundaries that have constant + * external fields. + * @tparam real_t + */ template struct ConstantPulse : PulseBase { From bd0f25f84d472495b25ec293f33ba2373b9db8e7 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 22 Jul 2026 14:49:42 -0400 Subject: [PATCH 092/106] radiating: set normal E to 0 --- src/libpsc/psc_bnd_fields/radiating.hxx | 47 +++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index 13318e32c..8dc755661 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -39,15 +39,48 @@ struct Radiating : FieldBcBase void apply_j_bcs(MfieldsState& mflds) override {} /** - * @brief Don't do anything to the electric field. This is technically wrong - * for higher-order particles, which can feel deeper into the ghost region. - * Even for 1st-order particles, which can feel the first layer of the - * transverse E ghosts, it's unclear if doing nothing is correct. Those ghosts - * are self-consistently evolved from the transverse H ghosts and interior - * particle motion, at least. + * @brief Set the normal E to 0. They could be self-consistently evolved + * instead, but ghost corners aren't handled yet. Transverse components are + * deep enough to not affect 1st-order particles. * @param mflds fields */ - void apply_e_bcs(MfieldsState& mflds) override {} + void apply_e_bcs(MfieldsState& mflds) override + { + const Grid_t& grid = mflds.grid(); + int d0 = d; + + for (int p = 0; p < mflds.n_patches(); p++) { + if (lohi == Lo && grid.atBoundaryLo(p, d)) { + auto F = make_Fields3d(mflds[p]); + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d0] = -1; + stop[d0] = start[d0] + 1; + + int E0 = EX + d0; + + for (Int3 i3 : VecRange(start, stop)) { + F(E0, i3) = 0.0; + } + } + + if (lohi == Hi && grid.atBoundaryHi(p, d)) { + auto F = make_Fields3d(mflds[p]); + + Int3 start = mflds.ib(); + Int3 stop = mflds.ib() + mflds.im(); + start[d0] = grid.ldims[d0]; + stop[d0] = start[d0] + 1; + + int E0 = EX + d0; + + for (Int3 i3 : VecRange(start, stop)) { + F(E0, i3) = 0.0; + } + } + } + } /** * @brief Set the first layer of transverse H ghosts such that the inflowing S From 3a8c0d81b77c2e5542c632f039c397f3e83d3de9 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 11:17:02 -0400 Subject: [PATCH 093/106] radiating: sample ghost E --- src/libpsc/psc_bnd_fields/radiating.hxx | 32 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/libpsc/psc_bnd_fields/radiating.hxx b/src/libpsc/psc_bnd_fields/radiating.hxx index 8dc755661..f61eabc65 100644 --- a/src/libpsc/psc_bnd_fields/radiating.hxx +++ b/src/libpsc/psc_bnd_fields/radiating.hxx @@ -47,7 +47,14 @@ struct Radiating : FieldBcBase void apply_e_bcs(MfieldsState& mflds) override { const Grid_t& grid = mflds.grid(); - int d0 = d; + pulse.tick(grid.time()); + + int d0 = d, d1 = d.next(), d2 = d.prev(); + int E0 = EX + d0, E1 = EX + d1, E2 = EX + d2; + + Int3 d0hat = Int3::unit(d0); + Int3 d1hat = Int3::unit(d1); + Int3 d2hat = Int3::unit(d2); for (int p = 0; p < mflds.n_patches(); p++) { if (lohi == Lo && grid.atBoundaryLo(p, d)) { @@ -58,25 +65,34 @@ struct Radiating : FieldBcBase start[d0] = -1; stop[d0] = start[d0] + 1; - int E0 = EX + d0; - for (Int3 i3 : VecRange(start, stop)) { - F(E0, i3) = 0.0; + Real3 x_e1 = + (Real3(i3) + Real3(d1hat) * real_t(0.5)) * Real3(grid.domain.dx); + Real3 x_e2 = + (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); + + F(E0, i3 - d0hat) = 0.0; + F(E1, i3) = pulse.sample_exterior_field(E1, grid.time(), p, x_e1); + F(E2, i3) = pulse.sample_exterior_field(E2, grid.time(), p, x_e2); } } - if (lohi == Hi && grid.atBoundaryHi(p, d)) { auto F = make_Fields3d(mflds[p]); Int3 start = mflds.ib(); Int3 stop = mflds.ib() + mflds.im(); - start[d0] = grid.ldims[d0]; + start[d0] = grid.ldims[d0] + 1; stop[d0] = start[d0] + 1; - int E0 = EX + d0; - for (Int3 i3 : VecRange(start, stop)) { + Real3 x_e1 = + (Real3(i3) + Real3(d1hat) * real_t(0.5)) * Real3(grid.domain.dx); + Real3 x_e2 = + (Real3(i3) + Real3(d2hat) * real_t(0.5)) * Real3(grid.domain.dx); + F(E0, i3) = 0.0; + F(E1, i3) = pulse.sample_exterior_field(E1, grid.time(), p, x_e1); + F(E2, i3) = pulse.sample_exterior_field(E2, grid.time(), p, x_e2); } } } From 2102c8a26f2e8d4d5fa0b41ba6236285506910a2 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 12:31:31 -0400 Subject: [PATCH 094/106] boundary_injector: fix Real3 --- src/include/boundary_injector.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index bb1733d57..8de9cba2f 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -117,7 +117,7 @@ public: typename Current::fields_t J(flds); for (Int3 initial_idx : VecRange(ilo, ihi)) { - Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; + Real3 cell_corner = Real3(initial_idx) * grid.domain.dx; int n_prts_to_try_inject = get_n_in_cell(density, grid.norm.prts_per_unit_density, true); @@ -169,7 +169,7 @@ public: typename Current::fields_t J(flds); for (Int3 initial_idx : VecRange(ilo, ihi)) { - Real3 cell_corner = Double3(initial_idx) * grid.domain.dx; + Real3 cell_corner = Real3(initial_idx) * grid.domain.dx; int n_prts_to_try_inject = get_n_in_cell(density, grid.norm.prts_per_unit_density, true); From 15b9baca2b0af25345730e35646e6af32110755c Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 12:34:03 -0400 Subject: [PATCH 095/106] boundary_injector: preacceleration v2, energy-based --- src/include/boundary_injector.hxx | 81 +++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 8de9cba2f..cb66efb4e 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -81,6 +81,7 @@ public: using MfieldsState = typename PushParticles::MfieldsState; using Current = typename PushParticles::Current; using real_t = typename PushParticles::real_t; + using InterpolateEM_t = typename PushParticles::InterpolateEM_t; using Real3 = Vec3; BoundaryInjector(ParticleGenerator particle_generator) @@ -115,29 +116,89 @@ public: auto&& injector = injectors_by_patch[p]; auto flds = mflds[p]; typename Current::fields_t J(flds); + typename InterpolateEM_t::fields_t EM(flds.storage(), flds.ib()); + InterpolateEM_t ip; + AdvanceParticle advance{grid.dt}; for (Int3 initial_idx : VecRange(ilo, ihi)) { Real3 cell_corner = Real3(initial_idx) * grid.domain.dx; int n_prts_to_try_inject = get_n_in_cell(density, grid.norm.prts_per_unit_density, true); - Int3 inner_idx = initial_idx + Int3::unit(INJECT_DIM_IDX_); - real_t inner_e = mflds(EX + INJECT_DIM_IDX_, inner_idx[0], - inner_idx[1], inner_idx[2], p); - for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { + // 1. sample position uniformly from first ghost layer, and velocity + // from vdf at x=infty psc::particle::Inject prt = particle_generator_.get(cell_corner, grid.domain.dx); - // FIXME no factor of dt - why does this work? can it be better? - real_t du_from_e = - inner_e * grid.kinds[prt.kind].q / grid.kinds[prt.kind].m; - prt.u[INJECT_DIM_IDX_] += du_from_e; + real_t m = grid.kinds[prt.kind].m; + real_t q = grid.kinds[prt.kind].q; - AdvanceParticle advance{grid.dt}; - Real3 v = advance.calc_v(prt.u); Real3 initial_normalized_pos = prt.x * dxi; + + bool preaccelerate = true; + if (preaccelerate) { + // 2. calculate change in energy from traveling from -infty to xp + real_t xp = initial_normalized_pos[INJECT_DIM_IDX_]; + // x0 < xp < x1 and E0=E(x0), E1=E(x1) + real_t x0, x1; + real_t E0, E1; + + if (xp < -0.5) { + x0 = -1.5; + E0 = 0.0; + + x1 = -0.5; + } else { + x0 = -0.5; + ip.set_coeffs( + initial_normalized_pos.with_component(INJECT_DIM_IDX_, x0)); + switch (INJECT_DIM_IDX_) { + case 0: E0 = ip.ex(EM); break; + case 1: E0 = ip.ey(EM); break; + case 2: E0 = ip.ez(EM); break; + default: assert(false); + } + + x1 = 0.5; + } + ip.set_coeffs( + initial_normalized_pos.with_component(INJECT_DIM_IDX_, x1)); + switch (INJECT_DIM_IDX_) { + case 0: E1 = ip.ex(EM); break; + case 1: E1 = ip.ey(EM); break; + case 2: E1 = ip.ez(EM); break; + default: assert(false); + } + + real_t dx = grid.domain.dx[INJECT_DIM_IDX_]; + real_t work = + 0.5f * q * dx * + (E0 * (2 - sqr(xp - x0 - 1)) + E1 * sqr(xp - x1 + 1)); + + // 3. recalculate normal velocity based on work already done + real_t u_inf = prt.u[INJECT_DIM_IDX_]; + real_t gamma_inf = sqrt(1 + prt.u.mag2()); + real_t gamma_inf_perp = + sqrt(1 + prt.u.with_component(INJECT_DIM_IDX_, 0).mag2()); + real_t gamma_p = gamma_inf + work / m; + if (gamma_p < gamma_inf_perp) { + // particle didn't have enough energy to reach xp + continue; + } + // sign is a hack + prt.u[INJECT_DIM_IDX_] = + sqrt(sqr(gamma_p) - sqr(gamma_inf_perp)) * (u_inf < 0 ? -1 : 1); + prt.w *= u_inf * gamma_p / (prt.u[INJECT_DIM_IDX_] * gamma_inf); + + // 4. push normal u a half step + real_t E_interp = E0 * (1 - xp + x0) + E1 * (1 + xp - x1); + prt.u[INJECT_DIM_IDX_] += 0.5f * grid.dt * q * E_interp / m; + } + + // 5. push normal x + Real3 v = advance.calc_v(prt.u); advance.push_x(prt.x, v); if (prt.x[INJECT_DIM_IDX_] < 0.0) { From 026a472c5f7a3aec0bb75d0aca62ebde22b7faaa Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 12:05:23 -0400 Subject: [PATCH 096/106] boundary_injector: screw it, just kick them --- src/include/boundary_injector.hxx | 100 +++++++++++------------------- 1 file changed, 35 insertions(+), 65 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index cb66efb4e..241448344 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -104,6 +104,16 @@ public: Real3 dxi = grid.domain.dx_inv; Current current(grid); + bool preaccelerate = true; + real_t npp = 0.5; // number of plasma periods + + real_t plasma_freq_sq = 0.0; + for (Grid_t::Kind kind : grid.kinds) { + plasma_freq_sq += density / kind.m; + } + real_t plasma_period = 2.f * M_PI / sqrt(plasma_freq_sq); + real_t t_accel = npp * plasma_period; + for (int p = 0; p < grid.n_patches(); p++) { // TODO: combine paths as much as possible if (inject_lo && grid.atBoundaryLo(p, INJECT_DIM_IDX_)) { @@ -127,7 +137,7 @@ public: for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { - // 1. sample position uniformly from first ghost layer, and velocity + // sample position uniformly from first ghost layer, and velocity // from vdf at x=infty psc::particle::Inject prt = particle_generator_.get(cell_corner, grid.domain.dx); @@ -137,67 +147,22 @@ public: Real3 initial_normalized_pos = prt.x * dxi; - bool preaccelerate = true; if (preaccelerate) { - // 2. calculate change in energy from traveling from -infty to xp - real_t xp = initial_normalized_pos[INJECT_DIM_IDX_]; - // x0 < xp < x1 and E0=E(x0), E1=E(x1) - real_t x0, x1; - real_t E0, E1; - - if (xp < -0.5) { - x0 = -1.5; - E0 = 0.0; - - x1 = -0.5; - } else { - x0 = -0.5; - ip.set_coeffs( - initial_normalized_pos.with_component(INJECT_DIM_IDX_, x0)); - switch (INJECT_DIM_IDX_) { - case 0: E0 = ip.ex(EM); break; - case 1: E0 = ip.ey(EM); break; - case 2: E0 = ip.ez(EM); break; - default: assert(false); - } - - x1 = 0.5; - } - ip.set_coeffs( - initial_normalized_pos.with_component(INJECT_DIM_IDX_, x1)); + real_t E_interp; + + ip.set_coeffs(initial_normalized_pos); switch (INJECT_DIM_IDX_) { - case 0: E1 = ip.ex(EM); break; - case 1: E1 = ip.ey(EM); break; - case 2: E1 = ip.ez(EM); break; + case 0: E_interp = ip.ex(EM); break; + case 1: E_interp = ip.ey(EM); break; + case 2: E_interp = ip.ez(EM); break; default: assert(false); } - real_t dx = grid.domain.dx[INJECT_DIM_IDX_]; - real_t work = - 0.5f * q * dx * - (E0 * (2 - sqr(xp - x0 - 1)) + E1 * sqr(xp - x1 + 1)); - - // 3. recalculate normal velocity based on work already done - real_t u_inf = prt.u[INJECT_DIM_IDX_]; - real_t gamma_inf = sqrt(1 + prt.u.mag2()); - real_t gamma_inf_perp = - sqrt(1 + prt.u.with_component(INJECT_DIM_IDX_, 0).mag2()); - real_t gamma_p = gamma_inf + work / m; - if (gamma_p < gamma_inf_perp) { - // particle didn't have enough energy to reach xp - continue; - } - // sign is a hack - prt.u[INJECT_DIM_IDX_] = - sqrt(sqr(gamma_p) - sqr(gamma_inf_perp)) * (u_inf < 0 ? -1 : 1); - prt.w *= u_inf * gamma_p / (prt.u[INJECT_DIM_IDX_] * gamma_inf); - - // 4. push normal u a half step - real_t E_interp = E0 * (1 - xp + x0) + E1 * (1 + xp - x1); - prt.u[INJECT_DIM_IDX_] += 0.5f * grid.dt * q * E_interp / m; + real_t gamma = sqrt(1 + prt.u.mag2()); // time dilation + prt.u[INJECT_DIM_IDX_] += (t_accel * gamma) * q * E_interp / m; } - // 5. push normal x + // push normal x Real3 v = advance.calc_v(prt.u); advance.push_x(prt.x, v); @@ -211,9 +176,8 @@ public: injector.inject_local(prt); - real_t qni_wni = grid.kinds[prt.kind].q * prt.w; current.calc_j(J, initial_normalized_pos, final_normalized_pos, - final_idx, initial_idx, qni_wni, v); + final_idx, initial_idx, q * prt.w, v); } } } @@ -234,19 +198,25 @@ public: int n_prts_to_try_inject = get_n_in_cell(density, grid.norm.prts_per_unit_density, true); - Int3 inner_idx = initial_idx - Int3::unit(INJECT_DIM_IDX_); - real_t inner_e = mflds(EX + INJECT_DIM_IDX_, inner_idx[0], - inner_idx[1], inner_idx[2], p); - for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { psc::particle::Inject prt = particle_generator_.get(cell_corner, grid.domain.dx); - // FIXME no factor of dt - why does this work? can it be better? - real_t du_from_e = - inner_e * grid.kinds[prt.kind].q / grid.kinds[prt.kind].m; - prt.u[INJECT_DIM_IDX_] += du_from_e; + if (preaccelerate) { + real_t E_interp; + + ip.set_coeffs(initial_normalized_pos); + switch (INJECT_DIM_IDX_) { + case 0: E_interp = ip.ex(EM); break; + case 1: E_interp = ip.ey(EM); break; + case 2: E_interp = ip.ez(EM); break; + default: assert(false); + } + + real_t gamma = sqrt(1 + prt.u.mag2()); // time dilation + prt.u[INJECT_DIM_IDX_] += (t_accel * gamma) * q * E_interp / m; + } AdvanceParticle advance{grid.dt}; Real3 v = advance.calc_v(prt.u); From e3ac8565ab07566a0b5cacd75ade64563494f245 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 11:56:59 -0400 Subject: [PATCH 097/106] boundary_injector: hi matches lo --- src/include/boundary_injector.hxx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 241448344..53916e713 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -192,6 +192,9 @@ public: auto&& injector = injectors_by_patch[p]; auto flds = mflds[p]; typename Current::fields_t J(flds); + typename InterpolateEM_t::fields_t EM(flds.storage(), flds.ib()); + InterpolateEM_t ip; + AdvanceParticle advance{grid.dt}; for (Int3 initial_idx : VecRange(ilo, ihi)) { Real3 cell_corner = Real3(initial_idx) * grid.domain.dx; @@ -200,9 +203,16 @@ public: for (int prt_count = 0; prt_count < n_prts_to_try_inject; prt_count++) { + // sample position uniformly from first ghost layer, and velocity + // from vdf at x=infty psc::particle::Inject prt = particle_generator_.get(cell_corner, grid.domain.dx); + real_t m = grid.kinds[prt.kind].m; + real_t q = grid.kinds[prt.kind].q; + + Real3 initial_normalized_pos = prt.x * dxi; + if (preaccelerate) { real_t E_interp; @@ -218,10 +228,10 @@ public: prt.u[INJECT_DIM_IDX_] += (t_accel * gamma) * q * E_interp / m; } - AdvanceParticle advance{grid.dt}; + // push normal x Real3 v = advance.calc_v(prt.u); - Real3 initial_normalized_pos = prt.x * dxi; advance.push_x(prt.x, v); + Real3 final_normalized_pos = prt.x * dxi; Int3 final_idx = final_normalized_pos.fint(); @@ -232,9 +242,8 @@ public: injector.inject_local(prt); - real_t qni_wni = grid.kinds[prt.kind].q * prt.w; current.calc_j(J, initial_normalized_pos, final_normalized_pos, - final_idx, initial_idx, qni_wni, v); + final_idx, initial_idx, q * prt.w, v); } } } From ce5d22b4c6a143c89c1976528150f4ac15c51d9e Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 12:32:42 -0400 Subject: [PATCH 098/106] boundary_injector; *: template by LOHI --- src/include/boundary_injector.hxx | 94 ++++----------------- src/libpsc/tests/test_boundary_injector.cxx | 12 +-- src/psc_shock.cxx | 37 ++++---- 3 files changed, 38 insertions(+), 105 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index 53916e713..cb5224afa 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -11,8 +11,11 @@ #include "setup_particles.hxx" #include "kg/VecRange.hxx" #include "../libpsc/psc_push_particles/inc_push.cxx" +#include "../libpsc/psc_bnd/psc_bnd_util.hxx" #include "injector_base.hxx" +using psc::bnd::LoHi; + /// @brief A particle generator for use with @ref BoundaryInjector. Samples /// particles from a (possibly shifted) Maxwellian distribution. class ParticleGeneratorMaxwellian @@ -61,12 +64,13 @@ private: /// @brief Injects particles on a given boundary, sampling from a given particle /// generator. For precise control over multiple particle species, use one /// BoundaryInjector per species. +/// @tparam LOHI whether to inject at the lower or upper boundary /// @tparam PARTICLE_GENERATOR a type that defines `get(min_pos, pos_range)` and /// returns an injectable particle within that range of positions (usually a /// grid cell); see @ref ParticleGeneratorMaxwellian /// @tparam PUSH_PARTICLES type that provides the types `Mparticles`, /// `MfieldsState`, `Current`, `real_t`, etc. -template +template class BoundaryInjector : public InjectorBase @@ -84,6 +88,8 @@ public: using InterpolateEM_t = typename PushParticles::InterpolateEM_t; using Real3 = Vec3; + static const bool lo = LOHI == LoHi::Lo; + BoundaryInjector(ParticleGenerator particle_generator) : particle_generator_{particle_generator} {} @@ -115,14 +121,8 @@ public: real_t t_accel = npp * plasma_period; for (int p = 0; p < grid.n_patches(); p++) { - // TODO: combine paths as much as possible - if (inject_lo && grid.atBoundaryLo(p, INJECT_DIM_IDX_)) { - Int3 ilo = {0, 0, 0}; - Int3 ihi = grid.ldims; - - ilo[INJECT_DIM_IDX_] = -1; - ihi[INJECT_DIM_IDX_] = 0; - + if (lo ? grid.atBoundaryLo(p, INJECT_DIM_IDX_) + : grid.atBoundaryHi(p, INJECT_DIM_IDX_)) { auto&& injector = injectors_by_patch[p]; auto flds = mflds[p]; typename Current::fields_t J(flds); @@ -130,73 +130,12 @@ public: InterpolateEM_t ip; AdvanceParticle advance{grid.dt}; - for (Int3 initial_idx : VecRange(ilo, ihi)) { - Real3 cell_corner = Real3(initial_idx) * grid.domain.dx; - int n_prts_to_try_inject = - get_n_in_cell(density, grid.norm.prts_per_unit_density, true); - - for (int prt_count = 0; prt_count < n_prts_to_try_inject; - prt_count++) { - // sample position uniformly from first ghost layer, and velocity - // from vdf at x=infty - psc::particle::Inject prt = - particle_generator_.get(cell_corner, grid.domain.dx); - - real_t m = grid.kinds[prt.kind].m; - real_t q = grid.kinds[prt.kind].q; - - Real3 initial_normalized_pos = prt.x * dxi; - - if (preaccelerate) { - real_t E_interp; - - ip.set_coeffs(initial_normalized_pos); - switch (INJECT_DIM_IDX_) { - case 0: E_interp = ip.ex(EM); break; - case 1: E_interp = ip.ey(EM); break; - case 2: E_interp = ip.ez(EM); break; - default: assert(false); - } - - real_t gamma = sqrt(1 + prt.u.mag2()); // time dilation - prt.u[INJECT_DIM_IDX_] += (t_accel * gamma) * q * E_interp / m; - } - - // push normal x - Real3 v = advance.calc_v(prt.u); - advance.push_x(prt.x, v); - - if (prt.x[INJECT_DIM_IDX_] < 0.0) { - // don't inject a particle that fails to enter the patch - continue; - } - - Real3 final_normalized_pos = prt.x * dxi; - Int3 final_idx = final_normalized_pos.fint(); - - injector.inject_local(prt); - - current.calc_j(J, initial_normalized_pos, final_normalized_pos, - final_idx, initial_idx, q * prt.w, v); - } - } - } - - if (inject_hi && grid.atBoundaryHi(p, INJECT_DIM_IDX_)) { - Int3 ilo = {0, 0, 0}; - Int3 ihi = grid.ldims; - - ilo[INJECT_DIM_IDX_] = grid.ldims[INJECT_DIM_IDX_]; - ihi[INJECT_DIM_IDX_] = grid.ldims[INJECT_DIM_IDX_] + 1; - - auto&& injector = injectors_by_patch[p]; - auto flds = mflds[p]; - typename Current::fields_t J(flds); - typename InterpolateEM_t::fields_t EM(flds.storage(), flds.ib()); - InterpolateEM_t ip; - AdvanceParticle advance{grid.dt}; + Int3 start = Int3{0, 0, 0}.with_component( + INJECT_DIM_IDX_, lo ? -1 : grid.ldims[INJECT_DIM_IDX_]); + Int3 stop = grid.ldims.with_component(INJECT_DIM_IDX_, + start[INJECT_DIM_IDX_] + 1); - for (Int3 initial_idx : VecRange(ilo, ihi)) { + for (Int3 initial_idx : VecRange(start, stop)) { Real3 cell_corner = Real3(initial_idx) * grid.domain.dx; int n_prts_to_try_inject = get_n_in_cell(density, grid.norm.prts_per_unit_density, true); @@ -235,7 +174,8 @@ public: Real3 final_normalized_pos = prt.x * dxi; Int3 final_idx = final_normalized_pos.fint(); - if (final_idx[INJECT_DIM_IDX_] >= initial_idx[INJECT_DIM_IDX_]) { + if (lo ? final_idx[INJECT_DIM_IDX_] <= start[INJECT_DIM_IDX_] + : final_idx[INJECT_DIM_IDX_] >= start[INJECT_DIM_IDX_]) { // don't inject a particle that fails to enter the patch continue; } @@ -252,8 +192,6 @@ public: public: real_t density = 1.0; - bool inject_lo = true; - bool inject_hi = false; private: ParticleGenerator particle_generator_; diff --git a/src/libpsc/tests/test_boundary_injector.cxx b/src/libpsc/tests/test_boundary_injector.cxx index a1f3b5777..86a45f472 100644 --- a/src/libpsc/tests/test_boundary_injector.cxx +++ b/src/libpsc/tests/test_boundary_injector.cxx @@ -130,9 +130,9 @@ TEST(BoundaryInjectorTest, Integration1Particle) auto psc = makePscIntegrator(psc_params, grid, mflds, mprts, balance, collision, checks); - psc.add_injector( - new BoundaryInjector( - ParticleGenerator(1, 1))); + psc.add_injector(new BoundaryInjector( + ParticleGenerator(1, 1))); // ---------------------------------------------------------------------- // set up initial conditions @@ -188,7 +188,7 @@ TEST(BoundaryInjectorTest, IntegrationManyParticles) balance, collision, checks); psc.add_injector( - new BoundaryInjector( + new BoundaryInjector( ParticleGenerator(-1, 1))); // ---------------------------------------------------------------------- @@ -242,10 +242,10 @@ TEST(BoundaryInjectorTest, IntegrationManySpecies) Collision collision{grid, 0, 0.1}; auto inject_electrons = - BoundaryInjector{ + BoundaryInjector{ ParticleGenerator(-1, 0)}; auto inject_ions = - BoundaryInjector{ + BoundaryInjector{ ParticleGenerator(-1, 1)}; auto psc = makePscIntegrator(psc_params, grid, mflds, mprts, diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 39c5c779c..43ef26a27 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -932,34 +932,29 @@ static void run(int argc, char** argv) int oute_interval = -100; DiagEnergies oute{grid.comm(), oute_interval}; - auto ion_injector_lo = - BoundaryInjector( - ParticleGeneratorMaxwellian(KIND_ION, grid.kinds[KIND_ION], v_upstream, - {ti_upstream, ti_upstream, ti_upstream})); + auto ion_injector_lo = BoundaryInjector( + ParticleGeneratorMaxwellian(KIND_ION, grid.kinds[KIND_ION], v_upstream, + {ti_upstream, ti_upstream, ti_upstream})); ion_injector_lo.density = n_upstream; auto electron_injector_lo = - BoundaryInjector( - ParticleGeneratorMaxwellian(KIND_ELECTRON, grid.kinds[KIND_ELECTRON], - v_upstream, - {te_upstream, te_upstream, te_upstream})); + BoundaryInjector(ParticleGeneratorMaxwellian( + KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_upstream, + {te_upstream, te_upstream, te_upstream})); electron_injector_lo.density = n_upstream; - auto ion_injector_hi = - BoundaryInjector( - ParticleGeneratorMaxwellian( - KIND_ION, grid.kinds[KIND_ION], v_downstream, - {ti_downstream, ti_downstream, ti_downstream})); + auto ion_injector_hi = BoundaryInjector( + ParticleGeneratorMaxwellian(KIND_ION, grid.kinds[KIND_ION], v_downstream, + {ti_downstream, ti_downstream, ti_downstream})); ion_injector_hi.density = n_downstream; - ion_injector_hi.inject_lo = false; - ion_injector_hi.inject_hi = true; auto electron_injector_hi = - BoundaryInjector( - ParticleGeneratorMaxwellian( - KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_downstream, - {te_downstream, te_downstream, te_downstream})); + BoundaryInjector(ParticleGeneratorMaxwellian( + KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_downstream, + {te_downstream, te_downstream, te_downstream})); electron_injector_hi.density = n_downstream; - electron_injector_hi.inject_lo = false; - electron_injector_hi.inject_hi = true; // ---------------------------------------------------------------------- // set up initial conditions From f3869367a3b50ac8c35310603cc627f3f8fa424c Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 11:41:02 -0400 Subject: [PATCH 099/106] boundary_injector: take density as optional ctor arg --- src/include/boundary_injector.hxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index cb5224afa..c5d52b012 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -90,8 +90,8 @@ public: static const bool lo = LOHI == LoHi::Lo; - BoundaryInjector(ParticleGenerator particle_generator) - : particle_generator_{particle_generator} + BoundaryInjector(ParticleGenerator particle_generator, real_t density = 1.0) + : particle_generator_{particle_generator}, density{density} {} /// Injects particles at specified y-bounds as if there were a population of @@ -191,7 +191,7 @@ public: } public: - real_t density = 1.0; + real_t density; private: ParticleGenerator particle_generator_; From c1f5f90631913955a3f51a96ee3568b5190126c7 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 28 Jul 2026 11:22:06 -0400 Subject: [PATCH 100/106] boundary_injector: get E from edge --- src/include/boundary_injector.hxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index c5d52b012..de75ab725 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -155,7 +155,8 @@ public: if (preaccelerate) { real_t E_interp; - ip.set_coeffs(initial_normalized_pos); + ip.set_coeffs(initial_normalized_pos.with_component( + INJECT_DIM_IDX_, start[INJECT_DIM_IDX_] + (lo ? 1 : 0))); switch (INJECT_DIM_IDX_) { case 0: E_interp = ip.ex(EM); break; case 1: E_interp = ip.ey(EM); break; From be8e594435867544d6e53bd7052081e6e5828028 Mon Sep 17 00:00:00 2001 From: James McClung Date: Wed, 5 Aug 2026 15:20:44 -0400 Subject: [PATCH 101/106] boundary_injector: drop gamma factor --- src/include/boundary_injector.hxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/include/boundary_injector.hxx b/src/include/boundary_injector.hxx index de75ab725..24be69e67 100644 --- a/src/include/boundary_injector.hxx +++ b/src/include/boundary_injector.hxx @@ -164,8 +164,7 @@ public: default: assert(false); } - real_t gamma = sqrt(1 + prt.u.mag2()); // time dilation - prt.u[INJECT_DIM_IDX_] += (t_accel * gamma) * q * E_interp / m; + prt.u[INJECT_DIM_IDX_] += t_accel * q * E_interp / m; } // push normal x From ea388f3ce68f8f8e9fbc2efd6283bd45c1d7a54e Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 28 Jul 2026 08:51:35 -0400 Subject: [PATCH 102/106] ->psc_particle_injectors/...; * --- src/include/psc.hxx | 2 +- .../psc_particle_injectors}/boundary_injector.hxx | 4 ++-- .../psc_particle_injectors}/injector_base.hxx | 0 src/libpsc/tests/test_boundary_injector.cxx | 3 +-- src/psc_shock.cxx | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) rename src/{include => libpsc/psc_particle_injectors}/boundary_injector.hxx (98%) rename src/{include => libpsc/psc_particle_injectors}/injector_base.hxx (100%) diff --git a/src/include/psc.hxx b/src/include/psc.hxx index e3650e204..4c7e77294 100644 --- a/src/include/psc.hxx +++ b/src/include/psc.hxx @@ -9,9 +9,9 @@ #include "../libpsc/psc_bnd_fields/field_bc_base.hxx" #include "../libpsc/psc_bnd_fields/conducting_wall.hxx" +#include "../libpsc/psc_particle_injectors/injector_base.hxx" #include "gauss_corrector_base.hxx" #include "diagnostic_base.hxx" -#include "injector_base.hxx" #include "external_current_base.hxx" #include #include diff --git a/src/include/boundary_injector.hxx b/src/libpsc/psc_particle_injectors/boundary_injector.hxx similarity index 98% rename from src/include/boundary_injector.hxx rename to src/libpsc/psc_particle_injectors/boundary_injector.hxx index 24be69e67..84f4ee32e 100644 --- a/src/include/boundary_injector.hxx +++ b/src/libpsc/psc_particle_injectors/boundary_injector.hxx @@ -10,8 +10,8 @@ #include "dim.hxx" #include "setup_particles.hxx" #include "kg/VecRange.hxx" -#include "../libpsc/psc_push_particles/inc_push.cxx" -#include "../libpsc/psc_bnd/psc_bnd_util.hxx" +#include "../psc_push_particles/inc_push.cxx" +#include "../psc_bnd/psc_bnd_util.hxx" #include "injector_base.hxx" using psc::bnd::LoHi; diff --git a/src/include/injector_base.hxx b/src/libpsc/psc_particle_injectors/injector_base.hxx similarity index 100% rename from src/include/injector_base.hxx rename to src/libpsc/psc_particle_injectors/injector_base.hxx diff --git a/src/libpsc/tests/test_boundary_injector.cxx b/src/libpsc/tests/test_boundary_injector.cxx index 86a45f472..ffeb3e04a 100644 --- a/src/libpsc/tests/test_boundary_injector.cxx +++ b/src/libpsc/tests/test_boundary_injector.cxx @@ -2,11 +2,10 @@ #include "test_common.hxx" -#include "boundary_injector.hxx" - #include "psc.hxx" #include "output_fields.hxx" #include "../psc_config.hxx" +#include "../psc_particle_injectors/boundary_injector.hxx" TEST(BoundaryInjectorTest, ParticleGeneratorMaxwellianTest) { diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 43ef26a27..cff3edb28 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -4,11 +4,11 @@ #include "output_fields.hxx" #include "psc_config.hxx" -#include "include/boundary_injector.hxx" #include "input_params.hxx" #include "kg/include/kg/VecRange.hxx" #include "libpsc/psc_output_particles/output_particles_adios2_impl.hxx" #include "libpsc/psc_bnd_fields/radiating.hxx" +#include "libpsc/psc_particle_injectors/boundary_injector.hxx" #include "libpsc/axis.hxx" // ====================================================================== From e1a7b52e40a251a6e80df5b0e1f7641485916c58 Mon Sep 17 00:00:00 2001 From: James McClung Date: Tue, 30 Jun 2026 19:25:36 +0200 Subject: [PATCH 103/106] test_boundary_injector: fix u/v mixup --- src/libpsc/tests/test_boundary_injector.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libpsc/tests/test_boundary_injector.cxx b/src/libpsc/tests/test_boundary_injector.cxx index ffeb3e04a..3398df836 100644 --- a/src/libpsc/tests/test_boundary_injector.cxx +++ b/src/libpsc/tests/test_boundary_injector.cxx @@ -12,18 +12,19 @@ TEST(BoundaryInjectorTest, ParticleGeneratorMaxwellianTest) int kind_idx = 15; Grid_t::Kind kind{1.0, 1836.0, "ion"}; ParticleGeneratorMaxwellian::Real w = 1.0; - ParticleGeneratorMaxwellian::Real3 mean_u{0.0, 5.0, 15.0}; + ParticleGeneratorMaxwellian::Real3 mean_v{0.0, .05, .15}; ParticleGeneratorMaxwellian::Real3 temperature{0.0, 0.0, 0.0}; ParticleGeneratorMaxwellian::Real3 pos{1.0, 2.0, 5.0}; - ParticleGeneratorMaxwellian gen{kind_idx, kind, mean_u, temperature}; + ParticleGeneratorMaxwellian gen{kind_idx, kind, mean_v, temperature}; auto prt = gen.get(pos, {0.0, 0.0, 0.0}); ASSERT_EQ(prt.kind, kind_idx); ASSERT_EQ(prt.w, w); ASSERT_EQ(prt.tag, 0); - ASSERT_EQ(prt.u, mean_u); // zero temperature => exact velocity + ASSERT_EQ(prt.u / sqrt(1 + prt.u.mag2()), + mean_v); // zero temperature => exact velocity ASSERT_EQ(prt.x, pos); } From 6a2e6911dcca2e4814346e25fd80546dc54104a9 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 23 Jul 2026 15:11:09 -0400 Subject: [PATCH 104/106] psc_shock: print time when cycle turb --- src/psc_shock.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index cff3edb28..2ef2efc4f 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -836,7 +836,7 @@ struct AdvectedPeriodicFields : psc::bnd::field::PulseBase void cycle_turbulence(int n_patches) { - LOG_INFO("cycling turbulence...\n"); + LOG_INFO("cycling turbulence... (t=%f)\n", grid.time()); // hack: guess the rank based on how mrc does it for simple domains // (can't use mrc, because it wouldn't apply periodicity) From 34002aa00d82501b1ab39c835b7f594130eeb841 Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 11:41:14 -0400 Subject: [PATCH 105/106] psc_shock: density in ctor --- src/psc_shock.cxx | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/psc_shock.cxx b/src/psc_shock.cxx index 2ef2efc4f..0f39645f4 100644 --- a/src/psc_shock.cxx +++ b/src/psc_shock.cxx @@ -935,26 +935,28 @@ static void run(int argc, char** argv) auto ion_injector_lo = BoundaryInjector( ParticleGeneratorMaxwellian(KIND_ION, grid.kinds[KIND_ION], v_upstream, - {ti_upstream, ti_upstream, ti_upstream})); - ion_injector_lo.density = n_upstream; + {ti_upstream, ti_upstream, ti_upstream}), + n_upstream); auto electron_injector_lo = BoundaryInjector(ParticleGeneratorMaxwellian( - KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_upstream, - {te_upstream, te_upstream, te_upstream})); - electron_injector_lo.density = n_upstream; + PscConfig::PushParticles>( + ParticleGeneratorMaxwellian(KIND_ELECTRON, grid.kinds[KIND_ELECTRON], + v_upstream, + {te_upstream, te_upstream, te_upstream}), + n_upstream); auto ion_injector_hi = BoundaryInjector( ParticleGeneratorMaxwellian(KIND_ION, grid.kinds[KIND_ION], v_downstream, - {ti_downstream, ti_downstream, ti_downstream})); - ion_injector_hi.density = n_downstream; + {ti_downstream, ti_downstream, ti_downstream}), + n_downstream); auto electron_injector_hi = BoundaryInjector(ParticleGeneratorMaxwellian( - KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_downstream, - {te_downstream, te_downstream, te_downstream})); - electron_injector_hi.density = n_downstream; + PscConfig::PushParticles>( + ParticleGeneratorMaxwellian( + KIND_ELECTRON, grid.kinds[KIND_ELECTRON], v_downstream, + {te_downstream, te_downstream, te_downstream}), + n_downstream); // ---------------------------------------------------------------------- // set up initial conditions From 261690ecf618bd15b36e39c4b43e88e5c8927f0e Mon Sep 17 00:00:00 2001 From: James McClung Date: Thu, 6 Aug 2026 14:43:43 -0400 Subject: [PATCH 106/106] psc: don't add conducting BC to CUDA mfields --- src/include/psc.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/include/psc.hxx b/src/include/psc.hxx index 4c7e77294..ac1dda9fe 100644 --- a/src/include/psc.hxx +++ b/src/include/psc.hxx @@ -149,6 +149,7 @@ struct Psc } } +#ifndef USE_CUDA for (int d = 0; d < 3; d++) { using psc::bnd::LoHi; using psc::bnd::field::ConductingWall; @@ -160,6 +161,7 @@ struct Psc add_field_bc(new ConductingWall{d, LoHi::Hi}); } } +#endif int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank);