/*******************************************************************************
 * ALBERTA:  an Adaptive multi Level finite element toolbox using
 *           Bisectioning refinement and Error control by Residual
 *           Techniques for scientific Applications
 *
 * file:     disc-ortho-poly.c
 *
 * description: Orthogonal discontinuous polynomials of degree 1 and 2.
 *
 *******************************************************************************
 *
 *  authors:   Claus-Justus Heine
 *             Abteilung fuer Angewandte Mathematik
 *             Albert-Ludwigs-Universitaet Freiburg
 *             Hermann-Herder-Str. 10
 *             D-79104 Freiburg im Breisgau, Germany
 *
 *  http://www.alberta-fem.de
 *
 *  (c) by C.-J. Heine (2008)
 *
 ******************************************************************************/

#include "alberta.h"

#define COMPILED_CONSTANTS 0

#if COMPILED_CONSTANTS
# define WEIGHT_V  (2.0*sqrt(6.0))
# define WEIGHT_E  (60.0/sqrt(35.0-10.0*sqrt(10.0)))
#else /* computed */
# define WEIGHT_V 4.898979485566356196394568149411782783932L
# define WEIGHT_E 32.64911064067351732799557417773087413491L
#endif

static ORTHO_DATA d_ortho_1_2d_data;
static ORTHO_DATA d_ortho_2_2d_data;

/* p.w. linear orthogonal polynomials */
static inline REAL d_ortho_phi_v_2d(const REAL_B lambda, int v)
{
  return WEIGHT_V * (lambda[v] - 0.5);
}

static inline const REAL *
d_ortho_grd_phi_v_2d(REAL_B result, const REAL_B lambda, int v, bool doinit)
{
  if (doinit) {
    SET_BAR(2 /* dim */, 0.0, result);
  }

  result[v] = WEIGHT_V;

  return result;
}

static REAL d_ortho_phi_v0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_v_2d(lambda, 0);
}

static REAL d_ortho_phi_v1_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_v_2d(lambda, 1);
}

static REAL d_ortho_phi_v2_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_v_2d(lambda, 2);
}

static const REAL *
d_ortho_grd_phi_v0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_v_2d(grd, lambda, 0, false);
}

static const REAL *
d_ortho_grd_phi_v1_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_v_2d(grd, lambda, 1, false);
}

static const REAL *
d_ortho_grd_phi_v2_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_v_2d(grd, lambda, 2, false);
}

static inline const REAL_B *d_ortho_D2_phi_v_2d(REAL_BB result,
						const REAL_B lambda, int v,
						bool doinit)
{
  if (doinit) {
    MSET_BAR(2 /* dim */, 0.0, result);
  }
  
  return (const REAL_B *)result;
}

static const REAL_B *
d_ortho_D2_phi_v0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  return d_ortho_D2_phi_v_2d(D2, lambda, 0, false);
}

static const REAL_B *
d_ortho_D2_phi_v1_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  return d_ortho_D2_phi_v_2d(D2, lambda, 1, false);
}

static const REAL_B *
d_ortho_D2_phi_v2_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;
  
  return d_ortho_D2_phi_v_2d(D2, lambda, 2, false);
}

/*******************************************************************************
 *
 * p.w. quadratic orthogonal add-on
 *
 ******************************************************************************/
#if COMPILED_CONSTANTS
# define B (-3.0/2.0+0.5*sqrt(10.0)) /* (sqrt(46.0) / 120.0) */
# define D (sqrt(15.0) / 60.0)
# define E (-sqrt(6.0) / 24.0 + sqrt(15.0) / 30.0)
#else /* computed */
# define B 0.081138830084189665999446772216359266860L
# define D 0.06454972243679028141965442332970666018056L
# define E 0.0270373722576148087477553435466678456958L
#endif

static inline REAL d_ortho_phi_e_2d(const REAL_B lambda, int e)
{
  int i1 = (e + 1) % N_VERTICES_2D, i2 = (e + 2) % N_VERTICES_2D;
  
  return
    WEIGHT_E
    *
    (lambda[i1]*lambda[i2]
     +
     B*lambda[e]*(lambda[i1]+lambda[i2])
     +
     D*d_ortho_phi_v_2d(lambda, e)
     +
     E*(d_ortho_phi_v_2d(lambda, i1) + d_ortho_phi_v_2d(lambda, i2)));
}
    
static inline const REAL *
d_ortho_grd_phi_e_2d(REAL_B grd, const REAL_B lambda, int e)
{
  REAL_B tmp;
  int i1 = (e + 1) % N_VERTICES_2D, i2 = (e + 2) % N_VERTICES_2D;

  grd[e]  = B * (lambda[i1] + lambda[i2]);
  grd[i1] = lambda[i2] + B * lambda[e];
  grd[i2] = lambda[i1] + B * lambda[e];

  AXPY_BAR(2 /* dim */, D, d_ortho_grd_phi_v_2d(tmp, lambda, e, true), grd);
  AXPY_BAR(2 /* dim */, E, d_ortho_grd_phi_v_2d(tmp, lambda, i1, true), grd);
  AXPY_BAR(2 /* dim */, E, d_ortho_grd_phi_v_2d(tmp, lambda, i2, true), grd);

  SCAL_BAR(2 /* dim */, WEIGHT_E, grd);
  
  return grd;
}

static inline const REAL_B *
d_ortho_D2_phi_e_2d(REAL_BB D2, const REAL_B lambda, int e)
{
  REAL_BB tmp = { { 0.0, } };
  int i1 = (e + 1) % N_VERTICES_2D, i2 = (e + 2) % N_VERTICES_2D;

  D2[e][i1]  = D2[i1][e] = B;
  D2[e][i2]  = D2[i2][e] = B;
  D2[i1][i2] = 1.0;
  D2[i2][i1] = 1.0;

  MAXPY_BAR(2 /* dim */, D, d_ortho_D2_phi_v_2d(tmp, lambda, e, true), D2);
  MAXPY_BAR(2 /* dim */, E, d_ortho_D2_phi_v_2d(tmp, lambda, i1, true), D2);
  MAXPY_BAR(2 /* dim */, E, d_ortho_D2_phi_v_2d(tmp, lambda, i2, true), D2);

  MSCAL_BAR(2 /* dim */, WEIGHT_E, D2);
  
  return (const REAL_B *)D2;
}

#undef B
#undef D
#undef E
#undef WEIGHT_E
#undef WEIGHT_V

static REAL d_ortho_phi_e0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_e_2d(lambda, 0);
}

static REAL d_ortho_phi_e1_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_e_2d(lambda, 1);
}

static REAL d_ortho_phi_e2_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  return d_ortho_phi_e_2d(lambda, 2);
}

static const REAL *
d_ortho_grd_phi_e0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_e_2d(grd, lambda, 0);
}

static const REAL *
d_ortho_grd_phi_e1_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_e_2d(grd, lambda, 1);
}

static const REAL *
d_ortho_grd_phi_e2_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_B grd;

  return d_ortho_grd_phi_e_2d(grd, lambda, 2);
}

static const REAL_B *
d_ortho_D2_phi_e0_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  return d_ortho_D2_phi_e_2d(D2, lambda, 0);
}

static const REAL_B *
d_ortho_D2_phi_e1_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  return d_ortho_D2_phi_e_2d(D2, lambda, 1);
}

static const REAL_B *
d_ortho_D2_phi_e2_2d(const REAL_B lambda, const BAS_FCTS *thisptr)
{
  static REAL_BB D2;

  return d_ortho_D2_phi_e_2d(D2, lambda, 2);
}

static const BAS_FCT d_ortho_phi_2d[N_BAS_LAG_2_2D] = {
  d_ortho_phi_v0_2d, d_ortho_phi_v1_2d, d_ortho_phi_v2_2d,
  d_ortho_phi_e0_2d, d_ortho_phi_e1_2d, d_ortho_phi_e2_2d
};

static const GRD_BAS_FCT d_ortho_grd_phi_2d[N_BAS_LAG_2_2D] = {
  d_ortho_grd_phi_v0_2d, d_ortho_grd_phi_v1_2d, d_ortho_grd_phi_v2_2d,
  d_ortho_grd_phi_e0_2d, d_ortho_grd_phi_e1_2d, d_ortho_grd_phi_e2_2d
};

static const D2_BAS_FCT d_ortho_D2_phi_2d[N_BAS_LAG_2_2D] = {
  d_ortho_D2_phi_v0_2d, d_ortho_D2_phi_v1_2d, d_ortho_D2_phi_v2_2d,
  d_ortho_D2_phi_e0_2d, d_ortho_D2_phi_e1_2d, d_ortho_D2_phi_e2_2d
};

/******************************************************************************
 *
 * Degree 1
 *
 ******************************************************************************/

#undef DEF_EL_VEC_D_ORTHO_1_2D
#define DEF_EL_VEC_D_ORTHO_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_ORTHO_1_2D
#define DEFUN_GET_EL_VEC_D_ORTHO_1_2D(name, type, admin, body, ...)	\
  static const EL_##type##_VEC *					\
  d_ortho_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_ORTHO_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_ORTHO_1_2D
#define DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_2D(name, type, ASSIGN)		\
  DEFUN_GET_EL_VEC_D_ORTHO_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_ortho_get_##name##_vec_1_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_ortho_get__##name##_vec_1_2d(vec, el, dv);		\
    } else {								\
      d_ortho_get__##name##_vec_1_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_ORTHO_1_2D(dof_indices, DOF, admin,
			      rvec[ibas] = dof,
			      const DOF_ADMIN *admin, const BAS_FCTS *thisptr);

static const EL_BNDRY_VEC *d_ortho_get_bound_1_2d(BNDRY_FLAGS *vec,
						  const EL_INFO *el_info,
						  const BAS_FCTS *thisptr)
{
  FUNCNAME("d_get_bound2_2d");
  static DEF_EL_VEC_D_ORTHO_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[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_ORTHO_1_2D(int, INT, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_2D(real, REAL, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_2D(real_d, REAL_D, COPY_DOW);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_2D(schar, SCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_2D(uchar, UCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_2D(ptr, PTR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_1_2D(real_dd, REAL_DD, _AI_MCOPY_DOW);

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

static void
d_ortho_real_refine_inter_1_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  DOF pdof[N_VERTICES_2D];
  DOF cdof;
  EL  *child;
  int i, j, n0, node;

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

  for (i = 0; i < n; i++) {
    EL *el = list[i].el_info.el;
    for (j = 0; j < N_VERTICES_2D; j++) {
      pdof[j] = el->dof[node][n0+j];
    }

    /* child0
     * phi0 = 1/2*phi1_c - 1/2*phi0_c;
     * phi1 = phi2_c + 1/2*(phi0_c + phi1_c);
     * phi2 = phi0_c;
     */
    child = list[i].el_info.el->child[0];

    cdof = child->dof[node][n0+0];
    drv->vec[cdof] =
      -0.5*drv->vec[pdof[0]] + 0.5*drv->vec[pdof[1]] + drv->vec[pdof[2]];

    cdof = child->dof[node][n0+1];
    drv->vec[cdof] = 0.5*drv->vec[pdof[0]] + 0.5*drv->vec[pdof[1]];
    
    cdof = child->dof[node][n0+2];
    drv->vec[cdof] = drv->vec[pdof[1]];

    /* child1
     * phi0 = phi2_c + 1/2*(phi0_c + phi1_c);
     * phi1 = 1/2*phi0_c -1/2*phi1_c;
     * phi2 = phi1_c;
     */
    child = list[i].el_info.el->child[1];

    cdof = child->dof[node][n0+0];
    drv->vec[cdof] = 0.5*drv->vec[pdof[0]] + 0.5*drv->vec[pdof[1]];
      
    cdof = child->dof[node][n0+1];
    drv->vec[cdof] =
      0.5*drv->vec[pdof[0]] - 0.5*drv->vec[pdof[1]] + drv->vec[pdof[2]];

    cdof = child->dof[node][n0+2];
    drv->vec[cdof] = drv->vec[pdof[0]];
  }
}

static void
d_ortho_real_coarse_inter_1_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  DOF pdof[N_VERTICES_2D];
  DOF cdof;
  EL  *child;
  int i, j, n0, node;

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

  for (i = 0; i < n; i++) {
    EL *el = list[i].el_info.el;
    for (j = 0; j < N_VERTICES_2D; j++) {
      pdof[j] = el->dof[node][n0+j];
      drv->vec[pdof[j]] = 0.0;
    }

    /* child0:
     * phi0.phi0_c = 1/2 (phi1_c - phi0_c).phi0_c = -1/2
     * phi0.phi1_c = ...                          = +1/2
     * phi0.phi2_c =                              = 0
     *
     * phi1.phi0_c = (phi2_c + 1/2*(phi0_c + phi1_c)).phi0_c = 1/2
     * phi1.phi1_c = (phi2_c + 1/2*(phi0_c + phi1_c)).phi1_c = 1/2
     * phi1.phi2_c = (phi2_c + 1/2*(phi0_c + phi1_c)).phi2_c = 1
     *
     * phi2.phi0_c = phi0_c.phi0_c = 1
     * phi2.phi1_c = 0
     * phi2.phi2_c = 0
     *
     * child1:
     * phi0.phi0_c = (phi2_c + 1/2*(phi0_c + phi1_c)).phi0_c = 1/2 
     * phi0.phi1_c = (phi2_c + 1/2*(phi0_c + phi1_c)).phi1_c = 1/2 
     * phi0.phi2_c = (phi2_c + 1/2*(phi0_c + phi1_c)).phi2_c = 1
     *
     * phi1.phi0_c = (1/2*phi0_c -1/2*phi1_c).phi0_c = +1/2
     * phi1.phi1_c = (1/2*phi0_c -1/2*phi1_c).phi1_c = -1/2
     * phi1.phi2_c = (1/2*phi0_c -1/2*phi1_c).phi2_c = 0
     *
     * phi2.phi0_c = phi1_c.phi0_c = 0
     * phi2.phi1_c = phi1_c.phi1_c = 1
     * phi2.phi2_c = phi1_c.phi2_c = 0
     */

    child = list[i].el_info.el->child[0];

    cdof = child->dof[node][n0+0];
    drv->vec[pdof[0]] += 0.5 * drv->vec[cdof] * (-0.5);
    drv->vec[pdof[1]] += 0.5 * drv->vec[cdof] * (+0.5);
    drv->vec[pdof[2]] += 0.5 * drv->vec[cdof] * (+1.0);
    cdof = child->dof[node][n0+1];
    drv->vec[pdof[0]] += 0.5 * drv->vec[cdof] * (+0.5);
    drv->vec[pdof[1]] += 0.5 * drv->vec[cdof] * (+0.5);    
    cdof = child->dof[node][n0+2];
    drv->vec[pdof[1]] += 0.5 * drv->vec[cdof] * (+1.0);

    child = list[i].el_info.el->child[1];
    cdof = child->dof[node][n0+0];
    drv->vec[pdof[0]] += 0.5 * drv->vec[cdof] * (+0.5);
    drv->vec[pdof[1]] += 0.5 * drv->vec[cdof] * (+0.5);
    cdof = child->dof[node][n0+1];
    drv->vec[pdof[0]] += 0.5 * drv->vec[cdof] * (+0.5);
    drv->vec[pdof[1]] += 0.5 * drv->vec[cdof] * (-0.5);
    drv->vec[pdof[2]] += 0.5 * drv->vec[cdof] * (+1.0);
    cdof = child->dof[node][n0+2];
    drv->vec[pdof[0]] += 0.5 * drv->vec[cdof] * (+1.0);
  }
}

/* Interpolation: we always use L2-interpolation over the entire
 * element, even if WALL >= 0. The quadrature degree is such that we
 * would reproduce the polynomials of the given degree.
 */
static void
d_ortho_interpol_1_2d(EL_REAL_VEC *el_vec,
		      const EL_INFO *el_info, int wall,
		      int no, const int *b_no,
		      LOC_FCT_AT_QP f, void *f_data,
		      const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      el_vec->vec[b_no[i]] = 0.0;      
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_1_2D; ib++) {
      el_vec->vec[ib] = 0.0;
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (ib = 0; ib < N_BAS_LAG_1_2D; ib++) {
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  }
}

static void
d_ortho_interpol_d_1_2d(EL_REAL_D_VEC *el_vec,
			const EL_INFO *el_info, int wall,
			int no, const int *b_no,
			LOC_FCT_D_AT_QP f, void *f_data,
			const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      SET_DOW(0.0, el_vec->vec[b_no[i]]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	AXPY_DOW( qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_1_2D; ib++) {
      SET_DOW(0.0, el_vec->vec[ib]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (ib = 0; ib < N_BAS_LAG_1_2D; ib++) {
	AXPY_DOW(qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  }
}

static void
d_ortho_interpol_dow_1_2d(EL_REAL_VEC_D *el_vec,
			  const EL_INFO *el_info, int wall,
			  int no, const int *b_no,
			  LOC_FCT_D_AT_QP f, void *f_data,
			  const BAS_FCTS *thisptr)
{
  d_ortho_interpol_d_1_2d((EL_REAL_D_VEC *)el_vec,
			  el_info, wall, no, b_no, f, f_data, thisptr);
}

/* The trace-space is degenerated: all basis functions have non-zero
 * trace on all walls. If permute the stuff cyclic s.t. we would
 * eventually have the chance to really define the trace-space as
 * degenerated set of basis functions.
 */
static const int trace_mapping_d_ortho_1_2d[N_WALLS_2D][N_BAS_LAG_1_2D] = {
  { 1, 2, 0 }, { 2, 0, 1 }, { 0, 1, 2 }
};

static const BAS_FCTS disc_ortho1_2d = {
  DISC_ORTHO_NAME(1)"_2d", 2, 1, N_BAS_LAG_1_2D, N_BAS_LAG_1_2D, 1,
  {0, N_BAS_LAG_1_2D, 0, 0},
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(disc_ortho1_2d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  d_ortho_phi_2d, d_ortho_grd_phi_2d, d_ortho_D2_phi_2d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  NULL, /* trace space */
  { { { trace_mapping_d_ortho_1_2d[0],
	trace_mapping_d_ortho_1_2d[1],
	trace_mapping_d_ortho_1_2d[2], }, }, }, /* trace mapping */
  { N_BAS_LAG_1_2D,
    N_BAS_LAG_1_2D,
    N_BAS_LAG_1_2D, }, /* n_trace_bas_fcts */
  d_ortho_get_dof_indices_1_2d,
  d_ortho_get_bound_1_2d,
  d_ortho_interpol_1_2d,
  d_ortho_interpol_d_1_2d,
  d_ortho_interpol_dow_1_2d,
  d_ortho_get_int_vec_1_2d,
  d_ortho_get_real_vec_1_2d,
  d_ortho_get_real_d_vec_1_2d,
  (GET_REAL_VEC_D_TYPE)d_ortho_get_real_d_vec_1_2d,
  d_ortho_get_uchar_vec_1_2d,
  d_ortho_get_schar_vec_1_2d,
  d_ortho_get_ptr_vec_1_2d,
  d_ortho_get_real_dd_vec_1_2d,
  d_ortho_real_refine_inter_1_2d,
  d_ortho_real_coarse_inter_1_2d,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  (void *)&d_ortho_1_2d_data
};

/******************************************************************************
 *
 * Degree 2
 *
 ******************************************************************************/

#undef DEF_EL_VEC_D_ORTHO_2_2D
#define DEF_EL_VEC_D_ORTHO_2_2D(type, name)			\
  DEF_EL_VEC_CONST(type, name, N_BAS_LAG_2_2D, N_BAS_LAG_2_2D)

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

#undef DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D
#define DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D(name, type, ASSIGN)		\
  DEFUN_GET_EL_VEC_D_ORTHO_2_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_ortho_get_##name##_vec_2_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_ortho_get__##name##_vec_2_2d(vec, el, dv);		\
    } else {								\
      d_ortho_get__##name##_vec_2_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_ORTHO_2_2D(dof_indices, DOF, admin,
			      rvec[ibas] = dof,
			      const DOF_ADMIN *admin, const BAS_FCTS *thisptr);

static const EL_BNDRY_VEC *d_ortho_get_bound_2_2d(BNDRY_FLAGS *vec,
						  const EL_INFO *el_info,
						  const BAS_FCTS *thisptr)
{
  FUNCNAME("d_get_bound2_2d");
  static DEF_EL_VEC_D_ORTHO_2_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_2_2D; 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_ORTHO_2_2D(int, INT, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D(real, REAL, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D(real_d, REAL_D, COPY_DOW);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D(schar, SCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D(uchar, UCHAR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D(ptr, PTR, COPY_EQ);

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

DEFUN_GET_EL_DOF_VEC_D_ORTHO_2_2D(real_dd, REAL_DD, _AI_MCOPY_DOW);

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

static void
d_ortho_real_refine_inter_2_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  REAL pcoeff[N_BAS_LAG_2_2D];
  EL *ch0, *ch1;
  DOF cdof;
  int i, j, n0, node;

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

  for (i = 0; i < n; i++) {
    EL *el = list[i].el_info.el;
    for (j = 0; j < N_BAS_LAG_2_2D; j++) {
      pcoeff[j] = drv->vec[el->dof[node][n0+j]];
    }

/* Define some "magic" numbers ... */
#define SQRT2  sqrt(2.0)
#define SQRT6  sqrt(6.0)
#define SQRT10 sqrt(10.0)
#define SQRT15 sqrt(15.0)

#define DENOM sqrt(-10.0*SQRT10+35.0)

#define COEFF11 (1.0/8.0*(5.0*SQRT6-2.0*SQRT15) / DENOM)
#define COEFF12 (11.0*SQRT6-6.0*SQRT15) / (8.0*DENOM)
#define COEFF13 (SQRT6-2.0*SQRT15)/(8.0*DENOM)
#define COEFF14 (SQRT6/(2.0*DENOM))

    /* linear sub-space */
    ch0 = list[i].el_info.el->child[0];
    ch1 = list[i].el_info.el->child[1];

    cdof = ch0->dof[node][n0+0];
    drv->vec[cdof] =
      0.5*(-pcoeff[0] + pcoeff[1]) + pcoeff[2] + COEFF11*(-pcoeff[3]+pcoeff[4]);
    cdof = ch1->dof[node][n0+1];
    drv->vec[cdof] =
      0.5*(+pcoeff[0] - pcoeff[1]) + pcoeff[2] + COEFF11*(+pcoeff[3]-pcoeff[4]);
    
    cdof = ch0->dof[node][n0+1];
    drv->vec[cdof] =
      0.5*(pcoeff[0]+pcoeff[1])
      +
      COEFF12*pcoeff[3] + COEFF13*pcoeff[4] - COEFF14*pcoeff[5];    
    cdof = ch1->dof[node][n0+0];
    drv->vec[cdof] =
      0.5*(pcoeff[0]+pcoeff[1])
      +
      COEFF13*pcoeff[3] + COEFF12*pcoeff[4] - COEFF14*pcoeff[5];

    cdof = ch0->dof[node][n0+2];
    drv->vec[cdof] =
      pcoeff[1]
      - COEFF13*pcoeff[3] - COEFF12*pcoeff[4] + COEFF14*pcoeff[5];    
    cdof = ch1->dof[node][n0+2];
    drv->vec[cdof] =
      pcoeff[0]
      - COEFF12*pcoeff[3] - COEFF13*pcoeff[4] + COEFF14*pcoeff[5];
    
    /* quadratic add-on */
#define DENOM2 (2.0*SQRT10-7.0)
    cdof = ch0->dof[node][n0+3];
    drv->vec[cdof] =
      0.125*(-22.0+7.0*SQRT10)/DENOM2*pcoeff[3]
      +
      0.125*(-3.0*SQRT10+10.0)/DENOM2*pcoeff[4]
      +
      0.125*(-52.0+16.0*SQRT10)/DENOM2*pcoeff[5];
    cdof = ch1->dof[node][n0+4];
    drv->vec[cdof] =
      0.125*(-22.0+7.0*SQRT10)/DENOM2*pcoeff[4]
      +
      0.125*(-3.0*SQRT10+10.0)/DENOM2*pcoeff[3]
      +
      0.125*(-52.0+16.0*SQRT10)/DENOM2*pcoeff[5];

    cdof = ch0->dof[node][n0+4];
    drv->vec[cdof] =
      1.0/40.0*(43.0*SQRT10-150.0)/DENOM2*pcoeff[3]
      +
      1.0/40.0*(-7.0*SQRT10+10.0)/DENOM2*pcoeff[4]
      +
      1.0/40.0*(-20.0+8.0*SQRT10)/DENOM2*pcoeff[5];
    cdof = ch1->dof[node][n0+3];
    drv->vec[cdof] =
      1.0/40.0*(43.0*SQRT10-150.0)/DENOM2*pcoeff[4]
      +
      1.0/40.0*(-7.0*SQRT10+10.0)/DENOM2*pcoeff[3]
      +
      1.0/40.0*(-20.0+8.0*SQRT10)/DENOM2*pcoeff[5];

    cdof = ch0->dof[node][n0+5];
    drv->vec[cdof] =
      -1.0/40.0*(13.0*SQRT10-40.0)/DENOM2*pcoeff[3]
      -1.0/40.0*(-17.0*SQRT10+80.0)/DENOM2*pcoeff[4]
      -1.0/40.0*(-12.0*SQRT10+40.0)/DENOM2*pcoeff[5];
    cdof = ch1->dof[node][n0+5];
    drv->vec[cdof] =
      -1.0/40.0*(13.0*SQRT10-40.0)/DENOM2*pcoeff[4]
      -1.0/40.0*(-17.0*SQRT10+80.0)/DENOM2*pcoeff[3]
      -1.0/40.0*(-12.0*SQRT10+40.0)/DENOM2*pcoeff[5];    
  }
}

static void
d_ortho_real_coarse_inter_2_2d(DOF_REAL_VEC *drv, RC_LIST_EL *list, int n)
{
  DOF pdof[N_EDGES_2D];
  DOF cdof;
  EL  *child;
  int i, j, n0, node;

  /* The linear sub-space restricts to the coarser grid just like in
   * the linear case.
   */
  d_ortho_real_coarse_inter_1_2d(drv, list, n);
  
  n0 = drv->fe_space->admin->n0_dof[CENTER];
  node = drv->fe_space->admin->mesh->node[CENTER];

  for (i = 0; i < n; i++) {
    EL *el = list[i].el_info.el;
    for (j = 0; j < N_EDGES_2D; j++) {
      pdof[j] = el->dof[node][n0+j+N_VERTICES_2D];
      drv->vec[pdof[j]] = 0.0;
    }
   
    child = list[i].el_info.el->child[0];

    cdof = child->dof[node][n0+0];
    drv->vec[pdof[0]] += -COEFF11 * drv->vec[cdof];
    drv->vec[pdof[1]] += +COEFF11 * drv->vec[cdof];

    cdof = child->dof[node][n0+1];
    drv->vec[pdof[0]] += +COEFF12 * drv->vec[cdof];
    drv->vec[pdof[1]] += +COEFF13 * drv->vec[cdof];
    drv->vec[pdof[2]] += -COEFF14 * drv->vec[cdof];
    
    cdof = child->dof[node][n0+2];
    drv->vec[pdof[0]] += -COEFF13 * drv->vec[cdof];
    drv->vec[pdof[1]] += -COEFF12 * drv->vec[cdof];
    drv->vec[pdof[2]] += +COEFF14 * drv->vec[cdof];
    
    cdof = child->dof[node][n0+3];
    drv->vec[pdof[0]] += 0.125*(-22.0+7.0*SQRT10)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[1]] += 0.125*(-3.0*SQRT10+10.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[2]] += 0.125*(-52.0+16.0*SQRT10)/DENOM2 * drv->vec[cdof];
    
    cdof = child->dof[node][n0+4];
    drv->vec[pdof[0]] += 1.0/40.0*(43.0*SQRT10-150.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[1]] += 1.0/40.0*(-7.0*SQRT10+10.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[2]] += 1.0/40.0*(-20.0+8.0*SQRT10)/DENOM2 * drv->vec[cdof];

    cdof = child->dof[node][n0+5];
    drv->vec[pdof[0]] += -1.0/40.0*(13.0*SQRT10-40.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[1]] += -1.0/40.0*(-17.0*SQRT10+80.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[2]] += -1.0/40.0*(-12.0*SQRT10+40.0)/DENOM2 * drv->vec[cdof];

    /* Second child*/
    child = list[i].el_info.el->child[1];

    cdof = child->dof[node][n0+1];
    drv->vec[pdof[0]] += +COEFF11 * drv->vec[cdof];
    drv->vec[pdof[1]] += -COEFF11 * drv->vec[cdof];

    cdof = child->dof[node][n0+0];
    drv->vec[pdof[0]] += +COEFF13 * drv->vec[cdof];
    drv->vec[pdof[1]] += +COEFF12 * drv->vec[cdof];
    drv->vec[pdof[2]] += -COEFF14 * drv->vec[cdof];
    
    cdof = child->dof[node][n0+2];
    drv->vec[pdof[0]] += -COEFF12 * drv->vec[cdof];
    drv->vec[pdof[1]] += -COEFF13 * drv->vec[cdof];
    drv->vec[pdof[2]] += +COEFF14 * drv->vec[cdof];
    
    cdof = child->dof[node][n0+4];
    drv->vec[pdof[1]] += 0.125*(-22.0+7.0*SQRT10)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[0]] += 0.125*(-3.0*SQRT10+10.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[2]] += 0.125*(-52.0+16.0*SQRT10)/DENOM2 * drv->vec[cdof];
    
    cdof = child->dof[node][n0+3];
    drv->vec[pdof[1]] += 1.0/40.0*(43.0*SQRT10-150.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[0]] += 1.0/40.0*(-7.0*SQRT10+10.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[2]] += 1.0/40.0*(-20.0+8.0*SQRT10)/DENOM2 * drv->vec[cdof];

    cdof = child->dof[node][n0+5];
    drv->vec[pdof[1]] += -1.0/40.0*(13.0*SQRT10-40.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[0]] += -1.0/40.0*(-17.0*SQRT10+80.0)/DENOM2 * drv->vec[cdof];
    drv->vec[pdof[2]] += -1.0/40.0*(-12.0*SQRT10+40.0)/DENOM2 * drv->vec[cdof];

    for (j = 0; j < N_EDGES_2D; j++) {
      pdof[j] = el->dof[node][n0+j+N_VERTICES_2D];
      drv->vec[pdof[j]] *= 0.5;
    }
  }
}

#undef SQRT2
#undef SQRT6
#undef SQRT10
#undef SQRT15
#undef DENOM
#undef DENOM2
#undef COEFF11
#undef COEFF12
#undef COEFF13
#undef COEFF14

/* Interpolation: we always use L2-interpolation over the entire
 * element, even if WALL >= 0. The quadrature degree is such that we
 * would reproduce the polynomials of the given degree.
 */
static void
d_ortho_interpol_2_2d(EL_REAL_VEC *el_vec,
		      const EL_INFO *el_info, int wall,
		      int no, const int *b_no,
		      LOC_FCT_AT_QP f, void *f_data,
		      const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      el_vec->vec[b_no[i]] = 0.0;      
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_2_2D; ib++) {
      el_vec->vec[ib] = 0.0;
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL value = qfast->w[iq] * f(el_info, qfast->quad, iq, f_data);
      for (ib = 0; ib < N_BAS_LAG_2_2D; ib++) {
	el_vec->vec[ib] += qfast->phi[iq][ib] * value;
      }
    }
  }
}

static void
d_ortho_interpol_d_2_2d(EL_REAL_D_VEC *el_vec,
			const EL_INFO *el_info, int wall,
			int no, const int *b_no,
			LOC_FCT_D_AT_QP f, void *f_data,
			const BAS_FCTS *thisptr)
{
  const QUAD_FAST *qfast = ((ORTHO_DATA *)thisptr->ext_data)->qfast;
  
  if (b_no) {
    int i, iq;
    
    for (i = 0; i < no; i++) {
      SET_DOW(0.0, el_vec->vec[b_no[i]]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (i = 0; i < no; i++) {
	int ib = b_no[i];
	AXPY_DOW( qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  } else {
    int iq, ib;

    for (ib = 0; ib < N_BAS_LAG_2_2D; ib++) {
      SET_DOW(0.0, el_vec->vec[ib]);
    }
    for (iq = 0; iq < qfast->n_points; iq++) {
      REAL_D value;

      f(value, el_info, qfast->quad, iq, f_data);
      SCAL_DOW(qfast->w[iq], value);
      for (ib = 0; ib < N_BAS_LAG_2_2D; ib++) {
	AXPY_DOW(qfast->phi[iq][ib], value, el_vec->vec[ib]);
      }
    }
  }
}

static void
d_ortho_interpol_dow_2_2d(EL_REAL_VEC_D *el_vec,
			  const EL_INFO *el_info, int wall,
			  int no, const int *b_no,
			  LOC_FCT_D_AT_QP f, void *f_data,
			  const BAS_FCTS *thisptr)
{
  d_ortho_interpol_d_1_2d((EL_REAL_D_VEC *)el_vec,
			  el_info, wall, no, b_no, f, f_data, thisptr);
}

/* The trace-space is degenerated: all basis functions have non-zero
 * trace on all walls. If permute the stuff cyclic s.t. we would
 * eventually have the chance to really define the trace-space as
 * degenerated set of basis functions.
 */
static const int trace_mapping_d_ortho_2_2d[N_WALLS_2D][N_BAS_LAG_2_2D] = {
  { 1, 2, 3, 0, 4, 5 }, { 2, 0, 4, 1, 5, 3 }, { 0, 1, 5, 2, 3, 4 }
};


static const BAS_FCTS disc_ortho2_2d = {
  DISC_ORTHO_NAME(2)"_2d", 2, 1, N_BAS_LAG_2_2D, N_BAS_LAG_2_2D, 2,
  {0, N_BAS_LAG_2_2D, 0, 0},
  -1, /* trace_admin */
  INIT_BFCTS_CHAIN(disc_ortho2_2d),
  INIT_ELEMENT_INITIALIZER(NULL, FILL_NOTHING), /* init_element + fill-flags */
  d_ortho_phi_2d, d_ortho_grd_phi_2d, d_ortho_D2_phi_2d,
  NULL, NULL, /* third and fourth derivatives */
  NULL, NULL, NULL, false, /* phi_d etc. */  
  /********************/
  NULL /* &disc_lagrange2_1d */, /* trace space */
  { { { trace_mapping_d_ortho_2_2d[0],
	trace_mapping_d_ortho_2_2d[1],
	trace_mapping_d_ortho_2_2d[2], }, }, }, /* trace mapping */
  { N_BAS_LAG_2_2D,
    N_BAS_LAG_2_2D,
    N_BAS_LAG_2_2D, }, /* n_trace_bas_fcts */
  d_ortho_get_dof_indices_2_2d,
  d_ortho_get_bound_2_2d,
  d_ortho_interpol_2_2d,
  d_ortho_interpol_d_2_2d,
  d_ortho_interpol_dow_2_2d,
  d_ortho_get_int_vec_2_2d,
  d_ortho_get_real_vec_2_2d,
  d_ortho_get_real_d_vec_2_2d,
  (GET_REAL_VEC_D_TYPE)d_ortho_get_real_d_vec_2_2d,
  d_ortho_get_uchar_vec_2_2d,
  d_ortho_get_schar_vec_2_2d,
  d_ortho_get_ptr_vec_2_2d,
  d_ortho_get_real_dd_vec_2_2d,
  d_ortho_real_refine_inter_2_2d,
  d_ortho_real_coarse_inter_2_2d,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  (void *)&d_ortho_2_2d_data
};

