13#include <unordered_set>
21#define CHECK_NETCDF_CODE(return_code)\
23 if ((return_code) != NC_NOERR)\
25 MFEM_ABORT("NetCDF error: " << nc_strerror((return_code)));\
34namespace ExodusIISideMaps
58namespace ExodusIINodeOrderings
64 1, 2, 3, 4, 5, 8, 6, 7, 9, 10
69 1, 2, 3, 4, 5, 6, 7, 8, 9,
70 10, 11, 12, 17, 18, 19, 20, 13, 14,
71 15, 16, 27, 21, 26, 25, 23, 22, 24
76 1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 10, 11, 12, 16, 17, 18
81 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
86namespace ExodusIILabels
128 ExodusIIWriter(
Mesh & mesh) : mesh{mesh} {}
130 ExodusIIWriter() =
delete;
138 void PrintExodusII(std::string fpath,
int flags = NC_CLOBBER);
144 static void PrintExodusII(Mesh & mesh, std::string fpath,
145 int flags = NC_CLOBBER);
149 void OpenExodusII(std::string fpath,
int flags);
152 void CloseExodusII();
159 void GenerateExodusIIElementBlocks();
163 void GenerateExodusIIBoundaryInfo();
167 std::unordered_set<int> GenerateUniqueNodeIDs();
170 void ExtractVertexCoordinates(std::vector<double> & coordx,
171 std::vector<double> & coordy,
172 std::vector<double> & coordz);
176 void WriteNodeConnectivityForBlock(
const int block_id);
179 void WriteBoundaries();
182 void WriteBlockIDs();
188 void WriteNumOfElements();
191 void WriteFloatingPointWordSize();
194 void WriteAPIVersion();
197 void WriteDatabaseVersion();
200 void WriteMaxLineLength();
203 void WriteMaxNameLength();
206 void WriteNumElementBlocks();
209 void WriteElementBlocks();
213 void WriteElementBlockParameters(
int block_id);
216 void WriteNodalCoordinates();
221 void WriteFileSize();
224 void WriteNodeSets();
227 void WriteMeshDimension();
231 void WriteTimesteps();
238 void WriteDummyVariable();
241 void DefineDimension(
const char *name,
size_t len,
int *dim_id);
244 void DefineVar(
const char *name, nc_type xtype,
int ndims,
const int *dimidsp,
249 void PutVar(
int varid,
const void * data);
252 void DefineAndPutVar(
const char *name, nc_type xtype,
int ndims,
253 const int *dimidsp,
const void *data);
257 void PutAtt(
int varid,
const char *name, nc_type xtype,
size_t len,
262 char * GenerateLabel(
const char * format, ...);
266 void WriteExodusIIFileInformation();
269 void WriteExodusIIMeshInformation();
273 void CheckNodalFESpaceIsSecondOrderH1()
const;
279 bool file_open{
false};
285 std::vector<int> block_ids;
286 std::map<int, Element::Type> element_type_for_block_id;
287 std::map<int, std::vector<int>> element_ids_for_block_id;
289 std::vector<int> boundary_ids;
290 std::map<int, std::vector<int>> exodusII_element_ids_for_boundary_id;
291 std::map<int, std::vector<int>> exodusII_side_ids_for_boundary_id;
296 ExodusIIWriter::PrintExodusII(*
this, fpath);
299void ExodusIIWriter::DefineDimension(
const char *name,
size_t len,
int *dim_id)
302 CHECK_NETCDF_CODE(nc_def_dim(exid, name, len, dim_id));
305void ExodusIIWriter::DefineVar(
const char *name, nc_type xtype,
int ndims,
306 const int *dimidsp,
int *varidp)
309 CHECK_NETCDF_CODE(nc_def_var(exid, name, xtype, ndims, dimidsp,
313void ExodusIIWriter::PutAtt(
int varid,
const char *name, nc_type xtype,
314 size_t len,
const void * data)
317 CHECK_NETCDF_CODE(nc_put_att(exid, varid, name, xtype, len, data));
320void ExodusIIWriter::PutVar(
int varid,
const void * data)
323 CHECK_NETCDF_CODE(nc_put_var(exid, varid, data));
326void ExodusIIWriter::DefineAndPutVar(
const char *name, nc_type xtype,
int ndims,
327 const int *dimidsp,
const void *data)
330 DefineVar(name, xtype, ndims, dimidsp, &varid);
334void ExodusIIWriter::WriteExodusIIFileInformation()
338 WriteDatabaseVersion();
341 WriteFloatingPointWordSize();
345 WriteMaxNameLength();
346 WriteMaxLineLength();
348 WriteDummyVariable();
351void ExodusIIWriter::WriteExodusIIMeshInformation()
353 WriteMeshDimension();
354 WriteNumOfElements();
358 WriteNodalCoordinates();
360 WriteElementBlocks();
365void ExodusIIWriter::PrintExodusII(std::string fpath,
int flags)
367 OpenExodusII(fpath, flags);
369 WriteExodusIIFileInformation();
370 WriteExodusIIMeshInformation();
374 mfem::out <<
"Mesh successfully written to Exodus II file" << std::endl;
377void ExodusIIWriter::PrintExodusII(Mesh & mesh, std::string fpath,
380 ExodusIIWriter writer(mesh);
382 writer.PrintExodusII(fpath, flags);
385void ExodusIIWriter::OpenExodusII(std::string fpath,
int flags)
389 CHECK_NETCDF_CODE(nc_create(fpath.c_str(), flags, &exid));
394void ExodusIIWriter::CloseExodusII()
396 if (!file_open) {
return; }
398 CHECK_NETCDF_CODE(nc_close(exid));
404ExodusIIWriter::~ExodusIIWriter()
409void ExodusIIWriter::WriteTitle()
416void ExodusIIWriter::WriteNumOfElements()
423void ExodusIIWriter::WriteFloatingPointWordSize()
425 const int word_size = 8;
431void ExodusIIWriter::WriteAPIVersion()
437void ExodusIIWriter::WriteDatabaseVersion()
443void ExodusIIWriter::WriteMaxNameLength()
449void ExodusIIWriter::WriteMaxLineLength()
455void ExodusIIWriter::WriteBlockIDs()
466void ExodusIIWriter::WriteElementBlocks()
468 GenerateExodusIIElementBlocks();
470 WriteNumElementBlocks();
473 for (
int block_id : block_ids)
475 WriteElementBlockParameters(block_id);
479char * ExodusIIWriter::GenerateLabel(
const char * format, ...)
482 va_start(arglist, format);
484 const int buffer_size = 100;
486 static char buffer[buffer_size];
487 int nwritten = vsnprintf(buffer, buffer_size, format, arglist);
489 bool ok = (nwritten > 0 && nwritten < buffer_size);
492 MFEM_ABORT(
"Unable to write characters to buffer.");
499void ExodusIIWriter::WriteElementBlockParameters(
int block_id)
501 char * label{
nullptr};
503 const std::vector<int> & block_element_ids = element_ids_for_block_id.at(
505 const Element * front_element = mesh.GetElement(block_element_ids.front());
508 label = GenerateLabel(
"num_el_in_blk%d", block_id);
510 int num_el_in_blk_id;
511 DefineDimension(label, block_element_ids.size(),
515 label = GenerateLabel(
"num_nod_per_el%d", block_id);
517 int num_node_per_el_id;
521 CheckNodalFESpaceIsSecondOrderH1();
524 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
526 auto & block_elements = element_ids_for_block_id.at(block_id);
528 int first_element_id = block_elements.front();
531 fespace->GetElementDofs(first_element_id, dofs);
533 DefineDimension(label, dofs.Size(),
534 &num_node_per_el_id);
538 DefineDimension(label, front_element->GetNVertices(),
539 &num_node_per_el_id);
543 label = GenerateLabel(
"num_edg_per_el%d", block_id);
545 int num_edg_per_el_id;
546 DefineDimension(label, front_element->GetNEdges(),
550 label = GenerateLabel(
"num_fac_per_el%d", block_id);
552 int num_fac_per_el_id;
553 DefineDimension(label, front_element->GetNFaces(),
557 WriteNodeConnectivityForBlock(block_id);
560 std::string element_type;
562 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
565 MFEM_ASSERT((!fespace || (fespace &&
566 !fespace->IsVariableOrder())),
567 "Spaces with varying element orders are not supported.");
569 bool higher_order = (fespace && fespace->GetMaxElementOrder() > 1);
571 switch (front_element->GetType())
574 element_type = higher_order ?
"HEX27" :
"Hex8";
577 element_type = higher_order ?
"TETRA10" :
"TETRA4";
580 element_type = higher_order ?
"WEDGE18" :
"WEDGE6";
583 element_type = higher_order ?
"PYRAMID14" :
"PYRAMID5";
586 MFEM_ABORT(
"Unsupported MFEM element type: " << front_element->GetType());
589 label = GenerateLabel(
"connect%d", block_id);
592 CHECK_NETCDF_CODE(nc_inq_varid(exid, label, &connect_id));
595 element_type.length(),
596 element_type.c_str());
599void ExodusIIWriter::WriteNodalCoordinates()
602 std::unordered_set<int> unique_node_ids = GenerateUniqueNodeIDs();
603 const size_t num_nodes = unique_node_ids.size();
607 DefineDimension(
"num_nodes", num_nodes, &num_nodes_id);
612 std::vector<double> coordx(num_nodes);
613 std::vector<double> coordy(num_nodes);
614 std::vector<double> coordz(mesh.Dimension() == 3 ? num_nodes : 0);
616 ExtractVertexCoordinates(coordx, coordy, coordz);
626 if (mesh.Dimension() == 3)
634void ExodusIIWriter::WriteBoundaries()
637 GenerateExodusIIBoundaryInfo();
640 int num_side_sets_ids;
646 int boundary_ids_dim;
653 boundary_ids.data());
656 for (
int boundary_id : boundary_ids)
658 size_t num_elements_for_boundary = exodusII_element_ids_for_boundary_id.at(
661 char * label = GenerateLabel(
"num_side_ss%d", boundary_id);
664 DefineDimension(label, num_elements_for_boundary,
669 for (
int boundary_id : boundary_ids)
671 const std::vector<int> & side_ids = exodusII_side_ids_for_boundary_id.at(
674 char * label = GenerateLabel(
"side_ss%d_dim", boundary_id);
677 DefineDimension(label, side_ids.size(), &side_id_dim);
679 label = GenerateLabel(
"side_ss%d", boundary_id);
680 DefineAndPutVar(label, NC_INT, 1, &side_id_dim, side_ids.data());
684 for (
int boundary_id : boundary_ids)
686 const std::vector<int> & element_ids = exodusII_element_ids_for_boundary_id.at(
689 char * label = GenerateLabel(
"elem_ss%d_dim", boundary_id);
692 DefineDimension(label, element_ids.size(), &elem_ids_dim);
694 label = GenerateLabel(
"elem_ss%d", boundary_id);
695 DefineAndPutVar(label, NC_INT, 1, &elem_ids_dim,
700void ExodusIIWriter::WriteNodeConnectivityForBlock(
const int block_id)
702 std::vector<int> block_node_connectivity;
704 int * node_ordering_map =
nullptr;
707 Element::Type block_type = element_type_for_block_id.at(block_id);
712 node_ordering_map = (
int *)
716 node_ordering_map = (
int *)
720 node_ordering_map = (
int *)
724 node_ordering_map = (
int *)
728 MFEM_ABORT(
"Higher-order elements of type '" << block_type <<
729 "' are not supported.");
732 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
734 Array<int> element_dofs;
735 for (
int element_id : element_ids_for_block_id.at(block_id))
739 fespace->GetElementDofs(element_id, element_dofs);
741 for (
int j = 0; j < element_dofs.Size(); j++)
743 int dof_index = node_ordering_map[j] - 1;
744 int dof = element_dofs[dof_index];
746 block_node_connectivity.push_back(dof + 1);
751 mesh.GetElementVertices(element_id, element_dofs);
753 for (
int vertex_id : element_dofs)
755 block_node_connectivity.push_back(vertex_id + 1);
760 char * label = GenerateLabel(
"connect%d_dim", block_id);
762 int node_connectivity_dim;
763 DefineDimension(label, block_node_connectivity.size(),
764 &node_connectivity_dim);
767 label = GenerateLabel(
"connect%d", block_id);
768 DefineAndPutVar(label, NC_INT, 1, &node_connectivity_dim,
769 block_node_connectivity.data());
773void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
774 std::vector<double> & coordy,
775 std::vector<double> & coordz)
779 std::unordered_set<int> unordered_node_ids = GenerateUniqueNodeIDs();
781 std::vector<int> sorted_node_ids(unordered_node_ids.size());
782 sorted_node_ids.assign(unordered_node_ids.begin(), unordered_node_ids.end());
783 std::sort(sorted_node_ids.begin(), sorted_node_ids.end());
785 double coordinates[3];
786 for (
size_t i = 0; i < sorted_node_ids.size(); i++)
788 int node_id = sorted_node_ids[i];
790 mesh.GetNode(node_id, coordinates);
792 coordx[node_id] = coordinates[0];
793 coordy[node_id] = coordinates[1];
795 if (mesh.Dimension() == 3)
797 coordz[node_id] = coordinates[2];
803 for (
int ivertex = 0; ivertex < mesh.GetNV(); ivertex++)
805 double * coordinates = mesh.GetVertex(ivertex);
807 coordx[ivertex] = coordinates[0];
808 coordy[ivertex] = coordinates[1];
810 if (mesh.Dimension() == 3)
812 coordz[ivertex] = coordinates[2];
818void ExodusIIWriter::WriteFileSize()
822 const int file_size = 1;
828void ExodusIIWriter::WriteMeshDimension()
835void ExodusIIWriter::WriteNodeSets()
838 int num_node_sets_ids;
843void ExodusIIWriter::WriteTimesteps()
850void ExodusIIWriter::WriteDummyVariable()
852 int dummy_var_dim_id, dummy_value = 1;
854 DefineDimension(
"dummy_var_dim", 1, &dummy_var_dim_id);
856 DefineAndPutVar(
"dummy_var", NC_INT, 1, &dummy_var_dim_id,
860void ExodusIIWriter::GenerateExodusIIElementBlocks()
863 element_ids_for_block_id.clear();
864 element_type_for_block_id.clear();
866 std::unordered_set<int> observed_block_ids;
869 for (
int ielement = 0; ielement < mesh.GetNE(); ielement++)
873 int block_id = mesh.GetAttribute(ielement);
875 if (observed_block_ids.count(block_id) == 0)
877 block_ids.push_back(block_id);
879 element_type_for_block_id[block_id] = element_type;
880 element_ids_for_block_id[block_id] = { ielement };
882 observed_block_ids.insert(block_id);
886 auto & block_element_ids = element_ids_for_block_id.at(block_id);
887 block_element_ids.push_back(ielement);
891 if (element_type != element_type_for_block_id.at(block_id))
893 MFEM_ABORT(
"Multiple element types are defined for block: " << block_id);
899void ExodusIIWriter::WriteNumElementBlocks()
908std::unordered_set<int> ExodusIIWriter::GenerateUniqueNodeIDs()
910 std::unordered_set<int> unique_node_ids;
912 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
915 for (
int ielement = 0; ielement < mesh.GetNE(); ielement++)
919 fespace->GetElementDofs(ielement, element_dofs);
923 mesh.GetElementVertices(ielement, element_dofs);
926 for (
int dof : element_dofs)
928 unique_node_ids.insert(dof);
932 return unique_node_ids;
935void ExodusIIWriter::GenerateExodusIIBoundaryInfo()
938 boundary_ids.clear();
939 exodusII_element_ids_for_boundary_id.clear();
940 exodusII_side_ids_for_boundary_id.clear();
947 struct GlobalFaceIndexInfo
950 int local_face_index;
952 GlobalFaceIndexInfo() : element_index{0}, local_face_index{0} {}
954 GlobalFaceIndexInfo(
int element_index,
int local_face_index)
956 this->element_index = element_index;
957 this->local_face_index = local_face_index;
961 std::unordered_map<int, GlobalFaceIndexInfo>
962 mfem_face_index_info_for_global_face_index;
963 std::unordered_set<int> blacklisted_global_face_indices;
965 Array<int> global_face_indices, orient;
966 for (
int ielement = 0; ielement < mesh.GetNE(); ielement++)
968 mesh.GetElementFaces(ielement, global_face_indices, orient);
970 for (
int iface = 0; iface < global_face_indices.Size(); iface++)
972 int face_index = global_face_indices[iface];
974 if (blacklisted_global_face_indices.count(face_index))
979 if (mfem_face_index_info_for_global_face_index.count(face_index))
982 blacklisted_global_face_indices.insert(face_index);
983 mfem_face_index_info_for_global_face_index.erase(face_index);
987 mfem_face_index_info_for_global_face_index[face_index] = GlobalFaceIndexInfo(
992 std::unordered_set<int> unique_boundary_attributes;
994 for (
int ibdr_element = 0; ibdr_element < mesh.GetNBE(); ibdr_element++)
996 int boundary_id = mesh.GetBdrAttribute(ibdr_element);
997 int bdr_element_face_index = mesh.GetBdrElementFaceIndex(ibdr_element);
1000 if (mesh.FaceIsInterior(bdr_element_face_index))
1002 MFEM_WARNING(
"Skipping internal boundary " << ibdr_element);
1007 auto & element_face_info = mfem_face_index_info_for_global_face_index.at(
1008 bdr_element_face_index);
1010 int ielement = element_face_info.element_index;
1011 int iface = element_face_info.local_face_index;
1014 int exodusII_element_id = ielement + 1;
1017 int exodusII_face_id;
1020 switch (element_type)
1035 MFEM_ABORT(
"Cannot handle element of type " << element_type);
1038 unique_boundary_attributes.insert(boundary_id);
1040 exodusII_element_ids_for_boundary_id[boundary_id].push_back(
1041 exodusII_element_id);
1042 exodusII_side_ids_for_boundary_id[boundary_id].push_back(exodusII_face_id);
1045 boundary_ids.assign(unique_boundary_attributes.begin(),
1046 unique_boundary_attributes.end());
1047 std::sort(boundary_ids.begin(), boundary_ids.end());
1050void ExodusIIWriter::CheckNodalFESpaceIsSecondOrderH1()
const
1052 const FiniteElementSpace * fespace = mesh.GetNodalFESpace();
1055 MFEM_ABORT(
"The mesh has no nodal fespace.");
1059 const int fespace_order = fespace->GetMaxElementOrder();
1060 if (fespace_order != 2)
1062 MFEM_ABORT(
"Nodal fespace is of order " << fespace_order <<
1063 ". Expected 2nd order.");
1067 const FiniteElementCollection * fec = fespace->FEColl();
1070 MFEM_ABORT(
"No FECollection associated with nodal fespace.");
1074 if (strncmp(fec->Name(),
"H1", 2) != 0)
1076 MFEM_ABORT(
"Nodal fespace's FECollection is '" << fec->Name() <<
Type
Constants for the classes derived from Element.
void PrintExodusII(const std::string fpath)
Export a mesh to an Exodus II file.
const char * EXODUS_DATABASE_VERSION_LABEL
const char * EXODUS_NUM_DIM_LABEL
const char * EXODUS_COORDY_LABEL
const char * EXODUS_MESH_TITLE
const int EXODUS_MAX_NAME_LENGTH
const char * EXODUS_NUM_ELEM_LABEL
const char * EXODUS_COORDZ_LABEL
const char * EXODUS_TIME_STEP_LABEL
const char * EXODUS_FILE_SIZE_LABEL
const char * EXODUS_TITLE_LABEL
const char * EXODUS_NUM_BOUNDARIES_LABEL
const char * EXODUS_NUM_NODE_SETS_LABEL
const char * EXODUS_NUM_BLOCKS_LABEL
const char * EXODUS_NUM_ELEMENT_BLOCKS_LABEL
const float EXODUS_DATABASE_VERSION
const char * EXODUS_MAX_LINE_LENGTH_LABEL
const char * EXODUS_ELEMENT_TYPE_LABEL
const char * EXODUS_COORDX_LABEL
const char * EXODUS_ELEMENT_BLOCK_IDS_LABEL
const char * EXODUS_MAX_NAME_LENGTH_LABEL
const char * EXODUS_API_VERSION_LABEL
const char * EXODUS_NUM_SIDE_SETS_LABEL
const float EXODUS_API_VERSION
const int EXODUS_MAX_LINE_LENGTH
const char * EXODUS_FLOATING_POINT_WORD_SIZE_LABEL
const char * EXODUS_SIDE_SET_IDS_LABEL
const int mfem_to_exodusII_node_ordering_pyramid14[]
const int mfem_to_exodusII_node_ordering_wedge18[]
const int mfem_to_exodusII_node_ordering_tet10[]
const int mfem_to_exodusII_node_ordering_hex27[]
const int mfem_to_exodusII_side_map_wedge6[]
const int mfem_to_exodusII_side_map_hex8[]
const int mfem_to_exodusII_side_map_pyramid5[]
const int mfem_to_exodusII_side_map_tet4[]
Convert from the MFEM face numbering to the ExodusII face numbering.
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...