Пример #1
0
fe_mesh_t* fe_mesh_from_mesh(mesh_t* fv_mesh,
                             string_array_t* element_block_tags)
{
  fe_mesh_t* fe_mesh = fe_mesh_new(fv_mesh->comm, fv_mesh->num_nodes);

  if ((element_block_tags != NULL) && (element_block_tags->size > 1))
  {
    // Block-by-block construction.
    for (int b = 0; b < element_block_tags->size; ++b)
    {
      char* tag_name = element_block_tags->data[b];
      size_t num_elem;
      int* block_tag = mesh_tag(fv_mesh->cell_tags, tag_name, &num_elem);
      int* num_elem_faces = polymec_malloc(sizeof(int) * num_elem);
      int elem_faces_size = 0;
      for (int i = 0; i < num_elem; ++i)
      {
        int nef = fv_mesh->cell_face_offsets[block_tag[i]+1] - fv_mesh->cell_face_offsets[block_tag[i]];
        num_elem_faces[i] = nef;
        elem_faces_size += nef;
      }
      int* elem_faces = polymec_malloc(sizeof(int) * elem_faces_size);
      int offset = 0;
      for (int i = 0; i < num_elem; ++i)
      {
        for (int f = 0; f < num_elem_faces[i]; ++f, ++offset)
          elem_faces[offset+f] = fv_mesh->cell_faces[fv_mesh->cell_face_offsets[block_tag[i]+f]];
      }
      fe_block_t* block = polyhedral_fe_block_new((int)num_elem, num_elem_faces, fv_mesh->cell_faces);
      fe_mesh_add_block(fe_mesh, tag_name, block);
      polymec_free(num_elem_faces);
      polymec_free(elem_faces);
    }
  }
  else
  {
    // One honking block.
    int num_elem = fv_mesh->num_cells;
    int* num_elem_faces = polymec_malloc(sizeof(int) * num_elem);
    for (int i = 0; i < num_elem; ++i)
      num_elem_faces[i] = fv_mesh->cell_face_offsets[i+1] - fv_mesh->cell_face_offsets[i];
    fe_block_t* block = polyhedral_fe_block_new(num_elem, num_elem_faces, fv_mesh->cell_faces);
    fe_mesh_add_block(fe_mesh, "block_1", block);
    polymec_free(num_elem_faces);
  }

  // Copy coordinates.
  memcpy(fe_mesh_node_positions(fe_mesh), fv_mesh->nodes, sizeof(point_t) * fv_mesh->num_nodes);

  return fe_mesh;
}
Пример #2
0
void test_write_poly_exodus_file(void** state)
{
  fe_mesh_t* mesh = fe_mesh_new(MPI_COMM_WORLD, 14);
  int num_elem_faces[3] = {5, 5, 7};
  int elem_face_indices[] = {0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 7, 9, 10, 11, 12, 13, 14};
  fe_block_t* block = polyhedral_fe_block_new(3, num_elem_faces, elem_face_indices);
  fe_mesh_add_block(mesh, "nfaced_1", block);
  
  int num_face_nodes[15] = {2, 2, 3, 3, 3, 2, 2, 3, 3, 4, 4, 3, 3, 3, 3};
  int face_nodes[] = {4, 5, 7, 1, 0, 3, 5, 1, 3, 7, 7, 3, 0, 4, 0, 1, 5, 4, 4, 7, 6, 0,
                      2, 3, 6, 7, 3, 2, 6, 2, 0, 4, 7, 3, 13, 9, 11, 6, 10, 8, 12, 2, 6, 7,
                      11, 10, 10, 11, 9, 8, 8, 9, 13, 12, 12, 13, 3, 2};
  fe_mesh_set_face_nodes(mesh, 15, num_face_nodes, face_nodes);

  point_t* X = fe_mesh_node_positions(mesh);
  X[ 0].x = 0.0; X[ 0].y = 0.0; X[ 0].z = 0.0;
  X[ 1].x = 2.0; X[ 1].y = 0.0; X[ 1].z = 0.0;
  X[ 2].x = 0.0; X[ 2].y = 2.0; X[ 2].z = 0.0;
  X[ 3].x = 2.0; X[ 3].y = 2.0; X[ 3].z = 0.0;
  X[ 4].x = 0.0; X[ 4].y = 0.0; X[ 4].z = 2.0;
  X[ 5].x = 2.0; X[ 5].y = 0.0; X[ 5].z = 2.0;
  X[ 6].x = 0.0; X[ 6].y = 2.0; X[ 6].z = 2.0;
  X[ 7].x = 2.0; X[ 7].y = 2.0; X[ 7].z = 2.0;
  X[ 8].x = 0.0; X[ 8].y = 3.5; X[ 8].z = 1.0;
  X[ 9].x = 2.0; X[ 9].y = 3.5; X[ 9].z = 1.0;
  X[10].x = 0.0; X[10].y = 3.0; X[10].z = 1.0;
  X[11].x = 2.0; X[11].y = 3.0; X[11].z = 1.0;
  X[12].x = 0.0; X[12].y = 3.0; X[12].z = 0.5;
  X[13].x = 2.0; X[13].y = 3.0; X[13].z = 0.5;

  exodus_file_t* file = exodus_file_new(MPI_COMM_WORLD, "test-nfaced-2.exo");
  assert_true(file != NULL);
  exodus_file_set_title(file, "This is a test");
  exodus_file_write_mesh(file, mesh);
  exodus_file_close(file);

  fe_mesh_free(mesh);
}
Пример #3
0
mesh_t* mesh_from_fe_mesh(fe_mesh_t* fe_mesh)
{
  // Feel out the faces for the finite element mesh. Do we need to create 
  // them ourselves, or are they already all there?
  int num_cells = fe_mesh_num_elements(fe_mesh);
  int num_faces = fe_mesh_num_faces(fe_mesh);
  int* cell_face_offsets = polymec_malloc(sizeof(int) * (num_cells + 1));
  cell_face_offsets[0] = 0;
  int* cell_faces = NULL;
  int* face_node_offsets = NULL;
  int* face_nodes = NULL;
  if (num_faces == 0)
  {
    // Traverse the element blocks and figure out the number of faces per cell.
    int pos = 0, elem_offset = 0;
    char* block_name;
    fe_block_t* block;
    while (fe_mesh_next_block(fe_mesh, &pos, &block_name, &block))
    {
      int num_block_elem = fe_block_num_elements(block);
      fe_mesh_element_t elem_type = fe_block_element_type(block);
      int num_elem_faces = get_num_cell_faces(elem_type);
      for (int i = 0; i < num_block_elem; ++i)
        cell_face_offsets[elem_offset+i+1] = cell_face_offsets[elem_offset+i] + num_elem_faces;
      elem_offset += num_block_elem;
    }

    // Now assemble the faces for each cell.
    int_tuple_int_unordered_map_t* node_face_map = int_tuple_int_unordered_map_new();
    cell_faces = polymec_malloc(sizeof(int) * cell_face_offsets[num_cells]);

    // We build the face->node connectivity data on-the-fly.
    int_array_t* face_node_offsets_array = int_array_new();
    int_array_append(face_node_offsets_array, 0);
    int_array_t* face_nodes_array = int_array_new();

    pos = 0, elem_offset = 0;
    while (fe_mesh_next_block(fe_mesh, &pos, &block_name, &block))
    {
      int num_block_elem = fe_block_num_elements(block);
      fe_mesh_element_t elem_type = fe_block_element_type(block);
      int num_elem_nodes = fe_block_num_element_nodes(block, 0);
      int elem_nodes[num_elem_nodes];
      for (int i = 0; i < num_block_elem; ++i)
      {
        fe_block_get_element_nodes(block, i, elem_nodes);
        int offset = cell_face_offsets[elem_offset+i];
        get_cell_faces(elem_type, elem_nodes, node_face_map, 
                       &cell_faces[offset], face_node_offsets_array,
                       face_nodes_array);
      }
      elem_offset += num_block_elem;
    }

    // Record the total number of faces and discard the map.
    num_faces = node_face_map->size;
    int_tuple_int_unordered_map_free(node_face_map);

    // Gift the contents of the arrays to our pointers.
    face_node_offsets = face_node_offsets_array->data;
    int_array_release_data_and_free(face_node_offsets_array);
    face_nodes = face_nodes_array->data;
    int_array_release_data_and_free(face_nodes_array);
  }
  else
  {
    // Fill in these arrays block by block.
    int pos = 0;
    char* block_name;
    fe_block_t* block;
    int block_cell_offset = 0;
    while (fe_mesh_next_block(fe_mesh, &pos, &block_name, &block))
    {
      int num_block_elem = fe_block_num_elements(block);
      for (int i = 0; i < num_block_elem; ++i)
        cell_face_offsets[block_cell_offset+i] = cell_face_offsets[block_cell_offset+i-1] + block->elem_face_offsets[i];
      block_cell_offset += num_block_elem;
    }

    cell_faces = polymec_malloc(sizeof(int) * cell_face_offsets[num_cells]);
    pos = 0, block_cell_offset = 0;
    while (fe_mesh_next_block(fe_mesh, &pos, &block_name, &block))
    {
      int num_block_elem = fe_block_num_elements(block);
      memcpy(&cell_faces[cell_face_offsets[block_cell_offset]], block->elem_faces, sizeof(int) * block->elem_face_offsets[num_block_elem]);
      block_cell_offset += num_block_elem;
    }

    // We just borrow these from the mesh. Theeenks!
    face_node_offsets = fe_mesh->face_node_offsets;
    face_nodes = fe_mesh->face_nodes;
  }
  ASSERT(cell_faces != NULL);
  ASSERT(face_node_offsets != NULL);
  ASSERT(face_nodes != NULL);

  // Create the finite volume mesh and set up its cell->face and face->node 
  // connectivity.
  int num_ghost_cells = 0; // FIXME!
  mesh_t* mesh = mesh_new(fe_mesh_comm(fe_mesh), 
                          num_cells, num_ghost_cells, 
                          num_faces,
                          fe_mesh_num_nodes(fe_mesh));
  memcpy(mesh->cell_face_offsets, cell_face_offsets, sizeof(int) * (mesh->num_cells+1));
  memcpy(mesh->face_node_offsets, face_node_offsets, sizeof(int) * (mesh->num_faces+1));
  mesh_reserve_connectivity_storage(mesh);
  memcpy(mesh->cell_faces, cell_faces, sizeof(int) * (mesh->cell_face_offsets[mesh->num_cells]));
  memcpy(mesh->face_nodes, face_nodes, sizeof(int) * (mesh->face_node_offsets[mesh->num_faces]));

  // Set up face->cell connectivity.
  for (int c = 0; c < mesh->num_cells; ++c)
  {
    for (int f = mesh->cell_face_offsets[c]; f < mesh->cell_face_offsets[c+1]; ++f)
    {
      int face = mesh->cell_faces[f];
      if (mesh->face_cells[2*face] == -1)
        mesh->face_cells[2*face] = c;
      else
        mesh->face_cells[2*face+1] = c;
    }
  }

  // Set up face->edge connectivity and edge->node connectivity (if provided).
  if (fe_mesh->face_edges != NULL)
  {
    memcpy(mesh->face_edge_offsets, fe_mesh->face_edge_offsets, sizeof(int) * (mesh->num_faces+1));
    mesh->face_edges = polymec_malloc(sizeof(int) * fe_mesh->face_edge_offsets[mesh->num_faces]);
    memcpy(mesh->face_edges, fe_mesh->face_edges, sizeof(int) * fe_mesh->face_edge_offsets[mesh->num_faces]);
  }
  else
  {
    // Construct edges if we didn't find them.
    mesh_construct_edges(mesh);
  }

  // Copy the node positions into place.
  memcpy(mesh->nodes, fe_mesh_node_positions(fe_mesh), sizeof(point_t) * mesh->num_nodes);

  // Calculate geometry.
  mesh_compute_geometry(mesh);

  // Sets -> tags.
  int pos = 0, *set;
  size_t set_size;
  char* set_name;
  while (fe_mesh_next_element_set(fe_mesh, &pos, &set_name, &set, &set_size))
  {
    int* tag = mesh_create_tag(mesh->cell_tags, set_name, set_size);
    memcpy(tag, set, sizeof(int) * set_size);
  }
  pos = 0;
  while (fe_mesh_next_face_set(fe_mesh, &pos, &set_name, &set, &set_size))
  {
    int* tag = mesh_create_tag(mesh->face_tags, set_name, set_size);
    memcpy(tag, set, sizeof(int) * set_size);
  }
  pos = 0;
  while (fe_mesh_next_edge_set(fe_mesh, &pos, &set_name, &set, &set_size))
  {
    int* tag = mesh_create_tag(mesh->edge_tags, set_name, set_size);
    memcpy(tag, set, sizeof(int) * set_size);
  }
  pos = 0;
  while (fe_mesh_next_node_set(fe_mesh, &pos, &set_name, &set, &set_size))
  {
    int* tag = mesh_create_tag(mesh->node_tags, set_name, set_size);
    memcpy(tag, set, sizeof(int) * set_size);
  }

  // Clean up.
  polymec_free(cell_face_offsets);
  polymec_free(cell_faces);
  if (fe_mesh_num_faces(fe_mesh) == 0)
  {
    polymec_free(face_node_offsets);
    polymec_free(face_nodes);
  }

  return mesh;
}
Пример #4
0
void test_write_exodus_file(void** state)
{
  fe_mesh_t* mesh = fe_mesh_new(MPI_COMM_WORLD, 22);
  int elem1_node_indices[] = {0, 1, 2, 3, 4, 5, 6, 7};
  fe_block_t* block = fe_block_new(1, FE_HEXAHEDRON, 8, elem1_node_indices);
  fe_mesh_add_block(mesh, "block_1", block);
  int elem2_node_indices[] = {8, 9, 10, 11};
  block = fe_block_new(1, FE_TETRAHEDRON, 4, elem2_node_indices);
  fe_mesh_add_block(mesh, "block_2", block);
  int elem3_node_indices[] = {12, 13, 14, 15, 16, 17};
  block = fe_block_new(1, FE_WEDGE, 6, elem3_node_indices);
  fe_mesh_add_block(mesh, "block_3", block);
  int elem4_node_indices[] = {18, 19, 20, 21};
  block = fe_block_new(1, FE_TETRAHEDRON, 4, elem4_node_indices);
  fe_mesh_add_block(mesh, "block_4", block);
  
  point_t* X = fe_mesh_node_positions(mesh);
  X[ 0].x =  0.0; X[ 0].y =  0.0; X[ 0].z =  0.0;
  X[ 1].x = 10.0; X[ 1].y =  0.0; X[ 1].z =  0.0;
  X[ 2].x = 10.0; X[ 2].y =  0.0; X[ 2].z = -10.0;
  X[ 3].x =  1.0; X[ 3].y =  0.0; X[ 3].z = -10.0;
  X[ 4].x =  0.0; X[ 4].y = 10.0; X[ 4].z =  0.0;
  X[ 5].x = 10.0; X[ 5].y = 10.0; X[ 5].z =  0.0;
  X[ 6].x = 10.0; X[ 6].y = 10.0; X[ 6].z = -10.0;
  X[ 7].x =  1.0; X[ 7].y = 10.0; X[ 7].z = -10.0;

  X[ 8].x =  0.0; X[ 8].y =  0.0; X[ 8].z =  0.0;
  X[ 9].x =  1.0; X[ 9].y =  0.0; X[ 9].z =  5.0;
  X[10].x = 10.0; X[10].y =  0.0; X[10].z =  2.0;
  X[11].x =  7.0; X[11].y =  5.0; X[11].z =  3.0;

  X[12].x =  3.0; X[12].y =  0.0; X[12].z =  6.0;
  X[13].x =  6.0; X[13].y =  0.0; X[13].z =  0.0;
  X[14].x =  0.0; X[14].y =  0.0; X[14].z =  0.0;
  X[15].x =  3.0; X[15].y =  2.0; X[15].z =  6.0;
  X[16].x =  6.0; X[16].y =  2.0; X[16].z =  2.5;
  X[17].x =  2.0; X[17].y =  2.0; X[17].z =  0.0;

  X[18].x =  2.7; X[18].y =  1.7; X[18].z =  2.7;
  X[19].x =  6.0; X[19].y =  1.7; X[19].z =  3.3;
  X[20].x =  5.7; X[20].y =  1.7; X[20].z =  1.7;
  X[21].x =  3.7; X[21].y =  0.0; X[21].z =  2.3;

  // Node sets.
  int* ns1 = fe_mesh_create_node_set(mesh, "nset_1", 5);
  ns1[0] = 1; ns1[1] = 2; ns1[2] = 3; ns1[3] = 4; ns1[4] = 5;
  int* ns2 = fe_mesh_create_node_set(mesh, "nset_2", 3);
  ns2[0] = 11; ns2[1] = 12; ns2[2] = 13; 

  // Side set.
  int* ss1 = fe_mesh_create_side_set(mesh, "sset_1", 1);
  ss1[0] = 0; ss1[1] = 5;

  exodus_file_t* file = exodus_file_new(MPI_COMM_WORLD, "test-3d.exo");
  assert_true(file != NULL);
  exodus_file_set_title(file, "This is a test");
  exodus_file_write_mesh(file, mesh);
  exodus_file_close(file);

  fe_mesh_free(mesh);
}
Пример #5
0
fe_mesh_t* exodus_file_read_mesh(exodus_file_t* file)
{
  // Create the "host" FE mesh.
  fe_mesh_t* mesh = fe_mesh_new(file->comm, file->num_nodes);

  // Count up the number of polyhedral blocks.
  int num_poly_blocks = 0;
  for (int i = 0; i < file->num_elem_blocks; ++i)
  {
    int elem_block = file->elem_block_ids[i];
    char elem_type_name[MAX_NAME_LENGTH+1];
    int num_elem, num_nodes_per_elem, num_faces_per_elem;
    ex_get_block(file->ex_id, EX_ELEM_BLOCK, elem_block, 
                 elem_type_name, &num_elem,
                 &num_nodes_per_elem, NULL,
                 &num_faces_per_elem, NULL);
    fe_mesh_element_t elem_type = get_element_type(elem_type_name);
    if (elem_type == FE_POLYHEDRON)
      ++num_poly_blocks;
  }

  // If we have any polyhedral element blocks, we read a single face 
  // block that incorporates all of the polyhedral elements.
  if (num_poly_blocks > 0)
  {
    // Dig up the face block corresponding to this element block.
    char face_type[MAX_NAME_LENGTH+1];
    int num_faces, num_nodes;
    ex_get_block(file->ex_id, EX_FACE_BLOCK, file->face_block_ids[0], face_type, &num_faces,
                 &num_nodes, NULL, NULL, NULL);
    if (string_ncasecmp(face_type, "nsided", 6) != 0)
    {
      fe_mesh_free(mesh);
      ex_close(file->ex_id);
      polymec_error("Invalid face type for polyhedral element block.");
    }

    // Find the number of nodes for each face in the block.
    int* num_face_nodes = polymec_malloc(sizeof(int) * num_faces);
    ex_get_entity_count_per_polyhedra(file->ex_id, EX_FACE_BLOCK, 
                                      file->face_block_ids[0], 
                                      num_face_nodes);

    // Read face->node connectivity information.
    int face_node_size = 0;
    for (int i = 0; i < num_faces; ++i)
      face_node_size += num_face_nodes[i];
    int* face_nodes = polymec_malloc(sizeof(int) * face_node_size);
    ex_get_conn(file->ex_id, EX_FACE_BLOCK, 1, face_nodes, NULL, NULL);
    for (int i = 0; i < face_node_size; ++i)
      face_nodes[i] -= 1;
    fe_mesh_set_face_nodes(mesh, num_faces, num_face_nodes, face_nodes);

    // Clean up.
    polymec_free(num_face_nodes);
  }

  // Go over the element blocks and feel out the data.
  for (int i = 0; i < file->num_elem_blocks; ++i)
  {
    int elem_block = file->elem_block_ids[i];
    char elem_type_name[MAX_NAME_LENGTH+1];
    int num_elem, num_nodes_per_elem, num_faces_per_elem;
    ex_get_block(file->ex_id, EX_ELEM_BLOCK, elem_block, 
                 elem_type_name, &num_elem,
                 &num_nodes_per_elem, NULL,
                 &num_faces_per_elem, NULL);

    // Get the type of element for this block.
    fe_mesh_element_t elem_type = get_element_type(elem_type_name);
    fe_block_t* block = NULL;
    char block_name[MAX_NAME_LENGTH+1];
    if (elem_type == FE_POLYHEDRON)
    {
      // Find the number of faces for each element in the block.
      int* num_elem_faces = polymec_malloc(sizeof(int) * num_elem);
      ex_get_entity_count_per_polyhedra(file->ex_id, EX_ELEM_BLOCK, elem_block, 
                                        num_elem_faces);

      // Get the element->face connectivity.
      int elem_face_size = 0;
      for (int j = 0; j < num_elem; ++j)
        elem_face_size += num_elem_faces[j];
      int* elem_faces = polymec_malloc(sizeof(int) * elem_face_size);
      ex_get_conn(file->ex_id, EX_ELEM_BLOCK, elem_block, NULL, NULL, elem_faces);

      // Subtract 1 from each element face.
      for (int j = 0; j < elem_face_size; ++j)
        elem_faces[j] -= 1;

      // Create the element block.
      block = polyhedral_fe_block_new(num_elem, num_elem_faces, elem_faces);
    }
    else if (elem_type != FE_INVALID)
    {
      // Get the element's nodal mapping.
      int* node_conn = polymec_malloc(sizeof(int) * num_elem * num_nodes_per_elem);
      ex_get_conn(file->ex_id, EX_ELEM_BLOCK, elem_block, node_conn, NULL, NULL);
      
      // Subtract 1 from each element node.
      for (int j = 0; j < num_elem * num_nodes_per_elem; ++j)
        node_conn[j] -= 1;

      // Build the element block.
      block = fe_block_new(num_elem, elem_type, num_nodes_per_elem, node_conn);
    }
    else
    {
      fe_mesh_free(mesh);
      ex_close(file->ex_id);
      polymec_error("Block %d contains an invalid (3D) element type.", elem_block);
    }

    // Fish out the element block name if it has one, or make a default.
    ex_get_name(file->ex_id, EX_ELEM_BLOCK, elem_block, block_name);
    if (strlen(block_name) == 0)
      sprintf(block_name, "block_%d", elem_block);

    // Add the element block to the mesh.
    fe_mesh_add_block(mesh, block_name, block);
  }

  // Fetch node positions and compute geometry.
  real_t x[file->num_nodes], y[file->num_nodes], z[file->num_nodes];
  ex_get_coord(file->ex_id, x, y, z);
  point_t* X = fe_mesh_node_positions(mesh);
  for (int n = 0; n < file->num_nodes; ++n)
  {
    X[n].x = x[n];
    X[n].y = y[n];
    X[n].z = z[n];
  }

  // Fetch sets of entities.
  for (int i = 1; i <= file->num_elem_sets; ++i)
    fetch_set(file, EX_ELEM_SET, i, mesh, fe_mesh_create_element_set);
  for (int i = 1; i <= file->num_face_sets; ++i)
    fetch_set(file, EX_FACE_SET, i, mesh, fe_mesh_create_face_set);
  for (int i = 1; i <= file->num_edge_sets; ++i)
    fetch_set(file, EX_EDGE_SET, i, mesh, fe_mesh_create_edge_set);
  for (int i = 1; i <= file->num_node_sets; ++i)
    fetch_set(file, EX_NODE_SET, i, mesh, fe_mesh_create_node_set);
  for (int i = 1; i <= file->num_side_sets; ++i)
    fetch_set(file, EX_SIDE_SET, i, mesh, fe_mesh_create_side_set);

  return mesh;
}
Пример #6
0
void exodus_file_write_mesh(exodus_file_t* file,
                            fe_mesh_t* mesh)
{
  ASSERT(file->writing);

  // See whether we have polyhedral blocks, and whether the non-polyhedral
  // blocks have supported element types.
  int num_blocks = fe_mesh_num_blocks(mesh);
  bool is_polyhedral = false;
  int pos = 0;
  char* block_name;
  fe_block_t* block;
  while (fe_mesh_next_block(mesh, &pos, &block_name, &block))
  {
    fe_mesh_element_t elem_type = fe_block_element_type(block);
    if (elem_type == FE_POLYHEDRON)
    {
      is_polyhedral = true;
      break;
    }
    else if (elem_type != FE_INVALID)
    {
      // Check the number of nodes for the element.
      if (!element_is_supported(elem_type, fe_block_num_element_nodes(block, 0)))
        polymec_error("exodus_file_write_mesh: Element type in block %s has invalid number of nodes.", block_name);
    }
    else
      polymec_error("exodus_file_write_mesh: Invalid element type for block %s.", block_name);
  }

  // Write out information about elements, faces, edges, nodes.
  file->num_nodes = fe_mesh_num_nodes(mesh);
  ex_init_params params;
  strcpy(params.title, file->title);
  params.num_dim = 3;
  params.num_nodes = file->num_nodes;
  int num_edges = fe_mesh_num_edges(mesh);
  params.num_edge = num_edges;
  params.num_edge_blk = 0;
  int num_faces = fe_mesh_num_faces(mesh);
  params.num_face = num_faces;
  params.num_face_blk = (is_polyhedral) ? 1 : 0;
  int num_elem = fe_mesh_num_elements(mesh);
  params.num_elem = num_elem;
  params.num_elem_blk = num_blocks;
  params.num_elem_sets = file->num_elem_sets = fe_mesh_num_element_sets(mesh);
  params.num_face_sets = file->num_face_sets = fe_mesh_num_face_sets(mesh);
  params.num_edge_sets = file->num_edge_sets = fe_mesh_num_edge_sets(mesh);
  params.num_node_sets = file->num_node_sets = fe_mesh_num_node_sets(mesh);
  params.num_side_sets = file->num_side_sets = fe_mesh_num_side_sets(mesh);
  params.num_elem_maps = 0;
  params.num_face_maps = 0;
  params.num_edge_maps = 0;
  params.num_node_maps = 0;
  ex_put_init_ext(file->ex_id, &params);

  // If we have any polyhedral element blocks, we write out a single face 
  // block that incorporates all of the polyhedral elements.
  if (is_polyhedral)
  {
    // Generate face->node connectivity information.
    int num_pfaces = fe_mesh_num_faces(mesh);
    int face_node_size = 0;
    int num_face_nodes[num_pfaces];
    for (int f = 0; f < num_pfaces; ++f)
    {
      int num_nodes = fe_mesh_num_face_nodes(mesh, f);
      num_face_nodes[f] = num_nodes;
      face_node_size += num_nodes;
    }
    int* face_nodes = polymec_malloc(sizeof(int) * face_node_size);
    int offset = 0;
    for (int f = 0; f < num_pfaces; ++f)
    {
      fe_mesh_get_face_nodes(mesh, f, &face_nodes[offset]);
      offset += num_face_nodes[f];
    }
    for (int i = 0; i < face_node_size; ++i)
      face_nodes[i] += 1;

    // Write an "nsided" face block.
    ex_put_block(file->ex_id, EX_FACE_BLOCK, 1, "nsided",
                 num_pfaces, face_node_size, 0, 0, 0);
    ex_put_name(file->ex_id, EX_FACE_BLOCK, 1, "face_block");
    ex_put_conn(file->ex_id, EX_FACE_BLOCK, 1, face_nodes, NULL, NULL);

    // Clean up.
    polymec_free(face_nodes);

    // Number of nodes per face.
    ex_put_entity_count_per_polyhedra(file->ex_id, EX_FACE_BLOCK, 
                                      1, num_face_nodes); 
  }

  // Go over the element blocks and write out the data.
  pos = 0;
  while (fe_mesh_next_block(mesh, &pos, &block_name, &block))
  {
    int elem_block = pos;
    int num_e = fe_block_num_elements(block);
    fe_mesh_element_t elem_type = fe_block_element_type(block);
    if (elem_type == FE_POLYHEDRON)
    {
      // Count up the faces in the block and write the block information.
      int tot_num_elem_faces = 0;
      int faces_per_elem[num_e];
      for (int i = 0; i < num_e; ++i)
      {
        faces_per_elem[i] = fe_block_num_element_faces(block, i);
        tot_num_elem_faces += faces_per_elem[i];
      }
      ex_put_block(file->ex_id, EX_ELEM_BLOCK, elem_block, "nfaced", 
                   num_e, 0, 0, tot_num_elem_faces, 0);

      // Write elem->face connectivity information.
      int elem_faces[tot_num_elem_faces], offset = 0;
      for (int i = 0; i < num_e; ++i)
      {
        fe_block_get_element_faces(block, i, &elem_faces[offset]);
        offset += faces_per_elem[i];
      }
      for (int i = 0; i < tot_num_elem_faces; ++i)
        elem_faces[i] += 1;
      ex_put_conn(file->ex_id, EX_ELEM_BLOCK, elem_block, NULL, NULL, elem_faces);
      ex_put_entity_count_per_polyhedra(file->ex_id, EX_ELEM_BLOCK, elem_block, faces_per_elem); 
    }
    else if (elem_type != FE_INVALID)
    {
      // Get element information.
      char elem_type_name[MAX_NAME_LENGTH+1];
      get_elem_name(elem_type, elem_type_name);
      int num_nodes_per_elem = fe_block_num_element_nodes(block, 0);

      // Write the block.
      ex_put_block(file->ex_id, EX_ELEM_BLOCK, elem_block, elem_type_name, 
                   num_e, num_nodes_per_elem, 0, 0, 0);

      // Write the elem->node connectivity.
      int elem_nodes[num_e* num_nodes_per_elem], offset = 0;
      for (int i = 0; i < num_e; ++i)
      {
        fe_block_get_element_nodes(block, i, &elem_nodes[offset]);
        offset += num_nodes_per_elem;
      }
      for (int i = 0; i < num_e* num_nodes_per_elem; ++i)
        elem_nodes[i] += 1;
      ex_put_conn(file->ex_id, EX_ELEM_BLOCK, elem_block, elem_nodes, NULL, NULL);
    }

    // Set the element block name.
    ex_put_name(file->ex_id, EX_ELEM_BLOCK, elem_block, block_name);
  }

  // Set node positions.
  real_t x[file->num_nodes], y[file->num_nodes], z[file->num_nodes];
  point_t* X = fe_mesh_node_positions(mesh);
  for (int n = 0; n < file->num_nodes; ++n)
  {
    x[n] = X[n].x;
    y[n] = X[n].y;
    z[n] = X[n].z;
  }
  ex_put_coord(file->ex_id, x, y, z);
  char* coord_names[3] = {"x", "y", "z"};
  ex_put_coord_names(file->ex_id, coord_names);

  // Write sets of entities.
  int *set, set_id = 0;
  size_t set_size;
  char* set_name;
  pos = set_id = 0;
  while (fe_mesh_next_element_set(mesh, &pos, &set_name, &set, &set_size))
    write_set(file, EX_ELEM_SET, ++set_id, set_name, set, set_size);
  pos = set_id = 0;
  while (fe_mesh_next_face_set(mesh, &pos, &set_name, &set, &set_size))
    write_set(file, EX_FACE_SET, ++set_id, set_name, set, set_size);
  pos = set_id = 0;
  while (fe_mesh_next_edge_set(mesh, &pos, &set_name, &set, &set_size))
    write_set(file, EX_EDGE_SET, ++set_id, set_name, set, set_size);
  pos = set_id = 0;
  while (fe_mesh_next_node_set(mesh, &pos, &set_name, &set, &set_size))
    write_set(file, EX_NODE_SET, ++set_id, set_name, set, set_size);
  pos = set_id = 0;
  while (fe_mesh_next_side_set(mesh, &pos, &set_name, &set, &set_size))
    write_set(file, EX_SIDE_SET, ++set_id, set_name, set, set_size);
}