Пример #1
0
// `volumes` must be preallocated.
int32 mesh_get_volumes(Mesh *mesh, float64 *volumes, int32 dim)
{
#define VS(ic, id) (coors[nc*entity_vertices->indices[ic] + id])

  int32 ret = RET_OK;
  uint32 D = mesh->topology->max_dim;
  uint32 nc = mesh->geometry->dim;
  uint32 id;
  uint32 indx2[6];
  float64 vol, aux, vv0, vv1, vv2, vv3;
  float64 *ptr = volumes;
  float64 *coors = mesh->geometry->coors;
  float64 v0[3], v1[3], v2[3], ndir[3];
  Indices entity_vertices[1];
  MeshEntityIterator it0[1];
  MeshConnectivity *cd0 = 0; // d -> 0

  if (!dim) {
    errput("vertices have no volume!\n");
    ERR_CheckGo(ret);
  }

  cd0 = mesh->topology->conn[IJ(D, dim, 0)];

  for (mei_init(it0, mesh, dim); mei_go(it0); mei_next(it0)) {
    me_get_incident2(it0->entity, entity_vertices, cd0);
    /* mei_print(it0, stdout); */
    /* ind_print(entity_vertices, stdout); */
    vol = 0;

    if (dim == 1) { // Edges.
      for (id = 0; id < nc; id++) {
        aux = VS(1, id) - VS(0, id);
        vol += aux * aux;
      }
      ptr[0] = sqrt(vol);

    } else if (entity_vertices->num == 3) { // Triangles.
      ptr[0] = _tri_area(coors, entity_vertices->indices, nc);

    } else if (nc == 2) { // Quadrilateral cells.
      ptr[0] = _tri_area(coors, entity_vertices->indices, nc);
      indx2[0] = entity_vertices->indices[2];
      indx2[1] = entity_vertices->indices[3];
      indx2[2] = entity_vertices->indices[0];
      ptr[0] += _tri_area(coors, indx2, nc);

    } else if (nc == 3) { // 3D.
      if (entity_vertices->num == 4) {
        if (dim == 2) { // Quadrilateral faces (approximate).
          indx2[0] = entity_vertices->indices[0];
          indx2[1] = entity_vertices->indices[1];
          indx2[2] = entity_vertices->indices[2];
          indx2[3] = entity_vertices->indices[3];
          indx2[4] = entity_vertices->indices[0];
          indx2[5] = entity_vertices->indices[1];

          aux = _tri_area(coors, indx2, nc);
          aux += _tri_area(coors, indx2 + 1, nc);
          aux += _tri_area(coors, indx2 + 2, nc);
          aux += _tri_area(coors, indx2 + 3, nc);
          ptr[0] = 0.5 * aux;

        } else { // Tetrahedral cells.
          for (id = 0; id < nc; id++) {
            vv0 = VS(0, id);
            vv1 = VS(1, id);
            vv2 = VS(2, id);
            vv3 = VS(3, id);

            v0[id] = vv1 - vv0;
            v1[id] = vv2 - vv0;
            v2[id] = vv3 - vv2;
          }
          gtr_cross_product(ndir, v0, v1);
          gtr_dot_v3(ptr, v2, ndir, 3);
          ptr[0] /= 6.0;
        }

      } else { // Hexahedral cells with trilinear interpolation.
        // See https://math.stackexchange.com/questions/1628540/what-is-the-enclosed-volume-of-an-irregular-cube-given-the-x-y-z-coordinates-of
        // Uses 0 1 3 2 4 5 7 6 ordering w.r.t. sfepy.
        aux = _aux_hex(coors, entity_vertices->indices, nc, 0, 1, 3, 2);
        aux -= _aux_hex(coors, entity_vertices->indices, nc, 4, 5, 7, 6);
        aux -= _aux_hex(coors, entity_vertices->indices, nc, 0, 1, 4, 5);
        aux += _aux_hex(coors, entity_vertices->indices, nc, 3, 2, 7, 6);
        aux += _aux_hex(coors, entity_vertices->indices, nc, 0, 3, 4, 7);
        aux -= _aux_hex(coors, entity_vertices->indices, nc, 1, 2, 5, 6);
        ptr[0] = aux / 12.0;

      }
    }

    ptr += 1;
  }

 end_label:
  return(ret);

#undef VS
}
Пример #2
0
int32 mesh_intersect(Mesh *mesh, int32 d1, int32 d2, int32 d3)
{
  int32 ret = RET_OK;
  uint32 D = mesh->topology->max_dim;
  uint32 n_incident, ii;
  uint32 *nd2 = 0;
  char *mask = 0;
  MeshEntityIterator it1[1], it2[1], it3[1];
  Indices ei1[1], ei2[1];
  MeshConnectivity *c12 = 0; // d1 -> d2 - to compute
  MeshConnectivity *c10 = 0; // d1 -> 0 - known
  MeshConnectivity *c20 = 0; // d2 -> 0 - known

  debprintf("intersect %d -> %d (%d)\n", d1, d2, d3);

  if (d1 < d2) {
    errput("d1 must be greater or equal to d2 in mesh_intersect()!\n");
    ERR_CheckGo(ret);
  }

  c12 = mesh->topology->conn[IJ(D, d1, d2)];
  if (d1 > d2) {
    c10 = mesh->topology->conn[IJ(D, d1, 0)];
    c20 = mesh->topology->conn[IJ(D, d2, 0)];
  }

  mask = alloc_mem(char, mesh->topology->num[d2]);

  // Count entities of d2 -> d1.
  conn_alloc(c12, mesh->topology->num[d1], 0);
  ERR_CheckGo(ret);
  nd2 = c12->offsets + 1;

  for (mei_init(it1, mesh, d1); mei_go(it1); mei_next(it1)) {
    // Clear mask for it1 incident entities of dimension d2.
    for (mei_init_conn(it3, it1->entity, d3); mei_go(it3); mei_next(it3)) {
      for (mei_init_conn(it2, it3->entity, d2); mei_go(it2); mei_next(it2)) {
        mask[it2->entity->ii] = 0;
      }
    }

    for (mei_init_conn(it3, it1->entity, d3); mei_go(it3); mei_next(it3)) {
      for (mei_init_conn(it2, it3->entity, d2); mei_go(it2); mei_next(it2)) {
        if (mask[it2->entity->ii]) continue;
        mask[it2->entity->ii] = 1;

        if (d1 == d2) {
          if (it1->entity->ii != it2->entity->ii) {
            nd2[it1->entity->ii]++;
          }
        } else {
          // Get incident vertices.
          me_get_incident2(it1->entity, ei1, c10);
          me_get_incident2(it2->entity, ei2, c20);
          if (contains(ei1, ei2)) {
            nd2[it1->entity->ii]++;
          }
        }
      }
    }
  }

  // c12->offsets now contains counts - make a cumsum to get offsets.
  for (ii = 1; ii < c12->num + 1; ii++) {
    c12->offsets[ii] += c12->offsets[ii-1];
  }

  n_incident = c12->offsets[c12->num];
  debprintf("intersect n_incident (%d -> %d): %d\n", d1, d2, n_incident);

  // Fill in the indices.
  conn_alloc(c12, 0, n_incident);
  ERR_CheckGo(ret);
  for (ii = 0; ii < c12->n_incident; ii++) {
    c12->indices[ii] = UINT32_None; // "not set" value.
  }

  for (mei_init(it1, mesh, d1); mei_go(it1); mei_next(it1)) {
    // Clear mask for it1 incident entities of dimension d2.
    for (mei_init_conn(it3, it1->entity, d3); mei_go(it3); mei_next(it3)) {
      for (mei_init_conn(it2, it3->entity, d2); mei_go(it2); mei_next(it2)) {
        mask[it2->entity->ii] = 0;
      }
    }

    for (mei_init_conn(it3, it1->entity, d3); mei_go(it3); mei_next(it3)) {
      for (mei_init_conn(it2, it3->entity, d2); mei_go(it2); mei_next(it2)) {
        if (mask[it2->entity->ii]) continue;
        mask[it2->entity->ii] = 1;

        if (d1 == d2) {
          if (it1->entity->ii != it2->entity->ii) {
            conn_set_to_free(c12, it1->entity->ii, it2->entity->ii);
            ERR_CheckGo(ret);
          }
        } else {
          // Get incident vertices.
          me_get_incident2(it1->entity, ei1, c10);
          me_get_incident2(it2->entity, ei2, c20);
          if (contains(ei1, ei2)) {
            conn_set_to_free(c12, it1->entity->ii, it2->entity->ii);
            ERR_CheckGo(ret);
          }
        }
      }
    }
  }

 end_label:
  free_mem(mask);

  return(ret);
}
Пример #3
0
int32 mesh_build(Mesh *mesh, int32 dim)
{
  int32 ret = RET_OK;
  uint32 n_incident, n_v_max, n_loc;
  uint32 ii, ic, id, found;
  uint32 D = mesh->topology->max_dim;
  uint32 facet[4]; // Max. space for single facet.
  uint32 *oris = 0;
  uint32 loc_oris[12];
  uint32 *nDd = 0;
  uint32 *ptr1 = 0, *ptr2 = 0;
  uint32 *cell_types = mesh->topology->cell_types;
  Indices cell_vertices[1];
  MeshEntityIterator it0[1], it1[1], it2[1];
  MeshConnectivity *cD0 = 0; // D -> 0 - known
  MeshConnectivity *cDd = 0; // D -> d - to compute
  MeshConnectivity *cd0 = 0; // d -> 0 - to compute
  MeshConnectivity **locs = 0;
  MeshConnectivity *loc = 0;
  MeshConnectivity sloc[1]; // Local connectivity with sorted global vertices.
  MeshConnectivity gloc[1]; // Local connectivity with global vertices.

  debprintf("build %d\n", dim);

  if (!mesh->topology->conn[IJ(D, D, D)]->num) {
    mesh_setup_connectivity(mesh, D, D);
  }

  cD0 = mesh->topology->conn[IJ(D, D, 0)];
  cDd = mesh->topology->conn[IJ(D, D, dim)];
  cd0 = mesh->topology->conn[IJ(D, dim, 0)];

  locs = (dim == 1) ? mesh->entities->edges : mesh->entities->faces;

  // Max. number of vertices in facets.
  n_v_max = (dim == 1) ? 2 : 4;

  // Count entities of D -> d.
  conn_alloc(cDd, mesh->topology->num[D], 0);
  ERR_CheckGo(ret);
  nDd = cDd->offsets + 1;

  for (mei_init(it0, mesh, D); mei_go(it0); mei_next(it0)) {
    loc = locs[cell_types[it0->it]];
    nDd[it0->it] = loc->num;
  }

  // cDd->offsets now contains counts - make a cumsum to get offsets.
  for (ii = 1; ii < cDd->num + 1; ii++) {
    cDd->offsets[ii] += cDd->offsets[ii-1];
  }

  n_incident = cDd->offsets[cDd->num];
  debprintf("build n_incident (%d -> %d): %d\n", D, dim, n_incident);

  // Cell-local orientations w.r.t. D -> d.
  oris = alloc_mem(uint32, n_incident);
  if (dim == 2) {
    free_mem(mesh->topology->face_oris);
    mesh->topology->face_oris = oris;
  } else {
    free_mem(mesh->topology->edge_oris);
    mesh->topology->edge_oris = oris;
  }

  // Allocate D -> d indices.
  conn_alloc(cDd, 0, n_incident);
  ERR_CheckGo(ret);
  for (ii = 0; ii < cDd->n_incident; ii++) {
    cDd->indices[ii] = UINT32_None; // "not set" value.
  }

  // Allocate maximal buffers for d -> 0 arrays.
  conn_alloc(cd0, n_incident, n_incident * n_v_max);
  debprintf("build max. n_incident_vertex: %d\n", n_incident * n_v_max);

  // Allocate maximal buffers for local connectivity with sorted global
  // vertices. Counts have to be set to zero to avoid spurious calls to
  // conn_free()!
  sloc->num = sloc->n_incident = 0;
  conn_alloc(sloc, 12, n_v_max * 12);
  gloc->num = gloc->n_incident = 0;
  conn_alloc(gloc, 12, n_v_max * 12);

  id = 0;
  for (mei_init(it0, mesh, D); mei_go(it0); mei_next(it0)) {
    // Get vertex sets for local entities of current cell.
    loc = locs[cell_types[it0->it]];
    me_get_incident2(it0->entity, cell_vertices, cD0);
    get_local_connectivity(gloc, cell_vertices, loc);
    conn_set_from(sloc, gloc);
    sort_local_connectivity(sloc, loc_oris, loc->num);

    // Iterate over entities in the vertex sets.
    for (ii = 0; ii < loc->num; ii++) {
      // ii points to a vertex set in sloc/gloc.
      n_loc = sloc->offsets[ii+1] - sloc->offsets[ii];

      // Try to find entity in cells visited previously.
      for (mei_init_conn(it1, it0->entity, D); mei_go(it1); mei_next(it1)) {
        if (it1->entity->ii >= it0->entity->ii) continue;

        // Iterate over facets of visited cells.
        for (mei_init_conn(it2, it1->entity, dim); mei_go(it2); mei_next(it2)) {
          ptr1 = cd0->indices + cd0->offsets[it2->entity->ii];
          uint32_sort234_copy(facet, ptr1, n_loc);
          ptr2 = sloc->indices + sloc->offsets[ii];
          found = 1;
          for (ic = 0; ic < n_loc; ic++) {
            if (facet[ic] != ptr2[ic]) {
              found = 0;
              break;
            }
          }
          if (found) {
            // Assign existing entity to D -> d.
            conn_set_to_free(cDd, it0->entity->ii, it2->entity->ii);
            goto found_label;
          }
        }
      }
      // Entity not found - create new.

      // Add it as 'id' to D -> d.
      conn_set_to_free(cDd, it0->entity->ii, id);

      // Add vertices in gloc to d -> 0.
      cd0->offsets[id+1] = cd0->offsets[id] + n_loc;

      ptr1 = cd0->indices + cd0->offsets[id];
      ptr2 = gloc->indices + gloc->offsets[ii];
      for (ic = 0; ic < n_loc; ic++) {
        ptr1[ic] = ptr2[ic];
      }

      // Increment entity counter.
      id++;

    found_label:
      // Store entity orientation key to position of the last used item in cDd.
      ptr1 = cDd->offsets + it0->entity->ii;
      ic = ptr1[1] - 1;
      while (ic >= ptr1[0]) {
        if (cDd->indices[ic] != UINT32_None) { // Not found & free slot.
          break;
        }
        ic--;
      }
      // printf("%d << %d, %d\n", ic, ii, loc_oris[ii]);
      oris[ic] = loc_oris[ii];
    }
  }
  debprintf("build n_unique: %d, n_incident (%d -> 0): %d\n",
            id, dim, cd0->offsets[id]);

  // Update entity count in topology.
  mesh->topology->num[dim] = id;

  // Strip d -> 0.
  conn_resize(cd0, id, cd0->offsets[id]);

 end_label:
  conn_free(sloc);
  conn_free(gloc);

  return(ret);
}
Пример #4
0
// `normals` must be preallocated.
int32 mesh_get_facet_normals(Mesh *mesh, float64 *normals, int32 which)
{
#define VS(ic, id) (coors[nc*cell_vertices->indices[ik[ic]] + id])

  uint32 D = mesh->topology->max_dim;
  uint32 nc = mesh->geometry->dim;
  int32 dim = D - 1;
  uint32 ii, id, n_loc;
  uint32 *ik;
  uint32 *cell_types = mesh->topology->cell_types;
  float64 *coors = mesh->geometry->coors;
  float64 vv0, vv1, vv2, vv3, v0[3], v1[3], v2[3], v3[3], ndir[3], ndir1[3];
  Indices cell_vertices[1];
  MeshEntityIterator it0[1];
  MeshConnectivity *cD0 = 0; // D -> 0
  MeshConnectivity *cDd = 0; // D -> d
  MeshConnectivity *loc = 0;
  MeshConnectivity **locs = 0;

  cD0 = mesh->topology->conn[IJ(D, D, 0)];
  cDd = mesh->topology->conn[IJ(D, D, dim)];

  // Local entities - reference cell edges or faces.
  locs = (dim == 1) ? mesh->entities->edges : mesh->entities->faces;

  for (mei_init(it0, mesh, D); mei_go(it0); mei_next(it0)) {
    me_get_incident2(it0->entity, cell_vertices, cD0);
    loc = locs[cell_types[it0->it]];

    for (ii = 0; ii < loc->num; ii++) {
      ik = loc->indices + loc->offsets[ii]; // Points to local facet vertices.

      n_loc = loc->offsets[ii+1] - loc->offsets[ii];
      if (n_loc == 2) { // Edge normals.
        for (id = 0; id < nc; id++) {
          v0[id] = VS(1, id) - VS(0, id);
        }
        ndir[0] = v0[1];
        ndir[1] = -v0[0];

      } else if (n_loc == 3) { // Triangular face normals.
        for (id = 0; id < nc; id++) {
          vv0 = VS(0, id);
          v0[id] = VS(1, id) - vv0;
          v1[id] = VS(2, id) - vv0;
        }
        gtr_cross_product(ndir, v0, v1);

      } else if (n_loc == 4) { // Quadrilateral face normals.
        for (id = 0; id < nc; id++) {
          vv0 = VS(0, id);
          vv1 = VS(1, id);
          vv2 = VS(2, id);
          vv3 = VS(3, id);

          v0[id] = vv1 - vv0;
          v1[id] = vv3 - vv0;
          v2[id] = vv3 - vv2;
          v3[id] = vv1 - vv2;
        }

        if (which == 0) {
          gtr_cross_product(ndir, v0, v1);

        } else if (which == 1) {
          gtr_cross_product(ndir, v2, v3);

        } else {
          gtr_cross_product(ndir, v0, v1);
          gtr_cross_product(ndir1, v2, v3);
          for (id = 0; id < nc; id++) {
            ndir[id] += ndir1[id];
          }
        }
      }

      gtr_normalize_v3(ndir, ndir, nc, 0);
      for (id = 0; id < nc; id++) {
        normals[nc * (cDd->offsets[it0->it] + ii) + id] = ndir[id];
      }
    }
  }

  return(RET_OK);

#undef VS
}
Пример #5
0
int32 refc_find_ref_coors(FMField *ref_coors,
                          int32 *cells, int32 n_cells,
                          int32 *status, int32 n_status,
                          FMField *coors,
                          Mesh *mesh,
                          int32 *candidates, int32 n_candidates,
                          int32 *offsets, int32 n_offsets,
                          int32 allow_extrapolation,
                          float64 qp_eps,
                          float64 close_limit,
                          void *_ctx)
{
  BasisContext *ctx = (BasisContext *) _ctx;

  int32 ip, ic, ii, imin, ok, xi_ok, ret = RET_OK;
  int32 D = mesh->topology->max_dim;
  int32 nc = mesh->geometry->dim;
  float64 d_min, dist;
  float64 *mesh_coors = mesh->geometry->coors;
  float64 buf3[3];
  FMField point[1], e_coors[1], xi[1];
  Indices cell_vertices[1];
  MeshEntity cell_ent[1];
  MeshConnectivity *cD0 = 0; // D -> 0

  mesh_setup_connectivity(mesh, D, 0);
  cD0 = mesh->topology->conn[IJ(D, D, 0)];

  fmf_pretend_nc(point, coors->nRow, 1, 1, nc, coors->val);

  fmf_pretend_nc(xi, 1, 1, 1, nc, buf3);
  fmf_fillC(xi, 0.0);

  ctx->is_dx = 0;

  for (ip = 0; ip < coors->nRow; ip++) {
    FMF_SetCell(point, ip);

    if (offsets[ip] == offsets[ip+1]) {
      status[ip] = 5;
      cells[ip] = 0;
      for (ii = 0; ii < nc; ii++) {
        ref_coors->val[nc*ip+ii] = 0.0;
      }
      continue;
    }

    ok = xi_ok = 0;
    d_min = 1e10;
    imin = candidates[offsets[ip]];

    for (ic = offsets[ip]; ic < offsets[ip+1]; ic++) {
      /* output("***** %d %d %d\n", ip, ic, candidates[ic]); */

      ctx->iel = candidates[ic];
      cell_ent->ii = candidates[ic];
      me_get_incident2(cell_ent, cell_vertices, cD0);

      _get_cell_coors(e_coors, cell_vertices, mesh_coors, nc,
                      ctx->e_coors_max->val);
      xi_ok = ctx->get_xi_dist(&dist, xi, point, e_coors, ctx);

      if (xi_ok) {
        if (dist < qp_eps) {
          imin = cell_ent->ii;
          ok = 1;
          break;
        } else if (dist < d_min) {
          d_min = dist;
          imin = cell_ent->ii;
        }
      } else if (dist < d_min) {
        d_min = dist;
        imin = cell_ent->ii;
      }
    }
    /* output("-> %d %d %d %.3e\n", imin, xi_ok, ok, d_min); */

    cells[ip] = imin;

    if (ok != 1) {
      if (!xi_ok) {
        status[ip] = 4;
      } else if (allow_extrapolation) {
        if (sqrt(d_min) < close_limit) {
          status[ip] = 1;
        } else {
          status[ip] = 2;
        }
      } else {
        status[ip] = 3;
      }
    } else {
      status[ip] = 0;
    }

    for (ii = 0; ii < nc; ii++) {
      ref_coors->val[nc*ip+ii] = xi->val[ii];
    }
    ERR_CheckGo(ret);
  }

 end_label:
  return(ret);
}
Пример #6
0
int32 refc_find_ref_coors_convex(FMField *ref_coors,
                                 int32 *cells, int32 n_cells,
                                 int32 *status, int32 n_status,
                                 FMField *coors,
                                 Mesh *mesh,
                                 FMField *centroids,
                                 FMField *normals0,
                                 FMField *normals1,
                                 int32 *ics, int32 n_ics,
                                 int32 allow_extrapolation,
                                 float64 qp_eps,
                                 float64 close_limit,
                                 void *_ctx)
{
  BasisContext *ctx = (BasisContext *) _ctx;
  int32 ip, ic, icell, icell_max = 0, ii, imin, ik, ok, ret = RET_OK;
  int32 xi_ok, hexa_reverse;
  int32 D = mesh->topology->max_dim;
  int32 dim = D - 1;
  int32 nc = mesh->geometry->dim;
  uint32 tri0[] = {0, 1, 3};
  uint32 tri1[] = {2, 3, 1};
  uint32 cell, cell0, cell00, facet;
  uint32 *noffs, *foffs, aux[2];
  uint32 *cell_types = mesh->topology->cell_types;
  float64 d_min, tmin, tt, dist;
  float64 *mesh_coors = mesh->geometry->coors;
  float64 buf3[3];
  float64 buf9[9];
  FMField point[1], centroid[1], _normals0[1], _normals1[1], e_coors[1], xi[1];
  Indices cell_vertices[1];
  MeshEntity cell_ent[1];
  MeshConnectivity *cD0 = 0; // D -> 0
  MeshConnectivity *c0D = 0; // 0 -> D
  MeshConnectivity *cDd = 0; // cell -> facet
  MeshConnectivity *cdD = 0; // facet -> cell
  MeshConnectivity *loc = 0;
  MeshConnectivity **locs = 0;

  mesh_setup_connectivity(mesh, D, 0);
  cD0 = mesh->topology->conn[IJ(D, D, 0)];

  mesh_setup_connectivity(mesh, 0, D);
  c0D = mesh->topology->conn[IJ(D, 0, D)];

  mesh_setup_connectivity(mesh, D, dim);
  cDd = mesh->topology->conn[IJ(D, D, dim)];
  noffs = cDd->offsets;

  mesh_setup_connectivity(mesh, dim, D);
  cdD = mesh->topology->conn[IJ(D, dim, D)];

  // Local entities - reference cell edges or faces.
  locs = (dim == 1) ? mesh->entities->edges : mesh->entities->faces;

  fmf_pretend_nc(point, coors->nRow, 1, 1, nc, coors->val);
  fmf_pretend_nc(centroid, centroids->nRow, 1, 1, nc, centroids->val);

  fmf_pretend_nc(xi, 1, 1, 1, nc, buf3);
  fmf_fillC(xi, 0.0);

  ctx->is_dx = 0;

  for (ip = 0; ip < coors->nRow; ip++) {
    ic = ics[ip];
    /* output("***** %d %d\n", ip, ic); */

    FMF_SetCell(point, ip);
    /* fmf_print(point, stdout, 0); */

    cell = cell0 = cell00 = c0D->indices[c0D->offsets[ic]];

    ok = icell = hexa_reverse = imin = 0;
    d_min = 1e10;
    while (1) {
      /* output("*** %d %d %d\n", icell, cell, hexa_reverse); */
      FMF_SetCell(centroid, cell);
      /* fmf_print(centroid, stdout, 0); */

      ctx->iel = cell;
      cell_ent->ii = cell;
      me_get_incident2(cell_ent, cell_vertices, cD0);

      loc = locs[cell_types[cell]];
      foffs = loc->offsets;

      if (cell_types[cell] != 4) { // No hexahedron -> planar facet.
        fmf_pretend_nc(_normals0, noffs[cell+1] - noffs[cell], 1, 1, nc,
                       normals0->val + nc * noffs[cell]);

        tmin = 1e10;
        for (ii = 0; ii < loc->num; ii++) {
          FMF_SetCell(_normals0, ii);
          ik = loc->indices[foffs[ii]];

          _intersect_line_plane(&tt, centroid->val, point->val,
                                mesh_coors + nc * cell_vertices->indices[ik],
                                _normals0->val, nc);
          if ((tt >= -qp_eps) && (tt < (tmin + qp_eps))) {
            imin = ii;
            tmin = tt;
          }
        }

        if (tmin >= (1.0 - qp_eps)) {
          _get_cell_coors(e_coors, cell_vertices, mesh_coors, nc,
                          ctx->e_coors_max->val);
          /* fmf_print(e_coors, stdout, 0); */

          xi_ok = ctx->get_xi_dist(&dist, xi, point, e_coors, ctx);

          d_min = Min(dist, d_min);
          if (xi_ok && (dist < qp_eps)) {
            ok = 1;
          }
          break;
        }

      } else { // Hexahedron -> non-planar facet in general.
        fmf_pretend_nc(_normals0, noffs[cell+1] - noffs[cell], 1, 1, nc,
                       normals0->val + nc * noffs[cell]);
        fmf_pretend_nc(_normals1, noffs[cell+1] - noffs[cell], 1, 1, nc,
                       normals1->val + nc * noffs[cell]);
        for (ii = 0; ii < loc->num; ii++) {
          FMF_SetCell(_normals0, ii);
          _get_tri_coors(buf9, loc->indices, foffs[ii],
                         tri0, mesh_coors, cell_vertices->indices);
          _intersect_line_triangle(&tt, centroid->val, point->val,
                                   buf9, _normals0->val);
          if ((tt >= -qp_eps) && (tt < 1e10)) {
            ok = 2;
            imin = ii;
            if ((tt >= (1.0 - qp_eps)) || hexa_reverse) {
              _get_cell_coors(e_coors, cell_vertices, mesh_coors, nc,
                              ctx->e_coors_max->val);

              xi_ok = ctx->get_xi_dist(&dist, xi, point, e_coors, ctx);

              d_min = Min(dist, d_min);
              if (xi_ok && (dist < qp_eps)) {
                ok = 1;
              } else {
                hexa_reverse = 1;
              }
            }
            break;
          }

          FMF_SetCell(_normals1, ii);
          _get_tri_coors(buf9, loc->indices, foffs[ii],
                         tri1, mesh_coors, cell_vertices->indices);
          _intersect_line_triangle(&tt, centroid->val, point->val,
                                   buf9, _normals1->val);
          if ((tt >= -qp_eps) && (tt < 1e10)) {
            ok = 2;
            imin = ii;
            if ((tt >= (1.0 - qp_eps)) || hexa_reverse) {
              _get_cell_coors(e_coors, cell_vertices, mesh_coors, nc,
                              ctx->e_coors_max->val);

              xi_ok = ctx->get_xi_dist(&dist, xi, point, e_coors, ctx);

              d_min = Min(dist, d_min);
              if (xi_ok && (dist < qp_eps)) {
                ok = 1;
              } else {
                hexa_reverse = 1;
              }
            }
            break;
          }
        }
        if (ok == 1) {
          break;
        }
        if (ok == 0) {
          errput("cannot intersect bilinear faces!\n");
          ERR_CheckGo(ret);
        }
      }

      facet = cDd->indices[cDd->offsets[cell] + imin];
      if ((cdD->offsets[facet+1] - cdD->offsets[facet]) == 2) {
        aux[0] = cdD->indices[cdD->offsets[facet]];
        aux[1] = cdD->indices[cdD->offsets[facet]+1];
        cell00 = cell0;
        cell0 = cell;
        cell = (aux[0] == cell) ? aux[1] : aux[0];

        if (cell == cell00) { // Ping-pong between two cells.
          hexa_reverse = 1;
        }

      } else { // Boundary facet.
        ctx->iel = cell;
        cell_ent->ii = cell;
        me_get_incident2(cell_ent, cell_vertices, cD0);

        _get_cell_coors(e_coors, cell_vertices, mesh_coors, nc,
                        ctx->e_coors_max->val);
        xi_ok = ctx->get_xi_dist(&dist, xi, point, e_coors, ctx);

        d_min = Min(dist, d_min);
        if (xi_ok && (dist < qp_eps)) {
          ok = 1;
        } else {
          ok = 0;
        }
        break;
      }

      icell++;
      icell_max = Max(icell, icell_max);
      if (icell > 10000) {
        errput("cannot find containing cell!\n");
        ERR_CheckGo(ret);
      }
    }

    /* fmf_print(xi, stdout, 0); */
    /* output("-> %d %d %d %.3e\n", cell, xi_ok, ok, d_min); */

    cells[ip] = cell;

    if (ok != 1) {
      if (!xi_ok) {
        status[ip] = 4;
      } else if (allow_extrapolation) {
        // Try using minimum distance xi.
        if (sqrt(d_min) < close_limit) {
          status[ip] = 1;
        } else {
          status[ip] = 2;
        }
      } else {
        status[ip] = 3;
      }
    } else {
      status[ip] = 0;
    }

    for (ii = 0; ii < nc; ii++) {
      ref_coors->val[nc*ip+ii] = xi->val[ii];
    }
  }
  /* output("%d\n", icell_max); */

 end_label:
  return(ret);
}