diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/Evolve/WarpXEvolve.cpp | 1 | ||||
-rw-r--r-- | Source/Initialization/WarpXInitData.cpp | 252 | ||||
-rw-r--r-- | Source/Parallelization/WarpXRegrid.cpp | 6 | ||||
-rw-r--r-- | Source/WarpX.H | 15 | ||||
-rw-r--r-- | Source/WarpX.cpp | 34 |
5 files changed, 308 insertions, 0 deletions
diff --git a/Source/Evolve/WarpXEvolve.cpp b/Source/Evolve/WarpXEvolve.cpp index 21deb66d1..269270bd9 100644 --- a/Source/Evolve/WarpXEvolve.cpp +++ b/Source/Evolve/WarpXEvolve.cpp @@ -324,6 +324,7 @@ WarpX::Evolve (int numsteps) // This is currently a lab frame calculation. ComputeMagnetostaticField(); } + AddExternalFields(); ExecutePythonCallback("afterEsolve"); } diff --git a/Source/Initialization/WarpXInitData.cpp b/Source/Initialization/WarpXInitData.cpp index cc2225375..062f8a83b 100644 --- a/Source/Initialization/WarpXInitData.cpp +++ b/Source/Initialization/WarpXInitData.cpp @@ -20,6 +20,7 @@ #include "Filter/BilinearFilter.H" #include "Filter/NCIGodfreyFilter.H" #include "Particles/MultiParticleContainer.H" +#include "Utils/Algorithms/LinearInterpolation.H" #include "Utils/Logo/GetLogo.H" #include "Utils/MPIInitHelpers.H" #include "Utils/Parser/ParserUtils.H" @@ -72,6 +73,10 @@ #include <string> #include <utility> +#ifdef WARPX_USE_OPENPMD +# include <openPMD/openPMD.hpp> +#endif + #include "FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.H" using namespace amrex; @@ -435,6 +440,12 @@ WarpX::InitData () if (electrostatic_solver_id == ElectrostaticSolverAlgo::LabFrameElectroMagnetostatic) ComputeMagnetostaticField(); + // Set up an invariant condition through the rest of + // execution, that any code besides the field solver that + // looks at field values will see the composite of the field + // solution and any external field + AddExternalFields(); + // Write full diagnostics before the first iteration. multi_diags->FilterComputePackFlush( -1 ); @@ -452,6 +463,23 @@ WarpX::InitData () } void +WarpX::AddExternalFields () { + for (int lev = 0; lev <= finest_level; ++lev) { + // FIXME: RZ multimode has more than one component for all these + if (add_external_E_field) { + amrex::MultiFab::Add(*Efield_fp[lev][0], *Efield_fp_external[lev][0], 0, 0, 1, guard_cells.ng_alloc_EB); + amrex::MultiFab::Add(*Efield_fp[lev][1], *Efield_fp_external[lev][1], 0, 0, 1, guard_cells.ng_alloc_EB); + amrex::MultiFab::Add(*Efield_fp[lev][2], *Efield_fp_external[lev][2], 0, 0, 1, guard_cells.ng_alloc_EB); + } + if (add_external_B_field) { + amrex::MultiFab::Add(*Bfield_fp[lev][0], *Bfield_fp_external[lev][0], 0, 0, 1, guard_cells.ng_alloc_EB); + amrex::MultiFab::Add(*Bfield_fp[lev][1], *Bfield_fp_external[lev][1], 0, 0, 1, guard_cells.ng_alloc_EB); + amrex::MultiFab::Add(*Bfield_fp[lev][2], *Bfield_fp_external[lev][2], 0, 0, 1, guard_cells.ng_alloc_EB); + } + } +} + +void WarpX::InitDiagnostics () { multi_diags->InitData(); reduced_diags->InitData(); @@ -870,6 +898,38 @@ WarpX::InitLevelData (int lev, Real /*time*/) } } + // Reading external fields from data file + if (add_external_B_field) { + std::string read_fields_from_path="./"; + pp_warpx.query("read_fields_from_path", read_fields_from_path); +#if defined(WARPX_DIM_RZ) + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(n_rz_azimuthal_modes == 1, + "External field reading is not implemented for more than one RZ mode (see #3829)"); + ReadExternalFieldFromFile(read_fields_from_path, Bfield_fp_external[lev][0].get(), "B", "r"); + ReadExternalFieldFromFile(read_fields_from_path, Bfield_fp_external[lev][1].get(), "B", "t"); + ReadExternalFieldFromFile(read_fields_from_path, Bfield_fp_external[lev][2].get(), "B", "z"); +#else + ReadExternalFieldFromFile(read_fields_from_path, Bfield_fp_external[lev][0].get(), "B", "x"); + ReadExternalFieldFromFile(read_fields_from_path, Bfield_fp_external[lev][1].get(), "B", "y"); + ReadExternalFieldFromFile(read_fields_from_path, Bfield_fp_external[lev][2].get(), "B", "z"); +#endif + } + if (add_external_E_field) { + std::string read_fields_from_path="./"; + pp_warpx.query("read_fields_from_path", read_fields_from_path); +#if defined(WARPX_DIM_RZ) + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(n_rz_azimuthal_modes == 1, + "External field reading is not implemented for more than one RZ mode (see #3829)"); + ReadExternalFieldFromFile(read_fields_from_path, Efield_fp_external[lev][0].get(), "E", "r"); + ReadExternalFieldFromFile(read_fields_from_path, Efield_fp_external[lev][1].get(), "E", "t"); + ReadExternalFieldFromFile(read_fields_from_path, Efield_fp_external[lev][2].get(), "E", "z"); +#else + ReadExternalFieldFromFile(read_fields_from_path, Efield_fp_external[lev][0].get(), "E", "x"); + ReadExternalFieldFromFile(read_fields_from_path, Efield_fp_external[lev][1].get(), "E", "y"); + ReadExternalFieldFromFile(read_fields_from_path, Efield_fp_external[lev][2].get(), "E", "z"); +#endif + } + if (costs[lev]) { const auto iarr = costs[lev]->IndexArray(); for (int i : iarr) { @@ -1242,3 +1302,195 @@ void WarpX::CheckKnownIssues() ablastr::warn_manager::WarnPriority::low); } } + +#if defined(WARPX_USE_OPENPMD) && !defined(WARPX_DIM_1D_Z) && !defined(WARPX_DIM_XZ) +void +WarpX::ReadExternalFieldFromFile ( + std::string read_fields_from_path, amrex::MultiFab* mf, + std::string F_name, std::string F_component) +{ + // Get WarpX domain info + auto& warpx = WarpX::GetInstance(); + amrex::Geometry const& geom0 = warpx.Geom(0); + const amrex::RealBox& real_box = geom0.ProbDomain(); + const auto dx = geom0.CellSizeArray(); + amrex::IntVect nodal_flag = mf->ixType().toIntVect(); + + // Read external field openPMD data + auto series = openPMD::Series(read_fields_from_path, openPMD::Access::READ_ONLY); + auto iseries = series.iterations.begin()->second; + auto F = iseries.meshes[F_name]; + + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(F.getAttribute("dataOrder").get<std::string>() == "C", + "Reading from files with non-C dataOrder is not implemented"); + + auto axisLabels = F.getAttribute("axisLabels").get<std::vector<std::string>>(); + auto fileGeom = F.getAttribute("geometry").get<std::string>(); + +#if defined(WARPX_DIM_3D) + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(fileGeom == "cartesian", "3D can only read from files with cartesian geometry"); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(axisLabels[0] == "x" && axisLabels[1] == "y" && axisLabels[2] == "z", + "3D expects axisLabels {x, y, z}"); +#elif defined(WARPX_DIM_XZ) + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(fileGeom == "cartesian", "XZ can only read from files with cartesian geometry"); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(axisLabels[0] == "x" && axisLabels[1] == "z", + "XZ expects axisLabels {x, z}"); +#elif defined(WARPX_DIM_1D_Z) + amrex::Abort(Utils::TextMsg::Err( + "Reading from openPMD for external fields is not known to work with 1D3V (see #3830)")); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(fileGeom == "cartesian", "1D3V can only read from files with cartesian geometry"); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(axisLabels[0] == "z"); +#elif defined(WARPX_DIM_RZ) + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(fileGeom == "thetaMode", "RZ can only read from files with 'thetaMode' geometry"); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(axisLabels[0] == "r" && axisLabels[1] == "z", + "RZ expects axisLabels {r, z}"); +#endif + + auto offset = F.gridGlobalOffset(); + amrex::Real offset0 = offset[0]; + amrex::Real offset1 = offset[1]; +#if defined(WARPX_DIM_3D) + amrex::Real offset2 = offset[2]; +#endif + auto d = F.gridSpacing<long double>(); + +#if defined(WARPX_DIM_RZ) + amrex::Real file_dr = d[0]; + amrex::Real file_dz = d[1]; +#elif defined(WARPX_DIM_3D) + amrex::Real file_dx = d[0]; + amrex::Real file_dy = d[1]; + amrex::Real file_dz = d[2]; +#endif + + auto FC = F[F_component]; + auto extent = FC.getExtent(); + int extent0 = extent[0]; + int extent1 = extent[1]; + int extent2 = extent[2]; + + // Determine the chunk data that will be loaded. + // Now, the full range of data is loaded. + // Loading chunk data can speed up the process. + // Thus, `chunk_offset` and `chunk_extent` should be modified accordingly in another PR. + openPMD::Offset chunk_offset = {0,0,0}; + openPMD::Extent chunk_extent = {extent[0], extent[1], extent[2]}; + + auto FC_chunk_data = FC.loadChunk<double>(chunk_offset,chunk_extent); + series.flush(); + auto FC_data_host = FC_chunk_data.get(); + + // Load data to GPU + size_t total_extent = size_t(extent[0]) * extent[1] * extent[2]; + amrex::Gpu::DeviceVector<double> FC_data_gpu(total_extent); + auto FC_data = FC_data_gpu.data(); + amrex::Gpu::copy(amrex::Gpu::hostToDevice, FC_data_host, FC_data_host + total_extent, FC_data); + + // Loop over boxes + for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + amrex::Box box = mfi.growntilebox(); + amrex::Box tb = mfi.tilebox(nodal_flag, mf->nGrowVect()); + auto const& mffab = mf->array(mfi); + + // Start ParallelFor + amrex::ParallelFor (tb, + [=] AMREX_GPU_DEVICE (int i, int j, int k) { + // i,j,k denote x,y,z indices in 3D xyz. + // i,j denote r,z indices in 2D rz; k is just 0 + + // ii is used for 2D RZ mode + int ii = i; +#if defined(WARPX_DIM_RZ) + // In 2D RZ, i denoting r can be < 0 + // but mirrored values should be assigned. + // Namely, mffab(i) = FC_data[-i] when i<0. + if (i<0) {ii = -i;} +#endif + + // Physical coordinates of the grid point + // 0,1,2 denote x,y,z in 3D xyz. + // 0,1 denote r,z in 2D rz. + amrex::Real x0, x1; + if ( box.type(0)==amrex::IndexType::CellIndex::NODE ) + { x0 = real_box.lo(0) + ii*dx[0]; } + else { x0 = real_box.lo(0) + ii*dx[0] + 0.5*dx[0]; } + if ( box.type(1)==amrex::IndexType::CellIndex::NODE ) + { x1 = real_box.lo(1) + j*dx[1]; } + else { x1 = real_box.lo(1) + j*dx[1] + 0.5*dx[1]; } + +#if defined(WARPX_DIM_RZ) + // Get index of the external field array + int const ir = floor( (x0-offset0)/file_dr ); + int const iz = floor( (x1-offset1)/file_dz ); + + // Get coordinates of external grid point + amrex::Real const xx0 = offset0 + ir * file_dr; + amrex::Real const xx1 = offset1 + iz * file_dz; + +#elif defined(WARPX_DIM_3D) + amrex::Real x2; + if ( box.type(2)==amrex::IndexType::CellIndex::NODE ) + { x2 = real_box.lo(2) + k*dx[2]; } + else { x2 = real_box.lo(2) + k*dx[2] + 0.5*dx[2]; } + + // Get index of the external field array + int const ix = floor( (x0-offset0)/file_dx ); + int const iy = floor( (x1-offset1)/file_dy ); + int const iz = floor( (x2-offset2)/file_dz ); + + // Get coordinates of external grid point + amrex::Real const xx0 = offset0 + ix * file_dx; + amrex::Real const xx1 = offset1 + iy * file_dy; + amrex::Real const xx2 = offset2 + iz * file_dz; +#endif + +#if defined(WARPX_DIM_RZ) + amrex::Array4<double> fc_array(FC_data, {0,0,0}, {extent0, extent2, extent1}, 1); + double + f00 = fc_array(0, iz , ir ), + f01 = fc_array(0, iz , ir+1), + f10 = fc_array(0, iz+1, ir ), + f11 = fc_array(0, iz+1, ir+1); + mffab(i,j,k) = utils::algorithms::bilinear_interp<double> + (xx0, xx0+file_dr, xx1, xx1+file_dz, + f00, f01, f10, f11, + x0, x1); +#elif defined(WARPX_DIM_3D) + amrex::Array4<double> fc_array(FC_data, {0,0,0}, {extent2, extent1, extent0}, 1); + double + f000 = fc_array(iz , iy , ix ), + f001 = fc_array(iz+1, iy , ix ), + f010 = fc_array(iz , iy+1, ix ), + f011 = fc_array(iz+1, iy+1, ix ), + f100 = fc_array(iz , iy , ix+1), + f101 = fc_array(iz+1, iy , ix+1), + f110 = fc_array(iz , iy+1, ix+1), + f111 = fc_array(iz+1, iy+1, ix+1); + mffab(i,j,k) = utils::algorithms::trilinear_interp<double> + (xx0, xx0+file_dx, xx1, xx1+file_dy, xx2, xx2+file_dz, + f000, f001, f010, f011, f100, f101, f110, f111, + x0, x1, x2); +#endif + + } + + ); // End ParallelFor + + } // End loop over boxes. + +} // End function WarpX::ReadExternalFieldFromFile +#else // WARPX_USE_OPENPMD && !WARPX_DIM_1D_Z && !defined(WARPX_DIM_XZ) +void +WarpX::ReadExternalFieldFromFile (std::string , amrex::MultiFab* ,std::string, std::string) +{ +#if defined(WARPX_DIM_1D) + Abort(Utils::TextMsg::Err("Reading fields from openPMD files is not supported in 1D"); +#elif defined(WARPX_DIM_XZ) + Abort(Utils::TextMsg::Err( + "Reading from openPMD for external fields is not known to work with XZ (see #3828)")); +#elif !defined(WARPX_USE_OPENPMD) + Abort(Utils::TextMsg::Err("OpenPMD field reading requires OpenPMD support to be enabled")); +#endif +} +#endif // WARPX_USE_OPENPMD diff --git a/Source/Parallelization/WarpXRegrid.cpp b/Source/Parallelization/WarpXRegrid.cpp index 3c90a07cb..16ecd49ce 100644 --- a/Source/Parallelization/WarpXRegrid.cpp +++ b/Source/Parallelization/WarpXRegrid.cpp @@ -169,6 +169,12 @@ WarpX::RemakeLevel (int lev, Real /*time*/, const BoxArray& ba, const Distributi { RemakeMultiFab(Bfield_fp[lev][idim], dm, true); RemakeMultiFab(Efield_fp[lev][idim], dm, true); + if (add_external_B_field) { + RemakeMultiFab(Bfield_fp_external[lev][idim], dm, true); + } + if (add_external_E_field) { + RemakeMultiFab(Efield_fp_external[lev][idim], dm, true); + } RemakeMultiFab(current_fp[lev][idim], dm, false); RemakeMultiFab(current_store[lev][idim], dm, false); if (current_deposition_algo == CurrentDepositionAlgo::Vay) { diff --git a/Source/WarpX.H b/Source/WarpX.H index 7d06b8b19..7b013f1c6 100644 --- a/Source/WarpX.H +++ b/Source/WarpX.H @@ -122,6 +122,11 @@ public: //! Initialization type for external electric field on the grid static std::string E_ext_grid_s; + //! Whether to apply the effect of an externally-defined electric field + static bool add_external_E_field; + //! Whether to apply the effect of an externally-defined magnetic field + static bool add_external_B_field; + //! String storing parser function to initialize x-component of the magnetic field on the grid static std::string str_Bx_ext_grid_function; //! String storing parser function to initialize y-component of the magnetic field on the grid @@ -976,6 +981,10 @@ public: const char field, const int lev, PatchType patch_type); + void ReadExternalFieldFromFile ( + std::string read_fields_from_path, amrex::MultiFab* mf, + std::string F_name, std::string F_component); + /** * \brief * This function initializes and calculates grid quantities used along with @@ -1099,6 +1108,8 @@ private: void FillBoundaryB_avg (int lev, PatchType patch_type, amrex::IntVect ng); void FillBoundaryE_avg (int lev, PatchType patch_type, amrex::IntVect ng); + void AddExternalFields (); + void OneStep_nosub (amrex::Real t); void OneStep_sub1 (amrex::Real t); @@ -1311,6 +1322,10 @@ private: amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > vector_potential_grad_buf_e_stag; amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > vector_potential_grad_buf_b_stag; + // Same as Bfield_fp/Efield_fp for reading external field data + amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > Efield_fp_external; + amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > Bfield_fp_external; + //! EB: Lengths of the mesh edges amrex::Vector<std::array< std::unique_ptr<amrex::MultiFab>, 3 > > m_edge_lengths; //! EB: Areas of the mesh faces diff --git a/Source/WarpX.cpp b/Source/WarpX.cpp index 518d86def..dfc340861 100644 --- a/Source/WarpX.cpp +++ b/Source/WarpX.cpp @@ -88,6 +88,8 @@ 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"; +bool WarpX::add_external_E_field = false; +bool WarpX::add_external_B_field = false; // Parser for B_external on the grid std::string WarpX::str_Bx_ext_grid_function; @@ -310,6 +312,10 @@ WarpX::WarpX () Bfield_avg_fp.resize(nlevs_max); } + // Same as Bfield_fp/Efield_fp for reading external field data + Bfield_fp_external.resize(1); + Efield_fp_external.resize(1); + m_edge_lengths.resize(nlevs_max); m_face_areas.resize(nlevs_max); m_distance_to_eb.resize(nlevs_max); @@ -671,6 +677,22 @@ WarpX::ReadParameters () moving_window_v *= PhysConst::c; } + pp_warpx.query("B_ext_grid_init_style", WarpX::B_ext_grid_s); + pp_warpx.query("E_ext_grid_init_style", WarpX::E_ext_grid_s); + + if (WarpX::B_ext_grid_s == "read_from_file") + { + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(max_level == 0, + "External field reading is not implemented for more than one level"); + add_external_B_field = true; + } + if (WarpX::E_ext_grid_s == "read_from_file") + { + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(max_level == 0, + "External field reading is not implemented for more than one level"); + add_external_E_field = true; + } + electrostatic_solver_id = GetAlgorithmInteger(pp_warpx, "do_electrostatic"); // if an electrostatic solver is used, set the Maxwell solver to None if (electrostatic_solver_id != ElectrostaticSolverAlgo::None) { @@ -2074,6 +2096,18 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm AllocInitMultiFab(current_fp[lev][1], amrex::convert(ba, jy_nodal_flag), dm, ncomps, ngJ, tag("current_fp[y]"), 0.0_rt); AllocInitMultiFab(current_fp[lev][2], amrex::convert(ba, jz_nodal_flag), dm, ncomps, ngJ, tag("current_fp[z]"), 0.0_rt); + // Match external field MultiFabs to fine patch + if (add_external_B_field) { + AllocInitMultiFab(Bfield_fp_external[lev][0], amrex::convert(ba, Bx_nodal_flag), dm, ncomps, ngEB, tag("Bfield_fp_external[x]")); + AllocInitMultiFab(Bfield_fp_external[lev][1], amrex::convert(ba, By_nodal_flag), dm, ncomps, ngEB, tag("Bfield_fp_external[y]")); + AllocInitMultiFab(Bfield_fp_external[lev][2], amrex::convert(ba, Bz_nodal_flag), dm, ncomps, ngEB, tag("Bfield_fp_external[z]")); + } + if (add_external_E_field) { + AllocInitMultiFab(Efield_fp_external[lev][0], amrex::convert(ba, Ex_nodal_flag), dm, ncomps, ngEB, tag("Efield_fp_external[x]")); + AllocInitMultiFab(Efield_fp_external[lev][1], amrex::convert(ba, Ey_nodal_flag), dm, ncomps, ngEB, tag("Efield_fp_external[y]")); + AllocInitMultiFab(Efield_fp_external[lev][2], amrex::convert(ba, Ez_nodal_flag), dm, ncomps, ngEB, tag("Efield_fp_external[z]")); + } + if (do_current_centering) { amrex::BoxArray const& nodal_ba = amrex::convert(ba, amrex::IntVect::TheNodeVector()); |