/*  This file is part of the OpenLB library
 *
 *  Copyright (C) 2016-2017 Davide Dapelo, Mathias J. Krause
 *  OpenLB 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.
*/
/** \file
 * Helper functions for the implementation of the
 * Guo-ZhapLB dynamics.
 */
#ifndef LB_GUOZHAO_HELPERS_H
#define LB_GUOZHAO_HELPERS_H
#include "dynamics/guoZhaoLatticeDescriptors.h"
#include "core/cell.h"
#include "core/util.h"
namespace olb {
// Forward declarations
template struct GuoZhaoLbDynamicsHelpers;
template struct GuoZhaoLbExternalHelpers;
/// This structure forwards the calls to the appropriate Guo Zhao helper class
template
struct GuoZhaoLbHelpers {
  static T equilibrium(int iPop, T epsilon, T rho, const T u[DESCRIPTOR::d], const T uSqr) {
    return GuoZhaoLbDynamicsHelpers
           ::equilibrium(iPop, epsilon, rho, u, uSqr);
  }
  static T bgkCollision(Cell& cell, T const& epsilon, T const& rho, const T u[DESCRIPTOR::d], T const& omega) {
    return GuoZhaoLbDynamicsHelpers
           ::bgkCollision(cell, epsilon, rho, u, omega);
  }
  static void updateGuoZhaoForce(Cell& cell, const T u[DESCRIPTOR::d]) {
    GuoZhaoLbExternalHelpers::updateGuoZhaoForce(cell, u);
  }
  static void addExternalForce(Cell& cell, const T u[DESCRIPTOR::d], T omega, T rho, T epsilon=(T)1)
  {
    GuoZhaoLbExternalHelpers::addExternalForce(cell, u, omega, rho, epsilon);
  }
};  // struct GuoZhaoLbHelpers
/// All Guo Zhao helper functions are inside this structure
template
struct GuoZhaoLbDynamicsHelpers {
  /// Computation of Guo Zhao equilibrium distribution - original (compressible) formulation following Guo and Zhao (2002).
  static T forceEquilibrium(int iPop, T epsilon, T rho, const T u[DESCRIPTORBASE::d], const T force[DESCRIPTORBASE::d], T nu) {
  }
  /// Computation of Guo Zhao equilibrium distribution - original (compressible) formulation following Guo and Zhao (2002).
  static T equilibrium(int iPop, T epsilon, T rho, const T u[DESCRIPTORBASE::d], const T uSqr) {
    T c_u = T();
    for (int iD=0; iD < DESCRIPTORBASE::d; ++iD) {
      c_u += descriptors::c(iPop,iD)*u[iD];
    }
    return rho * descriptors::t(iPop) * (
             (T)1 + descriptors::invCs2() * c_u +
             descriptors::invCs2() * descriptors::invCs2()/((T)2*epsilon) * c_u*c_u -
             descriptors::invCs2()/((T)2*epsilon) * uSqr
           ) - descriptors::t(iPop);
  }
  /// Guo Zhao BGK collision step
  static T bgkCollision(CellBase& cell, T const& epsilon, T const& rho, const T u[DESCRIPTORBASE::d], T const& omega) {
    const T uSqr = util::normSqr(u);
    for (int iPop=0; iPop < DESCRIPTORBASE::q; ++iPop) {
      cell[iPop] *= (T)1-omega;
      cell[iPop] += omega * GuoZhaoLbDynamicsHelpers::equilibrium (
                      iPop, epsilon, rho, u, uSqr );
    }
    return uSqr;
  }
};  // struct GuoZhaoLbDynamicsHelpers
/// Helper functions for dynamics that access external field
template
/// Updates Guo Zhao porous force
struct GuoZhaoLbExternalHelpers {
  static void updateGuoZhaoForce(Cell& cell, const T u[DESCRIPTOR::d]) {
    T epsilon = *cell.template getFieldPointer();
    T k       = *cell.template getFieldPointer();
    T nu      = *cell.template getFieldPointer();
    T* force[DESCRIPTOR::d];
    for (int iDim=0; iDim () + iDim;
    }
    T bodyF[DESCRIPTOR::d];
    for (int iDim=0; iDim ()[iDim];
    }
    T uMag = 0.;
    for (int iDim=0; iDim & cell, const T u[DESCRIPTOR::d], T omega, T rho, T epsilon)
  {
    T* force = cell.template getFieldPointer();
    for (int iPop=0; iPop < DESCRIPTOR::q; ++iPop) {
      T c_u = T();
      for (int iD=0; iD < DESCRIPTOR::d; ++iD) {
        c_u += descriptors::c(iPop,iD)*u[iD];
      }
      c_u *= descriptors::invCs2()*descriptors::invCs2()/epsilon;
      T forceTerm = T();
      for (int iD=0; iD < DESCRIPTOR::d; ++iD) {
        forceTerm +=
          (   (epsilon*(T)descriptors::c(iPop,iD)-u[iD]) * descriptors::invCs2()/epsilon
              + c_u * descriptors::c(iPop,iD)
          )
          * force[iD];
      }
      forceTerm *= descriptors::t(iPop);
      forceTerm *= T(1) - omega/T(2);
      forceTerm *= rho;
      cell[iPop] += forceTerm;
    }
  }
};  // struct GuoZhaoLbExternalHelpers
}  // namespace olb
#endif