/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/* file:     heat.c                                                         */
/*                                                                          */
/* description:  solver for parabolic model problem                         */
/*                                                                          */
/*                   u,t - \Delta u = f  in \Omega                          */
/*                                u = g  on \partial \Omega                 */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Universitaet Bremen                                          */
/*             Bibliothekstr. 2                                             */
/*             D-28359 Bremen, Germany                                      */
/*                                                                          */
/*             Kunibert G. Siebert                                          */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  http://www.alberta-fem.de/                                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  (c) by A. Schmidt and K.G. Siebert (1996-2004)                          */
/*                                                                          */
/*     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    */
/*     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.                         */
/*--------------------------------------------------------------------------*/

#include <alberta.h>

/*--------------------------------------------------------------------------*/
/*  function for displaying mesh, discrete solution, and/or estimate        */
/*  defined in graphics.c                                                   */
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
/* global variables: finite element space, discrete solution, discrete      */
/*                   solution from previous time step, load vector,         */
/*                   system matrix, and structure for adaptive procedure    */
/*--------------------------------------------------------------------------*/
#include "demoheat.h"
#include "dxexport.hh"
static REAL time_est;/*muss hier hin!*/
#include "demographics.h"
#include "problem.cc"

/*--------------------------------------------------------------------------*/
/* init_dof_admin(): init DOFs for Lagrange elements of order               */
/*                   <polynomial degree>, once called by GET_MESH(), now by main              */
/*--------------------------------------------------------------------------*/

static void init_dof_admin(MESH *mesh)
{
  FUNCNAME("init_dof_admin");
  int             degree = 1;
  const BAS_FCTS  *lagrange, *lagrange0;

  GET_PARAMETER(1, "polynomial degree", "%d", &degree);
  lagrange = get_lagrange(mesh->dim, degree);
  TEST_EXIT(lagrange, "no lagrange BAS_FCTS\n");

  fe_space = get_fe_space(mesh, lagrange->name, NULL, lagrange, false);
  lagrange0 = get_lagrange(mesh->dim,0);
  const_fe_space = get_fe_space(mesh, lagrange0->name,NULL, lagrange0, false);
#if USE_NCDF
  init_export_dof_admin(mesh);
#endif
  return;
}


/*--------------------------------------------------------------------------*/
/* struct heat_leaf_data: structure for storing one REAL value on each      */
/*                          leaf element as LEAF_DATA                       */
/* init_leaf_data(): initialization of leaf data, called by GET_MESH()      */
/* rw_el_est():  return a pointer to the memory for storing the element     */
/*               estimate (stored as LEAF_DATA), called by heat_est()       */
/* get_el_est(): return the value of the element estimates (from LEAF_DATA),*/
/*               called by adapt_method_stat() and graphics()               */
/*--------------------------------------------------------------------------*/

struct heat_leaf_data
{
  REAL estimate;            /*  one real for the estimate                   */
  REAL est_c;               /*  one real for the coarsening estimate        */
};
#if 0
static void init_leaf_data(LEAF_DATA_INFO *leaf_data_info)
{
  leaf_data_info->leaf_data_size = sizeof(struct heat_leaf_data);
  leaf_data_info->coarsen_leaf_data = nil; /* no transformation             */
  leaf_data_info->refine_leaf_data = nil;  /* no transformation             */
  return;
#endif
#define USE_DOF_ERR 1
#if USE_DOF_ERR
/*Kopie aus est.cc*/
static REAL get_el_est(EL *el)
{REAL err_est;
  if (IS_LEAF_EL(el))
   {
	(const_fe_space->bas_fcts->get_real_vec)(el, error_estimate, &err_est);
   	return(err_est);
	}
  else
    return(0.0);
}

static REAL * rw_el_est(EL *el)
{
 static const DOF    *(*get_dof)(const EL *, const DOF_ADMIN *, DOF *)
   	= const_fe_space->bas_fcts->get_dof_indices;
  if (IS_LEAF_EL(el))
   {
    const DOF    *dof       = (*get_dof)(el, const_fe_space->admin, nil);
   	return(&error_estimate->vec[*dof]);
	}
  else
    return(nil);
}
#else
static REAL *rw_el_est(EL *el)
{
  if (IS_LEAF_EL(el))
    return(&((struct heat_leaf_data *)LEAF_DATA(el))->estimate);
  else
    return(nil);
}
static REAL get_el_est(EL *el)
{
  if (IS_LEAF_EL(el))
    return(((struct heat_leaf_data *)LEAF_DATA(el))->estimate);
  else
    return(0.0);
}
#endif

static REAL *rw_el_estc(EL *el)
{
  if (IS_LEAF_EL(el))
    return(&((struct heat_leaf_data *)LEAF_DATA(el))->est_c);
  else
    return(nil);
}

static REAL get_el_estc(EL *el)
{
  if (IS_LEAF_EL(el))
    return(((struct heat_leaf_data *)LEAF_DATA(el))->est_c);
  else
    return(0.0);
}

//static REAL time_est = 0.0;

static REAL get_time_est(MESH *mesh, ADAPT_INSTAT *adapt)
{
  return(time_est);
}

/*--------------------------------------------------------------------------*/
/* For test purposes: exact solution and its gradient (optional)            */
/*--------------------------------------------------------------------------*/




/*---8<---------------------------------------------------------------------*/
/*---  write error and estimator data to files                           ---*/
/*--------------------------------------------------------------------->8---*/


/*---8<---------------------------------------------------------------------*/
/*---   interpolation is solve on the initial grid                       ---*/
/*--------------------------------------------------------------------->8---*/

static void interpol_u0(MESH *mesh)
{
  dof_compress(mesh);
  interpol(u0, u_h);

  return;
}

static void init_timestep(MESH *mesh, ADAPT_INSTAT *adapt)
{
  FUNCNAME("init_timestep");

  INFO(adapt_instat->info,1,
    	"---8<---------------------------------------------------\n");
  INFO(adapt_instat->info, 1,"starting new timestep\n");

  dof_copy(u_h, u_old);
  return;
}

static void set_time(MESH *mesh, ADAPT_INSTAT *adapt)
{
  FUNCNAME("set_time");

  INFO(adapt->info,1,
    "---8<---------------------------------------------------\n");
  if (adapt->time == adapt->start_time)
  {
    INFO(adapt->info, 1,"start time: %.4le\n", adapt->time);
  }
  else
  {
    INFO(adapt->info, 1,"timestep for (%.4le %.4le), tau = %.4le\n",
			 adapt->time-adapt->timestep, adapt->time,
			 adapt->timestep);
  }

  eval_time_f = adapt->time - (1 - theta)*adapt->timestep;
  eval_time_g = adapt->time;

  return;
}



/*--------------------------------------------------------------------------*/
/* build(): assemblage of the linear system: matrix, load vector,           */
/*          boundary values, called by adapt_method_stat()                  */
/*          on the first call initialize u_h, f_h, matrix and information   */
/*          for assembling the system matrix                                */
/*                                                                          */
/* struct op_info: structure for passing information from init_element() to */
/*                 LALt()                                                   */
/* init_element(): initialization on the element; calculates the            */
/*                 coordinates and |det DF_S| used by LALt; passes these    */
/*                 values to LALt via user_data,                            */
/*                 called on each element by update_matrix()                */
/* LALt():         implementation of -Lambda id Lambda^t for -Delta u,      */
/*                 called by update_matrix()                                */
/* c():            implementation of 1/tau*m(,)                             */
/*--------------------------------------------------------------------------*/

struct op_info
{
  REAL_D  Lambda[DIM+1];    /*  the gradient of the barycentric coordinates */
  REAL    det;              /*  |det D F_S|                                 */

  REAL    tau_1;
};

static const REAL (*LALt(const EL_INFO *el_info, const QUAD *quad, 
			 int iq, void *ud))[DIM+1]
{
  struct op_info *info =(struct op_info *) ud;
  int            i, j, k;
  static REAL    LALt[DIM+1][DIM+1];

  for (i = 0; i <= DIM; i++)
    for (j = i; j <= DIM; j++)
    {
      for (LALt[i][j] = k = 0; k < DIM_OF_WORLD; k++)
	LALt[i][j] += info->Lambda[i][k]*info->Lambda[j][k];
      LALt[i][j] *= info->det;
      LALt[j][i] = LALt[i][j];
    }
  return((const REAL (*)[DIM+1]) LALt);
}

static REAL c(const EL_INFO *el_info, const QUAD *quad, int iq, void *ud)
{
  struct op_info *info = (struct op_info *)ud;

  return(info->tau_1*info->det);
}

static void assemble(DOF_REAL_VEC *u_old, DOF_MATRIX *matrix, DOF_REAL_VEC *fh,
		     DOF_REAL_VEC *u_h, REAL theta, REAL tau,
		     REAL (*f)(const REAL_D), REAL (*g)(const REAL_D))
{
  FUNCNAME("assemble");
  static struct op_info *op_info = nil;
//  static const REAL     **(*fill_a)(const EL_INFO *, void *) = nil;
  static const REAL     *const*(*fill_a)(const EL_INFO *, void *) = NULL;
  static void           *a_info = nil;
//  static const REAL     **(*fill_c)(const EL_INFO *, void *) = nil;
  static const REAL     *const*(*fill_c)(const EL_INFO *, void *) = NULL;
  static void           *c_info = nil;

  static const DOF_ADMIN *admin = nil;
  static int             n;
  static const REAL   *(*get_u_loc)(const EL *, const DOF_REAL_VEC *, REAL *);
  static const DOF    *(*get_dof)(const EL *, const DOF_ADMIN *, DOF *);
  static const S_CHAR *(*get_bound)(const EL_INFO *, S_CHAR *);

  TRAVERSE_STACK *stack = get_traverse_stack();
  const EL_INFO  *el_info;
  FLAGS          fill_flag;
//  const REAL     **a_mat, **c_mat;
  const REAL            *const*a_mat, *const*c_mat;
  REAL           *f_vec;
  const QUAD     *quad;
  int            i, j;

  quad = get_quadrature(DIM, 2*u_h->fe_space->bas_fcts->degree);

/*--------------------------------------------------------------------------*/
/*  init functions for matrix assembling                                    */
/*--------------------------------------------------------------------------*/

  if (admin != u_h->fe_space->admin)
  {
    OPERATOR_INFO          o_info2 = {nil}, o_info0 = {nil};
    const EL_MATRIX_INFO   *matrix_info;
    const BAS_FCTS         *bas_fcts = u_h->fe_space->bas_fcts;

    admin      = u_h->fe_space->admin;

    n = bas_fcts->n_bas_fcts;
    get_dof    = bas_fcts->get_dof_indices;
    get_bound  = bas_fcts->get_bound;
    get_u_loc  = bas_fcts->get_real_vec;

    if (!op_info)
      op_info = MEM_ALLOC(1, struct op_info);

    o_info2.row_fe_space = o_info2.col_fe_space = u_h->fe_space;

    o_info2.quad[2]        = quad;
    o_info2.LALt           = LALt;
    o_info2.LALt_pw_const  = true;
    o_info2.LALt_symmetric = true;
    o_info2.user_data      = op_info;

    matrix_info = fill_matrix_info(&o_info2, nil);
    fill_a = matrix_info->el_matrix_fct;
    a_info = matrix_info->fill_info;

    o_info0.row_fe_space = o_info0.col_fe_space = u_h->fe_space;

    o_info0.quad[0]        = quad;
    o_info0.c              = c;
    o_info0.c_pw_const     = true;
    o_info0.user_data      = op_info;

    matrix_info = fill_matrix_info(&o_info0, nil);
    fill_c = matrix_info->el_matrix_fct;
    c_info = matrix_info->fill_info;
  }

  op_info->tau_1 = 1.0/tau;

/*--------------------------------------------------------------------------*/
/*  and now assemble the matrix and right hand side                         */
/*--------------------------------------------------------------------------*/

  clear_dof_matrix(matrix);
  dof_set(0.0, fh);
  f_vec = fh->vec;

  fill_flag = CALL_LEAF_EL|FILL_COORDS|FILL_BOUND;
  el_info = traverse_first(stack, u_h->fe_space->mesh, -1, fill_flag);
  while (el_info)
  {
    const REAL   *u_old_loc = (*get_u_loc)(el_info->el, u_old, nil);
    const DOF    *dof       = (*get_dof)(el_info->el, admin, nil);
    const S_CHAR *bound     = (*get_bound)(el_info, nil);

/*--------------------------------------------------------------------------*/
/* initialization of values used by LALt and c                              */
/*--------------------------------------------------------------------------*/
    op_info->det   = el_grd_lambda(el_info, op_info->Lambda);

    a_mat = fill_a(el_info, a_info);
    c_mat = fill_c(el_info, c_info);

/*--------------------------------------------------------------------------*/
/*  add theta*a(psi_i,psi_j) + 1/tau*m(4*u^3*psi_i,psi_j)                   */
/*--------------------------------------------------------------------------*/

    if (theta)
    {
      add_element_matrix(matrix, theta, n, n, dof, dof, a_mat, bound);
    }
    add_element_matrix(matrix, 1.0, n, n, dof, dof, c_mat, bound);

/*--------------------------------------------------------------------------*/
/*  f += -(1-theta)*a(u_old,psi_i) + 1/tau*m(u_old,psi_i)                   */
/*--------------------------------------------------------------------------*/

    if (1.0 - theta)
    {
      REAL theta1 = 1.0 - theta;
      for (i = 0; i < n; i++)
      {
	if (bound[i] < DIRICHLET)
	{
	  REAL val = 0.0;
	  for (j = 0; j < n; j++)
	    val += (-theta1*a_mat[i][j] + c_mat[i][j])*u_old_loc[j];
	  f_vec[dof[i]] += val;
	}
      }
    }
    else
    {
      for (i = 0; i < n; i++)
      {
	if (bound[i] < DIRICHLET)
	{
	  REAL val = 0.0;
	  for (j = 0; j < n; j++)
	    val += c_mat[i][j]*u_old_loc[j];
	  f_vec[dof[i]] += val;
	}
      }
    }
    el_info = traverse_next(stack, el_info);
  }

  free_traverse_stack(stack);

  L2scp_fct_bas(f, quad, fh);
  dirichlet_bound(g, fh, u_h, nil);

  return;
}

static void build(MESH *mesh, U_CHAR flag)
{
  FUNCNAME("build");

  dof_compress(mesh);
  
  INFO(adapt_instat->adapt_space->info, 2 ,"%d DOFs for %s\n", fe_space->admin->size_used, fe_space->name);

  assemble(u_old, matrix, f_h, u_h, theta, adapt_instat->timestep, f, g);
  if (adapt_instat->adapt_space->info >= 8)
	{
	 	print_dof_real_vec(f_h);
		if (adapt_instat->adapt_space->info >= 9)
			print_dof_matrix(matrix);
	}
  return;
}

/*--------------------------------------------------------------------------*/
/* solve(): solve the linear system, called by adapt_method_stat()          */
/*--------------------------------------------------------------------------*/

static void solve(MESH *mesh)
{
  FUNCNAME("solve");
  static REAL       tol = 1.e-8;
  static int        max_iter = 1000, info = 2, icon = 1, restart = 0;
  static OEM_SOLVER solver = NoSolver;

  if (solver == NoSolver)
  {
    tol = 1.e-8;
    GET_PARAMETER(1, "heat solver", "%d", &solver);
    GET_PARAMETER(1, "heat solver tolerance", "%f", &tol);
    GET_PARAMETER(1, "heat solver precon", "%d", &icon);
    GET_PARAMETER(1, "heat solver max iteration", "%d", &max_iter);
    GET_PARAMETER(1, "heat solver info", "%d", &info);
    if (solver == GMRes)
      GET_PARAMETER(1, "solver restart", "%d", &restart);
  }
//print_dof_real_vec(u_h);
//print_dof_real_vec(f_h);
  oem_solve_s(matrix, NULL/*bound*/,f_h,  u_h, solver, tol, (OEM_PRECON)icon, restart, max_iter, info);

  return;
}

/*--------------------------------------------------------------------------*/
/* Functions for error estimate:                                            */
/* estimate():   calculates error estimate via heat_est()                   */
/*               calculates exact error also (only for test purpose),       */
/*               called by adapt_method_stat()                              */
/*--------------------------------------------------------------------------*/

/*static REAL r(const EL_INFO *el_info, const QUAD *quad, int iq, REAL t, 
	      REAL uh_iq, const REAL_D grd_uh_iq)
*/static REAL r(const EL_INFO *el_info,
	      const QUAD *quad, int iq,
	      REAL uh_at_qp, const REAL_D grd_uh_at_qp,
	      REAL t)
{
  REAL_D      x;
  coord_to_world(el_info, quad->lambda[iq], x);
  eval_time_f = t;
  return(-f(x));
}


static REAL estimate(MESH *mesh, ADAPT_STAT *adapt)
{
  FUNCNAME("estimate");
  static int     degree;
  static REAL    C[4] = {-1.0, 1.0, 1.0, 1.0};
  REAL_DD        A = {{0.0}};
  FLAGS          r_flag = 0;  /* = (INIT_UH|INIT_GRD_UH), if needed by r()  */
  int            n;
  REAL           space_est;

  for (n = 0; n < DIM_OF_WORLD; n++)
    A[n][n] = 1.0;   /* set diogonal of A; all other elements are zero      */

  eval_time_u = adapt_instat->time;

  if (C[0] < 0)
  {
    C[0] = 1.0;
    GET_PARAMETER(1, "th estimator C0", "%f", &C[0]);
    GET_PARAMETER(1, "th estimator C1", "%f", &C[1]);
    GET_PARAMETER(1, "th estimator C2", "%f", &C[2]);
    GET_PARAMETER(1, "th estimator C3", "%f", &C[3]);
  }

  degree = 2*u_h->fe_space->bas_fcts->degree;
  time_est = heat_est(u_h, adapt_instat, rw_el_est, rw_el_estc,
		      degree, C, u_old, (const REAL_D *) A, r, r_flag
		      		, NULL /* gn() */, 0 /* gn_flag */);
#if USE_DOF_ERR
//  print_dof_real_vec(error_estimate);
#endif
  space_est = adapt_instat->adapt_space->err_sum;
//  err_L2 = L2_err(u, u_h, nil, 0, nil, nil);
  err_L2 = L2_err(u, u_h, NULL, false, false, NULL, NULL);

  INFO(adapt_instat->info,2
    ,"---8<---------------------------------------------------\n");
  INFO(adapt_instat->info, 6,"time_est = %.4lg\n", time_est);
  INFO(adapt_instat->info, 2,"time = %.4le with timestep = %.4le\n",
			      adapt_instat->time, adapt_instat->timestep);
  INFO(adapt_instat->info, 2,"||u-uh||L2 = %.4le, ratio = %.2lf\n", err_L2,
			      err_L2/MAX(space_est,1.e-20));
  INFO(adapt_instat->info, 2,"estimate   = %.4le, max = %.4le\n", space_est,
			      sqrt(adapt_instat->adapt_space->err_max));

  return(adapt_instat->adapt_space->err_sum);
}

static REAL est_initial(MESH *mesh, ADAPT_STAT *adapt)
{
  FUNCNAME("est_initial");
  err_L2 = adapt->err_sum = L2_err(u0, u_h, nil, false, false ,rw_el_est, &adapt->err_max);
  MSG("L2 error = %.3le\n", adapt->err_sum);
  return(adapt->err_sum);
}

/*--------------------------------------------------------------------------*/
/* main program                                                             */
/*--------------------------------------------------------------------------*/

int main(int argc, char **argv)
{
  FUNCNAME("main");
  MACRO_DATA     *data;
  MESH   *mesh;
  int    n_refine = 0, k, p = 1, dim=2;
  char   filename[128];
  REAL   fac = 1.0;

/*--------------------------------------------------------------------------*/
/*  first of all, init parameters of the init file                          */
/*--------------------------------------------------------------------------*/

  init_parameters(0, "INIT/heat.dat");
  for (k = 1; k+1 < argc; k += 2)
    ADD_PARAMETER(0, argv[k], argv[k+1]);
  
/*--------------------------------------------------------------------------*/
/*  get a mesh, and read the macro triangulation from file                  */
/*--------------------------------------------------------------------------*/
//von 2.0
  GET_PARAMETER(1, "mesh dimension", "%d", &dim);
   GET_PARAMETER(1, "macro file name", "%s", filename);
  GET_PARAMETER(1, "global refinements", "%d", &n_refine);
  GET_PARAMETER(1, "polynomial degree", "%d", &p);
  data = read_macro(filename);

  mesh = GET_MESH(dim, "ALBERTA mesh", data, NULL, NULL);
  free_macro_data(data);
  init_dof_admin( mesh);
  init_leaf_data(mesh, sizeof(struct heat_leaf_data), NULL, NULL);
/*init_leaf_data(MESH *mesh, size_t size,
			     void (*refine_leaf_data)(EL *parent,
						      EL *child[2]),
			     void (*coarsen_leaf_data)(EL *parent,
						       EL *child[2]));
*/

/*  mesh = GET_MESH("ALBERTA mesh", init_dof_admin, init_leaf_data);
  read_macro(mesh, filename, nil);
*/  global_refine(mesh, n_refine*DIM);
  graphics(mesh, nil, nil, nil, 0.0);

  GET_PARAMETER(1, "parameter theta", "%e", &theta);
  if (theta < 0.5)
  {
    WARNING("You are using the explicit Euler scheme\n");
    WARNING("Use a sufficiently small time step size!!!\n");
    fac = 1.0e-3;
  }

  matrix = get_dof_matrix("A", fe_space, NULL);
  f_h    = get_dof_real_vec("f_h", fe_space);
  u_h    = get_dof_real_vec("u_h", fe_space);
  u_h->refine_interpol = fe_space->bas_fcts->real_refine_inter;
  u_h->coarse_restrict = fe_space->bas_fcts->real_coarse_inter;
  u_old  = get_dof_real_vec("u_old", fe_space);
  u_old->refine_interpol = fe_space->bas_fcts->real_refine_inter;
  u_old->coarse_restrict = fe_space->bas_fcts->real_coarse_inter;
  dof_set(0.0, u_h);      /*  initialize u_h  !                          */
/*in order to be able to print vector of error estimate. Switch between element data and
dof_real_vec mode on top of get_el_est definition.*/
  error_estimate  = get_dof_real_vec("error_estimate", const_fe_space);
  error_estimate->refine_interpol = nil;
  error_estimate->coarse_restrict = nil;
  dof_set(0.0, error_estimate);      /*  initialize u_h  !                          */
/*--------------------------------------------------------------------------*/
/*  init adapt structure and start adaptive method                          */
/*--------------------------------------------------------------------------*/

  adapt_instat = get_adapt_instat(dim, "heat", "adapt", 2, adapt_instat);

/*--------------------------------------------------------------------------*/
/*  adapt time step size to refinement level and polynomial degree          */
/*--------------------------------------------------------------------------*/


//  if (theta == 0.5)
//    adapt_instat->timestep *= fac*pow(2, -(REAL)(p*(n_refine))/2.0);
//  else 
//    adapt_instat->timestep *= fac*pow(2, -(REAL)(p*(n_refine)));
  MSG("using initial timestep size = %.4le\n", adapt_instat->timestep);

  eval_time_u0 = adapt_instat->start_time;

  adapt_instat->adapt_initial->get_el_est = get_el_est;
  adapt_instat->adapt_initial->estimate = est_initial;
  adapt_instat->adapt_initial->solve = interpol_u0;

  adapt_instat->adapt_space->get_el_est   = get_el_est;
  adapt_instat->adapt_space->get_el_estc  = get_el_estc;
  adapt_instat->adapt_space->estimate = estimate;
  adapt_instat->adapt_space->build_after_coarsen = build;
  adapt_instat->adapt_space->solve = solve;

  adapt_instat->init_timestep  = init_timestep;
  adapt_instat->set_time       = set_time;
  adapt_instat->get_time_est   = get_time_est;
  adapt_instat->close_timestep = close_timestep;

  adapt_method_instat(mesh, adapt_instat);
  
  WAIT_REALLY;
  return(0);
}
