/*
** (c) 1996-2000 The Regents of the University of California (through
** E.O. Lawrence Berkeley National Laboratory), subject to approval by
** the U.S. Department of Energy.  Your use of this software is under
** license -- the license agreement is attached and included in the
** directory as license.txt or you may contact Berkeley Lab's Technology
** Transfer Department at TTD@lbl.gov.  NOTICE OF U.S. GOVERNMENT RIGHTS.
** The Software was developed under funding from the U.S. Government
** which consequently retains certain rights as follows: the
** U.S. Government has been granted for itself and others acting on its
** behalf a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, and perform publicly
** and display publicly.  Beginning five (5) years after the date
** permission to assert copyright is obtained from the U.S. Department of
** Energy, and subject to any subsequent five (5) year renewals, the
** U.S. Government is granted for itself and others acting on its behalf
** a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, distribute copies to
** the public, perform publicly and display publicly, and to permit
** others to do so.
*/


#ifndef _Projection_H_
#define _Projection_H_

//
// $Id: Projection.H,v 1.41 2003/02/21 22:49:10 car Exp $
//

#include <BC_TYPES.H>
#include <BCRec.H>
#include <Amr.H>
#include <AmrLevel.H>
#include <RegType.H>
#include <SyncRegister.H>
#include <FluxRegister.H>

class holy_grail_amr_projector;
#ifdef BL_USE_HGPROJ_SERIAL
class inviscid_fluid_boundary_class;
#else
class inviscid_fluid_boundary;
#endif

class Projection 
{
public:

    Projection (Amr*   _parent,
                BCRec* _phys_bc, 
                int    _do_sync_proj,
                int    _finest_level, 
                int    _radius_grow );

    virtual ~Projection ();
    //
    // Set a level in the projector.
    //
    void install_level (int           level,
                        AmrLevel*     level_data,
                        PArray<Real>* _radius );
    //
    // Reset finest_level.
    //
    void setFinestLevel (int new_finest) { finest_level = new_finest; }
    //
    // Compute the level projection in NavierStokes::advance.
    //
    void level_project (int             level,
                        Real            time,
                        Real            dt,
                        Real            cur_pres_time,
                        Real            prev_pres_time,
                        const Geometry& geom, 
                        MultiFab&       U_old,
                        MultiFab&       U_new,
                        MultiFab&       P_old,
                        MultiFab&       P_new,
                        MultiFab*       rho_half,
                        SyncRegister*   crse_sync_reg,
                        SyncRegister*   fine_sync_reg,
                        int             crse_dt_ratio,
                        int**           bc,
                        int             iteration,
                        int             have_divu,
                        int             Divu_Type);

    // solve DG(correction to P_new) = -D G^perp p^(n-half)
    //  or   DG(correction to P_new) = -D G^perp p^(n-half) - D(U^n /dt)
    void filterP(int level,
                 const Geometry& geom,
                 MultiFab &P_old,
                 MultiFab &P_new,
                 MultiFab &U_old,
                 MultiFab * rho_half,
                 int     ** bc,
                 Real            time,
                 Real            dt,
                 int    have_divu);
    //
    // A sync project involving only the coarse level of a 2 level system.
    //
    void syncProject (int             crse_level,
                      MultiFab&       pres,
                      MultiFab&       vel,
                      MultiFab*       rho_half,
                      MultiFab*       Vsync,
                      MultiFab&       phi,
                      SyncRegister*   rhs_sync_reg,
                      SyncRegister*   crsr_sync_reg, 
                      const BoxArray& sync_boxes,
                      const Geometry& geom,
                      const Real*     dx,
                      Real            dt_crse,
                      int             crse_iteration,
                      int             crse_dt_ratio);
    //
    // A sync project involving the coarse and fine level of a 2 level system.
    //
    void MLsyncProject (int             crse_level,
                        MultiFab&       pres_crse,
                        MultiFab&       vel_crse,
                        MultiFab&       cc_rhs_crse,
                        MultiFab&       pres_fine,
                        MultiFab&       vel_fine,
                        MultiFab&       cc_rhs_fine,
                        MultiFab&       rho_half,
                        MultiFab&       rho_fine,
                        MultiFab*       Vsync,
                        MultiFab&       V_corr,
                        MultiFab&       phi,
                        SyncRegister*   rhs_sync_reg,
                        SyncRegister*   crsr_sync_reg,
                        const Real*     dx,
                        Real            dt_crse,
                        IntVect&        ratio,
                        int             crse_iteration,
                        int             crse_dt_ratio,
                        const Geometry& fine_geom,
                        const Geometry& crse_geom,
                        bool		pressure_time_is_interval,
                        bool		first_crse_step_after_initial_iters,
                        Real		 cur_crse_pres_time,
                        Real		prev_crse_pres_time,
                        Real		 cur_fine_pres_time,
                        Real		prev_fine_pres_time);
    //
    // This projects an initial vorticity field to define a velocity field.
    //
    void initialVorticityProject (int crse_level);
    //
    // This computes the stream function, given the velocity field
    //  *AND ASSUMING* no-flow boundaries on all physical boundaries.
    //
    void getStreamFunction (PArray<MultiFab>&);
    //
    // The initial velocity projection in post_init.
    // This function ensures that the velocities are nondivergent.
    //
    void initialVelocityProject (int  crse_level,
                                 Real divu_time,
                                 int  have_divu);

    //
    // This function creates an initially hydrostatic pressure field
    //   in the case of nonzero gravity.
    //
    void initialPressureProject (int  crse_level);
    //
    // The velocity projection in post_init, which computes the initial
    // pressure used in the timestepping.
    //
    void initialSyncProject (int       crse_level,
                             MultiFab* sig[],
                             Real      dt, 
                             Real      strt_time,
                             Real      dt_init,
                             int       have_divu);
    //
    // Enforce periodicity on a multifab.
    //
    void EnforcePeriodicity (MultiFab&       psi,
                             int             nvar,
                             const BoxArray& grids,
                             const Geometry& geom);
    //
    // Multiply/divide a MultiFab by radius for r-z coordinates.
    // These should really be protected.
    //
    void radMult (int       level,
                  MultiFab& mf,
                  int       comp);

    void radDiv (int       level,
                 MultiFab& mf,
                 int       comp);
    //
    // Put divu in the rhs of the projector.
    //
    void put_divu_in_node_rhs (MultiFab&       rhs,
                               int             level,
                               const int&      nghost,
                               Real            time,
                               int             user_rz = -1);

    void put_divu_in_cc_rhs (MultiFab&       rhs,
                             int             level,
                             const BoxArray& grids,
                             Real            time);

    //
    // Helper function for NavierStokes::getGradP().
    //
    static
    void getGradP (FArrayBox& p_fab,
                   FArrayBox& gp,
                   const Box& gpbox_to_fill,
                   const Real* dx);

protected:
    //
    // Read parameters from the input file.
    //
    static void read_params ();
    //
    // Set up the sync projection.
    //
    void bldSyncProject ();
    //
    // Convert U from an Accleration like quantity to a velocity
    // Unew = Uold + alpha*Unew
    //
    void UnConvertUnew (MultiFab&       Uold,
                        Real            alpha,
                        MultiFab&       Unew, 
                        const BoxArray& grids );
    //
    // Convert U from an Accleration like quantity to a velocity
    // Unew = Uold + alpha*Unew
    //
    void UnConvertUnew (FArrayBox& Uold,
                        Real       alpha,
                        FArrayBox& Unew,
                        const Box& grd);
    //
    // Convert U to an Accleration like quantity
    // Unew = (Unew - Uold)/alpha
    //
    void ConvertUnew (MultiFab&       Unew,
                      MultiFab&       Uold,
                      Real            alpha,
                      const BoxArray& grids );
    //
    // Convert U to an Accleration like quantity.
    // Unew = (Unew - Uold)/alpha
    //
    void ConvertUnew (FArrayBox& Unew,
                      FArrayBox& Uold,
                      Real       alpha,
                      const Box& grd);
    //
    // Update a quantity U using the formula
    // Unew = Unew + alpha*Uold
    //
    void UpdateArg1 (MultiFab&       Unew,
                     Real            alpha,
                     MultiFab&       Uold,
                     int             nvar,
                     const BoxArray& grids,
                     int ngrow);
    //
    // Update a quantity U using the formula
    // Unew = Unew + alpha*Uold
    //
    void UpdateArg1 (FArrayBox& Unew,
                     Real       alpha,
                     FArrayBox& Uold,
                     int        nvar,
                     const Box& grd,
                     int        ngrow);
    //
    // Add phi to P.
    //
    void AddPhi (MultiFab&       p,
                 MultiFab&       phi,
                 const BoxArray& grids);
    //
    // Convert phi into p^n+1/2.
    //
    void incrPress (int  level,
                    Real dt);
    //
    // This function scales variables at the start of a projection.
    //
    void scaleVar (MultiFab*       sig,
                   int             sig_nghosts,
                   MultiFab*       vel,
                   const BoxArray& grids,
                   int             level);
    //
    // This function rescales variables at the end of a projection.
    //
    void rescaleVar (MultiFab*       sig,
                     int             sig_nghosts,
                     MultiFab*       vel,
                     const BoxArray& grids,
                     int             level);

    void setUpBcs ();

    void set_outflow_bcs (int        which_call,
                          MultiFab** phi,
                          MultiFab** Vel_in,
                          MultiFab** Divu_in,
                          MultiFab** Sig_in,
                          int        c_lev,
                          int        f_lev,
                          int        have_divu);

    void set_outflow_bcs_at_level (int          which_call,
                                   int          lev,
                                   int          c_lev,
                                   Box*         state_strip,
                                   Orientation* outFacesAtThisLevel,
                                   int          numOutFlowFaces,
                                   MultiFab**   phi,
                                   MultiFab*    Vel_in,
                                   MultiFab*    Divu_in,
                                   MultiFab*    Sig_in,
                                   int          have_divu,
                                   Real         gravity);



#if 0
    void computeBC(FArrayBox& vel, FArrayBox& divu,
		   FArrayBox& rho, FArrayBox& phi,
		   const Geometry& geom, const Orientation& outFace,
                   Real gravity = 0.0);

    void computeRhoG(FArrayBox& rho, FArrayBox& phi,
		     const Geometry& geom, const Orientation& outFace,
                     Real gravity);
#endif


protected:

    void putDown(MultiFab** phi, FArrayBox* phi_fine_strip,
		 int c_lev, int f_lev, const Orientation* outFaces,
		 int numOutFlowFaces, int ncStripWidth);

    //
    // Pointers to amrlevel and amr.
    //
    Amr*             parent;
    PArray<AmrLevel> LevelData;
    //
    // The array of Radii, and number of radii ghost cells.
    //
    int                    radius_grow;
    PArray< PArray<Real> > radius;
    //
    // The aliasLib level projector.
    //
    holy_grail_amr_projector *sync_proj;
    //
    // Boundary objects.
    //
    BCRec*                         phys_bc;
#ifdef BL_USE_HGPROJ_SERIAL
    inviscid_fluid_boundary_class* projector_bndry;
#else
    inviscid_fluid_boundary* projector_bndry;
#endif
    int finest_level;
    int do_sync_proj;

    static int  verbose;
    static int  P_code;
    static Real proj_tol;
    static Real sync_tol;
    static Real proj_abs_tol;
    static Real filter_factor;
    static int  proj_0;
    static int  proj_2;
    static int  filter_u;
    static int  rho_wgt_vel_proj;
    static int  do_outflow_bcs;
    static int  make_sync_solvable;
    static Real divu_minus_s_factor;
    static int  add_vort_proj;
};
#endif
