WarpX
VectorPoissonSolver.H
Go to the documentation of this file.
1 /* Copyright 2022 S. Eric Clark, LLNL
2  *
3  * This file is part of WarpX.
4  *
5  * License: BSD-3-Clause-LBNL
6  */
7 #ifndef ABLASTR_VECTOR_POISSON_SOLVER_H
8 #define ABLASTR_VECTOR_POISSON_SOLVER_H
9 
10 #include <ablastr/constant.H>
13 #include <ablastr/utils/TextMsg.H>
15 
16 #include <AMReX_MultiFab.H>
17 #include <AMReX_REAL.H>
18 #include <AMReX_MLLinOp.H>
19 #include <AMReX_Vector.H>
20 #include <AMReX_Array.H>
22 #ifdef AMREX_USE_EB
23 # include <AMReX_EBFabFactory.H>
24 #endif
25 #include <AMReX_Parser.H>
26 #include <AMReX_REAL.H>
27 #include <AMReX_MFInterp_C.H>
28 
29 #include <AMReX_Array.H>
30 #include <AMReX_Array4.H>
31 #include <AMReX_BLassert.H>
32 #include <AMReX_Box.H>
33 #include <AMReX_BoxArray.H>
34 #include <AMReX_Config.H>
36 #include <AMReX_FArrayBox.H>
37 #include <AMReX_FabArray.H>
38 #include <AMReX_Geometry.H>
39 #include <AMReX_GpuControl.H>
40 #include <AMReX_GpuLaunch.H>
41 #include <AMReX_GpuQualifiers.H>
42 #include <AMReX_IndexType.H>
43 #include <AMReX_IntVect.H>
44 #include <AMReX_LO_BCTYPES.H>
45 #include <AMReX_MFIter.H>
46 #include <AMReX_MLMG.H>
47 #include <AMReX_MultiFab.H>
48 #include <AMReX_REAL.H>
49 #include <AMReX_SPACE.H>
50 #include <AMReX_Vector.H>
51 #include <AMReX_MFInterp_C.H>
52 
53 #include <array>
54 #include <optional>
55 
56 namespace ablastr::fields {
57 
87 template<
88  typename T_BoundaryHandler,
89  typename T_PostACalculationFunctor = std::nullopt_t,
90  typename T_FArrayBoxFactory = void
91 >
92 void
95  amrex::Real const relative_tolerance,
96  amrex::Real absolute_tolerance,
97  int const max_iters,
98  int const verbosity,
101  amrex::Vector<amrex::BoxArray> const grids,
102  T_BoundaryHandler const boundary_handler,
103  bool const do_single_precision_comms = false,
104  std::optional<amrex::Vector<amrex::IntVect> > rel_ref_ratio = std::nullopt,
105  [[maybe_unused]] T_PostACalculationFunctor post_A_calculation = std::nullopt,
106  [[maybe_unused]] std::optional<amrex::Real const> current_time = std::nullopt, // only used for EB
107  [[maybe_unused]] std::optional<amrex::Vector<T_FArrayBoxFactory const *> > eb_farray_box_factory = std::nullopt // only used for EB
108 )
109 {
110  using namespace amrex::literals;
111 
112  if (!rel_ref_ratio.has_value()) {
113  ABLASTR_ALWAYS_ASSERT_WITH_MESSAGE(curr.size() == 1u,
114  "rel_ref_ratio must be set if mesh-refinement is used");
115  rel_ref_ratio = amrex::Vector<amrex::IntVect>{{amrex::IntVect(AMREX_D_DECL(1, 1, 1))}};
116  }
117 
118  auto const finest_level = static_cast<int>(curr.size()) - 1;
119 
120  // scale J appropriately; also determine if current is zero everywhere
121  amrex::Real max_comp_J = 0.0;
122  for (int lev=0; lev<=finest_level; lev++) {
123  for (int adim=0; adim<3; adim++) {
124  curr[lev][adim]->mult(-1._rt*ablastr::constant::SI::mu0); // Unscaled below
125  max_comp_J = amrex::max(max_comp_J, curr[lev][adim]->norm0());
126  }
127  }
129 
130  const bool always_use_bnorm = (max_comp_J > 0);
131  if (!always_use_bnorm) {
132  if (absolute_tolerance == 0.0) absolute_tolerance = amrex::Real(1e-6);
134  "MagnetostaticSolver",
135  "Max norm of J is 0",
137  );
138  }
139 
140  const amrex::LPInfo info;
141 
142  // Loop over dimensions of A to solve each component individually
143  for (int lev=0; lev<=finest_level; lev++) {
145  {geom[lev]}, {grids[lev]}, {dmap[lev]}, info
146 #if defined(AMREX_USE_EB)
147  , {eb_farray_box_factory.value()[lev]}
148 #endif
149  );
151  {geom[lev]}, {grids[lev]}, {dmap[lev]}, info
152 #if defined(AMREX_USE_EB)
153  , {eb_farray_box_factory.value()[lev]}
154 #endif
155  );
157  {geom[lev]}, {grids[lev]}, {dmap[lev]}, info
158 #if defined(AMREX_USE_EB)
159  , {eb_farray_box_factory.value()[lev]}
160 #endif
161  );
162 
163  amrex::Array<amrex::MLEBNodeFDLaplacian*,3> linop = {&linopx,&linopy,&linopz};
165 
166  for (int adim=0; adim<3; adim++) {
167  // Solve the Poisson equation
168  // This is solving the self fields using the magnetostatic solver in the lab frame
169 
170  // Note: this assumes that beta is zero
171  linop[adim]->setSigma({AMREX_D_DECL(1._rt, 1._rt, 1._rt)});
172 
173 #if defined(AMREX_USE_EB)
174  // Set Homogeneous Dirichlet Boundary on EB
175  linop[adim]->setEBDirichlet(0_rt);
176 #endif
177 
178 #ifdef WARPX_DIM_RZ
179  linop[adim]->setRZ(true);
180 #endif
181 
182  linop[adim]->setDomainBC( boundary_handler.lobc[adim], boundary_handler.hibc[adim] );
183 
184  mlmg[adim] = std::make_unique<amrex::MLMG>(*linop[adim]);
185 
186  mlmg[adim]->setVerbose(verbosity);
187  mlmg[adim]->setMaxIter(max_iters);
188  mlmg[adim]->setAlwaysUseBNorm(always_use_bnorm);
189 
190  // Solve Poisson equation at lev
191  mlmg[adim]->solve( {A[lev][adim]}, {curr[lev][adim]},
192  relative_tolerance, absolute_tolerance );
193 
194  // Synchronize the ghost cells, do halo exchange
196  A[lev][adim]->nGrowVect(),
198  geom[lev].periodicity(),
199  false);
200 
201  // needed for solving the levels by levels:
202  // - coarser level is initial guess for finer level
203  // - coarser level provides boundary values for finer level patch
204  // Interpolation from phi[lev] to phi[lev+1]
205  // (This provides both the boundary conditions and initial guess for phi[lev+1])
206  if (lev < finest_level) {
207 
208  // Allocate A_cp for lev+1
209  amrex::BoxArray ba = A[lev+1][adim]->boxArray();
210  const amrex::IntVect& refratio = rel_ref_ratio.value()[lev];
211  ba.coarsen(refratio);
212  const int ncomp = linop[adim]->getNComp();
213  amrex::MultiFab A_cp(ba, A[lev+1][adim]->DistributionMap(), ncomp, 1);
214 
215  // Copy from A[lev] to A_cp (in parallel)
217  const amrex::Periodicity& crse_period = geom[lev].periodicity();
218 
220  A_cp,
221  *A[lev][adim],
222  0,
223  0,
224  1,
225  ng,
226  ng,
227  do_single_precision_comms,
228  crse_period
229  );
230 
231  // Local interpolation from A_cp to A[lev+1]
232 #ifdef AMREX_USE_OMP
233 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
234 #endif
235  for (amrex::MFIter mfi(*A[lev+1][adim],amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
236  {
237  amrex::Array4<amrex::Real> const& A_fp_arr = A[lev+1][adim]->array(mfi);
238  amrex::Array4<amrex::Real> const& A_cp_arr = A_cp.array(mfi);
239 
240  details::PoissonInterpCPtoFP const interp(A_fp_arr, A_cp_arr, refratio);
241 
242  amrex::Box const b = mfi.tilebox(A[lev + 1][adim]->ixType().toIntVect());
244  }
245 
246  }
247 
248  // Unscale current
249  curr[lev][adim]->mult(-1._rt/ablastr::constant::SI::mu0);
250  } // Loop over adim
251  // Run additional operations, such as calculation of the B fields for embedded boundaries
253  if (post_A_calculation.has_value())
254  post_A_calculation.value()(mlmg, lev);
255  } // loop over lev(els)
256 }
257 } // namepace Magnetostatic
258 #endif
#define AMREX_D_DECL(a, b, c)
#define ABLASTR_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition: TextMsg.H:75
static bool do_single_precision_comms
perform field communications in single precision
Definition: WarpX.H:231
BoxArray & coarsen(int refinement_ratio)
Array4< typename FabArray< FArrayBox >::value_type const > array(const MFIter &mfi) const noexcept
AMREX_GPU_HOST_DEVICE static constexpr AMREX_FORCE_INLINE IntVect TheUnitVector() noexcept
void setSigma(Array< Real, AMREX_SPACEDIM > const &a_sigma) noexcept
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void interp(int j, int k, int l, amrex::Array4< amrex::Real > const &fine, amrex::Array4< amrex::Real const > const &crse, const amrex::IntVect r_ratio, IntVect const &type) noexcept
Definition: Interpolate_K.H:9
static constexpr auto mu0
vacuum permeability: magnetic permeability of vacuum = 4.0e-7 * pi [H/m]
Definition: constant.H:48
Definition: PoissonSolver.H:59
void computeVectorPotential(amrex::Vector< amrex::Array< amrex::MultiFab *, 3 > > const &curr, amrex::Vector< amrex::Array< amrex::MultiFab *, 3 > > &A, amrex::Real const relative_tolerance, amrex::Real absolute_tolerance, int const max_iters, int const verbosity, amrex::Vector< amrex::Geometry > const geom, amrex::Vector< amrex::DistributionMapping > const dmap, amrex::Vector< amrex::BoxArray > const grids, T_BoundaryHandler const boundary_handler, bool const do_single_precision_comms=false, std::optional< amrex::Vector< amrex::IntVect > > rel_ref_ratio=std::nullopt, [[maybe_unused]] T_PostACalculationFunctor post_A_calculation=std::nullopt, [[maybe_unused]] std::optional< amrex::Real const > current_time=std::nullopt, [[maybe_unused]] std::optional< amrex::Vector< T_FArrayBoxFactory const * > > eb_farray_box_factory=std::nullopt)
Definition: VectorPoissonSolver.H:93
void FillBoundary(amrex::MultiFab &mf, amrex::IntVect ng, bool do_single_precision_comms, const amrex::Periodicity &period, std::optional< bool > nodal_sync)
Definition: Communication.cpp:71
void ParallelCopy(amrex::MultiFab &dst, const amrex::MultiFab &src, int src_comp, int dst_comp, int num_comp, const amrex::IntVect &src_nghost, const amrex::IntVect &dst_nghost, bool do_single_precision_comms, const amrex::Periodicity &period, amrex::FabArrayBase::CpOp op)
Definition: Communication.cpp:28
void WMRecordWarning(std::string topic, std::string text, WarnPriority priority=WarnPriority::medium)
Helper function to abbreviate the call to WarnManager::GetInstance().RecordWarning (recording a warni...
Definition: WarnManager.cpp:310
void ReduceRealMax(Vector< std::reference_wrapper< Real > > &&)
AMREX_GPU_HOST_DEVICE constexpr AMREX_FORCE_INLINE const T & max(const T &a, const T &b) noexcept
std::enable_if_t< std::is_integral< T >::value > ParallelFor(TypeList< CTOs... >, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
bool TilingIfNotGPU() noexcept
std::array< T, N > Array
value
Definition: updateAMReX.py:141
Definition: PoissonSolver.H:74
int verbosity()