/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     lagrange_1_3d.c                                                */
/*                                                                          */
/* description:  piecewise linear Lagrange elements in 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.mathematik.uni-freiburg.de/IAM/ALBERTA                       */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

static const REAL_B bary1_3d[N_BAS_LAG_1_3D] = {
  {1.0, 0.0, 0.0, 0.0},
  {0.0, 1.0, 0.0, 0.0},
  {0.0, 0.0, 1.0, 0.0},
  {0.0, 0.0, 0.0, 1.0}
};

static LAGRANGE_DATA lag_1_3d_data = {
  bary1_3d,
  NULL /* lumping_quad */,
};

/*--------------------------------------------------------------------------*/
/*  basisfunction at vertex 0                                               */
/*--------------------------------------------------------------------------*/

static REAL phi1v0_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return lambda[0];
}

static const REAL *grd_phi1v0_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static const REAL_B grd = {1.0, 0.0, 0.0, 0.0};
  return grd;
}

static const REAL_B (*D2_phi1v0_3d(const REAL *lambda, const BAS_FCTS *thisptr))
{
  static  const REAL_BB D2 = { { 0.0, } };
  return D2;
}

/*--------------------------------------------------------------------------*/
/*  basisfunction at vertex 1                                               */
/*--------------------------------------------------------------------------*/

static REAL phi1v1_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return lambda[1];
}

static const REAL *grd_phi1v1_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static const REAL_B grd = {0.0, 1.0, 0.0, 0.0};
  return grd;
}

static const REAL_B (*D2_phi1v1_3d(const REAL *lambda, const BAS_FCTS *thisptr))
{
  static  const REAL_BB D2 = { { 0.0, } };
  return D2;
}

/*--------------------------------------------------------------------------*/
/*  basisfunction at vertex 2                                               */
/*--------------------------------------------------------------------------*/

static REAL phi1v2_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return lambda[2];
}

static const REAL *grd_phi1v2_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static const REAL_B grd = {0.0, 0.0, 1.0, 0.0};
  return grd;
}

static const REAL_B (*D2_phi1v2_3d(const REAL *lambda, const BAS_FCTS *thisptr))
{
  static  const REAL_BB D2 = { { 0.0, } };
  return D2;
}

/*--------------------------------------------------------------------------*/
/*  basisfunction at vertex 3                                               */
/*--------------------------------------------------------------------------*/

static REAL phi1v3_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return lambda[3];
}

static const REAL *grd_phi1v3_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static const REAL_B grd = {0.0, 0.0, 0.0, 1.0};
  return grd;
}

static const REAL_B (*D2_phi1v3_3d(const REAL *lambda, const BAS_FCTS *thisptr))
{
  static  const REAL_BB D2 = { { 0.0, } };
  return D2;
}

#undef DEF_EL_VEC_1_3D
#define DEF_EL_VEC_1_3D(type, name)				\
  DEF_EL_VEC_CONST(type, name, N_BAS_LAG_1_3D, N_BAS_LAG_1_3D)

#undef DEFUN_GET_EL_VEC_1_3D
#define DEFUN_GET_EL_VEC_1_3D(name, type, admin, body, ...)		\
  static const EL_##type##_VEC *					\
  get_##name##1_3d(type##_VEC_TYPE *vec, const EL *el, __VA_ARGS__)	\
  {									\
    FUNCNAME("d_get_"#name"1_3d");					\
    static DEF_EL_VEC_1_3D(type, rvec_space);				\
    type##_VEC_TYPE *rvec = vec ? vec : rvec_space->vec;		\
    int n0, /* node, */ ibas;						\
    DOF **dofptr = el->dof, dof;					\
									\
    DEBUG_TEST_EXIT(true, "");						\
									\
    /* node = (admin)->mesh->node[VERTEX]; */				\
    n0   = (admin)->n0_dof[VERTEX];					\
    for (ibas = 0; ibas < N_BAS_LAG_1_3D; ibas++) {			\
      dof = dofptr[/* node+ */ibas][n0];				\
      body;								\
    }									\
									\
    return vec ? NULL : rvec_space;					\
  }									\
  struct _AI_semicolon_dummy

#undef DEFUN_GET_EL_DOF_VEC_1_3D
#define DEFUN_GET_EL_DOF_VEC_1_3D(name, type, ASSIGN)			\
  DEFUN_GET_EL_VEC_1_3D(_##name##_vec, type, dv->fe_space->admin,	\
			ASSIGN(dv->vec[dof], rvec[ibas]),		\
			const DOF_##type##_VEC *dv);			\
  static const EL_##type##_VEC *					\
  get_##name##_vec1_3d(type##_VEC_TYPE *vec, const EL *el,		\
		       const DOF_##type##_VEC *dv)			\
  {									\
    EL_##type##_VEC *vec_loc = dv->vec_loc;				\
									\
    if (vec != NULL || vec_loc == NULL) {				\
      return get__##name##_vec1_3d(vec, el, dv);			\
    } else {								\
      get__##name##_vec1_3d(vec_loc->vec, el, dv);			\
      return vec_loc;							\
    }									\
  }									\
  struct _AI_semicolon_dummy

#undef COPY_EQ
#define COPY_EQ(a, b) (b) = (a)

/*--------------------------------------------------------------------------*/
/*  functions for combining basisfunctions with coefficients                */
/*--------------------------------------------------------------------------*/

DEFUN_GET_EL_VEC_1_3D(dof_indices, DOF, admin,
		      rvec[ibas] = dof,
		      const DOF_ADMIN *admin, const BAS_FCTS *thisptr);

static const EL_BNDRY_VEC *
get_bound1_3d(BNDRY_FLAGS *vec,
	      const EL_INFO *el_info, const BAS_FCTS *thisptr)
{
  FUNCNAME("get_bound1_3d");
  static DEF_EL_VEC_1_3D(BNDRY, rvec_space);
  BNDRY_FLAGS *rvec = vec ? vec : rvec_space->vec;
  int            i;

  DEBUG_TEST_FLAG(FILL_BOUND, el_info);

  for (i = 0; i < N_VERTICES_3D; i++) {
    BNDRY_FLAGS_CPY(rvec[i], el_info->vertex_bound[i]);
  }

  return vec ? NULL : rvec_space;
}

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_INT_VEC                   ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_1_3D(int, INT, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_VEC                  ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_1_3D(real, REAL, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_D_VEC                ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_1_3D(real_d, REAL_D, COPY_DOW);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_SCHAR_VEC                 ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_1_3D(schar, SCHAR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_UCHAR_VEC                 ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_1_3D(uchar, UCHAR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_PTR_VEC                   ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_1_3D(ptr, PTR, COPY_EQ);

/*--------------------------------------------------------------------*/
/*--- function for accessing a local DOF_REAL_DD_VEC               ---*/
/*--------------------------------------------------------------------*/

DEFUN_GET_EL_DOF_VEC_1_3D(real_dd, REAL_DD, _AI_MCOPY_DOW);

/*--------------------------------------------------------------------*/
/*--- function for local interpolaton of scalar functions          ---*/
/*--------------------------------------------------------------------*/
 
GENERATE_INTERPOL(/**/, 1, 3, N_BAS_LAG_1_3D);

/*--------------------------------------------------------------------*/
/*--- function for local interpolaton of vector functions          ---*/
/*--------------------------------------------------------------------*/
 
GENERATE_INTERPOL_D(/**/, 1, 3, N_BAS_LAG_1_3D);

GENERATE_INTERPOL_DOW(/**/, 1, 3, N_BAS_LAG_1_3D);

/*--------------------------------------------------------------------------*/
/*  functions for interpolation/ restriction during refinement/coarsening   */
/*--------------------------------------------------------------------------*/

static void real_refine_inter1_3d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("real_refine_inter1_3d");
  EL      *el;
  REAL    *vec = NULL;
  DOF     dof_new, dof0, dof1;
  int     n0;

  if (n < 1) return;
  GET_DOF_VEC(vec, drv);

  n0 = drv->fe_space->admin->n0_dof[VERTEX];
  el = list->el_info.el;
  dof0 = el->dof[0][n0];           /* 1st endpoint of refinement edge */
  dof1 = el->dof[1][n0];           /* 2nd endpoint of refinement edge */
  dof_new = el->child[0]->dof[3][n0];  /*      newest vertex is dim */
  vec[dof_new] = 0.5*(vec[dof0] + vec[dof1]);
}

/*--------------------------------------------------------------------------*/
/*  linear interpolation during coarsening: do nothing                      */
/*--------------------------------------------------------------------------*/

static void real_coarse_restr1_3d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  /*FUNCNAME("real_coarse_restr1_3d");*/
  EL      *el;
  REAL    *vec = drv->vec;
  DOF     dof_new, dof0, dof1;
  int     n0;

  n0 = drv->fe_space->admin->n0_dof[VERTEX];
  el = list->el_info.el;
  dof0 = el->dof[0][n0];           /* 1st endpoint of refinement edge */
  dof1 = el->dof[1][n0];           /* 2nd endpoint of refinement edge */
  dof_new = el->child[0]->dof[3][n0];  /*      newest vertex is DIM */
  vec[dof0] += 0.5*vec[dof_new];
  vec[dof1] += 0.5*vec[dof_new];

  return;
}

static void real_d_refine_inter1_3d(DOF_REAL_D_VEC *drdv,
				    RC_LIST_EL *list, int n)
{
  FUNCNAME("real_d_refine_inter1_3d");
  EL      *el;
  REAL_D  *vec = NULL;
  DOF     dof_new, dof0, dof1;
  int     n0, k;

  if (n < 1) return;
  GET_DOF_VEC(vec, drdv);
  n0 = drdv->fe_space->admin->n0_dof[VERTEX];
  el = list->el_info.el;
  dof0 = el->dof[0][n0];           /* 1st endpoint of refinement edge */
  dof1 = el->dof[1][n0];           /* 2nd endpoint of refinement edge */
  dof_new = el->child[0]->dof[3][n0];  /*      newest vertex is DIM */

  for (k = 0; k < DIM_OF_WORLD; k++)
    vec[dof_new][k] = 0.5*(vec[dof0][k] + vec[dof1][k]);
}

/*--------------------------------------------------------------------------*/
/*  linear interpolation during coarsening: do nothing                      */
/*--------------------------------------------------------------------------*/

static void real_d_coarse_restr1_3d(DOF_REAL_D_VEC *drdv,
				    RC_LIST_EL *list, int n)
{
  FUNCNAME("real_coarse_restr1_3d");
  EL      *el;
  REAL_D  *vec = NULL;
  DOF     dof_new, dof0, dof1;
  int     n0, k;

  if (n < 1) return;
  GET_DOF_VEC(vec, drdv);

  n0 = drdv->fe_space->admin->n0_dof[VERTEX];
  el = list->el_info.el;
  dof0 = el->dof[0][n0];           /* 1st endpoint of refinement edge */
  dof1 = el->dof[1][n0];           /* 2nd endpoint of refinement edge */
  dof_new = el->child[0]->dof[3][n0];  /*      newest vertex is DIM */

  for (k = 0; k < DIM_OF_WORLD; k++)
  {
    vec[dof0][k] += 0.5*vec[dof_new][k];
    vec[dof1][k] += 0.5*vec[dof_new][k];
  }
  return;
}

static const BAS_FCT     phi1_3d[N_BAS_LAG_1_3D]     = {
  phi1v0_3d, phi1v1_3d,
  phi1v2_3d, phi1v3_3d
};
static const GRD_BAS_FCT grd_phi1_3d[N_BAS_LAG_1_3D] = {
  grd_phi1v0_3d, grd_phi1v1_3d,
  grd_phi1v2_3d, grd_phi1v3_3d
};
static const D2_BAS_FCT  D2_phi1_3d[N_BAS_LAG_1_3D]  = {
  D2_phi1v0_3d, D2_phi1v1_3d,
  D2_phi1v2_3d, D2_phi1v3_3d
};

/* For each wall the mapping from the wall basis functions to the
 * local DOFs on the reference element. See also submesh.c.
 *
 * Meaning of the dimensions is the same as in the BAS_FCTS structure
 *
 * [type][orientation][wall][bas_fcts]
 */
static const int trace_mapping_lag_1_3d[2][2][N_WALLS_3D][N_BAS_LAG_1_2D] = {
  {
    { { 3, 1, 2 }, { 2, 0, 3 }, { 0, 1, 3 }, { 1, 0, 2 } }, /* t = 0, o = + */
    { { 1, 3, 2 }, { 0, 2, 3 }, { 1, 0, 3 }, { 0, 1, 2 } }  /* t = 0, o = - */
  },
  {
    { { 1, 2, 3 }, { 2, 0, 3 }, { 0, 1, 3 }, { 1, 0, 2 } }, /* t = 1, o = + */
    { { 2, 1, 3 }, { 0, 2, 3 }, { 1, 0, 3 }, { 0, 1, 2 } }  /* t = 1, o = - */
  }
};

static const BAS_FCTS lagrange1_3d = {
  "lagrange1_3d", 3, 1, N_BAS_LAG_1_3D, N_BAS_LAG_1_3D, 1,
  {1,0,0,0}, /* VERTEX,CENTER,EDGE,FACE */
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(lagrange1_3d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  phi1_3d, grd_phi1_3d, D2_phi1_3d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  &lagrange1_2d, /* trace space */
  { { { trace_mapping_lag_1_3d[0][0][0], /* t = 0, o = + */
	trace_mapping_lag_1_3d[0][0][1],
	trace_mapping_lag_1_3d[0][0][2],
	trace_mapping_lag_1_3d[0][0][3] },
      { trace_mapping_lag_1_3d[0][1][0], /* t = 0, o = - */
	trace_mapping_lag_1_3d[0][1][1],
	trace_mapping_lag_1_3d[0][1][2],
	trace_mapping_lag_1_3d[0][1][3] } },
    { { trace_mapping_lag_1_3d[1][0][0], /* t = 1, o = + */
	trace_mapping_lag_1_3d[1][0][1],
	trace_mapping_lag_1_3d[1][0][2],
	trace_mapping_lag_1_3d[1][0][3] },
      { trace_mapping_lag_1_3d[1][1][0], /* t = 1, o = - */
	trace_mapping_lag_1_3d[1][1][1],
	trace_mapping_lag_1_3d[1][1][2],
	trace_mapping_lag_1_3d[1][1][3] } } }, /* trace mapping */
  { N_BAS_LAG_1_2D,
    N_BAS_LAG_1_2D,
    N_BAS_LAG_1_2D,
    N_BAS_LAG_1_2D }, /* n_trace_bas_fcts */
  get_dof_indices1_3d,
  get_bound1_3d,
  interpol1_3d,
  interpol_d_1_3d,
  interpol_dow_1_3d,
  get_int_vec1_3d,
  get_real_vec1_3d,
  get_real_d_vec1_3d,
  (GET_REAL_VEC_D_TYPE)get_real_d_vec1_3d,
  get_uchar_vec1_3d,
  get_schar_vec1_3d,
  get_ptr_vec1_3d,
  get_real_dd_vec1_3d,
  real_refine_inter1_3d,
  NULL,
  real_coarse_restr1_3d,
  real_d_refine_inter1_3d,
  NULL,
  real_d_coarse_restr1_3d,
  (REF_INTER_FCT_D)real_d_refine_inter1_3d,
  NULL,
  (REF_INTER_FCT_D)real_d_coarse_restr1_3d,
  (void *)&lag_1_3d_data
};
