/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/* file:     submesh_2d.c                                                   */
/*                                                                          */
/*                                                                          */
/* description: Support for master/slave meshes for master->dim == 2        */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Daniel Koester                                               */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*             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 D. Koester (2005)                                                */
/*--------------------------------------------------------------------------*/


static void get_slave_elements_rec_2d(MESH *master, MESH *slave,
				      int neigh, EL *m_el, EL *s_el)
{
  /* FUNCNAME("get_slave_elements_rec_2d"); */
  EL_INFO el_info = { NULL, };


  if (m_el->child[0]) {
    if(neigh == 0)
      get_slave_elements_rec_2d(master, slave, 2, m_el->child[1], s_el);
    else if (neigh == 1)
      get_slave_elements_rec_2d(master, slave, 2, m_el->child[0], s_el);
    else if (neigh == 2) {
      /* We MUST copy m_el->new_coord here!!! */
      if (m_el->new_coord) {
	s_el->new_coord = get_real_d(slave);
	COPY_DOW(m_el->new_coord, s_el->new_coord);
      }
      el_info.mesh = slave;
      el_info.el   = s_el;
      s_el->mark   = 1;

      AI_refine_fct_1d(&el_info, NULL);

      get_slave_elements_rec_2d(master, slave, 0,
				m_el->child[0], s_el->child[0]);
      get_slave_elements_rec_2d(master, slave, 1,
				m_el->child[1], s_el->child[1]);
    }
  }

  return;
}


static void get_slave_elements_2d(MESH *master, MESH *slave,
				  bool (*binding_method)
				  (MESH *master, MACRO_EL *el, int wall,
				   void *data), void *data)
{
  FUNCNAME("get_slave_elements_2d");
  MACRO_EL *m_mel, *s_mel;
  int       n, i;

  s_mel = slave->macro_els;
  for(n = 0; n < master->n_macro_el; n++) {
    m_mel = master->macro_els + n;

    for (i = 0; i < N_NEIGH_2D; i++)
      if (binding_method(master, m_mel, i, data)) {
	DEBUG_TEST_EXIT(s_mel,
		    "Ran out of slave macro elements... Wrong meshes?\n");

	get_slave_elements_rec_2d(master, slave,
				  i, m_mel->el, s_mel->el);

	s_mel++;
      }
  }

  return;
}


/****************************************************************************/
/* master interpolation/restriction routines.                               */
/* These call the corresponding refinement/coarsening routines for the slave*/
/* mesh patchwise.                                                          */
/****************************************************************************/

static void master_interpol_2d(DOF_PTR_VEC *m_dpv, RC_LIST_EL *rclist, int mn)
{
  FUNCNAME("master_interpol_2d");
  MESH_MEM_INFO   *m_mem_info = (MESH_MEM_INFO *)
    m_dpv->fe_space->admin->mesh->mem_info;
  int              m_n0 = m_dpv->fe_space->admin->n0_dof[EDGE];
  int              m_n = m_dpv->fe_space->admin->mesh->node[EDGE];
  EL              *m_el, *s_el;
  EL              *s_child[2], *m_child[2];
  int              s_n0, s_n, i, j, n_slaves = m_mem_info->n_slaves;
  MESH            *slave = NULL;
  DOF_PTR_VEC     *s_dpv;
  const NODE_PROJECTION *s_proj;

/****************************************************************************/
/* Retrieve the slave mesh. Rather unelegant, sorry...                      */
/****************************************************************************/
  for (j = 0; j < n_slaves; j++) {
    slave = m_mem_info->slaves[j];
    if(((MESH_MEM_INFO *)slave->mem_info)->slave_binding == m_dpv) break;
  }
  DEBUG_TEST_EXIT(j < n_slaves, "Slave mesh not found!\n");
  
  s_dpv = ((MESH_MEM_INFO *)slave->mem_info)->master_binding; 
  s_n0  = s_dpv->fe_space->admin->n0_dof[CENTER];
  s_n   = slave->node[CENTER];

/****************************************************************************/
/* Run over all patch elements. Check if any edges belong to slave elements.*/
/* If so, determine if they should be refined together with the master      */
/* elements. All necessary DOFs are set. New DOFs are set to NULL, if they   */
/* should not point to anything. THIS IS NOT JUST A SECURITY MEASURE - IT   */
/* IS ABSOLUTELY NECESSARY! At the moment, new entries in a DOF vector can  */
/* not be guaranteed to be clean.                                           */
/****************************************************************************/

  for(i = 0; i < mn; i++) {
    m_el = rclist[i].el_info.el;

    m_child[0] = m_el->child[0];
    m_child[1] = m_el->child[1];

/****************************************************************************/
/* DOF pointers of the master child elements along the bisection line are   */
/* set to NULL.                                                              */
/****************************************************************************/
    m_dpv->vec[m_child[0]->dof[m_n+1][m_n0]] = NULL;
    m_dpv->vec[m_child[1]->dof[m_n+0][m_n0]] = NULL;

    for(j = 0; j < N_WALLS_2D; j++) {
      s_el = (EL *)m_dpv->vec[m_el->dof[m_n+j][m_n0]];
    
      if(s_el && m_el==(EL *)s_dpv->vec[s_el->dof[s_n][s_n0]]) {
	EL_INFO el_info = { NULL, };

	if(j == 2) {
/****************************************************************************/
/* Refine this slave element. Take precautions to preserve the coarse DOFs  */
/* on master and slave elements, since they will be needed for coarsening.  */
/****************************************************************************/
	  s_el->mark                = MAX(s_el->mark, 1);
	  el_info.el                = s_el;
	  el_info.mesh              = slave;
	  el_info.master.el         = m_el;
	  el_info.master.opp_vertex = j;

/* Fill active projection on slave rclist for parametric meshes.            */
	  s_proj = wall_proj(&rclist[i].el_info, j);
	  if(!s_proj)
	    s_proj = wall_proj(&rclist[i].el_info, -1);
	  el_info.active_projection = s_proj;

/* DK: Also fill coordinate information on the ad-hoc el_info. This is      */
/* necessary when we have a non-parametric mesh with projection functions,  */
/* i.e. we are using the traditional "new_coord" mechanism.                 */

	  COPY_DOW(rclist[i].el_info.coord[0],
		   el_info.coord[0]);
	  COPY_DOW(rclist[i].el_info.coord[1],
		   el_info.coord[1]);

	  AI_refine_fct_1d(&el_info, NULL);

/****************************************************************************/
/* Now retrieve the new child elements and set DOF pointers.                */
/****************************************************************************/
	  s_child[0] = s_el->child[0];
	  s_child[1] = s_el->child[1];
	  
	  m_dpv->vec[m_child[0]->dof[m_n+0][m_n0]] = s_child[0];
	  m_dpv->vec[m_child[1]->dof[m_n+1][m_n0]] = s_child[1];

	  s_dpv->vec[s_child[0]->dof[s_n][s_n0]] = m_child[0];
	  s_dpv->vec[s_child[1]->dof[s_n][s_n0]] = m_child[1];

	}
	else if (s_el) {
/****************************************************************************/
/* If we are not on a refinement edge, set the correct master child         */
/* pointer to point to the slave element. Edge 2 of child[1-j] should point */
/* to the current slave element, and vice versa.                            */
/****************************************************************************/
	  m_dpv->vec[m_child[1-j]->dof[m_n+2][m_n0]] = s_el;

	  s_dpv->vec[s_el->dof[s_n][s_n0]] = m_child[1-j];
	}
      }
/****************************************************************************/
/* If there is no slave element on this refinement edge, zero the master    */
/* child pointers.                                                          */
/****************************************************************************/
      else if (!s_el) {
	if(j == 2) {
	  m_dpv->vec[m_child[0]->dof[m_n+0][m_n0]] = NULL;
	  m_dpv->vec[m_child[1]->dof[m_n+1][m_n0]] = NULL;
	}
	else
	  m_dpv->vec[m_child[1-j]->dof[m_n+2][m_n0]] = NULL;
      }
    }
  }

  return;
}

static void master_restrict_2d(DOF_PTR_VEC *m_dpv, RC_LIST_EL *rclist, int mn)
{
  FUNCNAME("master_restrict_2d");
  MESH_MEM_INFO   *m_mem_info = (MESH_MEM_INFO *)
    m_dpv->fe_space->admin->mesh->mem_info;
  int              m_n0 = m_dpv->fe_space->admin->n0_dof[EDGE];
  int              m_n = m_dpv->fe_space->admin->mesh->node[EDGE];
  EL              *m_el, *s_el, *cm_el;
  EL              *s_child[2], *m_child[2];
  int              s_n0, s_n, i, j, n_slaves = m_mem_info->n_slaves;
  MESH            *slave = NULL;
  DOF_PTR_VEC     *s_dpv;

/****************************************************************************/
/* Retrieve the slave mesh. Rather unelegant, sorry...                      */
/****************************************************************************/
  for (j = 0; j < n_slaves; j++) {
    slave = m_mem_info->slaves[j];
    if(((MESH_MEM_INFO *)slave->mem_info)->slave_binding == m_dpv) break;
  }
  DEBUG_TEST_EXIT(j < n_slaves, "Slave mesh not found!\n");
  
  s_dpv = ((MESH_MEM_INFO *)slave->mem_info)->master_binding; 
  s_n0  = s_dpv->fe_space->admin->n0_dof[CENTER];
  s_n   = slave->node[CENTER];

/****************************************************************************/
/* Run over all patch elements. Check if any edges belong to slave elements.*/
/* If so, determine if they should be coarsened together with the master    */
/* elements.                                                                */
/****************************************************************************/

  for(i = 0; i < mn; i++) {
    m_el = rclist[i].el_info.el;

    m_child[0] = m_el->child[0];
    m_child[1] = m_el->child[1];

    for(j = 0; j < N_WALLS_2D; j++) {
      s_el  = (EL *)m_dpv->vec[m_el->dof[m_n+j][m_n0]];

      if(s_el) {
	cm_el = (EL *)s_dpv->vec[s_el->dof[s_n][s_n0]];

	if (cm_el==m_child[0] || cm_el==m_child[1] || cm_el==m_el) {
/****************************************************************************/
/* Set the slave pointer to point to the parent master element.             */
/****************************************************************************/
	  s_dpv->vec[s_el->dof[s_n][s_n0]] = m_el;

	  if (j==2) {
/****************************************************************************/
/* Coarsen this slave element. Old parent DOFs still exist due to the       */
/* preserve_coarse_dofs-setting during refinement.                          */
/****************************************************************************/
	    EL_INFO el_info = { NULL, };

	    s_child[0] = s_el->child[0];
	    s_child[1] = s_el->child[1];

	    s_child[0]->mark = -1;
	    s_child[1]->mark = -1;
	    
	    el_info.mesh = slave;
	    el_info.el   = s_el;
	    AI_coarse_fct_1d(&el_info, NULL);
	  }	  
	}
      }
    }
  }

  return;
}


static void join_elements_recursive_2d(const MESH *master,
				       const MESH *slave,
				       const DOF_ADMIN *m_admin,
				       const DOF_ADMIN *s_admin,
				       const DOF_PTR_VEC *m_dpv,
				       const DOF_PTR_VEC *s_dpv,
				       const int subsimplex,
				       const EL *m_el,
				       const EL *s_el)
{
  FUNCNAME("join_elements_recursive_2d");

  s_dpv->vec[s_el->dof[slave->node[CENTER]]
	     [s_admin->n0_dof[CENTER]]] = (void *)m_el;

  m_dpv->vec[m_el->dof[master->node[EDGE] + subsimplex]
	     [m_admin->n0_dof[EDGE]]] = (void *)s_el;

  if (m_el->child[0]) {
    if (subsimplex == 2) {   /* this is the refinement edge */
      DEBUG_TEST_EXIT(s_el->child[0],
		  "Could not find slave children!\n");
      
      join_elements_recursive_2d(master, slave, m_admin, s_admin,
				 m_dpv, s_dpv,
				 0, m_el->child[0], s_el->child[0]);
      join_elements_recursive_2d(master, slave, m_admin, s_admin,
				 m_dpv, s_dpv,
				 1, m_el->child[1], s_el->child[1]);
    }
    else
      join_elements_recursive_2d(master, slave, m_admin, s_admin,
				 m_dpv, s_dpv,
				 2, m_el->child[1 - subsimplex], s_el);
  }

  return;
}

/* If we have a periodic mesh then we need to transfer the wall
 * transformations to the slave mesh. Of course, the slave can only
 * inherit transformations which make sense, i.e. do not map vertices
 * away from the child.
 *
 * Wall transformations must be set before calling
 * compute_neigh_fast()
 *
 * We may produce duplicates here, but that does not matter, its just
 * a slight performance hit.
 */
static void transfer_wall_trafos_2d(MESH *master,
				    MACRO_DATA *sdata,
				    int *vert_ind)
{
  int (*master_wall_vtx_trafos)[N_VERTICES(DIM_MAX-1)][2];
  int (*slave_wall_vtx_trafos)[N_VERTICES(DIM_MAX-1)][2] = NULL;
  int nwt;
  int nswt, v, i;

  nwt = _AI_compute_macro_wall_trafos(master, &master_wall_vtx_trafos);

  nswt = 0;
  for (i = 0; i < nwt; i++) {
    for (v = 0; v < N_VERTICES_1D; v++) {
      if (vert_ind[master_wall_vtx_trafos[i][v][0]] >= 0 &&
	  vert_ind[master_wall_vtx_trafos[i][v][1]] >= 0) {
	++nswt;
      }
    }
  }

  if (nswt > 0) {
    slave_wall_vtx_trafos = (int (*)[N_VERTICES(DIM_MAX-1)][2])
      MEM_ALLOC(nswt*sizeof(*slave_wall_vtx_trafos), char);

    nswt = 0;
    for (i = 0; i < nwt; i++) {
      for (v = 0; v < N_VERTICES_1D; v++) {
	int from = vert_ind[master_wall_vtx_trafos[i][v][0]];
	int to   = vert_ind[master_wall_vtx_trafos[i][v][1]];
	  
	if (from >= 0 && to >= 0) {
	  slave_wall_vtx_trafos[nswt][0][0] = from;
	  slave_wall_vtx_trafos[nswt][0][1] = to;
	  ++nswt;
	}
      }
    }
    sdata->n_wall_vtx_trafos = nswt;
    sdata->wall_vtx_trafos   = slave_wall_vtx_trafos;

    sdata->el_wall_vtx_trafos =
      MEM_ALLOC(sdata->n_macro_elements*N_WALLS_1D, int);
    _AI_compute_element_wall_transformations(sdata);
  }

  MEM_FREE(master_wall_vtx_trafos, nwt*sizeof(*master_wall_vtx_trafos), char);
}

/****************************************************************************/
/* get_submesh_2d(master, name, binding_method): returns a 1D submesh of    */
/* master based on the information given by the user routine                */
/* binding_method().                                                        */
/****************************************************************************/

static MESH *
get_submesh_2d(MESH *master, const char *name,
	       bool (*binding_method)(MESH *master,
				      MACRO_EL *el, int edge,
				      void *data),
	       void *data)
{
  FUNCNAME("get_submesh_2d");
  MACRO_DATA       s_data = { 0 /* dim */, };
  MESH_MEM_INFO   *m_info, *s_info;
  MESH            *slave = NULL;
  const FE_SPACE  *slave_space, *master_space;
  DOF_PTR_VEC     *slave_to_master_binding;
  DOF_PTR_VEC     *master_to_slave_binding;
  int              s_n_dof[N_NODE_TYPES] = { 0, };
  int              m_n_dof[N_NODE_TYPES] = { 0, };
  MACRO_EL        *m_mel, *s_mel;
  const DOF_ADMIN *m_admin, *s_admin;
  int              i, j, k, n, ne = 0, nv = 0, *vert_ind = NULL, index;
  char             new_name[1024];

  m_info = ((MESH_MEM_INFO *)master->mem_info);

/****************************************************************************/
/* Count all needed vertices and elements.                                  */
/****************************************************************************/

  s_data.dim = 1;
  s_data.coords = MEM_ALLOC(master->n_vertices, REAL_D);  /* resized later! */

  vert_ind = MEM_ALLOC(master->n_vertices, int);
  for (i = 0; i < master->n_vertices; i++)
    vert_ind[i] = -1;
  
  for (n = 0; n < master->n_macro_el; n++) {
    m_mel = master->macro_els + n;

    for (i = 0; i < N_WALLS_2D; i++)
      if (binding_method(master, m_mel, i, data)) {
	ne++;
	
	for (j = 0; j < N_VERTICES_2D; j++)
	  if (j != i) {
/* Make use of the slave->mem_info->coords vector to get vertex indices.    */
	    index = m_mel->coord[j] - &m_info->coords[0];
	    
	    if (vert_ind[index] < 0) {
	      vert_ind[index] = nv;    
	      for(k = 0; k < DIM_OF_WORLD; k++)
		s_data.coords[nv][k] = m_info->coords[index][k];
	      
	      nv++;
	    }
	  }
      }
  }

/****************************************************************************/
/* Allocate the needed amount of macro elements and coordinates.            */
/* Fill element and coordinate information.                                 */
/****************************************************************************/

  TEST_EXIT(nv,"Bad mesh: no vertices counted!\n");
  TEST_EXIT(ne,"Bad mesh: no elements counted!\n");

  s_data.n_total_vertices = nv;
  s_data.n_macro_elements = ne;
  s_data.coords = MEM_REALLOC(s_data.coords, master->n_vertices,
			      nv, REAL_D);
  s_data.mel_vertices = MEM_ALLOC(ne * N_NEIGH_1D, int);
  ne = 0;

  for (n = 0; n < master->n_macro_el; n++) {
    m_mel = master->macro_els + n;

    for (i = 0; i < N_WALLS_2D; i++)
      if (binding_method(master, m_mel, i, data)) {
	for (j = 0; j < N_VERTICES_2D; j++)
	  if (j != i) {
	    index = m_mel->coord[j] - &m_info->coords[0];
	    nv = vert_ind[index];
	    
	    s_data.mel_vertices[N_VERTICES_1D * ne + (j + 2 - i) % 3] = nv;  
	  }
	
	ne++;
      }
  }

  /* Wall transformations must be set before calling
   * compute_neigh_fast(). Slaves of periodic meshes need not be
   * periodic.
   */
  if (master->is_periodic) {
    transfer_wall_trafos_2d(master, &s_data, vert_ind);
  }

  compute_neigh_fast(&s_data);  

  /* Assign all boundaries a no-boundary type (also allocates .boundary) */
  default_boundary(&s_data, INTERIOR, true);

  /* Now correct the boundary values if the master vertex has nonzero type. */
  ne = 0;
  for (ne = n = 0; n < master->n_macro_el; n++) {
    m_mel = master->macro_els + n;

    for (i = 0; i < N_WALLS_2D; i++)
      if (binding_method(master, m_mel, i, data)) {
	for (j = 0; j < N_NEIGH_1D; j++) {
	  if (s_data.neigh[N_NEIGH_1D * ne + j] < 0) {
	    BNDRY_FLAGS mask;
	    BNDRY_FLAGS_INIT(mask);
	    BNDRY_FLAGS_SET(mask, m_mel->wall_bound[i]);
	    BNDRY_FLAGS_XOR(mask, m_mel->vertex_bound[(i+2-j) % N_VERTICES_2D]);
	    int bound = BNDRY_FLAGS_FFBB(mask);
	    if (bound > INTERIOR &&
		(s_data.boundary[N_NEIGH_1D * ne + j] == INTERIOR ||
		 (unsigned)bound < s_data.boundary[N_NEIGH_1D * ne + j])) {
	      s_data.boundary[N_NEIGH_1D * ne + j] = bound;
	    }
	  }
	}
	ne++;
      }
  }

  /* Assign all remaining boundaries a boundary type of 1. Should we
   * make this configurable? Or use (N_BNDRY_BITS-1) in order not to
   * interfere with existing boundary classifications?
   */
  default_boundary(&s_data, DIRICHLET, false);

/****************************************************************************/
/* Allocate a submesh.                                                      */
/****************************************************************************/

  if(!name) {
    static int count_2d = 1;

    sprintf(new_name, "Submesh %d of %s", count_2d, master->name);
    name = new_name;

    count_2d++;
  }

  slave = GET_MESH(1, name, &s_data, NULL, NULL);

/****************************************************************************/
/* Clean up.                                                                */
/****************************************************************************/

  nv = s_data.n_total_vertices;
  ne = s_data.n_macro_elements;

  MEM_FREE(s_data.coords, nv, REAL_D);
  MEM_FREE(s_data.mel_vertices, ne*N_VERTICES_1D, int);
  MEM_FREE(s_data.neigh, ne*N_NEIGH_1D, int);
  MEM_FREE(s_data.opp_vertex, ne*N_NEIGH_1D, int);
  MEM_FREE(s_data.boundary, ne*N_NEIGH_1D, S_CHAR); 
  MEM_FREE(vert_ind, master->n_vertices, int);

/****************************************************************************/
/* Fill more slave elements, if the master mesh was already refined.        */
/****************************************************************************/

  get_slave_elements_2d(master, slave, binding_method, data);

/****************************************************************************/
/*  Allocate special FE spaces for the slave.                               */
/****************************************************************************/

  s_n_dof[CENTER] = 1;

  slave_space = get_dof_space(slave, "Center dof fe_space", s_n_dof,
			      ADM_PRESERVE_COARSE_DOFS);

  slave_to_master_binding = get_dof_ptr_vec("Slave - master pointers",
					    slave_space);

/****************************************************************************/
/*  Allocate special FE spaces for master.                                  */
/****************************************************************************/
  
  m_n_dof[EDGE] = 1;
  master_space = get_dof_space(master, "Edge dof fe_space", m_n_dof,
			       ADM_PRESERVE_COARSE_DOFS);

#if ALBERTA_DEBUG == 1
  check_mesh(slave);
#endif

/****************************************************************************/
/* Allocate special DOF_PTR_VECs for both master and slave. These serve to  */
/* help find the corresponding subsimplex to each boundary master simplex   */
/* during refinement and vice versa.                                        */
/****************************************************************************/

  master_to_slave_binding = get_dof_ptr_vec("Master - slave pointers",
					    master_space);

  master_to_slave_binding->refine_interpol = master_interpol_2d;
  master_to_slave_binding->coarse_restrict = master_restrict_2d;

/****************************************************************************/
/* Set the special pointers in the MESH_MEM_INFO components of both master  */
/* and slave grids.                                                         */
/****************************************************************************/

  s_info                  = (MESH_MEM_INFO *)slave->mem_info;
  s_info->master          = master;
  s_info->slave_binding  = master_to_slave_binding;
  s_info->master_binding = slave_to_master_binding;

  m_info->slaves = MEM_REALLOC(m_info->slaves,
			       m_info->n_slaves,
			       m_info->n_slaves + 1,
			       MESH *);
  m_info->slaves[m_info->n_slaves] = slave;

  m_info->n_slaves++;

/****************************************************************************/
/* Set the element pointer vec entries to the correct values.               */
/* This assumes that slave macro elements were allocated in the order given */
/* by the loop below.                                                       */
/****************************************************************************/

  m_admin = master_to_slave_binding->fe_space->admin;
  s_admin = slave_to_master_binding->fe_space->admin;

  FOR_ALL_DOFS(s_admin, slave_to_master_binding->vec[dof] = NULL);
  FOR_ALL_DOFS(m_admin, master_to_slave_binding->vec[dof] = NULL);

  s_mel = slave->macro_els;
  for(n = 0; n < master->n_macro_el; n++) {
    int ov;

    m_mel = master->macro_els + n;

    for (ov = 0; ov < N_NEIGH_2D; ov++)
      if (binding_method(master, m_mel, ov, data)) {
	int v0, v1;

	DEBUG_TEST_EXIT(s_mel,
		    "Ran out of slave macro elements... Wrong meshes?\n");

	/* Here we take care of node projection function transfer. */
	if(m_mel->projection[ov+1])
	  s_mel->projection[0] = m_mel->projection[ov+1];
	else
	  s_mel->projection[0] = m_mel->projection[0];
	    
	join_elements_recursive_2d(master, slave, m_admin, s_admin,
				   master_to_slave_binding, 
				   slave_to_master_binding,
				   ov, m_mel->el, s_mel->el);

	s_mel->master.macro_el   = m_mel;
	s_mel->master.opp_vertex = ov;

	/* also store the boundary classification of the master
	 * vertices and edges.
	 */
	v0 = vertex_of_wall_2d[ov][0];
	v1 = vertex_of_wall_2d[ov][1];
	if (m_info->master) {
	  /* slave hierarchy. */
	  BNDRY_FLAGS_CPY(s_mel->master.vertex_bound[0],
			  m_mel->master.vertex_bound[v0]);
	  BNDRY_FLAGS_CPY(s_mel->master.np_vertex_bound[0],
			  m_mel->master.np_vertex_bound[v0]);
	  BNDRY_FLAGS_CPY(s_mel->master.vertex_bound[1],
			  m_mel->master.vertex_bound[v1]);
	  BNDRY_FLAGS_CPY(s_mel->master.np_vertex_bound[1],
			  m_mel->master.np_vertex_bound[v1]);
	  BNDRY_FLAGS_CPY(s_mel->master.edge_bound[0],
			  m_mel->master.edge_bound[ov]);
	  BNDRY_FLAGS_CPY(s_mel->master.np_edge_bound[0],
			  m_mel->master.np_edge_bound[ov]);
	} else {
	  BNDRY_FLAGS_CPY(s_mel->master.vertex_bound[0],
			  m_mel->vertex_bound[v0]);
	  BNDRY_FLAGS_CPY(s_mel->master.np_vertex_bound[0],
			  m_mel->np_vertex_bound[v0]);
	  BNDRY_FLAGS_CPY(s_mel->master.vertex_bound[1],
			  m_mel->vertex_bound[v1]);
	  BNDRY_FLAGS_CPY(s_mel->master.np_vertex_bound[1],
			  m_mel->np_vertex_bound[v1]);
	  BNDRY_FLAGS_INIT(s_mel->master.edge_bound[0]);
	  BNDRY_FLAGS_INIT(s_mel->master.np_edge_bound[0]);
	  if (m_mel->neigh_vertices[ov][0] == -1) {
	    BNDRY_FLAGS_SET(s_mel->master.edge_bound[0],
			    m_mel->wall_bound[ov]);
	  }
	  BNDRY_FLAGS_SET(s_mel->master.np_edge_bound[0],
			  m_mel->wall_bound[ov]);
	}

	s_mel++;
      }
  }

  return slave;
}

