// Copyright (C) 2005 Johan Hoffman.
// Licensed under the GNU GPL Version 2.
//
// Modified by Anders Logg 2005.
//
// First added:  2002-11-29
// Last changed: 2005-12-28
//
// A cG(1)cG(1) solver for the incompressible Navier-Stokes equations 
//
//     du/dt + u * grad u - nu * div grad u + grad p = f 
//     div u = 0 

#include <dolfin.h>
#include <unicorn/FSISolver.h>

using namespace dolfin::unicorn;
using namespace dolfin;


namespace Geo
{

    // Geometry details /////////////////////////////////////////////////
    real xmin = -1;
    real xmax = 1;

    real ymin = -0.1;
    real ymax = 0.1;

    real zmin = -0.1;
    real zmax = 0.1;

    real margin = DOLFIN_EPS + 1e-9;

    bool isStructure(const real* r)
    {
	return (2*ymax   < r[0] && r[0] < (2*ymax+0.5) &&
		0.45*ymax < r[1]);
    }
}
// Sub domain for outflow
class OutflowBoundary : public SubDomain
{
public:
  bool inside(const real* p, bool on_boundary) const
  {
    return on_boundary && (p[0] < (Geo::xmin + Geo::margin));
  }
};

// Sub domain for Dirichlet boundary condition
class ChannelBoundary : public SubDomain
{
public:
  bool inside(const real* p, bool on_boundary) const
  {
    return on_boundary && (p[0] >= (Geo::xmin + Geo::margin));
  }
};
//---------------------------------------------------------------------
// Force term
class ForceFunction_3D : public Function
{
public:
  ForceFunction_3D(Mesh& mesh) : Function(mesh) {}
  void eval(real* values, const real* x) const
  {
    int d = cell().dim();
    for(int i = 0; i < d; i++)
    {
      values[i] = 0.0;
    }
  }
};
//---------------------------------------------------------------------
class IC_Momentum : public Function
{
public:
  IC_Momentum(Mesh& mesh) : Function(mesh) {}
  void eval(real* values, const real* x) const
  {
    int d = cell().dim();
    for(int i = 0; i < d; i++)
    {
      values[i] = 0.0;
    }
  }
};
//---------------------------------------------------------------------
// Boundary condition for momentum equation 
class BC_Momentum_3D : public Function
{
public:
  BC_Momentum_3D(Mesh& mesh) : Function(mesh) {}
  real inflow(const real* r, unsigned int i) const
  {
    real u_bar = 15.0; // [ 0.2 1 2 ]
    
    if (i == 0) // x-direction
      return u_bar;
    else return 0;
  }
  
  void eval(real* values, const real* p) const
  {
    unsigned int d = cell().dim();
    for(unsigned int i = 0; i < d; i++)
    {
      if (p[0] > (Geo::xmax - Geo::margin)) {
	values[i] = -inflow(p,i);
      } else values[i] = 0.0;
    }
  }
};
//---------------------------------------------------------------------
// Boundary condition for continuity equation 
class BC_Continuity_3D : public Function
{
public:
  BC_Continuity_3D(Mesh& mesh) : Function(mesh) {}
  void eval(real* values, const real* p) const
  {
//     int d = cell().dim();
//     for(int i = 0; i < d; i++)
//     {
//       values[i] = 0.0;
//     }
    values[0] = 0.0;
  }
};
//---------------------------------------------------------------------
class BisectionFunction : public Function
{
public:
  BisectionFunction(Mesh& mesh) : Function(mesh) {}
  void eval(real* values, const real* p) const
  {
    if (Geo::isStructure(p))
      values[0] = 0.0;
    else
      values[0] = 1.0;
  }
};
//---------------------------------------------------------------------
int main(int argc, char* argv[])
{
    dolfin_init(argc, argv);
  
    Mesh mesh("simple3D.xml.gz");
  
    IC_Momentum	      ic(mesh);
    BC_Momentum_3D    bcf_mom(mesh);  
    BC_Continuity_3D  bcf_con(mesh);
    ForceFunction_3D  f(mesh);
    BisectionFunction bisect(mesh);

    ChannelBoundary cboundary;
    OutflowBoundary oboundary;
    DirichletBC bc_mom(bcf_mom, mesh, cboundary);
    DirichletBC bc_con(bcf_con, mesh, oboundary);
 
    // default values for benchmarking (Note: u_bar is in bc_mom)
    real eta  = 0.4;
    real miu  = 5.0e5;    // [ 0.5 0.5 2.0 ]
    real rhof = 1.0;                   // fluid:     density
    real nu   = 1.0e-3;                // fluid:     viscosity 
    real rhos = 10.0;    // [ 1 10 1]   // structure: density
    real E    = miu*(2 + 2*eta);       // structure: Young's modulus
    real T    = 0.5;                  // final time
    real k    = 1e-4;                     // time step size  -- zero means 0.25*minCellDiameter
    real C_cfl= 1.0;
    real C_k  = 0.1;

    FSISolver::solve(mesh, f, ic, bc_mom, bc_con, bcf_mom, bcf_con,
		     bisect, rhof, rhos, E, eta, nu, T, k, C_cfl, C_k); 
    return 0;
}
