// 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) FEM solver for the incompressible Navier-Stokes equations 
//
//     du/dt + u * grad u - nu * div grad u + grad p = f 
//     div u = 0 
//
//  FIXME:: Temporarily we use no-slip instead of slip in the 3D benchmark


#include <dolfin.h>
#include "unicorn/NSESolver.h"

using namespace dolfin;
using namespace dolfin::unicorn;

real bmarg = 1.0e-3 + DOLFIN_EPS;

// 2D data:
// real xmin = 0.0;
// real xmax = 2.2;
// real ymin = 0.0;
// real ymax = 0.41;
// real zmin = 0.0;
// real zmax = 0.0;
// real xcenter = 0.2;
// real ycenter = 0.2;
// real radius = 0.05;

///*
//3D data:
real xmin = 0.0;
real xmax = 2.1;
real ymin = 0.0;
real ymax = 1.4;
real zmin = 0.0;
real zmax = 0.4;
real xcenter = 0.5;
real ycenter = 0.7;
real radius = 0.05;
// //*/

// Sub domain for outflow
class OutflowBoundary : public SubDomain
{
public:
  bool inside(const real* p, bool on_boundary) const
  {
    return on_boundary && (p[0] > (xmax - bmarg));
  }
};

// Sub domain for inflow
class InflowTopBottomBoundary2D : public SubDomain
{
public:
  bool inside(const real* p, bool on_boundary) const
  {
    return on_boundary && p[0] < xmax - bmarg && 
      p[0] < bmarg || p[1] < ymin + bmarg || p[1] > ymax - bmarg;
  }
};

// Sub domain for inflow
class InflowTopBottomBoundary3D : public SubDomain
{
public:
  bool inside(const real* p, bool on_boundary) const
  {
    return on_boundary && p[0] < xmax - bmarg && 
      p[0] < bmarg ||
      p[1] < ymin + bmarg || p[1] > ymax - bmarg ||
      p[2] < zmin + bmarg || p[2] > zmax - bmarg;
  }
};


// Sub domain for Dirichlet boundary condition
class CylinderBoundary : public SubDomain
{
public:
  bool inside(const real* p, bool on_boundary) const
  {
    return on_boundary &&
      ((sqrt(sqr(p[0] - xcenter) + sqr(p[1] - ycenter)) <
	(radius + bmarg)));
  }
};

// Force term
class ForceFunction : public Function
{
public:
  ForceFunction(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
{
  // These are boundary conditions for the flow past a 
  // circular cylinder in 2d. 
  // This is the  benchmark problem: 2D-3, defined in  
  // http://www.mathematik.uni-dortmund.de/~featflow/ture/paper/benchmark_results.ps.gz

public:
  BC_Momentum_2D(Mesh& mesh) : Function(mesh) {}
  void eval(real* values, const real* x) const
  {
    for(int i = 0; i < 2; i++)
    {
      if (i==0)
      {
	if ( x[0] < (0.0 + bmarg))
	  {
	  //values[i] = (1.0/sqr(0.41)) * sin(DOLFIN_PI*time()*0.125) * 6.0*x[1]*(0.41-x[1]);
	  values[i] = (1.0/sqr(0.41)) * sin(DOLFIN_PI*0.125) * 6.0*x[1]*(0.41-x[1]);
	  }
	else
	{
	  values[i] = 0.0;
	}
      } 
      else if (i==1)
      {
	values[i] = 0.0;
      } 
    }
  }
};

// Boundary condition for momentum equation
class BC_Momentum_3D : public Function
{
  // These are boundary conditions for the flow past a
  // circular cylinder in 3d. We use a uniform unit inflow
  // velocity, no slip bc at the lateral boundaries:
  //
  // u = (u_1,u_2,u_3) = (1,0,0) at x = 0
  // u_2 = 0 for y = 0 and y = 1.4
  // u_3 = 0 for z = 0 and z = 0.4
  // u = 0 on the cylinder with radie 0.05 and center at (0.5,0.7,z)
  //
public:
  BC_Momentum_3D(Mesh& mesh) : Function(mesh) {}
  void eval(real* values, const real* x) const
  {
    for(int i = 0; i < 3; i++)
      {
	if (i==0)
	  {
	    if ( x[0] < bmarg)
	      {
		values[i] = 1.0;
	      }
	    else
	      values[i] = 0.0;
	  }
	else if (i==1)
	  {
	    if (x[0] < bmarg)
	      {
		values[i] = 0.0;
	      }
	    else if (x[1] > ymax - bmarg || x[1] < ymin + bmarg)
	      {
		values[i] = 0.0;
	      }	      
	    else
	      values[i] = 0.0;
	  }
	else if (i==2)
	  {
	    if (x[0] < bmarg)
	      {
		values[i] = 0.0;
	      }
	    else if (x[2] > zmax - bmarg || x[2] < zmin + bmarg)
	      {
		values[i] = 0.0;
	      }	      
	    else
	      values[i] = 0.0;
	  }
      }
  }
};


// Boundary condition for continuity equation 
class BC_Continuity : public Function
{
  // This is an approximation of the outflow boundary condition: 
  // 
  // nu * du/dn - np = 0 
  // 
  // Assuming the viscosity nu is small, we may approximate 
  // this boundary condition with zero pressure at outflow. 
public:
  BC_Continuity(Mesh& mesh) : Function(mesh) {}
  
  void eval(real* values, const real* x) const
  {
    values[0] = 0.0;
  }
};

int main(int argc, char* argv[])
{
  //dolfin_init(argc, argv);
  //  /*
  // This is the 2d benchmark problem: 2D-3, defined in  
  // http://www.mathematik.uni-dortmund.de/~featflow/ture/paper/benchmark_results.ps.gz

  //Mesh mesh("cylinder_2d_bmk.xml.gz");
  // InflowTopBottomBoundary2D iboundary;
  // BC_Momentum_2D bcf_mom(mesh);
  //-----------

  // This is a 3d benchmark problem with Re = 3900, described in 
  // http://www.nada.kth.se/~jhoffman/archive/papers/cc.pdf
  Mesh mesh("cylinder_3d_bmk.xml.gz");
  InflowTopBottomBoundary3D iboundary;
  BC_Momentum_3D bcf_mom(mesh);
  //    -----------

  BC_Continuity bcf_con(mesh);
  ForceFunction f(mesh);
  OutflowBoundary oboundary;
  CylinderBoundary cboundary;
  Function zero(mesh, 0.0);

  DirichletBC bc_mom0(bcf_mom, mesh, iboundary);
  DirichletBC bc_mom1(zero, mesh, cboundary);
  DirichletBC bc_con(bcf_con, mesh, oboundary);

  Array <BoundaryCondition*> bc_mom(&bc_mom0, &bc_mom1);

  NSESolver::solve(mesh, f, bc_mom, bc_con); 
  
  return 0;
}
