// 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 cont_L = 2.5;
    real cont_H = 0.41;

    real pin_l = 0.35;
    real pin_h = 0.02;

    real circ_cx = 0.2;
    real circ_cy = 0.2;

    real pin_r = 0.05;

    real pin_start_x = circ_cx;  
    real pin_end_x   = pin_start_x + pin_r + pin_l;
  
    real pin_start_y = circ_cy - (0.5 * pin_h);
    real pin_end_y   = pin_start_y + pin_h;

    real xmin = 0.0; real xmax = cont_L;
    real ymin = 0.0; real ymax = cont_H;

    real pi = M_PI;

    real margin = DOLFIN_EPS + 1e-9;


    bool isStructure(const real* r)
    {
	// Define the area that the pin occupies
	return (((pin_start_x - margin) < r[0] && r[0] < (pin_end_x + margin)) &&
		((pin_start_y - margin) < r[1] && r[1] < (pin_end_y + margin)));
    }
}
// Sub domain for outflow
class OutflowBoundary : public SubDomain
{
public:
  bool inside(const real* p, bool on_boundary) const
  {
    return on_boundary && (p[0] > (Geo::xmax - 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::xmax - Geo::margin));
  }
};
//---------------------------------------------------------------------
// Force term
class ForceFunction_2D : public Function
{
public:
  ForceFunction_2D(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_2D : public Function
{
public:
  BC_Momentum_2D(Mesh& mesh) : Function(mesh) {}
  real inflow(const real* r, unsigned int i) const
  {
    real u_bar = 1; // [ 0.2 1 2 ]
    
    if (i == 0) // x-direction
      return ( 1.5*u_bar*( (r[1]*(Geo::cont_H - r[1]))/
			   ((0.5*Geo::cont_H)*(0.5*Geo::cont_H)) ) );
    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::xmin + Geo::margin)) {
	values[i] = inflow(p,i);
      } else values[i] = 0.0;
    }
  }
};
//---------------------------------------------------------------------
// Boundary condition for continuity equation 
class BC_Continuity_2D : public Function
{
public:
  BC_Continuity_2D(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;
    }
  }
};
//---------------------------------------------------------------------
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("fsi_femlab_bench.xml");
  
    IC_Momentum	      ic(mesh);
    BC_Momentum_2D    bcf_mom(mesh);  
    BC_Continuity_2D  bcf_con(mesh);
    ForceFunction_2D  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  = 0.5e3;    // [ 0.5 0.5 2.0 ]
    real rhof = 1.0e0;                   // fluid:     density
    real nu   = 1.0e-3;                // fluid:     viscosity 
    real rhos = 10.0e0;    // [ 1 10 1]   // structure: density
    real E    = miu*(2 + 2*eta);       // structure: Young's modulus
    real T    = 4.0;                  // final time
    real k    = 1.0e-3;                     // time step size  -- zero means 0.25*minCellDiameter
    real C_cfl= 1.0;
    real C_k  = 0.25;
  
    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;
}
