diff options
Diffstat (limited to 'Source')
161 files changed, 4526 insertions, 860 deletions
diff --git a/Source/BoundaryConditions/PML.H b/Source/BoundaryConditions/PML.H index 5ab84439f..b8ed0ff7a 100644 --- a/Source/BoundaryConditions/PML.H +++ b/Source/BoundaryConditions/PML.H @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Aurore Blelly, Axel Huebl + * Maxence Thevenet, Remi Lehe, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <array> #ifndef WARPX_PML_H_ diff --git a/Source/BoundaryConditions/PML.cpp b/Source/BoundaryConditions/PML.cpp index 51439430d..3f2acc6a8 100644 --- a/Source/BoundaryConditions/PML.cpp +++ b/Source/BoundaryConditions/PML.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Aurore Blelly, Axel Huebl + * Maxence Thevenet, Remi Lehe, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <PML.H> #include <WarpX.H> #include <WarpXConst.H> diff --git a/Source/BoundaryConditions/PML_current.H b/Source/BoundaryConditions/PML_current.H index fa5bbf3f9..1d0249d56 100644 --- a/Source/BoundaryConditions/PML_current.H +++ b/Source/BoundaryConditions/PML_current.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Aurore Blelly, Axel Huebl, Maxence Thevenet + * Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef PML_CURRENT_H_ #define PML_CURRENT_H_ diff --git a/Source/BoundaryConditions/WarpXEvolvePML.cpp b/Source/BoundaryConditions/WarpXEvolvePML.cpp index bd29d1b65..0e3665c86 100644 --- a/Source/BoundaryConditions/WarpXEvolvePML.cpp +++ b/Source/BoundaryConditions/WarpXEvolvePML.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Aurore Blelly, Axel Huebl, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <cmath> #include <limits> diff --git a/Source/BoundaryConditions/WarpX_PML_kernels.H b/Source/BoundaryConditions/WarpX_PML_kernels.H index 89fdb4911..8a573c4b9 100644 --- a/Source/BoundaryConditions/WarpX_PML_kernels.H +++ b/Source/BoundaryConditions/WarpX_PML_kernels.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Remi Lehe, Revathi Jambunathan, Revathi Jambunathan + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PML_KERNELS_H_ #define WARPX_PML_KERNELS_H_ diff --git a/Source/Diagnostics/BackTransformedDiagnostic.H b/Source/Diagnostics/BackTransformedDiagnostic.H index 5621d48c6..0d36c97d9 100644 --- a/Source/Diagnostics/BackTransformedDiagnostic.H +++ b/Source/Diagnostics/BackTransformedDiagnostic.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, Maxence Thevenet + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_BackTransformedDiagnostic_H_ #define WARPX_BackTransformedDiagnostic_H_ diff --git a/Source/Diagnostics/BackTransformedDiagnostic.cpp b/Source/Diagnostics/BackTransformedDiagnostic.cpp index 452828f02..0f45c9f42 100644 --- a/Source/Diagnostics/BackTransformedDiagnostic.cpp +++ b/Source/Diagnostics/BackTransformedDiagnostic.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, Maxence Thevenet + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <AMReX_MultiFabUtil.H> #include <AMReX_MultiFabUtil_C.H> @@ -559,7 +566,7 @@ BackTransformedDiagnostic(Real zmin_lab, Real zmax_lab, Real v_window_lab, std::vector<std::string> user_fields_to_dump; ParmParse pp("warpx"); bool do_user_fields; - do_user_fields = pp.queryarr("boosted_frame_diag_fields", + do_user_fields = pp.queryarr("back_transformed_diag_fields", user_fields_to_dump); // If user specifies fields to dump, overwrite ncomp_to_dump, // map_actual_fields_to_dump and mesh_field_names. diff --git a/Source/Diagnostics/ElectrostaticIO.cpp b/Source/Diagnostics/ElectrostaticIO.cpp index 332638cff..8fb90ae4c 100644 --- a/Source/Diagnostics/ElectrostaticIO.cpp +++ b/Source/Diagnostics/ElectrostaticIO.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, David Bizzozero + * Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <WarpX_f.H> diff --git a/Source/Diagnostics/FieldIO.H b/Source/Diagnostics/FieldIO.H index 7cdc9b710..193fe8bd5 100644 --- a/Source/Diagnostics/FieldIO.H +++ b/Source/Diagnostics/FieldIO.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, David Grote, Igor Andriyash + * Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_FielIO_H_ #define WARPX_FielIO_H_ diff --git a/Source/Diagnostics/FieldIO.cpp b/Source/Diagnostics/FieldIO.cpp index e1bb8cb54..26545ea04 100644 --- a/Source/Diagnostics/FieldIO.cpp +++ b/Source/Diagnostics/FieldIO.cpp @@ -1,8 +1,16 @@ +/* Copyright 2019-2020 Andrew Myers, Axel Huebl, David Grote + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <FieldIO.H> #ifdef WARPX_USE_OPENPMD -#include <openPMD/openPMD.hpp> +# include <openPMD/openPMD.hpp> #endif #include <AMReX_FillPatchUtil_F.H> @@ -352,6 +360,27 @@ AverageAndPackVectorField( MultiFab& mf_avg, } } +/** \brief Takes all of the components of the three fields and + * averages and packs them into the MultiFab mf_avg. + */ +void +AverageAndPackVectorFieldComponents (MultiFab& mf_avg, + const std::array< std::unique_ptr<MultiFab>, 3 >& vector_field, + const DistributionMapping& dm, + int& dcomp, const int ngrow ) +{ + if (vector_field[0]->nComp() > 1) { + std::array<std::unique_ptr<MultiFab>,3> vector_field_component; + for (int icomp=0 ; icomp < vector_field[0]->nComp() ; icomp++) { + vector_field_component[0].reset(new MultiFab(*vector_field[0], amrex::make_alias, icomp, 1)); + vector_field_component[1].reset(new MultiFab(*vector_field[1], amrex::make_alias, icomp, 1)); + vector_field_component[2].reset(new MultiFab(*vector_field[2], amrex::make_alias, icomp, 1)); + AverageAndPackVectorField(mf_avg, vector_field_component, dm, dcomp, ngrow); + dcomp += 3; + } + } +} + /** \brief Take a MultiFab `scalar_field` * averages it to the cell center, and stores the * resulting MultiFab in mf_avg (in the components dcomp) @@ -378,6 +407,74 @@ AverageAndPackScalarField( MultiFab& mf_avg, } } +/** \brief Takes the specified component of the scalar and + * averages and packs it into the MultiFab mf_avg. + */ +void +AverageAndPackScalarFieldComponent (MultiFab& mf_avg, + const MultiFab& scalar_field, + const int icomp, + const int dcomp, const int ngrow ) +{ + MultiFab scalar_field_component(scalar_field, amrex::make_alias, icomp, 1); + AverageAndPackScalarField(mf_avg, scalar_field_component, dcomp, ngrow); +} + +/** \brief Generate mode variable name + */ +std::string +ComponentName(std::string fieldname, int mode, std::string type) +{ + if (type == "real") { + return fieldname + "_" + std::to_string(mode) + "_" + "real"; + } else if (type == "imag") { + return fieldname + "_" + std::to_string(mode) + "_" + "imag"; + } else { + AMREX_ALWAYS_ASSERT( false ); + } + // This should never be done + return ""; +} + +/* \brief Copy vector field component data into the MultiFab that will be written out + */ +void +CopyVectorFieldComponentsToMultiFab (int lev, amrex::Vector<MultiFab>& mf_avg, MultiFab& mf_tmp, + int icomp, int& dcomp, int ngrow, + std::string fieldname, Vector<std::string>& varnames) +{ + if (mf_tmp.nComp() > 3) { + if (lev==0) varnames.push_back(ComponentName(fieldname, 0, "real")); + MultiFab::Copy( mf_avg[lev], mf_tmp, 3+icomp, dcomp++, 1, ngrow); + int const nmodes = mf_tmp.nComp()/6; + for (int mode=1 ; mode < nmodes ; mode++) { + if (lev==0) varnames.push_back(ComponentName(fieldname, mode, "real")); + MultiFab::Copy( mf_avg[lev], mf_tmp, 3*2*mode+icomp, dcomp++, 1, ngrow); + if (lev==0) varnames.push_back(ComponentName(fieldname, mode, "imag")); + MultiFab::Copy( mf_avg[lev], mf_tmp, 3*2*mode+3+icomp, dcomp++, 1, ngrow); + } + } +} + +/* \brief Copy scalar field component data into the MultiFab that will be written out + */ +void +CopyScalarFieldComponentsToMultiFab (int lev, amrex::Vector<MultiFab>& mf_avg, MultiFab& mf_tmp, + int& dcomp, int ngrow, int n_rz_azimuthal_modes, + std::string fieldname, Vector<std::string>& varnames) +{ + if (n_rz_azimuthal_modes > 1) { + if (lev==0) varnames.push_back(ComponentName(fieldname, 0, "real")); + AverageAndPackScalarFieldComponent(mf_avg[lev], mf_tmp, 0, dcomp++, ngrow); + for (int mode=1 ; mode < n_rz_azimuthal_modes ; mode++) { + if (lev==0) varnames.push_back(ComponentName(fieldname, mode, "real")); + AverageAndPackScalarFieldComponent(mf_avg[lev], mf_tmp, 2*mode-1, dcomp++, ngrow); + if (lev==0) varnames.push_back(ComponentName(fieldname, mode, "imag")); + AverageAndPackScalarFieldComponent(mf_avg[lev], mf_tmp, 2*mode , dcomp++, ngrow); + } + } +} + /** \brief Add variable names to the list. */ void @@ -387,6 +484,15 @@ AddToVarNames (Vector<std::string>& varnames, for(auto coord:coords) varnames.push_back(name+coord+suffix); } +/** \brief Add RZ variable names to the list. + */ +void +AddToVarNamesRZ (Vector<std::string>& varnames, + std::string name, std::string suffix) { + auto coords = {"r", "theta", "z"}; + for(auto coord:coords) varnames.push_back(name+coord+suffix); +} + /** \brief Write the different fields that are meant for output, * into the vector of MultiFab `mf_avg` (one MultiFab per level) * after averaging them to the cell centers. @@ -396,11 +502,28 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, amrex::Vector<MultiFab>& mf_avg, const int ngrow) const { // Count how many different fields should be written (ncomp) - const int ncomp = fields_to_plot.size() + int ncomp = fields_to_plot.size() + static_cast<int>(plot_finepatch)*6 + static_cast<int>(plot_crsepatch)*6 + static_cast<int>(costs[0] != nullptr and plot_costs); + // Add in the RZ modes + if (n_rz_azimuthal_modes > 1) { + for (std::string field : fields_to_plot) { + if (is_in_vector({"Ex", "Ey", "Ez", "Bx", "By", "Bz", "jx", "jy", "jz", "rho", "F"}, {field})) { + ncomp += 2*n_rz_azimuthal_modes - 1; + } + } + if (plot_finepatch) { + ncomp += 6*(2*n_rz_azimuthal_modes - 1); + } + } + + int nvecs = 3; + if (n_rz_azimuthal_modes > 1) { + nvecs += 3*(2*n_rz_azimuthal_modes - 1); + } + // Loop over levels of refinement for (int lev = 0; lev <= finest_level; ++lev) { @@ -413,19 +536,25 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, // Build mf_tmp_E is at least one component of E is requested if (is_in_vector(fields_to_plot, {"Ex", "Ey", "Ez"} )){ // Allocate temp MultiFab with 3 components - mf_tmp_E = MultiFab(grids[lev], dmap[lev], 3, ngrow); + mf_tmp_E = MultiFab(grids[lev], dmap[lev], nvecs, ngrow); // Fill MultiFab mf_tmp_E with averaged E AverageAndPackVectorField(mf_tmp_E, Efield_aux[lev], dmap[lev], 0, ngrow); + int dcomp = 3; + AverageAndPackVectorFieldComponents(mf_tmp_E, Efield_aux[lev], dmap[lev], dcomp, ngrow); } // Same for B if (is_in_vector(fields_to_plot, {"Bx", "By", "Bz"} )){ - mf_tmp_B = MultiFab(grids[lev], dmap[lev], 3, ngrow); + mf_tmp_B = MultiFab(grids[lev], dmap[lev], nvecs, ngrow); AverageAndPackVectorField(mf_tmp_B, Bfield_aux[lev], dmap[lev], 0, ngrow); + int dcomp = 3; + AverageAndPackVectorFieldComponents(mf_tmp_B, Bfield_aux[lev], dmap[lev], dcomp, ngrow); } // Same for J if (is_in_vector(fields_to_plot, {"jx", "jy", "jz"} )){ - mf_tmp_J = MultiFab(grids[lev], dmap[lev], 3, ngrow); + mf_tmp_J = MultiFab(grids[lev], dmap[lev], nvecs, ngrow); AverageAndPackVectorField(mf_tmp_J, current_fp[lev], dmap[lev], 0, ngrow); + int dcomp = 3; + AverageAndPackVectorFieldComponents(mf_tmp_J, current_fp[lev], dmap[lev], dcomp, ngrow); } int dcomp; @@ -433,37 +562,51 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, // mf_avg[lev] add the corresponding names to `varnames`. // plot_fine_patch and plot_coarse_patch are treated separately // (after this for loop). - for (dcomp=0; dcomp<fields_to_plot.size(); dcomp++){ - std::string fieldname = fields_to_plot[dcomp]; + dcomp = 0; + for (int ifield=0; ifield<fields_to_plot.size(); ifield++){ + std::string fieldname = fields_to_plot[ifield]; if(lev==0) varnames.push_back(fieldname); if (fieldname == "Ex"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_E, 0, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_E, 0, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_E, 0, dcomp, ngrow, "Er", varnames); } else if (fieldname == "Ey"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_E, 1, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_E, 1, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_E, 1, dcomp, ngrow, "Etheta", varnames); } else if (fieldname == "Ez"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_E, 2, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_E, 2, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_E, 2, dcomp, ngrow, "Ez", varnames); } else if (fieldname == "Bx"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_B, 0, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_B, 0, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_B, 0, dcomp, ngrow, "Br", varnames); } else if (fieldname == "By"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_B, 1, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_B, 1, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_B, 1, dcomp, ngrow, "Btheta", varnames); } else if (fieldname == "Bz"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_B, 2, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_B, 2, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_B, 2, dcomp, ngrow, "Bz", varnames); } else if (fieldname == "jx"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_J, 0, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_J, 0, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_J, 0, dcomp, ngrow, "jr", varnames); } else if (fieldname == "jy"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_J, 1, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_J, 1, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_J, 1, dcomp, ngrow, "jtheta", varnames); } else if (fieldname == "jz"){ - MultiFab::Copy( mf_avg[lev], mf_tmp_J, 2, dcomp, 1, ngrow); + MultiFab::Copy( mf_avg[lev], mf_tmp_J, 2, dcomp++, 1, ngrow); + CopyVectorFieldComponentsToMultiFab(lev, mf_avg, mf_tmp_J, 2, dcomp, ngrow, "jz", varnames); } else if (fieldname == "rho"){ - AverageAndPackScalarField( mf_avg[lev], *rho_fp[lev], dcomp, ngrow ); + AverageAndPackScalarField( mf_avg[lev], *rho_fp[lev], dcomp++, ngrow ); + CopyScalarFieldComponentsToMultiFab(lev, mf_avg, *rho_fp[lev], dcomp, ngrow, n_rz_azimuthal_modes, + fieldname, varnames); } else if (fieldname == "F"){ - AverageAndPackScalarField( mf_avg[lev], *F_fp[lev], dcomp, ngrow); + AverageAndPackScalarField( mf_avg[lev], *F_fp[lev], dcomp++, ngrow ); + CopyScalarFieldComponentsToMultiFab(lev, mf_avg, *F_fp[lev], dcomp, ngrow, n_rz_azimuthal_modes, + fieldname, varnames); } else if (fieldname == "part_per_cell") { MultiFab temp_dat(grids[lev],mf_avg[lev].DistributionMap(),1,0); temp_dat.setVal(0); // MultiFab containing number of particles in each cell mypc->Increment(temp_dat, lev); - AverageAndPackScalarField( mf_avg[lev], temp_dat, dcomp, ngrow ); + AverageAndPackScalarField( mf_avg[lev], temp_dat, dcomp++, ngrow ); } else if (fieldname == "part_per_grid"){ const Vector<long>& npart_in_grid = mypc->NumberOfParticlesInGrid(lev); // MultiFab containing number of particles per grid @@ -473,7 +616,7 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, #endif for (MFIter mfi(mf_avg[lev]); mfi.isValid(); ++mfi) { (mf_avg[lev])[mfi].setVal(static_cast<Real>(npart_in_grid[mfi.index()]), - dcomp); + dcomp++); } } else if (fieldname == "part_per_proc"){ const Vector<long>& npart_in_grid = mypc->NumberOfParticlesInGrid(lev); @@ -486,7 +629,7 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, for (MFIter mfi(mf_avg[lev]); mfi.isValid(); ++mfi) { n_per_proc += npart_in_grid[mfi.index()]; } - mf_avg[lev].setVal(static_cast<Real>(n_per_proc), dcomp,1); + mf_avg[lev].setVal(static_cast<Real>(n_per_proc), dcomp++,1); } else if (fieldname == "proc_number"){ // MultiFab containing the Processor ID #ifdef _OPENMP @@ -494,11 +637,11 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, #endif for (MFIter mfi(mf_avg[lev]); mfi.isValid(); ++mfi) { (mf_avg[lev])[mfi].setVal(static_cast<Real>(ParallelDescriptor::MyProc()), - dcomp); + dcomp++); } } else if (fieldname == "divB"){ if (do_nodal) amrex::Abort("TODO: do_nodal && plot divb"); - ComputeDivB(mf_avg[lev], dcomp, + ComputeDivB(mf_avg[lev], dcomp++, {Bfield_aux[lev][0].get(), Bfield_aux[lev][1].get(), Bfield_aux[lev][2].get()}, @@ -512,7 +655,7 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, Efield_aux[lev][1].get(), Efield_aux[lev][2].get()}, WarpX::CellSize(lev) ); - AverageAndPackScalarField( mf_avg[lev], dive, dcomp, ngrow ); + AverageAndPackScalarField( mf_avg[lev], dive, dcomp++, ngrow ); } else { amrex::Abort("unknown field in fields_to_plot: " + fieldname); } @@ -520,11 +663,31 @@ WarpX::AverageAndPackFields ( Vector<std::string>& varnames, if (plot_finepatch) { AverageAndPackVectorField( mf_avg[lev], Efield_fp[lev], dmap[lev], dcomp, ngrow ); - if (lev == 0) AddToVarNames(varnames, "E", "_fp"); dcomp += 3; + AverageAndPackVectorFieldComponents(mf_avg[lev], Efield_fp[lev], dmap[lev], dcomp, ngrow); + if (lev == 0) { + AddToVarNames(varnames, "E", "_fp"); + if (n_rz_azimuthal_modes > 1) { + AddToVarNamesRZ(varnames, "E", ComponentName("_fp", 0, "real")); + for (int mode=1 ; mode < n_rz_azimuthal_modes ; mode++) { + AddToVarNamesRZ(varnames, "E", ComponentName("_fp", mode, "real")); + AddToVarNamesRZ(varnames, "E", ComponentName("_fp", mode, "imag")); + } + } + } AverageAndPackVectorField( mf_avg[lev], Bfield_fp[lev], dmap[lev], dcomp, ngrow ); - if (lev == 0) AddToVarNames(varnames, "B", "_fp"); dcomp += 3; + AverageAndPackVectorFieldComponents(mf_avg[lev], Bfield_fp[lev], dmap[lev], dcomp, ngrow); + if (lev == 0) { + AddToVarNames(varnames, "B", "_fp"); + if (n_rz_azimuthal_modes > 1) { + AddToVarNamesRZ(varnames, "B", ComponentName("_fp", 0, "real")); + for (int mode=1 ; mode < n_rz_azimuthal_modes ; mode++) { + AddToVarNamesRZ(varnames, "B", ComponentName("_fp", mode, "real")); + AddToVarNamesRZ(varnames, "B", ComponentName("_fp", mode, "imag")); + } + } + } } if (plot_crsepatch) diff --git a/Source/Diagnostics/Make.package b/Source/Diagnostics/Make.package index 710e4399b..12560b49e 100644 --- a/Source/Diagnostics/Make.package +++ b/Source/Diagnostics/Make.package @@ -15,5 +15,7 @@ ifeq ($(USE_OPENPMD), TRUE) CEXE_sources += WarpXOpenPMD.cpp endif +include $(WARPX_HOME)/Source/Diagnostics/ReducedDiags/Make.package + INCLUDE_LOCATIONS += $(WARPX_HOME)/Source/Diagnostics VPATH_LOCATIONS += $(WARPX_HOME)/Source/Diagnostics diff --git a/Source/Diagnostics/ParticleIO.cpp b/Source/Diagnostics/ParticleIO.cpp index c08d58d36..ca9e86fdd 100644 --- a/Source/Diagnostics/ParticleIO.cpp +++ b/Source/Diagnostics/ParticleIO.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, David Grote + * Luca Fedeli, Maxence Thevenet, Revathi Jambunathan + * Weiqun Zhang, levinem + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <MultiParticleContainer.H> #include <WarpX.H> diff --git a/Source/Diagnostics/ReducedDiags/FieldEnergy.H b/Source/Diagnostics/ReducedDiags/FieldEnergy.H new file mode 100644 index 000000000..82fa4b6c4 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/FieldEnergy.H @@ -0,0 +1,36 @@ +/* Copyright 2019-2020 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_FIELDENERGY_H_ +#define WARPX_DIAGNOSTICS_REDUCEDDIAGS_FIELDENERGY_H_ + +#include "ReducedDiags.H" +#include <fstream> + +/** + * This class mainly contains a function that + * computes the field energy. + */ +class FieldEnergy : public ReducedDiags +{ +public: + + /** constructor + * @param[in] rd_name reduced diags names */ + FieldEnergy(std::string rd_name); + + /** This funciton computes the field energy (EF). + * EF = E eps / 2 + B / mu / 2, + * where E is the electric field, + * B is the magnetic field, + * eps is the vacuum permittivity, + * mu is the vacuum permeability. */ + virtual void ComputeDiags(int step) override final; + +}; + +#endif diff --git a/Source/Diagnostics/ReducedDiags/FieldEnergy.cpp b/Source/Diagnostics/ReducedDiags/FieldEnergy.cpp new file mode 100644 index 000000000..73e6a1c9a --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/FieldEnergy.cpp @@ -0,0 +1,139 @@ +/* Copyright 2019-2020 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "FieldEnergy.H" +#include "WarpX.H" +#include "WarpXConst.H" +#include "AMReX_REAL.H" +#include "AMReX_ParticleReduce.H" +#include <iostream> +#include <cmath> + +using namespace amrex; + +// constructor +FieldEnergy::FieldEnergy (std::string rd_name) +: ReducedDiags{rd_name} +{ + + // RZ coordinate is not working + #if (defined WARPX_DIM_RZ) + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(false, + "FieldEnergy reduced diagnostics does not work for RZ coordinate."); + #endif + + // get WarpX class object + auto & warpx = WarpX::GetInstance(); + + // read number of levels + int nLevel = 0; + ParmParse pp("amr"); + pp.query("max_level", nLevel); + nLevel += 1; + + // resize data array + m_data.resize(3*nLevel,0.0); + + if (ParallelDescriptor::IOProcessor()) + { + if ( m_IsNotRestart ) + { + // open file + std::ofstream ofs; + ofs.open(m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app); + // write header row + ofs << "#"; + ofs << "[1]step"; + ofs << m_sep; + ofs << "[2]time(s)"; + for (int lev = 0; lev < nLevel; ++lev) + { + ofs << m_sep; + ofs << "[" + std::to_string(3+3*lev) + "]"; + ofs << "total(J)lev"+std::to_string(lev); + ofs << m_sep; + ofs << "[" + std::to_string(4+3*lev) + "]"; + ofs << "E(J)lev"+std::to_string(lev); + ofs << m_sep; + ofs << "[" + std::to_string(5+3*lev) + "]"; + ofs << "B(J)lev"+std::to_string(lev); + } + ofs << std::endl; + // close file + ofs.close(); + } + } + +} +// end constructor + +// function that computes field energy +void FieldEnergy::ComputeDiags (int step) +{ + + // Judge if the diags should be done + if ( (step+1) % m_freq != 0 ) { return; } + + // get WarpX class object + auto & warpx = WarpX::GetInstance(); + + // get number of level + auto nLevel = warpx.finestLevel() + 1; + + // loop over refinement levels + for (int lev = 0; lev < nLevel; ++lev) + { + + // get MultiFab data at lev + const MultiFab & Ex = warpx.getEfield(lev,0); + const MultiFab & Ey = warpx.getEfield(lev,1); + const MultiFab & Ez = warpx.getEfield(lev,2); + const MultiFab & Bx = warpx.getBfield(lev,0); + const MultiFab & By = warpx.getBfield(lev,1); + const MultiFab & Bz = warpx.getBfield(lev,2); + + // get cell size + Geometry const & geom = warpx.Geom(lev); + auto domain_box = geom.Domain(); + #if (AMREX_SPACEDIM == 2) + auto dV = geom.CellSize(0) * geom.CellSize(1); + #elif (AMREX_SPACEDIM == 3) + auto dV = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2); + #endif + + // compute E squared + Real tmpx = Ex.norm2(0,geom.periodicity()); + Real tmpy = Ey.norm2(0,geom.periodicity()); + Real tmpz = Ez.norm2(0,geom.periodicity()); + Real Es = tmpx*tmpx + tmpy*tmpy + tmpz*tmpz; + + // compute B squared + tmpx = Bx.norm2(0,geom.periodicity()); + tmpy = By.norm2(0,geom.periodicity()); + tmpz = Bz.norm2(0,geom.periodicity()); + Real Bs = tmpx*tmpx + tmpy*tmpy + tmpz*tmpz; + + // save data + m_data[lev*3+1] = 0.5 * Es * PhysConst::ep0 * dV; + m_data[lev*3+2] = 0.5 * Bs / PhysConst::mu0 * dV; + m_data[lev*3+0] = m_data[lev*3+1] + m_data[lev*3+2]; + + } + // end loop over refinement levels + + /* m_data now contains up-to-date values for: + * [total field energy at level 0, + * electric field energy at level 0, + * magnetic field energy at level 0, + * total field energy at level 1, + * electric field energy at level 1, + * magnetic field energy at level 1, + * ......] */ + +} +// end void FieldEnergy::ComputeDiags diff --git a/Source/Diagnostics/ReducedDiags/Make.package b/Source/Diagnostics/ReducedDiags/Make.package new file mode 100644 index 000000000..37f76d3d5 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/Make.package @@ -0,0 +1,14 @@ +CEXE_headers += MultiReducedDiags.H +CEXE_sources += MultiReducedDiags.cpp + +CEXE_headers += ReducedDiags.H +CEXE_sources += ReducedDiags.cpp + +CEXE_headers += ParticleEnergy.H +CEXE_sources += ParticleEnergy.cpp + +CEXE_headers += FieldEnergy.H +CEXE_sources += FieldEnergy.cpp + +INCLUDE_LOCATIONS += $(WARPX_HOME)/Source/Diagnostics/ReducedDiags +VPATH_LOCATIONS += $(WARPX_HOME)/Source/Diagnostics/ReducedDiags diff --git a/Source/Diagnostics/ReducedDiags/MultiReducedDiags.H b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.H new file mode 100644 index 000000000..79f487d81 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.H @@ -0,0 +1,47 @@ +/* Copyright 2019-2020 Maxence Thevenet, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_MULTIREDUCEDDIAGS_H_ +#define WARPX_DIAGNOSTICS_REDUCEDDIAGS_MULTIREDUCEDDIAGS_H_ + +#include "ReducedDiags.H" +#include <vector> +#include <string> +#include <memory> + +/** + * This class holds multiple instances of ReducedDiagnostics, and contains + * general functions to initialize, compute, and write these diagnostics + * to file. + */ +class MultiReducedDiags +{ +public: + + /// Bool: whether or not reduced diagnostics are activated + int m_plot_rd = 0; + + /// names of reduced diagnostics + std::vector<std::string> m_rd_names; + + /// m_multi_rd stores a pointer to each reduced diagnostics + std::vector<std::unique_ptr<ReducedDiags>> m_multi_rd; + + /// constructor + MultiReducedDiags(); + + /** Loop over all ReducedDiags and call their ComputeDiags + * @param[in] step current iteration time */ + void ComputeDiags(int step); + + /** Loop over all ReducedDiags and call their WriteToFile + * @param[in] step current iteration time */ + void WriteToFile(int step); + +}; + +#endif diff --git a/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp new file mode 100644 index 000000000..71a33924b --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp @@ -0,0 +1,94 @@ +/* Copyright 2019-2020 Maxence Thevenet, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "ParticleEnergy.H" +#include "FieldEnergy.H" +#include "MultiReducedDiags.H" +#include "AMReX_ParmParse.H" +#include "AMReX_ParallelDescriptor.H" +#include <fstream> + +using namespace amrex; + +// constructor +MultiReducedDiags::MultiReducedDiags () +{ + + // read reduced diags names + ParmParse pp("warpx"); + m_plot_rd = pp.queryarr("reduced_diags_names", m_rd_names); + + // if names are not given, reduced diags will not be done + if ( m_plot_rd == 0 ) { return; } + + // resize + m_multi_rd.resize(m_rd_names.size()); + + // loop over all reduced diags + for (int i_rd = 0; i_rd < m_rd_names.size(); ++i_rd) + { + + ParmParse pp(m_rd_names[i_rd]); + + // read reduced diags type + std::string rd_type; + pp.query("type", rd_type); + + // match diags + if (rd_type.compare("ParticleEnergy") == 0) + { + m_multi_rd[i_rd].reset + ( new ParticleEnergy(m_rd_names[i_rd])); + } + else if (rd_type.compare("FieldEnergy") == 0) + { + m_multi_rd[i_rd].reset + ( new FieldEnergy(m_rd_names[i_rd])); + } + else + { Abort("No matching reduced diagnostics type found."); } + // end if match diags + + } + // end loop over all reduced diags + +} +// end constructor + +// call functions to compute diags +void MultiReducedDiags::ComputeDiags (int step) +{ + // loop over all reduced diags + for (int i_rd = 0; i_rd < m_rd_names.size(); ++i_rd) + { + m_multi_rd[i_rd] -> ComputeDiags(step); + } + // end loop over all reduced diags +} +// end void MultiReducedDiags::ComputeDiags + +// funciton to write data +void MultiReducedDiags::WriteToFile (int step) +{ + + // Only the I/O rank does + if ( !ParallelDescriptor::IOProcessor() ) { return; } + + // loop over all reduced diags + for (int i_rd = 0; i_rd < m_rd_names.size(); ++i_rd) + { + + // Judge if the diags should be done + if ( (step+1) % m_multi_rd[i_rd]->m_freq != 0 ) { return; } + + // call the write to file function + m_multi_rd[i_rd]->WriteToFile(step); + + } + // end loop over all reduced diags +} +// end void MultiReducedDiags::WriteToFile diff --git a/Source/Diagnostics/ReducedDiags/ParticleEnergy.H b/Source/Diagnostics/ReducedDiags/ParticleEnergy.H new file mode 100644 index 000000000..d7c60a24e --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ParticleEnergy.H @@ -0,0 +1,37 @@ +/* Copyright 2019-2020 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_PARTICLEENERGY_H_ +#define WARPX_DIAGNOSTICS_REDUCEDDIAGS_PARTICLEENERGY_H_ + +#include "ReducedDiags.H" +#include <fstream> + +/** + * This class mainly contains a function that + * computes the particle relativistic kinetic energy + * of each species. + */ +class ParticleEnergy : public ReducedDiags +{ +public: + + /** constructor + * @param[in] rd_name reduced diags names */ + ParticleEnergy(std::string rd_name); + + /** This funciton computes the particle relativistic + * kinetic energy (EP). + * \param [in] step current time step + * EP = sqrt( p^2 c^2 + m^2 c^4 ) - m c^2, + * where p is the relativistic momentum, + * m is the particle rest mass. */ + virtual void ComputeDiags(int step) override final; + +}; + +#endif diff --git a/Source/Diagnostics/ReducedDiags/ParticleEnergy.cpp b/Source/Diagnostics/ReducedDiags/ParticleEnergy.cpp new file mode 100644 index 000000000..132ad2165 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ParticleEnergy.cpp @@ -0,0 +1,166 @@ +/* Copyright 2019-2020 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "ParticleEnergy.H" +#include "WarpX.H" +#include "WarpXConst.H" +#include "AMReX_REAL.H" +#include "AMReX_ParticleReduce.H" +#include <iostream> +#include <cmath> +#include <limits> + +using namespace amrex; + +// constructor +ParticleEnergy::ParticleEnergy (std::string rd_name) +: ReducedDiags{rd_name} +{ + // get WarpX class object + auto & warpx = WarpX::GetInstance(); + + // get MultiParticleContainer class object + auto & mypc = warpx.GetPartContainer(); + + // get number of species (int) + auto nSpecies = mypc.nSpecies(); + + // resize data array + m_data.resize(2*nSpecies+2,0.0); + + // get species names (std::vector<std::string>) + auto species_names = mypc.GetSpeciesNames(); + + if (ParallelDescriptor::IOProcessor()) + { + if ( m_IsNotRestart ) + { + // open file + std::ofstream ofs; + ofs.open(m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app); + // write header row + ofs << "#"; + ofs << "[1]step"; + ofs << m_sep; + ofs << "[2]time(s)"; + ofs << m_sep; + ofs << "[3]total(J)"; + for (int i = 0; i < nSpecies; ++i) + { + ofs << m_sep; + ofs << "[" + std::to_string(4+i) + "]"; + ofs << species_names[i]+"(J)"; + } + ofs << m_sep; + ofs << "[" + std::to_string(4+nSpecies) + "]"; + ofs << "total.mean(J)"; + for (int i = 0; i < nSpecies; ++i) + { + ofs << m_sep; + ofs << "[" + std::to_string(5+nSpecies+i) + "]"; + ofs << species_names[i]+".mean(J)"; + } + ofs << std::endl; + // close file + ofs.close(); + } + } + +} +// end constructor + +// function that computes kinetic energy +void ParticleEnergy::ComputeDiags (int step) +{ + + // Judge if the diags should be done + if ( (step+1) % m_freq != 0 ) { return; } + + // get MultiParticleContainer class object + auto & mypc = WarpX::GetInstance().GetPartContainer(); + + // get number of species (int) + auto nSpecies = mypc.nSpecies(); + + // get species names (std::vector<std::string>) + auto species_names = mypc.GetSpeciesNames(); + + // speed of light squared + auto c2 = PhysConst::c * PhysConst::c; + + // loop over species + for (int i_s = 0; i_s < nSpecies; ++i_s) + { + // get WarpXParticleContainer class object + auto & myspc = mypc.GetParticleContainer(i_s); + + // get mass (Real) + auto m = myspc.getMass(); + + using PType = typename WarpXParticleContainer::SuperParticleType; + + // Use amex::ReduceSum to compute the sum of energies of all particles + // held by the current MPI rank, for this species. This involves a loop over all + // boxes held by this MPI rank. + auto Etot = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { + auto w = p.rdata(PIdx::w); + auto ux = p.rdata(PIdx::ux); + auto uy = p.rdata(PIdx::uy); + auto uz = p.rdata(PIdx::uz); + auto us = (ux*ux + uy*uy + uz*uz); + return ( std::sqrt(us*c2 + c2*c2) - c2 ) * m * w; + }); + + // Same thing for the particles weights. + auto Wtot = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { + return p.rdata(PIdx::w); + }); + + // reduced sum over mpi ranks + ParallelDescriptor::ReduceRealSum + (Etot, ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::ReduceRealSum + (Wtot, ParallelDescriptor::IOProcessorNumber()); + + // save results for this species i_s into m_data + m_data[i_s+1] = Etot; + if ( Wtot > std::numeric_limits<Real>::min() ) + { m_data[nSpecies+2+i_s] = Etot / Wtot; } + else + { m_data[nSpecies+2+i_s] = 0.0; } + + } + // end loop over species + + // save total energy + // loop over species + m_data[0] = 0.0; // total energy + m_data[nSpecies+1] = 0.0; // total mean energy + for (int i_s = 0; i_s < nSpecies; ++i_s) + { + m_data[0] += m_data[i_s+1]; + m_data[nSpecies+1] += m_data[nSpecies+2+i_s]; + } + // end loop over species + + /* m_data now contains up-to-date values for: + * [total energy (all species), + * total energy (species 1), + * ..., + * total energy (species n), + * mean energy (all species), + * mean energy (species 1), + * ..., + * mean energy (species n)] */ + +} +// end void ParticleEnergy::ComputeDiags diff --git a/Source/Diagnostics/ReducedDiags/ReducedDiags.H b/Source/Diagnostics/ReducedDiags/ReducedDiags.H new file mode 100644 index 000000000..7ff065f49 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ReducedDiags.H @@ -0,0 +1,59 @@ +/* Copyright 2019-2020 Maxence Thevenet, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_REDUCEDDIAGS_H_ +#define WARPX_DIAGNOSTICS_REDUCEDDIAGS_REDUCEDDIAGS_H_ + +#include "AMReX_REAL.H" +#include <string> +#include <vector> +#include <fstream> + +/** + * Base class for reduced diagnostics. Each type of reduced diagnostics is + * implemented in a derived class, and must override the (pure virtual) + * function ComputeDiags. + */ +class ReducedDiags +{ +public: + + /// output path (default) + std::string m_path = "./diags/reducedfiles/"; + + /// output extension (default) + std::string m_extension = "txt"; + + /// diags name + std::string m_rd_name; + + /// output frequency + int m_freq = 1; + + /// check if it is a restart run + int m_IsNotRestart = 1; + + /// separator in the output file + std::string m_sep = ","; + + /// output data + std::vector<amrex::Real> m_data; + + /** constructor + * @param[in] rd_name reduced diags name */ + ReducedDiags(std::string rd_name); + + /// function to compute diags + virtual void ComputeDiags(int step) = 0; + + /** write to file function + * @param[in] step time step */ + virtual void WriteToFile(int step) const; + +}; + +#endif diff --git a/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp b/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp new file mode 100644 index 000000000..81831aa79 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp @@ -0,0 +1,92 @@ +/* Copyright 2019-2020 Maxence Thevenet, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "ReducedDiags.H" +#include "WarpX.H" +#include "AMReX_ParmParse.H" +#include "AMReX_Utility.H" +#include <iomanip> + +using namespace amrex; + +// constructor +ReducedDiags::ReducedDiags (std::string rd_name) +{ + + m_rd_name = rd_name; + + ParmParse pp(m_rd_name); + + // read path + pp.query("path", m_path); + + // read extension + pp.query("extension", m_extension); + + // creater folder + if (!UtilCreateDirectory(m_path, 0755)) + { CreateDirectoryFailed(m_path); } + + // check if it is a restart run + std::string restart_chkfile = ""; + ParmParse pp_amr("amr"); + pp_amr.query("restart", restart_chkfile); + m_IsNotRestart = restart_chkfile.empty(); + + // replace / create output file + if ( m_IsNotRestart ) // not a restart + { + std::ofstream ofs; + ofs.open(m_path+m_rd_name+"."+m_extension, std::ios::trunc); + ofs.close(); + } + + // read reduced diags frequency + pp.query("frequency", m_freq); + + // read separator + pp.query("separator", m_sep); + +} +// end constructor + +// write to file function +void ReducedDiags::WriteToFile (int step) const +{ + + // open file + std::ofstream ofs; + ofs.open(m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app); + + // write step + ofs << step+1; + + ofs << m_sep; + + // set precision + ofs << std::fixed << std::setprecision(14) << std::scientific; + + // write time + ofs << WarpX::GetInstance().gett_new(0); + + // loop over data size and write + for (int i = 0; i < m_data.size(); ++i) + { + ofs << m_sep; + ofs << m_data[i]; + } + // end loop over data size + + // end line + ofs << std::endl; + + // close file + ofs.close(); + +} +// end ReducedDiags::WriteToFile diff --git a/Source/Diagnostics/SliceDiagnostic.H b/Source/Diagnostics/SliceDiagnostic.H index 1b9ca3967..68a8c1f92 100644 --- a/Source/Diagnostics/SliceDiagnostic.H +++ b/Source/Diagnostics/SliceDiagnostic.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_SliceDiagnostic_H_ #define WARPX_SliceDiagnostic_H_ diff --git a/Source/Diagnostics/SliceDiagnostic.cpp b/Source/Diagnostics/SliceDiagnostic.cpp index 79f03985b..c6b5dd4da 100644 --- a/Source/Diagnostics/SliceDiagnostic.cpp +++ b/Source/Diagnostics/SliceDiagnostic.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 Luca Fedeli, Revathi Jambunathan, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "SliceDiagnostic.H" #include <AMReX_MultiFabUtil.H> #include <AMReX_PlotFileUtil.H> @@ -236,8 +243,10 @@ CheckSliceInput( const RealBox real_box, RealBox &slice_cc_nd_box, { // Modify coarsening ratio if the input value is not an exponent of 2 for AMR // if ( slice_cr_ratio[idim] > 0 ) { - int log_cr_ratio = floor ( log2( double(slice_cr_ratio[idim]))); - slice_cr_ratio[idim] = exp2( double(log_cr_ratio) ); + int log_cr_ratio = + static_cast<int>(floor ( log2( double(slice_cr_ratio[idim])))); + slice_cr_ratio[idim] = + static_cast<int> (exp2( double(log_cr_ratio) )); } //// Default coarsening ratio is 1 // @@ -269,20 +278,24 @@ CheckSliceInput( const RealBox real_box, RealBox &slice_cc_nd_box, // check for interpolation -- compute index lo with floor and ceil if ( slice_cc_nd_box.lo(idim) - real_box.lo(idim) >= fac ) { - slice_lo[idim] = floor( ( (slice_cc_nd_box.lo(idim) + slice_lo[idim] = static_cast<int>( + floor( ( (slice_cc_nd_box.lo(idim) - (real_box.lo(idim) + fac ) ) - / dom_geom[0].CellSize(idim)) + fac * 1E-10); - slice_lo2[idim] = ceil( ( (slice_cc_nd_box.lo(idim) + / dom_geom[0].CellSize(idim)) + fac * 1E-10) ); + slice_lo2[idim] = static_cast<int>( + ceil( ( (slice_cc_nd_box.lo(idim) - (real_box.lo(idim) + fac) ) - / dom_geom[0].CellSize(idim)) - fac * 1E-10 ); + / dom_geom[0].CellSize(idim)) - fac * 1E-10 ) ); } else { - slice_lo[idim] = round( (slice_cc_nd_box.lo(idim) + slice_lo[idim] = static_cast<int>( + round( (slice_cc_nd_box.lo(idim) - (real_box.lo(idim) ) ) - / dom_geom[0].CellSize(idim)); - slice_lo2[idim] = ceil((slice_cc_nd_box.lo(idim) + / dom_geom[0].CellSize(idim)) ); + slice_lo2[idim] = static_cast<int>( + ceil((slice_cc_nd_box.lo(idim) - (real_box.lo(idim) ) ) - / dom_geom[0].CellSize(idim) ); + / dom_geom[0].CellSize(idim) ) ); } // flag for interpolation -- if reduced dimension location // @@ -302,10 +315,10 @@ CheckSliceInput( const RealBox real_box, RealBox &slice_cc_nd_box, else { // moving realbox.lo and reabox.hi to nearest coarsenable grid point // - int index_lo = floor(((slice_realbox.lo(idim) + 1E-10 - - (real_box.lo(idim))) / dom_geom[0].CellSize(idim))); - int index_hi = ceil(((slice_realbox.hi(idim) - 1E-10 - - (real_box.lo(idim))) / dom_geom[0].CellSize(idim))); + auto index_lo = static_cast<int>(floor(((slice_realbox.lo(idim) + 1E-10 + - (real_box.lo(idim))) / dom_geom[0].CellSize(idim))) ); + auto index_hi = static_cast<int>(ceil(((slice_realbox.hi(idim) - 1E-10 + - (real_box.lo(idim))) / dom_geom[0].CellSize(idim))) ); bool modify_cr = true; diff --git a/Source/Diagnostics/WarpXIO.cpp b/Source/Diagnostics/WarpXIO.cpp index 31a5f700d..f85b9df3b 100644 --- a/Source/Diagnostics/WarpXIO.cpp +++ b/Source/Diagnostics/WarpXIO.cpp @@ -1,3 +1,12 @@ +/* Copyright 2019-2020 Andrew Myers, Ann Almgren, Axel Huebl + * Burlen Loring, David Grote, Gunther H. Weber + * Junmin Gu, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <AMReX_MultiFabUtil.H> #include <AMReX_PlotFileUtil.H> #include <AMReX_FillPatchUtil_F.H> @@ -19,7 +28,7 @@ #endif #ifdef WARPX_USE_OPENPMD -#include "WarpXOpenPMD.H" +# include "WarpXOpenPMD.H" #endif @@ -499,41 +508,68 @@ WarpX::UpdateInSitu () const } void -WarpX::WritePlotFile () const -{ - BL_PROFILE("WarpX::WritePlotFile()"); - - const std::string& plotfilename = amrex::Concatenate(plot_file,istep[0]); - amrex::Print() << " Writing plotfile " << plotfilename << "\n"; - +WarpX::prepareFields( + int const step, + Vector<std::string>& varnames, + Vector<MultiFab>& mf_avg, + Vector<const MultiFab*>& output_mf, + Vector<Geometry>& output_geom +) const { // Average the fields from the simulation grid to the cell centers const int ngrow = 0; - Vector<std::string> varnames; // Name of the written fields - // mf_avg will contain the averaged, cell-centered fields - Vector<MultiFab> mf_avg; WarpX::AverageAndPackFields( varnames, mf_avg, ngrow ); // Coarsen the fields, if requested by the user - Vector<const MultiFab*> output_mf; // will point to the data to be written Vector<MultiFab> coarse_mf; // will remain empty if there is no coarsening - Vector<Geometry> output_geom; if (plot_coarsening_ratio != 1) { coarsenCellCenteredFields( coarse_mf, output_geom, mf_avg, Geom(), - plot_coarsening_ratio, finest_level ); + plot_coarsening_ratio, finest_level ); output_mf = amrex::GetVecOfConstPtrs(coarse_mf); } else { // No averaging necessary, simply point to mf_avg output_mf = amrex::GetVecOfConstPtrs(mf_avg); output_geom = Geom(); } +} + +void +WarpX::WriteOpenPMDFile () const +{ + BL_PROFILE("WarpX::WriteOpenPMDFile()"); #ifdef WARPX_USE_OPENPMD - m_OpenPMDPlotWriter->SetStep(istep[0]); - if (dump_openpmd) - m_OpenPMDPlotWriter->WriteOpenPMDFields(varnames, - *output_mf[0], output_geom[0], istep[0], t_new[0] ); + const auto step = istep[0]; + + Vector<std::string> varnames; // Name of the written fields + Vector<MultiFab> mf_avg; // contains the averaged, cell-centered fields + Vector<const MultiFab*> output_mf; // will point to the data to be written + Vector<Geometry> output_geom; + + prepareFields(step, varnames, mf_avg, output_mf, output_geom); + + m_OpenPMDPlotWriter->SetStep(step); + // fields: only dumped for coarse level + m_OpenPMDPlotWriter->WriteOpenPMDFields( + varnames, *output_mf[0], output_geom[0], step, t_new[0]); + // particles: all (reside only on locally finest level) + m_OpenPMDPlotWriter->WriteOpenPMDParticles(mypc); #endif +} + +void +WarpX::WritePlotFile () const +{ + BL_PROFILE("WarpX::WritePlotFile()"); - if (dump_plotfiles || dump_openpmd) { + const auto step = istep[0]; + const std::string& plotfilename = amrex::Concatenate(plot_file,step); + amrex::Print() << " Writing plotfile " << plotfilename << "\n"; + + Vector<std::string> varnames; // Name of the written fields + Vector<MultiFab> mf_avg; // contains the averaged, cell-centered fields + Vector<const MultiFab*> output_mf; // will point to the data to be written + Vector<Geometry> output_geom; + + prepareFields(step, varnames, mf_avg, output_mf, output_geom); // Write the fields contained in `mf_avg`, and corresponding to the // names `varnames`, into a plotfile. @@ -616,23 +652,13 @@ WarpX::WritePlotFile () const } } -#ifdef WARPX_USE_OPENPMD - // Write openPMD format: only for level 0 - if (dump_openpmd) - m_OpenPMDPlotWriter->WriteOpenPMDParticles(mypc); -#endif - // leaving the option of binary output through AMREx around - // regardless of openPMD. This can be adjusted later - { - mypc->WritePlotFile(plotfilename); - } + mypc->WritePlotFile(plotfilename); WriteJobInfo(plotfilename); WriteWarpXHeader(plotfilename); VisMF::SetHeaderVersion(current_version); - } // endif: dump_plotfiles } diff --git a/Source/Diagnostics/WarpXOpenPMD.H b/Source/Diagnostics/WarpXOpenPMD.H index c79a12066..bd18ec8c1 100644 --- a/Source/Diagnostics/WarpXOpenPMD.H +++ b/Source/Diagnostics/WarpXOpenPMD.H @@ -1,10 +1,27 @@ +/* Copyright 2019-2020 Axel Huebl, Junmin Gu, Maxence Thevenet + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_OPEN_PMD_H_ #define WARPX_OPEN_PMD_H_ -#include <MultiParticleContainer.H> // has AMReX Vector etc used below +#include "WarpXParticleContainer.H" +#include "MultiParticleContainer.H" // PIdx + +#include <AMReX_ParallelDescriptor.H> +#include <AMReX_REAL.H> +#include <AMReX_Utility.H> #include <openPMD/openPMD.hpp> +#include <memory> +#include <string> +#include <vector> + + // // helper class // @@ -61,10 +78,14 @@ private: class WarpXOpenPMDPlot { public: - // not using const string, to allow std::move to be effective - WarpXOpenPMDPlot(bool, std::string& filetype); + /** Initialize openPMD I/O routines + * + * @param oneFilePerTS write one file per timestep + * @param filetype file backend, e.g. "bp" or "h5" + * @param fieldPMLdirections PML field solver, @see WarpX::getPMLdirections() + */ + WarpXOpenPMDPlot(bool oneFilePerTS, std::string filetype, std::vector<bool> fieldPMLdirections); - //WarpXOpenPMDPlot(const std::string& dir, const std::string& fileType); ~WarpXOpenPMDPlot(); void SetStep(int ts); @@ -82,12 +103,14 @@ private: void Init(//const std::string& filename, openPMD::AccessType accessType); - /** This function sets up the entries for storing the particle positions in an openPMD species + /** This function sets up the entries for storing the particle positions, global IDs, and constant records (charge, mass) * - * @param[in] currSpecies The openPMD species - * @param[in] np Number of particles + * @param[in] pc WarpX particle container + * @param[in] currSpecies Corresponding openPMD species + * @param[in] np Number of particles */ - void SetupPos(openPMD::ParticleSpecies& currSpecies, + void SetupPos(const std::unique_ptr<WarpXParticleContainer>& pc, + openPMD::ParticleSpecies& currSpecies, const unsigned long long& np) const ; /** This function sets up the entries for particle properties @@ -149,6 +172,9 @@ private: bool m_OneFilePerTS = true; //! write in openPMD fileBased manner for individual time steps std::string m_OpenPMDFileType = "bp"; //! MPI-parallel openPMD backend: bp or h5 int m_CurrentStep = -1; + + // meta data + std::vector< bool > m_fieldPMLdirections; //! @see WarpX::getPMLdirections() }; diff --git a/Source/Diagnostics/WarpXOpenPMD.cpp b/Source/Diagnostics/WarpXOpenPMD.cpp index 05c2066de..85c64c8c9 100644 --- a/Source/Diagnostics/WarpXOpenPMD.cpp +++ b/Source/Diagnostics/WarpXOpenPMD.cpp @@ -1,18 +1,121 @@ +/* Copyright 2019-2020 Axel Huebl, Junmin Gu + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "WarpXOpenPMD.H" +#include "WarpXAlgorithmSelection.H" #include "FieldIO.H" // for getReversedVec +#include <algorithm> +#include <cstdint> +#include <map> +#include <set> +#include <string> +#include <sstream> +#include <tuple> +#include <utility> + + +namespace detail +{ + + /** Convert AMReX AoS .id and .cpu to a globally unique particle ID + */ + union GlobalID { + struct { int id; int cpu; }; //! MPI-rank local ID (and rank/cpu) + uint64_t global_id; //! global ID that is unique in the whole simulation + }; + static_assert(sizeof(int) * 2u <= sizeof(uint64_t), "int size might cause collisions in global IDs"); + + /** Unclutter a real_names to openPMD record + * + * @param fullName name as in real_names variable + * @return pair of openPMD record and component name + */ + inline std::pair< std::string, std::string > + name2openPMD( std::string const& fullName ) + { + std::string record_name = fullName; + std::string component_name = openPMD::RecordComponent::SCALAR; + std::size_t startComp = fullName.find_last_of("_"); + + if( startComp != std::string::npos ) { // non-scalar + record_name = fullName.substr(0, startComp); + component_name = fullName.substr(startComp + 1u); + } + return make_pair(record_name, component_name); + } + + /** Get the openPMD physical dimensionality of a record + * + * @param record_name name of the openPMD record + * @return map with base quantities and power scaling + */ + std::map< openPMD::UnitDimension, double > + getUnitDimension( std::string const & record_name ) + { + + if( record_name == "position" ) return { + {openPMD::UnitDimension::L, 1.} + }; + else if( record_name == "positionOffset" ) return { + {openPMD::UnitDimension::L, 1.} + }; + else if( record_name == "momentum" ) return { + {openPMD::UnitDimension::L, 1.}, + {openPMD::UnitDimension::M, 1.}, + {openPMD::UnitDimension::T, -1.} + }; + else if( record_name == "charge" ) return { + {openPMD::UnitDimension::T, 1.}, + {openPMD::UnitDimension::I, 1.} + }; + else if( record_name == "mass" ) return { + {openPMD::UnitDimension::M, 1.} + }; + else if( record_name == "E" ) return { + {openPMD::UnitDimension::L, 1.}, + {openPMD::UnitDimension::M, 1.}, + {openPMD::UnitDimension::T, -3.}, + {openPMD::UnitDimension::I, -1.}, + }; + else if( record_name == "B" ) return { + {openPMD::UnitDimension::M, 1.}, + {openPMD::UnitDimension::I, -1.}, + {openPMD::UnitDimension::T, -2.} + }; + else return {}; + } +} + WarpXOpenPMDPlot::WarpXOpenPMDPlot(bool oneFilePerTS, - std::string& openPMDFileType) + std::string openPMDFileType, std::vector<bool> fieldPMLdirections) :m_Series(nullptr), m_OneFilePerTS(oneFilePerTS), - m_OpenPMDFileType(std::move(openPMDFileType)) - //m_OpenPMDFileType(openPMDFileType) -{} + m_OpenPMDFileType(std::move(openPMDFileType)), + m_fieldPMLdirections(std::move(fieldPMLdirections)) +{ + // pick first available backend if default is chosen + if( m_OpenPMDFileType == "default" ) +#if openPMD_HAVE_ADIOS2==1 + m_OpenPMDFileType = "bp"; +#elif openPMD_HAVE_ADIOS1==1 + m_OpenPMDFileType = "bp"; +#elif openPMD_HAVE_HDF5==1 + m_OpenPMDFileType = "h5"; +#else + m_OpenPMDFileType = "json"; +#endif +} WarpXOpenPMDPlot::~WarpXOpenPMDPlot() { - if (nullptr != m_Series) { + if( m_Series ) + { m_Series->flush(); + m_Series.reset( nullptr ); } } @@ -48,34 +151,42 @@ void WarpXOpenPMDPlot::SetStep(int ts) void WarpXOpenPMDPlot::Init(openPMD::AccessType accessType) { - if (!m_OneFilePerTS) {// one file - if (nullptr != m_Series) { - return; - } - } + // either for the next ts file, + // or init a single file for all ts + std::string filename; + GetFileName(filename); - // either for the next ts file, - // or init a single file for all ts - std::string filename; - GetFileName(filename); - - if (m_Series != nullptr) { - m_Series->flush(); - m_Series = nullptr; - } - - if (amrex::ParallelDescriptor::NProcs() > 1) { - m_Series = std::make_unique<openPMD::Series>(filename, - accessType, - amrex::ParallelDescriptor::Communicator()); - m_MPISize = amrex::ParallelDescriptor::NProcs(); - m_MPIRank = amrex::ParallelDescriptor::MyProc(); - } + if( amrex::ParallelDescriptor::NProcs() > 1 ) + { + m_Series = std::make_unique<openPMD::Series>( + filename, accessType, + amrex::ParallelDescriptor::Communicator() + ); + m_MPISize = amrex::ParallelDescriptor::NProcs(); + m_MPIRank = amrex::ParallelDescriptor::MyProc(); + } else - m_Series = std::make_unique<openPMD::Series>(filename, accessType); + { + m_Series = std::make_unique<openPMD::Series>(filename, accessType); + m_MPISize = 1; + m_MPIRank = 1; + } - // actually default is "particles" by openPMD. - m_Series->setParticlesPath("particles"); + // input file / simulation setup author + if( WarpX::authors.size() > 0u ) + m_Series->setAuthor( WarpX::authors ); + // more natural naming for PIC + m_Series->setMeshesPath( "fields" ); + // conform to ED-PIC extension of openPMD + uint32_t const openPMD_ED_PIC = 1u; + m_Series->setOpenPMDextension( openPMD_ED_PIC ); + // meta info +#if (OPENPMDAPI_VERSION_MAJOR>=0) && (OPENPMDAPI_VERSION_MINOR>=11) + m_Series->setSoftware( "WarpX", WarpX::Version() ); +#else + m_Series->setSoftware( "WarpX" ); + m_Series->setSoftwareVersion( WarpX::Version() ); +#endif } @@ -89,23 +200,27 @@ WarpXOpenPMDPlot::WriteOpenPMDParticles(const std::unique_ptr<MultiParticleConta auto& pc = mpc->GetUniqueContainer(i); if (pc->plot_species) { + // names of amrex::Real and int particle attributes in SoA data amrex::Vector<std::string> real_names; amrex::Vector<std::string> int_names; amrex::Vector<int> int_flags; - real_names.push_back("weight"); + // see openPMD ED-PIC extension for namings + // note: an underscore separates the record name from its component + // for non-scalar records + real_names.push_back("weighting"); real_names.push_back("momentum_x"); real_names.push_back("momentum_y"); real_names.push_back("momentum_z"); - real_names.push_back("Ex"); - real_names.push_back("Ey"); - real_names.push_back("Ez"); + real_names.push_back("E_x"); + real_names.push_back("E_y"); + real_names.push_back("E_z"); - real_names.push_back("Bx"); - real_names.push_back("By"); - real_names.push_back("Bz"); + real_names.push_back("B_x"); + real_names.push_back("B_y"); + real_names.push_back("B_z"); #ifdef WARPX_DIM_RZ real_names.push_back("theta"); @@ -152,61 +267,104 @@ WarpXOpenPMDPlot::SavePlotFile (const std::unique_ptr<WarpXParticleContainer>& p const amrex::Vector<std::string>& real_comp_names, const amrex::Vector<std::string>& int_comp_names) const { - if ( nullptr == m_Series) - return; + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_Series != nullptr, "openPMD series must be initialized"); WarpXParticleCounter counter(pc); openPMD::Iteration currIteration = m_Series->iterations[iteration]; openPMD::ParticleSpecies currSpecies = currIteration.particles[name]; + // meta data for ED-PIC extension + currSpecies.setAttribute( "particleShape", double( WarpX::noz ) ); + // TODO allow this per direction in the openPMD standard, ED-PIC extension? + currSpecies.setAttribute( "particleShapes", [](){ + return std::vector< double >{ + double(WarpX::nox), +#if AMREX_SPACEDIM==3 + double(WarpX::noy), +#endif + double(WarpX::noz) + }; + }() ); + currSpecies.setAttribute( "particlePush", [](){ + switch( WarpX::particle_pusher_algo ) { + case ParticlePusherAlgo::Boris : return "Boris"; + case ParticlePusherAlgo::Vay : return "Vay"; + case ParticlePusherAlgo::HigueraCary : return "HigueraCary"; + default: return "other"; + } + }() ); + currSpecies.setAttribute( "particleInterpolation", [](){ + switch( WarpX::field_gathering_algo ) { + case GatheringAlgo::EnergyConserving : return "energyConserving"; + case GatheringAlgo::MomentumConserving : return "momentumConserving"; + default: return "other"; + } + }() ); + currSpecies.setAttribute( "particleSmoothing", "none" ); + currSpecies.setAttribute( "currentDeposition", [](){ + switch( WarpX::current_deposition_algo ) { + case CurrentDepositionAlgo::Esirkepov : return "Esirkepov"; + default: return "directMorseNielson"; + } + }() ); + // // define positions & offsets // - SetupPos(currSpecies, counter.GetTotalNumParticles()); + SetupPos(pc, currSpecies, counter.GetTotalNumParticles()); SetupRealProperties(currSpecies, write_real_comp, real_comp_names, counter.GetTotalNumParticles()); - // forces the files created by all processors! this is the key to resolve RZ storage issue!! + // open files from all processors, in case some will not contribute below m_Series->flush(); + for (auto currentLevel = 0; currentLevel <= pc->finestLevel(); currentLevel++) { - //long numParticles = counter.m_ParticleSizeAtRank[currentLevel] - unsigned long long const numParticles = counter.m_ParticleSizeAtRank[currentLevel]; - unsigned long long offset = counter.m_ParticleOffsetAtRank[currentLevel]; - - if (0 == numParticles) - return; - - // pc->NumIntComp() & NumRealComp() are protected, - // from WarpXParIter template class definition, we know that - // soa num real attributes = PIdx::nattribs, and num int in soa is 0 + uint64_t offset = static_cast<uint64_t>( counter.m_ParticleOffsetAtRank[currentLevel] ); for (WarpXParIter pti(*pc, currentLevel); pti.isValid(); ++pti) { - auto numParticleOnTile = pti.numParticles(); + auto const numParticleOnTile = pti.numParticles(); + uint64_t const numParticleOnTile64 = static_cast<uint64_t>( numParticleOnTile ); - // get position from aos + // get position and particle ID from aos + // note: this implementation iterates the AoS 4x... + // if we flush late as we do now, we can also copy out the data in one go const auto& aos = pti.GetArrayOfStructs(); // size = numParticlesOnTile - { // :: Save Postions, 1D-3D:: - // this code is tested with laser 3d, not tested with 2D examples... + { + // Save positions std::vector<std::string> axisNames={"x", "y", "z"}; - for (auto currDim = 0; currDim < AMREX_SPACEDIM; currDim++) { - std::vector<amrex::ParticleReal> curr(numParticleOnTile, 0); + std::shared_ptr< amrex::ParticleReal > curr( + new amrex::ParticleReal[numParticleOnTile], + [](amrex::ParticleReal const *p){ delete[] p; } + ); for (auto i=0; i<numParticleOnTile; i++) { - curr[i] = aos[i].m_rdata.pos[currDim]; + curr.get()[i] = aos[i].m_rdata.pos[currDim]; } - currSpecies["position"][axisNames[currDim]].storeChunk(curr, {offset}, {static_cast<unsigned long long>(numParticleOnTile)}); - m_Series->flush(); + currSpecies["position"][axisNames[currDim]].storeChunk(curr, {offset}, {numParticleOnTile64}); } + + // save particle ID after converting it to a globally unique ID + std::shared_ptr< uint64_t > ids( + new uint64_t[numParticleOnTile], + [](uint64_t const *p){ delete[] p; } + ); + for (auto i=0; i<numParticleOnTile; i++) { + detail::GlobalID const nextID = { aos[i].m_idata.id, aos[i].m_idata.cpu }; + ids.get()[i] = nextID.global_id; + } + auto const scalar = openPMD::RecordComponent::SCALAR; + currSpecies["id"][scalar].storeChunk(ids, {offset}, {numParticleOnTile64}); } - // save properties + // save "extra" particle properties in AoS and SoA SaveRealProperty(pti, currSpecies, offset, write_real_comp, real_comp_names); - offset += numParticleOnTile; + offset += numParticleOnTile64; } } + m_Series->flush(); } void @@ -224,46 +382,78 @@ WarpXOpenPMDPlot::SetupRealProperties(openPMD::ParticleSpecies& currSpecies, auto counter = std::min(write_real_comp.size(), real_comp_names.size()); for (int i = 0; i < counter; ++i) if (write_real_comp[i]) { - auto& particleVar = currSpecies[real_comp_names[i]]; - auto& particleVarComp = particleVar[openPMD::RecordComponent::SCALAR]; + // handle scalar and non-scalar records by name + std::string record_name, component_name; + std::tie(record_name, component_name) = detail::name2openPMD(real_comp_names[i]); + + auto particleVarComp = currSpecies[record_name][component_name]; particleVarComp.resetDataset(particlesLineup); } + + std::set< std::string > addedRecords; // add meta-data per record only once + for (auto idx=0; idx<m_NumSoARealAttributes; idx++) { + auto ii = m_NumAoSRealAttributes + idx; + if (write_real_comp[ii]) { + // handle scalar and non-scalar records by name + std::string record_name, component_name; + std::tie(record_name, component_name) = detail::name2openPMD(real_comp_names[ii]); + auto currRecord = currSpecies[record_name]; + + // meta data for ED-PIC extension + bool newRecord = false; + std::tie(std::ignore, newRecord) = addedRecords.insert(record_name); + if( newRecord ) { + currRecord.setUnitDimension( detail::getUnitDimension(record_name) ); + currRecord.setAttribute( "macroWeighted", 0u ); + if( record_name == "momentum" ) + currRecord.setAttribute( "weightingPower", 1.0 ); + else + currRecord.setAttribute( "weightingPower", 0.0 ); + } + } + } } void WarpXOpenPMDPlot::SaveRealProperty(WarpXParIter& pti, openPMD::ParticleSpecies& currSpecies, - unsigned long long offset, - const amrex::Vector<int>& write_real_comp, - const amrex::Vector<std::string>& real_comp_names) const + unsigned long long const offset, + amrex::Vector<int> const& write_real_comp, + amrex::Vector<std::string> const& real_comp_names) const { int numOutputReal = 0; int const totalRealAttrs = m_NumAoSRealAttributes + m_NumSoARealAttributes; - for (int i = 0; i < totalRealAttrs; ++i) - if (write_real_comp[i]) + for( int i = 0; i < totalRealAttrs; ++i ) + if( write_real_comp[i] ) ++numOutputReal; - auto numParticleOnTile = pti.numParticles(); - const auto& aos = pti.GetArrayOfStructs(); // size = numParticlesOnTile - const auto& soa = pti.GetStructOfArrays(); + auto const numParticleOnTile = pti.numParticles(); + uint64_t const numParticleOnTile64 = static_cast<uint64_t>( numParticleOnTile ); + auto const& aos = pti.GetArrayOfStructs(); // size = numParticlesOnTile + auto const& soa = pti.GetStructOfArrays(); // properties are saved separately { - for (auto idx=0; idx<m_NumAoSRealAttributes; idx++) { - if (write_real_comp[idx]) { - auto& currVar = currSpecies[real_comp_names[idx]][openPMD::RecordComponent::SCALAR]; - typename amrex::ParticleReal *d = - static_cast<typename amrex::ParticleReal*> (malloc(sizeof(typename amrex::ParticleReal) * numParticleOnTile)); - - for (auto kk=0; kk<numParticleOnTile; kk++) - d[kk] = aos[kk].m_rdata.arr[AMREX_SPACEDIM+idx]; - - std::shared_ptr <typename amrex::ParticleReal> data(d, free); - currVar.storeChunk(data, - {offset}, {static_cast<unsigned long long>(numParticleOnTile)}); - m_Series->flush(); + for( auto idx=0; idx<m_NumAoSRealAttributes; idx++ ) { + if( write_real_comp[idx] ) { + // handle scalar and non-scalar records by name + std::string record_name, component_name; + std::tie(record_name, component_name) = detail::name2openPMD(real_comp_names[idx]); + auto currRecord = currSpecies[record_name]; + auto currRecordComp = currRecord[component_name]; + + std::shared_ptr< amrex::ParticleReal > d( + new amrex::ParticleReal[numParticleOnTile], + [](amrex::ParticleReal const *p){ delete[] p; } + ); + + for( auto kk=0; kk<numParticleOnTile; kk++ ) + d.get()[kk] = aos[kk].m_rdata.arr[AMREX_SPACEDIM+idx]; + + currRecordComp.storeChunk(d, + {offset}, {numParticleOnTile64}); } } } @@ -272,37 +462,60 @@ WarpXOpenPMDPlot::SaveRealProperty(WarpXParIter& pti, for (auto idx=0; idx<m_NumSoARealAttributes; idx++) { auto ii = m_NumAoSRealAttributes + idx; if (write_real_comp[ii]) { - auto& currVar = currSpecies[real_comp_names[ii]][openPMD::RecordComponent::SCALAR]; - currVar.storeChunk(openPMD::shareRaw(soa.GetRealData(idx)), - {offset}, {static_cast<unsigned long long>(numParticleOnTile)}); + // handle scalar and non-scalar records by name + std::string record_name, component_name; + std::tie(record_name, component_name) = detail::name2openPMD(real_comp_names[ii]); + auto& currRecord = currSpecies[record_name]; + auto& currRecordComp = currRecord[component_name]; + + currRecordComp.storeChunk(openPMD::shareRaw(soa.GetRealData(idx)), + {offset}, {numParticleOnTile64}); } } - m_Series->flush(); } } void -WarpXOpenPMDPlot::SetupPos(openPMD::ParticleSpecies& currSpecies, - const unsigned long long& np) const +WarpXOpenPMDPlot::SetupPos(const std::unique_ptr<WarpXParticleContainer>& pc, + openPMD::ParticleSpecies& currSpecies, + const unsigned long long& np) const { - auto particleLineup = openPMD::Dataset(openPMD::determineDatatype<amrex::ParticleReal>(), {np}); - - currSpecies["positionOffset"]["x"].resetDataset(particleLineup); - currSpecies["positionOffset"]["x"].makeConstant(0); - currSpecies["positionOffset"]["y"].resetDataset(particleLineup); - currSpecies["positionOffset"]["y"].makeConstant(0); - currSpecies["positionOffset"]["z"].resetDataset(particleLineup); - currSpecies["positionOffset"]["z"].makeConstant(0); - - //auto positions = openPMD::Dataset(openPMD::determineDatatype<amrex::ParticleReal>(), {np}); - currSpecies["position"]["x"].resetDataset(particleLineup); - currSpecies["position"]["y"].resetDataset(particleLineup); - currSpecies["position"]["z"].resetDataset(particleLineup); -} + auto const realType = openPMD::Dataset(openPMD::determineDatatype<amrex::ParticleReal>(), {np}); + auto const idType = openPMD::Dataset(openPMD::determineDatatype< uint64_t >(), {np}); + for( auto const& comp : {"x", "y", "z"} ) { + currSpecies["positionOffset"][comp].resetDataset( realType ); + currSpecies["positionOffset"][comp].makeConstant( 0. ); + currSpecies["position"][comp].resetDataset( realType ); + } + auto const scalar = openPMD::RecordComponent::SCALAR; + currSpecies["id"][scalar].resetDataset( idType ); + currSpecies["charge"][scalar].resetDataset( realType ); + currSpecies["charge"][scalar].makeConstant( pc->getCharge() ); + currSpecies["mass"][scalar].resetDataset( realType ); + currSpecies["mass"][scalar].makeConstant( pc->getMass() ); + + // meta data + currSpecies["position"].setUnitDimension( detail::getUnitDimension("position") ); + currSpecies["positionOffset"].setUnitDimension( detail::getUnitDimension("positionOffset") ); + currSpecies["charge"].setUnitDimension( detail::getUnitDimension("charge") ); + currSpecies["mass"].setUnitDimension( detail::getUnitDimension("mass") ); + + // meta data for ED-PIC extension + currSpecies["position"].setAttribute( "macroWeighted", 0u ); + currSpecies["position"].setAttribute( "weightingPower", 0.0 ); + currSpecies["positionOffset"].setAttribute( "macroWeighted", 0u ); + currSpecies["positionOffset"].setAttribute( "weightingPower", 0.0 ); + currSpecies["id"].setAttribute( "macroWeighted", 0u ); + currSpecies["id"].setAttribute( "weightingPower", 0.0 ); + currSpecies["charge"].setAttribute( "macroWeighted", 0u ); + currSpecies["charge"].setAttribute( "weightingPower", 1.0 ); + currSpecies["mass"].setAttribute( "macroWeighted", 0u ); + currSpecies["mass"].setAttribute( "weightingPower", 1.0 ); +} // @@ -316,48 +529,108 @@ WarpXOpenPMDPlot::WriteOpenPMDFields( //const std::string& filename, const int iteration, const double time ) const { - //This is AmrEx's tiny profiler. Possibly will apply it later + //This is AMReX's tiny profiler. Possibly will apply it later BL_PROFILE("WarpXOpenPMDPlot::WriteOpenPMDFields()"); - if ( nullptr == m_Series) - return; + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_Series != nullptr, "openPMD series must be initialized"); - const int ncomp = mf.nComp(); + int const ncomp = mf.nComp(); // Create a few vectors that store info on the global domain // Swap the indices for each of them, since AMReX data is Fortran order // and since the openPMD API assumes contiguous C order // - Size of the box, in integer number of cells - const amrex::Box& global_box = geom.Domain(); - auto global_size = getReversedVec(global_box.size()); + amrex::Box const & global_box = geom.Domain(); + auto const global_size = getReversedVec(global_box.size()); // - Grid spacing - std::vector<double> grid_spacing = getReversedVec(geom.CellSize()); + std::vector<double> const grid_spacing = getReversedVec(geom.CellSize()); // - Global offset - std::vector<double> global_offset = getReversedVec(geom.ProbLo()); + std::vector<double> const global_offset = getReversedVec(geom.ProbLo()); // - AxisLabels #if AMREX_SPACEDIM==3 - std::vector<std::string> axis_labels{"x", "y", "z"}; + std::vector<std::string> const axis_labels{"x", "y", "z"}; #else - std::vector<std::string> axis_labels{"x", "z"}; + std::vector<std::string> const axis_labels{"x", "z"}; #endif // Prepare the type of dataset that will be written - openPMD::Datatype datatype = openPMD::determineDatatype<amrex::Real>(); - auto dataset = openPMD::Dataset(datatype, global_size); + openPMD::Datatype const datatype = openPMD::determineDatatype<amrex::Real>(); + auto const dataset = openPMD::Dataset(datatype, global_size); + // meta data auto series_iteration = m_Series->iterations[iteration]; series_iteration.setTime( time ); + // meta data for ED-PIC extension + auto const period = geom.periodicity(); // TODO double-check: is this the proper global bound or of some level? + std::vector< std::string > fieldBoundary( 6, "reflecting" ); + std::vector< std::string > particleBoundary( 6, "absorbing" ); +#if AMREX_SPACEDIM!=3 + fieldBoundary.resize(4); + particleBoundary.resize(4); +#endif + + for( auto i = 0u; i < fieldBoundary.size() / 2u; ++i ) + if( m_fieldPMLdirections.at( i ) ) + fieldBoundary.at( i ) = "open"; + + for( auto i = 0u; i < fieldBoundary.size() / 2u; ++i ) + if( period.isPeriodic( i ) ) { + fieldBoundary.at(2u*i ) = "periodic"; + fieldBoundary.at(2u*i + 1u) = "periodic"; + particleBoundary.at(2u*i ) = "periodic"; + particleBoundary.at(2u*i + 1u) = "periodic"; + } + + auto meshes = series_iteration.meshes; + meshes.setAttribute( "fieldSolver", [](){ +#ifdef WARPX_USE_PSATD + return "PSATD"; // TODO double-check if WARPX_USE_PSATD_HYBRID is covered +#else + switch( WarpX::particle_pusher_algo ) { + case MaxwellSolverAlgo::Yee : return "Yee"; + case MaxwellSolverAlgo::CKC : return "CK"; + default: return "other"; + } +#endif + }() ); + meshes.setAttribute( "fieldBoundary", fieldBoundary ); + meshes.setAttribute( "particleBoundary", particleBoundary ); + meshes.setAttribute( "currentSmoothing", [](){ + if( WarpX::use_filter ) return "Binomial"; + else return "none"; + }() ); + if( WarpX::use_filter ) + meshes.setAttribute( "currentSmoothingParameters", [](){ + std::stringstream ss; + ss << "period=1;compensator=false"; + ss << ";numPasses_x=" << WarpX::filter_npass_each_dir[0]; +#if (AMREX_SPACEDIM == 3) + ss << ";numPasses_y=" << WarpX::filter_npass_each_dir[1]; + ss << ";numPasses_z=" << WarpX::filter_npass_each_dir[2]; +#else + ss << ";numPasses_z=" << WarpX::filter_npass_each_dir[1]; +#endif + std::string currentSmoothingParameters = ss.str(); + return std::move(currentSmoothingParameters); + }() ); + meshes.setAttribute("chargeCorrection", [](){ + if( WarpX::do_dive_cleaning ) return "hyperbolic"; // TODO or "spectral" or something? double-check + else return "none"; + }() ); + if( WarpX::do_dive_cleaning ) + meshes.setAttribute("chargeCorrectionParameters", "period=1"); + // Loop through the different components, i.e. different fields stored in mf for (int icomp=0; icomp<ncomp; icomp++){ // Check if this field is a vector or a scalar, and extract the field name - const std::string& varname = varnames[icomp]; + std::string const & varname = varnames[icomp]; std::string field_name = varname; std::string comp_name = openPMD::MeshRecordComponent::SCALAR; - for (const char* vector_field: {"E", "B", "j"}){ - for (const char* comp: {"x", "y", "z"}){ - if (varname[0] == *vector_field && varname[1] == *comp ){ + for( char const* vector_field: {"E", "B", "j"} ) { + for( char const* comp: {"x", "y", "z"} ) { + if( varname[0] == *vector_field && varname[1] == *comp ) { field_name = varname[0] + varname.substr(2); // Strip component comp_name = varname[1]; } @@ -365,35 +638,36 @@ WarpXOpenPMDPlot::WriteOpenPMDFields( //const std::string& filename, } // Setup the mesh record accordingly - auto mesh = series_iteration.meshes[field_name]; - mesh.setDataOrder(openPMD::Mesh::DataOrder::F); // MultiFab: Fortran order + auto mesh = meshes[field_name]; + mesh.setDataOrder( openPMD::Mesh::DataOrder::F ); // MultiFab: Fortran order of indices and axes mesh.setAxisLabels( axis_labels ); mesh.setGridSpacing( grid_spacing ); mesh.setGridGlobalOffset( global_offset ); + mesh.setAttribute( "fieldSmoothing", "none" ); setOpenPMDUnit( mesh, field_name ); // Create a new mesh record component, and store the associated metadata auto mesh_comp = mesh[comp_name]; mesh_comp.resetDataset( dataset ); // Cell-centered data: position is at 0.5 of a cell size. - mesh_comp.setPosition(std::vector<double>{AMREX_D_DECL(0.5, 0.5, 0.5)}); + mesh_comp.setPosition( std::vector<double>{AMREX_D_DECL(0.5, 0.5, 0.5)} ); // Loop through the multifab, and store each box as a chunk, // in the openPMD file. - for (amrex::MFIter mfi(mf); mfi.isValid(); ++mfi) { + for( amrex::MFIter mfi(mf); mfi.isValid(); ++mfi ) { - const amrex::FArrayBox& fab = mf[mfi]; - const amrex::Box& local_box = fab.box(); + amrex::FArrayBox const& fab = mf[mfi]; + amrex::Box const& local_box = fab.box(); // Determine the offset and size of this chunk - amrex::IntVect box_offset = local_box.smallEnd() - global_box.smallEnd(); - auto chunk_offset = getReversedVec(box_offset); - auto chunk_size = getReversedVec(local_box.size()); + amrex::IntVect const box_offset = local_box.smallEnd() - global_box.smallEnd(); + auto const chunk_offset = getReversedVec( box_offset ); + auto const chunk_size = getReversedVec( local_box.size() ); // Write local data - const double* local_data = fab.dataPtr(icomp); - mesh_comp.storeChunk(openPMD::shareRaw(local_data), - chunk_offset, chunk_size); + double const * local_data = fab.dataPtr( icomp ); + mesh_comp.storeChunk( openPMD::shareRaw(local_data), + chunk_offset, chunk_size ); } } // Flush data to disk after looping over all components @@ -444,13 +718,14 @@ WarpXParticleCounter::WarpXParticleCounter(const std::unique_ptr<WarpXParticleCo // get the offset in the overall particle id collection // +// note: this is a MPI-collective operation +// // input: num of particles of from each processor // // output: // offset within <all> the particles in the comm // sum of all particles in the comm // - void WarpXParticleCounter::GetParticleOffsetOfProcessor(const long& numParticles, unsigned long long& offset, diff --git a/Source/Diagnostics/requirements.txt b/Source/Diagnostics/requirements.txt new file mode 100644 index 000000000..5a3933299 --- /dev/null +++ b/Source/Diagnostics/requirements.txt @@ -0,0 +1,8 @@ +# Copyright 2020 Axel Huebl +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + +# keep this entry for GitHub's dependency graph +openPMD-api>=0.10.3 diff --git a/Source/Evolve/WarpXDtType.H b/Source/Evolve/WarpXDtType.H index c2c01db00..89fcb506f 100644 --- a/Source/Evolve/WarpXDtType.H +++ b/Source/Evolve/WarpXDtType.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_DTTYPE_H_ #define WARPX_DTTYPE_H_ diff --git a/Source/Evolve/WarpXEvolveEM.cpp b/Source/Evolve/WarpXEvolveEM.cpp index f5491ffe3..bb1300562 100644 --- a/Source/Evolve/WarpXEvolveEM.cpp +++ b/Source/Evolve/WarpXEvolveEM.cpp @@ -1,3 +1,13 @@ +/* Copyright 2019-2020 Andrew Myers, Ann Almgren, Aurore Blelly + * Axel Huebl, Burlen Loring, David Grote + * Glenn Richardson, Jean-Luc Vay, Luca Fedeli + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * Weiqun Zhang, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <cmath> #include <limits> @@ -5,6 +15,7 @@ #include <WarpXConst.H> #include <WarpX_f.H> #include <WarpXUtil.H> +#include <WarpXAlgorithmSelection.H> #ifdef WARPX_USE_PY #include <WarpX_py.H> #endif @@ -13,7 +24,6 @@ #include <AMReX_AmrMeshInSituBridge.H> #endif - using namespace amrex; void @@ -75,8 +85,9 @@ WarpX::EvolveEM (int numsteps) // Particles have p^{n} and x^{n}. // is_synchronized is true. if (is_synchronized) { - FillBoundaryE(); - FillBoundaryB(); + // Not called at each iteration, so exchange all guard cells + FillBoundaryE(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); + FillBoundaryB(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); UpdateAuxilaryData(); // on first step, push p by -0.5*dt for (int lev = 0; lev <= finest_level; ++lev) { @@ -89,14 +100,23 @@ WarpX::EvolveEM (int numsteps) } else { // Beyond one step, we have E^{n} and B^{n}. // Particles have p^{n-1/2} and x^{n}. - FillBoundaryE(); - FillBoundaryB(); - UpdateAuxilaryData(); + // E and B are up-to-date inside the domain only + FillBoundaryE(guard_cells.ng_FieldGather, guard_cells.ng_Extra); + FillBoundaryB(guard_cells.ng_FieldGather, guard_cells.ng_Extra); + // E and B: enough guard cells to update Aux or call Field Gather in fp and cp + // Need to update Aux on lower levels, to interpolate to higher levels. +#ifndef WARPX_USE_PSATD + FillBoundaryAux(guard_cells.ng_UpdateAux); +#endif + UpdateAuxilaryData(); } if (do_subcycling == 0 || finest_level == 0) { OneStep_nosub(cur_time); + // E : guard cells are up-to-date + // B : guard cells are NOT up-to-date + // F : guard cells are NOT up-to-date } else if (do_subcycling == 1 && finest_level == 1) { OneStep_sub1(cur_time); } else { @@ -106,6 +126,8 @@ WarpX::EvolveEM (int numsteps) if (num_mirrors>0){ applyMirrors(cur_time); + // E : guard cells are NOT up-to-date + // B : guard cells are NOT up-to-date } #ifdef WARPX_USE_PY @@ -133,7 +155,8 @@ WarpX::EvolveEM (int numsteps) cur_time += dt[0]; - bool to_make_plot = (plot_int > 0) && ((step+1) % plot_int == 0); + bool to_make_plot = ( (plot_int > 0) && ((step+1) % plot_int == 0) ); + bool to_write_openPMD = ( (openpmd_int > 0) && ((step+1) % openpmd_int == 0) ); // slice generation // bool to_make_slice_plot = (slice_plot_int > 0) && ( (step+1)% slice_plot_int == 0); @@ -149,7 +172,7 @@ WarpX::EvolveEM (int numsteps) myBFD->writeLabFrameData(cell_centered_data.get(), *mypc, geom[0], cur_time, dt[0]); } - bool move_j = is_synchronized || to_make_plot || do_insitu; + bool move_j = is_synchronized || to_make_plot || to_write_openPMD || do_insitu; // If is_synchronized we need to shift j too so that next step we can evolve E by dt/2. // We might need to move j because we are going to make a plotfile. @@ -182,11 +205,24 @@ WarpX::EvolveEM (int numsteps) t_new[i] = cur_time; } + /// reduced diags + if (reduced_diags->m_plot_rd != 0) + { + reduced_diags->ComputeDiags(step); + reduced_diags->WriteToFile(step); + } + // slice gen // - if (to_make_plot || do_insitu || to_make_slice_plot) + if (to_make_plot || to_write_openPMD || do_insitu || to_make_slice_plot) { - FillBoundaryE(); - FillBoundaryB(); + // This is probably overkill, but it's not called often + FillBoundaryE(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); + // This is probably overkill, but it's not called often + FillBoundaryB(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); + // This is probably overkill, but it's not called often +#ifndef WARPX_USE_PSATD + FillBoundaryAux(guard_cells.ng_UpdateAux); +#endif UpdateAuxilaryData(); for (int lev = 0; lev <= finest_level; ++lev) { @@ -200,6 +236,8 @@ WarpX::EvolveEM (int numsteps) if (to_make_plot) WritePlotFile(); + if (to_write_openPMD) + WriteOpenPMDFile(); if (to_make_slice_plot) { @@ -231,14 +269,21 @@ WarpX::EvolveEM (int numsteps) bool write_plot_file = plot_int > 0 && istep[0] > last_plot_file_step && (max_time_reached || istep[0] >= max_step); + bool write_openPMD = openpmd_int > 0 && (max_time_reached || istep[0] >= max_step); bool do_insitu = (insitu_start >= istep[0]) && (insitu_int > 0) && (istep[0] > last_insitu_step) && (max_time_reached || istep[0] >= max_step); - if (write_plot_file || do_insitu) + if (write_plot_file || write_openPMD || do_insitu) { - FillBoundaryE(); - FillBoundaryB(); + // This is probably overkill, but it's not called often + FillBoundaryE(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); + // This is probably overkill, but it's not called often + FillBoundaryB(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); + // This is probably overkill +#ifndef WARPX_USE_PSATD + FillBoundaryAux(guard_cells.ng_UpdateAux); +#endif UpdateAuxilaryData(); for (int lev = 0; lev <= finest_level; ++lev) { @@ -251,6 +296,8 @@ WarpX::EvolveEM (int numsteps) if (write_plot_file) WritePlotFile(); + if (write_openPMD) + WriteOpenPMDFile(); if (do_insitu) UpdateInSitu(); @@ -300,6 +347,10 @@ WarpX::OneStep_nosub (Real cur_time) SyncRho(); + // At this point, J is up-to-date inside the domain, and E and B are + // up-to-date including enough guard cells for first step of the field + // solve. + // For extended PML: copy J from regular grid to PML, and damp J in PML if (do_pml && pml_has_particles) CopyJPML(); if (do_pml && do_pml_j_damping) DampJPML(); @@ -309,24 +360,28 @@ WarpX::OneStep_nosub (Real cur_time) #ifdef WARPX_USE_PSATD PushPSATD(dt[0]); if (do_pml) DampPML(); - FillBoundaryE(); - FillBoundaryB(); + FillBoundaryE(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); + FillBoundaryB(guard_cells.ng_alloc_EB, guard_cells.ng_Extra); #else EvolveF(0.5*dt[0], DtType::FirstHalf); - FillBoundaryF(); + FillBoundaryF(guard_cells.ng_FieldSolverF); EvolveB(0.5*dt[0]); // We now have B^{n+1/2} - FillBoundaryB(); + FillBoundaryB(guard_cells.ng_FieldSolver, IntVect::TheZeroVector()); EvolveE(dt[0]); // We now have E^{n+1} - FillBoundaryE(); + + FillBoundaryE(guard_cells.ng_FieldSolver, IntVect::TheZeroVector()); EvolveF(0.5*dt[0], DtType::SecondHalf); EvolveB(0.5*dt[0]); // We now have B^{n+1} if (do_pml) { + FillBoundaryF(guard_cells.ng_alloc_F); DampPML(); - FillBoundaryE(); + FillBoundaryE(guard_cells.ng_MovingWindow, IntVect::TheZeroVector()); + FillBoundaryF(guard_cells.ng_MovingWindow); + FillBoundaryB(guard_cells.ng_MovingWindow, IntVect::TheZeroVector()); } - FillBoundaryB(); - + // E and B are up-to-date in the domain, but all guard cells are + // outdated. #endif } @@ -345,11 +400,12 @@ WarpX::OneStep_nosub (Real cur_time) * steps of the fine grid. * */ + + void WarpX::OneStep_sub1 (Real curtime) { // TODO: we could save some charge depositions - // Loop over species. For each ionizable species, create particles in // product species. mypc->doFieldIonization(); @@ -369,21 +425,22 @@ WarpX::OneStep_sub1 (Real curtime) EvolveB(fine_lev, PatchType::fine, 0.5*dt[fine_lev]); EvolveF(fine_lev, PatchType::fine, 0.5*dt[fine_lev], DtType::FirstHalf); - FillBoundaryB(fine_lev, PatchType::fine); - FillBoundaryF(fine_lev, PatchType::fine); + FillBoundaryB(fine_lev, PatchType::fine, guard_cells.ng_FieldSolver); + FillBoundaryF(fine_lev, PatchType::fine, guard_cells.ng_alloc_F); EvolveE(fine_lev, PatchType::fine, dt[fine_lev]); - FillBoundaryE(fine_lev, PatchType::fine); + FillBoundaryE(fine_lev, PatchType::fine, guard_cells.ng_FieldGather); EvolveB(fine_lev, PatchType::fine, 0.5*dt[fine_lev]); EvolveF(fine_lev, PatchType::fine, 0.5*dt[fine_lev], DtType::SecondHalf); if (do_pml) { + FillBoundaryF(fine_lev, PatchType::fine, guard_cells.ng_alloc_F); DampPML(fine_lev, PatchType::fine); - FillBoundaryE(fine_lev, PatchType::fine); + FillBoundaryE(fine_lev, PatchType::fine, guard_cells.ng_FieldGather); } - FillBoundaryB(fine_lev, PatchType::fine); + FillBoundaryB(fine_lev, PatchType::fine, guard_cells.ng_FieldGather); // ii) Push particles on the coarse patch and mother grid. // Push the fields on the coarse patch and mother grid @@ -395,20 +452,21 @@ WarpX::OneStep_sub1 (Real curtime) EvolveB(fine_lev, PatchType::coarse, dt[fine_lev]); EvolveF(fine_lev, PatchType::coarse, dt[fine_lev], DtType::FirstHalf); - FillBoundaryB(fine_lev, PatchType::coarse); - FillBoundaryF(fine_lev, PatchType::coarse); + FillBoundaryB(fine_lev, PatchType::coarse, guard_cells.ng_FieldGather); + FillBoundaryF(fine_lev, PatchType::coarse, guard_cells.ng_FieldSolverF); EvolveE(fine_lev, PatchType::coarse, dt[fine_lev]); - FillBoundaryE(fine_lev, PatchType::coarse); + FillBoundaryE(fine_lev, PatchType::coarse, guard_cells.ng_FieldGather); EvolveB(coarse_lev, PatchType::fine, 0.5*dt[coarse_lev]); EvolveF(coarse_lev, PatchType::fine, 0.5*dt[coarse_lev], DtType::FirstHalf); - FillBoundaryB(coarse_lev, PatchType::fine); - FillBoundaryF(coarse_lev, PatchType::fine); + FillBoundaryB(coarse_lev, PatchType::fine, guard_cells.ng_FieldGather + guard_cells.ng_Extra); + FillBoundaryF(coarse_lev, PatchType::fine, guard_cells.ng_FieldSolverF); EvolveE(coarse_lev, PatchType::fine, 0.5*dt[coarse_lev]); - FillBoundaryE(coarse_lev, PatchType::fine); + FillBoundaryE(coarse_lev, PatchType::fine, guard_cells.ng_FieldGather + guard_cells.ng_Extra); + FillBoundaryAux(guard_cells.ng_UpdateAux); // iii) Get auxiliary fields on the fine grid, at dt[fine_lev] UpdateAuxilaryData(); @@ -423,22 +481,21 @@ WarpX::OneStep_sub1 (Real curtime) EvolveB(fine_lev, PatchType::fine, 0.5*dt[fine_lev]); EvolveF(fine_lev, PatchType::fine, 0.5*dt[fine_lev], DtType::FirstHalf); - FillBoundaryB(fine_lev, PatchType::fine); - FillBoundaryF(fine_lev, PatchType::fine); + FillBoundaryB(fine_lev, PatchType::fine, guard_cells.ng_FieldSolver); + FillBoundaryF(fine_lev, PatchType::fine, guard_cells.ng_FieldSolverF); EvolveE(fine_lev, PatchType::fine, dt[fine_lev]); - FillBoundaryE(fine_lev, PatchType::fine); + FillBoundaryE(fine_lev, PatchType::fine, guard_cells.ng_FieldSolver); EvolveB(fine_lev, PatchType::fine, 0.5*dt[fine_lev]); EvolveF(fine_lev, PatchType::fine, 0.5*dt[fine_lev], DtType::SecondHalf); if (do_pml) { DampPML(fine_lev, PatchType::fine); - FillBoundaryE(fine_lev, PatchType::fine); + FillBoundaryE(fine_lev, PatchType::fine, guard_cells.ng_FieldSolver); } - FillBoundaryB(fine_lev, PatchType::fine); - FillBoundaryF(fine_lev, PatchType::fine); + FillBoundaryB(fine_lev, PatchType::fine, guard_cells.ng_FieldSolver); // v) Push the fields on the coarse patch and mother grid // by only half a coarse step (second half) @@ -447,32 +504,38 @@ WarpX::OneStep_sub1 (Real curtime) AddRhoFromFineLevelandSumBoundary(coarse_lev, ncomps, ncomps); EvolveE(fine_lev, PatchType::coarse, dt[fine_lev]); - FillBoundaryE(fine_lev, PatchType::coarse); + FillBoundaryE(fine_lev, PatchType::coarse, guard_cells.ng_FieldSolver); EvolveB(fine_lev, PatchType::coarse, dt[fine_lev]); EvolveF(fine_lev, PatchType::coarse, dt[fine_lev], DtType::SecondHalf); if (do_pml) { + FillBoundaryF(fine_lev, PatchType::fine, guard_cells.ng_FieldSolverF); DampPML(fine_lev, PatchType::coarse); // do it twice DampPML(fine_lev, PatchType::coarse); - FillBoundaryE(fine_lev, PatchType::coarse); + FillBoundaryE(fine_lev, PatchType::coarse, guard_cells.ng_alloc_EB); } - FillBoundaryB(fine_lev, PatchType::coarse); - FillBoundaryF(fine_lev, PatchType::coarse); + FillBoundaryB(fine_lev, PatchType::coarse, guard_cells.ng_FieldSolver); + + FillBoundaryF(fine_lev, PatchType::coarse, guard_cells.ng_FieldSolverF); EvolveE(coarse_lev, PatchType::fine, 0.5*dt[coarse_lev]); - FillBoundaryE(coarse_lev, PatchType::fine); + FillBoundaryE(coarse_lev, PatchType::fine, guard_cells.ng_FieldSolver); EvolveB(coarse_lev, PatchType::fine, 0.5*dt[coarse_lev]); EvolveF(coarse_lev, PatchType::fine, 0.5*dt[coarse_lev], DtType::SecondHalf); if (do_pml) { + if (do_moving_window){ + // Exchance guard cells of PMLs only (0 cells are exchanged for the + // regular B field MultiFab). This is required as B and F have just been + // evolved. + FillBoundaryB(coarse_lev, PatchType::fine, IntVect::TheZeroVector()); + FillBoundaryF(coarse_lev, PatchType::fine, IntVect::TheZeroVector()); + } DampPML(coarse_lev, PatchType::fine); - FillBoundaryE(coarse_lev, PatchType::fine); } - - FillBoundaryB(coarse_lev, PatchType::fine); } void @@ -615,9 +678,10 @@ WarpX::computeMaxStepBoostAccelerator(const amrex::Geometry& a_geom){ // Divide by dt, and update value of max_step. int computed_max_step; if (do_subcycling){ - computed_max_step = interaction_time_boost/dt[0]; + computed_max_step = static_cast<int>(interaction_time_boost/dt[0]); } else { - computed_max_step = interaction_time_boost/dt[maxLevel()]; + computed_max_step = + static_cast<int>(interaction_time_boost/dt[maxLevel()]); } max_step = computed_max_step; Print()<<"max_step computed in computeMaxStepBoostAccelerator: " diff --git a/Source/Evolve/WarpXEvolveES.cpp b/Source/Evolve/WarpXEvolveES.cpp index 555ab37ad..77e037154 100644 --- a/Source/Evolve/WarpXEvolveES.cpp +++ b/Source/Evolve/WarpXEvolveES.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, David Bizzozero + * David Grote, Maxence Thevenet, Remi Lehe + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <WarpX_f.H> @@ -58,7 +66,7 @@ WarpX::EvolveES (int numsteps) { // Beyond one step, particles have p^{n-1/2} and x^{n}. if (is_synchronized) { // on first step, push X by 0.5*dt - mypc->PushXES(0.5*dt[lev]); + mypc->PushX(0.5*dt[lev]); UpdatePlasmaInjectionPosition(0.5*dt[lev]); mypc->Redistribute(); mypc->DepositCharge(rhoNodal); @@ -95,7 +103,7 @@ WarpX::EvolveES (int numsteps) { if (cur_time + dt[0] >= stop_time - 1.e-3*dt[0] || step == numsteps_max-1) { // on last step, push by only 0.5*dt to synchronize all at n+1/2 - mypc->PushXES(-0.5*dt[lev]); + mypc->PushX(-0.5*dt[lev]); UpdatePlasmaInjectionPosition(-0.5*dt[lev]); is_synchronized = true; } diff --git a/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridFFTData.H b/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridFFTData.H index 4e97ab675..44bb42982 100644 --- a/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridFFTData.H +++ b/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridFFTData.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PICSAR_HYBRID_FFTDATA_H_ #define WARPX_PICSAR_HYBRID_FFTDATA_H_ diff --git a/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridSpectralSolver.cpp b/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridSpectralSolver.cpp index 6b7a34271..5b7d95775 100644 --- a/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridSpectralSolver.cpp +++ b/Source/FieldSolver/PicsarHybridSpectralSolver/PicsarHybridSpectralSolver.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <WarpX_f.H> diff --git a/Source/FieldSolver/PicsarHybridSpectralSolver/picsar_hybrid_spectral.F90 b/Source/FieldSolver/PicsarHybridSpectralSolver/picsar_hybrid_spectral.F90 index 35357a63f..96bfb6111 100644 --- a/Source/FieldSolver/PicsarHybridSpectralSolver/picsar_hybrid_spectral.F90 +++ b/Source/FieldSolver/PicsarHybridSpectralSolver/picsar_hybrid_spectral.F90 @@ -1,3 +1,10 @@ +! Copyright 2019 Andrew Myers, Maxence Thevenet, Remi Lehe +! Weiqun Zhang +! +! This file is part of WarpX. +! +! License: BSD-3-Clause-LBNL + module warpx_fft_module use amrex_error_module, only : amrex_error, amrex_abort diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H index 80ceb2e93..4452002d1 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Axel Huebl, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PML_PSATD_ALGORITHM_H_ #define WARPX_PML_PSATD_ALGORITHM_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp index d76259d4c..bae74daf6 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <PMLPsatdAlgorithm.H> #include <WarpXConst.H> #include <cmath> diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H index b7aed9e40..4abe89d9d 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PSATD_ALGORITHM_H_ #define WARPX_PSATD_ALGORITHM_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp index d45b01bda..4f4963e95 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe, Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <PsatdAlgorithm.H> #include <WarpXConst.H> #include <cmath> diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H index 5d5e376c1..2c4946190 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_SPECTRAL_BASE_ALGORITHM_H_ #define WARPX_SPECTRAL_BASE_ALGORITHM_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralFieldData.H b/Source/FieldSolver/SpectralSolver/SpectralFieldData.H index dc83d279d..e66a9ce50 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralFieldData.H +++ b/Source/FieldSolver/SpectralSolver/SpectralFieldData.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_SPECTRAL_FIELD_DATA_H_ #define WARPX_SPECTRAL_FIELD_DATA_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralFieldData.cpp b/Source/FieldSolver/SpectralSolver/SpectralFieldData.cpp index edd4df34d..81e2b0907 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralFieldData.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralFieldData.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <SpectralFieldData.H> using namespace amrex; diff --git a/Source/FieldSolver/SpectralSolver/SpectralKSpace.H b/Source/FieldSolver/SpectralSolver/SpectralKSpace.H index eb07e8fe6..c8b4f49eb 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralKSpace.H +++ b/Source/FieldSolver/SpectralSolver/SpectralKSpace.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Remi Lehe + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_SPECTRAL_K_SPACE_H_ #define WARPX_SPECTRAL_K_SPACE_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp b/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp index aee131324..d0355fc1e 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 Andrew Myers, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpXConst.H> #include <SpectralKSpace.H> #include <cmath> @@ -144,12 +151,7 @@ SpectralKSpace::getSpectralShiftFactor( const DistributionMapping& dm, } const Complex I{0,1}; for (int i=0; i<k.size(); i++ ){ -#ifdef AMREX_USE_GPU - shift[i] = thrust::exp( I*sign*k[i]*0.5*dx[i_dim] ); -#else - shift[i] = std::exp( I*sign*k[i]*0.5*dx[i_dim] ); -#endif - + shift[i] = exp( I*sign*k[i]*0.5*dx[i_dim]); } } return shift_factor; diff --git a/Source/FieldSolver/SpectralSolver/SpectralSolver.H b/Source/FieldSolver/SpectralSolver/SpectralSolver.H index bd92d003f..a6375e483 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralSolver.H +++ b/Source/FieldSolver/SpectralSolver/SpectralSolver.H @@ -1,3 +1,9 @@ +/* Copyright 2019-2020 Maxence Thevenet, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_SPECTRAL_SOLVER_H_ #define WARPX_SPECTRAL_SOLVER_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp index 4b9def013..ca7bd06a0 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <SpectralKSpace.H> #include <SpectralSolver.H> #include <PsatdAlgorithm.H> diff --git a/Source/FieldSolver/SpectralSolver/WarpX_ComplexForFFT.H b/Source/FieldSolver/SpectralSolver/WarpX_ComplexForFFT.H index 43c7a1950..f75c9b19a 100644 --- a/Source/FieldSolver/SpectralSolver/WarpX_ComplexForFFT.H +++ b/Source/FieldSolver/SpectralSolver/WarpX_ComplexForFFT.H @@ -1,3 +1,9 @@ +/* Copyright 2019 David Grote + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_COMPLEXFORFFT_H_ #define WARPX_COMPLEXFORFFT_H_ diff --git a/Source/FieldSolver/WarpXPushFieldsEM.cpp b/Source/FieldSolver/WarpXPushFieldsEM.cpp index 4848b051e..1e922ecd3 100644 --- a/Source/FieldSolver/WarpXPushFieldsEM.cpp +++ b/Source/FieldSolver/WarpXPushFieldsEM.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Aurore Blelly, Axel Huebl + * David Grote, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <cmath> #include <limits> diff --git a/Source/FieldSolver/WarpX_FDTD.H b/Source/FieldSolver/WarpX_FDTD.H index b93c0f40a..4ad251264 100644 --- a/Source/FieldSolver/WarpX_FDTD.H +++ b/Source/FieldSolver/WarpX_FDTD.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Axel Huebl, David Grote + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_FDTD_H_ #define WARPX_FDTD_H_ diff --git a/Source/FieldSolver/WarpX_K.H b/Source/FieldSolver/WarpX_K.H index f61a71e21..a4f657909 100644 --- a/Source/FieldSolver/WarpX_K.H +++ b/Source/FieldSolver/WarpX_K.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_K_H_ #define WARPX_K_H_ diff --git a/Source/Filter/BilinearFilter.H b/Source/Filter/BilinearFilter.H index 150ca8e08..f5405946b 100644 --- a/Source/Filter/BilinearFilter.H +++ b/Source/Filter/BilinearFilter.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <Filter.H> #ifndef WARPX_BILIN_FILTER_H_ diff --git a/Source/Filter/BilinearFilter.cpp b/Source/Filter/BilinearFilter.cpp index 23abaf8bc..fd9153316 100644 --- a/Source/Filter/BilinearFilter.cpp +++ b/Source/Filter/BilinearFilter.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <BilinearFilter.H> #include <WarpX_f.H> diff --git a/Source/Filter/Filter.H b/Source/Filter/Filter.H index 1ecb1bff4..4e36a1f1f 100644 --- a/Source/Filter/Filter.H +++ b/Source/Filter/Filter.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Maxence Thevenet, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <AMReX_MultiFab.H> #ifndef WARPX_FILTER_H_ diff --git a/Source/Filter/Filter.cpp b/Source/Filter/Filter.cpp index 93729a0ae..dbe13747e 100644 --- a/Source/Filter/Filter.cpp +++ b/Source/Filter/Filter.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <Filter.H> diff --git a/Source/Filter/NCIGodfreyFilter.H b/Source/Filter/NCIGodfreyFilter.H index de41de7ae..c174422c4 100644 --- a/Source/Filter/NCIGodfreyFilter.H +++ b/Source/Filter/NCIGodfreyFilter.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <Filter.H> #ifndef WARPX_GODFREY_FILTER_H_ diff --git a/Source/Filter/NCIGodfreyFilter.cpp b/Source/Filter/NCIGodfreyFilter.cpp index 8723322f6..1a400cb32 100644 --- a/Source/Filter/NCIGodfreyFilter.cpp +++ b/Source/Filter/NCIGodfreyFilter.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019-2020 Luca Fedeli, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <NCIGodfreyFilter.H> #include <NCIGodfreyTables.H> @@ -35,7 +41,7 @@ void NCIGodfreyFilter::ComputeStencils(){ "ERROR: NCI filter requires 5 points in z"); // Interpolate coefficients from the table, and store into prestencil. - int index = tab_length*m_cdtodz; + auto index = static_cast<int>(tab_length*m_cdtodz); index = min(index, tab_length-2); index = max(index, 0); Real weight_right = m_cdtodz - index/tab_length; diff --git a/Source/FortranInterface/WarpX_f.F90 b/Source/FortranInterface/WarpX_f.F90 index 762ed2cd8..71b1ae9fa 100644 --- a/Source/FortranInterface/WarpX_f.F90 +++ b/Source/FortranInterface/WarpX_f.F90 @@ -1,3 +1,11 @@ +! Copyright 2019 Andrew Myers, Axel Huebl, David Grote +! Ligia Diana Amorim, Mathieu Lobet, Maxence Thevenet +! Remi Lehe, Weiqun Zhang +! +! This file is part of WarpX. +! +! License: BSD-3-Clause-LBNL + module warpx_module @@ -8,49 +16,6 @@ module warpx_module contains - subroutine warpx_compute_E (lo, hi, & - phi, phlo, phhi, & - Ex, Exlo, Exhi, & - Ey, Eylo, Eyhi, & - Ez, Ezlo, Ezhi, & - dx) bind(c,name='warpx_compute_E') - integer(c_int), intent(in) :: lo(3), hi(3), phlo(3), phhi(3), Exlo(3), Exhi(3), & - Eylo(3), Eyhi(3), Ezlo(3), Ezhi(3) - real(amrex_real), intent(in) :: dx(3) - real(amrex_real), intent(in ) :: phi(phlo(1):phhi(1),phlo(2):phhi(2),phlo(3):phhi(3)) - real(amrex_real), intent(inout) :: Ex (Exlo(1):Exhi(1),Exlo(2):Exhi(2),Exlo(3):Exhi(3)) - real(amrex_real), intent(inout) :: Ey (Eylo(1):Eyhi(1),Eylo(2):Eyhi(2),Eylo(3):Eyhi(3)) - real(amrex_real), intent(inout) :: Ez (Ezlo(1):Ezhi(1),Ezlo(2):Ezhi(2),Ezlo(3):Ezhi(3)) - - integer :: i, j, k - real(amrex_real) :: dxinv(3) - - dxinv = 1.0 / dx - - do k = lo(3), hi(3) - do j = lo(2), hi(2) - - do i = lo(1), hi(1)-1 - Ex(i,j,k) = dxinv(1) * (phi(i,j,k) - phi(i+1,j,k)) - end do - - if (j < hi(2)) then - do i = lo(1), hi(1) - Ey(i,j,k) = dxinv(2) * (phi(i,j,k) - phi(i,j+1,k)) - end do - end if - - if (k < hi(3)) then - do i = lo(1), hi(1) - Ez(i,j,k) = dxinv(3) * (phi(i,j,k) - phi(i,j,k+1)) - end do - end if - - end do - end do - - end subroutine warpx_compute_E - subroutine warpx_build_buffer_masks (lo, hi, msk, mlo, mhi, gmsk, glo, ghi, ng) & bind(c, name='warpx_build_buffer_masks') integer, dimension(3), intent(in) :: lo, hi, mlo, mhi, glo, ghi diff --git a/Source/FortranInterface/WarpX_f.H b/Source/FortranInterface/WarpX_f.H index 48aaac275..74fd9f379 100644 --- a/Source/FortranInterface/WarpX_f.H +++ b/Source/FortranInterface/WarpX_f.H @@ -1,3 +1,13 @@ +/* Copyright 2019 Andrew Myers, Aurore Blelly, Axel Huebl + * David Grote, Jean-Luc Vay, Ligia Diana Amorim + * Luca Fedeli, Mathieu Lobet, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_F_H_ #define WARPX_F_H_ @@ -24,7 +34,6 @@ #define WRPX_INTERPOLATE_CIC warpx_interpolate_cic_3d #define WRPX_INTERPOLATE_CIC_TWO_LEVELS warpx_interpolate_cic_two_levels_3d #define WRPX_PUSH_LEAPFROG warpx_push_leapfrog_3d -#define WRPX_PUSH_LEAPFROG_POSITIONS warpx_push_leapfrog_positions_3d #elif (AMREX_SPACEDIM == 2) @@ -33,7 +42,6 @@ #define WRPX_INTERPOLATE_CIC warpx_interpolate_cic_2d #define WRPX_INTERPOLATE_CIC_TWO_LEVELS warpx_interpolate_cic_two_levels_2d #define WRPX_PUSH_LEAPFROG warpx_push_leapfrog_2d -#define WRPX_PUSH_LEAPFROG_POSITIONS warpx_push_leapfrog_positions_2d #endif diff --git a/Source/Initialization/CustomDensityProb.H b/Source/Initialization/CustomDensityProb.H index c5c159ee8..804b56ce8 100644 --- a/Source/Initialization/CustomDensityProb.H +++ b/Source/Initialization/CustomDensityProb.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Maxence Thevenet, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef CUSTOM_DENSITY_PROB_H_ #define CUSTOM_DENSITY_PROB_H_ diff --git a/Source/Initialization/CustomMomentumProb.H b/Source/Initialization/CustomMomentumProb.H index f8bc29a05..0b4d531c7 100644 --- a/Source/Initialization/CustomMomentumProb.H +++ b/Source/Initialization/CustomMomentumProb.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Maxence Thevenet, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef CUSTOM_MOMENTUM_PROB_H #define CUSTOM_MOMENTUM_PROB_H diff --git a/Source/Initialization/InitSpaceChargeField.cpp b/Source/Initialization/InitSpaceChargeField.cpp index 0a873b742..36914d2c6 100644 --- a/Source/Initialization/InitSpaceChargeField.cpp +++ b/Source/Initialization/InitSpaceChargeField.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <AMReX_ParallelDescriptor.H> #include <AMReX_MLMG.H> diff --git a/Source/Initialization/InjectorDensity.H b/Source/Initialization/InjectorDensity.H index 7e61ae27d..4558eeb96 100644 --- a/Source/Initialization/InjectorDensity.H +++ b/Source/Initialization/InjectorDensity.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef INJECTOR_DENSITY_H_ #define INJECTOR_DENSITY_H_ diff --git a/Source/Initialization/InjectorDensity.cpp b/Source/Initialization/InjectorDensity.cpp index 9f711a7af..f59202db9 100644 --- a/Source/Initialization/InjectorDensity.cpp +++ b/Source/Initialization/InjectorDensity.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 Axel Huebl, Ligia Diana Amorim, Maxence Thevenet + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <InjectorDensity.H> #include <PlasmaInjector.H> @@ -36,8 +43,8 @@ InjectorDensity::sharedMemoryNeeded () const noexcept case Type::parser: { // For parser injector, the 3D position of each particle - // is stored in shared memory. - return amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double) * 3; + // and time, t, is stored in shared memory. + return amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double) * 4; } default: return 0; diff --git a/Source/Initialization/InjectorMomentum.H b/Source/Initialization/InjectorMomentum.H index 88c954df6..bb5a70784 100644 --- a/Source/Initialization/InjectorMomentum.H +++ b/Source/Initialization/InjectorMomentum.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, Cameron Yang, Maxence Thevenet + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef INJECTOR_MOMENTUM_H_ #define INJECTOR_MOMENTUM_H_ diff --git a/Source/Initialization/InjectorMomentum.cpp b/Source/Initialization/InjectorMomentum.cpp index 8fadf0c4b..edbba8ac5 100644 --- a/Source/Initialization/InjectorMomentum.cpp +++ b/Source/Initialization/InjectorMomentum.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 Axel Huebl, Maxence Thevenet, Revathi Jambunathan + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <InjectorMomentum.H> #include <PlasmaInjector.H> @@ -30,9 +37,9 @@ InjectorMomentum::sharedMemoryNeeded () const noexcept { case Type::parser: { - // For parser injector, the 3D position of each particle + // For parser injector, the 3D position of each particle and time, t, // is stored in shared memory. - return amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double) * 3; + return amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double) * 4; } default: return 0; diff --git a/Source/Initialization/InjectorPosition.H b/Source/Initialization/InjectorPosition.H index 4ab2fa022..a8d2200e9 100644 --- a/Source/Initialization/InjectorPosition.H +++ b/Source/Initialization/InjectorPosition.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, David Grote, Maxence Thevenet + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef INJECTOR_POSITION_H_ #define INJECTOR_POSITION_H_ diff --git a/Source/Initialization/PlasmaInjector.H b/Source/Initialization/PlasmaInjector.H index 56b32c827..70d99b9a3 100644 --- a/Source/Initialization/PlasmaInjector.H +++ b/Source/Initialization/PlasmaInjector.H @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, David Grote + * Maxence Thevenet, Remi Lehe, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef PLASMA_INJECTOR_H_ #define PLASMA_INJECTOR_H_ diff --git a/Source/Initialization/PlasmaInjector.cpp b/Source/Initialization/PlasmaInjector.cpp index f7c7e498f..96e82d749 100644 --- a/Source/Initialization/PlasmaInjector.cpp +++ b/Source/Initialization/PlasmaInjector.cpp @@ -1,8 +1,18 @@ +/* Copyright 2019-2020 Andrew Myers, Axel Huebl, Cameron Yang + * David Grote, Luca Fedeli, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "PlasmaInjector.H" #include <WarpXConst.H> #include <WarpX_f.H> #include <WarpX.H> +#include <WarpXUtil.H> #include <AMReX.H> @@ -168,34 +178,6 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name) } } -namespace { -WarpXParser makeParser (std::string const& parse_function) -{ - WarpXParser parser(parse_function); - parser.registerVariables({"x","y","z"}); - - ParmParse pp("my_constants"); - std::set<std::string> symbols = parser.symbols(); - symbols.erase("x"); - symbols.erase("y"); - symbols.erase("z"); // after removing variables, we are left with constants - for (auto it = symbols.begin(); it != symbols.end(); ) { - Real v; - if (pp.query(it->c_str(), v)) { - parser.setConstant(*it, v); - it = symbols.erase(it); - } else { - ++it; - } - } - for (auto const& s : symbols) { // make sure there no unknown symbols - amrex::Abort("PlasmaInjector::makeParser: Unknown symbol "+s); - } - - return parser; -} -} - // Depending on injection type at runtime, initialize inj_rho // so that inj_rho->getDensity calls // InjectorPosition[Constant or Custom or etc.].getDensity. @@ -217,11 +199,7 @@ void PlasmaInjector::parseDensity (ParmParse& pp) // Construct InjectorDensity with InjectorDensityPredefined. inj_rho.reset(new InjectorDensity((InjectorDensityPredefined*)nullptr,species_name)); } else if (rho_prof_s == "parse_density_function") { - std::vector<std::string> f; - pp.getarr("density_function(x,y,z)", f); - for (auto const& s : f) { - str_density_function += s; - } + Store_parserString(pp, "density_function(x,y,z)", str_density_function); // Construct InjectorDensity with InjectorDensityParser. inj_rho.reset(new InjectorDensity((InjectorDensityParser*)nullptr, makeParser(str_density_function))); @@ -339,21 +317,12 @@ void PlasmaInjector::parseMomentum (ParmParse& pp) inj_mom.reset(new InjectorMomentum ((InjectorMomentumRadialExpansion*)nullptr, u_over_r)); } else if (mom_dist_s == "parse_momentum_function") { - std::vector<std::string> f; - pp.getarr("momentum_function_ux(x,y,z)", f); - for (auto const& s : f) { - str_momentum_function_ux += s; - } - f.clear(); - pp.getarr("momentum_function_uy(x,y,z)", f); - for (auto const& s : f) { - str_momentum_function_uy += s; - } - f.clear(); - pp.getarr("momentum_function_uz(x,y,z)", f); - for (auto const& s : f) { - str_momentum_function_uz += s; - } + Store_parserString(pp, "momentum_function_ux(x,y,z)", + str_momentum_function_ux); + Store_parserString(pp, "momentum_function_uy(x,y,z)", + str_momentum_function_uy); + Store_parserString(pp, "momentum_function_uz(x,y,z)", + str_momentum_function_uz); // Construct InjectorMomentum with InjectorMomentumParser. inj_mom.reset(new InjectorMomentum((InjectorMomentumParser*)nullptr, makeParser(str_momentum_function_ux), diff --git a/Source/Initialization/WarpXInitData.cpp b/Source/Initialization/WarpXInitData.cpp index 29c9a8955..8b2fe1831 100644 --- a/Source/Initialization/WarpXInitData.cpp +++ b/Source/Initialization/WarpXInitData.cpp @@ -1,4 +1,12 @@ - +/* Copyright 2019-2020 Andrew Myers, Ann Almgren, Aurore Blelly + * Axel Huebl, Burlen Loring, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <WarpX_f.H> #include <BilinearFilter.H> @@ -10,6 +18,9 @@ #ifdef BL_USE_SENSEI_INSITU #include <AMReX_AmrMeshInSituBridge.H> #endif +#include <GpuParser.H> +#include <WarpXUtil.H> + using namespace amrex; @@ -72,11 +83,21 @@ WarpX::InitData () if (plot_int > 0) WritePlotFile(); + if (openpmd_int > 0) + WriteOpenPMDFile(); + if (check_int > 0) WriteCheckPointFile(); if ((insitu_int > 0) && (insitu_start == 0)) UpdateInSitu(); + + // Write reduced diagnostics before the first iteration. + if (reduced_diags->m_plot_rd != 0) + { + reduced_diags->ComputeDiags(-1); + reduced_diags->WriteToFile(-1); + } } } @@ -231,24 +252,161 @@ WarpX::PostRestart () mypc->PostRestart(); } + void WarpX::InitLevelData (int lev, Real time) { + + ParmParse pp("warpx"); + + // default values of E_external_grid and B_external_grid + // are used to set the E and B field when "constant" or + // "parser" is not explicitly used in the input. + pp.query("B_ext_grid_init_style", B_ext_grid_s); + std::transform(B_ext_grid_s.begin(), + B_ext_grid_s.end(), + B_ext_grid_s.begin(), + ::tolower); + + pp.query("E_ext_grid_init_style", E_ext_grid_s); + std::transform(E_ext_grid_s.begin(), + E_ext_grid_s.end(), + E_ext_grid_s.begin(), + ::tolower); + + // if the input string is "constant", the values for the + // external grid must be provided in the input. + if (B_ext_grid_s == "constant") + pp.getarr("B_external_grid", B_external_grid); + + // if the input string is "constant", the values for the + // external grid must be provided in the input. + if (E_ext_grid_s == "constant") + pp.getarr("E_external_grid", E_external_grid); + for (int i = 0; i < 3; ++i) { current_fp[lev][i]->setVal(0.0); - Efield_fp[lev][i]->setVal(E_external_grid[i]); - Bfield_fp[lev][i]->setVal(B_external_grid[i]); + if (lev > 0) + current_cp[lev][i]->setVal(0.0); + + if (B_ext_grid_s == "constant" || B_ext_grid_s == "default") { + Bfield_fp[lev][i]->setVal(B_external_grid[i]); + if (lev > 0) { + Bfield_aux[lev][i]->setVal(B_external_grid[i]); + Bfield_cp[lev][i]->setVal(B_external_grid[i]); + } + } + if (E_ext_grid_s == "constant" || E_ext_grid_s == "default") { + Efield_fp[lev][i]->setVal(E_external_grid[i]); + if (lev > 0) { + Efield_aux[lev][i]->setVal(E_external_grid[i]); + Efield_cp[lev][i]->setVal(E_external_grid[i]); + } + } } - if (lev > 0) { - for (int i = 0; i < 3; ++i) { - Efield_aux[lev][i]->setVal(E_external_grid[i]); - Bfield_aux[lev][i]->setVal(B_external_grid[i]); + // if the input string for the B-field is "parse_b_ext_grid_function", + // then the analytical expression or function must be + // provided in the input file. + if (B_ext_grid_s == "parse_b_ext_grid_function") { - current_cp[lev][i]->setVal(0.0); - Efield_cp[lev][i]->setVal(E_external_grid[i]); - Bfield_cp[lev][i]->setVal(B_external_grid[i]); - } +#ifdef WARPX_DIM_RZ + amrex::Abort("E and B parser for external fields does not work with RZ -- TO DO"); +#endif + Store_parserString(pp, "Bx_external_grid_function(x,y,z)", + str_Bx_ext_grid_function); + Store_parserString(pp, "By_external_grid_function(x,y,z)", + str_By_ext_grid_function); + Store_parserString(pp, "Bz_external_grid_function(x,y,z)", + str_Bz_ext_grid_function); + + Bxfield_parser.reset(new ParserWrapper( + makeParser(str_Bx_ext_grid_function))); + Byfield_parser.reset(new ParserWrapper( + makeParser(str_By_ext_grid_function))); + Bzfield_parser.reset(new ParserWrapper( + makeParser(str_Bz_ext_grid_function))); + + // Initialize Bfield_fp with external function + InitializeExternalFieldsOnGridUsingParser(Bfield_fp[lev][0].get(), + Bfield_fp[lev][1].get(), + Bfield_fp[lev][2].get(), + Bxfield_parser.get(), + Byfield_parser.get(), + Bzfield_parser.get(), + Bx_nodal_flag, By_nodal_flag, + Bz_nodal_flag, lev); + if (lev > 0) { + InitializeExternalFieldsOnGridUsingParser(Bfield_aux[lev][0].get(), + Bfield_aux[lev][1].get(), + Bfield_aux[lev][2].get(), + Bxfield_parser.get(), + Byfield_parser.get(), + Bzfield_parser.get(), + Bx_nodal_flag, By_nodal_flag, + Bz_nodal_flag, lev); + + InitializeExternalFieldsOnGridUsingParser(Bfield_cp[lev][0].get(), + Bfield_cp[lev][1].get(), + Bfield_cp[lev][2].get(), + Bxfield_parser.get(), + Byfield_parser.get(), + Bzfield_parser.get(), + Bx_nodal_flag, By_nodal_flag, + Bz_nodal_flag, lev); + } + } + + // if the input string for the E-field is "parse_e_ext_grid_function", + // then the analytical expression or function must be + // provided in the input file. + if (E_ext_grid_s == "parse_e_ext_grid_function") { + +#ifdef WARPX_DIM_RZ + amrex::Abort("E and B parser for external fields does not work with RZ -- TO DO"); +#endif + Store_parserString(pp, "Ex_external_grid_function(x,y,z)", + str_Ex_ext_grid_function); + Store_parserString(pp, "Ey_external_grid_function(x,y,z)", + str_Ey_ext_grid_function); + Store_parserString(pp, "Ez_external_grid_function(x,y,z)", + str_Ez_ext_grid_function); + + Exfield_parser.reset(new ParserWrapper( + makeParser(str_Ex_ext_grid_function))); + Eyfield_parser.reset(new ParserWrapper( + makeParser(str_Ey_ext_grid_function))); + Ezfield_parser.reset(new ParserWrapper( + makeParser(str_Ez_ext_grid_function))); + + // Initialize Efield_fp with external function + InitializeExternalFieldsOnGridUsingParser(Efield_fp[lev][0].get(), + Efield_fp[lev][1].get(), + Efield_fp[lev][2].get(), + Exfield_parser.get(), + Eyfield_parser.get(), + Ezfield_parser.get(), + Ex_nodal_flag, Ey_nodal_flag, + Ez_nodal_flag, lev); + if (lev > 0) { + InitializeExternalFieldsOnGridUsingParser(Efield_aux[lev][0].get(), + Efield_aux[lev][1].get(), + Efield_aux[lev][2].get(), + Exfield_parser.get(), + Eyfield_parser.get(), + Ezfield_parser.get(), + Ex_nodal_flag, Ey_nodal_flag, + Ez_nodal_flag, lev); + + InitializeExternalFieldsOnGridUsingParser(Efield_cp[lev][0].get(), + Efield_cp[lev][1].get(), + Efield_cp[lev][2].get(), + Exfield_parser.get(), + Eyfield_parser.get(), + Ezfield_parser.get(), + Ex_nodal_flag, Ey_nodal_flag, + Ez_nodal_flag, lev); + } } if (F_fp[lev]) { @@ -306,3 +464,100 @@ WarpX::InitLevelDataFFT (int lev, Real time) } #endif + +void +WarpX::InitializeExternalFieldsOnGridUsingParser ( + MultiFab *mfx, MultiFab *mfy, MultiFab *mfz, + ParserWrapper *xfield_parser, ParserWrapper *yfield_parser, + ParserWrapper *zfield_parser, IntVect x_nodal_flag, + IntVect y_nodal_flag, IntVect z_nodal_flag, + const int lev) +{ + + const auto dx_lev = geom[lev].CellSizeArray(); + const RealBox& real_box = geom[lev].ProbDomain(); + for ( MFIter mfi(*mfx, TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + const Box& tbx = mfi.tilebox(x_nodal_flag); + const Box& tby = mfi.tilebox(y_nodal_flag); + const Box& tbz = mfi.tilebox(z_nodal_flag); + + auto const& mfxfab = mfx->array(mfi); + auto const& mfyfab = mfy->array(mfi); + auto const& mfzfab = mfz->array(mfi); + + auto const& mfx_IndexType = (*mfx).ixType(); + auto const& mfy_IndexType = (*mfy).ixType(); + auto const& mfz_IndexType = (*mfz).ixType(); + + // Initialize IntVect based on the index type of multiFab + // 0 if cell-centered, 1 if node-centered. + IntVect mfx_type(AMREX_D_DECL(0,0,0)); + IntVect mfy_type(AMREX_D_DECL(0,0,0)); + IntVect mfz_type(AMREX_D_DECL(0,0,0)); + + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + mfx_type[idim] = mfx_IndexType.nodeCentered(idim); + mfy_type[idim] = mfy_IndexType.nodeCentered(idim); + mfz_type[idim] = mfz_IndexType.nodeCentered(idim); + } + + amrex::ParallelFor (tbx, tby, tbz, + [=] AMREX_GPU_DEVICE (int i, int j, int k) { + // Shift required in the x-, y-, or z- position + // depending on the index type of the multifab + Real fac_x = (1.0 - mfx_type[0]) * dx_lev[0]*0.5; + Real x = i*dx_lev[0] + real_box.lo(0) + fac_x; +#if (AMREX_SPACEDIM==2) + Real y = 0.0; + Real fac_z = (1.0 - mfx_type[1]) * dx_lev[1]*0.5; + Real z = j*dx_lev[1] + real_box.lo(1) + fac_z; +#else + Real fac_y = (1.0 - mfx_type[1]) * dx_lev[1]*0.5; + Real y = j*dx_lev[1] + real_box.lo(1) + fac_y; + Real fac_z = (1.0 - mfx_type[2]) * dx_lev[2]*0.5; + Real z = k*dx_lev[2] + real_box.lo(2) + fac_z; +#endif + // Initialize the x-component of the field. + mfxfab(i,j,k) = xfield_parser->getField(x,y,z); + }, + [=] AMREX_GPU_DEVICE (int i, int j, int k) { + Real fac_x = (1.0 - mfy_type[0]) * dx_lev[0]*0.5; + Real x = i*dx_lev[0] + real_box.lo(0) + fac_x; +#if (AMREX_SPACEDIM==2) + Real y = 0.0; + Real fac_z = (1.0 - mfx_type[1]) * dx_lev[1]*0.5; + Real z = j*dx_lev[1] + real_box.lo(1) + fac_z; +#elif (AMREX_SPACEDIM==3) + Real fac_y = (1.0 - mfx_type[1]) * dx_lev[1]*0.5; + Real y = j*dx_lev[1] + real_box.lo(1) + fac_y; + Real fac_z = (1.0 - mfx_type[2]) * dx_lev[2]*0.5; + Real z = k*dx_lev[2] + real_box.lo(2) + fac_z; +#endif + // Initialize the y-component of the field. + mfyfab(i,j,k) = yfield_parser->getField(x,y,z); + }, + [=] AMREX_GPU_DEVICE (int i, int j, int k) { + Real fac_x = (1.0 - mfz_type[0]) * dx_lev[0]*0.5; + Real x = i*dx_lev[0] + real_box.lo(0) + fac_x; +#if (AMREX_SPACEDIM==2) + Real y = 0.0; + Real fac_z = (1.0 - mfx_type[1]) * dx_lev[1]*0.5; + Real z = j*dx_lev[1] + real_box.lo(1) + fac_z; +#elif (AMREX_SPACEDIM==3) + Real fac_y = (1.0 - mfx_type[1]) * dx_lev[1]*0.5; + Real y = j*dx_lev[1] + real_box.lo(1) + fac_y; + Real fac_z = (1.0 - mfz_type[2]) * dx_lev[2]*0.5; + Real z = k*dx_lev[2] + real_box.lo(2) + fac_z; +#endif + // Initialize the z-component of the field. + mfzfab(i,j,k) = zfield_parser->getField(x,y,z); + }, + /* To allocate shared memory for the GPU threads. */ + /* But, for now only 4 doubles (x,y,z,t) are allocated. */ + amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double) * 4 + ); + + } + +} diff --git a/Source/Laser/LaserParticleContainer.H b/Source/Laser/LaserParticleContainer.H index 1e83ca02f..0d56783ed 100644 --- a/Source/Laser/LaserParticleContainer.H +++ b/Source/Laser/LaserParticleContainer.H @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, David Grote + * Luca Fedeli, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_LaserParticleContainer_H_ #define WARPX_LaserParticleContainer_H_ diff --git a/Source/Laser/LaserParticleContainer.cpp b/Source/Laser/LaserParticleContainer.cpp index a330200cc..de192e65e 100644 --- a/Source/Laser/LaserParticleContainer.cpp +++ b/Source/Laser/LaserParticleContainer.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019-2020 Andrew Myers, Axel Huebl, David Grote + * Luca Fedeli, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <limits> #include <cmath> #include <algorithm> @@ -249,8 +257,8 @@ LaserParticleContainer::InitData (int lev) { auto compute_min_max = [&](Real x, Real y, Real z){ const Vector<Real>& pos_plane = InverseTransform({x, y, z}); - int i = pos_plane[0]/S_X; - int j = pos_plane[1]/S_Y; + auto i = static_cast<int>(pos_plane[0]/S_X); + auto j = static_cast<int>(pos_plane[1]/S_Y); plane_lo[0] = std::min(plane_lo[0], i); plane_lo[1] = std::min(plane_lo[1], j); plane_hi[0] = std::max(plane_hi[0], i); @@ -386,6 +394,9 @@ LaserParticleContainer::Evolve (int lev, t_lab = 1./WarpX::gamma_boost*t + WarpX::beta_boost*Z0_lab/PhysConst::c; } + // Update laser profile + m_up_laser_profile->update(t); + BL_ASSERT(OnSameGrids(lev,jx)); MultiFab* cost = WarpX::getCosts(lev); @@ -463,17 +474,19 @@ LaserParticleContainer::Evolve (int lev, // Current Deposition // // Deposit inside domains - int* ion_lev = nullptr; - DepositCurrent(pti, wp, uxp, uyp, uzp, ion_lev, &jx, &jy, &jz, - 0, np_current, thread_num, - lev, lev, dt); - - bool has_buffer = cjx; - if (has_buffer){ - // Deposit in buffers - DepositCurrent(pti, wp, uxp, uyp, uzp, ion_lev, cjx, cjy, cjz, - np_current, np-np_current, thread_num, - lev, lev-1, dt); + { + int* ion_lev = nullptr; + DepositCurrent(pti, wp, uxp, uyp, uzp, ion_lev, &jx, &jy, &jz, + 0, np_current, thread_num, + lev, lev, dt); + + bool has_buffer = cjx; + if (has_buffer){ + // Deposit in buffers + DepositCurrent(pti, wp, uxp, uyp, uzp, ion_lev, cjx, cjy, cjz, + np_current, np-np_current, thread_num, + lev, lev-1, dt); + } } // diff --git a/Source/Laser/LaserProfiles.H b/Source/Laser/LaserProfiles.H index e0ec0dc28..94cfae090 100644 --- a/Source/Laser/LaserProfiles.H +++ b/Source/Laser/LaserProfiles.H @@ -1,3 +1,9 @@ +/* Copyright 2019-2020 Luca Fedeli, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_LaserProfiles_H_ #define WARPX_LaserProfiles_H_ @@ -5,6 +11,7 @@ #include <WarpXParser.H> #include <AMReX_ParmParse.H> #include <AMReX_Vector.H> +#include <AMReX_Gpu.H> #include <WarpXParser.H> @@ -13,6 +20,7 @@ #include <memory> #include <functional> #include <limits> +#include <utility> namespace WarpXLaserProfiles { @@ -31,8 +39,8 @@ struct CommonLaserParameters /** Abstract interface for laser profile classes * - * Each new laser profile should inherit this interface and implement two - * methods: init and fill_amplitude (described below). + * Each new laser profile should inherit this interface and implement three + * methods: init, update and fill_amplitude (described below). * * The declaration of a LaserProfile class should be placed in this file, * while the implementation of the methods should be in a dedicated file in @@ -60,6 +68,17 @@ public: const amrex::ParmParse& ppc, CommonLaserParameters params) = 0; + /** Update Laser Profile + * + * Some laser profiles might need to perform an "update" operation per + * time step. + * + * @param[in] t Current physical time in the simulation (seconds) + */ + virtual void + update ( + amrex::Real t) = 0; + /** Fill Electric Field Amplitude for each particle of the antenna. * * Xp, Yp and amplitude must be arrays with the same length @@ -76,7 +95,7 @@ public: amrex::Real const * AMREX_RESTRICT const Xp, amrex::Real const * AMREX_RESTRICT const Yp, amrex::Real t, - amrex::Real * AMREX_RESTRICT const amplitude) = 0; + amrex::Real * AMREX_RESTRICT const amplitude) const = 0; virtual ~ILaserProfile(){}; }; @@ -94,13 +113,17 @@ public: const amrex::ParmParse& ppc, CommonLaserParameters params) override final; + //No update needed + void + update (amrex::Real /*t */) override final {} + void fill_amplitude ( const int np, amrex::Real const * AMREX_RESTRICT const Xp, amrex::Real const * AMREX_RESTRICT const Yp, amrex::Real t, - amrex::Real * AMREX_RESTRICT const amplitude) override final; + amrex::Real * AMREX_RESTRICT const amplitude) const override final; private: struct { @@ -132,13 +155,17 @@ public: const amrex::ParmParse& ppc, CommonLaserParameters params) override final; + //No update needed + void + update (amrex::Real /*t */) override final {} + void fill_amplitude ( const int np, amrex::Real const * AMREX_RESTRICT const Xp, amrex::Real const * AMREX_RESTRICT const Yp, amrex::Real t, - amrex::Real * AMREX_RESTRICT const amplitude) override final; + amrex::Real * AMREX_RESTRICT const amplitude) const override final; private: struct { @@ -163,13 +190,17 @@ public: const amrex::ParmParse& ppc, CommonLaserParameters params) override final; + //No update needed + void + update (amrex::Real /*t */) override final {} + void fill_amplitude ( const int np, amrex::Real const * AMREX_RESTRICT const Xp, amrex::Real const * AMREX_RESTRICT const Yp, amrex::Real t, - amrex::Real * AMREX_RESTRICT const amplitude) override final; + amrex::Real * AMREX_RESTRICT const amplitude) const override final; private: struct{ @@ -180,6 +211,160 @@ private: }; /** + * Laser profile read from an 'TXYE' file + * The binary file must contain: + * - 3 unsigned integers (4 bytes): nt (points along t), nx (points along x) and ny (points along y) + * - nt*nx*ny doubles (8 bytes) in row major order : field amplitude + */ +class FromTXYEFileLaserProfile : public ILaserProfile +{ + +public: + void + init ( + const amrex::ParmParse& ppl, + const amrex::ParmParse& ppc, + CommonLaserParameters params) override final; + + /** \brief Reads new field data chunk from file if needed + * + * @param[in] t simulation time (seconds) + */ + void + update (amrex::Real t) override final; + + /** \brief compute field amplitude at particles' position for a laser beam + * loaded from an E(x,y,t) file. + * + * Both Xp and Yp are given in laser plane coordinate. + * For each particle with position Xp and Yp, this routine computes the + * amplitude of the laser electric field, stored in array amplitude. + * + * \param np: number of laser particles + * \param Xp: pointer to first component of positions of laser particles + * \param Yp: pointer to second component of positions of laser particles + * \param t: Current physical time + * \param amplitude: pointer to array of field amplitude. + */ + void + fill_amplitude ( + const int np, + amrex::Real const * AMREX_RESTRICT const Xp, + amrex::Real const * AMREX_RESTRICT const Yp, + amrex::Real t, + amrex::Real * AMREX_RESTRICT const amplitude) const override final; + + /** \brief Function to fill the amplitude in case of a uniform grid. + * This function cannot be private due to restrictions related to + * the use of extended __device__ lambda + * + * \param idx_t_left index of the last time coordinate < t + * \param np: number of laser particles + * \param Xp: pointer to first component of positions of laser particles + * \param Yp: pointer to second component of positions of laser particles + * \param t: Current physical time + * \param amplitude: pointer to array of field amplitude. + */ + void internal_fill_amplitude_uniform( + const int idx_t_left, + const int np, + amrex::Real const * AMREX_RESTRICT const Xp, + amrex::Real const * AMREX_RESTRICT const Yp, + amrex::Real t, + amrex::Real * AMREX_RESTRICT const amplitude) const; + + /** \brief Function to fill the amplitude in case of a non-uniform grid. + * This function cannot be private due to restrictions related to + * the use of extended __device__ lambda + * + * \param idx_t_left index of the last time coordinate < t + * \param np: number of laser particles + * \param Xp: pointer to first component of positions of laser particles + * \param Yp: pointer to second component of positions of laser particles + * \param t: Current physical time + * \param amplitude: pointer to array of field amplitude. + */ + void internal_fill_amplitude_nonuniform( + const int idx_t_left, + const int np, + amrex::Real const * AMREX_RESTRICT const Xp, + amrex::Real const * AMREX_RESTRICT const Yp, + amrex::Real t, + amrex::Real * AMREX_RESTRICT const amplitude) const; + +private: + /** \brief parse a field file in the binary 'txye' format (whose details are given below). + * + * A 'txye' file should be a binary file with the following format: + * -flag to indicate if the grid is uniform or not (1 byte, 0 means non-uniform, !=0 means uniform) + * -np, number of timesteps (uint32_t, must be >=2) + * -nx, number of points along x (uint32_t, must be >=2) + * -ny, number of points along y (uint32_t, must be 1 for 2D simulations and >=2 for 3D simulations) + * -timesteps (double[2] if grid is uniform, double[np] otherwise) + * -x_coords (double[2] if grid is uniform, double[nx] otherwise) + * -y_coords (double[1] if 2D, double[2] if 3D & uniform grid, double[ny] if 3D & non uniform grid) + * -field_data (double[nt * nx * ny], with nt being the slowest coordinate). + * The spatiotemporal grid must be rectangular. + * + * \param txye_file_name: name of the file to parse + */ + void parse_txye_file(std::string txye_file_name); + + /** \brief Finds left and right time indices corresponding to time t + * + * + * \param t: simulation time + */ + std::pair<int,int> find_left_right_time_indices(amrex::Real t) const; + + /** \brief Load field data within the temporal range [t_begin, t_end) + * + * Must be called after having parsed a data file with parse_txye_file. + * + * \param t_begin: left limit of the timestep range to read + * \param t_end: right limit of the timestep range to read (t_end is not read) + */ + void read_data_t_chuck(int t_begin, int t_end); + + /** + * \brief m_params contains all the internal parameters + * used by this laser profile + */ + struct{ + /** Name of the file containing the data */ + std::string txye_file_name; + /** Flag to store if the grid is uniform */ + bool is_grid_uniform = false; + /** Dimensions of E_data. nt, nx must be >=2. + * If DIM=3, ny must be >=2 as well. + * If DIM=2, ny must be 1 */ + int nt, nx, ny; + /** Vector of temporal coordinates. For a non-uniform grid, it contains + * all values of time. For a uniform grid, it contains only the start and stop + * times and intermediate times are obtained with nt */ + amrex::Gpu::ManagedVector<amrex::Real> t_coords; + /** Vector or x coordinates. For a non-uniform grid, it contains all values + * of space dimension x. For a uniform grid, it contains only the min and max + * x coordinates, and intermediate positions are obtained with nx */ + amrex::Gpu::ManagedVector<amrex::Real> x_coords; + /** Vector or y coordinates. For a non-uniform grid, it contains all values + * of space dimension y. For a uniform grid, it contains only the min and max + * y coordinates, and intermediate positions are obtained with ny */ + amrex::Gpu::ManagedVector<amrex::Real> y_coords; + /** Size of the timestep range to load */ + int time_chunk_size; + /** Index of the first timestep in memory */ + int first_time_index; + /** Index of the last timestep in memory */ + int last_time_index; + /** Field data */ + amrex::Gpu::ManagedVector<amrex::Real> E_data; + } m_params; + + CommonLaserParameters m_common_params; +}; + +/** * Maps laser profile names to lambdas returing unique pointers * to the corresponding laser profile objects. */ @@ -195,7 +380,9 @@ laser_profiles_dictionary = {"harris", [] () {return std::make_unique<HarrisLaserProfile>();} }, {"parse_field_function", - [] () {return std::make_unique<FieldFunctionLaserProfile>();} } + [] () {return std::make_unique<FieldFunctionLaserProfile>();} }, + {"from_txye_file", + [] () {return std::make_unique<FromTXYEFileLaserProfile>();} } }; } //WarpXLaserProfiles diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp index 3c9d7379a..66660d6be 100644 --- a/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp +++ b/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <LaserProfiles.H> #include <WarpX_Complex.H> @@ -37,7 +43,7 @@ FieldFunctionLaserProfile::init ( void FieldFunctionLaserProfile::fill_amplitude ( const int np, Real const * AMREX_RESTRICT const Xp, Real const * AMREX_RESTRICT const Yp, - Real t, Real * AMREX_RESTRICT const amplitude) + Real t, Real * AMREX_RESTRICT const amplitude) const { for (int i = 0; i < np; ++i) { amplitude[i] = m_parser.eval(Xp[i], Yp[i], t); diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp new file mode 100644 index 000000000..114464dbf --- /dev/null +++ b/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp @@ -0,0 +1,485 @@ +/* Copyright 2019-2020 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include <LaserProfiles.H> + +#include <WarpX_Complex.H> +#include <WarpXConst.H> +#include <WarpXUtil.H> + +#include <AMReX_Print.H> +#include <AMReX_ParallelDescriptor.H> + +#include <limits> +#include <iostream> +#include <fstream> +#include <cstdint> +#include <algorithm> + +using namespace amrex; +using namespace WarpXLaserProfiles; + +void +FromTXYEFileLaserProfile::init ( + const amrex::ParmParse& ppl, + const amrex::ParmParse& /* ppc */, + CommonLaserParameters params) +{ + 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!)"; + } + + // Parse the TXYE file + ppl.get("txye_file_name", m_params.txye_file_name); + if(m_params.txye_file_name.empty()) + { + Abort("txye_file_name must be provided for txye_file laser profile!"); + } + parse_txye_file(m_params.txye_file_name); + + //Set time_chunk_size + m_params.time_chunk_size = m_params.nt; + int temp = 1; + if(ppl.query("time_chunk_size", temp)){ + m_params.time_chunk_size = min( + temp, m_params.time_chunk_size); + } + if(m_params.time_chunk_size < 2){ + Abort("Error! time_chunk_size must be >= 2!"); + } + + //Allocate memory for E_data Vector + const int data_size = m_params.time_chunk_size* + m_params.nx*m_params.ny; + m_params.E_data = Gpu::ManagedVector<amrex::Real>(data_size); + + //Read first time chunck + read_data_t_chuck(0, m_params.time_chunk_size); + + //Copy common params + m_common_params = params; +} + +void +FromTXYEFileLaserProfile::update (amrex::Real t) +{ + if(t >= m_params.t_coords.back()) + return; + + const auto idx_times = find_left_right_time_indices(t); + const auto idx_t_left = idx_times.first; + const auto idx_t_right = idx_times.second; + + //Load data chunck if needed + if(idx_t_right > m_params.last_time_index){ + read_data_t_chuck(idx_t_left, idx_t_left+m_params.time_chunk_size); + } +} + +void +FromTXYEFileLaserProfile::fill_amplitude ( + const int np, + Real const * AMREX_RESTRICT const Xp, Real const * AMREX_RESTRICT const Yp, + Real t, Real * AMREX_RESTRICT const amplitude) const +{ + //Amplitude is 0 if time is out of range + if(t < m_params.t_coords.front() || t > m_params.t_coords.back()){ + amrex::ParallelFor(np, + [=] AMREX_GPU_DEVICE (int i) { + amplitude[i] = 0.0_rt;}); + return; + } + + //Find left and right time indices + int idx_t_left, idx_t_right; + std::tie(idx_t_left, idx_t_right) = find_left_right_time_indices(t); + + if(idx_t_left < m_params.first_time_index){ + Abort("Something bad has happened with the simulation time"); + } + + if(m_params.is_grid_uniform){ + internal_fill_amplitude_uniform( + idx_t_left, np, Xp, Yp, t, amplitude); + } + else{ + internal_fill_amplitude_nonuniform( + idx_t_left, np, Xp, Yp, t, amplitude); + } +} + +void +FromTXYEFileLaserProfile::parse_txye_file(std::string txye_file_name) +{ + if(ParallelDescriptor::IOProcessor()){ + std::ifstream inp(txye_file_name, std::ios::binary); + if(!inp) Abort("Failed to open txye file"); + + //Uniform grid flag + char flag; + inp.read(&flag, 1); + if(!inp) Abort("Failed to read sizes from txye file"); + m_params.is_grid_uniform=flag; + + //Grid points along t, x and y + auto const three_uint32_size = sizeof(uint32_t)*3; + char buf[three_uint32_size]; + inp.read(buf, three_uint32_size); + if(!inp) Abort("Failed to read sizes from txye file"); + m_params.nt = reinterpret_cast<uint32_t*>(buf)[0]; + m_params.nx = reinterpret_cast<uint32_t*>(buf)[1]; + m_params.ny = reinterpret_cast<uint32_t*>(buf)[2]; + if(m_params.nt <= 1) Abort("nt in txye file must be >=2"); + if(m_params.nx <= 1) Abort("nx in txye file must be >=2"); +#if (AMREX_SPACEDIM == 3) + if(m_params.ny <= 1) Abort("ny in txye file must be >=2 in 3D"); +#elif(AMREX_SPACEDIM == 2) + if(m_params.ny != 1) Abort("ny in txye file must be 1 in 2D"); +#endif + + //Coordinates + Vector<double> buf_t, buf_x, buf_y; + if(m_params.is_grid_uniform){ + buf_t.resize(2); + buf_x.resize(2); +#if (AMREX_SPACEDIM == 3) + buf_y.resize(2); +#elif(AMREX_SPACEDIM == 2) + buf_y.resize(1); +#endif + } + else{ + buf_t.resize(m_params.nt); + buf_x.resize(m_params.nx); + buf_y.resize(m_params.ny); + } + inp.read(reinterpret_cast<char*>(buf_t.dataPtr()), + buf_t.size()*sizeof(double)); + if(!inp) + Abort("Failed to read coords from txye file"); + if (!std::is_sorted(buf_t.begin(), buf_t.end())) + Abort("Coordinates are not sorted in txye file"); + inp.read(reinterpret_cast<char*>(buf_x.dataPtr()), + buf_x.size()*sizeof(double)); + if(!inp) + Abort("Failed to read coords from txye file"); + if (!std::is_sorted(buf_x.begin(), buf_x.end())) + Abort("Coordinates are not sorted in txye file"); + inp.read(reinterpret_cast<char*>(buf_y.dataPtr()), + buf_y.size()*sizeof(double)); + if(!inp) + Abort("Failed to read coords from txye file"); + if (!std::is_sorted(buf_y.begin(), buf_y.end())) + Abort("Coordinates are not sorted in txye file"); + m_params.t_coords = Gpu::ManagedVector<amrex::Real>(buf_t.size()); + m_params.x_coords = Gpu::ManagedVector<amrex::Real>(buf_x.size()); + m_params.y_coords = Gpu::ManagedVector<amrex::Real>(buf_y.size()); + // Convert from double to amrex::Real + std::transform(buf_t.begin(), buf_t.end(), m_params.t_coords.begin(), + [](auto x) {return static_cast<amrex::Real>(x);} ); + std::transform(buf_x.begin(), buf_x.end(), m_params.x_coords.begin(), + [](auto x) {return static_cast<amrex::Real>(x);} ); + std::transform(buf_y.begin(), buf_y.end(), m_params.y_coords.begin(), + [](auto x) {return static_cast<amrex::Real>(x);} ); + } + + //Broadcast grid uniformity + char is_grid_uniform = m_params.is_grid_uniform; + ParallelDescriptor::Bcast(&is_grid_uniform, 1, + ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::Barrier(); + m_params.is_grid_uniform = is_grid_uniform; + + //Broadcast grid size and coordinate sizes + //When a non-uniform grid is used, nt, nx and ny are identical + //to t_coords.size(), x_coords.size() and y_coords.size(). + //When a uniform grid is used, nt,nx and ny store the number of points + //used for the mesh, while t_coords, x_coords and y_coords store the + //extrems in each direaction. Thus t_coords and x_coords in this case + //have size 2 and y_coords has size 1 in 2D and size 2 in 3D. + int t_sizes[6] = {m_params.nt, m_params.nx, m_params.ny, + static_cast<int>(m_params.t_coords.size()), + static_cast<int>(m_params.x_coords.size()), + static_cast<int>(m_params.y_coords.size())}; + ParallelDescriptor::Bcast(t_sizes, 6, + ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::Barrier(); + m_params.nt = t_sizes[0]; m_params.nx = t_sizes[1]; m_params.ny = t_sizes[2]; + + //Broadcast coordinates + if(!ParallelDescriptor::IOProcessor()){ + m_params.t_coords = Gpu::ManagedVector<amrex::Real>(t_sizes[3]); + m_params.x_coords = Gpu::ManagedVector<amrex::Real>(t_sizes[4]); + m_params.y_coords = Gpu::ManagedVector<amrex::Real>(t_sizes[5]); + } + ParallelDescriptor::Bcast(m_params.t_coords.dataPtr(), + m_params.t_coords.size(), ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::Bcast(m_params.x_coords.dataPtr(), + m_params.x_coords.size(), ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::Bcast(m_params.y_coords.dataPtr(), + m_params.y_coords.size(), ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::Barrier(); +} + +std::pair<int,int> +FromTXYEFileLaserProfile::find_left_right_time_indices(amrex::Real t) const +{ + int idx_t_right; + if(m_params.is_grid_uniform){ + const auto t_min = m_params.t_coords.front(); + const auto t_max = m_params.t_coords.back(); + const auto temp_idx_t_right = static_cast<int>( + ceil( (m_params.nt-1)*(t-t_min)/(t_max-t_min))); + idx_t_right = max(min(temp_idx_t_right, m_params.nt-1),1); + } + else{ + idx_t_right = std::distance(m_params.t_coords.begin(), + std::upper_bound(m_params.t_coords.begin(), + m_params.t_coords.end(), t)); + } + return std::make_pair(idx_t_right-1, idx_t_right); +} + +void +FromTXYEFileLaserProfile::read_data_t_chuck(int t_begin, int t_end) +{ + amrex::Print() << + "Reading [" << t_begin << ", " << t_end << + ") data chunk from " << m_params.txye_file_name << "\n"; + + //Indices of the first and last timestep to read + auto i_first = max(0, t_begin); + auto i_last = min(t_end-1, m_params.nt-1); + if(i_last-i_first+1 > m_params.E_data.size()) + Abort("Data chunk to read from file is too large"); + + if(ParallelDescriptor::IOProcessor()){ + //Read data chunk + std::ifstream inp(m_params.txye_file_name, std::ios::binary); + if(!inp) Abort("Failed to open txye file"); + auto skip_amount = 1 + + 3*sizeof(uint32_t) + + m_params.t_coords.size()*sizeof(double) + + m_params.x_coords.size()*sizeof(double) + + m_params.y_coords.size()*sizeof(double) + + sizeof(double)*t_begin*m_params.nx*m_params.ny; + inp.ignore(skip_amount); + if(!inp) Abort("Failed to read field data from txye file"); + const int read_size = (i_last - i_first + 1)* + m_params.nx*m_params.ny; + Vector<double> buf_e(read_size); + inp.read(reinterpret_cast<char*>(buf_e.dataPtr()), read_size*sizeof(double)); + if(!inp) Abort("Failed to read field data from txye file"); + std::transform(buf_e.begin(), buf_e.end(), m_params.E_data.begin(), + [](auto x) {return static_cast<amrex::Real>(x);} ); + } + + //Broadcast E_data + ParallelDescriptor::Bcast(m_params.E_data.dataPtr(), + m_params.E_data.size(), ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::Barrier(); + + //Update first and last indices + m_params.first_time_index = i_first; + m_params.last_time_index = i_last; +} + +void +FromTXYEFileLaserProfile::internal_fill_amplitude_uniform( + const int idx_t_left, + const int np, + Real const * AMREX_RESTRICT const Xp, Real const * AMREX_RESTRICT const Yp, + Real t, Real * AMREX_RESTRICT const amplitude) const +{ + // Copy member variables to tmp copies + // and get pointers to underlying data for GPU. + const auto tmp_e_max = m_common_params.e_max; + const auto tmp_x_min = m_params.x_coords.front(); + const auto tmp_x_max = m_params.x_coords.back(); + const auto tmp_y_min = m_params.y_coords.front(); + const auto tmp_y_max = m_params.y_coords.back(); + const auto tmp_nx = m_params.nx; +#if (AMREX_SPACEDIM == 3) + const auto tmp_ny = m_params.ny; +#endif + const auto p_E_data = m_params.E_data.dataPtr(); + const auto tmp_idx_first_time = m_params.first_time_index; + const int idx_t_right = idx_t_left+1; + const auto t_left = idx_t_left* + (m_params.t_coords.back()-m_params.t_coords.front())/(m_params.nt-1) + + m_params.t_coords.front(); + const auto t_right = idx_t_right* + (m_params.t_coords.back()-m_params.t_coords.front())/(m_params.nt-1) + + m_params.t_coords.front(); + + // Loop through the macroparticle to calculate the proper amplitude + amrex::ParallelFor( + np, + [=] AMREX_GPU_DEVICE (int i) { + //Amplitude is zero if we are out of bounds + if (Xp[i] <= tmp_x_min || Xp[i] >= tmp_x_max){ + amplitude[i] = 0.0_rt; + return; + } +#if (AMREX_SPACEDIM == 3) + if (Yp[i] <= tmp_y_min || Yp[i] >= tmp_y_max){ + amplitude[i] = 0.0_rt; + return; + } +#endif + //Find indices and coordinates along x + const int temp_idx_x_right = static_cast<int>( + ceil((tmp_nx-1)*(Xp[i]- tmp_x_min)/(tmp_x_max-tmp_x_min))); + const int idx_x_right = + max(min(temp_idx_x_right,tmp_nx-1),static_cast<int>(1)); + const int idx_x_left = idx_x_right - 1; + const auto x_0 = + idx_x_left*(tmp_x_max-tmp_x_min)/(tmp_nx-1) + tmp_x_min; + const auto x_1 = + idx_x_right*(tmp_x_max-tmp_x_min)/(tmp_nx-1) + tmp_x_min; + +#if (AMREX_SPACEDIM == 2) + //Interpolate amplitude + const auto idx = [=](int i, int j){ + return (i-tmp_idx_first_time) * tmp_nx + j; + }; + amplitude[i] = WarpXUtilAlgo::bilinear_interp( + t_left, t_right, + x_0, x_1, + p_E_data[idx(idx_t_left, idx_x_left)], + p_E_data[idx(idx_t_left, idx_x_right)], + p_E_data[idx(idx_t_right, idx_x_left)], + p_E_data[idx(idx_t_right, idx_x_right)], + t, Xp[i])*tmp_e_max; + +#elif (AMREX_SPACEDIM == 3) + //Find indices and coordinates along y + const int temp_idx_y_right = static_cast<int>( + ceil((tmp_ny-1)*(Yp[i]- tmp_y_min)/(tmp_y_max-tmp_y_min))); + const int idx_y_right = + max(min(temp_idx_y_right,tmp_ny-1),static_cast<int>(1)); + const int idx_y_left = idx_y_right - 1; + const auto y_0 = + idx_y_left*(tmp_y_max-tmp_y_min)/(tmp_ny-1) + tmp_y_min; + const auto y_1 = + idx_y_right*(tmp_y_max-tmp_y_min)/(tmp_ny-1) + tmp_y_min; + + //Interpolate amplitude + const auto idx = [=](int i, int j, int k){ + return + (i-tmp_idx_first_time)*tmp_nx*tmp_ny+ + j*tmp_ny + k; + }; + amplitude[i] = WarpXUtilAlgo::trilinear_interp( + t_left, t_right, + x_0, x_1, + y_0, y_1, + p_E_data[idx(idx_t_left, idx_x_left, idx_y_left)], + p_E_data[idx(idx_t_left, idx_x_left, idx_y_right)], + p_E_data[idx(idx_t_left, idx_x_right, idx_y_left)], + p_E_data[idx(idx_t_left, idx_x_right, idx_y_right)], + p_E_data[idx(idx_t_right, idx_x_left, idx_y_left)], + p_E_data[idx(idx_t_right, idx_x_left, idx_y_right)], + p_E_data[idx(idx_t_right, idx_x_right, idx_y_left)], + p_E_data[idx(idx_t_right, idx_x_right, idx_y_right)], + t, Xp[i], Yp[i])*tmp_e_max; +#endif + } + ); +} + +void +FromTXYEFileLaserProfile::internal_fill_amplitude_nonuniform( + const int idx_t_left, + const int np, + Real const * AMREX_RESTRICT const Xp, Real const * AMREX_RESTRICT const Yp, + Real t, Real * AMREX_RESTRICT const amplitude) const +{ + // Copy member variables to tmp copies + // and get pointers to underlying data for GPU. + const auto tmp_e_max = m_common_params.e_max; + const auto tmp_x_min = m_params.x_coords.front(); + const auto tmp_x_max = m_params.x_coords.back(); + const auto tmp_y_min = m_params.y_coords.front(); + const auto tmp_y_max = m_params.y_coords.back(); + const auto p_x_coords = m_params.x_coords.dataPtr(); + const int tmp_x_coords_size = static_cast<int>(m_params.x_coords.size()); + const auto p_y_coords = m_params.y_coords.dataPtr(); + const int tmp_y_coords_size = static_cast<int>(m_params.y_coords.size()); + const auto p_E_data = m_params.E_data.dataPtr(); + const auto tmp_idx_first_time = m_params.first_time_index; + const int idx_t_right = idx_t_left+1; + const auto t_left = m_params.t_coords[idx_t_left]; + const auto t_right = m_params.t_coords[idx_t_right]; + + // Loop through the macroparticle to calculate the proper amplitude + amrex::ParallelFor( + np, + [=] AMREX_GPU_DEVICE (int i) { + //Amplitude is zero if we are out of bounds + if (Xp[i] <= tmp_x_min || Xp[i] >= tmp_x_max){ + amplitude[i] = 0.0_rt; + return; + } +#if (AMREX_SPACEDIM == 3) + if (Yp[i] <= tmp_y_min || Yp[i] >= tmp_y_max){ + amplitude[i] = 0.0_rt; + return; + } +#endif + + //Find indices along x + auto const p_x_right = WarpXUtilAlgo::upper_bound( + p_x_coords, p_x_coords+tmp_x_coords_size, Xp[i]); + const int idx_x_right = p_x_right - p_x_coords; + const int idx_x_left = idx_x_right - 1; + +#if (AMREX_SPACEDIM == 2) + //Interpolate amplitude + const auto idx = [=](int i, int j){ + return (i-tmp_idx_first_time) * tmp_x_coords_size + j; + }; + amplitude[i] = WarpXUtilAlgo::bilinear_interp( + t_left, t_right, + p_x_coords[idx_x_left], p_x_coords[idx_x_right], + p_E_data[idx(idx_t_left, idx_x_left)], + p_E_data[idx(idx_t_left, idx_x_right)], + p_E_data[idx(idx_t_right, idx_x_left)], + p_E_data[idx(idx_t_right, idx_x_right)], + t, Xp[i])*tmp_e_max; + +#elif (AMREX_SPACEDIM == 3) + //Find indices along y + auto const p_y_right = WarpXUtilAlgo::upper_bound( + p_y_coords, p_y_coords+tmp_y_coords_size, Yp[i]); + const int idx_y_right = p_y_right - p_y_coords; + const int idx_y_left = idx_y_right - 1; + + //Interpolate amplitude + const auto idx = [=](int i, int j, int k){ + return + (i-tmp_idx_first_time)*tmp_x_coords_size*tmp_y_coords_size+ + j*tmp_y_coords_size + k; + }; + amplitude[i] = WarpXUtilAlgo::trilinear_interp( + t_left, t_right, + p_x_coords[idx_x_left], p_x_coords[idx_x_right], + p_y_coords[idx_y_left], p_y_coords[idx_y_right], + p_E_data[idx(idx_t_left, idx_x_left, idx_y_left)], + p_E_data[idx(idx_t_left, idx_x_left, idx_y_right)], + p_E_data[idx(idx_t_left, idx_x_right, idx_y_left)], + p_E_data[idx(idx_t_left, idx_x_right, idx_y_right)], + p_E_data[idx(idx_t_right, idx_x_left, idx_y_left)], + p_E_data[idx(idx_t_right, idx_x_left, idx_y_right)], + p_E_data[idx(idx_t_right, idx_x_right, idx_y_left)], + p_E_data[idx(idx_t_right, idx_x_right, idx_y_right)], + t, Xp[i], Yp[i])*tmp_e_max; +#endif + } + ); +} diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp index a0b5dd855..31e64eca5 100644 --- a/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp +++ b/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, Luca Fedeli, Maxence Thevenet + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <LaserProfiles.H> #include <WarpX_Complex.H> @@ -72,7 +79,7 @@ GaussianLaserProfile::init ( void GaussianLaserProfile::fill_amplitude ( const int np, Real const * AMREX_RESTRICT const Xp, Real const * AMREX_RESTRICT const Yp, - Real t, Real * AMREX_RESTRICT const amplitude) + Real t, Real * AMREX_RESTRICT const amplitude) const { Complex I(0,1); // Calculate a few factors which are independent of the macroparticle diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileHarris.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileHarris.cpp index 55374c5ea..de4879939 100644 --- a/Source/Laser/LaserProfilesImpl/LaserProfileHarris.cpp +++ b/Source/Laser/LaserProfilesImpl/LaserProfileHarris.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <LaserProfiles.H> #include <WarpX_Complex.H> @@ -35,7 +41,7 @@ HarrisLaserProfile::init ( void HarrisLaserProfile::fill_amplitude ( const int np, Real const * AMREX_RESTRICT const Xp, Real const * AMREX_RESTRICT const Yp, - Real t, Real * AMREX_RESTRICT const amplitude) + Real t, Real * AMREX_RESTRICT const amplitude) const { // This function uses the Harris function as the temporal profile of the pulse const Real omega0 = diff --git a/Source/Laser/LaserProfilesImpl/Make.package b/Source/Laser/LaserProfilesImpl/Make.package index 32284c4e4..2fef27b9f 100644 --- a/Source/Laser/LaserProfilesImpl/Make.package +++ b/Source/Laser/LaserProfilesImpl/Make.package @@ -1,6 +1,7 @@ CEXE_sources += LaserProfileGaussian.cpp CEXE_sources += LaserProfileHarris.cpp CEXE_sources += LaserProfileFieldFunction.cpp +CEXE_sources += LaserProfileFromTXYEFile.cpp INCLUDE_LOCATIONS += $(WARPX_HOME)/Source/Laser/LaserProfilesImpl VPATH_LOCATIONS += $(WARPX_HOME)/Source/Laser/LaserProfilesImpl diff --git a/Source/Parallelization/GuardCellManager.H b/Source/Parallelization/GuardCellManager.H new file mode 100644 index 000000000..ef7738178 --- /dev/null +++ b/Source/Parallelization/GuardCellManager.H @@ -0,0 +1,82 @@ +/* Copyright 2019-2020 Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef GUARDCELLMANAGER_H_ +#define GUARDCELLMANAGER_H_ + +#include <AMReX_IntVect.H> + +/** + * \brief This class computes and stores the number of guard cells needed for + * the allocation of the MultiFabs and required for each part of the PIC loop. + */ +class guardCellManager{ + +public: + + /** + * \brief Initialize number of guard cells depending on the options used. + * + * \param do_subcycling bool, whether to use subcycling + * \param do_fdtd_nci_corr bool, whether to use Godfrey NCI corrector + * \param do_nodal bool, whether the field solver is nodal + * \param do_moving_window bool, whether to use moving window + * \param do_fft_mpi_dec bool, whether to do parallel FFTs for PSATD + * \param aux_is_nodal bool, true if the aux grid is nodal + * \param moving_window_dir direction of moving window + * \param nox order of current deposition + * \param nox_fft order of PSATD in x direction + * \param noy_fft order of PSATD in y direction + * \param noz_fft order of PSATD in z direction + * \param nci_corr_stencil stencil of NCI corrector + * \param maxwell_fdtd_solver_id if of Maxwell solver + * \param max_level max level of the simulation + */ + void Init( + const bool do_subcycling, + const bool do_fdtd_nci_corr, + const bool do_nodal, + const bool do_moving_window, + const bool do_fft_mpi_dec, + const bool aux_is_nodal, + const int moving_window_dir, + const int nox, + const int nox_fft, const int noy_fft, const int noz_fft, + const int nci_corr_stencil, + const int maxwell_fdtd_solver_id, + const int max_level, + const bool exchange_all_guard_cells); + + // Guard cells allocated for MultiFabs E and B + amrex::IntVect ng_alloc_EB = amrex::IntVect::TheZeroVector(); + // Guard cells allocated for MultiFab J + amrex::IntVect ng_alloc_J = amrex::IntVect::TheZeroVector(); + // Guard cells allocated for MultiFab Rho + amrex::IntVect ng_alloc_Rho = amrex::IntVect::TheZeroVector(); + // Guard cells allocated for MultiFab F + amrex::IntVect ng_alloc_F = amrex::IntVect::TheZeroVector(); + + // Guard cells exchanged for specific parts of the PIC loop + + // Number of guard cells of E and B that must exchanged before Field Solver + amrex::IntVect ng_FieldSolver = amrex::IntVect::TheZeroVector(); + // Number of guard cells of F that must exchanged before Field Solver + amrex::IntVect ng_FieldSolverF = amrex::IntVect::TheZeroVector(); + // Number of guard cells of E and B that must exchanged before Field Gather + amrex::IntVect ng_FieldGather = amrex::IntVect::TheZeroVector(); + // Number of guard cells of E and B that must exchanged before updating the Aux grid + amrex::IntVect ng_UpdateAux = amrex::IntVect::TheZeroVector(); + // Number of guard cells of all MultiFabs that must exchanged before moving window + amrex::IntVect ng_MovingWindow = amrex::IntVect::TheZeroVector(); + + // When the auxiliary grid is nodal but the field solver is staggered + // (typically with momentum-conserving gather with FDTD Yee solver), + // An extra guard cell is needed on the fine grid to do the interpolation + // for E and B. + amrex::IntVect ng_Extra = amrex::IntVect::TheZeroVector(); +}; + +#endif // GUARDCELLMANAGER_H_ diff --git a/Source/Parallelization/GuardCellManager.cpp b/Source/Parallelization/GuardCellManager.cpp new file mode 100644 index 000000000..d845a7447 --- /dev/null +++ b/Source/Parallelization/GuardCellManager.cpp @@ -0,0 +1,177 @@ +/* Copyright 2019-2020 Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "GuardCellManager.H" +#include "NCIGodfreyFilter.H" +#include <AMReX_Print.H> + +using namespace amrex; + +void +guardCellManager::Init( + const bool do_subcycling, + const bool do_fdtd_nci_corr, + const bool do_nodal, + const bool do_moving_window, + const bool do_fft_mpi_dec, + const bool aux_is_nodal, + const int moving_window_dir, + const int nox, + const int nox_fft, const int noy_fft, const int noz_fft, + const int nci_corr_stencil, + const int maxwell_fdtd_solver_id, + const int max_level, + const bool exchange_all_guard_cells) +{ + // When using subcycling, the particles on the fine level perform two pushes + // before being redistributed ; therefore, we need one extra guard cell + // (the particles may move by 2*c*dt) + const int ngx_tmp = (max_level > 0 && do_subcycling == 1) ? nox+1 : nox; + const int ngy_tmp = (max_level > 0 && do_subcycling == 1) ? nox+1 : nox; + const int ngz_tmp = (max_level > 0 && do_subcycling == 1) ? nox+1 : nox; + + // Ex, Ey, Ez, Bx, By, and Bz have the same number of ghost cells. + // jx, jy, jz and rho have the same number of ghost cells. + // E and B have the same number of ghost cells as j and rho if NCI filter is not used, + // but different number of ghost cells in z-direction if NCI filter is used. + // The number of cells should be even, in order to easily perform the + // interpolation from coarse grid to fine grid. + int ngx = (ngx_tmp % 2) ? ngx_tmp+1 : ngx_tmp; // Always even number + int ngy = (ngy_tmp % 2) ? ngy_tmp+1 : ngy_tmp; // Always even number + int ngz_nonci = (ngz_tmp % 2) ? ngz_tmp+1 : ngz_tmp; // Always even number + int ngz; + if (do_fdtd_nci_corr) { + int ng = ngz_tmp + nci_corr_stencil; + ngz = (ng % 2) ? ng+1 : ng; + } else { + ngz = ngz_nonci; + } + + // J is only interpolated from fine to coarse (not coarse to fine) + // and therefore does not need to be even. + int ngJx = ngx_tmp; + int ngJy = ngy_tmp; + int ngJz = ngz_tmp; + + // When calling the moving window (with one level of refinement), we shift + // the fine grid by 2 cells ; therefore, we need at least 2 guard cells + // on level 1. This may not be necessary for level 0. + if (do_moving_window) { + ngx = std::max(ngx,2); + ngy = std::max(ngy,2); + ngz = std::max(ngz,2); + ngJx = std::max(ngJx,2); + ngJy = std::max(ngJy,2); + ngJz = std::max(ngJz,2); + } + +#if (AMREX_SPACEDIM == 3) + ng_alloc_EB = IntVect(ngx,ngy,ngz); + ng_alloc_J = IntVect(ngJx,ngJy,ngJz); +#elif (AMREX_SPACEDIM == 2) + ng_alloc_EB = IntVect(ngx,ngz); + ng_alloc_J = IntVect(ngJx,ngJz); +#endif + + ng_alloc_Rho = ng_alloc_J+1; //One extra ghost cell, so that it's safe to deposit charge density + // after pushing particle. + int ng_alloc_F_int = (do_moving_window) ? 2 : 0; + // CKC solver requires one additional guard cell + if (maxwell_fdtd_solver_id == 1) ng_alloc_F_int = std::max( ng_alloc_F_int, 1 ); + ng_alloc_F = IntVect(AMREX_D_DECL(ng_alloc_F_int, ng_alloc_F_int, ng_alloc_F_int)); + +#ifdef WARPX_USE_PSATD + if (do_fft_mpi_dec == false){ + // All boxes should have the same number of guard cells + // (to avoid temporary parallel copies) + // Thus take the max of the required number of guards for each field + // Also: the number of guard cell should be enough to contain + // the stencil of the FFT solver. Here, this number (`ngFFT`) + // is determined *empirically* to be the order of the solver + // for nodal, and half the order of the solver for staggered. + IntVect ngFFT; + if (do_nodal) { + ngFFT = IntVect(AMREX_D_DECL(nox_fft, noy_fft, noz_fft)); + } else { + ngFFT = IntVect(AMREX_D_DECL(nox_fft/2, noy_fft/2, noz_fft/2)); + } + for (int i_dim=0; i_dim<AMREX_SPACEDIM; i_dim++ ){ + int ng_required = ngFFT[i_dim]; + // Get the max + ng_required = std::max( ng_required, ng_alloc_EB[i_dim] ); + ng_required = std::max( ng_required, ng_alloc_J[i_dim] ); + ng_required = std::max( ng_required, ng_alloc_Rho[i_dim] ); + ng_required = std::max( ng_required, ng_alloc_F[i_dim] ); + // Set the guard cells to this max + ng_alloc_EB[i_dim] = ng_required; + ng_alloc_J[i_dim] = ng_required; + ng_alloc_F[i_dim] = ng_required; + ng_alloc_Rho[i_dim] = ng_required; + ng_alloc_F_int = ng_required; + } + } + ng_alloc_F = IntVect(AMREX_D_DECL(ng_alloc_F_int, ng_alloc_F_int, ng_alloc_F_int)); +#endif + + ng_Extra = IntVect(static_cast<int>(aux_is_nodal and !do_nodal)); + + // Compute number of cells required for Field Solver +#ifdef WARPX_USE_PSATD + ng_FieldSolver = ng_alloc_EB; + ng_FieldSolverF = ng_alloc_EB; +#else + ng_FieldSolver = IntVect(AMREX_D_DECL(1,1,1)); + ng_FieldSolverF = IntVect(AMREX_D_DECL(1,1,1)); +#endif + + if (exchange_all_guard_cells){ + // Run in safe mode: exchange all allocated guard cells at each + // call of FillBoundary + ng_FieldSolver = ng_alloc_EB; + ng_FieldSolverF = ng_alloc_F; + ng_FieldGather = ng_alloc_EB; + ng_UpdateAux = ng_alloc_EB; + if (do_moving_window){ + ng_MovingWindow = ng_alloc_EB; + } + } else { + + ng_FieldSolver = ng_FieldSolver.min(ng_alloc_EB); + + // Compute number of cells required for Field Gather + int FGcell[4] = {0,1,1,2}; // Index is nox + IntVect ng_FieldGather_noNCI = IntVect(AMREX_D_DECL(FGcell[nox],FGcell[nox],FGcell[nox])); + // Add one cell if momentum_conserving gather in a staggered-field simulation + ng_FieldGather_noNCI += ng_Extra; + // Not sure why, but need one extra guard cell when using MR + if (max_level >= 1) ng_FieldGather_noNCI += ng_Extra; + ng_FieldGather_noNCI = ng_FieldGather_noNCI.min(ng_alloc_EB); + // If NCI filter, add guard cells in the z direction + IntVect ng_NCIFilter = IntVect::TheZeroVector(); + if (do_fdtd_nci_corr) + ng_NCIFilter[AMREX_SPACEDIM-1] = NCIGodfreyFilter::m_stencil_width; + // Note: communications of guard cells for bilinear filter are handled + // separately. + ng_FieldGather = ng_FieldGather_noNCI + ng_NCIFilter; + + // Guard cells for auxiliary grid. + // Not sure why there is a 2* here... + ng_UpdateAux = 2*ng_FieldGather_noNCI + ng_NCIFilter; + + // Make sure we do not exchange more guard cells than allocated. + ng_FieldGather = ng_FieldGather.min(ng_alloc_EB); + ng_UpdateAux = ng_UpdateAux.min(ng_alloc_EB); + ng_FieldSolverF = ng_FieldSolverF.min(ng_alloc_F); + // Only FillBoundary(ng_FieldGather) is called between consecutive + // field solves. So ng_FieldGather must have enough cells + // for the field solve too. + ng_FieldGather = ng_FieldGather.max(ng_FieldSolver); + + if (do_moving_window){ + ng_MovingWindow[moving_window_dir] = 1; + } + } +} diff --git a/Source/Parallelization/InterpolateCurrentFineToCoarse.H b/Source/Parallelization/InterpolateCurrentFineToCoarse.H index cbbcdfab5..43cda26df 100644 --- a/Source/Parallelization/InterpolateCurrentFineToCoarse.H +++ b/Source/Parallelization/InterpolateCurrentFineToCoarse.H @@ -1,4 +1,4 @@ -/* Copyright 2019 Axel Huebl, Weiqun Zhang +/* Copyright 2019-2020 Axel Huebl * * This file is part of WarpX. * @@ -63,9 +63,9 @@ public: // return zero for out-of-bounds accesses during interpolation // this is efficiently used as a method to add neutral elements beyond guards in the average below - auto const fine = [fine_unsafe] AMREX_GPU_DEVICE (int const j, int const k, int const l) noexcept + auto const fine = [fine_unsafe] AMREX_GPU_DEVICE (int const jj, int const kk, int const ll) noexcept { - return fine_unsafe.contains(j, k, l) ? fine_unsafe(j, k, l) : amrex::Real{0.}; + return fine_unsafe.contains(jj, kk, ll) ? fine_unsafe(jj, kk, ll) : amrex::Real{0.}; }; int const ii = i * m_refinement_ratio; diff --git a/Source/Parallelization/InterpolateDensityFineToCoarse.H b/Source/Parallelization/InterpolateDensityFineToCoarse.H index 947db2aac..5d679583a 100644 --- a/Source/Parallelization/InterpolateDensityFineToCoarse.H +++ b/Source/Parallelization/InterpolateDensityFineToCoarse.H @@ -1,4 +1,4 @@ -/* Copyright 2019 Axel Huebl, Weiqun Zhang +/* Copyright 2019 Axel Huebl * * This file is part of WarpX. * diff --git a/Source/Parallelization/Make.package b/Source/Parallelization/Make.package index 7c3c38627..065556b33 100644 --- a/Source/Parallelization/Make.package +++ b/Source/Parallelization/Make.package @@ -1,7 +1,9 @@ CEXE_sources += WarpXComm.cpp CEXE_sources += WarpXRegrid.cpp +CEXE_sources += GuardCellManager.cpp CEXE_headers += WarpXSumGuardCells.H CEXE_headers += WarpXComm_K.H +CEXE_headers += GuardCellManager.H CEXE_headers += WarpXComm.H INCLUDE_LOCATIONS += $(WARPX_HOME)/Source/Parallelization diff --git a/Source/Parallelization/WarpXComm.H b/Source/Parallelization/WarpXComm.H index 81f00088e..7352e797e 100644 --- a/Source/Parallelization/WarpXComm.H +++ b/Source/Parallelization/WarpXComm.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARALLELIZATION_COMM_H_ #define WARPX_PARALLELIZATION_COMM_H_ diff --git a/Source/Parallelization/WarpXComm.cpp b/Source/Parallelization/WarpXComm.cpp index b61ae4fc7..31bb802f7 100644 --- a/Source/Parallelization/WarpXComm.cpp +++ b/Source/Parallelization/WarpXComm.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Aurore Blelly, Axel Huebl + * David Grote, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpXComm.H> #include <WarpXComm_K.H> #include <WarpX.H> @@ -321,41 +329,41 @@ WarpX::UpdateAuxilaryDataSameType () } void -WarpX::FillBoundaryB () +WarpX::FillBoundaryB (IntVect ng, IntVect ng_extra_fine) { for (int lev = 0; lev <= finest_level; ++lev) { - FillBoundaryB(lev); + FillBoundaryB(lev, ng, ng_extra_fine); } } void -WarpX::FillBoundaryE () +WarpX::FillBoundaryE (IntVect ng, IntVect ng_extra_fine) { for (int lev = 0; lev <= finest_level; ++lev) { - FillBoundaryE(lev); + FillBoundaryE(lev, ng, ng_extra_fine); } } void -WarpX::FillBoundaryF () +WarpX::FillBoundaryF (IntVect ng) { for (int lev = 0; lev <= finest_level; ++lev) { - FillBoundaryF(lev); + FillBoundaryF(lev, ng); } } void -WarpX::FillBoundaryE(int lev) +WarpX::FillBoundaryE(int lev, IntVect ng, IntVect ng_extra_fine) { - FillBoundaryE(lev, PatchType::fine); - if (lev > 0) FillBoundaryE(lev, PatchType::coarse); + FillBoundaryE(lev, PatchType::fine, ng+ng_extra_fine); + if (lev > 0) FillBoundaryE(lev, PatchType::coarse, ng); } void -WarpX::FillBoundaryE (int lev, PatchType patch_type) +WarpX::FillBoundaryE (int lev, PatchType patch_type, IntVect ng) { if (patch_type == PatchType::fine) { @@ -370,8 +378,12 @@ WarpX::FillBoundaryE (int lev, PatchType patch_type) } const auto& period = Geom(lev).periodicity(); - Vector<MultiFab*> mf{Efield_fp[lev][0].get(),Efield_fp[lev][1].get(),Efield_fp[lev][2].get()}; - amrex::FillBoundary(mf, period); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + ng <= Efield_fp[lev][0]->nGrowVect(), + "Error: in FillBoundaryE, requested more guard cells than allocated"); + Efield_fp[lev][0]->FillBoundary(ng, period); + Efield_fp[lev][1]->FillBoundary(ng, period); + Efield_fp[lev][2]->FillBoundary(ng, period); } else if (patch_type == PatchType::coarse) { @@ -386,20 +398,24 @@ WarpX::FillBoundaryE (int lev, PatchType patch_type) } const auto& cperiod = Geom(lev-1).periodicity(); - Vector<MultiFab*> mf{Efield_cp[lev][0].get(),Efield_cp[lev][1].get(),Efield_cp[lev][2].get()}; - amrex::FillBoundary(mf, cperiod); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + ng <= Efield_cp[lev][0]->nGrowVect(), + "Error: in FillBoundaryE, requested more guard cells than allocated"); + Efield_cp[lev][0]->FillBoundary(ng, cperiod); + Efield_cp[lev][1]->FillBoundary(ng, cperiod); + Efield_cp[lev][2]->FillBoundary(ng, cperiod); } } void -WarpX::FillBoundaryB (int lev) +WarpX::FillBoundaryB (int lev, IntVect ng, IntVect ng_extra_fine) { - FillBoundaryB(lev, PatchType::fine); - if (lev > 0) FillBoundaryB(lev, PatchType::coarse); + FillBoundaryB(lev, PatchType::fine, ng + ng_extra_fine); + if (lev > 0) FillBoundaryB(lev, PatchType::coarse, ng); } void -WarpX::FillBoundaryB (int lev, PatchType patch_type) +WarpX::FillBoundaryB (int lev, PatchType patch_type, IntVect ng) { if (patch_type == PatchType::fine) { @@ -413,8 +429,12 @@ WarpX::FillBoundaryB (int lev, PatchType patch_type) pml[lev]->FillBoundaryB(patch_type); } const auto& period = Geom(lev).periodicity(); - Vector<MultiFab*> mf{Bfield_fp[lev][0].get(),Bfield_fp[lev][1].get(),Bfield_fp[lev][2].get()}; - amrex::FillBoundary(mf, period); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + ng <= Bfield_fp[lev][0]->nGrowVect(), + "Error: in FillBoundaryB, requested more guard cells than allocated"); + Bfield_fp[lev][0]->FillBoundary(ng, period); + Bfield_fp[lev][1]->FillBoundary(ng, period); + Bfield_fp[lev][2]->FillBoundary(ng, period); } else if (patch_type == PatchType::coarse) { @@ -428,20 +448,24 @@ WarpX::FillBoundaryB (int lev, PatchType patch_type) pml[lev]->FillBoundaryB(patch_type); } const auto& cperiod = Geom(lev-1).periodicity(); - Vector<MultiFab*> mf{Bfield_cp[lev][0].get(),Bfield_cp[lev][1].get(),Bfield_cp[lev][2].get()}; - amrex::FillBoundary(mf, cperiod); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + ng <= Bfield_cp[lev][0]->nGrowVect(), + "Error: in FillBoundaryB, requested more guard cells than allocated"); + Bfield_cp[lev][0]->FillBoundary(ng, cperiod); + Bfield_cp[lev][1]->FillBoundary(ng, cperiod); + Bfield_cp[lev][2]->FillBoundary(ng, cperiod); } } void -WarpX::FillBoundaryF (int lev) +WarpX::FillBoundaryF (int lev, IntVect ng) { - FillBoundaryF(lev, PatchType::fine); - if (lev > 0) FillBoundaryF(lev, PatchType::coarse); + FillBoundaryF(lev, PatchType::fine, ng); + if (lev > 0) FillBoundaryF(lev, PatchType::coarse, ng); } void -WarpX::FillBoundaryF (int lev, PatchType patch_type) +WarpX::FillBoundaryF (int lev, PatchType patch_type, IntVect ng) { if (patch_type == PatchType::fine && F_fp[lev]) { @@ -453,7 +477,10 @@ WarpX::FillBoundaryF (int lev, PatchType patch_type) } const auto& period = Geom(lev).periodicity(); - F_fp[lev]->FillBoundary(period); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + ng <= F_fp[lev]->nGrowVect(), + "Error: in FillBoundaryF, requested more guard cells than allocated"); + F_fp[lev]->FillBoundary(ng, period); } else if (patch_type == PatchType::coarse && F_cp[lev]) { @@ -465,11 +492,35 @@ WarpX::FillBoundaryF (int lev, PatchType patch_type) } const auto& cperiod = Geom(lev-1).periodicity(); - F_cp[lev]->FillBoundary(cperiod); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + ng <= F_cp[lev]->nGrowVect(), + "Error: in FillBoundaryF, requested more guard cells than allocated"); + F_cp[lev]->FillBoundary(ng, cperiod); } } void +WarpX::FillBoundaryAux (IntVect ng) +{ + for (int lev = 0; lev <= finest_level-1; ++lev) + { + FillBoundaryAux(lev, ng); + } +} + +void +WarpX::FillBoundaryAux (int lev, IntVect ng) +{ + const auto& period = Geom(lev).periodicity(); + Efield_aux[lev][0]->FillBoundary(ng, period); + Efield_aux[lev][1]->FillBoundary(ng, period); + Efield_aux[lev][2]->FillBoundary(ng, period); + Bfield_aux[lev][0]->FillBoundary(ng, period); + Bfield_aux[lev][1]->FillBoundary(ng, period); + Bfield_aux[lev][2]->FillBoundary(ng, period); +} + +void WarpX::SyncCurrent () { BL_PROFILE("SyncCurrent()"); diff --git a/Source/Parallelization/WarpXComm_K.H b/Source/Parallelization/WarpXComm_K.H index 169cd0ee1..1b6eceb93 100644 --- a/Source/Parallelization/WarpXComm_K.H +++ b/Source/Parallelization/WarpXComm_K.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Axel Huebl, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_COMM_K_H_ #define WARPX_COMM_K_H_ diff --git a/Source/Parallelization/WarpXRegrid.cpp b/Source/Parallelization/WarpXRegrid.cpp index 29ccc8f4d..54166e8ce 100644 --- a/Source/Parallelization/WarpXRegrid.cpp +++ b/Source/Parallelization/WarpXRegrid.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Ann Almgren, Axel Huebl + * David Grote, Maxence Thevenet, Remi Lehe + * Weiqun Zhang, levinem + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <AMReX_BLProfiler.H> diff --git a/Source/Parallelization/WarpXSumGuardCells.H b/Source/Parallelization/WarpXSumGuardCells.H index 36eb4ed6c..972c1cd2d 100644 --- a/Source/Parallelization/WarpXSumGuardCells.H +++ b/Source/Parallelization/WarpXSumGuardCells.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Maxence Thevenet, Remi Lehe, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_SUM_GUARD_CELLS_H_ #define WARPX_SUM_GUARD_CELLS_H_ diff --git a/Source/Parser/GpuParser.H b/Source/Parser/GpuParser.H index c158ee314..c6d870800 100644 --- a/Source/Parser/GpuParser.H +++ b/Source/Parser/GpuParser.H @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 Maxence Thevenet, Revathi Jambunathan, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_GPU_PARSER_H_ #define WARPX_GPU_PARSER_H_ @@ -17,7 +24,7 @@ public: AMREX_GPU_HOST_DEVICE amrex::Real - operator() (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + operator() (amrex::Real x, amrex::Real y, amrex::Real z, amrex::Real t=0.0) const noexcept { #ifdef AMREX_USE_GPU @@ -27,15 +34,17 @@ public: amrex::Gpu::SharedMemory<amrex::Real> gsm; amrex::Real* p = gsm.dataPtr(); int tid = threadIdx.x + threadIdx.y*blockDim.x + threadIdx.z*(blockDim.x*blockDim.y); - p[tid*3] = x; - p[tid*3+1] = y; - p[tid*3+2] = z; + p[tid*4] = x; + p[tid*4+1] = y; + p[tid*4+2] = z; + p[tid*4+3] = t; return wp_ast_eval(m_gpu_parser.ast); #else // WarpX compiled for GPU, function compiled for __host__ m_var.x = x; m_var.y = y; m_var.z = z; + m_t = t; return wp_ast_eval(m_cpu_parser.ast); #endif @@ -49,10 +58,12 @@ public: m_var[tid].x = x; m_var[tid].y = y; m_var[tid].z = z; + m_t[tid] = t; return wp_ast_eval(m_parser[tid]->ast); #endif } + private: #ifdef AMREX_USE_GPU @@ -61,10 +72,12 @@ private: // Copy of the parser running on __host__ struct wp_parser m_cpu_parser; mutable amrex::XDim3 m_var; + mutable amrex::Real m_t; #else // Only one parser struct wp_parser** m_parser; mutable amrex::XDim3* m_var; + mutable amrex::Real* m_t; int nthreads; #endif }; diff --git a/Source/Parser/GpuParser.cpp b/Source/Parser/GpuParser.cpp index 5078b498b..22fab6313 100644 --- a/Source/Parser/GpuParser.cpp +++ b/Source/Parser/GpuParser.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 Maxence Thevenet, Revathi Jambunathan, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <GpuParser.H> GpuParser::GpuParser (WarpXParser const& wp) @@ -16,6 +23,7 @@ GpuParser::GpuParser (WarpXParser const& wp) wp_parser_regvar_gpu(&m_gpu_parser, "x", 0); wp_parser_regvar_gpu(&m_gpu_parser, "y", 1); wp_parser_regvar_gpu(&m_gpu_parser, "z", 2); + wp_parser_regvar_gpu(&m_gpu_parser, "t", 3); // Initialize CPU parser: allocate memory in CUDA managed memory, // copy all data needed on CPU to m_cpu_parser @@ -28,6 +36,7 @@ GpuParser::GpuParser (WarpXParser const& wp) wp_parser_regvar(&m_cpu_parser, "x", &(m_var.x)); wp_parser_regvar(&m_cpu_parser, "y", &(m_var.y)); wp_parser_regvar(&m_cpu_parser, "z", &(m_var.z)); + wp_parser_regvar(&m_cpu_parser, "t", &(m_t)); #else // not defined AMREX_USE_GPU @@ -39,6 +48,7 @@ GpuParser::GpuParser (WarpXParser const& wp) m_parser = ::new struct wp_parser*[nthreads]; m_var = ::new amrex::XDim3[nthreads]; + m_t = ::new amrex::Real[nthreads]; for (int tid = 0; tid < nthreads; ++tid) { @@ -50,6 +60,7 @@ GpuParser::GpuParser (WarpXParser const& wp) wp_parser_regvar(m_parser[tid], "x", &(m_var[tid].x)); wp_parser_regvar(m_parser[tid], "y", &(m_var[tid].y)); wp_parser_regvar(m_parser[tid], "z", &(m_var[tid].z)); + wp_parser_regvar(m_parser[tid], "t", &(m_t[tid])); } #endif // AMREX_USE_GPU diff --git a/Source/Parser/Make.package b/Source/Parser/Make.package index 5ce02cbda..15115c138 100644 --- a/Source/Parser/Make.package +++ b/Source/Parser/Make.package @@ -5,6 +5,7 @@ CEXE_sources += WarpXParser.cpp CEXE_headers += WarpXParser.H CEXE_headers += GpuParser.H CEXE_sources += GpuParser.cpp +CEXE_headers += WarpXParserWrapper.H INCLUDE_LOCATIONS += $(WARPX_HOME)/Source/Parser VPATH_LOCATIONS += $(WARPX_HOME)/Source/Parser diff --git a/Source/Parser/WarpXParser.H b/Source/Parser/WarpXParser.H index 8c1d854d8..863b35fb8 100644 --- a/Source/Parser/WarpXParser.H +++ b/Source/Parser/WarpXParser.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARSER_H_ #define WARPX_PARSER_H_ diff --git a/Source/Parser/WarpXParser.cpp b/Source/Parser/WarpXParser.cpp index ced536327..8c8be7ecb 100644 --- a/Source/Parser/WarpXParser.cpp +++ b/Source/Parser/WarpXParser.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <algorithm> #include "WarpXParser.H" diff --git a/Source/Parser/WarpXParserWrapper.H b/Source/Parser/WarpXParserWrapper.H new file mode 100644 index 000000000..2c76d97a3 --- /dev/null +++ b/Source/Parser/WarpXParserWrapper.H @@ -0,0 +1,41 @@ +/* Copyright 2020 Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_PARSER_WRAPPER_H_ +#define WARPX_PARSER_WRAPPER_H_ + +#include <WarpXParser.H> +#include <AMReX_Gpu.H> +#include <GpuParser.H> +/** + * \brief + * The ParserWrapper struct is constructed to safely use the GpuParser class + * that can essentially be though of as a raw pointer. The GpuParser does + * not have an explicit destructor and the AddPlasma subroutines handle the GpuParser + * in a safe way. The ParserWrapper struct is used to avoid memory leak + * in the EB parser functions. + */ +struct ParserWrapper + : public amrex::Gpu::Managed +{ + ParserWrapper (WarpXParser const& a_parser) noexcept + : m_parser(a_parser) {} + + ~ParserWrapper() { + m_parser.clear(); + } + + AMREX_GPU_HOST_DEVICE + amrex::Real + getField (amrex::Real x, amrex::Real y, amrex::Real z, amrex::Real t=0.0) const noexcept + { + return m_parser(x,y,z,t); + } + + GpuParser m_parser; +}; + +#endif diff --git a/Source/Parser/wp_parser_c.h b/Source/Parser/wp_parser_c.h index 970d6b355..2cf0e2c00 100644 --- a/Source/Parser/wp_parser_c.h +++ b/Source/Parser/wp_parser_c.h @@ -30,7 +30,7 @@ wp_ast_eval (struct wp_node* node) #ifdef AMREX_DEVICE_COMPILE extern __shared__ amrex_real extern_xyz[]; int tid = threadIdx.x + threadIdx.y*blockDim.x + threadIdx.z*(blockDim.x*blockDim.y); - amrex_real* x = extern_xyz + tid*3; + amrex_real* x = extern_xyz + tid*4; // parser assumes 4 independent variables (x,y,z,t) #endif switch (node->type) diff --git a/Source/Particles/Collision/CollisionType.H b/Source/Particles/Collision/CollisionType.H index d020f47e8..29fdfb029 100644 --- a/Source/Particles/Collision/CollisionType.H +++ b/Source/Particles/Collision/CollisionType.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_COLLISION_COLLISIONTYPE_H_ #define WARPX_PARTICLES_COLLISION_COLLISIONTYPE_H_ diff --git a/Source/Particles/Collision/CollisionType.cpp b/Source/Particles/Collision/CollisionType.cpp index b8014579d..1d384ed8c 100644 --- a/Source/Particles/Collision/CollisionType.cpp +++ b/Source/Particles/Collision/CollisionType.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "CollisionType.H" #include "ShuffleFisherYates.H" #include "ElasticCollisionPerez.H" @@ -8,11 +14,9 @@ CollisionType::CollisionType( std::string const collision_name) { -#if defined WARPX_DIM_XZ - amrex::Abort("Collisions only work in 3D geometry for now."); -#elif defined WARPX_DIM_RZ + #if defined WARPX_DIM_RZ amrex::Abort("Collisions only work in Cartesian geometry for now."); -#endif + #endif // read collision species std::vector<std::string> collision_species; @@ -66,7 +70,7 @@ namespace { const auto dxi = geom.InvCellSizeArray(); const auto plo = geom.ProbLoArray(); - // Find particles that are in each cell ; + // Find particles that are in each cell; // results are stored in the object `bins`. ParticleBins bins; bins.build(np, particle_ptr, cbx, @@ -128,7 +132,11 @@ void CollisionType::doCoulombCollisionsWithinTile const Real dt = WarpX::GetInstance().getdt(lev); Geometry const& geom = WarpX::GetInstance().Geom(lev); - const Real dV = geom.CellSize(0)*geom.CellSize(1)*geom.CellSize(2); + #if (AMREX_SPACEDIM == 2) + auto dV = geom.CellSize(0) * geom.CellSize(1); + #elif (AMREX_SPACEDIM == 3) + auto dV = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2); + #endif // Loop over cells amrex::ParallelFor( n_cells, @@ -200,7 +208,11 @@ void CollisionType::doCoulombCollisionsWithinTile const Real dt = WarpX::GetInstance().getdt(lev); Geometry const& geom = WarpX::GetInstance().Geom(lev); - const Real dV = geom.CellSize(0)*geom.CellSize(1)*geom.CellSize(2); + #if (AMREX_SPACEDIM == 2) + auto dV = geom.CellSize(0) * geom.CellSize(1); + #elif (AMREX_SPACEDIM == 3) + auto dV = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2); + #endif // Loop over cells amrex::ParallelFor( n_cells, diff --git a/Source/Particles/Collision/ComputeTemperature.H b/Source/Particles/Collision/ComputeTemperature.H index 770510d74..81cb14dad 100644 --- a/Source/Particles/Collision/ComputeTemperature.H +++ b/Source/Particles/Collision/ComputeTemperature.H @@ -1,3 +1,9 @@ +/* Copyright 2019-2020 Andrew Myers, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_COLLISION_COMPUTE_TEMPERATURE_H_ #define WARPX_PARTICLES_COLLISION_COMPUTE_TEMPERATURE_H_ diff --git a/Source/Particles/Collision/ElasticCollisionPerez.H b/Source/Particles/Collision/ElasticCollisionPerez.H index 8e16d95cc..b1fa64300 100644 --- a/Source/Particles/Collision/ElasticCollisionPerez.H +++ b/Source/Particles/Collision/ElasticCollisionPerez.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_COLLISION_ELASTIC_COLLISION_PEREZ_H_ #define WARPX_PARTICLES_COLLISION_ELASTIC_COLLISION_PEREZ_H_ diff --git a/Source/Particles/Collision/ShuffleFisherYates.H b/Source/Particles/Collision/ShuffleFisherYates.H index 621e654d6..614b44d37 100644 --- a/Source/Particles/Collision/ShuffleFisherYates.H +++ b/Source/Particles/Collision/ShuffleFisherYates.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_COLLISION_SHUFFLE_FISHER_YATES_H_ #define WARPX_PARTICLES_COLLISION_SHUFFLE_FISHER_YATES_H_ diff --git a/Source/Particles/Collision/UpdateMomentumPerezElastic.H b/Source/Particles/Collision/UpdateMomentumPerezElastic.H index 948e8b075..05c8cd227 100644 --- a/Source/Particles/Collision/UpdateMomentumPerezElastic.H +++ b/Source/Particles/Collision/UpdateMomentumPerezElastic.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_COLLISION_UPDATE_MOMENTUM_PEREZ_ELASTIC_H_ #define WARPX_PARTICLES_COLLISION_UPDATE_MOMENTUM_PEREZ_ELASTIC_H_ diff --git a/Source/Particles/Deposition/ChargeDeposition.H b/Source/Particles/Deposition/ChargeDeposition.H index eec407a2b..669fc4c57 100755 --- a/Source/Particles/Deposition/ChargeDeposition.H +++ b/Source/Particles/Deposition/ChargeDeposition.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, David Grote, Maxence Thevenet + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef CHARGEDEPOSITION_H_ #define CHARGEDEPOSITION_H_ diff --git a/Source/Particles/Deposition/CurrentDeposition.H b/Source/Particles/Deposition/CurrentDeposition.H index 870dbcd33..90039dea2 100644 --- a/Source/Particles/Deposition/CurrentDeposition.H +++ b/Source/Particles/Deposition/CurrentDeposition.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, David Grote, Maxence Thevenet + * Remi Lehe, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef CURRENTDEPOSITION_H_ #define CURRENTDEPOSITION_H_ diff --git a/Source/Particles/Gather/FieldGather.H b/Source/Particles/Gather/FieldGather.H index 57c5d1a4a..0a58f8425 100644 --- a/Source/Particles/Gather/FieldGather.H +++ b/Source/Particles/Gather/FieldGather.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, David Grote, Maxence Thevenet + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef FIELDGATHER_H_ #define FIELDGATHER_H_ diff --git a/Source/Particles/MultiParticleContainer.H b/Source/Particles/MultiParticleContainer.H index 9db129b05..65c13e39b 100644 --- a/Source/Particles/MultiParticleContainer.H +++ b/Source/Particles/MultiParticleContainer.H @@ -1,3 +1,13 @@ +/* Copyright 2019-2020 Andrew Myers, Ann Almgren, Axel Huebl + * David Grote, Jean-Luc Vay, Junmin Gu + * Luca Fedeli, Mathieu Lobet, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_ParticleContainer_H_ #define WARPX_ParticleContainer_H_ @@ -8,6 +18,7 @@ #include <RigidInjectedParticleContainer.H> #include <PhotonParticleContainer.H> #include <LaserParticleContainer.H> +#include <WarpXParserWrapper.H> #include <AMReX_Particles.H> #ifdef WARPX_QED @@ -81,13 +92,6 @@ public: amrex::Real t, amrex::Real dt); /// - /// This pushes the particle positions by one half time step for all the species in the - /// MultiParticleContainer. It is used to desynchronize the particles after initializaton - /// or when restarting from a checkpoint. This is the electrostatic version. - /// - void PushXES (amrex::Real dt); - - /// /// This deposits the particle charge onto rho, accumulating the value for all the species /// in the MultiParticleContainer. rho is assumed to contain node-centered multifabs. /// This version is hard-coded for CIC deposition. @@ -128,7 +132,7 @@ public: /// /// This pushes the particle positions by one half time step for all the species in the /// MultiParticleContainer. It is used to desynchronize the particles after initializaton - /// or when restarting from a checkpoint. This is the electromagnetic version. + /// or when restarting from a checkpoint. /// void PushX (amrex::Real dt); @@ -215,6 +219,21 @@ public: IonizationProcess ionization_process; + std::string m_B_ext_particle_s = "default"; + std::string m_E_ext_particle_s = "default"; + // External fields added to particle fields. + amrex::Vector<amrex::Real> m_B_external_particle; + amrex::Vector<amrex::Real> m_E_external_particle; + // ParserWrapper for B_external on the particle + std::unique_ptr<ParserWrapper> m_Bx_particle_parser; + std::unique_ptr<ParserWrapper> m_By_particle_parser; + std::unique_ptr<ParserWrapper> m_Bz_particle_parser; + // ParserWrapper for E_external on the particle + std::unique_ptr<ParserWrapper> m_Ex_particle_parser; + std::unique_ptr<ParserWrapper> m_Ey_particle_parser; + std::unique_ptr<ParserWrapper> m_Ez_particle_parser; + + protected: // Particle container types diff --git a/Source/Particles/MultiParticleContainer.cpp b/Source/Particles/MultiParticleContainer.cpp index d84bc1afa..f9a0d230b 100644 --- a/Source/Particles/MultiParticleContainer.cpp +++ b/Source/Particles/MultiParticleContainer.cpp @@ -1,3 +1,13 @@ +/* Copyright 2019-2020 Andrew Myers, Ann Almgren, Axel Huebl + * David Grote, Jean-Luc Vay, Luca Fedeli + * Mathieu Lobet, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang, Yinjian Zhao + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <MultiParticleContainer.H> #include <AMReX_Vector.H> @@ -72,6 +82,93 @@ MultiParticleContainer::ReadParameters () { ParmParse pp("particles"); + // allocating and initializing default values of external fields for particles + m_E_external_particle.resize(3); + m_B_external_particle.resize(3); + // initialize E and B fields to 0.0 + for (int idim = 0; idim < 3; ++idim) { + m_E_external_particle[idim] = 0.0; + m_B_external_particle[idim] = 0.0; + } + // default values of E_external_particle and B_external_particle + // are used to set the E and B field when "constant" or "parser" + // is not explicitly used in the input + pp.query("B_ext_particle_init_style", m_B_ext_particle_s); + std::transform(m_B_ext_particle_s.begin(), + m_B_ext_particle_s.end(), + m_B_ext_particle_s.begin(), + ::tolower); + pp.query("E_ext_particle_init_style", m_E_ext_particle_s); + std::transform(m_E_ext_particle_s.begin(), + m_E_ext_particle_s.end(), + m_E_ext_particle_s.begin(), + ::tolower); + // if the input string for B_external on particles is "constant" + // then the values for the external B on particles must + // be provided in the input file. + if (m_B_ext_particle_s == "constant") + pp.getarr("B_external_particle", m_B_external_particle); + + // if the input string for E_external on particles is "constant" + // then the values for the external E on particles must + // be provided in the input file. + if (m_E_ext_particle_s == "constant") + pp.getarr("E_external_particle", m_E_external_particle); + + // if the input string for B_ext_particle_s is + // "parse_b_ext_particle_function" then the mathematical expression + // for the Bx_, By_, Bz_external_particle_function(x,y,z) + // must be provided in the input file. + if (m_B_ext_particle_s == "parse_b_ext_particle_function") { + // store the mathematical expression as string + std::string str_Bx_ext_particle_function; + std::string str_By_ext_particle_function; + std::string str_Bz_ext_particle_function; + Store_parserString(pp, "Bx_external_particle_function(x,y,z,t)", + str_Bx_ext_particle_function); + Store_parserString(pp, "By_external_particle_function(x,y,z,t)", + str_By_ext_particle_function); + Store_parserString(pp, "Bz_external_particle_function(x,y,z,t)", + str_Bz_ext_particle_function); + + // Parser for B_external on the particle + m_Bx_particle_parser.reset(new ParserWrapper( + makeParser(str_Bx_ext_particle_function))); + m_By_particle_parser.reset(new ParserWrapper( + makeParser(str_By_ext_particle_function))); + m_Bz_particle_parser.reset(new ParserWrapper( + makeParser(str_Bz_ext_particle_function))); + + } + + // if the input string for E_ext_particle_s is + // "parse_e_ext_particle_function" then the mathematical expression + // for the Ex_, Ey_, Ez_external_particle_function(x,y,z) + // must be provided in the input file. + if (m_E_ext_particle_s == "parse_e_ext_particle_function") { + // store the mathematical expression as string + std::string str_Ex_ext_particle_function; + std::string str_Ey_ext_particle_function; + std::string str_Ez_ext_particle_function; + Store_parserString(pp, "Ex_external_particle_function(x,y,z,t)", + str_Ex_ext_particle_function); + Store_parserString(pp, "Ey_external_particle_function(x,y,z,t)", + str_Ey_ext_particle_function); + Store_parserString(pp, "Ez_external_particle_function(x,y,z,t)", + str_Ez_ext_particle_function); + // Parser for E_external on the particle + m_Ex_particle_parser.reset(new ParserWrapper( + makeParser(str_Ex_ext_particle_function))); + m_Ey_particle_parser.reset(new ParserWrapper( + makeParser(str_Ey_ext_particle_function))); + m_Ez_particle_parser.reset(new ParserWrapper( + makeParser(str_Ez_ext_particle_function))); + + } + + + + pp.query("nspecies", nspecies); BL_ASSERT(nspecies >= 0); @@ -215,14 +312,6 @@ MultiParticleContainer::EvolveES (const Vector<std::array<std::unique_ptr<MultiF } void -MultiParticleContainer::PushXES (Real dt) -{ - for (auto& pc : allcontainers) { - pc->PushXES(dt); - } -} - -void MultiParticleContainer:: DepositCharge (Vector<std::unique_ptr<MultiFab> >& rho, bool local) { diff --git a/Source/Particles/ParticleCreation/CopyParticle.H b/Source/Particles/ParticleCreation/CopyParticle.H index 5e51c5283..8b2770891 100644 --- a/Source/Particles/ParticleCreation/CopyParticle.H +++ b/Source/Particles/ParticleCreation/CopyParticle.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef COPYPARTICLE_H_ #define COPYPARTICLE_H_ diff --git a/Source/Particles/ParticleCreation/ElementaryProcess.H b/Source/Particles/ParticleCreation/ElementaryProcess.H index bd9342e46..568f73fff 100644 --- a/Source/Particles/ParticleCreation/ElementaryProcess.H +++ b/Source/Particles/ParticleCreation/ElementaryProcess.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef ELEMENTARYPROCESS_H_ #define ELEMENTARYPROCESS_H_ diff --git a/Source/Particles/ParticleCreation/TransformParticle.H b/Source/Particles/ParticleCreation/TransformParticle.H index c0158db78..eb5820e32 100644 --- a/Source/Particles/ParticleCreation/TransformParticle.H +++ b/Source/Particles/ParticleCreation/TransformParticle.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef TRANSFORMPARTICLE_H_ #define TRANSFORMPARTICLE_H_ diff --git a/Source/Particles/PhotonParticleContainer.H b/Source/Particles/PhotonParticleContainer.H index 9b688cc59..7750d5ce8 100644 --- a/Source/Particles/PhotonParticleContainer.H +++ b/Source/Particles/PhotonParticleContainer.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, David Grote, Luca Fedeli + * Maxence Thevenet, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PhotonParticleContainer_H_ #define WARPX_PhotonParticleContainer_H_ diff --git a/Source/Particles/PhotonParticleContainer.cpp b/Source/Particles/PhotonParticleContainer.cpp index c03ed00c2..ab85170ac 100644 --- a/Source/Particles/PhotonParticleContainer.cpp +++ b/Source/Particles/PhotonParticleContainer.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Luca Fedeli, Maxence Thevenet + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <limits> #include <sstream> #include <algorithm> diff --git a/Source/Particles/PhysicalParticleContainer.H b/Source/Particles/PhysicalParticleContainer.H index 0192ffb37..18a6540d5 100644 --- a/Source/Particles/PhysicalParticleContainer.H +++ b/Source/Particles/PhysicalParticleContainer.H @@ -1,3 +1,12 @@ +/* Copyright 2019-2020 Andrew Myers, Axel Huebl, David Grote + * Ligia Diana Amorim, Luca Fedeli, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PhysicalParticleContainer_H_ #define WARPX_PhysicalParticleContainer_H_ @@ -47,6 +56,26 @@ public: amrex::Real t, amrex::Real dt) override; #endif // WARPX_DO_ELECTROSTATIC + /** + * \brief Apply external E and B fields on the particles. The E and B + * fields could be defined as a constant or using a parser for reading + * in a mathematical expression. The default value for the E- and B-fields + * is (0.0,0.0,0.0). + * + * \param[in,out] Exp-Bzp pointer to fields on particles modified based + * on external E and B + * \param[in] xp,yp,zp arrays of particle positions required to compute + * mathematical expression for the external fields + * using parser. + */ + void AssignExternalFieldOnParticles ( WarpXParIter& pti, + RealVector& Exp, RealVector& Eyp, + RealVector& Ezp, RealVector& Bxp, + RealVector& Byp, RealVector& Bzp, + const amrex::Gpu::ManagedDeviceVector<amrex::ParticleReal>& xp, + const amrex::Gpu::ManagedDeviceVector<amrex::ParticleReal>& yp, + const amrex::Gpu::ManagedDeviceVector<amrex::ParticleReal>& zp, int lev); + virtual void FieldGather (int lev, const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, @@ -318,6 +347,7 @@ protected: //radiation reaction bool do_classical_radiation_reaction = false; + #ifdef WARPX_QED // A flag to enable quantum_synchrotron process for leptons bool m_do_qed_quantum_sync = false; diff --git a/Source/Particles/PhysicalParticleContainer.cpp b/Source/Particles/PhysicalParticleContainer.cpp index 94d9bc363..2891cada3 100644 --- a/Source/Particles/PhysicalParticleContainer.cpp +++ b/Source/Particles/PhysicalParticleContainer.cpp @@ -1,3 +1,13 @@ +/* Copyright 2019-2020 Andrew Myers, Aurore Blelly, Axel Huebl + * David Grote, Glenn Richardson, Jean-Luc Vay + * Ligia Diana Amorim, Luca Fedeli, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <limits> #include <sstream> @@ -40,6 +50,7 @@ PhysicalParticleContainer::PhysicalParticleContainer (AmrCore* amr_core, int isp pp.query("do_splitting", do_splitting); pp.query("split_type", split_type); pp.query("do_not_deposit", do_not_deposit); + pp.query("do_not_push", do_not_push); pp.query("do_continuous_injection", do_continuous_injection); pp.query("initialize_self_fields", initialize_self_fields); @@ -450,7 +461,8 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) overlap_box.setBig( dir, int( std::round((overlap_realbox.hi(dir)-overlap_realbox.lo(dir)) /dx[dir] )) - 1); - shifted[dir] = std::round((overlap_realbox.lo(dir)-problo[dir])/dx[dir]); + shifted[dir] = + static_cast<int>(std::round((overlap_realbox.lo(dir)-problo[dir])/dx[dir])); // shifted is exact in non-moving-window direction. That's all we care. } if (no_overlap == 1) { @@ -492,7 +504,9 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) // Update NextID to include particles created in this function int pid; +#ifdef _OPENMP #pragma omp critical (add_plasma_nextid) +#endif { pid = ParticleType::NextID(); ParticleType::NextID(pid+max_new_particles); @@ -580,7 +594,8 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) IntVect iv = overlap_box.atOffset(cellid); - const XDim3 r = inj_pos->getPositionUnitBox(i_part, fac); + const XDim3 r = + inj_pos->getPositionUnitBox(i_part, static_cast<int>(fac)); #if (AMREX_SPACEDIM == 3) Real x = overlap_corner[0] + (iv[0]+r.x)*dx[0]; Real y = overlap_corner[1] + (iv[1]+r.y)*dx[1]; @@ -962,6 +977,79 @@ PhysicalParticleContainer::EvolveES (const Vector<std::array<std::unique_ptr<Mul #endif // WARPX_DO_ELECTROSTATIC void +PhysicalParticleContainer::AssignExternalFieldOnParticles(WarpXParIter& pti, + RealVector& Exp, RealVector& Eyp, RealVector& Ezp, + RealVector& Bxp, RealVector& Byp, RealVector& Bzp, + const Gpu::ManagedDeviceVector<ParticleReal>& xp, + const Gpu::ManagedDeviceVector<ParticleReal>& yp, + const Gpu::ManagedDeviceVector<ParticleReal>& zp, int lev) +{ + const long np = pti.numParticles(); + /// get WarpX class object + auto & warpx = WarpX::GetInstance(); + /// get MultiParticleContainer class object + auto & mypc = warpx.GetPartContainer(); + if (mypc.m_E_ext_particle_s=="constant" || + mypc.m_E_ext_particle_s=="default") { + Exp.assign(np,mypc.m_E_external_particle[0]); + Eyp.assign(np,mypc.m_E_external_particle[1]); + Ezp.assign(np,mypc.m_E_external_particle[2]); + } + if (mypc.m_B_ext_particle_s=="constant" || + mypc.m_B_ext_particle_s=="default") { + Bxp.assign(np,mypc.m_B_external_particle[0]); + Byp.assign(np,mypc.m_B_external_particle[1]); + Bzp.assign(np,mypc.m_B_external_particle[2]); + } + if (mypc.m_E_ext_particle_s=="parse_e_ext_particle_function") { + const Real* const AMREX_RESTRICT xp_data = xp.dataPtr(); + const Real* const AMREX_RESTRICT yp_data = yp.dataPtr(); + const Real* const AMREX_RESTRICT zp_data = zp.dataPtr(); + Real* const AMREX_RESTRICT Exp_data = Exp.dataPtr(); + Real* const AMREX_RESTRICT Eyp_data = Eyp.dataPtr(); + Real* const AMREX_RESTRICT Ezp_data = Ezp.dataPtr(); + ParserWrapper *xfield_partparser = mypc.m_Ex_particle_parser.get(); + ParserWrapper *yfield_partparser = mypc.m_Ey_particle_parser.get(); + ParserWrapper *zfield_partparser = mypc.m_Ez_particle_parser.get(); + Real time = warpx.gett_new(lev); + amrex::ParallelFor(pti.numParticles(), + [=] AMREX_GPU_DEVICE (long i) { + Exp_data[i] = xfield_partparser->getField(xp_data[i],yp_data[i],zp_data[i],time); + Eyp_data[i] = yfield_partparser->getField(xp_data[i],yp_data[i],zp_data[i],time); + Ezp_data[i] = zfield_partparser->getField(xp_data[i],yp_data[i],zp_data[i],time); + }, + /* To allocate shared memory for the GPU threads. */ + /* But, for now only 4 doubles (x,y,z,t) are allocated. */ + amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double) * 4 + ); + } + if (mypc.m_B_ext_particle_s=="parse_b_ext_particle_function") { + const Real* const AMREX_RESTRICT xp_data = xp.dataPtr(); + const Real* const AMREX_RESTRICT yp_data = yp.dataPtr(); + const Real* const AMREX_RESTRICT zp_data = zp.dataPtr(); + Real* const AMREX_RESTRICT Bxp_data = Bxp.dataPtr(); + Real* const AMREX_RESTRICT Byp_data = Byp.dataPtr(); + Real* const AMREX_RESTRICT Bzp_data = Bzp.dataPtr(); + ParserWrapper *xfield_partparser = mypc.m_Bx_particle_parser.get(); + ParserWrapper *yfield_partparser = mypc.m_By_particle_parser.get(); + ParserWrapper *zfield_partparser = mypc.m_Bz_particle_parser.get(); + Real time = warpx.gett_new(lev); + amrex::ParallelFor(pti.numParticles(), + [=] AMREX_GPU_DEVICE (long i) { + Bxp_data[i] = xfield_partparser->getField(xp_data[i],yp_data[i],zp_data[i],time); + Byp_data[i] = yfield_partparser->getField(xp_data[i],yp_data[i],zp_data[i],time); + Bzp_data[i] = zfield_partparser->getField(xp_data[i],yp_data[i],zp_data[i],time); + }, + /* To allocate shared memory for the GPU threads. */ + /* But, for now only 4 doubles (x,y,z,t) are allocated. */ + amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double) * 4 + ); + } +} + + + +void PhysicalParticleContainer::FieldGather (int lev, const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, @@ -1010,13 +1098,6 @@ PhysicalParticleContainer::FieldGather (int lev, const FArrayBox& byfab = By[pti]; const FArrayBox& bzfab = Bz[pti]; - Exp.assign(np,WarpX::E_external_particle[0]); - Eyp.assign(np,WarpX::E_external_particle[1]); - Ezp.assign(np,WarpX::E_external_particle[2]); - Bxp.assign(np,WarpX::B_external_particle[0]); - Byp.assign(np,WarpX::B_external_particle[1]); - Bzp.assign(np,WarpX::B_external_particle[2]); - // // copy data from particle container to temp arrays // @@ -1041,6 +1122,9 @@ PhysicalParticleContainer::FieldGather (int lev, costarr(i,j,k) += wt; }); } + // synchronize avoids cudaStreams from over-writing the temporary arrays used to + // store positions + Gpu::synchronize(); } } } @@ -1147,14 +1231,6 @@ PhysicalParticleContainer::Evolve (int lev, exfab, eyfab, ezfab, bxfab, byfab, bzfab); } - Exp.assign(np,WarpX::E_external_particle[0]); - Eyp.assign(np,WarpX::E_external_particle[1]); - Ezp.assign(np,WarpX::E_external_particle[2]); - - Bxp.assign(np,WarpX::B_external_particle[0]); - Byp.assign(np,WarpX::B_external_particle[1]); - Bzp.assign(np,WarpX::B_external_particle[2]); - // Determine which particles deposit/gather in the buffer, and // which particles deposit/gather in the fine patch long nfine_current = np; @@ -1614,10 +1690,9 @@ PhysicalParticleContainer::PushPX(WarpXParIter& pti, #ifdef WARPX_QED - auto t_chi_max = m_shr_p_qs_engine->get_ref_ctrl().chi_part_min; - if(do_classical_radiation_reaction){ if(m_do_qed_quantum_sync){ + const auto t_chi_max = m_shr_p_qs_engine->get_ref_ctrl().chi_part_min; amrex::ParallelFor( pti.numParticles(), [=] AMREX_GPU_DEVICE (long i) { @@ -1795,14 +1870,6 @@ PhysicalParticleContainer::PushP (int lev, Real dt, const FArrayBox& byfab = By[pti]; const FArrayBox& bzfab = Bz[pti]; - Exp.assign(np,WarpX::E_external_particle[0]); - Eyp.assign(np,WarpX::E_external_particle[1]); - Ezp.assign(np,WarpX::E_external_particle[2]); - - Bxp.assign(np,WarpX::B_external_particle[0]); - Byp.assign(np,WarpX::B_external_particle[1]); - Bzp.assign(np,WarpX::B_external_particle[2]); - // // copy data from particle container to temp arrays // @@ -1976,7 +2043,6 @@ void PhysicalParticleContainer::GetParticleSlice(const int direction, const Real #endif for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) { - int counter_for_ParticleCopy = 0; const Box& box = pti.validbox(); auto index = std::make_pair(pti.index(), pti.LocalTileIndex()); const RealBox tile_real_box(box, dx, plo); @@ -2172,9 +2238,16 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, AMREX_ALWAYS_ASSERT_WITH_MESSAGE((gather_lev==(lev-1)) || (gather_lev==(lev )), "Gather buffers only work for lev-1"); - // If no particles, do not do anything if (np_to_gather == 0) return; + + // initializing the field value to the externally applied field before + // gathering fields from the grid to the particles. + AssignExternalFieldOnParticles(pti, Exp, Eyp, Ezp, Bxp, Byp, Bzp, + m_xp[thread_num], m_yp[thread_num], + m_zp[thread_num], lev); + + // Get cell size on gather_lev const std::array<Real,3>& dx = WarpX::CellSize(std::max(gather_lev,0)); @@ -2420,4 +2493,5 @@ set_quantum_sync_engine_ptr(std::shared_ptr<QuantumSynchrotronEngine> ptr) { m_shr_p_qs_engine = ptr; } + #endif diff --git a/Source/Particles/Pusher/GetAndSetPosition.H b/Source/Particles/Pusher/GetAndSetPosition.H index ae73a74e4..7180f3c55 100644 --- a/Source/Particles/Pusher/GetAndSetPosition.H +++ b/Source/Particles/Pusher/GetAndSetPosition.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_PUSHER_GETANDSETPOSITION_H_ #define WARPX_PARTICLES_PUSHER_GETANDSETPOSITION_H_ diff --git a/Source/Particles/Pusher/UpdateMomentumBoris.H b/Source/Particles/Pusher/UpdateMomentumBoris.H index 160f38ade..13582d7e0 100644 --- a/Source/Particles/Pusher/UpdateMomentumBoris.H +++ b/Source/Particles/Pusher/UpdateMomentumBoris.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_BORIS_H_ #define WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_BORIS_H_ diff --git a/Source/Particles/Pusher/UpdateMomentumBorisWithRadiationReaction.H b/Source/Particles/Pusher/UpdateMomentumBorisWithRadiationReaction.H index 0bc0f5d01..d8489e23e 100644 --- a/Source/Particles/Pusher/UpdateMomentumBorisWithRadiationReaction.H +++ b/Source/Particles/Pusher/UpdateMomentumBorisWithRadiationReaction.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_BORIS_WITHRR_H_ #define WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_BORIS_WITHRR_H_ diff --git a/Source/Particles/Pusher/UpdateMomentumHigueraCary.H b/Source/Particles/Pusher/UpdateMomentumHigueraCary.H index 51d7fd620..de2436ce2 100644 --- a/Source/Particles/Pusher/UpdateMomentumHigueraCary.H +++ b/Source/Particles/Pusher/UpdateMomentumHigueraCary.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_HIGUERACARY_H_ #define WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_HIGUERACARY_H_ diff --git a/Source/Particles/Pusher/UpdateMomentumVay.H b/Source/Particles/Pusher/UpdateMomentumVay.H index f7ec79d89..846d59310 100644 --- a/Source/Particles/Pusher/UpdateMomentumVay.H +++ b/Source/Particles/Pusher/UpdateMomentumVay.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_VAY_H_ #define WARPX_PARTICLES_PUSHER_UPDATEMOMENTUM_VAY_H_ diff --git a/Source/Particles/Pusher/UpdatePosition.H b/Source/Particles/Pusher/UpdatePosition.H index 9943128f1..7f86a106d 100644 --- a/Source/Particles/Pusher/UpdatePosition.H +++ b/Source/Particles/Pusher/UpdatePosition.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_PUSHER_UPDATEPOSITION_H_ #define WARPX_PARTICLES_PUSHER_UPDATEPOSITION_H_ diff --git a/Source/Particles/Pusher/UpdatePositionPhoton.H b/Source/Particles/Pusher/UpdatePositionPhoton.H index 1a0bd114f..3a28e87a1 100644 --- a/Source/Particles/Pusher/UpdatePositionPhoton.H +++ b/Source/Particles/Pusher/UpdatePositionPhoton.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Luca Fedeli, Maxence Thevenet + * Remi Lehe, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_PUSHER_UPDATEPOSITIONPHOTON_H_ #define WARPX_PARTICLES_PUSHER_UPDATEPOSITIONPHOTON_H_ diff --git a/Source/Particles/RigidInjectedParticleContainer.H b/Source/Particles/RigidInjectedParticleContainer.H index fecb9c48e..5e5749093 100644 --- a/Source/Particles/RigidInjectedParticleContainer.H +++ b/Source/Particles/RigidInjectedParticleContainer.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, David Grote, Maxence Thevenet + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_RigidInjectedParticleContainer_H_ #define WARPX_RigidInjectedParticleContainer_H_ diff --git a/Source/Particles/RigidInjectedParticleContainer.cpp b/Source/Particles/RigidInjectedParticleContainer.cpp index bee71fba1..f9db682d7 100644 --- a/Source/Particles/RigidInjectedParticleContainer.cpp +++ b/Source/Particles/RigidInjectedParticleContainer.cpp @@ -1,3 +1,12 @@ +/* Copyright 2019-2020 Andrew Myers, David Grote, Glenn Richardson + * Ligia Diana Amorim, Luca Fedeli, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <limits> #include <sstream> #include <algorithm> @@ -392,13 +401,6 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, const FArrayBox& byfab = By[pti]; const FArrayBox& bzfab = Bz[pti]; - Exp.assign(np,WarpX::E_external_particle[0]); - Eyp.assign(np,WarpX::E_external_particle[1]); - Ezp.assign(np,WarpX::E_external_particle[2]); - Bxp.assign(np,WarpX::B_external_particle[0]); - Byp.assign(np,WarpX::B_external_particle[1]); - Bzp.assign(np,WarpX::B_external_particle[2]); - // // copy data from particle container to temp arrays // diff --git a/Source/Particles/ShapeFactors.H b/Source/Particles/ShapeFactors.H index be79a4871..dd36fb31f 100644 --- a/Source/Particles/ShapeFactors.H +++ b/Source/Particles/ShapeFactors.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef SHAPEFACTORS_H_ #define SHAPEFACTORS_H_ diff --git a/Source/Particles/Sorting/Partition.cpp b/Source/Particles/Sorting/Partition.cpp index e88af017f..c25c24d5d 100644 --- a/Source/Particles/Sorting/Partition.cpp +++ b/Source/Particles/Sorting/Partition.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <SortingUtils.H> #include <PhysicalParticleContainer.H> #include <WarpX.H> diff --git a/Source/Particles/Sorting/SortingUtils.H b/Source/Particles/Sorting/SortingUtils.H index 35bc059aa..f0e991367 100644 --- a/Source/Particles/Sorting/SortingUtils.H +++ b/Source/Particles/Sorting/SortingUtils.H @@ -1,12 +1,16 @@ +/* Copyright 2019-2020 Andrew Myers, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PARTICLES_SORTING_SORTINGUTILS_H_ #define WARPX_PARTICLES_SORTING_SORTINGUTILS_H_ #include <WarpXParticleContainer.H> #include <AMReX_Gpu.H> -#ifdef AMREX_USE_GPU - #include <thrust/partition.h> - #include <thrust/distance.h> -#endif +#include <AMReX_Partition.H> /** \brief Fill the elements of the input vector with consecutive integer, * starting from 0 @@ -16,8 +20,10 @@ void fillWithConsecutiveIntegers( amrex::Gpu::DeviceVector<long>& v ) { #ifdef AMREX_USE_GPU - // On GPU: Use thrust - thrust::sequence( v.begin(), v.end() ); + // On GPU: Use amrex + auto data = v.data(); + auto N = v.size(); + AMREX_FOR_1D( N, i, data[i] = i;); #else // On CPU: Use std library std::iota( v.begin(), v.end(), 0L ); @@ -35,17 +41,18 @@ void fillWithConsecutiveIntegers( amrex::Gpu::DeviceVector<long>& v ) */ template< typename ForwardIterator > ForwardIterator stablePartition(ForwardIterator const index_begin, - ForwardIterator const index_end, - amrex::Gpu::DeviceVector<int> const& predicate) + ForwardIterator const index_end, + amrex::Gpu::DeviceVector<int> const& predicate) { #ifdef AMREX_USE_GPU - // On GPU: Use thrust + // On GPU: Use amrex int const* AMREX_RESTRICT predicate_ptr = predicate.dataPtr(); - ForwardIterator const sep = thrust::stable_partition( - thrust::cuda::par(amrex::Gpu::The_ThrustCachedAllocator()), - index_begin, index_end, - [predicate_ptr] AMREX_GPU_DEVICE (long i) { return predicate_ptr[i]; } - ); + int N = static_cast<int>(std::distance(index_begin, index_end)); + auto num_true = amrex::StablePartition(&(*index_begin), N, + [predicate_ptr] AMREX_GPU_DEVICE (long i) { return predicate_ptr[i]; }); + + ForwardIterator sep = index_begin; + std::advance(sep, num_true); #else // On CPU: Use std library ForwardIterator const sep = std::stable_partition( @@ -66,12 +73,7 @@ template< typename ForwardIterator > int iteratorDistance(ForwardIterator const first, ForwardIterator const last) { -#ifdef AMREX_USE_GPU - // On GPU: Use thrust - return thrust::distance( first, last ); -#else return std::distance( first, last ); -#endif } /** \brief Functor that fills the elements of the particle array `inexflag` diff --git a/Source/Particles/WarpXParticleContainer.H b/Source/Particles/WarpXParticleContainer.H index 33c10e8de..b89ea83fc 100644 --- a/Source/Particles/WarpXParticleContainer.H +++ b/Source/Particles/WarpXParticleContainer.H @@ -1,3 +1,12 @@ +/* Copyright 2019-2020 Andrew Myers, Axel Huebl, David Grote + * Jean-Luc Vay, Junmin Gu, Luca Fedeli + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * Weiqun Zhang, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_WarpXParticleContainer_H_ #define WARPX_WarpXParticleContainer_H_ @@ -193,15 +202,6 @@ public: /// This pushes the particle positions by one half time step. /// It is used to desynchronize the particles after initializaton /// or when restarting from a checkpoint. - /// This is the electrostatic version of the particle push. - /// - void PushXES (amrex::Real dt); - - /// - /// This pushes the particle positions by one half time step. - /// It is used to desynchronize the particles after initializaton - /// or when restarting from a checkpoint. - /// This is the electromagnetic version of the particle push. /// void PushX ( amrex::Real dt); void PushX (int lev, amrex::Real dt); @@ -339,8 +339,8 @@ protected: //! instead of gathering fields from the finest patch level, gather from the coarsest bool m_gather_from_main_grid = false; - static int do_not_push; - static int do_not_deposit; + int do_not_push = 0; + int do_not_deposit = 0; // Whether to allow particles outside of the simulation domain to be // initialized when they enter the domain. diff --git a/Source/Particles/WarpXParticleContainer.cpp b/Source/Particles/WarpXParticleContainer.cpp index 15a6cff9b..7628cfd85 100644 --- a/Source/Particles/WarpXParticleContainer.cpp +++ b/Source/Particles/WarpXParticleContainer.cpp @@ -1,3 +1,12 @@ +/* Copyright 2019-2020 Andrew Myers, Axel Huebl, David Grote + * Jean-Luc Vay, Luca Fedeli, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * Yinjian Zhao, levinem + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <limits> #include <MultiParticleContainer.H> @@ -16,9 +25,6 @@ using namespace amrex; -int WarpXParticleContainer::do_not_push = 0; -int WarpXParticleContainer::do_not_deposit = 0; - WarpXParIter::WarpXParIter (ContainerType& pc, int level) : ParIter(pc, level, MFItInfo().SetDynamic(WarpX::do_dynamic_scheduling)) { @@ -113,6 +119,7 @@ WarpXParticleContainer::WarpXParticleContainer (AmrCore* amr_core, int ispecies) m_xp.resize(num_threads); m_yp.resize(num_threads); m_zp.resize(num_threads); + } void @@ -129,7 +136,6 @@ WarpXParticleContainer::ReadParameters () do_tiling = true; #endif pp.query("do_tiling", do_tiling); - pp.query("do_not_push", do_not_push); initialized = true; } @@ -737,37 +743,6 @@ Real WarpXParticleContainer::maxParticleVelocity(bool local) { } void -WarpXParticleContainer::PushXES (Real dt) -{ - BL_PROFILE("WPC::PushXES()"); - - const int num_levels = finestLevel() + 1; - - for (int lev = 0; lev < num_levels; ++lev) { - const auto& gm = m_gdb->Geom(lev); - const RealBox& prob_domain = gm.ProbDomain(); - for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) { - auto& particles = pti.GetArrayOfStructs(); - int nstride = particles.dataShape().first; - const long np = pti.numParticles(); - - auto& attribs = pti.GetAttribs(); - auto& uxp = attribs[PIdx::ux]; - auto& uyp = attribs[PIdx::uy]; - auto& uzp = attribs[PIdx::uz]; - - WRPX_PUSH_LEAPFROG_POSITIONS(particles.dataPtr(), nstride, np, - uxp.dataPtr(), uyp.dataPtr(), -#if AMREX_SPACEDIM == 3 - uzp.dataPtr(), -#endif - &dt, - prob_domain.lo(), prob_domain.hi()); - } - } -} - -void WarpXParticleContainer::PushX (amrex::Real dt) { const int nLevels = finestLevel(); diff --git a/Source/Particles/interpolate_cic.F90 b/Source/Particles/interpolate_cic.F90 index 3eb361d2f..97bd8c153 100644 --- a/Source/Particles/interpolate_cic.F90 +++ b/Source/Particles/interpolate_cic.F90 @@ -1,3 +1,9 @@ +! Copyright 2019 Maxence Thevenet, Weiqun Zhang +! +! This file is part of WarpX. +! +! License: BSD-3-Clause-LBNL + module warpx_ES_interpolate_cic use iso_c_binding diff --git a/Source/Particles/push_particles_ES.F90 b/Source/Particles/push_particles_ES.F90 index b84f48d5f..a22ee5a62 100644 --- a/Source/Particles/push_particles_ES.F90 +++ b/Source/Particles/push_particles_ES.F90 @@ -1,3 +1,9 @@ +! Copyright 2019 Maxence Thevenet, Weiqun Zhang +! +! This file is part of WarpX. +! +! License: BSD-3-Clause-LBNL + module warpx_ES_push_particles use iso_c_binding @@ -145,114 +151,4 @@ contains end subroutine warpx_push_leapfrog_2d - -! -! This routine advances the particle positions using the current -! velocity. This is needed to desynchronize the particle positions -! from the velocities after particle initialization. -! -! Arguments: -! particles : a pointer to the particle array-of-structs -! ns : the stride length of particle struct (the size of the struct in number of reals) -! np : the number of particles -! xx_p : the electric field in the x-direction at the particle positions -! vy_p : the electric field in the y-direction at the particle positions -! vz_p : the electric field in the z-direction at the particle positions -! dt : the time step -! prob_lo : the left-hand corner of the problem domain -! prob_hi : the right-hand corner of the problem domain -! - subroutine warpx_push_leapfrog_positions_3d(particles, ns, np, & - vx_p, vy_p, vz_p, dt, & - prob_lo, prob_hi) & - bind(c,name='warpx_push_leapfrog_positions_3d') - integer, value, intent(in) :: ns, np - real(amrex_particle_real), intent(inout) :: particles(ns,np) - real(amrex_particle_real), intent(inout) :: vx_p(np), vy_p(np), vz_p(np) - real(amrex_real), intent(in) :: dt - real(amrex_real), intent(in) :: prob_lo(3), prob_hi(3) - - integer n - - do n = 1, np - - particles(1, n) = particles(1, n) + dt * vx_p(n) - particles(2, n) = particles(2, n) + dt * vy_p(n) - particles(3, n) = particles(3, n) + dt * vz_p(n) - -! bounce off the walls in the x... - do while (particles(1, n) .lt. prob_lo(1) .or. particles(1, n) .gt. prob_hi(1)) - if (particles(1, n) .lt. prob_lo(1)) then - particles(1, n) = 2.d0*prob_lo(1) - particles(1, n) - else - particles(1, n) = 2.d0*prob_hi(1) - particles(1, n) - end if - vx_p(n) = -vx_p(n) - end do - -! ... y... - do while (particles(2, n) .lt. prob_lo(2) .or. particles(2, n) .gt. prob_hi(2)) - if (particles(2, n) .lt. prob_lo(2)) then - particles(2, n) = 2.d0*prob_lo(2) - particles(2, n) - else - particles(2, n) = 2.d0*prob_hi(2) - particles(2, n) - end if - vy_p(n) = -vy_p(n) - end do - -! ... and z directions - do while (particles(3, n) .lt. prob_lo(3) .or. particles(3, n) .gt. prob_hi(3)) - if (particles(3, n) .lt. prob_lo(3)) then - particles(3, n) = 2.d0*prob_lo(3) - particles(3, n) - else - particles(3, n) = 2.d0*prob_hi(3) - particles(3, n) - end if - vz_p(n) = -vz_p(n) - end do - - end do - - end subroutine warpx_push_leapfrog_positions_3d - - subroutine warpx_push_leapfrog_positions_2d(particles, ns, np, & - vx_p, vy_p, dt, & - prob_lo, prob_hi) & - bind(c,name='warpx_push_leapfrog_positions_2d') - integer, value, intent(in) :: ns, np - real(amrex_particle_real), intent(inout) :: particles(ns,np) - real(amrex_particle_real), intent(inout) :: vx_p(np), vy_p(np) - real(amrex_real), intent(in) :: dt - real(amrex_real), intent(in) :: prob_lo(2), prob_hi(2) - - integer n - - do n = 1, np - - particles(1, n) = particles(1, n) + dt * vx_p(n) - particles(2, n) = particles(2, n) + dt * vy_p(n) - -! bounce off the walls in the x... - do while (particles(1, n) .lt. prob_lo(1) .or. particles(1, n) .gt. prob_hi(1)) - if (particles(1, n) .lt. prob_lo(1)) then - particles(1, n) = 2.d0*prob_lo(1) - particles(1, n) - else - particles(1, n) = 2.d0*prob_hi(1) - particles(1, n) - end if - vx_p(n) = -vx_p(n) - end do - -! ... y... - do while (particles(2, n) .lt. prob_lo(2) .or. particles(2, n) .gt. prob_hi(2)) - if (particles(2, n) .lt. prob_lo(2)) then - particles(2, n) = 2.d0*prob_lo(2) - particles(2, n) - else - particles(2, n) = 2.d0*prob_hi(2) - particles(2, n) - end if - vy_p(n) = -vy_p(n) - end do - - end do - - end subroutine warpx_push_leapfrog_positions_2d - end module warpx_ES_push_particles diff --git a/Source/Python/WarpXWrappers.cpp b/Source/Python/WarpXWrappers.cpp index 54dfe1955..bf5377dae 100644 --- a/Source/Python/WarpXWrappers.cpp +++ b/Source/Python/WarpXWrappers.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019 Andrew Myers, Axel Huebl, David Grote + * Luca Fedeli, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpXWrappers.h> #include <WarpXParticleContainer.H> @@ -346,11 +354,11 @@ extern "C" } void warpx_FillBoundaryE () { WarpX& warpx = WarpX::GetInstance(); - warpx.FillBoundaryE (); + warpx.FillBoundaryE (warpx.getngE()); } void warpx_FillBoundaryB () { WarpX& warpx = WarpX::GetInstance(); - warpx.FillBoundaryB (); + warpx.FillBoundaryB (warpx.getngE()); } void warpx_SyncCurrent () { WarpX& warpx = WarpX::GetInstance(); @@ -404,6 +412,11 @@ extern "C" return warpx.plotInt (); } + int warpx_openpmdInt () { + WarpX& warpx = WarpX::GetInstance(); + return warpx.openpmdInt (); + } + void warpx_WriteCheckPointFile () { WarpX& warpx = WarpX::GetInstance(); warpx.WriteCheckPointFile (); @@ -412,6 +425,10 @@ extern "C" WarpX& warpx = WarpX::GetInstance(); warpx.WritePlotFile (); } + void warpx_WriteOpenPMDFile () { + WarpX& warpx = WarpX::GetInstance(); + warpx.WriteOpenPMDFile (); + } int warpx_finestLevel () { WarpX& warpx = WarpX::GetInstance(); diff --git a/Source/Python/WarpXWrappers.h b/Source/Python/WarpXWrappers.h index 4de885b88..53309ad93 100644 --- a/Source/Python/WarpXWrappers.h +++ b/Source/Python/WarpXWrappers.h @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, David Grote, Maxence Thevenet + * Remi Lehe, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_WRAPPERS_H_ #define WARPX_WRAPPERS_H_ @@ -95,9 +102,11 @@ extern "C" { int warpx_checkInt (); int warpx_plotInt (); + int warpx_openpmdInt (); void warpx_WriteCheckPointFile (); void warpx_WritePlotFile (); + void warpx_WriteOpenPMDFile (); int warpx_finestLevel (); diff --git a/Source/Python/WarpX_py.H b/Source/Python/WarpX_py.H index d8cf22155..b6a813bfc 100644 --- a/Source/Python/WarpX_py.H +++ b/Source/Python/WarpX_py.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_PY_H_ #define WARPX_PY_H_ diff --git a/Source/Python/WarpX_py.cpp b/Source/Python/WarpX_py.cpp index 276d637d7..4ca06b644 100644 --- a/Source/Python/WarpX_py.cpp +++ b/Source/Python/WarpX_py.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX_py.H> extern "C" { diff --git a/Source/QED/BreitWheelerDummyTable.H b/Source/QED/BreitWheelerDummyTable.H index e03f9d20b..95f65923e 100644 --- a/Source/QED/BreitWheelerDummyTable.H +++ b/Source/QED/BreitWheelerDummyTable.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_breit_wheeler_dummy_tables_h_ #define WARPX_breit_wheeler_dummy_tables_h_ diff --git a/Source/QED/BreitWheelerEngineInnards.H b/Source/QED/BreitWheelerEngineInnards.H index 640cdfa94..d6c644aa3 100644 --- a/Source/QED/BreitWheelerEngineInnards.H +++ b/Source/QED/BreitWheelerEngineInnards.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_breit_wheeler_engine_innards_h_ #define WARPX_breit_wheeler_engine_innards_h_ diff --git a/Source/QED/BreitWheelerEngineTableBuilder.H b/Source/QED/BreitWheelerEngineTableBuilder.H index e30b82208..98b0b17a4 100644 --- a/Source/QED/BreitWheelerEngineTableBuilder.H +++ b/Source/QED/BreitWheelerEngineTableBuilder.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_breit_wheeler_engine_table_builder_h_ #define WARPX_breit_wheeler_engine_table_builder_h_ diff --git a/Source/QED/BreitWheelerEngineTableBuilder.cpp b/Source/QED/BreitWheelerEngineTableBuilder.cpp index 3326d5b59..7cb41f7ea 100644 --- a/Source/QED/BreitWheelerEngineTableBuilder.cpp +++ b/Source/QED/BreitWheelerEngineTableBuilder.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "BreitWheelerEngineTableBuilder.H" //Include the full Breit Wheeler engine with table generation support diff --git a/Source/QED/BreitWheelerEngineWrapper.H b/Source/QED/BreitWheelerEngineWrapper.H index 369c64375..102d15efa 100644 --- a/Source/QED/BreitWheelerEngineWrapper.H +++ b/Source/QED/BreitWheelerEngineWrapper.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_breit_wheeler_engine_wrapper_h_ #define WARPX_breit_wheeler_engine_wrapper_h_ diff --git a/Source/QED/BreitWheelerEngineWrapper.cpp b/Source/QED/BreitWheelerEngineWrapper.cpp index c963d44d1..e1eb55714 100644 --- a/Source/QED/BreitWheelerEngineWrapper.cpp +++ b/Source/QED/BreitWheelerEngineWrapper.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "BreitWheelerEngineWrapper.H" #include "QedTableParserHelperFunctions.H" diff --git a/Source/QED/QedChiFunctions.H b/Source/QED/QedChiFunctions.H index dd8ffac0e..bdeb6df57 100644 --- a/Source/QED/QedChiFunctions.H +++ b/Source/QED/QedChiFunctions.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_amrex_qed_chi_functions_h_ #define WARPX_amrex_qed_chi_functions_h_ diff --git a/Source/QED/QedTableParserHelperFunctions.H b/Source/QED/QedTableParserHelperFunctions.H index 528613727..dd66f626f 100644 --- a/Source/QED/QedTableParserHelperFunctions.H +++ b/Source/QED/QedTableParserHelperFunctions.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_amrex_qed_table_parser_helper_functions_h_ #define WARPX_amrex_qed_table_parser_helper_functions_h_ diff --git a/Source/QED/QedWrapperCommons.H b/Source/QED/QedWrapperCommons.H index 210e7247e..1d4500a81 100644 --- a/Source/QED/QedWrapperCommons.H +++ b/Source/QED/QedWrapperCommons.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_amrex_qed_wrapper_commons_h_ #define WARPX_amrex_qed_wrapper_commons_h_ diff --git a/Source/QED/QuantumSyncDummyTable.H b/Source/QED/QuantumSyncDummyTable.H index 587e8b546..f34c521a3 100644 --- a/Source/QED/QuantumSyncDummyTable.H +++ b/Source/QED/QuantumSyncDummyTable.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_quantum_sync_dummy_tables_h_ #define WARPX_quantum_sync_dummy_tables_h_ diff --git a/Source/QED/QuantumSyncEngineInnards.H b/Source/QED/QuantumSyncEngineInnards.H index 6206b103a..64e67690a 100644 --- a/Source/QED/QuantumSyncEngineInnards.H +++ b/Source/QED/QuantumSyncEngineInnards.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_quantum_sync_engine_innards_h_ #define WARPX_quantum_sync_engine_innards_h_ diff --git a/Source/QED/QuantumSyncEngineTableBuilder.H b/Source/QED/QuantumSyncEngineTableBuilder.H index e70f5d02f..16be2d5eb 100644 --- a/Source/QED/QuantumSyncEngineTableBuilder.H +++ b/Source/QED/QuantumSyncEngineTableBuilder.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_quantum_sync_engine_table_builder_h_ #define WARPX_quantum_sync_engine_table_builder_h_ diff --git a/Source/QED/QuantumSyncEngineTableBuilder.cpp b/Source/QED/QuantumSyncEngineTableBuilder.cpp index 51c3720f2..c4e500122 100644 --- a/Source/QED/QuantumSyncEngineTableBuilder.cpp +++ b/Source/QED/QuantumSyncEngineTableBuilder.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "QuantumSyncEngineTableBuilder.H" //Include the full Quantum Synchrotron engine with table generation support diff --git a/Source/QED/QuantumSyncEngineWrapper.H b/Source/QED/QuantumSyncEngineWrapper.H index df0bdc5f5..e1e3d94eb 100644 --- a/Source/QED/QuantumSyncEngineWrapper.H +++ b/Source/QED/QuantumSyncEngineWrapper.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_quantum_sync_engine_wrapper_h_ #define WARPX_quantum_sync_engine_wrapper_h_ diff --git a/Source/QED/QuantumSyncEngineWrapper.cpp b/Source/QED/QuantumSyncEngineWrapper.cpp index ffafec761..b185251d8 100644 --- a/Source/QED/QuantumSyncEngineWrapper.cpp +++ b/Source/QED/QuantumSyncEngineWrapper.cpp @@ -1,3 +1,9 @@ +/* Copyright 2019 Luca Fedeli, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include "QuantumSyncEngineWrapper.H" #include "QedTableParserHelperFunctions.H" diff --git a/Source/Utils/IonizationEnergiesTable.H b/Source/Utils/IonizationEnergiesTable.H index 47b71e4f1..f910fa4fe 100644 --- a/Source/Utils/IonizationEnergiesTable.H +++ b/Source/Utils/IonizationEnergiesTable.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ // This script was automatically generated! // Edit dev/Source/Utils/write_atomic_data_cpp.py instead! #ifndef WARPX_IONIZATION_TABLE_H_ diff --git a/Source/Utils/NCIGodfreyTables.H b/Source/Utils/NCIGodfreyTables.H index 708215c77..0403b10c3 100644 --- a/Source/Utils/NCIGodfreyTables.H +++ b/Source/Utils/NCIGodfreyTables.H @@ -1,3 +1,9 @@ +/* Copyright 2019 Maxence Thevenet + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <AMReX_AmrCore.H> #ifndef WARPX_GODFREY_COEFF_TABLE_H_ diff --git a/Source/Utils/WarpXAlgorithmSelection.H b/Source/Utils/WarpXAlgorithmSelection.H index 7d26e7af5..919428704 100644 --- a/Source/Utils/WarpXAlgorithmSelection.H +++ b/Source/Utils/WarpXAlgorithmSelection.H @@ -1,3 +1,10 @@ +/* Copyright 2019 David Grote, Luca Fedeli, Remi Lehe + * Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef UTILS_WARPXALGORITHMSELECTION_H_ #define UTILS_WARPXALGORITHMSELECTION_H_ diff --git a/Source/Utils/WarpXAlgorithmSelection.cpp b/Source/Utils/WarpXAlgorithmSelection.cpp index 08272089e..f6e2405a9 100644 --- a/Source/Utils/WarpXAlgorithmSelection.cpp +++ b/Source/Utils/WarpXAlgorithmSelection.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019-2020 Axel Huebl, David Grote, Luca Fedeli + * Remi Lehe, Weiqun Zhang, Yinjian Zhao + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpXAlgorithmSelection.H> #include <map> diff --git a/Source/Utils/WarpXConst.H b/Source/Utils/WarpXConst.H index 70436cb72..34e08118d 100644 --- a/Source/Utils/WarpXConst.H +++ b/Source/Utils/WarpXConst.H @@ -1,3 +1,10 @@ +/* Copyright 2019 Andrew Myers, Luca Fedeli, Maxence Thevenet + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_CONST_H_ #define WARPX_CONST_H_ diff --git a/Source/Utils/WarpXMovingWindow.cpp b/Source/Utils/WarpXMovingWindow.cpp index c577da7f3..f6cd6de20 100644 --- a/Source/Utils/WarpXMovingWindow.cpp +++ b/Source/Utils/WarpXMovingWindow.cpp @@ -1,4 +1,14 @@ +/* Copyright 2019-2020 Andrew Myers, Axel Huebl, Maxence Thevenet + * Remi Lehe, Revathi Jambunathan, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "GuardCellManager.H" #include <WarpX.H> +#include <WarpXUtil.H> #include <WarpXConst.H> using namespace amrex; @@ -28,6 +38,9 @@ WarpX::MoveWindow (bool move_j) { if (do_moving_window == 0) return 0; + IntVect ng_extra = guard_cells.ng_Extra; + IntVect ng_zero = IntVect::TheZeroVector(); + // Update the continuous position of the moving window, // and of the plasma injection moving_window_x += moving_window_v * dt[0]; @@ -97,34 +110,49 @@ WarpX::MoveWindow (bool move_j) // Shift each component of vector fields (E, B, j) for (int dim = 0; dim < 3; ++dim) { - // Fine grid - shiftMF(*Bfield_fp[lev][dim], geom[lev], num_shift, dir, B_external_grid[dim]); - shiftMF(*Efield_fp[lev][dim], geom[lev], num_shift, dir, E_external_grid[dim]); + ParserWrapper *Bfield_parser; + ParserWrapper *Efield_parser; + bool use_Bparser = false; + bool use_Eparser = false; + if (B_ext_grid_s == "parse_b_ext_grid_function") { + use_Bparser = true; + if (dim == 0) Bfield_parser = Bxfield_parser.get(); + if (dim == 1) Bfield_parser = Byfield_parser.get(); + if (dim == 2) Bfield_parser = Bzfield_parser.get(); + } + if (E_ext_grid_s == "parse_e_ext_grid_function") { + use_Eparser = true; + if (dim == 0) Efield_parser = Exfield_parser.get(); + if (dim == 1) Efield_parser = Eyfield_parser.get(); + if (dim == 2) Efield_parser = Ezfield_parser.get(); + } + shiftMF(*Bfield_fp[lev][dim], geom[lev], num_shift, dir, ng_extra, B_external_grid[dim], use_Bparser, Bfield_parser); + shiftMF(*Efield_fp[lev][dim], geom[lev], num_shift, dir, ng_extra, E_external_grid[dim], use_Eparser, Efield_parser); if (move_j) { - shiftMF(*current_fp[lev][dim], geom[lev], num_shift, dir); + shiftMF(*current_fp[lev][dim], geom[lev], num_shift, dir, ng_zero); } if (do_pml && pml[lev]->ok()) { const std::array<MultiFab*, 3>& pml_B = pml[lev]->GetB_fp(); const std::array<MultiFab*, 3>& pml_E = pml[lev]->GetE_fp(); - shiftMF(*pml_B[dim], geom[lev], num_shift, dir); - shiftMF(*pml_E[dim], geom[lev], num_shift, dir); + shiftMF(*pml_B[dim], geom[lev], num_shift, dir, ng_extra); + shiftMF(*pml_E[dim], geom[lev], num_shift, dir, ng_extra); } if (lev > 0) { - // Coarse grid - shiftMF(*Bfield_cp[lev][dim], geom[lev-1], num_shift_crse, dir, B_external_grid[dim]); - shiftMF(*Efield_cp[lev][dim], geom[lev-1], num_shift_crse, dir, E_external_grid[dim]); - shiftMF(*Bfield_aux[lev][dim], geom[lev], num_shift, dir); - shiftMF(*Efield_aux[lev][dim], geom[lev], num_shift, dir); + // coarse grid + shiftMF(*Bfield_cp[lev][dim], geom[lev-1], num_shift_crse, dir, ng_zero, B_external_grid[dim], use_Bparser, Bfield_parser); + shiftMF(*Efield_cp[lev][dim], geom[lev-1], num_shift_crse, dir, ng_zero, E_external_grid[dim], use_Eparser, Efield_parser); + shiftMF(*Bfield_aux[lev][dim], geom[lev], num_shift, dir, ng_zero); + shiftMF(*Efield_aux[lev][dim], geom[lev], num_shift, dir, ng_zero); if (move_j) { - shiftMF(*current_cp[lev][dim], geom[lev-1], num_shift_crse, dir); + shiftMF(*current_cp[lev][dim], geom[lev-1], num_shift_crse, dir, ng_zero); } if (do_pml && pml[lev]->ok()) { const std::array<MultiFab*, 3>& pml_B = pml[lev]->GetB_cp(); const std::array<MultiFab*, 3>& pml_E = pml[lev]->GetE_cp(); - shiftMF(*pml_B[dim], geom[lev-1], num_shift_crse, dir); - shiftMF(*pml_E[dim], geom[lev-1], num_shift_crse, dir); + shiftMF(*pml_B[dim], geom[lev-1], num_shift_crse, dir, ng_extra); + shiftMF(*pml_E[dim], geom[lev-1], num_shift_crse, dir, ng_extra); } } } @@ -132,19 +160,19 @@ WarpX::MoveWindow (bool move_j) // Shift scalar component F for dive cleaning if (do_dive_cleaning) { // Fine grid - shiftMF(*F_fp[lev], geom[lev], num_shift, dir); + shiftMF(*F_fp[lev], geom[lev], num_shift, dir, ng_zero); if (do_pml && pml[lev]->ok()) { MultiFab* pml_F = pml[lev]->GetF_fp(); - shiftMF(*pml_F, geom[lev], num_shift, dir); + shiftMF(*pml_F, geom[lev], num_shift, dir, ng_extra); } if (lev > 0) { // Coarse grid - shiftMF(*F_cp[lev], geom[lev-1], num_shift_crse, dir); + shiftMF(*F_cp[lev], geom[lev-1], num_shift_crse, dir, ng_zero); if (do_pml && pml[lev]->ok()) { MultiFab* pml_F = pml[lev]->GetF_cp(); - shiftMF(*pml_F, geom[lev-1], num_shift_crse, dir); + shiftMF(*pml_F, geom[lev-1], num_shift_crse, dir, ng_zero); } - shiftMF(*rho_cp[lev], geom[lev-1], num_shift_crse, dir); + shiftMF(*rho_cp[lev], geom[lev-1], num_shift_crse, dir, ng_zero); } } @@ -152,10 +180,10 @@ WarpX::MoveWindow (bool move_j) if (move_j) { if (rho_fp[lev]){ // Fine grid - shiftMF(*rho_fp[lev], geom[lev], num_shift, dir); + shiftMF(*rho_fp[lev], geom[lev], num_shift, dir, ng_zero); if (lev > 0){ // Coarse grid - shiftMF(*rho_cp[lev], geom[lev-1], num_shift_crse, dir); + shiftMF(*rho_cp[lev], geom[lev-1], num_shift_crse, dir, ng_zero); } } } @@ -204,7 +232,8 @@ WarpX::MoveWindow (bool move_j) void WarpX::shiftMF (MultiFab& mf, const Geometry& geom, int num_shift, int dir, - amrex::Real external_field) + IntVect ng_extra, amrex::Real external_field, bool useparser, + ParserWrapper *field_parser) { BL_PROFILE("WarpX::shiftMF()"); const BoxArray& ba = mf.boxArray(); @@ -216,7 +245,16 @@ WarpX::shiftMF (MultiFab& mf, const Geometry& geom, int num_shift, int dir, MultiFab tmpmf(ba, dm, nc, ng); MultiFab::Copy(tmpmf, mf, 0, 0, nc, ng); - tmpmf.FillBoundary(geom.periodicity()); + + IntVect ng_mw = IntVect::TheUnitVector(); + // Enough guard cells in the MW direction + ng_mw[dir] = num_shift; + // Add the extra cell (if momentum-conserving gather with staggered field solve) + ng_mw += ng_extra; + // Make sure we don't exceed number of guard cells allocated + ng_mw = ng_mw.min(ng); + // Fill guard cells. + tmpmf.FillBoundary(ng_mw, geom.periodicity()); // Make a box that covers the region that the window moved into const IndexType& typ = ba.ixType(); @@ -246,20 +284,57 @@ WarpX::shiftMF (MultiFab& mf, const Geometry& geom, int num_shift, int dir, shiftiv[dir] = num_shift; Dim3 shift = shiftiv.dim3(); + const RealBox& real_box = geom.ProbDomain(); + const auto dx = geom.CellSizeArray(); + #ifdef _OPENMP #pragma omp parallel if (Gpu::notInLaunchRegion()) #endif + + for (MFIter mfi(tmpmf); mfi.isValid(); ++mfi ) { auto const& dstfab = mf.array(mfi); auto const& srcfab = tmpmf.array(mfi); const Box& outbox = mfi.fabbox() & adjBox; + if (outbox.ok()) { - AMREX_PARALLEL_FOR_4D ( outbox, nc, i, j, k, n, - { - srcfab(i,j,k,n) = external_field; - }); + if (useparser == false) { + AMREX_PARALLEL_FOR_4D ( outbox, nc, i, j, k, n, + { + srcfab(i,j,k,n) = external_field; + }); + } else if (useparser == true) { + // index type of the src mf + auto const& mf_IndexType = (tmpmf).ixType(); + IntVect mf_type(AMREX_D_DECL(0,0,0)); + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + mf_type[idim] = mf_IndexType.nodeCentered(idim); + } + + amrex::ParallelFor (outbox, nc, + [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept + { + // Compute x,y,z co-ordinates based on index type of mf + Real fac_x = (1.0 - mf_type[0]) * dx[0]*0.5; + Real x = i*dx[0] + real_box.lo(0) + fac_x; +#if (AMREX_SPACEDIM==2) + Real y = 0.0; + Real fac_z = (1.0 - mf_type[1]) * dx[1]*0.5; + Real z = j*dx[1] + real_box.lo(1) + fac_z; +#else + Real fac_y = (1.0 - mf_type[1]) * dx[1]*0.5; + Real y = j*dx[1] + real_box.lo(1) + fac_y; + Real fac_z = (1.0 - mf_type[2]) * dx[2]*0.5; + Real z = k*dx[2] + real_box.lo(2) + fac_z; +#endif + srcfab(i,j,k,n) = field_parser->getField(x,y,z); + } + , amrex::Gpu::numThreadsPerBlockParallelFor() * sizeof(double)*4 + ); + } + } Box dstBox = mf[mfi].box(); diff --git a/Source/Utils/WarpXTagging.cpp b/Source/Utils/WarpXTagging.cpp index 91bb802e8..b2ac48e40 100644 --- a/Source/Utils/WarpXTagging.cpp +++ b/Source/Utils/WarpXTagging.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019 Axel Huebl, Maxence Thevenet, Weiqun Zhang + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <AMReX_BoxIterator.H> diff --git a/Source/Utils/WarpXUtil.H b/Source/Utils/WarpXUtil.H index 195e309cf..9231fa60a 100644 --- a/Source/Utils/WarpXUtil.H +++ b/Source/Utils/WarpXUtil.H @@ -1,9 +1,18 @@ +/* Copyright 2019-2020 Andrew Myers, Luca Fedeli, Maxence Thevenet + * Revathi Jambunathan, Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_UTILS_H_ #define WARPX_UTILS_H_ #include <AMReX_REAL.H> #include <AMReX_Vector.H> #include <AMReX_MultiFab.H> +#include <AMReX_ParmParse.H> +#include <WarpXParser.H> #include <string> @@ -15,14 +24,108 @@ void ConvertLabParamsToBoost(); void NullifyMF(amrex::MultiFab& mf, int lev, amrex::Real zmin, amrex::Real zmax); +/** + * \brief Parse a string (typically a mathematical expression) from the + * input file and store it into a variable. + * + * \param ParmParse pp used to read the query_string pp.<function>=string + * \param parmparse_string String used to initialize ParmParse + * \param query_string ParmParse.query will look for this string + * \param stored_string variable in which the string to parse is stored + */ +void Store_parserString(amrex::ParmParse &pp, std::string query_string, + std::string& stored_string); + namespace WarpXUtilIO{ - /** - * A helper function to write binary data on disk. - * @param[in] filename where to write - * @param[in] data Vector containing binary data to write on disk - * return true if it succeeds, false otherwise - */ - bool WriteBinaryDataOnFile(std::string filename, const amrex::Vector<char>& data); +/** + * A helper function to write binary data on disk. + * @param[in] filename where to write + * @param[in] data Vector containing binary data to write on disk + * return true if it succeeds, false otherwise + */ +bool WriteBinaryDataOnFile(std::string filename, const amrex::Vector<char>& data); +} + +namespace WarpXUtilAlgo{ + +/** \brief Returns a pointer to the first element in the range [first, last) that is greater than val + * + * A re-implementation of the upper_bound algorithm suitable for GPU kernels. + * + * @param first: pointer to left limit of the range to consider + * @param last: pointer to right limit of the range to consider + * @param val: value to compare the elements of [first, last) to + */ +template<typename T> AMREX_GPU_DEVICE AMREX_FORCE_INLINE +const T* upper_bound(const T* first, const T* last, const T& val) +{ + const T* it; + size_t count, step; + count = last-first; + while(count>0){ + it = first; + step = count/2; + it += step; + if (!(val<*it)){ + first = ++it; + count -= step + 1; + } + else{ + count = step; + } + } + return first; } +/** \brief Performs a linear interpolation + * + * Performs a linear interpolation at x given the 2 points + * (x0, f0) and (x1, f1) + */ +template<typename T> AMREX_GPU_DEVICE AMREX_FORCE_INLINE +T linear_interp(T x0, T x1, T f0, T f1, T x) +{ + return ((x1-x)*f0 + (x-x0)*f1)/(x1-x0); +} + +/** \brief Performs a bilinear interpolation + * + * Performs a bilinear interpolation at (x,y) given the 4 points + * (x0, y0, f00), (x0, y1, f01), (x1, y0, f10), (x1, y1, f11). + */ +template<typename T> AMREX_GPU_DEVICE AMREX_FORCE_INLINE +T bilinear_interp(T x0, T x1, T y0, T y1, T f00, T f01, T f10, T f11, T x, T y) +{ + const T fx0 = linear_interp(x0, x1, f00, f10, x); + const T fx1 = linear_interp(x0, x1, f01, f11, x); + return linear_interp(y0, y1, fx0, fx1, y); +} + +/** \brief Performs a trilinear interpolation + * + * Performs a trilinear interpolation at (x,y,z) given the 8 points + * (x0, y0, z0, f000), (x0, y0, z1, f001), (x0, y1, z0, f010), (x0, y1, z1, f011), + * (x1, y0, z0, f100), (x1, y0, z1, f101), (x1, y1, z0, f110), (x1, y1, z1, f111) + */ +template<typename T> AMREX_GPU_DEVICE AMREX_FORCE_INLINE +T trilinear_interp(T x0, T x1,T y0, T y1, T z0, T z1, + T f000, T f001, T f010, T f011, T f100, T f101, T f110, T f111, + T x, T y, T z) +{ + const T fxy0 = bilinear_interp( + x0, x1, y0, y1, f000, f010, f100, f110, x, y); + const T fxy1 = bilinear_interp( + x0, x1, y0, y1, f001, f011, f101, f111, x, y); + return linear_interp(z0, z1, fxy0, fxy1, z); +} + +} + +/** +* \brief Initialize a WarpXParser object from a string containing a math expression +* +* \param parse_function String to read to initialize the parser. +*/ +WarpXParser makeParser (std::string const& parse_function); + #endif //WARPX_UTILS_H_ diff --git a/Source/Utils/WarpXUtil.cpp b/Source/Utils/WarpXUtil.cpp index 8764a09c6..983654aed 100644 --- a/Source/Utils/WarpXUtil.cpp +++ b/Source/Utils/WarpXUtil.cpp @@ -1,3 +1,11 @@ +/* Copyright 2019-2020 Andrew Myers, Burlen Loring, Luca Fedeli + * Maxence Thevenet, Remi Lehe, Revathi Jambunathan + * Revathi Jambunathan + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpXUtil.H> #include <WarpXConst.H> #include <AMReX_ParmParse.H> @@ -164,3 +172,45 @@ namespace WarpXUtilIO{ } } +void Store_parserString(amrex::ParmParse& pp, std::string query_string, + std::string& stored_string) +{ + + char cstr[query_string.size()+1]; + strcpy(cstr, query_string.c_str()); + + std::vector<std::string> f; + pp.getarr(cstr, f); + stored_string.clear(); + for (auto const& s : f) { + stored_string += s; + } + f.clear(); + +} + + +WarpXParser makeParser (std::string const& parse_function) +{ + WarpXParser parser(parse_function); + parser.registerVariables({"x","y","z","t"}); + ParmParse pp("my_constants"); + std::set<std::string> symbols = parser.symbols(); + symbols.erase("x"); + symbols.erase("y"); + symbols.erase("z"); + symbols.erase("t"); + for (auto it = symbols.begin(); it != symbols.end(); ) { + Real v; + if (pp.query(it->c_str(), v)) { + parser.setConstant(*it, v); + it = symbols.erase(it); + } else { + ++it; + } + } + for (auto const& s : symbols) { + amrex::Abort("makeParser::Unknown symbol "+s); + } + return parser; +} diff --git a/Source/Utils/WarpX_Complex.H b/Source/Utils/WarpX_Complex.H index 25ed1a53a..cda4204a8 100644 --- a/Source/Utils/WarpX_Complex.H +++ b/Source/Utils/WarpX_Complex.H @@ -1,15 +1,23 @@ +/* Copyright 2019-2020 Andrew Myers, David Grote, Maxence Thevenet + * Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_COMPLEX_H_ #define WARPX_COMPLEX_H_ #include <AMReX_REAL.H> #include <AMReX_Gpu.H> +#include <AMReX_GpuComplex.H> + +#include <complex> // Define complex type on GPU/CPU #ifdef AMREX_USE_GPU -#include <thrust/complex.h> - -using Complex = thrust::complex<amrex::Real>; +using Complex = amrex::GpuComplex<amrex::Real>; #ifdef WARPX_USE_PSATD #include <cufft.h> @@ -19,7 +27,6 @@ static_assert( sizeof(Complex) == sizeof(cuDoubleComplex), #else -#include <complex> using Complex = std::complex<amrex::Real>; #ifdef WARPX_USE_PSATD @@ -39,7 +46,7 @@ namespace MathFunc template<typename T> AMREX_GPU_HOST_DEVICE T exp (const T& val){ #ifdef AMREX_USE_GPU - return thrust::exp(val); + return amrex::exp(val); #else return std::exp(val); #endif @@ -49,7 +56,7 @@ namespace MathFunc template<typename T> AMREX_GPU_HOST_DEVICE T sqrt (const T& val){ #ifdef AMREX_USE_GPU - return thrust::sqrt(val); + return amrex::sqrt(val); #else return std::sqrt(val); #endif @@ -59,7 +66,7 @@ namespace MathFunc template<typename T1, typename T2> AMREX_GPU_HOST_DEVICE T1 pow (const T1& val, const T2& power){ #ifdef AMREX_USE_GPU - return thrust::pow(val, power); + return amrex::pow(val, power); #else return std::pow(val, power); #endif diff --git a/Source/Utils/atomic_data.txt b/Source/Utils/atomic_data.txt index 140f5a26a..cd58e076a 100644 --- a/Source/Utils/atomic_data.txt +++ b/Source/Utils/atomic_data.txt @@ -1,3 +1,9 @@ +# Copyright 2019 Maxence Thevenet +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + # Reference: # Kramida, A., Ralchenko, Yu., Reader, J., and NIST ASD Team (2014). # NIST Atomic Spectra Database (ver. 5.2), [Online]. diff --git a/Source/Utils/utils_ES.F90 b/Source/Utils/utils_ES.F90 index baadeb7af..c11e849eb 100644 --- a/Source/Utils/utils_ES.F90 +++ b/Source/Utils/utils_ES.F90 @@ -1,3 +1,9 @@ +! Copyright 2019 Maxence Thevenet, Remi Lehe +! +! This file is part of WarpX. +! +! License: BSD-3-Clause-LBNL + module warpx_ES_utils use iso_c_binding diff --git a/Source/Utils/write_atomic_data_cpp.py b/Source/Utils/write_atomic_data_cpp.py index 35baf2cdf..12cafad0c 100644 --- a/Source/Utils/write_atomic_data_cpp.py +++ b/Source/Utils/write_atomic_data_cpp.py @@ -1,3 +1,10 @@ +# Copyright 2019-2020 Axel Huebl, Luca Fedeli, Maxence Thevenet +# +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + ''' This python script reads ionization tables in atomic_data.txt (generated from the NIST website) and extracts ionization levels into C++ file @@ -6,7 +13,6 @@ IonizationEnergiesTable.H, which contains tables + metadata. import re, os import numpy as np -from scipy.constants import e filename = os.path.join( '.', 'atomic_data.txt' ) with open(filename) as f: diff --git a/Source/WarpX.H b/Source/WarpX.H index decde3dc2..ae25a8168 100644 --- a/Source/WarpX.H +++ b/Source/WarpX.H @@ -1,3 +1,14 @@ +/* Copyright 2016-2020 Andrew Myers, Ann Almgren, Aurore Blelly + * Axel Huebl, Burlen Loring, David Grote + * Glenn Richardson, Junmin Gu, Luca Fedeli + * Mathieu Lobet, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang, Yinjian Zhao + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #ifndef WARPX_H_ #define WARPX_H_ @@ -7,6 +18,7 @@ #include <BackTransformedDiagnostic.H> #include <BilinearFilter.H> #include <NCIGodfreyFilter.H> +#include "MultiReducedDiags.H" #ifdef WARPX_USE_PSATD # include <SpectralSolver.H> @@ -24,13 +36,16 @@ #include <AMReX_LayoutData.H> #include <AMReX_Interpolater.H> #include <AMReX_FillPatchUtil.H> +#include <WarpXParserWrapper.H> + +#include "GuardCellManager.H" #ifdef _OPENMP # include <omp.h> #endif #ifdef WARPX_USE_OPENPMD -#include <WarpXOpenPMD.H> +# include <WarpXOpenPMD.H> #endif #include <iostream> @@ -62,8 +77,8 @@ public: WarpX (); ~WarpX (); - static std::string Version (); - static std::string PicsarVersion (); + static std::string Version (); //! Version of WarpX executable + static std::string PicsarVersion (); //! Version of PICSAR dependency int Verbose () const { return verbose; } @@ -73,19 +88,42 @@ public: MultiParticleContainer& GetPartContainer () { return *mypc; } - static void shiftMF(amrex::MultiFab& mf, const amrex::Geometry& geom, int num_shift, int dir, - amrex::Real external_field = 0.); + static void shiftMF (amrex::MultiFab& mf, const amrex::Geometry& geom, + int num_shift, int dir, amrex::IntVect ng_extra, + amrex::Real external_field=0.0, bool useparser = false, + ParserWrapper *field_parser=nullptr); static void GotoNextLine (std::istream& is); - // External fields added to particle fields. - static amrex::Vector<amrex::Real> B_external_particle; - static amrex::Vector<amrex::Real> E_external_particle; + //! Author of an input file / simulation setup + static std::string authors; // Initial field on the grid. static amrex::Vector<amrex::Real> E_external_grid; static amrex::Vector<amrex::Real> B_external_grid; + // Initialization Type for External E and B on grid + static std::string B_ext_grid_s; + static std::string E_ext_grid_s; + + // Parser for B_external on the grid + static std::string str_Bx_ext_grid_function; + static std::string str_By_ext_grid_function; + static std::string str_Bz_ext_grid_function; + // Parser for E_external on the grid + static std::string str_Ex_ext_grid_function; + static std::string str_Ey_ext_grid_function; + static std::string str_Ez_ext_grid_function; + + // ParserWrapper for B_external on the grid + std::unique_ptr<ParserWrapper> Bxfield_parser; + std::unique_ptr<ParserWrapper> Byfield_parser; + std::unique_ptr<ParserWrapper> Bzfield_parser; + // ParserWrapper for E_external on the grid + std::unique_ptr<ParserWrapper> Exfield_parser; + std::unique_ptr<ParserWrapper> Eyfield_parser; + std::unique_ptr<ParserWrapper> Ezfield_parser; + // Algorithms static long current_deposition_algo; static long charge_deposition_algo; @@ -93,6 +131,9 @@ public: static long particle_pusher_algo; static int maxwell_fdtd_solver_id; + // div E cleaning + static int do_dive_cleaning; + // Interpolation order static long nox; static long noy; @@ -130,6 +171,8 @@ public: static int do_subcycling; + static bool exchange_all_guard_cells; + // buffers static int n_field_gather_buffer; //! in number of cells from the edge (identical for each dimension) static int n_current_deposition_buffer; //! in number of cells from the edge (identical for each dimension) @@ -149,6 +192,9 @@ public: const amrex::MultiFab& getEfield_fp (int lev, int direction) {return *Efield_fp[lev][direction];} const amrex::MultiFab& getBfield_fp (int lev, int direction) {return *Bfield_fp[lev][direction];} + /** get low-high-low-high-... vector for each direction indicating if mother grid PMLs are enabled */ + std::vector<bool> getPMLdirections() const; + static amrex::MultiFab* getCosts (int lev) { if (m_instance) { return m_instance->costs[lev].get(); @@ -167,6 +213,9 @@ public: amrex::Vector<amrex::Real> mirror_z_width; amrex::Vector<int> mirror_z_npoints; + /// object with all reduced diagnotics, similar to MultiParticleContainer for species. + MultiReducedDiags* reduced_diags; + void applyMirrors(amrex::Real time); void ComputeDt (); @@ -219,12 +268,14 @@ public: void UpdateAuxilaryDataSameType (); // Fill boundary cells including coarse/fine boundaries - void FillBoundaryB (); - void FillBoundaryE (); - void FillBoundaryF (); - void FillBoundaryE (int lev); - void FillBoundaryB (int lev); - void FillBoundaryF (int lev); + void FillBoundaryB (amrex::IntVect ng, amrex::IntVect ng_extra_fine=amrex::IntVect::TheZeroVector()); + void FillBoundaryE (amrex::IntVect ng, amrex::IntVect ng_extra_fine=amrex::IntVect::TheZeroVector()); + void FillBoundaryF (amrex::IntVect ng); + void FillBoundaryAux (amrex::IntVect ng); + void FillBoundaryE (int lev, amrex::IntVect ng, amrex::IntVect ng_extra_fine=amrex::IntVect::TheZeroVector()); + void FillBoundaryB (int lev, amrex::IntVect ng, amrex::IntVect ng_extra_fine=amrex::IntVect::TheZeroVector()); + void FillBoundaryF (int lev, amrex::IntVect ng); + void FillBoundaryAux (int lev, amrex::IntVect ng); void SyncCurrent (); void SyncRho (); @@ -241,12 +292,18 @@ public: int checkInt () const {return check_int;} int plotInt () const {return plot_int;} + int openpmdInt () const {return openpmd_int;} void WriteCheckPointFile () const; + void WriteOpenPMDFile () const; void WritePlotFile () const; void UpdateInSitu () const; void AverageAndPackFields( amrex::Vector<std::string>& varnames, amrex::Vector<amrex::MultiFab>& mf_avg, const int ngrow) const; + void prepareFields( int const step, amrex::Vector<std::string>& varnames, + amrex::Vector<amrex::MultiFab>& mf_avg, + amrex::Vector<const amrex::MultiFab*>& output_mf, + amrex::Vector<amrex::Geometry>& output_geom ) const; void WritePlotFileES(const amrex::Vector<std::unique_ptr<amrex::MultiFab> >& rho, const amrex::Vector<std::unique_ptr<amrex::MultiFab> >& phi, @@ -308,6 +365,8 @@ public: const std::array<const amrex::MultiFab*, 3>& B, const std::array<amrex::Real,3>& dx, int ngrow); + const amrex::IntVect getngE() const { return guard_cells.ng_alloc_EB; }; + const amrex::IntVect getngF() const { return guard_cells.ng_alloc_F; }; void InitSpaceChargeField (WarpXParticleContainer& pc); void computePhi (const amrex::Vector<std::unique_ptr<amrex::MultiFab> >& rho, @@ -321,8 +380,50 @@ public: const amrex::Vector<std::unique_ptr<amrex::MultiFab> >& phi, std::array<amrex::Real, 3> const beta = {{0,0,0}} ) const; + /** + * \brief + * This function initializes the E and B fields on each level + * using the parser and the user-defined function for the external fields. + * The subroutine will parse the x_/y_z_external_grid_function and + * then, the B or E multifab is initialized based on the (x,y,z) position + * on the staggered yee-grid or cell-centered grid. + */ + void InitializeExternalFieldsOnGridUsingParser ( + amrex::MultiFab *mfx, amrex::MultiFab *mfy, amrex::MultiFab *mfz, + ParserWrapper *xfield_parser, ParserWrapper *yfield_parser, + ParserWrapper *zfield_parser, amrex::IntVect x_nodal_flag, + amrex::IntVect y_nodal_flag, amrex::IntVect z_nodal_flag, + const int lev); + protected: + /** + * \brief + * This function initializes E, B, rho, and F, at all the levels + * of the multifab. rho and F are initialized with 0. + * The E and B fields are initialized using user-defined inputs. + * The initialization type is set using "B_ext_grid_init_style" + * and "E_ext_grid_init_style". The initialization style is set to "default" + * if not explicitly defined by the user, and the E and B fields are + * initialized with E_external_grid and B_external_grid, respectively, each with + * a default value of 0. + * If the initialization type for the E and B field is "constant", + * then, the E and B fields at all the levels are initialized with + * user-defined values for E_external_grid and B_external_grid. + * If the initialization type for B-field is set to + * "parse_B_ext_grid_function", then, the parser is used to read + * Bx_external_grid_function(x,y,z), By_external_grid_function(x,y,z), + * and Bz_external_grid_function(x,y,z). + * Similarly, if the E-field initialization type is set to + * "parse_E_ext_grid_function", then, the parser is used to read + * Ex_external_grid_function(x,y,z), Ey_external_grid_function(x,y,z), + * and Ex_external_grid_function(x,y,z). The parser for the E and B + * initialization assumes that the function has three independent + * variables, at max, namely, x, y, z. However, any number of constants + * can be used in the function used to define the E and B fields on the grid. + */ + void InitLevelData (int lev, amrex::Real time); + //! Tagging cells for refinement virtual void ErrorEst (int lev, amrex::TagBoxArray& tags, amrex::Real time, int /*ngrow*/) final; @@ -358,9 +459,9 @@ private: /// void EvolveEM(int numsteps); - void FillBoundaryB (int lev, PatchType patch_type); - void FillBoundaryE (int lev, PatchType patch_type); - void FillBoundaryF (int lev, PatchType patch_type); + void FillBoundaryB (int lev, PatchType patch_type, amrex::IntVect ng); + void FillBoundaryE (int lev, PatchType patch_type, amrex::IntVect ng); + void FillBoundaryF (int lev, PatchType patch_type, amrex::IntVect ng); void OneStep_nosub (amrex::Real t); void OneStep_sub1 (amrex::Real t); @@ -410,7 +511,6 @@ private: void AllocLevelData (int lev, const amrex::BoxArray& new_grids, const amrex::DistributionMapping& new_dmap); - void InitLevelData (int lev, amrex::Real time); void InitFromCheckpoint (); void PostRestart (); @@ -449,7 +549,8 @@ private: void AllocLevelMFs (int lev, const amrex::BoxArray& ba, const amrex::DistributionMapping& dm, const amrex::IntVect& ngE, const amrex::IntVect& ngJ, - const amrex::IntVect& ngRho, int ngF); + const amrex::IntVect& ngRho, const amrex::IntVect& ngF, + const amrex::IntVect& ngextra, const bool aux_is_nodal); amrex::Vector<int> istep; // which step? amrex::Vector<int> nsubsteps; // how many substeps on each level? @@ -499,9 +600,6 @@ private: amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > current_buf; amrex::Vector<std::unique_ptr<amrex::MultiFab> > charge_buf; - // div E cleaning - int do_dive_cleaning = 0; - // PML int do_pml = 1; int pml_ncell = 10; @@ -551,18 +649,13 @@ private: int check_int = -1; int plot_int = -1; - std::string openpmd_backend {"bp"}; + std::string openpmd_backend {"default"}; + int openpmd_int = -1; + bool openpmd_tspf = true; //!< one file per timestep (or one file for all steps) #ifdef WARPX_USE_OPENPMD - bool dump_plotfiles = false; - bool dump_openpmd = true; - bool openpmd_tspf = true; // one file per timestep - //bool openpmd_tspf = false; // one file all timesteps WarpXOpenPMDPlot* m_OpenPMDPlotWriter = nullptr; - -#else - bool dump_plotfiles = true; - bool dump_openpmd = false; #endif + bool plot_rho = false; bool plot_costs = true; bool plot_finepatch = false; @@ -587,6 +680,8 @@ private: bool is_synchronized = true; + guardCellManager guard_cells; + //Slice Parameters int slice_max_grid_size; int slice_plot_int = -1; @@ -612,18 +707,19 @@ private: amrex::Vector< std::unique_ptr<amrex::MultiFab> > rho_cp_fft; #endif + bool fft_hybrid_mpi_decomposition = false; + int nox_fft = 16; + int noy_fft = 16; + int noz_fft = 16; + #ifdef WARPX_USE_PSATD private: void EvolvePSATD (int numsteps); void PushPSATD (amrex::Real dt); void PushPSATD_localFFT (int lev, amrex::Real dt); - bool fft_hybrid_mpi_decomposition = false; int ngroups_fft = 4; int fftw_plan_measure = 1; - int nox_fft = 16; - int noy_fft = 16; - int noz_fft = 16; amrex::Vector<std::unique_ptr<SpectralSolver>> spectral_solver_fp; amrex::Vector<std::unique_ptr<SpectralSolver>> spectral_solver_cp; @@ -665,4 +761,5 @@ private: int insitu_pin_mesh; }; + #endif diff --git a/Source/WarpX.cpp b/Source/WarpX.cpp index 377d103d1..d93fab7df 100644 --- a/Source/WarpX.cpp +++ b/Source/WarpX.cpp @@ -1,3 +1,14 @@ +/* Copyright 2016-2020 Andrew Myers, Ann Almgren, Aurore Blelly + * Axel Huebl, Burlen Loring, David Grote + * Glenn Richardson, Jean-Luc Vay, Junmin Gu + * Mathieu Lobet, Maxence Thevenet, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang, Yinjian Zhao + * levinem + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <WarpX_f.H> #include <WarpXConst.H> @@ -24,12 +35,22 @@ using namespace amrex; -Vector<Real> WarpX::B_external_particle(3, 0.0); -Vector<Real> WarpX::E_external_particle(3, 0.0); - Vector<Real> WarpX::E_external_grid(3, 0.0); Vector<Real> WarpX::B_external_grid(3, 0.0); +std::string WarpX::authors = ""; +std::string WarpX::B_ext_grid_s = "default"; +std::string WarpX::E_ext_grid_s = "default"; + +// Parser for B_external on the grid +std::string WarpX::str_Bx_ext_grid_function; +std::string WarpX::str_By_ext_grid_function; +std::string WarpX::str_Bz_ext_grid_function; +// Parser for E_external on the grid +std::string WarpX::str_Ex_ext_grid_function; +std::string WarpX::str_Ey_ext_grid_function; +std::string WarpX::str_Ez_ext_grid_function; + int WarpX::do_moving_window = 0; int WarpX::moving_window_dir = -1; Real WarpX::moving_window_v = std::numeric_limits<amrex::Real>::max(); @@ -45,6 +66,7 @@ long WarpX::charge_deposition_algo; long WarpX::field_gathering_algo; long WarpX::particle_pusher_algo; int WarpX::maxwell_fdtd_solver_id; +int WarpX::do_dive_cleaning = 0; long WarpX::n_rz_azimuthal_modes = 1; long WarpX::ncomps = 1; @@ -78,6 +100,7 @@ Real WarpX::particle_slice_width_lab = 0.0; bool WarpX::do_dynamic_scheduling = true; int WarpX::do_subcycling = 0; +bool WarpX::exchange_all_guard_cells = 0; #if (AMREX_SPACEDIM == 3) IntVect WarpX::Bx_nodal_flag(1,0,0); @@ -141,7 +164,7 @@ WarpX::WarpX () ReadParameters(); #ifdef WARPX_USE_OPENPMD - m_OpenPMDPlotWriter = new WarpXOpenPMDPlot(openpmd_tspf, openpmd_backend); + m_OpenPMDPlotWriter = new WarpXOpenPMDPlot(openpmd_tspf, openpmd_backend, WarpX::getPMLdirections()); #endif // Geometry on all levels has been defined already. @@ -178,6 +201,9 @@ WarpX::WarpX () } do_back_transformed_particles = mypc->doBackTransformedDiagnostics(); + /** create object for reduced diagnostics */ + reduced_diags = new MultiReducedDiags(); + Efield_aux.resize(nlevs_max); Bfield_aux.resize(nlevs_max); @@ -247,6 +273,25 @@ WarpX::WarpX () // at different levels (the stencil depends on c*dt/dz) nci_godfrey_filter_exeybz.resize(nlevs_max); nci_godfrey_filter_bxbyez.resize(nlevs_max); + + // Sanity checks. Must be done after calling the MultiParticleContainer + // constructor, as it reads additional parameters + // (e.g., use_fdtd_nci_corr) + +#ifndef WARPX_USE_PSATD + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + not ( do_pml && do_nodal ), + "PML + do_nodal for finite-difference not implemented" + ); +#endif + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + not ( do_dive_cleaning && do_nodal ), + "divE cleaning + do_nodal not implemented" + ); +#ifdef WARPX_USE_PSATD + AMREX_ALWAYS_ASSERT(use_fdtd_nci_corr == 0); + AMREX_ALWAYS_ASSERT(do_subcycling == 0); +#endif } WarpX::~WarpX () @@ -256,6 +301,8 @@ WarpX::~WarpX () ClearLevel(lev); } + delete reduced_diags; + #ifdef BL_USE_SENSEI_INSITU delete insitu_bridge; #endif @@ -272,6 +319,7 @@ WarpX::ReadParameters () ParmParse pp;// Traditionally, max_step and stop_time do not have prefix. pp.query("max_step", max_step); pp.query("stop_time", stop_time); + pp.query("authors", authors); } { @@ -293,6 +341,7 @@ WarpX::ReadParameters () pp.query("verbose", verbose); pp.query("regrid_int", regrid_int); pp.query("do_subcycling", do_subcycling); + pp.query("exchange_all_guard_cells", exchange_all_guard_cells); pp.query("override_sync_int", override_sync_int); AMREX_ALWAYS_ASSERT_WITH_MESSAGE(do_subcycling != 1 || max_level <= 1, @@ -306,12 +355,6 @@ WarpX::ReadParameters () pp.query("zmax_plasma_to_compute_max_step", zmax_plasma_to_compute_max_step); - pp.queryarr("B_external_particle", B_external_particle); - pp.queryarr("E_external_particle", E_external_particle); - - pp.queryarr("E_external_grid", E_external_grid); - pp.queryarr("B_external_grid", B_external_grid); - pp.query("do_moving_window", do_moving_window); if (do_moving_window) { @@ -439,12 +482,11 @@ WarpX::ReadParameters () amrex::Abort("J-damping can only be done when PML are inside simulation domain (do_pml_in_domain=1)"); } - pp.query("dump_openpmd", dump_openpmd); + pp.query("openpmd_int", openpmd_int); pp.query("openpmd_backend", openpmd_backend); #ifdef WARPX_USE_OPENPMD pp.query("openpmd_tspf", openpmd_tspf); #endif - pp.query("dump_plotfiles", dump_plotfiles); pp.query("plot_costs", plot_costs); pp.query("plot_raw_fields", plot_raw_fields); pp.query("plot_raw_fields_guards", plot_raw_fields_guards); @@ -633,7 +675,6 @@ WarpX::ReadParameters () } } - } // This is a virtual function. @@ -717,58 +758,23 @@ WarpX::ClearLevel (int lev) void WarpX::AllocLevelData (int lev, const BoxArray& ba, const DistributionMapping& dm) { - // When using subcycling, the particles on the fine level perform two pushes - // before being redistributed ; therefore, we need one extra guard cell - // (the particles may move by 2*c*dt) - const int ngx_tmp = (maxLevel() > 0 && do_subcycling == 1) ? WarpX::nox+1 : WarpX::nox; - const int ngy_tmp = (maxLevel() > 0 && do_subcycling == 1) ? WarpX::noy+1 : WarpX::noy; - const int ngz_tmp = (maxLevel() > 0 && do_subcycling == 1) ? WarpX::noz+1 : WarpX::noz; - - // Ex, Ey, Ez, Bx, By, and Bz have the same number of ghost cells. - // jx, jy, jz and rho have the same number of ghost cells. - // E and B have the same number of ghost cells as j and rho if NCI filter is not used, - // but different number of ghost cells in z-direction if NCI filter is used. - // The number of cells should be even, in order to easily perform the - // interpolation from coarse grid to fine grid. - int ngx = (ngx_tmp % 2) ? ngx_tmp+1 : ngx_tmp; // Always even number - int ngy = (ngy_tmp % 2) ? ngy_tmp+1 : ngy_tmp; // Always even number - int ngz_nonci = (ngz_tmp % 2) ? ngz_tmp+1 : ngz_tmp; // Always even number - int ngz; - if (WarpX::use_fdtd_nci_corr) { - int ng = ngz_tmp + NCIGodfreyFilter::m_stencil_width; - ngz = (ng % 2) ? ng+1 : ng; - } else { - ngz = ngz_nonci; - } - - // J is only interpolated from fine to coarse (not coarse to fine) - // and therefore does not need to be even. - int ngJx = ngx_tmp; - int ngJy = ngy_tmp; - int ngJz = ngz_tmp; - - // When calling the moving window (with one level of refinement), we shift - // the fine grid by 2 cells ; therefore, we need at least 2 guard cells - // on level 1. This may not be necessary for level 0. - if (do_moving_window) { - ngx = std::max(ngx,2); - ngy = std::max(ngy,2); - ngz = std::max(ngz,2); - ngJx = std::max(ngJx,2); - ngJy = std::max(ngJy,2); - ngJz = std::max(ngJz,2); - } -#if (AMREX_SPACEDIM == 3) - IntVect ngE(ngx,ngy,ngz); - IntVect ngJ(ngJx,ngJy,ngJz); -#elif (AMREX_SPACEDIM == 2) - IntVect ngE(ngx,ngz); - IntVect ngJ(ngJx,ngJz); -#endif + bool aux_is_nodal = (field_gathering_algo == GatheringAlgo::MomentumConserving); - IntVect ngRho = ngJ+1; //One extra ghost cell, so that it's safe to deposit charge density - // after pushing particle. + guard_cells.Init( + do_subcycling, + WarpX::use_fdtd_nci_corr, + do_nodal, + do_moving_window, + fft_hybrid_mpi_decomposition, + aux_is_nodal, + moving_window_dir, + WarpX::nox, + nox_fft, noy_fft, noz_fft, + NCIGodfreyFilter::m_stencil_width, + maxwell_fdtd_solver_id, + maxLevel(), + exchange_all_guard_cells); if (mypc->nSpeciesDepositOnMainGrid() && n_current_deposition_buffer == 0) { n_current_deposition_buffer = 1; @@ -779,54 +785,22 @@ WarpX::AllocLevelData (int lev, const BoxArray& ba, const DistributionMapping& d } if (n_current_deposition_buffer < 0) { - n_current_deposition_buffer = ngJ.max(); + n_current_deposition_buffer = guard_cells.ng_alloc_J.max(); } if (n_field_gather_buffer < 0) { // Field gather buffer should be larger than current deposition buffers n_field_gather_buffer = n_current_deposition_buffer + 1; } - int ngF = (do_moving_window) ? 2 : 0; - // CKC solver requires one additional guard cell - if (maxwell_fdtd_solver_id == 1) ngF = std::max( ngF, 1 ); - -#ifdef WARPX_USE_PSATD - if (fft_hybrid_mpi_decomposition == false){ - // All boxes should have the same number of guard cells - // (to avoid temporary parallel copies) - // Thus take the max of the required number of guards for each field - // Also: the number of guard cell should be enough to contain - // the stencil of the FFT solver. Here, this number (`ngFFT`) - // is determined *empirically* to be the order of the solver - // for nodal, and half the order of the solver for staggered. - IntVect ngFFT; - if (do_nodal) { - ngFFT = IntVect(AMREX_D_DECL(nox_fft, noy_fft, noz_fft)); - } else { - ngFFT = IntVect(AMREX_D_DECL(nox_fft/2, noy_fft/2, noz_fft/2)); - } - for (int i_dim=0; i_dim<AMREX_SPACEDIM; i_dim++ ){ - int ng_required = ngFFT[i_dim]; - // Get the max - ng_required = std::max( ng_required, ngE[i_dim] ); - ng_required = std::max( ng_required, ngJ[i_dim] ); - ng_required = std::max( ng_required, ngRho[i_dim] ); - ng_required = std::max( ng_required, ngF ); - // Set the guard cells to this max - ngE[i_dim] = ng_required; - ngJ[i_dim] = ng_required; - ngRho[i_dim] = ng_required; - ngF = ng_required; - } - } -#endif - - AllocLevelMFs(lev, ba, dm, ngE, ngJ, ngRho, ngF); + AllocLevelMFs(lev, ba, dm, guard_cells.ng_alloc_EB, guard_cells.ng_alloc_J, + guard_cells.ng_alloc_Rho, guard_cells.ng_alloc_F, + guard_cells.ng_Extra, aux_is_nodal); } void WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm, - const IntVect& ngE, const IntVect& ngJ, const IntVect& ngRho, int ngF) + const IntVect& ngE, const IntVect& ngJ, const IntVect& ngRho, + const IntVect& ngF, const IntVect& ngextra, const bool aux_is_nodal) { #if defined WARPX_DIM_RZ @@ -838,9 +812,6 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm ncomps = n_rz_azimuthal_modes*2 - 1; #endif - bool aux_is_nodal = (field_gathering_algo == GatheringAlgo::MomentumConserving); - IntVect ngextra(static_cast<int>(aux_is_nodal and !do_nodal)); - // // The fine patch // @@ -870,7 +841,7 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm if (do_dive_cleaning) { - F_fp[lev].reset (new MultiFab(amrex::convert(ba,IntVect::TheUnitVector()),dm,ncomps, ngF)); + F_fp[lev].reset (new MultiFab(amrex::convert(ba,IntVect::TheUnitVector()),dm,ncomps, ngF.max())); } #ifdef WARPX_USE_PSATD else @@ -955,7 +926,7 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm } if (do_dive_cleaning) { - F_cp[lev].reset (new MultiFab(amrex::convert(cba,IntVect::TheUnitVector()),dm,ncomps, ngF)); + F_cp[lev].reset (new MultiFab(amrex::convert(cba,IntVect::TheUnitVector()),dm,ncomps, ngF.max())); } #ifdef WARPX_USE_PSATD else @@ -1262,6 +1233,24 @@ WarpX::GetPML (int lev) } } +std::vector< bool > +WarpX::getPMLdirections() const +{ + std::vector< bool > dirsWithPML( 6, false ); +#if AMREX_SPACEDIM!=3 + dirsWithPML.resize( 4 ); +#endif + if( do_pml ) + { + for( auto i = 0u; i < dirsWithPML.size() / 2u; ++i ) + { + dirsWithPML.at( 2u*i ) = bool(do_pml_Lo[i]); + dirsWithPML.at( 2u*i + 1u ) = bool(do_pml_Hi[i]); + } + } + return dirsWithPML; +} + void WarpX::BuildBufferMasks () { @@ -1344,7 +1333,7 @@ WarpX::Version () std::string WarpX::PicsarVersion () { -#ifdef WARPX_GIT_VERSION +#ifdef PICSAR_GIT_VERSION return std::string(PICSAR_GIT_VERSION); #else return std::string("Unknown"); diff --git a/Source/main.cpp b/Source/main.cpp index 19413da7a..fc705bdf0 100644 --- a/Source/main.cpp +++ b/Source/main.cpp @@ -1,3 +1,11 @@ +/* Copyright 2016-2020 Andrew Myers, Ann Almgren, Axel Huebl + * David Grote, Jean-Luc Vay, Remi Lehe + * Revathi Jambunathan, Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ #include <WarpX.H> #include <WarpXUtil.H> |