diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/Diagnostics/ReducedDiags/FieldProbe.H | 40 | ||||
-rw-r--r-- | Source/Diagnostics/ReducedDiags/FieldProbe.cpp | 364 | ||||
-rw-r--r-- | Source/Diagnostics/ReducedDiags/FieldProbeParticleContainer.cpp | 3 | ||||
-rw-r--r-- | Source/Evolve/WarpXEvolve.cpp | 1 |
4 files changed, 315 insertions, 93 deletions
diff --git a/Source/Diagnostics/ReducedDiags/FieldProbe.H b/Source/Diagnostics/ReducedDiags/FieldProbe.H index 8028d987d..15c5d18f2 100644 --- a/Source/Diagnostics/ReducedDiags/FieldProbe.H +++ b/Source/Diagnostics/ReducedDiags/FieldProbe.H @@ -17,6 +17,17 @@ #include <unordered_map> #include <string> +#include <vector> + +/** + * This enumeration is used for assigning structural geometry levels (point vs line vs plane) + */ +enum struct DetectorGeometry +{ + Point = 0, + Line, + Plane +}; /** * This class mainly contains a function that computes the value of each component @@ -52,18 +63,41 @@ public: * Define constants used throughout FieldProbe */ - //! noutputs is 7 (Ex, Ey, Ez, Bx, By, Bz, S) - static constexpr int noutputs = FieldProbePIdx::nattribs; + //! noutputs is 10 (x, y, z, Ex, Ey, Ez, Bx, By, Bz, S) + static constexpr int noutputs = FieldProbePIdx::nattribs + 3; private: amrex::Real x_probe, y_probe, z_probe; + amrex::Real x1_probe, y1_probe, z1_probe; + amrex::Real target_normal_x, target_normal_y, target_normal_z; + amrex::Real target_up_x, target_up_y, target_up_z; + amrex::Real detector_radius; + + //! counts number of particles for all MPI ranks + long m_valid_particles {0}; + + //! determines geometry of detector point distribution + DetectorGeometry m_probe_geometry = DetectorGeometry::Point; + + //! determines number of particles places for non-point geometries + int m_resolution = 0; + + //! Empty vector for to which data is pushed + amrex::Vector<amrex::Real> m_data; + + //! Empty array to be used by IOProcessor node to store and output data + amrex::Vector<amrex::Real> m_data_out; //! this is the particle container in which probe particles are stored FieldProbeParticleContainer m_probe; - //! if true, integrate values over time instead of probing instantaneous values + //! if true, integrate values over time instead of probing instantaneous values bool m_field_probe_integrate = false; + + //! particle shape used for field gather int interp_order = 1; + + //! Judges whether to gather raw fields or interpolated data bool raw_fields = false; /** diff --git a/Source/Diagnostics/ReducedDiags/FieldProbe.cpp b/Source/Diagnostics/ReducedDiags/FieldProbe.cpp index d29795c35..3abf83347 100644 --- a/Source/Diagnostics/ReducedDiags/FieldProbe.cpp +++ b/Source/Diagnostics/ReducedDiags/FieldProbe.cpp @@ -31,9 +31,13 @@ #include <AMReX_Vector.H> #include <cmath> +#include <cstddef> +#include <cstdlib> +#include <iostream> #include <ostream> #include <string> #include <unordered_map> +#include <vector> using namespace amrex; @@ -72,18 +76,55 @@ FieldProbe::FieldProbe (std::string rd_name) * Define whether ot not to integrate fields */ amrex::ParmParse pp_rd_name(rd_name); - getWithParser(pp_rd_name, "x_probe", x_probe); - getWithParser(pp_rd_name, "y_probe", y_probe); - getWithParser(pp_rd_name, "z_probe", z_probe); + std::string m_probe_geometry_str = "Point"; + pp_rd_name.query("probe_geometry", m_probe_geometry_str); + + if (m_probe_geometry_str == "Point") + { + m_probe_geometry = DetectorGeometry::Point; + getWithParser(pp_rd_name, "x_probe", x_probe); + getWithParser(pp_rd_name, "y_probe", y_probe); + getWithParser(pp_rd_name, "z_probe", z_probe); + } + else if (m_probe_geometry_str == "Line") + { + m_probe_geometry = DetectorGeometry::Line; + getWithParser(pp_rd_name, "x_probe", x_probe); + getWithParser(pp_rd_name, "y_probe", y_probe); + getWithParser(pp_rd_name, "z_probe", z_probe); + getWithParser(pp_rd_name, "x1_probe", x1_probe); + getWithParser(pp_rd_name, "y1_probe", y1_probe); + getWithParser(pp_rd_name, "z1_probe", z1_probe); + getWithParser(pp_rd_name, "resolution", m_resolution); + } + else if (m_probe_geometry_str == "Plane") + { + m_probe_geometry = DetectorGeometry::Plane; + getWithParser(pp_rd_name, "x_probe", x_probe); + getWithParser(pp_rd_name, "y_probe", y_probe); + getWithParser(pp_rd_name, "z_probe", z_probe); + getWithParser(pp_rd_name, "target_normal_x", target_normal_x); + getWithParser(pp_rd_name, "target_normal_y", target_normal_y); + getWithParser(pp_rd_name, "target_normal_z", target_normal_z); + getWithParser(pp_rd_name, "target_up_x", target_up_x); + getWithParser(pp_rd_name, "target_up_y", target_up_y); + getWithParser(pp_rd_name, "target_up_z", target_up_z); + getWithParser(pp_rd_name, "detector_radius", detector_radius); + getWithParser(pp_rd_name, "resolution", m_resolution); + } + else + { + std::string err_str = "ERROR: Invalid probe geometry '"; + err_str.append(m_probe_geometry_str); + err_str.append("'. Valid geometries are Point, Line or Plane."); + amrex::Abort(err_str); + } pp_rd_name.query("integrate", m_field_probe_integrate); pp_rd_name.query("raw_fields", raw_fields); pp_rd_name.query("interp_order", interp_order); AMREX_ALWAYS_ASSERT_WITH_MESSAGE(interp_order <= WarpX::nox , "Field probe interp_order should be less than or equal to algo.particle_shape"); - // resize data array - m_data.resize(noutputs, 0.0_rt); - if (ParallelDescriptor::IOProcessor()) { if ( m_IsNotRestart ) @@ -93,7 +134,6 @@ FieldProbe::FieldProbe (std::string rd_name) // write header row int c = 0; - ofs << "#"; ofs << "[" << c++ << "]step()"; ofs << m_sep; ofs << "[" << c++ << "]time(s)"; @@ -104,44 +144,50 @@ FieldProbe::FieldProbe (std::string rd_name) { u_map = { - {FieldProbePIdx::Ex , " (V*s/m) "}, - {FieldProbePIdx::Ey , " (V*s/m) "}, - {FieldProbePIdx::Ez , " (V*s/m) "}, - {FieldProbePIdx::Bx , " (T*s) "}, - {FieldProbePIdx::By , " (T*s) "}, - {FieldProbePIdx::Bz , " (T*s) "}, - {FieldProbePIdx::S , " (W*s/m^2) "} + {FieldProbePIdx::Ex , "-(V*s/m)"}, + {FieldProbePIdx::Ey , "-(V*s/m)"}, + {FieldProbePIdx::Ez , "-(V*s/m)"}, + {FieldProbePIdx::Bx , "-(T*s)"}, + {FieldProbePIdx::By , "-(T*s)"}, + {FieldProbePIdx::Bz , "-(T*s)"}, + {FieldProbePIdx::S , "-(W*s/m^2)"} }; } else { u_map = { - {FieldProbePIdx::Ex , " (V/m) "}, - {FieldProbePIdx::Ey , " (V/m) "}, - {FieldProbePIdx::Ez , " (V/m) "}, - {FieldProbePIdx::Bx , " (T) "}, - {FieldProbePIdx::By , " (T) "}, - {FieldProbePIdx::Bz , " (T) "}, - {FieldProbePIdx::S , " (W/m^2) "} + {FieldProbePIdx::Ex , "-(V/m)"}, + {FieldProbePIdx::Ey , "-(V/m)"}, + {FieldProbePIdx::Ez , "-(V/m)"}, + {FieldProbePIdx::Bx , "-(T)"}, + {FieldProbePIdx::By , "-(T)"}, + {FieldProbePIdx::Bz , "-(T)"}, + {FieldProbePIdx::S , "-(W/m^2)"} }; } for (int lev = 0; lev < nLevel; ++lev) { ofs << m_sep; - ofs << "[" << c++ << "]probe_Ex_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Ex]; + ofs << "[" << c++ << "]part_x_lev" + std::to_string(lev) + "-(m)"; + ofs << m_sep; + ofs << "[" << c++ << "]part_y_lev" + std::to_string(lev) + "-(m)"; + ofs << m_sep; + ofs << "[" << c++ << "]part_z_lev" + std::to_string(lev) + "-(m)"; + ofs << m_sep; + ofs << "[" << c++ << "]part_Ex_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Ex]; ofs << m_sep; - ofs << "[" << c++ << "]probe_Ey_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Ey]; + ofs << "[" << c++ << "]part_Ey_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Ey]; ofs << m_sep; - ofs << "[" << c++ << "]probe_Ez_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Ez]; + ofs << "[" << c++ << "]part_Ez_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Ez]; ofs << m_sep; - ofs << "[" << c++ << "]probe_Bx_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Bx]; + ofs << "[" << c++ << "]part_Bx_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Bx]; ofs << m_sep; - ofs << "[" << c++ << "]probe_By_lev" + std::to_string(lev) + u_map[FieldProbePIdx::By]; + ofs << "[" << c++ << "]part_By_lev" + std::to_string(lev) + u_map[FieldProbePIdx::By]; ofs << m_sep; - ofs << "[" << c++ << "]probe_Bz_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Bz]; + ofs << "[" << c++ << "]part_Bz_lev" + std::to_string(lev) + u_map[FieldProbePIdx::Bz]; ofs << m_sep; - ofs << "[" << c++ << "]probe_S_lev" + std::to_string(lev) + u_map[FieldProbePIdx::S]; //update all units if integrating (might be energy / m^2) + ofs << "[" << c++ << "]part_S_lev" + std::to_string(lev) + u_map[FieldProbePIdx::S]; } ofs << std::endl; @@ -153,24 +199,111 @@ FieldProbe::FieldProbe (std::string rd_name) void FieldProbe::InitData () { - //create 1D array for X, Y, and Z of particles - amrex::Vector<amrex::ParticleReal> xpos; - amrex::Vector<amrex::ParticleReal> ypos; - amrex::Vector<amrex::ParticleReal> zpos; + if (m_probe_geometry == DetectorGeometry::Point) + { - // for now, only one MPI rank adds a probe particle - if (ParallelDescriptor::IOProcessor()) + // create 1D vector for X, Y, and Z of particles + amrex::Vector<amrex::ParticleReal> xpos; + amrex::Vector<amrex::ParticleReal> ypos; + amrex::Vector<amrex::ParticleReal> zpos; + + // for now, only one MPI rank adds a probe particle + if (ParallelDescriptor::IOProcessor()) + { + xpos.push_back(x_probe); + ypos.push_back(y_probe); + zpos.push_back(z_probe); + } + + // add particles on lev 0 to m_probe + m_probe.AddNParticles(0, xpos, ypos, zpos); + } + else if (m_probe_geometry == DetectorGeometry::Line) { - xpos.push_back(x_probe); - ypos.push_back(y_probe); - zpos.push_back(z_probe); + amrex::Vector<amrex::ParticleReal> xpos; + amrex::Vector<amrex::ParticleReal> ypos; + amrex::Vector<amrex::ParticleReal> zpos; + if (ParallelDescriptor::IOProcessor()) + { + xpos.reserve(m_resolution); + ypos.reserve(m_resolution); + zpos.reserve(m_resolution); + + // Final - initial / steps. Array contains dx, dy, dz + amrex::Real DetLineStepSize[3]{ + (x1_probe - x_probe) / (m_resolution - 1), + (y1_probe - y_probe) / (m_resolution - 1), + (z1_probe - z_probe) / (m_resolution - 1)}; + for ( int step = 0; step < m_resolution; step++) + { + xpos.push_back(x_probe + (DetLineStepSize[0] * step)); + ypos.push_back(y_probe + (DetLineStepSize[1] * step)); + zpos.push_back(z_probe + (DetLineStepSize[2] * step)); + } + } + m_probe.AddNParticles(0, xpos, ypos, zpos); + } + else if (m_probe_geometry == DetectorGeometry::Plane) + { + amrex::Vector<amrex::ParticleReal> xpos; + amrex::Vector<amrex::ParticleReal> ypos; + amrex::Vector<amrex::ParticleReal> zpos; + if (ParallelDescriptor::IOProcessor()) + { + std::size_t const res2 = std::size_t(m_resolution) * std::size_t(m_resolution); + xpos.reserve(res2); + ypos.reserve(res2); + zpos.reserve(res2); + + // create vector orthonormal to input vectors + amrex::Real orthotarget[3]{ + target_normal_y * target_up_z - target_normal_z * target_up_y, + target_normal_z * target_up_x - target_normal_x * target_up_z, + target_normal_x * target_up_y - target_normal_y * target_up_x}; + // find upper left and lower right bounds of detector + amrex::Real direction[3]{ + orthotarget[0] - target_up_x, + orthotarget[1] - target_up_y, + orthotarget[2] - target_up_z}; + amrex::Real upperleft[3]{ + x_probe - (direction[0] * detector_radius), + y_probe - (direction[1] * detector_radius), + z_probe - (direction[2] * detector_radius)}; + amrex::Real lowerright[3]{ + x_probe + (direction[0] * detector_radius), + y_probe + (direction[1] * detector_radius), + z_probe + (direction[2] * detector_radius)}; + // create array containing point-to-point step size + amrex::Real DetPlaneStepSize[3]{ + (lowerright[0] - upperleft[0]) / (m_resolution - 1), + (lowerright[1] - upperleft[1]) / (m_resolution - 1), + (lowerright[2] - upperleft[2]) / (m_resolution - 1)}; + amrex::Real temp_pos[3]{}; + // Target point on top of plane (arbitrarily top of plane perpendicular to yz) + // For each point along top of plane, fill in YZ's beneath, then push back + for ( int step = 0; step < m_resolution; step++) + { + temp_pos[0] = upperleft[0] + (DetPlaneStepSize[0] * step); + for ( int yzstep = 0; yzstep < m_resolution; yzstep++) + { + temp_pos[1] = upperleft[1] + (DetPlaneStepSize[1] * yzstep); + temp_pos[2] = upperleft[2] + (DetPlaneStepSize[2] * yzstep); + xpos.push_back(temp_pos[0]); + ypos.push_back(temp_pos[1]); + zpos.push_back(temp_pos[2]); + } + } + } + m_probe.AddNParticles(0, xpos, ypos, zpos); + } + else + { + amrex::Abort("ERROR: Invalid probe geometry. Valid geometries are point (0) and line (1)."); } - - // add np particles on lev 0 to m_probe - m_probe.AddNParticles(0, xpos, ypos, zpos); } -void FieldProbe::LoadBalance() { +void FieldProbe::LoadBalance () +{ m_probe.Redistribute(); } @@ -239,15 +372,28 @@ void FieldProbe::ComputeDiags (int step) amrex::IndexType const Bytype = By.ixType(); amrex::IndexType const Bztype = Bz.ixType(); - //defined for use in determining which CPU contains the particles - int probe_proc = -1; - // loop over each particle // TODO: add OMP parallel as in PhysicalParticleContainer::Evolve + long numparticles = 0; // particles on this MPI rank using MyParIter = FieldProbeParticleContainer::iterator; for (MyParIter pti(m_probe, lev); pti.isValid(); ++pti) { + // count particle on MPI rank + numparticles += pti.numParticles(); + } + + if (m_intervals.contains(step+1)) + { + // reset m_data vector to clear pushed values. Reserves data + m_data.clear(); + m_data.shrink_to_fit(); + m_data.reserve(numparticles * noutputs); + } + + for (MyParIter pti(m_probe, lev); pti.isValid(); ++pti) + { const auto getPosition = GetParticlePosition(pti); + auto const np = pti.numParticles(); if( ProbeInDomain() ) { @@ -295,7 +441,6 @@ void FieldProbe::ComputeDiags (int step) const amrex::GpuArray<amrex::Real, 3> xyzmin_arr = {xyzmin[0], xyzmin[1], xyzmin[2]}; const Dim3 lo = lbound(box); - // Interpolating to the probe positions for each particle // Temporarily defining modes and interp outside ParallelFor to avoid GPU compilation errors. const int temp_modes = WarpX::n_rz_azimuthal_modes; const int temp_interp_order = interp_order; @@ -303,7 +448,7 @@ void FieldProbe::ComputeDiags (int step) const bool temp_field_probe_integrate = m_field_probe_integrate; amrex::Real const dt = WarpX::GetInstance().getdt(lev); - long const np = pti.numParticles(); + // Interpolating to the probe positions for each particle amrex::ParallelFor( np, [=] AMREX_GPU_DEVICE (long ip) { amrex::ParticleReal xp, yp, zp; @@ -354,7 +499,6 @@ void FieldProbe::ComputeDiags (int step) } else { - // Either save the interpolated fields or the raw fields depending on the raw_fields flag part_Ex[ip] = Exp; //remember to add lorentz transform part_Ey[ip] = Eyp; //remember to add lorentz transform part_Ez[ip] = Ezp; //remember to add lorentz transform @@ -364,63 +508,107 @@ void FieldProbe::ComputeDiags (int step) part_S[ip] = S; //remember to add lorentz transform } });// ParallelFor Close - // this check is here because for m_field_probe_integrate == True, we always compute // but we only write when we truly are in an output interval step - if (m_intervals.contains(step+1)) { - for (int ip = 0; ip < np; ip++) { - // Fill output array - m_data[ip * noutputs + FieldProbePIdx::Ex] = part_Ex[ip]; - m_data[ip * noutputs + FieldProbePIdx::Ey] = part_Ey[ip]; - m_data[ip * noutputs + FieldProbePIdx::Ez] = part_Ez[ip]; - m_data[ip * noutputs + FieldProbePIdx::Bx] = part_Bx[ip]; - m_data[ip * noutputs + FieldProbePIdx::By] = part_By[ip]; - m_data[ip * noutputs + FieldProbePIdx::Bz] = part_Bz[ip]; - m_data[ip * noutputs + FieldProbePIdx::S] = part_S[ip]; + if (m_intervals.contains(step+1)) + { + for (auto ip=0; ip < np; ip++) + { + amrex::ParticleReal xp, yp, zp; + getPosition(ip, xp, yp, zp); + + // push to output vector + m_data.push_back(xp); + m_data.push_back(yp); + m_data.push_back(zp); + m_data.push_back(part_Ex[ip]); + m_data.push_back(part_Ey[ip]); + m_data.push_back(part_Ez[ip]); + m_data.push_back(part_Bx[ip]); + m_data.push_back(part_By[ip]); + m_data.push_back(part_Bz[ip]); + m_data.push_back(part_S[ip]); } - /* m_data now contains up-to-date values for: - * [Ex, Ey, Ez, Bx, By, Bz, and S] */ + /* m_data now contains up-to-date values for: + * [x, y, z, Ex, Ey, Ez, Bx, By, Bz, and S] */ } - - // do we have the one and only probe particle on our MPI rank? - if (np > 0) - probe_proc = amrex::ParallelDescriptor::MyProc(); } - } // end particle iterator loop - - // make sure data is in m_data Gpu::synchronize(); + if (m_intervals.contains(step+1)) + { + // returns total number of mpi notes into mpisize + int mpisize = ParallelDescriptor::NProcs(); - // this check is here because for m_field_probe_integrate == True, we always compute - // but we only write when we truly are in an output interval step - if (m_intervals.contains(step+1)) { - /* - * All the processors have probe_proc = -1 except the one that contains the point, which - * has probe_proc equal to a number >=0. Therefore, ReduceIntMax communicates to all the - * processors the rank of the processor which contains the point - */ - amrex::ParallelDescriptor::ReduceIntMax(probe_proc); - - if (probe_proc != amrex::ParallelDescriptor::IOProcessorNumber() and probe_proc != -1) { - if (amrex::ParallelDescriptor::MyProc() == probe_proc) { - amrex::ParallelDescriptor::Send(m_data.data(), noutputs, - amrex::ParallelDescriptor::IOProcessorNumber(), - 0); - } - if (amrex::ParallelDescriptor::MyProc() - == amrex::ParallelDescriptor::IOProcessorNumber()) { - amrex::ParallelDescriptor::Recv(m_data.data(), noutputs, probe_proc, 0); + // allocates data space for length_array. Will contain size of m_data from each processor + amrex::Vector<int> length_vector; + amrex::Vector<int> localsize; + + if (amrex::ParallelDescriptor::IOProcessor()) { + length_vector.resize(mpisize, 0); + } + localsize.resize(1,0); + localsize[0] = m_data.size(); + + // gather size of m_data from each processor + amrex::ParallelDescriptor::Gather(localsize.data(), 1, + length_vector.data(), 1, + amrex::ParallelDescriptor::IOProcessorNumber()); + + // IO processor sums values from length_array to get size of total output array. + /* displs records the size of each m_data as well as previous displs. This array + * tells Gatherv where in the m_data_out array allocation to write incomming data. */ + long total_data_size = 0; + amrex::Vector<int> displs_vector; + if (amrex::ParallelDescriptor::IOProcessor()) { + displs_vector.resize(mpisize, 0); + displs_vector[0] = 0; + total_data_size += length_vector[0]; + for (int i=1; i<mpisize; i++) { + displs_vector[i] = (displs_vector[i-1] + length_vector[i-1]); + total_data_size += length_vector[i]; } + // valid particles are counted (for all MPI ranks) to inform output processes as to size of output + m_valid_particles = total_data_size / noutputs; + m_data_out.resize(total_data_size, 0); } - } // send to IO Processor + // resize receive buffer (resize, initialize 0) + // gather m_data of varied lengths from all processors. Prints to m_data_out + amrex::ParallelDescriptor::Gatherv(m_data.data(), localsize[0], + m_data_out.data(), length_vector, displs_vector, + amrex::ParallelDescriptor::IOProcessorNumber()); + } }// end loop over refinement levels + // make sure data is in m_data on the IOProcessor + // TODO: In the future, we want to use a parallel I/O method instead (plotfiles or openPMD) } // end void FieldProbe::ComputeDiags void FieldProbe::WriteToFile (int step) const { if (ProbeInDomain() && amrex::ParallelDescriptor::IOProcessor()) { - ReducedDiags::WriteToFile (step); + // open file + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; + + // loop over num valid particles and write + for (int i = 0; i < m_valid_particles; i++) + { + ofs << std::fixed << std::defaultfloat; + ofs << step + 1; + ofs << m_sep; + ofs << std::fixed << std::setprecision(14) << std::scientific; + // write time + ofs << WarpX::GetInstance().gett_new(0); + + for (int k = 0; k < noutputs; k++) + { + ofs << m_sep; + ofs << m_data_out[i * noutputs + k]; + } + ofs << std::endl; + } // end loop over data size + // close file + ofs.close(); } } diff --git a/Source/Diagnostics/ReducedDiags/FieldProbeParticleContainer.cpp b/Source/Diagnostics/ReducedDiags/FieldProbeParticleContainer.cpp index c72362b6b..111e97e96 100644 --- a/Source/Diagnostics/ReducedDiags/FieldProbeParticleContainer.cpp +++ b/Source/Diagnostics/ReducedDiags/FieldProbeParticleContainer.cpp @@ -108,8 +108,7 @@ FieldProbeParticleContainer::AddNParticles (int lev, #elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) amrex::ignore_unused(y) ; p.pos(0) = x[i]; - p.pos(1) = 0; - p.pos(2) = z[i]; + p.pos(1) = z[i]; #endif // write position, cpu id, and particle id to particle pinned_tile.push_back(p); diff --git a/Source/Evolve/WarpXEvolve.cpp b/Source/Evolve/WarpXEvolve.cpp index 547f632e9..c3c297b56 100644 --- a/Source/Evolve/WarpXEvolve.cpp +++ b/Source/Evolve/WarpXEvolve.cpp @@ -335,6 +335,7 @@ WarpX::Evolve (int numsteps) /// reduced diags if (reduced_diags->m_plot_rd != 0) { + reduced_diags->LoadBalance(); reduced_diags->ComputeDiags(step); reduced_diags->WriteToFile(step); } |