/*copied from ... for gettig torus wall trafo.*/
/*******************************************************************************
 *
 * ALBERTA:  an Adaptive multi Level finite element toolbox  using
 *           Bisectioning refinement and Error control by Residual
 *           Techniques
 *
 * www.alberta-fem.de
 *
 * File:     ellipt-periodic.c
 *
 * Description:  solver for a simple Poisson equation on a periodic domain:
 *
 *                 -\Delta_\Gamma u = f
 *                                u = g  on \partial\Gamma
 *
 * The computational domain is either a topological torus or a
 * topological Klein's bottle (or whatever the name of it may be in
 * 3d).
 *
 ******************************************************************************
 *
 * author(s): Claus-Justus Heine
 *            Abteilung fuer Angewandte Mathematik
 *            Albert-Ludwigs-Universitaet Freiburg
 *            Hermann-Herder-Str. 10
 *            79104 Freiburg
 *            Germany
 *            Claus.Heine@Mathematik.Uni-Freiburg.DE
 *
 * (c) by C.-J. Heine (2006-2007)
 *
 * Based on the famous ellipt.c demo-program by K.G. Siebert and A. Schmidt.
 *
 ******************************************************************************/

#include <alberta.h>

#include <limits.h>
#include <getopt.h>
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif


/* Extra code for testing sub-meshes of periodic meshes */
#define TEST_PERIODIC_SUBMESHES 0

#define MESH_DIM MIN(DIM_OF_WORLD, 3)

/*******************************************************************************
 * global variables: finite element space, discrete solution
 *                   load vector and system matrix
 *
 * These variables are kept global because they are shared across build(),
 * solve() and estimate().
 ******************************************************************************/



/*******************************************************************************
 * Problem data: either for a topological torus or a Klein's bottle
 ******************************************************************************/

/* The affine transformations that map the fundamental domain to its
 * periodic neighbour across a certain wall. init_wall_trafos() (just
 * in front of main(), see below) will invert these transformations as
 * needed.
 */
static AFF_TRAFO torus_wall_trafos[DIM_OF_WORLD] = {
#if DIM_OF_WORLD == 1
    { {{1.0}}, {2.0} },
#endif
#if DIM_OF_WORLD == 2
    { {{1.0, 0.0},
       {0.0, 1.0}}, {1.0, 0.0} },
    { {{1.0, 0.0},
       {0.0, 1.0}}, {0.0, 1.0} },
#endif
#if DIM_OF_WORLD == 3
    { {{1.0, 0.0, 0.0},
       {0.0, 1.0, 0.0},
       {0.0, 0.0, 1.0}}, {2.0, 0.0, 0.0} },
    { {{1.0, 0.0, 0.0},
       {0.0, 1.0, 0.0},
       {0.0, 0.0, 1.0}}, {0.0, 2.0, 0.0} },
    { {{1.0, 0.0, 0.0},
       {0.0, 1.0, 0.0},
       {0.0, 0.0, 1.0}}, {0.0, 0.0, 2.0} },
#endif
};

#if 0
static AFF_TRAFO kleinsbottle_wall_trafos[DIM_OF_WORLD] = {
#if DIM_OF_WORLD == 1
  { {{1.0}}, {2.0} } /* not really a Klein's bottle ... */
#endif
#if DIM_OF_WORLD == 2
  { {{ 1.0, 0.0}, /* identify left and right edges */
     { 0.0, 1.0}},
    {2.0, 0.0} },
  { {{ -1.0, 0.0}, /* identify flipped top and bottom edges */
     { 0.0, 1.0}},
    {0.0, 2.0} },
#endif
#if DIM_OF_WORLD == 3
  { {{ 1.0, 0.0, 0.0},
     { 0.0, 1.0, 0.0},
     { 0.0, 0.0, 1.0}}, {2.0, 0.0, 0.0} },
  { {{ 1.0, 0.0, 0.0},
     { 0.0, 1.0, 0.0},
     { 0.0, 0.0, 1.0}}, {0.0, 2.0, 0.0} },
  { {{-1.0, 0.0, 0.0}, /* indentify reflected top and bottom */
     { 0.0, 1.0, 0.0},
     { 0.0, 0.0, 1.0}}, {0.0, 0.0, 2.0} }
#endif
};

/* various parameters to tune the test problems. */

#define K_X 1.0 /* number of periods in x direction */
#define K_Y 1.0 /* number of periods in y direction (DIM_OF_WORLD > 1) */
#define K_Z 1.0 /* number of periods in z direction (DIM_OF_WORLD > 2) */

static REAL_D K_NR = {
  K_X,
#if DIM_OF_WORLD > 1
  K_Y,
#endif
#if DIM_OF_WORLD > 2
  K_Z
#endif
};

/* just something to avoid zero boundary conditions */
#define PHASE_OFFSET 0.378
#endif






/*******************************************************************************
 * initialization of wall-transformations.
 ******************************************************************************/
AFF_TRAFO *init_wall_trafos(MESH *mesh, MACRO_EL *mel, int wall)
{
  FUNCNAME("init_wall_trafos");
  static AFF_TRAFO inverse_wall_trafos[DIM_OF_WORLD];
  static int init_done;
   
  if (!init_done) {
    int i, j, k;
	MSG("Warning! left bound x coord must be lower than or eq. to 0\n");
	MSG("lower  bound y coord must be lower than or eq. to 0 \n");
	MSG("and vice versa for right and upper bound for use of torus periodic bounds!\n");
	MSG("Further, use -1 for vertical bounds and -2 for the horizontal bounds in 2d.\n");
 	MSG("If you don't like it, rewrite it. Its easy.\n");
    init_done = true;

    /* ALBERTA does not invert the wall-transformation by itself, so
     * we have to do that ourselves here. This piece of code is, of
     * course, overly complicated for our simple case. But this is the
     * way a general case could be handled. Wall-transformation MUST
     * BE isometries, i.e. the inverse of the matrix is just its
     * transpose, the translation is transformed by the transpose of
     * the matrix and negated.
     */
    for (i = 0; i < DIM_OF_WORLD; i++) {
      for (j = 0; j < DIM_OF_WORLD; j++) {
	inverse_wall_trafos[i].t[j] = 0.0;
	for (k = 0; k < DIM_OF_WORLD; k++) {
	  inverse_wall_trafos[i].M[j][k] = /*problem->wall_trafos*/torus_wall_trafos[i].M[k][j];
	  inverse_wall_trafos[i].t[j] -=
	  /*problem->wall_trafos*/torus_wall_trafos[i].M[k][j] * /*problem->wall_trafos*/torus_wall_trafos[i].t[k];
	}
      }
    }
  }

  switch (mel->wall_bound[wall]) {
  case -1: /* translation in x[0] direction */
 //   if (mel->coord[(wall+1) % N_VERTICES(MESH_DIM)][0] > 0.1) {
      /* actually == 1.0 */
      return &inverse_wall_trafos[0];
//    } else {
  case -3: /* inverse translation in x[0] direction */
      return &/*problem->wall_trafos*/torus_wall_trafos[0];
// Vorsicht! nicht einfach wieder einkommentieren, dann sind die Trafos falschrum.   }
  case -2: /* translation in x[1] direction (+ possible reflection) */
    if (mel->coord[(wall+1) % N_VERTICES(MESH_DIM)][1] > 0.1) {
      /* actually == 1.0 */
      return &/*problem->wall_trafos*/torus_wall_trafos[1];
    } else {
      return &inverse_wall_trafos[1];
    }
  case -4://-3: hab ich eh nicht /* translation in x[2] + reflection about x[0] == 0 */
    if (mel->coord[(wall+1) % N_VERTICES(MESH_DIM)][2] > 0.1) {
      /* actually == 1.0 */
      return &/*problem->wall_trafos*/torus_wall_trafos[2];
    } else {
      return &inverse_wall_trafos[2];
    }
  }

  return NULL;
}

#if TEST_PERIODIC_SUBMESHES
void togeomview(MESH *mesh,
		const DOF_REAL_VEC *u_h,
		REAL uh_min, REAL uh_max,
		REAL (*get_est)(EL *el),
		REAL est_min, REAL est_max,
		REAL (*u_loc)(const EL_INFO *el_info,
			      const REAL_B lambda,
			      void *ud),
		void *ud, FLAGS fill_flags,
		REAL u_min, REAL u_max);

/* The submesh is spanned by all peridic boundary simplexes; it should
 * inherit part of the periodic structure of the master-mesh.
 *
 * Note, however, that a submesh of a periodic master-mesh must not
 * contain simplexes which are mapped to each other by the
 * wall-transformations of the master mesh.
 *
 * The example below chooses just one periodic wall as sub-mesh, which
 * then again a topological torus or a Klein's bottle.
 */
static int periodic_bndry_binding_method(MESH *master,
					 MACRO_EL *mel, int face, void *ud)
{
  if (mel->wall_bound[face] == master->dim &&
      mel->coord[(face + 1) % N_VERTICES(master->dim)][DIM_OF_WORLD-1] == -1.0) {
    return true;
  } else {
    return false;
  }
}
#endif

/*******************************************************************************
 * helper function for command-line provided parameters
 ******************************************************************************/
#if 0
sieht praktisch aus
static void parse_parameters(int argc, char *argv[], const char *init_file)
{
  struct option long_options[] = {
    {"init-file", required_argument, NULL, 'i'},
    {"parameters", required_argument, NULL, 'p'},
    {"help", no_argument, NULL, 'h'},
    {NULL, 0, NULL, '\0'}
  };
  int option_index = 0;
  int c;
  char *params = NULL;

  /* first of all, init parameters of the init file */
  do {
    c = getopt_long(argc, argv, "i:p:h", long_options, &option_index);
    switch(c) {
    case 'h':
	 printf("Usage: %s [-h] [-i INITFILE] [-p PARAMETERS]\n"
		"[--help] [--init-file=INITFILE] [--parameters=PARAMETERS]\n",
		argv[0]);
	 exit(0);
	 break;
    case 'i':
	 init_file = optarg;
	 break;
    case 'p':
	 params = optarg;
	 break;
    }
  } while (c != -1);


  init_parameters(0, init_file);
  if (params) {
       char *param, *value;
       do {
	    param = strtok(params, "'");
	    params = NULL;
	    if (param) {
		 TEST_EXIT((value = strchr(param, '=')) != NULL,
			   "Command-line specified parameters are garbled, "
			   "not '=' sign found.\n");
		 *value++ = '\0';
		 ADD_PARAMETER(0, param, value);
	    }
       } while (param);
  }
}
#endif

/*******************************************************************************
 * main program
 ******************************************************************************/
#if 0
int main(int argc, char **argv)
{
  FUNCNAME("main");
  MACRO_DATA     *data;
  MESH           *mesh;
  int            n_refine = 0, dim, degree = 1;
  unsigned int   probnr = 0;
  const BAS_FCTS *lagrange;
  ADAPT_STAT     *adapt;
  char filename[PATH_MAX];

  /*****************************************************************************
   * first of all, initialize the access to parameters of the init file
   ****************************************************************************/
  parse_parameters(argc, argv, "INIT/ellipt-periodic.dat");

  /*****************************************************************************
   * then read some basic parameters
   ****************************************************************************/
  GET_PARAMETER(1, "mesh dimension", "%d", &dim);
  GET_PARAMETER(1, "macro file name", "%s", filename);
  GET_PARAMETER(1, "polynomial degree", "%d", &degree);
  GET_PARAMETER(1, "global refinements", "%d", &n_refine);
  GET_PARAMETER(1, "online graphics", "%d", &do_graphics);
  
  /*****************************************************************************
   * initialize the test-problem
   ****************************************************************************/
  GET_PARAMETER(1, "problem number", "%d", &probnr);

  TEST_EXIT(probnr < sizeof(problems)/sizeof(*problems),
	    "Not so many test-problems: %d (only %d)\n",
	    probnr, sizeof(problems)/sizeof(*problems));
  problem = &problems[probnr];

  /*****************************************************************************
  *  get a mesh, and read the macro triangulation from file
  ****************************************************************************/
  data = read_macro(filename);
  mesh = GET_MESH(dim, "ALBERTA mesh", data,
		  NULL /* init_node_projection() */,
		  init_wall_trafos);
  free_macro_data(data);

  init_leaf_data(mesh, sizeof(struct ellipt_leaf_data),
		 NULL /* refine_leaf_data() */,
		 NULL /* coarsen_leaf_data() */);

  /*****************************************************************************
   * periodic boundary conditions are implemented by really
   * identifying DOFs; all what the application needs to do is to pass
   * the "ADM_PERIODIC" flag to get_fe_space(). The hard work is
   * hidden in ALBERTA's internals -- and of course in the proper
   * definition of the periodic mesh by defining consistent wall
   * transformations.
   ****************************************************************************/
  lagrange = get_lagrange(mesh->dim, degree);
  TEST_EXIT(lagrange, "no lagrange BAS_FCTS\n");
  fe_space = get_fe_space(mesh, lagrange->name,
			  NULL /* n_dof[] */, lagrange, ADM_PERIODIC);

  global_refine(mesh, n_refine * mesh->dim);

  if (do_graphics) {
    graphics(mesh, NULL /* u_h */, NULL /* get_est()*/ , NULL /* u_exact() */);
  }

  /*****************************************************************************
   *  initialize the global variables shared across build(), solve()
   *  and estimate().
   ****************************************************************************/
  matrix = get_dof_matrix("A", fe_space, NULL /* col_fe_space */);
  f_h    = get_dof_real_vec("f_h", fe_space);
  u_h    = get_dof_real_vec("u_h", fe_space);
  u_h->refine_interpol = fe_space->bas_fcts->real_refine_inter;
  u_h->coarse_restrict = fe_space->bas_fcts->real_coarse_inter;
  dof_set(0.0, u_h);      /* initialize u_h */

  /*****************************************************************************
   *  init adapt structure and start adaptive method
   ****************************************************************************/
  adapt = get_adapt_stat(mesh->dim, "ellipt", "adapt", 2,
			 NULL /* ADAPT_STAT storage area, optional */);
  adapt->estimate = estimate;
  adapt->get_el_est = get_el_est;
  adapt->build_after_coarsen = build;
  adapt->solve = solve;

  adapt_method_stat(mesh, adapt);

#if TEST_PERIODIC_SUBMESHES
  {
    MESH *slave;
    const FE_SPACE *s_fe_space;
    DOF_REAL_VEC *uh_trace;

    MSG("Proceeding with a test for periodic sub-meshes.\n");
    WAIT_REALLY;

    slave = get_submesh(mesh, "periodic slave-mesh",
			periodic_bndry_binding_method, NULL);

    s_fe_space = get_fe_space(slave,
			      lagrange->trace_bas_fcts->name,
			      NULL /* n_dof[] */,
			      lagrange->trace_bas_fcts, ADM_PERIODIC);
    uh_trace = get_dof_real_vec("trace u_h", s_fe_space);
    trace_dof_real_vec(uh_trace, u_h);

    togeomview(slave,
	       uh_trace, 1.0, 0.0,
	       NULL, 0.0, 0.0,
	       NULL, NULL, 0, 0.0, 0.0);
  }
#endif

  WAIT_REALLY;
    
  return 0;
}
#endif
