/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     lagrange_1_2d.c                                                */
/*                                                                          */
/* description:  piecewise linear 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 bary1_2d[N_BAS_LAG_1_2D] = {
  INIT_BARY_2D(1.0, 0.0, 0.0),
  INIT_BARY_2D(0.0, 1.0, 0.0),
  INIT_BARY_2D(0.0, 0.0, 1.0)
};

static LAGRANGE_DATA lag_1_2d_data = {
  bary1_2d,
  NULL /* lumping_quad */,
};

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

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

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

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

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

  return D2;
}

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

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

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

static const EL_BNDRY_VEC *
get_bound1_2d(BNDRY_FLAGS *vec,
	      const EL_INFO *el_info, const BAS_FCTS *thisptr)
{
  FUNCNAME("get_bound1_2d");
  static DEF_EL_VEC_1_2D(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_2D; 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_2D(int, INT, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_1_2D(real, REAL, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_1_2D(real_d, REAL_D, COPY_DOW);

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

DEFUN_GET_EL_DOF_VEC_1_2D(schar, SCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_1_2D(uchar, UCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_1_2D(ptr, PTR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_1_2D(real_dd, REAL_DD, _AI_MCOPY_DOW);

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

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

GENERATE_INTERPOL_DOW(/**/, 1, 2, N_BAS_LAG_1_2D);

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

static void real_refine_inter1_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("real_refine_inter1_2d");
  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[2][n0];  /*     newest vertex is dim==2 */
  vec[dof_new] = 0.5*(vec[dof0] + vec[dof1]);

  return;
}

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

static void real_coarse_restr1_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  FUNCNAME("real_coarse_restr1_2d");
  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[2][n0];    /*   newest vertex is dim==2 */
  vec[dof0] += 0.5*vec[dof_new];
  vec[dof1] += 0.5*vec[dof_new];

  return;
}

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

  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[2][n0];  /*     newest vertex is dim==2 */
  for (j = 0; j < DIM_OF_WORLD; j++)
    vec[dof_new][j] = 0.5*(vec[dof0][j] + vec[dof1][j]);

  return;
}

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

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

  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[2][n0];  /*     newest vertex is dim==2 */
  for (j = 0; j < DIM_OF_WORLD; j++)
  {
    vec[dof0][j] += 0.5 * vec[dof_new][j];
    vec[dof1][j] += 0.5 * vec[dof_new][j];
  }

  return;
}

static const BAS_FCT      phi1_2d[N_BAS_LAG_1_2D]     = {
  phi1v0_2d, phi1v1_2d, phi1v2_2d
};
static const GRD_BAS_FCT  grd_phi1_2d[N_BAS_LAG_1_2D] = {
  grd_phi1v0_2d, grd_phi1v1_2d,
  grd_phi1v2_2d
};
static const D2_BAS_FCT   D2_phi1_2d[N_BAS_LAG_1_2D]  = {
  D2_phi1v0_2d, D2_phi1v1_2d,
  D2_phi1v2_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_1_2d[N_WALLS_2D][N_BAS_LAG_1_1D] = {
  { 1, 2 }, { 2, 0 }, { 0, 1 }
};

static const BAS_FCTS lagrange1_2d = {
  "lagrange1_2d", 2, 1, N_BAS_LAG_1_2D, N_BAS_LAG_1_2D, 1,
  {1, 0, 0, 0}, /* VERTEX, CENTER, EDGE, FACE */
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(lagrange1_2d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  phi1_2d, grd_phi1_2d, D2_phi1_2d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  &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 */
  get_dof_indices1_2d,
  get_bound1_2d,
  interpol1_2d,
  interpol_d_1_2d,
  interpol_dow_1_2d,
  get_int_vec1_2d,
  get_real_vec1_2d,
  get_real_d_vec1_2d,
  (GET_REAL_VEC_D_TYPE)get_real_d_vec1_2d,
  get_uchar_vec1_2d,
  get_schar_vec1_2d,
  get_ptr_vec1_2d,
  get_real_dd_vec1_2d,
  real_refine_inter1_2d,
  NULL,
  real_coarse_restr1_2d,
  real_d_refine_inter1_2d,
  NULL,
  real_d_coarse_restr1_2d,
  (REF_INTER_FCT_D)real_d_refine_inter1_2d,
  NULL,
  (REF_INTER_FCT_D)real_d_coarse_restr1_2d,
  (void *)&lag_1_2d_data
};
