/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     disc_lagrange_1_3d.c                                           */
/*                                                                          */
/* description:  piecewise linear discontinuous 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)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#if 0
/* The inverse of the mass-matrix on the reference element. */
static const REAL inv_mass_1_3d[N_BAS_LAG_2_3D][N_BAS_LAG_2_3D] = {
  {96., -24., -24., -24.},
  {-24., 96., -24., -24.},
  {-24., -24., 96., -24.},
  {-24., -24., -24., 96.}
};
#endif

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

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

static const REAL *d_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 (*d_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 d_phi1v1_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return lambda[1];
}

static const REAL *d_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 (*d_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 d_phi1v2_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return lambda[2];
}

static const REAL *d_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 (*d_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 d_phi1v3_3d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return lambda[3];
}

static const REAL *d_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 (*d_D2_phi1v3_3d(const REAL *lambda, const BAS_FCTS *thisptr))
{
  static  const REAL_BB D2 = { { 0.0, } };

  return D2;
}

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

#undef DEF_EL_VEC_D_1_3D
#define DEF_EL_VEC_D_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_D_1_3D
#define DEFUN_GET_EL_VEC_D_1_3D(name, type, admin, body, ...)		\
  static const EL_##type##_VEC *					\
  d_get_##name##1_3d(type##_VEC_TYPE *vec, const EL *el, __VA_ARGS__)	\
  {									\
    FUNCNAME("d_get_"#name"d_1_3d");					\
    static DEF_EL_VEC_D_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[CENTER];					\
    n0   = (admin)->n0_dof[CENTER];					\
    for (ibas = 0; ibas < N_BAS_LAG_1_3D; ibas++) {			\
      dof = dofptr[node][n0+ibas];					\
      body;								\
    }									\
									\
    return vec ? NULL : rvec_space;					\
  }									\
  struct _AI_semicolon_dummy

#undef DEFUN_GET_EL_DOF_VEC_D_1_3D
#define DEFUN_GET_EL_DOF_VEC_D_1_3D(name, type, ASSIGN)			\
  DEFUN_GET_EL_VEC_D_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 *					\
  d_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 d_get__##name##_vec1_3d(vec, el, dv);			\
    } else {								\
      d_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_D_1_3D(dof_indices, DOF, admin,
			rvec[ibas] = dof,
			const DOF_ADMIN *admin, const BAS_FCTS *thisptr);

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

  TEST_FLAG(FILL_BOUND, el_info);

  for (i = 0; i < N_BAS_LAG_1_3D; i++) {
    BNDRY_FLAGS_INIT(rvec[i]);
    BNDRY_FLAGS_SET(rvec[i], el_info->face_bound[0]);
  }

  return vec ? NULL : rvec_space;
}

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

DEFUN_GET_EL_DOF_VEC_D_1_3D(int, INT, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_3D(real, REAL, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_3D(real_d, REAL_D, COPY_DOW);

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

DEFUN_GET_EL_DOF_VEC_D_1_3D(schar, SCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_3D(uchar, UCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_3D(ptr, PTR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_3D(real_dd, REAL_DD, _AI_MCOPY_DOW);

/*--------------------------------------------------------------------*/
/*--- function for local interpolaton of scalar functions          ---*/
/*--------------------------------------------------------------------*/

GENERATE_INTERPOL(d_, 1, 3, N_BAS_LAG_1_3D);

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

GENERATE_INTERPOL_DOW(d_, 1, 3, N_BAS_LAG_1_3D);

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

static void d_real_refine_inter1_3d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_refine_inter1_3d");
  EL      *el, *child;
  REAL    *vec = NULL, avg;
  DOF     dofc;
  U_CHAR  type;
  int     i, n0, node;

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

  n0 = drv->fe_space->admin->n0_dof[CENTER];
  node = drv->fe_space->admin->mesh->node[CENTER];

  for (i = 0; i < n; i++)
  {
    el = list[i].el_info.el;
    avg = 0.5*(vec[el->dof[node][n0+0]] + vec[el->dof[node][n0+1]]);

    /* --- child 0 --- */
    child = el->child[0];

    dofc = child->dof[node][n0+0];
    vec[dofc] = vec[el->dof[node][n0+0]];

    dofc = child->dof[node][n0+1];
    vec[dofc] = vec[el->dof[node][n0+2]];

    dofc = child->dof[node][n0+2];
    vec[dofc] = vec[el->dof[node][n0+3]];

    dofc = child->dof[node][n0+3];       /*    newest vertex is DIM */
    vec[dofc] = avg;

    /* --- child 1 --- */
    child = el->child[1];

    dofc = child->dof[node][n0+0];
    vec[dofc] = vec[el->dof[node][n0+1]];

#if NEIGH_IN_EL
    type = list[i].el_info.el->el_type;
#else
    type = list[i].el_info.el_type;
#endif

    if (type == 0)
    {
      dofc = child->dof[node][n0+1];
      vec[dofc] = vec[el->dof[node][n0+3]];

      dofc = child->dof[node][n0+2];
      vec[dofc] = vec[el->dof[node][n0+2]];
    }
    else
    {
      dofc = child->dof[node][n0+1];
      vec[dofc] = vec[el->dof[node][n0+2]];

      dofc = child->dof[node][n0+2];
      vec[dofc] = vec[el->dof[node][n0+3]];
    }

    dofc = child->dof[node][n0+3];       /*    newest vertex is DIM */
    vec[dofc] = avg;
  }

  return;
}

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

static void d_real_coarse_inter1_3d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_coarse_inter1_3d");
  EL      *el, **child;
  REAL    *vec = NULL;
  U_CHAR  type;
  int     i, n0, node;

  if (n < 1) return;
  GET_DOF_VEC(vec, drv);
  n0 = drv->fe_space->admin->n0_dof[CENTER];
  node = drv->fe_space->admin->mesh->node[CENTER];

  for (i = 0; i < n; i++)
  {
    el = list[i].el_info.el;
    child = el->child;

#if NEIGH_IN_EL
    type = list[i].el_info.el->el_type;
#else
    type = list[i].el_info.el_type;
#endif

    if (type == 0)
    {
      vec[el->dof[node][n0+2]] = 0.5*
	(vec[child[0]->dof[node][n0+1]]+vec[child[1]->dof[node][n0+2]]);
      vec[el->dof[node][n0+3]] = 0.5*
	(vec[child[0]->dof[node][n0+2]]+vec[child[1]->dof[node][n0+1]]);
    }
    else
    {
      vec[el->dof[node][n0+2]] = 0.5*
	(vec[child[0]->dof[node][n0+1]]+vec[child[1]->dof[node][n0+1]]);
      vec[el->dof[node][n0+3]] = 0.5*
	(vec[child[0]->dof[node][n0+2]]+vec[child[1]->dof[node][n0+2]]);
    }
  }

  return;
}

static const BAS_FCT      d_phi1_3d[N_BAS_LAG_1_3D]     = {
  d_phi1v0_3d, d_phi1v1_3d, d_phi1v2_3d,
  d_phi1v3_3d
};
static const GRD_BAS_FCT  d_grd_phi1_3d[N_BAS_LAG_1_3D] = {
  d_grd_phi1v0_3d, d_grd_phi1v1_3d,
  d_grd_phi1v2_3d, d_grd_phi1v3_3d}
  ;
static const D2_BAS_FCT   d_D2_phi1_3d[N_BAS_LAG_1_3D]  = {
  d_D2_phi1v0_3d, d_D2_phi1v1_3d,
  d_D2_phi1v2_3d, d_D2_phi1v3_3d
};

static const BAS_FCTS disc_lagrange1_3d = {
  "disc_lagrange1_3d", 3, 1, N_BAS_LAG_1_3D, N_BAS_LAG_1_3D, 1,
  {0, 4, 0, 0},
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(disc_lagrange1_3d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  d_phi1_3d, d_grd_phi1_3d, d_D2_phi1_3d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  &disc_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 */
  d_get_dof_indices1_3d,
  d_get_bound1_3d,
  d_interpol1_3d,
  d_interpol_d_1_3d,
  d_interpol_dow_1_3d,
  d_get_int_vec1_3d,
  d_get_real_vec1_3d,
  d_get_real_d_vec1_3d,
  (GET_REAL_VEC_D_TYPE)d_get_real_d_vec1_3d,
  d_get_uchar_vec1_3d,
  d_get_schar_vec1_3d,
  d_get_ptr_vec1_3d,
  d_get_real_dd_vec1_3d,
  d_real_refine_inter1_3d,
  d_real_coarse_inter1_3d,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  (void *)&lag_1_3d_data
};
