/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     mesh-interface-common.c                                        */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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                                    */
/*                                                                          */
/*             Claus-Justus Heine                                           */
/*             Abteilung fuer Angewandte Mathematik                         */
/*             Albert-Ludwigs-Universitaet Freiburg                         */
/*             Hermann-Herder-Str. 10                                       */
/*             D-79104 Freiburg im Breisgau, Germany                        */
/*                                                                          */
/*             Robert Kloefkorn                                             */
/*             Abteilung fuer Angewandte Mathematik                         */
/*             Albert-Ludwigs-Universitaet Freiburg                         */
/*             Hermann-Herder-Str. 10                                       */
/*             D-79104 Freiburg im Breisgau, Germany                        */
/*                                                                          */
/*  http://www.alberta-fem.de/                                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  (c) by A. Schmidt and K.G. Siebert (1996-2004)                          */
/*         C.-J. Heine and R. Kloefkorn (1998-2007)                         */
/*                                                                          */
/*     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.                         */
/*--------------------------------------------------------------------------*/

/****************************************************************************/
/*  Mesh hierarchy traversal:   first(), next(), ...       		    */
/****************************************************************************/

#if FE_DIM == 3
typedef HELEMENT3D   GRAPE_ELEMENT;
typedef HMESH3D      GRAPE_MESH;
typedef GENMESH3D    GRAPE_GENMESH;
typedef F_HEL_INFO3D F_EL_INFO;
typedef F_HDATA3D    F_DATA;
#define Grape_Mesh  HMesh3d
#else
typedef HELEMENT2D   GRAPE_ELEMENT;
typedef GENMESH2D    GRAPE_GENMESH;
typedef F_HEL_INFO2D F_EL_INFO;

/* higher order visual */
typedef F_HPDATA2D   F_DATA;
typedef HPMESH2D     GRAPE_MESH;
#define Grape_Mesh   HPMesh2d
/*
typedef F_HDATA2D    F_DATA;
#define Grape_Mesh  HMesh2d
*/
#endif

static int        traverse_level     = -1;
static FLAGS      traverse_fill_flag = FILL_COORDS | FILL_BOUND | FILL_NEIGH
#if FE_DIM==3
                                        | FILL_ORIENTATION
#endif
                                        | CALL_LEAF_EL;
static const EL_INFO *el_info;

/* coord has to be double [n][3], because hmesh in grape is always
 * of dimension 3 and for FE_DIM == 2 we set the third component to zero */
static double        coord[N_VERTICES_LIMIT][3];
static int           vindex[N_VERTICES_LIMIT];
static GRAPE_ELEMENT element;

static double  *vertex[N_VERTICES_LIMIT] = {coord[0], coord[1],
					    coord[2], coord[3] };

/* implementation of functions */

void setup_traverse(int level, FLAGS fill_flag)
{
  traverse_level     = level;
  traverse_fill_flag = fill_flag;
  return;
}

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

static void fill_grape_element(void)
{
  int      i, j;

  for (i = 0; i < N_VERTICES(FE_DIM); i++)
  {
    vindex[i] = el_info->el->dof[i][0];
    for (j = 0; j < DIM_OF_WORLD; j++) {
      coord[i][j] = el_info->coord[i][j]; /* double to float conv */
    }
  }
  element.vertex = (double G_CONST*G_CONST*) vertex;
  element.vindex = vindex;

#if FE_DIM == 3
  if (el_info->orientation > 0)
    element.descr = &tetra_description_even;
  else
    element.descr = &tetra_description_odd;
#else
    element.descr = &tria_description;
#endif

  element.eindex = INDEX(el_info->el);
  element.user_data = (void *)el_info;
}

/****************************************************************************/
static TRAVERSE_STACK *stack = NULL;

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

/* returns first leaf element, which is treated like a macro element */
/* because traverse_fill_flag contains the CALL_LEAF_EL flag */
static GRAPE_ELEMENT *first_grape_element(GRAPE_GENMESH *grape_mesh,
					  MESH_ELEMENT_FLAGS flags)
{
  if (!stack) stack = get_traverse_stack();
  el_info = traverse_first(stack, (MESH *)(grape_mesh->user_data),
			   traverse_level, traverse_fill_flag);
  if (el_info)
  {
    fill_grape_element();
    element.mesh = (GRAPE_GENMESH *) grape_mesh;
    return(&element);
  }
  else
    return(NULL);
}

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

/* returns next leaf element, which is treated like a macro element */
/* because traverse_fill_flag contains the CALL_LEAF_EL flag */
static GRAPE_ELEMENT *next_grape_element(GRAPE_ELEMENT *el,
					 MESH_ELEMENT_FLAGS flags)
{
  if ((el_info = traverse_next(stack, (EL_INFO *)el->user_data)))
  {
    fill_grape_element();
    return(&element);
  }
  else
    return(NULL);
}

/* we have no children */
static GRAPE_ELEMENT *fake_child(GRAPE_ELEMENT *el,MESH_ELEMENT_FLAGS flags)
{
  return (NULL);
}
/* we dont want to select no child */
static GRAPE_ELEMENT *fake_select(GRAPE_ELEMENT *el,double *parent_coord,
            double * child_coord, MESH_ELEMENT_FLAGS flags)
{
  return (NULL);
}

/****************************************************************************/
static GRAPE_ELEMENT *complete_grape_element(GRAPE_ELEMENT *el,
					 MESH_ELEMENT_FLAGS flags)
{
  if (el_info)
    return(&element);
  else
    return(NULL);
}

/* mkae copy of element is not used, because we are acting on leaf level */
static GRAPE_ELEMENT *copy_grape_element(GRAPE_ELEMENT *el,
					 MESH_ELEMENT_FLAGS flags)
{
  FUNCNAME("copy_grape_element");

  MSG("not implemented yet!\n");
  return ( NULL );
}

/****************************************************************************/
/* not used, because copy_element is not implemented */
static void free_grape_element(GRAPE_ELEMENT *el)
{
  return;
}

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

static void f_bounds(GRAPE_ELEMENT *el, double* min, double* max,
		     void *function_data)
{
  (*min) =  1.0E+308;
  (*max) = -1.0E+308;
}

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

static void grape_get_vertex_estimate(GRAPE_ELEMENT *el, double *value,
                                      void *function_data)
{
  *value = 0.0;
  return;
}

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

static double grape_get_element_estimate(GRAPE_ELEMENT *el, void *function_data)
{
  return 0.0;
}


/****************************************************************************/
static void f_real(GRAPE_ELEMENT *el, int ind, double G_CONST *coord,
		   double *val, void *function_data)
{
  FUNCNAME("f_real");
  EL_INFO         *el_info = (EL_INFO *)(el->user_data);
  DOF_REAL_VEC      *u_h;
  const EL_REAL_VEC *uh_loc;
  const BAS_FCTS    *bas_fcts;

  static REAL_B   vert_coords[N_VERTICES_LIMIT] = {
    INIT_BARY_0D(1.0),
    INIT_BARY_1D(0.0, 1.0),
    INIT_BARY_2D(0.0, 0.0, 1.0),
    INIT_BARY_3D(0.0,0.0,0.0,1.0)
  };

  u_h = (DOF_REAL_VEC *)(function_data);
  if (u_h) {
    if (u_h->fe_space && (bas_fcts = u_h->fe_space->bas_fcts)) {
      uh_loc   = fill_el_real_vec(NULL, el_info->el, u_h);
      if (coord) {
	val[0] = eval_uh(coord, uh_loc, bas_fcts);
      } else {
	if ((ind >= 0) && (ind < N_VERTICES(FE_DIM))) {
	  val[0] = eval_uh(vert_coords[ind], uh_loc, bas_fcts);
	} else {
	  MSG("invalid vertex number %d\n",ind);
	  val[0] = 0.0;
	}
      }
    } else {
      MSG("no fe_space or bas_fcts in dof_real_vec <%s>\n", u_h->name);
      val[0] = 0.0;
    }
  } else {
    MSG("function_data == NULL\n");
    val[0] = 0.0;
  }
}


static void f_real_el_info(GRAPE_ELEMENT *el, F_EL_INFO *f_el_info,
			   void *function_data)
{
  FUNCNAME("f_real_el_info");
  DOF_REAL_VEC *u_h = (DOF_REAL_VEC *)(function_data);

  if (u_h && u_h->fe_space && u_h->fe_space->bas_fcts) {
    f_el_info->polynomial_degree = u_h->fe_space->bas_fcts->degree;
  } else {
    ERROR("no uh or fe_space or bas_fcts\n");
    f_el_info->polynomial_degree = 1;
  }

  return;
}

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

static void f_real_d(GRAPE_ELEMENT *el, int ind, double G_CONST *coord,
		     double *val, void *function_data)
{
  FUNCNAME("f_real_d");
  EL_INFO             *el_info = (EL_INFO *)(el->user_data);
  DOF_REAL_VEC_D      *u_h;
  const EL_REAL_VEC_D *uh_loc;
  const BAS_FCTS      *bas_fcts;
  static REAL_B        vert_coords[N_VERTICES_LIMIT] = {
    INIT_BARY_0D(1.0),
    INIT_BARY_1D(0.0, 1.0),
    INIT_BARY_2D(0.0, 0.0, 1.0),
    INIT_BARY_3D(0.0,0.0,0.0,1.0)
  };

  u_h = (DOF_REAL_VEC_D *)function_data;
  if (u_h) {
    if (u_h->fe_space && (bas_fcts = u_h->fe_space->bas_fcts)) {
      uh_loc   = fill_el_real_vec_d(NULL, el_info->el, u_h);
      if (coord) {
	eval_uh_dow(val, coord, uh_loc, bas_fcts);
      } else {
	if ((ind >= 0) && (ind < N_VERTICES(FE_DIM))) {
	  eval_uh_dow(val, vert_coords[ind], uh_loc, bas_fcts);
	} else {
	  MSG("invalid vertex number %d\n",ind);
	  val[0] = 0.0;
	}
      }
    } else {
      MSG("no fe_space or bas_fcts in dof_real_vec <%s>\n", u_h->name);
	val[0] = 0.0;
    }
  } else {
    MSG("no dof_real_vec as function_data\n");
    val[0] = 0.0;
  }
}


static void f_real_d_el_info(GRAPE_ELEMENT *el, F_EL_INFO *f_el_info,
			     void *function_data)
{
  FUNCNAME("f_real_el_info");
  DOF_REAL_VEC_D *u_h = (DOF_REAL_VEC_D *)function_data;

  if (u_h && u_h->fe_space && u_h->fe_space->bas_fcts) {
    f_el_info->polynomial_degree = u_h->fe_space->bas_fcts->degree;
  } else {
    ERROR("no uh or fe_space or bas_fcts\n");
    f_el_info->polynomial_degree = 1;
  }

  return;
}

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


void grape_ini_f_data(GRAPE_MESH *grape_mesh, DOF_REAL_VEC *dof_vec)
{
  FUNCNAME("grape_ini_f_data");
  F_DATA *f_data = NULL;

  if (!grape_mesh)
  {
    MSG("no grape_mesh\n");
    return;
  }

  if (dof_vec && dof_vec->vec)
  {
    for (f_data = (void *)grape_mesh->f_data; f_data;
	       f_data = (void *)f_data->next)
    {
      if (f_data->function_data == dof_vec)
      	break;
    }

    if (!f_data)
        for (f_data = (void *)grape_mesh->f_data; f_data;
	        f_data = (void *)f_data->last)
    {
	    if (f_data->function_data == dof_vec)
	      break;
    }


    if (!f_data)
    {
      MSG("generate scalar f_data for `%s'\n", dof_vec->name);
      f_data = (F_DATA *)g_mem_alloc(sizeof(F_DATA));
      f_data->name = (char *) dof_vec->name;
      f_data->dimension_of_value = 1;
      if (strstr(dof_vec->fe_space->bas_fcts->name, "disc"))
      	f_data->continuous_data    = 0;
      else
      	f_data->continuous_data    = 1;

      f_data->f                   = f_real;
      f_data->f_el_info           = f_real_el_info;

      f_data->next = NULL;
      f_data->last = NULL;

      /* this is done by add-funtion
        f_data->next = grape_mesh->f_data;
        f_data->last = grape_mesh->f_data ? grape_mesh->f_data->last : NULL;
      */

      f_data->function_data = (void *) dof_vec;

      f_data->get_bounds      = f_bounds;
      f_data->get_vertex_estimate   = grape_get_vertex_estimate;
      f_data->get_element_estimate  = grape_get_element_estimate;
      f_data->threshold     = 0.0;
#if FE_DIM == 3
      f_data->geometry_threshold     = 0.0;
#else
    f_data->get_element_p_estimates = NULL;
    f_data->get_edge_p_estimates    = NULL; 
#endif
      f_data->hp_threshold    = 0.0;
      f_data->hp_maxlevel     = 0;

      /* grape_mesh->f_data = (GENMESH_FDATA *) f_data; */
      /* use the hamehs add-function method to add data to mesh */
      grape_mesh = (GRAPE_MESH *) GRAPE(grape_mesh,"add-function")(f_data);

    } else if (grape_mesh->f_data != (GENMESH_FDATA *)f_data) {
      MSG("select f_data for `%s'\n", dof_vec->name);
      grape_mesh = (GRAPE_MESH *) GRAPE(grape_mesh,"add-function")(f_data);
      /* use the hamehs add-function method to add data to mesh */
      /* grape_mesh->f_data = (GENMESH_FDATA *)f_data; */
    }
  } else {
    MSG("no dof_vec, or no vec\n");
  }

  return;
}

void grape_ini_f_data_d(GRAPE_MESH *grape_mesh, DOF_REAL_VEC_D *dof_vec)
{
  FUNCNAME("grape_ini_f_data_d");
  F_DATA *f_data=NULL;

  if (!grape_mesh)
  {
    MSG("no grape_mesh\n");
    return;
  }

  if (dof_vec && dof_vec->vec)
  {
    for (f_data = (void *)grape_mesh->f_data; f_data;
	       f_data = (void *)f_data->next)
    {
      if (f_data->function_data == dof_vec)
      	break;
    }

    if (!f_data)
      for (f_data = (void *)grape_mesh->f_data; f_data;
	   f_data = (void *)f_data->last)
      {
	if (f_data->function_data == dof_vec)
	  break;
      }


    if (!f_data) {
      MSG("generate vector-valued f_data for `%s'\n", dof_vec->name);
      f_data = (F_DATA *)g_mem_alloc(sizeof(F_DATA));
      f_data->name = (char *) dof_vec->name;
      f_data->dimension_of_value = DIM_OF_WORLD;
      if (strstr(dof_vec->fe_space->bas_fcts->name, "disc"))
      	f_data->continuous_data    = 0;
      else
      	f_data->continuous_data    = 1;

      f_data->f                   = f_real_d;
      f_data->f_el_info           = f_real_d_el_info;

      f_data->next = NULL;
      f_data->last = NULL;

      /* this is done by add-function
        f_data->next = grape_mesh->f_data;
        f_data->last = grape_mesh->f_data ? grape_mesh->f_data->last : NULL;
      */

      f_data->function_data = (void *) dof_vec;

      f_data->get_bounds      = f_bounds;
      f_data->get_vertex_estimate   = grape_get_vertex_estimate;
      f_data->get_element_estimate  = grape_get_element_estimate;
      f_data->threshold     = 0.0;
#if FE_DIM == 3
      f_data->geometry_threshold     = 0.0;
#endif

      f_data->hp_threshold    = 0.0;
      f_data->hp_maxlevel     = 0;

      /*grape_mesh->f_data = (GENMESH_FDATA *)f_data;*/
      grape_mesh = (GRAPE_MESH *) GRAPE(grape_mesh,"add-function")(f_data);
    } else if (grape_mesh->f_data != (GENMESH_FDATA *)f_data) {
      MSG("select f_data for `%s'\n", dof_vec->name);
      grape_mesh = (GRAPE_MESH *) GRAPE(grape_mesh,"add-function")(f_data);
      /*grape_mesh->f_data = (GENMESH_FDATA *)f_data;*/
    }
  } else {
    MSG("no dof_vec, or no vec\n");
  }

  return;
}

/****************************************************************************/
/* handling of multiple functions (selection by next/last)                  */
/****************************************************************************/

static GRAPE_MESH *next_f_data_send(void)
{
  GRAPE_MESH *self;

  self = (GRAPE_MESH *)START_METHOD(G_INSTANCE);
  ASSURE(self, "", END_METHOD(NULL));

  if (self->f_data && self->f_data->next)
  {
    self->f_data->next->last = self->f_data;  /*only to be sure...*/
    self->f_data = self->f_data->next;
  }
  if (self->f_data)
    printf("new f_data is: %s\n", self->f_data->name);

  END_METHOD(self);
}

static GRAPE_MESH *prev_f_data_send(void)
{
  GRAPE_MESH *self;

  self = (GRAPE_MESH *)START_METHOD(G_INSTANCE);
  ASSURE(self, "", END_METHOD(NULL));

  if (self->f_data && self->f_data->last)
  {
    self->f_data->last->next = self->f_data;  /*only to be sure...*/
    self->f_data = self->f_data->last;
  }
  if (self->f_data)
    printf("new f_data is: %s\n", self->f_data->name);

  END_METHOD(self);
}

/* copy function data */
static inline void copyFdata(F_DATA *copy, F_DATA *org)
{
  copy->name = org->name;
  copy->last = org->last;
  copy->next = org->next;

  copy->dimension_of_value  = org->dimension_of_value;
  copy->continuous_data = org->continuous_data;
  copy->function_data  = org->function_data;

  copy->f = org->f;
  copy->f_el_info = org->f_el_info;

  copy->get_bounds  = org->get_bounds;
  copy->get_vertex_estimate = org->get_vertex_estimate;
  copy->get_element_estimate  = org->get_element_estimate;

  copy->threshold = org->threshold;
#if DIM_OF_WORLD == 3
  copy->geometry_threshold = org->geometry_threshold;
#else
  copy->get_element_p_estimates = org->get_element_p_estimates;
  copy->get_edge_p_estimates    = org->get_edge_p_estimates;
#endif
  copy->hp_threshold = org->hp_threshold;
  copy->hp_maxlevel = org->hp_maxlevel;
}

/* also used in albert_movi */
void copyHmeshes(GRAPE_MESH *orgMesh, GRAPE_MESH *self)
{
  FUNCNAME("copyHmeshes");

  DEBUG_TEST_EXIT(self != NULL, "self == NULL");
  DEBUG_TEST_EXIT(orgMesh != NULL, "orgMesh == NULL");

  if (!self->f_data && orgMesh->f_data) {
    self->level_of_interest = orgMesh->level_of_interest;
    F_DATA *next_data = (F_DATA *) orgMesh->f_data;
    /* to keep the same order we have to go backward */
    while (next_data) {
      if (next_data->next) {
	next_data = (F_DATA *) next_data->next;
      } else {
	break;
      }
    }
    while (next_data) {
      F_DATA * f_data = (F_DATA *) malloc(sizeof(F_DATA));

      DEBUG_TEST_EXIT(f_data != NULL, "f_data == NULL");
      copyFdata(f_data,next_data);

      self = (GRAPE_MESH *)GRAPE(self,"add-function")(f_data);
      next_data = (F_DATA *)next_data->last;
    }
  }

  self->max_dimension_of_coord = orgMesh->max_dimension_of_coord;
  self->max_eindex = orgMesh->max_eindex;
  self->max_vindex = orgMesh->max_vindex;
  self->max_dindex = orgMesh->max_dindex;
  self->max_number_of_vertices = orgMesh->max_number_of_vertices;

  self->access_mode = orgMesh->access_mode;
  self->access_capability = orgMesh->access_capability;

  /* we have to do that, GRAPE sucks  */
  /* set other function_data pointers */
  if (orgMesh->f_data) {
    GENMESH_FDATA * sf = self->f_data;
    while(sf != NULL) {
      const char * sfname = sf->name;
      GENMESH_FDATA * nf = orgMesh->f_data;
      int length = strlen(sfname);
      while( (nf != NULL) ) {
        /* compare the real function name, ha, not with me */
        const char * nfname = nf->name;
        if (strncmp(sfname,nfname,length) == 0) {
          sf->function_data = nf->function_data;
          break;
        }

        /* GRAPE sucks, sucks, sucks
         * Robert after debugin' for this shit more than one day */
        if (nf != nf->last ) {
          nf = nf->next;
	} else {
          break;
	}
      }

      /* go next f_data */
      if (sf != sf->last ) {
        sf = sf->next;
      }
    }
  }

  /* copy current function selections to orgMesh */
  self = (GRAPE_MESH *) GRAPE(self, "copy-function-selector")(orgMesh);

  self->user_data = orgMesh->user_data;

  self->copy_element = orgMesh->copy_element;
  self->free_element = orgMesh->free_element;

  self->complete_element = orgMesh->complete_element;
  self->set_time = orgMesh->set_time;
  self->get_time = orgMesh->get_time;

  self->first_macro = orgMesh->first_macro;
  self->next_macro = orgMesh->next_macro;
  self->first_child = orgMesh->first_child;
  self->next_child = orgMesh->next_child;
  self->select_child = orgMesh->select_child;

  self->max_level = orgMesh->max_level;
  self->level_of_interest = orgMesh->level_of_interest;
  /* do not set level_of_interest, because is set by user during run time */

  self->get_geometry_vertex_estimate  = orgMesh->get_geometry_vertex_estimate;
  self->get_geometry_element_estimate = orgMesh->get_geometry_element_estimate;
  self->get_lens_element_estimate     = orgMesh->get_lens_element_estimate;
  self->threshold                     = orgMesh->threshold;

#if DIM_OF_WORLD == 2
  self->dimension_of_world = orgMesh->dimension_of_world;
#endif
}

/* make hard copy of hmesh, i.e. create new object and copy data */
static GRAPE_MESH* newHmeshHardCopy(void)
{
  GRAPE_MESH * hmesh = (GRAPE_MESH*) START_METHOD (G_INSTANCE);
  ALERT (hmesh, "hmesh-hardcopy: No hmesh!", END_METHOD(NULL));
  char *newName = malloc(strlen(hmesh->name)+sizeof("H: "));
  // get new hmesh
  GRAPE_MESH* copy;

  sprintf(newName, "H: %s", hmesh->name);

  copy = (GRAPE_MESH *) GRAPE(Grape_Mesh,"new-instance")(newName);
  ALERT (copy, "hmesh_hardcopy: No new instance!", END_METHOD(NULL));

  // copy mesh
  copyHmeshes(hmesh, copy);

  END_METHOD (copy);
}

static void grape_add_methods(void)
{
  static bool init_done;

  if (!init_done) {
    init_done = true;
    GRAPE(Grape_Mesh, "add-method")("next-f-data-send",next_f_data_send);
    GRAPE(Grape_Mesh, "add-method")("prev-f-data-send",prev_f_data_send);

    if (GRAPE(Grape_Mesh, "find-method")("=hardcopy")) {
      GRAPE(Grape_Mesh, "delete-method")("hardcopy");
    }
    GRAPE(Grape_Mesh, "add-method")("hardcopy", &newHmeshHardCopy);
  }
}

/****************************************************************************/
/* convert ALBERTA mesh into GRAPE mesh                                      */
/****************************************************************************/

GRAPE_MESH *setup_grape_mesh(MESH *mesh, char *name)
{
  FUNCNAME("setup_grape_mesh");
  GRAPE_MESH *grape_mesh;
  int        i, j;

  grape_add_methods();

  ASSURE((grape_mesh = (GRAPE_MESH *)GRAPE(Grape_Mesh,"new-instance")(name)),
	       "can't get Grape Mesh instance",
	       return(NULL));

  grape_mesh->max_level = 0;

  for (i=0; i < N_VERTICES(FE_DIM); i++)
    for (j=0; j<3; j++)
      coord[i][j] = 0.0;

  /* Funktioniert halt nur fuer alle leaf Element, gell! */
  grape_mesh->first_child  = fake_child;
  grape_mesh->next_child   = fake_child;
  grape_mesh->first_macro  = first_grape_element;
  grape_mesh->next_macro   = next_grape_element;
  grape_mesh->copy_element  = copy_grape_element;
  grape_mesh->free_element  = free_grape_element;

  grape_mesh->select_child = fake_select;
  grape_mesh->complete_element = complete_grape_element;

  /* robert: hier lieber nitch NULL setzen, wer weiss?
    grape_mesh->get_lens_element_estimate = NULL;
    grape_mesh->get_geometry_element_estimate = NULL;
    grape_mesh->get_geometry_vertex_estimate = NULL;
    grape_mesh->set_time = NULL;
    grape_mesh->get_time = NULL;
  */


  grape_mesh->max_number_of_vertices = N_VERTICES(FE_DIM);
  grape_mesh->max_eindex = mesh->n_hier_elements;
  grape_mesh->max_vindex = mesh->n_vertices;
  grape_mesh->max_dimension_of_coord = N_VERTICES(FE_DIM);

  grape_mesh->max_dindex = 1;

  grape_mesh->level_of_interest = grape_mesh->max_level;

#if FE_DIM==2
#if (DIM_OF_WORLD > 2) || ADD_Z
  grape_mesh->dimension_of_world = 3;  /* !!!! */
#else
  grape_mesh->dimension_of_world = 2;
#endif
#endif

  grape_mesh->user_data = (void *)mesh;

  element.mesh  = (GRAPE_GENMESH *) grape_mesh;
#if FE_DIM==2
  element.descr = &tria_description;
#else
  element.descr = &tetra_description_even;
#endif

  /* adjust max_vindex such that simple algorithm is ok. (dof[v][0]) */
  for (i = 0; i < mesh->n_dof_admin; i++) {
    if ((mesh->dof_admin[i]->n_dof[VERTEX] > 0) &&
	(mesh->dof_admin[i]->n0_dof[VERTEX] == 0)) {
      grape_mesh->max_vindex = mesh->dof_admin[i]->size_used;
      MSG("use admin <%s> for vertex numbering, max=%d\n",
	  NAME(mesh->dof_admin[i]), mesh->dof_admin[i]->size_used);
      break;
    }
  }

  return grape_mesh;
}

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