/*  This file is part of the OpenLB library
 *
 *  Copyright (C) 2006, 2007 Jonas Latt
 *  E-mail contact: info@openlb.net
 *  The most recent release of OpenLB can be downloaded at
 *  
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public
 *  License along with this program; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA  02110-1301, USA.
*/
#ifndef FINITE_DIFFERENCE_3D_H
#define FINITE_DIFFERENCE_3D_H
#include "finiteDifference.h"
namespace olb {
namespace fd {
template
struct DirectedGradients3D {
  static void interpolateVector( T velDeriv[DESCRIPTOR::d],
                                 BlockLattice3D const& blockLattice,
                                 int iX, int iY, int iZ );
  static void interpolateScalar( T& rhoDeriv,
                                 BlockLattice3D const& blockLattice,
                                 int iX, int iY, int iZ );
};
// Implementation for orthogonal==true; i.e. the derivative is along the boundary normal.
template
struct DirectedGradients3D {
  static void interpolateVector(T velDeriv[DESCRIPTOR::d],
                                BlockLattice3D const& blockLattice,
                                int iX, int iY, int iZ)
  {
    using namespace fd;
    T u0[DESCRIPTOR::d], u1[DESCRIPTOR::d], u2[DESCRIPTOR::d];
    blockLattice.get(iX,iY,iZ).computeU(u0);
    blockLattice.get (
      iX+(direction==0 ? (-orientation):0),
      iY+(direction==1 ? (-orientation):0),
      iZ+(direction==2 ? (-orientation):0)  ).computeU(u1);
    blockLattice.get (
      iX+(direction==0 ? (-2*orientation):0),
      iY+(direction==1 ? (-2*orientation):0),
      iZ+(direction==2 ? (-2*orientation):0) ).computeU(u2);
    for (int iD=0; iD const& blockLattice,
                                int iX, int iY, int iZ)
  {
    using namespace fd;
    // note that the derivative runs along direction.
    T rho0 = blockLattice.get(iX,iY,iZ).computeRho();
    T rho1 = blockLattice.get (
               iX+(direction==0 ? (-orientation):0),
               iY+(direction==1 ? (-orientation):0),
               iZ+(direction==2 ? (-orientation):0)  ).computeRho();
    T rho2 = blockLattice.get (
               iX+(direction==0 ? (-2*orientation):0),
               iY+(direction==1 ? (-2*orientation):0),
               iZ+(direction==2 ? (-2*orientation):0) ).computeRho();
    rhoDeriv = -orientation * boundaryGradient(rho0, rho1, rho2);
  }
};
// Implementation for orthogonal==false; i.e. the derivative is aligned with the boundary.
template
struct DirectedGradients3D {
  static void  interpolateVector(T velDeriv[DESCRIPTOR::d],
                                 BlockLattice3D const& blockLattice,
                                 int iX, int iY, int iZ)
  {
    using namespace fd;
    T u_p1[DESCRIPTOR::d], u_m1[DESCRIPTOR::d];
    blockLattice.get (
      iX+(deriveDirection==0 ? 1:0),
      iY+(deriveDirection==1 ? 1:0),
      iZ+(deriveDirection==2 ? 1:0) ).computeU(u_p1);
    blockLattice.get (
      iX+(deriveDirection==0 ? (-1):0),
      iY+(deriveDirection==1 ? (-1):0),
      iZ+(deriveDirection==2 ? (-1):0) ).computeU(u_m1);
    for (int iD=0; iD const& blockLattice,
                                 int iX, int iY, int iZ)
  {
    using namespace fd;
    T rho_p1 = blockLattice.get (
                 iX+(deriveDirection==0 ? 1:0),
                 iY+(deriveDirection==1 ? 1:0),
                 iZ+(deriveDirection==2 ? 1:0) ).computeRho();
    T rho_m1 = blockLattice.get (
                 iX+(deriveDirection==0 ? (-1):0),
                 iY+(deriveDirection==1 ? (-1):0),
                 iZ+(deriveDirection==2 ? (-1):0) ).computeRho();
    rhoDeriv = centralGradient(rho_p1, rho_m1);
  }
};
}  // namespace fd
}  // namespace olb
#endif