/****************************************************************************/
/* ALBERT:   an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques                                                     */
/****************************************************************************/
/*        authors: Alfred Schmidt and Kunibert G. Siebert                   */
/*                 Institut f"ur Angewandte Mathematik                      */
/*                 Albert-Ludwigs-Universit"at Freiburg                     */
/*                 Hermann-Herder-Str. 10                                   */
/*                 79104 Freiburg                                           */
/*                 Germany                                                  */
/*                                                                          */
/*        email:   alfred,kunibert@mathematik.uni-freiburg.de               */
/*                                                                          */
/*     (c) by A. Schmidt and K.G. Siebert (1996-2000)                       */
/****************************************************************************/

#include <alberta.h>
#include "newalberta.hh"
//#include "../../AlbertaNum/phase_elast.hh"
//#include "../../AlbertaNum/PE_phase.h"
#include "master.hh"	//time_est z.B
#include "do_on_el.hh"
#include "do_elfunc_on_el.hh"
#include "prototype_fem_elfunc.hh"
#include "traverse.hh"
#include "fem_elfunc.hh"
#include "fem_base.hh"
#include "build_stat.hh"
#include "build_diff.hh"
#include "est.hh"
#include "ellipt_est.hh"
#include "parab_est.hh"
#include "sys_est.hh"
#include "operator_parts.hh"
#include "demoheat.h"
// #define USE_DXEXPORT (1)
#ifdef USE_DXEXPORT
#include "dxexport.hh"
#endif
#define __newalberta_hh__ 1
#include "heat_problem.hh"
#include "demographics.h"
#include "problem.cc"
#include "error_c.h"
#include "adapt.hh"
#include "heat.hh" 
#include "periodic.h" 
//aus heat.cc, aber nicht in heat.hh. Besser extern deklarieren als per header 
//ueberall hinschmieren.
REAL estimate_heat(MESH *mesh, ADAPT_STAT *adapt);
 ADAPT_INSTAT   *adapt_heat;

extern const  PROB_DATA_DIFF* prob_data_heat; //brauchen tut man bloss sol_0 u sol_ex

extern build_diff *build_heat;
//static parab_est	*est_heat;
extern parab_est	*est_heat;
REAL time_est; 
 /****************************************************************************/
struct heat_leaf_data
{
  REAL estimate;            /*  one real for the estimate                   */
  REAL est_c;               /*  one real for the coarsening estimate        */
};

static void h_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;
}


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

// voruebergehend gewrappt
/****************************************************************************/
/****************************************************************************/
/* init_dof_admin(): init DOFs for Lagrange elements of order               */
/*                   <polynomial degree>, called by GET_MESH()              */
/****************************************************************************/


static void init_dof_admin(MESH *mesh)
{
   init_heat_dof_admin(mesh);
   // BAUSTELLE
#ifdef USE_DXEXPORT
   init_export_dof_admin(mesh);
#endif   
 return;
}

/****************************************************************************/



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

  static REAL err_L2L2 = 0.0;
  static REAL err_LinftyL2 = 0.0;

  err_LinftyL2 = MAX(err_LinftyL2, err_L2);

  err_L2L2 += adapt->timestep * err_L2*err_L2;

  MSG("Heat  LinftyL2 error: %.3le  L2L2 error: %.3le  total error: %.3le\n",
      err_LinftyL2, sqrt(err_L2L2),
      err_LinftyL2 + sqrt(err_L2L2));
//  graphics(mesh, build_heat->get_old_sol(), get_el_est);
//  graphics(mesh, build_heat->get_rhs(), get_el_est);
//  graphics(mesh, build_heat->get_sol(), nil);

//alt:  graphics(mesh, build_heat->get_sol(), adapt_heat->adapt_space->get_el_est);
  graphics(mesh, build_heat->get_sol(), adapt_heat->adapt_space->get_el_est, nil, adapt->time);
//  graphics(2);
  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(,)                             */
/****************************************************************************/


/****************************************************************************/


/****************************************************************************/


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


/****************************************************************************/
/* 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 h_estimate_heat(MESH *mesh, ADAPT_STAT *adapt)
{
  FUNCNAME("h_estimate_heat");
  eval_time_u = adapt_heat->time;
  time_est = 0.0;//egal!!
  estimate_heat( mesh,  adapt);
  INFO(adapt_heat->info, 2,
    "time=%.4le  estimate   = %.8le, max=%.3le\n", adapt_heat->time,
     sqrt(adapt_heat->adapt_space->err_sum), sqrt(adapt_heat->adapt_space->err_max));
	time_est = sqrt(time_est);
  return(sqrt(adapt_heat->adapt_space->err_sum));
}

/****************************************************************************/


/****************************************************************************/
int main(int argc, char **argv)
{
  FUNCNAME("main");
  MESH   *mesh;
  int    n_refine = 0, k, p = 1;
  char   filename[128];
  REAL   fac = 1.0;
  FLAGS periodic = 0U;

/*--------------------------------------------------------------------------*/
/*  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                  */
/*--------------------------------------------------------------------------*/

  GET_PARAMETER(1, "macro file name", "%s", filename);
  GET_PARAMETER(1, "global refinements", "%d", &n_refine);
  GET_PARAMETER(2, "periodic", "%d", &periodic);

  MACRO_DATA *data = read_macro(filename);

   if (periodic) 	mesh = GET_MESH(DIM, "ALBERTA mesh", data, NULL,init_wall_trafos);
	else 				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);

/*  mesh = GET_MESH(DIM, "ALBERTA mesh", init_dof_admin, init_leaf_data_local, nil);
  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;
  }

  adapt_heat = get_adapt_instat(DIM, "heat", "adapt", 2, adapt_heat);
	prob_data_heat = init_problem();
  init_heat(adapt_heat, prob_data_heat);
/*--------------------------------------------------------------------------*/
/*  init adapt structure and start adaptive method                          */
/*--------------------------------------------------------------------------*/

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

  GET_PARAMETER(1, "polynomial degree", "%d", &p);

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

  eval_time_u0 = adapt_heat->start_time;

//  adapt_heat->adapt_initial->solve = interpol_u0;

//  adapt_heat->adapt_space->build_after_coarsen = build;

//  adapt_heat->init_timestep  = init_timestep;
  adapt_heat->set_time       = h_set_time;
  adapt_heat->get_time_est   = get_time_est;
  adapt_heat->close_timestep = close_h_timestep;


  adapt_heat->adapt_initial->marking = okmarking;
  adapt_heat->adapt_space->estimate    =	h_estimate_heat;
  adapt_heat->adapt_space->marking = okmarking;
// fem_base* base = new fem_base("name",   fe_space,
//	   &prob_data_heat, 1, &adapt_heat->start_time);

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