deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
grid_in.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 1999 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
17
20#include <deal.II/grid/tria.h>
21
22#include <boost/algorithm/string.hpp>
23#include <boost/archive/binary_iarchive.hpp>
24#include <boost/io/ios_state.hpp>
25#include <boost/property_tree/ptree.hpp>
26#include <boost/property_tree/xml_parser.hpp>
27#include <boost/serialization/serialization.hpp>
28
29#ifdef DEAL_II_GMSH_WITH_API
30# include <deal.II/grid/cell_id.h>
32
33# include <gmsh.h>
34#endif
35
36
37#include <algorithm>
38#include <cctype>
39#include <filesystem>
40#include <fstream>
41#include <limits>
42#include <map>
43
44#ifdef DEAL_II_WITH_ASSIMP
45# include <assimp/Importer.hpp> // C++ importer interface
46# include <assimp/postprocess.h> // Post processing flags
47# include <assimp/scene.h> // Output data structure
48#endif
49
50#ifdef DEAL_II_TRILINOS_WITH_SEACAS
51# include <exodusII.h>
52#endif
53
54
56
57
58namespace
59{
77 template <int spacedim>
78 void
79 assign_1d_boundary_ids(
80 const std::vector<std::pair<Point<spacedim>, types::boundary_id>>
81 &boundary_ids,
82 Triangulation<1, spacedim> &triangulation)
83 {
84 for (auto &cell : triangulation.active_cell_iterators())
85 for (const unsigned int face_no : ReferenceCells::Line.face_indices())
86 if (cell->face(face_no)->at_boundary())
87 for (const auto &pair : boundary_ids)
88 if (cell->face(face_no)->vertex(0) == pair.first)
89 {
90 cell->face(face_no)->set_boundary_id(pair.second);
91 break;
92 }
93 }
94
95
96 template <int dim, int spacedim>
97 void
98 assign_1d_boundary_ids(
99 const std::vector<std::pair<Point<spacedim>, types::boundary_id>> &,
101 {
102 // we shouldn't get here since boundary ids are not assigned to
103 // vertices except in 1d
104 Assert(dim != 1, ExcInternalError());
105 }
106
110 template <int dim, int spacedim>
111 void
112 apply_grid_fixup_functions(std::vector<Point<spacedim>> &vertices,
113 std::vector<CellData<dim>> &cells,
114 SubCellData &subcelldata)
115 {
116 // check that no forbidden arrays are used
117 Assert(subcelldata.check_consistency(dim), ExcInternalError());
118 const auto n_hypercube_vertices =
119 ReferenceCells::get_hypercube<dim>().n_vertices();
120 bool is_only_hypercube = true;
121 for (const CellData<dim> &cell : cells)
122 if (cell.vertices.size() != n_hypercube_vertices)
123 {
124 is_only_hypercube = false;
125 break;
126 }
127
128 GridTools::delete_unused_vertices(vertices, cells, subcelldata);
129 if constexpr (dim == spacedim)
131
132 if (is_only_hypercube)
134 }
135} // namespace
136
137template <int dim, int spacedim>
139 : tria(nullptr, typeid(*this).name())
140 , default_format(ucd)
141{}
142
143
144
145template <int dim, int spacedim>
147 : tria(&t, typeid(*this).name())
148 , default_format(ucd)
149{}
150
151
152
153template <int dim, int spacedim>
154void
159
160
161
162template <int dim, int spacedim>
163void
165{
166 std::string line;
167 std::string vtk_version;
168 // verify that the third and fourth lines match
169 // expectations. the first line is not checked to allow use of
170 // different vtk versions and the second line of the file may
171 // essentially be anything the author of the file chose to
172 // identify what's in there, so we just ensure that we can read it.
173 {
174 std::string text[4];
175 // text[0] will contain the version string after reading the preamble.
176 text[1] = "****";
177 text[2] = "ASCII";
178 text[3] = "DATASET UNSTRUCTURED_GRID";
179
180 for (unsigned int i = 0; i < 4; ++i)
181 {
182 getline(in, line);
183
184 if (i == 0)
185 text[0] = line;
186 if (i == 2 || i == 3)
188 line.compare(text[i]) == 0,
190 std::string(
191 "While reading VTK file, failed to find a header line with text <") +
192 text[i] + ">"));
193 }
194
195 // Get the version of the VTK file
196 vtk_version = text[0].substr(23, 3);
197 }
198
199
200 //-----------------Declaring storage and mappings------------------
201
202 std::vector<Point<spacedim>> vertices;
203 std::vector<CellData<dim>> cells;
204 SubCellData subcelldata;
205
206 std::string keyword;
207
208 in >> keyword;
209
210 //----------------Processing the POINTS section---------------
211
212 if (keyword == "POINTS")
213 {
214 unsigned int n_vertices;
215 in >> n_vertices;
216
217 in >> keyword; // float, double, int, char, etc.
218
219 for (unsigned int vertex = 0; vertex < n_vertices; ++vertex)
220 {
221 // VTK format always specifies vertex coordinates with 3 components
222 Point<3> x;
223 in >> x[0] >> x[1] >> x[2];
224
225 vertices.emplace_back();
226 for (unsigned int d = 0; d < spacedim; ++d)
227 vertices.back()[d] = x[d];
228 }
229 }
230
231 else
232 AssertThrow(false,
234 "While reading VTK file, failed to find POINTS section"));
235
236 in >> keyword;
237
238 unsigned int n_geometric_objects = 0;
239 unsigned int n_ints;
240 std::vector<unsigned int> n_points_per_cell;
241
242 if (keyword == "CELLS")
243 {
244 // jump to the `CELL_TYPES` section and read in cell types
245 std::vector<unsigned int> cell_types;
246 {
247 std::streampos oldpos = in.tellg();
248
249
250 while (in >> keyword)
251 if (keyword == "CELL_TYPES")
252 {
253 in >> n_ints;
254
255 cell_types.resize(n_ints);
256
257 for (unsigned int i = 0; i < n_ints; ++i)
258 in >> cell_types[i];
259
260 break;
261 }
262
263 in.seekg(oldpos);
264 }
265
266 in >> n_geometric_objects;
267 in >> n_ints; // Ignore this, since we don't need it.
268
269 if (vtk_version == "5.1") // we need to store OFFSETS and CONNECTIVITY
270 // arrays which exist in VTK 5.1 file formats
271 {
272 in >> keyword;
273
274 Assert(
275 keyword == "OFFSETS",
277 "While reading VTK file, failed to find OFFSETS array which must exist in a VTK file with version 5.1."));
278
279 // If VTK 3.0, n_geometric_objects = number of cells
280 // If VTK 5.1, n_geometric_objects = number of cells + 1
281 unsigned int n_offsets = n_geometric_objects;
282 std::string
283 vtktype; // vtktypeint64, vtktypeint32, etc...we do not need this
284
285 in >> vtktype;
286
287 // The OFFSETS array contains the indices in the CONNECTIVITY array
288 // where new cells start
289 unsigned int new_index = 0;
290 unsigned int old_index = 0;
291
292 // Now store how many vertices make up each cell
293 for (unsigned int p = 0; p < n_offsets; ++p)
294 {
295 unsigned int n_points_per_cell_tmp;
296
297 in >> new_index;
298
299 if (p == 0)
301 new_index == 0,
303 "While reading VTK file, the first index in the OFFSETS array should be 0"));
304 else
305 {
306 n_points_per_cell_tmp = new_index - old_index;
307 n_points_per_cell.push_back(n_points_per_cell_tmp);
308 }
309 old_index = new_index;
310 }
311
313 n_points_per_cell.size() == cell_types.size(),
315 "The number of cells inferred from the OFFSETS array (" +
316 std::to_string(n_points_per_cell.size()) +
317 ") does not match the number of entries in the CELL_TYPES array (" +
318 std::to_string(cell_types.size()) + ")"));
319
320 // Now that we know how many points correspond to each cell, we can
321 // read the CONNECTIVITY array
322 in >> keyword;
324 keyword == "CONNECTIVITY",
326 "While reading VTK file, failed to find CONNECTIVITY array which must exist in a VTK file containing an OFFSETS array."));
327
328 in >>
329 vtktype; // vtktypeint64, vtktypeint32, etc...we do not need this
330
331 // Update n_geometric_objects to be the number of cells
332 n_geometric_objects = n_points_per_cell.size();
333 }
334
335
336 if constexpr (dim == 3)
337 {
338 for (unsigned int count = 0; count < n_geometric_objects; ++count)
339 {
340 unsigned int n_vertices;
341
342 if (vtk_version == "3.0")
343 in >> n_vertices;
344 else if (vtk_version == "5.1") // If version 5.1, n_vertices is
345 // not given explicitly in the file
346 n_vertices = n_points_per_cell[count];
347 else
349 false,
351 "Unknown VTK version encountered...Only VTK versions 3.0 and 5.1 are supported."));
352
353 // VTK_TETRA is 10, VTK_HEXAHEDRON is 12
354 if (cell_types[count] == 10 || cell_types[count] == 12)
355 {
356 // we assume that the file contains first all cells,
357 // and only then any faces or lines
358 AssertThrow(subcelldata.boundary_quads.empty() &&
359 subcelldata.boundary_lines.empty(),
361
362 cells.emplace_back(n_vertices);
363
364 for (unsigned int j = 0; j < n_vertices;
365 j++) // loop to feed data
366 in >> cells.back().vertices[j];
367
368 // Hexahedra need a permutation to go from VTK numbering
369 // to deal numbering
370 if (cell_types[count] == 12)
371 {
372 std::swap(cells.back().vertices[2],
373 cells.back().vertices[3]);
374 std::swap(cells.back().vertices[6],
375 cells.back().vertices[7]);
376 }
377
378 cells.back().material_id = 0;
379 }
380 // VTK_TRIANGLE is 5, VTK_QUAD is 9
381 else if (cell_types[count] == 5 || cell_types[count] == 9)
382 {
383 // we assume that the file contains first all cells,
384 // then all faces, and finally all lines
385 AssertThrow(subcelldata.boundary_lines.empty(),
387
388 subcelldata.boundary_quads.emplace_back(n_vertices);
389
390 for (unsigned int j = 0; j < n_vertices;
391 j++) // loop to feed the data to the boundary
392 in >> subcelldata.boundary_quads.back().vertices[j];
393
394 subcelldata.boundary_quads.back().material_id = 0;
395 }
396 // VTK_LINE is 3
397 else if (cell_types[count] == 3)
398 {
399 subcelldata.boundary_lines.emplace_back(n_vertices);
400
401 for (unsigned int j = 0; j < n_vertices;
402 j++) // loop to feed the data to the boundary
403 in >> subcelldata.boundary_lines.back().vertices[j];
404
405 subcelldata.boundary_lines.back().material_id = 0;
406 }
407
408 else
410 false,
412 "While reading VTK file, unknown cell type encountered"));
413 }
414 }
415 else if constexpr (dim == 2)
416 {
417 for (unsigned int count = 0; count < n_geometric_objects; ++count)
418 {
419 unsigned int n_vertices;
420
421 if (vtk_version == "3.0")
422 in >> n_vertices;
423 else if (vtk_version == "5.1") // If version 5.1, n_vertices is
424 // not given explicitly in the file
425 n_vertices = n_points_per_cell[count];
426 else
428 false,
430 "Unknown VTK version encountered...Only VTK versions 3.0 and 5.1 are supported."));
431
432 // VTK_TRIANGLE is 5, VTK_QUAD is 9
433 if (cell_types[count] == 5 || cell_types[count] == 9)
434 {
435 // we assume that the file contains first all cells,
436 // and only then any faces
437 AssertThrow(subcelldata.boundary_lines.empty(),
439
440 cells.emplace_back(n_vertices);
441
442 for (unsigned int j = 0; j < n_vertices;
443 j++) // loop to feed data
444 in >> cells.back().vertices[j];
445
446 // Quadrilaterals need a permutation to go from VTK
447 // numbering to deal numbering
448 if (cell_types[count] == 9)
449 {
450 // Like Hexahedra - the last two vertices need to be
451 // flipped
452 std::swap(cells.back().vertices[2],
453 cells.back().vertices[3]);
454 }
455
456 cells.back().material_id = 0;
457 }
458 // VTK_LINE is 3
459 else if (cell_types[count] == 3)
460 {
461 // If this is encountered, the pointer comes out of the
462 // loop and starts processing boundaries.
463 subcelldata.boundary_lines.emplace_back(n_vertices);
464
465 for (unsigned int j = 0; j < n_vertices;
466 j++) // loop to feed the data to the boundary
467 {
468 in >> subcelldata.boundary_lines.back().vertices[j];
469 }
470
471 subcelldata.boundary_lines.back().material_id = 0;
472 }
473
474 else
476 false,
478 "While reading VTK file, unknown cell type encountered"));
479 }
480 }
481 else if constexpr (dim == 1)
482 {
483 for (unsigned int count = 0; count < n_geometric_objects; ++count)
484 {
485 unsigned int n_vertices;
486 if (vtk_version == "3.0")
487 in >> n_vertices;
488 else if (vtk_version == "5.1") // If version 5.1, n_vertices is
489 // not given explicitly in the file
490 n_vertices = n_points_per_cell[count];
491 else
493 false,
495 "Unknown VTK version encountered...Only VTK versions 3.0 and 5.1 are supported."));
496
498 cell_types[count] == 3 && n_vertices == 2,
500 "While reading VTK file, unknown cell type encountered"));
501 cells.emplace_back(n_vertices);
502
503 for (unsigned int j = 0; j < n_vertices; ++j) // loop to feed data
504 in >> cells.back().vertices[j];
505
506 cells.back().material_id = 0;
507 }
508 }
509 else
510 AssertThrow(false,
512 "While reading VTK file, failed to find CELLS section"));
513
514 // Processing the CELL_TYPES section
515
516 in >> keyword;
517
519 keyword == "CELL_TYPES",
520 ExcMessage(std::string(
521 "While reading VTK file, missing CELL_TYPES section. Found <" +
522 keyword + "> instead.")));
523
524 in >> n_ints;
525
527 n_ints == n_geometric_objects,
528 ExcMessage("The VTK reader found a CELL_DATA statement "
529 "that lists a total of " +
531 " cell data objects, but this needs to "
532 "equal the number of cells (which is " +
533 Utilities::int_to_string(cells.size()) +
534 ") plus the number of quads (" +
535 Utilities::int_to_string(subcelldata.boundary_quads.size()) +
536 " in 3d or the number of lines (" +
537 Utilities::int_to_string(subcelldata.boundary_lines.size()) +
538 ") in 2d."));
539
540 int tmp_int;
541 for (unsigned int i = 0; i < n_ints; ++i)
542 in >> tmp_int;
543
544
545 // Processing the CELL_DATA and FIELD_DATA sections
546
547 // Ignore everything up to CELL_DATA
548 while (in >> keyword)
549 {
550 if (keyword == "CELL_DATA")
551 {
552 unsigned int n_ids;
553 in >> n_ids;
554
556 n_ids == n_geometric_objects,
558 "The VTK reader found a CELL_DATA statement "
559 "that lists a total of " +
561 " cell data objects, but this needs to "
562 "equal the number of cells (which is " +
563 Utilities::int_to_string(cells.size()) +
564 ") plus the number of quads (" +
565 Utilities::int_to_string(subcelldata.boundary_quads.size()) +
566 " in 3d or the number of lines (" +
567 Utilities::int_to_string(subcelldata.boundary_lines.size()) +
568 ") in 2d."));
569
570 const std::vector<std::string> data_sets{"MaterialID",
571 "ManifoldID"};
572
573 in >> keyword;
574 for (unsigned int i = 0; i < data_sets.size(); ++i)
575 {
576 // Ignore everything until we get to a SCALARS data set
577 if (keyword == "SCALARS")
578 {
579 // Now see if we know about this type of data set,
580 // if not, just ignore everything till the next SCALARS
581 // keyword
582 std::string field_name;
583 in >> field_name;
584 if (std::find(data_sets.begin(),
585 data_sets.end(),
586 field_name) == data_sets.end())
587 // The data set here is not one of the ones we know, so
588 // keep ignoring everything until the next SCALARS
589 // keyword.
590 continue;
591
592 // Now we got somewhere. Proceed from here, assert
593 // that the type of the table is int, and ignore the
594 // rest of the line.
595 // SCALARS MaterialID int 1
596 // (the last number is optional)
597 std::string line;
598 std::getline(in, line);
599
601 line.substr(1,
602 std::min(static_cast<std::size_t>(3),
603 line.size() - 1)) == "int",
605 "While reading VTK file, material- and manifold IDs can only have type 'int'."));
606
607 in >> keyword;
609 keyword == "LOOKUP_TABLE",
611 "While reading VTK file, missing keyword 'LOOKUP_TABLE'."));
612
613 in >> keyword;
615 keyword == "default",
617 "While reading VTK file, missing keyword 'default'."));
618
619 // read material or manifold ids first for all cells,
620 // then for all faces, and finally for all lines. the
621 // assumption that cells come before all faces and
622 // lines has been verified above via an assertion, so
623 // the order used in the following blocks makes sense
624 for (unsigned int i = 0; i < cells.size(); ++i)
625 {
626 int id;
627 in >> id;
628 if (field_name == "MaterialID")
629 cells[i].material_id =
630 static_cast<types::material_id>(id);
631 else if (field_name == "ManifoldID")
632 cells[i].manifold_id =
633 static_cast<types::manifold_id>(id);
634 else
636 }
637
638 if constexpr (dim == 3)
639 {
640 for (auto &boundary_quad : subcelldata.boundary_quads)
641 {
642 int id;
643 in >> id;
644 if (field_name == "MaterialID")
645 boundary_quad.material_id =
646 static_cast<types::material_id>(id);
647 else if (field_name == "ManifoldID")
648 boundary_quad.manifold_id =
649 static_cast<types::manifold_id>(id);
650 else
652 }
653 for (auto &boundary_line : subcelldata.boundary_lines)
654 {
655 int id;
656 in >> id;
657 if (field_name == "MaterialID")
658 boundary_line.material_id =
659 static_cast<types::material_id>(id);
660 else if (field_name == "ManifoldID")
661 boundary_line.manifold_id =
662 static_cast<types::manifold_id>(id);
663 else
665 }
666 }
667 else if constexpr (dim == 2)
668 {
669 for (auto &boundary_line : subcelldata.boundary_lines)
670 {
671 int id;
672 in >> id;
673 if (field_name == "MaterialID")
674 boundary_line.material_id =
675 static_cast<types::material_id>(id);
676 else if (field_name == "ManifoldID")
677 boundary_line.manifold_id =
678 static_cast<types::manifold_id>(id);
679 else
681 }
682 }
683 }
684 // check if a second SCALAR exists. If so, read the new
685 // keyword SCALARS, otherwise, return to the bookmarked
686 // position.
687 std::streampos oldpos = in.tellg();
688 in >> keyword;
689 if (keyword == "SCALARS")
690 continue;
691 else
692 in.seekg(oldpos);
693 }
694 }
695
696
697 // Addition of FIELD DATA:
698
699
700 else if (keyword == "FIELD")
701 {
702 unsigned int n_fields;
703 in >> keyword;
705 keyword == "FieldData",
707 "While reading VTK file, missing keyword FieldData"));
708
709 in >> n_fields;
710
711 for (unsigned int i = 0; i < n_fields; ++i)
712 {
713 std::string section_name;
714 std::string data_type;
715 unsigned int temp, n_ids;
716 double data;
717 in >> section_name;
718 in >> temp;
719 in >> n_ids;
721 n_ids == n_geometric_objects,
723 "The VTK reader found a FIELD statement "
724 "that lists a total of " +
726 " cell data objects, but this needs to equal the number of cells (which is " +
727 Utilities::int_to_string(cells.size()) +
728 ") plus the number of quads (" +
730 subcelldata.boundary_quads.size()) +
731 " in 3d or the number of lines (" +
733 subcelldata.boundary_lines.size()) +
734 ") in 2d."));
735 in >> data_type;
736 Vector<double> temp_data;
737 temp_data.reinit(n_ids);
738 for (unsigned int j = 0; j < n_ids; ++j)
739 {
740 in >> data;
741 if (j < cells.size())
742 temp_data[j] = data;
743 }
744 this->cell_data[section_name] = std::move(temp_data);
745 }
746 }
747 else
748 {
749 // just ignore a line that doesn't start with any of the
750 // recognized tags
751 }
752 } // end of while loop
753 Assert(subcelldata.check_consistency(dim), ExcInternalError());
754
755 apply_grid_fixup_functions(vertices, cells, subcelldata);
756 tria->create_triangulation(vertices, cells, subcelldata);
757 }
758 else
759 AssertThrow(false,
761 "While reading VTK file, failed to find CELLS section"));
762}
763
764template <int dim, int spacedim>
765const std::map<std::string, Vector<double>> &
767{
768 return this->cell_data;
769}
770
771template <int dim, int spacedim>
772void
774{
775 namespace pt = boost::property_tree;
776 pt::ptree tree;
777 pt::read_xml(in, tree);
778 auto section = tree.get_optional<std::string>("VTKFile.dealiiData");
779
780 AssertThrow(section,
782 "While reading a VTU file, failed to find dealiiData section. "
783 "Notice that we can only read grid files in .vtu format that "
784 "were created by the deal.II library, using a call to "
785 "GridOut::write_vtu(), where the flag "
786 "GridOutFlags::Vtu::serialize_triangulation is set to true."));
787
788 const auto decoded =
789 Utilities::decode_base64({section->begin(), section->end() - 1});
790 const auto string_archive =
791 Utilities::decompress({decoded.begin(), decoded.end()});
792 std::istringstream in_stream(string_archive);
793 boost::archive::binary_iarchive ia(in_stream);
794 tria->load(ia, 0);
795}
796
797
798template <int dim, int spacedim>
799void
801{
802 Assert(tria != nullptr, ExcNoTriangulationSelected());
803 Assert((dim == 2) || (dim == 3), ExcNotImplemented());
804
805 AssertThrow(in.fail() == false, ExcIO());
806 skip_comment_lines(in, '#'); // skip comments (if any) at beginning of file
807
808 int tmp;
809
810 // loop over sections, read until section 2411 is found, and break once found
811 while (true)
812 {
813 AssertThrow(in.fail() == false, ExcIO());
814 in >> tmp;
815 AssertThrow(tmp == -1,
816 ExcMessage("Invalid UNV file format. "
817 "Expected '-1' before and after a section."));
818
819 AssertThrow(in.fail() == false, ExcIO());
820 in >> tmp;
821 AssertThrow(tmp >= 0, ExcUnknownSectionType(tmp));
822 if (tmp != 2411)
823 {
824 // read until the end of any section that is not 2411
825 while (true)
826 {
827 std::string line;
828 AssertThrow(in.fail() == false, ExcIO());
829 std::getline(in, line);
830 // remove leading and trailing spaces in the line
831 boost::algorithm::trim(line);
832 if (line.compare("-1") == 0) // end of section
833 break;
834 }
835 }
836 else
837 break; // found section 2411
838 }
839
840 // section 2411 describes vertices: see the following links
841 // https://docs.plm.automation.siemens.com/tdoc/nx/12/nx_help#uid:xid1128419:index_advanced:xid1404601:xid1404604
842 // https://www.ceas3.uc.edu/sdrluff/
843 std::vector<Point<spacedim>> vertices; // vector of vertex coordinates
844 std::map<int, int>
845 vertex_indices; // # vert in unv (key) ---> # vert in deal.II (value)
846
847 int n_vertices = 0; // deal.II
848
849 while (tmp != -1) // we do until reach end of 2411
850 {
851 int vertex_index; // unv
852 int dummy;
853 double x[3];
854
855 AssertThrow(in.fail() == false, ExcIO());
856 in >> vertex_index;
857
858 tmp = vertex_index;
859 if (tmp == -1)
860 break;
861
862 in >> dummy >> dummy >> dummy;
863
864 AssertThrow(in.fail() == false, ExcIO());
865 in >> x[0] >> x[1] >> x[2];
866
867 vertices.emplace_back();
868
869 for (unsigned int d = 0; d < spacedim; ++d)
870 vertices.back()[d] = x[d];
871
872 vertex_indices[vertex_index] = n_vertices;
873
874 ++n_vertices;
875 }
876
877 AssertThrow(in.fail() == false, ExcIO());
878 in >> tmp;
879 AssertThrow(in.fail() == false, ExcIO());
880 in >> tmp;
881
882 // section 2412 describes elements: see
883 // http://www.sdrl.uc.edu/sdrl/referenceinfo/universalfileformats/file-format-storehouse/universal-dataset-number-2412
884 AssertThrow(tmp == 2412, ExcUnknownSectionType(tmp));
885
886 std::vector<CellData<dim>> cells; // vector of cells
887 SubCellData subcelldata;
888
889 std::map<int, int>
890 cell_indices; // # cell in unv (key) ---> # cell in deal.II (value)
891 std::map<int, int>
892 line_indices; // # line in unv (key) ---> # line in deal.II (value)
893 std::map<int, int>
894 quad_indices; // # quad in unv (key) ---> # quad in deal.II (value)
895
896 int n_cells = 0; // deal.II
897 int n_lines = 0; // deal.II
898 int n_quads = 0; // deal.II
899
900 while (tmp != -1) // we do until reach end of 2412
901 {
902 int object_index; // unv
903 int type;
904 int dummy;
905
906 AssertThrow(in.fail() == false, ExcIO());
907 in >> object_index;
908
909 tmp = object_index;
910 if (tmp == -1)
911 break;
912
913 in >> type >> dummy >> dummy >> dummy >> dummy;
914
915 AssertThrow((type == 11) || (type == 44) || (type == 94) || (type == 115),
916 ExcUnknownElementType(type));
917
918 if ((((type == 44) || (type == 94)) && (dim == 2)) ||
919 ((type == 115) && (dim == 3))) // cell
920 {
921 const auto reference_cell = ReferenceCells::get_hypercube<dim>();
922 cells.emplace_back();
923
924 AssertThrow(in.fail() == false, ExcIO());
925 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
926 in >> cells.back()
927 .vertices[reference_cell.unv_vertex_to_deal_vertex(v)];
928
929 cells.back().material_id = 0;
930
931 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
932 cells.back().vertices[v] = vertex_indices[cells.back().vertices[v]];
933
934 cell_indices[object_index] = n_cells;
935
936 ++n_cells;
937 }
938 else if (((type == 11) && (dim == 2)) ||
939 ((type == 11) && (dim == 3))) // boundary line
940 {
941 AssertThrow(in.fail() == false, ExcIO());
942 in >> dummy >> dummy >> dummy;
943
944 subcelldata.boundary_lines.emplace_back();
945
946 AssertThrow(in.fail() == false, ExcIO());
947 for (unsigned int &vertex :
948 subcelldata.boundary_lines.back().vertices)
949 in >> vertex;
950
951 subcelldata.boundary_lines.back().material_id = 0;
952
953 for (unsigned int &vertex :
954 subcelldata.boundary_lines.back().vertices)
955 vertex = vertex_indices[vertex];
956
957 line_indices[object_index] = n_lines;
958
959 ++n_lines;
960 }
961 else if (((type == 44) || (type == 94)) && (dim == 3)) // boundary quad
962 {
963 const auto reference_cell = ReferenceCells::Quadrilateral;
964 subcelldata.boundary_quads.emplace_back();
965
966 AssertThrow(in.fail() == false, ExcIO());
967 Assert(subcelldata.boundary_quads.back().vertices.size() ==
970 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
971 in >> subcelldata.boundary_quads.back()
972 .vertices[reference_cell.unv_vertex_to_deal_vertex(v)];
973
974 subcelldata.boundary_quads.back().material_id = 0;
975
976 for (unsigned int &vertex :
977 subcelldata.boundary_quads.back().vertices)
978 vertex = vertex_indices[vertex];
979
980 quad_indices[object_index] = n_quads;
981
982 ++n_quads;
983 }
984 else
985 AssertThrow(false,
986 ExcMessage("Unknown element label <" +
988 "> when running in dim=" +
990 }
991
992 // note that so far all materials and bcs are explicitly set to 0
993 // if we do not need more info on materials and bcs - this is end of file
994 // if we do - section 2467 or 2477 comes
995
996 in >> tmp; // tmp can be either -1 or end-of-file
997
998 if (!in.eof())
999 {
1000 AssertThrow(in.fail() == false, ExcIO());
1001 in >> tmp;
1002
1003 // section 2467 (2477) describes (materials - first and bcs - second) or
1004 // (bcs - first and materials - second) - sequence depends on which
1005 // group is created first: see
1006 // http://www.sdrl.uc.edu/sdrl/referenceinfo/universalfileformats/file-format-storehouse/universal-dataset-number-2467
1007 AssertThrow((tmp == 2467) || (tmp == 2477), ExcUnknownSectionType(tmp));
1008
1009 while (tmp != -1) // we do until reach end of 2467 or 2477
1010 {
1011 int n_entities; // number of entities in group
1012 long id; // id is either material or bc
1013 int no; // unv
1014 int dummy;
1015
1016 AssertThrow(in.fail() == false, ExcIO());
1017 in >> dummy;
1018
1019 tmp = dummy;
1020 if (tmp == -1)
1021 break;
1022
1023 in >> dummy >> dummy >> dummy >> dummy >> dummy >> dummy >>
1024 n_entities;
1025
1026 AssertThrow(in.fail() == false, ExcIO());
1027 // Occasionally we encounter IDs that are not integers - we don't
1028 // support that case since there is no sane way for us to determine
1029 // integer IDs from, e.g., strings.
1030 std::string line;
1031 // The next character in the input buffer is a newline character so
1032 // we need a call to std::getline() to retrieve it (it is logically
1033 // a line):
1034 std::getline(in, line);
1035 AssertThrow(line.empty(),
1036 ExcMessage(
1037 "The line before the line containing an ID has too "
1038 "many entries. This is not a valid UNV file."));
1039 // now get the line containing the id:
1040 std::getline(in, line);
1041 AssertThrow(in.fail() == false, ExcIO());
1042 std::istringstream id_stream(line);
1043 id_stream >> id;
1045 !id_stream.fail() && id_stream.eof(),
1046 ExcMessage(
1047 "The given UNV file contains a boundary or material id set to '" +
1048 line +
1049 "', which cannot be parsed as a fixed-width integer, whereas "
1050 "deal.II only supports integer boundary and material ids. To fix "
1051 "this, ensure that all such ids are given integer values."));
1053 0 <= id &&
1054 id <= long(std::numeric_limits<types::material_id>::max()),
1055 ExcMessage("The provided integer id '" + std::to_string(id) +
1056 "' is not convertible to either types::material_id nor "
1057 "types::boundary_id."));
1058
1059 const unsigned int n_lines =
1060 (n_entities % 2 == 0) ? (n_entities / 2) : ((n_entities + 1) / 2);
1061
1062 for (unsigned int line = 0; line < n_lines; ++line)
1063 {
1064 unsigned int n_fragments;
1065
1066 if (line == n_lines - 1)
1067 n_fragments = (n_entities % 2 == 0) ? (2) : (1);
1068 else
1069 n_fragments = 2;
1070
1071 for (unsigned int no_fragment = 0; no_fragment < n_fragments;
1072 no_fragment++)
1073 {
1074 AssertThrow(in.fail() == false, ExcIO());
1075 in >> dummy >> no >> dummy >> dummy;
1076
1077 if (cell_indices.count(no) > 0) // cell - material
1078 cells[cell_indices[no]].material_id = id;
1079
1080 if (line_indices.count(no) > 0) // boundary line - bc
1081 subcelldata.boundary_lines[line_indices[no]].boundary_id =
1082 id;
1083
1084 if (quad_indices.count(no) > 0) // boundary quad - bc
1085 subcelldata.boundary_quads[quad_indices[no]].boundary_id =
1086 id;
1087 }
1088 }
1089 }
1090 }
1091
1092 apply_grid_fixup_functions(vertices, cells, subcelldata);
1093 tria->create_triangulation(vertices, cells, subcelldata);
1094}
1095
1096
1097
1098template <int dim, int spacedim>
1099void
1101 const bool apply_all_indicators_to_manifolds)
1102{
1103 Assert(tria != nullptr, ExcNoTriangulationSelected());
1104 AssertThrow(in.fail() == false, ExcIO());
1105
1106 // skip comments at start of file
1107 skip_comment_lines(in, '#');
1108
1109
1110 unsigned int n_vertices;
1111 unsigned int n_cells;
1112 int dummy;
1113
1114 in >> n_vertices >> n_cells >> dummy // number of data vectors
1115 >> dummy // cell data
1116 >> dummy; // model data
1117 AssertThrow(in.fail() == false, ExcIO());
1118
1119 // set up array of vertices
1120 std::vector<Point<spacedim>> vertices(n_vertices);
1121 // set up mapping between numbering
1122 // in ucd-file (key) and in the
1123 // vertices vector
1124 std::map<int, int> vertex_indices;
1125
1126 for (unsigned int vertex = 0; vertex < n_vertices; ++vertex)
1127 {
1128 int vertex_number;
1129 double x[3];
1130
1131 // read vertex
1132 AssertThrow(in.fail() == false, ExcIO());
1133 in >> vertex_number >> x[0] >> x[1] >> x[2];
1134
1135 // store vertex
1136 for (unsigned int d = 0; d < spacedim; ++d)
1137 vertices[vertex][d] = x[d];
1138 // store mapping; note that
1139 // vertices_indices[i] is automatically
1140 // created upon first usage
1141 vertex_indices[vertex_number] = vertex;
1142 }
1143
1144 // set up array of cells
1145 std::vector<CellData<dim>> cells;
1146 SubCellData subcelldata;
1147
1148 for (unsigned int cell = 0; cell < n_cells; ++cell)
1149 {
1150 // note that since in the input
1151 // file we found the number of
1152 // cells at the top, there
1153 // should still be input here,
1154 // so check this:
1155 AssertThrow(in.fail() == false, ExcIO());
1156
1157 std::string cell_type;
1158
1159 // we use an unsigned int because we
1160 // fill this variable through an read-in process
1161 unsigned int material_id;
1162
1163 in >> dummy // cell number
1164 >> material_id;
1165 in >> cell_type;
1166
1167 if (((dim == 1) && (cell_type == "line")) ||
1168 ((dim == 2) && (cell_type == "quad")) ||
1169 ((dim == 3) && (cell_type == "hex")))
1170 // found a cell
1171 {
1172 // allocate and read indices
1173 cells.emplace_back();
1174 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
1175 in >> cells.back().vertices[GeometryInfo<dim>::ucd_to_deal[i]];
1176
1177 // to make sure that the cast won't fail
1178 Assert(material_id <= std::numeric_limits<types::material_id>::max(),
1179 ExcIndexRange(material_id,
1180 0,
1181 std::numeric_limits<types::material_id>::max()));
1182 // we use only material_ids in the range from 0 to
1183 // numbers::invalid_material_id-1
1185
1186 if (apply_all_indicators_to_manifolds)
1187 cells.back().manifold_id =
1188 static_cast<types::manifold_id>(material_id);
1189 cells.back().material_id = material_id;
1190
1191 // transform from ucd to
1192 // consecutive numbering
1193 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
1194 if (vertex_indices.find(cells.back().vertices[i]) !=
1195 vertex_indices.end())
1196 // vertex with this index exists
1197 cells.back().vertices[i] =
1198 vertex_indices[cells.back().vertices[i]];
1199 else
1200 {
1201 // no such vertex index
1202 AssertThrow(false,
1203 ExcInvalidVertexIndex(cell,
1204 cells.back().vertices[i]));
1205
1206 cells.back().vertices[i] = numbers::invalid_unsigned_int;
1207 }
1208 }
1209 else if (((dim == 2) || (dim == 3)) && (cell_type == "line"))
1210 // boundary info
1211 {
1212 subcelldata.boundary_lines.emplace_back();
1213 in >> subcelldata.boundary_lines.back().vertices[0] >>
1214 subcelldata.boundary_lines.back().vertices[1];
1215
1216 // to make sure that the cast won't fail
1217 Assert(material_id <= std::numeric_limits<types::boundary_id>::max(),
1218 ExcIndexRange(material_id,
1219 0,
1220 std::numeric_limits<types::boundary_id>::max()));
1221 // we use only boundary_ids in the range from 0 to
1222 // numbers::internal_face_boundary_id-1
1224
1225 // Make sure to set both manifold id and boundary id appropriately in
1226 // both cases:
1227 // numbers::internal_face_boundary_id and numbers::flat_manifold_id
1228 // are ignored in Triangulation::create_triangulation.
1229 if (apply_all_indicators_to_manifolds)
1230 {
1231 subcelldata.boundary_lines.back().boundary_id =
1233 subcelldata.boundary_lines.back().manifold_id =
1234 static_cast<types::manifold_id>(material_id);
1235 }
1236 else
1237 {
1238 subcelldata.boundary_lines.back().boundary_id =
1239 static_cast<types::boundary_id>(material_id);
1240 subcelldata.boundary_lines.back().manifold_id =
1242 }
1243
1244 // transform from ucd to
1245 // consecutive numbering
1246 for (unsigned int &vertex :
1247 subcelldata.boundary_lines.back().vertices)
1248 if (vertex_indices.find(vertex) != vertex_indices.end())
1249 // vertex with this index exists
1250 vertex = vertex_indices[vertex];
1251 else
1252 {
1253 // no such vertex index
1254 AssertThrow(false, ExcInvalidVertexIndex(cell, vertex));
1256 }
1257 }
1258 else if ((dim == 3) && (cell_type == "quad"))
1259 // boundary info
1260 {
1261 subcelldata.boundary_quads.emplace_back();
1262 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
1263 in >> subcelldata.boundary_quads.back()
1264 .vertices[GeometryInfo<2>::ucd_to_deal[i]];
1265
1266 // to make sure that the cast won't fail
1267 Assert(material_id <= std::numeric_limits<types::boundary_id>::max(),
1268 ExcIndexRange(material_id,
1269 0,
1270 std::numeric_limits<types::boundary_id>::max()));
1271 // we use only boundary_ids in the range from 0 to
1272 // numbers::internal_face_boundary_id-1
1274
1275 // Make sure to set both manifold id and boundary id appropriately in
1276 // both cases:
1277 // numbers::internal_face_boundary_id and numbers::flat_manifold_id
1278 // are ignored in Triangulation::create_triangulation.
1279 if (apply_all_indicators_to_manifolds)
1280 {
1281 subcelldata.boundary_quads.back().boundary_id =
1283 subcelldata.boundary_quads.back().manifold_id =
1284 static_cast<types::manifold_id>(material_id);
1285 }
1286 else
1287 {
1288 subcelldata.boundary_quads.back().boundary_id =
1289 static_cast<types::boundary_id>(material_id);
1290 subcelldata.boundary_quads.back().manifold_id =
1292 }
1293
1294 // transform from ucd to
1295 // consecutive numbering
1296 for (unsigned int &vertex :
1297 subcelldata.boundary_quads.back().vertices)
1298 if (vertex_indices.find(vertex) != vertex_indices.end())
1299 // vertex with this index exists
1300 vertex = vertex_indices[vertex];
1301 else
1302 {
1303 // no such vertex index
1304 Assert(false, ExcInvalidVertexIndex(cell, vertex));
1306 }
1307 }
1308 else
1309 // cannot read this
1310 AssertThrow(false, ExcUnknownIdentifier(cell_type));
1311 }
1312
1313 AssertThrow(in.fail() == false, ExcIO());
1314
1315 apply_grid_fixup_functions(vertices, cells, subcelldata);
1316 tria->create_triangulation(vertices, cells, subcelldata);
1317}
1318
1319namespace
1320{
1321 template <int dim, int spacedim>
1322 class Abaqus_to_UCD
1323 {
1324 public:
1325 Abaqus_to_UCD();
1326
1327 void
1328 read_in_abaqus(std::istream &in);
1329 void
1330 write_out_avs_ucd(std::ostream &out) const;
1331
1332 private:
1333 const double tolerance;
1334
1335 std::vector<double>
1336 get_global_node_numbers(const int face_cell_no,
1337 const int face_cell_face_no) const;
1338
1339 // NL: Stored as [ global node-id (int), x-coord, y-coord, z-coord ]
1340 std::vector<std::vector<double>> node_list;
1341 // CL: Stored as [ material-id (int), node1, node2, node3, node4, node5,
1342 // node6, node7, node8 ]
1343 std::vector<std::vector<double>> cell_list;
1344 // FL: Stored as [ sideset-id (int), node1, node2, node3, node4 ]
1345 std::vector<std::vector<double>> face_list;
1346 // ELSET: Stored as [ (std::string) elset_name = (std::vector) of cells
1347 // numbers]
1348 std::map<std::string, std::vector<int>> elsets_list;
1349 };
1350} // namespace
1351
1352template <int dim, int spacedim>
1353void
1355 const bool apply_all_indicators_to_manifolds)
1356{
1357 Assert(tria != nullptr, ExcNoTriangulationSelected());
1358 // This implementation has only been verified for:
1359 // - 2d grids with codimension 0
1360 // - 3d grids with codimension 0
1361 // - 3d grids with codimension 1
1362 Assert((spacedim == 2 && dim == spacedim) ||
1363 (spacedim == 3 && (dim == spacedim || dim == spacedim - 1)),
1365 AssertThrow(in.fail() == false, ExcIO());
1366
1367 // Read in the Abaqus file into an intermediate object
1368 // that is to be passed along to the UCD reader
1369 Abaqus_to_UCD<dim, spacedim> abaqus_to_ucd;
1370 abaqus_to_ucd.read_in_abaqus(in);
1371
1372 std::stringstream in_ucd;
1373 abaqus_to_ucd.write_out_avs_ucd(in_ucd);
1374
1375 // This next call is wrapped in a try-catch for the following reason:
1376 // It ensures that if the Abaqus mesh is read in correctly but produces
1377 // an erroneous result then the user is alerted to the source of the problem
1378 // and doesn't think that they've somehow called the wrong function.
1379 try
1380 {
1381 read_ucd(in_ucd, apply_all_indicators_to_manifolds);
1382 }
1383 catch (std::exception &exc)
1384 {
1385 std::cerr << "Exception on processing internal UCD data: " << std::endl
1386 << exc.what() << std::endl;
1387
1389 false,
1390 ExcMessage(
1391 "Internal conversion from ABAQUS file to UCD format was unsuccessful. "
1392 "More information is provided in an error message printed above. "
1393 "Are you sure that your ABAQUS mesh file conforms with the requirements "
1394 "listed in the documentation?"));
1395 }
1396 catch (...)
1397 {
1399 false,
1400 ExcMessage(
1401 "Internal conversion from ABAQUS file to UCD format was unsuccessful. "
1402 "Are you sure that your ABAQUS mesh file conforms with the requirements "
1403 "listed in the documentation?"));
1404 }
1405}
1406
1407
1408template <int dim, int spacedim>
1409void
1411{
1412 Assert(tria != nullptr, ExcNoTriangulationSelected());
1413 Assert(dim == 2, ExcNotImplemented());
1414
1415 AssertThrow(in.fail() == false, ExcIO());
1416
1417 // skip comments at start of file
1418 skip_comment_lines(in, '#');
1419
1420 // first read in identifier string
1421 std::string line;
1422 getline(in, line);
1423
1424 AssertThrow(line == "MeshVersionFormatted 0", ExcInvalidDBMESHInput(line));
1425
1426 skip_empty_lines(in);
1427
1428 // next read dimension
1429 getline(in, line);
1430 AssertThrow(line == "Dimension", ExcInvalidDBMESHInput(line));
1431 unsigned int dimension;
1432 in >> dimension;
1433 AssertThrow(dimension == dim, ExcDBMESHWrongDimension(dimension));
1434 skip_empty_lines(in);
1435
1436 // now there are a lot of fields of
1437 // which we don't know the exact
1438 // meaning and which are far from
1439 // being properly documented in the
1440 // manual. we skip everything until
1441 // we find a comment line with the
1442 // string "# END". at some point in
1443 // the future, someone may have the
1444 // knowledge to parse and interpret
1445 // the other fields in between as
1446 // well...
1447 while (getline(in, line), line.find("# END") == std::string::npos)
1448 ;
1449 skip_empty_lines(in);
1450
1451
1452 // now read vertices
1453 getline(in, line);
1454 AssertThrow(line == "Vertices", ExcInvalidDBMESHInput(line));
1455
1456 unsigned int n_vertices;
1457 double dummy;
1458
1459 in >> n_vertices;
1460 std::vector<Point<spacedim>> vertices(n_vertices);
1461 for (unsigned int vertex = 0; vertex < n_vertices; ++vertex)
1462 {
1463 // read vertex coordinates
1464 for (unsigned int d = 0; d < dim; ++d)
1465 in >> vertices[vertex][d];
1466 // read Ref phi_i, whatever that may be
1467 in >> dummy;
1468 }
1469 AssertThrow(in, ExcInvalidDBMeshFormat());
1470
1471 skip_empty_lines(in);
1472
1473 // read edges. we ignore them at
1474 // present, so just read them and
1475 // discard the input
1476 getline(in, line);
1477 AssertThrow(line == "Edges", ExcInvalidDBMESHInput(line));
1478
1479 unsigned int n_edges;
1480 in >> n_edges;
1481 for (unsigned int edge = 0; edge < n_edges; ++edge)
1482 {
1483 // read vertex indices
1484 in >> dummy >> dummy;
1485 // read Ref phi_i, whatever that may be
1486 in >> dummy;
1487 }
1488 AssertThrow(in, ExcInvalidDBMeshFormat());
1489
1490 skip_empty_lines(in);
1491
1492
1493
1494 // read cracked edges (whatever
1495 // that may be). we ignore them at
1496 // present, so just read them and
1497 // discard the input
1498 getline(in, line);
1499 AssertThrow(line == "CrackedEdges", ExcInvalidDBMESHInput(line));
1500
1501 in >> n_edges;
1502 for (unsigned int edge = 0; edge < n_edges; ++edge)
1503 {
1504 // read vertex indices
1505 in >> dummy >> dummy;
1506 // read Ref phi_i, whatever that may be
1507 in >> dummy;
1508 }
1509 AssertThrow(in, ExcInvalidDBMeshFormat());
1510
1511 skip_empty_lines(in);
1512
1513
1514 // now read cells.
1515 // set up array of cells
1516 getline(in, line);
1517 AssertThrow(line == "Quadrilaterals", ExcInvalidDBMESHInput(line));
1518
1519 constexpr std::array<unsigned int, 8> local_vertex_numbering = {
1520 {0, 1, 5, 4, 2, 3, 7, 6}};
1521 std::vector<CellData<dim>> cells;
1522 SubCellData subcelldata;
1523 unsigned int n_cells;
1524 in >> n_cells;
1525 for (unsigned int cell = 0; cell < n_cells; ++cell)
1526 {
1527 // read in vertex numbers. they
1528 // are 1-based, so subtract one
1529 cells.emplace_back();
1530 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
1531 {
1532 in >>
1533 cells.back().vertices[dim == 3 ? local_vertex_numbering[i] :
1535
1536 AssertThrow((cells.back().vertices[i] >= 1) &&
1537 (static_cast<unsigned int>(cells.back().vertices[i]) <=
1538 vertices.size()),
1539 ExcInvalidVertexIndex(cell, cells.back().vertices[i]));
1540
1541 --cells.back().vertices[i];
1542 }
1543
1544 // read and discard Ref phi_i
1545 in >> dummy;
1546 }
1547 AssertThrow(in, ExcInvalidDBMeshFormat());
1548
1549 skip_empty_lines(in);
1550
1551
1552 // then there are again a whole lot
1553 // of fields of which I have no
1554 // clue what they mean. skip them
1555 // all and leave the interpretation
1556 // to other implementers...
1557 while (getline(in, line), ((line.find("End") == std::string::npos) && (in)))
1558 ;
1559 // ok, so we are not at the end of
1560 // the file, that's it, mostly
1561 AssertThrow(in.fail() == false, ExcIO());
1562
1563 apply_grid_fixup_functions(vertices, cells, subcelldata);
1564 tria->create_triangulation(vertices, cells, subcelldata);
1565}
1566
1567
1568
1569template <int dim, int spacedim>
1570void
1572{
1573 Assert(tria != nullptr, ExcNoTriangulationSelected());
1574 AssertThrow(in.fail() == false, ExcIO());
1575
1576 const auto reference_cell = ReferenceCells::get_hypercube<dim>();
1577
1578 std::string line;
1579 // skip comments at start of file
1580 std::getline(in, line);
1581
1582 unsigned int n_vertices;
1583 unsigned int n_cells;
1584
1585 // read cells, throw away rest of line
1586 in >> n_cells;
1587 std::getline(in, line);
1588
1589 in >> n_vertices;
1590 std::getline(in, line);
1591
1592 // ignore following 8 lines
1593 for (unsigned int i = 0; i < 8; ++i)
1594 std::getline(in, line);
1595
1596 // set up array of cells
1597 std::vector<CellData<dim>> cells(n_cells);
1598 SubCellData subcelldata;
1599
1600 for (CellData<dim> &cell : cells)
1601 {
1602 // note that since in the input file we found the number of cells at the
1603 // top, there should still be input here, so check this:
1604 AssertThrow(in.fail() == false, ExcIO());
1605
1606 // XDA happens to use ExodusII's numbering because XDA/XDR is libMesh's
1607 // native format, and libMesh's node numberings come from ExodusII:
1608 for (unsigned int i = 0; i < GeometryInfo<dim>::vertices_per_cell; ++i)
1609 in >> cell.vertices[reference_cell.exodusii_vertex_to_deal_vertex(i)];
1610 }
1611
1612 // set up array of vertices
1613 std::vector<Point<spacedim>> vertices(n_vertices);
1614 for (Point<spacedim> &vertex : vertices)
1615 {
1616 for (unsigned int d = 0; d < spacedim; ++d)
1617 in >> vertex[d];
1618 for (unsigned int d = spacedim; d < 3; ++d)
1619 {
1620 // file is always in 3d
1621 double dummy;
1622 in >> dummy;
1623 }
1624 }
1625 AssertThrow(in.fail() == false, ExcIO());
1626
1627 apply_grid_fixup_functions(vertices, cells, subcelldata);
1628 tria->create_triangulation(vertices, cells, subcelldata);
1629}
1630
1631
1632
1633template <int dim, int spacedim>
1634void
1636{
1637 Assert(tria != nullptr, ExcNoTriangulationSelected());
1638 AssertThrow(in.fail() == false, ExcIO());
1639
1640 // Start by making our life a bit easier: The file format
1641 // allows for comments in a whole bunch of places, including
1642 // on separate lines, at line ends, and that's just a hassle to
1643 // parse because we will have to check in every line whether there
1644 // is a comment. To make things easier, just read it all in up
1645 // front, strip comments, eat trailing whitespace, and
1646 // concatenate it all into one big string from which we will
1647 // then read. We lose the ability to output error messages tied
1648 // to individual lines of the input, but none of the other
1649 // readers does that either.
1650 std::stringstream whole_file;
1651 while (in)
1652 {
1653 // read one line
1654 std::string line;
1655 std::getline(in, line);
1656
1657 // We tend to get these sorts of files from folks who run on
1658 // Windows and where line endings are \r\n instead of just
1659 // \n. The \r is redundant unless you still use a line printer,
1660 // so get rid of it in order to not confuse any of the functions
1661 // below that try to interpret the entire content of a line
1662 // as a string:
1663 if ((line.size() > 0) && (line.back() == '\r'))
1664 line.erase(line.size() - 1);
1665
1666 // Strip trailing comments, then strip whatever spaces are at the end
1667 // of the line, and if anything is left, concatenate that to the previous
1668 // content of the file :
1669 if (line.find('#') != std::string::npos)
1670 line.erase(line.find('#'), std::string::npos);
1671 while ((line.size() > 0) && (line.back() == ' '))
1672 line.erase(line.size() - 1);
1673
1674 if (line.size() > 0)
1675 whole_file << '\n' << line;
1676 }
1677
1678 // Now start to read the contents of this so-simplified file. A typical
1679 // header of these files will look like this:
1680 // # Created by COMSOL Multiphysics.
1681 //
1682 // # Major & minor version
1683 // 0 1
1684 // 1 # number of tags
1685 // # Tags
1686 // 5 mesh1
1687 // 1 # number of types
1688 // # Types
1689 // 3 obj
1690
1691 AssertThrow(whole_file.fail() == false, ExcIO());
1692
1693 {
1694 unsigned int version_major, version_minor;
1695 whole_file >> version_major >> version_minor;
1696 AssertThrow((version_major == 0) && (version_minor == 1),
1697 ExcMessage("deal.II can currently only read version 0.1 "
1698 "of the mphtxt file format."));
1699 }
1700
1701 // It's not clear what the 'tags' are, but read them and discard them
1702 {
1703 unsigned int n_tags;
1704 whole_file >> n_tags;
1705 for (unsigned int i = 0; i < n_tags; ++i)
1706 {
1707 std::string dummy;
1708 while (whole_file.peek() == '\n')
1709 whole_file.get();
1710 std::getline(whole_file, dummy);
1711 }
1712 }
1713
1714 // Do the same with the 'types'
1715 {
1716 unsigned int n_types;
1717 whole_file >> n_types;
1718 for (unsigned int i = 0; i < n_types; ++i)
1719 {
1720 std::string dummy;
1721 while (whole_file.peek() == '\n')
1722 whole_file.get();
1723 std::getline(whole_file, dummy);
1724 }
1725 }
1726
1727 // Then move on to the actual mesh. A typical header of this part will
1728 // look like this:
1729 // # --------- Object 0 ----------
1730 //
1731 // 0 0 1
1732 // 4 Mesh # class
1733 // 4 # version
1734 // 3 # sdim
1735 // 1204 # number of mesh vertices
1736 // 0 # lowest mesh vertex index
1737 //
1738 // # Mesh vertex coordinates
1739 // ...
1740 AssertThrow(whole_file.fail() == false, ExcIO());
1741 {
1742 unsigned int dummy;
1743 whole_file >> dummy >> dummy >> dummy;
1744 }
1745 {
1746 std::string s;
1747 while (whole_file.peek() == '\n')
1748 whole_file.get();
1749 std::getline(whole_file, s);
1750 AssertThrow(s == "4 Mesh",
1751 ExcMessage("Expected '4 Mesh', but got '" + s + "'."));
1752 }
1753 {
1754 unsigned int version;
1755 whole_file >> version;
1756 AssertThrow(version == 4, ExcNotImplemented());
1757 }
1758 {
1759 unsigned int file_space_dim;
1760 whole_file >> file_space_dim;
1761
1762 AssertThrow(file_space_dim == spacedim,
1763 ExcMessage(
1764 "The mesh file uses a different number of space dimensions "
1765 "than the triangulation you want to read it into."));
1766 }
1767 unsigned int n_vertices;
1768 whole_file >> n_vertices;
1769
1770 unsigned int starting_vertex_index;
1771 whole_file >> starting_vertex_index;
1772
1773 std::vector<Point<spacedim>> vertices(n_vertices);
1774 for (unsigned int v = 0; v < n_vertices; ++v)
1775 whole_file >> vertices[v];
1776
1777 // Then comes a block that looks like this:
1778 // 4 # number of element types
1779 //
1780 // # Type #0
1781 // 3 vtx # type name
1782 //
1783 //
1784 // 1 # number of vertices per element
1785 // 18 # number of elements
1786 // # Elements
1787 // 4
1788 // 12
1789 // 19
1790 // 80
1791 // 143
1792 // [...]
1793 // 1203
1794 //
1795 // 18 # number of geometric entity indices
1796 // # Geometric entity indices
1797 // 2
1798 // 0
1799 // 11
1800 // 6
1801 // 3
1802 // [...]
1803 AssertThrow(whole_file.fail() == false, ExcIO());
1804
1805 std::vector<CellData<dim>> cells;
1806 SubCellData subcelldata;
1807
1808 unsigned int n_types;
1809 whole_file >> n_types;
1810 for (unsigned int type = 0; type < n_types; ++type)
1811 {
1812 // The object type is prefixed by the number of characters the
1813 // object type string takes up (e.g., 3 for 'tri' and 5 for
1814 // 'prism'), but we really don't need that.
1815 {
1816 unsigned int dummy;
1817 whole_file >> dummy;
1818 }
1819
1820 // Read the object type. Also do a number of safety checks.
1821 std::string object_name;
1822 whole_file >> object_name;
1823
1824 const std::set<std::string> known_object_names = {
1825 "vtx", "edg", "tri", "quad", "tet", "prism"
1826 // TODO: Add hexahedra and pyramids once we have a sample input file
1827 // that contains these
1828 };
1829 AssertThrow(known_object_names.find(object_name) !=
1830 known_object_names.end(),
1831 ExcMessage("The input file contains a cell type <" +
1832 object_name +
1833 "> that the reader does not "
1834 "current support"));
1835
1836 unsigned int n_vertices_per_element;
1837 whole_file >> n_vertices_per_element;
1838
1839 unsigned int n_elements;
1840 whole_file >> n_elements;
1841
1842
1843 if ((dim >= 3) && (object_name == "tet"))
1844 {
1845 AssertThrow(dim >= 3,
1846 ExcMessage("Tetrahedra should not appear in input files "
1847 "for 1d or 2d meshes."));
1848 AssertThrow(n_vertices_per_element == 4, ExcInternalError());
1849 }
1850 else if ((dim >= 3) && (object_name == "prism"))
1851 {
1852 AssertThrow(dim >= 3,
1853 ExcMessage(
1854 "Prisms (wedges) should not appear in input files "
1855 "for 1d or 2d meshes."));
1856 AssertThrow(n_vertices_per_element == 6, ExcInternalError());
1857 }
1858 else if ((dim >= 2) && (object_name == "tri"))
1859 {
1860 AssertThrow(dim >= 2,
1861 ExcMessage("Triangles should not appear in input files "
1862 "for 1d meshes."));
1863 AssertThrow(n_vertices_per_element == 3, ExcInternalError());
1864 }
1865 else if ((dim >= 2) && (object_name == "quad"))
1866 {
1867 AssertThrow(dim >= 2,
1868 ExcMessage(
1869 "Quadrilaterals should not appear in input files "
1870 "for 1d meshes."));
1871 AssertThrow(n_vertices_per_element == 4, ExcInternalError());
1872 }
1873 else if (object_name == "edg")
1874 {
1875 AssertThrow(n_vertices_per_element == 2, ExcInternalError());
1876 }
1877 else if (object_name == "vtx")
1878 {
1879 AssertThrow(n_vertices_per_element == 1, ExcInternalError());
1880 }
1881 else
1883
1884 // Next, for each element read the vertex numbers. Then we have
1885 // to decide what to do with it. If it is a vertex, we ignore
1886 // the information. If it is a cell, we have to put it into the
1887 // appropriate object, and the same if it is an edge or
1888 // face. Since multiple object type blocks can refer to cells or
1889 // faces (e.g., for mixed meshes, or for prisms where there are
1890 // boundary triangles and boundary quads), the element index 'e'
1891 // below does not correspond to the index in the 'cells' or
1892 // 'subcelldata.boundary_*' objects; we just keep pushing
1893 // elements onto the back.
1894 //
1895 // In any case, we adjust vertex indices right after reading them based on
1896 // the starting index read above. As ReferenceCells::max_n_vertices<dim>()
1897 // > ReferenceCells::max_n_vertices<dim - 1>(), we can use this vector for
1898 // cell, face, and line vertices.
1900 ReferenceCells::max_n_vertices<dim>()>
1901 vertices_for_this_element(n_vertices_per_element);
1902 for (unsigned int e = 0; e < n_elements; ++e)
1903 {
1904 AssertThrow(whole_file.fail() == false, ExcIO());
1905 for (unsigned int v = 0; v < n_vertices_per_element; ++v)
1906 {
1907 whole_file >> vertices_for_this_element[v];
1908 vertices_for_this_element[v] -= starting_vertex_index;
1909 }
1910
1911 if ((dim >= 3) &&
1912 ((object_name == "tet") || (object_name == "prism")))
1913 {
1914 if constexpr (dim == 3)
1915 {
1916 cells.emplace_back();
1917 cells.back().vertices = vertices_for_this_element;
1918 }
1919 else
1921 }
1922 else if ((dim >= 2) &&
1923 ((object_name == "tri") || (object_name == "quad")))
1924 {
1925 if constexpr (dim == 2)
1926 {
1927 cells.emplace_back();
1928 cells.back().vertices = vertices_for_this_element;
1929 }
1930 else
1931 {
1932 subcelldata.boundary_quads.emplace_back();
1933 subcelldata.boundary_quads.back().vertices.assign(
1934 vertices_for_this_element.begin(),
1935 vertices_for_this_element.end());
1936 }
1937 }
1938 else if (object_name == "edg")
1939 {
1940 if constexpr (dim == 1)
1941 {
1942 cells.emplace_back();
1943 cells.back().vertices = vertices_for_this_element;
1944 }
1945 else
1946 {
1947 subcelldata.boundary_lines.emplace_back();
1948 subcelldata.boundary_lines.back().vertices.assign(
1949 vertices_for_this_element.begin(),
1950 vertices_for_this_element.end());
1951 }
1952 }
1953 else if (object_name == "vtx")
1954 ; // do nothing
1955
1956 else
1958 }
1959
1960 // Then also read the "geometric entity indices". There need to
1961 // be as many as there were elements to begin with, or
1962 // alternatively zero if no geometric entity indices will be set
1963 // at all.
1964 unsigned int n_geom_entity_indices;
1965 whole_file >> n_geom_entity_indices;
1966 AssertThrow((n_geom_entity_indices == 0) ||
1967 (n_geom_entity_indices == n_elements),
1969
1970 // Loop over these objects. Since we pushed them onto the back
1971 // of various arrays before, we need to recalculate which index
1972 // in these array element 'e' corresponds to when setting
1973 // boundary and manifold indicators.
1974 if (n_geom_entity_indices != 0)
1975 {
1976 for (unsigned int e = 0; e < n_geom_entity_indices; ++e)
1977 {
1978 AssertThrow(whole_file.fail() == false, ExcIO());
1979 unsigned int geometric_entity_index;
1980 whole_file >> geometric_entity_index;
1981 if (object_name == "vtx")
1982 ; // do nothing
1983 else if (object_name == "edg")
1984 {
1985 if constexpr (dim == 1)
1986 cells[cells.size() - n_elements + e].material_id =
1987 geometric_entity_index;
1988 else
1989 subcelldata
1990 .boundary_lines[subcelldata.boundary_lines.size() -
1991 n_elements + e]
1992 .boundary_id = geometric_entity_index;
1993 }
1994 else if ((dim >= 2) &&
1995 ((object_name == "tri") || (object_name == "quad")))
1996 {
1997 if constexpr (dim == 2)
1998 cells[cells.size() - n_elements + e].material_id =
1999 geometric_entity_index;
2000 else
2001 subcelldata
2002 .boundary_quads[subcelldata.boundary_quads.size() -
2003 n_elements + e]
2004 .boundary_id = geometric_entity_index;
2005 }
2006 else if ((dim >= 3) &&
2007 ((object_name == "tet") || (object_name == "prism")))
2008 {
2009 if constexpr (dim == 3)
2010 cells[cells.size() - n_elements + e].material_id =
2011 geometric_entity_index;
2012 else
2014 }
2015 else
2017 }
2018 }
2019 }
2020 AssertThrow(whole_file.fail() == false, ExcIO());
2021
2022 // Now finally create the mesh. Because of the quirk with boundary
2023 // edges and faces described in the documentation of this function,
2024 // we can't pass 'subcelldata' as third argument to this function.
2025 // Rather, we then have to fix up the generated triangulation
2026 // after the fact :-(
2027 tria->create_triangulation(vertices, cells, {});
2028
2029 // Now for the "fixing up" step mentioned above. To make things a bit
2030 // simpler, let us sort first normalize the order of vertices in edges
2031 // and triangles/quads, and then sort lexicographically:
2032 if constexpr (dim >= 2)
2033 {
2034 for (auto &line : subcelldata.boundary_lines)
2035 {
2036 Assert(line.vertices.size() == 2, ExcInternalError());
2037 if (line.vertices[1] < line.vertices[0])
2038 std::swap(line.vertices[0], line.vertices[1]);
2039 }
2040 std::sort(subcelldata.boundary_lines.begin(),
2041 subcelldata.boundary_lines.end(),
2042 [](const CellData<1> &a, const CellData<1> &b) {
2043 return std::lexicographical_compare(a.vertices.begin(),
2044 a.vertices.end(),
2045 b.vertices.begin(),
2046 b.vertices.end());
2047 });
2048 }
2049
2050 // Now for boundary faces. For triangles, we can sort the vertices in
2051 // ascending vertex index order because every order corresponds to a circular
2052 // order either seen from one side or the other. For quads, the situation is
2053 // more difficult. But fortunately, we do not actually need to keep the
2054 // vertices in any specific order because there can be no two quads with the
2055 // same vertices but listed in different orders that actually correspond to
2056 // different things. If we had given this information to
2057 // Triangulation::create_triangulation(), we would probably have wanted to
2058 // keep things in a specific order so that the vertices define a proper
2059 // coordinate system on the quad, but that's not our goal here so we just
2060 // sort.
2061 if constexpr (dim >= 3)
2062 {
2063 for (auto &face : subcelldata.boundary_quads)
2064 {
2065 Assert((face.vertices.size() == 3) || (face.vertices.size() == 4),
2067 std::sort(face.vertices.begin(), face.vertices.end());
2068 }
2069 std::sort(subcelldata.boundary_quads.begin(),
2070 subcelldata.boundary_quads.end(),
2071 [](const CellData<2> &a, const CellData<2> &b) {
2072 return std::lexicographical_compare(a.vertices.begin(),
2073 a.vertices.end(),
2074 b.vertices.begin(),
2075 b.vertices.end());
2076 });
2077 }
2078
2079 // OK, now we can finally go about fixing up edges and faces.
2080 if constexpr (dim >= 2)
2081 {
2082 for (const auto &cell : tria->active_cell_iterators())
2083 for (const auto &face : cell->face_iterators())
2084 if (face->at_boundary())
2085 {
2086 // We found a face at the boundary. Let us look up whether it
2087 // was listed in subcelldata
2088 if constexpr (dim == 2)
2089 {
2090 std::array<unsigned int, 2> face_vertex_indices = {
2091 {face->vertex_index(0), face->vertex_index(1)}};
2092 if (face_vertex_indices[0] > face_vertex_indices[1])
2093 std::swap(face_vertex_indices[0], face_vertex_indices[1]);
2094
2095 // See if we can find an edge with these indices:
2096 const auto p =
2097 std::lower_bound(subcelldata.boundary_lines.begin(),
2098 subcelldata.boundary_lines.end(),
2099 face_vertex_indices,
2100 [](const CellData<1> &a,
2101 const std::array<unsigned int, 2>
2102 &face_vertex_indices) -> bool {
2103 return std::lexicographical_compare(
2104 a.vertices.begin(),
2105 a.vertices.end(),
2106 face_vertex_indices.begin(),
2107 face_vertex_indices.end());
2108 });
2109
2110 if ((p != subcelldata.boundary_lines.end()) &&
2111 (p->vertices[0] == face_vertex_indices[0]) &&
2112 (p->vertices[1] == face_vertex_indices[1]))
2113 {
2114 face->set_boundary_id(p->boundary_id);
2115 }
2116 }
2117 else if constexpr (dim == 3)
2118 {
2119 // In 3d, we need to look things up in the boundary_quads
2120 // structure (which also stores boundary triangles) as well as
2121 // for the edges
2122 // Note: we are choosing size 16 instead of
2123 // ReferenceCells::max_n_vertices<2>() because of a gcc bug
2124 // that produces a warning about out-of-bound access inside
2125 // std::sort:
2127 face_vertex_indices(face->n_vertices());
2128 for (unsigned int v = 0; v < face->n_vertices(); ++v)
2129 face_vertex_indices[v] = face->vertex_index(v);
2130 std::sort(face_vertex_indices.begin(),
2131 face_vertex_indices.end());
2132
2133 // See if we can find a face with these indices:
2134 const auto p = std::lower_bound(
2135 subcelldata.boundary_quads.begin(),
2136 subcelldata.boundary_quads.end(),
2137 face_vertex_indices,
2138 [](const CellData<2> &a,
2139 const auto &face_vertex_indices) -> bool {
2140 return std::lexicographical_compare(
2141 a.vertices.begin(),
2142 a.vertices.end(),
2143 face_vertex_indices.begin(),
2144 face_vertex_indices.end());
2145 });
2146
2147 if ((p != subcelldata.boundary_quads.end()) &&
2148 (std::equal(p->vertices.begin(),
2149 p->vertices.end(),
2150 face_vertex_indices.begin(),
2151 face_vertex_indices.end())))
2152 {
2153 face->set_boundary_id(p->boundary_id);
2154 }
2155
2156
2157 // Now do the same for the edges
2158 for (unsigned int e = 0; e < face->n_lines(); ++e)
2159 {
2160 const auto edge = face->line(e);
2161
2162 std::array<unsigned int, 2> edge_vertex_indices = {
2163 {edge->vertex_index(0), edge->vertex_index(1)}};
2164 if (edge_vertex_indices[0] > edge_vertex_indices[1])
2165 std::swap(edge_vertex_indices[0],
2166 edge_vertex_indices[1]);
2167
2168 // See if we can find an edge with these indices:
2169 const auto p =
2170 std::lower_bound(subcelldata.boundary_lines.begin(),
2171 subcelldata.boundary_lines.end(),
2172 edge_vertex_indices,
2173 [](const CellData<1> &a,
2174 const std::array<unsigned int, 2>
2175 &edge_vertex_indices) -> bool {
2176 return std::lexicographical_compare(
2177 a.vertices.begin(),
2178 a.vertices.end(),
2179 edge_vertex_indices.begin(),
2180 edge_vertex_indices.end());
2181 });
2182
2183 if ((p != subcelldata.boundary_lines.end()) &&
2184 (p->vertices[0] == edge_vertex_indices[0]) &&
2185 (p->vertices[1] == edge_vertex_indices[1]))
2186 {
2187 edge->set_boundary_id(p->boundary_id);
2188 }
2189 }
2190 }
2191 }
2192 }
2193}
2194
2195
2196
2197template <int dim, int spacedim>
2198void
2199GridIn<dim, spacedim>::read_msh(std::istream &input_stream)
2200{
2201 Assert(tria != nullptr, ExcNoTriangulationSelected());
2202 AssertThrow(input_stream.fail() == false, ExcIO());
2203
2204 unsigned int n_vertices;
2205 unsigned int n_cells;
2206 unsigned int dummy;
2207 std::string line;
2208 // This array stores maps from the 'entities' to the 'physical tags' for
2209 // points, curves, surfaces and volumes. We use this information later to
2210 // assign boundary ids.
2211 std::array<std::map<int, int>, 4> tag_maps;
2212
2213 // contain the content of the file stripped of the comments
2214 std::string stripped_file;
2215
2216 // Comments can be included by mesh generating software and must be deleted,
2217 // a string is filed with the content of the file stripped of the comments
2218 while (std::getline(input_stream, line))
2219 {
2220 if (line == "@f$Comments")
2221 {
2222 while (std::getline(input_stream, line))
2223 {
2224 if (line == "@f$EndComments")
2225 {
2226 break;
2227 }
2228 }
2229 continue;
2230 }
2231 stripped_file += line + '\n';
2232 }
2233
2234 // Restart reading the file normally since it has been stripped of comments
2235 std::istringstream in(stripped_file);
2236
2237 in >> line;
2238
2239 // first determine file format
2240 unsigned int gmsh_file_format = 0;
2241 if (line == "@f$NOD")
2242 gmsh_file_format = 10;
2243 else if (line == "@f$MeshFormat")
2244 gmsh_file_format = 20;
2245 else
2246 AssertThrow(false, ExcInvalidGMSHInput(line));
2247
2248 // if file format is 2.0 or greater then we also have to read the rest of
2249 // the header
2250 if (gmsh_file_format == 20)
2251 {
2252 double version;
2253 unsigned int file_type, data_size;
2254
2255 in >> version >> file_type >> data_size;
2256
2257 AssertThrow((version >= 2.0) && (version <= 4.1), ExcNotImplemented());
2258 gmsh_file_format = static_cast<unsigned int>(version * 10);
2259
2260 AssertThrow(file_type == 0, ExcNotImplemented());
2261 AssertThrow(data_size == sizeof(double), ExcNotImplemented());
2262
2263 // read the end of the header and the first line of the nodes
2264 // description to synch ourselves with the format 1 handling above
2265 in >> line;
2266 AssertThrow(line == "@f$EndMeshFormat", ExcInvalidGMSHInput(line));
2267
2268 in >> line;
2269 // if the next block is of kind @f$PhysicalNames, ignore it
2270 if (line == "@f$PhysicalNames")
2271 {
2272 do
2273 {
2274 in >> line;
2275 }
2276 while (line != "@f$EndPhysicalNames");
2277 in >> line;
2278 }
2279
2280 // if the next block is of kind @f$Entities, parse it
2281 if (line == "@f$Entities")
2282 {
2283 unsigned long n_points, n_curves, n_surfaces, n_volumes;
2284
2285 in >> n_points >> n_curves >> n_surfaces >> n_volumes;
2286 for (unsigned int i = 0; i < n_points; ++i)
2287 {
2288 // parse point ids
2289 int tag;
2290 unsigned int n_physicals;
2291 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2292 box_max_z;
2293
2294 // we only care for 'tag' as key for tag_maps[0]
2295 if (gmsh_file_format > 40)
2296 {
2297 in >> tag >> box_min_x >> box_min_y >> box_min_z >>
2298 n_physicals;
2299 box_max_x = box_min_x;
2300 box_max_y = box_min_y;
2301 box_max_z = box_min_z;
2302 }
2303 else
2304 {
2305 in >> tag >> box_min_x >> box_min_y >> box_min_z >>
2306 box_max_x >> box_max_y >> box_max_z >> n_physicals;
2307 }
2308 // if there is a physical tag, we will use it as boundary id
2309 // below
2310 AssertThrow(n_physicals < 2,
2311 ExcMessage("More than one tag is not supported!"));
2312 // if there is no physical tag, use 0 as default
2313 int physical_tag = 0;
2314 for (unsigned int j = 0; j < n_physicals; ++j)
2315 in >> physical_tag;
2316 tag_maps[0][tag] = physical_tag;
2317 }
2318 for (unsigned int i = 0; i < n_curves; ++i)
2319 {
2320 // parse curve ids
2321 int tag;
2322 unsigned int n_physicals;
2323 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2324 box_max_z;
2325
2326 // we only care for 'tag' as key for tag_maps[1]
2327 in >> tag >> box_min_x >> box_min_y >> box_min_z >> box_max_x >>
2328 box_max_y >> box_max_z >> n_physicals;
2329 // if there is a physical tag, we will use it as boundary id
2330 // below
2331 AssertThrow(n_physicals < 2,
2332 ExcMessage("More than one tag is not supported!"));
2333 // if there is no physical tag, use 0 as default
2334 int physical_tag = 0;
2335 for (unsigned int j = 0; j < n_physicals; ++j)
2336 in >> physical_tag;
2337 tag_maps[1][tag] = physical_tag;
2338 // we don't care about the points associated to a curve, but
2339 // have to parse them anyway because their format is
2340 // unstructured
2341 in >> n_points;
2342 for (unsigned int j = 0; j < n_points; ++j)
2343 in >> tag;
2344 }
2345
2346 for (unsigned int i = 0; i < n_surfaces; ++i)
2347 {
2348 // parse surface ids
2349 int tag;
2350 unsigned int n_physicals;
2351 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2352 box_max_z;
2353
2354 // we only care for 'tag' as key for tag_maps[2]
2355 in >> tag >> box_min_x >> box_min_y >> box_min_z >> box_max_x >>
2356 box_max_y >> box_max_z >> n_physicals;
2357 // if there is a physical tag, we will use it as boundary id
2358 // below
2359 AssertThrow(n_physicals < 2,
2360 ExcMessage("More than one tag is not supported!"));
2361 // if there is no physical tag, use 0 as default
2362 int physical_tag = 0;
2363 for (unsigned int j = 0; j < n_physicals; ++j)
2364 in >> physical_tag;
2365 tag_maps[2][tag] = physical_tag;
2366 // we don't care about the curves associated to a surface, but
2367 // have to parse them anyway because their format is
2368 // unstructured
2369 in >> n_curves;
2370 for (unsigned int j = 0; j < n_curves; ++j)
2371 in >> tag;
2372 }
2373 for (unsigned int i = 0; i < n_volumes; ++i)
2374 {
2375 // parse volume ids
2376 int tag;
2377 unsigned int n_physicals;
2378 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2379 box_max_z;
2380
2381 // we only care for 'tag' as key for tag_maps[3]
2382 in >> tag >> box_min_x >> box_min_y >> box_min_z >> box_max_x >>
2383 box_max_y >> box_max_z >> n_physicals;
2384 // if there is a physical tag, we will use it as boundary id
2385 // below
2386 AssertThrow(n_physicals < 2,
2387 ExcMessage("More than one tag is not supported!"));
2388 // if there is no physical tag, use 0 as default
2389 int physical_tag = 0;
2390 for (unsigned int j = 0; j < n_physicals; ++j)
2391 in >> physical_tag;
2392 tag_maps[3][tag] = physical_tag;
2393 // we don't care about the surfaces associated to a volume, but
2394 // have to parse them anyway because their format is
2395 // unstructured
2396 in >> n_surfaces;
2397 for (unsigned int j = 0; j < n_surfaces; ++j)
2398 in >> tag;
2399 }
2400 in >> line;
2401 AssertThrow(line == "@f$EndEntities", ExcInvalidGMSHInput(line));
2402 in >> line;
2403 }
2404
2405 // if the next block is of kind @f$PartitionedEntities, ignore it
2406 if (line == "@f$PartitionedEntities")
2407 {
2408 do
2409 {
2410 in >> line;
2411 }
2412 while (line != "@f$EndPartitionedEntities");
2413 in >> line;
2414 }
2415
2416 // but the next thing should,
2417 // in any case, be the list of
2418 // nodes:
2419 AssertThrow(line == "@f$Nodes", ExcInvalidGMSHInput(line));
2420 }
2421
2422 // now read the nodes list
2423 int n_entity_blocks = 1;
2424 if (gmsh_file_format > 40)
2425 {
2426 int min_node_tag;
2427 int max_node_tag;
2428 in >> n_entity_blocks >> n_vertices >> min_node_tag >> max_node_tag;
2429 }
2430 else if (gmsh_file_format == 40)
2431 {
2432 in >> n_entity_blocks >> n_vertices;
2433 }
2434 else
2435 in >> n_vertices;
2436 std::vector<Point<spacedim>> vertices(n_vertices);
2437 // set up mapping between numbering
2438 // in msh-file (nod) and in the
2439 // vertices vector
2440 std::map<int, int> vertex_indices;
2441
2442 {
2443 unsigned int global_vertex = 0;
2444 for (int entity_block = 0; entity_block < n_entity_blocks; ++entity_block)
2445 {
2446 int parametric;
2447 unsigned long numNodes;
2448
2449 if (gmsh_file_format < 40)
2450 {
2451 numNodes = n_vertices;
2452 parametric = 0;
2453 }
2454 else
2455 {
2456 // for gmsh_file_format 4.1 the order of tag and dim is reversed,
2457 // but we are ignoring both anyway.
2458 int tagEntity, dimEntity;
2459 in >> tagEntity >> dimEntity >> parametric >> numNodes;
2460 }
2461
2462 std::vector<int> vertex_numbers;
2463 int vertex_number;
2464 if (gmsh_file_format > 40)
2465 for (unsigned long vertex_per_entity = 0;
2466 vertex_per_entity < numNodes;
2467 ++vertex_per_entity)
2468 {
2469 in >> vertex_number;
2470 vertex_numbers.push_back(vertex_number);
2471 }
2472
2473 for (unsigned long vertex_per_entity = 0; vertex_per_entity < numNodes;
2474 ++vertex_per_entity, ++global_vertex)
2475 {
2476 int vertex_number;
2477 double x[3];
2478
2479 // read vertex
2480 if (gmsh_file_format > 40)
2481 {
2482 vertex_number = vertex_numbers[vertex_per_entity];
2483 in >> x[0] >> x[1] >> x[2];
2484 }
2485 else
2486 in >> vertex_number >> x[0] >> x[1] >> x[2];
2487
2489 global_vertex < n_vertices,
2490 ExcMessage(
2491 "The Gmsh file lists more nodes than were declared in the "
2492 "header of the @f$Nodes section."));
2493
2494 for (unsigned int d = 0; d < spacedim; ++d)
2495 vertices[global_vertex][d] = x[d];
2496 // store mapping
2497 vertex_indices[vertex_number] = global_vertex;
2498
2499 // ignore parametric coordinates
2500 if (parametric != 0)
2501 {
2502 double u = 0.;
2503 double v = 0.;
2504 in >> u >> v;
2505 (void)u;
2506 (void)v;
2507 }
2508 }
2509 }
2510 AssertDimension(global_vertex, n_vertices);
2511 }
2512
2513 // Assert we reached the end of the block
2514 in >> line;
2515 const std::array<std::string, 2> end_nodes_marker{{"@f$ENDNOD", "@f$EndNodes"}};
2516 AssertThrow(line == end_nodes_marker[gmsh_file_format == 10 ? 0 : 1],
2517 ExcInvalidGMSHInput(line));
2518
2519 // Now read in next bit
2520 in >> line;
2521 const std::array<std::string, 2> begin_elements_marker{{"@f$ELM", "@f$Elements"}};
2522 AssertThrow(line == begin_elements_marker[gmsh_file_format == 10 ? 0 : 1],
2523 ExcInvalidGMSHInput(line));
2524
2525 // now read the cell list
2526 if (gmsh_file_format > 40)
2527 {
2528 int min_node_tag;
2529 int max_node_tag;
2530 in >> n_entity_blocks >> n_cells >> min_node_tag >> max_node_tag;
2531 }
2532 else if (gmsh_file_format == 40)
2533 {
2534 in >> n_entity_blocks >> n_cells;
2535 }
2536 else
2537 {
2538 n_entity_blocks = 1;
2539 in >> n_cells;
2540 }
2541
2542 // set up array of cells and subcells (faces). In 1d, there is currently no
2543 // standard way in deal.II to pass boundary indicators attached to
2544 // individual vertices, so do this by hand via the boundary_ids_1d array
2545 std::vector<CellData<dim>> cells;
2546 SubCellData subcelldata;
2547 std::map<unsigned int, types::boundary_id> boundary_ids_1d;
2548
2549 // Track the number of times each vertex is used in 1D. This determines
2550 // whether or not we can assign a boundary id to a vertex. This is necessary
2551 // because sometimes gmsh saves internal vertices in the @f$ELEM list in codim
2552 // 1 or codim 2.
2553 std::map<unsigned int, unsigned int> vertex_counts;
2554
2555 {
2556 constexpr std::array<unsigned int, 8> local_vertex_numbering = {
2557 {0, 1, 5, 4, 2, 3, 7, 6}};
2558 unsigned int global_cell = 0;
2559 for (int entity_block = 0; entity_block < n_entity_blocks; ++entity_block)
2560 {
2561 unsigned int material_id;
2562 unsigned long numElements;
2563 int cell_type;
2564
2565 if (gmsh_file_format < 40)
2566 {
2567 material_id = 0;
2568 cell_type = 0;
2569 numElements = n_cells;
2570 }
2571 else if (gmsh_file_format == 40)
2572 {
2573 int tagEntity;
2574 unsigned int dimEntity;
2575 in >> tagEntity >> dimEntity >> cell_type >> numElements;
2576 AssertThrow(dimEntity < tag_maps.size(),
2577 ExcInvalidGMSHInput(std::to_string(dimEntity)));
2578 material_id = tag_maps[dimEntity][tagEntity];
2579 }
2580 else
2581 {
2582 // for gmsh_file_format 4.1 the order of tag and dim is reversed,
2583 int tagEntity;
2584 unsigned int dimEntity;
2585 in >> dimEntity >> tagEntity >> cell_type >> numElements;
2586 AssertThrow(dimEntity < tag_maps.size(),
2587 ExcInvalidGMSHInput(std::to_string(dimEntity)));
2588 material_id = tag_maps[dimEntity][tagEntity];
2589 }
2590
2591 for (unsigned int cell_per_entity = 0; cell_per_entity < numElements;
2592 ++cell_per_entity, ++global_cell)
2593 {
2594 // note that since in the input
2595 // file we found the number of
2596 // cells at the top, there
2597 // should still be input here,
2598 // so check this:
2599 AssertThrow(in.fail() == false, ExcIO());
2600
2601 unsigned int nod_num;
2602
2603 /*
2604 For file format version 1, the format of each cell is as
2605 follows: elm-number elm-type reg-phys reg-elem number-of-nodes
2606 node-number-list
2607
2608 However, for version 2, the format reads like this:
2609 elm-number elm-type number-of-tags < tag > ...
2610 node-number-list
2611
2612 For version 4, we have:
2613 tag(int) numVert(int) ...
2614
2615 In the following, we will ignore the element number (we simply
2616 enumerate them in the order in which we read them, and we will
2617 take reg-phys (version 1) or the first tag (version 2, if any
2618 tag is given at all) as material id. For version 4, we already
2619 read the material and the cell type in above.
2620 */
2621
2622 unsigned int elm_number = 0;
2623 if (gmsh_file_format < 40)
2624 {
2625 in >> elm_number // ELM-NUMBER
2626 >> cell_type; // ELM-TYPE
2627 }
2628
2629 if (gmsh_file_format < 20)
2630 {
2631 in >> material_id // REG-PHYS
2632 >> dummy // reg_elm
2633 >> nod_num;
2634 }
2635 else if (gmsh_file_format < 40)
2636 {
2637 // read the tags; ignore all but the first one which we will
2638 // interpret as the material_id (for cells) or boundary_id
2639 // (for faces)
2640 unsigned int n_tags;
2641 in >> n_tags;
2642 if (n_tags > 0)
2643 in >> material_id;
2644 else
2645 material_id = 0;
2646
2647 for (unsigned int i = 1; i < n_tags; ++i)
2648 in >> dummy;
2649
2650 if (cell_type == 1) // line
2651 nod_num = 2;
2652 else if (cell_type == 2) // tri
2653 nod_num = 3;
2654 else if (cell_type == 3) // quad
2655 nod_num = 4;
2656 else if (cell_type == 4) // tet
2657 nod_num = 4;
2658 else if (cell_type == 5) // hex
2659 nod_num = 8;
2660 }
2661 else // file format version 4.0 and later
2662 {
2663 // ignore tag
2664 int tag;
2665 in >> tag;
2666
2667 if (cell_type == 1) // line
2668 nod_num = 2;
2669 else if (cell_type == 2) // tri
2670 nod_num = 3;
2671 else if (cell_type == 3) // quad
2672 nod_num = 4;
2673 else if (cell_type == 4) // tet
2674 nod_num = 4;
2675 else if (cell_type == 5) // hex
2676 nod_num = 8;
2677 }
2678
2679
2680 /* `ELM-TYPE'
2681 defines the geometrical type of the N-th element:
2682 `1'
2683 Line (2 nodes, 1 edge).
2684
2685 `2'
2686 Triangle (3 nodes, 3 edges).
2687
2688 `3'
2689 Quadrangle (4 nodes, 4 edges).
2690
2691 `4'
2692 Tetrahedron (4 nodes, 6 edges, 6 faces).
2693
2694 `5'
2695 Hexahedron (8 nodes, 12 edges, 6 faces).
2696
2697 `15'
2698 Point (1 node).
2699 */
2700
2701 if (((cell_type == 1) && (dim == 1)) || // a line in 1d
2702 ((cell_type == 2) && (dim == 2)) || // a triangle in 2d
2703 ((cell_type == 3) && (dim == 2)) || // a quadrilateral in 2d
2704 ((cell_type == 4) && (dim == 3)) || // a tet in 3d
2705 ((cell_type == 5) && (dim == 3))) // a hex in 3d
2706 // found a cell
2707 {
2708 unsigned int vertices_per_cell = 0;
2709 if (cell_type == 1) // line
2710 vertices_per_cell = 2;
2711 else if (cell_type == 2) // tri
2712 vertices_per_cell = 3;
2713 else if (cell_type == 3) // quad
2714 vertices_per_cell = 4;
2715 else if (cell_type == 4) // tet
2716 vertices_per_cell = 4;
2717 else if (cell_type == 5) // hex
2718 vertices_per_cell = 8;
2719
2720 AssertThrow(nod_num == vertices_per_cell,
2721 ExcMessage(
2722 "Number of nodes does not coincide with the "
2723 "number required for this object"));
2724
2725 // allocate and read indices
2726 cells.emplace_back();
2727 CellData<dim> &cell = cells.back();
2728 cell.vertices.resize(vertices_per_cell);
2729 for (unsigned int i = 0; i < vertices_per_cell; ++i)
2730 {
2731 // hypercube cells need to be reordered
2732 if (vertices_per_cell ==
2734 in >> cell.vertices[dim == 3 ?
2735 local_vertex_numbering[i] :
2737 else
2738 in >> cell.vertices[i];
2739 }
2740
2741 // to make sure that the cast won't fail
2742 AssertThrow(material_id <=
2743 std::numeric_limits<types::material_id>::max(),
2745 material_id,
2746 0,
2747 std::numeric_limits<types::material_id>::max()));
2748 // we use only material_ids in the range from 0 to
2749 // numbers::invalid_material_id-1
2751 ExcIndexRange(material_id,
2752 0,
2754
2755 cell.material_id = material_id;
2756
2757 // transform from gmsh to consecutive numbering
2758 for (unsigned int i = 0; i < vertices_per_cell; ++i)
2759 {
2760 AssertThrow(vertex_indices.find(cell.vertices[i]) !=
2761 vertex_indices.end(),
2762 ExcInvalidVertexIndexGmsh(cell_per_entity,
2763 elm_number,
2764 cell.vertices[i]));
2765
2766 const auto vertex = vertex_indices[cell.vertices[i]];
2767 if constexpr (dim == 1)
2768 vertex_counts[vertex] += 1u;
2769 cell.vertices[i] = vertex;
2770 }
2771 }
2772 else if ((cell_type == 1) &&
2773 ((dim == 2) || (dim == 3))) // a line in 2d or 3d
2774 // boundary info
2775 {
2776 subcelldata.boundary_lines.emplace_back();
2777 in >> subcelldata.boundary_lines.back().vertices[0] >>
2778 subcelldata.boundary_lines.back().vertices[1];
2779
2780 // to make sure that the cast won't fail
2781 AssertThrow(material_id <=
2782 std::numeric_limits<types::boundary_id>::max(),
2784 material_id,
2785 0,
2786 std::numeric_limits<types::boundary_id>::max()));
2787 // we use only boundary_ids in the range from 0 to
2788 // numbers::internal_face_boundary_id-1
2790 ExcIndexRange(material_id,
2791 0,
2793
2794 subcelldata.boundary_lines.back().boundary_id =
2795 static_cast<types::boundary_id>(material_id);
2796
2797 // transform from ucd to
2798 // consecutive numbering
2799 for (unsigned int &vertex :
2800 subcelldata.boundary_lines.back().vertices)
2801 if (vertex_indices.find(vertex) != vertex_indices.end())
2802 // vertex with this index exists
2803 vertex = vertex_indices[vertex];
2804 else
2805 {
2806 // no such vertex index
2807 AssertThrow(false,
2808 ExcInvalidVertexIndex(cell_per_entity,
2809 vertex));
2811 }
2812 }
2813 else if ((cell_type == 2 || cell_type == 3) &&
2814 (dim == 3)) // a triangle or a quad in 3d
2815 // boundary info
2816 {
2817 unsigned int vertices_per_cell = 0;
2818 // check cell type
2819 if (cell_type == 2) // tri
2820 vertices_per_cell = 3;
2821 else if (cell_type == 3) // quad
2822 vertices_per_cell = 4;
2823
2824 subcelldata.boundary_quads.emplace_back();
2825
2826 // resize vertices
2827 subcelldata.boundary_quads.back().vertices.resize(
2828 vertices_per_cell);
2829 // for loop
2830 for (unsigned int i = 0; i < vertices_per_cell; ++i)
2831 in >> subcelldata.boundary_quads.back().vertices[i];
2832
2833 // to make sure that the cast won't fail
2834 AssertThrow(material_id <=
2835 std::numeric_limits<types::boundary_id>::max(),
2837 material_id,
2838 0,
2839 std::numeric_limits<types::boundary_id>::max()));
2840 // we use only boundary_ids in the range from 0 to
2841 // numbers::internal_face_boundary_id-1
2843 ExcIndexRange(material_id,
2844 0,
2846
2847 subcelldata.boundary_quads.back().boundary_id =
2848 static_cast<types::boundary_id>(material_id);
2849
2850 // transform from gmsh to
2851 // consecutive numbering
2852 for (unsigned int &vertex :
2853 subcelldata.boundary_quads.back().vertices)
2854 if (vertex_indices.find(vertex) != vertex_indices.end())
2855 // vertex with this index exists
2856 vertex = vertex_indices[vertex];
2857 else
2858 {
2859 // no such vertex index
2860 AssertThrow(false,
2861 ExcInvalidVertexIndex(cell_per_entity,
2862 vertex));
2864 }
2865 }
2866 else if (cell_type == 15)
2867 {
2868 // read the indices of nodes given
2869 unsigned int node_index = 0;
2870 if (gmsh_file_format < 20)
2871 {
2872 // For points (cell_type==15), we can only ever
2873 // list one node index.
2874 AssertThrow(nod_num == 1, ExcInternalError());
2875 in >> node_index;
2876 }
2877 else
2878 {
2879 in >> node_index;
2880 }
2881
2882 // we only care about boundary indicators assigned to
2883 // individual vertices in 1d (because otherwise the vertices
2884 // are not faces)
2885 if constexpr (dim == 1)
2886 boundary_ids_1d[vertex_indices[node_index]] = material_id;
2887 }
2888 else
2889 {
2890 AssertThrow(false, ExcGmshUnsupportedGeometry(cell_type));
2891 }
2892 }
2893 }
2894 AssertDimension(global_cell, n_cells);
2895 }
2896 // Assert that we reached the end of the block
2897 in >> line;
2898 const std::array<std::string, 2> end_elements_marker{
2899 {"@f$ENDELM", "@f$EndElements"}};
2900 AssertThrow(line == end_elements_marker[gmsh_file_format == 10 ? 0 : 1],
2901 ExcInvalidGMSHInput(line));
2902 AssertThrow(in.fail() == false, ExcIO());
2903
2904 // check that we actually read some cells.
2905 AssertThrow(cells.size() > 0,
2906 ExcGmshNoCellInformation(subcelldata.boundary_lines.size(),
2907 subcelldata.boundary_quads.size()));
2908
2909 // apply_grid_fixup_functions() may invalidate the vertex indices (since it
2910 // will delete duplicated or unused vertices). Get around this by storing
2911 // Points directly in that case so that the comparisons are valid.
2912 std::vector<std::pair<Point<spacedim>, types::boundary_id>> boundary_id_pairs;
2913 if constexpr (dim == 1)
2914 for (const auto &pair : vertex_counts)
2915 if (pair.second == 1u)
2916 boundary_id_pairs.emplace_back(vertices[pair.first],
2917 boundary_ids_1d[pair.first]);
2918
2919 apply_grid_fixup_functions(vertices, cells, subcelldata);
2920 tria->create_triangulation(vertices, cells, subcelldata);
2921
2922 // in 1d, we also have to attach boundary ids to vertices, which does not
2923 // currently work through the call above.
2924 if constexpr (dim == 1)
2925 assign_1d_boundary_ids(boundary_id_pairs, *tria);
2926}
2927
2928
2929
2930template <int dim, int spacedim>
2931void
2932GridIn<dim, spacedim>::read_msh(const std::string &fname)
2933{
2934#ifdef DEAL_II_GMSH_WITH_API
2935 Assert(tria != nullptr, ExcNoTriangulationSelected());
2936 // gmsh -> deal.II types
2937 const std::map<int, std::uint8_t> gmsh_to_dealii_type = {
2938 {{15, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {7, 5}, {6, 6}, {5, 7}}};
2939
2940 // Vertex renumbering, by dealii type
2941 const std::array<std::vector<unsigned int>, 8> gmsh_to_dealii = {
2942 {{0},
2943 {{0, 1}},
2944 {{0, 1, 2}},
2945 {{0, 1, 3, 2}},
2946 {{0, 1, 2, 3}},
2947 {{0, 1, 3, 2, 4}},
2948 {{0, 1, 2, 3, 4, 5}},
2949 {{0, 1, 3, 2, 4, 5, 7, 6}}}};
2950
2951 std::vector<Point<spacedim>> vertices;
2952 std::vector<CellData<dim>> cells;
2953 SubCellData subcelldata;
2954 std::map<unsigned int, types::boundary_id> boundary_ids_1d;
2955
2956 // Track the number of times each vertex is used in 1D. This determines
2957 // whether or not we can assign a boundary id to a vertex. This is necessary
2958 // because sometimes gmsh saves internal vertices in the @f$ELEM list in codim
2959 // 1 or codim 2.
2960 std::map<unsigned int, unsigned int> vertex_counts;
2961
2962# if DEAL_II_GMSH_WITH_API_VERSION_GTE(4, 9, 4)
2963 AssertThrow(gmsh::isInitialized() == 1,
2964 ExcMessage("The GMSH API may only be called after GMSH is "
2965 "initialized, e.g., via the InitFinalize or "
2966 "MPI_InitFinalize classes or the gmsh::initialize() "
2967 "function."));
2968# endif
2969 gmsh::option::setNumber("General.Verbosity", 0);
2970 gmsh::clear();
2971 gmsh::open(fname);
2972
2973 AssertThrow(gmsh::model::getDimension() == dim,
2974 ExcMessage("You are trying to read a gmsh file with dimension " +
2975 std::to_string(gmsh::model::getDimension()) +
2976 " into a grid of dimension " + std::to_string(dim)));
2977
2978 // Read all nodes, and store them in our vector of vertices. Before we do
2979 // that, make sure all tags are consecutive
2980 {
2981 gmsh::model::mesh::removeDuplicateNodes();
2982 gmsh::model::mesh::renumberNodes();
2983 std::vector<std::size_t> node_tags;
2984 std::vector<double> coord;
2985 std::vector<double> parametricCoord;
2986 gmsh::model::mesh::getNodes(
2987 node_tags, coord, parametricCoord, -1, -1, false, false);
2988 vertices.resize(node_tags.size());
2989 for (unsigned int i = 0; i < node_tags.size(); ++i)
2990 {
2991 // Check that renumbering worked!
2992 AssertDimension(node_tags[i], i + 1);
2993 for (unsigned int d = 0; d < spacedim; ++d)
2994 vertices[i][d] = coord[i * 3 + d];
2995 if constexpr (running_in_debug_mode())
2996 {
2997 // Make sure the embedded dimension is right
2998 for (unsigned int d = spacedim; d < 3; ++d)
2999 Assert(std::abs(coord[i * 3 + d]) < 1e-10,
3000 ExcMessage(
3001 "The grid you are reading contains nodes that are "
3002 "nonzero in the coordinate with index " +
3003 std::to_string(d) +
3004 ", but you are trying to save "
3005 "it on a grid embedded in a " +
3006 std::to_string(spacedim) + " dimensional space."));
3007 }
3008 }
3009 }
3010
3011 // Get all the elementary entities in the model, as a vector of (dimension,
3012 // tag) pairs:
3013 std::vector<std::pair<int, int>> entities;
3014 gmsh::model::getEntities(entities);
3015
3016 for (const auto &[entity_dim, entity_tag] : entities)
3017 {
3018 // Dimension and tag of the entity:
3019
3020
3022 types::boundary_id boundary_id = 0;
3023
3024 // Get the physical tags, to deduce boundary, material, and manifold_id
3025 std::vector<int> physical_tags;
3026 gmsh::model::getPhysicalGroupsForEntity(entity_dim,
3027 entity_tag,
3028 physical_tags);
3029
3030 // Now fill manifold id and boundary or material id
3031 if (physical_tags.size())
3032 for (auto physical_tag : physical_tags)
3033 {
3034 std::string name;
3035 gmsh::model::getPhysicalName(entity_dim, physical_tag, name);
3036 if (!name.empty())
3037 {
3038 // Patterns::Tools::to_value throws an exception, if it can not
3039 // convert name to a map from string to int.
3040 try
3041 {
3042 std::map<std::string, int> id_names;
3043 Patterns::Tools::to_value(name, id_names);
3044 bool found_unrecognized_tag = false;
3045 bool found_boundary_id = false;
3046 // If the above did not throw, we keep going, and retrieve
3047 // all the information that we know how to translate.
3048 for (const auto &[name, id] : id_names)
3049 {
3050 if (entity_dim == dim && name == "MaterialID")
3051 {
3052 boundary_id = static_cast<types::boundary_id>(id);
3053 found_boundary_id = true;
3054 }
3055 else if (entity_dim < dim && name == "BoundaryID")
3056 {
3057 boundary_id = static_cast<types::boundary_id>(id);
3058 found_boundary_id = true;
3059 }
3060 else if (name == "ManifoldID")
3061 manifold_id = static_cast<types::manifold_id>(id);
3062 else
3063 // We did not recognize one of the keys. We'll fall
3064 // back to setting the boundary id to the physical tag
3065 // after reading all strings.
3066 found_unrecognized_tag = true;
3067 }
3068 // If we didn't find a BoundaryID:XX or MaterialID:XX, and
3069 // something was found but not recognized, then we set the
3070 // material id or boundary using the physical tag directly.
3071 if (found_unrecognized_tag && !found_boundary_id)
3072 boundary_id = physical_tag;
3073 }
3074 catch (...)
3075 {
3076 // When the above didn't work, we revert to the old
3077 // behaviour: the physical tag itself is interpreted either
3078 // as a material_id or a boundary_id, and no manifold id is
3079 // known
3080 boundary_id = physical_tag;
3081 }
3082 }
3083 }
3084
3085 // Get the mesh elements for the entity (dim, tag):
3086 std::vector<int> element_types;
3087 std::vector<std::vector<std::size_t>> element_ids, element_nodes;
3088 gmsh::model::mesh::getElements(
3089 element_types, element_ids, element_nodes, entity_dim, entity_tag);
3090
3091 for (unsigned int i = 0; i < element_types.size(); ++i)
3092 {
3093 const auto &type = gmsh_to_dealii_type.at(element_types[i]);
3094 const auto n_vertices = gmsh_to_dealii[type].size();
3095 const auto &elements = element_ids[i];
3096 const auto &nodes = element_nodes[i];
3097 for (unsigned int j = 0; j < elements.size(); ++j)
3098 {
3099 if (entity_dim == dim)
3100 {
3101 cells.emplace_back(n_vertices);
3102 auto &cell = cells.back();
3103 for (unsigned int v = 0; v < n_vertices; ++v)
3104 {
3105 cell.vertices[v] =
3106 nodes[n_vertices * j + gmsh_to_dealii[type][v]] - 1;
3107 if constexpr (dim == 1)
3108 vertex_counts[cell.vertices[v]] += 1u;
3109 }
3110 cell.manifold_id = manifold_id;
3111 cell.material_id = boundary_id;
3112 }
3113 else if (entity_dim == 2)
3114 {
3115 subcelldata.boundary_quads.emplace_back(n_vertices);
3116 auto &face = subcelldata.boundary_quads.back();
3117 for (unsigned int v = 0; v < n_vertices; ++v)
3118 face.vertices[v] =
3119 nodes[n_vertices * j + gmsh_to_dealii[type][v]] - 1;
3120
3121 face.manifold_id = manifold_id;
3122 face.boundary_id = boundary_id;
3123 }
3124 else if (entity_dim == 1)
3125 {
3126 subcelldata.boundary_lines.emplace_back(n_vertices);
3127 auto &line = subcelldata.boundary_lines.back();
3128 for (unsigned int v = 0; v < n_vertices; ++v)
3129 line.vertices[v] =
3130 nodes[n_vertices * j + gmsh_to_dealii[type][v]] - 1;
3131
3132 line.manifold_id = manifold_id;
3133 line.boundary_id = boundary_id;
3134 }
3135 else if (entity_dim == 0)
3136 {
3137 // This should only happen in one dimension.
3138 AssertDimension(dim, 1);
3139 for (unsigned int j = 0; j < elements.size(); ++j)
3140 boundary_ids_1d[nodes[j] - 1] = boundary_id;
3141 }
3142 }
3143 }
3144 }
3145
3146 // apply_grid_fixup_functions() may invalidate the vertex indices (since it
3147 // will delete duplicated or unused vertices). Get around this by storing
3148 // Points directly in that case so that the comparisons are valid.
3149 std::vector<std::pair<Point<spacedim>, types::boundary_id>> boundary_id_pairs;
3150 if constexpr (dim == 1)
3151 for (const auto &pair : vertex_counts)
3152 if (pair.second == 1u)
3153 boundary_id_pairs.emplace_back(vertices[pair.first],
3154 boundary_ids_1d[pair.first]);
3155
3156 apply_grid_fixup_functions(vertices, cells, subcelldata);
3157 tria->create_triangulation(vertices, cells, subcelldata);
3158
3159 // in 1d, we also have to attach boundary ids to vertices, which does not
3160 // currently work through the call above.
3161 if constexpr (dim == 1)
3162 assign_1d_boundary_ids(boundary_id_pairs, *tria);
3163
3164 gmsh::clear();
3165#else
3166 (void)fname;
3167 AssertThrow(false, ExcNeedsGMSHAPI());
3168#endif
3169}
3170
3171
3172
3173template <int dim, int spacedim>
3174void
3175GridIn<dim, spacedim>::read_partitioned_msh(const std::string &file_prefix,
3176 const std::string &file_suffix)
3177{
3178#ifdef DEAL_II_GMSH_WITH_API
3179 auto *parallel_tria =
3181 tria.get());
3182
3183 // Check that the cast succeeded
3184 AssertThrow(parallel_tria != nullptr,
3185 ExcMessage("Triangulation is not fully distributed!"));
3186
3187 // Now it's safe to call get_communicator()
3188 MPI_Comm mpi_comm = parallel_tria->get_mpi_communicator();
3189
3190 const unsigned int nprocs = Utilities::MPI::n_mpi_processes(mpi_comm);
3191 const unsigned int rank = Utilities::MPI::this_mpi_process(mpi_comm);
3192
3193 std::string fname =
3194 file_prefix + "_" + std::to_string(rank + 1) + "." + file_suffix;
3195
3196 if (nprocs == 1)
3197 {
3198 fname = file_prefix + "." + file_suffix;
3199
3200 AssertThrow(std::filesystem::exists(fname),
3201 ExcMessage("Missing mesh file: " + fname));
3202 }
3203 else
3204 {
3205 for (unsigned int i = 1; i <= nprocs; ++i)
3206 {
3207 const std::string check_fname =
3208 file_prefix + "_" + std::to_string(i) + "." + file_suffix;
3209
3210 AssertThrow(std::filesystem::exists(check_fname),
3211 ExcMessage("Missing mesh file: " + check_fname));
3212 }
3213
3214 const std::string extra_fname =
3215 file_prefix + "_" + std::to_string(nprocs + 1) + "." + file_suffix;
3216 AssertThrow(!std::filesystem::exists(extra_fname),
3217 ExcMessage("Expected " + std::to_string(nprocs) +
3218 " mesh files, but found extra: " + extra_fname));
3219 }
3220
3221 const std::map<int, std::uint8_t> gmsh_to_dealii_type = {
3222 {15, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {7, 5}, {6, 6}, {5, 7}};
3223
3224 const std::array<std::vector<unsigned int>, 8> gmsh_to_dealii = {
3225 {{0},
3226 {0, 1},
3227 {0, 1, 2},
3228 {0, 1, 3, 2},
3229 {0, 1, 2, 3},
3230 {0, 1, 3, 2, 4},
3231 {0, 1, 2, 3, 4, 5},
3232 {0, 1, 3, 2, 4, 5, 7, 6}}};
3233
3234# if DEAL_II_GMSH_WITH_API_VERSION_GTE(4, 9, 4)
3235 AssertThrow(gmsh::isInitialized() == 1,
3236 ExcMessage("The GMSH API may only be called after GMSH is "
3237 "initialized, e.g., via the InitFinalize or "
3238 "MPI_InitFinalize classes or the gmsh::initialize() "
3239 "function."));
3240# endif
3241 gmsh::option::setNumber("General.Verbosity", 0);
3242 gmsh::clear();
3243 gmsh::open(fname);
3244
3245 std::map<unsigned long, unsigned int> ghost_map;
3246
3247 std::vector<std::pair<int, int>> entities;
3248 gmsh::model::getEntities(entities);
3249
3250 for (const auto &e : entities)
3251 {
3252 const int entity_dim = e.first;
3253 const int entity_tag = e.second;
3254
3255 if (entity_dim == dim)
3256 {
3257 std::vector<std::size_t> element_tags;
3258 std::vector<int> partitions;
3259
3260 gmsh::model::mesh::getGhostElements(entity_dim,
3261 entity_tag,
3262 element_tags,
3263 partitions);
3264
3265 for (std::size_t i = 0; i < element_tags.size(); ++i)
3266 ghost_map[element_tags[i]] =
3267 static_cast<unsigned int>(partitions[i] - 1);
3268 }
3269 }
3270
3271 std::vector<std::size_t> node_tags;
3272 std::vector<double> coords, parametric_coords;
3273 gmsh::model::mesh::getNodes(node_tags, coords, parametric_coords);
3274
3276 triangulation_description;
3277 triangulation_description.comm = mpi_comm;
3278
3279 triangulation_description.cell_infos.resize(1);
3280 triangulation_description.coarse_cell_vertices.resize(node_tags.size(),
3281 Point<spacedim>());
3282
3283 std::map<std::size_t, unsigned int> node_tag_to_index;
3284 for (unsigned int i = 0; i < node_tags.size(); ++i)
3285 {
3286 node_tag_to_index[node_tags[i]] = i;
3287 for (unsigned int d = 0; d < spacedim; ++d)
3288 triangulation_description.coarse_cell_vertices[i][d] =
3289 coords[3 * i + d];
3290 }
3291
3292 // --- Collect physical groups for boundary and material info ---
3293 std::map<std::pair<int, int>, types::boundary_id> entity_to_boundary;
3294 std::map<std::pair<int, int>, types::material_id> entity_to_material;
3295
3296
3297 std::vector<std::pair<int, int>> physical_groups;
3298 gmsh::model::getPhysicalGroups(physical_groups);
3299
3300 for (const auto &[physical_dim, physical_tag] : physical_groups)
3301 {
3302 std::vector<int> physical_entities;
3303 gmsh::model::getEntitiesForPhysicalGroup(physical_dim,
3304 physical_tag,
3305 physical_entities);
3306
3307 for (const int ent : physical_entities)
3308 {
3309 // Assign boundary ID for faces (dim-1)
3310 if (physical_dim == dim - 1)
3311 entity_to_boundary[{physical_dim, ent}] = physical_tag;
3312 if (physical_dim == dim) // Volume materials
3313 entity_to_material[{physical_dim, ent}] = physical_tag;
3314 }
3315 }
3316
3317 // --- Build boundary face map for all (dim-1) entities ---
3318
3319 std::map<std::set<unsigned int>, types::boundary_id> boundary_face_map;
3320
3321 // Process all (dim-1)-dimensional entities for boundary assignment
3322 for (const auto &e : entities)
3323 if (e.first == dim - 1)
3324 if (auto it = entity_to_boundary.find({e.first, e.second});
3325 it != entity_to_boundary.end())
3326 {
3327 const types::boundary_id boundary_id = it->second;
3328
3329 std::vector<int> element_types;
3330 std::vector<std::vector<std::size_t>> element_ids, element_nodes;
3331
3332 // Get all elements on this (dim-1)-entity
3333 gmsh::model::mesh::getElements(
3334 element_types, element_ids, element_nodes, e.first, e.second);
3335
3336 for (unsigned int i = 0; i < element_types.size(); ++i)
3337 {
3338 if (element_ids[i].empty())
3339 continue;
3340
3341 const unsigned int n_nodes_per_elem =
3342 element_nodes[i].size() / element_ids[i].size();
3343
3344 for (unsigned int j = 0; j < element_ids[i].size(); ++j)
3345 {
3346 std::set<unsigned int> face_vertices;
3347 for (unsigned int k = 0; k < n_nodes_per_elem; ++k)
3348 {
3349 std::size_t node_tag =
3350 element_nodes[i][j * n_nodes_per_elem + k];
3351 face_vertices.insert(node_tag_to_index[node_tag]);
3352 }
3353
3354 // Store boundary IDs for this face
3355 if (boundary_id != numbers::invalid_boundary_id)
3356 boundary_face_map[face_vertices] = boundary_id;
3357 }
3358 }
3359 }
3360
3361
3362
3363 // Count total volume elements and reserve space
3364 std::size_t total_volume_elements = 0;
3365 for (const auto &[entity_dim, entity_tag] : entities)
3366 {
3367 if (entity_dim == dim) // we only care about cells here
3368 {
3369 std::vector<int> count_element_types;
3370 std::vector<std::vector<std::size_t>> count_element_ids,
3371 count_element_nodes;
3372
3373 gmsh::model::mesh::getElements(count_element_types,
3374 count_element_ids,
3375 count_element_nodes,
3376 entity_dim,
3377 entity_tag);
3378
3379 for (const auto &count_element_id : count_element_ids)
3380 total_volume_elements += count_element_id.size();
3381 }
3382 }
3383
3384 // Reserve space for all vectors that will grow during processing
3385 triangulation_description.coarse_cells.reserve(total_volume_elements);
3386 triangulation_description.coarse_cell_index_to_coarse_cell_id.reserve(
3387 total_volume_elements);
3388 triangulation_description.cell_infos[0].reserve(total_volume_elements);
3389
3390 for (const auto &[entity_dim, entity_tag] : entities)
3391 {
3392 if (entity_dim == dim)
3393 {
3394 std::vector<int> element_types;
3395 std::vector<std::vector<std::size_t>> element_ids, element_nodes;
3396
3397 gmsh::model::mesh::getElements(
3398 element_types, element_ids, element_nodes, entity_dim, entity_tag);
3399
3400 for (unsigned int i = 0; i < element_types.size(); ++i)
3401 {
3402 if (element_ids[i].empty())
3403 continue;
3404
3405 const unsigned int n_vertices =
3406 element_nodes[i].size() / element_ids[i].size();
3407
3408 for (unsigned int j = 0; j < element_ids[i].size(); ++j)
3409 {
3410 CellData<dim> cell(n_vertices);
3411 if (auto it = entity_to_material.find({dim, entity_tag});
3412 it != entity_to_material.end())
3413 cell.material_id = it->second;
3414
3415 const auto &type = gmsh_to_dealii_type.at(element_types[i]);
3416
3417 for (unsigned int v = 0; v < n_vertices; ++v)
3418 {
3419 const std::size_t node_tag =
3420 element_nodes[i]
3421 [j * n_vertices + gmsh_to_dealii[type][v]];
3422 AssertThrow(node_tag_to_index.find(node_tag) !=
3423 node_tag_to_index.end(),
3424 ExcMessage("Node tag " +
3425 std::to_string(node_tag) +
3426 " not found in node list!"));
3427 cell.vertices[v] = node_tag_to_index[node_tag];
3428 }
3429
3431 cell_info.id =
3432 CellId(element_ids[i][j], {}).template to_binary<dim>();
3433
3434 auto it = ghost_map.find(element_ids[i][j]);
3435 if (it != ghost_map.end())
3436 cell_info.subdomain_id = it->second;
3437 else
3438 cell_info.subdomain_id = rank;
3439
3440 cell_info.level_subdomain_id = cell_info.subdomain_id;
3441 cell_info.material_id = cell.material_id;
3442
3443 // --- Universal boundary ID assignment for faces ---
3444 if constexpr (dim > 0)
3445 {
3446 // Determine reference cell type from number of vertices
3447 const ReferenceCell<dim> ref_cell =
3448 ReferenceCells::n_vertices_to_reference_cell<dim>(
3449 n_vertices);
3450
3451 // Number of faces for this reference cell
3452 const unsigned int n_faces = ref_cell.n_faces();
3453
3454 for (unsigned int f = 0; f < n_faces; ++f)
3455 {
3456 std::set<unsigned int> face_vertices;
3457
3458 if constexpr (dim == 1)
3459 {
3460 // In 1D, faces are vertices
3461 face_vertices.insert(cell.vertices[f]);
3462 }
3463 else if constexpr (dim == 2)
3464 {
3465 // In 2D, manually handle triangles and quads
3466 if (ref_cell == ReferenceCells::Triangle)
3467 {
3468 // Triangle: face f connects vertices f and
3469 // (f+1)%3
3470 face_vertices.insert(cell.vertices[f]);
3471 face_vertices.insert(
3472 cell.vertices[(f + 1) % 3]);
3473 }
3474 else if (ref_cell ==
3476 {
3477 const unsigned int n_face_vertices =
3478 ref_cell.face_reference_cell(f)
3479 .n_vertices();
3480 for (unsigned int fv = 0;
3481 fv < n_face_vertices;
3482 ++fv)
3483 {
3484 const unsigned int vertex_index =
3485 ref_cell.face_to_cell_vertices(
3486 f,
3487 fv,
3488 numbers::
3489 default_geometric_orientation);
3490 face_vertices.insert(
3491 cell.vertices[vertex_index]);
3492 }
3493 }
3494 }
3495 else if constexpr (dim == 3)
3496 {
3497 // In 3D, get face vertices from reference cell
3498 const unsigned int n_face_vertices =
3499 ref_cell.face_reference_cell(f).n_vertices();
3500 for (unsigned int fv = 0; fv < n_face_vertices;
3501 ++fv)
3502 {
3503 const unsigned int vertex_index =
3504 ref_cell.face_to_cell_vertices(
3505 f,
3506 fv,
3508 face_vertices.insert(
3509 cell.vertices[vertex_index]);
3510 }
3511 }
3512
3513 // Assign boundary ID if found
3514 if (auto it = boundary_face_map.find(face_vertices);
3515 it != boundary_face_map.end())
3516 cell_info.boundary_ids.emplace_back(f, it->second);
3517 }
3518 }
3519
3520 triangulation_description.coarse_cells.push_back(cell);
3521 triangulation_description.coarse_cell_index_to_coarse_cell_id
3522 .push_back(element_ids[i][j]);
3523 triangulation_description.cell_infos[0].push_back(cell_info);
3524 }
3525 }
3526 }
3527 }
3528
3529 triangulation_description.settings =
3531
3532 parallel_tria->create_triangulation(triangulation_description);
3533
3534
3535# ifdef DEAL_II_WITH_MPI
3536 const int mpi_ierr = MPI_Barrier(mpi_comm);
3537 AssertThrowMPI(mpi_ierr);
3538# endif
3539
3540 gmsh::clear();
3541#else
3542 (void)file_prefix;
3543 (void)file_suffix;
3544 AssertThrow(false, ExcNeedsGMSHAPI());
3545#endif
3546}
3547
3548
3549template <int dim, int spacedim>
3550void
3552 std::string &header,
3553 std::vector<unsigned int> &tecplot2deal,
3554 unsigned int &n_vars,
3555 unsigned int &n_vertices,
3556 unsigned int &n_cells,
3557 std::vector<unsigned int> &IJK,
3558 bool &structured,
3559 bool &blocked)
3560{
3561 Assert(tecplot2deal.size() == dim, ExcInternalError());
3562 Assert(IJK.size() == dim, ExcInternalError());
3563 // initialize the output variables
3564 n_vars = 0;
3565 n_vertices = 0;
3566 n_cells = 0;
3567 switch (dim)
3568 {
3569 case 3:
3570 IJK[2] = 0;
3571 [[fallthrough]];
3572 case 2:
3573 IJK[1] = 0;
3574 [[fallthrough]];
3575 case 1:
3576 IJK[0] = 0;
3577 }
3578 structured = true;
3579 blocked = false;
3580
3581 // convert the string to upper case
3582 std::transform(header.begin(),
3583 header.end(),
3584 header.begin(),
3585 static_cast<int (*)(int)>(std::toupper));
3586
3587 // replace all tabs, commas, newlines by
3588 // whitespaces
3589 std::replace(header.begin(), header.end(), '\t', ' ');
3590 std::replace(header.begin(), header.end(), ',', ' ');
3591 std::replace(header.begin(), header.end(), '\n', ' ');
3592
3593 // now remove whitespace in front of and
3594 // after '='
3595 std::string::size_type pos = header.find('=');
3596
3597 while (pos != static_cast<std::string::size_type>(std::string::npos))
3598 if (header[pos + 1] == ' ')
3599 header.erase(pos + 1, 1);
3600 else if (header[pos - 1] == ' ')
3601 {
3602 header.erase(pos - 1, 1);
3603 --pos;
3604 }
3605 else
3606 pos = header.find('=', ++pos);
3607
3608 // split the string into individual entries
3609 std::vector<std::string> entries =
3610 Utilities::break_text_into_lines(header, 1, ' ');
3611
3612 // now go through the list and try to extract
3613 for (unsigned int i = 0; i < entries.size(); ++i)
3614 {
3615 if (Utilities::match_at_string_start(entries[i], "VARIABLES=\""))
3616 {
3617 ++n_vars;
3618 // we assume, that the first variable
3619 // is x or no coordinate at all (not y or z)
3620 if (Utilities::match_at_string_start(entries[i], "VARIABLES=\"X\""))
3621 {
3622 tecplot2deal[0] = 0;
3623 }
3624 ++i;
3625 while (entries[i][0] == '"')
3626 {
3627 if (entries[i] == "\"X\"")
3628 tecplot2deal[0] = n_vars;
3629 else if (entries[i] == "\"Y\"")
3630 {
3631 // we assume, that y contains
3632 // zero data in 1d, so do
3633 // nothing
3634 if constexpr (dim > 1)
3635 tecplot2deal[1] = n_vars;
3636 }
3637 else if (entries[i] == "\"Z\"")
3638 {
3639 // we assume, that z contains
3640 // zero data in 1d and 2d, so
3641 // do nothing
3642 if constexpr (dim > 2)
3643 tecplot2deal[2] = n_vars;
3644 }
3645 ++n_vars;
3646 ++i;
3647 }
3648 // set i back, so that the next
3649 // string is treated correctly
3650 --i;
3651
3653 n_vars >= dim,
3654 ExcMessage(
3655 "Tecplot file must contain at least one variable for each dimension"));
3656 for (unsigned int d = 1; d < dim; ++d)
3658 tecplot2deal[d] > 0,
3659 ExcMessage(
3660 "Tecplot file must contain at least one variable for each dimension."));
3661 }
3662 else if (Utilities::match_at_string_start(entries[i], "ZONETYPE=ORDERED"))
3663 structured = true;
3664 else if (Utilities::match_at_string_start(entries[i],
3665 "ZONETYPE=FELINESEG") &&
3666 dim == 1)
3667 structured = false;
3668 else if (Utilities::match_at_string_start(entries[i],
3669 "ZONETYPE=FEQUADRILATERAL") &&
3670 dim == 2)
3671 structured = false;
3672 else if (Utilities::match_at_string_start(entries[i],
3673 "ZONETYPE=FEBRICK") &&
3674 dim == 3)
3675 structured = false;
3676 else if (Utilities::match_at_string_start(entries[i], "ZONETYPE="))
3677 // unsupported ZONETYPE
3678 {
3679 AssertThrow(false,
3680 ExcMessage(
3681 "The tecplot file contains an unsupported ZONETYPE."));
3682 }
3683 else if (Utilities::match_at_string_start(entries[i],
3684 "DATAPACKING=POINT"))
3685 blocked = false;
3686 else if (Utilities::match_at_string_start(entries[i],
3687 "DATAPACKING=BLOCK"))
3688 blocked = true;
3689 else if (Utilities::match_at_string_start(entries[i], "F=POINT"))
3690 {
3691 structured = true;
3692 blocked = false;
3693 }
3694 else if (Utilities::match_at_string_start(entries[i], "F=BLOCK"))
3695 {
3696 structured = true;
3697 blocked = true;
3698 }
3699 else if (Utilities::match_at_string_start(entries[i], "F=FEPOINT"))
3700 {
3701 structured = false;
3702 blocked = false;
3703 }
3704 else if (Utilities::match_at_string_start(entries[i], "F=FEBLOCK"))
3705 {
3706 structured = false;
3707 blocked = true;
3708 }
3709 else if (Utilities::match_at_string_start(entries[i],
3710 "ET=QUADRILATERAL") &&
3711 dim == 2)
3712 structured = false;
3713 else if (Utilities::match_at_string_start(entries[i], "ET=BRICK") &&
3714 dim == 3)
3715 structured = false;
3716 else if (Utilities::match_at_string_start(entries[i], "ET="))
3717 // unsupported ElementType
3718 {
3720 false,
3721 ExcMessage(
3722 "The tecplot file contains an unsupported ElementType."));
3723 }
3724 else if (Utilities::match_at_string_start(entries[i], "I="))
3725 IJK[0] = Utilities::get_integer_at_position(entries[i], 2).first;
3726 else if (Utilities::match_at_string_start(entries[i], "J="))
3727 {
3728 IJK[1] = Utilities::get_integer_at_position(entries[i], 2).first;
3730 dim > 1 || IJK[1] == 1,
3731 ExcMessage(
3732 "Parameter 'J=' found in tecplot, although this is only possible for dimensions greater than 1."));
3733 }
3734 else if (Utilities::match_at_string_start(entries[i], "K="))
3735 {
3736 IJK[2] = Utilities::get_integer_at_position(entries[i], 2).first;
3738 dim > 2 || IJK[2] == 1,
3739 ExcMessage(
3740 "Parameter 'K=' found in tecplot, although this is only possible for dimensions greater than 2."));
3741 }
3742 else if (Utilities::match_at_string_start(entries[i], "N="))
3743 n_vertices = Utilities::get_integer_at_position(entries[i], 2).first;
3744 else if (Utilities::match_at_string_start(entries[i], "E="))
3745 n_cells = Utilities::get_integer_at_position(entries[i], 2).first;
3746 }
3747
3748 // now we have read all the fields we are
3749 // interested in. do some checks and
3750 // calculate the variables
3751 if (structured)
3752 {
3753 n_vertices = 1;
3754 n_cells = 1;
3755 for (unsigned int d = 0; d < dim; ++d)
3756 {
3758 IJK[d] > 0,
3759 ExcMessage(
3760 "Tecplot file does not contain a complete and consistent set of parameters"));
3761 n_vertices *= IJK[d];
3762 n_cells *= (IJK[d] - 1);
3763 }
3764 }
3765 else
3766 {
3768 n_vertices > 0,
3769 ExcMessage(
3770 "Tecplot file does not contain a complete and consistent set of parameters"));
3771 if (n_cells == 0)
3772 // this means an error, although
3773 // tecplot itself accepts entries like
3774 // 'J=20' instead of 'E=20'. therefore,
3775 // take the max of IJK
3776 n_cells = *std::max_element(IJK.begin(), IJK.end());
3778 n_cells > 0,
3779 ExcMessage(
3780 "Tecplot file does not contain a complete and consistent set of parameters"));
3781 }
3782}
3783
3784
3785
3786template <>
3787void
3789{
3790 const unsigned int dim = 2;
3791 const unsigned int spacedim = 2;
3792 Assert(tria != nullptr, ExcNoTriangulationSelected());
3793 AssertThrow(in.fail() == false, ExcIO());
3794
3795 // skip comments at start of file
3796 skip_comment_lines(in, '#');
3797
3798 // some strings for parsing the header
3799 std::string line, header;
3800
3801 // first, concatenate all header lines
3802 // create a searchstring with almost all
3803 // letters. exclude e and E from the letters
3804 // to search, as they might appear in
3805 // exponential notation
3806 std::string letters = "abcdfghijklmnopqrstuvwxyzABCDFGHIJKLMNOPQRSTUVWXYZ";
3807
3808 getline(in, line);
3809 while (line.find_first_of(letters) != std::string::npos)
3810 {
3811 header += " " + line;
3812 getline(in, line);
3813 }
3814
3815 // now create some variables holding
3816 // important information on the mesh, get
3817 // this information from the header string
3818 std::vector<unsigned int> tecplot2deal(dim);
3819 std::vector<unsigned int> IJK(dim);
3820 unsigned int n_vars, n_vertices, n_cells;
3821 bool structured, blocked;
3822
3823 parse_tecplot_header(header,
3824 tecplot2deal,
3825 n_vars,
3826 n_vertices,
3827 n_cells,
3828 IJK,
3829 structured,
3830 blocked);
3831
3832 // reserve space for vertices. note, that in
3833 // tecplot vertices are ordered beginning
3834 // with 1, whereas in deal all indices start
3835 // with 0. in order not to use -1 for all the
3836 // connectivity information, a 0th vertex
3837 // (unused) is inserted at the origin.
3838 std::vector<Point<spacedim>> vertices(n_vertices + 1);
3839 vertices[0] = Point<spacedim>();
3840 // reserve space for cells
3841 std::vector<CellData<dim>> cells(n_cells);
3842 SubCellData subcelldata;
3843
3844 if (blocked)
3845 {
3846 // blocked data format. first we get all
3847 // the values of the first variable for
3848 // all points, after that we get all
3849 // values for the second variable and so
3850 // on.
3851
3852 // dummy variable to read in all the info
3853 // we do not want to use
3854 double dummy;
3855 // which is the first index to read in
3856 // the loop (see below)
3857 unsigned int next_index = 0;
3858
3859 // note, that we have already read the
3860 // first line containing the first variable
3861 if (tecplot2deal[0] == 0)
3862 {
3863 // we need the information in this
3864 // line, so extract it
3865 std::vector<std::string> first_var =
3867 char *endptr;
3868 for (unsigned int i = 1; i < first_var.size() + 1; ++i)
3869 vertices[i][0] = std::strtod(first_var[i - 1].c_str(), &endptr);
3870
3871 // if there are many points, the data
3872 // for this var might continue in the
3873 // next line(s)
3874 for (unsigned int j = first_var.size() + 1; j < n_vertices + 1; ++j)
3875 in >> vertices[j][next_index];
3876 // now we got all values of the first
3877 // variable, so increase the counter
3878 next_index = 1;
3879 }
3880
3881 // main loop over all variables
3882 for (unsigned int i = 1; i < n_vars; ++i)
3883 {
3884 // if we read all the important
3885 // variables and do not want to
3886 // read further, because we are
3887 // using a structured grid, we can
3888 // stop here (and skip, for
3889 // example, a whole lot of solution
3890 // variables)
3891 if (next_index == dim && structured)
3892 break;
3893
3894 if ((next_index < dim) && (i == tecplot2deal[next_index]))
3895 {
3896 // we need this line, read it in
3897 for (unsigned int j = 1; j < n_vertices + 1; ++j)
3898 in >> vertices[j][next_index];
3899 ++next_index;
3900 }
3901 else
3902 {
3903 // we do not need this line, read
3904 // it in and discard it
3905 for (unsigned int j = 1; j < n_vertices + 1; ++j)
3906 in >> dummy;
3907 }
3908 }
3909 Assert(next_index == dim, ExcInternalError());
3910 }
3911 else
3912 {
3913 // the data is not blocked, so we get all
3914 // the variables for one point, then the
3915 // next and so on. create a vector to
3916 // hold these components
3917 std::vector<double> vars(n_vars);
3918
3919 // now fill the first vertex. note, that we
3920 // have already read the first line
3921 // containing the first vertex
3922 std::vector<std::string> first_vertex =
3924 char *endptr;
3925 for (unsigned int d = 0; d < dim; ++d)
3926 vertices[1][d] =
3927 std::strtod(first_vertex[tecplot2deal[d]].c_str(), &endptr);
3928
3929 // read the remaining vertices from the
3930 // list
3931 for (unsigned int v = 2; v < n_vertices + 1; ++v)
3932 {
3933 for (unsigned int i = 0; i < n_vars; ++i)
3934 in >> vars[i];
3935 // fill the vertex
3936 // coordinates. respect the position
3937 // of coordinates in the list of
3938 // variables
3939 for (unsigned int i = 0; i < dim; ++i)
3940 vertices[v][i] = vars[tecplot2deal[i]];
3941 }
3942 }
3943
3944 if (structured)
3945 {
3946 // this is the part of the code that only
3947 // works in 2d
3948 unsigned int I = IJK[0], J = IJK[1];
3949
3950 unsigned int cell = 0;
3951 // set up array of cells
3952 for (unsigned int j = 0; j < J - 1; ++j)
3953 for (unsigned int i = 1; i < I; ++i)
3954 {
3955 cells[cell].vertices[0] = i + j * I;
3956 cells[cell].vertices[1] = i + 1 + j * I;
3957 cells[cell].vertices[2] = i + (j + 1) * I;
3958 cells[cell].vertices[3] = i + 1 + (j + 1) * I;
3959 ++cell;
3960 }
3961 Assert(cell == n_cells, ExcInternalError());
3962 std::vector<unsigned int> boundary_vertices(2 * I + 2 * J - 4);
3963 unsigned int k = 0;
3964 for (unsigned int i = 1; i < I + 1; ++i)
3965 {
3966 boundary_vertices[k] = i;
3967 ++k;
3968 boundary_vertices[k] = i + (J - 1) * I;
3969 ++k;
3970 }
3971 for (unsigned int j = 1; j < J - 1; ++j)
3972 {
3973 boundary_vertices[k] = 1 + j * I;
3974 ++k;
3975 boundary_vertices[k] = I + j * I;
3976 ++k;
3977 }
3978 Assert(k == boundary_vertices.size(), ExcInternalError());
3979 // delete the duplicated vertices at the
3980 // boundary, which occur, e.g. in c-type
3981 // or o-type grids around a body
3982 // (airfoil). this automatically deletes
3983 // unused vertices as well.
3985 cells,
3986 subcelldata,
3987 boundary_vertices);
3988 }
3989 else
3990 {
3991 // set up array of cells, unstructured
3992 // mode, so the connectivity is
3993 // explicitly given
3994 for (unsigned int i = 0; i < n_cells; ++i)
3995 {
3996 // note that since in the input file
3997 // we found the number of cells at
3998 // the top, there should still be
3999 // input here, so check this:
4000 AssertThrow(in.fail() == false, ExcIO());
4001
4002 // get the connectivity from the
4003 // input file. the vertices are
4004 // ordered like in the ucd format
4005 for (const unsigned int j : GeometryInfo<dim>::vertex_indices())
4006 in >> cells[i].vertices[GeometryInfo<dim>::ucd_to_deal[j]];
4007 }
4008 }
4009 AssertThrow(in.fail() == false, ExcIO());
4010
4011 apply_grid_fixup_functions(vertices, cells, subcelldata);
4012 tria->create_triangulation(vertices, cells, subcelldata);
4013}
4014
4015
4016
4017template <int dim, int spacedim>
4018void
4023
4024
4025
4026template <int dim, int spacedim>
4027void
4028GridIn<dim, spacedim>::read_assimp(const std::string &filename,
4029 const unsigned int mesh_index,
4030 const bool remove_duplicates,
4031 const double tol,
4032 const bool ignore_unsupported_types)
4033{
4034#ifdef DEAL_II_WITH_ASSIMP
4035 // Only good for surface grids.
4036 AssertThrow(dim < 3, ExcImpossibleInDim(dim));
4037
4038 // Create an instance of the Importer class
4039 Assimp::Importer importer;
4040
4041 // And have it read the given file with some postprocessing
4042 const aiScene *scene =
4043 importer.ReadFile(filename.c_str(),
4044 aiProcess_RemoveComponent |
4045 aiProcess_JoinIdenticalVertices |
4046 aiProcess_ImproveCacheLocality | aiProcess_SortByPType |
4047 aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes);
4048
4049 // If the import failed, report it
4050 AssertThrow(scene != nullptr, ExcMessage(importer.GetErrorString()));
4051
4052 AssertThrow(scene->mNumMeshes != 0,
4053 ExcMessage("Input file contains no meshes."));
4054
4056 (mesh_index < scene->mNumMeshes),
4057 ExcMessage("Too few meshes in the file."));
4058
4059 unsigned int start_mesh =
4060 (mesh_index == numbers::invalid_unsigned_int ? 0 : mesh_index);
4061 unsigned int end_mesh =
4062 (mesh_index == numbers::invalid_unsigned_int ? scene->mNumMeshes :
4063 mesh_index + 1);
4064
4065 // Deal.II objects are created empty, and then filled with imported file.
4066 std::vector<Point<spacedim>> vertices;
4067 std::vector<CellData<dim>> cells;
4068 SubCellData subcelldata;
4069
4070 // A series of counters to merge cells.
4071 unsigned int v_offset = 0;
4072 unsigned int c_offset = 0;
4073
4074 constexpr std::array<unsigned int, 8> local_vertex_numbering = {
4075 {0, 1, 5, 4, 2, 3, 7, 6}};
4076 // The index of the mesh will be used as a material index.
4077 for (unsigned int m = start_mesh; m < end_mesh; ++m)
4078 {
4079 const aiMesh *mesh = scene->mMeshes[m];
4080
4081 // Check that we know what to do with this mesh, otherwise just
4082 // ignore it
4083 if ((dim == 2) && mesh->mPrimitiveTypes != aiPrimitiveType_POLYGON)
4084 {
4085 AssertThrow(ignore_unsupported_types,
4086 ExcMessage("Incompatible mesh " + std::to_string(m) +
4087 "/" + std::to_string(scene->mNumMeshes)));
4088 continue;
4089 }
4090 else if ((dim == 1) && mesh->mPrimitiveTypes != aiPrimitiveType_LINE)
4091 {
4092 AssertThrow(ignore_unsupported_types,
4093 ExcMessage("Incompatible mesh " + std::to_string(m) +
4094 "/" + std::to_string(scene->mNumMeshes)));
4095 continue;
4096 }
4097 // Vertices
4098 const unsigned int n_vertices = mesh->mNumVertices;
4099 const aiVector3D *mVertices = mesh->mVertices;
4100
4101 // Faces
4102 const unsigned int n_faces = mesh->mNumFaces;
4103 const aiFace *mFaces = mesh->mFaces;
4104
4105 vertices.resize(v_offset + n_vertices);
4106 cells.resize(c_offset + n_faces);
4107
4108 for (unsigned int i = 0; i < n_vertices; ++i)
4109 for (unsigned int d = 0; d < spacedim; ++d)
4110 vertices[i + v_offset][d] = mVertices[i][d];
4111
4112 unsigned int valid_cell = c_offset;
4113 for (unsigned int i = 0; i < n_faces; ++i)
4114 {
4115 if (mFaces[i].mNumIndices == GeometryInfo<dim>::vertices_per_cell)
4116 {
4117 for (const unsigned int f : GeometryInfo<dim>::vertex_indices())
4118 {
4119 cells[valid_cell]
4120 .vertices[dim == 3 ? local_vertex_numbering[f] :
4122 mFaces[i].mIndices[f] + v_offset;
4123 }
4124 cells[valid_cell].material_id = m;
4125 ++valid_cell;
4126 }
4127 else
4128 {
4129 AssertThrow(ignore_unsupported_types,
4130 ExcMessage("Face " + std::to_string(i) + " of mesh " +
4131 std::to_string(m) + " has " +
4132 std::to_string(mFaces[i].mNumIndices) +
4133 " vertices. We expected only " +
4134 std::to_string(
4136 }
4137 }
4138 cells.resize(valid_cell);
4139
4140 // The vertices are added all at once. Cells are checked for
4141 // validity, so only valid_cells are now present in the deal.II
4142 // list of cells.
4143 v_offset += n_vertices;
4144 c_offset = valid_cell;
4145 }
4146
4147 // No cells were read
4148 if (cells.empty())
4149 return;
4150
4151 if (remove_duplicates)
4152 {
4153 // The function delete_duplicated_vertices() needs to be called more
4154 // than once if a vertex is duplicated more than once. So we keep
4155 // calling it until the number of vertices does not change any more.
4156 unsigned int n_verts = 0;
4157 while (n_verts != vertices.size())
4158 {
4159 n_verts = vertices.size();
4160 std::vector<unsigned int> considered_vertices;
4162 vertices, cells, subcelldata, considered_vertices, tol);
4163 }
4164 }
4165
4166 apply_grid_fixup_functions(vertices, cells, subcelldata);
4167 tria->create_triangulation(vertices, cells, subcelldata);
4168
4169#else
4170 (void)filename;
4171 (void)mesh_index;
4172 (void)remove_duplicates;
4173 (void)tol;
4174 (void)ignore_unsupported_types;
4175 AssertThrow(false, ExcNeedsAssimp());
4176#endif
4177}
4178
4179
4180
4181template <int dim, int spacedim>
4182void
4184{
4185 Assert(dim > 1,
4186 ExcMessage("The ugrid format reader currently supports only 2- and "
4187 "3-dimensional meshes."));
4188 Assert(tria != nullptr, ExcNoTriangulationSelected());
4189 AssertThrow(in.fail() == false, ExcIO());
4190
4191 // Start with the header:
4192 // { Number_of_Nodes, Number_of_Surf_Trias, Number_of_Surf_Quads,
4193 // Number_of_Vol_Tets, Number_of_Vol_Pents_5, Number_of_Vol_Pents_6,
4194 // Number_of_Vol_Hexs }
4195 unsigned int n_nodes, n_tris, n_quads, n_tets, n_pyramids, n_wedges, n_hexes;
4196 in >> n_nodes >> n_tris >> n_quads >> n_tets >> n_pyramids >> n_wedges >>
4197 n_hexes;
4198 if constexpr (dim == 2)
4200 n_tris + n_quads > 0,
4201 ExcMessage(
4202 "When reading a 2-dimensional triangulation, "
4203 "there need to be more than zero triangles or quadrilaterals."));
4204 else
4205 AssertThrow(n_tets + n_pyramids + n_wedges + n_hexes > 0,
4206 ExcMessage("When reading a 3-dimensional triangulation, "
4207 "there need to be more than zero tetrahedra, "
4208 "pyramids, wedges, or hexahedra."));
4209
4210 // Then first read the nodes:
4211 std::vector<Point<spacedim>> vertices(n_nodes);
4212 for (Point<spacedim> &vertex : vertices)
4213 {
4214 in >> vertex;
4215
4216 // Coordinates are always provided in 3d, so read any trailing
4217 // coordinates. They need to be zero for this to make sense.
4218 for (unsigned int d = spacedim; d < 3; ++d)
4219 {
4220 double dummy;
4221 in >> dummy;
4223 dummy == 0,
4224 ExcMessage(
4225 "You are reading a mesh in spacedim=" + std::to_string(spacedim) +
4226 " but some of the vertex positions have trailing coordinates "
4227 "that are not zero."));
4228 }
4229 }
4230
4231 // Then read the two-dimensional objects.
4232 std::vector<CellData<2>> objects_2d;
4233 objects_2d.reserve(n_tris + n_quads);
4234 for (unsigned int i = 0; i < n_tris; ++i)
4235 {
4236 CellData<2> object(ReferenceCells::Triangle.n_vertices());
4237
4238 // Read the vertex indices from the file. In ugrid files, these are
4239 // 1-based, so translate to 0-based next.
4240 for (unsigned int &vertex_index : object.vertices)
4241 {
4242 in >> vertex_index;
4243 --vertex_index;
4244 }
4245 objects_2d.emplace_back(object);
4246 }
4247 for (unsigned int i = 0; i < n_quads; ++i)
4248 {
4250 false,
4251 ExcMessage(
4252 "Reading quadrilaterals is not currently implemented by the ugrid "
4253 "reader because we do not know the order of vertices and have no example "
4254 "to look at. If you have a test file, please contact us and/or help us "
4255 "implement this missing case."));
4256 }
4257
4258 // ugrid files next store boundary ids for 2d objects, presumably
4259 // under the assumption that all meshes are 3d and that consequently
4260 // all 2d objects are at the boundary of the domain. We translate that
4261 // to material ids if we are dealing with 2d meshes.
4262 for (auto &object : objects_2d)
4263 if constexpr (dim == 2)
4264 in >> object.material_id;
4265 else
4266 in >> object.boundary_id;
4267
4268
4269 // Next read the three-dimensional objects.
4270 std::vector<CellData<3>> objects_3d;
4271 objects_3d.reserve(n_tets + n_pyramids + n_wedges + n_hexes);
4272 for (unsigned int i = 0; i < n_tets; ++i)
4273 {
4275 false,
4276 ExcMessage(
4277 "Reading tetrahedra is not currently implemented by the ugrid "
4278 "reader because we do not know the order of vertices and have no example "
4279 "to look at. If you have a test file, please contact us and/or help us "
4280 "implement this missing case."));
4281 }
4282 for (unsigned int i = 0; i < n_pyramids; ++i)
4283 {
4285 false,
4286 ExcMessage(
4287 "Reading pyramids is not currently implemented by the ugrid "
4288 "reader because we do not know the order of vertices and have no example "
4289 "to look at. If you have a test file, please contact us and/or help us "
4290 "implement this missing case."));
4291 }
4292 for (unsigned int i = 0; i < n_wedges; ++i)
4293 {
4295 false,
4296 ExcMessage(
4297 "Reading wedges is not currently implemented by the ugrid "
4298 "reader because we do not know the order of vertices and have no example "
4299 "to look at. If you have a test file, please contact us and/or help us "
4300 "implement this missing case."));
4301 }
4302 for (unsigned int i = 0; i < n_hexes; ++i)
4303 {
4305 false,
4306 ExcMessage(
4307 "Reading hexahdrea is not currently implemented by the ugrid "
4308 "reader because we do not know the order of vertices and have no example "
4309 "to look at. If you have a test file, please contact us and/or help us "
4310 "implement this missing case."));
4311 }
4312
4313 // The next section in ugrid files concerns Number_of_BL_Vol_Tets, which
4314 // I interpret as a way of specifying boundary layer cells that can be
4315 // extruded. I don't know how to interpret this information, so assume that it
4316 // isn't present in the current file.
4317 //
4318 // In fact, in the files we have this section simply doesn't exist. So the
4319 // following block is commented out:
4320
4321 // unsigned int n_BL_vol_tets;
4322 // in >> n_BL_vol_tets;
4323 // AssertThrow(n_BL_vol_tets == 0,
4324 // ExcMessage("Dealing with boundary layer descriptions in ugrid
4325 // "
4326 // "files is not currently supported"));
4327
4328
4329 // In the files we have, there is also a section that describes
4330 // boundary ids for 1d lines. This section is not described in
4331 // https://www.simcenter.msstate.edu/software/documentation/ug_io/3d_grid_file_type_ugrid.html,
4332 // but we want to read that data anyway:
4333 unsigned int n_lines;
4334 in >> n_lines;
4335 std::vector<CellData<1>> objects_1d(n_lines);
4336 for (CellData<1> &line : objects_1d)
4337 {
4338 line.vertices.resize(2);
4339
4340 // Read vertex indices for the line. Then again translate to zero-based:
4341 for (unsigned int &vertex_index : line.vertices)
4342 {
4343 in >> vertex_index;
4344 --vertex_index;
4345 }
4346
4347 // Then also read the boundary id of the line:
4348 in >> line.boundary_id;
4349 }
4350
4351 // Now that we have everything, create the triangulation with it:
4352 if constexpr (dim == 2)
4353 {
4354 SubCellData face_data;
4355 face_data.boundary_lines = std::move(objects_1d);
4356 tria->create_triangulation(vertices, objects_2d, face_data);
4357 }
4358 else
4360}
4361
4362
4363
4364#ifdef DEAL_II_TRILINOS_WITH_SEACAS
4365// Namespace containing some extra functions for reading ExodusII files
4366namespace
4367{
4368 // Convert ExodusII strings to cell types. Use the number of nodes per element
4369 // to disambiguate some cases. If the conversion fails then a
4370 // ReferenceCells::Invalid<dim> is returned.
4371 template <int dim>
4373 exodusii_name_to_type(const std::string &type_name,
4374 const int n_nodes_per_element)
4375 {
4376 Assert(type_name.size() > 0, ExcInternalError());
4377 // Try to canonify the name by switching to upper case and removing
4378 // trailing numbers. This makes, e.g., pyramid, PYRAMID, PYRAMID5, and
4379 // PYRAMID13 all equal.
4380 std::string type_name_2 = type_name;
4381 std::transform(type_name_2.begin(),
4382 type_name_2.end(),
4383 type_name_2.begin(),
4384 static_cast<int (*)(int)>(std::toupper));
4385 const std::string numbers = "0123456789";
4386 type_name_2.erase(std::find_first_of(type_name_2.begin(),
4387 type_name_2.end(),
4388 numbers.begin(),
4389 numbers.end()),
4390 type_name_2.end());
4391
4392 // The manual specifies BAR, BEAM, and TRUSS: in practice people use EDGE
4393 if constexpr (dim == 1)
4394 {
4395 if (type_name_2 == "BAR" || type_name_2 == "BEAM" ||
4396 type_name_2 == "EDGE" || type_name_2 == "TRUSS")
4397 return ReferenceCells::Line;
4398 }
4399 else if constexpr (dim == 2)
4400 {
4401 if (type_name_2 == "TRI" || type_name_2 == "TRIANGLE")
4403 else if (type_name_2 == "QUAD" || type_name_2 == "QUADRILATERAL")
4405 else if (type_name_2 == "SHELL")
4406 {
4407 if (n_nodes_per_element == 3)
4409 else
4411 }
4412 }
4413 else if constexpr (dim == 3)
4414 {
4415 if (type_name_2 == "TET" || type_name_2 == "TETRA" ||
4416 type_name_2 == "TETRAHEDRON")
4418 else if (type_name_2 == "PYRA" || type_name_2 == "PYRAMID")
4420 else if (type_name_2 == "WEDGE")
4421 return ReferenceCells::Wedge;
4422 else if (type_name_2 == "HEX" || type_name_2 == "HEXAHEDRON")
4424 }
4425
4426 return ReferenceCells::Invalid<dim>;
4427 }
4428
4429 // Associate deal.II boundary ids with sidesets (a face can be in multiple
4430 // sidesets - to translate we assign each set of side set ids to a
4431 // boundary_id or manifold_id)
4432 template <int dim, int spacedim = dim>
4433 std::pair<SubCellData, std::vector<std::vector<int>>>
4434 read_exodusii_sidesets(const int ex_id,
4435 const int n_sidesets,
4436 const std::vector<CellData<dim>> &cells,
4437 const bool apply_all_indicators_to_manifolds)
4438 {
4439 SubCellData subcelldata;
4440 std::vector<std::vector<int>> b_or_m_id_to_sideset_ids;
4441 // boundary id 0 is the default
4442 b_or_m_id_to_sideset_ids.emplace_back();
4443 // deal.II does not support assigning boundary ids with nonzero
4444 // codimension meshes so completely skip this information in that case.
4445 //
4446 // Exodus prints warnings if we try to get empty sets so always check
4447 // first
4448 if (dim == spacedim && n_sidesets > 0)
4449 {
4450 std::vector<int> sideset_ids(n_sidesets);
4451 int ierr = ex_get_ids(ex_id, EX_SIDE_SET, sideset_ids.data());
4452 AssertThrowExodusII(ierr);
4453 std::sort(sideset_ids.begin(), sideset_ids.end());
4454
4455 // First collect all side sets on all boundary faces (indexed here as
4456 // ReferenceCells::max_n_faces<dim>() * cell_n + face_n). We then sort
4457 // and uniquify the side sets so that we can convert a set of side set
4458 // indices into a single deal.II boundary or manifold id (and save the
4459 // correspondence).
4460 std::size_t n_total_sides = 0;
4461 for (const int &sideset_id : sideset_ids)
4462 {
4463 int n_sides = -1;
4464 int n_distribution_factors = -1;
4465
4466 ierr = ex_get_set_param(ex_id,
4467 EX_SIDE_SET,
4468 sideset_id,
4469 &n_sides,
4470 &n_distribution_factors);
4471 AssertThrowExodusII(ierr);
4472 Assert(n_sides > 0, ExcInternalError());
4473 n_total_sides += std::size_t(n_sides);
4474 }
4475 std::vector<std::pair<std::size_t, int>> face_sidesets;
4476 face_sidesets.reserve(n_total_sides);
4477
4478 for (const int &sideset_id : sideset_ids)
4479 {
4480 int n_sides = -1;
4481 int n_distribution_factors = -1;
4482
4483 ierr = ex_get_set_param(ex_id,
4484 EX_SIDE_SET,
4485 sideset_id,
4486 &n_sides,
4487 &n_distribution_factors);
4488 AssertThrowExodusII(ierr);
4489 if (n_sides > 0)
4490 {
4491 std::vector<int> elements(n_sides);
4492 std::vector<int> faces(n_sides);
4493 ierr = ex_get_set(ex_id,
4494 EX_SIDE_SET,
4495 sideset_id,
4496 elements.data(),
4497 faces.data());
4498 AssertThrowExodusII(ierr);
4499
4500 // According to the manual (subsection 4.8): "The internal
4501 // number of an element numbering is defined implicitly by the
4502 // order in which it appears in the file. Elements are
4503 // numbered internally (beginning with 1) consecutively across
4504 // all element blocks." Hence element i in Exodus numbering is
4505 // entry i - 1 in the cells array.
4506 for (int side_n = 0; side_n < n_sides; ++side_n)
4507 {
4508 const long cell_n = elements[side_n] - 1;
4509 Assert(cell_n >= 0, ExcInternalError());
4510 const long face_n = faces[side_n] - 1;
4511 Assert(face_n >= 0, ExcInternalError());
4512 const std::size_t face_id =
4513 cell_n * ReferenceCells::max_n_faces<dim>() + face_n;
4514 face_sidesets.emplace_back(face_id, sideset_id);
4515 }
4516 }
4517 }
4518 // Do a full sort so we get consistent order of (face, sideset) pairs:
4519 std::sort(face_sidesets.begin(), face_sidesets.end());
4520
4521 // Collect into something we can sort by sideset ids (but avoid
4522 // copying any actual data, since we typically have multiple sidesets
4523 // per face):
4524 std::deque<ArrayView<std::pair<std::size_t, int>>>
4525 face_id_to_sideset_ids;
4526 if (face_sidesets.size() > 0)
4527 {
4528 auto start = face_sidesets.begin();
4529 for (auto it = face_sidesets.begin() + 1; it < face_sidesets.end();
4530 ++it)
4531 if (start->first != it->first)
4532 {
4533 face_id_to_sideset_ids.emplace_back(
4534 make_array_view(start, it));
4535 start = it;
4536 }
4537 face_id_to_sideset_ids.emplace_back(
4538 make_array_view(start, face_sidesets.end()));
4539 }
4540
4541 // Sort by sideset ids per face, so that faces with the same sidesets
4542 // are consecutive. Use a stable sort so we get consistent output (since
4543 // this step ignores face ids)
4544 std::stable_sort(face_id_to_sideset_ids.begin(),
4545 face_id_to_sideset_ids.end(),
4546 [](const auto &a, const auto &b) {
4547 return std::lexicographical_compare(
4548 a.begin(),
4549 a.end(),
4550 b.begin(),
4551 b.end(),
4552 [](const auto &c, const auto &d) {
4553 return c.second < d.second;
4554 });
4555 });
4556
4557 if constexpr (dim == 2)
4558 subcelldata.boundary_lines.reserve(face_id_to_sideset_ids.size());
4559 else if constexpr (dim == 3)
4560 subcelldata.boundary_quads.reserve(face_id_to_sideset_ids.size());
4561 types::boundary_id current_b_or_m_id = 0;
4562 std::vector<int> face_sideset_ids;
4563 for (const auto &pairs : face_id_to_sideset_ids)
4564 {
4565 const std::size_t face_id = pairs[0].first;
4566 Assert(std::all_of(pairs.begin(),
4567 pairs.end(),
4568 [&](const auto &a) {
4569 return a.first == face_id;
4570 }),
4572 face_sideset_ids.resize(pairs.size());
4573 for (std::size_t i = 0; i < pairs.size(); ++i)
4574 face_sideset_ids[i] = pairs[i].second;
4575
4576 if (face_sideset_ids != b_or_m_id_to_sideset_ids.back())
4577 {
4578 // Since we sorted by sideset ids we are guaranteed that if
4579 // this doesn't match the last set then it has not yet been
4580 // seen (but check that via assertion)
4581 Assert(std::find(b_or_m_id_to_sideset_ids.begin(),
4582 b_or_m_id_to_sideset_ids.end(),
4583 face_sideset_ids) ==
4584 b_or_m_id_to_sideset_ids.end(),
4586 ++current_b_or_m_id;
4587 b_or_m_id_to_sideset_ids.emplace_back(face_sideset_ids.begin(),
4588 face_sideset_ids.end());
4589 Assert(current_b_or_m_id == b_or_m_id_to_sideset_ids.size() - 1,
4591 }
4592 // Record the b_or_m_id of the current face.
4593 const unsigned int local_face_n =
4594 face_id % ReferenceCells::max_n_faces<dim>();
4595 const CellData<dim> &cell =
4596 cells[face_id / ReferenceCells::max_n_faces<dim>()];
4597 const auto reference_cell =
4598 ReferenceCells::n_vertices_to_reference_cell<dim>(
4599 cell.vertices.size());
4600 const unsigned int deal_face_n =
4601 reference_cell.exodusii_face_to_deal_face(local_face_n);
4602 const auto face_reference_cell =
4603 reference_cell.face_reference_cell(deal_face_n);
4604
4605 // The orientation we pick doesn't matter here since when we
4606 // create the Triangulation we will sort the vertices for each
4607 // CellData object created here.
4608 if constexpr (dim == 2)
4609 {
4610 CellData<1> boundary_line(face_reference_cell.n_vertices());
4611 if (apply_all_indicators_to_manifolds)
4612 boundary_line.manifold_id = current_b_or_m_id;
4613 else
4614 boundary_line.boundary_id = current_b_or_m_id;
4615 for (unsigned int j = 0; j < face_reference_cell.n_vertices();
4616 ++j)
4617 boundary_line.vertices[j] =
4618 cell.vertices[reference_cell.face_to_cell_vertices(
4619 deal_face_n, j, 0)];
4620
4621 subcelldata.boundary_lines.push_back(std::move(boundary_line));
4622 }
4623 else if constexpr (dim == 3)
4624 {
4625 CellData<2> boundary_quad(face_reference_cell.n_vertices());
4626 if (apply_all_indicators_to_manifolds)
4627 boundary_quad.manifold_id = current_b_or_m_id;
4628 else
4629 boundary_quad.boundary_id = current_b_or_m_id;
4630 for (unsigned int j = 0; j < face_reference_cell.n_vertices();
4631 ++j)
4632 boundary_quad.vertices[j] =
4633 cell.vertices[reference_cell.face_to_cell_vertices(
4634 deal_face_n, j, 0)];
4635
4636 subcelldata.boundary_quads.push_back(std::move(boundary_quad));
4637 }
4638 }
4639 }
4640
4641 return std::make_pair(std::move(subcelldata),
4642 std::move(b_or_m_id_to_sideset_ids));
4643 }
4644} // namespace
4645#endif
4646
4647template <int dim, int spacedim>
4650 const std::string &filename,
4651 const bool apply_all_indicators_to_manifolds)
4652{
4653#ifdef DEAL_II_TRILINOS_WITH_SEACAS
4654 // deal.II always uses double precision numbers for geometry
4655 int component_word_size = sizeof(double);
4656 // setting to zero uses the stored word size
4657 int floating_point_word_size = 0;
4658 float ex_version = 0.0;
4659
4660 const int ex_id = ex_open(filename.c_str(),
4661 EX_READ,
4662 &component_word_size,
4663 &floating_point_word_size,
4664 &ex_version);
4665 AssertThrow(ex_id > 0,
4666 ExcMessage("ExodusII failed to open the specified input file."));
4667
4668 // Read basic mesh information:
4669 std::vector<char> string_temp(MAX_LINE_LENGTH + 1, '\0');
4670 int mesh_dimension = 0;
4671 int n_nodes = 0;
4672 int n_elements = 0;
4673 int n_element_blocks = 0;
4674 int n_nodesets = 0;
4675 int n_sidesets = 0;
4676
4677 int ierr = ex_get_init(ex_id,
4678 string_temp.data(),
4679 &mesh_dimension,
4680 &n_nodes,
4681 &n_elements,
4682 &n_element_blocks,
4683 &n_nodesets,
4684 &n_sidesets);
4685 AssertThrowExodusII(ierr);
4686 AssertDimension(mesh_dimension, spacedim);
4687
4688 // Read nodes:
4689 //
4690 // Even if there is a node numbering array the values stored inside the
4691 // ExodusII file must use the contiguous, internal ordering (see Section 4.5
4692 // of the manual - "Internal (contiguously numbered) node and element IDs
4693 // must be used for all data structures that contain node or element numbers
4694 // (IDs), including node set node lists, side set element lists, and element
4695 // connectivity.")
4696 std::vector<Point<spacedim>> vertices;
4697 vertices.reserve(n_nodes);
4698 {
4699 std::vector<double> xs(n_nodes);
4700 std::vector<double> ys(n_nodes);
4701 std::vector<double> zs(n_nodes);
4702
4703 ierr = ex_get_coord(ex_id, xs.data(), ys.data(), zs.data());
4704 AssertThrowExodusII(ierr);
4705
4706 for (int vertex_n = 0; vertex_n < n_nodes; ++vertex_n)
4707 {
4708 switch (spacedim)
4709 {
4710 case 1:
4711 vertices.emplace_back(xs[vertex_n]);
4712 break;
4713 case 2:
4714 vertices.emplace_back(xs[vertex_n], ys[vertex_n]);
4715 break;
4716 case 3:
4717 vertices.emplace_back(xs[vertex_n], ys[vertex_n], zs[vertex_n]);
4718 break;
4719 default:
4720 Assert(spacedim <= 3, ExcNotImplemented());
4721 }
4722 }
4723 }
4724
4725 std::vector<int> element_block_ids(n_element_blocks);
4726 ierr = ex_get_ids(ex_id, EX_ELEM_BLOCK, element_block_ids.data());
4727 AssertThrowExodusII(ierr);
4728
4729 std::vector<CellData<dim>> cells;
4730 cells.reserve(n_elements);
4731 // Elements are grouped together by same reference cell type in element
4732 // blocks. There may be multiple blocks for a single reference cell type,
4733 // but "each element block may contain only one element type".
4734 for (const int element_block_id : element_block_ids)
4735 {
4736 std::fill(string_temp.begin(), string_temp.end(), '\0');
4737 int n_block_elements = 0;
4738 int n_nodes_per_element = 0;
4739 int n_edges_per_element = 0;
4740 int n_faces_per_element = 0;
4741 int n_attributes_per_element = 0;
4742
4743 // Extract element data.
4744 ierr = ex_get_block(ex_id,
4745 EX_ELEM_BLOCK,
4746 element_block_id,
4747 string_temp.data(),
4748 &n_block_elements,
4749 &n_nodes_per_element,
4750 &n_edges_per_element,
4751 &n_faces_per_element,
4752 &n_attributes_per_element);
4753 AssertThrowExodusII(ierr);
4754 const auto type =
4755 exodusii_name_to_type<dim>(string_temp.data(), n_nodes_per_element);
4756 AssertThrow(type != ReferenceCells::Invalid<dim>,
4757 ExcMessage(
4758 "The ExodusII block " + std::to_string(element_block_id) +
4759 " with element type " + std::string(string_temp.data()) +
4760 " does not have a corresponding ReferenceCell<dim> with a"
4761 " dimension matching the topological mesh dimension " +
4762 std::to_string(dim) + "."));
4763
4764 // The number of nodes per element may be larger than what we want to
4765 // read - for example, if the Exodus file contains a QUAD9 element, we
4766 // only want to read the first four values and ignore the rest.
4767 Assert(int(type.n_vertices()) <= n_nodes_per_element, ExcInternalError());
4768
4769 std::vector<int> connection(n_nodes_per_element * n_block_elements);
4770 ierr = ex_get_conn(ex_id,
4771 EX_ELEM_BLOCK,
4772 element_block_id,
4773 connection.data(),
4774 nullptr,
4775 nullptr);
4776 AssertThrowExodusII(ierr);
4777
4778 for (unsigned int elem_n = 0; elem_n < connection.size();
4779 elem_n += n_nodes_per_element)
4780 {
4781 CellData<dim> cell(type.n_vertices());
4782 for (const unsigned int i : type.vertex_indices())
4783 {
4784 cell.vertices[type.exodusii_vertex_to_deal_vertex(i)] =
4785 connection[elem_n + i] - 1;
4786 }
4787 cell.material_id = element_block_id;
4788 cells.push_back(std::move(cell));
4789 }
4790 }
4791
4792 // Extract boundary data.
4793 auto pair = read_exodusii_sidesets<dim, spacedim>(
4794 ex_id, n_sidesets, cells, apply_all_indicators_to_manifolds);
4795 ierr = ex_close(ex_id);
4796 AssertThrowExodusII(ierr);
4797
4798 apply_grid_fixup_functions(vertices, cells, pair.first);
4799 tria->create_triangulation(vertices, cells, pair.first);
4800 ExodusIIData out;
4801 out.id_to_sideset_ids = std::move(pair.second);
4802 return out;
4803#else
4804 (void)filename;
4805 (void)apply_all_indicators_to_manifolds;
4806 AssertThrow(false, ExcNeedsExodusII());
4807 return {};
4808#endif
4809}
4810
4811
4812template <int dim, int spacedim>
4813void
4815{
4816 std::string line;
4817 while (in)
4818 {
4819 // get line
4820 getline(in, line);
4821
4822 // check if this is a line that
4823 // consists only of spaces, and
4824 // if not put the whole thing
4825 // back and return
4826 if (std::find_if(line.begin(), line.end(), [](const char c) {
4827 return c != ' ';
4828 }) != line.end())
4829 {
4830 in.putback('\n');
4831 for (int i = line.size() - 1; i >= 0; --i)
4832 in.putback(line[i]);
4833 return;
4834 }
4835
4836 // else: go on with next line
4837 }
4838}
4839
4840
4841
4842template <int dim, int spacedim>
4843void
4845 const char comment_start)
4846{
4847 char c;
4848 // loop over the following comment
4849 // lines
4850 while (in.get(c) && c == comment_start)
4851 // loop over the characters after
4852 // the comment starter
4853 while (in.get() != '\n')
4854 ;
4855
4856
4857 // put back first character of
4858 // first non-comment line
4859 if (in)
4860 in.putback(c);
4861
4862 // at last: skip additional empty lines, if present
4863 skip_empty_lines(in);
4864}
4865
4866
4867
4868template <int dim, int spacedim>
4869void
4871 const std::vector<CellData<dim>> & /*cells*/,
4872 const std::vector<Point<spacedim>> & /*vertices*/,
4873 std::ostream & /*out*/)
4874{
4876}
4877
4878
4879
4880template <>
4881void
4883 const std::vector<Point<2>> &vertices,
4884 std::ostream &out)
4885{
4886 double min_x = vertices[cells[0].vertices[0]][0],
4887 max_x = vertices[cells[0].vertices[0]][0],
4888 min_y = vertices[cells[0].vertices[0]][1],
4889 max_y = vertices[cells[0].vertices[0]][1];
4890
4891 for (unsigned int i = 0; i < cells.size(); ++i)
4892 {
4893 for (const auto vertex : cells[i].vertices)
4894 {
4895 const Point<2> &p = vertices[vertex];
4896
4897 if (p[0] < min_x)
4898 min_x = p[0];
4899 if (p[0] > max_x)
4900 max_x = p[0];
4901 if (p[1] < min_y)
4902 min_y = p[1];
4903 if (p[1] > max_y)
4904 max_y = p[1];
4905 }
4906
4907 out << "# cell " << i << std::endl;
4908 Point<2> center;
4909 for (const auto vertex : cells[i].vertices)
4910 center += vertices[vertex];
4911 center /= 4;
4912
4913 out << "set label \"" << i << "\" at " << center[0] << ',' << center[1]
4914 << " center" << std::endl;
4915
4916 // first two line right direction
4917 for (unsigned int f = 0; f < 2; ++f)
4918 out << "set arrow from " << vertices[cells[i].vertices[f]][0] << ','
4919 << vertices[cells[i].vertices[f]][1] << " to "
4920 << vertices[cells[i].vertices[(f + 1) % 4]][0] << ','
4921 << vertices[cells[i].vertices[(f + 1) % 4]][1] << std::endl;
4922 // other two lines reverse direction
4923 for (unsigned int f = 2; f < 4; ++f)
4924 out << "set arrow from " << vertices[cells[i].vertices[(f + 1) % 4]][0]
4925 << ',' << vertices[cells[i].vertices[(f + 1) % 4]][1] << " to "
4926 << vertices[cells[i].vertices[f]][0] << ','
4927 << vertices[cells[i].vertices[f]][1] << std::endl;
4928 out << std::endl;
4929 }
4930
4931
4932 out << std::endl
4933 << "set nokey" << std::endl
4934 << "pl [" << min_x << ':' << max_x << "][" << min_y << ':' << max_y
4935 << "] " << min_y << std::endl
4936 << "pause -1" << std::endl;
4937}
4938
4939
4940
4941template <>
4942void
4944 const std::vector<Point<3>> &vertices,
4945 std::ostream &out)
4946{
4947 for (const auto &cell : cells)
4948 {
4949 // line 0
4950 out << vertices[cell.vertices[0]] << std::endl
4951 << vertices[cell.vertices[1]] << std::endl
4952 << std::endl
4953 << std::endl;
4954 // line 1
4955 out << vertices[cell.vertices[1]] << std::endl
4956 << vertices[cell.vertices[2]] << std::endl
4957 << std::endl
4958 << std::endl;
4959 // line 2
4960 out << vertices[cell.vertices[3]] << std::endl
4961 << vertices[cell.vertices[2]] << std::endl
4962 << std::endl
4963 << std::endl;
4964 // line 3
4965 out << vertices[cell.vertices[0]] << std::endl
4966 << vertices[cell.vertices[3]] << std::endl
4967 << std::endl
4968 << std::endl;
4969 // line 4
4970 out << vertices[cell.vertices[4]] << std::endl
4971 << vertices[cell.vertices[5]] << std::endl
4972 << std::endl
4973 << std::endl;
4974 // line 5
4975 out << vertices[cell.vertices[5]] << std::endl
4976 << vertices[cell.vertices[6]] << std::endl
4977 << std::endl
4978 << std::endl;
4979 // line 6
4980 out << vertices[cell.vertices[7]] << std::endl
4981 << vertices[cell.vertices[6]] << std::endl
4982 << std::endl
4983 << std::endl;
4984 // line 7
4985 out << vertices[cell.vertices[4]] << std::endl
4986 << vertices[cell.vertices[7]] << std::endl
4987 << std::endl
4988 << std::endl;
4989 // line 8
4990 out << vertices[cell.vertices[0]] << std::endl
4991 << vertices[cell.vertices[4]] << std::endl
4992 << std::endl
4993 << std::endl;
4994 // line 9
4995 out << vertices[cell.vertices[1]] << std::endl
4996 << vertices[cell.vertices[5]] << std::endl
4997 << std::endl
4998 << std::endl;
4999 // line 10
5000 out << vertices[cell.vertices[2]] << std::endl
5001 << vertices[cell.vertices[6]] << std::endl
5002 << std::endl
5003 << std::endl;
5004 // line 11
5005 out << vertices[cell.vertices[3]] << std::endl
5006 << vertices[cell.vertices[7]] << std::endl
5007 << std::endl
5008 << std::endl;
5009 }
5010}
5011
5012
5013
5014template <int dim, int spacedim>
5015void
5016GridIn<dim, spacedim>::read(const std::string &filename, Format format)
5017{
5018 // Check early that the file actually exists and if not throw ExcFileNotOpen.
5019 AssertThrow(std::filesystem::exists(filename), ExcFileNotOpen(filename));
5020
5021 if (format == Default)
5022 {
5023 const std::string::size_type slashpos = filename.find_last_of('/');
5024 const std::string::size_type dotpos = filename.find_last_of('.');
5025 if (dotpos < filename.size() &&
5026 (dotpos > slashpos || slashpos == std::string::npos))
5027 {
5028 std::string ext = filename.substr(dotpos + 1);
5029 format = parse_format(ext);
5030 }
5031 }
5032
5033 if (format == assimp)
5034 {
5035 read_assimp(filename);
5036 }
5037 else if (format == exodusii)
5038 {
5039 read_exodusii(filename);
5040 }
5041 else
5042 {
5043 std::ifstream in(filename);
5044 read(in, format);
5045 }
5046}
5047
5048
5049template <int dim, int spacedim>
5050void
5051GridIn<dim, spacedim>::read(std::istream &in, Format format)
5052{
5053 if (format == Default)
5054 format = default_format;
5055
5056 switch (format)
5057 {
5058 case dbmesh:
5059 read_dbmesh(in);
5060 return;
5061
5062 case msh:
5063 read_msh(in);
5064 return;
5065
5066 case vtk:
5067 read_vtk(in);
5068 return;
5069
5070 case vtu:
5071 read_vtu(in);
5072 return;
5073
5074 case unv:
5075 read_unv(in);
5076 return;
5077
5078 case ucd:
5079 read_ucd(in);
5080 return;
5081
5082 case abaqus:
5083 read_abaqus(in);
5084 return;
5085
5086 case xda:
5087 read_xda(in);
5088 return;
5089
5090 case tecplot:
5091 read_tecplot(in);
5092 return;
5093
5094 case ugrid:
5095 read_ugrid(in);
5096 return;
5097
5098 case assimp:
5099 Assert(false,
5100 ExcMessage("There is no read_assimp(istream &) function. "
5101 "Use the read_assimp(string &filename, ...) "
5102 "functions, instead."));
5103 return;
5104
5105 case exodusii:
5106 Assert(false,
5107 ExcMessage("There is no read_exodusii(istream &) function. "
5108 "Use the read_exodusii(string &filename, ...) "
5109 "function, instead."));
5110 return;
5111
5112 case Default:
5113 break;
5114 }
5116}
5117
5118
5119
5120template <int dim, int spacedim>
5121std::string
5123{
5124 switch (format)
5125 {
5126 case dbmesh:
5127 return ".dbmesh";
5128 case exodusii:
5129 return ".e";
5130 case msh:
5131 return ".msh";
5132 case vtk:
5133 return ".vtk";
5134 case vtu:
5135 return ".vtu";
5136 case unv:
5137 return ".unv";
5138 case ucd:
5139 return ".inp";
5140 case abaqus:
5141 return ".inp"; // Typical suffix for Abaqus mesh files conflicts with
5142 // UCD.
5143 case xda:
5144 return ".xda";
5145 case tecplot:
5146 return ".dat";
5147 case ugrid:
5148 return ".txt";
5149 default:
5151 return ".unknown_format";
5152 }
5153}
5154
5155
5156
5157template <int dim, int spacedim>
5159GridIn<dim, spacedim>::parse_format(const std::string &format_name)
5160{
5161 if (format_name == "dbmesh")
5162 return dbmesh;
5163
5164 if (format_name == "exodusii")
5165 return exodusii;
5166
5167 if (format_name == "msh")
5168 return msh;
5169
5170 if (format_name == "unv")
5171 return unv;
5172
5173 if (format_name == "vtk")
5174 return vtk;
5175
5176 if (format_name == "vtu")
5177 return vtu;
5178
5179 // This is also the typical extension of Abaqus input files.
5180 if (format_name == "inp")
5181 return ucd;
5182
5183 if (format_name == "ucd")
5184 return ucd;
5185
5186 if (format_name == "xda")
5187 return xda;
5188
5189 if (format_name == "tecplot")
5190 return tecplot;
5191
5192 if (format_name == "dat")
5193 return tecplot;
5194
5195 if (format_name == "ugrid")
5196 return ugrid;
5197
5198 if (format_name == "plt")
5199 // Actually, this is the extension for the
5200 // tecplot binary format, which we do not
5201 // support right now. However, some people
5202 // tend to create tecplot ascii files with
5203 // the extension 'plt' instead of
5204 // 'dat'. Thus, include this extension
5205 // here. If it actually is a binary file,
5206 // the read_tecplot() function will fail
5207 // and throw an exception, anyway.
5208 return tecplot;
5209
5210 AssertThrow(false, ExcInvalidState());
5211 // return something weird
5212 return Format(Default);
5213}
5214
5215
5216
5217template <int dim, int spacedim>
5218std::string
5220{
5221 return "dbmesh|exodusii|msh|unv|vtk|vtu|ucd|abaqus|xda|tecplot|assimp|ugrid";
5222}
5223
5224
5225
5226namespace
5227{
5228 template <int dim, int spacedim>
5229 Abaqus_to_UCD<dim, spacedim>::Abaqus_to_UCD()
5230 : tolerance(5e-16) // Used to offset Cubit tolerance error when outputting
5231 // value close to zero
5232 {
5233 AssertThrow(spacedim == 2 || spacedim == 3, ExcNotImplemented());
5234 }
5235
5236
5237
5238 // Convert from a string to some other data type
5239 // Reference: http://www.codeguru.com/forum/showthread.php?t=231054
5240 template <class T>
5241 bool
5242 from_string(T &t, const std::string &s, std::ios_base &(*f)(std::ios_base &))
5243 {
5244 std::istringstream iss(s);
5245 return !(iss >> f >> t).fail();
5246 }
5247
5248
5249
5250 // Extract an integer from a string
5251 int
5252 extract_int(const std::string &s)
5253 {
5254 std::string tmp;
5255 for (const char c : s)
5256 {
5257 if (std::isdigit(c) != 0)
5258 {
5259 tmp += c;
5260 }
5261 }
5262
5263 int number = 0;
5264 from_string(number, tmp, std::dec);
5265 return number;
5266 }
5267
5268
5269
5270 template <int dim, int spacedim>
5271 void
5272 Abaqus_to_UCD<dim, spacedim>::read_in_abaqus(std::istream &input_stream)
5273 {
5274 // References:
5275 // http://www.egr.msu.edu/software/abaqus/Documentation/docs/v6.7/books/usb/default.htm?startat=pt01ch02.html
5276 // http://www.cprogramming.com/tutorial/string.html
5277
5278 AssertThrow(input_stream.fail() == false, ExcIO());
5279 std::string line;
5280
5281 while (std::getline(input_stream, line))
5282 {
5283 cont:
5284 std::transform(line.begin(),
5285 line.end(),
5286 line.begin(),
5287 static_cast<int (*)(int)>(std::toupper));
5288
5289 if (line.compare("*HEADING") == 0 || line.compare(0, 2, "**") == 0 ||
5290 line.compare(0, 5, "*PART") == 0)
5291 {
5292 // Skip header and comments
5293 while (std::getline(input_stream, line))
5294 {
5295 if (line[0] == '*')
5296 goto cont; // My eyes, they burn!
5297 }
5298 }
5299 else if (line.compare(0, 5, "*NODE") == 0)
5300 {
5301 // Extract list of vertices
5302 // Header line might be:
5303 // *NODE, NSET=ALLNODES
5304 // *NODE
5305
5306 // Contains lines in the form:
5307 // Index, x, y, z
5308 while (std::getline(input_stream, line))
5309 {
5310 if (line[0] == '*')
5311 goto cont;
5312
5313 std::vector<double> node(spacedim + 1);
5314
5315 std::istringstream iss(line);
5316 char comma;
5317 for (unsigned int i = 0; i < spacedim + 1; ++i)
5318 iss >> node[i] >> comma;
5319
5320 node_list.push_back(node);
5321 }
5322 }
5323 else if (line.compare(0, 8, "*ELEMENT") == 0)
5324 {
5325 // Element construction.
5326 // There are different header formats, the details
5327 // of which we're not particularly interested in except
5328 // whether they represent quads or hexahedrals.
5329 // *ELEMENT, TYPE=S4R, ELSET=EB<material id>
5330 // *ELEMENT, TYPE=C3d8R, ELSET=EB<material id>
5331 // *ELEMENT, TYPE=C3d8
5332 // Elements itself (n=4 or n=8):
5333 // Index, i[0], ..., i[n]
5334
5335 int material = 0;
5336 // Scan for material id
5337 {
5338 const std::string before_material = "ELSET=EB";
5339 const std::size_t idx = line.find(before_material);
5340 if (idx != std::string::npos)
5341 {
5342 from_string(material,
5343 line.substr(idx + before_material.size()),
5344 std::dec);
5345 }
5346 }
5347
5348 // Read ELEMENT definition
5349 while (std::getline(input_stream, line))
5350 {
5351 if (line[0] == '*')
5352 goto cont;
5353
5354 std::istringstream iss(line);
5355 char comma;
5356
5357 // We will store the material id in the zeroth entry of the
5358 // vector and the rest of the elements represent the global
5359 // node numbers
5360 const unsigned int n_data_per_cell =
5362 std::vector<double> cell(n_data_per_cell);
5363 for (unsigned int i = 0; i < n_data_per_cell; ++i)
5364 iss >> cell[i] >> comma;
5365
5366 // Overwrite cell index from file by material
5367 cell[0] = static_cast<double>(material);
5368 cell_list.push_back(cell);
5369 }
5370 }
5371 else if (line.compare(0, 8, "*SURFACE") == 0)
5372 {
5373 // Extract the definitions of boundary surfaces
5374 // Old format from Cubit:
5375 // *SURFACE, NAME=SS<boundary indicator>
5376 // <element index>, S<face number>
5377 // Abaqus default format:
5378 // *SURFACE, TYPE=ELEMENT, NAME=SURF-<indicator>
5379
5380 // Get name of the surface and extract id from it;
5381 // this will be the boundary indicator
5382 const std::string name_key = "NAME=";
5383 const std::size_t name_idx_start =
5384 line.find(name_key) + name_key.size();
5385 std::size_t name_idx_end = line.find(',', name_idx_start);
5386 if (name_idx_end == std::string::npos)
5387 {
5388 name_idx_end = line.size();
5389 }
5390 const int b_indicator = extract_int(
5391 line.substr(name_idx_start, name_idx_end - name_idx_start));
5392
5393 // Read SURFACE definition
5394 // Note that the orientation of the faces is embedded within the
5395 // definition of each "set" of faces that comprise the surface
5396 // These are either marked by an "S" or "E" in 3d or 2d
5397 // respectively.
5398 while (std::getline(input_stream, line))
5399 {
5400 if (line[0] == '*')
5401 goto cont;
5402
5403 // Change all characters to upper case
5404 std::transform(line.begin(),
5405 line.end(),
5406 line.begin(),
5407 static_cast<int (*)(int)>(std::toupper));
5408
5409 // Surface can be created from ELSET, or directly from cells
5410 // If elsets_list contains a key with specific name - refers
5411 // to that ELSET, otherwise refers to cell
5412 std::istringstream iss(line);
5413 int el_idx;
5414 int face_number;
5415 char temp;
5416
5417 // Get relevant faces, taking into account the element
5418 // orientation
5419 std::vector<double> quad_node_list;
5420 const std::string elset_name = line.substr(0, line.find(','));
5421 if (elsets_list.count(elset_name) != 0)
5422 {
5423 // Surface refers to ELSET
5424 std::string stmp;
5425 iss >> stmp >> temp >> face_number;
5426
5427 const std::vector<int> cells = elsets_list[elset_name];
5428 for (const int cell : cells)
5429 {
5430 el_idx = cell;
5431 quad_node_list =
5432 get_global_node_numbers(el_idx, face_number);
5433 quad_node_list.insert(quad_node_list.begin(),
5434 b_indicator);
5435
5436 face_list.push_back(quad_node_list);
5437 }
5438 }
5439 else
5440 {
5441 // Surface refers directly to elements
5442 char comma;
5443 iss >> el_idx >> comma >> temp >> face_number;
5444 quad_node_list =
5445 get_global_node_numbers(el_idx, face_number);
5446 quad_node_list.insert(quad_node_list.begin(), b_indicator);
5447
5448 face_list.push_back(quad_node_list);
5449 }
5450 }
5451 }
5452 else if (line.compare(0, 6, "*ELSET") == 0)
5453 {
5454 // Get ELSET name.
5455 // Materials are attached to elsets with specific name
5456 std::string elset_name;
5457 {
5458 const std::string elset_key = "*ELSET, ELSET=";
5459 const std::size_t idx = line.find(elset_key);
5460 if (idx != std::string::npos)
5461 {
5462 const std::string comma = ",";
5463 const std::size_t first_comma = line.find(comma);
5464 const std::size_t second_comma =
5465 line.find(comma, first_comma + 1);
5466 const std::size_t elset_name_start =
5467 line.find(elset_key) + elset_key.size();
5468 elset_name = line.substr(elset_name_start,
5469 second_comma - elset_name_start);
5470 }
5471 }
5472
5473 // There are two possibilities of storing cells numbers in ELSET:
5474 // 1. If the header contains the 'GENERATE' keyword, then the next
5475 // line describes range of cells as:
5476 // cell_id_start, cell_id_end, cell_step
5477 // 2. If the header does not contain the 'GENERATE' keyword, then
5478 // the next lines contain cells numbers
5479 std::vector<int> elements;
5480 const std::size_t generate_idx = line.find("GENERATE");
5481 if (generate_idx != std::string::npos)
5482 {
5483 // Option (1)
5484 std::getline(input_stream, line);
5485 std::istringstream iss(line);
5486 char comma;
5487 int elid_start;
5488 int elid_end;
5489 int elis_step = 1; // Default if case stride not provided
5490
5491 // Some files don't have the stride size
5492 // Compare mesh test cases ./grids/abaqus/3d/other_simple.inp
5493 // to
5494 // ./grids/abaqus/2d/2d_test_abaqus.inp
5495 iss >> elid_start >> comma >> elid_end;
5496 AssertThrow(comma == ',',
5497 ExcMessage(
5498 std::string(
5499 "While reading an ABAQUS file, the reader "
5500 "expected a comma but found a <") +
5501 comma + "> in the line <" + line + ">."));
5503 elid_start <= elid_end,
5504 ExcMessage(
5505 std::string(
5506 "While reading an ABAQUS file, the reader encountered "
5507 "a GENERATE statement in which the upper bound <") +
5508 Utilities::int_to_string(elid_end) +
5509 "> for the element numbers is not larger or equal "
5510 "than the lower bound <" +
5511 Utilities::int_to_string(elid_start) + ">."));
5512
5513 // https://stackoverflow.com/questions/8046357/how-do-i-check-if-a-stringstream-variable-is-empty-null
5514 if (iss.rdbuf()->in_avail() != 0)
5515 iss >> comma >> elis_step;
5516 AssertThrow(comma == ',',
5517 ExcMessage(
5518 std::string(
5519 "While reading an ABAQUS file, the reader "
5520 "expected a comma but found a <") +
5521 comma + "> in the line <" + line + ">."));
5522
5523 for (int i = elid_start; i <= elid_end; i += elis_step)
5524 elements.push_back(i);
5525 elsets_list[elset_name] = elements;
5526
5527 std::getline(input_stream, line);
5528 }
5529 else
5530 {
5531 // Option (2)
5532 while (std::getline(input_stream, line))
5533 {
5534 if (line[0] == '*')
5535 break;
5536
5537 std::istringstream iss(line);
5538 char comma;
5539 int elid;
5540 while (!iss.eof())
5541 {
5542 iss >> elid >> comma;
5544 comma == ',',
5545 ExcMessage(
5546 std::string(
5547 "While reading an ABAQUS file, the reader "
5548 "expected a comma but found a <") +
5549 comma + "> in the line <" + line + ">."));
5550
5551 elements.push_back(elid);
5552 }
5553 }
5554
5555 elsets_list[elset_name] = elements;
5556 }
5557
5558 goto cont;
5559 }
5560 else if (line.compare(0, 5, "*NSET") == 0)
5561 {
5562 // Skip nodesets; we have no use for them
5563 while (std::getline(input_stream, line))
5564 {
5565 if (line[0] == '*')
5566 goto cont;
5567 }
5568 }
5569 else if (line.compare(0, 14, "*SOLID SECTION") == 0)
5570 {
5571 // The ELSET name, which describes a section for particular
5572 // material
5573 const std::string elset_key = "ELSET=";
5574 const std::size_t elset_start =
5575 line.find("ELSET=") + elset_key.size();
5576 const std::size_t elset_end = line.find(',', elset_start + 1);
5577 const std::string elset_name =
5578 line.substr(elset_start, elset_end - elset_start);
5579
5580 // Solid material definition.
5581 // We assume that material id is taken from material name,
5582 // eg. "Material-1" -> ID=1
5583 const std::string material_key = "MATERIAL=";
5584 const std::size_t last_equal =
5585 line.find("MATERIAL=") + material_key.size();
5586 const std::size_t material_id_start = line.find('-', last_equal);
5587 int material_id = 0;
5588 from_string(material_id,
5589 line.substr(material_id_start + 1),
5590 std::dec);
5591
5592 // Assign material id to cells
5593 const std::vector<int> &elset_cells = elsets_list[elset_name];
5594 for (const int elset_cell : elset_cells)
5595 {
5596 const int cell_id = elset_cell - 1;
5597 cell_list[cell_id][0] = material_id;
5598 }
5599 }
5600 // Note: All other lines / entries are ignored
5601 }
5602 }
5603
5604 template <int dim, int spacedim>
5605 std::vector<double>
5606 Abaqus_to_UCD<dim, spacedim>::get_global_node_numbers(
5607 const int face_cell_no,
5608 const int face_cell_face_no) const
5609 {
5610 std::vector<double> quad_node_list(GeometryInfo<dim>::vertices_per_face);
5611
5612 // Given the indexing below, face_cell_no-1 must be a valid index:
5613 Assert((face_cell_no >= 1) &&
5614 (static_cast<typename decltype(cell_list)::size_type>(
5615 face_cell_no) <= cell_list.size()),
5617
5618 // These orderings were reverse engineered by hand and may
5619 // conceivably be erroneous.
5620 // TODO: Currently one test (2d unstructured mesh) in the test
5621 // suite fails, presumably because of an ordering issue.
5622 if constexpr (dim == 2)
5623 {
5624 if (face_cell_face_no == 1)
5625 {
5626 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5627 quad_node_list[1] = cell_list[face_cell_no - 1][2];
5628 }
5629 else if (face_cell_face_no == 2)
5630 {
5631 quad_node_list[0] = cell_list[face_cell_no - 1][2];
5632 quad_node_list[1] = cell_list[face_cell_no - 1][3];
5633 }
5634 else if (face_cell_face_no == 3)
5635 {
5636 quad_node_list[0] = cell_list[face_cell_no - 1][3];
5637 quad_node_list[1] = cell_list[face_cell_no - 1][4];
5638 }
5639 else if (face_cell_face_no == 4)
5640 {
5641 quad_node_list[0] = cell_list[face_cell_no - 1][4];
5642 quad_node_list[1] = cell_list[face_cell_no - 1][1];
5643 }
5644 else
5645 {
5646 AssertThrow(face_cell_face_no <= 4,
5647 ExcMessage("Invalid face number in 2d"));
5648 }
5649 }
5650 else if constexpr (dim == 3)
5651 {
5652 if (face_cell_face_no == 1)
5653 {
5654 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5655 quad_node_list[1] = cell_list[face_cell_no - 1][4];
5656 quad_node_list[2] = cell_list[face_cell_no - 1][3];
5657 quad_node_list[3] = cell_list[face_cell_no - 1][2];
5658 }
5659 else if (face_cell_face_no == 2)
5660 {
5661 quad_node_list[0] = cell_list[face_cell_no - 1][5];
5662 quad_node_list[1] = cell_list[face_cell_no - 1][8];
5663 quad_node_list[2] = cell_list[face_cell_no - 1][7];
5664 quad_node_list[3] = cell_list[face_cell_no - 1][6];
5665 }
5666 else if (face_cell_face_no == 3)
5667 {
5668 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5669 quad_node_list[1] = cell_list[face_cell_no - 1][2];
5670 quad_node_list[2] = cell_list[face_cell_no - 1][6];
5671 quad_node_list[3] = cell_list[face_cell_no - 1][5];
5672 }
5673 else if (face_cell_face_no == 4)
5674 {
5675 quad_node_list[0] = cell_list[face_cell_no - 1][2];
5676 quad_node_list[1] = cell_list[face_cell_no - 1][3];
5677 quad_node_list[2] = cell_list[face_cell_no - 1][7];
5678 quad_node_list[3] = cell_list[face_cell_no - 1][6];
5679 }
5680 else if (face_cell_face_no == 5)
5681 {
5682 quad_node_list[0] = cell_list[face_cell_no - 1][3];
5683 quad_node_list[1] = cell_list[face_cell_no - 1][4];
5684 quad_node_list[2] = cell_list[face_cell_no - 1][8];
5685 quad_node_list[3] = cell_list[face_cell_no - 1][7];
5686 }
5687 else if (face_cell_face_no == 6)
5688 {
5689 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5690 quad_node_list[1] = cell_list[face_cell_no - 1][5];
5691 quad_node_list[2] = cell_list[face_cell_no - 1][8];
5692 quad_node_list[3] = cell_list[face_cell_no - 1][4];
5693 }
5694 else
5695 {
5696 AssertThrow(face_cell_no <= 6,
5697 ExcMessage("Invalid face number in 3d"));
5698 }
5699 }
5700 else
5701 {
5702 AssertThrow(dim == 2 || dim == 3, ExcNotImplemented());
5703 }
5704
5705 return quad_node_list;
5706 }
5707
5708 template <int dim, int spacedim>
5709 void
5710 Abaqus_to_UCD<dim, spacedim>::write_out_avs_ucd(std::ostream &output) const
5711 {
5712 // References:
5713 // http://dealii.org/developer/doxygen/deal.II/structGeometryInfo.html
5714 // http://people.scs.fsu.edu/~burkardt/data/ucd/ucd.html
5715
5716 AssertThrow(output.fail() == false, ExcIO());
5717
5718 // save old formatting options
5719 const boost::io::ios_base_all_saver formatting_saver(output);
5720
5721 // Write out title - Note: No other commented text can be inserted below
5722 // the title in a UCD file
5723 output << "# Abaqus to UCD mesh conversion" << '\n';
5724 output << "# Mesh type: AVS UCD" << '\n';
5725
5726 // ========================================================
5727 // ASCII UCD File Format
5728 // The input file cannot contain blank lines or lines with leading blanks.
5729 // Comments, if present, must precede all data in the file.
5730 // Comments within the data will cause read errors.
5731 // The general order of the data is as follows:
5732 // 1. Numbers defining the overall structure, including the number of
5733 // nodes,
5734 // the number of cells, and the length of the vector of data associated
5735 // with the nodes, cells, and the model.
5736 // e.g. 1:
5737 // <num_nodes> <num_cells> <num_ndata> <num_cdata> <num_mdata>
5738 // e.g. 2:
5739 // n_elements = n_hex_cells + n_bc_quads + n_quad_cells +
5740 // n_bc_edges outfile.write(str(n_nodes) + " " + str(n_elements) +
5741 // " 0 0 0\n")
5742 // 2. For each node, its node id and the coordinates of that node in
5743 // space.
5744 // Node-ids must be integers, but any number including non sequential
5745 // numbers can be used. Mid-edge nodes are treated like any other node.
5746 // 3. For each cell: its cell-id, material, cell type (hexahedral,
5747 // pyramid,
5748 // etc.), and the list of node-ids that correspond to each of the
5749 // cell's vertices. The below table specifies the different cell types
5750 // and the keyword used to represent them in the file.
5751
5752 // Write out header
5753 output << node_list.size() << "\t" << (cell_list.size() + face_list.size())
5754 << "\t0\t0\t0" << '\n';
5755
5756 // Write out node numbers
5757 // Loop over all nodes
5758 output.setf(std::ios::scientific, std::ios::floatfield);
5759 for (const auto &node : node_list)
5760 {
5761 // Node number. It is stored as a double, but it's an integer, so we can
5762 // just output it as that:
5763 output << static_cast<int>(node[0]) << "\t";
5764
5765 // Node coordinates. For these, we need to set the precision and width
5766 // so that we catch as much precision as possible:
5767 for (unsigned int jj = 1; jj < spacedim + 1; ++jj)
5768 {
5769 output.width(20);
5770 output.precision(16);
5771
5772 // invoke tolerance -> set points close to zero equal to zero
5773 if (std::abs(node[jj]) > tolerance)
5774 output << static_cast<double>(node[jj]) << "\t";
5775 else
5776 output << 0.0 << "\t";
5777 }
5778 if (spacedim == 2)
5779 output << 0.0 << "\t";
5780
5781 output << '\n';
5782 }
5783 output.unsetf(std::ios::floatfield);
5784
5785 // Write out cell node numbers
5786 for (unsigned int ii = 0; ii < cell_list.size(); ++ii)
5787 {
5788 output << ii + 1 << "\t" << cell_list[ii][0] << "\t"
5789 << (dim == 2 ? "quad" : "hex") << "\t";
5790 for (unsigned int jj = 1; jj < GeometryInfo<dim>::vertices_per_cell + 1;
5791 ++jj)
5792 output << cell_list[ii][jj] << "\t";
5793
5794 output << '\n';
5795 }
5796
5797 // Write out quad node numbers
5798 for (unsigned int ii = 0; ii < face_list.size(); ++ii)
5799 {
5800 output << ii + 1 << "\t" << face_list[ii][0] << "\t"
5801 << (dim == 2 ? "line" : "quad") << "\t";
5802 for (unsigned int jj = 1; jj < GeometryInfo<dim>::vertices_per_face + 1;
5803 ++jj)
5804 output << face_list[ii][jj] << "\t";
5805
5806 output << '\n';
5807 }
5808
5809 output << std::flush;
5810 }
5811} // namespace
5812
5813
5814// explicit instantiations
5815#include "grid/grid_in.inst"
5816
*  *  for(const auto &cell :triangulation.active_cell_iterators())
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
void read_vtk(std::istream &in)
Definition grid_in.cc:164
GridIn()
Definition grid_in.cc:138
static void skip_empty_lines(std::istream &in)
Definition grid_in.cc:4814
void read_partitioned_msh(const std::string &file_prefix, const std::string &file_suffix="msh")
Definition grid_in.cc:3175
void read_assimp(const std::string &filename, const unsigned int mesh_index=numbers::invalid_unsigned_int, const bool remove_duplicates=true, const double tol=1e-12, const bool ignore_unsupported_element_types=true)
Definition grid_in.cc:4028
void read_ugrid(std::istream &in)
Definition grid_in.cc:4183
void read_abaqus(std::istream &in, const bool apply_all_indicators_to_manifolds=false)
Definition grid_in.cc:1354
static std::string default_suffix(const Format format)
Definition grid_in.cc:5122
void read_xda(std::istream &in)
Definition grid_in.cc:1571
void read_comsol_mphtxt(std::istream &in)
Definition grid_in.cc:1635
static void skip_comment_lines(std::istream &in, const char comment_start)
Definition grid_in.cc:4844
void attach_triangulation(Triangulation< dim, spacedim > &tria)
Definition grid_in.cc:155
const std::map< std::string, Vector< double > > & get_cell_data() const
Definition grid_in.cc:766
void read_msh(std::istream &in)
Definition grid_in.cc:2199
static Format parse_format(const std::string &format_name)
Definition grid_in.cc:5159
void read_vtu(std::istream &in)
Definition grid_in.cc:773
void read_tecplot(std::istream &in)
Definition grid_in.cc:4019
ExodusIIData read_exodusii(const std::string &filename, const bool apply_all_indicators_to_manifolds=false)
Definition grid_in.cc:4649
void read_dbmesh(std::istream &in)
Definition grid_in.cc:1410
void read_ucd(std::istream &in, const bool apply_all_indicators_to_manifolds=false)
Definition grid_in.cc:1100
void read(std::istream &in, Format format=Default)
Definition grid_in.cc:5051
static void debug_output_grid(const std::vector< CellData< dim > > &cells, const std::vector< Point< spacedim > > &vertices, std::ostream &out)
Definition grid_in.cc:4870
static std::string get_format_names()
Definition grid_in.cc:5219
void read_unv(std::istream &in)
Definition grid_in.cc:800
static void parse_tecplot_header(std::string &header, std::vector< unsigned int > &tecplot2deal, unsigned int &n_vars, unsigned int &n_vertices, unsigned int &n_cells, std::vector< unsigned int > &IJK, bool &structured, bool &blocked)
Definition grid_in.cc:3551
Definition point.h:111
constexpr ReferenceCell< dim - 1 > face_reference_cell(const unsigned int face_index) const
constexpr unsigned int n_faces() const
unsigned int face_to_cell_vertices(const unsigned int face, const unsigned int vertex, const types::geometric_orientation face_orientation) const
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
constexpr iterator end() noexcept
constexpr size_type size() const noexcept
constexpr iterator begin() noexcept
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
unsigned int vertex_indices[2]
static ::ExceptionBase & ExcIO()
static ::ExceptionBase & ExcFileNotOpen(std::string arg1)
static ::ExceptionBase & ExcNeedsAssimp()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
static ::ExceptionBase & ExcNeedsGMSHAPI()
static ::ExceptionBase & ExcNeedsExodusII()
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertThrowExodusII(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
static ::ExceptionBase & ExcInvalidState()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
void consistently_order_cells(std::vector< CellData< dim > > &cells)
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
void reference_cell(Triangulation< dim, spacedim > &tria, const ReferenceCell< dim > &reference_cell)
void delete_unused_vertices(std::vector< Point< spacedim > > &vertices, std::vector< CellData< dim > > &cells, SubCellData &subcelldata)
std::size_t invert_cells_with_negative_measure(const std::vector< Point< spacedim > > &all_vertices, std::vector< CellData< dim > > &cells)
void delete_duplicated_vertices(std::vector< Point< spacedim > > &all_vertices, std::vector< CellData< dim > > &cells, SubCellData &subcelldata, std::vector< unsigned int > &considered_vertices, const double tol=1e-12)
void to_value(const std::string &s, T &t)
Definition patterns.h:2458
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
constexpr ReferenceCell< 3 > Hexahedron
constexpr ReferenceCell< 2 > Quadrilateral
constexpr ReferenceCell< 1 > Line
constexpr ReferenceCell< 2 > Triangle
constexpr ReferenceCell< 3 > Tetrahedron
constexpr ReferenceCell< 3 > Pyramid
constexpr ReferenceCell< 3 > Wedge
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
std::pair< int, unsigned int > get_integer_at_position(const std::string &name, const unsigned int position)
Definition utilities.cc:831
std::vector< unsigned char > decode_base64(const std::string &base64_input)
Definition utilities.cc:438
std::vector< std::string > break_text_into_lines(const std::string &original_text, const unsigned int width, const char delimiter=' ')
Definition utilities.cc:749
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition utilities.cc:464
bool match_at_string_start(const std::string &name, const std::string &pattern)
Definition utilities.cc:816
std::string decompress(const std::string &compressed_input)
Definition utilities.cc:403
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
constexpr types::boundary_id internal_face_boundary_id
Definition types.h:319
constexpr types::boundary_id invalid_boundary_id
Definition types.h:299
constexpr types::manifold_id flat_manifold_id
Definition types.h:332
constexpr types::material_id invalid_material_id
Definition types.h:284
constexpr types::geometric_orientation default_geometric_orientation
Definition types.h:342
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int material_id
Definition types.h:182
std_cxx26::inplace_vector< unsigned int, ReferenceCells::max_n_vertices< structdim >()> vertices
Definition cell_data.h:84
types::material_id material_id
Definition cell_data.h:103
static std_cxx20::ranges::iota_view< unsigned int, unsigned int > vertex_indices()
std::vector< std::vector< int > > id_to_sideset_ids
Definition grid_in.h:746
std::vector< CellData< 2 > > boundary_quads
Definition cell_data.h:247
bool check_consistency(const unsigned int dim) const
std::vector< CellData< 1 > > boundary_lines
Definition cell_data.h:231
std_cxx26::inplace_vector< std::pair< unsigned int, types::boundary_id >, ReferenceCells::max_n_faces< dim >()> boundary_ids
std::vector< std::vector< CellData< dim > > > cell_infos
std::vector<::CellData< dim > > coarse_cells
std::vector< Point< spacedim > > coarse_cell_vertices
std::vector< types::coarse_cell_id > coarse_cell_index_to_coarse_cell_id