/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/* file:     submesh_1d.c                                                   */
/*                                                                          */
/*                                                                          */
/* description: Support for master/slave meshes for master->dim == 1        */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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),                                               */
/*         C.-J. Heine (2006-2007)                                          */
/*--------------------------------------------------------------------------*/


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

static void master_interpol_1d(DOF_PTR_VEC *m_dpv, RC_LIST_EL *rclist, int mn)
{
  FUNCNAME("master_interpol_1d");
  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[VERTEX];
  int              m_n = m_dpv->fe_space->admin->mesh->node[VERTEX];
  EL              *m_el, *s_el;
  EL              *m_child[2];
  int              s_n0, s_n, 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];

/****************************************************************************/
/* Check if any vertices belong to slave 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.   */
/****************************************************************************/

  m_el = rclist->el_info.el;
  m_child[0] = m_el->child[0];
  m_child[1] = m_el->child[1];

/****************************************************************************/
/* DOF pointers of the master child elements at the new vertex 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_1D; 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]]) {
      m_dpv->vec[m_child[j]->dof[m_n+j][m_n0]] = s_el;
      s_dpv->vec[s_el->dof[s_n][s_n0]] = m_child[j];
    }
/****************************************************************************/
/* If there is no slave element on this refinement edge, zero the master    */
/* child pointers.                                                          */
/****************************************************************************/
    else if (s_el) m_dpv->vec[m_child[j]->dof[m_n+j][m_n0]] = NULL;
  }

/****************************************************************************/
/* NOTE TO MAINTAINERS: Here might be the correct place to set "newcoords"  */
/* entries on the slave element.                                            */
/****************************************************************************/

  return;
}


static void master_restrict_1d(DOF_PTR_VEC *m_dpv, RC_LIST_EL *rclist, int mn)
{
  FUNCNAME("master_restrict_1d");
  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[VERTEX];
  int              m_n = m_dpv->fe_space->admin->mesh->node[VERTEX];
  EL              *m_el, *s_el, *cm_el;
  EL              *m_child[2];
  int              s_n0, s_n, 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];

/****************************************************************************/
/* Check if any edges belong to slave elements.                             */
/****************************************************************************/

  m_el = rclist->el_info.el;

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

  for (j = 0; j < N_WALLS_1D; 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;
      }
    }
  }

  return;
}


static void join_elements_recursive_1d(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_1d"); */

  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[VERTEX] + subsimplex]
	     [m_admin->n0_dof[VERTEX]]] = (void *)s_el;

  if (m_el->child[0]) {
    if (subsimplex == 0)
      /* Elements in 0D do not have children. We therefore simply pass      */
      /* the pointers on to master children.                                */
      
      join_elements_recursive_1d(master, slave, m_admin, s_admin,
				 m_dpv, s_dpv,
				 0, m_el->child[0], s_el);
    else
      join_elements_recursive_1d(master, slave, m_admin, s_admin,
				 m_dpv, s_dpv,
				 1, m_el->child[1], s_el);
  }

  return;
}


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

static MESH *get_submesh_1d(MESH *master, const char *name,
			    bool (*binding_method)
			    (MESH *master, MACRO_EL *el, int face, void *data),
			    void *data)
{
  FUNCNAME("get_submesh_1d");
  MACRO_DATA       s_data = { 0, };
  MESH_MEM_INFO   *s_info, *m_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;
  DOF_SCHAR_VEC   *master_el_orient_type;
  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, 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 = 0;
  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_1D; i++)
      if (binding_method(master, m_mel, i, data)) {
	ne++;
	
 /* Make use of the slave->mem_info->coords vector to get vertex indices.    */
	index = *m_mel->coord[i] - 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++;
	}
      }
  }

  /* FIXME: should we generate periodic 0d meshes??? We do not so now ... */

/****************************************************************************/
/* Allocate the needed amount of macro elements and coordinates.            */
/* Fill element and coordinate information. Boundary info is irrelevant in  */
/* the 0D slave case.                                                       */
/****************************************************************************/

  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, int);
  ne = 0;

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

    for (i = 0; i < N_WALLS_1D; i++)
      if (binding_method(master, m_mel, i, data)) {
	index = m_mel->coord[i] - &m_info->coords[0];
	nv = vert_ind[index];
	
	s_data.mel_vertices[ne] = nv;  
	
	ne++;
      }
  }

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

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

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

    count_1d++;
  }

  slave = GET_MESH(0, 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, int);
  MEM_FREE(vert_ind, master->n_vertices, int);

/****************************************************************************/
/*  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);
  master_el_orient_type = get_dof_schar_vec("Master element type",
					    slave_space);

/****************************************************************************/
/*  Allocate special FE spaces for master.                                  */
/****************************************************************************/
  
  m_n_dof[VERTEX] = 1;
  master_space = get_dof_space(master, "VERTEX 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_1d;
  master_to_slave_binding->coarse_restrict = master_restrict_1d;

/****************************************************************************/
/* 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++;

/****************************************************************************/
/* Convert the macro data structure to a mesh. Set the correct DOFs on      */
/* master and slave elements.                                               */
/****************************************************************************/

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

  FOR_ALL_DOFS(m_admin, master_to_slave_binding->vec[dof] = NULL;
	       master_el_orient_type->vec[dof] = 0);
  FOR_ALL_DOFS(s_admin, slave_to_master_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_WALLS_1D; ov++)
      if (binding_method(master, m_mel, ov, data)) {
	DEBUG_TEST_EXIT(s_mel,
			"Ran out of slave macro elements... Wrong meshes?\n");

	join_elements_recursive_1d(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;
	if (m_info->master) {
	  BNDRY_FLAGS_CPY(s_mel->master.vertex_bound[0],
			  m_mel->master.vertex_bound[1-ov]);
	  BNDRY_FLAGS_CPY(s_mel->master.np_vertex_bound[0],
			  m_mel->master.np_vertex_bound[1-ov]);
	} else {
	  BNDRY_FLAGS_INIT(s_mel->master.np_vertex_bound[0]);
	  BNDRY_FLAGS_INIT(s_mel->master.vertex_bound[0]);
	  if (!master->is_periodic) {
	    BNDRY_FLAGS_SET(s_mel->master.vertex_bound[0],
			    m_mel->wall_bound[ov]);
	  } else {
	    if (m_mel->neigh_vertices[ov][0] == -1) {
	      BNDRY_FLAGS_SET(s_mel->master.vertex_bound[0],
			      m_mel->wall_bound[ov]);
	    }
	    BNDRY_FLAGS_SET(s_mel->master.np_vertex_bound[0],
			    m_mel->wall_bound[ov]);
	  }
	}
	s_mel++;
      }    
  }  
  
  return slave;
}
