//Kopie von PE_heat,soll dann fuer Diffusionsprobleme genommen werden.
/****************************************************************************/
/* 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 "master.hh"
#include "do_on_el.hh"
#include "prototype_fem_elfunc.hh"
#include "do_elfunc_on_el.hh"
// #include "public_do_elfunc_on_el.hh"
#include "traverse.hh"
#include "fem_elfunc.hh"
#include "fem_base.hh"
#include "est.hh"
#include "ellipt_est.hh"
#include "build_stat.hh"
#include "build_diff.hh"
#include "parab_est.hh"
#include "error_c.h"
//#include "heat_problem.hh" das wollen wir ja gerade nicht. Wollen lokale prob_data
//#include "PE_graphics.h"
//ist so schon fast verallgemeinert zu einer Klasse... Eigentlich nur noch nach C++ uebersetzen
 const  PROB_DATA_DIFF* prob_data_heat;

static const FE_SPACE *fe_space=nil;
extern ADAPT_INSTAT   *adapt_heat; //datei mit main definiert das
//auch Mist
static REAL err_L2 = 0.0;
static fem_base *base_heat = nil;
build_diff *build_heat = nil;
parab_est	*est_heat = nil;
static DOF_REAL_VEC* th_h=nil;
//static const DOF_REAL_VEC* th_h_old if needed!
 /****************************************************************************/

const DOF_REAL_VEC * const get_temperature(void)
{
  if (!th_h)
  	th_h           = get_dof_real_vec("th_h", fe_space);
  return((const DOF_REAL_VEC *const )th_h);
//return(base_heat->get_sol());
}

const DOF_REAL_VEC * const get_old_temperature(void)
{
//  return((const DOF_REAL_VEC *const)th_old);
return(build_heat->get_old_sol());
}
// voruebergehend gewrappt
/****************************************************************************/
/****************************************************************************/
/* init_dof_admin(): init DOFs for Lagrange elements of order               */
/*                   <polynomial degree>, called by GET_MESH()              */
/****************************************************************************/

void init_heat_dof_admin(MESH *mesh)
{
  FUNCNAME("init_heat_dof_admin");
  int             degree = 1;
  const BAS_FCTS  *lagrange;
  FLAGS periodic = 0U, flag = 0U;
  GET_PARAMETER(2, "periodic", "%d", &periodic);
  if (periodic) 	{flag = ADM_PERIODIC ;}
	else 				{flag = 0U;}
  GET_PARAMETER(2, "heat 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, nil, lagrange, flag);
	
 init_err_dof_admin(mesh);

  return;
}

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

static REAL *wrap_rw_el_est(EL *el)
{
    return((est_heat->rw_el_est)(el,0));
}

static REAL get_el_est(EL *el) //brauchen wir fuer graphics
{
    return((est_heat->get_el_est)(el,0));
}


static REAL get_el_estc(EL *el)
{
    return(*(est_heat->rw_el_estc(el,0)));
}


void 	clear_indicator_heat(MESH * mesh)
{if (est_heat) est_heat->clear_indicator( mesh);
  else MSG("no est_heat constructed\n");
}

static void interpol0(MESH *mesh)
{
  dof_compress(mesh);
  interpol(prob_data_heat->sol_0,base_heat->get_sol() );
  interpol(prob_data_heat->sol_0,base_heat->get_old_sol() );

  return;
}

static void init_h_timestep(MESH *mesh, ADAPT_INSTAT *adapt)
{
//  dof_copy(build_heat->get_sol(),build_heat->get_old_sol() );
  build_heat->init_timestep();
  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(2); das sollte in adapt_master->close_timestep passieren
  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(,)                             */
/****************************************************************************/


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


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

static void build(MESH *mesh, U_CHAR flag) //eben fast nur wrapper
{
  FUNCNAME("build (PE_heat)");
//Es gibt hier viel Sparpotential, z.B. Massenmat einmal machen f alle. WEr will, der kann.
  dof_compress(mesh);
  INFO(adapt_heat->adapt_space->info, 3,
    "%d DOFs for %s\n", fe_space->admin->size_used, fe_space->name);
	dof_set(0.0, build_heat->get_rhs());
	build_heat->assemble();	
	if (adapt_heat->adapt_space->info >= 8)
	{
	 	print_dof_real_vec(build_heat->get_rhs());
		if (adapt_heat->adapt_space->info >= 9)
			print_dof_matrix(build_heat->get_mat());
	}
  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,  restart = 0;
  static OEM_SOLVER solver = NoSolver;
  static OEM_PRECON icon = DiagPrecon;

  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, "heat solver restart", "%d", &restart);
  }
  if(solver)
     {
	build(mesh, 1);
	TEST_EXIT(build_heat->get_mat(),
		  "No operator_info, no Matrix, but solver switched on\n");
	oem_solve_s(build_heat->get_mat(),NULL/*bound - mal Effekt von build_heat->get_bound testen!*/,build_heat->get_rhs(),
		    /*nil,*/build_heat->get_sol(),
         			solver, tol, /*1 Precon*/icon, restart, max_iter, info);
				//else {MSG("No operator_info, but solve switched on\n");}
        }
  else
        {TEST_EXIT(prob_data_heat->sol_ex, "heat solver switched off, but no ex_sol given\n");
             interpol(prob_data_heat->sol_ex, build_heat->get_sol());/*ok Baustelle*/
		}

#if 0
  MSG("latent_heat=%.3e\n", prob_data_heat->physconstants[LATENT_PEARLITE]);
  print_dof_real_vec(th_old);
//  print_dof_matrix(matrix);
  WAIT;
#endif

  return;
}
static void interpol(MESH *mesh)
{  
   FUNCNAME("interpol");
    TEST_EXIT(prob_data_heat->sol_ex, "heat solver switched off, but no ex_sol given\n");
    interpol(prob_data_heat->sol_ex, base_heat->get_sol());/*ok Baustelle*/
}


/****************************************************************************/
/* 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 estimate_heat(MESH *mesh, ADAPT_STAT *adapt)
{
  FUNCNAME("estimate_heat");
 INFO(adapt_heat->info,2,
    "---8<---------------------------------------------------\n");
  INFO(adapt_heat->info, 2, "time = %.4le with timestep = %.4le\n",
			      adapt_heat->time, adapt_heat->timestep);
  if (est_heat)
  {
  		adapt_heat->adapt_space->err_sum = 0.0 ;
  		adapt_heat->adapt_space->err_max = 0.0 ;
		time_est +=est_heat->estimator(adapt_heat/*, adapt_master->time, adapt_master->timestep*/);
//time_est muss vor Aufruf von master-> ...->estimate geloescht werden!
// hoffe adapt_instat als Argument fallen lassen zu koennen
   	if (prob_data_heat->sol_ex) 
	{
    		err_L2 = L2_err(prob_data_heat->sol_ex, est_heat->get_sol(), nil, false, false, nil, nil);
    		INFO(adapt_heat->info, 2, "||u-uh||L2 = %.4le, ratio = %.2lf\n", err_L2,
			      err_L2/MAX(sqrt(adapt_heat->adapt_space->err_sum), 1.e-20));
   	}
  	else
    	err_L2 = 0.0;

 
  INFO(adapt_heat->info, 2,
    "time=%.4le  estimate^2   = %.8le, max^2=%.3le\n", adapt_heat->time,
     adapt_heat->adapt_space->err_sum, adapt_heat->adapt_space->err_max);

  return(adapt_heat->adapt_space->err_sum);
  }
  else
  {
  	INFO(adapt_heat->info, 3, "no est_heat given\n");
	return(0.0);
  }	
}

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

static REAL est_initial_heat(MESH *mesh, ADAPT_STAT *adapt)
{
  FUNCNAME("est_initial_heat");
  static REAL C = -1.0;

  if (C < 0.0) {
    C = 1.0;
    GET_PARAMETER(1, "heat estimator C0", "%f", &C);
  }
    	err_L2 = L2_err(prob_data_heat->sol_ex, /*build*/base_heat->get_sol(), nil, false, false, nil, nil);
//so gehts auch: adapt->err_sum = L2_err/*_c*/(prob_data_heat->sol_0, build_heat->get_sol(),
//                            nil, false, false, wrap_rw_el_est, &adapt->err_max);
    	INFO(adapt_heat->info, 2, "||u-uh||L2 = %.4le, ratio = %.2lf\n", err_L2,
			      err_L2/MAX(adapt_heat->adapt_space->err_sum, 1.e-20));
 
//  adapt->err_sum = L2_err_c(prob_data_heat->sol_0, build_heat->get_sol()/* th_h*/,
//                            nil, wrap_rw_el_est, &adapt->err_max, C);
/*Baustelle.Sollte aber elementweisen Fehlerindikator liefern (rw_el_est)*/
//  MSG("L2 error = %.3le\n", adapt->err_sum);

  return(adapt->err_sum);
}

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

void init_heat(ADAPT_INSTAT *adapt_heat,const PROB_DATA_DIFF* prob_data)
{
  FUNCNAME("init_heat");
  REAL theta = 1.0;   // standard: impliziter loeser
 
  static DOF_REAL_VEC* f_th;
  static DOF_REAL_VEC* th_h_old;
  static DOF_SCHAR_VEC* th_bound;
  TEST_EXIT(adapt_heat, "no adapt_heat\n");
  TEST_EXIT(prob_data, "no prob_data_heat\n");
	prob_data_heat=prob_data;
//suboptimale Regelung, wird fuer jedes Teilproblem wiederholt
  if (!th_h)
  	th_h           = get_dof_real_vec("th_h", fe_space);
  th_h_old       = get_dof_real_vec("th_h_old", fe_space);
  th_h->refine_interpol = th_h_old->refine_interpol =	fe_space->bas_fcts->real_refine_inter;
  th_h->coarse_restrict = th_h_old->coarse_restrict =	fe_space->bas_fcts->real_coarse_inter;
  th_bound  	= get_dof_schar_vec("th_bound", fe_space);

  base_heat = new //fem_base("th", fe_space, &prob_data,1, &(adapt_heat->time));
  fem_base("th",
	   &th_h, &th_h_old, &th_bound,
	   &prob_data, 1
	   , &(adapt_heat->time));

  if (prob_data_heat->diffmat)
  	{
	  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");
		}
    	 f_th    = get_dof_real_vec("f_th", fe_space);
	 dof_set(0.0, f_th);
	build_heat=new build_diff("th",base_heat,&f_th, -1, &(adapt_heat->timestep), theta); 
	 est_heat = new parab_est("th",base_heat, &(adapt_heat->timestep), -1);
	 adapt_heat->adapt_space->solve       = solve;
	 adapt_heat->adapt_space->estimate    = estimate_heat;
  	adapt_heat->init_timestep  = init_h_timestep;
	}
  else  
  	{MSG("exact sol will be used\n");
	TEST_EXIT(prob_data_heat->sol_ex, "no prob_data_heat->sol_ex\n");
	 adapt_heat->adapt_space->solve       = interpol;
	 adapt_heat->adapt_space->estimate    = nil;
	}


 adapt_heat->set_time  = set_time;	//in master.cc
  adapt_heat->close_timestep = close_h_timestep;
/*    adapt_heat->get_time_est   = get_time_est; */

  adapt_heat->adapt_initial->solve      = interpol0;
  adapt_heat->adapt_initial->estimate   = est_initial_heat;
  adapt_heat->adapt_initial->get_el_est = get_el_est;

//13.8 raus    adapt_heat->adapt_space->build_after_coarsen = build; // das funkt nicht. Wird nur in
//adapt_mesh aufgerufen.
  adapt_heat->adapt_space->get_el_est  = get_el_est;/* static in heat!"*/
  adapt_heat->adapt_space->get_el_estc = get_el_estc;/* static in heat!"*/
//  adapt_heat->adapt_space->marking =	okmarking; /* wrapper, adapt.c Standard. */
        

  return;
}
