aboutsummaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp2
-rw-r--r--Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp18
-rw-r--r--Source/Diagnostics/SliceDiagnostic.cpp33
-rw-r--r--Source/Diagnostics/WarpXOpenPMD.cpp8
-rw-r--r--Source/Evolve/WarpXEvolve.cpp1
-rw-r--r--Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp19
-rw-r--r--Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp24
-rw-r--r--Source/Initialization/PlasmaInjector.cpp43
-rw-r--r--Source/Initialization/WarpXInitData.cpp10
-rw-r--r--Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp7
-rw-r--r--Source/Particles/LaserParticleContainer.cpp14
-rw-r--r--Source/Particles/MultiParticleContainer.cpp31
-rw-r--r--Source/Particles/PhysicalParticleContainer.cpp17
-rw-r--r--Source/Particles/Resampling/LevelingThinning.cpp7
-rw-r--r--Source/Utils/CMakeLists.txt3
-rw-r--r--Source/Utils/MPIInitHelpers.cpp16
-rw-r--r--Source/Utils/Make.package3
-rw-r--r--Source/Utils/MsgLogger/CMakeLists.txt4
-rw-r--r--Source/Utils/MsgLogger/Make.package3
-rw-r--r--Source/Utils/MsgLogger/MsgLogger.H293
-rw-r--r--Source/Utils/MsgLogger/MsgLogger.cpp653
-rw-r--r--Source/Utils/MsgLogger/MsgLoggerSerialization.H189
-rw-r--r--Source/Utils/MsgLogger/MsgLogger_fwd.H24
-rw-r--r--Source/Utils/WarnManager.H135
-rw-r--r--Source/Utils/WarnManager.cpp239
-rw-r--r--Source/Utils/WarnManager_fwd.H16
-rw-r--r--Source/WarpX.H55
-rw-r--r--Source/WarpX.cpp82
-rw-r--r--Source/main.cpp2
29 files changed, 1861 insertions, 90 deletions
diff --git a/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp b/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp
index 8af12308e..c3fd8e522 100644
--- a/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp
+++ b/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp
@@ -43,7 +43,7 @@ FlushFormatOpenPMD::FlushFormatOpenPMD (const std::string& diag_name)
( openPMD::IterationEncoding::groupBased != encoding ) )
{
std::string warnMsg = diag_name+" Unable to support BTD with streaming. Using GroupBased ";
- amrex::Warning(warnMsg);
+ WarpX::GetInstance().RecordWarning("Diagnostics", warnMsg);
encoding = openPMD::IterationEncoding::groupBased;
}
}
diff --git a/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp b/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp
index 08230ac1a..62fea2dd8 100644
--- a/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp
+++ b/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp
@@ -522,14 +522,16 @@ FlushFormatPlotfile::WriteAllRawFields(
WriteRawMF( warpx.getBfield_fp(lev, 2), dm, raw_pltname, default_level_prefix, "Bz_fp", lev, plot_raw_fields_guards);
if (plot_raw_F) {
if (warpx.get_pointer_F_fp(lev) == nullptr) {
- amrex::Warning("The user requested to write raw F data, but F_fp was not allocated");
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ "The user requested to write raw F data, but F_fp was not allocated");
} else {
WriteRawMF(warpx.getF_fp(lev), dm, raw_pltname, default_level_prefix, "F_fp", lev, plot_raw_fields_guards);
}
}
if (plot_raw_rho) {
if (warpx.get_pointer_rho_fp(lev) == nullptr) {
- amrex::Warning("The user requested to write raw rho data, but rho_fp was not allocated");
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ "The user requested to write raw rho data, but rho_fp was not allocated");
} else {
// Use the component 1 of `rho_fp`, i.e. rho_new for time synchronization
// If nComp > 1, this is the upper half of the list of components.
@@ -579,9 +581,11 @@ FlushFormatPlotfile::WriteAllRawFields(
dm, raw_pltname, default_level_prefix, lev, plot_raw_fields_guards);
if (plot_raw_F) {
if (warpx.get_pointer_F_fp(lev) == nullptr) {
- amrex::Warning("The user requested to write raw F data, but F_fp was not allocated");
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ "The user requested to write raw F data, but F_fp was not allocated");
} else if (warpx.get_pointer_F_cp(lev) == nullptr) {
- amrex::Warning("The user requested to write raw F data, but F_cp was not allocated");
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ "The user requested to write raw F data, but F_cp was not allocated");
} else {
WriteCoarseScalar("F", warpx.get_pointer_F_cp(lev), warpx.get_pointer_F_fp(lev),
dm, raw_pltname, default_level_prefix, lev, plot_raw_fields_guards, 0);
@@ -589,9 +593,11 @@ FlushFormatPlotfile::WriteAllRawFields(
}
if (plot_raw_rho) {
if (warpx.get_pointer_rho_fp(lev) == nullptr) {
- amrex::Warning("The user requested to write raw rho data, but rho_fp was not allocated");
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ "The user requested to write raw rho data, but rho_fp was not allocated");
} else if (warpx.get_pointer_rho_cp(lev) == nullptr) {
- amrex::Warning("The user requested to write raw rho data, but rho_cp was not allocated");
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ "The user requested to write raw rho data, but rho_cp was not allocated");
} else {
// Use the component 1 of `rho_cp`, i.e. rho_new for time synchronization
WriteCoarseScalar("rho", warpx.get_pointer_rho_cp(lev), warpx.get_pointer_rho_fp(lev),
diff --git a/Source/Diagnostics/SliceDiagnostic.cpp b/Source/Diagnostics/SliceDiagnostic.cpp
index 185c560ae..8a018e7e5 100644
--- a/Source/Diagnostics/SliceDiagnostic.cpp
+++ b/Source/Diagnostics/SliceDiagnostic.cpp
@@ -35,6 +35,7 @@
#include <cmath>
#include <memory>
+#include <sstream>
using namespace amrex;
@@ -124,7 +125,8 @@ CreateSlice( const MultiFab& mf, const Vector<Geometry> &dom_geom,
}
}
if (configuration_dim==1) {
- amrex::Warning("The slice configuration is 1D and cannot be visualized using yt.");
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ "The slice configuration is 1D and cannot be visualized using yt.");
}
// Slice generation with index type inheritance //
@@ -278,17 +280,23 @@ CheckSliceInput( const RealBox real_box, RealBox &slice_cc_nd_box,
// Modify lo if input is out of bounds //
if ( slice_realbox.lo(idim) < real_box.lo(idim) ) {
slice_realbox.setLo( idim, real_box.lo(idim));
- amrex::Print() << " slice lo is out of bounds. " <<
- " Modified it in dimension " << idim <<
- " to be aligned with the domain box\n";
+ std::stringstream warnMsg;
+ warnMsg << " slice lo is out of bounds. " <<
+ " Modified it in dimension " << idim <<
+ " to be aligned with the domain box.";
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ warnMsg.str(), WarnPriority::low);
}
// Modify hi if input in out od bounds //
if ( slice_realbox.hi(idim) > real_box.hi(idim) ) {
slice_realbox.setHi( idim, real_box.hi(idim));
- amrex::Print() << " slice hi is out of bounds." <<
- " Modified it in dimension " << idim <<
- " to be aligned with the domain box\n";
+ std::stringstream warnMsg;
+ warnMsg << " slice hi is out of bounds. " <<
+ " Modified it in dimension " << idim <<
+ " to be aligned with the domain box.";
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ warnMsg.str(), WarnPriority::low);
}
// Factor to ensure index values computation depending on index type //
@@ -372,11 +380,12 @@ CheckSliceInput( const RealBox real_box, RealBox &slice_cc_nd_box,
}
if ( (hi_new - lo_new) == 0 ){
- amrex::Print() << " Diagnostic Warning :: ";
- amrex::Print() << " Coarsening ratio ";
- amrex::Print() << slice_cr_ratio[idim] << " in dim "<< idim;
- amrex::Print() << "is leading to zero cells for slice.";
- amrex::Print() << " Thus reducing cr_ratio by half.\n";
+ std::stringstream warnMsg;
+ warnMsg << " Coarsening ratio " << slice_cr_ratio[idim] << " in dim "<< idim <<
+ "is leading to zero cells for slice." << " Thus reducing cr_ratio by half.\n";
+
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ warnMsg.str());
slice_cr_ratio[idim] = slice_cr_ratio[idim]/2;
modify_cr = true;
diff --git a/Source/Diagnostics/WarpXOpenPMD.cpp b/Source/Diagnostics/WarpXOpenPMD.cpp
index 2a1a18bd1..d1c45abda 100644
--- a/Source/Diagnostics/WarpXOpenPMD.cpp
+++ b/Source/Diagnostics/WarpXOpenPMD.cpp
@@ -397,10 +397,10 @@ void WarpXOpenPMDPlot::SetStep (int ts, const std::string& dirPrefix, int file_m
if (m_CurrentStep >= ts) {
// note m_Series is reset in Init(), so using m_Series->iterations.contains(ts) is only able to check the
// last written step in m_Series's life time, but not other earlier written steps by other m_Series
- std::string warnMsg =
- " Warning from openPMD writer: Already written iteration:" + std::to_string(ts);
- std::cout << warnMsg << std::endl;
- amrex::Warning(warnMsg);
+ WarpX::GetInstance().RecordWarning("Diagnostics",
+ " Warning from openPMD writer: Already written iteration:"
+ + std::to_string(ts)
+ );
}
}
diff --git a/Source/Evolve/WarpXEvolve.cpp b/Source/Evolve/WarpXEvolve.cpp
index b6dadd8a1..caffe11d7 100644
--- a/Source/Evolve/WarpXEvolve.cpp
+++ b/Source/Evolve/WarpXEvolve.cpp
@@ -326,6 +326,7 @@ WarpX::Evolve (int numsteps)
if (!early_params_checked) {
amrex::Print() << "\n"; // better: conditional \n based on return value
amrex::ParmParse().QueryUnusedInputs();
+ this->PrintGlobalWarnings("FIRST STEP"); //Print the warning list right after the first step.
early_params_checked = true;
}
diff --git a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp
index 7166eb99d..577fe9de7 100644
--- a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp
+++ b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp
@@ -19,6 +19,7 @@
#include <AMReX_BaseFwd.H>
#include <memory>
+#include <sstream>
using namespace amrex;
@@ -90,7 +91,11 @@ MacroscopicProperties::ReadParameters ()
sigma_specified = true;
}
if (!sigma_specified) {
- amrex::Print() << "WARNING: Material conductivity is not specified. Using default vacuum value of " << m_sigma << " in the simulation\n";
+ std::stringstream warnMsg;
+ warnMsg << "Material conductivity is not specified. Using default vacuum value of " <<
+ m_sigma << " in the simulation.";
+ WarpX::GetInstance().RecordWarning("Macroscopic properties",
+ warnMsg.str());
}
// initialization of sigma (conductivity) with parser
if (m_sigma_s == "parse_sigma_function") {
@@ -109,7 +114,11 @@ MacroscopicProperties::ReadParameters ()
epsilon_specified = true;
}
if (!epsilon_specified) {
- amrex::Print() << "WARNING: Material permittivity is not specified. Using default vacuum value of " << m_epsilon << " in the simulation\n";
+ std::stringstream warnMsg;
+ warnMsg << "Material permittivity is not specified. Using default vacuum value of " <<
+ m_epsilon << " in the simulation.";
+ WarpX::GetInstance().RecordWarning("Macroscopic properties",
+ warnMsg.str());
}
// initialization of epsilon (permittivity) with parser
@@ -130,7 +139,11 @@ MacroscopicProperties::ReadParameters ()
mu_specified = true;
}
if (!mu_specified) {
- amrex::Print() << "WARNING: Material permittivity is not specified. Using default vacuum value of " << m_mu << " in the simulation\n";
+ std::stringstream warnMsg;
+ warnMsg << "Material permittivity is not specified. Using default vacuum value of " <<
+ m_mu << " in the simulation.";
+ WarpX::GetInstance().RecordWarning("Macroscopic properties",
+ warnMsg.str());
}
// initialization of mu (permeability) with parser
diff --git a/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp b/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp
index 9604ccb44..a44ecb47e 100644
--- a/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp
+++ b/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp
@@ -86,7 +86,8 @@ SpectralFieldDataRZ::SpectralFieldDataRZ (const int lev,
result = cufftPlanMany(&forward_plan[mfi], 1, fft_length, inembed, istride, idist,
onembed, ostride, odist, cufft_type, batch);
if (result != CUFFT_SUCCESS) {
- amrex::AllPrint() << " cufftPlanMany failed! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "cufftPlanMany failed!", WarnPriority::high);
}
// The backward plane is the same as the forward since the direction is passed when executed.
#elif defined(AMREX_USE_HIP)
@@ -114,7 +115,8 @@ SpectralFieldDataRZ::SpectralFieldDataRZ (const int lev,
grid_size[0], // number of transforms
description);
if (result != rocfft_status_success) {
- amrex::AllPrint() << " rocfft_plan_create failed! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "rocfft_plan_create failed!\n", WarnPriority::high);
}
result = rocfft_plan_create(&(backward_plan[mfi]),
@@ -129,12 +131,14 @@ SpectralFieldDataRZ::SpectralFieldDataRZ (const int lev,
grid_size[0], // number of transforms
description);
if (result != rocfft_status_success) {
- amrex::AllPrint() << " rocfft_plan_create failed! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "rocfft_plan_create failed!\n", WarnPriority::high);
}
result = rocfft_plan_description_destroy(description);
if (result != rocfft_status_success) {
- amrex::AllPrint() << " rocfft_plan_description_destroy failed! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "rocfft_plan_description_destroy failed!\n", WarnPriority::high);
}
#else
// Create FFTW plans.
@@ -240,7 +244,8 @@ SpectralFieldDataRZ::FABZForwardTransform (amrex::MFIter const & mfi, amrex::Box
reinterpret_cast<AnyFFT::Complex*>(tmpSpectralField[mfi].dataPtr(mode)), // Complex *out
CUFFT_FORWARD);
if (result != CUFFT_SUCCESS) {
- amrex::AllPrint() << " forward transform using cufftExecZ2Z failed ! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "forward transform using cufftExecZ2Z failed!", WarnPriority::high);
}
}
#elif defined(AMREX_USE_HIP)
@@ -257,7 +262,8 @@ SpectralFieldDataRZ::FABZForwardTransform (amrex::MFIter const & mfi, amrex::Box
void* out_array[] = {(void*)(tmpSpectralField[mfi].dataPtr(mode))};
result = rocfft_execute(forward_plan[mfi], in_array, out_array, execinfo);
if (result != rocfft_status_success) {
- amrex::AllPrint() << " forward transform using rocfft_execute failed ! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "forward transform using rocfft_execute failed!", WarnPriority::high);
}
}
@@ -349,7 +355,8 @@ SpectralFieldDataRZ::FABZBackwardTransform (amrex::MFIter const & mfi, amrex::Bo
reinterpret_cast<AnyFFT::Complex*>(tempHTransformed[mfi].dataPtr(mode)), // Complex *out
CUFFT_INVERSE);
if (result != CUFFT_SUCCESS) {
- amrex::AllPrint() << " backwardtransform using cufftExecZ2Z failed ! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "backwardtransform using cufftExecZ2Z failed!", WarnPriority::high);
}
}
#elif defined(AMREX_USE_HIP)
@@ -366,7 +373,8 @@ SpectralFieldDataRZ::FABZBackwardTransform (amrex::MFIter const & mfi, amrex::Bo
void* out_array[] = {(void*)(tempHTransformed[mfi].dataPtr(mode))};
result = rocfft_execute(backward_plan[mfi], in_array, out_array, execinfo);
if (result != rocfft_status_success) {
- amrex::AllPrint() << " forward transform using rocfft_execute failed ! \n";
+ WarpX::GetInstance().RecordWarning("Spectral solver",
+ "forward transform using rocfft_execute failed!", WarnPriority::high);
}
}
diff --git a/Source/Initialization/PlasmaInjector.cpp b/Source/Initialization/PlasmaInjector.cpp
index 1e4b632c0..572267d0e 100644
--- a/Source/Initialization/PlasmaInjector.cpp
+++ b/Source/Initialization/PlasmaInjector.cpp
@@ -131,9 +131,11 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name)
bool mass_is_specified = queryWithParser(pp_species_name, "mass", mass);
if ( charge_is_specified && species_is_specified ){
- amrex::Print() << "WARNING: Both '" << species_name << ".charge' and "
- << species_name << ".species_type' are specified\n'"
- << species_name << ".charge' will take precedence.\n";
+ WarpX::GetInstance().RecordWarning("Species",
+ "Both '" + species_name + ".charge' and " +
+ species_name + ".species_type' are specified.\n" +
+ species_name + ".charge' will take precedence.\n");
+
}
if (!charge_is_specified && !species_is_specified && injection_style != "external_file"){
// external file will throw own assertions below if charge cannot be found
@@ -141,9 +143,10 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name)
}
if ( mass_is_specified && species_is_specified ){
- amrex::Print() << "WARNING: Both '" << species_name << ".mass' and "
- << species_name << ".species_type' are specified\n'"
- << species_name << ".mass' will take precedence.\n";
+ WarpX::GetInstance().RecordWarning("Species",
+ "Both '" + species_name + ".mass' and " +
+ species_name + ".species_type' are specified.\n" +
+ species_name + ".mass' will take precedence.\n");
}
if (!mass_is_specified && !species_is_specified && injection_style != "external_file"){
// external file will throw own assertions below if mass cannot be found
@@ -343,14 +346,16 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name)
"'" + ps_name + ".species_type' in your input file!\n");
if (charge_is_specified) {
- amrex::Print() << "WARNING: Both '" << ps_name << ".charge' and '"
- << ps_name << ".injection_file' specify a charge.\n'"
- << ps_name << ".charge' will take precedence.\n";
+ WarpX::GetInstance().RecordWarning("Species",
+ "Both '" + ps_name + ".charge' and '" +
+ ps_name + ".injection_file' specify a charge.\n'" +
+ ps_name + ".charge' will take precedence.\n");
}
else if (species_is_specified) {
- amrex::Print() << "WARNING: Both '" << ps_name << ".species_type' and '"
- << ps_name << ".injection_file' specify a charge.\n'"
- << ps_name << ".species_type' will take precedence.\n";
+ WarpX::GetInstance().RecordWarning("Species",
+ "Both '" + ps_name + ".species_type' and '" +
+ ps_name + ".injection_file' specify a charge.\n'" +
+ ps_name + ".species_type' will take precedence.\n");
}
else {
// TODO: Add ASSERT_WITH_MESSAGE to test if charge is a constant record
@@ -360,14 +365,16 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name)
charge = p_q * charge_unit;
}
if (mass_is_specified) {
- amrex::Print() << "WARNING: Both '" << ps_name << ".mass' and '"
- << ps_name << ".injection_file' specify a mass.\n'"
- << ps_name << ".mass' will take precedence.\n";
+ WarpX::GetInstance().RecordWarning("Species",
+ "Both '" + ps_name + ".mass' and '" +
+ ps_name + ".injection_file' specify a charge.\n'" +
+ ps_name + ".mass' will take precedence.\n");
}
else if (species_is_specified) {
- amrex::Print() << "WARNING: Both '" << ps_name << ".species_type' and '"
- << ps_name << ".injection_file' specify a mass.\n'"
- << ps_name << ".species_type' will take precedence.\n";
+ WarpX::GetInstance().RecordWarning("Species",
+ "Both '" + ps_name + ".species_type' and '" +
+ ps_name + ".injection_file' specify a mass.\n'" +
+ ps_name + ".species_type' will take precedence.\n");
}
else {
// TODO: Add ASSERT_WITH_MESSAGE to test if mass is a constant record
diff --git a/Source/Initialization/WarpXInitData.cpp b/Source/Initialization/WarpXInitData.cpp
index d8b6cdd16..98c4b448c 100644
--- a/Source/Initialization/WarpXInitData.cpp
+++ b/Source/Initialization/WarpXInitData.cpp
@@ -62,7 +62,7 @@
#include <string>
#include <utility>
#include <vector>
-
+#include <sstream>
using namespace amrex;
@@ -757,8 +757,9 @@ WarpX::PerformanceHints ()
for (int ilev = 0; ilev <= finestLevel(); ++ilev) {
total_nboxes += boxArray(ilev).size();
}
- if (ParallelDescriptor::NProcs() > total_nboxes)
- amrex::Print() << "\n[Warning] [Performance] Too many resources / too little work!\n"
+ if (ParallelDescriptor::NProcs() > total_nboxes){
+ std::stringstream warnMsg;
+ warnMsg << "Too many resources / too little work!\n"
<< " It looks like you requested more compute resources than "
<< "there are total number of boxes of cells available ("
<< total_nboxes << "). "
@@ -773,6 +774,9 @@ WarpX::PerformanceHints ()
<< " More information:\n"
<< " https://warpx.readthedocs.io/en/latest/running_cpp/parallelization.html\n";
+ WarpX::GetInstance().RecordWarning("Performance", warnMsg.str(), WarnPriority::high);
+ }
+
// TODO: warn if some ranks have disproportionally more work than all others
// tricky: it can be ok to assign "vacuum" boxes to some ranks w/o slowing down
// all other ranks; we need to measure this with our load-balancing
diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp
index b9b31ec7e..b99a9e0a3 100644
--- a/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp
+++ b/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp
@@ -8,6 +8,7 @@
#include "Utils/WarpXUtil.H"
#include "Utils/WarpX_Complex.H"
+#include "WarpX.H"
#include <AMReX.H>
#include <AMReX_Algorithm.H>
@@ -45,8 +46,10 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::init (
{
if (!std::numeric_limits< double >::is_iec559)
{
- Print() << R"(Warning: double does not comply with IEEE 754: bad
- things will happen parsing the X, Y and T profiles for the laser!)";
+ WarpX::GetInstance().RecordWarning("Laser",
+ "(Double does not comply with IEEE 754: bad"
+ "things will happen parsing the X, Y and T profiles for the laser!)",
+ WarnPriority::high);
}
// Parse the TXYE file
diff --git a/Source/Particles/LaserParticleContainer.cpp b/Source/Particles/LaserParticleContainer.cpp
index 1f5a7e8d0..1790f3019 100644
--- a/Source/Particles/LaserParticleContainer.cpp
+++ b/Source/Particles/LaserParticleContainer.cpp
@@ -116,7 +116,9 @@ LaserParticleContainer::LaserParticleContainer (AmrCore* amr_core, int ispecies,
pp_laser_name.query("min_particles_per_mode", m_min_particles_per_mode);
if (m_e_max == amrex::Real(0.)){
- amrex::Print() << m_laser_name << " with zero amplitude disabled.\n";
+ WarpX::GetInstance().RecordWarning("Laser",
+ m_laser_name + " with zero amplitude disabled.",
+ WarnPriority::low);
m_enabled = false;
return; // Disable laser if amplitude is 0
}
@@ -290,7 +292,9 @@ LaserParticleContainer::InitData ()
InitData(maxLevel());
if(!do_continuous_injection && (TotalNumberOfParticles() == 0)){
- amrex::Print() << "WARNING: laser antenna is completely out of the simulation box !!!\n";
+ WarpX::GetInstance().RecordWarning("Laser",
+ "The antenna is completely out of the simulation box for laser " + m_laser_name,
+ WarnPriority::high);
m_enabled = false; // Disable laser if antenna is completely out of the simulation box
}
}
@@ -662,8 +666,10 @@ LaserParticleContainer::ComputeWeightMobility (Real Sx, Real Sy)
// calculated antenna particle velocities may exceed c, which can cause a segfault.
constexpr Real warning_tol = 0.1_rt;
if (m_wavelength < std::min(Sx,Sy)*warning_tol){
- amrex::Warning("WARNING: laser wavelength seems to be much smaller than the grid size."
- " This may cause a segmentation fault");
+ WarpX::GetInstance().RecordWarning("Laser",
+ "Laser wavelength seems to be much smaller than the grid size."
+ " This may cause a segmentation fault",
+ WarnPriority::high);
}
}
diff --git a/Source/Particles/MultiParticleContainer.cpp b/Source/Particles/MultiParticleContainer.cpp
index 6cf890821..b877708f9 100644
--- a/Source/Particles/MultiParticleContainer.cpp
+++ b/Source/Particles/MultiParticleContainer.cpp
@@ -1007,8 +1007,9 @@ void MultiParticleContainer::InitQuantumSync ()
m_quantum_sync_photon_creation_energy_threshold = temp;
}
else{
- amrex::Print() << "Using default value (2*me*c^2)" <<
- " for photon energy creation threshold \n" ;
+ WarpX::GetInstance().RecordWarning("QED",
+ "Using default value (2*me*c^2) for photon energy creation threshold",
+ WarnPriority::low);
}
// qs_minimum_chi_part is the minimum chi parameter to be
@@ -1024,7 +1025,9 @@ void MultiParticleContainer::InitQuantumSync ()
}
if(lookup_table_mode == "generate"){
- amrex::Print() << "Quantum Synchrotron table will be generated. \n" ;
+ WarpX::GetInstance().RecordWarning("QED",
+ "A new Quantum Synchrotron table will be generated.",
+ WarnPriority::low);
#ifndef WARPX_QED_TABLE_GEN
amrex::Error("Error: Compile with QED_TABLE_GEN=TRUE to enable table generation!\n");
#else
@@ -1032,9 +1035,11 @@ void MultiParticleContainer::InitQuantumSync ()
#endif
}
else if(lookup_table_mode == "load"){
- amrex::Print() << "Quantum Synchrotron table will be read from file. \n" ;
std::string load_table_name;
pp_qed_qs.query("load_table_from", load_table_name);
+ WarpX::GetInstance().RecordWarning("QED",
+ "The Quantum Synchrotron table will be read from the file: " + load_table_name,
+ WarnPriority::low);
if(load_table_name.empty()){
amrex::Abort("Quantum Synchrotron table name should be provided");
}
@@ -1045,7 +1050,10 @@ void MultiParticleContainer::InitQuantumSync ()
qs_minimum_chi_part);
}
else if(lookup_table_mode == "builtin"){
- amrex::Print() << "Built-in Quantum Synchrotron table will be used. \n" ;
+ WarpX::GetInstance().RecordWarning("QED",
+ "The built-in Quantum Synchrotron table will be used."
+ "This low resolution table is intended for testing purposes only.",
+ WarnPriority::medium);
m_shr_p_qs_engine->init_builtin_tables(qs_minimum_chi_part);
}
else{
@@ -1075,7 +1083,9 @@ void MultiParticleContainer::InitBreitWheeler ()
}
if(lookup_table_mode == "generate"){
- amrex::Print() << "Breit Wheeler table will be generated. \n" ;
+ WarpX::GetInstance().RecordWarning("QED",
+ "A new Breit Wheeler table will be generated.",
+ WarnPriority::low);
#ifndef WARPX_QED_TABLE_GEN
amrex::Error("Error: Compile with QED_TABLE_GEN=TRUE to enable table generation!\n");
#else
@@ -1083,9 +1093,11 @@ void MultiParticleContainer::InitBreitWheeler ()
#endif
}
else if(lookup_table_mode == "load"){
- amrex::Print() << "Breit Wheeler table will be read from file. \n" ;
std::string load_table_name;
pp_qed_bw.query("load_table_from", load_table_name);
+ WarpX::GetInstance().RecordWarning("QED",
+ "The Breit Wheeler table will be read from the file:" + load_table_name,
+ WarnPriority::low);
if(load_table_name.empty()){
amrex::Abort("Breit Wheeler table name should be provided");
}
@@ -1096,7 +1108,10 @@ void MultiParticleContainer::InitBreitWheeler ()
table_data, bw_minimum_chi_part);
}
else if(lookup_table_mode == "builtin"){
- amrex::Print() << "Built-in Breit Wheeler table will be used. \n" ;
+ WarpX::GetInstance().RecordWarning("QED",
+ "The built-in Breit Wheeler table will be used."
+ "This low resolution table is intended for testing purposes only.",
+ WarnPriority::medium);
m_shr_p_bw_engine->init_builtin_tables(bw_minimum_chi_part);
}
else{
diff --git a/Source/Particles/PhysicalParticleContainer.cpp b/Source/Particles/PhysicalParticleContainer.cpp
index d6238528f..ea7287bac 100644
--- a/Source/Particles/PhysicalParticleContainer.cpp
+++ b/Source/Particles/PhysicalParticleContainer.cpp
@@ -101,6 +101,7 @@
#include <string>
#include <utility>
#include <vector>
+#include <sstream>
using namespace amrex;
@@ -533,9 +534,11 @@ PhysicalParticleContainer::AddPlasmaFromFile(ParticleReal q_tot,
if (q_tot != 0.0) {
weight = std::abs(q_tot) / ( std::abs(charge) * ParticleReal(npart) );
if (ps.contains("weighting")) {
- Print() << "WARNING: Both '" << ps_name << ".q_tot' and '"
+ std::stringstream ss;
+ ss << "Both '" << ps_name << ".q_tot' and '"
<< ps_name << ".injection_file' specify a total charge.\n'"
- << ps_name << ".q_tot' will take precedence.\n";
+ << ps_name << ".q_tot' will take precedence.";
+ WarpX::GetInstance().RecordWarning("Species", ss.str());
}
}
// ED-PIC extension?
@@ -570,7 +573,9 @@ PhysicalParticleContainer::AddPlasmaFromFile(ParticleReal q_tot,
}
auto const np = particle_z.size();
if (np < npart) {
- Print() << "WARNING: Simulation box doesn't cover all particles\n";
+ WarpX::GetInstance().RecordWarning("Species",
+ "Simulation box doesn't cover all particles",
+ WarnPriority::high);
}
} // IO Processor
auto const np = particle_z.size();
@@ -2517,8 +2522,10 @@ PhysicalParticleContainer::InitIonizationModule ()
if (!do_field_ionization) return;
ParmParse pp_species_name(species_name);
if (charge != PhysConst::q_e){
- amrex::Warning(
- "charge != q_e for ionizable species: overriding user value and setting charge = q_e.");
+ WarpX::GetInstance().RecordWarning("Species",
+ "charge != q_e for ionizable species '" +
+ species_name + "':" +
+ "overriding user value and setting charge = q_e.");
charge = PhysConst::q_e;
}
queryWithParser(pp_species_name, "ionization_initial_level", ionization_initial_level);
diff --git a/Source/Particles/Resampling/LevelingThinning.cpp b/Source/Particles/Resampling/LevelingThinning.cpp
index 33ad87630..716b0d2ab 100644
--- a/Source/Particles/Resampling/LevelingThinning.cpp
+++ b/Source/Particles/Resampling/LevelingThinning.cpp
@@ -9,6 +9,7 @@
#include "Particles/WarpXParticleContainer.H"
#include "Utils/ParticleUtils.H"
#include "Utils/WarpXUtil.H"
+#include "WarpX.H"
#include <AMReX.H>
#include <AMReX_BLassert.H>
@@ -36,8 +37,10 @@ LevelingThinning::LevelingThinning (const std::string species_name)
"Resampling target ratio should be strictly greater than 0");
if (m_target_ratio <= 1._rt)
{
- amrex::Warning("WARNING: target ratio for leveling thinning is smaller or equal to one."
- " It is possible that no particle will be removed during resampling");
+ WarpX::GetInstance().RecordWarning("Species",
+ "For species '" + species_name + "' " +
+ "target ratio for leveling thinning is smaller or equal to one." +
+ "It is possible that no particle will be removed during resampling");
}
queryWithParser(pp_species_name, "resampling_algorithm_min_ppc", m_min_ppc);
diff --git a/Source/Utils/CMakeLists.txt b/Source/Utils/CMakeLists.txt
index 40d3a2c31..26982fb91 100644
--- a/Source/Utils/CMakeLists.txt
+++ b/Source/Utils/CMakeLists.txt
@@ -7,8 +7,11 @@ target_sources(WarpX
MPIInitHelpers.cpp
ParticleUtils.cpp
RelativeCellPosition.cpp
+ WarnManager.cpp
WarpXAlgorithmSelection.cpp
WarpXMovingWindow.cpp
WarpXTagging.cpp
WarpXUtil.cpp
)
+
+add_subdirectory(MsgLogger)
diff --git a/Source/Utils/MPIInitHelpers.cpp b/Source/Utils/MPIInitHelpers.cpp
index d4b049d7c..da9409e31 100644
--- a/Source/Utils/MPIInitHelpers.cpp
+++ b/Source/Utils/MPIInitHelpers.cpp
@@ -6,6 +6,8 @@
*/
#include "MPIInitHelpers.H"
+#include "WarpX.H"
+
#include <AMReX_Config.H>
#include <AMReX_ParallelDescriptor.H>
#include <AMReX_Print.H>
@@ -16,6 +18,7 @@
#include <string>
#include <utility>
+#include <sstream>
namespace utils
{
@@ -47,16 +50,21 @@ namespace utils
auto const thread_provided = mpi_thread_levels.second;
auto mtn = amrex::ParallelDescriptor::mpi_level_to_string;
- if( thread_provided < thread_required )
- amrex::Print() << "WARNING: Provided MPI thread safety level ("
+ std::stringstream ss;
+ if( thread_provided < thread_required ){
+ ss << "WARNING: Provided MPI thread safety level ("
<< mtn(thread_provided) << ") is LOWER than requested "
<< mtn(thread_required) << "). This might lead to undefined "
<< "results in asynchronous operations (e.g. async_io).";
- if( thread_provided > thread_required )
- amrex::Print() << "NOTE: Provided MPI thread safety level ("
+ WarpX::GetInstance().RecordWarning("MPI", ss.str(), WarnPriority::high);
+ }
+ if( thread_provided > thread_required ){
+ ss << "NOTE: Provided MPI thread safety level ("
<< mtn(thread_provided) << ") is stricter than requested "
<< mtn(thread_required) << "). This might reduce multi-node "
<< "communication performance.";
+ WarpX::GetInstance().RecordWarning("MPI", ss.str());
+ }
#else
amrex::ignore_unused(mpi_thread_levels);
#endif
diff --git a/Source/Utils/Make.package b/Source/Utils/Make.package
index 3b1ff6a84..f1b31104c 100644
--- a/Source/Utils/Make.package
+++ b/Source/Utils/Make.package
@@ -7,7 +7,10 @@ CEXE_sources += CoarsenMR.cpp
CEXE_sources += Interpolate.cpp
CEXE_sources += IntervalsParser.cpp
CEXE_sources += MPIInitHelpers.cpp
+CEXE_sources += WarnManager.cpp
CEXE_sources += RelativeCellPosition.cpp
CEXE_sources += ParticleUtils.cpp
VPATH_LOCATIONS += $(WARPX_HOME)/Source/Utils
+
+include $(WARPX_HOME)/Source/Utils/MsgLogger/Make.package
diff --git a/Source/Utils/MsgLogger/CMakeLists.txt b/Source/Utils/MsgLogger/CMakeLists.txt
new file mode 100644
index 000000000..bd167d0e4
--- /dev/null
+++ b/Source/Utils/MsgLogger/CMakeLists.txt
@@ -0,0 +1,4 @@
+target_sources(WarpX
+ PRIVATE
+ MsgLogger.cpp
+)
diff --git a/Source/Utils/MsgLogger/Make.package b/Source/Utils/MsgLogger/Make.package
new file mode 100644
index 000000000..e02476222
--- /dev/null
+++ b/Source/Utils/MsgLogger/Make.package
@@ -0,0 +1,3 @@
+CEXE_sources += MsgLogger.cpp
+
+VPATH_LOCATIONS += $(WARPX_HOME)/Source/Utils/MsgLogger
diff --git a/Source/Utils/MsgLogger/MsgLogger.H b/Source/Utils/MsgLogger/MsgLogger.H
new file mode 100644
index 000000000..ca55289b2
--- /dev/null
+++ b/Source/Utils/MsgLogger/MsgLogger.H
@@ -0,0 +1,293 @@
+/* Copyright 2021 Luca Fedeli
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#ifndef WARPX_MSG_LOGGER_H_
+#define WARPX_MSG_LOGGER_H_
+
+#include <AMReX.H>
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace Utils{
+namespace MsgLogger{
+
+ /** Priority is recorded together with messages. It influences
+ * the display order and the appearance of a message.
+ */
+ enum class Priority
+ {
+ /** Low priority message */
+ low,
+ /** Medium priority message */
+ medium,
+ /** High priority message */
+ high
+ };
+
+ /**
+ * \brief This function converts a Priority into the corresponding
+ * string (e.g, Priority::low --> "low")
+ *
+ * @param[in] priority the priority
+ * @return the corresponding string
+ */
+ std::string PriorityToString(const Priority& priority);
+
+ /**
+ * \brief This function converts a string into the corresponding
+ * priority (e.g, "low" --> Priority::low)
+ *
+ * @param[in] priority_string the priority string
+ * @return the corresponding priority
+ */
+ Priority StringToPriority(const std::string& priority_string);
+
+ /**
+ * This struct represents a message, which is composed by
+ * a topic, a text and a priority. It also provides methods for
+ * serialization and deserialization.
+ */
+ struct Msg
+ {
+ std::string topic /*! The message topic*/;
+ std::string text /*! The message text*/;
+ Priority priority /*! The priority of the message*/;
+
+ /**
+ * \brief This function returns a byte representation of the struct
+ *
+ * @return a byte vector
+ */
+ std::vector<char> serialize() const;
+
+ /**
+ * \brief This function generates a Msg struct from a byte vector
+ *
+ * @param[in] it iterator of a byte array
+ * @return a Msg struct
+ */
+ static Msg deserialize(std::vector<char>::const_iterator& it);
+
+ /**
+ * \brief Same as static Msg deserialize(std::vector<char>::const_iterator& it)
+ * but accepting an rvalue as an argument
+ *
+ * @param[in] it iterator of a byte array
+ * @return a Msg struct
+ */
+ static Msg deserialize(std::vector<char>::const_iterator&& it);
+ };
+
+ /**
+ * This struct represents a message with counter, which is composed
+ * by a message and a counter. The latter is intended to store the
+ * number of times a message is recorded. The struct also provides
+ * methods for serialization and deserialization.
+ */
+ struct MsgWithCounter
+ {
+ Msg msg /*! A message*/;
+ std::int64_t counter /*! The counter*/;
+
+ /**
+ * \brief This function returns a byte representation of the struct
+ *
+ * @return a byte vector
+ */
+ std::vector<char> serialize() const;
+
+ /**
+ * \brief This function generates a MsgWithCounter struct from a byte vector
+ *
+ * @param[in] it iterator of a byte array
+ * @return a MsgWithCounter struct
+ */
+ static MsgWithCounter deserialize(std::vector<char>::const_iterator& it);
+
+ /**
+ * \brief Same as static Msg MsgWithCounter(std::vector<char>::const_iterator& it)
+ * but accepting an rvalue as an argument
+ *
+ * @param[in] it iterator of a byte array
+ * @return a MsgWithCounter struct
+ */
+ static MsgWithCounter deserialize(std::vector<char>::const_iterator&& it);
+ };
+
+ /**
+ * This struct represents a message with counter and ranks, which is
+ * composed by a message with counter, a bool flag and a std::vector<int>.
+ * The bool flag is used to store if a message is emitted by all the ranks.
+ * The std::vector<int> is used to store the affected ranks
+ * (note: when we switch to C++17, should we consider variants?).
+ * The struct also provides methods for serialization and deserialization.
+ */
+ struct MsgWithCounterAndRanks
+ {
+ MsgWithCounter msg_with_counter /*! A message with counter*/;
+ bool all_ranks /*! Flag to store if message is emitted by all ranks*/;
+ std::vector<int> ranks /*! Affected ranks*/;
+
+ /**
+ * \brief This function returns a byte representation of the struct
+ *
+ * @return a byte vector
+ */
+ std::vector<char> serialize() const;
+
+ /**
+ * \brief This function generates a MsgWithCounterAndRanks struct from a byte vector
+ *
+ * @param[in] it iterator of a byte array
+ * @return a MsgWithCounterAndRanks struct
+ */
+ static MsgWithCounterAndRanks deserialize(std::vector<char>::const_iterator& it);
+
+ /**
+ * \brief Same as static Msg MsgWithCounterAndRanks(std::vector<char>::const_iterator& it)
+ * but accepting an rvalue as an argument
+ *
+ * @param[in] it iterator of a byte array
+ * @return a MsgWithCounterAndRanks struct
+ */
+ static MsgWithCounterAndRanks deserialize(std::vector<char>::const_iterator&& it);
+ };
+
+ /**
+ * \brief This implements the < operator for Msg.
+ * Warning messages are first ordered by priority (warning: high < medium < low
+ * to give precedence to higher priorities), then by topic (alphabetically),
+ * and finally by text (alphabetically).
+ *
+ * @param[in] l a Msg
+ * @param[in] r a Msg
+ * @return true if l<r, false otherwise
+ */
+ constexpr bool operator<(const Msg& l, const Msg& r)
+ {
+ return
+ (l.priority > r.priority) ||
+ ((l.priority == r.priority) && (l.topic < r.topic)) ||
+ ((l.priority == r.priority) && (l.topic == r.topic) && (l.text < r.text));
+ }
+
+ /**
+ * This class is responsible for storing messages and merging messages
+ * collected by different processes.
+ */
+ class Logger
+ {
+ public:
+
+ /**
+ * \brief The constructor.
+ */
+ Logger();
+
+ /**
+ * \brief This function records a message
+ *
+ * @param[in] msg a Msg struct
+ */
+ void record_msg(Msg msg);
+
+ /**
+ * \brief This function returns a vector containing the recorded messages
+ *
+ * @return a vector of the recorded messages
+ */
+ std::vector<Msg> get_msgs() const;
+
+ /**
+ * \brief This function returns a vector containing the recorded messages
+ * with the corresponding counters
+ *
+ * @return a vector of the recorded messages with counters
+ */
+ std::vector<MsgWithCounter> get_msgs_with_counter() const;
+
+ /**
+ * \brief This collective function generates a vector containing the messages
+ * with counters and emitting ranks by gathering data from
+ * all the ranks
+ *
+ * @return a vector of messages with counters and ranks if I/O rank, an empty vector otherwise
+ */
+ std::vector<MsgWithCounterAndRanks>
+ collective_gather_msgs_with_counter_and_ranks() const;
+
+ private:
+
+ /**
+ * \brief This function implements the trivial special case of
+ * collective_gather_msgs_with_counter_and_ranks when there is only one rank.
+ *
+ * @return a vector of messages with counters and ranks
+ */
+ std::vector<MsgWithCounterAndRanks>
+ one_rank_gather_msgs_with_counter_and_ranks() const;
+
+#ifdef AMREX_USE_MPI
+ /**
+ * \brief This collective function finds the rank having the
+ * most messages and how many messages this rank has. The
+ * rank having the most messages is designated as "gather rank".
+ *
+ * @param[in] how_many_msgs the number of messages that the current rank has
+ * @return a pair containing the ID of the "gather rank" and its number of messages
+ */
+ std::pair<int, int> find_gather_rank_and_its_msgs(
+ int how_many_msgs) const;
+
+ /**
+ * \brief This function uses data gathered on the "gather rank" to generate
+ * a vector of messages with global counters and emitting rank lists
+ *
+ * @param[in] my_msg_map messages and counters of the current rank (as a map)
+ * @param[in] all_data a byte array containing all the data gathered on the gather rank
+ * @param[in] displacements a vector of displacements to access data corresponding to a given rank in all_data
+ * @param[in] gather_rank the ID of the "gather rank"
+ * @return if gather_rank==m_rank a vector of messages with global counters and emitting rank lists, dummy data otherwise
+ */
+ std::vector<MsgWithCounterAndRanks>
+ compute_msgs_with_counter_and_ranks(
+ const std::map<Msg,std::int64_t>& my_msg_map,
+ const std::vector<char>& all_data,
+ const std::vector<int>& displacements,
+ const int gather_rank
+ ) const;
+
+
+ /**
+ * \brief If the gather_rank is not the I/O rank, this function sends msgs_with_counter_and_ranks
+ * to the I/O rank. This function uses point-to-point communications.
+ *
+ * @param[in] msgs_with_counter_and_ranks a vector of messages with counters and ranks
+ * @param[in] gather_rank the ID of the "gather rank"
+ */
+ void
+ swap_with_io_rank(
+ std::vector<MsgWithCounterAndRanks>& msgs_with_counter_and_ranks,
+ int gather_rank) const;
+
+#endif
+
+ int m_rank = 0 /*! MPI rank of the current process*/;
+ int m_num_procs = 0 /*! Number of MPI ranks*/;
+ int m_io_rank = 0 /*! Rank of the I/O process*/;
+ bool m_am_i_io = false /*! Flag to store if the process is responsible for I/O*/;
+
+ std::map<Msg, std::int64_t> m_messages /*! This stores a map to associate warning messages with the corresponding counters*/;
+ };
+}
+}
+
+#endif //WARPX_MSG_LOGGER_H_
diff --git a/Source/Utils/MsgLogger/MsgLogger.cpp b/Source/Utils/MsgLogger/MsgLogger.cpp
new file mode 100644
index 000000000..c1c1f9324
--- /dev/null
+++ b/Source/Utils/MsgLogger/MsgLogger.cpp
@@ -0,0 +1,653 @@
+/* Copyright 2021 Luca Fedeli
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#include "MsgLogger.H"
+
+#include "MsgLoggerSerialization.H"
+
+#ifdef AMREX_USE_MPI
+# include <AMReX_ParallelDescriptor.H>
+#endif
+#include <AMReX_Print.H>
+
+#include <iostream>
+#include <sstream>
+#include <numeric>
+
+using namespace Utils::MsgLogger;
+
+#ifdef AMREX_USE_MPI
+// Helper functions used only in this source file
+namespace
+{
+ /**
+ * \brief This collective function returns the messages of the "gather rank"
+ * as a byte array.
+ *
+ * @param[in] my_msgs the messages of the current rank
+ * @param[in] gather_rank the ID of the "gather rank"
+ * @param[in] my_rank the ID of the current rank
+ * @return the messages of the "gather rank" as a byte array
+ */
+ std::vector<char>
+ get_serialized_gather_rank_msgs(
+ const std::vector<Msg>& my_msgs,
+ const int gather_rank,
+ const int my_rank);
+
+ /**
+ * \brief This function generates data to send back to the "gather rank"
+ *
+ * @param[in] serialized_gather_rank_msgs the serialized messages of the gather rank
+ * @param[in] gather_rank_how_many_msgs number of messages of the "gather rank"
+ * @param[in] my_msg_map messages and counters of the current rank (as a map)
+ * @param[in] is_gather_rank true if the rank is the "gather rank", false otherwise
+ * @return a byte array to send back to the "gather rank" (or a dummy vector in case is_gather_rank is true)
+ */
+ std::vector<char>
+ compute_package_for_gather_rank(
+ const std::vector<char>& serialized_gather_rank_msgs,
+ const std::int64_t gather_rank_how_many_msgs,
+ const std::map<Msg, std::int64_t>& my_msg_map,
+ const bool is_gather_rank
+ );
+
+ /**
+ * \brief This collective function gathers data generated with compute_package_for_gather_rank
+ * to the gather rank.
+ * If my_rank != gather_rank the function returns dummy data. Otherwise the function returns
+ * a pair containing:
+ * 1) a byte array containing info on messages seen by other ranks
+ * 2) a vector of displacements to access data corresponding to a given rank
+ *
+ * @param[in] package_for_gather_rank a byte array generated by compute_package_for_gather_rank
+ * @param[in] gather_rank the ID of the "gather rank"
+ * @param[in] my_rank the ID of the current rank
+ * @return (see function description)
+ */
+ std::pair<std::vector<char>, std::vector<int>>
+ gather_all_data(
+ const std::vector<char>& package_for_gather_rank,
+ const int gather_rank, const int my_rank);
+
+ /**
+ * \brief This function converts a vector of Msg struct into a byte array
+ *
+ * @param[in] msgs the vector of Msg struct
+ * @return a byte array
+ */
+ std::vector<char> serialize_msgs(
+ const std::vector<Msg>& msgs);
+
+ /**
+ * \brief This function converts a byte array into a vector of Msg struct
+ *
+ * @param[in] serialized the byte array
+ * @return a vector of Msg struct
+ */
+ std::vector<Msg> deserialize_msgs(
+ const std::vector<char>& serialized);
+}
+#endif
+
+std::string Utils::MsgLogger::PriorityToString(const Priority& priority)
+{
+ if(priority == Priority::high)
+ return "high";
+ else if (priority == Priority::medium)
+ return "medium";
+ else
+ return "low";
+}
+
+Priority Utils::MsgLogger::StringToPriority(const std::string& priority_string)
+{
+ if(priority_string == "high")
+ return Priority::high;
+ else if (priority_string == "medium")
+ return Priority::medium;
+ else if (priority_string == "low")
+ return Priority::low;
+ else
+ amrex::Abort(
+ "Priority string '" + priority_string + "' not recognized");
+
+ //this silences a "non-void function does not return a value in all control paths" warning
+ return Priority::low;
+}
+
+std::vector<char> Msg::serialize() const
+{
+ std::vector<char> serialized_msg;
+
+ put_in(this->topic, serialized_msg);
+ put_in(this->text, serialized_msg);
+ const int int_priority = static_cast<int>(this->priority);
+ put_in(int_priority, serialized_msg);
+
+ return serialized_msg;
+}
+
+Msg Msg::deserialize (std::vector<char>::const_iterator& it)
+{
+ Msg msg;
+
+ msg.topic = get_out<std::string> (it);
+ msg.text = get_out<std::string> (it);
+ msg.priority = static_cast<Priority> (get_out<int> (it));
+
+ return msg;
+}
+
+Msg Msg::deserialize (std::vector<char>::const_iterator&& it)
+{
+ return Msg::deserialize(it);
+}
+
+std::vector<char> MsgWithCounter::serialize() const
+{
+ std::vector<char> serialized_msg_with_counter;
+
+ put_in_vec(msg.serialize(), serialized_msg_with_counter);
+ put_in(this->counter, serialized_msg_with_counter);
+
+ return serialized_msg_with_counter;
+}
+
+MsgWithCounter MsgWithCounter::deserialize (std::vector<char>::const_iterator& it)
+{
+ MsgWithCounter msg_with_counter;
+
+ const auto vec = get_out_vec<char>(it);
+ auto iit = vec.begin();
+ msg_with_counter.msg = Msg::deserialize(iit);
+ msg_with_counter.counter = get_out<std::int64_t> (it);
+
+ return msg_with_counter;
+}
+
+MsgWithCounter MsgWithCounter::deserialize (std::vector<char>::const_iterator&& it)
+{
+ return MsgWithCounter::deserialize(it);
+}
+
+std::vector<char> MsgWithCounterAndRanks::serialize() const
+{
+ std::vector<char> serialized_msg_with_counter_and_ranks;
+
+ put_in_vec(this->msg_with_counter.serialize(), serialized_msg_with_counter_and_ranks);
+ put_in(this->all_ranks, serialized_msg_with_counter_and_ranks);
+ put_in_vec(this->ranks, serialized_msg_with_counter_and_ranks);
+
+ return serialized_msg_with_counter_and_ranks;
+}
+
+MsgWithCounterAndRanks
+MsgWithCounterAndRanks::deserialize (std::vector<char>::const_iterator& it)
+{
+ MsgWithCounterAndRanks msg_with_counter_and_ranks;
+
+ const auto vec = get_out_vec<char>(it);
+ auto iit = vec.begin();
+ msg_with_counter_and_ranks.msg_with_counter = MsgWithCounter::deserialize(iit);
+ msg_with_counter_and_ranks.all_ranks = get_out<bool>(it);
+ msg_with_counter_and_ranks.ranks = get_out_vec<int>(it);
+
+ return msg_with_counter_and_ranks;
+}
+
+MsgWithCounterAndRanks
+MsgWithCounterAndRanks::deserialize (std::vector<char>::const_iterator&& it)
+{
+ return MsgWithCounterAndRanks::deserialize(it);
+}
+
+Logger::Logger(){
+ m_rank = amrex::ParallelDescriptor::MyProc();
+ m_num_procs = amrex::ParallelDescriptor::NProcs();
+ m_io_rank = amrex::ParallelDescriptor::IOProcessorNumber();
+ m_am_i_io = (m_rank == m_io_rank);
+}
+
+void Logger::record_msg(Msg msg)
+{
+ m_messages[msg]++;
+}
+
+std::vector<Msg> Logger::get_msgs() const
+{
+ auto res = std::vector<Msg>{};
+
+ for (const auto& msg_w_counter : m_messages)
+ res.emplace_back(msg_w_counter.first);
+
+ return res;
+}
+
+std::vector<MsgWithCounter> Logger::get_msgs_with_counter() const
+{
+ auto res = std::vector<MsgWithCounter>{};
+
+ for (const auto& msg : m_messages)
+ res.emplace_back(MsgWithCounter{msg.first, msg.second});
+
+ return res;
+}
+
+std::vector<MsgWithCounterAndRanks>
+Logger::collective_gather_msgs_with_counter_and_ranks() const
+{
+
+#ifdef AMREX_USE_MPI
+
+ // Trivial case of only one rank
+ if (m_num_procs == 1)
+ return one_rank_gather_msgs_with_counter_and_ranks();
+
+ // Find out who is the "gather rank" and how many messages it has
+ const auto my_msgs = get_msgs();
+ const auto how_many_msgs = my_msgs.size();
+ int gather_rank = 0;
+ std::int64_t gather_rank_how_many_msgs = 0;
+ std::tie(gather_rank, gather_rank_how_many_msgs) =
+ find_gather_rank_and_its_msgs(how_many_msgs);
+
+ // If the "gather rank" has zero messages there are no messages at all
+ if(gather_rank_how_many_msgs == 0)
+ return std::vector<MsgWithCounterAndRanks>{};
+
+ // All the ranks receive the msgs of the "gather rank" as a byte array
+ const auto serialized_gather_rank_msgs =
+ ::get_serialized_gather_rank_msgs(my_msgs, gather_rank, m_rank);
+
+ // Each rank assembles a message to send back to the "gather rank"
+ const bool is_gather_rank = (gather_rank == m_rank);
+ const auto package_for_gather_rank =
+ ::compute_package_for_gather_rank(
+ serialized_gather_rank_msgs,
+ gather_rank_how_many_msgs,
+ m_messages, is_gather_rank);
+
+ // Send back all the data to the "gather rank"
+ auto all_data = std::vector<char>{};
+ auto displacements = std::vector<int>{};
+ std::tie(all_data, displacements) =
+ ::gather_all_data(
+ package_for_gather_rank,
+ gather_rank, m_rank);
+
+ // Use the gathered data to generate (on the "gather rank") a vector of all the
+ // messages seen by all the ranks with the corresponding counters and
+ // emitting rank lists.
+ auto msgs_with_counter_and_ranks =
+ compute_msgs_with_counter_and_ranks(
+ m_messages,
+ all_data,
+ displacements,
+ gather_rank);
+
+ // If the current rank is not the I/O rank, send msgs_with_counter_and_ranks
+ // to the I/O rank
+ swap_with_io_rank(
+ msgs_with_counter_and_ranks,
+ gather_rank);
+
+ return msgs_with_counter_and_ranks;
+#else
+ return one_rank_gather_msgs_with_counter_and_ranks();
+#endif
+}
+
+std::vector<MsgWithCounterAndRanks>
+Logger::one_rank_gather_msgs_with_counter_and_ranks() const
+{
+ std::vector<MsgWithCounterAndRanks> res;
+ for (const auto& el : m_messages)
+ {
+ res.emplace_back(
+ MsgWithCounterAndRanks{
+ MsgWithCounter{el.first, el.second},
+ true,
+ std::vector<int>{m_rank}});
+ }
+ return res;
+}
+
+#ifdef AMREX_USE_MPI
+
+std::pair<int,int> Logger::find_gather_rank_and_its_msgs(int how_many_msgs) const
+{
+ int max_items = 0;
+ int max_rank = 0;
+
+ const auto num_msg =
+ amrex::ParallelDescriptor::Gather(how_many_msgs, m_io_rank);
+
+ if (m_am_i_io){
+ const auto it_max = std::max_element(num_msg.begin(), num_msg.end());
+ max_items = *it_max;
+
+ //In case of an "ex aequo" the I/O rank should be the gather rank
+ max_rank = (max_items == how_many_msgs) ?
+ m_io_rank : it_max - num_msg.begin();
+ }
+
+ auto package = std::array<int,2>{max_rank, max_items};
+ amrex::ParallelDescriptor::Bcast(package.data(), 2, m_io_rank);
+
+ return std::make_pair(package[0], package[1]);
+}
+
+std::vector<MsgWithCounterAndRanks>
+Logger::compute_msgs_with_counter_and_ranks(
+ const std::map<Msg,std::int64_t>& my_msg_map,
+ const std::vector<char>& all_data,
+ const std::vector<int>& displacements,
+ const int gather_rank) const
+{
+ if(m_rank != gather_rank) return std::vector<MsgWithCounterAndRanks>{};
+
+ std::vector<MsgWithCounterAndRanks> msgs_with_counter_and_ranks;
+
+ // Put messages of the gather rank in msgs_with_counter_and_ranks
+ for (const auto& el : my_msg_map)
+ {
+ msgs_with_counter_and_ranks.emplace_back(
+ MsgWithCounterAndRanks{
+ MsgWithCounter{el.first, el.second},
+ false,
+ std::vector<int>{m_rank}});
+ }
+
+ // We need a temporary map
+ std::map<Msg, MsgWithCounterAndRanks> tmap;
+
+#ifdef AMREX_USE_OMP
+ #pragma omp parallel for
+#endif
+ for(int rr = 0; rr < m_num_procs; ++rr){ //for each rank
+ if(rr == gather_rank) // (skip gather_rank)
+ continue;
+
+ // get counters generated by rank rr
+ auto it = all_data.begin() + displacements[rr];
+ const auto counters_rr = get_out_vec<std::int64_t>(it);
+
+ //for each counter from rank rr
+ std::int64_t c = 0;
+ for (const auto& counter : counters_rr){
+#ifdef AMREX_USE_OMP
+ #pragma omp atomic
+#endif
+ msgs_with_counter_and_ranks[c].msg_with_counter.counter +=
+ counter; //update corresponding global counter
+
+ //and add rank to rank list if it has emitted the message
+ if (counter > 0){
+#ifdef AMREX_USE_OMP
+ #pragma omp critical
+#endif
+ {
+ msgs_with_counter_and_ranks[c].ranks.push_back(rr);
+ }
+ }
+ c++;
+ }
+
+ // for each additional message coming from rank rr
+ const auto how_many_additional_msgs_with_counter = get_out<int>(it);
+ for(int i = 0; i < how_many_additional_msgs_with_counter; ++i){
+
+ //deserialize the message
+ const auto serialized_msg_with_counter = get_out_vec<char>(it);
+ auto msg_with_counter =
+ MsgWithCounter::deserialize(serialized_msg_with_counter.begin());
+
+ //and eventually add it to the temporary map
+#ifdef AMREX_USE_OMP
+ #pragma omp critical
+#endif
+ {
+ if (tmap.find(msg_with_counter.msg) == tmap.end()){
+ const auto msg_with_counter_and_ranks =
+ MsgWithCounterAndRanks{
+ msg_with_counter,
+ false,
+ std::vector<int>{rr}
+ };
+ tmap[msg_with_counter.msg] = msg_with_counter_and_ranks;
+ }
+ else{
+ tmap[msg_with_counter.msg].msg_with_counter.counter +=
+ msg_with_counter.counter;
+ tmap[msg_with_counter.msg].ranks.push_back(rr);
+ }
+ }
+ }
+ }
+
+ // Check if messages emitted by "gather rank" are actually emitted by all ranks
+ const auto ssize = static_cast<int>(msgs_with_counter_and_ranks.size());
+ for (int i = 0; i < ssize; ++i){
+ const auto how_many =
+ static_cast<int>(msgs_with_counter_and_ranks[i].ranks.size());
+ if(how_many == m_num_procs){
+ msgs_with_counter_and_ranks[i].all_ranks = true;
+ // trick to force free memory
+ std::vector<int>{}.swap(msgs_with_counter_and_ranks[i].ranks);
+ }
+ }
+
+ // Add elements from the temporary map
+ for(const auto& el : tmap){
+ msgs_with_counter_and_ranks.push_back(el.second);
+ }
+
+ // Sort affected ranks lists
+ for(auto& el : msgs_with_counter_and_ranks){
+ std::sort(el.ranks.begin(), el.ranks.end());
+ }
+
+ return msgs_with_counter_and_ranks;
+}
+
+void Logger::swap_with_io_rank(
+ std::vector<MsgWithCounterAndRanks>& msgs_with_counter_and_ranks,
+ int gather_rank) const
+{
+ if (gather_rank != m_io_rank){
+ if(m_rank == gather_rank){
+ auto package = std::vector<char>{};
+ for (const auto& el: msgs_with_counter_and_ranks)
+ put_in_vec<char>(el.serialize(), package);
+
+ auto package_size = static_cast<int>(package.size());
+ amrex::ParallelDescriptor::Send(&package_size, 1, m_io_rank, 0);
+ amrex::ParallelDescriptor::Send(package, m_io_rank, 1);
+ int list_size = static_cast<int>(msgs_with_counter_and_ranks.size());
+ amrex::ParallelDescriptor::Send(&list_size, 1, m_io_rank, 2);
+ }
+ else if (m_rank == m_io_rank){
+ int vec_size = 0;
+ amrex::ParallelDescriptor::Recv(&vec_size, 1, gather_rank, 0);
+ std::vector<char> package(vec_size);
+ amrex::ParallelDescriptor::Recv(package, gather_rank, 1);
+ int list_size = 0;
+ amrex::ParallelDescriptor::Recv(&list_size, 1, gather_rank, 2);
+ auto it = package.cbegin();
+ for (int i = 0; i < list_size; ++i){
+ const auto vec = get_out_vec<char>(it);
+ msgs_with_counter_and_ranks.emplace_back(
+ MsgWithCounterAndRanks::deserialize(vec.begin())
+ );
+ }
+ }
+ }
+}
+
+namespace
+{
+std::vector<char>
+get_serialized_gather_rank_msgs(
+ const std::vector<Msg>& my_msgs,
+ const int gather_rank,
+ const int my_rank)
+{
+ const bool is_gather_rank = (my_rank == gather_rank);
+
+ auto serialized_gather_rank_msgs = std::vector<char>{};
+ int size_serialized_gather_rank_msgs = 0;
+
+ if (is_gather_rank){
+ serialized_gather_rank_msgs = ::serialize_msgs(my_msgs);
+ size_serialized_gather_rank_msgs = static_cast<int>(
+ serialized_gather_rank_msgs.size());
+ }
+
+ amrex::ParallelDescriptor::Bcast(
+ &size_serialized_gather_rank_msgs, 1, gather_rank);
+
+ if (!is_gather_rank)
+ serialized_gather_rank_msgs.resize(
+ size_serialized_gather_rank_msgs);
+
+ amrex::ParallelDescriptor::Bcast(
+ serialized_gather_rank_msgs.data(),
+ size_serialized_gather_rank_msgs, gather_rank);
+
+ return serialized_gather_rank_msgs;
+}
+
+std::vector<char>
+compute_package_for_gather_rank(
+ const std::vector<char>& serialized_gather_rank_msgs,
+ const std::int64_t gather_rank_how_many_msgs,
+ const std::map<Msg, std::int64_t>& my_msg_map,
+ const bool is_gather_rank)
+{
+ if(!is_gather_rank){
+ auto package = std::vector<char>{};
+
+ //generates a copy of the message map
+ auto msgs_to_send = std::map<Msg, std::int64_t>{my_msg_map};
+
+ // For each message of the "gather rank" store how many times
+ // the message has been emitted by the current ranks.
+ const auto gather_rank_msgs =
+ ::deserialize_msgs(serialized_gather_rank_msgs);
+ std::vector<std::int64_t> gather_rank_msg_counters(gather_rank_how_many_msgs);
+ std::int64_t counter = 0;
+ for (const auto& msg : gather_rank_msgs){
+ const auto pp = msgs_to_send.find(msg);
+ if (pp != msgs_to_send.end()){
+ gather_rank_msg_counters[counter] += pp->second;
+ // Remove messages already seen by "gather rank" from
+ // the messages to send back
+ msgs_to_send.erase(msg);
+ }
+ counter++;
+ }
+ put_in_vec(gather_rank_msg_counters, package);
+
+ // Add the additional messages seen by the current rank to the package
+ put_in(static_cast<int>(msgs_to_send.size()), package);
+ for (const auto& el : msgs_to_send)
+ put_in_vec<char>(
+ MsgWithCounter{el.first, el.second}.serialize(), package);
+
+ return package;
+ }
+
+ return std::vector<char>{};
+}
+
+std::pair<std::vector<char>, std::vector<int>>
+gather_all_data(
+ const std::vector<char>& package_for_gather_rank,
+ const int gather_rank, const int my_rank)
+{
+ auto package_lengths = std::vector<int>{};
+ auto all_data = std::vector<char>{};
+ auto displacements = std::vector<int>{};
+
+ if(gather_rank != my_rank){
+ amrex::ParallelDescriptor::Gather(
+ static_cast<int>(package_for_gather_rank.size()), gather_rank);
+ amrex::ParallelDescriptor::Gatherv(
+ package_for_gather_rank.data(),
+ package_for_gather_rank.size(),
+ all_data.data(),
+ package_lengths,
+ displacements,
+ gather_rank);
+ }
+ else{
+ const int zero_size = 0;
+ package_lengths =
+ amrex::ParallelDescriptor::Gather(zero_size, gather_rank);
+
+ // Compute displacements
+ // Given (n1, n2, n3, n4, ..., n_n) we need (0, n1, n1+n2, n1+n2+n3, ...),
+ // but partial_sum gives us (n1,n1+n2, n1+n2+n3, n1+n2+n3+n4, ...).
+ // Rotating this last vector by one is just shifting: (n1+n2+n3+n4+...,n1, n1+n2, n1+n2+n3, ...).
+ // Then we just need to replace the first element with zero: (0,n1, n1+n2, n1+n2+n3, ...).
+ displacements.resize(package_lengths.size());
+ std::partial_sum(package_lengths.begin(), package_lengths.end(),
+ displacements.begin());
+ const auto total_sum = displacements.back();
+ std::rotate(displacements.rbegin(),
+ displacements.rbegin()+1,
+ displacements.rend());
+ displacements[0] = 0;
+
+ all_data.resize(total_sum);
+
+ amrex::ParallelDescriptor::Gatherv(
+ static_cast<char*>(nullptr),
+ 0,
+ all_data.data(),
+ package_lengths,
+ displacements,
+ gather_rank);
+ }
+ return std::make_pair(all_data, displacements);
+}
+
+std::vector<char> serialize_msgs(
+ const std::vector<Msg>& msgs)
+{
+ auto serialized = std::vector<char>{};
+
+ const auto how_many = static_cast<int> (msgs.size());
+ put_in (how_many, serialized);
+
+ for (auto msg : msgs){
+ put_in_vec(msg.serialize(), serialized);
+ }
+ return serialized;
+}
+
+std::vector<Msg> deserialize_msgs(
+ const std::vector<char>& serialized)
+{
+ auto it = serialized.begin();
+
+ const auto how_many = get_out<int>(it);
+ auto msgs = std::vector<Msg>{};
+ msgs.reserve(how_many);
+
+ for (int i = 0; i < how_many; ++i){
+ const auto vv = get_out_vec<char>(it);
+ msgs.emplace_back(Msg::deserialize(vv.begin()));
+ }
+
+ return msgs;
+}
+}
+
+#endif
+
diff --git a/Source/Utils/MsgLogger/MsgLoggerSerialization.H b/Source/Utils/MsgLogger/MsgLoggerSerialization.H
new file mode 100644
index 000000000..fba8bb0d1
--- /dev/null
+++ b/Source/Utils/MsgLogger/MsgLoggerSerialization.H
@@ -0,0 +1,189 @@
+/* Copyright 2021 Luca Fedeli
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#ifndef WARPX_MSG_LOGGER_SERIALIZATION_H_
+#define WARPX_MSG_LOGGER_SERIALIZATION_H_
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace Utils{
+namespace MsgLogger{
+
+ /**
+ * This function transforms a variable of type T into a vector of chars holding its
+ * byte representation and it appends this vector at the end of an
+ * existing vector of chars. T must be either a trivially copyable type or an std::string
+ * (see specialization)
+ *
+ * @tparam T the variable type
+ * @param[in] val a variable of type T to be serialized
+ * @param[in, out] vec a reference to the vector to which the byte representation of val is appended
+ */
+ template <typename T>
+ void put_in(const T& val, std::vector<char>& vec)
+ {
+ static_assert(std::is_trivially_copyable<T>(),
+ "Cannot serialize non-trivally copyable types, except std::string.");
+
+ const auto* ptr_val = reinterpret_cast<const char*>(&val);
+ vec.insert(vec.end(), ptr_val, ptr_val+sizeof(T));
+ }
+
+ /**
+ * This function transforms a string into a vector of chars holding its
+ * byte representation and it appends this vector at the end of an
+ * existing vector of chars (specialization of put_in<T>).
+ *
+ * @param[in] val a std::string to be serialized
+ * @param[in, out] vec a reference to the vector to which the byte representation of val is appended
+ */
+ template <>
+ inline void put_in<std::string> (const std::string& val, std::vector<char>& vec)
+ {
+ const char* c_str = val.c_str();
+ const auto length = static_cast<int>(val.size());
+
+ put_in(length, vec);
+ vec.insert(vec.end(), c_str, c_str+length);
+ }
+
+ /**
+ * This function transforms an std::vector<T> into a vector of chars holding its
+ * byte representation and it appends this vector at the end of an
+ * existing vector of chars. T must be either a trivially copyable type or an std::string.
+ * A specialization exists in case val is a vector of chars.
+ *
+ * @tparam T the variable type
+ * @param[in] val a variable of type T to be serialized
+ * @param[in, out] vec a reference to the vector to which the byte representation of val is appended
+ */
+ template <typename T>
+ inline void put_in_vec (const std::vector<T>& val, std::vector<char>& vec)
+ {
+ static_assert(std::is_trivially_copyable<T>() || std::is_same<T,std::string>(),
+ "Cannot serialize vectors of non-trivally copyable types"
+ ", except vectors of std::string.");
+
+ put_in(static_cast<int>(val.size()), vec);
+ for (const auto& el : val)
+ put_in(el, vec);
+ }
+
+ /**
+ * This function transforms an std::vector<char> into a vector of chars holding its
+ * byte representation and it appends this vector at the end of an
+ * existing vector of chars (specialization of put_in_vec<T>).
+ *
+ * @tparam T the variable type
+ * @param[in] val a variable of type T to be serialized
+ * @param[in, out] vec a reference to the vector to which the byte representation of val is appended
+ */
+ template <>
+ inline void put_in_vec <char> (const std::vector<char>& val, std::vector<char>& vec)
+ {
+ put_in(static_cast<int>(val.size()), vec);
+ vec.insert(vec.end(), val.begin(), val.end());
+ }
+
+ /**
+ * This function extracts a variable of type T from a byte vector, at the position
+ * given by a std::vector<char> iterator. The iterator is then advanced according to
+ * the number of bytes read from the byte vector. T must be either a trivially copyable type
+ * or an std::string (see specialization below).
+ *
+ * @tparam T the variable type (must be trivially copyable)
+ * @param[in, out] it the iterator to a byte vector
+ * @return the variable extracted from the byte array
+ */
+ template<typename T>
+ T get_out(std::vector<char>::const_iterator& it)
+ {
+ static_assert(std::is_trivially_copyable<T>(),
+ "Cannot extract non-trivally copyable types from char vectors,"
+ " with the exception of std::string.");
+
+ auto temp = std::array<char, sizeof(T)>{};
+ std::copy(it, it + sizeof(T), temp.begin());
+ it += sizeof(T);
+ T res;
+ std::memcpy(&res, temp.data(), sizeof(T));
+
+ return res;
+ }
+
+ /**
+ * This function extracts an std::string from a byte vector, at the position
+ * given by a std::vector<char> iterator. The iterator is then advanced according to
+ * the number of bytes read from the byte vector. This is a specialization of
+ * get_out<T>
+ *
+ * @param[in, out] it the iterator to a byte vector
+ * @return the std::string extracted from the byte array
+ */
+ template<>
+ inline std::string get_out<std::string> (std::vector<char>::const_iterator& it)
+ {
+ const auto length = get_out<int> (it);
+ const auto str = std::string{it, it+length};
+ it += length;
+
+ return str;
+ }
+
+ /**
+ * This function extracts an std::vector<T> from a byte vector, at the position
+ * given by a std::vector<char> iterator. The iterator is then advanced according to
+ * the number of bytes read from the byte vector. T must be either a trivially copyable type
+ * or an std::string.
+ *
+ * @tparam T the variable type (must be trivially copyable)
+ * @param[in, out] it the iterator to a byte vector
+ * @return the variable extracted from the byte array
+ */
+ template<typename T>
+ inline std::vector<T> get_out_vec (std::vector<char>::const_iterator& it)
+ {
+ static_assert(std::is_trivially_copyable<T>() || std::is_same<T,std::string>(),
+ "Cannot extract non-trivally copyable types from char vectors,"
+ " with the exception of std::string.");
+
+ const auto length = get_out<int> (it);
+ std::vector<T> res(length);
+ for (int i = 0; i < length; ++i)
+ res[i] = get_out<T>(it);
+
+ return res;
+ }
+
+ /**
+ * This function extracts an std::vector<char> from a byte vector, at the position
+ * given by a std::vector<char> iterator. The iterator is then advanced according to
+ * the number of bytes read from the byte vector. This is a specialization of get_out_vec<T>.
+ *
+ * @param[in, out] it the iterator to a byte vector
+ * @return the variable extracted from the byte array
+ */
+ template<>
+ inline std::vector<char> get_out_vec<char> (std::vector<char>::const_iterator& it)
+ {
+ const auto length = get_out<int> (it);
+ std::vector<char> res(length);
+ std::copy(it, it+length, res.begin());
+ it += length;
+
+ return res;
+ }
+
+}
+}
+
+#endif //WARPX_MSG_LOGGER_SERIALIZATION_H_
diff --git a/Source/Utils/MsgLogger/MsgLogger_fwd.H b/Source/Utils/MsgLogger/MsgLogger_fwd.H
new file mode 100644
index 000000000..626348670
--- /dev/null
+++ b/Source/Utils/MsgLogger/MsgLogger_fwd.H
@@ -0,0 +1,24 @@
+/* Copyright 2021 Luca Fedeli
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#ifndef WARPX_MSG_LOGGER_FWD_H
+#define WARPX_MSG_LOGGER_FWD_H
+
+namespace Utils{
+namespace MsgLogger{
+
+ enum class Priority;
+
+ struct Msg;
+ struct MsgWithCounter;
+ struct MsgWithCounterAndRanks;
+
+ class Logger;
+}
+}
+
+#endif //WARPX_MSG_LOGGER_FWD_H
diff --git a/Source/Utils/WarnManager.H b/Source/Utils/WarnManager.H
new file mode 100644
index 000000000..9a360ac83
--- /dev/null
+++ b/Source/Utils/WarnManager.H
@@ -0,0 +1,135 @@
+/* Copyright 2021 Luca Fedeli
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#ifndef WARPX_WARN_MANAGER_H_
+#define WARPX_WARN_MANAGER_H_
+
+#include "WarnManager_fwd.H"
+
+#include "MsgLogger/MsgLogger_fwd.H"
+
+#include <AMReX_ParmParse.H>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace Utils
+{
+ /**
+ * The class WarnManager manages warning messages in WarpX,
+ * providing methods to record warnings, and print warning
+ * lists.
+ */
+ class WarnManager
+ {
+ public:
+
+ /**
+ * The constructor.
+ */
+ WarnManager();
+
+ /**
+ * \brief This function records a warning message.
+ *
+ * @param[in] topic a string to identify the topic of the warning (e.g., "parallelization", "pbc", "particles"...)
+ * @param[in] text the text of the warning message
+ * @param[in] priority priority of the warning message ("medium" by default)
+ */
+ void record_warning(
+ std::string topic,
+ std::string text,
+ MsgLogger::Priority priority);
+
+ /**
+ * \brief This function prints all the warning messages collected on the present MPI rank
+ * (i.e., this is not a collective call). This function is mainly intended for debug purposes.
+ *
+ * @param[in] when a string to mark when the warnings are printed out (it appears in the warning list)
+ * @return a string containing the "local" warning list
+ */
+ std::string print_local_warnings(
+ const std::string& when) const;
+
+ /**
+ * \brief This function prints all the warning messages collected by all the MPI ranks
+ * (i.e., this is a collective call). Only the I/O rank prints the message.
+ *
+ * @param[in] when a string to mark when the warnings are printed out (it appears in the warning list)
+ * @return a string containing the "global" warning list
+ */
+ std::string print_global_warnings(
+ const std::string& when) const;
+
+ /**
+ * \brief This function reads warning messages from the inputfile. It is intended for
+ * debug&testing purposes
+ *
+ * @param[in, out] params the inputfile parser
+ */
+ void debug_read_warnings_from_input(amrex::ParmParse& params);
+
+ static const int warn_line_size = 80 /*! Maximum line length to be used in formatting warning list*/;
+ static const int warn_tab_size = 5 /*! Tabulation size to be used in formatting warning list*/;
+
+ private:
+
+ /**
+ * \brief This function generates the header of the warning messages list
+ *
+ * @param[in] when a string to mark when the warnings are printed out (it appears in the warning list)
+ * @param[in] line_size maximum line length to be used in formatting warning list
+ * @param[in] is_global flag: true if the header is for a global warning list, false otherwise
+ * @return a string containing the header of the warning list
+ */
+ std::string get_header(
+ const std::string& when,
+ const int line_size,
+ const bool is_global) const;
+
+ /**
+ * \brief This function generates a string for a single entry of the warning list
+ * for a MessageWithCounter struct (i.e., a warning message paired with a counter storing
+ * how many times the warning has been raised)
+ *
+ * @param[in] msg_with_counter a MessageWithCounter
+ * @return a string containing the warning message
+ */
+ std::string print_warn_msg(
+ const MsgLogger::MsgWithCounter& msg_with_counter) const;
+
+ /**
+ * \brief This function generates a string for a single entry of the warning list
+ * for a MsgWithCounterAndRanks struct (i.e., a warning message paired with a counter storing
+ * how many times the warning has been raised and info on which ranks have raised the warning)
+ *
+ * @param[in] msg_with_counter_and_ranks a MsgWithCounterAndRanks
+ * @return a string containing the warning message
+ */
+ std::string print_warn_msg(
+ const MsgLogger::MsgWithCounterAndRanks& msg_with_counter_and_ranks) const;
+
+ /**
+ * \brief This function formats each line of a warning message text
+ *
+ * @param[in] msg the warning message text
+ * @param[in] line_size maximum line length to be used in formatting warning list
+ * @param[in] tab_size tabulation size to be used in formatting warning list
+ * @return a string containing the formatted warning message text
+ */
+ std::string msg_formatter(
+ const std::string& msg,
+ const int line_size,
+ const int tab_size) const;
+
+ int m_rank = 0 /*! MPI rank (appears in the warning list)*/;
+ std::unique_ptr<MsgLogger::Logger> m_p_logger /*! The Logger stores all the warning messages*/;
+ };
+}
+
+#endif //WARPX_WARN_MANAGER_H_
diff --git a/Source/Utils/WarnManager.cpp b/Source/Utils/WarnManager.cpp
new file mode 100644
index 000000000..c3b8d3159
--- /dev/null
+++ b/Source/Utils/WarnManager.cpp
@@ -0,0 +1,239 @@
+/* Copyright 2021 Luca Fedeli
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#include "WarnManager.H"
+
+#include "MsgLogger/MsgLogger.H"
+
+#include <AMReX_ParallelDescriptor.H>
+
+#include <algorithm>
+#include <sstream>
+
+using namespace Utils;
+using namespace Utils::MsgLogger;
+
+WarnManager::WarnManager(){
+ m_rank = amrex::ParallelDescriptor::MyProc();
+ m_p_logger = std::make_unique<Logger>();
+}
+
+void WarnManager::record_warning(
+ std::string topic,
+ std::string text,
+ Priority priority)
+{
+ m_p_logger->record_msg(Msg{topic, text, priority});
+}
+
+std::string WarnManager::print_local_warnings(const std::string& when) const
+{
+ auto all_warnings = m_p_logger->get_msgs_with_counter();
+ std::sort(all_warnings.begin(), all_warnings.end(),
+ [](const auto& a, const auto& b){return a.msg < b.msg;});
+
+ std::stringstream ss;
+
+ ss << "\n" << get_header(when, warn_line_size, false);
+
+ if(all_warnings.size() == 0){
+ ss << "* No recorded warnings.\n";
+ }
+ else{
+ for(const auto& warn_msg : all_warnings){
+ ss << print_warn_msg(warn_msg);
+ ss << "*\n";
+ }
+ }
+
+ ss << std::string(warn_line_size, '*') << "\n\n" ;
+
+ return ss.str();
+}
+
+std::string WarnManager::print_global_warnings(const std::string& when) const
+{
+ auto all_warnings =
+ m_p_logger->collective_gather_msgs_with_counter_and_ranks();
+
+ if(m_rank != amrex::ParallelDescriptor::IOProcessorNumber())
+ return "[see I/O rank message]";
+
+ std::sort(all_warnings.begin(), all_warnings.end(),
+ [](const auto& a, const auto& b){
+ return a.msg_with_counter.msg < b.msg_with_counter.msg;});
+
+ std::stringstream ss;
+
+ ss << "\n" << get_header(when, warn_line_size, true);
+
+ if(all_warnings.size() == 0){
+ ss << "* No recorded warnings.\n";
+ }
+ else{
+ for(const auto& warn_msg : all_warnings){
+ ss << print_warn_msg(warn_msg);
+ ss << "*\n";
+ }
+ }
+
+ ss << std::string(warn_line_size, '*') << "\n\n" ;
+
+ return ss.str();
+}
+
+void WarnManager::debug_read_warnings_from_input(amrex::ParmParse& params)
+{
+ std::vector<std::string> warnings;
+ params.queryarr("test_warnings", warnings);
+
+ for (const auto& warn : warnings){
+ amrex::ParmParse pp_warn(warn);
+
+ std::string topic;
+ pp_warn.query("topic", topic);
+
+ std::string msg;
+ pp_warn.query("msg", msg);
+
+ std::string spriority;
+ pp_warn.query("priority", spriority);
+ Priority priority = StringToPriority(spriority);
+
+ int all_involved = 0;
+ pp_warn.query("all_involved", all_involved);
+ if(all_involved){
+ this->record_warning(topic, msg, priority);
+ }
+ else{
+ std::vector<int> who_involved;
+ pp_warn.queryarr("who_involved", who_involved);
+ if(std::find (who_involved.begin(), who_involved.end(), m_rank)
+ != who_involved.end()){
+ this->record_warning(topic, msg, priority);
+ }
+ }
+ }
+
+}
+
+std::string WarnManager::get_header(
+ const std::string& when,
+ const int line_size,
+ const bool is_global) const
+{
+ const std::string warn_header{"**** WARNINGS "};
+
+ std::stringstream ss;
+
+ ss << warn_header <<
+ std::string(line_size - static_cast<int>(warn_header.length()), '*') << "\n" ;
+
+ if(is_global){
+ ss << "* GLOBAL warning list after " << " [ " << when << " ]\n*\n";
+ }
+ else{
+ auto const mpi_rank = amrex::ParallelDescriptor::MyProc();
+ ss << "* LOCAL" << " ( rank # " << mpi_rank << " ) "
+ << " warning list after " << when << "\n*\n";
+ }
+
+ return ss.str();
+}
+
+std::string WarnManager::print_warn_msg(
+ const MsgLogger::MsgWithCounter& msg_with_counter) const
+{
+ std::stringstream ss;
+ ss << "* --> ";
+ if (msg_with_counter.msg.priority == MsgLogger::Priority::high)
+ ss << "[!!!]";
+ else if (msg_with_counter.msg.priority == MsgLogger::Priority::medium)
+ ss << "[!! ]";
+ else if (msg_with_counter.msg.priority == MsgLogger::Priority::low)
+ ss << "[! ]";
+ else
+ ss << "[???]";
+
+ ss << " [" + msg_with_counter.msg.topic << "] ";
+
+ if(msg_with_counter.counter == 2)
+ ss << "[raised twice]\n";
+ else if(msg_with_counter.counter == 1)
+ ss << "[raised once]\n";
+ else
+ ss << "[raised " << msg_with_counter.counter << " times]\n";
+
+ ss << msg_formatter(msg_with_counter.msg.text, warn_line_size, warn_tab_size);
+
+ return ss.str();
+}
+
+std::string WarnManager::print_warn_msg(
+ const MsgLogger::MsgWithCounterAndRanks& msg_with_counter_and_ranks) const
+{
+ std::stringstream ss;
+ ss << this->print_warn_msg(msg_with_counter_and_ranks.msg_with_counter);
+
+ std::string raised_by = "@ Raised by: ";
+ if (!msg_with_counter_and_ranks.all_ranks){
+ for (const auto rr : msg_with_counter_and_ranks.ranks)
+ raised_by += " " + std::to_string(rr);
+ }
+ else{
+ raised_by += "ALL\n";
+ }
+ ss << msg_formatter(raised_by, warn_line_size, warn_tab_size);
+
+ return ss.str();
+}
+
+std::string
+WarnManager::msg_formatter(
+ const std::string& msg,
+ const int line_size,
+ const int tab_size) const
+{
+ const auto prefix = "*" + std::string(tab_size, ' ');
+ const auto prefix_length = static_cast<int>(prefix.length());
+
+ std::stringstream ss_out;
+ std::stringstream ss_msg{msg};
+
+ std::string line;
+ std::string word;
+
+ while(std::getline(ss_msg, line,'\n')){
+ ss_out << prefix;
+
+ std::stringstream ss_line{line};
+ int counter = prefix_length;
+
+ while (ss_line >> word){
+ const auto wlen = static_cast<int>(word.length());
+
+ if(counter == prefix_length){
+ ss_out << word;
+ counter += wlen;
+ }
+ else{
+ if (counter + wlen < line_size){
+ ss_out << " " << word;
+ counter += (wlen+1);
+ }
+ else{
+ ss_out << "\n" << prefix << word;
+ counter = prefix_length + wlen;
+ }
+ }
+ }
+
+ ss_out << '\n';
+ }
+
+ return ss_out.str();
+}
diff --git a/Source/Utils/WarnManager_fwd.H b/Source/Utils/WarnManager_fwd.H
new file mode 100644
index 000000000..e12cf7910
--- /dev/null
+++ b/Source/Utils/WarnManager_fwd.H
@@ -0,0 +1,16 @@
+/* Copyright 2021 Luca Fedeli
+ *
+ * This file is part of WarpX.
+ *
+ * License: BSD-3-Clause-LBNL
+ */
+
+#ifndef WARPX_WARN_MANAGER_FWD_H
+#define WARPX_WARN_MANAGER_FWD_H
+
+namespace Utils
+{
+ class WarnManager;
+}
+
+#endif //WARPX_WARN_MANAGER_FWD_H
diff --git a/Source/WarpX.H b/Source/WarpX.H
index 588648c69..0ba6e9001 100644
--- a/Source/WarpX.H
+++ b/Source/WarpX.H
@@ -35,6 +35,7 @@
#include "Particles/MultiParticleContainer_fwd.H"
#include "Particles/WarpXParticleContainer_fwd.H"
#include "Utils/IntervalsParser.H"
+#include "Utils/WarnManager_fwd.H"
#include "Utils/WarpXAlgorithmSelection.H"
#include <AMReX.H>
@@ -74,6 +75,27 @@ enum struct PatchType : int
coarse
};
+/** WarnPriority is recorded together with warning messages. It influences
+ * the display order and the appearance of a warning message.
+ * This enum class mirrors Utils::MsgLogger::Priority.
+*/
+enum class WarnPriority
+{
+ /** Low priority warning:
+ * essentially an informative message
+ */
+ low,
+ /** Medium priority warning:
+ * a bug or a performance issue may affect the simulation
+ */
+ medium,
+ /** High priority warning:
+ * a very serious bug or performance issue
+ * almost certainly affects the simulation
+ */
+ high
+};
+
class WarpX
: public amrex::AmrCore
{
@@ -92,6 +114,34 @@ public:
int Verbose () const { return verbose; }
+ /**
+ * \brief This function records a warning message.
+ * RecordWarning is thread safe: it can be used within OpenMP parallel loops.
+ *
+ * @param[in] topic a string to identify the topic of the warning (e.g., "parallelization", "pbc", "particles"...)
+ * @param[in] text the text of the warning message
+ * @param[in] priority priority of the warning message ("medium" by default)
+ */
+ void RecordWarning(
+ std::string topic,
+ std::string text,
+ WarnPriority priority = WarnPriority::medium);
+
+ /**
+ * \brief This function prints all the warning messages collected on the present MPI rank
+ * (i.e., this is not a collective call). This function is mainly intended for debug purposes.
+ *
+ * @param[in] when a string to mark when the warnings are printed out (it appears in the warning list)
+ */
+ void PrintLocalWarnings(const std::string& when);
+
+ /**
+ * \brief This function prints all the warning messages collected by all the MPI ranks
+ * (i.e., this is a collective call). Only the I/O rank prints the message.
+ *
+ * @param[in] when a string to mark when the warnings are printed out (it appears in the warning list)
+ */
+ void PrintGlobalWarnings(const std::string& when);
void InitData ();
@@ -938,6 +988,11 @@ private:
# endif
#endif
+ // Warning manager: it allows recording and printing error messages
+ std::unique_ptr<Utils::WarnManager> m_p_warn_manager;
+ // Flag to control if WarpX has to emit a warning message as soon as a warning is recorded
+ bool m_always_warn_immediately = false;
+
amrex::Vector<int> istep; // which step?
amrex::Vector<int> nsubsteps; // how many substeps on each level?
diff --git a/Source/WarpX.cpp b/Source/WarpX.cpp
index 7f620e515..acc6e5844 100644
--- a/Source/WarpX.cpp
+++ b/Source/WarpX.cpp
@@ -29,8 +29,11 @@
#include "Filter/NCIGodfreyFilter.H"
#include "Particles/MultiParticleContainer.H"
#include "Particles/ParticleBoundaryBuffer.H"
+#include "Utils/MsgLogger/MsgLogger.H"
+#include "Utils/WarnManager.H"
#include "Utils/WarpXAlgorithmSelection.H"
#include "Utils/WarpXConst.H"
+#include "Utils/WarpXProfilerWrapper.H"
#include "Utils/WarpXUtil.H"
#ifdef AMREX_USE_SENSEI_INSITU
@@ -216,6 +219,9 @@ WarpX::ResetInstance ()
WarpX::WarpX ()
{
m_instance = this;
+
+ m_p_warn_manager = std::make_unique<Utils::WarnManager>();
+
ReadParameters();
BackwardCompatibility();
@@ -410,6 +416,51 @@ WarpX::~WarpX ()
}
void
+WarpX::RecordWarning(
+ std::string topic,
+ std::string text,
+ WarnPriority priority)
+{
+ WARPX_PROFILE("WarpX::RecordWarning");
+
+ auto msg_priority = Utils::MsgLogger::Priority::high;
+ if(priority == WarnPriority::low)
+ msg_priority = Utils::MsgLogger::Priority::low;
+ else if(priority == WarnPriority::medium)
+ msg_priority = Utils::MsgLogger::Priority::medium;
+
+ if(m_always_warn_immediately){
+ amrex::Warning(
+ "!!!!!! WARNING: ["
+ + std::string(Utils::MsgLogger::PriorityToString(msg_priority))
+ + "][" + topic + "] " + text);
+ }
+
+#ifdef AMREX_USE_OMP
+ #pragma omp critical
+#endif
+ {
+ m_p_warn_manager->record_warning(topic, text, msg_priority);
+ }
+}
+
+void
+WarpX::PrintLocalWarnings(const std::string& when)
+{
+ WARPX_PROFILE("WarpX::PrintLocalWarnings");
+ const auto warn_string = m_p_warn_manager->print_local_warnings(when);
+ amrex::AllPrint() << warn_string;
+}
+
+void
+WarpX::PrintGlobalWarnings(const std::string& when)
+{
+ WARPX_PROFILE("WarpX::PrintGlobalWarnings");
+ const auto warn_string = m_p_warn_manager->print_global_warnings(when);
+ amrex::Print() << warn_string;
+}
+
+void
WarpX::ReadParameters ()
{
{
@@ -433,6 +484,11 @@ WarpX::ReadParameters ()
{
ParmParse pp_warpx("warpx");
+ //"Synthetic" warning messages may be injected in the Warning Manager via
+ // inputfile for debug&testing purposes.
+ m_p_warn_manager->debug_read_warnings_from_input(pp_warpx);
+ pp_warpx.query("always_warn_immediately", m_always_warn_immediately);
+
std::vector<int> numprocs_in;
queryArrWithParser(pp_warpx, "numprocs", numprocs_in, 0, AMREX_SPACEDIM);
if (not numprocs_in.empty()) {
@@ -620,10 +676,9 @@ WarpX::ReadParameters ()
// (see https://github.com/ECP-WarpX/WarpX/issues/1943)
if (use_filter)
{
- amrex::Print() << "\nWARNING:"
- << "\nFilter currently not working with FDTD solver in RZ geometry:"
- << "\nwe recommend setting warpx.use_filter = 0 in the input file.\n"
- << std::endl;
+ this->RecordWarning("Filter",
+ "Filter currently not working with FDTD solver in RZ geometry."
+ "We recommend setting warpx.use_filter = 0 in the input file.");
}
}
#endif
@@ -904,9 +959,10 @@ WarpX::ReadParameters ()
if ((maxLevel() > 0) && (particle_shape > 1) && (do_pml_j_damping == 1))
{
- amrex::Warning("\nWARNING: When algo.particle_shape > 1,"
- " some numerical artifact will be present at the interface between coarse and fine patch."
- "\nWe recommend setting algo.particle_shape = 1 in order to avoid this issue");
+ this->RecordWarning("Particles",
+ "When algo.particle_shape > 1,"
+ "some numerical artifact will be present at the interface between coarse and fine patch."
+ "We recommend setting algo.particle_shape = 1 in order to avoid this issue");
}
// default sort interval for particles if species or lasers vector is not empty
@@ -1279,17 +1335,23 @@ WarpX::BackwardCompatibility ()
ParmParse pp_particles("particles");
int nspecies;
if (pp_particles.query("nspecies", nspecies)){
- amrex::Print()<<"particles.nspecies is ignored. Just use particles.species_names please.\n";
+ this->RecordWarning("Species",
+ "particles.nspecies is ignored. Just use particles.species_names please.",
+ WarnPriority::low);
}
ParmParse pp_collisions("collisions");
int ncollisions;
if (pp_collisions.query("ncollisions", ncollisions)){
- amrex::Print()<<"collisions.ncollisions is ignored. Just use particles.collision_names please.\n";
+ this->RecordWarning("Collisions",
+ "collisions.ncollisions is ignored. Just use particles.collision_names please.",
+ WarnPriority::low);
}
ParmParse pp_lasers("lasers");
int nlasers;
if (pp_lasers.query("nlasers", nlasers)){
- amrex::Print()<<"lasers.nlasers is ignored. Just use lasers.names please.\n";
+ this->RecordWarning("Laser",
+ "lasers.nlasers is ignored. Just use lasers.names please.",
+ WarnPriority::low);
}
}
diff --git a/Source/main.cpp b/Source/main.cpp
index 751a63fb0..185746a4d 100644
--- a/Source/main.cpp
+++ b/Source/main.cpp
@@ -66,6 +66,8 @@ int main(int argc, char* argv[])
warpx.Evolve();
+ warpx.PrintGlobalWarnings("THE END"); //Print warning messages at the end of the simulation
+
if (warpx.Verbose()) {
auto end_total = static_cast<Real>(amrex::second()) - strt_total;
ParallelDescriptor::ReduceRealMax(end_total, ParallelDescriptor::IOProcessorNumber());