/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     disc_lagrange_0_2d.c                                           */
/*                                                                          */
/* description:  piecewise constant discontinuous Lagrange elements in 2d   */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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 bary0_2d[N_BAS_LAG_0_2D] = {
  INIT_BARY_2D(1.0/3.0, 1.0/3.0, 1.0/3.0)
};

static LAGRANGE_DATA lag_0_2d_data = {
  bary0_2d,
  NULL /* lumping_quad */,
};

static REAL d_phi0c0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return 1.0;
}

static const REAL *d_grd_phi0c0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static const REAL_B grd = {0};

  return grd;
}

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

  return D2;
}

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

#undef DEF_EL_VEC_D_0_2D
#define DEF_EL_VEC_D_0_2D(type, name)				\
  DEF_EL_VEC_CONST(type, name, N_BAS_LAG_0_2D, N_BAS_LAG_0_2D)

#undef DEFUN_GET_EL_VEC_D_0_2D
#define DEFUN_GET_EL_VEC_D_0_2D(name, type, admin, body, ...)		\
  static const EL_##type##_VEC *					\
  d_get_##name##0_2d(type##_VEC_TYPE *vec, const EL *el, __VA_ARGS__) \
  {									\
    FUNCNAME("d_get_"#name"2_2d");					\
    static DEF_EL_VEC_D_0_2D(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];					\
    dof = dofptr[node][n0]; ibas = 0;					\
    body;								\
									\
    return vec ? NULL : rvec_space;					\
  }									\
  struct _AI_semicolon_dummy

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

static const EL_BNDRY_VEC *
d_get_bound0_2d(BNDRY_FLAGS *vec,
		const EL_INFO *el_info, const BAS_FCTS *thisptr)
{
  static DEF_EL_VEC_D_0_2D(BNDRY, rvec_space);
  BNDRY_FLAGS *rvec = vec ? vec : rvec_space->vec;

  BNDRY_FLAGS_INIT(rvec[0]);
  BNDRY_FLAGS_SET(rvec[0], el_info->face_bound[0]);

  return vec ? NULL : rvec_space;
}

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

DEFUN_GET_EL_DOF_VEC_D_0_2D(int, INT, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_0_2D(real, REAL, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_0_2D(real_d, REAL_D, COPY_DOW);

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

DEFUN_GET_EL_DOF_VEC_D_0_2D(schar, SCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_0_2D(uchar, UCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_0_2D(ptr, PTR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_0_2D(real_dd, REAL_DD, _AI_MCOPY_DOW);

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

GENERATE_INTERPOL(d_, 0, 2, N_BAS_LAG_0_2D);

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

GENERATE_INTERPOL_D(d_, 0, 2, N_BAS_LAG_0_2D);

GENERATE_INTERPOL_DOW(d_, 0, 2, N_BAS_LAG_0_2D);

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

static void d_real_refine_inter0_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_refine_inter0_2d");
  EL      *el;
  REAL    *vec = NULL;
  int     cdof, pdof, node0, n0, i;

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

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

    pdof = el->dof[node0][n0];
    cdof = el->child[0]->dof[node0][n0];
    vec[cdof] = vec[pdof];
    cdof = el->child[1]->dof[node0][n0];
    vec[cdof] = vec[pdof];
  }
  return;
}

static void d_real_coarse_inter0_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_coarse_inter0_2d");
  EL      *el;
  REAL    *vec = NULL;
  int     cdof0, cdof1, pdof, node0, n0, i;

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

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

    pdof = el->dof[node0][n0];
    cdof0 = el->child[0]->dof[node0][n0];
    cdof1 = el->child[1]->dof[node0][n0];

    vec[pdof] = 0.5*(vec[cdof0] + vec[cdof1]);
  }
  return;
}

static void d_real_coarse_restr0_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_coarse_restr0_2d");
  EL      *el;
  REAL    *vec = NULL;
  int     cdof0, cdof1, pdof, node0, n0, i;

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

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

    pdof = el->dof[node0][n0];
    cdof0 = el->child[0]->dof[node0][n0];
    cdof1 = el->child[1]->dof[node0][n0];

    vec[pdof] = vec[cdof0] + vec[cdof1];
  }
  return;
}

static void d_real_d_refine_inter0_2d(DOF_REAL_D_VEC *drdv, RC_LIST_EL *list,
				      int n)
{
  FUNCNAME("d_real_d_refine_inter0_2d");
  EL      *el;
  REAL_D  *vec = NULL;
  int     cdof, pdof, node0, n0, i, k;

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

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

    pdof = el->dof[node0][n0];
    cdof = el->child[0]->dof[node0][n0];
    for (k = 0; k < DIM_OF_WORLD; k++)
      vec[cdof][k] = vec[pdof][k];
    cdof = el->child[1]->dof[node0][n0];
    for (k = 0; k < DIM_OF_WORLD; k++)
      vec[cdof][k] = vec[pdof][k];
  }
  return;
}

static void d_real_d_coarse_inter0_2d(DOF_REAL_D_VEC *drdv,
				      RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_coarse_inter0_2d");
  EL      *el;
  REAL_D  *vec = NULL;
  int     cdof0, cdof1, pdof, node0, n0, i, k;

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

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

    pdof = el->dof[node0][n0];
    cdof0 = el->child[0]->dof[node0][n0];
    cdof1 = el->child[1]->dof[node0][n0];

    for (k = 0; k < DIM_OF_WORLD; k++)
      vec[pdof][k] = 0.5*(vec[cdof0][k] + vec[cdof1][k]);
  }
  return;
}

static void d_real_d_coarse_restr0_2d(DOF_REAL_D_VEC *drdv,
				      RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_d_coarse_restr0_2d");
  EL      *el;
  REAL_D  *vec = NULL;
  int     cdof0, cdof1, pdof, node0, n0, i, k;

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

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

    pdof = el->dof[node0][n0];
    cdof0 = el->child[0]->dof[node0][n0];
    cdof1 = el->child[1]->dof[node0][n0];

    for (k = 0; k < DIM_OF_WORLD; k++)
      vec[pdof] [k]= vec[cdof0][k] + vec[cdof1][k];
  }
  return;
}

static const BAS_FCT      d_phi0_2d[N_BAS_LAG_0_2D]     = {d_phi0c0_2d};
static const GRD_BAS_FCT  d_grd_phi0_2d[N_BAS_LAG_0_2D] = {d_grd_phi0c0_2d};
static const D2_BAS_FCT   d_D2_phi0_2d[N_BAS_LAG_0_2D]  = {d_D2_phi0c0_2d};

/* For each wall the mapping from the wall basis functions to the
 * local DOFs on the reference element. See also submesh.c.
 */
static const int trace_mapping_lag_0_2d[N_BAS_LAG_0_1D] = { 0 };

static const BAS_FCTS disc_lagrange0_2d = {
  "disc_lagrange0_2d", 2, 1, N_BAS_LAG_0_2D, N_BAS_LAG_0_2D, 0,
  {0, 1, 0, 0},
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(disc_lagrange0_2d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  d_phi0_2d, d_grd_phi0_2d, d_D2_phi0_2d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  &disc_lagrange0_1d, /* trace space */
  { { { trace_mapping_lag_0_2d,
	trace_mapping_lag_0_2d,
	trace_mapping_lag_0_2d, }, }, }, /* trace mapping */
  { N_BAS_LAG_0_1D,
    N_BAS_LAG_0_1D,
    N_BAS_LAG_0_1D, }, /* n_trace_bas_fcts */
  d_get_dof_indices0_2d,
  d_get_bound0_2d,
  d_interpol0_2d,
  d_interpol_d_0_2d,
  d_interpol_dow_0_2d,
  d_get_int_vec0_2d,
  d_get_real_vec0_2d,
  d_get_real_d_vec0_2d,
  (GET_REAL_VEC_D_TYPE)d_get_real_d_vec0_2d,
  d_get_uchar_vec0_2d,
  d_get_schar_vec0_2d,
  d_get_ptr_vec0_2d,
  d_get_real_dd_vec0_2d,
  d_real_refine_inter0_2d,
  d_real_coarse_inter0_2d,
  d_real_coarse_restr0_2d,
  d_real_d_refine_inter0_2d,
  d_real_d_coarse_inter0_2d,
  d_real_d_coarse_restr0_2d,
  (REF_INTER_FCT_D)d_real_d_refine_inter0_2d,
  (REF_INTER_FCT_D)d_real_d_coarse_inter0_2d,
  (REF_INTER_FCT_D)d_real_d_coarse_restr0_2d,
  (void *)&lag_0_2d_data,
};
