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

#if 0
/* The inverse of the mass-matrix on the reference element. */
static const REAL inv_mass_1_2d[N_BAS_LAG_1_2D][N_BAS_LAG_1_2D] = {
  {18., -6., -6.},
  {-6., 18., -6.},
  {-6., -6., 18.}
};
#endif
   
/*--------------------------------------------------------------------------*/
/*  basisfunction at vertex 0                                               */
/*--------------------------------------------------------------------------*/

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

static const REAL *d_grd_phi1v0_2d(const REAL_B lambda, const BAS_FCTS *thisptr )
{
  static const REAL_B grd = INIT_BARY_2D(1.0, 0.0, 0.0);

  return grd;
}

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

  return D2;
}

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

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

static const REAL *d_grd_phi1v1_2d(const REAL_B lambda, const BAS_FCTS *thisptr )
{
  static const REAL_B grd = INIT_BARY_2D(0.0, 1.0, 0.0);

  return grd;
}

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

  return D2;
}

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

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

static const REAL *d_grd_phi1v2_2d(const REAL_B lambda, const BAS_FCTS *thisptr )
{
  static const REAL_B grd = INIT_BARY_2D(0.0, 0.0, 1.0);

  return grd;
}

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

  return D2;
}

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

#undef DEF_EL_VEC_D_1_2D
#define DEF_EL_VEC_D_1_2D(type, name)				\
  DEF_EL_VEC_CONST(type, name, N_BAS_LAG_1_2D, N_BAS_LAG_1_2D)

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

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

static const EL_BNDRY_VEC *d_get_bound1_2d(BNDRY_FLAGS *vec,
					   const EL_INFO *el_info,
					   const BAS_FCTS *thisptr)
{
  FUNCNAME("d_get_bound1_2d");
  static DEF_EL_VEC_D_1_2D(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_2D; i++) {
    BNDRY_FLAGS_INIT(rvec[i]);
    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_1_2D(int, INT, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_2D(real, REAL, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_2D(real_d, REAL_D, COPY_DOW);

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

DEFUN_GET_EL_DOF_VEC_D_1_2D(schar, SCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_2D(uchar, UCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_2D(ptr, PTR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_1_2D(real_dd, REAL_DD, _AI_MCOPY_DOW);

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

GENERATE_INTERPOL(d_, 1, 2, N_BAS_LAG_1_2D);

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

GENERATE_INTERPOL_DOW(d_, 1, 2, N_BAS_LAG_1_2D);

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

static void d_real_refine_inter1_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_refine_inter1_2d");
  EL      *el, *child;
  REAL    *vec = NULL, avg;
  DOF     dofc;
  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 = el->child[0];

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

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

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

    child = el->child[1];

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

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

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

  return;
}

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

static void d_real_coarse_inter1_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("d_real_coarse_inter1_2d");
  EL      *el, **child;
  REAL    *vec = NULL, avg;
  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;

    avg = 0.5*(vec[child[0]->dof[node][n0]] + vec[child[1]->dof[node][n0+1]]);

    vec[el->dof[node][n0+0]] = vec[child[0]->dof[node][n0+1]];
    vec[el->dof[node][n0+1]] = vec[child[1]->dof[node][n0+0]];
    vec[el->dof[node][n0+2]] = avg;
  }

  return;
}

static const BAS_FCT      d_phi1_2d[N_BAS_LAG_1_2D]     = {
  d_phi1v0_2d, d_phi1v1_2d, d_phi1v2_2d
};
static const GRD_BAS_FCT  d_grd_phi1_2d[N_BAS_LAG_1_2D] = {
  d_grd_phi1v0_2d, d_grd_phi1v1_2d, d_grd_phi1v2_2d
};
static const D2_BAS_FCT   d_D2_phi1_2d[N_BAS_LAG_1_2D]  = {
  d_D2_phi1v0_2d, d_D2_phi1v1_2d, d_D2_phi1v2_2d
};

static const BAS_FCTS disc_lagrange1_2d = {
  "disc_lagrange1_2d", 2, 1, N_BAS_LAG_1_2D, N_BAS_LAG_1_2D, 1,
  {0, 3, 0, 0}, 
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(disc_lagrange1_2d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  d_phi1_2d, d_grd_phi1_2d, d_D2_phi1_2d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  &disc_lagrange1_1d, /* trace space */
  { { { trace_mapping_lag_1_2d[0],
	trace_mapping_lag_1_2d[1],
	trace_mapping_lag_1_2d[2], }, }, }, /* trace mapping */
  { N_BAS_LAG_1_1D,
    N_BAS_LAG_1_1D,
    N_BAS_LAG_1_1D, }, /* n_trace_bas_fcts */
  d_get_dof_indices1_2d,
  d_get_bound1_2d, 
  d_interpol1_2d,
  d_interpol_d_1_2d,
  d_interpol_dow_1_2d,
  d_get_int_vec1_2d,
  d_get_real_vec1_2d,
  d_get_real_d_vec1_2d,
  (GET_REAL_VEC_D_TYPE)d_get_real_d_vec1_2d,
  d_get_uchar_vec1_2d,
  d_get_schar_vec1_2d,
  d_get_ptr_vec1_2d,
  d_get_real_dd_vec1_2d,
  d_real_refine_inter1_2d,
  d_real_coarse_inter1_2d,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  (void *)&lag_1_2d_data
};
