void
cs_gui_mesh_smoothe(cs_mesh_t  *mesh)
{
  char  *path = NULL;
  int mesh_smooting = 0;
  double angle = 25.;

  if (!cs_gui_file_is_loaded())
    return;

  path = cs_xpath_init_path();
  cs_xpath_add_elements(&path, 2, "solution_domain", "mesh_smoothing");
  cs_xpath_add_attribute(&path, "status");

  cs_gui_get_status(path, &mesh_smooting);

  if (mesh_smooting) {

    BFT_FREE(path);

    path = cs_xpath_init_path();
    cs_xpath_add_elements(&path, 3,
                          "solution_domain",
                          "mesh_smoothing",
                          "smooth_angle");
    cs_xpath_add_function_text(&path);

    if (!cs_gui_get_double(path, &angle))
      angle = 25.;

#if _XML_DEBUG_
  bft_printf("==> uicwf\n");
  bft_printf("--mesh_smoothing = %d\n"
             "--angle          = %f\n",
             mesh_smooting, angle);
#endif

    int *vtx_is_fixed = NULL;

    BFT_MALLOC(vtx_is_fixed, mesh->n_vertices, int);

    /* Get fixed boundary vertices flag */

    cs_mesh_smoother_fix_by_feature(mesh,
                                    angle,
                                    vtx_is_fixed);

    /* Call unwarping smoother */

    cs_mesh_smoother_unwarp(mesh, vtx_is_fixed);

    /* Free memory */

    BFT_FREE(vtx_is_fixed);
  }
  BFT_FREE(path);
}
void
cs_gui_mesh_define_joinings(void)
{
  int join_id;
  int n_join = 0;

  if (!cs_gui_file_is_loaded())
    return;

  n_join = cs_gui_get_tag_number("/solution_domain/joining/face_joining", 1);

  if (n_join == 0)
    return;

  for (join_id = 0; join_id < n_join; join_id++) {

    char *selector_s  =  _get_face_joining("selector", join_id+1);
    char *fraction_s  =  _get_face_joining("fraction", join_id+1);
    char *plane_s     =  _get_face_joining("plane", join_id+1);
    char *verbosity_s =  _get_face_joining("verbosity", join_id+1);
    char *visu_s      =  _get_face_joining("visualization", join_id+1);

    double fraction = (fraction_s != NULL) ? atof(fraction_s) : 0.1;
    double plane = (plane_s != NULL) ? atof(plane_s) : 25.0;
    int verbosity = (verbosity_s != NULL) ? atoi(verbosity_s) : 1;
    int visualization = (visu_s != NULL) ? atoi(visu_s) : 1;

    cs_join_add(selector_s,
                fraction,
                plane,
                verbosity,
                visualization);

#if _XML_DEBUG_
    bft_printf("==> cs_gui_mesh_define_joinings\n");
    bft_printf("--selector  = %s\n", selector_s);
    bft_printf("--fraction  = %s\n", fraction_s);
    bft_printf("--plane     = %s\n", plane_s);
    bft_printf("--verbosity = %s\n", verbosity_s);
    bft_printf("--visualization = %s\n", visu_s);
#endif

    BFT_FREE(selector_s);
    BFT_FREE(fraction_s);
    BFT_FREE(plane_s);
    BFT_FREE(verbosity_s);
    BFT_FREE(visu_s);
  }
}
static void
_get_periodicity_translation(int     number,
                             double  trans[3])
{
  size_t dir_id = 0;
  char *path = cs_xpath_init_path();
  cs_xpath_add_elements(&path, 2, "solution_domain", "periodicity");
  cs_xpath_add_element_num(&path, "face_periodicity", number);
  cs_xpath_add_elements(&path, 2, "translation", "translation_x");
  dir_id = strlen(path) - 1;
  cs_xpath_add_function_text(&path);

  if (!cs_gui_get_double(path, trans + 0))
    trans[0] = 0.0;

  path[dir_id] = 'y';
  if (!cs_gui_get_double(path, trans + 1))
    trans[1] = 0.0;

  path[dir_id] = 'z';
  if (!cs_gui_get_double(path, trans + 2))
    trans[2] = 0.0;

  BFT_FREE(path);

#if _XML_DEBUG_
  bft_printf("==> _get_periodicity_translation\n");
  bft_printf("--translation = [%f %f %f]\n",
             trans[0], trans[1], trans[2]);
#endif

  return;
}
void
fvm_neighborhood_destroy(fvm_neighborhood_t  **n)
{
  if (n != NULL) {
    fvm_neighborhood_t *_n = *n;
    if (_n != NULL) {
      if (_n->elt_num != NULL)
        BFT_FREE(_n->elt_num);
      if (_n->neighbor_index != NULL)
        BFT_FREE(_n->neighbor_index);
      if (_n->neighbor_num != NULL)
        BFT_FREE(_n->neighbor_num);
    }
    BFT_FREE(*n);
  }
}
void
cs_gui_mesh_warping(void)
{
  char  *path = NULL;
  int cut_warped_faces = 0;
  double max_warp_angle = -1;

  if (!cs_gui_file_is_loaded())
    return;

  path = cs_xpath_init_path();
  cs_xpath_add_elements(&path, 2, "solution_domain", "faces_cutting");
  cs_xpath_add_attribute(&path, "status");

  cs_gui_get_status(path, &cut_warped_faces);

  if (cut_warped_faces) {

    BFT_FREE(path);

    path = cs_xpath_init_path();
    cs_xpath_add_elements(&path, 3,
                          "solution_domain",
                          "faces_cutting",
                          "warp_angle_max");
    cs_xpath_add_function_text(&path);

    if (!cs_gui_get_double(path, &max_warp_angle))
      max_warp_angle = -1;

#if _XML_DEBUG_
  bft_printf("==> uicwf\n");
  bft_printf("--cut_warped_faces = %d\n"
             "--warp_angle_max   = %f\n",
             cut_warped_faces, max_warp_angle);
#endif

  }

  BFT_FREE(path);

  /* Apply warp angle options now */

  if (cut_warped_faces && max_warp_angle > 0.0)
    cs_mesh_warping_set_defaults(max_warp_angle, 0);
}
static void
_remove_matched_builder_entries(void)
{
  int i;
  int n_unmatched_entries = 0;

  /* First, free arrays associated with marked entries */

  for (i = 0; i < _syr_coupling_builder_size; i++) {

    _cs_syr_coupling_builder_t *scb = _syr_coupling_builder + i;

    if (scb->match_id > -1) {
      if (scb->face_sel_c != NULL)
        BFT_FREE(scb->face_sel_c);
      if (scb->cell_sel_c != NULL)
        BFT_FREE(scb->cell_sel_c);
      if (scb->app_name != NULL)
        BFT_FREE(scb->app_name);
    }
  }

  /* Now, remove marked entries and resize */

  for (i = 0; i < _syr_coupling_builder_size; i++) {
    _cs_syr_coupling_builder_t *scb = _syr_coupling_builder + i;
    if (scb->match_id < 0) {
      *(_syr_coupling_builder + n_unmatched_entries) = *scb;
      n_unmatched_entries += 1;
    }
  }

  _syr_coupling_builder_size = n_unmatched_entries;

  BFT_REALLOC(_syr_coupling_builder,
              _syr_coupling_builder_size,
              _cs_syr_coupling_builder_t);
}
static char *
_get_periodicity(const char  *keyword,
                 int          number)
{
  char* value = NULL;
  char *path = cs_xpath_init_path();
  cs_xpath_add_elements(&path, 2, "solution_domain", "periodicity");
  cs_xpath_add_element_num(&path, "face_periodicity", number);
  cs_xpath_add_element(&path, keyword);
  cs_xpath_add_function_text(&path);
  value = cs_gui_get_text_value(path);
  BFT_FREE(path);
  return value;
}
static void
_get_periodicity_mixed(int     number,
                       double  matrix[3][4])
{
  int i, j;
  size_t coeff_id = 0;
  char *path = cs_xpath_init_path();
  const char id_str[] = {'1', '2', '3','4'};

  cs_xpath_add_elements(&path, 2, "solution_domain", "periodicity");
  cs_xpath_add_element_num(&path, "face_periodicity", number);
  cs_xpath_add_elements(&path, 2, "mixed", "matrix_11");
  coeff_id = strlen(path) - 2;
  cs_xpath_add_function_text(&path);

  for (i = 0; i < 3; i++) {
    path[coeff_id] = id_str[i];

    for (j = 0; j < 4; j++) {
      path[coeff_id + 1] = id_str[j];

      if (!cs_gui_get_double(path, &(matrix[i][j]))) {
        if (i != j)
          matrix[i][j] = 0.0;
        else
          matrix[i][j] = 1.0;
      }
    }
  }

  BFT_FREE(path);

#if _XML_DEBUG_
  bft_printf("==> _get_periodicity_translation\n");
  bft_printf("--matrix = [[%f %f %f %f]\n"
             "            [%f %f %f %f]\n"
             "            [%f %f %f %f]]\n",
             matrix[0][0], matrix[0][1] ,matrix[0][2], matrix[0][3],
             matrix[1][0], matrix[1][1] ,matrix[1][2], matrix[1][3],
             matrix[2][0], matrix[2][1] ,matrix[2][2], matrix[2][3]);
#endif

  return;
}
BEGIN_C_DECLS

/*! \cond DOXYGEN_SHOULD_SKIP_THIS */

/*=============================================================================
 * Local Macro Definitions
 *============================================================================*/

/* debugging switch */
#define _XML_DEBUG_ 0

/*============================================================================
 * Private function definitions
 *============================================================================*/

/*-----------------------------------------------------------------------------
 * Return the value to a face joining markup
 *
 * parameters:
 *   keyword <-- label of the markup
 *   number  <-- joining number
 *----------------------------------------------------------------------------*/

static char *
_get_face_joining(const char  *keyword,
                  int          number)
{
  char* value = NULL;
  char *path = cs_xpath_init_path();
  cs_xpath_add_elements(&path, 2, "solution_domain", "joining");
  cs_xpath_add_element_num(&path, "face_joining", number);
  cs_xpath_add_element(&path, keyword);
  cs_xpath_add_function_text(&path);
  value = cs_gui_get_text_value(path);
  BFT_FREE(path);
  return value;
}
static void
_sync_by_block(fvm_neighborhood_t  *n,
               cs_gnum_t            n_g_elts)
{
  cs_lnum_t   i, j;
  int  rank_id, n_ranks, n_recv_elts, n_sub_elts, shift;

  int  *send_count = NULL, *recv_count = NULL;
  int  *send_shift = NULL, *recv_shift = NULL;
  cs_lnum_t   *counter = NULL;
  cs_gnum_t   *send_buf = NULL, *recv_buf = NULL;

  assert(n != NULL);

  if (n_g_elts == 0 || n->comm == MPI_COMM_NULL)
    return;

  /* Initialization */

  MPI_Comm_rank(n->comm, &rank_id);
  MPI_Comm_size(n->comm, &n_ranks);

  cs_block_dist_info_t bi = cs_block_dist_compute_sizes(rank_id,
                                                        n_ranks,
                                                        0,
                                                        0,
                                                        n_g_elts);

  /* Allocate buffers */

  BFT_MALLOC(send_count, n_ranks, int);
  BFT_MALLOC(recv_count, n_ranks, int);
  BFT_MALLOC(send_shift, n_ranks + 1, int);
  BFT_MALLOC(recv_shift, n_ranks + 1, int);

  for (i = 0; i < n_ranks; i++)
    send_count[i] = 0;

  /* Synchronize definition for each global element */

  for (i = 0; i < n->n_elts; i++) {
    long int g_ent_id = n->elt_num[i] -1;
    int send_rank = (g_ent_id/bi.block_size)*bi.rank_step;
    n_sub_elts = n->neighbor_index[i+1] - n->neighbor_index[i];
    send_count[send_rank] += 2 + n_sub_elts;
  }

  MPI_Alltoall(send_count, 1, MPI_INT, recv_count, 1, MPI_INT, n->comm);

  send_shift[0] = 0;
  recv_shift[0] = 0;

  for (i = 0; i < n_ranks; i++) {
    send_shift[i + 1] = send_shift[i] + send_count[i];
    recv_shift[i + 1] = recv_shift[i] + recv_count[i];
  }

  /* Fill send_buf: global number and number of elements in index */

  BFT_MALLOC(send_buf, send_shift[n_ranks], cs_gnum_t);
  BFT_MALLOC(recv_buf, recv_shift[n_ranks], cs_gnum_t);

  for (i = 0; i < n_ranks; i++)
    send_count[i] = 0;

  for (i = 0; i < n->n_elts; i++) {

    long int g_ent_num = n->elt_num[i];
    int send_rank = ((g_ent_num - 1)/bi.block_size)*bi.rank_step;

    shift = send_shift[send_rank] + send_count[send_rank];
    n_sub_elts = n->neighbor_index[i+1] - n->neighbor_index[i];

    send_buf[shift++] = g_ent_num;
    send_buf[shift++] = n_sub_elts;

    for (j = 0; j < n_sub_elts; j++)
      send_buf[shift + j] = n->neighbor_num[n->neighbor_index[i] + j];

    send_count[send_rank] += 2 + n_sub_elts;
  }

  MPI_Alltoallv(send_buf, send_count, send_shift, CS_MPI_GNUM,
                recv_buf, recv_count, recv_shift, CS_MPI_GNUM,
                n->comm);

  n_recv_elts = recv_shift[n_ranks];

  /* Free what we may */

  BFT_FREE(send_buf);
  BFT_FREE(send_count);
  BFT_FREE(send_shift);
  BFT_FREE(recv_count);
  BFT_FREE(recv_shift);

  /* Build arrays corresponding to block distribution of neighborhood */

  n->n_elts = bi.gnum_range[1] - bi.gnum_range[0];

  BFT_FREE(n->elt_num);
  BFT_FREE(n->neighbor_index);
  BFT_FREE(n->neighbor_num);

  BFT_MALLOC(n->elt_num, n->n_elts, cs_gnum_t);
  BFT_MALLOC(n->neighbor_index, n->n_elts + 1, cs_lnum_t);

  for (i = 0; i < n->n_elts; i++) {
    n->elt_num[i] = bi.gnum_range[0] + i;
    n->neighbor_index[i] = 0;
  }
  n->neighbor_index[n->n_elts] = 0;

  /* Count element neighbors in block distribution */

  i = 0; /* start position in recv_buf */

  while (i < n_recv_elts) {

    cs_lnum_t elt_id = recv_buf[i++] - bi.gnum_range[0];

    assert(n->elt_num[elt_id] == recv_buf[i-1]);

    n_sub_elts = recv_buf[i++];
    n->neighbor_index[elt_id + 1] += n_sub_elts;
    i += n_sub_elts;
  }

  /* Transform element neighbor count to index */

  n->neighbor_index[0] = 0;
  for (i = 0; i < n->n_elts; i++)
    n->neighbor_index[i+1] += n->neighbor_index[i];

  BFT_MALLOC(n->neighbor_num, n->neighbor_index[n->n_elts], cs_gnum_t);

  /* Fill element neighbors in block distribution */

  BFT_MALLOC(counter, n->n_elts, cs_lnum_t);

  for (i = 0; i < n->n_elts; i++)
    counter[i] = 0;

  i = 0; /* start position in recv_buf */

  while (i < n_recv_elts) {

    cs_lnum_t elt_id = recv_buf[i++] - bi.gnum_range[0];

    n_sub_elts = recv_buf[i++];

    shift = n->neighbor_index[elt_id] + counter[elt_id];

    for (j = 0; j < n_sub_elts; j++)
      n->neighbor_num[j + shift] = recv_buf[i++];

    counter[elt_id] += n_sub_elts;

  } /* End of loop on ranks */

  BFT_FREE(recv_buf);
  BFT_FREE(counter);

  /* Remove redundant data */

  _clean_neighbor_nums(n);
}
static void
_order_neighborhood(fvm_neighborhood_t  *n)
{
  cs_lnum_t   i, j, k, order_id, shift, e_count;
  cs_lnum_t   n_elts, n_neighbors, n_elt_neighbors;
  cs_gnum_t   prev_num, cur_num;

  cs_lnum_t   *order = NULL, *old_index = NULL;
  cs_gnum_t   *old_e_num = NULL, *old_n_num = NULL;

  assert(n != NULL);

  if (n->n_elts == 0)
    return;

  n_elts = n->n_elts;
  n_neighbors = n->neighbor_index[n_elts];

  BFT_MALLOC(order, n_elts, cs_lnum_t);
  BFT_MALLOC(old_e_num, n_elts, cs_gnum_t);
  BFT_MALLOC(old_index, n_elts + 1, cs_lnum_t);
  BFT_MALLOC(old_n_num, n_neighbors, cs_gnum_t);

  memcpy(old_e_num, n->elt_num, n_elts*sizeof(cs_gnum_t));
  memcpy(old_index, n->neighbor_index, (n_elts + 1)*sizeof(cs_lnum_t));
  memcpy(old_n_num, n->neighbor_num, n_neighbors*sizeof(cs_gnum_t));

  /* Order elt_num */

  cs_order_gnum_allocated(NULL, old_e_num, order, n_elts);

  /* Reshape according to the new ordering */

  /* Add first element */

  order_id = order[0];
  shift = 0;

  n->elt_num[0] = old_e_num[order_id];
  prev_num = n->elt_num[0];

  n->neighbor_index[0] = 0;
  n->neighbor_index[1] = old_index[order_id+1] - old_index[order_id];

  /* Loop on second-to last elements, merging data if an element has
     already appeared */

  for (i = 1, e_count = 1; i < n_elts; i++) {

    order_id = order[i];

    n_elt_neighbors = old_index[order_id+1] - old_index[order_id];

    shift = n->neighbor_index[i];

    cur_num = old_e_num[order_id];

    if (cur_num != prev_num) {
      n->elt_num[e_count] = cur_num;
      n->neighbor_index[e_count+1] = (  n->neighbor_index[e_count]
                                      + n_elt_neighbors);
      e_count += 1;
      prev_num = cur_num;
    }
    else
      n->neighbor_index[e_count] += n_elt_neighbors;

    for (j = old_index[order_id], k = 0; k < n_elt_neighbors; j++, k++)
      n->neighbor_num[shift + k] = old_n_num[j];

  } /* End of loop on elements */

  assert(n->neighbor_index[e_count] == n_neighbors);

  /* Free temporary memory */

  BFT_FREE(order);
  BFT_FREE(old_e_num);
  BFT_FREE(old_index);
  BFT_FREE(old_n_num);
}
void
cs_mesh_smoother_unwarp(cs_mesh_t  *mesh,
                        const int   vtx_is_fixed[])
{
  int face;
  cs_real_t maxwarp, minhist_i, minhist_b, maxhist_i, maxhist_b;
  cs_real_t rnorm_b, rnorm_i;
  bool conv = false;
  int iter = 0;
  int max_iter = UNWARPING_MAX_LOOPS;
  double frac = 0.1;
  double eps = 1.e-4;
  cs_real_t maxwarp_p = 90;
  cs_real_t *vtx_tolerance = NULL;
  cs_real_t *loc_vtx_mvt = NULL;
  cs_real_t *i_face_norm = NULL;
  cs_real_t *i_face_cog = NULL;
  cs_real_t *b_face_norm = NULL;
  cs_real_t *b_face_cog = NULL;
  cs_real_t *b_face_warp = NULL;
  cs_real_t *i_face_warp = NULL;

  if (mesh->have_rotation_perio)
    bft_error(__FILE__, __LINE__, 0,
              "Smoothing in case of periodicity of rotation not yet handled.");

  bft_printf(_("\n Start unwarping algorithm\n\n"));

  BFT_MALLOC(b_face_warp, mesh->n_b_faces, cs_real_t);
  BFT_MALLOC(i_face_warp, mesh->n_i_faces, cs_real_t);

  BFT_MALLOC(vtx_tolerance, mesh->n_vertices, cs_real_t);
  BFT_MALLOC(loc_vtx_mvt, 3*(mesh->n_vertices), cs_real_t);

  while (!conv) {

    cs_mesh_quantities_i_faces(mesh,
                               &(i_face_cog),
                               &(i_face_norm));

    cs_mesh_quantities_b_faces(mesh,
                               &(b_face_cog),
                               &(b_face_norm));

    cs_mesh_quality_compute_warping(mesh,
                                    i_face_norm,
                                    b_face_norm,
                                    i_face_warp,
                                    b_face_warp);

    _get_tolerance(mesh,
                   vtx_tolerance,
                   frac);

    for (face = 0; face < mesh->n_i_faces; face++) {
      rnorm_i = sqrt (  i_face_norm[3*face]*i_face_norm[3*face]
                      + i_face_norm[3*face + 1]*i_face_norm[3*face + 1]
                      + i_face_norm[3*face + 2]*i_face_norm[3*face + 2]);

      i_face_norm[3*face   ] /= rnorm_i;
      i_face_norm[3*face +1] /= rnorm_i;
      i_face_norm[3*face +2] /= rnorm_i;
    }

    for (face = 0; face < mesh->n_b_faces; face++) {
      rnorm_b = sqrt(  b_face_norm[3*face]*b_face_norm[3*face]
                     + b_face_norm[3*face + 1]*b_face_norm[3*face + 1]
                     + b_face_norm[3*face + 2]*b_face_norm[3*face + 2]);

      b_face_norm[3*face   ] /= rnorm_b;
      b_face_norm[3*face +1] /= rnorm_b;
      b_face_norm[3*face +2] /= rnorm_b;
    }

    maxwarp = _unwarping_mvt(mesh,
                             i_face_norm,
                             b_face_norm,
                             i_face_cog,
                             b_face_cog,
                             loc_vtx_mvt,
                             i_face_warp,
                             b_face_warp,
                             vtx_tolerance,
                             frac);

    if (iter == 0) {
     _compute_minmax(mesh->n_i_faces,
                     i_face_warp,
                     &minhist_i,
                     &maxhist_i);
     _compute_minmax(mesh->n_b_faces,
                     b_face_warp,
                     &minhist_b,
                     &maxhist_b);
     bft_printf(_("\n  Histogram of the boundary faces warping"
                  " before unwarping algorithm:\n\n"));

     _histogram(mesh->n_b_faces,
                b_face_warp,
                minhist_b,
                maxhist_b,
                minhist_b,
                maxhist_b);
     bft_printf(_("\n  Histogram of the interior faces warping"
                  " before unwarping algorithm:\n\n"));

     _int_face_histogram(mesh,
                         i_face_warp,
                         minhist_i,
                         maxhist_i,
                         minhist_i,
                         maxhist_i);
    }

    if (maxwarp/maxwarp_p > 1.005) {
      if (iter <= 1)
        bft_error(__FILE__, __LINE__, 0,
                  _("\nUnwarping algorithm failed."));
      else {
        cs_base_warn(__FILE__, __LINE__);
        bft_printf(_("\nUnwarping algorithm stopped at iteration %d"
                     " because it starting to diverge.\n"), iter);
        iter = max_iter +100;
        conv = true;
      }
    }
    if (   ((1 - maxwarp/maxwarp_p) > 0 && (1 - maxwarp/maxwarp_p) < eps)
        || iter == max_iter) {
      conv = true;
      bft_printf(_("\nUnwarping algorithm converged at iteration %d \n"), iter +1);
    }
    maxwarp_p = maxwarp;

    if (iter <= max_iter)
      _move_vertices(mesh,
                     loc_vtx_mvt,
                     vtx_is_fixed);

    BFT_FREE(i_face_norm);
    BFT_FREE(b_face_norm);
    BFT_FREE(i_face_cog);
    BFT_FREE(b_face_cog);
    iter++;
  }

  /* Output quality histograms */

  {
    cs_real_t min_b, max_b, max_i, min_i;

    _compute_minmax(mesh->n_i_faces,
                    i_face_warp,
                    &min_i,
                    &max_i);
    _compute_minmax(mesh->n_b_faces,
                    b_face_warp,
                    &min_b,
                    &max_b);

    bft_printf(_("\n  Histogram of the boundary faces warping"
                 " after unwarping algorithm:\n\n"));

    _histogram(mesh->n_b_faces,
               b_face_warp,
               minhist_b,
               maxhist_b,
               min_b,
               max_b);
    bft_printf(_("\n  Histogram of the interior faces warping"
                 " after unwarping algorithm:\n\n"));

    _int_face_histogram(mesh,
                        i_face_warp,
                        minhist_i,
                        maxhist_i,
                        min_i,
                        max_i);
  }

  BFT_FREE(vtx_tolerance);
  BFT_FREE(loc_vtx_mvt);

  BFT_FREE(i_face_warp);
  BFT_FREE(b_face_warp);

  bft_printf(_("\n End unwarping algorithm\n\n"));
}
void
cs_mesh_smoother_fix_by_feature(cs_mesh_t   *mesh,
                                cs_real_t    feature_angle,
                                int          vtx_is_fixed[])
{
  cs_lnum_t face, j;

  cs_real_t rnorm_b;
  cs_real_t *face_norm, *vtx_norm;
  cs_real_t *b_face_norm = NULL;
  cs_real_t *b_face_cog = NULL;
  cs_real_t *b_vtx_norm = NULL;
  cs_real_t *_vtx_is_fixed = NULL;

  BFT_MALLOC(_vtx_is_fixed, mesh->n_vertices, cs_real_t);
  BFT_MALLOC(b_vtx_norm, 3*(mesh->n_vertices), cs_real_t);

  cs_mesh_quantities_b_faces(mesh,
                             &(b_face_cog),
                             &(b_face_norm));
  BFT_FREE(b_face_cog);

  for (face = 0; face < mesh->n_b_faces; face++) {
    rnorm_b = sqrt(  b_face_norm[3*face    ]*b_face_norm[3*face    ]
                   + b_face_norm[3*face + 1]*b_face_norm[3*face + 1]
                   + b_face_norm[3*face + 2]*b_face_norm[3*face + 2]);

    b_face_norm[3*face    ] /= rnorm_b;
    b_face_norm[3*face + 1] /= rnorm_b;
    b_face_norm[3*face + 2] /= rnorm_b;
  }

  _compute_vtx_normals(mesh,
                       b_face_norm,
                       b_vtx_norm);

  for (j = 0; j < mesh->n_vertices; j++)
    _vtx_is_fixed[j] = 0;

  for (face = 0; face < mesh->n_b_faces; face++) {
    for (j = mesh->b_face_vtx_idx[face];
         j < mesh->b_face_vtx_idx[face +1];
         j++) {
      face_norm = &b_face_norm[face*3];
      vtx_norm = &b_vtx_norm[(mesh->b_face_vtx_lst[j])*3];

      if (_DOT_PRODUCT_3D(face_norm, vtx_norm) < cos(feature_angle*_PI_/180.0)
          || feature_angle < DBL_MIN)
        _vtx_is_fixed[mesh->b_face_vtx_lst[j]] += 1;
    }
  }

  if (mesh->vtx_interfaces != NULL) {
    cs_interface_set_sum(mesh->vtx_interfaces,
                         mesh->n_vertices,
                         1,
                         true,
                         CS_REAL_TYPE,
                         _vtx_is_fixed);
  }

  for (j = 0; j < mesh->n_vertices; j++) {
    if (_vtx_is_fixed[j] > 0.1)
      vtx_is_fixed[j] = 1;
    else
      vtx_is_fixed[j] = 0;
  }

  BFT_FREE(b_face_norm);
  BFT_FREE(b_vtx_norm);

  BFT_FREE(_vtx_is_fixed);
}
static void
_get_global_tolerance(cs_mesh_t            *mesh,
                      cs_real_t            *vtx_tolerance)
{
  cs_int_t  i, rank, vtx_id, block_size, shift;
  cs_gnum_t  first_vtx_gnum;

  cs_lnum_t n_vertices = mesh->n_vertices;
  double  *g_vtx_tolerance = NULL, *send_list = NULL, *recv_list = NULL;
  cs_int_t  *send_count = NULL, *recv_count = NULL;
  cs_int_t  *send_shift = NULL, *recv_shift = NULL;
  cs_gnum_t  *send_glist = NULL, *recv_glist = NULL;
  cs_gnum_t  n_g_vertices = mesh->n_g_vertices;
  const cs_gnum_t  *io_gnum = mesh->global_vtx_num;

  MPI_Comm  mpi_comm = cs_glob_mpi_comm;
  const int  local_rank = CS_MAX(cs_glob_rank_id, 0);
  const int  n_ranks = cs_glob_n_ranks;

  /* Define a fvm_io_num_t structure on vertices */

  block_size = n_g_vertices / n_ranks;
  if (n_g_vertices % n_ranks > 0)
    block_size += 1;

  /* Count the number of vertices to send to each rank */
  /* ------------------------------------------------- */

  BFT_MALLOC(send_count, n_ranks, int);
  BFT_MALLOC(recv_count, n_ranks, int);
  BFT_MALLOC(send_shift, n_ranks + 1, int);
  BFT_MALLOC(recv_shift, n_ranks + 1, int);

  send_shift[0] = 0;
  recv_shift[0] = 0;

  for (rank = 0; rank < n_ranks; rank++)
    send_count[rank] = 0;

  for (i = 0; i < n_vertices; i++) {
    rank = (io_gnum[i] - 1)/block_size;
    send_count[rank] += 1;
  }

  MPI_Alltoall(send_count, 1, MPI_INT, recv_count, 1, MPI_INT, mpi_comm);

  for (rank = 0; rank < n_ranks; rank++) {
    send_shift[rank + 1] = send_shift[rank] + send_count[rank];
    recv_shift[rank + 1] = recv_shift[rank] + recv_count[rank];
  }

  assert(send_shift[n_ranks] == n_vertices);

  /* Send the global numbering for each vertex */
  /* ----------------------------------------- */

  BFT_MALLOC(send_glist, n_vertices, cs_gnum_t);
  BFT_MALLOC(recv_glist, recv_shift[n_ranks], cs_gnum_t);

  for (rank = 0; rank < n_ranks; rank++)
    send_count[rank] = 0;

  for (i = 0; i < n_vertices; i++) {
    rank = (io_gnum[i] - 1)/block_size;
    shift = send_shift[rank] + send_count[rank];
    send_count[rank] += 1;
    send_glist[shift] = io_gnum[i];
  }

  MPI_Alltoallv(send_glist, send_count, send_shift, CS_MPI_GNUM,
                recv_glist, recv_count, recv_shift, CS_MPI_GNUM, mpi_comm);

  /* Send the vertex tolerance for each vertex */
  /* ----------------------------------------- */

  BFT_MALLOC(send_list, n_vertices, double);
  BFT_MALLOC(recv_list, recv_shift[n_ranks], double);

  for (rank = 0; rank < n_ranks; rank++)
    send_count[rank] = 0;

  for (i = 0; i < n_vertices; i++) {
    rank = (io_gnum[i] - 1)/block_size;
    shift = send_shift[rank] + send_count[rank];
    send_count[rank] += 1;
    send_list[shift] = vtx_tolerance[i];
  }

  MPI_Alltoallv(send_list, send_count, send_shift, MPI_DOUBLE,
                recv_list, recv_count, recv_shift, MPI_DOUBLE, mpi_comm);

  /* Define the global tolerance array */

  BFT_MALLOC(g_vtx_tolerance, block_size, double);

  for (i = 0; i < block_size; i++)
    g_vtx_tolerance[i] = DBL_MAX;

  first_vtx_gnum = block_size * local_rank + 1;

  for (i = 0; i < recv_shift[n_ranks]; i++) {
    vtx_id = recv_glist[i] - first_vtx_gnum;
    g_vtx_tolerance[vtx_id] = CS_MIN(g_vtx_tolerance[vtx_id], recv_list[i]);
  }

  /* Replace local vertex tolerance by the new computed global tolerance */

  for (i = 0; i < recv_shift[n_ranks]; i++) {
    vtx_id = recv_glist[i] - first_vtx_gnum;
    recv_list[i] = g_vtx_tolerance[vtx_id];
  }

  MPI_Alltoallv(recv_list, recv_count, recv_shift, MPI_DOUBLE,
                send_list, send_count, send_shift, MPI_DOUBLE, mpi_comm);

  for (rank = 0; rank < n_ranks; rank++)
    send_count[rank] = 0;

  for (i = 0; i < n_vertices; i++) {
    rank = (io_gnum[i] - 1)/block_size;
    shift = send_shift[rank] + send_count[rank];
    send_count[rank] += 1;
    vtx_tolerance[i] = send_list[shift];
  }

  /* Free memory */

  BFT_FREE(recv_glist);
  BFT_FREE(send_glist);
  BFT_FREE(send_list);
  BFT_FREE(recv_list);
  BFT_FREE(recv_count);
  BFT_FREE(send_count);
  BFT_FREE(recv_shift);
  BFT_FREE(send_shift);
  BFT_FREE(g_vtx_tolerance);
}
void
cs_join_post_after_merge(cs_join_param_t          join_param,
                         const cs_join_select_t  *join_select)
{
  if (_cs_join_post_initialized == true)
    return;

  int  adj_mesh_id, sel_mesh_id;

  int  writer_ids[] = {_cs_join_post_param.writer_num};
  char  *mesh_name = NULL;
  fvm_nodal_t *adj_mesh = NULL, *sel_mesh = NULL;

  adj_mesh_id = cs_post_get_free_mesh_id();

  BFT_MALLOC(mesh_name, strlen("AdjacentJoinFaces_j") + 2 + 1, char);
  sprintf(mesh_name,"%s%02d", "AdjacentJoinFaces_j", join_param.num);

  adj_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
                                            mesh_name,
                                            false, /* include families */
                                            join_select->n_i_adj_faces,
                                            join_select->n_b_adj_faces,
                                            join_select->i_adj_faces,
                                            join_select->b_adj_faces);

  cs_post_define_existing_mesh(adj_mesh_id,
                               adj_mesh,
                               0,    /* dim_shift */
                               true, /* transfer ownership */
                               false,
                               1,
                               writer_ids);

  sel_mesh_id = cs_post_get_free_mesh_id();

  BFT_REALLOC(mesh_name, strlen("JoinFacesAfterMerge_j") + 2 + 1, char);
  sprintf(mesh_name,"%s%02d", "JoinFacesAfterMerge_j", join_param.num);

  sel_mesh = cs_mesh_connect_faces_to_nodal(cs_glob_mesh,
                                            mesh_name,
                                            false, /* include families */
                                            0,
                                            join_select->n_faces,
                                            NULL,
                                            join_select->faces);

  cs_post_define_existing_mesh(sel_mesh_id,
                               sel_mesh,
                               0,    /* dim_shift */
                               true, /* transfer ownership */
                               false,
                               1,
                               writer_ids);

  /* Post */

  cs_post_activate_writer(_cs_join_post_param.writer_num, 1);
  cs_post_write_meshes(NULL);

  cs_post_free_mesh(sel_mesh_id);
  cs_post_free_mesh(adj_mesh_id);

  BFT_FREE(mesh_name);
}
void
cs_join_post_mesh(const char            *mesh_name,
                  const cs_join_mesh_t  *join_mesh)
{
  if (_cs_join_post_initialized == true)
    return;

  int  i, j;
  cs_lnum_t  n_vertices;

  const char *name = NULL;
  int  *ifield = NULL;
  double  *dfield = NULL;
  cs_gnum_t  *vertex_gnum = NULL;
  cs_real_t  *vertex_coord = NULL;
  cs_lnum_t  *parent_vtx_num = NULL;
  fvm_nodal_t  *post_mesh = NULL;
  fvm_writer_t  *writer = _cs_join_post_param.writer;

  const int  local_rank = CS_MAX(cs_glob_rank_id, 0);
  const cs_lnum_t  face_list_shift[2] = {0, join_mesh->n_faces};
  const cs_lnum_t  *face_vertex_idx[1] = {join_mesh->face_vtx_idx};
  const cs_lnum_t  *face_vertex_lst[1] = {join_mesh->face_vtx_lst};

  /* Define an fvm_nodal_mesh_t structure from a cs_join_mesh_t structure */

  /* Create an empty fvm_nodal_t structure. */

  if (mesh_name == NULL)
    name = join_mesh->name;
  else
    name = mesh_name;

  post_mesh = fvm_nodal_create(name, 3);

  /* Define fvm_nodal_t structure */

  fvm_nodal_from_desc_add_faces(post_mesh,
                                join_mesh->n_faces,
                                NULL,
                                1,
                                face_list_shift,
                                face_vertex_idx,
                                face_vertex_lst,
                                NULL,
                                NULL);

  /* Define vertex_coord for fvm_nodal_set_shared_vertices() */

  BFT_MALLOC(vertex_coord, 3*join_mesh->n_vertices, cs_real_t);

  for (i = 0; i < join_mesh->n_vertices; i++)
    for (j = 0; j < 3; j++)
      vertex_coord[3*i+j] = (join_mesh->vertices[i]).coord[j];

  fvm_nodal_set_shared_vertices(post_mesh, vertex_coord);

  /* Order faces by increasing global number */

  fvm_nodal_order_faces(post_mesh, join_mesh->face_gnum);
  fvm_nodal_init_io_num(post_mesh, join_mesh->face_gnum, 2);

  /* Order vertices by increasing global number */

  BFT_MALLOC(vertex_gnum, join_mesh->n_vertices, cs_gnum_t);

  for (i = 0; i < join_mesh->n_vertices; i++)
    vertex_gnum[i] = (join_mesh->vertices[i]).gnum;

  fvm_nodal_order_vertices(post_mesh, vertex_gnum);
  fvm_nodal_init_io_num(post_mesh, vertex_gnum, 0);

  /* Write current mesh */

  fvm_writer_export_nodal(writer, post_mesh);

  BFT_FREE(vertex_gnum);
  BFT_FREE(vertex_coord);

  /* Write rank associated to each face */

  BFT_MALLOC(ifield, join_mesh->n_faces, int);

  for (i = 0; i < join_mesh->n_faces; i++)
    ifield[i] = local_rank;

  _post_elt_ifield(post_mesh, _("Rank"), 1, ifield);

  BFT_FREE(ifield);

  /* Write vertex tolerance */

  n_vertices = fvm_nodal_get_n_entities(post_mesh, 0);

  BFT_MALLOC(parent_vtx_num, n_vertices, cs_lnum_t);
  BFT_MALLOC(dfield, n_vertices, double);

  fvm_nodal_get_parent_num(post_mesh, 0, parent_vtx_num);

  for (i = 0; i < n_vertices; i++) {

    cs_join_vertex_t  data = join_mesh->vertices[parent_vtx_num[i]-1];

    dfield[i] = data.tolerance;
  }

  _post_vtx_dfield(post_mesh, _("VtxTolerance"), 1, dfield);

  BFT_FREE(parent_vtx_num);
  BFT_FREE(dfield);

  post_mesh = fvm_nodal_destroy(post_mesh);
}
void
fvm_neighborhood_by_boxes(fvm_neighborhood_t  *n,
                          int                  dim,
                          cs_lnum_t            n_boxes,
                          const cs_gnum_t     *box_gnum,
                          const cs_coord_t    *extents,
                          cs_gnum_t          **box_gnum_assigned,
                          cs_coord_t         **extents_assigned)
{
  double  clock_start, clock_end, cpu_start, cpu_end;

  fvm_box_tree_t  *bt = NULL;
  fvm_box_set_t  *boxes = NULL;

  const cs_gnum_t   *_box_gnum = box_gnum;
  const cs_coord_t  *_extents = extents;

  int  n_ranks = 1;

  clock_start = cs_timer_wtime();
  cpu_start = cs_timer_cpu_time();

  /* Transfer data if necessary */

  if (box_gnum_assigned != NULL)
    _box_gnum = *box_gnum_assigned;
  if (extents_assigned != NULL)
    _extents = *extents_assigned;

  /* Reset structure if necessary */

  n->n_elts = 0;
  if (n->elt_num != NULL)
    BFT_FREE(n->elt_num);
  if (n->neighbor_index != NULL)
    BFT_FREE(n->neighbor_index);
  if (n->neighbor_num != NULL)
    BFT_FREE(n->neighbor_num);

  /* Allocate fvm_box_set_t structure and initialize it */

#if defined(HAVE_MPI)

  if (n->comm != MPI_COMM_NULL)
    MPI_Comm_size(n->comm, &n_ranks);

  boxes = fvm_box_set_create(dim,
                             1,  /* normalize */
                             1,  /* allow_projection */
                             n_boxes,
                             _box_gnum,
                             _extents,
                             n->comm);

  if (n_ranks > 1)
    _redistribute_boxes(n, boxes);

#else

  boxes = fvm_box_set_create(dim,
                             1,  /* normalize */
                             1,  /* allow_projection */
                             n_boxes,
                             _box_gnum,
                             _extents);

#endif

  /* Free transferred data if applicable */

  if (box_gnum_assigned != NULL) {
    _box_gnum = NULL;
    BFT_FREE(*box_gnum_assigned);
  }

  if (extents_assigned != NULL) {
    _extents = NULL;
    BFT_FREE(*extents_assigned);
  }

  /* Build a tree structure and use it to order bounding boxes */

  /* Create and initialize a box tree structure */

  bt = fvm_box_tree_create(n->max_tree_depth,
                           n->leaf_threshold,
                           n->max_box_ratio);

  /* Build a tree and put bounding boxes */

  fvm_box_tree_set_boxes(bt,
                         boxes,
                         FVM_BOX_TREE_ASYNC_LEVEL);

  _update_bt_statistics((&n->bt_stats), bt);

  /* Update construction times. */

  clock_end = cs_timer_wtime();
  cpu_end = cs_timer_cpu_time();

  n->cpu_time[0] = cpu_end - cpu_start;
  n->wtime[0] = clock_end - clock_start;

  clock_start = clock_end;
  cpu_start = cpu_end;

  /* Allocate structure to store intersections between boxes */

  n->n_elts = fvm_box_set_get_size(boxes);

  BFT_MALLOC(n->elt_num, n->n_elts, cs_gnum_t);
  memcpy(n->elt_num,
         fvm_box_set_get_g_num(boxes),
         n->n_elts*sizeof(cs_gnum_t));

  fvm_box_tree_get_intersects(bt,
                              boxes,
                              &(n->neighbor_index),
                              &(n->neighbor_num));

#if 0 && defined(DEBUG) && !defined(NDEBUG)
  fvm_box_tree_dump(bt);
  fvm_box_set_dump(boxes, 1);
#endif

  /* Destroy the associated box tree */

  fvm_box_tree_destroy(&bt);

  /* Compact intersections list, delete redundancies and order intersections */

  _order_neighborhood(n);

#if defined(HAVE_MPI)

  /* Synchronize list of intersections for each element of the list
     and distribute it by block over the ranks */

  if (n_ranks > 1)
    _sync_by_block(n, fvm_box_set_get_global_size(boxes));

#endif /* HAVE_MPI */

  /* Destroy the box set structures */

  fvm_box_set_destroy(&boxes);

  _clean_neighbor_nums(n);

  /* Update query times. */

  clock_end = cs_timer_wtime();
  cpu_end = cs_timer_cpu_time();

  n->cpu_time[1] = cpu_end - cpu_start;
  n->wtime[1] = clock_end - clock_start;
}
Exemple #18
0
void
cs_gui_mesh_define_periodicities(void)
{
  int perio_id;
  double angle, trans[3], axis[3], invariant[3], matrix[3][4];

  int  n_perio = 0;
  int  n_modes = 0;
  char  **modes = NULL;
  char  *path = NULL;

  if (!cs_gui_file_is_loaded())
    return;

  n_perio
    = cs_gui_get_tag_number("/solution_domain/periodicity/face_periodicity",
                            1);
  if (n_perio == 0)
    return;

  /* Get modes associated with each periodicity */

  path = cs_xpath_init_path();
  cs_xpath_add_elements(&path, 3,
                        "solution_domain",
                        "periodicity", "face_periodicity");
  cs_xpath_add_attribute(&path, "mode");
  modes = cs_gui_get_attribute_values(path, &n_modes);

  if (n_modes != n_perio)
    bft_error(__FILE__, __LINE__, 0,
              _("Number of periodicities (%d) and modes (%d) do not match."),
              n_perio, n_modes);

  BFT_FREE(path);

  /* loop on periodicities */

  for (perio_id = 0; perio_id < n_perio; perio_id++) {

    char *selector_s  =  _get_periodicity("selector", perio_id+1);
    char *fraction_s  =  _get_periodicity("fraction", perio_id+1);
    char *plane_s     =  _get_periodicity("plane", perio_id+1);
    char *verbosity_s =  _get_periodicity("verbosity", perio_id+1);
    char *visu_s      =  _get_periodicity("visualization", perio_id+1);

    double fraction = (fraction_s != NULL) ? atof(fraction_s) : 0.1;
    double plane = (plane_s != NULL) ? atof(plane_s) : 25.0;
    int verbosity = (verbosity_s != NULL) ? atoi(verbosity_s) : 1;
    int visualization = (visu_s != NULL) ? atoi(visu_s) : 1;

    if (!strcmp(modes[perio_id], "translation")) {
      _get_periodicity_translation(perio_id+1, trans);
      cs_join_perio_add_translation(selector_s,
                                    fraction,
                                    plane,
                                    verbosity,
                                    visualization,
                                    trans);
    }

    else if (!strcmp(modes[perio_id], "rotation")) {
      _get_periodicity_rotation(perio_id+1, &angle, axis, invariant);
      cs_join_perio_add_rotation(selector_s,
                                 fraction,
                                 plane,
                                 verbosity,
                                 visualization,
                                 angle,
                                 axis,
                                 invariant);
    }

    else if (!strcmp(modes[perio_id], "mixed")) {
      _get_periodicity_mixed(perio_id+1, matrix);
      cs_join_perio_add_mixed(selector_s,
                              fraction,
                              plane,
                              verbosity,
                              visualization,
                              matrix);
    }

    else
      bft_error(__FILE__, __LINE__, 0,
                _("Periodicity mode \"%s\" unknown."), modes[perio_id]);

#if _XML_DEBUG_
    bft_printf("==> cs_gui_mesh_define_periodicities\n");
    bft_printf("--selector      = %s\n", selector_s);
    bft_printf("--fraction      = %s\n", fraction_s);
    bft_printf("--plane         = %s\n", plane_s);
    bft_printf("--verbosity     = %s\n", verbosity_s);
    bft_printf("--visualization = %s\n", visu_s);
#endif

    BFT_FREE(selector_s);
    BFT_FREE(fraction_s);
    BFT_FREE(plane_s);
    BFT_FREE(verbosity_s);
    BFT_FREE(visu_s);
  }

  for (perio_id = 0; perio_id < n_perio; perio_id++)
    BFT_FREE(modes[perio_id]);
  BFT_FREE(modes);
}
Exemple #19
0
static void
_get_periodicity_rotation(int      number,
                          double  *angle,
                          double   axis[3],
                          double   invariant[3])
{
  size_t coo_id = 0;
  char *path = NULL;
  char *path_0 = cs_xpath_init_path();
  cs_xpath_add_elements(&path_0, 2, "solution_domain", "periodicity");
  cs_xpath_add_element_num(&path_0, "face_periodicity", number);
  cs_xpath_add_element(&path_0, "rotation");

  BFT_MALLOC(path, strlen(path_0) + 1, char);
  strcpy(path, path_0);

  /* Angle */

  cs_xpath_add_element(&path, "angle");
  cs_xpath_add_function_text(&path);
  if (!cs_gui_get_double(path, angle))
    *angle = 0.0;

  /* Axis */

  strcpy(path, path_0);
  cs_xpath_add_element(&path, "axis_x");
  coo_id = strlen(path) - 1;
  cs_xpath_add_function_text(&path);

  if (!cs_gui_get_double(path, axis + 0))
    axis[0] = 0.0;

  path[coo_id] = 'y';
  if (!cs_gui_get_double(path, axis + 1))
    axis[1] = 0.0;

  path[coo_id] = 'z';
  if (!cs_gui_get_double(path, axis + 2))
    axis[2] = 0.0;

  /* Invariant */

  strcpy(path, path_0);
  cs_xpath_add_element(&path, "invariant_x");
  coo_id = strlen(path) - 1;
  cs_xpath_add_function_text(&path);

  if (!cs_gui_get_double(path, invariant + 0))
    invariant[0] = 0.0;

  path[coo_id] = 'y';
  if (!cs_gui_get_double(path, invariant + 1))
    invariant[1] = 0.0;

  path[coo_id] = 'z';
  if (!cs_gui_get_double(path, invariant + 2))
    invariant[2] = 0.0;

  BFT_FREE(path);
  BFT_FREE(path_0);

#if _XML_DEBUG_
  bft_printf("==> _get_periodicity_translation\n");
  bft_printf("--angle = %f\n",
             *angle);
  bft_printf("--axis = [%f %f %f]\n",
             axis[0], axis[1], axis[2]);
  bft_printf("--invariant = [%f %f %f]\n",
             invariant[0], invariant[1], invariant[2]);
#endif

  return;
}
void
cs_user_postprocess_meshes(void)
{
  /* Post-processing meshes may be defined using one of several functions,
   * whose protypes are defined in cs_post.h; these functions are:
   *
   * Functions simplest to use are cs_post_define_volume_mesh() and
   * cs_post_define_surface_mesh(), which allow defining volume or surface
   * post-processing meshes using selection criteria.
   *
   * parameters for cs_post_define_volume_mesh():
   *   mesh_id        <-- id of mesh to define (< 0 reserved, > 0 for user)
   *   mesh_name      <-- associated mesh name
   *   cell_criteria  <-- selection criteria for cells
   *   add_groups     <-- if true, add group information if present
   *   auto_variables <-- if true, automatic output of main variables
   *   n_writers      <-- number of associated writers
   *   writer_ids     <-- ids of associated writers
   *
   * parameters for cs_post_define_surface_mesh():
   *   mesh_id         <-- id of mesh to define (< 0 reserved, > 0 for user)
   *   mesh_name       <-- associated mesh name
   *   i_face_criteria <-- selection criteria for interior faces
   *   b_face_criteria <-- selection criteria for boundary faces
   *   add_groups      <-- if true, add group information if present
   *   auto_variables  <-- if true, automatic output of main variables
   *   n_writers       <-- number of associated writers
   *   writer_ids      <-- ids of associated writers
   *
   * If no writer is associated to a mesh, it is not output, and its
   * construction may be avoided altogether (at least when defined
   * by one of the above functions).
   *
   * More advanced functions are described along with examples below. */

  /*--------------------------------------------------------------------------*/

  /* Reconfigure predefined meshes (mesh_id -1 for volume, -2 for boundary */

  if (false) {

    /* De-activate boundary mesh output by redefining it with no writer
       association (default is:
       int n_writers = 1;
       const int writer_ids[] = {-1});
    */

    int n_writers = 0;
    const int *writer_ids = NULL;

    cs_post_define_surface_mesh(-2,          /* mesh_id of main boundary mesh */
                                "Boundary",  /* mesh name */
                                NULL,        /* interior face selection criteria */
                                "all[]",     /* boundary face selection criteria */
                                true,        /* add_groups */
                                true,        /* automatic variables output */
                                n_writers,
                                writer_ids);

  }

  /*--------------------------------------------------------------------------*/

  if (false) {

    /* Example: select interior faces with y = 0.5 */

    const int n_writers = 2;
    const int writer_ids[] = {1, 4};  /* Associate to writers 1 and 4 */

    const char *interior_criteria = "plane[0, -1, 0, 0.5, "
                                    "epsilon = 0.0001]";
    const char *boundary_criteria = NULL;

    cs_post_define_surface_mesh(1,               /* mesh id */
                                "Median plane",
                                interior_criteria,
                                boundary_criteria,
                                false, /* add_groups */
                                false, /* auto_variables */
                                n_writers,
                                writer_ids);

  }

  /*--------------------------------------------------------------------------*/

  if (false) {

    /* Example: select all cells, and will modify the selection in 'usmpst' */

    const int n_writers = 1;
    const int writer_ids[] = {3};  /* Associate to writer 3 */

    cs_post_define_volume_mesh(2,                 /* mesh id */
                               "Volume v > 0.5",
                               "all[]",
                               false,             /* add_groups */
                               true,              /* auto_variables */
                               n_writers,
                               writer_ids);

  }

  /*--------------------------------------------------------------------------*/

  if (false) {

    /* Example: select all boundary faces, and will modify the selection
       in 'usmpst'. */

    const int n_writers = 1;
    const int writer_ids[] = {3};  /* Associate to writer 3 */

    const char *interior_criteria = NULL;
    const char *boundary_criteria = "all[]";

    cs_post_define_surface_mesh(3,                /* mesh id */
                                "Surface iso v",
                                interior_criteria,
                                boundary_criteria,
                                false,            /* add_groups */
                                true,             /* auto_variables */
                                n_writers,
                                writer_ids);

  }

  /*--------------------------------------------------------------------------*/

  /* The same variables will be output through all writers associated
   * to a mesh. In cases where different variables of a same mesh should
   * be output throught different writers, the solution is to define one or
   * several "aliases" of that mesh, allowing to assign a different id,
   * writers, and variables to each secondary copy of the mesh, without the
   * overhead of a full copy. The cs_post_define_alias_mesh() function
   * may be used for such a purpose.
   *
   * parameters for cs_post_define_alias_mesh():
   *   mesh_id         <-- id of mesh to define (< 0 reserved, > 0 for user)
   *   aliased_mesh_id <-- id of aliased mesh
   *   auto_variables  <-- if true, automatic output of main variables
   *   n_writers       <-- number of associated writers
   *   writer_ids      <-- ids of associated writers  */

  if (false) {

    /* Example: define an alias of the main surface mesh */

    const int n_writers = 1;
    const int writer_ids[] = {4};  /* Associate to writer 4 */

    cs_post_define_alias_mesh(4,                 /* mesh id */
                              -2,                /* aliased mesh id */
                              false,             /* auto_variables */
                              n_writers,
                              writer_ids);

  }

  /*--------------------------------------------------------------------------*/

  /* More advanced mesh element selection is possible using
   * cs_post_define_volume_mesh_by_list() or
   * cs_post_define_surface_mesh_by_list(), which allow defining
   * volume or surface meshes using user-defined element lists.
   *
   * parameters for cs_post_define_volume_mesh_by_list():
   *   mesh_id        <-- id of mesh to define (< 0 reserved, > 0 for user)
   *   mesh_name      <-- associated mesh name
   *   n_cells        <-- number of selected cells
   *   cell_list      <-> list of selected cells (1 to n numbering)
   *   add_groups     <-- if true, add group information if present
   *   auto_variables <-- if true, automatic output of main variables
   *   n_writers      <-- number of associated writers
   *   writer_ids     <-- ids of associated writers
   *
   * parameters for cs_post_define_surface_mesh_by_list():
   *   mesh_id        <-- id of mesh to define (< 0 reserved, > 0 for user)
   *   mesh_name      <-- associated mesh name
   *   n_i_faces      <-- number of associated interior faces
   *   n_b_faces      <-- number of associated boundary faces
   *   i_face_list    <-> list of associated interior faces (1 to n numbering)
   *   b_face_list    <-> list of associated boundary faces (1 to n numbering)
   *   add_groups     <-- if true, add group information if present
   *   auto_variables <-- if true, automatic output of main variables
   *   n_writers      <-- number of associated writers
   *   writer_ids     <-- ids of associated writers */

  if (false) {

    /* Advanced example:
       Build a surface mesh containing interior faces separating cells of group "2"
       from those of group "3", (assuming no cell has both colors), as well as
       boundary faces of group "4". */

    fvm_lnum_t n_i_faces = 0, n_b_faces = 0;
    fvm_lnum_t *i_face_list = NULL, *b_face_list = NULL;

    const int n_writers = 1;
    const int writer_ids[] = {1};  /* Associate to writer 1 */

    const cs_mesh_t *m = cs_glob_mesh;

    /* Allocate selection lists */

    BFT_MALLOC(i_face_list, m->n_i_faces, fvm_lnum_t);
    BFT_MALLOC(b_face_list, m->n_b_faces, fvm_lnum_t);

    /* Select interior faces */

    {
      fvm_lnum_t i, face_id;
      fvm_lnum_t n_families = 0;
      cs_int_t *family_list = NULL;
      int *family_mask = NULL;

      /* Build mask on families matching groups "2" (1), "3" (2) */

      BFT_MALLOC(family_list, m->n_families, cs_int_t);
      BFT_MALLOC(family_mask, m->n_families, int);

      for (i = 0; i < m->n_families; i++)
        family_mask[i] = 0;

      cs_selector_get_family_list("2",  &n_families, family_list);

      for (i = 0; i < n_families; i++)
        family_mask[family_list[i] - 1] += 1;

      cs_selector_get_family_list("3",  &n_families, family_list);

      for (i = 0; i < n_families; i++)
        family_mask[family_list[i] - 1] += 2;

      BFT_FREE(family_list);

      /* Now that mask is built, test for adjacency */

      for (face_id = 0; face_id < m->n_i_faces; face_id++) {

        /* Adjacent cells  and flags */

        fvm_lnum_t c1 = m->i_face_cells[face_id*2] - 1;
        fvm_lnum_t c2 = m->i_face_cells[face_id*2 + 1] - 1;

        int iflag1 = family_mask[m->cell_family[c1]];
        int iflag2 = family_mask[m->cell_family[c2]];

        /* Should the face belong to the extracted mesh ? */

        if ((iflag1 == 1 && iflag2 == 2) || (iflag1 == 2 && iflag2 == 1)) {
          i_face_list[n_i_faces] = face_id + 1;
          n_i_faces += 1;
        }

      }

      BFT_FREE(family_mask);
    }

    /* Select boundary faces */

    cs_selector_get_b_face_list("4", &n_b_faces, b_face_list);

    /* Define postprocessing mesh */

    cs_post_define_surface_mesh_by_list(5,               /* mesh id */
                                        "Mixed surface",
                                        n_i_faces,
                                        n_b_faces,
                                        i_face_list,
                                        b_face_list,
                                        false,           /* add_groups */
                                        false,           /* auto_variables */
                                        n_writers,
                                        writer_ids);

    /* Free selection lists */

    BFT_FREE(i_face_list);
    BFT_FREE(b_face_list);

  }
void
cs_user_solver(const cs_mesh_t             *mesh,
               const cs_mesh_quantities_t  *mesh_quantities)
{
  return; /* REMOVE_LINE_FOR_USE_OF_SUBROUTINE */
  cs_int_t    i, iter, n_iter;

  cs_real_t   x0, xL, t0, tL, L;
  cs_real_t   r;

  cs_real_t  *t = NULL, *t_old = NULL, *t_sol = NULL;

  cs_restart_t    *restart, *checkpoint;
  cs_time_plot_t  *time_plot;

  const cs_int_t   n = mesh->n_cells;
  const cs_real_t  pi = 4.*atan(1.);

  const char  var_name[] = "temperature";

  /* Initialization */

  BFT_MALLOC(t, n, cs_real_t);
  BFT_MALLOC(t_old, n, cs_real_t);
  BFT_MALLOC(t_sol, n, cs_real_t);

  x0 =  1.e30;
  xL = -1.e30;

  for (i = 0; i < mesh->n_b_faces; i++) {
    cs_real_t  x_face = mesh_quantities->b_face_cog[3*i];
    if (x_face < x0) x0 = x_face;
    if (x_face > xL) xL = x_face;
  }

  L = xL - x0; /* it is assumed that dx is constant and x0 = 0, XL =1 */

  t0 = 0.;
  tL = 0.;

  r = 0.2; /* Fourier number */

  n_iter = 100000;

  for (i = 0; i < n; i++)
    t_old[i] = sin(pi*(0.5+i)/n);

  /* ------- */
  /* Restart */
  /* ------- */

  if (1 == 0)
  {
    restart = cs_restart_create("main",                 /* file name */
                                NULL,                   /* force directory */
                                CS_RESTART_MODE_READ);  /* read mode */

    cs_restart_read_section(restart,   /* restart file */
                            var_name,  /* buffer name */
                            1,         /* location id */
                            1,         /* number of values per location */
                            2,         /* value type */
                            t_old);    /* buffer */

    cs_restart_destroy(&restart);
  }

  /* --------------- */
  /* Time monitoring */
  /* --------------- */

  time_plot = cs_time_plot_init_probe(var_name,          /* variable name */
                                      "probes_",         /* file prefix */
                                      CS_TIME_PLOT_DAT,  /* file format */
                                      true,              /* use iter. numbers */
                                      -1.,               /* force flush */
                                      0,                 /* buffer size */
                                      1,                 /* number of probes */
                                      NULL,              /* probes list */
                                      NULL);             /* probes coord. */

  /* ----------- */
  /* Calculation */
  /* ----------- */

  /* Heat equation resolution by Finite Volume method */

  for (iter = 0; iter < n_iter; iter++) {

    /* 1D Finite Volume scheme, with constant dx */

    t[0] = t_old[0] + r*(t_old[1] - 3.*t_old[0] + 2.*t0);

    for (i = 1; i < n-1;  i++)
      t[i] = t_old[i] + r*(t_old[i+1] - 2.*t_old[i] + t_old[i-1]);

    t[n-1] = t_old[n-1] + r*(2.*tL - 3.*t_old[n-1] + t_old[n-2]);

    /* Update previous value of the temperature */

    for (i = 0; i < n; i++)
      t_old[i] = t[i];

    /* Analytical solution at the current time */

    for (i = 0; i < n; i++)
      t_sol[i] = sin(pi*(0.5+i)/n)*exp(-r*pi*pi*(iter+1)/(n*n));

    /* Plot maximum temperature value (center-value) */

    cs_time_plot_vals_write(time_plot,   /* time plot structure */
                            iter,        /* current iteration number */
                            -1.,         /* current time */
                            1,           /* number of values */
                            &(t[n/2]));  /* values */

  }

  /* --------- */
  /* Chekpoint */
  /* --------- */

  checkpoint = cs_restart_create("main",                  /* file name */
                                 NULL,                    /* force directory */
                                 CS_RESTART_MODE_WRITE);  /* write mode */

  cs_restart_write_section(checkpoint,  /* restart file */
                           var_name,    /* buffer name */
                           1,           /* location id */
                           1,           /* number of values per location */
                           2,           /* value type */
                           t);          /* buffer */

  cs_restart_destroy(&checkpoint);

  /* --------------- */
  /* Post-processing */
  /* --------------- */

  cs_post_activate_writer(-1,     /* default writer */
                          true);  /* activate if 1 */

  cs_post_write_var(-1,                      /* default mesh */
                    var_name,                /* variable name */
                    1,                       /* variable dimension */
                    false,                   /* interleave if true */
                    true,                    /* define on parents */
                    CS_POST_TYPE_cs_real_t,  /* type */
                    t,                       /* value on cells */
                    NULL,                    /* value on interior faces */
                    NULL,                    /* value on boundary faces */
                    NULL);                   /* time-independent output */

  cs_post_write_var(-1,                      /* default mesh */
                    "solution",              /* variable name */
                    1,                       /* variable dimension */
                    false,                   /* interleave if true */
                    true,                    /* define on parents */
                    CS_POST_TYPE_cs_real_t,  /* type */
                    t_sol,                   /* value on cells */
                    NULL,                    /* value on interior faces */
                    NULL,                    /* value on boundary faces */
                    NULL);                   /* time-independent output */

  /* Finalization */

  BFT_FREE(t);
  BFT_FREE(t_old);
  BFT_FREE(t_sol);

  cs_time_plot_finalize(&time_plot);

  return;
}