ARTEMIS
PoissonSolver.H
Go to the documentation of this file.
1 /* Copyright 2019-2022 Axel Huebl, Remi Lehe
2  *
3  * This file is part of WarpX.
4  *
5  * License: BSD-3-Clause-LBNL
6  */
7 #ifndef ABLASTR_POISSON_SOLVER_H
8 #define ABLASTR_POISSON_SOLVER_H
9 
10 #include "Utils/WarpXConst.H"
11 
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 #if defined(AMREX_USE_EB) || defined(WARPX_DIM_RZ)
24 #endif
25 #ifdef AMREX_USE_EB
26 # include <AMReX_EBFabFactory.H>
27 #endif
28 #include <AMReX_Parser.H>
29 #include <AMReX_REAL.H>
30 #include <AMReX_MFInterp_C.H>
31 
32 #include <AMReX_Array.H>
33 #include <AMReX_Array4.H>
34 #include <AMReX_BLassert.H>
35 #include <AMReX_Box.H>
36 #include <AMReX_BoxArray.H>
37 #include <AMReX_Config.H>
39 #include <AMReX_FArrayBox.H>
40 #include <AMReX_FabArray.H>
41 #include <AMReX_Geometry.H>
42 #include <AMReX_GpuControl.H>
43 #include <AMReX_GpuLaunch.H>
44 #include <AMReX_GpuQualifiers.H>
45 #include <AMReX_IndexType.H>
46 #include <AMReX_IntVect.H>
47 #include <AMReX_LO_BCTYPES.H>
48 #include <AMReX_MFIter.H>
49 #include <AMReX_MLMG.H>
50 #include <AMReX_MultiFab.H>
51 #include <AMReX_REAL.H>
52 #include <AMReX_SPACE.H>
53 #include <AMReX_Vector.H>
54 #include <AMReX_MFInterp_C.H>
55 
56 #include <array>
57 #include <optional>
58 
59 
60 namespace ablastr::fields {
61 
62 namespace details
63 {
75  {
77  amrex::Array4<amrex::Real> const phi_fp_arr,
78  amrex::Array4<amrex::Real const> const phi_cp_arr,
79  amrex::IntVect const refratio)
80  : m_phi_fp_arr(phi_fp_arr), m_phi_cp_arr(phi_cp_arr), m_refratio(refratio)
81  {}
82 
84  void
85  operator() (long i, long j, long k) const noexcept
86  {
88  0, m_refratio);
89  }
90 
94  };
95 }
96 
127 template<
128  typename T_BoundaryHandler,
129  typename T_PostPhiCalculationFunctor = std::nullopt_t,
130  typename T_FArrayBoxFactory = void
131 >
132 void
135  std::array<amrex::Real, 3> const beta,
136  amrex::Real const relative_tolerance,
137  amrex::Real absolute_tolerance,
138  int const max_iters,
139  int const verbosity,
142  amrex::Vector<amrex::BoxArray> const grids,
143  T_BoundaryHandler const boundary_handler,
144  bool const do_single_precision_comms = false,
145  std::optional<amrex::Vector<amrex::IntVect> > rel_ref_ratio = std::nullopt,
146  [[maybe_unused]] T_PostPhiCalculationFunctor post_phi_calculation = std::nullopt,
147  [[maybe_unused]] std::optional<amrex::Real const> current_time = std::nullopt, // only used for EB
148  [[maybe_unused]] std::optional<amrex::Vector<T_FArrayBoxFactory const *> > eb_farray_box_factory = std::nullopt // only used for EB
149 )
150 {
151  using namespace amrex::literals;
152 
153  if (!rel_ref_ratio.has_value()) {
155  "rel_ref_ratio must be set if mesh-refinement is used");
156  rel_ref_ratio = amrex::Vector<amrex::IntVect>{{amrex::IntVect(AMREX_D_DECL(1, 1, 1))}};
157  }
158 
159  int const finest_level = rho.size() - 1u;
160 
161  // scale rho appropriately; also determine if rho is zero everywhere
162  amrex::Real max_norm_b = 0.0;
163  for (int lev=0; lev<=finest_level; lev++) {
164  rho[lev]->mult(-1._rt/PhysConst::ep0); // TODO: when do we "un-multiply" this? We need to document this side-effect!
165  max_norm_b = amrex::max(max_norm_b, rho[lev]->norm0());
166  }
168 
169  bool always_use_bnorm = (max_norm_b > 0);
170  if (!always_use_bnorm) {
171  if (absolute_tolerance == 0.0) absolute_tolerance = amrex::Real(1e-6);
173  "ElectrostaticSolver",
174  "Max norm of rho is 0",
176  );
177  }
178 
179  amrex::LPInfo info;
180  for (int lev=0; lev<=finest_level; lev++) {
181  // Set the value of beta
183 #if defined(WARPX_DIM_1D_Z)
184  {{ beta[2] }}; // beta_x and beta_z
185 #elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ)
186  {{ beta[0], beta[2] }}; // beta_x and beta_z
187 #else
188  {{ beta[0], beta[1], beta[2] }};
189 #endif
190 
191 #if !(defined(AMREX_USE_EB) || defined(WARPX_DIM_RZ))
192  // Determine whether to use semi-coarsening
194  {AMREX_D_DECL(geom[lev].CellSize(0)/std::sqrt(1._rt-beta_solver[0]*beta_solver[0]),
195  geom[lev].CellSize(1)/std::sqrt(1._rt-beta_solver[1]*beta_solver[1]),
196  geom[lev].CellSize(2)/std::sqrt(1._rt-beta_solver[2]*beta_solver[2]))};
197  int max_semicoarsening_level = 0;
198  int semicoarsening_direction = -1;
199  int min_dir = std::distance(dx_scaled.begin(),
200  std::min_element(dx_scaled.begin(),dx_scaled.end()));
201  int max_dir = std::distance(dx_scaled.begin(),
202  std::max_element(dx_scaled.begin(),dx_scaled.end()));
203  if (dx_scaled[max_dir] > dx_scaled[min_dir]) {
204  semicoarsening_direction = max_dir;
205  max_semicoarsening_level = static_cast<int>
206  (std::log2(dx_scaled[max_dir]/dx_scaled[min_dir]));
207  }
208  if (max_semicoarsening_level > 0) {
209  info.setSemicoarsening(true);
210  info.setMaxSemicoarseningLevel(max_semicoarsening_level);
211  info.setSemicoarseningDirection(semicoarsening_direction);
212  }
213 #endif
214 
215 #if defined(AMREX_USE_EB) || defined(WARPX_DIM_RZ)
216  // In the presence of EB or RZ: the solver assumes that the beam is
217  // propagating along one of the axes of the grid, i.e. that only *one*
218  // of the components of `beta` is non-negligible.
219  amrex::MLEBNodeFDLaplacian linop( {geom[lev]}, {grids[lev]}, {dmap[lev]}, info
220 #if defined(AMREX_USE_EB)
221  , {eb_farray_box_factory.value()[lev]}
222 #endif
223  );
224 
225  // Note: this assumes that the beam is propagating along
226  // one of the axes of the grid, i.e. that only *one* of the
227  // components of `beta` is non-negligible. // we use this
228 #if defined(WARPX_DIM_RZ)
229  linop.setSigma({0._rt, 1._rt-beta_solver[1]*beta_solver[1]});
230 #else
231  linop.setSigma({AMREX_D_DECL(
232  1._rt-beta_solver[0]*beta_solver[0],
233  1._rt-beta_solver[1]*beta_solver[1],
234  1._rt-beta_solver[2]*beta_solver[2])});
235 #endif
236 
237 #if defined(AMREX_USE_EB)
238  // if the EB potential only depends on time, the potential can be passed
239  // as a float instead of a callable
240  if (boundary_handler.phi_EB_only_t) {
241  linop.setEBDirichlet(boundary_handler.potential_eb_t(current_time.value()));
242  }
243  else
244  linop.setEBDirichlet(boundary_handler.getPhiEB(current_time.value()));
245 #endif
246 #else
247  // In the absence of EB and RZ: use a more generic solver
248  // that can handle beams propagating in any direction
249  amrex::MLNodeTensorLaplacian linop( {geom[lev]}, {grids[lev]},
250  {dmap[lev]}, info );
251  linop.setBeta( beta_solver ); // for the non-axis-aligned solver
252 #endif
253 
254  // Solve the Poisson equation
255  linop.setDomainBC( boundary_handler.lobc, boundary_handler.hibc );
256 #ifdef WARPX_DIM_RZ
257  linop.setRZ(true);
258 #endif
259  amrex::MLMG mlmg(linop); // actual solver defined here
260  mlmg.setVerbose(verbosity);
261  mlmg.setMaxIter(max_iters);
262  mlmg.setConvergenceNormType(always_use_bnorm ? amrex::MLMGNormType::bnorm : amrex::MLMGNormType::greater);
263 
264  // Solve Poisson equation at lev
265  mlmg.solve( {phi[lev]}, {rho[lev]},
266  relative_tolerance, absolute_tolerance );
267 
268  // needed for solving the levels by levels:
269  // - coarser level is initial guess for finer level
270  // - coarser level provides boundary values for finer level patch
271  // Interpolation from phi[lev] to phi[lev+1]
272  // (This provides both the boundary conditions and initial guess for phi[lev+1])
273  if (lev < finest_level) {
274 
275  // Allocate phi_cp for lev+1
276  amrex::BoxArray ba = phi[lev+1]->boxArray();
277  const amrex::IntVect& refratio = rel_ref_ratio.value()[lev];
278  ba.coarsen(refratio);
279  const int ncomp = linop.getNComp();
280  amrex::MultiFab phi_cp(ba, phi[lev+1]->DistributionMap(), ncomp, 1);
281 
282  // Copy from phi[lev] to phi_cp (in parallel)
284  const amrex::Periodicity& crse_period = geom[lev].periodicity();
285 
287  phi_cp,
288  *phi[lev],
289  0,
290  0,
291  1,
292  ng,
293  ng,
294  do_single_precision_comms,
295  crse_period
296  );
297 
298  // Local interpolation from phi_cp to phi[lev+1]
299 #ifdef AMREX_USE_OMP
300 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
301 #endif
302  for (amrex::MFIter mfi(*phi[lev + 1], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) {
303  amrex::Array4<amrex::Real> const phi_fp_arr = phi[lev + 1]->array(mfi);
304  amrex::Array4<amrex::Real const> const phi_cp_arr = phi_cp.array(mfi);
305 
306  details::PoissonInterpCPtoFP const interp(phi_fp_arr, phi_cp_arr, refratio);
307 
308  amrex::Box const b = mfi.tilebox(phi[lev + 1]->ixType().toIntVect());
310  }
311 
312  }
313 
314  // Run additional operations, such as calculation of the E field for embedded boundaries
316  if (post_phi_calculation.has_value())
317  post_phi_calculation.value()(mlmg, lev);
318 
319  } // loop over lev(els)
320 }
321 
322 } // namespace ablastr::fields
323 
324 #endif // ABLASTR_POISSON_SOLVER_H
#define AMREX_FORCE_INLINE
#define AMREX_GPU_DEVICE
#define AMREX_D_DECL(a, b, c)
#define ABLASTR_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition: TextMsg.H:77
BoxArray & coarsen(int refinement_ratio)
Array4< typename FabArray< FAB >::value_type const > array(const MFIter &mfi) const noexcept
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE constexpr IntVectND< dim > TheUnitVector() noexcept
void setSigma(Array< Real, AMREX_SPACEDIM > const &a_sigma) noexcept
void setVerbose(int v) noexcept
void setConvergenceNormType(MLMGNormType norm) noexcept
RT solve(const Vector< AMF * > &a_sol, const Vector< AMF const * > &a_rhs, RT a_tol_rel, RT a_tol_abs, const char *checkpoint_file=nullptr)
void setMaxIter(int n) noexcept
void setBeta(Array< Real, AMREX_SPACEDIM > const &a_beta) noexcept
Long size() const 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 ep0
vacuum permittivity: dielectric permittivity of vacuum [F/m]
Definition: constant.H:46
Definition: PoissonSolver.H:60
void computePhi(amrex::Vector< amrex::MultiFab * > const &rho, amrex::Vector< amrex::MultiFab * > &phi, std::array< amrex::Real, 3 > const beta, 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_PostPhiCalculationFunctor post_phi_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: PoissonSolver.H:133
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:22
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:306
void ReduceRealMax(Vector< std::reference_wrapper< Real > > const &)
std::enable_if_t< std::is_integral_v< T > > ParallelFor(TypeList< CTOs... > ctos, std::array< int, sizeof...(CTOs)> const &runtime_options, T N, F &&f)
IntVectND< AMREX_SPACEDIM > IntVect
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void mf_nodebilin_interp(int i, int, int, int n, Array4< Real > const &fine, int fcomp, Array4< Real const > const &crse, int ccomp, IntVect const &ratio) noexcept
bool TilingIfNotGPU() noexcept
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE constexpr const T & max(const T &a, const T &b) noexcept
std::array< T, N > Array
i
Definition: check_interp_points_and_weights.py:174
value
Definition: updateAMReX.py:141
Definition: PoissonSolver.H:75
PoissonInterpCPtoFP(amrex::Array4< amrex::Real > const phi_fp_arr, amrex::Array4< amrex::Real const > const phi_cp_arr, amrex::IntVect const refratio)
Definition: PoissonSolver.H:76
amrex::Array4< amrex::Real > const m_phi_fp_arr
Definition: PoissonSolver.H:91
amrex::Array4< amrex::Real const > const m_phi_cp_arr
Definition: PoissonSolver.H:92
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void operator()(long i, long j, long k) const noexcept
Definition: PoissonSolver.H:85
amrex::IntVect const m_refratio
Definition: PoissonSolver.H:93
LPInfo & setSemicoarsening(bool x) noexcept
LPInfo & setSemicoarseningDirection(int n) noexcept
LPInfo & setMaxSemicoarseningLevel(int n) noexcept