/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/* file:     parametric_1d.c                                                */
/*                                                                          */
/* description: Support for parametric elements in 0D                       */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Daniel Koester                                               */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*             Claus-Justus Heine                                           */
/*             Abteilung fuer Angewandte Mathematik                         */
/*             Universitaet Freiburg                                        */
/*             Hermann-Herder-Str. 10                                       */
/*             79104 Freiburg, Germany                                      */
/*                                                                          */
/*  http://www.alberta-fem.de                                               */
/*                                                                          */
/*  (c) by D. Koester (2005),                                               */
/*         C.-J. Heine (2006-2007).                                         */
/*--------------------------------------------------------------------------*/

#ifdef MESH_DIM
# undef MESH_DIM
#endif
#define MESH_DIM 0

#ifdef N_BAS_MAX
# undef N_BAS_MAX
#endif
#define N_BAS_MAX N_BAS_LAGRANGE(LAGRANGE_DEG_MAX, MESH_DIM)

static const REAL_B vertex_bary_0d[1] = {
  { 1.0, },
};

/*--------------------------------------------------------------------------*/
/* Functions for affine elements as parametric elements. (suffix 1_1d)      */
/*--------------------------------------------------------------------------*/

static bool param_init_element_0d(const EL_INFO *el_info,
				  const PARAMETRIC *parametric)
{
  LAGRANGE_PARAM_DATA *data = (LAGRANGE_PARAM_DATA *)parametric->data;
  DOF_REAL_D_VEC *coords = data->coords;
  int node_v, n0_v;
  EL *el = el_info->el;
  EL_INFO *mod_el_info = (EL_INFO *)el_info; /* modifyable pointer reference */

  data->el = el_info->el;

  node_v = el_info->mesh->node[VERTEX];
  n0_v   = coords->fe_space->admin->n0_dof[VERTEX];
   
  if (!parametric->use_reference_mesh) {
    data->local_coords = mod_el_info->coord;
    mod_el_info->fill_flag |= FILL_COORDS;
  } else {
    data->local_coords = data->param_local_coords;
  }
  COPY_DOW(coords->vec[el->dof[node_v][n0_v]], data->local_coords[0]);

  return false; /* not really parametric */
}

static void det_0d(const EL_INFO *el_info, const QUAD *quad, int N,
		   const REAL_B lambda[], REAL dets[])
{
  int  n;

  if (quad) {
    N = quad->n_points;
  }

  for (n = 0; n < N; n++) {
    dets[n] = 1.0;
  }
}


static void grd_lambda_0d(const EL_INFO *el_info, const QUAD *quad,
			  int N, const REAL_B lambda[],
			  REAL_BD grd_lam[], REAL_BDD D2_lam[], REAL dets[])
{
  int i, n;

  if (quad) {
    N = quad->n_points;
  }

  for (n = 0; n < N; n++) {
    for (i = 0; i < N_LAMBDA_1D; i++) {
      SET_DOW(0.0, grd_lam[n][i]);
    }
    for (; i < N_LAMBDA_MAX; i++) {
      SET_DOW(0.0, grd_lam[n][i]);
    }
    if (dets) {
      dets[n] = 0.0;
    }
  }
  if (D2_lam) {
    for (n = 0; n < N; n++) {
      for (i = 0; i < N_LAMBDA_MAX;i++) {
	MSET_DOW(0.0, D2_lam[n][i]);
      }
    }
  }
}

static void
grd_world_0d(const EL_INFO *el_info, const QUAD *quad,
	     int N, const REAL_B lambda[],
	     REAL_BD grd_Xtr[], REAL_BDB D2_Xtr[], REAL_BDBB D3_Xtr[])
{
  int i, n;

  if (quad) {
    N = quad->n_points;
  }

  for (n = 0; n < N; n++) {
    COPY_DOW(el_info->coord[0], grd_Xtr[n][0]);
    for (i = 1; i < N_LAMBDA_MAX; i++) {
      SET_DOW(0.0, grd_Xtr[n][i]);
    }
  }
  if (D2_Xtr) {
    memset(D2_Xtr, 0, N*sizeof(REAL_BDB));
  }
  if (D3_Xtr) {
    memset(D3_Xtr, 0, N*sizeof(REAL_BDBB));
  }
}

/* Compute the co-normal for WALL at the barycentric coordinates
 * specified by LAMBDA. LAMBDA are 1d barycentric coordinates,
 * i.e. lambda[wall] == 0.0.
 */
static void
wall_normal_0d(const EL_INFO *el_info, int wall,
	       const QUAD *quad,
	       int n, const REAL_B lambda[],
	       REAL_D normals[], REAL_DB grd_normals[], REAL_DBB D2_normals[],
	       REAL dets[])
{
  if (quad) {
    n = quad->n_points;
  }

  if (dets) {
    memset(dets, 0, n*sizeof(REAL));
  }
  if (normals) {
    memset(normals, 0, n*sizeof(REAL_D));
  }
  if (grd_normals) {
    memset(grd_normals, 0, n*sizeof(REAL_DB));
  }
  if (D2_normals) {
    memset(D2_normals, 0, n*sizeof(REAL_DBB));
  }
}

/****************************************************************************/
/* fill_coords_0d(data): initialize the DOF_REAL_D_VEC coords containing    */
/* the position data of the parametric elements (coordinates of vertices in */
/* this case).                                                              */
/****************************************************************************/

static void fill_coords_0d(LAGRANGE_PARAM_DATA *data)
{
  DOF_REAL_D_VEC  *coords   = data->coords;
  NODE_PROJECTION *n_proj   = data->n_proj;
  bool            selective = n_proj != NULL;
  const NODE_PROJECTION *act_proj;
  FLAGS           fill_flag = CALL_LEAF_EL|FILL_COORDS|FILL_PROJECTION;
  const BAS_FCTS  *bfcts;
  const DOF_ADMIN *admin;
  MESH            *mesh;
  DOF             dof[N_VERTICES_1D];

  admin = coords->fe_space->admin;
  bfcts = coords->fe_space->bas_fcts;
  mesh  = coords->fe_space->mesh;

  TRAVERSE_FIRST(mesh, -1, fill_flag) {
    REAL *vec;

    GET_DOF_INDICES(bfcts, el_info->el, admin, dof);

    vec = coords->vec[dof[0]];
    
    COPY_DOW(el_info->coord[0], vec);

    /* Look for a projection function that applies to vertex[i]. */
    /* Apply this projection if found.                           */
    
    if (!selective || n_proj->func) {
      act_proj = el_info->active_projection;
      if (act_proj && act_proj->func && (!selective || act_proj == n_proj)) {
	act_proj->func(vec, el_info, vertex_bary_0d[0]);
      }
    }
  } TRAVERSE_NEXT();
}

static void vertex_coords_0d(EL_INFO *el_info)
{
  PARAMETRIC          *parametric = el_info->mesh->parametric;
  LAGRANGE_PARAM_DATA *data       = (LAGRANGE_PARAM_DATA *)parametric->data;
  DOF_REAL_D_VEC      *coords     = data->coords;
  EL                  *el         = el_info->el;
  int node_v, n0_v;

  node_v = el_info->mesh->node[VERTEX];
  n0_v   = coords->fe_space->admin->n0_dof[VERTEX];
    
  el_info->fill_flag |= FILL_COORDS;
  COPY_DOW(coords->vec[el->dof[node_v][n0_v]], el_info->coord[0]);
}

#if DIM_MAX > 0
static void slave_fill_coords_0d(LAGRANGE_PARAM_DATA *s_data)
{
  DOF_REAL_D_VEC      *s_coords     = s_data->coords;
  MESH                *slave        = s_coords->fe_space->mesh;
  const BAS_FCTS      *s_bfcts      = s_coords->fe_space->bas_fcts;
  const DOF_ADMIN     *s_admin      = s_coords->fe_space->admin;
  MESH                *master       = get_master(slave);
  PARAMETRIC          *m_parametric = master->parametric;
  LAGRANGE_PARAM_DATA *m_data       = (LAGRANGE_PARAM_DATA *)m_parametric->data;
  DOF_REAL_D_VEC      *m_coords     = m_data->coords;
  const BAS_FCTS      *m_bfcts      = m_coords->fe_space->bas_fcts;
  const DOF_ADMIN     *m_admin      = m_coords->fe_space->admin;
  EL *m_el, *s_el;
  int  wall;
  const int *trace_map;
  DOF m_dofs[m_bfcts->n_bas_fcts];
  DOF s_dofs[s_bfcts->n_bas_fcts];

  TRAVERSE_FIRST(slave, -1, CALL_LEAF_EL|FILL_MASTER_INFO) {

    s_el = el_info->el;
    m_el = el_info->master.el;
    wall = el_info->master.opp_vertex;
    trace_map = m_bfcts->trace_dof_map[0][0][wall];

    GET_DOF_INDICES(s_bfcts, s_el, s_admin, s_dofs);
    GET_DOF_INDICES(m_bfcts, m_el, m_admin, m_dofs);

    COPY_DOW(m_coords->vec[m_dofs[trace_map[0]]], s_coords->vec[s_dofs[0]]);
     
  } TRAVERSE_NEXT();
}
#endif

static PARAMETRIC lagrange_parametric_0d = {
  "0D Lagrange parametric elements",
  false,  /* not_all */
  false,  /* use_reference_mesh */
  param_init_element_0d,
  vertex_coords_0d,
  param_coord_to_world,
  param_world_to_coord,
  det_0d,
  grd_lambda_0d,
  grd_world_0d,
  wall_normal_0d,
  NULL       /* data */
};
