/*============================================================================
 * Calculation of DLVO forces
 *============================================================================*/

/*
  This file is part of Code_Saturne, a general-purpose CFD tool.

  Copyright (C) 1998-2013 EDF S.A.

  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.
*/

/*----------------------------------------------------------------------------*/

/*============================================================================
 * Functions dealing with the DLVO forces
 *============================================================================*/

#include "cs_defs.h"

/*----------------------------------------------------------------------------
 * Standard C library headers
 *----------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <assert.h>

/*----------------------------------------------------------------------------
 *  Local headers
 *----------------------------------------------------------------------------*/

#include "bft_printf.h"
#include "bft_error.h"
#include "bft_mem.h"

#include "fvm_periodicity.h"

#include "cs_base.h"
#include "cs_interface.h"
#include "cs_mesh.h"
#include "cs_mesh_quantities.h"
#include "cs_parall.h"
#include "cs_prototypes.h"
#include "cs_search.h"
#include "cs_lagr_utils.h"
#include "cs_halo.h"

/*----------------------------------------------------------------------------
 *  Header for the current file
 *----------------------------------------------------------------------------*/

#include "cs_lagr_dlvo.h"

/*----------------------------------------------------------------------------*/

BEGIN_C_DECLS

/*! \cond DOXYGEN_SHOULD_SKIP_THIS */

/*============================================================================
 * Local macro declarations
 *============================================================================*/

#define PG_CST 8.314  /* Ideal gas constant */

/*============================================================================
 * Local structure declarations
 *============================================================================*/

static cs_lagr_dlvo_param_t cs_lagr_dlvo_param;

/*============================================================================
 * Static global variables
 *============================================================================*/

static const double _pi = 3.14159265358979323846;

/*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */

/*============================================================================
 * Public function prototypes for Fortran API
 *============================================================================*/

/*----------------------------------------------------------------------------
 * DLVO initialization:
 *  - Retrieve various parameters for storing in global structure
 *  - Compute and store the Debye screening length
 *----------------------------------------------------------------------------*/

void
CS_PROCF (dlvo_init, DLVO_INIT)(const cs_real_t   *faraday_cst,
                                const cs_real_t   *free_space_permit,
                                const cs_real_t   *water_permit,
                                const cs_real_t   *ionic_strength,
                                const cs_real_t    temperature[],
                                const cs_real_t   *valen,
                                const cs_real_t   *phi_p,
                                const cs_real_t   *phi_s,
                                const cs_real_t   *cstham,
                                const cs_real_t   *dcutof,
                                const cs_real_t   *lambwl,
                                const cs_real_t   *kboltz)
{
  int ifac;

  const cs_mesh_t  *mesh = cs_glob_mesh;

  /* Retrieve physical parameters related to clogging modeling */
  /* and fill the global structure cs_lagr_clog_param          */

  cs_lagr_dlvo_param.faraday_cst = *faraday_cst;
  cs_lagr_dlvo_param.free_space_permit = *free_space_permit;
  cs_lagr_dlvo_param.water_permit = *water_permit;
  cs_lagr_dlvo_param.ionic_strength = *ionic_strength;
  cs_lagr_dlvo_param.valen = *valen;
  cs_lagr_dlvo_param.phi_p = *phi_p;
  cs_lagr_dlvo_param.phi_s = *phi_s;
  cs_lagr_dlvo_param.cstham = *cstham;
  cs_lagr_dlvo_param.dcutof = *dcutof;
  cs_lagr_dlvo_param.lambwl = *lambwl;
  cs_lagr_dlvo_param.kboltz = *kboltz;

  /* Allocate memory for the temperature and Debye length arrays */

  if (cs_lagr_dlvo_param.temperature == NULL)
    BFT_MALLOC(cs_lagr_dlvo_param.temperature, mesh->n_b_faces, cs_real_t);

  if (cs_lagr_dlvo_param.debye_length == NULL)
    BFT_MALLOC(cs_lagr_dlvo_param.debye_length, mesh->n_b_faces, cs_real_t);

  /* Store the temperature */

  for (ifac = 0; ifac < mesh->n_b_faces; ifac++)
    cs_lagr_dlvo_param.temperature[ifac] = temperature[ifac];

  /* Computation and storage of the Debye length */

  for (ifac = 0; ifac < mesh->n_b_faces ; ifac++)

    cs_lagr_dlvo_param.debye_length[ifac]
      =   pow(2e3 * pow(cs_lagr_dlvo_param.faraday_cst,2)
        * cs_lagr_dlvo_param.ionic_strength /
        (cs_lagr_dlvo_param.water_permit
         * cs_lagr_dlvo_param.free_space_permit * PG_CST
         * cs_lagr_dlvo_param.temperature[ifac]), -0.5);

#if 0 && defined(DEBUG) && !defined(NDEBUG)
  bft_printf(" cstfar = %g\n", cs_lagr_dlvo_param.faraday_cst);
  bft_printf(" epsvid = %g\n", cs_lagr_dlvo_param.free_space_permit);
  bft_printf(" epseau = %g\n", cs_lagr_dlvo_param.water_permit);
  bft_printf(" fion   = %g\n", cs_lagr_dlvo_param.ionic_strength);
  bft_printf(" temp[1]   = %g\n", cs_lagr_dlvo_param.temperature[0]);
  bft_printf(" valen   = %g\n", cs_lagr_dlvo_param.valen);
  bft_printf(" debye[1]   = %g\n", cs_lagr_dlvo_param.debye_length[0]);
  bft_printf(" phi_p   = %g\n", cs_lagr_dlvo_param.phi_p);
  bft_printf(" phi_s  = %g\n", cs_lagr_dlvo_param.phi_s);
#endif

}

/*============================================================================
 * Public function prototypes
 *============================================================================*/

/*----------------------------------------------------------------------------
 * Deallocate the arrays storing temperature and Debye length.
 *----------------------------------------------------------------------------*/

void
cs_lagr_dlvo_finalize()
{
  BFT_FREE(cs_lagr_dlvo_param.temperature);
  BFT_FREE(cs_lagr_dlvo_param.debye_length);
}

/*----------------------------------------------------------------------------
 * Compute the energy barrier for a smooth wall.
 *----------------------------------------------------------------------------*/

void
cs_lagr_barrier(const void                     *particle,
                const cs_lagr_attribute_map_t  *attr_map,
                cs_lnum_t                       face_id,
                cs_real_t                      *energy_barrier)
{
  cs_lnum_t i;
  cs_real_t rpart = cs_lagr_particle_get_real(particle, attr_map,
                                              CS_LAGR_DIAMETER) * 0.5;

  *energy_barrier = 0.;

  cs_real_t barr = 0.;
  cs_real_t distp = 0.;

  /* Computation of the energy barrier */

  for (i = 0; i < 1001; i++) {

    cs_real_t  step = 1e-10;

    /* Interaction between the sphere and the plate */

    distp = cs_lagr_dlvo_param.dcutof + i * step;

    cs_real_t var1
      = cs_lagr_van_der_waals_sphere_plane(distp,
                                           rpart,
                                           cs_lagr_dlvo_param.lambwl,
                                           cs_lagr_dlvo_param.cstham);

    cs_real_t var2
      = cs_lagr_edl_sphere_plane(distp,
                                 rpart,
                                 cs_lagr_dlvo_param.valen,
                                 cs_lagr_dlvo_param.phi_p,
                                 cs_lagr_dlvo_param.phi_s,
                                 cs_lagr_dlvo_param.kboltz,
                                 cs_lagr_dlvo_param.temperature[face_id],
                                 cs_lagr_dlvo_param.debye_length[face_id],
                                 cs_lagr_dlvo_param.free_space_permit,
                                 cs_lagr_dlvo_param.water_permit);

    barr = (var1 + var2);

    if (barr >  *energy_barrier)
      *energy_barrier = barr;
    if ( *energy_barrier < 0)
      *energy_barrier = 0;
   }

  *energy_barrier = *energy_barrier / rpart;
}

/*----------------------------------------------------------------------------
 * Van der Waals interaction between a sphere and a plane
 * using formulas from Czarnecki (large distances)
 *                 and Gregory (small distances)
 *----------------------------------------------------------------------------*/

cs_real_t
cs_lagr_van_der_waals_sphere_plane (cs_real_t distp,
                                    cs_real_t rpart,
                                    cs_real_t lambwl,
                                    cs_real_t cstham)
{
  cs_real_t var;

  if (distp < (lambwl / 2 / _pi)) {
    var = -cstham * rpart / (6 * distp)
          * (1 / (1 + 14 * distp / lambwl + 5 * _pi/4.9
          *  pow(distp,3) / lambwl / pow(rpart,2)));
  }
  else {
    var = cstham
      * ((2.45 * lambwl ) /(60. * _pi)
         * (  (distp - rpart) / pow(distp,2)
            - (distp + 3. * rpart) / pow(distp + 2. * rpart,2))
            - 2.17 / 720. / pow(_pi,2) * pow(lambwl,2) * ((distp - 2. * rpart)
            / pow(distp,3) - (distp + 4. * rpart) / pow(distp + 2. * rpart,3))
            + 0.59 / 5040. / pow(_pi,3) * pow(lambwl,3)
            * ((distp - 3. * rpart) /  pow(distp,4) - (distp + 5. * rpart)
            / pow(distp + 2. * rpart,4)));
  }

  return var;
}

/*----------------------------------------------------------------------------
 * Calculation of the Van der Waals interaction between two spheres
 * following the formula from Gregory (1981a)
 *----------------------------------------------------------------------------*/

cs_real_t
cs_lagr_van_der_waals_sphere_sphere(cs_real_t  distcc,
                                    cs_real_t  rpart1,
                                    cs_real_t  rpart2,
                                    cs_real_t  lambwl,
                                    cs_real_t  cstham)
{
  cs_real_t var = - cstham * rpart1 * rpart2 / (6 * (distcc - rpart1 - rpart2)
               * (rpart1 + rpart2)) * (1 - 5.32 * (distcc - rpart1 - rpart2)
              / lambwl * log(1 + lambwl / (distcc - rpart1 - rpart2) / 5.32));

  return var;
}

/*----------------------------------------------------------------------------
 * Electric Double Layer (EDL) interaction between a sphere and a plane
 * using the formula from Bell & al (1970)
 * based on the McCartney & Levine method
 *----------------------------------------------------------------------------*/

cs_real_t
cs_lagr_edl_sphere_plane(cs_real_t  distp,
                         cs_real_t  rpart,
                         cs_real_t  valen,
                         cs_real_t  phi1,
                         cs_real_t  phi2,
                         cs_real_t  kboltz,
                         cs_real_t  temp,
                         cs_real_t  debye_length,
                         cs_real_t  free_space_permit,
                         cs_real_t  water_permit)
{

  cs_real_t charge = 1.6e-19;
  /* Reduced zeta potential */
  cs_real_t lphi1 =  valen * charge * phi1 /  kboltz / temp;
  cs_real_t lphi2 =  valen * charge * phi2 /  kboltz / temp;

  cs_real_t tau = rpart / debye_length;

  /* Extended reduced zeta potential */
  /* (following the work from Ohshima et al, 1982, JCIS, 90, 17-26) */

  lphi1 = 8. * tanh(lphi1 / 4.) /
         ( 1. + pow(1. - (2. * tau + 1.) / (pow(tau + 1,2))
         * pow(tanh(lphi1 / 4.),2),0.5));

  lphi2 = 4. * tanh(lphi2 / 4.) ;

  cs_real_t alpha =   sqrt((distp + rpart) / rpart)
                    + sqrt(rpart / (distp + rpart));
  cs_real_t omega1 = pow(lphi1,2) + pow(lphi2,2) + alpha * lphi1 * lphi2;
  cs_real_t omega2 = pow(lphi1,2) + pow(lphi2,2) - alpha * lphi1 * lphi2;
  cs_real_t gamma = sqrt(rpart / (distp + rpart)) * exp(-1./debye_length * distp);

  cs_real_t var = 2 * _pi * free_space_permit * water_permit
    * pow((kboltz * temp / (1. * valen) / charge),2)
                  * rpart * (distp + rpart) / (distp + 2 * rpart)
                  * (omega1 * log(1 + gamma) + omega2 * log(1 - gamma));

  return var;
}

/*----------------------------------------------------------------------------
 * Calculation of the EDL interaction between two spheres
 * using the formula from Bell & al (1970)
 * based on the McCartney & Levine method
 *----------------------------------------------------------------------------*/

cs_real_t
cs_lagr_edl_sphere_sphere(cs_real_t  distcc,
                          cs_real_t  rpart1,
                          cs_real_t  rpart2,
                          cs_real_t  valen,
                          cs_real_t  phi1,
                          cs_real_t  phi2,
                          cs_real_t  kboltz,
                          cs_real_t  temp,
                          cs_real_t  debye_length,
                          cs_real_t  free_space_permit,
                          cs_real_t  water_permit)
{
  cs_real_t charge = 1.6e-19;

  /* Reduced zeta potential */
  cs_real_t lphi1 =  valen * charge * phi1 /  kboltz / temp;
  cs_real_t lphi2 =  valen * charge * phi2 /  kboltz / temp;


  /* Extended reduced zeta potential */
  /* (following the work from Ohshima et al, 1982, JCIS, 90, 17-26) */

  cs_real_t tau1 = rpart1 / debye_length;
  lphi1 = 8. * tanh(lphi1 / 4.) /
    ( 1. + pow(1. - (2. * tau1 + 1.) / (pow(tau1 + 1,2))
               * pow(tanh(lphi1 / 4.),2),0.5));

  cs_real_t tau2 = rpart2 / debye_length;
  lphi2 = 8. * tanh(lphi2 / 4.) /
         ( 1. + pow(1. - (2. * tau2 + 1.) / (pow(tau2 + 1,2))
          * pow(tanh(lphi2 / 4.),2),0.5));

  cs_real_t alpha =    sqrt(rpart2 * (distcc - rpart2)
                    / (rpart1 * (distcc - rpart1)))
                     + sqrt(rpart1 * (distcc - rpart1)
                    / (rpart2 * (distcc - rpart2)));

  cs_real_t omega1 = pow(lphi1,2) + pow(lphi2,2) + alpha * lphi1 * lphi2;

  cs_real_t omega2 = pow(lphi1,2) + pow(lphi2,2) - alpha * lphi1 * lphi2;

  cs_real_t gamma = sqrt(rpart1 * rpart2 / (distcc-rpart1) / (distcc-rpart2))
                    *exp(1. / debye_length * (rpart1 + rpart2 - distcc));

  cs_real_t var = 2 * _pi * free_space_permit * water_permit
                  * pow((kboltz * temp / charge),2)
                  * rpart1 * rpart2 * (distcc - rpart1) * (distcc - rpart2)
                  / (distcc * (  distcc * (rpart1  + rpart2)
                               - pow(rpart1,2) - pow(rpart2,2)))
                  * (omega1 * log(1 + gamma) + omega2 * log(1 - gamma));

  return var;
}

/*----------------------------------------------------------------------------*/

END_C_DECLS
