/**
   This is called by cc projection to enforceVelocityBCs on face
   velocities averaged from centers before calculating divergence in mac projection.
*/
void
InflowOutflowPoissonDomainBC::
getFaceVel(Real&                 a_faceFlux,
           const FaceIndex&      a_face,
           const EBFluxFAB&      a_vel,
           const RealVect&       a_probLo,
           const RealVect&       a_dx,
           const int&            a_idir,
           const int&            a_icomp,
           const Real&           a_time,
           const Side::LoHiSide& a_side,
           const bool&           a_doDivFreeOutflow)
{
  int velcomp =  DirichletPoissonEBBC::s_velComp;
  CH_assert(a_vel.nComp() == 1);
  bool isInflow = (a_side==Side::Lo) && (a_idir==m_flowDir);
  bool isOutflow= (a_side==Side::Hi) && (a_idir==m_flowDir);

  RealVect point =  EBArith::getFaceLocation(a_face, a_dx, a_probLo);
  RealVect normal = EBArith::getDomainNormal(a_idir, a_side);

  const EBISBox& ebisBox= a_vel[0].getEBISBox();

  if (isOutflow)
    {
      //quadratic extrapolation with homogeneous Neumann for outflow bc
      a_faceFlux = EBArith::extrapFaceVelToOutflow(a_face,a_side,a_idir,ebisBox.getEBGraph(),a_vel[a_idir],0);//be careful of this 0, it is the component
    }
  else if (isInflow && (velcomp==m_flowDir))
    {
      //input component is always zero.
      //value of face direction corresponds to velocity direction here
      if (!m_doPoiseInflow && !m_doWomersleyInflow)
      {
        a_faceFlux = m_inflowVel;
      }
      else if (m_doPoiseInflow)
        {
          RealVect prob_lo = RealVect::Zero;
          const RealVect loc  = EBArith::getFaceLocation(a_face, a_dx, prob_lo);
          Real radius = m_poiseInflowFunc->getRadius(loc);
          a_faceFlux = m_poiseInflowFunc->getVel(radius)[m_flowDir];
        }
      else if (m_doWomersleyInflow)
        {
          double PI = 3.1416;
          int freq[10] =
          {
            1,2,3,4,5,6,7,8
          };
          double Vp[10] =
          {
            0.33,0.24,0.24,0.12,0.11,0.13,0.06,0.04
          };
          double Theta[10] =
          {
            74,79,121,146,147,179,233,218
          };
          double AmpXsi[10] =
          {
            1.7639,1.4363,1.2517,1.1856,1.1603,1.1484,1.1417,1.1214
          };
          double AngXsi[10] =
          {
            -0.2602,-0.3271,-0.2799,-0.2244,-0.1843,-0.1576,-0.1439,-0.1195
          };

          Real VelMult = 2;

          int Maxp = 8;

          for (int p=0;p<Maxp;p++)
            VelMult += Vp[p] * AmpXsi[p] * cos (2 * PI * freq[p] * g_simulationTime - Theta[p]*PI/180 + AngXsi[p]);

          a_faceFlux = m_inflowVel*VelMult/2;
        }
    }
  else
    {
      //must be a solid wall or tangential velocity at inflow
      a_faceFlux = 0;
    }
}
/**
   This is called by cc projection to enforceVelocityBCs on face
   velocities averaged from centers before calculating divergence in mac projection.
*/
void
InflowOutflowPoissonDomainBC::
getFaceVel(Real&                 a_faceFlux,
           const FaceIndex&      a_face,
           const EBFluxFAB&      a_vel,
           const RealVect&       a_probLo,
           const RealVect&       a_dx,
           const int&            a_idir,
           const int&            a_icomp,
           const Real&           a_time,
           const Side::LoHiSide& a_side,
           const bool&           a_doDivFreeOutflow)
{
  int velcomp =  DirichletPoissonEBBC::s_velComp;
  CH_assert(a_vel.nComp() == 1);
  bool isInflow = (a_side==Side::Lo) && (a_idir==m_flowDir);
  bool isOutflow= (a_side==Side::Hi) && (a_idir==m_flowDir);

  RealVect point =  EBArith::getFaceLocation(a_face, a_dx, a_probLo);
  RealVect normal = EBArith::getDomainNormal(a_idir, a_side);

  const EBISBox& ebisBox= a_vel[0].getEBISBox();

  if (isOutflow)
    {
      if (!(m_doJet2))
        {
          //quadratic extrapolation with homogeneous Neumann for outflow bc
          a_faceFlux = EBArith::extrapFaceVelToOutflow(a_face,a_side,a_idir,ebisBox.getEBGraph(),a_vel[a_idir],0);//be careful of this 0, it is the component
        }
      else if (m_doJet2)
        {
          const RealVect tubeCenter = m_jet2PoiseInflowFunc->getTubeCenter();
          Real tubeRadius = m_jet2PoiseInflowFunc->getTubeRadius();
          CH_assert(point[m_flowDir] == tubeCenter[m_flowDir]);
          Real radius = m_jet2PoiseInflowFunc->getRadius(point);
          bool isInsideTube = false;

// start hardwire
          Real wallThickness = 0.075;
          Real bcFactor = 0.5; // % of outflow face to be set to inflow condition
          ParmParse pp;
          pp.get("wall_thickness",wallThickness);
          tubeRadius += wallThickness * bcFactor /2.0;
// end hardwire

          if (radius <= tubeRadius)
            {
              isInsideTube = true;
            }
          if (isInsideTube && (velcomp == m_flowDir))
            {
              if (m_doJet2PoiseInflow)
                {
                  a_faceFlux = m_jet2PoiseInflowFunc->getVel(radius)[m_flowDir];
                }
              else if (!(m_doJet2PoiseInflow))
                {
                  a_faceFlux = m_jet2inflowVel;
                }
            }
          else if (isInsideTube && (velcomp != m_flowDir))
            {
              a_faceFlux = 0; // set tangential components to 0
            }
          else if (!(isInsideTube))
            {
              //quadratic extrapolation with homogeneous Neumann for outflow bc
              a_faceFlux = EBArith::extrapFaceVelToOutflow(a_face,a_side,a_idir,ebisBox.getEBGraph(),a_vel[a_idir],0);//be careful of this 0, it is the component 
            } 
        }
    }
  else if (isInflow && (velcomp==m_flowDir))
    {
      //input component is always zero.
      //value of face direction corresponds to velocity direction here
      if (!m_doJet1PoiseInflow)
      {
        a_faceFlux = m_jet1inflowVel;
      }
      else if (m_doJet1PoiseInflow)
        {
          RealVect prob_lo = RealVect::Zero;
          const RealVect loc  = EBArith::getFaceLocation(a_face, a_dx, prob_lo);
          Real radius = m_jet1PoiseInflowFunc->getRadius(loc);
          a_faceFlux = m_jet1PoiseInflowFunc->getVel(radius)[m_flowDir];
        }
    }
  else
    {
      //must be a solid wall or tangential velocity at inflow
      a_faceFlux = 0;
    }
}
void
EBCompositeMACProjector::
correctVelocityComponent(BaseIVFAB<Real>       &  a_coveredVel,
                         const Vector<VolIndex>&  a_coveredFace,
                         const IntVectSet      &  a_coveredSets,
                         const EBFluxFAB       &  a_macGradient,
                         const EBISBox         &  a_ebisBox,
                         int    a_coveredFaceDir,  Side::LoHiSide a_sd,    int a_faceGradComp)
{
  CH_TIME("EBCompositeMACProjector::correctVelocityComponent");
  //if a_coveredFaceDir  == faceGradComp, just linearly extrapolate gradient and subtract.
  //if a_coveredFaceDir != faceGradComp, need to average to cell centers then extrapolate and subtract
  CH_assert(a_coveredVel.nComp() == 1);
  CH_assert(a_macGradient.nComp() == 1);
  const EBFaceFAB& macGradFAB = a_macGradient[a_faceGradComp];
  for (int ivof = 0; ivof < a_coveredFace.size(); ivof++)
    {
      const VolIndex& vof = a_coveredFace[ivof];
      Vector<FaceIndex> nearFaces, farFaces;
      bool hasNearFaces = false;
      bool hasFarFaces  = false;
      VolIndex  nearVoF, farVoF;
      FaceIndex nearFace, farFace;

      nearFaces = a_ebisBox.getFaces(vof, a_coveredFaceDir, flip(a_sd));
      hasNearFaces = (nearFaces.size()==1) && (!nearFaces[0].isBoundary());
      if (hasNearFaces)
        {
          nearFace = nearFaces[0];
          nearVoF = nearFace.getVoF(flip(a_sd));

          farFaces = a_ebisBox.getFaces(nearVoF, a_coveredFaceDir, flip(a_sd));
          hasFarFaces = (farFaces.size()==1)  && (!farFaces[0].isBoundary());
          if (hasFarFaces)
            {
              farFace = farFaces[0];
              farVoF  = farFace.getVoF(flip(a_sd));
            }
        }
      Real extrapGrad;
      if (a_coveredFaceDir == a_faceGradComp)
        {
          //if a_coveredFaceDir  == faceGradComp, just linearly extrapolate gradient and subtract.
          if (hasNearFaces && hasFarFaces)
            {
              Real nearGrad  = macGradFAB(nearFace, 0);
              Real farGrad   = macGradFAB(farFace,  0);
              extrapGrad = 2.0*nearGrad - farGrad;
            }
          else if (hasNearFaces)
            {
              extrapGrad  = macGradFAB(nearFace, 0);
            }
          else
            {
              extrapGrad = 0.0;
            }

        }
      else
        {
          //if a_coveredFaceDir != faceGradComp, need to average to cell centers then extrapolate and subtract
          Real nearGrad = getAverageFaceGrad(vof,     macGradFAB, a_ebisBox, a_faceGradComp);
          if (hasNearFaces)
            {
              Real farGrad =  getAverageFaceGrad(nearVoF, macGradFAB, a_ebisBox, a_faceGradComp);
              extrapGrad = 1.5*nearGrad - 0.5*farGrad;
            }
          else
            {
              extrapGrad = nearGrad;
            }
        }
      a_coveredVel(vof, 0) -= extrapGrad;
    }
}
Real
EBGradDivFilter::
getDomainDivergence(const EBCellFAB&       a_gradVel,
                    const EBCellFAB&       a_vel,
                    const EBFluxFAB&       a_fluxVel,
                    const Box&             a_grid,
                    const EBISBox&         a_ebisBox,
                    const int&             a_faceDir,
                    const FaceIndex&       a_face,
                    const Side::LoHiSide&  a_side)
{
  CH_TIME("EBGradDivFilter::getDomainDivergence");
  Real divVel;
  CH_assert(a_fluxVel.nComp() == 1);
  Real velB = a_fluxVel[a_faceDir](a_face, 0);
  //compute normal derivative.
  //need second-order derivative at the boundary so
  //given u_b, u_i, u_i+1,
  //ux_b = (9u_i - u_i+1 - 8u_b)/(6 dx)
  VolIndex  vofNear, vofStart;
  Real valNear=0;
  Real valStart =0;
  bool hasVoFNear;
  vofStart = a_face.getVoF(flip(a_side));
  valStart = a_vel(vofStart, a_faceDir);
  int iflipsign = sign(flip(a_side));
  Real rsign = Real(iflipsign);

  Vector<FaceIndex> facesNear = a_ebisBox.getFaces(vofStart, a_faceDir, flip(a_side));
  hasVoFNear = (facesNear.size() == 1);
  if (hasVoFNear)
    {
      vofNear = facesNear[0].getVoF(flip(a_side));
      valNear = a_vel(vofNear, a_faceDir);
    }

  Real hsign = rsign*m_dxFine[a_faceDir];
  if (hasVoFNear)
    {
      divVel  = (2.25*(valStart-velB)  - 0.25*(valNear-velB))/(0.75*hsign);
    }
  else
    {
      //if there is no farther away value, drop order of derivative
      divVel = (valStart - velB)/(0.5*hsign);
    }

  //now add in tangential derivatives of tangential velocities
  //using extrapolated gradients
  for (int tanDir = 0; tanDir< SpaceDim; tanDir++)
    {
      if (tanDir != a_faceDir)
        {
          Real gradVel = 0.0;
          int gradcomp = getGradComp(tanDir, tanDir);
          if (hasVoFNear)
            {
              Real gradNear = a_gradVel(vofNear,   gradcomp);
              Real gradStart = a_gradVel(vofStart, gradcomp);
              gradVel = 1.5*gradStart - 0.5*gradNear;
            }
          else
            {
              Real gradStart = a_gradVel(vofStart, gradcomp);
              gradVel = gradStart;
            }
          divVel += gradVel;
        }
    }

  return divVel;

}