LCOV - code coverage report
Current view: top level - src/mesh - tetgen_io.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 74 110 67.3 %
Date: 2025-08-19 19:27:09 Functions: 4 5 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : // Local includes
      20             : #include "libmesh/tetgen_io.h"
      21             : 
      22             : #include "libmesh/cell_tet4.h"
      23             : #include "libmesh/cell_tet10.h"
      24             : #include "libmesh/mesh_base.h"
      25             : #include "libmesh/utility.h"
      26             : 
      27             : // C++ includes
      28             : #include <array>
      29             : #include <fstream>
      30             : 
      31             : namespace libMesh
      32             : {
      33             : 
      34             : // ------------------------------------------------------------
      35             : // TetgenIO class members
      36           2 : void TetGenIO::read (const std::string & name)
      37             : {
      38             :   // This is a serial-only process for now;
      39             :   // the Mesh should be read on processor 0 and
      40             :   // broadcast later
      41           1 :   libmesh_assert_equal_to (MeshOutput<MeshBase>::mesh().processor_id(), 0);
      42             : 
      43           2 :   std::string name_node, name_ele, dummy;
      44             : 
      45             :   // tetgen only works in 3D
      46           2 :   MeshInput<MeshBase>::mesh().set_mesh_dimension(3);
      47             : 
      48             : #if LIBMESH_DIM < 3
      49             :   libmesh_error_msg("Cannot open dimension 3 mesh file when configured without 3D support.");
      50             : #endif
      51             : 
      52             :   // Check name for *.node or *.ele extension.
      53             :   // Set std::istream for node_stream and ele_stream.
      54             :   //
      55           2 :   if (Utility::contains(name, ".node"))
      56             :     {
      57           0 :       name_node            = name;
      58           0 :       dummy                = name;
      59           0 :       std::size_t position = dummy.rfind(".node");
      60           0 :       name_ele             = dummy.replace(position, 5, ".ele");
      61             :     }
      62           2 :   else if (Utility::contains(name, ".ele"))
      63             :     {
      64           1 :       name_ele = name;
      65           1 :       dummy    = name;
      66           1 :       std::size_t position = dummy.rfind(".ele");
      67           1 :       name_node    = dummy.replace(position, 4, ".node");
      68             :     }
      69             :   else
      70           0 :     libmesh_error_msg("ERROR: Unrecognized file name: " << name);
      71             : 
      72             : 
      73             : 
      74             :   // Set the streams from which to read in
      75           4 :   std::ifstream node_stream (name_node.c_str());
      76           4 :   std::ifstream ele_stream  (name_ele.c_str());
      77             : 
      78           2 :   libmesh_error_msg_if(!node_stream.good() || !ele_stream.good(),
      79             :                        "Error while opening either "
      80             :                        << name_node
      81             :                        << " or "
      82             :                        << name_ele);
      83             : 
      84           1 :   libMesh::out<< "TetGenIO found the tetgen files to read " <<std::endl;
      85             : 
      86             :   // Skip the comment lines at the beginning
      87           2 :   this->skip_comment_lines (node_stream, '#');
      88           2 :   this->skip_comment_lines (ele_stream, '#');
      89             : 
      90             :   // Read the nodes and elements from the streams
      91           2 :   this->read_nodes_and_elem (node_stream, ele_stream);
      92           1 :   libMesh::out<< "TetGenIO read in nodes and elements " <<std::endl;
      93           2 : }
      94             : 
      95             : 
      96             : 
      97           2 : void TetGenIO::read_nodes_and_elem (std::istream & node_stream,
      98             :                                     std::istream & ele_stream)
      99             : {
     100           2 :   _num_nodes    = 0;
     101           2 :   _num_elements = 0;
     102             : 
     103             :   // Read all the datasets.
     104           2 :   this->node_in    (node_stream);
     105           2 :   this->element_in (ele_stream);
     106             : 
     107             :   // some more clean-up
     108           1 :   _assign_nodes.clear();
     109           2 : }
     110             : 
     111             : 
     112             : 
     113             : //----------------------------------------------------------------------
     114             : // Function to read in the node table.
     115           2 : void TetGenIO::node_in (std::istream & node_stream)
     116             : {
     117             :   // Check input buffer
     118           1 :   libmesh_assert (node_stream.good());
     119             : 
     120             :   // Get a reference to the mesh
     121           2 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
     122             : 
     123           2 :   unsigned int dimension=0, nAttributes=0, BoundaryMarkers=0;
     124             : 
     125           2 :   node_stream >> _num_nodes       // Read the number of nodes from the stream
     126           1 :               >> dimension        // Read the dimension from the stream
     127           1 :               >> nAttributes      // Read the number of attributes from stream
     128           1 :               >> BoundaryMarkers; // Read if or not boundary markers are included in *.node (0 or 1)
     129             : 
     130             :   // Read the nodal coordinates from the node_stream (*.node file).
     131           2 :   unsigned int node_lab=0;
     132             :   Real dummy;
     133             : 
     134             :   // If present, make room for node attributes to be stored.
     135           2 :   this->node_attributes.resize(nAttributes);
     136           2 :   for (unsigned i=0; i<nAttributes; ++i)
     137           0 :     this->node_attributes[i].resize(_num_nodes);
     138             : 
     139             : 
     140          22 :   for (unsigned int i=0; i<_num_nodes; i++)
     141             :     {
     142             :       // Check input buffer
     143          10 :       libmesh_assert (node_stream.good());
     144             : 
     145             :       std::array<Real, 3> xyz;
     146             : 
     147          10 :       node_stream >> node_lab  // node number
     148          10 :                   >> xyz[0]    // x-coordinate value
     149          10 :                   >> xyz[1]    // y-coordinate value
     150          10 :                   >> xyz[2];   // z-coordinate value
     151             : 
     152             :       // Read and store attributes from the stream.
     153          20 :       for (unsigned int j=0; j<nAttributes; j++)
     154           0 :         node_stream >> node_attributes[j][i];
     155             : 
     156             :       // Read (and discard) boundary marker if BoundaryMarker=1.
     157             :       // TODO: should we store this somehow?
     158          20 :       if (BoundaryMarkers == 1)
     159           0 :         node_stream >> dummy;
     160             : 
     161             :       // Store the new position of the node under its label.
     162             :       //_assign_nodes.emplace(node_lab,i);
     163          20 :       _assign_nodes[node_lab] = i;
     164             : 
     165          20 :       Point p(xyz[0]);
     166             : #if LIBMESH_DIM > 1
     167          20 :       p(1) = xyz[1];
     168             : #else
     169             :       libmesh_assert_equal_to(xyz[1], 0);
     170             : #endif
     171             : #if LIBMESH_DIM > 2
     172          20 :       p(2) = xyz[2];
     173             : #else
     174             :       libmesh_assert_equal_to(xyz[2], 0);
     175             : #endif
     176             : 
     177             :       // Add this point to the Mesh.
     178          20 :       mesh.add_point(p, i);
     179             :     }
     180           2 : }
     181             : 
     182             : 
     183             : 
     184             : //----------------------------------------------------------------------
     185             : // Function to read in the element table.
     186           2 : void TetGenIO::element_in (std::istream & ele_stream)
     187             : {
     188             :   // Check input buffer
     189           1 :   libmesh_assert (ele_stream.good());
     190             : 
     191             :   // Get a reference to the mesh
     192           2 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
     193             : 
     194             :   // Read the elements from the ele_stream (*.ele file).
     195           2 :   unsigned int element_lab=0, n_nodes=0, region_attribute=0;
     196             : 
     197           2 :   ele_stream >> _num_elements // Read the number of tetrahedrons from the stream.
     198           1 :              >> n_nodes       // Read the number of nodes per tetrahedron from the stream (defaults to 4).
     199           1 :              >> region_attribute; // Read the number of attributes from stream.
     200             : 
     201             :   // According to the Tetgen docs for .ele files:
     202             :   // http://wias-berlin.de/software/tetgen/1.5/doc/manual/manual006.html#ff_ele
     203             :   // region_attribute can either 0 or 1, and specifies whether, for
     204             :   // each tetrahedron, there is an extra integer specifying which
     205             :   // region it belongs to. Normally, this id matches a value in a
     206             :   // corresponding .poly or .smesh file, but here we simply use it to
     207             :   // set the subdomain_id of the element in question.
     208           2 :   libmesh_error_msg_if(region_attribute > 1,
     209             :                        "Invalid region_attribute " << region_attribute << " specified in .ele file.");
     210             : 
     211             :   // Vector that maps Tetgen node numbering to libMesh node numbering. Tet4s are
     212             :   // numbered identically, but Tet10s are not.
     213             :   static const unsigned int assign_elm_nodes[] = {0, 1, 2, 3, 9, 7, 4, 5, 8, 6};
     214             : 
     215           4 :   for (dof_id_type i=0; i<_num_elements; i++)
     216             :     {
     217           1 :       libmesh_assert (ele_stream.good());
     218             : 
     219             :       // TetGen only supports Tet4 and Tet10 elements.
     220           1 :       Elem * elem = nullptr;
     221             : 
     222           2 :       if (n_nodes==4)
     223           0 :         elem = mesh.add_elem(Elem::build_with_id(TET4, i));
     224             : 
     225           2 :       else if (n_nodes==10)
     226           3 :         elem = mesh.add_elem(Elem::build_with_id(TET10, i));
     227             : 
     228             :       else
     229           0 :         libmesh_error_msg("Elements with " << n_nodes <<
     230             :                           " nodes are not supported in the LibMesh tetgen module.");
     231             : 
     232           1 :       libmesh_assert(elem);
     233           1 :       libmesh_assert_equal_to (elem->n_nodes(), n_nodes);
     234             : 
     235             :       // The first number on the line is the tetrahedron number. We
     236             :       // have previously ignored this, preferring to set our own ids,
     237             :       // but this could be changed to respect the Tetgen numbering if
     238             :       // desired.
     239           1 :       ele_stream >> element_lab;
     240             : 
     241             :       // Read node labels
     242          22 :       for (dof_id_type j=0; j<n_nodes; j++)
     243             :         {
     244             :           dof_id_type node_label;
     245          10 :           ele_stream >> node_label;
     246             : 
     247             :           // Assign node to element
     248          20 :           elem->set_node(assign_elm_nodes[j],
     249          20 :             mesh.node_ptr(_assign_nodes[node_label]));
     250             :         }
     251             : 
     252             :       // Read the region attribute (if present) and use it to set the subdomain id.
     253           2 :       if (region_attribute)
     254             :         {
     255             :           unsigned int region;
     256           0 :           ele_stream >> region;
     257             : 
     258             :           // Make sure that the id we read can be successfully cast to
     259             :           // an integral value of type subdomain_id_type.
     260           0 :           elem->subdomain_id() = cast_int<subdomain_id_type>(region);
     261             :         }
     262             :     }
     263           2 : }
     264             : 
     265             : 
     266             : /**
     267             :  * This method implements writing a mesh to a specified ".poly" file.
     268             :  * ".poly" files defines so called Piecewise Linear Complex (PLC).
     269             :  */
     270           0 : void TetGenIO::write (const std::string & fname)
     271             : {
     272             :   // libmesh_assert three dimensions (should be extended later)
     273           0 :   libmesh_assert_equal_to (MeshOutput<MeshBase>::mesh().mesh_dimension(), 3);
     274             : 
     275           0 :   libmesh_error_msg_if(!Utility::contains(fname, ".poly"),
     276             :                        "ERROR: Unrecognized file name: " << fname);
     277             : 
     278             :   // Open the output file stream
     279           0 :   std::ofstream out_stream (fname.c_str());
     280             : 
     281             :   // Make sure it opened correctly
     282           0 :   if (!out_stream.good())
     283           0 :     libmesh_file_error(fname.c_str());
     284             : 
     285             :   // Get a reference to the mesh
     286           0 :   const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     287             : 
     288             :   // Begin interfacing with the .poly file
     289             :   {
     290             :     // header:
     291           0 :     out_stream << "# poly file output generated by libmesh\n"
     292           0 :                << mesh.n_nodes() << " 3 0 0\n";
     293             : 
     294             :     // write the nodes:
     295           0 :     for (auto v : make_range(mesh.n_nodes()))
     296           0 :       out_stream << v << " "
     297           0 :                  << mesh.point(v)(0) << " "
     298           0 :                  << mesh.point(v)(1) << " "
     299           0 :                  << mesh.point(v)(2) << "\n";
     300             :   }
     301             : 
     302             :   {
     303             :     // write the connectivity:
     304           0 :     out_stream << "# Facets:\n"
     305           0 :                << mesh.n_elem() << " 0\n";
     306             : 
     307           0 :     for (const auto & elem : mesh.active_element_ptr_range())
     308           0 :       out_stream << "1\n3 " // no. of facet polygons
     309             :         //  << elem->n_nodes() << " "
     310           0 :                  << elem->node_id(0)   << " "
     311           0 :                  << elem->node_id(1)   << " "
     312           0 :                  << elem->node_id(2)   << "\n";
     313             :   }
     314             : 
     315             :   // end of the file
     316           0 :   out_stream << "0\n"; // no holes output!
     317           0 :   out_stream << "\n\n# end of file\n";
     318           0 : }
     319             : 
     320             : } // namespace libMesh

Generated by: LCOV version 1.14