LCOV - code coverage report
Current view: top level - ext/amrex/3d-coverage-g++-24.08/include - AMReX_MLCellLinOp.H (source / functions) Hit Total Coverage
Test: coverage_merged.info Lines: 0 1 0.0 %
Date: 2024-11-18 05:28:54 Functions: 0 2 0.0 %

          Line data    Source code
       1             : #ifndef AMREX_ML_CELL_LINOP_H_
       2             : #define AMREX_ML_CELL_LINOP_H_
       3             : #include <AMReX_Config.H>
       4             : 
       5             : #include <AMReX_MLLinOp.H>
       6             : #include <AMReX_iMultiFab.H>
       7             : #include <AMReX_MultiFabUtil.H>
       8             : #include <AMReX_YAFluxRegister.H>
       9             : #include <AMReX_MLLinOp_K.H>
      10             : #include <AMReX_MLMG_K.H>
      11             : 
      12             : #ifndef BL_NO_FORT
      13             : #include <AMReX_MLLinOp_F.H>
      14             : #endif
      15             : 
      16             : namespace amrex {
      17             : 
      18             : template <typename MF>
      19             : class MLCellLinOpT  // NOLINT(cppcoreguidelines-virtual-class-destructor)
      20             :     : public MLLinOpT<MF>
      21             : {
      22             : public:
      23             : 
      24             :     using FAB = typename MF::fab_type;
      25             :     using RT  = typename MF::value_type;
      26             : 
      27             :     using BCType = LinOpBCType;
      28             :     using BCMode    = typename MLLinOpT<MF>::BCMode;
      29             :     using StateMode = typename MLLinOpT<MF>::StateMode;
      30             :     using Location  = typename MLLinOpT<MF>::Location;
      31             : 
      32             :     MLCellLinOpT ();
      33           0 :     ~MLCellLinOpT () override = default;
      34             : 
      35             :     MLCellLinOpT (const MLCellLinOpT<MF>&) = delete;
      36             :     MLCellLinOpT (MLCellLinOpT<MF>&&) = delete;
      37             :     MLCellLinOpT<MF>& operator= (const MLCellLinOpT<MF>&) = delete;
      38             :     MLCellLinOpT<MF>& operator= (MLCellLinOpT<MF>&&) = delete;
      39             : 
      40             :     void define (const Vector<Geometry>& a_geom,
      41             :                  const Vector<BoxArray>& a_grids,
      42             :                  const Vector<DistributionMapping>& a_dmap,
      43             :                  const LPInfo& a_info = LPInfo(),
      44             :                  const Vector<FabFactory<FAB> const*>& a_factory = {});
      45             : 
      46             :     void setLevelBC (int amrlev, const MF* levelbcdata,
      47             :                              const MF* robinbc_a = nullptr,
      48             :                              const MF* robinbc_b = nullptr,
      49             :                              const MF* robinbc_f = nullptr) final;
      50             : 
      51             :     using MLLinOpT<MF>::setLevelBC;
      52             : 
      53             :     bool needsUpdate () const override {
      54             :         return MLLinOpT<MF>::needsUpdate();
      55             :     }
      56             :     void update () override;
      57             : 
      58             :     virtual bool isCrossStencil () const { return true; }
      59             :     virtual bool isTensorOp () const { return false; }
      60             : 
      61             :     void updateSolBC (int amrlev, const MF& crse_bcdata) const;
      62             :     void updateCorBC (int amrlev, const MF& crse_bcdata) const;
      63             : 
      64             :     virtual void applyBC (int amrlev, int mglev, MF& in, BCMode bc_mode, StateMode s_mode,
      65             :                           const MLMGBndryT<MF>* bndry=nullptr, bool skip_fillboundary=false) const;
      66             : 
      67             :     BoxArray makeNGrids (int grid_size) const;
      68             : 
      69             :     void restriction (int, int, MF& crse, MF& fine) const override;
      70             : 
      71             :     void interpolation (int amrlev, int fmglev, MF& fine, const MF& crse) const override;
      72             : 
      73             :     void interpAssign (int amrlev, int fmglev, MF& fine, MF& crse) const override;
      74             : 
      75             :     void interpolationAmr (int famrlev, MF& fine, const MF& crse,
      76             :                                    IntVect const& nghost) const override;
      77             : 
      78             :     void averageDownSolutionRHS (int camrlev, MF& crse_sol, MF& crse_rhs,
      79             :                                          const MF& fine_sol, const MF& fine_rhs) override;
      80             : 
      81             :     void apply (int amrlev, int mglev, MF& out, MF& in, BCMode bc_mode,
      82             :                         StateMode s_mode, const MLMGBndryT<MF>* bndry=nullptr) const override;
      83             :     void smooth (int amrlev, int mglev, MF& sol, const MF& rhs,
      84             :                          bool skip_fillboundary=false) const final;
      85             : 
      86             :     void solutionResidual (int amrlev, MF& resid, MF& x, const MF& b,
      87             :                                    const MF* crse_bcdata=nullptr) override;
      88             : 
      89             :     void correctionResidual (int amrlev, int mglev, MF& resid, MF& x, const MF& b,
      90             :                                      BCMode bc_mode, const MF* crse_bcdata=nullptr) final;
      91             : 
      92             :     // The assumption is crse_sol's boundary has been filled, but not fine_sol.
      93             :     void reflux (int crse_amrlev,
      94             :                          MF& res, const MF& crse_sol, const MF&,
      95             :                          MF&, MF& fine_sol, const MF&) const final;
      96             :     void compFlux (int amrlev, const Array<MF*,AMREX_SPACEDIM>& fluxes,
      97             :                            MF& sol, Location loc) const override;
      98             :     void compGrad (int amrlev, const Array<MF*,AMREX_SPACEDIM>& grad,
      99             :                            MF& sol, Location loc) const override;
     100             : 
     101             :     void applyMetricTerm (int amrlev, int mglev, MF& rhs) const final;
     102             :     void unapplyMetricTerm (int amrlev, int mglev, MF& rhs) const final;
     103             :     Vector<RT> getSolvabilityOffset (int amrlev, int mglev,
     104             :                                              MF const& rhs) const override;
     105             :     void fixSolvabilityByOffset (int amrlev, int mglev, MF& rhs,
     106             :                                          Vector<RT> const& offset) const override;
     107             : 
     108             :     void prepareForSolve () override;
     109             : 
     110             :     RT xdoty (int amrlev, int mglev, const MF& x, const MF& y, bool local) const final;
     111             : 
     112             :     virtual void Fapply (int amrlev, int mglev, MF& out, const MF& in) const = 0;
     113             :     virtual void Fsmooth (int amrlev, int mglev, MF& sol, const MF& rhs, int redblack) const = 0;
     114             :     virtual void FFlux (int amrlev, const MFIter& mfi,
     115             :                         const Array<FAB*,AMREX_SPACEDIM>& flux,
     116             :                         const FAB& sol, Location loc, int face_only=0) const = 0;
     117             : 
     118             :     virtual void addInhomogNeumannFlux (int /*amrlev*/,
     119             :                                         const Array<MF*,AMREX_SPACEDIM>& /*grad*/,
     120             :                                         MF const& /*sol*/,
     121             :                                         bool /*mult_bcoef*/) const {}
     122             : 
     123             :     RT normInf (int amrlev, MF const& mf, bool local) const override;
     124             : 
     125             :     void averageDownAndSync (Vector<MF>& sol) const override;
     126             : 
     127             :     void avgDownResAmr (int clev, MF& cres, MF const& fres) const override;
     128             : 
     129             :     struct BCTL {
     130             :         BoundCond type;
     131             :         RT location;
     132             :     };
     133             : 
     134             :     Vector<std::unique_ptr<MF> > m_robin_bcval;
     135             : 
     136             : protected:
     137             : 
     138             :     bool m_has_metric_term = false;
     139             : 
     140             :     Vector<std::unique_ptr<MLMGBndryT<MF>> >   m_bndry_sol;
     141             :     Vector<std::unique_ptr<BndryRegisterT<MF>> > m_crse_sol_br;
     142             : 
     143             :     Vector<std::unique_ptr<MLMGBndryT<MF>> > m_bndry_cor;
     144             :     Vector<std::unique_ptr<BndryRegisterT<MF>> > m_crse_cor_br;
     145             : 
     146             :     // In case of agglomeration, coarse MG grids on amr level 0 are
     147             :     // not simply coarsened from fine MG grids.  So we need to build
     148             :     // bcond and bcloc for each MG level.
     149             :     using RealTuple = Array<RT,2*BL_SPACEDIM>;
     150             :     using BCTuple   = Array<BoundCond,2*BL_SPACEDIM>;
     151             :     class BndryCondLoc
     152             :     {
     153             :     public:
     154             :         BndryCondLoc (const BoxArray& ba, const DistributionMapping& dm, int ncomp);
     155             : 
     156             :         void setLOBndryConds (const Geometry& geom, const Real* dx,
     157             :                               const Vector<Array<BCType,AMREX_SPACEDIM> >& lobc,
     158             :                               const Vector<Array<BCType,AMREX_SPACEDIM> >& hibc,
     159             :                               IntVect const& ratio, const RealVect& interior_bloc,
     160             :                               const Array<Real,AMREX_SPACEDIM>& domain_bloc_lo,
     161             :                               const Array<Real,AMREX_SPACEDIM>& domain_bloc_hi,
     162             :                               LinOpBCType crse_fine_bc_type);
     163             : 
     164             :         const Vector<BCTuple>& bndryConds (const MFIter& mfi) const noexcept {
     165             :             return bcond[mfi];
     166             :         }
     167             :         const Vector<RealTuple>& bndryLocs (const MFIter& mfi) const noexcept {
     168             :             return bcloc[mfi];
     169             :         }
     170             :         const BCTuple& bndryConds (const MFIter& mfi, int icomp) const noexcept {
     171             :             return bcond[mfi][icomp];
     172             :         }
     173             :         const RealTuple& bndryLocs (const MFIter& mfi, int icomp) const noexcept {
     174             :             return bcloc[mfi][icomp];
     175             :         }
     176             :         GpuArray<BCTL,2*AMREX_SPACEDIM> const* getBCTLPtr (const MFIter& mfi) const noexcept {
     177             :             return bctl[mfi];
     178             :         }
     179             :     private:
     180             :         LayoutData<Vector<BCTuple> >   bcond;
     181             :         LayoutData<Vector<RealTuple> > bcloc;
     182             :         LayoutData<GpuArray<BCTL,2*AMREX_SPACEDIM>*> bctl;
     183             :         Gpu::DeviceVector<GpuArray<BCTL,2*AMREX_SPACEDIM> > bctl_dv;
     184             :         int m_ncomp;
     185             :     };
     186             :     Vector<Vector<std::unique_ptr<BndryCondLoc> > > m_bcondloc;
     187             : 
     188             :     // used to save interpolation coefficients of the first interior cells
     189             :     mutable Vector<Vector<BndryRegisterT<MF>> > m_undrrelxr;
     190             : 
     191             :     // boundary cell flags for covered, not_covered, outside_domain
     192             :     Vector<Vector<Array<MultiMask,2*AMREX_SPACEDIM> > > m_maskvals;
     193             : 
     194             :     Vector<std::unique_ptr<iMultiFab> > m_norm_fine_mask;
     195             : 
     196             :     mutable Vector<YAFluxRegisterT<MF>> m_fluxreg;
     197             : 
     198             : private:
     199             : 
     200             :     void defineAuxData ();
     201             :     void defineBC ();
     202             : 
     203             :     void computeVolInv () const;
     204             :     mutable Vector<Vector<RT> > m_volinv; // used by solvability fix
     205             : };
     206             : 
     207             : template <typename T>
     208             : struct MLMGABCTag {
     209             :     Array4<T> fab;
     210             :     Array4<T const> bcval_lo;
     211             :     Array4<T const> bcval_hi;
     212             :     Array4<int const> mask_lo;
     213             :     Array4<int const> mask_hi;
     214             :     T bcloc_lo;
     215             :     T bcloc_hi;
     216             :     Box bx;
     217             :     BoundCond bctype_lo;
     218             :     BoundCond bctype_hi;
     219             :     int blen;
     220             :     int comp;
     221             :     int dir;
     222             : 
     223             :     [[nodiscard]] AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
     224             :     Box const& box() const noexcept { return bx; }
     225             : };
     226             : 
     227             : template <typename T>
     228             : struct MLMGPSTag {
     229             :     Array4<T> flo;
     230             :     Array4<T> fhi;
     231             :     Array4<int const> mlo;
     232             :     Array4<int const> mhi;
     233             :     T bcllo;
     234             :     T bclhi;
     235             :     Box bx;
     236             :     BoundCond bctlo;
     237             :     BoundCond bcthi;
     238             :     int blen;
     239             :     int comp;
     240             :     int dir;
     241             : 
     242             :     [[nodiscard]] AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
     243             :     Box const& box() const noexcept { return bx; }
     244             : };
     245             : 
     246             : #ifdef AMREX_USE_EB
     247             : template <typename T>
     248             : struct MLMGPSEBTag {
     249             :     Array4<T> flo;
     250             :     Array4<T> fhi;
     251             :     Array4<T const> ap;
     252             :     Array4<int const> mlo;
     253             :     Array4<int const> mhi;
     254             :     T bcllo;
     255             :     T bclhi;
     256             :     Box bx;
     257             :     BoundCond bctlo;
     258             :     BoundCond bcthi;
     259             :     int blen;
     260             :     int comp;
     261             :     int dir;
     262             : 
     263             :     [[nodiscard]] AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
     264             :     Box const& box() const noexcept { return bx; }
     265             : };
     266             : #endif
     267             : 
     268             : template <typename MF>
     269             : MLCellLinOpT<MF>::BndryCondLoc::BndryCondLoc (const BoxArray& ba,
     270             :                                               const DistributionMapping& dm,
     271             :                                               int ncomp)
     272             :     : bcond(ba, dm),
     273             :       bcloc(ba, dm),
     274             :       bctl(ba, dm),
     275             :       bctl_dv(bctl.local_size()*ncomp),
     276             :       m_ncomp(ncomp)
     277             : {
     278             :     auto* dp = bctl_dv.data();
     279             :     for (MFIter mfi(bcloc); mfi.isValid(); ++mfi) {
     280             :         bcond[mfi].resize(ncomp);
     281             :         bcloc[mfi].resize(ncomp);
     282             :         bctl[mfi] = dp;
     283             :         dp += ncomp;
     284             :     }
     285             : }
     286             : 
     287             : template <typename MF>
     288             : void
     289             : MLCellLinOpT<MF>::BndryCondLoc::
     290             : setLOBndryConds (const Geometry& geom, const Real* dx,
     291             :                  const Vector<Array<BCType,AMREX_SPACEDIM> >& lobc,
     292             :                  const Vector<Array<BCType,AMREX_SPACEDIM> >& hibc,
     293             :                  IntVect const& ratio, const RealVect& interior_bloc,
     294             :                  const Array<Real,AMREX_SPACEDIM>& domain_bloc_lo,
     295             :                  const Array<Real,AMREX_SPACEDIM>& domain_bloc_hi,
     296             :                  LinOpBCType crse_fine_bc_type)
     297             : {
     298             :     const Box& domain = geom.Domain();
     299             : 
     300             : #ifdef AMREX_USE_OMP
     301             : #pragma omp parallel
     302             : #endif
     303             :     for (MFIter mfi(bcloc); mfi.isValid(); ++mfi)
     304             :     {
     305             :         const Box& bx = mfi.validbox();
     306             :         for (int icomp = 0; icomp < m_ncomp; ++icomp) {
     307             :             RealTuple & bloc  = bcloc[mfi][icomp];
     308             :             BCTuple   & bctag = bcond[mfi][icomp];
     309             :             MLMGBndryT<MF>::setBoxBC(bloc, bctag, bx, domain,
     310             :                                      lobc[icomp], hibc[icomp],
     311             :                                      dx, ratio, interior_bloc,
     312             :                                      domain_bloc_lo, domain_bloc_hi,
     313             :                                      geom.isPeriodicArray(),
     314             :                                      crse_fine_bc_type);
     315             :         }
     316             :     }
     317             : 
     318             :     Gpu::PinnedVector<GpuArray<BCTL,2*AMREX_SPACEDIM> > hv;
     319             :     hv.reserve(bctl_dv.size());
     320             :     for (MFIter mfi(bctl); mfi.isValid(); ++mfi)
     321             :     {
     322             :         for (int icomp = 0; icomp < m_ncomp; ++icomp) {
     323             :             GpuArray<BCTL,2*AMREX_SPACEDIM> tmp;
     324             :             for (int m = 0; m < 2*AMREX_SPACEDIM; ++m) {
     325             :                 tmp[m].type = bcond[mfi][icomp][m];
     326             :                 tmp[m].location = bcloc[mfi][icomp][m];
     327             :             }
     328             :             hv.push_back(std::move(tmp));
     329             :         }
     330             :     }
     331             :     Gpu::copyAsync(Gpu::hostToDevice, hv.begin(), hv.end(), bctl_dv.begin());
     332             :     Gpu::streamSynchronize();
     333             : }
     334             : 
     335             : template <typename MF>
     336             : MLCellLinOpT<MF>::MLCellLinOpT ()
     337             : {
     338             :     this->m_ixtype = IntVect::TheCellVector();
     339             : }
     340             : 
     341             : template <typename MF>
     342             : void
     343             : MLCellLinOpT<MF>::define (const Vector<Geometry>& a_geom,
     344             :                           const Vector<BoxArray>& a_grids,
     345             :                           const Vector<DistributionMapping>& a_dmap,
     346             :                           const LPInfo& a_info,
     347             :                           const Vector<FabFactory<FAB> const*>& a_factory)
     348             : {
     349             :     MLLinOpT<MF>::define(a_geom, a_grids, a_dmap, a_info, a_factory);
     350             :     defineAuxData();
     351             :     defineBC();
     352             : }
     353             : 
     354             : template <typename MF>
     355             : void
     356             : MLCellLinOpT<MF>::defineAuxData ()
     357             : {
     358             :     BL_PROFILE("MLCellLinOp::defineAuxData()");
     359             : 
     360             :     m_undrrelxr.resize(this->m_num_amr_levels);
     361             :     m_maskvals.resize(this->m_num_amr_levels);
     362             :     m_fluxreg.resize(this->m_num_amr_levels-1);
     363             :     m_norm_fine_mask.resize(this->m_num_amr_levels-1);
     364             : 
     365             :     const int ncomp = this->getNComp();
     366             : 
     367             :     for (int amrlev = 0; amrlev < this->m_num_amr_levels; ++amrlev)
     368             :     {
     369             :         m_undrrelxr[amrlev].resize(this->m_num_mg_levels[amrlev]);
     370             :         for (int mglev = 0; mglev < this->m_num_mg_levels[amrlev]; ++mglev)
     371             :         {
     372             :             m_undrrelxr[amrlev][mglev].define(this->m_grids[amrlev][mglev],
     373             :                                               this->m_dmap[amrlev][mglev],
     374             :                                               1, 0, 0, ncomp);
     375             :         }
     376             :     }
     377             : 
     378             :     for (int amrlev = 0; amrlev < this->m_num_amr_levels; ++amrlev)
     379             :     {
     380             :         m_maskvals[amrlev].resize(this->m_num_mg_levels[amrlev]);
     381             :         for (int mglev = 0; mglev < this->m_num_mg_levels[amrlev]; ++mglev)
     382             :         {
     383             :             for (OrientationIter oitr; oitr; ++oitr)
     384             :             {
     385             :                 const Orientation face = oitr();
     386             :                 const int ngrow = 1;
     387             :                 const int extent = this->isCrossStencil() ? 0 : 1; // extend to corners
     388             :                 m_maskvals[amrlev][mglev][face].define(this->m_grids[amrlev][mglev],
     389             :                                                        this->m_dmap[amrlev][mglev],
     390             :                                                        this->m_geom[amrlev][mglev],
     391             :                                                        face, 0, ngrow, extent, 1, true);
     392             :             }
     393             :         }
     394             :     }
     395             : 
     396             :     for (int amrlev = 0; amrlev < this->m_num_amr_levels-1; ++amrlev)
     397             :     {
     398             :         const IntVect ratio{this->m_amr_ref_ratio[amrlev]};
     399             :         m_fluxreg[amrlev].define(this->m_grids[amrlev+1][0],
     400             :                                  this->m_grids[amrlev][0],
     401             :                                  this->m_dmap[amrlev+1][0],
     402             :                                  this->m_dmap[amrlev][0],
     403             :                                  this->m_geom[amrlev+1][0],
     404             :                                  this->m_geom[amrlev][0],
     405             :                                  ratio, amrlev+1, ncomp);
     406             :         m_norm_fine_mask[amrlev] = std::make_unique<iMultiFab>
     407             :             (makeFineMask(this->m_grids[amrlev][0], this->m_dmap[amrlev][0],
     408             :                           this->m_grids[amrlev+1][0],
     409             :                           ratio, 1, 0));
     410             :     }
     411             : 
     412             : #if (AMREX_SPACEDIM != 3)
     413             :     m_has_metric_term = !this->m_geom[0][0].IsCartesian() && this->info.has_metric_term;
     414             : #endif
     415             : }
     416             : 
     417             : template <typename MF>
     418             : void
     419             : MLCellLinOpT<MF>::defineBC ()
     420             : {
     421             :     BL_PROFILE("MLCellLinOp::defineBC()");
     422             : 
     423             :     const int ncomp = this->getNComp();
     424             : 
     425             :     m_bndry_sol.resize(this->m_num_amr_levels);
     426             :     m_crse_sol_br.resize(this->m_num_amr_levels);
     427             : 
     428             :     m_bndry_cor.resize(this->m_num_amr_levels);
     429             :     m_crse_cor_br.resize(this->m_num_amr_levels);
     430             : 
     431             :     m_robin_bcval.resize(this->m_num_amr_levels);
     432             : 
     433             :     for (int amrlev = 0; amrlev < this->m_num_amr_levels; ++amrlev)
     434             :     {
     435             :         m_bndry_sol[amrlev] = std::make_unique<MLMGBndryT<MF>>(this->m_grids[amrlev][0],
     436             :                                                                this->m_dmap[amrlev][0],
     437             :                                                                ncomp,
     438             :                                                                this->m_geom[amrlev][0]);
     439             :     }
     440             : 
     441             :     for (int amrlev = 1; amrlev < this->m_num_amr_levels; ++amrlev)
     442             :     {
     443             :         const int in_rad = 0;
     444             :         const int out_rad = 1;
     445             :         const int extent_rad = 2;
     446             :         const int crse_ratio = this->m_amr_ref_ratio[amrlev-1];
     447             :         BoxArray cba = this->m_grids[amrlev][0];
     448             :         cba.coarsen(crse_ratio);
     449             :         m_crse_sol_br[amrlev] = std::make_unique<BndryRegisterT<MF>>
     450             :             (cba, this->m_dmap[amrlev][0], in_rad, out_rad, extent_rad, ncomp);
     451             :     }
     452             : 
     453             :     for (int amrlev = 1; amrlev < this->m_num_amr_levels; ++amrlev)
     454             :     {
     455             :         const int in_rad = 0;
     456             :         const int out_rad = 1;
     457             :         const int extent_rad = 2;
     458             :         const int crse_ratio = this->m_amr_ref_ratio[amrlev-1];
     459             :         BoxArray cba = this->m_grids[amrlev][0];
     460             :         cba.coarsen(crse_ratio);
     461             :         m_crse_cor_br[amrlev] = std::make_unique<BndryRegisterT<MF>>
     462             :             (cba, this->m_dmap[amrlev][0], in_rad, out_rad, extent_rad, ncomp);
     463             :         m_crse_cor_br[amrlev]->setVal(RT(0.0));
     464             :     }
     465             : 
     466             :     // This has be to done after m_crse_cor_br is defined.
     467             :     for (int amrlev = 1; amrlev < this->m_num_amr_levels; ++amrlev)
     468             :     {
     469             :         m_bndry_cor[amrlev] = std::make_unique<MLMGBndryT<MF>>
     470             :             (this->m_grids[amrlev][0], this->m_dmap[amrlev][0], ncomp, this->m_geom[amrlev][0]);
     471             :         MF bc_data(this->m_grids[amrlev][0], this->m_dmap[amrlev][0], ncomp, 1);
     472             :         bc_data.setVal(0.0);
     473             : 
     474             :         m_bndry_cor[amrlev]->setBndryValues(*m_crse_cor_br[amrlev], 0, bc_data, 0, 0, ncomp,
     475             :                                             IntVect(this->m_amr_ref_ratio[amrlev-1]));
     476             : 
     477             :         Vector<Array<LinOpBCType,AMREX_SPACEDIM> > bclohi
     478             :             (ncomp,Array<LinOpBCType,AMREX_SPACEDIM>{{AMREX_D_DECL(BCType::Dirichlet,
     479             :                                                                    BCType::Dirichlet,
     480             :                                                                    BCType::Dirichlet)}});
     481             :         m_bndry_cor[amrlev]->setLOBndryConds(bclohi, bclohi, this->m_amr_ref_ratio[amrlev-1], RealVect{});
     482             :     }
     483             : 
     484             :     m_bcondloc.resize(this->m_num_amr_levels);
     485             :     for (int amrlev = 0; amrlev < this->m_num_amr_levels; ++amrlev)
     486             :     {
     487             :         m_bcondloc[amrlev].resize(this->m_num_mg_levels[amrlev]);
     488             :         for (int mglev = 0; mglev < this->m_num_mg_levels[amrlev]; ++mglev)
     489             :         {
     490             :             m_bcondloc[amrlev][mglev] = std::make_unique<BndryCondLoc>(this->m_grids[amrlev][mglev],
     491             :                                                                        this->m_dmap[amrlev][mglev],
     492             :                                                                        ncomp);
     493             :         }
     494             :     }
     495             : }
     496             : 
     497             : template <typename MF>
     498             : void
     499             : MLCellLinOpT<MF>::setLevelBC (int amrlev, const MF* a_levelbcdata, const MF* robinbc_a,
     500             :                               const MF* robinbc_b, const MF* robinbc_f)
     501             : {
     502             :     BL_PROFILE("MLCellLinOp::setLevelBC()");
     503             : 
     504             :     AMREX_ALWAYS_ASSERT(amrlev >= 0 && amrlev < this->m_num_amr_levels);
     505             : 
     506             :     const int ncomp = this->getNComp();
     507             : 
     508             :     MF zero;
     509             :     IntVect ng(1);
     510             :     if (this->hasHiddenDimension()) { ng[this->hiddenDirection()] = 0; }
     511             :     if (a_levelbcdata == nullptr) {
     512             :         zero.define(this->m_grids[amrlev][0], this->m_dmap[amrlev][0], ncomp, ng);
     513             :         zero.setVal(RT(0.0));
     514             :     } else {
     515             :         AMREX_ALWAYS_ASSERT(a_levelbcdata->nGrowVect().allGE(ng));
     516             :     }
     517             :     const MF& bcdata = (a_levelbcdata == nullptr) ? zero : *a_levelbcdata;
     518             : 
     519             :     IntVect br_ref_ratio(-1);
     520             : 
     521             :     if (amrlev == 0)
     522             :     {
     523             :         if (this->needsCoarseDataForBC())
     524             :         {
     525             :             AMREX_ALWAYS_ASSERT(!this->hasHiddenDimension());
     526             :             br_ref_ratio = this->m_coarse_data_crse_ratio.allGT(0) ? this->m_coarse_data_crse_ratio : IntVect(2);
     527             :             if (m_crse_sol_br[amrlev] == nullptr && br_ref_ratio.allGT(0))
     528             :             {
     529             :                 const int in_rad = 0;
     530             :                 const int out_rad = 1;
     531             :                 const int extent_rad = 2;
     532             :                 const IntVect crse_ratio = br_ref_ratio;
     533             :                 BoxArray cba = this->m_grids[amrlev][0];
     534             :                 cba.coarsen(crse_ratio);
     535             :                 m_crse_sol_br[amrlev] = std::make_unique<BndryRegisterT<MF>>
     536             :                     (cba, this->m_dmap[amrlev][0], in_rad, out_rad, extent_rad, ncomp);
     537             :             }
     538             :             if (this->m_coarse_data_for_bc != nullptr) {
     539             :                 AMREX_ALWAYS_ASSERT(this->m_coarse_data_crse_ratio.allGT(0));
     540             :                 const Box& cbx = amrex::coarsen(this->m_geom[0][0].Domain(), this->m_coarse_data_crse_ratio);
     541             :                 m_crse_sol_br[amrlev]->copyFrom(*this->m_coarse_data_for_bc, 0, 0, 0, ncomp,
     542             :                                                 this->m_geom[0][0].periodicity(cbx));
     543             :             } else {
     544             :                 m_crse_sol_br[amrlev]->setVal(RT(0.0));
     545             :             }
     546             :             m_bndry_sol[amrlev]->setBndryValues(*m_crse_sol_br[amrlev], 0,
     547             :                                                 bcdata, 0, 0, ncomp, br_ref_ratio);
     548             :             br_ref_ratio = this->m_coarse_data_crse_ratio;
     549             :         }
     550             :         else
     551             :         {
     552             :             m_bndry_sol[amrlev]->setPhysBndryValues(bcdata,0,0,ncomp);
     553             :             br_ref_ratio = IntVect(1);
     554             :         }
     555             :     }
     556             :     else
     557             :     {
     558             :         m_bndry_sol[amrlev]->setPhysBndryValues(bcdata,0,0,ncomp);
     559             :         br_ref_ratio = IntVect(this->m_amr_ref_ratio[amrlev-1]);
     560             :     }
     561             : 
     562             :     auto crse_fine_bc_type = (amrlev == 0) ? this->m_coarse_fine_bc_type : LinOpBCType::Dirichlet;
     563             :     m_bndry_sol[amrlev]->setLOBndryConds(this->m_lobc, this->m_hibc, br_ref_ratio,
     564             :                                          this->m_coarse_bc_loc, crse_fine_bc_type);
     565             : 
     566             :     const Real* dx = this->m_geom[amrlev][0].CellSize();
     567             :     for (int mglev = 0; mglev < this->m_num_mg_levels[amrlev]; ++mglev)
     568             :     {
     569             :         m_bcondloc[amrlev][mglev]->setLOBndryConds(this->m_geom[amrlev][mglev], dx,
     570             :                                                    this->m_lobc, this->m_hibc,
     571             :                                                    br_ref_ratio, this->m_coarse_bc_loc,
     572             :                                                    this->m_domain_bloc_lo, this->m_domain_bloc_hi,
     573             :                                                    crse_fine_bc_type);
     574             :     }
     575             : 
     576             :     if (this->hasRobinBC()) {
     577             :         AMREX_ASSERT(robinbc_a != nullptr && robinbc_b != nullptr && robinbc_f != nullptr);
     578             :         m_robin_bcval[amrlev] = std::make_unique<MF>(this->m_grids[amrlev][0],
     579             :                                                      this->m_dmap[amrlev][0],
     580             :                                                      ncomp*3, 1);
     581             :         const Box& domain = this->m_geom[amrlev][0].Domain();
     582             :         MFItInfo mfi_info;
     583             :         if (Gpu::notInLaunchRegion()) { mfi_info.SetDynamic(true); }
     584             : #ifdef AMREX_USE_OMP
     585             : #pragma omp parallel if (Gpu::notInLaunchRegion())
     586             : #endif
     587             :         for (MFIter mfi(*m_robin_bcval[amrlev], mfi_info); mfi.isValid(); ++mfi) {
     588             :             Box const& vbx = mfi.validbox();
     589             :             Array4<RT const> const& ra = robinbc_a->const_array(mfi);
     590             :             Array4<RT const> const& rb = robinbc_b->const_array(mfi);
     591             :             Array4<RT const> const& rf = robinbc_f->const_array(mfi);
     592             :             for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
     593             :                 const Box& blo = amrex::adjCellLo(vbx, idim);
     594             :                 const Box& bhi = amrex::adjCellHi(vbx, idim);
     595             :                 bool outside_domain_lo = !(domain.contains(blo));
     596             :                 bool outside_domain_hi = !(domain.contains(bhi));
     597             :                 if ((!outside_domain_lo) && (!outside_domain_hi)) { continue; }
     598             :                 for (int icomp = 0; icomp < ncomp; ++icomp) {
     599             :                     Array4<RT> const& rbc = (*m_robin_bcval[amrlev])[mfi].array(icomp*3);
     600             :                     if (this->m_lobc_orig[icomp][idim] == LinOpBCType::Robin && outside_domain_lo)
     601             :                     {
     602             :                         AMREX_HOST_DEVICE_PARALLEL_FOR_3D(blo, i, j, k,
     603             :                         {
     604             :                             rbc(i,j,k,0) = ra(i,j,k,icomp);
     605             :                             rbc(i,j,k,1) = rb(i,j,k,icomp);
     606             :                             rbc(i,j,k,2) = rf(i,j,k,icomp);
     607             :                         });
     608             :                     }
     609             :                     if (this->m_hibc_orig[icomp][idim] == LinOpBCType::Robin && outside_domain_hi)
     610             :                     {
     611             :                         AMREX_HOST_DEVICE_PARALLEL_FOR_3D(bhi, i, j, k,
     612             :                         {
     613             :                             rbc(i,j,k,0) = ra(i,j,k,icomp);
     614             :                             rbc(i,j,k,1) = rb(i,j,k,icomp);
     615             :                             rbc(i,j,k,2) = rf(i,j,k,icomp);
     616             :                         });
     617             :                     }
     618             :                 }
     619             :             }
     620             :         }
     621             :     }
     622             : }
     623             : 
     624             : template <typename MF>
     625             : void
     626             : MLCellLinOpT<MF>::update ()
     627             : {
     628             :     if (MLLinOpT<MF>::needsUpdate()) { MLLinOpT<MF>::update(); }
     629             : }
     630             : 
     631             : template <typename MF>
     632             : void
     633             : MLCellLinOpT<MF>::updateSolBC (int amrlev, const MF& crse_bcdata) const
     634             : {
     635             :     BL_PROFILE("MLCellLinOp::updateSolBC()");
     636             : 
     637             :     AMREX_ALWAYS_ASSERT(amrlev > 0);
     638             :     const int ncomp = this->getNComp();
     639             :     m_crse_sol_br[amrlev]->copyFrom(crse_bcdata, 0, 0, 0, ncomp,
     640             :                                     this->m_geom[amrlev-1][0].periodicity());
     641             :     m_bndry_sol[amrlev]->updateBndryValues(*m_crse_sol_br[amrlev], 0, 0, ncomp,
     642             :                                            IntVect(this->m_amr_ref_ratio[amrlev-1]));
     643             : }
     644             : 
     645             : template <typename MF>
     646             : void
     647             : MLCellLinOpT<MF>::updateCorBC (int amrlev, const MF& crse_bcdata) const
     648             : {
     649             :     BL_PROFILE("MLCellLinOp::updateCorBC()");
     650             :     AMREX_ALWAYS_ASSERT(amrlev > 0);
     651             :     const int ncomp = this->getNComp();
     652             :     m_crse_cor_br[amrlev]->copyFrom(crse_bcdata, 0, 0, 0, ncomp,
     653             :                                     this->m_geom[amrlev-1][0].periodicity());
     654             :     m_bndry_cor[amrlev]->updateBndryValues(*m_crse_cor_br[amrlev], 0, 0, ncomp,
     655             :                                            IntVect(this->m_amr_ref_ratio[amrlev-1]));
     656             : }
     657             : 
     658             : template <typename MF>
     659             : void
     660             : MLCellLinOpT<MF>::applyBC (int amrlev, int mglev, MF& in, BCMode bc_mode, StateMode,
     661             :                            const MLMGBndryT<MF>* bndry, bool skip_fillboundary) const
     662             : {
     663             :     BL_PROFILE("MLCellLinOp::applyBC()");
     664             :     // No coarsened boundary values, cannot apply inhomog at mglev>0.
     665             :     BL_ASSERT(mglev == 0 || bc_mode == BCMode::Homogeneous);
     666             :     BL_ASSERT(bndry != nullptr || bc_mode == BCMode::Homogeneous);
     667             : 
     668             :     const int ncomp = this->getNComp();
     669             :     const int cross = isCrossStencil();
     670             :     const int tensorop = isTensorOp();
     671             :     if (!skip_fillboundary) {
     672             :         in.FillBoundary(0, ncomp, this->m_geom[amrlev][mglev].periodicity(), cross);
     673             :     }
     674             : 
     675             :     int flagbc = bc_mode == BCMode::Inhomogeneous;
     676             :     const int imaxorder = this->maxorder;
     677             : 
     678             :     const Real* dxinv = this->m_geom[amrlev][mglev].InvCellSize();
     679             :     const RT dxi = static_cast<RT>(dxinv[0]);
     680             :     const RT dyi = (AMREX_SPACEDIM >= 2) ? static_cast<RT>(dxinv[1]) : RT(1.0);
     681             :     const RT dzi = (AMREX_SPACEDIM == 3) ? static_cast<RT>(dxinv[2]) : RT(1.0);
     682             : 
     683             :     const auto& maskvals = m_maskvals[amrlev][mglev];
     684             :     const auto& bcondloc = *m_bcondloc[amrlev][mglev];
     685             : 
     686             :     FAB foofab(Box::TheUnitBox(),ncomp);
     687             :     const auto& foo = foofab.const_array();
     688             : 
     689             :     MFItInfo mfi_info;
     690             :     if (Gpu::notInLaunchRegion()) { mfi_info.SetDynamic(true); }
     691             : 
     692             :     AMREX_ALWAYS_ASSERT_WITH_MESSAGE(cross || tensorop || Gpu::notInLaunchRegion(),
     693             :                                      "non-cross stencil not support for gpu");
     694             : 
     695             :     const int hidden_direction = this->hiddenDirection();
     696             : 
     697             : #ifdef AMREX_USE_GPU
     698             :     if ((cross || tensorop) && Gpu::inLaunchRegion())
     699             :     {
     700             :         Vector<MLMGABCTag<RT>> tags;
     701             :         tags.reserve(in.local_size()*AMREX_SPACEDIM*ncomp);
     702             : 
     703             :         for (MFIter mfi(in); mfi.isValid(); ++mfi) {
     704             :             const Box& vbx = mfi.validbox();
     705             :             const auto& iofab = in.array(mfi);
     706             :             const auto & bdlv = bcondloc.bndryLocs(mfi);
     707             :             const auto & bdcv = bcondloc.bndryConds(mfi);
     708             : 
     709             :             for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
     710             :                 if (idim != hidden_direction) {
     711             :                     const Orientation olo(idim,Orientation::low);
     712             :                     const Orientation ohi(idim,Orientation::high);
     713             :                     const auto& bvlo = (bndry != nullptr) ?
     714             :                         bndry->bndryValues(olo).const_array(mfi) : foo;
     715             :                     const auto& bvhi = (bndry != nullptr) ?
     716             :                         bndry->bndryValues(ohi).const_array(mfi) : foo;
     717             :                     for (int icomp = 0; icomp < ncomp; ++icomp) {
     718             :                         tags.emplace_back(MLMGABCTag<RT>{iofab, bvlo, bvhi,
     719             :                                                  maskvals[olo].const_array(mfi),
     720             :                                                  maskvals[ohi].const_array(mfi),
     721             :                                                  bdlv[icomp][olo], bdlv[icomp][ohi],
     722             :                                                  amrex::adjCell(vbx,olo),
     723             :                                                  bdcv[icomp][olo], bdcv[icomp][ohi],
     724             :                                                  vbx.length(idim), icomp, idim});
     725             :                     }
     726             :                 }
     727             :             }
     728             :         }
     729             : 
     730             :         ParallelFor(tags,
     731             :         [=] AMREX_GPU_DEVICE (int i, int j, int k, MLMGABCTag<RT> const& tag) noexcept
     732             :         {
     733             :             if (tag.dir == 0)
     734             :             {
     735             :                 mllinop_apply_bc_x(0, i, j, k, tag.blen, tag.fab,
     736             :                                    tag.mask_lo, tag.bctype_lo, tag.bcloc_lo, tag.bcval_lo,
     737             :                                    imaxorder, dxi, flagbc, tag.comp);
     738             :                 mllinop_apply_bc_x(1, i+tag.blen+1, j, k, tag.blen, tag.fab,
     739             :                                    tag.mask_hi, tag.bctype_hi, tag.bcloc_hi, tag.bcval_hi,
     740             :                                    imaxorder, dxi, flagbc, tag.comp);
     741             :             }
     742             : #if (AMREX_SPACEDIM > 1)
     743             :             else
     744             : #if (AMREX_SPACEDIM > 2)
     745             :             if (tag.dir == 1)
     746             : #endif
     747             :             {
     748             :                 mllinop_apply_bc_y(0, i, j, k, tag.blen, tag.fab,
     749             :                                    tag.mask_lo, tag.bctype_lo, tag.bcloc_lo, tag.bcval_lo,
     750             :                                    imaxorder, dyi, flagbc, tag.comp);
     751             :                 mllinop_apply_bc_y(1, i, j+tag.blen+1, k, tag.blen, tag.fab,
     752             :                                    tag.mask_hi, tag.bctype_hi, tag.bcloc_hi, tag.bcval_hi,
     753             :                                    imaxorder, dyi, flagbc, tag.comp);
     754             :             }
     755             : #if (AMREX_SPACEDIM > 2)
     756             :             else {
     757             :                 mllinop_apply_bc_z(0, i, j, k, tag.blen, tag.fab,
     758             :                                    tag.mask_lo, tag.bctype_lo, tag.bcloc_lo, tag.bcval_lo,
     759             :                                    imaxorder, dzi, flagbc, tag.comp);
     760             :                 mllinop_apply_bc_z(1, i, j, k+tag.blen+1, tag.blen, tag.fab,
     761             :                                    tag.mask_hi, tag.bctype_hi, tag.bcloc_hi, tag.bcval_hi,
     762             :                                    imaxorder, dzi, flagbc, tag.comp);
     763             :             }
     764             : #endif
     765             : #endif
     766             :         });
     767             :     } else
     768             : #endif
     769             :     if (cross || tensorop)
     770             :     {
     771             : #ifdef AMREX_USE_OMP
     772             : #pragma omp parallel if (Gpu::notInLaunchRegion())
     773             : #endif
     774             :         for (MFIter mfi(in, mfi_info); mfi.isValid(); ++mfi)
     775             :         {
     776             :             const Box& vbx   = mfi.validbox();
     777             :             const auto& iofab = in.array(mfi);
     778             : 
     779             :             const auto & bdlv = bcondloc.bndryLocs(mfi);
     780             :             const auto & bdcv = bcondloc.bndryConds(mfi);
     781             : 
     782             :             for (int idim = 0; idim < AMREX_SPACEDIM; ++idim)
     783             :             {
     784             :                 if (hidden_direction == idim) { continue; }
     785             :                 const Orientation olo(idim,Orientation::low);
     786             :                 const Orientation ohi(idim,Orientation::high);
     787             :                 const Box blo = amrex::adjCellLo(vbx, idim);
     788             :                 const Box bhi = amrex::adjCellHi(vbx, idim);
     789             :                 const int blen = vbx.length(idim);
     790             :                 const auto& mlo = maskvals[olo].array(mfi);
     791             :                 const auto& mhi = maskvals[ohi].array(mfi);
     792             :                 const auto& bvlo = (bndry != nullptr) ? bndry->bndryValues(olo).const_array(mfi) : foo;
     793             :                 const auto& bvhi = (bndry != nullptr) ? bndry->bndryValues(ohi).const_array(mfi) : foo;
     794             :                 for (int icomp = 0; icomp < ncomp; ++icomp) {
     795             :                     const BoundCond bctlo = bdcv[icomp][olo];
     796             :                     const BoundCond bcthi = bdcv[icomp][ohi];
     797             :                     const RT bcllo = bdlv[icomp][olo];
     798             :                     const RT bclhi = bdlv[icomp][ohi];
     799             :                     if (idim == 0) {
     800             :                         mllinop_apply_bc_x(0, blo, blen, iofab, mlo,
     801             :                                            bctlo, bcllo, bvlo,
     802             :                                            imaxorder, dxi, flagbc, icomp);
     803             :                         mllinop_apply_bc_x(1, bhi, blen, iofab, mhi,
     804             :                                            bcthi, bclhi, bvhi,
     805             :                                            imaxorder, dxi, flagbc, icomp);
     806             :                     } else if (idim == 1) {
     807             :                         mllinop_apply_bc_y(0, blo, blen, iofab, mlo,
     808             :                                            bctlo, bcllo, bvlo,
     809             :                                            imaxorder, dyi, flagbc, icomp);
     810             :                         mllinop_apply_bc_y(1, bhi, blen, iofab, mhi,
     811             :                                            bcthi, bclhi, bvhi,
     812             :                                            imaxorder, dyi, flagbc, icomp);
     813             :                     } else {
     814             :                         mllinop_apply_bc_z(0, blo, blen, iofab, mlo,
     815             :                                            bctlo, bcllo, bvlo,
     816             :                                            imaxorder, dzi, flagbc, icomp);
     817             :                         mllinop_apply_bc_z(1, bhi, blen, iofab, mhi,
     818             :                                            bcthi, bclhi, bvhi,
     819             :                                            imaxorder, dzi, flagbc, icomp);
     820             :                     }
     821             :                 }
     822             :             }
     823             :         }
     824             :     }
     825             :     else
     826             :     {
     827             : #ifdef BL_NO_FORT
     828             :         amrex::Abort("amrex_mllinop_apply_bc not available when BL_NO_FORT=TRUE");
     829             : #else
     830             :         if constexpr (std::is_same_v<Real,RT>) {
     831             : #ifdef AMREX_USE_OMP
     832             : #pragma omp parallel
     833             : #endif
     834             :             for (MFIter mfi(in, mfi_info); mfi.isValid(); ++mfi)
     835             :             {
     836             :                 const Box& vbx   = mfi.validbox();
     837             : 
     838             :                 const auto & bdlv = bcondloc.bndryLocs(mfi);
     839             :                 const auto & bdcv = bcondloc.bndryConds(mfi);
     840             : 
     841             :                 const RealTuple & bdl = bdlv[0];
     842             :                 const BCTuple   & bdc = bdcv[0];
     843             : 
     844             :                 for (OrientationIter oitr; oitr; ++oitr)
     845             :                 {
     846             :                     const Orientation ori = oitr();
     847             : 
     848             :                     int cdr = ori;
     849             :                     RT  bcl = bdl[ori];
     850             :                     int bct = bdc[ori];
     851             : 
     852             :                     const auto& fsfab = (bndry != nullptr) ? bndry->bndryValues(ori)[mfi] : foofab;
     853             : 
     854             :                     const Mask& m = maskvals[ori][mfi];
     855             : 
     856             :                     amrex_mllinop_apply_bc(BL_TO_FORTRAN_BOX(vbx),
     857             :                                            BL_TO_FORTRAN_ANYD(in[mfi]),
     858             :                                            BL_TO_FORTRAN_ANYD(m),
     859             :                                            cdr, bct, bcl,
     860             :                                            BL_TO_FORTRAN_ANYD(fsfab),
     861             :                                            imaxorder, dxinv, flagbc, ncomp, cross);
     862             :                 }
     863             :             }
     864             :         } else {
     865             :             amrex::Abort("Not supported");
     866             :         }
     867             : #endif
     868             :     }
     869             : }
     870             : 
     871             : template <typename MF>
     872             : BoxArray
     873             : MLCellLinOpT<MF>::makeNGrids (int grid_size) const
     874             : {
     875             :     const Box& dombx = this->m_geom[0].back().Domain();
     876             : 
     877             :     const BoxArray& old_ba = this->m_grids[0].back();
     878             :     const int N = old_ba.size();
     879             :     Vector<Box> bv;
     880             :     bv.reserve(N);
     881             :     for (int i = 0; i < N; ++i)
     882             :     {
     883             :         Box b = old_ba[i];
     884             :         b.coarsen(grid_size);
     885             :         b.refine(grid_size);
     886             :         IntVect sz = b.size();
     887             :         const IntVect nblks {AMREX_D_DECL(sz[0]/grid_size, sz[1]/grid_size, sz[2]/grid_size)};
     888             : 
     889             :         IntVect big = b.smallEnd() + grid_size - 1;
     890             :         b.setBig(big);
     891             : 
     892             : #if (AMREX_SPACEDIM == 3)
     893             :         for (int kk = 0; kk < nblks[2]; ++kk) {
     894             : #endif
     895             : #if (AMREX_SPACEDIM >= 2)
     896             :             for (int jj = 0; jj < nblks[1]; ++jj) {
     897             : #endif
     898             :                 for (int ii = 0; ii < nblks[0]; ++ii)
     899             :                 {
     900             :                     IntVect shft{AMREX_D_DECL(ii*grid_size,jj*grid_size,kk*grid_size)};
     901             :                     Box bb = amrex::shift(b,shft);
     902             :                     bb &= dombx;
     903             :                     bv.push_back(bb);
     904             :                 }
     905             : #if (AMREX_SPACEDIM >= 2)
     906             :             }
     907             : #endif
     908             : #if (AMREX_SPACEDIM == 3)
     909             :         }
     910             : #endif
     911             :     }
     912             : 
     913             :     std::sort(bv.begin(), bv.end());
     914             :     bv.erase(std::unique(bv.begin(), bv.end()), bv.end());
     915             : 
     916             :     BoxList bl(std::move(bv));
     917             : 
     918             :     return BoxArray{std::move(bl)};
     919             : }
     920             : 
     921             : template <typename MF>
     922             : void
     923             : MLCellLinOpT<MF>::restriction (int amrlev, int cmglev, MF& crse, MF& fine) const
     924             : {
     925             :     const int ncomp = this->getNComp();
     926             :     IntVect ratio = (amrlev > 0) ? IntVect(2) : this->mg_coarsen_ratio_vec[cmglev-1];
     927             :     amrex::average_down(fine, crse, 0, ncomp, ratio);
     928             : }
     929             : 
     930             : template <typename MF>
     931             : void
     932             : MLCellLinOpT<MF>::interpolation (int amrlev, int fmglev, MF& fine, const MF& crse) const
     933             : {
     934             :     const int ncomp = this->getNComp();
     935             : 
     936             :     Dim3 ratio3 = {2,2,2};
     937             :     IntVect ratio = (amrlev > 0) ? IntVect(2) : this->mg_coarsen_ratio_vec[fmglev];
     938             :     AMREX_D_TERM(ratio3.x = ratio[0];,
     939             :                  ratio3.y = ratio[1];,
     940             :                  ratio3.z = ratio[2];);
     941             : 
     942             : #ifdef AMREX_USE_GPU
     943             :     if (Gpu::inLaunchRegion() && fine.isFusingCandidate()) {
     944             :         auto const& finema = fine.arrays();
     945             :         auto const& crsema = crse.const_arrays();
     946             :         ParallelFor(fine, IntVect(0), ncomp,
     947             :         [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
     948             :         {
     949             :             int ic = amrex::coarsen(i,ratio3.x);
     950             :             int jc = amrex::coarsen(j,ratio3.y);
     951             :             int kc = amrex::coarsen(k,ratio3.z);
     952             :             finema[box_no](i,j,k,n) += crsema[box_no](ic,jc,kc,n);
     953             :         });
     954             :         Gpu::streamSynchronize();
     955             :     } else
     956             : #endif
     957             :     {
     958             : #ifdef AMREX_USE_OMP
     959             : #pragma omp parallel if (Gpu::notInLaunchRegion())
     960             : #endif
     961             :         for (MFIter mfi(fine,TilingIfNotGPU()); mfi.isValid(); ++mfi)
     962             :         {
     963             :             const Box& bx    = mfi.tilebox();
     964             :             Array4<RT const> const& cfab = crse.const_array(mfi);
     965             :             Array4<RT> const& ffab = fine.array(mfi);
     966             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( bx, ncomp, i, j, k, n,
     967             :             {
     968             :                 int ic = amrex::coarsen(i,ratio3.x);
     969             :                 int jc = amrex::coarsen(j,ratio3.y);
     970             :                 int kc = amrex::coarsen(k,ratio3.z);
     971             :                 ffab(i,j,k,n) += cfab(ic,jc,kc,n);
     972             :             });
     973             :         }
     974             :     }
     975             : }
     976             : 
     977             : template <typename MF>
     978             : void
     979             : MLCellLinOpT<MF>::interpAssign (int amrlev, int fmglev, MF& fine, MF& crse) const
     980             : {
     981             :     const int ncomp = this->getNComp();
     982             : 
     983             :     const Geometry& crse_geom = this->Geom(amrlev,fmglev+1);
     984             :     const IntVect refratio = (amrlev > 0) ? IntVect(2) : this->mg_coarsen_ratio_vec[fmglev];
     985             :     const IntVect ng = crse.nGrowVect();
     986             : 
     987             :     MF cfine;
     988             :     const MF* cmf;
     989             : 
     990             :     if (amrex::isMFIterSafe(crse, fine))
     991             :     {
     992             :         crse.FillBoundary(crse_geom.periodicity());
     993             :         cmf = &crse;
     994             :     }
     995             :     else
     996             :     {
     997             :         BoxArray cba = fine.boxArray();
     998             :         cba.coarsen(refratio);
     999             :         cfine.define(cba, fine.DistributionMap(), ncomp, ng);
    1000             :         cfine.setVal(RT(0.0));
    1001             :         cfine.ParallelCopy(crse, 0, 0, ncomp, IntVect(0), ng, crse_geom.periodicity());
    1002             :         cmf = & cfine;
    1003             :     }
    1004             : 
    1005             :     bool isEB = fine.hasEBFabFactory();
    1006             :     ignore_unused(isEB);
    1007             : 
    1008             : #ifdef AMREX_USE_EB
    1009             :     const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(&(fine.Factory()));
    1010             :     const FabArray<EBCellFlagFab>* flags = (factory) ? &(factory->getMultiEBCellFlagFab()) : nullptr;
    1011             : #endif
    1012             : 
    1013             :     MFItInfo mfi_info;
    1014             :     if (Gpu::notInLaunchRegion()) { mfi_info.EnableTiling().SetDynamic(true); }
    1015             : #ifdef AMREX_USE_OMP
    1016             : #pragma omp parallel if (Gpu::notInLaunchRegion())
    1017             : #endif
    1018             :     for (MFIter mfi(fine, mfi_info); mfi.isValid(); ++mfi)
    1019             :     {
    1020             :         const Box& bx = mfi.tilebox();
    1021             :         const auto& ff = fine.array(mfi);
    1022             :         const auto& cc = cmf->array(mfi);
    1023             : #ifdef AMREX_USE_EB
    1024             :         bool call_lincc;
    1025             :         if (isEB)
    1026             :         {
    1027             :             const auto& flag = (*flags)[mfi];
    1028             :             if (flag.getType(amrex::grow(bx,1)) == FabType::regular) {
    1029             :                 call_lincc = true;
    1030             :             } else {
    1031             :                 Array4<EBCellFlag const> const& flg = flag.const_array();
    1032             :                 AMREX_LAUNCH_HOST_DEVICE_LAMBDA (bx, tbx,
    1033             :                 {
    1034             :                     mlmg_eb_cc_interp_r<2>(tbx, ff, cc, flg, ncomp);
    1035             :                 });
    1036             : 
    1037             :                 call_lincc = false;
    1038             :             }
    1039             :         }
    1040             :         else
    1041             :         {
    1042             :             call_lincc = true;
    1043             :         }
    1044             : #else
    1045             :         const bool call_lincc = true;
    1046             : #endif
    1047             :         if (call_lincc)
    1048             :         {
    1049             : #if (AMREX_SPACEDIM == 3)
    1050             :             if (this->hasHiddenDimension()) {
    1051             :                 Box const& bx_2d = this->compactify(bx);
    1052             :                 auto const& ff_2d = this->compactify(ff);
    1053             :                 auto const& cc_2d = this->compactify(cc);
    1054             :                 AMREX_LAUNCH_HOST_DEVICE_LAMBDA (bx_2d, tbx,
    1055             :                 {
    1056             :                     TwoD::mlmg_lin_cc_interp_r2(tbx, ff_2d, cc_2d, ncomp);
    1057             :                 });
    1058             :             } else
    1059             : #endif
    1060             :             {
    1061             :                 AMREX_LAUNCH_HOST_DEVICE_LAMBDA (bx, tbx,
    1062             :                 {
    1063             :                     mlmg_lin_cc_interp_r2(tbx, ff, cc, ncomp);
    1064             :                 });
    1065             :             }
    1066             :         }
    1067             :     }
    1068             : }
    1069             : 
    1070             : template <typename MF>
    1071             : void
    1072             : MLCellLinOpT<MF>::interpolationAmr (int famrlev, MF& fine, const MF& crse,
    1073             :                                     IntVect const& /*nghost*/) const
    1074             : {
    1075             :     const int ncomp = this->getNComp();
    1076             :     const int refratio = this->AMRRefRatio(famrlev-1);
    1077             : 
    1078             : #ifdef AMREX_USE_EB
    1079             :     const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(this->Factory(famrlev));
    1080             :     const FabArray<EBCellFlagFab>* flags = (factory) ? &(factory->getMultiEBCellFlagFab()) : nullptr;
    1081             : #endif
    1082             : 
    1083             :     MFItInfo mfi_info;
    1084             :     if (Gpu::notInLaunchRegion()) { mfi_info.EnableTiling().SetDynamic(true); }
    1085             : #ifdef AMREX_USE_OMP
    1086             : #pragma omp parallel if (Gpu::notInLaunchRegion())
    1087             : #endif
    1088             :     for (MFIter mfi(fine, mfi_info); mfi.isValid(); ++mfi)
    1089             :     {
    1090             :         const Box& bx = mfi.tilebox();
    1091             :         auto const& ff = fine.array(mfi);
    1092             :         auto const& cc = crse.const_array(mfi);
    1093             : #ifdef AMREX_USE_EB
    1094             :         bool call_lincc;
    1095             :         if (factory)
    1096             :         {
    1097             :             const auto& flag = (*flags)[mfi];
    1098             :             if (flag.getType(amrex::grow(bx,1)) == FabType::regular) {
    1099             :                 call_lincc = true;
    1100             :             } else {
    1101             :                 Array4<EBCellFlag const> const& flg = flag.const_array();
    1102             :                 switch(refratio) {
    1103             :                 case 2:
    1104             :                 {
    1105             :                     AMREX_LAUNCH_HOST_DEVICE_LAMBDA (bx, tbx,
    1106             :                     {
    1107             :                         mlmg_eb_cc_interp_r<2>(tbx, ff, cc, flg, ncomp);
    1108             :                     });
    1109             :                     break;
    1110             :                 }
    1111             :                 case 4:
    1112             :                 {
    1113             :                     AMREX_LAUNCH_HOST_DEVICE_LAMBDA (bx, tbx,
    1114             :                     {
    1115             :                         mlmg_eb_cc_interp_r<4>(tbx, ff, cc, flg, ncomp);
    1116             :                     });
    1117             :                     break;
    1118             :                 }
    1119             :                 default:
    1120             :                     amrex::Abort("mlmg_eb_cc_interp: only refratio 2 and 4 are supported");
    1121             :                 }
    1122             : 
    1123             :                 call_lincc = false;
    1124             :             }
    1125             :         }
    1126             :         else
    1127             :         {
    1128             :             call_lincc = true;
    1129             :         }
    1130             : #else
    1131             :         const bool call_lincc = true;
    1132             : #endif
    1133             :         if (call_lincc)
    1134             :         {
    1135             :             switch(refratio) {
    1136             :             case 2:
    1137             :             {
    1138             :                 AMREX_LAUNCH_HOST_DEVICE_LAMBDA (bx, tbx,
    1139             :                 {
    1140             :                     mlmg_lin_cc_interp_r2(tbx, ff, cc, ncomp);
    1141             :                 });
    1142             :                 break;
    1143             :             }
    1144             :             case 4:
    1145             :             {
    1146             :                 AMREX_LAUNCH_HOST_DEVICE_LAMBDA (bx, tbx,
    1147             :                 {
    1148             :                     mlmg_lin_cc_interp_r4(tbx, ff, cc, ncomp);
    1149             :                 });
    1150             :                 break;
    1151             :             }
    1152             :             default:
    1153             :                 amrex::Abort("mlmg_lin_cc_interp: only refratio 2 and 4 are supported");
    1154             :             }
    1155             :         }
    1156             :     }
    1157             : }
    1158             : 
    1159             : template <typename MF>
    1160             : void
    1161             : MLCellLinOpT<MF>::averageDownSolutionRHS (int camrlev, MF& crse_sol, MF& crse_rhs,
    1162             :                                           const MF& fine_sol, const MF& fine_rhs)
    1163             : {
    1164             :     const auto amrrr = this->AMRRefRatio(camrlev);
    1165             :     const int ncomp = this->getNComp();
    1166             :     amrex::average_down(fine_sol, crse_sol, 0, ncomp, amrrr);
    1167             :     amrex::average_down(fine_rhs, crse_rhs, 0, ncomp, amrrr);
    1168             : }
    1169             : 
    1170             : template <typename MF>
    1171             : void
    1172             : MLCellLinOpT<MF>::apply (int amrlev, int mglev, MF& out, MF& in, BCMode bc_mode,
    1173             :                     StateMode s_mode, const MLMGBndryT<MF>* bndry) const
    1174             : {
    1175             :     BL_PROFILE("MLCellLinOp::apply()");
    1176             :     applyBC(amrlev, mglev, in, bc_mode, s_mode, bndry);
    1177             :     Fapply(amrlev, mglev, out, in);
    1178             : }
    1179             : 
    1180             : template <typename MF>
    1181             : void
    1182             : MLCellLinOpT<MF>::smooth (int amrlev, int mglev, MF& sol, const MF& rhs,
    1183             :                           bool skip_fillboundary) const
    1184             : {
    1185             :     BL_PROFILE("MLCellLinOp::smooth()");
    1186             :     for (int redblack = 0; redblack < 2; ++redblack)
    1187             :     {
    1188             :         applyBC(amrlev, mglev, sol, BCMode::Homogeneous, StateMode::Solution,
    1189             :                 nullptr, skip_fillboundary);
    1190             :         Fsmooth(amrlev, mglev, sol, rhs, redblack);
    1191             :         skip_fillboundary = false;
    1192             :     }
    1193             : }
    1194             : 
    1195             : template <typename MF>
    1196             : void
    1197             : MLCellLinOpT<MF>::solutionResidual (int amrlev, MF& resid, MF& x, const MF& b,
    1198             :                                     const MF* crse_bcdata)
    1199             : {
    1200             :     BL_PROFILE("MLCellLinOp::solutionResidual()");
    1201             :     const int ncomp = this->getNComp();
    1202             :     if (crse_bcdata != nullptr) {
    1203             :         updateSolBC(amrlev, *crse_bcdata);
    1204             :     }
    1205             :     const int mglev = 0;
    1206             :     apply(amrlev, mglev, resid, x, BCMode::Inhomogeneous, StateMode::Solution,
    1207             :           m_bndry_sol[amrlev].get());
    1208             : 
    1209             :     AMREX_ASSERT(resid.nComp() == b.nComp());
    1210             :     MF::Xpay(resid, RT(-1.0), b, 0, 0, ncomp, IntVect(0));
    1211             : }
    1212             : 
    1213             : template <typename MF>
    1214             : void
    1215             : MLCellLinOpT<MF>::correctionResidual (int amrlev, int mglev, MF& resid, MF& x, const MF& b,
    1216             :                                       BCMode bc_mode, const MF* crse_bcdata)
    1217             : {
    1218             :     BL_PROFILE("MLCellLinOp::correctionResidual()");
    1219             :     const int ncomp = this->getNComp();
    1220             :     if (bc_mode == BCMode::Inhomogeneous)
    1221             :     {
    1222             :         if (crse_bcdata)
    1223             :         {
    1224             :             AMREX_ASSERT(mglev == 0 && amrlev > 0);
    1225             :             updateCorBC(amrlev, *crse_bcdata);
    1226             :         }
    1227             :         apply(amrlev, mglev, resid, x, BCMode::Inhomogeneous, StateMode::Correction,
    1228             :               m_bndry_cor[amrlev].get());
    1229             :     }
    1230             :     else
    1231             :     {
    1232             :         AMREX_ASSERT(crse_bcdata == nullptr);
    1233             :         apply(amrlev, mglev, resid, x, BCMode::Homogeneous, StateMode::Correction, nullptr);
    1234             :     }
    1235             : 
    1236             :     MF::Xpay(resid, Real(-1.0), b, 0, 0, ncomp, IntVect(0));
    1237             : }
    1238             : 
    1239             : template <typename MF>
    1240             : void
    1241             : MLCellLinOpT<MF>::reflux (int crse_amrlev, MF& res, const MF& crse_sol, const MF&,
    1242             :                           MF&, MF& fine_sol, const MF&) const
    1243             : {
    1244             :     BL_PROFILE("MLCellLinOp::reflux()");
    1245             : 
    1246             :     auto& fluxreg = m_fluxreg[crse_amrlev];
    1247             :     fluxreg.reset();
    1248             : 
    1249             :     const int ncomp = this->getNComp();
    1250             : 
    1251             :     const int fine_amrlev = crse_amrlev+1;
    1252             : 
    1253             :     Real dt = Real(1.0);
    1254             :     const Real* crse_dx = this->m_geom[crse_amrlev][0].CellSize();
    1255             :     const Real* fine_dx = this->m_geom[fine_amrlev][0].CellSize();
    1256             : 
    1257             :     const int mglev = 0;
    1258             :     applyBC(fine_amrlev, mglev, fine_sol, BCMode::Inhomogeneous, StateMode::Solution,
    1259             :             m_bndry_sol[fine_amrlev].get());
    1260             : 
    1261             :     MFItInfo mfi_info;
    1262             :     if (Gpu::notInLaunchRegion()) { mfi_info.EnableTiling().SetDynamic(true); }
    1263             : 
    1264             : #ifdef AMREX_USE_OMP
    1265             : #pragma omp parallel if (Gpu::notInLaunchRegion())
    1266             : #endif
    1267             :     {
    1268             :         Array<FAB,AMREX_SPACEDIM> flux;
    1269             :         Array<FAB*,AMREX_SPACEDIM> pflux {{ AMREX_D_DECL(flux.data(), flux.data()+1, flux.data()+2) }};
    1270             :         Array<FAB const*,AMREX_SPACEDIM> cpflux {{ AMREX_D_DECL(flux.data(), flux.data()+1, flux.data()+2) }};
    1271             : 
    1272             :         for (MFIter mfi(crse_sol, mfi_info);  mfi.isValid(); ++mfi)
    1273             :         {
    1274             :             if (fluxreg.CrseHasWork(mfi))
    1275             :             {
    1276             :                 const Box& tbx = mfi.tilebox();
    1277             :                 AMREX_D_TERM(flux[0].resize(amrex::surroundingNodes(tbx,0),ncomp);,
    1278             :                              flux[1].resize(amrex::surroundingNodes(tbx,1),ncomp);,
    1279             :                              flux[2].resize(amrex::surroundingNodes(tbx,2),ncomp););
    1280             :                 AMREX_D_TERM(Elixir elifx = flux[0].elixir();,
    1281             :                              Elixir elify = flux[1].elixir();,
    1282             :                              Elixir elifz = flux[2].elixir(););
    1283             :                 FFlux(crse_amrlev, mfi, pflux, crse_sol[mfi], Location::FaceCentroid);
    1284             :                 fluxreg.CrseAdd(mfi, cpflux, crse_dx, dt, RunOn::Gpu);
    1285             :             }
    1286             :         }
    1287             : 
    1288             : #ifdef AMREX_USE_OMP
    1289             : #pragma omp barrier
    1290             : #endif
    1291             : 
    1292             :         for (MFIter mfi(fine_sol, mfi_info);  mfi.isValid(); ++mfi)
    1293             :         {
    1294             :             if (fluxreg.FineHasWork(mfi))
    1295             :             {
    1296             :                 const Box& tbx = mfi.tilebox();
    1297             :                 const int face_only = true;
    1298             :                 AMREX_D_TERM(flux[0].resize(amrex::surroundingNodes(tbx,0),ncomp);,
    1299             :                              flux[1].resize(amrex::surroundingNodes(tbx,1),ncomp);,
    1300             :                              flux[2].resize(amrex::surroundingNodes(tbx,2),ncomp););
    1301             :                 AMREX_D_TERM(Elixir elifx = flux[0].elixir();,
    1302             :                              Elixir elify = flux[1].elixir();,
    1303             :                              Elixir elifz = flux[2].elixir(););
    1304             :                 FFlux(fine_amrlev, mfi, pflux, fine_sol[mfi], Location::FaceCentroid, face_only);
    1305             :                 fluxreg.FineAdd(mfi, cpflux, fine_dx, dt, RunOn::Gpu);
    1306             :             }
    1307             :         }
    1308             :     }
    1309             : 
    1310             :     fluxreg.Reflux(res);
    1311             : }
    1312             : 
    1313             : template <typename MF>
    1314             : void
    1315             : MLCellLinOpT<MF>::compFlux (int amrlev, const Array<MF*,AMREX_SPACEDIM>& fluxes,
    1316             :                             MF& sol, Location loc) const
    1317             : {
    1318             :     BL_PROFILE("MLCellLinOp::compFlux()");
    1319             : 
    1320             :     const int mglev = 0;
    1321             :     const int ncomp = this->getNComp();
    1322             :     applyBC(amrlev, mglev, sol, BCMode::Inhomogeneous, StateMode::Solution,
    1323             :             m_bndry_sol[amrlev].get());
    1324             : 
    1325             :     MFItInfo mfi_info;
    1326             :     if (Gpu::notInLaunchRegion()) { mfi_info.EnableTiling().SetDynamic(true); }
    1327             : 
    1328             : #ifdef AMREX_USE_OMP
    1329             : #pragma omp parallel if (Gpu::notInLaunchRegion())
    1330             : #endif
    1331             :     {
    1332             :         Array<FAB,AMREX_SPACEDIM> flux;
    1333             :         Array<FAB*,AMREX_SPACEDIM> pflux {{ AMREX_D_DECL(flux.data(), flux.data()+1, flux.data()+2) }};
    1334             :         for (MFIter mfi(sol, mfi_info);  mfi.isValid(); ++mfi)
    1335             :         {
    1336             :             const Box& tbx = mfi.tilebox();
    1337             :             AMREX_D_TERM(flux[0].resize(amrex::surroundingNodes(tbx,0),ncomp);,
    1338             :                          flux[1].resize(amrex::surroundingNodes(tbx,1),ncomp);,
    1339             :                          flux[2].resize(amrex::surroundingNodes(tbx,2),ncomp););
    1340             :             AMREX_D_TERM(Elixir elifx = flux[0].elixir();,
    1341             :                          Elixir elify = flux[1].elixir();,
    1342             :                          Elixir elifz = flux[2].elixir(););
    1343             :             FFlux(amrlev, mfi, pflux, sol[mfi], loc);
    1344             :             for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
    1345             :                 const Box& nbx = mfi.nodaltilebox(idim);
    1346             :                 auto const& dst = fluxes[idim]->array(mfi);
    1347             :                 auto const& src =  pflux[idim]->const_array();
    1348             :                 AMREX_HOST_DEVICE_PARALLEL_FOR_4D (nbx, ncomp, i, j, k, n,
    1349             :                 {
    1350             :                     dst(i,j,k,n) = src(i,j,k,n);
    1351             :                 });
    1352             :             }
    1353             :         }
    1354             :     }
    1355             : }
    1356             : 
    1357             : template <typename MF>
    1358             : void
    1359             : MLCellLinOpT<MF>::compGrad (int amrlev, const Array<MF*,AMREX_SPACEDIM>& grad,
    1360             :                             MF& sol, Location /*loc*/) const
    1361             : {
    1362             :     BL_PROFILE("MLCellLinOp::compGrad()");
    1363             : 
    1364             :     if (sol.nComp() > 1) {
    1365             :         amrex::Abort("MLCellLinOp::compGrad called, but only works for single-component solves");
    1366             :     }
    1367             : 
    1368             :     const int mglev = 0;
    1369             :     applyBC(amrlev, mglev, sol, BCMode::Inhomogeneous, StateMode::Solution,
    1370             :             m_bndry_sol[amrlev].get());
    1371             : 
    1372             :     const int ncomp = this->getNComp();
    1373             : 
    1374             :     AMREX_D_TERM(const RT dxi = static_cast<RT>(this->m_geom[amrlev][mglev].InvCellSize(0));,
    1375             :                  const RT dyi = static_cast<RT>(this->m_geom[amrlev][mglev].InvCellSize(1));,
    1376             :                  const RT dzi = static_cast<RT>(this->m_geom[amrlev][mglev].InvCellSize(2)););
    1377             : #ifdef AMREX_USE_OMP
    1378             : #pragma omp parallel if (Gpu::notInLaunchRegion())
    1379             : #endif
    1380             :     for (MFIter mfi(sol, TilingIfNotGPU());  mfi.isValid(); ++mfi)
    1381             :     {
    1382             :         AMREX_D_TERM(const Box& xbx = mfi.nodaltilebox(0);,
    1383             :                      const Box& ybx = mfi.nodaltilebox(1);,
    1384             :                      const Box& zbx = mfi.nodaltilebox(2););
    1385             :         const auto& s = sol.array(mfi);
    1386             :         AMREX_D_TERM(const auto& gx = grad[0]->array(mfi);,
    1387             :                      const auto& gy = grad[1]->array(mfi);,
    1388             :                      const auto& gz = grad[2]->array(mfi););
    1389             : 
    1390             :         AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( xbx, ncomp, i, j, k, n,
    1391             :         {
    1392             :             gx(i,j,k,n) = dxi*(s(i,j,k,n) - s(i-1,j,k,n));
    1393             :         });
    1394             : #if (AMREX_SPACEDIM >= 2)
    1395             :         AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( ybx, ncomp, i, j, k, n,
    1396             :         {
    1397             :             gy(i,j,k,n) = dyi*(s(i,j,k,n) - s(i,j-1,k,n));
    1398             :         });
    1399             : #endif
    1400             : #if (AMREX_SPACEDIM == 3)
    1401             :         AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( zbx, ncomp, i, j, k, n,
    1402             :         {
    1403             :             gz(i,j,k,n) = dzi*(s(i,j,k,n) - s(i,j,k-1,n));
    1404             :         });
    1405             : #endif
    1406             :     }
    1407             : 
    1408             :     addInhomogNeumannFlux(amrlev, grad, sol, false);
    1409             : }
    1410             : 
    1411             : template <typename MF>
    1412             : void
    1413             : MLCellLinOpT<MF>::applyMetricTerm (int amrlev, int mglev, MF& rhs) const
    1414             : {
    1415             :     amrex::ignore_unused(amrlev,mglev,rhs);
    1416             : #if (AMREX_SPACEDIM != 3)
    1417             :     if (!m_has_metric_term) { return; }
    1418             : 
    1419             :     const int ncomp = rhs.nComp();
    1420             : 
    1421             :     bool cc = rhs.ixType().cellCentered(0);
    1422             : 
    1423             :     const Geometry& geom = this->m_geom[amrlev][mglev];
    1424             :     const RT dx = static_cast<RT>(geom.CellSize(0));
    1425             :     const RT probxlo = static_cast<RT>(geom.ProbLo(0));
    1426             : 
    1427             : #ifdef AMREX_USE_OMP
    1428             : #pragma omp parallel if (Gpu::notInLaunchRegion())
    1429             : #endif
    1430             :     for (MFIter mfi(rhs,TilingIfNotGPU()); mfi.isValid(); ++mfi)
    1431             :     {
    1432             :         const Box& tbx = mfi.tilebox();
    1433             :         auto const& rhsarr = rhs.array(mfi);
    1434             : #if (AMREX_SPACEDIM == 1)
    1435             :         if (cc) {
    1436             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1437             :             {
    1438             :                 RT rc = probxlo + (i+RT(0.5))*dx;
    1439             :                 rhsarr(i,j,k,n) *= rc*rc;
    1440             :             });
    1441             :         } else {
    1442             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1443             :             {
    1444             :                 RT re = probxlo + i*dx;
    1445             :                 rhsarr(i,j,k,n) *= re*re;
    1446             :             });
    1447             :         }
    1448             : #elif (AMREX_SPACEDIM == 2)
    1449             :         if (cc) {
    1450             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1451             :             {
    1452             :                 RT rc = probxlo + (i+RT(0.5))*dx;
    1453             :                 rhsarr(i,j,k,n) *= rc;
    1454             :             });
    1455             :         } else {
    1456             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1457             :             {
    1458             :                 RT re = probxlo + i*dx;
    1459             :                 rhsarr(i,j,k,n) *= re;
    1460             :             });
    1461             :         }
    1462             : #endif
    1463             :     }
    1464             : #endif
    1465             : }
    1466             : 
    1467             : template <typename MF>
    1468             : void
    1469             : MLCellLinOpT<MF>::unapplyMetricTerm (int amrlev, int mglev, MF& rhs) const
    1470             : {
    1471             :     amrex::ignore_unused(amrlev,mglev,rhs);
    1472             : #if (AMREX_SPACEDIM != 3)
    1473             :     if (!m_has_metric_term) { return; }
    1474             : 
    1475             :     const int ncomp = rhs.nComp();
    1476             : 
    1477             :     bool cc = rhs.ixType().cellCentered(0);
    1478             : 
    1479             :     const Geometry& geom = this->m_geom[amrlev][mglev];
    1480             :     const RT dx = static_cast<RT>(geom.CellSize(0));
    1481             :     const RT probxlo = static_cast<RT>(geom.ProbLo(0));
    1482             : 
    1483             : #ifdef AMREX_USE_OMP
    1484             : #pragma omp parallel if (Gpu::notInLaunchRegion())
    1485             : #endif
    1486             :     for (MFIter mfi(rhs,TilingIfNotGPU()); mfi.isValid(); ++mfi)
    1487             :     {
    1488             :         const Box& tbx = mfi.tilebox();
    1489             :         auto const& rhsarr = rhs.array(mfi);
    1490             : #if (AMREX_SPACEDIM == 1)
    1491             :         if (cc) {
    1492             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1493             :             {
    1494             :                 RT rcinv = RT(1.0)/(probxlo + (i+RT(0.5))*dx);
    1495             :                 rhsarr(i,j,k,n) *= rcinv*rcinv;
    1496             :             });
    1497             :         } else {
    1498             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1499             :             {
    1500             :                 RT re = probxlo + i*dx;
    1501             :                 RT reinv = (re==RT(0.0)) ? RT(0.0) : RT(1.)/re;
    1502             :                 rhsarr(i,j,k,n) *= reinv*reinv;
    1503             :             });
    1504             :         }
    1505             : #elif (AMREX_SPACEDIM == 2)
    1506             :         if (cc) {
    1507             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1508             :             {
    1509             :                 RT rcinv = RT(1.0)/(probxlo + (i+RT(0.5))*dx);
    1510             :                 rhsarr(i,j,k,n) *= rcinv;
    1511             :             });
    1512             :         } else {
    1513             :             AMREX_HOST_DEVICE_PARALLEL_FOR_4D ( tbx, ncomp, i, j, k, n,
    1514             :             {
    1515             :                 RT re = probxlo + i*dx;
    1516             :                 RT reinv = (re==RT(0.0)) ? RT(0.0) : RT(1.)/re;
    1517             :                 rhsarr(i,j,k,n) *= reinv;
    1518             :             });
    1519             :         }
    1520             : #endif
    1521             :     }
    1522             : #endif
    1523             : }
    1524             : 
    1525             : template <typename MF>
    1526             : auto
    1527             : MLCellLinOpT<MF>::getSolvabilityOffset (int amrlev, int mglev, MF const& rhs) const
    1528             :     -> Vector<RT>
    1529             : {
    1530             :     computeVolInv();
    1531             : 
    1532             :     const int ncomp = this->getNComp();
    1533             :     Vector<RT> offset(ncomp);
    1534             : 
    1535             : #ifdef AMREX_USE_EB
    1536             :     const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(this->Factory(amrlev,mglev));
    1537             :     if (factory && !factory->isAllRegular())
    1538             :     {
    1539             :         if constexpr (std::is_same<MF,MultiFab>()) {
    1540             :             const MultiFab& vfrac = factory->getVolFrac();
    1541             :             for (int c = 0; c < ncomp; ++c) {
    1542             :                 offset[c] = amrex::Dot(rhs, c, vfrac, 0, 1, IntVect(0), true)
    1543             :                     * m_volinv[amrlev][mglev];
    1544             :             }
    1545             :         } else {
    1546             :             amrex::Abort("TODO: MLMG with EB only works with MultiFab");
    1547             :         }
    1548             :     }
    1549             :     else
    1550             : #endif
    1551             :     {
    1552             :         for (int c = 0; c < ncomp; ++c) {
    1553             :             offset[c] = rhs.sum(c,IntVect(0),true) * m_volinv[amrlev][mglev];
    1554             :         }
    1555             :     }
    1556             : 
    1557             :     ParallelAllReduce::Sum(offset.data(), ncomp, ParallelContext::CommunicatorSub());
    1558             : 
    1559             :     return offset;
    1560             : }
    1561             : 
    1562             : template <typename MF>
    1563             : void
    1564             : MLCellLinOpT<MF>::fixSolvabilityByOffset (int /*amrlev*/, int /*mglev*/, MF& rhs,
    1565             :                                           Vector<RT> const& offset) const
    1566             : {
    1567             :     const int ncomp = this->getNComp();
    1568             :     for (int c = 0; c < ncomp; ++c) {
    1569             :         rhs.plus(-offset[c], c, 1);
    1570             :     }
    1571             : #ifdef AMREX_USE_EB
    1572             :     if (!rhs.isAllRegular()) {
    1573             :         if constexpr (std::is_same<MF,MultiFab>()) {
    1574             :             amrex::EB_set_covered(rhs, 0, ncomp, 0, 0.0_rt);
    1575             :         } else {
    1576             :             amrex::Abort("amrex::EB_set_covered only works with MultiFab");
    1577             :         }
    1578             :     }
    1579             : #endif
    1580             : }
    1581             : 
    1582             : template <typename MF>
    1583             : void
    1584             : MLCellLinOpT<MF>::prepareForSolve ()
    1585             : {
    1586             :     BL_PROFILE("MLCellLinOp::prepareForSolve()");
    1587             : 
    1588             :     const int imaxorder = this->maxorder;
    1589             :     const int ncomp = this->getNComp();
    1590             :     const int hidden_direction = this->hiddenDirection();
    1591             :     for (int amrlev = 0;  amrlev < this->m_num_amr_levels; ++amrlev)
    1592             :     {
    1593             :         for (int mglev = 0; mglev < this->m_num_mg_levels[amrlev]; ++mglev)
    1594             :         {
    1595             :             const auto& bcondloc = *m_bcondloc[amrlev][mglev];
    1596             :             const auto& maskvals = m_maskvals[amrlev][mglev];
    1597             : 
    1598             :             const RT dxi = static_cast<RT>(this->m_geom[amrlev][mglev].InvCellSize(0));
    1599             :             const RT dyi = static_cast<RT>((AMREX_SPACEDIM >= 2) ? this->m_geom[amrlev][mglev].InvCellSize(1) : Real(1.0));
    1600             :             const RT dzi = static_cast<RT>((AMREX_SPACEDIM == 3) ? this->m_geom[amrlev][mglev].InvCellSize(2) : Real(1.0));
    1601             : 
    1602             :             auto& undrrelxr = this->m_undrrelxr[amrlev][mglev];
    1603             :             MF foo(this->m_grids[amrlev][mglev], this->m_dmap[amrlev][mglev], ncomp, 0, MFInfo().SetAlloc(false));
    1604             : 
    1605             : #ifdef AMREX_USE_EB
    1606             :             const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(this->m_factory[amrlev][mglev].get());
    1607             :             const FabArray<EBCellFlagFab>* flags =
    1608             :                 (factory) ? &(factory->getMultiEBCellFlagFab()) : nullptr;
    1609             :             auto area = (factory) ? factory->getAreaFrac()
    1610             :                 : Array<const MultiCutFab*,AMREX_SPACEDIM>{AMREX_D_DECL(nullptr,nullptr,nullptr)};
    1611             :             amrex::ignore_unused(area);
    1612             : #endif
    1613             : 
    1614             : #ifdef AMREX_USE_GPU
    1615             :             if (Gpu::inLaunchRegion()) {
    1616             : #ifdef AMREX_USE_EB
    1617             :                 if (factory && !factory->isAllRegular()) {
    1618             :                     if constexpr (!std::is_same<MF,MultiFab>()) {
    1619             :                         amrex::Abort("MLCellLinOp with EB only works with MultiFab");
    1620             :                     } else {
    1621             :                         Vector<MLMGPSEBTag<RT>> tags;
    1622             :                         tags.reserve(foo.local_size()*AMREX_SPACEDIM*ncomp);
    1623             : 
    1624             :                         for (MFIter mfi(foo); mfi.isValid(); ++mfi)
    1625             :                         {
    1626             :                             const Box& vbx = mfi.validbox();
    1627             : 
    1628             :                             const auto & bdlv = bcondloc.bndryLocs(mfi);
    1629             :                             const auto & bdcv = bcondloc.bndryConds(mfi);
    1630             : 
    1631             :                             auto fabtyp = (flags) ? (*flags)[mfi].getType(vbx) : FabType::regular;
    1632             : 
    1633             :                             for (int idim = 0; idim < AMREX_SPACEDIM; ++idim)
    1634             :                             {
    1635             :                                 if (idim != hidden_direction && fabtyp != FabType::covered) {
    1636             :                                     const Orientation olo(idim,Orientation::low);
    1637             :                                     const Orientation ohi(idim,Orientation::high);
    1638             :                                     auto const& ap = (fabtyp == FabType::singlevalued)
    1639             :                                         ? area[idim]->const_array(mfi) : Array4<Real const>{};
    1640             :                                     for (int icomp = 0; icomp < ncomp; ++icomp) {
    1641             :                                         tags.emplace_back(MLMGPSEBTag<RT>{undrrelxr[olo].array(mfi),
    1642             :                                                                           undrrelxr[ohi].array(mfi),
    1643             :                                                                           ap,
    1644             :                                                                           maskvals[olo].const_array(mfi),
    1645             :                                                                           maskvals[ohi].const_array(mfi),
    1646             :                                                                           bdlv[icomp][olo], bdlv[icomp][ohi],
    1647             :                                                                           amrex::adjCell(vbx,olo),
    1648             :                                                                           bdcv[icomp][olo], bdcv[icomp][ohi],
    1649             :                                                                           vbx.length(idim), icomp, idim});
    1650             :                                     }
    1651             :                                 }
    1652             :                             }
    1653             :                         }
    1654             : 
    1655             :                         ParallelFor(tags,
    1656             :                         [=] AMREX_GPU_DEVICE (int i, int j, int k, MLMGPSEBTag<RT> const& tag) noexcept
    1657             :                         {
    1658             :                             if (tag.ap) {
    1659             :                                 if (tag.dir == 0)
    1660             :                                 {
    1661             :                                     mllinop_comp_interp_coef0_x_eb
    1662             :                                         (0, i           , j, k, tag.blen, tag.flo, tag.mlo, tag.ap,
    1663             :                                          tag.bctlo, tag.bcllo, imaxorder, dxi, tag.comp);
    1664             :                                     mllinop_comp_interp_coef0_x_eb
    1665             :                                         (1, i+tag.blen+1, j, k, tag.blen, tag.fhi, tag.mhi, tag.ap,
    1666             :                                          tag.bcthi, tag.bclhi, imaxorder, dxi, tag.comp);
    1667             :                                 }
    1668             : #if (AMREX_SPACEDIM > 1)
    1669             :                                 else
    1670             : #if (AMREX_SPACEDIM > 2)
    1671             :                                 if (tag.dir == 1)
    1672             : #endif
    1673             :                                 {
    1674             :                                     mllinop_comp_interp_coef0_y_eb
    1675             :                                         (0, i, j           , k, tag.blen, tag.flo, tag.mlo, tag.ap,
    1676             :                                          tag.bctlo, tag.bcllo, imaxorder, dyi, tag.comp);
    1677             :                                     mllinop_comp_interp_coef0_y_eb
    1678             :                                         (1, i, j+tag.blen+1, k, tag.blen, tag.fhi, tag.mhi, tag.ap,
    1679             :                                          tag.bcthi, tag.bclhi, imaxorder, dyi, tag.comp);
    1680             :                                 }
    1681             : #if (AMREX_SPACEDIM > 2)
    1682             :                                 else {
    1683             :                                     mllinop_comp_interp_coef0_z_eb
    1684             :                                         (0, i, j, k           , tag.blen, tag.flo, tag.mlo, tag.ap,
    1685             :                                          tag.bctlo, tag.bcllo, imaxorder, dzi, tag.comp);
    1686             :                                     mllinop_comp_interp_coef0_z_eb
    1687             :                                         (1, i, j, k+tag.blen+1, tag.blen, tag.fhi, tag.mhi, tag.ap,
    1688             :                                          tag.bcthi, tag.bclhi, imaxorder, dzi, tag.comp);
    1689             :                                 }
    1690             : #endif
    1691             : #endif
    1692             :                             } else {
    1693             :                                 if (tag.dir == 0)
    1694             :                                 {
    1695             :                                     mllinop_comp_interp_coef0_x
    1696             :                                         (0, i           , j, k, tag.blen, tag.flo, tag.mlo,
    1697             :                                          tag.bctlo, tag.bcllo, imaxorder, dxi, tag.comp);
    1698             :                                     mllinop_comp_interp_coef0_x
    1699             :                                         (1, i+tag.blen+1, j, k, tag.blen, tag.fhi, tag.mhi,
    1700             :                                          tag.bcthi, tag.bclhi, imaxorder, dxi, tag.comp);
    1701             :                                 }
    1702             : #if (AMREX_SPACEDIM > 1)
    1703             :                                 else
    1704             : #if (AMREX_SPACEDIM > 2)
    1705             :                                 if (tag.dir == 1)
    1706             : #endif
    1707             :                                 {
    1708             :                                     mllinop_comp_interp_coef0_y
    1709             :                                         (0, i, j           , k, tag.blen, tag.flo, tag.mlo,
    1710             :                                          tag.bctlo, tag.bcllo, imaxorder, dyi, tag.comp);
    1711             :                                     mllinop_comp_interp_coef0_y
    1712             :                                         (1, i, j+tag.blen+1, k, tag.blen, tag.fhi, tag.mhi,
    1713             :                                          tag.bcthi, tag.bclhi, imaxorder, dyi, tag.comp);
    1714             :                                 }
    1715             : #if (AMREX_SPACEDIM > 2)
    1716             :                                 else {
    1717             :                                     mllinop_comp_interp_coef0_z
    1718             :                                         (0, i, j, k           , tag.blen, tag.flo, tag.mlo,
    1719             :                                          tag.bctlo, tag.bcllo, imaxorder, dzi, tag.comp);
    1720             :                                     mllinop_comp_interp_coef0_z
    1721             :                                         (1, i, j, k+tag.blen+1, tag.blen, tag.fhi, tag.mhi,
    1722             :                                          tag.bcthi, tag.bclhi, imaxorder, dzi, tag.comp);
    1723             :                                 }
    1724             : #endif
    1725             : #endif
    1726             :                             }
    1727             :                         });
    1728             :                     }
    1729             :                 } else
    1730             : #endif
    1731             :                 {
    1732             :                     Vector<MLMGPSTag<RT>> tags;
    1733             :                     tags.reserve(foo.local_size()*AMREX_SPACEDIM*ncomp);
    1734             : 
    1735             :                     for (MFIter mfi(foo); mfi.isValid(); ++mfi)
    1736             :                     {
    1737             :                         const Box& vbx = mfi.validbox();
    1738             : 
    1739             :                         const auto & bdlv = bcondloc.bndryLocs(mfi);
    1740             :                         const auto & bdcv = bcondloc.bndryConds(mfi);
    1741             : 
    1742             :                         for (int idim = 0; idim < AMREX_SPACEDIM; ++idim)
    1743             :                         {
    1744             :                             if (idim != hidden_direction) {
    1745             :                                 const Orientation olo(idim,Orientation::low);
    1746             :                                 const Orientation ohi(idim,Orientation::high);
    1747             :                                 for (int icomp = 0; icomp < ncomp; ++icomp) {
    1748             :                                     tags.emplace_back(MLMGPSTag<RT>{undrrelxr[olo].array(mfi),
    1749             :                                                             undrrelxr[ohi].array(mfi),
    1750             :                                                             maskvals[olo].const_array(mfi),
    1751             :                                                             maskvals[ohi].const_array(mfi),
    1752             :                                                             bdlv[icomp][olo], bdlv[icomp][ohi],
    1753             :                                                             amrex::adjCell(vbx,olo),
    1754             :                                                             bdcv[icomp][olo], bdcv[icomp][ohi],
    1755             :                                                             vbx.length(idim), icomp, idim});
    1756             :                                 }
    1757             :                             }
    1758             :                         }
    1759             :                     }
    1760             : 
    1761             :                     ParallelFor(tags,
    1762             :                     [=] AMREX_GPU_DEVICE (int i, int j, int k, MLMGPSTag<RT> const& tag) noexcept
    1763             :                     {
    1764             :                         if (tag.dir == 0)
    1765             :                         {
    1766             :                             mllinop_comp_interp_coef0_x
    1767             :                                 (0, i           , j, k, tag.blen, tag.flo, tag.mlo,
    1768             :                                  tag.bctlo, tag.bcllo, imaxorder, dxi, tag.comp);
    1769             :                             mllinop_comp_interp_coef0_x
    1770             :                                 (1, i+tag.blen+1, j, k, tag.blen, tag.fhi, tag.mhi,
    1771             :                                  tag.bcthi, tag.bclhi, imaxorder, dxi, tag.comp);
    1772             :                         }
    1773             : #if (AMREX_SPACEDIM > 1)
    1774             :                         else
    1775             : #if (AMREX_SPACEDIM > 2)
    1776             :                         if (tag.dir == 1)
    1777             : #endif
    1778             :                         {
    1779             :                             mllinop_comp_interp_coef0_y
    1780             :                                 (0, i, j           , k, tag.blen, tag.flo, tag.mlo,
    1781             :                                  tag.bctlo, tag.bcllo, imaxorder, dyi, tag.comp);
    1782             :                             mllinop_comp_interp_coef0_y
    1783             :                                 (1, i, j+tag.blen+1, k, tag.blen, tag.fhi, tag.mhi,
    1784             :                                  tag.bcthi, tag.bclhi, imaxorder, dyi, tag.comp);
    1785             :                         }
    1786             : #if (AMREX_SPACEDIM > 2)
    1787             :                         else {
    1788             :                             mllinop_comp_interp_coef0_z
    1789             :                                 (0, i, j, k           , tag.blen, tag.flo, tag.mlo,
    1790             :                                  tag.bctlo, tag.bcllo, imaxorder, dzi, tag.comp);
    1791             :                             mllinop_comp_interp_coef0_z
    1792             :                                 (1, i, j, k+tag.blen+1, tag.blen, tag.fhi, tag.mhi,
    1793             :                                  tag.bcthi, tag.bclhi, imaxorder, dzi, tag.comp);
    1794             :                         }
    1795             : #endif
    1796             : #endif
    1797             :                     });
    1798             :                 }
    1799             :             } else
    1800             : #endif
    1801             :             {
    1802             : #ifdef AMREX_USE_OMP
    1803             : #pragma omp parallel
    1804             : #endif
    1805             :                 for (MFIter mfi(foo, MFItInfo{}.SetDynamic(true)); mfi.isValid(); ++mfi)
    1806             :                 {
    1807             :                     const Box& vbx = mfi.validbox();
    1808             : 
    1809             :                     const auto & bdlv = bcondloc.bndryLocs(mfi);
    1810             :                     const auto & bdcv = bcondloc.bndryConds(mfi);
    1811             : 
    1812             : #ifdef AMREX_USE_EB
    1813             :                     auto fabtyp = (flags) ? (*flags)[mfi].getType(vbx) : FabType::regular;
    1814             : #endif
    1815             :                     for (int idim = 0; idim < AMREX_SPACEDIM; ++idim)
    1816             :                     {
    1817             :                         if (idim == hidden_direction) { continue; }
    1818             :                         const Orientation olo(idim,Orientation::low);
    1819             :                         const Orientation ohi(idim,Orientation::high);
    1820             :                         const Box blo = amrex::adjCellLo(vbx, idim);
    1821             :                         const Box bhi = amrex::adjCellHi(vbx, idim);
    1822             :                         const int blen = vbx.length(idim);
    1823             :                         const auto& mlo = maskvals[olo].array(mfi);
    1824             :                         const auto& mhi = maskvals[ohi].array(mfi);
    1825             :                         const auto& flo = undrrelxr[olo].array(mfi);
    1826             :                         const auto& fhi = undrrelxr[ohi].array(mfi);
    1827             :                         for (int icomp = 0; icomp < ncomp; ++icomp) {
    1828             :                             const BoundCond bctlo = bdcv[icomp][olo];
    1829             :                             const BoundCond bcthi = bdcv[icomp][ohi];
    1830             :                             const auto bcllo = bdlv[icomp][olo];
    1831             :                             const auto bclhi = bdlv[icomp][ohi];
    1832             : #ifdef AMREX_USE_EB
    1833             :                             if (fabtyp == FabType::singlevalued) {
    1834             :                                 if constexpr (!std::is_same<MF,MultiFab>()) {
    1835             :                                     amrex::Abort("MLCellLinOp with EB only works with MultiFab");
    1836             :                                 } else {
    1837             :                                     auto const& ap = area[idim]->const_array(mfi);
    1838             :                                     if (idim == 0) {
    1839             :                                         mllinop_comp_interp_coef0_x_eb
    1840             :                                             (0, blo, blen, flo, mlo, ap, bctlo, bcllo,
    1841             :                                              imaxorder, dxi, icomp);
    1842             :                                         mllinop_comp_interp_coef0_x_eb
    1843             :                                             (1, bhi, blen, fhi, mhi, ap, bcthi, bclhi,
    1844             :                                              imaxorder, dxi, icomp);
    1845             :                                     } else if (idim == 1) {
    1846             :                                         mllinop_comp_interp_coef0_y_eb
    1847             :                                             (0, blo, blen, flo, mlo, ap, bctlo, bcllo,
    1848             :                                              imaxorder, dyi, icomp);
    1849             :                                         mllinop_comp_interp_coef0_y_eb
    1850             :                                             (1, bhi, blen, fhi, mhi, ap, bcthi, bclhi,
    1851             :                                              imaxorder, dyi, icomp);
    1852             :                                     } else {
    1853             :                                         mllinop_comp_interp_coef0_z_eb
    1854             :                                             (0, blo, blen, flo, mlo, ap, bctlo, bcllo,
    1855             :                                              imaxorder, dzi, icomp);
    1856             :                                         mllinop_comp_interp_coef0_z_eb
    1857             :                                             (1, bhi, blen, fhi, mhi, ap, bcthi, bclhi,
    1858             :                                              imaxorder, dzi, icomp);
    1859             :                                     }
    1860             :                                 }
    1861             :                             } else if (fabtyp == FabType::regular)
    1862             : #endif
    1863             :                             {
    1864             :                                 if (idim == 0) {
    1865             :                                     mllinop_comp_interp_coef0_x
    1866             :                                         (0, blo, blen, flo, mlo, bctlo, bcllo,
    1867             :                                          imaxorder, dxi, icomp);
    1868             :                                     mllinop_comp_interp_coef0_x
    1869             :                                         (1, bhi, blen, fhi, mhi, bcthi, bclhi,
    1870             :                                          imaxorder, dxi, icomp);
    1871             :                                 } else if (idim == 1) {
    1872             :                                     mllinop_comp_interp_coef0_y
    1873             :                                         (0, blo, blen, flo, mlo, bctlo, bcllo,
    1874             :                                          imaxorder, dyi, icomp);
    1875             :                                     mllinop_comp_interp_coef0_y
    1876             :                                         (1, bhi, blen, fhi, mhi, bcthi, bclhi,
    1877             :                                          imaxorder, dyi, icomp);
    1878             :                                 } else {
    1879             :                                     mllinop_comp_interp_coef0_z
    1880             :                                         (0, blo, blen, flo, mlo, bctlo, bcllo,
    1881             :                                          imaxorder, dzi, icomp);
    1882             :                                     mllinop_comp_interp_coef0_z
    1883             :                                         (1, bhi, blen, fhi, mhi, bcthi, bclhi,
    1884             :                                          imaxorder, dzi, icomp);
    1885             :                                 }
    1886             :                             }
    1887             :                         }
    1888             :                     }
    1889             :                 }
    1890             :             }
    1891             :         }
    1892             :     }
    1893             : }
    1894             : 
    1895             : template <typename MF>
    1896             : auto
    1897             : MLCellLinOpT<MF>::xdoty (int /*amrlev*/, int /*mglev*/, const MF& x, const MF& y, bool local) const
    1898             :     -> RT
    1899             : {
    1900             :     const int ncomp = this->getNComp();
    1901             :     const IntVect nghost(0);
    1902             :     RT result = amrex::Dot(x,0,y,0,ncomp,nghost,true);
    1903             :     if (!local) {
    1904             :         ParallelAllReduce::Sum(result, ParallelContext::CommunicatorSub());
    1905             :     }
    1906             :     return result;
    1907             : }
    1908             : 
    1909             : template <typename MF>
    1910             : void
    1911             : MLCellLinOpT<MF>::computeVolInv () const
    1912             : {
    1913             :     if (!m_volinv.empty()) { return; }
    1914             : 
    1915             :     m_volinv.resize(this->m_num_amr_levels);
    1916             :     for (int amrlev = 0; amrlev < this->m_num_amr_levels; ++amrlev) {
    1917             :         m_volinv[amrlev].resize(this->NMGLevels(amrlev));
    1918             :     }
    1919             : 
    1920             :     AMREX_ASSERT(this->m_coarse_fine_bc_type == LinOpBCType::Dirichlet || ! this->hasHiddenDimension());
    1921             : 
    1922             :     // We don't need to compute for every level
    1923             : 
    1924             :     auto f = [&] (int amrlev, int mglev) {
    1925             : #ifdef AMREX_USE_EB
    1926             :         const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(this->Factory(amrlev,mglev));
    1927             :         if (factory && !factory->isAllRegular())
    1928             :         {
    1929             :             if constexpr (std::is_same<MF,MultiFab>()) {
    1930             :                 const auto& vfrac = factory->getVolFrac();
    1931             :                 m_volinv[amrlev][mglev] = vfrac.sum(0,true);
    1932             :             } else {
    1933             :                 amrex::Abort("MLCellLinOp with EB only works with MultiFab");
    1934             :             }
    1935             :         }
    1936             :         else
    1937             : #endif
    1938             :         {
    1939             :             if (this->m_coarse_fine_bc_type == LinOpBCType::Dirichlet) {
    1940             :                 m_volinv[amrlev][mglev]
    1941             :                     = RT(1.0 / this->compactify(this->Geom(amrlev,mglev).Domain()).d_numPts());
    1942             :             } else {
    1943             :                 m_volinv[amrlev][mglev]
    1944             :                     = RT(1.0 / this->m_grids[amrlev][mglev].d_numPts());
    1945             :             }
    1946             :         }
    1947             :     };
    1948             : 
    1949             :     // amrlev = 0, mglev = 0
    1950             :     f(0,0);
    1951             : 
    1952             :     int mgbottom = this->NMGLevels(0)-1;
    1953             :     f(0,mgbottom);
    1954             : 
    1955             : #ifdef AMREX_USE_EB
    1956             :     RT temp1, temp2;
    1957             :     const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(this->Factory(0,0));
    1958             :     if (factory && !factory->isAllRegular())
    1959             :     {
    1960             :         ParallelAllReduce::Sum<RT>({m_volinv[0][0], m_volinv[0][mgbottom]},
    1961             :                                    ParallelContext::CommunicatorSub());
    1962             :         temp1 = RT(1.0)/m_volinv[0][0];
    1963             :         temp2 = RT(1.0)/m_volinv[0][mgbottom];
    1964             :     }
    1965             :     else
    1966             :     {
    1967             :         temp1 = m_volinv[0][0];
    1968             :         temp2 = m_volinv[0][mgbottom];
    1969             :     }
    1970             :     m_volinv[0][0] = temp1;
    1971             :     m_volinv[0][mgbottom] = temp2;
    1972             : #endif
    1973             : }
    1974             : 
    1975             : template <typename MF>
    1976             : auto
    1977             : MLCellLinOpT<MF>::normInf (int amrlev, MF const& mf, bool local) const -> RT
    1978             : {
    1979             :     const int ncomp = this->getNComp();
    1980             :     const int finest_level = this->NAMRLevels() - 1;
    1981             :     RT norm = RT(0.0);
    1982             : #ifdef AMREX_USE_EB
    1983             :     if (! mf.isAllRegular()) {
    1984             :         if constexpr (!std::is_same<MF,MultiFab>()) {
    1985             :             amrex::Abort("MLCellLinOpT with EB only works with MultiFab");
    1986             :         } else {
    1987             :             const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(this->Factory(amrlev));
    1988             :             const MultiFab& vfrac = factory->getVolFrac();
    1989             :             if (amrlev == finest_level) {
    1990             : #ifdef AMREX_USE_GPU
    1991             :                 if (Gpu::inLaunchRegion()) {
    1992             :                     auto const& ma = mf.const_arrays();
    1993             :                     auto const& vfrac_ma = vfrac.const_arrays();
    1994             :                     norm = ParReduce(TypeList<ReduceOpMax>{}, TypeList<Real>{},
    1995             :                                      mf, IntVect(0), ncomp,
    1996             :                                      [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n)
    1997             :                                          -> GpuTuple<Real>
    1998             :                                      {
    1999             :                                          return std::abs(ma[box_no](i,j,k,n)
    2000             :                                                                  *vfrac_ma[box_no](i,j,k));
    2001             :                                      });
    2002             :                 } else
    2003             : #endif
    2004             :                 {
    2005             : #ifdef AMREX_USE_OMP
    2006             : #pragma omp parallel reduction(max:norm)
    2007             : #endif
    2008             :                     for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
    2009             :                         Box const& bx = mfi.tilebox();
    2010             :                         auto const& fab = mf.const_array(mfi);
    2011             :                         auto const& v = vfrac.const_array(mfi);
    2012             :                         AMREX_LOOP_4D(bx, ncomp, i, j, k, n,
    2013             :                         {
    2014             :                             norm = std::max(norm, std::abs(fab(i,j,k,n)*v(i,j,k)));
    2015             :                         });
    2016             :                     }
    2017             :                 }
    2018             :             } else {
    2019             : #ifdef AMREX_USE_GPU
    2020             :                 if (Gpu::inLaunchRegion()) {
    2021             :                     auto const& ma = mf.const_arrays();
    2022             :                     auto const& mask_ma = m_norm_fine_mask[amrlev]->const_arrays();
    2023             :                     auto const& vfrac_ma = vfrac.const_arrays();
    2024             :                     norm = ParReduce(TypeList<ReduceOpMax>{}, TypeList<Real>{},
    2025             :                                      mf, IntVect(0), ncomp,
    2026             :                                      [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n)
    2027             :                                          -> GpuTuple<Real>
    2028             :                                      {
    2029             :                                          if (mask_ma[box_no](i,j,k)) {
    2030             :                                              return std::abs(ma[box_no](i,j,k,n)
    2031             :                                                                      *vfrac_ma[box_no](i,j,k));
    2032             :                                          } else {
    2033             :                                              return Real(0.0);
    2034             :                                          }
    2035             :                                      });
    2036             :                 } else
    2037             : #endif
    2038             :                 {
    2039             : #ifdef AMREX_USE_OMP
    2040             : #pragma omp parallel reduction(max:norm)
    2041             : #endif
    2042             :                     for (MFIter mfi(mf,true); mfi.isValid(); ++mfi) {
    2043             :                         Box const& bx = mfi.tilebox();
    2044             :                         auto const& fab = mf.const_array(mfi);
    2045             :                         auto const& mask = m_norm_fine_mask[amrlev]->const_array(mfi);
    2046             :                         auto const& v = vfrac.const_array(mfi);
    2047             :                         AMREX_LOOP_4D(bx, ncomp, i, j, k, n,
    2048             :                         {
    2049             :                             if (mask(i,j,k)) {
    2050             :                                 norm = std::max(norm, std::abs(fab(i,j,k,n)*v(i,j,k)));
    2051             :                             }
    2052             :                         });
    2053             :                     }
    2054             :                 }
    2055             :             }
    2056             :         }
    2057             :     } else
    2058             : #endif
    2059             :     {
    2060             :         if (amrlev == finest_level) {
    2061             :             norm = mf.norminf(0, ncomp, IntVect(0), true);
    2062             :         } else {
    2063             :             norm = mf.norminf(*m_norm_fine_mask[amrlev], 0, ncomp, IntVect(0), true);
    2064             :         }
    2065             :     }
    2066             : 
    2067             :     if (!local) { ParallelAllReduce::Max(norm, ParallelContext::CommunicatorSub()); }
    2068             :     return norm;
    2069             : }
    2070             : 
    2071             : template <typename MF>
    2072             : void
    2073             : MLCellLinOpT<MF>::averageDownAndSync (Vector<MF>& sol) const
    2074             : {
    2075             :     int ncomp = this->getNComp();
    2076             :     for (int falev = this->NAMRLevels()-1; falev > 0; --falev)
    2077             :     {
    2078             : #ifdef AMREX_USE_EB
    2079             :         if (!sol[falev].isAllRegular()) {
    2080             :             if constexpr (std::is_same<MF,MultiFab>()) {
    2081             :                 amrex::EB_average_down(sol[falev], sol[falev-1], 0, ncomp, this->AMRRefRatio(falev-1));
    2082             :             } else {
    2083             :                 amrex::Abort("EB_average_down only works with MultiFab");
    2084             :             }
    2085             :         } else
    2086             : #endif
    2087             :         {
    2088             :             amrex::average_down(sol[falev], sol[falev-1], 0, ncomp, this->AMRRefRatio(falev-1));
    2089             :         }
    2090             :     }
    2091             : }
    2092             : 
    2093             : template <typename MF>
    2094             : void
    2095             : MLCellLinOpT<MF>::avgDownResAmr (int clev, MF& cres, MF const& fres) const
    2096             : {
    2097             : #ifdef AMREX_USE_EB
    2098             :     if (!fres.isAllRegular()) {
    2099             :         if constexpr (std::is_same<MF,MultiFab>()) {
    2100             :             amrex::EB_average_down(fres, cres, 0, this->getNComp(),
    2101             :                                    this->AMRRefRatio(clev));
    2102             :         } else {
    2103             :             amrex::Abort("EB_average_down only works with MultiFab");
    2104             :         }
    2105             :     } else
    2106             : #endif
    2107             :     {
    2108             :         amrex::average_down(fres, cres, 0, this->getNComp(),
    2109             :                             this->AMRRefRatio(clev));
    2110             :     }
    2111             : }
    2112             : 
    2113             : extern template class MLCellLinOpT<MultiFab>;
    2114             : 
    2115             : using MLCellLinOp = MLCellLinOpT<MultiFab>;
    2116             : 
    2117             : }
    2118             : 
    2119             : #endif

Generated by: LCOV version 1.14