//called by projection solve for irreg x domain
void InflowOutflowPoissonDomainBC::getInhomFaceFlux(Real&                 a_faceFlux,
                                                    const VolIndex&       a_vof,
                                                    const int&            a_comp,
                                                    const EBCellFAB&      a_phi,
                                                    const RealVect&       a_probLo,
                                                    const RealVect&       a_dx,
                                                    const int&            a_idir,
                                                    const Side::LoHiSide& a_side,
                                                    const DataIndex&      a_dit,
                                                    const Real&           a_time)
{
  bool isOutflow= (a_side==Side::Hi) && (a_idir==m_flowDir);
  if (!isOutflow)
    {
      NeumannPoissonDomainBC neumannBC;
      neumannBC.setValue(0.0);
      neumannBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                                 a_dx, a_idir, a_side, a_dit, a_time);
    }
  else
    {
      DirichletPoissonDomainBC diriBC;
      diriBC.setValue(0.0);
      diriBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                              a_dx, a_idir, a_side, a_dit, a_time);
      // diriBC.getHigherOrderFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
      //                               a_dx, a_idir, a_side, a_dit, a_time);
    }
}
//called by projection solve for irreg x domain
void InflowOutflowPoissonDomainBC::getInhomFaceFlux(Real&                 a_faceFlux,
                                                    const VolIndex&       a_vof,
                                                    const int&            a_comp,
                                                    const EBCellFAB&      a_phi,
                                                    const RealVect&       a_probLo,
                                                    const RealVect&       a_dx,
                                                    const int&            a_idir,
                                                    const Side::LoHiSide& a_side,
                                                    const DataIndex&      a_dit,
                                                    const Real&           a_time)
{
  bool isInsideTube = false;
  if ((m_doJet2) && (a_side==Side::Hi) && (a_idir == m_flowDir))
    {
      RealVect probLo = RealVect::Zero;
      RealVect loc = EBArith::getVofLocation(a_vof,a_dx,probLo);
      loc[a_idir] += 0.5*a_dx[a_idir]; // loc at face center
      const RealVect tubeCenter = m_jet2PoiseInflowFunc->getTubeCenter();
      Real tubeRadius = m_jet2PoiseInflowFunc->getTubeRadius();
      Real radius = m_jet2PoiseInflowFunc->getRadius(loc);
      CH_assert(loc[m_flowDir] == tubeCenter[m_flowDir]);

// 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;
        }
    }
  bool isOutflow = (a_side==Side::Hi) && (a_idir==m_flowDir) && (!(isInsideTube));
//  bool isOutflow= (a_side==Side::Hi) && (a_idir==m_flowDir);
  if (!isOutflow)
    {
      NeumannPoissonDomainBC neumannBC;
      neumannBC.setValue(0.0);
      neumannBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                                 a_dx, a_idir, a_side, a_dit, a_time);
    }
  else
    {
      DirichletPoissonDomainBC diriBC;
      diriBC.setValue(0.0);
      diriBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                              a_dx, a_idir, a_side, a_dit, a_time);
      // diriBC.getHigherOrderFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
      //                               a_dx, a_idir, a_side, a_dit, a_time);
    }
}
//called by EBAMRPoissonOp::applyOp for viscous Helmholtz EB x domain
void InflowOutflowHelmholtzDomainBC::getInhomFaceFlux(Real&                 a_faceFlux,
                                                      const VolIndex&       a_vof,
                                                      const int&            a_comp,
                                                      const EBCellFAB&      a_phi,
                                                      const RealVect&       a_probLo,
                                                      const RealVect&       a_dx,
                                                      const int&            a_idir,
                                                      const Side::LoHiSide& a_side,
                                                      const DataIndex&      a_dit,
                                                      const Real&           a_time)
{
  //vel: outflow is Neumann. all others Dirichlet
  int velcomp =  DirichletPoissonEBBC::s_velComp;
  bool isOutflow =  ((a_side==Side::Hi) && (a_idir==m_flowDir));
  bool isInflow =  ((a_side==Side::Lo) && (a_idir==m_flowDir));
  bool isSlipWall = ((a_idir!=m_flowDir) && (a_idir != velcomp) && ((m_doSlipWallsHi[a_idir]==1 && a_side == Side::Hi)||(m_doSlipWallsLo[a_idir]==1 && a_side == Side::Lo)));
  bool isVelNeum =  (isOutflow || isSlipWall);

  if (isVelNeum)
    {
      NeumannPoissonDomainBC neumannBC;
      neumannBC.setValue(0.);
      neumannBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                                 a_dx, a_idir, a_side, a_dit,a_time);
    }
  else if (isInflow)
    {
      DirichletPoissonDomainBC diriBC;
      if (velcomp==m_flowDir)
        {
          if (!m_doPoiseInflow && !m_doWomersleyInflow)
          {
            diriBC.setValue(m_inflowVel);
          }
          else if (m_doPoiseInflow)
            {
              diriBC.setFunction(m_poiseInflowFunc);
            }
          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]);

              diriBC.setValue(m_inflowVel*VelMult/2);
            }
        }
      else
        {
          diriBC.setValue(0.0);
        }
      //called by EBAMRPoissonOp::applyOp for viscous Helmholtz EB x domain
      if (s_higherOrderHelmBC)
        {
          diriBC.getHigherOrderInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit, a_time);
        }
      else
        {
          diriBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit,a_time);
        }
    }
  else
    {
      //wall bc no slip
      DirichletPoissonDomainBC diriBC;
      diriBC.setValue(0.0);
      if (s_higherOrderHelmBC)
        {
          diriBC.getHigherOrderInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit, a_time);
        }
      else
        {
          diriBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit,a_time);
        }
    }
}
//called by EBAMRPoissonOp::applyOp for viscous Helmholtz EB x domain
void InflowOutflowHelmholtzDomainBC::getInhomFaceFlux(Real&                 a_faceFlux,
                                                      const VolIndex&       a_vof,
                                                      const int&            a_comp,
                                                      const EBCellFAB&      a_phi,
                                                      const RealVect&       a_probLo,
                                                      const RealVect&       a_dx,
                                                      const int&            a_idir,
                                                      const Side::LoHiSide& a_side,
                                                      const DataIndex&      a_dit,
                                                      const Real&           a_time)
{
  //vel: outflow is Neumann. all others Dirichlet
  int velcomp =  DirichletPoissonEBBC::s_velComp;
  bool isOutflow =  ((a_side==Side::Hi) && (a_idir==m_flowDir));
  bool isInflow =  ((a_side==Side::Lo) && (a_idir==m_flowDir));
  bool isSlipWall = ((a_idir!=m_flowDir) && (a_idir != velcomp) && ((m_doSlipWallsHi[a_idir]==1 && a_side == Side::Hi)||(m_doSlipWallsLo[a_idir]==1 && a_side == Side::Lo)));
//  bool isVelNeum =  (isOutflow || isSlipWall);

  if (isSlipWall)
    {
      NeumannPoissonDomainBC neumannBC;
      neumannBC.setValue(0.);
      neumannBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                                 a_dx, a_idir, a_side, a_dit,a_time);
    }
  else if (isInflow)
    {
      DirichletPoissonDomainBC diriBC;
      if (velcomp==m_flowDir)
        {
          if (!m_doJet1PoiseInflow)
          {
            diriBC.setValue(m_jet1inflowVel);
          }
          else if (m_doJet1PoiseInflow)
            {
              diriBC.setFunction(m_jet1PoiseInflowFunc);
            }
        }
      else
        {
          diriBC.setValue(0.0);
        }
      //called by EBAMRPoissonOp::applyOp for viscous Helmholtz EB x domain
      if (s_higherOrderHelmBC)
        {
          diriBC.getHigherOrderInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit, a_time);
        }
      else
        {
          diriBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit,a_time);
        }
    }
  else if (isOutflow)
    {
      if (!m_doJet2)
        {
          NeumannPoissonDomainBC neumannBC;
          neumannBC.setValue(0.);
          neumannBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                                     a_dx, a_idir, a_side, a_dit,a_time);
        }
      else if (m_doJet2)
        {
          const IntVect& iv = a_vof.gridIndex();
          RealVect loc = EBArith::getIVLocation(iv,a_dx,a_probLo);
          loc[a_idir] += 0.5*a_dx[a_idir]; // loc is face centered
          const RealVect tubeCenter = m_jet2PoiseInflowFunc->getTubeCenter();
          Real tubeRadius = m_jet2PoiseInflowFunc->getTubeRadius();
          CH_assert(loc[m_flowDir] == tubeCenter[m_flowDir]);
          bool isInsideTube = false;
          Real radius = m_jet2PoiseInflowFunc->getRadius(loc);

// 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))
            {
              NeumannPoissonDomainBC neumannBC;
              neumannBC.setValue(0.);
              neumannBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo,
                                         a_dx, a_idir, a_side, a_dit,a_time);
            }
          else if (isInsideTube)
            {
              DirichletPoissonDomainBC diriBC;
              if (velcomp==m_flowDir)
                {
                  if (!m_doJet2PoiseInflow)
                    {
                      diriBC.setValue(m_jet2inflowVel);
                    }
                  else if (m_doJet2PoiseInflow)
                    {
                      diriBC.setFunction(m_jet2PoiseInflowFunc);
                    }
                }
              else
                {
                  diriBC.setValue(0.0);
                }
              //called by EBAMRPoissonOp::applyOp for viscous Helmholtz EB x domain
              if (s_higherOrderHelmBC)
                {
                  diriBC.getHigherOrderInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit, a_time);
                }
              else
                {
                  diriBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit,a_time);
                }
            } 
        }
    }
  else
    {
      //wall bc no slip
      DirichletPoissonDomainBC diriBC;
      diriBC.setValue(0.0);
      if (s_higherOrderHelmBC)
        {
          diriBC.getHigherOrderInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit, a_time);
        }
      else
        {
          diriBC.getInhomFaceFlux(a_faceFlux, a_vof, a_comp, a_phi, a_probLo, a_dx, a_idir, a_side, a_dit,a_time);
        }
    }
}