/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     level.c                                                        */
/*                                                                          */
/* description:  routines for extracting level set of a piecewise linear    */
/*               fe-function                                                */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*            file is includes by level_xd.c in 2d and 3d!                  */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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"

#if DIM_MAX > 1 && DIM_MAX < 4
/* Return 0 if this element should be skipped (i.e. because the work
 * was already done by the neighbour. For an "interior" edge (i.e. one
 * inside an element) the parameter "face" is -1. "v" are the values
 * of the level-set function at the vertices of the element, "lambda"
 * hold its barycentric coordinates.
 */
static int (*init_element)(const EL_INFO *el_info, REAL v[], 
			   int N, int face, const REAL_B lambda[]);
/* "coord" are the coordinates of the vertices of the boundary
 * facette. In 3d the boundary facette may be a quadrilateral in which
 * case "i" gives the number of the sub-triangle of the virtual
 * triangulation of the boundary.
 */
static void (*cal_element)(const EL_INFO *el_info, REAL v[],
			   int i, 
			   int face, const REAL_B lambda[], 
			   const REAL_D coord[]);

static REAL  level_value;
static REAL  small = 1.0E-8;

static const DOF_REAL_VEC *level;
static const EL_REAL_VEC *
(*get_real_vec)(REAL *el_vec, const EL *, const DOF_REAL_VEC *);
static const BAS_FCTS *linear;

# if DIM_MAX > 1
#  include "level_2d.c"
# endif
# if DIM_MAX > 2
#  include "level_3d.c"
# endif

#endif

int find_level(MESH *mesh, FLAGS fill_flag, const DOF_REAL_VEC *Level,
	       REAL value,
	       int (*init)(const EL_INFO *el_info,
			   REAL v[], 
			   int N, int face, const REAL_B lambda[]),
	       void (*cal)(const EL_INFO *el_info,
			   REAL v[],
			   int i, 
			   int face, const REAL_B lambda[], 
			   const REAL_D coord[]))
{
#if DIM_MAX > 1 && DIM_MAX < 4
  FUNCNAME("find_level");
  REAL  max_abs = 1.e-15;

  TEST_EXIT(mesh->dim >=2 && mesh->dim <= 3,
	    "level set support unimplemented for dim != 2,3.\n");
  TEST_EXIT((level = Level)  && level->fe_space,
	    "no level function or no fe-space in level function\n");
  TEST_EXIT(linear = level->fe_space->bas_fcts,
	    "no basis functions in level function\n");
  TEST_EXIT(linear->degree == 1, "only for degree 1\n");
  get_real_vec = linear->get_real_vec;

  level_value = value;
  init_element = init;
  cal_element = cal;

  FOR_ALL_DOFS(level->fe_space->admin, 
	       max_abs = MAX(max_abs, ABS(level->vec[dof] - value)));
  small = 1.0e-8*MIN(1.0e-2,max_abs);

  fill_flag |= CALL_LEAF_EL|FILL_COORDS;
  switch (mesh->dim) {
  case 2:
    n_el = 0;
    mesh_traverse(mesh, -1, fill_flag, level_fct_2d, NULL);
    return n_el;
#if DIM_MAX > 2
  case 3:
    n_tri = n_quad = 0;
    mesh_traverse(mesh, -1, fill_flag, level_fct_3d, NULL);
    return n_tri+n_quad;
#endif
  }
#endif
  return 0; /* not reached */
}

static void set_mark_fct(const EL_INFO *el_info, void *markerp)
{
  el_info->el->mark = *(S_CHAR *)markerp;
}

void set_element_mark(MESH *mesh, FLAGS fill_flag, S_CHAR mark)
{
  mesh_traverse(mesh, -1, fill_flag, set_mark_fct, &mark);
}

