text: /*
   * Copyright (c) 2014-2017 The University of Utah
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to
   * deal in the Software without restriction, including without limitation the
   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   * sell copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   * IN THE SOFTWARE.
   */
  
  #ifndef SpatialOps_FVStaggeredStencilBCOp_h
  #define SpatialOps_FVStaggeredStencilBCOp_h
  
  #include <spatialops/structured/stencil/FVStaggeredOperatorTypes.h>
  #include <spatialops/NeboStencilBuilder.h>
  
  namespace SpatialOps{
  
    template< typename CellT > class BCOpTypesGeneral
    {
      typedef BasicOpTypes<CellT> OpT;
    public:
      typedef NeboBoundaryConditionBuilder<typename OpT::InterpC2FX>  DirichletX;
      typedef NeboBoundaryConditionBuilder<typename OpT::InterpC2FY>  DirichletY;
      typedef NeboBoundaryConditionBuilder<typename OpT::InterpC2FZ>  DirichletZ;
  
      typedef NeboBoundaryConditionBuilder<typename OpT::GradX>       NeumannX;
      typedef NeboBoundaryConditionBuilder<typename OpT::GradY>       NeumannY;
      typedef NeboBoundaryConditionBuilder<typename OpT::GradZ>       NeumannZ;
    };
  
    /**
     * \class  BCOpTypes
     * \author James C. Sutherland, Tony Saad
     * \date   November, 2014
     * \ingroup boundaryconditions
     *
     * \brief Provides type inference to obtain BC operator types for a field type
     *
     * The following public typedefs are made:
     *
     *  Type  | Description
     *  --------------|----------------
     *  \c DirichletX | Set ghost value to achieve a desired value on the x-surface
     *  \c DirichletY | Set ghost value to achieve a desired value on the y-surface
     *  \c DirichletZ | Set ghost value to achieve a desired value on the z-surface
     *  \c NeumannX   | Set ghost value to achieve a desired derivative on the x-surface
     *  \c NeumannY   | Set ghost value to achieve a desired derivative on the y-surface
     *  \c NeumannZ   | Set ghost value to achieve a desired derivative on the z-surface
     *
     * Example:
     * \code{.cpp}
     * typedef BCOpTypes<SVolField>::NeumannY  BCOpT;
     * BCOpT bcop = *opDB.retrieve_operator<BCOpT>();
     * bcop( mask, field, bcValue );  // set bcValue by inverting operator on mask points
     * \endcode
     */
    template<typename FieldT> struct BCOpTypes : public BCOpTypesGeneral<FieldT>{};
  
    template<> struct BCOpTypes<SSurfXField> {
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Divergence, SSurfXField,SVolField>::type> NeumannX;
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Interpolant,SSurfXField,SVolField>::type> DirichletX;
    };
  
    template<> struct BCOpTypes<SSurfYField> {
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Divergence, SSurfYField,SVolField>::type> NeumannY;
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Interpolant,SSurfYField,SVolField>::type> DirichletY;
    };
  
    template<> struct BCOpTypes<SSurfZField> {
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Divergence, SSurfZField,SVolField>::type> NeumannZ;
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Interpolant,SSurfZField,SVolField>::type> DirichletZ;
    };
  
    // partial specialization with inheritance for XVolFields
    template<> struct BCOpTypes<XVolField> : public BCOpTypesGeneral<XVolField>{
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<GradientX, XVolField, XVolField >::type>  NeumannX;
    };
  
    // partial specialization with inheritance for YVolFields
    template<> struct BCOpTypes<YVolField> : public BCOpTypesGeneral<YVolField>{
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<GradientY, YVolField, YVolField >::type>  NeumannY;
    };
  
    // partial specialization with inheritance for ZVolFields
    template<> struct BCOpTypes<ZVolField> : public BCOpTypesGeneral<ZVolField> {
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<GradientZ, ZVolField, ZVolField >::type> NeumannZ;
    };
  
    template<> struct BCOpTypes< FaceTypes<XVolField>::XFace >{
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Interpolant, XSurfXField, XVolField >::type> DirichletX;
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Divergence,  XSurfXField, XVolField >::type> NeumannX;
    };
  
    template<> struct BCOpTypes<FaceTypes<YVolField>::YFace>{
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Interpolant, YSurfYField, YVolField >::type> DirichletY;
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Divergence,  YSurfYField, YVolField >::type> NeumannY;
    };
  
    template<> struct BCOpTypes<FaceTypes<ZVolField>::ZFace>
    {
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Interpolant, ZSurfZField, ZVolField >::type> DirichletZ;
      typedef NeboBoundaryConditionBuilder<OperatorTypeBuilder<Divergence,  ZSurfZField, ZVolField >::type> NeumannZ;
    };
  
  
    /**
     * \class  BCOpTypesFromDirection
     * \author James C. Sutherland
     * \date   November, 2014
     * \ingroup boundaryconditions
     *
     * \brief Given a field type and direction type, this provides type inference
     *        to obtain BC operator types to achieve Dirichlet & Neumann BCs.
     *
     * \tparam FieldT the type of field that we are interested in setting BCs on.
     * \tparam DirT the direction type that we want to achieve the BC on.  This is
     *         one of: SpatialOps::XDIR, SpatialOps::YDIR, SpatialOps::ZDIR
     *
     * The following public typedefs are made:
     *  - Dirichlet
     *  - Neumann
     * These correspond to an operator that will achieve Dirichlet and Neumann
     * conditions on \c FieldT in direction \c DirT.
     *
     * Example:
     * \code{.cpp}
     * typedef BCOpTypesFromDirection<SVolField,XDIR>::Neumann  BCOpT;
     * BCOpT bcop = *opDB.retrieve_operator<BCOpT>();
     * bcop( mask, field, bcValue );  // set bcValue by inverting operator on mask points
     * \endcode
     */
    template< typename FieldT, typename DirT> struct BCOpTypesFromDirection;
  
    template< typename FieldT > struct BCOpTypesFromDirection<FieldT,XDIR>{
      typedef typename BCOpTypes<FieldT>::DirichletX Dirichlet;
      typedef typename BCOpTypes<FieldT>::NeumannX   Neumann;
    };
    template< typename FieldT > struct BCOpTypesFromDirection<FieldT,YDIR>{
      typedef typename BCOpTypes<FieldT>::DirichletY Dirichlet;
      typedef typename BCOpTypes<FieldT>::NeumannY   Neumann;
    };
    template< typename FieldT > struct BCOpTypesFromDirection<FieldT,ZDIR>{
      typedef typename BCOpTypes<FieldT>::DirichletZ Dirichlet;
      typedef typename BCOpTypes<FieldT>::NeumannZ   Neumann;
    };
  
  } // namespace SpatialOps
  
  #endif // SpatialOps_FVStaggeredStencilBCOp_h
  