// Copyright (C) 2007 Johan Jansson.
// Licensed under the GNU GPL Version 2.

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

#include "Projection.h"
#include "Elasticity.h"
#include "ElasticityJac.h"
#include "Coefficients.h"
  
using namespace dolfin;
using namespace dolfin::unicorn;

class ElasticityPDE : public TimeDependentPDE
{
public:
  ElasticityPDE(Form* a, Form* L, Mesh& mesh, BoundaryCondition& bc, real T,
		Form* aJac, Form* aP, Form* LP) :
    TimeDependentPDE(*a, *L, mesh, bc, T,
		     aJac, aP, LP)
  {
  }
};


int main()
{
  // Set up problem
  int M = 10;

  UnitSquare mesh(6 * M, 3 * M);

  transform(mesh, 6 * M, 3 * M);

  cout << "Vertices: " << mesh.numVertices() << endl;

  MySource f(mesh);
  MyBC bcf(mesh);
  InitialValue u0(mesh);
  DirichletBoundary dboundary;

  DirichletBC bc(bcf, mesh, dboundary);

  real E = 1.0e6; // Young's modulus
  real nu = 0.3; // Poisson's ratio

  real lmbdaval = E * nu / ((1 + nu) * (1 - 2 * nu));
  real muval = E / (2 * (1 + nu));

  Function lmbda(mesh, lmbdaval);
  Function mu(mesh, muval);

  real T = 0.5;
  real k = 1.0e-2;

  set("ODE method", "cg");
  set("ODE order", 1);
  set("ODE implicit", true);
  //set("ODE implicit", false);
  //set("ODE nonlinear solver", "fixed-point");
  set("ODE nonlinear solver", "newton");
  //set("ODE linear solver", "direct");
  set("ODE linear solver", "iterative");
  set("ODE matrix-free jacobian", false);
  set("ODE tolerance", 1.0e-7);
  set("ODE discrete tolerance", 1.0e-7);

  set("ODE monitor convergence", true);
  
  set("ODE fixed time step", true);
  set("ODE initial time step", k);
  set("ODE maximum time step", k);

  set("ODE save solution", false);

  Function U;
  
  Form* a = new ElasticityBilinearForm();
  Form* L = new ElasticityLinearForm(U, mu, lmbda, f);
  Form* aJac = new ElasticityJacBilinearForm(mu, lmbda);
  Form* aP = new ProjectionBilinearForm();
  Form* LP = new ProjectionLinearForm(u0);

  ElasticityPDE pde(a, L, mesh, bc, T, aJac, aP, LP);

  // Compute solution
  pde.solve(U);

  return 0;
}
