deal.II version 9.8.0
\(\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 =
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 (dim == spacedim)
131
132 if (is_only_hypercube)
134 }
135} // namespace
136
137template <int dim, int spacedim>
139 : tria(nullptr, typeid(*this).name())
141{}
142
143
144
145template <int dim, int spacedim>
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 (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 (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 (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 (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 (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{
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),
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 (((cell_type == "line") && (dim == 1)) ||
1168 ((cell_type == "quad") && (dim == 2)) ||
1169 ((cell_type == "hex") && (dim == 3)))
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,
1204 cells.back().vertices[i]));
1205
1206 cells.back().vertices[i] = numbers::invalid_unsigned_int;
1207 }
1208 }
1209 else if ((cell_type == "line") && ((dim == 2) || (dim == 3)))
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 ((cell_type == "quad") && (dim == 3))
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;
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 }
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 }
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 }
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 }
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.
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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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
2124 face_vertex_indices(face->n_vertices());
2125 for (unsigned int v = 0; v < face->n_vertices(); ++v)
2126 face_vertex_indices[v] = face->vertex_index(v);
2127 std::sort(face_vertex_indices.begin(),
2128 face_vertex_indices.end());
2129
2130 // See if we can find a face with these indices:
2131 const auto p = std::lower_bound(
2132 subcelldata.boundary_quads.begin(),
2133 subcelldata.boundary_quads.end(),
2134 face_vertex_indices,
2135 [](const CellData<2> &a,
2136 const auto &face_vertex_indices) -> bool {
2137 return std::lexicographical_compare(
2138 a.vertices.begin(),
2139 a.vertices.end(),
2140 face_vertex_indices.begin(),
2141 face_vertex_indices.end());
2142 });
2143
2144 if ((p != subcelldata.boundary_quads.end()) &&
2145 (p->vertices == face_vertex_indices))
2146 {
2147 face->set_boundary_id(p->boundary_id);
2148 }
2149
2150
2151 // Now do the same for the edges
2152 for (unsigned int e = 0; e < face->n_lines(); ++e)
2153 {
2154 const auto edge = face->line(e);
2155
2156 std::array<unsigned int, 2> edge_vertex_indices = {
2157 {edge->vertex_index(0), edge->vertex_index(1)}};
2158 if (edge_vertex_indices[0] > edge_vertex_indices[1])
2159 std::swap(edge_vertex_indices[0],
2160 edge_vertex_indices[1]);
2161
2162 // See if we can find an edge with these indices:
2163 const auto p =
2164 std::lower_bound(subcelldata.boundary_lines.begin(),
2165 subcelldata.boundary_lines.end(),
2166 edge_vertex_indices,
2167 [](const CellData<1> &a,
2168 const std::array<unsigned int, 2>
2169 &edge_vertex_indices) -> bool {
2170 return std::lexicographical_compare(
2171 a.vertices.begin(),
2172 a.vertices.end(),
2173 edge_vertex_indices.begin(),
2174 edge_vertex_indices.end());
2175 });
2176
2177 if ((p != subcelldata.boundary_lines.end()) &&
2178 (p->vertices[0] == edge_vertex_indices[0]) &&
2179 (p->vertices[1] == edge_vertex_indices[1]))
2180 {
2181 edge->set_boundary_id(p->boundary_id);
2182 }
2183 }
2184 }
2185 }
2186 }
2187}
2188
2189
2190
2191template <int dim, int spacedim>
2192void
2193GridIn<dim, spacedim>::read_msh(std::istream &input_stream)
2194{
2195 Assert(tria != nullptr, ExcNoTriangulationSelected());
2196 AssertThrow(input_stream.fail() == false, ExcIO());
2197
2198 unsigned int n_vertices;
2199 unsigned int n_cells;
2200 unsigned int dummy;
2201 std::string line;
2202 // This array stores maps from the 'entities' to the 'physical tags' for
2203 // points, curves, surfaces and volumes. We use this information later to
2204 // assign boundary ids.
2205 std::array<std::map<int, int>, 4> tag_maps;
2206
2207 // contain the content of the file stripped of the comments
2208 std::string stripped_file;
2209
2210 // Comments can be included by mesh generating software and must be deleted,
2211 // a string is filed with the content of the file stripped of the comments
2212 while (std::getline(input_stream, line))
2213 {
2214 if (line == "@f$Comments")
2215 {
2216 while (std::getline(input_stream, line))
2217 {
2218 if (line == "@f$EndComments")
2219 {
2220 break;
2221 }
2222 }
2223 continue;
2224 }
2225 stripped_file += line + '\n';
2226 }
2227
2228 // Restart reading the file normally since it has been stripped of comments
2229 std::istringstream in(stripped_file);
2230
2231 in >> line;
2232
2233 // first determine file format
2234 unsigned int gmsh_file_format = 0;
2235 if (line == "@f$NOD")
2236 gmsh_file_format = 10;
2237 else if (line == "@f$MeshFormat")
2238 gmsh_file_format = 20;
2239 else
2240 AssertThrow(false, ExcInvalidGMSHInput(line));
2241
2242 // if file format is 2.0 or greater then we also have to read the rest of
2243 // the header
2244 if (gmsh_file_format == 20)
2245 {
2246 double version;
2247 unsigned int file_type, data_size;
2248
2249 in >> version >> file_type >> data_size;
2250
2251 AssertThrow((version >= 2.0) && (version <= 4.1), ExcNotImplemented());
2252 gmsh_file_format = static_cast<unsigned int>(version * 10);
2253
2254 AssertThrow(file_type == 0, ExcNotImplemented());
2255 AssertThrow(data_size == sizeof(double), ExcNotImplemented());
2256
2257 // read the end of the header and the first line of the nodes
2258 // description to synch ourselves with the format 1 handling above
2259 in >> line;
2260 AssertThrow(line == "@f$EndMeshFormat", ExcInvalidGMSHInput(line));
2261
2262 in >> line;
2263 // if the next block is of kind @f$PhysicalNames, ignore it
2264 if (line == "@f$PhysicalNames")
2265 {
2266 do
2267 {
2268 in >> line;
2269 }
2270 while (line != "@f$EndPhysicalNames");
2271 in >> line;
2272 }
2273
2274 // if the next block is of kind @f$Entities, parse it
2275 if (line == "@f$Entities")
2276 {
2277 unsigned long n_points, n_curves, n_surfaces, n_volumes;
2278
2279 in >> n_points >> n_curves >> n_surfaces >> n_volumes;
2280 for (unsigned int i = 0; i < n_points; ++i)
2281 {
2282 // parse point ids
2283 int tag;
2284 unsigned int n_physicals;
2285 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2286 box_max_z;
2287
2288 // we only care for 'tag' as key for tag_maps[0]
2289 if (gmsh_file_format > 40)
2290 {
2291 in >> tag >> box_min_x >> box_min_y >> box_min_z >>
2292 n_physicals;
2293 box_max_x = box_min_x;
2294 box_max_y = box_min_y;
2295 box_max_z = box_min_z;
2296 }
2297 else
2298 {
2299 in >> tag >> box_min_x >> box_min_y >> box_min_z >>
2300 box_max_x >> box_max_y >> box_max_z >> n_physicals;
2301 }
2302 // if there is a physical tag, we will use it as boundary id
2303 // below
2304 AssertThrow(n_physicals < 2,
2305 ExcMessage("More than one tag is not supported!"));
2306 // if there is no physical tag, use 0 as default
2307 int physical_tag = 0;
2308 for (unsigned int j = 0; j < n_physicals; ++j)
2309 in >> physical_tag;
2310 tag_maps[0][tag] = physical_tag;
2311 }
2312 for (unsigned int i = 0; i < n_curves; ++i)
2313 {
2314 // parse curve ids
2315 int tag;
2316 unsigned int n_physicals;
2317 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2318 box_max_z;
2319
2320 // we only care for 'tag' as key for tag_maps[1]
2321 in >> tag >> box_min_x >> box_min_y >> box_min_z >> box_max_x >>
2322 box_max_y >> box_max_z >> n_physicals;
2323 // if there is a physical tag, we will use it as boundary id
2324 // below
2325 AssertThrow(n_physicals < 2,
2326 ExcMessage("More than one tag is not supported!"));
2327 // if there is no physical tag, use 0 as default
2328 int physical_tag = 0;
2329 for (unsigned int j = 0; j < n_physicals; ++j)
2330 in >> physical_tag;
2331 tag_maps[1][tag] = physical_tag;
2332 // we don't care about the points associated to a curve, but
2333 // have to parse them anyway because their format is
2334 // unstructured
2335 in >> n_points;
2336 for (unsigned int j = 0; j < n_points; ++j)
2337 in >> tag;
2338 }
2339
2340 for (unsigned int i = 0; i < n_surfaces; ++i)
2341 {
2342 // parse surface ids
2343 int tag;
2344 unsigned int n_physicals;
2345 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2346 box_max_z;
2347
2348 // we only care for 'tag' as key for tag_maps[2]
2349 in >> tag >> box_min_x >> box_min_y >> box_min_z >> box_max_x >>
2350 box_max_y >> box_max_z >> n_physicals;
2351 // if there is a physical tag, we will use it as boundary id
2352 // below
2353 AssertThrow(n_physicals < 2,
2354 ExcMessage("More than one tag is not supported!"));
2355 // if there is no physical tag, use 0 as default
2356 int physical_tag = 0;
2357 for (unsigned int j = 0; j < n_physicals; ++j)
2358 in >> physical_tag;
2359 tag_maps[2][tag] = physical_tag;
2360 // we don't care about the curves associated to a surface, but
2361 // have to parse them anyway because their format is
2362 // unstructured
2363 in >> n_curves;
2364 for (unsigned int j = 0; j < n_curves; ++j)
2365 in >> tag;
2366 }
2367 for (unsigned int i = 0; i < n_volumes; ++i)
2368 {
2369 // parse volume ids
2370 int tag;
2371 unsigned int n_physicals;
2372 double box_min_x, box_min_y, box_min_z, box_max_x, box_max_y,
2373 box_max_z;
2374
2375 // we only care for 'tag' as key for tag_maps[3]
2376 in >> tag >> box_min_x >> box_min_y >> box_min_z >> box_max_x >>
2377 box_max_y >> box_max_z >> n_physicals;
2378 // if there is a physical tag, we will use it as boundary id
2379 // below
2380 AssertThrow(n_physicals < 2,
2381 ExcMessage("More than one tag is not supported!"));
2382 // if there is no physical tag, use 0 as default
2383 int physical_tag = 0;
2384 for (unsigned int j = 0; j < n_physicals; ++j)
2385 in >> physical_tag;
2386 tag_maps[3][tag] = physical_tag;
2387 // we don't care about the surfaces associated to a volume, but
2388 // have to parse them anyway because their format is
2389 // unstructured
2390 in >> n_surfaces;
2391 for (unsigned int j = 0; j < n_surfaces; ++j)
2392 in >> tag;
2393 }
2394 in >> line;
2395 AssertThrow(line == "@f$EndEntities", ExcInvalidGMSHInput(line));
2396 in >> line;
2397 }
2398
2399 // if the next block is of kind @f$PartitionedEntities, ignore it
2400 if (line == "@f$PartitionedEntities")
2401 {
2402 do
2403 {
2404 in >> line;
2405 }
2406 while (line != "@f$EndPartitionedEntities");
2407 in >> line;
2408 }
2409
2410 // but the next thing should,
2411 // in any case, be the list of
2412 // nodes:
2413 AssertThrow(line == "@f$Nodes", ExcInvalidGMSHInput(line));
2414 }
2415
2416 // now read the nodes list
2417 int n_entity_blocks = 1;
2418 if (gmsh_file_format > 40)
2419 {
2420 int min_node_tag;
2421 int max_node_tag;
2422 in >> n_entity_blocks >> n_vertices >> min_node_tag >> max_node_tag;
2423 }
2424 else if (gmsh_file_format == 40)
2425 {
2426 in >> n_entity_blocks >> n_vertices;
2427 }
2428 else
2429 in >> n_vertices;
2430 std::vector<Point<spacedim>> vertices(n_vertices);
2431 // set up mapping between numbering
2432 // in msh-file (nod) and in the
2433 // vertices vector
2434 std::map<int, int> vertex_indices;
2435
2436 {
2437 unsigned int global_vertex = 0;
2438 for (int entity_block = 0; entity_block < n_entity_blocks; ++entity_block)
2439 {
2440 int parametric;
2441 unsigned long numNodes;
2442
2443 if (gmsh_file_format < 40)
2444 {
2445 numNodes = n_vertices;
2446 parametric = 0;
2447 }
2448 else
2449 {
2450 // for gmsh_file_format 4.1 the order of tag and dim is reversed,
2451 // but we are ignoring both anyway.
2452 int tagEntity, dimEntity;
2453 in >> tagEntity >> dimEntity >> parametric >> numNodes;
2454 }
2455
2456 std::vector<int> vertex_numbers;
2457 int vertex_number;
2458 if (gmsh_file_format > 40)
2459 for (unsigned long vertex_per_entity = 0;
2460 vertex_per_entity < numNodes;
2461 ++vertex_per_entity)
2462 {
2463 in >> vertex_number;
2464 vertex_numbers.push_back(vertex_number);
2465 }
2466
2467 for (unsigned long vertex_per_entity = 0; vertex_per_entity < numNodes;
2468 ++vertex_per_entity, ++global_vertex)
2469 {
2470 int vertex_number;
2471 double x[3];
2472
2473 // read vertex
2474 if (gmsh_file_format > 40)
2475 {
2476 vertex_number = vertex_numbers[vertex_per_entity];
2477 in >> x[0] >> x[1] >> x[2];
2478 }
2479 else
2480 in >> vertex_number >> x[0] >> x[1] >> x[2];
2481
2483 global_vertex < n_vertices,
2484 ExcMessage(
2485 "The Gmsh file lists more nodes than were declared in the "
2486 "header of the @f$Nodes section."));
2487
2488 for (unsigned int d = 0; d < spacedim; ++d)
2489 vertices[global_vertex][d] = x[d];
2490 // store mapping
2491 vertex_indices[vertex_number] = global_vertex;
2492
2493 // ignore parametric coordinates
2494 if (parametric != 0)
2495 {
2496 double u = 0.;
2497 double v = 0.;
2498 in >> u >> v;
2499 (void)u;
2500 (void)v;
2501 }
2502 }
2503 }
2504 AssertDimension(global_vertex, n_vertices);
2505 }
2506
2507 // Assert we reached the end of the block
2508 in >> line;
2509 const std::array<std::string, 2> end_nodes_marker{{"@f$ENDNOD", "@f$EndNodes"}};
2510 AssertThrow(line == end_nodes_marker[gmsh_file_format == 10 ? 0 : 1],
2511 ExcInvalidGMSHInput(line));
2512
2513 // Now read in next bit
2514 in >> line;
2515 const std::array<std::string, 2> begin_elements_marker{{"@f$ELM", "@f$Elements"}};
2516 AssertThrow(line == begin_elements_marker[gmsh_file_format == 10 ? 0 : 1],
2517 ExcInvalidGMSHInput(line));
2518
2519 // now read the cell list
2520 if (gmsh_file_format > 40)
2521 {
2522 int min_node_tag;
2523 int max_node_tag;
2524 in >> n_entity_blocks >> n_cells >> min_node_tag >> max_node_tag;
2525 }
2526 else if (gmsh_file_format == 40)
2527 {
2528 in >> n_entity_blocks >> n_cells;
2529 }
2530 else
2531 {
2532 n_entity_blocks = 1;
2533 in >> n_cells;
2534 }
2535
2536 // set up array of cells and subcells (faces). In 1d, there is currently no
2537 // standard way in deal.II to pass boundary indicators attached to
2538 // individual vertices, so do this by hand via the boundary_ids_1d array
2539 std::vector<CellData<dim>> cells;
2540 SubCellData subcelldata;
2541 std::map<unsigned int, types::boundary_id> boundary_ids_1d;
2542
2543 // Track the number of times each vertex is used in 1D. This determines
2544 // whether or not we can assign a boundary id to a vertex. This is necessary
2545 // because sometimes gmsh saves internal vertices in the @f$ELEM list in codim
2546 // 1 or codim 2.
2547 std::map<unsigned int, unsigned int> vertex_counts;
2548
2549 {
2550 constexpr std::array<unsigned int, 8> local_vertex_numbering = {
2551 {0, 1, 5, 4, 2, 3, 7, 6}};
2552 unsigned int global_cell = 0;
2553 for (int entity_block = 0; entity_block < n_entity_blocks; ++entity_block)
2554 {
2555 unsigned int material_id;
2556 unsigned long numElements;
2557 int cell_type;
2558
2559 if (gmsh_file_format < 40)
2560 {
2561 material_id = 0;
2562 cell_type = 0;
2563 numElements = n_cells;
2564 }
2565 else if (gmsh_file_format == 40)
2566 {
2567 int tagEntity, dimEntity;
2568 in >> tagEntity >> dimEntity >> cell_type >> numElements;
2569 material_id = tag_maps[dimEntity][tagEntity];
2570 }
2571 else
2572 {
2573 // for gmsh_file_format 4.1 the order of tag and dim is reversed,
2574 int tagEntity, dimEntity;
2575 in >> dimEntity >> tagEntity >> cell_type >> numElements;
2576 material_id = tag_maps[dimEntity][tagEntity];
2577 }
2578
2579 for (unsigned int cell_per_entity = 0; cell_per_entity < numElements;
2580 ++cell_per_entity, ++global_cell)
2581 {
2582 // note that since in the input
2583 // file we found the number of
2584 // cells at the top, there
2585 // should still be input here,
2586 // so check this:
2587 AssertThrow(in.fail() == false, ExcIO());
2588
2589 unsigned int nod_num;
2590
2591 /*
2592 For file format version 1, the format of each cell is as
2593 follows: elm-number elm-type reg-phys reg-elem number-of-nodes
2594 node-number-list
2595
2596 However, for version 2, the format reads like this:
2597 elm-number elm-type number-of-tags < tag > ...
2598 node-number-list
2599
2600 For version 4, we have:
2601 tag(int) numVert(int) ...
2602
2603 In the following, we will ignore the element number (we simply
2604 enumerate them in the order in which we read them, and we will
2605 take reg-phys (version 1) or the first tag (version 2, if any
2606 tag is given at all) as material id. For version 4, we already
2607 read the material and the cell type in above.
2608 */
2609
2610 unsigned int elm_number = 0;
2611 if (gmsh_file_format < 40)
2612 {
2613 in >> elm_number // ELM-NUMBER
2614 >> cell_type; // ELM-TYPE
2615 }
2616
2617 if (gmsh_file_format < 20)
2618 {
2619 in >> material_id // REG-PHYS
2620 >> dummy // reg_elm
2621 >> nod_num;
2622 }
2623 else if (gmsh_file_format < 40)
2624 {
2625 // read the tags; ignore all but the first one which we will
2626 // interpret as the material_id (for cells) or boundary_id
2627 // (for faces)
2628 unsigned int n_tags;
2629 in >> n_tags;
2630 if (n_tags > 0)
2631 in >> material_id;
2632 else
2633 material_id = 0;
2634
2635 for (unsigned int i = 1; i < n_tags; ++i)
2636 in >> dummy;
2637
2638 if (cell_type == 1) // line
2639 nod_num = 2;
2640 else if (cell_type == 2) // tri
2641 nod_num = 3;
2642 else if (cell_type == 3) // quad
2643 nod_num = 4;
2644 else if (cell_type == 4) // tet
2645 nod_num = 4;
2646 else if (cell_type == 5) // hex
2647 nod_num = 8;
2648 }
2649 else // file format version 4.0 and later
2650 {
2651 // ignore tag
2652 int tag;
2653 in >> tag;
2654
2655 if (cell_type == 1) // line
2656 nod_num = 2;
2657 else if (cell_type == 2) // tri
2658 nod_num = 3;
2659 else if (cell_type == 3) // quad
2660 nod_num = 4;
2661 else if (cell_type == 4) // tet
2662 nod_num = 4;
2663 else if (cell_type == 5) // hex
2664 nod_num = 8;
2665 }
2666
2667
2668 /* `ELM-TYPE'
2669 defines the geometrical type of the N-th element:
2670 `1'
2671 Line (2 nodes, 1 edge).
2672
2673 `2'
2674 Triangle (3 nodes, 3 edges).
2675
2676 `3'
2677 Quadrangle (4 nodes, 4 edges).
2678
2679 `4'
2680 Tetrahedron (4 nodes, 6 edges, 6 faces).
2681
2682 `5'
2683 Hexahedron (8 nodes, 12 edges, 6 faces).
2684
2685 `15'
2686 Point (1 node).
2687 */
2688
2689 if (((cell_type == 1) && (dim == 1)) || // a line in 1d
2690 ((cell_type == 2) && (dim == 2)) || // a triangle in 2d
2691 ((cell_type == 3) && (dim == 2)) || // a quadrilateral in 2d
2692 ((cell_type == 4) && (dim == 3)) || // a tet in 3d
2693 ((cell_type == 5) && (dim == 3))) // a hex in 3d
2694 // found a cell
2695 {
2696 unsigned int vertices_per_cell = 0;
2697 if (cell_type == 1) // line
2699 else if (cell_type == 2) // tri
2701 else if (cell_type == 3) // quad
2703 else if (cell_type == 4) // tet
2705 else if (cell_type == 5) // hex
2707
2708 AssertThrow(nod_num == vertices_per_cell,
2709 ExcMessage(
2710 "Number of nodes does not coincide with the "
2711 "number required for this object"));
2712
2713 // allocate and read indices
2714 cells.emplace_back();
2715 CellData<dim> &cell = cells.back();
2717 for (unsigned int i = 0; i < vertices_per_cell; ++i)
2718 {
2719 // hypercube cells need to be reordered
2720 if (vertices_per_cell ==
2722 in >> cell.vertices[dim == 3 ?
2723 local_vertex_numbering[i] :
2725 else
2726 in >> cell.vertices[i];
2727 }
2728
2729 // to make sure that the cast won't fail
2730 AssertThrow(material_id <=
2731 std::numeric_limits<types::material_id>::max(),
2733 material_id,
2734 0,
2735 std::numeric_limits<types::material_id>::max()));
2736 // we use only material_ids in the range from 0 to
2737 // numbers::invalid_material_id-1
2739 ExcIndexRange(material_id,
2740 0,
2742
2743 cell.material_id = material_id;
2744
2745 // transform from gmsh to consecutive numbering
2746 for (unsigned int i = 0; i < vertices_per_cell; ++i)
2747 {
2748 AssertThrow(vertex_indices.find(cell.vertices[i]) !=
2749 vertex_indices.end(),
2750 ExcInvalidVertexIndexGmsh(cell_per_entity,
2751 elm_number,
2752 cell.vertices[i]));
2753
2754 const auto vertex = vertex_indices[cell.vertices[i]];
2755 if (dim == 1)
2756 vertex_counts[vertex] += 1u;
2757 cell.vertices[i] = vertex;
2758 }
2759 }
2760 else if ((cell_type == 1) &&
2761 ((dim == 2) || (dim == 3))) // a line in 2d or 3d
2762 // boundary info
2763 {
2764 subcelldata.boundary_lines.emplace_back();
2765 in >> subcelldata.boundary_lines.back().vertices[0] >>
2766 subcelldata.boundary_lines.back().vertices[1];
2767
2768 // to make sure that the cast won't fail
2769 AssertThrow(material_id <=
2770 std::numeric_limits<types::boundary_id>::max(),
2772 material_id,
2773 0,
2774 std::numeric_limits<types::boundary_id>::max()));
2775 // we use only boundary_ids in the range from 0 to
2776 // numbers::internal_face_boundary_id-1
2778 ExcIndexRange(material_id,
2779 0,
2781
2782 subcelldata.boundary_lines.back().boundary_id =
2783 static_cast<types::boundary_id>(material_id);
2784
2785 // transform from ucd to
2786 // consecutive numbering
2787 for (unsigned int &vertex :
2788 subcelldata.boundary_lines.back().vertices)
2789 if (vertex_indices.find(vertex) != vertex_indices.end())
2790 // vertex with this index exists
2791 vertex = vertex_indices[vertex];
2792 else
2793 {
2794 // no such vertex index
2795 AssertThrow(false,
2796 ExcInvalidVertexIndex(cell_per_entity,
2797 vertex));
2799 }
2800 }
2801 else if ((cell_type == 2 || cell_type == 3) &&
2802 (dim == 3)) // a triangle or a quad in 3d
2803 // boundary info
2804 {
2805 unsigned int vertices_per_cell = 0;
2806 // check cell type
2807 if (cell_type == 2) // tri
2809 else if (cell_type == 3) // quad
2811
2812 subcelldata.boundary_quads.emplace_back();
2813
2814 // resize vertices
2815 subcelldata.boundary_quads.back().vertices.resize(
2817 // for loop
2818 for (unsigned int i = 0; i < vertices_per_cell; ++i)
2819 in >> subcelldata.boundary_quads.back().vertices[i];
2820
2821 // to make sure that the cast won't fail
2822 AssertThrow(material_id <=
2823 std::numeric_limits<types::boundary_id>::max(),
2825 material_id,
2826 0,
2827 std::numeric_limits<types::boundary_id>::max()));
2828 // we use only boundary_ids in the range from 0 to
2829 // numbers::internal_face_boundary_id-1
2831 ExcIndexRange(material_id,
2832 0,
2834
2835 subcelldata.boundary_quads.back().boundary_id =
2836 static_cast<types::boundary_id>(material_id);
2837
2838 // transform from gmsh to
2839 // consecutive numbering
2840 for (unsigned int &vertex :
2841 subcelldata.boundary_quads.back().vertices)
2842 if (vertex_indices.find(vertex) != vertex_indices.end())
2843 // vertex with this index exists
2844 vertex = vertex_indices[vertex];
2845 else
2846 {
2847 // no such vertex index
2848 AssertThrow(false,
2849 ExcInvalidVertexIndex(cell_per_entity,
2850 vertex));
2852 }
2853 }
2854 else if (cell_type == 15)
2855 {
2856 // read the indices of nodes given
2857 unsigned int node_index = 0;
2858 if (gmsh_file_format < 20)
2859 {
2860 // For points (cell_type==15), we can only ever
2861 // list one node index.
2862 AssertThrow(nod_num == 1, ExcInternalError());
2863 in >> node_index;
2864 }
2865 else
2866 {
2867 in >> node_index;
2868 }
2869
2870 // we only care about boundary indicators assigned to
2871 // individual vertices in 1d (because otherwise the vertices
2872 // are not faces)
2873 if (dim == 1)
2874 boundary_ids_1d[vertex_indices[node_index]] = material_id;
2875 }
2876 else
2877 {
2878 AssertThrow(false, ExcGmshUnsupportedGeometry(cell_type));
2879 }
2880 }
2881 }
2882 AssertDimension(global_cell, n_cells);
2883 }
2884 // Assert that we reached the end of the block
2885 in >> line;
2886 const std::array<std::string, 2> end_elements_marker{
2887 {"@f$ENDELM", "@f$EndElements"}};
2888 AssertThrow(line == end_elements_marker[gmsh_file_format == 10 ? 0 : 1],
2889 ExcInvalidGMSHInput(line));
2890 AssertThrow(in.fail() == false, ExcIO());
2891
2892 // check that we actually read some cells.
2893 AssertThrow(cells.size() > 0,
2894 ExcGmshNoCellInformation(subcelldata.boundary_lines.size(),
2895 subcelldata.boundary_quads.size()));
2896
2897 // apply_grid_fixup_functions() may invalidate the vertex indices (since it
2898 // will delete duplicated or unused vertices). Get around this by storing
2899 // Points directly in that case so that the comparisons are valid.
2900 std::vector<std::pair<Point<spacedim>, types::boundary_id>> boundary_id_pairs;
2901 if (dim == 1)
2902 for (const auto &pair : vertex_counts)
2903 if (pair.second == 1u)
2904 boundary_id_pairs.emplace_back(vertices[pair.first],
2905 boundary_ids_1d[pair.first]);
2906
2907 apply_grid_fixup_functions(vertices, cells, subcelldata);
2908 tria->create_triangulation(vertices, cells, subcelldata);
2909
2910 // in 1d, we also have to attach boundary ids to vertices, which does not
2911 // currently work through the call above.
2912 if (dim == 1)
2913 assign_1d_boundary_ids(boundary_id_pairs, *tria);
2914}
2915
2916
2917
2918template <int dim, int spacedim>
2919void
2920GridIn<dim, spacedim>::read_msh(const std::string &fname)
2921{
2922#ifdef DEAL_II_GMSH_WITH_API
2923 Assert(tria != nullptr, ExcNoTriangulationSelected());
2924 // gmsh -> deal.II types
2925 const std::map<int, std::uint8_t> gmsh_to_dealii_type = {
2926 {{15, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {7, 5}, {6, 6}, {5, 7}}};
2927
2928 // Vertex renumbering, by dealii type
2929 const std::array<std::vector<unsigned int>, 8> gmsh_to_dealii = {
2930 {{0},
2931 {{0, 1}},
2932 {{0, 1, 2}},
2933 {{0, 1, 3, 2}},
2934 {{0, 1, 2, 3}},
2935 {{0, 1, 3, 2, 4}},
2936 {{0, 1, 2, 3, 4, 5}},
2937 {{0, 1, 3, 2, 4, 5, 7, 6}}}};
2938
2939 std::vector<Point<spacedim>> vertices;
2940 std::vector<CellData<dim>> cells;
2941 SubCellData subcelldata;
2942 std::map<unsigned int, types::boundary_id> boundary_ids_1d;
2943
2944 // Track the number of times each vertex is used in 1D. This determines
2945 // whether or not we can assign a boundary id to a vertex. This is necessary
2946 // because sometimes gmsh saves internal vertices in the @f$ELEM list in codim
2947 // 1 or codim 2.
2948 std::map<unsigned int, unsigned int> vertex_counts;
2949
2950# if DEAL_II_GMSH_WITH_API_VERSION_GTE(4, 9, 4)
2951 AssertThrow(gmsh::isInitialized() == 1,
2952 ExcMessage("The GMSH API may only be called after GMSH is "
2953 "initialized, e.g., via the InitFinalize or "
2954 "MPI_InitFinalize classes or the gmsh::initialize() "
2955 "function."));
2956# endif
2957 gmsh::option::setNumber("General.Verbosity", 0);
2958 gmsh::clear();
2959 gmsh::open(fname);
2960
2961 AssertThrow(gmsh::model::getDimension() == dim,
2962 ExcMessage("You are trying to read a gmsh file with dimension " +
2963 std::to_string(gmsh::model::getDimension()) +
2964 " into a grid of dimension " + std::to_string(dim)));
2965
2966 // Read all nodes, and store them in our vector of vertices. Before we do
2967 // that, make sure all tags are consecutive
2968 {
2969 gmsh::model::mesh::removeDuplicateNodes();
2970 gmsh::model::mesh::renumberNodes();
2971 std::vector<std::size_t> node_tags;
2972 std::vector<double> coord;
2973 std::vector<double> parametricCoord;
2974 gmsh::model::mesh::getNodes(
2975 node_tags, coord, parametricCoord, -1, -1, false, false);
2976 vertices.resize(node_tags.size());
2977 for (unsigned int i = 0; i < node_tags.size(); ++i)
2978 {
2979 // Check that renumbering worked!
2980 AssertDimension(node_tags[i], i + 1);
2981 for (unsigned int d = 0; d < spacedim; ++d)
2982 vertices[i][d] = coord[i * 3 + d];
2983 if constexpr (running_in_debug_mode())
2984 {
2985 // Make sure the embedded dimension is right
2986 for (unsigned int d = spacedim; d < 3; ++d)
2987 Assert(std::abs(coord[i * 3 + d]) < 1e-10,
2988 ExcMessage(
2989 "The grid you are reading contains nodes that are "
2990 "nonzero in the coordinate with index " +
2991 std::to_string(d) +
2992 ", but you are trying to save "
2993 "it on a grid embedded in a " +
2994 std::to_string(spacedim) + " dimensional space."));
2995 }
2996 }
2997 }
2998
2999 // Get all the elementary entities in the model, as a vector of (dimension,
3000 // tag) pairs:
3001 std::vector<std::pair<int, int>> entities;
3002 gmsh::model::getEntities(entities);
3003
3004 for (const auto &[entity_dim, entity_tag] : entities)
3005 {
3006 // Dimension and tag of the entity:
3007
3008
3010 types::boundary_id boundary_id = 0;
3011
3012 // Get the physical tags, to deduce boundary, material, and manifold_id
3013 std::vector<int> physical_tags;
3014 gmsh::model::getPhysicalGroupsForEntity(entity_dim,
3015 entity_tag,
3016 physical_tags);
3017
3018 // Now fill manifold id and boundary or material id
3019 if (physical_tags.size())
3020 for (auto physical_tag : physical_tags)
3021 {
3022 std::string name;
3023 gmsh::model::getPhysicalName(entity_dim, physical_tag, name);
3024 if (!name.empty())
3025 {
3026 // Patterns::Tools::to_value throws an exception, if it can not
3027 // convert name to a map from string to int.
3028 try
3029 {
3030 std::map<std::string, int> id_names;
3031 Patterns::Tools::to_value(name, id_names);
3032 bool found_unrecognized_tag = false;
3033 bool found_boundary_id = false;
3034 // If the above did not throw, we keep going, and retrieve
3035 // all the information that we know how to translate.
3036 for (const auto &[name, id] : id_names)
3037 {
3038 if (entity_dim == dim && name == "MaterialID")
3039 {
3040 boundary_id = static_cast<types::boundary_id>(id);
3041 found_boundary_id = true;
3042 }
3043 else if (entity_dim < dim && name == "BoundaryID")
3044 {
3045 boundary_id = static_cast<types::boundary_id>(id);
3046 found_boundary_id = true;
3047 }
3048 else if (name == "ManifoldID")
3049 manifold_id = static_cast<types::manifold_id>(id);
3050 else
3051 // We did not recognize one of the keys. We'll fall
3052 // back to setting the boundary id to the physical tag
3053 // after reading all strings.
3054 found_unrecognized_tag = true;
3055 }
3056 // If we didn't find a BoundaryID:XX or MaterialID:XX, and
3057 // something was found but not recognized, then we set the
3058 // material id or boundary using the physical tag directly.
3059 if (found_unrecognized_tag && !found_boundary_id)
3060 boundary_id = physical_tag;
3061 }
3062 catch (...)
3063 {
3064 // When the above didn't work, we revert to the old
3065 // behaviour: the physical tag itself is interpreted either
3066 // as a material_id or a boundary_id, and no manifold id is
3067 // known
3068 boundary_id = physical_tag;
3069 }
3070 }
3071 }
3072
3073 // Get the mesh elements for the entity (dim, tag):
3074 std::vector<int> element_types;
3075 std::vector<std::vector<std::size_t>> element_ids, element_nodes;
3076 gmsh::model::mesh::getElements(
3077 element_types, element_ids, element_nodes, entity_dim, entity_tag);
3078
3079 for (unsigned int i = 0; i < element_types.size(); ++i)
3080 {
3081 const auto &type = gmsh_to_dealii_type.at(element_types[i]);
3082 const auto n_vertices = gmsh_to_dealii[type].size();
3083 const auto &elements = element_ids[i];
3084 const auto &nodes = element_nodes[i];
3085 for (unsigned int j = 0; j < elements.size(); ++j)
3086 {
3087 if (entity_dim == dim)
3088 {
3089 cells.emplace_back(n_vertices);
3090 auto &cell = cells.back();
3091 for (unsigned int v = 0; v < n_vertices; ++v)
3092 {
3093 cell.vertices[v] =
3094 nodes[n_vertices * j + gmsh_to_dealii[type][v]] - 1;
3095 if (dim == 1)
3096 vertex_counts[cell.vertices[v]] += 1u;
3097 }
3098 cell.manifold_id = manifold_id;
3099 cell.material_id = boundary_id;
3100 }
3101 else if (entity_dim == 2)
3102 {
3103 subcelldata.boundary_quads.emplace_back(n_vertices);
3104 auto &face = subcelldata.boundary_quads.back();
3105 for (unsigned int v = 0; v < n_vertices; ++v)
3106 face.vertices[v] =
3107 nodes[n_vertices * j + gmsh_to_dealii[type][v]] - 1;
3108
3109 face.manifold_id = manifold_id;
3110 face.boundary_id = boundary_id;
3111 }
3112 else if (entity_dim == 1)
3113 {
3114 subcelldata.boundary_lines.emplace_back(n_vertices);
3115 auto &line = subcelldata.boundary_lines.back();
3116 for (unsigned int v = 0; v < n_vertices; ++v)
3117 line.vertices[v] =
3118 nodes[n_vertices * j + gmsh_to_dealii[type][v]] - 1;
3119
3120 line.manifold_id = manifold_id;
3121 line.boundary_id = boundary_id;
3122 }
3123 else if (entity_dim == 0)
3124 {
3125 // This should only happen in one dimension.
3126 AssertDimension(dim, 1);
3127 for (unsigned int j = 0; j < elements.size(); ++j)
3128 boundary_ids_1d[nodes[j] - 1] = boundary_id;
3129 }
3130 }
3131 }
3132 }
3133
3134 // apply_grid_fixup_functions() may invalidate the vertex indices (since it
3135 // will delete duplicated or unused vertices). Get around this by storing
3136 // Points directly in that case so that the comparisons are valid.
3137 std::vector<std::pair<Point<spacedim>, types::boundary_id>> boundary_id_pairs;
3138 if (dim == 1)
3139 for (const auto &pair : vertex_counts)
3140 if (pair.second == 1u)
3141 boundary_id_pairs.emplace_back(vertices[pair.first],
3142 boundary_ids_1d[pair.first]);
3143
3144 apply_grid_fixup_functions(vertices, cells, subcelldata);
3145 tria->create_triangulation(vertices, cells, subcelldata);
3146
3147 // in 1d, we also have to attach boundary ids to vertices, which does not
3148 // currently work through the call above.
3149 if (dim == 1)
3150 assign_1d_boundary_ids(boundary_id_pairs, *tria);
3151
3152 gmsh::clear();
3153#else
3154 (void)fname;
3155 AssertThrow(false, ExcNeedsGMSHAPI());
3156#endif
3157}
3158
3159
3160
3161template <int dim, int spacedim>
3162void
3163GridIn<dim, spacedim>::read_partitioned_msh(const std::string &file_prefix,
3164 const std::string &file_suffix)
3165{
3166#ifdef DEAL_II_GMSH_WITH_API
3167 auto *parallel_tria =
3169 tria.get());
3170
3171 // Check that the cast succeeded
3172 AssertThrow(parallel_tria != nullptr,
3173 ExcMessage("Triangulation is not fully distributed!"));
3174
3175 // Now it's safe to call get_communicator()
3176 MPI_Comm mpi_comm = parallel_tria->get_communicator();
3177
3178 const unsigned int nprocs = Utilities::MPI::n_mpi_processes(mpi_comm);
3179 const unsigned int rank = Utilities::MPI::this_mpi_process(mpi_comm);
3180
3181 std::string fname =
3182 file_prefix + "_" + std::to_string(rank + 1) + "." + file_suffix;
3183
3184 if (nprocs == 1)
3185 {
3186 fname = file_prefix + "." + file_suffix;
3187
3188 AssertThrow(std::filesystem::exists(fname),
3189 ExcMessage("Missing mesh file: " + fname));
3190 }
3191 else
3192 {
3193 for (unsigned int i = 1; i <= nprocs; ++i)
3194 {
3195 const std::string check_fname =
3196 file_prefix + "_" + std::to_string(i) + "." + file_suffix;
3197
3198 AssertThrow(std::filesystem::exists(check_fname),
3199 ExcMessage("Missing mesh file: " + check_fname));
3200 }
3201
3202 const std::string extra_fname =
3203 file_prefix + "_" + std::to_string(nprocs + 1) + "." + file_suffix;
3204 AssertThrow(!std::filesystem::exists(extra_fname),
3205 ExcMessage("Expected " + std::to_string(nprocs) +
3206 " mesh files, but found extra: " + extra_fname));
3207 }
3208
3209 const std::map<int, std::uint8_t> gmsh_to_dealii_type = {
3210 {15, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {7, 5}, {6, 6}, {5, 7}};
3211
3212 const std::array<std::vector<unsigned int>, 8> gmsh_to_dealii = {
3213 {{0},
3214 {0, 1},
3215 {0, 1, 2},
3216 {0, 1, 3, 2},
3217 {0, 1, 2, 3},
3218 {0, 1, 3, 2, 4},
3219 {0, 1, 2, 3, 4, 5},
3220 {0, 1, 3, 2, 4, 5, 7, 6}}};
3221
3222# if DEAL_II_GMSH_WITH_API_VERSION_GTE(4, 9, 4)
3223 AssertThrow(gmsh::isInitialized() == 1,
3224 ExcMessage("The GMSH API may only be called after GMSH is "
3225 "initialized, e.g., via the InitFinalize or "
3226 "MPI_InitFinalize classes or the gmsh::initialize() "
3227 "function."));
3228# endif
3229 gmsh::option::setNumber("General.Verbosity", 0);
3230 gmsh::clear();
3231 gmsh::open(fname);
3232
3233 std::map<unsigned long, unsigned int> ghost_map;
3234
3235 std::vector<std::pair<int, int>> entities;
3236 gmsh::model::getEntities(entities);
3237
3238 for (const auto &e : entities)
3239 {
3240 const int entity_dim = e.first;
3241 const int entity_tag = e.second;
3242
3243 if (entity_dim == dim)
3244 {
3245 std::vector<std::size_t> element_tags;
3246 std::vector<int> partitions;
3247
3248 gmsh::model::mesh::getGhostElements(entity_dim,
3249 entity_tag,
3250 element_tags,
3251 partitions);
3252
3253 for (std::size_t i = 0; i < element_tags.size(); ++i)
3254 ghost_map[element_tags[i]] =
3255 static_cast<unsigned int>(partitions[i] - 1);
3256 }
3257 }
3258
3259 std::vector<std::size_t> node_tags;
3260 std::vector<double> coords, parametric_coords;
3261 gmsh::model::mesh::getNodes(node_tags, coords, parametric_coords);
3262
3264 triangulation_description;
3265 triangulation_description.comm = mpi_comm;
3266
3267 triangulation_description.cell_infos.resize(1);
3268 triangulation_description.coarse_cell_vertices.resize(node_tags.size(),
3269 Point<spacedim>());
3270
3271 std::map<std::size_t, unsigned int> node_tag_to_index;
3272 for (unsigned int i = 0; i < node_tags.size(); ++i)
3273 {
3274 node_tag_to_index[node_tags[i]] = i;
3275 for (unsigned int d = 0; d < spacedim; ++d)
3276 triangulation_description.coarse_cell_vertices[i][d] =
3277 coords[3 * i + d];
3278 }
3279
3280 // --- Collect physical groups for boundary and material info ---
3281 std::map<std::pair<int, int>, types::boundary_id> entity_to_boundary;
3282 std::map<std::pair<int, int>, types::material_id> entity_to_material;
3283
3284
3285 std::vector<std::pair<int, int>> physical_groups;
3286 gmsh::model::getPhysicalGroups(physical_groups);
3287
3288 for (const auto &[physical_dim, physical_tag] : physical_groups)
3289 {
3290 std::vector<int> physical_entities;
3291 gmsh::model::getEntitiesForPhysicalGroup(physical_dim,
3292 physical_tag,
3293 physical_entities);
3294
3295 for (const int ent : physical_entities)
3296 {
3297 // Assign boundary ID for faces (dim-1)
3298 if (physical_dim == dim - 1)
3299 entity_to_boundary[{physical_dim, ent}] = physical_tag;
3300 if (physical_dim == dim) // Volume materials
3301 entity_to_material[{physical_dim, ent}] = physical_tag;
3302 }
3303 }
3304
3305 // --- Build boundary face map for all (dim-1) entities ---
3306
3307 std::map<std::set<unsigned int>, types::boundary_id> boundary_face_map;
3308
3309 // Process all (dim-1)-dimensional entities for boundary assignment
3310 for (const auto &e : entities)
3311 if (e.first == dim - 1)
3312 if (auto it = entity_to_boundary.find({e.first, e.second});
3313 it != entity_to_boundary.end())
3314 {
3315 const types::boundary_id boundary_id = it->second;
3316
3317 std::vector<int> element_types;
3318 std::vector<std::vector<std::size_t>> element_ids, element_nodes;
3319
3320 // Get all elements on this (dim-1)-entity
3321 gmsh::model::mesh::getElements(
3322 element_types, element_ids, element_nodes, e.first, e.second);
3323
3324 for (unsigned int i = 0; i < element_types.size(); ++i)
3325 {
3326 if (element_ids[i].empty())
3327 continue;
3328
3329 const unsigned int n_nodes_per_elem =
3330 element_nodes[i].size() / element_ids[i].size();
3331
3332 for (unsigned int j = 0; j < element_ids[i].size(); ++j)
3333 {
3334 std::set<unsigned int> face_vertices;
3335 for (unsigned int k = 0; k < n_nodes_per_elem; ++k)
3336 {
3337 std::size_t node_tag =
3338 element_nodes[i][j * n_nodes_per_elem + k];
3339 face_vertices.insert(node_tag_to_index[node_tag]);
3340 }
3341
3342 // Store boundary IDs for this face
3343 if (boundary_id != numbers::invalid_boundary_id)
3344 boundary_face_map[face_vertices] = boundary_id;
3345 }
3346 }
3347 }
3348
3349
3350
3351 // Count total volume elements and reserve space
3352 std::size_t total_volume_elements = 0;
3353 for (const auto &[entity_dim, entity_tag] : entities)
3354 {
3355 if (entity_dim == dim) // we only care about cells here
3356 {
3357 std::vector<int> count_element_types;
3358 std::vector<std::vector<std::size_t>> count_element_ids,
3359 count_element_nodes;
3360
3361 gmsh::model::mesh::getElements(count_element_types,
3362 count_element_ids,
3363 count_element_nodes,
3364 entity_dim,
3365 entity_tag);
3366
3367 for (const auto &count_element_id : count_element_ids)
3368 total_volume_elements += count_element_id.size();
3369 }
3370 }
3371
3372 // Reserve space for all vectors that will grow during processing
3373 triangulation_description.coarse_cells.reserve(total_volume_elements);
3374 triangulation_description.coarse_cell_index_to_coarse_cell_id.reserve(
3375 total_volume_elements);
3376 triangulation_description.cell_infos[0].reserve(total_volume_elements);
3377
3378 for (const auto &[entity_dim, entity_tag] : entities)
3379 {
3380 if (entity_dim == dim)
3381 {
3382 std::vector<int> element_types;
3383 std::vector<std::vector<std::size_t>> element_ids, element_nodes;
3384
3385 gmsh::model::mesh::getElements(
3386 element_types, element_ids, element_nodes, entity_dim, entity_tag);
3387
3388 for (unsigned int i = 0; i < element_types.size(); ++i)
3389 {
3390 if (element_ids[i].empty())
3391 continue;
3392
3393 const unsigned int n_vertices =
3394 element_nodes[i].size() / element_ids[i].size();
3395
3396 for (unsigned int j = 0; j < element_ids[i].size(); ++j)
3397 {
3398 CellData<dim> cell(n_vertices);
3399
3401 if (auto it = entity_to_material.find({dim, entity_tag});
3402 it != entity_to_material.end())
3403 cell.material_id = it->second;
3404
3405 const auto &type = gmsh_to_dealii_type.at(element_types[i]);
3406
3407 for (unsigned int v = 0; v < n_vertices; ++v)
3408 {
3409 const std::size_t node_tag =
3410 element_nodes[i]
3411 [j * n_vertices + gmsh_to_dealii[type][v]];
3412 AssertThrow(node_tag_to_index.find(node_tag) !=
3413 node_tag_to_index.end(),
3414 ExcMessage("Node tag " +
3415 std::to_string(node_tag) +
3416 " not found in node list!"));
3417 cell.vertices[v] = node_tag_to_index[node_tag];
3418 }
3419
3421 cell_info.id =
3422 CellId(element_ids[i][j], {}).template to_binary<dim>();
3423
3424 auto it = ghost_map.find(element_ids[i][j]);
3425 if (it != ghost_map.end())
3426 cell_info.subdomain_id = it->second;
3427 else
3428 cell_info.subdomain_id = rank;
3429
3430 cell_info.level_subdomain_id = cell_info.subdomain_id;
3431
3432
3433 // --- Universal boundary ID assignment for faces ---
3434 if constexpr (dim > 0)
3435 {
3436 // Determine reference cell type from number of vertices
3437 const ReferenceCell<dim> ref_cell =
3439 n_vertices);
3440
3441 // Number of faces for this reference cell
3442 const unsigned int n_faces = ref_cell.n_faces();
3443
3444 for (unsigned int f = 0; f < n_faces; ++f)
3445 {
3446 std::set<unsigned int> face_vertices;
3447
3448 if constexpr (dim == 1)
3449 {
3450 // In 1D, faces are vertices
3451 face_vertices.insert(cell.vertices[f]);
3452 }
3453 else if constexpr (dim == 2)
3454 {
3455 // In 2D, manually handle triangles and quads
3456 if (ref_cell == ReferenceCells::Triangle)
3457 {
3458 // Triangle: face f connects vertices f and
3459 // (f+1)%3
3460 face_vertices.insert(cell.vertices[f]);
3461 face_vertices.insert(
3462 cell.vertices[(f + 1) % 3]);
3463 }
3464 else if (ref_cell ==
3466 {
3467 const unsigned int n_face_vertices =
3468 ref_cell.face_reference_cell(f)
3469 .n_vertices();
3470 for (unsigned int fv = 0;
3471 fv < n_face_vertices;
3472 ++fv)
3473 {
3474 const unsigned int vertex_index =
3475 ref_cell.face_to_cell_vertices(
3476 f,
3477 fv,
3478 numbers::
3479 default_geometric_orientation);
3480 face_vertices.insert(
3481 cell.vertices[vertex_index]);
3482 }
3483 }
3484 }
3485 else if constexpr (dim == 3)
3486 {
3487 // In 3D, get face vertices from reference cell
3488 const unsigned int n_face_vertices =
3489 ref_cell.face_reference_cell(f).n_vertices();
3490 for (unsigned int fv = 0; fv < n_face_vertices;
3491 ++fv)
3492 {
3493 const unsigned int vertex_index =
3494 ref_cell.face_to_cell_vertices(
3495 f,
3496 fv,
3498 face_vertices.insert(
3499 cell.vertices[vertex_index]);
3500 }
3501 }
3502
3503 // Assign boundary ID if found
3504 if (auto it = boundary_face_map.find(face_vertices);
3505 it != boundary_face_map.end())
3506 cell_info.boundary_ids.emplace_back(f, it->second);
3507 }
3508 }
3509
3510 triangulation_description.coarse_cells.push_back(cell);
3511 triangulation_description.coarse_cell_index_to_coarse_cell_id
3512 .push_back(element_ids[i][j]);
3513 triangulation_description.cell_infos[0].push_back(cell_info);
3514 }
3515 }
3516 }
3517 }
3518
3519 triangulation_description.settings =
3521
3522 parallel_tria->create_triangulation(triangulation_description);
3523
3524
3525# ifdef DEAL_II_WITH_MPI
3526 const int mpi_ierr = MPI_Barrier(mpi_comm);
3527 AssertThrowMPI(mpi_ierr);
3528# endif
3529
3530 gmsh::clear();
3531#else
3532 (void)file_prefix;
3533 (void)file_suffix;
3534 AssertThrow(false, ExcNeedsGMSHAPI());
3535#endif
3536}
3537
3538
3539template <int dim, int spacedim>
3540void
3542 std::string &header,
3543 std::vector<unsigned int> &tecplot2deal,
3544 unsigned int &n_vars,
3545 unsigned int &n_vertices,
3546 unsigned int &n_cells,
3547 std::vector<unsigned int> &IJK,
3548 bool &structured,
3549 bool &blocked)
3550{
3551 Assert(tecplot2deal.size() == dim, ExcInternalError());
3552 Assert(IJK.size() == dim, ExcInternalError());
3553 // initialize the output variables
3554 n_vars = 0;
3555 n_vertices = 0;
3556 n_cells = 0;
3557 switch (dim)
3558 {
3559 case 3:
3560 IJK[2] = 0;
3562 case 2:
3563 IJK[1] = 0;
3565 case 1:
3566 IJK[0] = 0;
3567 }
3568 structured = true;
3569 blocked = false;
3570
3571 // convert the string to upper case
3572 std::transform(header.begin(),
3573 header.end(),
3574 header.begin(),
3575 static_cast<int (*)(int)>(std::toupper));
3576
3577 // replace all tabs, commas, newlines by
3578 // whitespaces
3579 std::replace(header.begin(), header.end(), '\t', ' ');
3580 std::replace(header.begin(), header.end(), ',', ' ');
3581 std::replace(header.begin(), header.end(), '\n', ' ');
3582
3583 // now remove whitespace in front of and
3584 // after '='
3585 std::string::size_type pos = header.find('=');
3586
3587 while (pos != static_cast<std::string::size_type>(std::string::npos))
3588 if (header[pos + 1] == ' ')
3589 header.erase(pos + 1, 1);
3590 else if (header[pos - 1] == ' ')
3591 {
3592 header.erase(pos - 1, 1);
3593 --pos;
3594 }
3595 else
3596 pos = header.find('=', ++pos);
3597
3598 // split the string into individual entries
3599 std::vector<std::string> entries =
3600 Utilities::break_text_into_lines(header, 1, ' ');
3601
3602 // now go through the list and try to extract
3603 for (unsigned int i = 0; i < entries.size(); ++i)
3604 {
3605 if (Utilities::match_at_string_start(entries[i], "VARIABLES=\""))
3606 {
3607 ++n_vars;
3608 // we assume, that the first variable
3609 // is x or no coordinate at all (not y or z)
3610 if (Utilities::match_at_string_start(entries[i], "VARIABLES=\"X\""))
3611 {
3612 tecplot2deal[0] = 0;
3613 }
3614 ++i;
3615 while (entries[i][0] == '"')
3616 {
3617 if (entries[i] == "\"X\"")
3618 tecplot2deal[0] = n_vars;
3619 else if (entries[i] == "\"Y\"")
3620 {
3621 // we assume, that y contains
3622 // zero data in 1d, so do
3623 // nothing
3624 if (dim > 1)
3625 tecplot2deal[1] = n_vars;
3626 }
3627 else if (entries[i] == "\"Z\"")
3628 {
3629 // we assume, that z contains
3630 // zero data in 1d and 2d, so
3631 // do nothing
3632 if (dim > 2)
3633 tecplot2deal[2] = n_vars;
3634 }
3635 ++n_vars;
3636 ++i;
3637 }
3638 // set i back, so that the next
3639 // string is treated correctly
3640 --i;
3641
3643 n_vars >= dim,
3644 ExcMessage(
3645 "Tecplot file must contain at least one variable for each dimension"));
3646 for (unsigned int d = 1; d < dim; ++d)
3648 tecplot2deal[d] > 0,
3649 ExcMessage(
3650 "Tecplot file must contain at least one variable for each dimension."));
3651 }
3652 else if (Utilities::match_at_string_start(entries[i], "ZONETYPE=ORDERED"))
3653 structured = true;
3654 else if (Utilities::match_at_string_start(entries[i],
3655 "ZONETYPE=FELINESEG") &&
3656 dim == 1)
3657 structured = false;
3658 else if (Utilities::match_at_string_start(entries[i],
3659 "ZONETYPE=FEQUADRILATERAL") &&
3660 dim == 2)
3661 structured = false;
3662 else if (Utilities::match_at_string_start(entries[i],
3663 "ZONETYPE=FEBRICK") &&
3664 dim == 3)
3665 structured = false;
3666 else if (Utilities::match_at_string_start(entries[i], "ZONETYPE="))
3667 // unsupported ZONETYPE
3668 {
3669 AssertThrow(false,
3670 ExcMessage(
3671 "The tecplot file contains an unsupported ZONETYPE."));
3672 }
3673 else if (Utilities::match_at_string_start(entries[i],
3674 "DATAPACKING=POINT"))
3675 blocked = false;
3676 else if (Utilities::match_at_string_start(entries[i],
3677 "DATAPACKING=BLOCK"))
3678 blocked = true;
3679 else if (Utilities::match_at_string_start(entries[i], "F=POINT"))
3680 {
3681 structured = true;
3682 blocked = false;
3683 }
3684 else if (Utilities::match_at_string_start(entries[i], "F=BLOCK"))
3685 {
3686 structured = true;
3687 blocked = true;
3688 }
3689 else if (Utilities::match_at_string_start(entries[i], "F=FEPOINT"))
3690 {
3691 structured = false;
3692 blocked = false;
3693 }
3694 else if (Utilities::match_at_string_start(entries[i], "F=FEBLOCK"))
3695 {
3696 structured = false;
3697 blocked = true;
3698 }
3699 else if (Utilities::match_at_string_start(entries[i],
3700 "ET=QUADRILATERAL") &&
3701 dim == 2)
3702 structured = false;
3703 else if (Utilities::match_at_string_start(entries[i], "ET=BRICK") &&
3704 dim == 3)
3705 structured = false;
3706 else if (Utilities::match_at_string_start(entries[i], "ET="))
3707 // unsupported ElementType
3708 {
3710 false,
3711 ExcMessage(
3712 "The tecplot file contains an unsupported ElementType."));
3713 }
3714 else if (Utilities::match_at_string_start(entries[i], "I="))
3715 IJK[0] = Utilities::get_integer_at_position(entries[i], 2).first;
3716 else if (Utilities::match_at_string_start(entries[i], "J="))
3717 {
3718 IJK[1] = Utilities::get_integer_at_position(entries[i], 2).first;
3720 dim > 1 || IJK[1] == 1,
3721 ExcMessage(
3722 "Parameter 'J=' found in tecplot, although this is only possible for dimensions greater than 1."));
3723 }
3724 else if (Utilities::match_at_string_start(entries[i], "K="))
3725 {
3726 IJK[2] = Utilities::get_integer_at_position(entries[i], 2).first;
3728 dim > 2 || IJK[2] == 1,
3729 ExcMessage(
3730 "Parameter 'K=' found in tecplot, although this is only possible for dimensions greater than 2."));
3731 }
3732 else if (Utilities::match_at_string_start(entries[i], "N="))
3733 n_vertices = Utilities::get_integer_at_position(entries[i], 2).first;
3734 else if (Utilities::match_at_string_start(entries[i], "E="))
3735 n_cells = Utilities::get_integer_at_position(entries[i], 2).first;
3736 }
3737
3738 // now we have read all the fields we are
3739 // interested in. do some checks and
3740 // calculate the variables
3741 if (structured)
3742 {
3743 n_vertices = 1;
3744 n_cells = 1;
3745 for (unsigned int d = 0; d < dim; ++d)
3746 {
3748 IJK[d] > 0,
3749 ExcMessage(
3750 "Tecplot file does not contain a complete and consistent set of parameters"));
3751 n_vertices *= IJK[d];
3752 n_cells *= (IJK[d] - 1);
3753 }
3754 }
3755 else
3756 {
3758 n_vertices > 0,
3759 ExcMessage(
3760 "Tecplot file does not contain a complete and consistent set of parameters"));
3761 if (n_cells == 0)
3762 // this means an error, although
3763 // tecplot itself accepts entries like
3764 // 'J=20' instead of 'E=20'. therefore,
3765 // take the max of IJK
3766 n_cells = *std::max_element(IJK.begin(), IJK.end());
3768 n_cells > 0,
3769 ExcMessage(
3770 "Tecplot file does not contain a complete and consistent set of parameters"));
3771 }
3772}
3773
3774
3775
3776template <>
3777void
3779{
3780 const unsigned int dim = 2;
3781 const unsigned int spacedim = 2;
3782 Assert(tria != nullptr, ExcNoTriangulationSelected());
3783 AssertThrow(in.fail() == false, ExcIO());
3784
3785 // skip comments at start of file
3786 skip_comment_lines(in, '#');
3787
3788 // some strings for parsing the header
3789 std::string line, header;
3790
3791 // first, concatenate all header lines
3792 // create a searchstring with almost all
3793 // letters. exclude e and E from the letters
3794 // to search, as they might appear in
3795 // exponential notation
3796 std::string letters = "abcdfghijklmnopqrstuvwxyzABCDFGHIJKLMNOPQRSTUVWXYZ";
3797
3798 getline(in, line);
3799 while (line.find_first_of(letters) != std::string::npos)
3800 {
3801 header += " " + line;
3802 getline(in, line);
3803 }
3804
3805 // now create some variables holding
3806 // important information on the mesh, get
3807 // this information from the header string
3808 std::vector<unsigned int> tecplot2deal(dim);
3809 std::vector<unsigned int> IJK(dim);
3810 unsigned int n_vars, n_vertices, n_cells;
3811 bool structured, blocked;
3812
3813 parse_tecplot_header(header,
3814 tecplot2deal,
3815 n_vars,
3816 n_vertices,
3817 n_cells,
3818 IJK,
3819 structured,
3820 blocked);
3821
3822 // reserve space for vertices. note, that in
3823 // tecplot vertices are ordered beginning
3824 // with 1, whereas in deal all indices start
3825 // with 0. in order not to use -1 for all the
3826 // connectivity information, a 0th vertex
3827 // (unused) is inserted at the origin.
3828 std::vector<Point<spacedim>> vertices(n_vertices + 1);
3829 vertices[0] = Point<spacedim>();
3830 // reserve space for cells
3831 std::vector<CellData<dim>> cells(n_cells);
3832 SubCellData subcelldata;
3833
3834 if (blocked)
3835 {
3836 // blocked data format. first we get all
3837 // the values of the first variable for
3838 // all points, after that we get all
3839 // values for the second variable and so
3840 // on.
3841
3842 // dummy variable to read in all the info
3843 // we do not want to use
3844 double dummy;
3845 // which is the first index to read in
3846 // the loop (see below)
3847 unsigned int next_index = 0;
3848
3849 // note, that we have already read the
3850 // first line containing the first variable
3851 if (tecplot2deal[0] == 0)
3852 {
3853 // we need the information in this
3854 // line, so extract it
3855 std::vector<std::string> first_var =
3857 char *endptr;
3858 for (unsigned int i = 1; i < first_var.size() + 1; ++i)
3859 vertices[i][0] = std::strtod(first_var[i - 1].c_str(), &endptr);
3860
3861 // if there are many points, the data
3862 // for this var might continue in the
3863 // next line(s)
3864 for (unsigned int j = first_var.size() + 1; j < n_vertices + 1; ++j)
3865 in >> vertices[j][next_index];
3866 // now we got all values of the first
3867 // variable, so increase the counter
3868 next_index = 1;
3869 }
3870
3871 // main loop over all variables
3872 for (unsigned int i = 1; i < n_vars; ++i)
3873 {
3874 // if we read all the important
3875 // variables and do not want to
3876 // read further, because we are
3877 // using a structured grid, we can
3878 // stop here (and skip, for
3879 // example, a whole lot of solution
3880 // variables)
3881 if (next_index == dim && structured)
3882 break;
3883
3884 if ((next_index < dim) && (i == tecplot2deal[next_index]))
3885 {
3886 // we need this line, read it in
3887 for (unsigned int j = 1; j < n_vertices + 1; ++j)
3888 in >> vertices[j][next_index];
3889 ++next_index;
3890 }
3891 else
3892 {
3893 // we do not need this line, read
3894 // it in and discard it
3895 for (unsigned int j = 1; j < n_vertices + 1; ++j)
3896 in >> dummy;
3897 }
3898 }
3899 Assert(next_index == dim, ExcInternalError());
3900 }
3901 else
3902 {
3903 // the data is not blocked, so we get all
3904 // the variables for one point, then the
3905 // next and so on. create a vector to
3906 // hold these components
3907 std::vector<double> vars(n_vars);
3908
3909 // now fill the first vertex. note, that we
3910 // have already read the first line
3911 // containing the first vertex
3912 std::vector<std::string> first_vertex =
3914 char *endptr;
3915 for (unsigned int d = 0; d < dim; ++d)
3916 vertices[1][d] =
3917 std::strtod(first_vertex[tecplot2deal[d]].c_str(), &endptr);
3918
3919 // read the remaining vertices from the
3920 // list
3921 for (unsigned int v = 2; v < n_vertices + 1; ++v)
3922 {
3923 for (unsigned int i = 0; i < n_vars; ++i)
3924 in >> vars[i];
3925 // fill the vertex
3926 // coordinates. respect the position
3927 // of coordinates in the list of
3928 // variables
3929 for (unsigned int i = 0; i < dim; ++i)
3930 vertices[v][i] = vars[tecplot2deal[i]];
3931 }
3932 }
3933
3934 if (structured)
3935 {
3936 // this is the part of the code that only
3937 // works in 2d
3938 unsigned int I = IJK[0], J = IJK[1];
3939
3940 unsigned int cell = 0;
3941 // set up array of cells
3942 for (unsigned int j = 0; j < J - 1; ++j)
3943 for (unsigned int i = 1; i < I; ++i)
3944 {
3945 cells[cell].vertices[0] = i + j * I;
3946 cells[cell].vertices[1] = i + 1 + j * I;
3947 cells[cell].vertices[2] = i + (j + 1) * I;
3948 cells[cell].vertices[3] = i + 1 + (j + 1) * I;
3949 ++cell;
3950 }
3951 Assert(cell == n_cells, ExcInternalError());
3952 std::vector<unsigned int> boundary_vertices(2 * I + 2 * J - 4);
3953 unsigned int k = 0;
3954 for (unsigned int i = 1; i < I + 1; ++i)
3955 {
3956 boundary_vertices[k] = i;
3957 ++k;
3958 boundary_vertices[k] = i + (J - 1) * I;
3959 ++k;
3960 }
3961 for (unsigned int j = 1; j < J - 1; ++j)
3962 {
3963 boundary_vertices[k] = 1 + j * I;
3964 ++k;
3965 boundary_vertices[k] = I + j * I;
3966 ++k;
3967 }
3968 Assert(k == boundary_vertices.size(), ExcInternalError());
3969 // delete the duplicated vertices at the
3970 // boundary, which occur, e.g. in c-type
3971 // or o-type grids around a body
3972 // (airfoil). this automatically deletes
3973 // unused vertices as well.
3975 cells,
3976 subcelldata,
3977 boundary_vertices);
3978 }
3979 else
3980 {
3981 // set up array of cells, unstructured
3982 // mode, so the connectivity is
3983 // explicitly given
3984 for (unsigned int i = 0; i < n_cells; ++i)
3985 {
3986 // note that since in the input file
3987 // we found the number of cells at
3988 // the top, there should still be
3989 // input here, so check this:
3990 AssertThrow(in.fail() == false, ExcIO());
3991
3992 // get the connectivity from the
3993 // input file. the vertices are
3994 // ordered like in the ucd format
3995 for (const unsigned int j : GeometryInfo<dim>::vertex_indices())
3996 in >> cells[i].vertices[GeometryInfo<dim>::ucd_to_deal[j]];
3997 }
3998 }
3999 AssertThrow(in.fail() == false, ExcIO());
4000
4001 apply_grid_fixup_functions(vertices, cells, subcelldata);
4002 tria->create_triangulation(vertices, cells, subcelldata);
4003}
4004
4005
4006
4007template <int dim, int spacedim>
4008void
4013
4014
4015
4016template <int dim, int spacedim>
4017void
4018GridIn<dim, spacedim>::read_assimp(const std::string &filename,
4019 const unsigned int mesh_index,
4020 const bool remove_duplicates,
4021 const double tol,
4022 const bool ignore_unsupported_types)
4023{
4024#ifdef DEAL_II_WITH_ASSIMP
4025 // Only good for surface grids.
4026 AssertThrow(dim < 3, ExcImpossibleInDim(dim));
4027
4028 // Create an instance of the Importer class
4029 Assimp::Importer importer;
4030
4031 // And have it read the given file with some postprocessing
4032 const aiScene *scene =
4033 importer.ReadFile(filename.c_str(),
4034 aiProcess_RemoveComponent |
4035 aiProcess_JoinIdenticalVertices |
4036 aiProcess_ImproveCacheLocality | aiProcess_SortByPType |
4037 aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes);
4038
4039 // If the import failed, report it
4040 AssertThrow(scene != nullptr, ExcMessage(importer.GetErrorString()));
4041
4042 AssertThrow(scene->mNumMeshes != 0,
4043 ExcMessage("Input file contains no meshes."));
4044
4046 (mesh_index < scene->mNumMeshes),
4047 ExcMessage("Too few meshes in the file."));
4048
4049 unsigned int start_mesh =
4050 (mesh_index == numbers::invalid_unsigned_int ? 0 : mesh_index);
4051 unsigned int end_mesh =
4052 (mesh_index == numbers::invalid_unsigned_int ? scene->mNumMeshes :
4053 mesh_index + 1);
4054
4055 // Deal.II objects are created empty, and then filled with imported file.
4056 std::vector<Point<spacedim>> vertices;
4057 std::vector<CellData<dim>> cells;
4058 SubCellData subcelldata;
4059
4060 // A series of counters to merge cells.
4061 unsigned int v_offset = 0;
4062 unsigned int c_offset = 0;
4063
4064 constexpr std::array<unsigned int, 8> local_vertex_numbering = {
4065 {0, 1, 5, 4, 2, 3, 7, 6}};
4066 // The index of the mesh will be used as a material index.
4067 for (unsigned int m = start_mesh; m < end_mesh; ++m)
4068 {
4069 const aiMesh *mesh = scene->mMeshes[m];
4070
4071 // Check that we know what to do with this mesh, otherwise just
4072 // ignore it
4073 if ((dim == 2) && mesh->mPrimitiveTypes != aiPrimitiveType_POLYGON)
4074 {
4075 AssertThrow(ignore_unsupported_types,
4076 ExcMessage("Incompatible mesh " + std::to_string(m) +
4077 "/" + std::to_string(scene->mNumMeshes)));
4078 continue;
4079 }
4080 else if ((dim == 1) && mesh->mPrimitiveTypes != aiPrimitiveType_LINE)
4081 {
4082 AssertThrow(ignore_unsupported_types,
4083 ExcMessage("Incompatible mesh " + std::to_string(m) +
4084 "/" + std::to_string(scene->mNumMeshes)));
4085 continue;
4086 }
4087 // Vertices
4088 const unsigned int n_vertices = mesh->mNumVertices;
4089 const aiVector3D *mVertices = mesh->mVertices;
4090
4091 // Faces
4092 const unsigned int n_faces = mesh->mNumFaces;
4093 const aiFace *mFaces = mesh->mFaces;
4094
4095 vertices.resize(v_offset + n_vertices);
4096 cells.resize(c_offset + n_faces);
4097
4098 for (unsigned int i = 0; i < n_vertices; ++i)
4099 for (unsigned int d = 0; d < spacedim; ++d)
4100 vertices[i + v_offset][d] = mVertices[i][d];
4101
4102 unsigned int valid_cell = c_offset;
4103 for (unsigned int i = 0; i < n_faces; ++i)
4104 {
4105 if (mFaces[i].mNumIndices == GeometryInfo<dim>::vertices_per_cell)
4106 {
4107 for (const unsigned int f : GeometryInfo<dim>::vertex_indices())
4108 {
4109 cells[valid_cell]
4110 .vertices[dim == 3 ? local_vertex_numbering[f] :
4112 mFaces[i].mIndices[f] + v_offset;
4113 }
4114 cells[valid_cell].material_id = m;
4115 ++valid_cell;
4116 }
4117 else
4118 {
4119 AssertThrow(ignore_unsupported_types,
4120 ExcMessage("Face " + std::to_string(i) + " of mesh " +
4121 std::to_string(m) + " has " +
4122 std::to_string(mFaces[i].mNumIndices) +
4123 " vertices. We expected only " +
4124 std::to_string(
4126 }
4127 }
4128 cells.resize(valid_cell);
4129
4130 // The vertices are added all at once. Cells are checked for
4131 // validity, so only valid_cells are now present in the deal.II
4132 // list of cells.
4133 v_offset += n_vertices;
4134 c_offset = valid_cell;
4135 }
4136
4137 // No cells were read
4138 if (cells.empty())
4139 return;
4140
4141 if (remove_duplicates)
4142 {
4143 // The function delete_duplicated_vertices() needs to be called more
4144 // than once if a vertex is duplicated more than once. So we keep
4145 // calling it until the number of vertices does not change any more.
4146 unsigned int n_verts = 0;
4147 while (n_verts != vertices.size())
4148 {
4149 n_verts = vertices.size();
4150 std::vector<unsigned int> considered_vertices;
4152 vertices, cells, subcelldata, considered_vertices, tol);
4153 }
4154 }
4155
4156 apply_grid_fixup_functions(vertices, cells, subcelldata);
4157 tria->create_triangulation(vertices, cells, subcelldata);
4158
4159#else
4160 (void)filename;
4161 (void)mesh_index;
4162 (void)remove_duplicates;
4163 (void)tol;
4164 (void)ignore_unsupported_types;
4165 AssertThrow(false, ExcNeedsAssimp());
4166#endif
4167}
4168
4169
4170
4171template <int dim, int spacedim>
4172void
4174{
4175 Assert(dim > 1,
4176 ExcMessage("The ugrid format reader currently supports only 2- and "
4177 "3-dimensional meshes."));
4178 Assert(tria != nullptr, ExcNoTriangulationSelected());
4179 AssertThrow(in.fail() == false, ExcIO());
4180
4181 // Start with the header:
4182 // { Number_of_Nodes, Number_of_Surf_Trias, Number_of_Surf_Quads,
4183 // Number_of_Vol_Tets, Number_of_Vol_Pents_5, Number_of_Vol_Pents_6,
4184 // Number_of_Vol_Hexs }
4185 unsigned int n_nodes, n_tris, n_quads, n_tets, n_pyramids, n_wedges, n_hexes;
4186 in >> n_nodes >> n_tris >> n_quads >> n_tets >> n_pyramids >> n_wedges >>
4187 n_hexes;
4188 if (dim == 2)
4190 n_tris + n_quads > 0,
4191 ExcMessage(
4192 "When reading a 2-dimensional triangulation, "
4193 "there need to be more than zero triangles or quadrilaterals."));
4194 else
4195 AssertThrow(n_tets + n_pyramids + n_wedges + n_hexes > 0,
4196 ExcMessage("When reading a 3-dimensional triangulation, "
4197 "there need to be more than zero tetrahedra, "
4198 "pyramids, wedges, or hexahedra."));
4199
4200 // Then first read the nodes:
4201 std::vector<Point<spacedim>> vertices(n_nodes);
4202 for (Point<spacedim> &vertex : vertices)
4203 {
4204 in >> vertex;
4205
4206 // Coordinates are always provided in 3d, so read any trailing
4207 // coordinates. They need to be zero for this to make sense.
4208 for (unsigned int d = spacedim; d < 3; ++d)
4209 {
4210 double dummy;
4211 in >> dummy;
4213 dummy == 0,
4214 ExcMessage(
4215 "You are reading a mesh in spacedim=" + std::to_string(spacedim) +
4216 " but some of the vertex positions have trailing coordinates "
4217 "that are not zero."));
4218 }
4219 }
4220
4221 // Then read the two-dimensional objects.
4222 std::vector<CellData<2>> objects_2d;
4223 objects_2d.reserve(n_tris + n_quads);
4224 for (unsigned int i = 0; i < n_tris; ++i)
4225 {
4226 CellData<2> object(ReferenceCells::Triangle.n_vertices());
4227
4228 // Read the vertex indices from the file. In ugrid files, these are
4229 // 1-based, so translate to 0-based next.
4230 for (unsigned int &vertex_index : object.vertices)
4231 {
4232 in >> vertex_index;
4233 --vertex_index;
4234 }
4235 objects_2d.emplace_back(object);
4236 }
4237 for (unsigned int i = 0; i < n_quads; ++i)
4238 {
4240 false,
4241 ExcMessage(
4242 "Reading quadrilaterals is not currently implemented by the ugrid "
4243 "reader because we do not know the order of vertices and have no example "
4244 "to look at. If you have a test file, please contact us and/or help us "
4245 "implement this missing case."));
4246 }
4247
4248 // ugrid files next store boundary ids for 2d objects, presumably
4249 // under the assumption that all meshes are 3d and that consequently
4250 // all 2d objects are at the boundary of the domain. We translate that
4251 // to material ids if we are dealing with 2d meshes.
4252 for (auto &object : objects_2d)
4253 if (dim == 2)
4254 in >> object.material_id;
4255 else
4256 in >> object.boundary_id;
4257
4258
4259 // Next read the three-dimensional objects.
4260 std::vector<CellData<3>> objects_3d;
4261 objects_3d.reserve(n_tets + n_pyramids + n_wedges + n_hexes);
4262 for (unsigned int i = 0; i < n_tets; ++i)
4263 {
4265 false,
4266 ExcMessage(
4267 "Reading tetrahedra is not currently implemented by the ugrid "
4268 "reader because we do not know the order of vertices and have no example "
4269 "to look at. If you have a test file, please contact us and/or help us "
4270 "implement this missing case."));
4271 }
4272 for (unsigned int i = 0; i < n_pyramids; ++i)
4273 {
4275 false,
4276 ExcMessage(
4277 "Reading pyramids 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_wedges; ++i)
4283 {
4285 false,
4286 ExcMessage(
4287 "Reading wedges 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_hexes; ++i)
4293 {
4295 false,
4296 ExcMessage(
4297 "Reading hexahdrea 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
4303 // The next section in ugrid files concerns Number_of_BL_Vol_Tets, which
4304 // I interpret as a way of specifying boundary layer cells that can be
4305 // extruded. I don't know how to interpret this information, so assume that it
4306 // isn't present in the current file.
4307 //
4308 // In fact, in the files we have this section simply doesn't exist. So the
4309 // following block is commented out:
4310
4311 // unsigned int n_BL_vol_tets;
4312 // in >> n_BL_vol_tets;
4313 // AssertThrow(n_BL_vol_tets == 0,
4314 // ExcMessage("Dealing with boundary layer descriptions in ugrid
4315 // "
4316 // "files is not currently supported"));
4317
4318
4319 // In the files we have, there is also a section that describes
4320 // boundary ids for 1d lines. This section is not described in
4321 // https://www.simcenter.msstate.edu/software/documentation/ug_io/3d_grid_file_type_ugrid.html,
4322 // but we want to read that data anyway:
4323 unsigned int n_lines;
4324 in >> n_lines;
4325 std::vector<CellData<1>> objects_1d(n_lines);
4326 for (CellData<1> &line : objects_1d)
4327 {
4328 line.vertices.resize(2);
4329
4330 // Read vertex indices for the line. Then again translate to zero-based:
4331 for (unsigned int &vertex_index : line.vertices)
4332 {
4333 in >> vertex_index;
4334 --vertex_index;
4335 }
4336
4337 // Then also read the boundary id of the line:
4338 in >> line.boundary_id;
4339 }
4340
4341 // Now that we have everything, create the triangulation with it:
4342 if constexpr (dim == 2)
4343 {
4344 SubCellData face_data;
4345 face_data.boundary_lines = std::move(objects_1d);
4346 tria->create_triangulation(vertices, objects_2d, face_data);
4347 }
4348 else
4350}
4351
4352
4353
4354#ifdef DEAL_II_TRILINOS_WITH_SEACAS
4355// Namespace containing some extra functions for reading ExodusII files
4356namespace
4357{
4358 // Convert ExodusII strings to cell types. Use the number of nodes per element
4359 // to disambiguate some cases. If the conversion fails then a
4360 // ReferenceCells::Invalid<dim> is returned.
4361 template <int dim>
4363 exodusii_name_to_type(const std::string &type_name,
4364 const int n_nodes_per_element)
4365 {
4366 Assert(type_name.size() > 0, ExcInternalError());
4367 // Try to canonify the name by switching to upper case and removing
4368 // trailing numbers. This makes, e.g., pyramid, PYRAMID, PYRAMID5, and
4369 // PYRAMID13 all equal.
4370 std::string type_name_2 = type_name;
4371 std::transform(type_name_2.begin(),
4372 type_name_2.end(),
4373 type_name_2.begin(),
4374 static_cast<int (*)(int)>(std::toupper));
4375 const std::string numbers = "0123456789";
4376 type_name_2.erase(std::find_first_of(type_name_2.begin(),
4377 type_name_2.end(),
4378 numbers.begin(),
4379 numbers.end()),
4380 type_name_2.end());
4381
4382 // The manual specifies BAR, BEAM, and TRUSS: in practice people use EDGE
4383 if constexpr (dim == 1)
4384 {
4385 if (type_name_2 == "BAR" || type_name_2 == "BEAM" ||
4386 type_name_2 == "EDGE" || type_name_2 == "TRUSS")
4387 return ReferenceCells::Line;
4388 }
4389 else if constexpr (dim == 2)
4390 {
4391 if (type_name_2 == "TRI" || type_name_2 == "TRIANGLE")
4393 else if (type_name_2 == "QUAD" || type_name_2 == "QUADRILATERAL")
4395 else if (type_name_2 == "SHELL")
4396 {
4397 if (n_nodes_per_element == 3)
4399 else
4401 }
4402 }
4403 else if constexpr (dim == 3)
4404 {
4405 if (type_name_2 == "TET" || type_name_2 == "TETRA" ||
4406 type_name_2 == "TETRAHEDRON")
4408 else if (type_name_2 == "PYRA" || type_name_2 == "PYRAMID")
4410 else if (type_name_2 == "WEDGE")
4411 return ReferenceCells::Wedge;
4412 else if (type_name_2 == "HEX" || type_name_2 == "HEXAHEDRON")
4414 }
4415
4417 }
4418
4419 // Associate deal.II boundary ids with sidesets (a face can be in multiple
4420 // sidesets - to translate we assign each set of side set ids to a
4421 // boundary_id or manifold_id)
4422 template <int dim, int spacedim = dim>
4423 std::pair<SubCellData, std::vector<std::vector<int>>>
4424 read_exodusii_sidesets(const int ex_id,
4425 const int n_sidesets,
4426 const std::vector<CellData<dim>> &cells,
4427 const bool apply_all_indicators_to_manifolds)
4428 {
4429 SubCellData subcelldata;
4430 std::vector<std::vector<int>> b_or_m_id_to_sideset_ids;
4431 // boundary id 0 is the default
4432 b_or_m_id_to_sideset_ids.emplace_back();
4433 // deal.II does not support assigning boundary ids with nonzero
4434 // codimension meshes so completely skip this information in that case.
4435 //
4436 // Exodus prints warnings if we try to get empty sets so always check
4437 // first
4438 if (dim == spacedim && n_sidesets > 0)
4439 {
4440 std::vector<int> sideset_ids(n_sidesets);
4441 int ierr = ex_get_ids(ex_id, EX_SIDE_SET, sideset_ids.data());
4442 AssertThrowExodusII(ierr);
4443 std::sort(sideset_ids.begin(), sideset_ids.end());
4444
4445 // First collect all side sets on all boundary faces (indexed here as
4446 // ReferenceCells::max_n_faces<dim>() * cell_n + face_n). We then sort
4447 // and uniquify the side sets so that we can convert a set of side set
4448 // indices into a single deal.II boundary or manifold id (and save the
4449 // correspondence).
4450 std::size_t n_total_sides = 0;
4451 for (const int &sideset_id : sideset_ids)
4452 {
4453 int n_sides = -1;
4454 int n_distribution_factors = -1;
4455
4456 ierr = ex_get_set_param(ex_id,
4457 EX_SIDE_SET,
4458 sideset_id,
4459 &n_sides,
4460 &n_distribution_factors);
4461 AssertThrowExodusII(ierr);
4462 Assert(n_sides > 0, ExcInternalError());
4463 n_total_sides += std::size_t(n_sides);
4464 }
4465 std::vector<std::pair<std::size_t, int>> face_sidesets;
4466 face_sidesets.reserve(n_total_sides);
4467
4468 for (const int &sideset_id : sideset_ids)
4469 {
4470 int n_sides = -1;
4471 int n_distribution_factors = -1;
4472
4473 ierr = ex_get_set_param(ex_id,
4474 EX_SIDE_SET,
4475 sideset_id,
4476 &n_sides,
4477 &n_distribution_factors);
4478 AssertThrowExodusII(ierr);
4479 if (n_sides > 0)
4480 {
4481 std::vector<int> elements(n_sides);
4482 std::vector<int> faces(n_sides);
4483 ierr = ex_get_set(ex_id,
4484 EX_SIDE_SET,
4485 sideset_id,
4486 elements.data(),
4487 faces.data());
4488 AssertThrowExodusII(ierr);
4489
4490 // According to the manual (subsection 4.8): "The internal
4491 // number of an element numbering is defined implicitly by the
4492 // order in which it appears in the file. Elements are
4493 // numbered internally (beginning with 1) consecutively across
4494 // all element blocks." Hence element i in Exodus numbering is
4495 // entry i - 1 in the cells array.
4496 for (int side_n = 0; side_n < n_sides; ++side_n)
4497 {
4498 const long cell_n = elements[side_n] - 1;
4499 Assert(cell_n >= 0, ExcInternalError());
4500 const long face_n = faces[side_n] - 1;
4501 Assert(face_n >= 0, ExcInternalError());
4502 const std::size_t face_id =
4503 cell_n * ReferenceCells::max_n_faces<dim>() + face_n;
4504 face_sidesets.emplace_back(face_id, sideset_id);
4505 }
4506 }
4507 }
4508 // Do a full sort so we get consistent order of (face, sideset) pairs:
4509 std::sort(face_sidesets.begin(), face_sidesets.end());
4510
4511 // Collect into something we can sort by sideset ids (but avoid
4512 // copying any actual data, since we typically have multiple sidesets
4513 // per face):
4514 std::deque<ArrayView<std::pair<std::size_t, int>>>
4515 face_id_to_sideset_ids;
4516 if (face_sidesets.size() > 0)
4517 {
4518 auto start = face_sidesets.begin();
4519 for (auto it = face_sidesets.begin() + 1; it < face_sidesets.end();
4520 ++it)
4521 if (start->first != it->first)
4522 {
4523 face_id_to_sideset_ids.emplace_back(
4524 make_array_view(start, it));
4525 start = it;
4526 }
4527 face_id_to_sideset_ids.emplace_back(
4528 make_array_view(start, face_sidesets.end()));
4529 }
4530
4531 // Sort by sideset ids per face, so that faces with the same sidesets
4532 // are consecutive. Use a stable sort so we get consistent output (since
4533 // this step ignores face ids)
4534 std::stable_sort(face_id_to_sideset_ids.begin(),
4535 face_id_to_sideset_ids.end(),
4536 [](const auto &a, const auto &b) {
4537 return std::lexicographical_compare(
4538 a.begin(),
4539 a.end(),
4540 b.begin(),
4541 b.end(),
4542 [](const auto &c, const auto &d) {
4543 return c.second < d.second;
4544 });
4545 });
4546
4547 if (dim == 2)
4548 subcelldata.boundary_lines.reserve(face_id_to_sideset_ids.size());
4549 else if (dim == 3)
4550 subcelldata.boundary_quads.reserve(face_id_to_sideset_ids.size());
4551 types::boundary_id current_b_or_m_id = 0;
4552 std::vector<int> face_sideset_ids;
4553 for (const auto &pairs : face_id_to_sideset_ids)
4554 {
4555 const std::size_t face_id = pairs[0].first;
4556 Assert(std::all_of(pairs.begin(),
4557 pairs.end(),
4558 [&](const auto &a) {
4559 return a.first == face_id;
4560 }),
4562 face_sideset_ids.resize(pairs.size());
4563 for (std::size_t i = 0; i < pairs.size(); ++i)
4564 face_sideset_ids[i] = pairs[i].second;
4565
4566 if (face_sideset_ids != b_or_m_id_to_sideset_ids.back())
4567 {
4568 // Since we sorted by sideset ids we are guaranteed that if
4569 // this doesn't match the last set then it has not yet been
4570 // seen (but check that via assertion)
4571 Assert(std::find(b_or_m_id_to_sideset_ids.begin(),
4572 b_or_m_id_to_sideset_ids.end(),
4573 face_sideset_ids) ==
4574 b_or_m_id_to_sideset_ids.end(),
4576 ++current_b_or_m_id;
4577 b_or_m_id_to_sideset_ids.emplace_back(face_sideset_ids.begin(),
4578 face_sideset_ids.end());
4579 Assert(current_b_or_m_id == b_or_m_id_to_sideset_ids.size() - 1,
4581 }
4582 // Record the b_or_m_id of the current face.
4583 const unsigned int local_face_n =
4585 const CellData<dim> &cell =
4586 cells[face_id / ReferenceCells::max_n_faces<dim>()];
4587 const auto reference_cell =
4589 cell.vertices.size());
4590 const unsigned int deal_face_n =
4591 reference_cell.exodusii_face_to_deal_face(local_face_n);
4592 const auto face_reference_cell =
4593 reference_cell.face_reference_cell(deal_face_n);
4594
4595 // The orientation we pick doesn't matter here since when we
4596 // create the Triangulation we will sort the vertices for each
4597 // CellData object created here.
4598 if (dim == 2)
4599 {
4600 CellData<1> boundary_line(face_reference_cell.n_vertices());
4601 if (apply_all_indicators_to_manifolds)
4602 boundary_line.manifold_id = current_b_or_m_id;
4603 else
4604 boundary_line.boundary_id = current_b_or_m_id;
4605 for (unsigned int j = 0; j < face_reference_cell.n_vertices();
4606 ++j)
4607 boundary_line.vertices[j] =
4608 cell.vertices[reference_cell.face_to_cell_vertices(
4609 deal_face_n, j, 0)];
4610
4611 subcelldata.boundary_lines.push_back(std::move(boundary_line));
4612 }
4613 else if (dim == 3)
4614 {
4615 CellData<2> boundary_quad(face_reference_cell.n_vertices());
4616 if (apply_all_indicators_to_manifolds)
4617 boundary_quad.manifold_id = current_b_or_m_id;
4618 else
4619 boundary_quad.boundary_id = current_b_or_m_id;
4620 for (unsigned int j = 0; j < face_reference_cell.n_vertices();
4621 ++j)
4622 boundary_quad.vertices[j] =
4623 cell.vertices[reference_cell.face_to_cell_vertices(
4624 deal_face_n, j, 0)];
4625
4626 subcelldata.boundary_quads.push_back(std::move(boundary_quad));
4627 }
4628 }
4629 }
4630
4631 return std::make_pair(std::move(subcelldata),
4632 std::move(b_or_m_id_to_sideset_ids));
4633 }
4634} // namespace
4635#endif
4636
4637template <int dim, int spacedim>
4640 const std::string &filename,
4641 const bool apply_all_indicators_to_manifolds)
4642{
4643#ifdef DEAL_II_TRILINOS_WITH_SEACAS
4644 // deal.II always uses double precision numbers for geometry
4645 int component_word_size = sizeof(double);
4646 // setting to zero uses the stored word size
4647 int floating_point_word_size = 0;
4648 float ex_version = 0.0;
4649
4650 const int ex_id = ex_open(filename.c_str(),
4651 EX_READ,
4652 &component_word_size,
4653 &floating_point_word_size,
4654 &ex_version);
4655 AssertThrow(ex_id > 0,
4656 ExcMessage("ExodusII failed to open the specified input file."));
4657
4658 // Read basic mesh information:
4659 std::vector<char> string_temp(MAX_LINE_LENGTH + 1, '\0');
4660 int mesh_dimension = 0;
4661 int n_nodes = 0;
4662 int n_elements = 0;
4663 int n_element_blocks = 0;
4664 int n_nodesets = 0;
4665 int n_sidesets = 0;
4666
4667 int ierr = ex_get_init(ex_id,
4668 string_temp.data(),
4669 &mesh_dimension,
4670 &n_nodes,
4671 &n_elements,
4672 &n_element_blocks,
4673 &n_nodesets,
4674 &n_sidesets);
4675 AssertThrowExodusII(ierr);
4676 AssertDimension(mesh_dimension, spacedim);
4677
4678 // Read nodes:
4679 //
4680 // Even if there is a node numbering array the values stored inside the
4681 // ExodusII file must use the contiguous, internal ordering (see Section 4.5
4682 // of the manual - "Internal (contiguously numbered) node and element IDs
4683 // must be used for all data structures that contain node or element numbers
4684 // (IDs), including node set node lists, side set element lists, and element
4685 // connectivity.")
4686 std::vector<Point<spacedim>> vertices;
4687 vertices.reserve(n_nodes);
4688 {
4689 std::vector<double> xs(n_nodes);
4690 std::vector<double> ys(n_nodes);
4691 std::vector<double> zs(n_nodes);
4692
4693 ierr = ex_get_coord(ex_id, xs.data(), ys.data(), zs.data());
4694 AssertThrowExodusII(ierr);
4695
4696 for (int vertex_n = 0; vertex_n < n_nodes; ++vertex_n)
4697 {
4698 switch (spacedim)
4699 {
4700 case 1:
4701 vertices.emplace_back(xs[vertex_n]);
4702 break;
4703 case 2:
4704 vertices.emplace_back(xs[vertex_n], ys[vertex_n]);
4705 break;
4706 case 3:
4707 vertices.emplace_back(xs[vertex_n], ys[vertex_n], zs[vertex_n]);
4708 break;
4709 default:
4710 Assert(spacedim <= 3, ExcNotImplemented());
4711 }
4712 }
4713 }
4714
4715 std::vector<int> element_block_ids(n_element_blocks);
4716 ierr = ex_get_ids(ex_id, EX_ELEM_BLOCK, element_block_ids.data());
4717 AssertThrowExodusII(ierr);
4718
4719 std::vector<CellData<dim>> cells;
4720 cells.reserve(n_elements);
4721 // Elements are grouped together by same reference cell type in element
4722 // blocks. There may be multiple blocks for a single reference cell type,
4723 // but "each element block may contain only one element type".
4724 for (const int element_block_id : element_block_ids)
4725 {
4726 std::fill(string_temp.begin(), string_temp.end(), '\0');
4727 int n_block_elements = 0;
4728 int n_nodes_per_element = 0;
4729 int n_edges_per_element = 0;
4730 int n_faces_per_element = 0;
4731 int n_attributes_per_element = 0;
4732
4733 // Extract element data.
4734 ierr = ex_get_block(ex_id,
4735 EX_ELEM_BLOCK,
4736 element_block_id,
4737 string_temp.data(),
4738 &n_block_elements,
4739 &n_nodes_per_element,
4740 &n_edges_per_element,
4741 &n_faces_per_element,
4742 &n_attributes_per_element);
4743 AssertThrowExodusII(ierr);
4744 const auto type =
4745 exodusii_name_to_type<dim>(string_temp.data(), n_nodes_per_element);
4747 ExcMessage(
4748 "The ExodusII block " + std::to_string(element_block_id) +
4749 " with element type " + std::string(string_temp.data()) +
4750 " does not have a corresponding ReferenceCell<dim> with a"
4751 " dimension matching the topological mesh dimension " +
4752 std::to_string(dim) + "."));
4753
4754 // The number of nodes per element may be larger than what we want to
4755 // read - for example, if the Exodus file contains a QUAD9 element, we
4756 // only want to read the first four values and ignore the rest.
4757 Assert(int(type.n_vertices()) <= n_nodes_per_element, ExcInternalError());
4758
4759 std::vector<int> connection(n_nodes_per_element * n_block_elements);
4760 ierr = ex_get_conn(ex_id,
4761 EX_ELEM_BLOCK,
4762 element_block_id,
4763 connection.data(),
4764 nullptr,
4765 nullptr);
4766 AssertThrowExodusII(ierr);
4767
4768 for (unsigned int elem_n = 0; elem_n < connection.size();
4769 elem_n += n_nodes_per_element)
4770 {
4771 CellData<dim> cell(type.n_vertices());
4772 for (const unsigned int i : type.vertex_indices())
4773 {
4774 cell.vertices[type.exodusii_vertex_to_deal_vertex(i)] =
4775 connection[elem_n + i] - 1;
4776 }
4777 cell.material_id = element_block_id;
4778 cells.push_back(std::move(cell));
4779 }
4780 }
4781
4782 // Extract boundary data.
4783 auto pair = read_exodusii_sidesets<dim, spacedim>(
4784 ex_id, n_sidesets, cells, apply_all_indicators_to_manifolds);
4785 ierr = ex_close(ex_id);
4786 AssertThrowExodusII(ierr);
4787
4788 apply_grid_fixup_functions(vertices, cells, pair.first);
4789 tria->create_triangulation(vertices, cells, pair.first);
4790 ExodusIIData out;
4791 out.id_to_sideset_ids = std::move(pair.second);
4792 return out;
4793#else
4794 (void)filename;
4795 (void)apply_all_indicators_to_manifolds;
4796 AssertThrow(false, ExcNeedsExodusII());
4797 return {};
4798#endif
4799}
4800
4801
4802template <int dim, int spacedim>
4803void
4805{
4806 std::string line;
4807 while (in)
4808 {
4809 // get line
4810 getline(in, line);
4811
4812 // check if this is a line that
4813 // consists only of spaces, and
4814 // if not put the whole thing
4815 // back and return
4816 if (std::find_if(line.begin(), line.end(), [](const char c) {
4817 return c != ' ';
4818 }) != line.end())
4819 {
4820 in.putback('\n');
4821 for (int i = line.size() - 1; i >= 0; --i)
4822 in.putback(line[i]);
4823 return;
4824 }
4825
4826 // else: go on with next line
4827 }
4828}
4829
4830
4831
4832template <int dim, int spacedim>
4833void
4835 const char comment_start)
4836{
4837 char c;
4838 // loop over the following comment
4839 // lines
4840 while (in.get(c) && c == comment_start)
4841 // loop over the characters after
4842 // the comment starter
4843 while (in.get() != '\n')
4844 ;
4845
4846
4847 // put back first character of
4848 // first non-comment line
4849 if (in)
4850 in.putback(c);
4851
4852 // at last: skip additional empty lines, if present
4853 skip_empty_lines(in);
4854}
4855
4856
4857
4858template <int dim, int spacedim>
4859void
4861 const std::vector<CellData<dim>> & /*cells*/,
4862 const std::vector<Point<spacedim>> & /*vertices*/,
4863 std::ostream & /*out*/)
4864{
4866}
4867
4868
4869
4870template <>
4871void
4873 const std::vector<Point<2>> &vertices,
4874 std::ostream &out)
4875{
4876 double min_x = vertices[cells[0].vertices[0]][0],
4877 max_x = vertices[cells[0].vertices[0]][0],
4878 min_y = vertices[cells[0].vertices[0]][1],
4879 max_y = vertices[cells[0].vertices[0]][1];
4880
4881 for (unsigned int i = 0; i < cells.size(); ++i)
4882 {
4883 for (const auto vertex : cells[i].vertices)
4884 {
4885 const Point<2> &p = vertices[vertex];
4886
4887 if (p[0] < min_x)
4888 min_x = p[0];
4889 if (p[0] > max_x)
4890 max_x = p[0];
4891 if (p[1] < min_y)
4892 min_y = p[1];
4893 if (p[1] > max_y)
4894 max_y = p[1];
4895 }
4896
4897 out << "# cell " << i << std::endl;
4898 Point<2> center;
4899 for (const auto vertex : cells[i].vertices)
4900 center += vertices[vertex];
4901 center /= 4;
4902
4903 out << "set label \"" << i << "\" at " << center[0] << ',' << center[1]
4904 << " center" << std::endl;
4905
4906 // first two line right direction
4907 for (unsigned int f = 0; f < 2; ++f)
4908 out << "set arrow from " << vertices[cells[i].vertices[f]][0] << ','
4909 << vertices[cells[i].vertices[f]][1] << " to "
4910 << vertices[cells[i].vertices[(f + 1) % 4]][0] << ','
4911 << vertices[cells[i].vertices[(f + 1) % 4]][1] << std::endl;
4912 // other two lines reverse direction
4913 for (unsigned int f = 2; f < 4; ++f)
4914 out << "set arrow from " << vertices[cells[i].vertices[(f + 1) % 4]][0]
4915 << ',' << vertices[cells[i].vertices[(f + 1) % 4]][1] << " to "
4916 << vertices[cells[i].vertices[f]][0] << ','
4917 << vertices[cells[i].vertices[f]][1] << std::endl;
4918 out << std::endl;
4919 }
4920
4921
4922 out << std::endl
4923 << "set nokey" << std::endl
4924 << "pl [" << min_x << ':' << max_x << "][" << min_y << ':' << max_y
4925 << "] " << min_y << std::endl
4926 << "pause -1" << std::endl;
4927}
4928
4929
4930
4931template <>
4932void
4934 const std::vector<Point<3>> &vertices,
4935 std::ostream &out)
4936{
4937 for (const auto &cell : cells)
4938 {
4939 // line 0
4940 out << vertices[cell.vertices[0]] << std::endl
4941 << vertices[cell.vertices[1]] << std::endl
4942 << std::endl
4943 << std::endl;
4944 // line 1
4945 out << vertices[cell.vertices[1]] << std::endl
4946 << vertices[cell.vertices[2]] << std::endl
4947 << std::endl
4948 << std::endl;
4949 // line 2
4950 out << vertices[cell.vertices[3]] << std::endl
4951 << vertices[cell.vertices[2]] << std::endl
4952 << std::endl
4953 << std::endl;
4954 // line 3
4955 out << vertices[cell.vertices[0]] << std::endl
4956 << vertices[cell.vertices[3]] << std::endl
4957 << std::endl
4958 << std::endl;
4959 // line 4
4960 out << vertices[cell.vertices[4]] << std::endl
4961 << vertices[cell.vertices[5]] << std::endl
4962 << std::endl
4963 << std::endl;
4964 // line 5
4965 out << vertices[cell.vertices[5]] << std::endl
4966 << vertices[cell.vertices[6]] << std::endl
4967 << std::endl
4968 << std::endl;
4969 // line 6
4970 out << vertices[cell.vertices[7]] << std::endl
4971 << vertices[cell.vertices[6]] << std::endl
4972 << std::endl
4973 << std::endl;
4974 // line 7
4975 out << vertices[cell.vertices[4]] << std::endl
4976 << vertices[cell.vertices[7]] << std::endl
4977 << std::endl
4978 << std::endl;
4979 // line 8
4980 out << vertices[cell.vertices[0]] << std::endl
4981 << vertices[cell.vertices[4]] << std::endl
4982 << std::endl
4983 << std::endl;
4984 // line 9
4985 out << vertices[cell.vertices[1]] << std::endl
4986 << vertices[cell.vertices[5]] << std::endl
4987 << std::endl
4988 << std::endl;
4989 // line 10
4990 out << vertices[cell.vertices[2]] << std::endl
4991 << vertices[cell.vertices[6]] << std::endl
4992 << std::endl
4993 << std::endl;
4994 // line 11
4995 out << vertices[cell.vertices[3]] << std::endl
4996 << vertices[cell.vertices[7]] << std::endl
4997 << std::endl
4998 << std::endl;
4999 }
5000}
5001
5002
5003
5004template <int dim, int spacedim>
5005void
5006GridIn<dim, spacedim>::read(const std::string &filename, Format format)
5007{
5008 // Check early that the file actually exists and if not throw ExcFileNotOpen.
5009 AssertThrow(std::filesystem::exists(filename), ExcFileNotOpen(filename));
5010
5011 if (format == Default)
5012 {
5013 const std::string::size_type slashpos = filename.find_last_of('/');
5014 const std::string::size_type dotpos = filename.find_last_of('.');
5015 if (dotpos < filename.size() &&
5016 (dotpos > slashpos || slashpos == std::string::npos))
5017 {
5018 std::string ext = filename.substr(dotpos + 1);
5019 format = parse_format(ext);
5020 }
5021 }
5022
5023 if (format == assimp)
5024 {
5025 read_assimp(filename);
5026 }
5027 else if (format == exodusii)
5028 {
5029 read_exodusii(filename);
5030 }
5031 else
5032 {
5033 std::ifstream in(filename);
5034 read(in, format);
5035 }
5036}
5037
5038
5039template <int dim, int spacedim>
5040void
5041GridIn<dim, spacedim>::read(std::istream &in, Format format)
5042{
5043 if (format == Default)
5044 format = default_format;
5045
5046 switch (format)
5047 {
5048 case dbmesh:
5049 read_dbmesh(in);
5050 return;
5051
5052 case msh:
5053 read_msh(in);
5054 return;
5055
5056 case vtk:
5057 read_vtk(in);
5058 return;
5059
5060 case vtu:
5061 read_vtu(in);
5062 return;
5063
5064 case unv:
5065 read_unv(in);
5066 return;
5067
5068 case ucd:
5069 read_ucd(in);
5070 return;
5071
5072 case abaqus:
5073 read_abaqus(in);
5074 return;
5075
5076 case xda:
5077 read_xda(in);
5078 return;
5079
5080 case tecplot:
5081 read_tecplot(in);
5082 return;
5083
5084 case ugrid:
5085 read_ugrid(in);
5086 return;
5087
5088 case assimp:
5089 Assert(false,
5090 ExcMessage("There is no read_assimp(istream &) function. "
5091 "Use the read_assimp(string &filename, ...) "
5092 "functions, instead."));
5093 return;
5094
5095 case exodusii:
5096 Assert(false,
5097 ExcMessage("There is no read_exodusii(istream &) function. "
5098 "Use the read_exodusii(string &filename, ...) "
5099 "function, instead."));
5100 return;
5101
5102 case Default:
5103 break;
5104 }
5106}
5107
5108
5109
5110template <int dim, int spacedim>
5111std::string
5113{
5114 switch (format)
5115 {
5116 case dbmesh:
5117 return ".dbmesh";
5118 case exodusii:
5119 return ".e";
5120 case msh:
5121 return ".msh";
5122 case vtk:
5123 return ".vtk";
5124 case vtu:
5125 return ".vtu";
5126 case unv:
5127 return ".unv";
5128 case ucd:
5129 return ".inp";
5130 case abaqus:
5131 return ".inp"; // Typical suffix for Abaqus mesh files conflicts with
5132 // UCD.
5133 case xda:
5134 return ".xda";
5135 case tecplot:
5136 return ".dat";
5137 case ugrid:
5138 return ".txt";
5139 default:
5141 return ".unknown_format";
5142 }
5143}
5144
5145
5146
5147template <int dim, int spacedim>
5149GridIn<dim, spacedim>::parse_format(const std::string &format_name)
5150{
5151 if (format_name == "dbmesh")
5152 return dbmesh;
5153
5154 if (format_name == "exodusii")
5155 return exodusii;
5156
5157 if (format_name == "msh")
5158 return msh;
5159
5160 if (format_name == "unv")
5161 return unv;
5162
5163 if (format_name == "vtk")
5164 return vtk;
5165
5166 if (format_name == "vtu")
5167 return vtu;
5168
5169 // This is also the typical extension of Abaqus input files.
5170 if (format_name == "inp")
5171 return ucd;
5172
5173 if (format_name == "ucd")
5174 return ucd;
5175
5176 if (format_name == "xda")
5177 return xda;
5178
5179 if (format_name == "tecplot")
5180 return tecplot;
5181
5182 if (format_name == "dat")
5183 return tecplot;
5184
5185 if (format_name == "ugrid")
5186 return ugrid;
5187
5188 if (format_name == "plt")
5189 // Actually, this is the extension for the
5190 // tecplot binary format, which we do not
5191 // support right now. However, some people
5192 // tend to create tecplot ascii files with
5193 // the extension 'plt' instead of
5194 // 'dat'. Thus, include this extension
5195 // here. If it actually is a binary file,
5196 // the read_tecplot() function will fail
5197 // and throw an exception, anyway.
5198 return tecplot;
5199
5200 AssertThrow(false, ExcInvalidState());
5201 // return something weird
5202 return Format(Default);
5203}
5204
5205
5206
5207template <int dim, int spacedim>
5208std::string
5210{
5211 return "dbmesh|exodusii|msh|unv|vtk|vtu|ucd|abaqus|xda|tecplot|assimp|ugrid";
5212}
5213
5214
5215
5216namespace
5217{
5218 template <int dim, int spacedim>
5219 Abaqus_to_UCD<dim, spacedim>::Abaqus_to_UCD()
5220 : tolerance(5e-16) // Used to offset Cubit tolerance error when outputting
5221 // value close to zero
5222 {
5223 AssertThrow(spacedim == 2 || spacedim == 3, ExcNotImplemented());
5224 }
5225
5226
5227
5228 // Convert from a string to some other data type
5229 // Reference: http://www.codeguru.com/forum/showthread.php?t=231054
5230 template <class T>
5231 bool
5232 from_string(T &t, const std::string &s, std::ios_base &(*f)(std::ios_base &))
5233 {
5234 std::istringstream iss(s);
5235 return !(iss >> f >> t).fail();
5236 }
5237
5238
5239
5240 // Extract an integer from a string
5241 int
5242 extract_int(const std::string &s)
5243 {
5244 std::string tmp;
5245 for (const char c : s)
5246 {
5247 if (std::isdigit(c) != 0)
5248 {
5249 tmp += c;
5250 }
5251 }
5252
5253 int number = 0;
5254 from_string(number, tmp, std::dec);
5255 return number;
5256 }
5257
5258
5259
5260 template <int dim, int spacedim>
5261 void
5262 Abaqus_to_UCD<dim, spacedim>::read_in_abaqus(std::istream &input_stream)
5263 {
5264 // References:
5265 // http://www.egr.msu.edu/software/abaqus/Documentation/docs/v6.7/books/usb/default.htm?startat=pt01ch02.html
5266 // http://www.cprogramming.com/tutorial/string.html
5267
5268 AssertThrow(input_stream.fail() == false, ExcIO());
5269 std::string line;
5270
5271 while (std::getline(input_stream, line))
5272 {
5273 cont:
5274 std::transform(line.begin(),
5275 line.end(),
5276 line.begin(),
5277 static_cast<int (*)(int)>(std::toupper));
5278
5279 if (line.compare("*HEADING") == 0 || line.compare(0, 2, "**") == 0 ||
5280 line.compare(0, 5, "*PART") == 0)
5281 {
5282 // Skip header and comments
5283 while (std::getline(input_stream, line))
5284 {
5285 if (line[0] == '*')
5286 goto cont; // My eyes, they burn!
5287 }
5288 }
5289 else if (line.compare(0, 5, "*NODE") == 0)
5290 {
5291 // Extract list of vertices
5292 // Header line might be:
5293 // *NODE, NSET=ALLNODES
5294 // *NODE
5295
5296 // Contains lines in the form:
5297 // Index, x, y, z
5298 while (std::getline(input_stream, line))
5299 {
5300 if (line[0] == '*')
5301 goto cont;
5302
5303 std::vector<double> node(spacedim + 1);
5304
5305 std::istringstream iss(line);
5306 char comma;
5307 for (unsigned int i = 0; i < spacedim + 1; ++i)
5308 iss >> node[i] >> comma;
5309
5310 node_list.push_back(node);
5311 }
5312 }
5313 else if (line.compare(0, 8, "*ELEMENT") == 0)
5314 {
5315 // Element construction.
5316 // There are different header formats, the details
5317 // of which we're not particularly interested in except
5318 // whether they represent quads or hexahedrals.
5319 // *ELEMENT, TYPE=S4R, ELSET=EB<material id>
5320 // *ELEMENT, TYPE=C3d8R, ELSET=EB<material id>
5321 // *ELEMENT, TYPE=C3d8
5322 // Elements itself (n=4 or n=8):
5323 // Index, i[0], ..., i[n]
5324
5325 int material = 0;
5326 // Scan for material id
5327 {
5328 const std::string before_material = "ELSET=EB";
5329 const std::size_t idx = line.find(before_material);
5330 if (idx != std::string::npos)
5331 {
5332 from_string(material,
5333 line.substr(idx + before_material.size()),
5334 std::dec);
5335 }
5336 }
5337
5338 // Read ELEMENT definition
5339 while (std::getline(input_stream, line))
5340 {
5341 if (line[0] == '*')
5342 goto cont;
5343
5344 std::istringstream iss(line);
5345 char comma;
5346
5347 // We will store the material id in the zeroth entry of the
5348 // vector and the rest of the elements represent the global
5349 // node numbers
5350 const unsigned int n_data_per_cell =
5352 std::vector<double> cell(n_data_per_cell);
5353 for (unsigned int i = 0; i < n_data_per_cell; ++i)
5354 iss >> cell[i] >> comma;
5355
5356 // Overwrite cell index from file by material
5357 cell[0] = static_cast<double>(material);
5358 cell_list.push_back(cell);
5359 }
5360 }
5361 else if (line.compare(0, 8, "*SURFACE") == 0)
5362 {
5363 // Extract the definitions of boundary surfaces
5364 // Old format from Cubit:
5365 // *SURFACE, NAME=SS<boundary indicator>
5366 // <element index>, S<face number>
5367 // Abaqus default format:
5368 // *SURFACE, TYPE=ELEMENT, NAME=SURF-<indicator>
5369
5370 // Get name of the surface and extract id from it;
5371 // this will be the boundary indicator
5372 const std::string name_key = "NAME=";
5373 const std::size_t name_idx_start =
5374 line.find(name_key) + name_key.size();
5375 std::size_t name_idx_end = line.find(',', name_idx_start);
5376 if (name_idx_end == std::string::npos)
5377 {
5378 name_idx_end = line.size();
5379 }
5380 const int b_indicator = extract_int(
5381 line.substr(name_idx_start, name_idx_end - name_idx_start));
5382
5383 // Read SURFACE definition
5384 // Note that the orientation of the faces is embedded within the
5385 // definition of each "set" of faces that comprise the surface
5386 // These are either marked by an "S" or "E" in 3d or 2d
5387 // respectively.
5388 while (std::getline(input_stream, line))
5389 {
5390 if (line[0] == '*')
5391 goto cont;
5392
5393 // Change all characters to upper case
5394 std::transform(line.begin(),
5395 line.end(),
5396 line.begin(),
5397 static_cast<int (*)(int)>(std::toupper));
5398
5399 // Surface can be created from ELSET, or directly from cells
5400 // If elsets_list contains a key with specific name - refers
5401 // to that ELSET, otherwise refers to cell
5402 std::istringstream iss(line);
5403 int el_idx;
5404 int face_number;
5405 char temp;
5406
5407 // Get relevant faces, taking into account the element
5408 // orientation
5409 std::vector<double> quad_node_list;
5410 const std::string elset_name = line.substr(0, line.find(','));
5411 if (elsets_list.count(elset_name) != 0)
5412 {
5413 // Surface refers to ELSET
5414 std::string stmp;
5415 iss >> stmp >> temp >> face_number;
5416
5417 const std::vector<int> cells = elsets_list[elset_name];
5418 for (const int cell : cells)
5419 {
5420 el_idx = cell;
5421 quad_node_list =
5422 get_global_node_numbers(el_idx, face_number);
5423 quad_node_list.insert(quad_node_list.begin(),
5424 b_indicator);
5425
5426 face_list.push_back(quad_node_list);
5427 }
5428 }
5429 else
5430 {
5431 // Surface refers directly to elements
5432 char comma;
5433 iss >> el_idx >> comma >> temp >> face_number;
5434 quad_node_list =
5435 get_global_node_numbers(el_idx, face_number);
5436 quad_node_list.insert(quad_node_list.begin(), b_indicator);
5437
5438 face_list.push_back(quad_node_list);
5439 }
5440 }
5441 }
5442 else if (line.compare(0, 6, "*ELSET") == 0)
5443 {
5444 // Get ELSET name.
5445 // Materials are attached to elsets with specific name
5446 std::string elset_name;
5447 {
5448 const std::string elset_key = "*ELSET, ELSET=";
5449 const std::size_t idx = line.find(elset_key);
5450 if (idx != std::string::npos)
5451 {
5452 const std::string comma = ",";
5453 const std::size_t first_comma = line.find(comma);
5454 const std::size_t second_comma =
5455 line.find(comma, first_comma + 1);
5456 const std::size_t elset_name_start =
5457 line.find(elset_key) + elset_key.size();
5458 elset_name = line.substr(elset_name_start,
5459 second_comma - elset_name_start);
5460 }
5461 }
5462
5463 // There are two possibilities of storing cells numbers in ELSET:
5464 // 1. If the header contains the 'GENERATE' keyword, then the next
5465 // line describes range of cells as:
5466 // cell_id_start, cell_id_end, cell_step
5467 // 2. If the header does not contain the 'GENERATE' keyword, then
5468 // the next lines contain cells numbers
5469 std::vector<int> elements;
5470 const std::size_t generate_idx = line.find("GENERATE");
5471 if (generate_idx != std::string::npos)
5472 {
5473 // Option (1)
5474 std::getline(input_stream, line);
5475 std::istringstream iss(line);
5476 char comma;
5477 int elid_start;
5478 int elid_end;
5479 int elis_step = 1; // Default if case stride not provided
5480
5481 // Some files don't have the stride size
5482 // Compare mesh test cases ./grids/abaqus/3d/other_simple.inp
5483 // to
5484 // ./grids/abaqus/2d/2d_test_abaqus.inp
5485 iss >> elid_start >> comma >> elid_end;
5486 AssertThrow(comma == ',',
5487 ExcMessage(
5488 std::string(
5489 "While reading an ABAQUS file, the reader "
5490 "expected a comma but found a <") +
5491 comma + "> in the line <" + line + ">."));
5493 elid_start <= elid_end,
5494 ExcMessage(
5495 std::string(
5496 "While reading an ABAQUS file, the reader encountered "
5497 "a GENERATE statement in which the upper bound <") +
5498 Utilities::int_to_string(elid_end) +
5499 "> for the element numbers is not larger or equal "
5500 "than the lower bound <" +
5501 Utilities::int_to_string(elid_start) + ">."));
5502
5503 // https://stackoverflow.com/questions/8046357/how-do-i-check-if-a-stringstream-variable-is-empty-null
5504 if (iss.rdbuf()->in_avail() != 0)
5505 iss >> comma >> elis_step;
5506 AssertThrow(comma == ',',
5507 ExcMessage(
5508 std::string(
5509 "While reading an ABAQUS file, the reader "
5510 "expected a comma but found a <") +
5511 comma + "> in the line <" + line + ">."));
5512
5513 for (int i = elid_start; i <= elid_end; i += elis_step)
5514 elements.push_back(i);
5515 elsets_list[elset_name] = elements;
5516
5517 std::getline(input_stream, line);
5518 }
5519 else
5520 {
5521 // Option (2)
5522 while (std::getline(input_stream, line))
5523 {
5524 if (line[0] == '*')
5525 break;
5526
5527 std::istringstream iss(line);
5528 char comma;
5529 int elid;
5530 while (!iss.eof())
5531 {
5532 iss >> elid >> comma;
5534 comma == ',',
5535 ExcMessage(
5536 std::string(
5537 "While reading an ABAQUS file, the reader "
5538 "expected a comma but found a <") +
5539 comma + "> in the line <" + line + ">."));
5540
5541 elements.push_back(elid);
5542 }
5543 }
5544
5545 elsets_list[elset_name] = elements;
5546 }
5547
5548 goto cont;
5549 }
5550 else if (line.compare(0, 5, "*NSET") == 0)
5551 {
5552 // Skip nodesets; we have no use for them
5553 while (std::getline(input_stream, line))
5554 {
5555 if (line[0] == '*')
5556 goto cont;
5557 }
5558 }
5559 else if (line.compare(0, 14, "*SOLID SECTION") == 0)
5560 {
5561 // The ELSET name, which describes a section for particular
5562 // material
5563 const std::string elset_key = "ELSET=";
5564 const std::size_t elset_start =
5565 line.find("ELSET=") + elset_key.size();
5566 const std::size_t elset_end = line.find(',', elset_start + 1);
5567 const std::string elset_name =
5568 line.substr(elset_start, elset_end - elset_start);
5569
5570 // Solid material definition.
5571 // We assume that material id is taken from material name,
5572 // eg. "Material-1" -> ID=1
5573 const std::string material_key = "MATERIAL=";
5574 const std::size_t last_equal =
5575 line.find("MATERIAL=") + material_key.size();
5576 const std::size_t material_id_start = line.find('-', last_equal);
5577 int material_id = 0;
5578 from_string(material_id,
5579 line.substr(material_id_start + 1),
5580 std::dec);
5581
5582 // Assign material id to cells
5583 const std::vector<int> &elset_cells = elsets_list[elset_name];
5584 for (const int elset_cell : elset_cells)
5585 {
5586 const int cell_id = elset_cell - 1;
5587 cell_list[cell_id][0] = material_id;
5588 }
5589 }
5590 // Note: All other lines / entries are ignored
5591 }
5592 }
5593
5594 template <int dim, int spacedim>
5595 std::vector<double>
5596 Abaqus_to_UCD<dim, spacedim>::get_global_node_numbers(
5597 const int face_cell_no,
5598 const int face_cell_face_no) const
5599 {
5600 std::vector<double> quad_node_list(GeometryInfo<dim>::vertices_per_face);
5601
5602 // Given the indexing below, face_cell_no-1 must be a valid index:
5603 Assert((face_cell_no >= 1) &&
5604 (static_cast<typename decltype(cell_list)::size_type>(
5605 face_cell_no) <= cell_list.size()),
5607
5608 // These orderings were reverse engineered by hand and may
5609 // conceivably be erroneous.
5610 // TODO: Currently one test (2d unstructured mesh) in the test
5611 // suite fails, presumably because of an ordering issue.
5612 if (dim == 2)
5613 {
5614 if (face_cell_face_no == 1)
5615 {
5616 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5617 quad_node_list[1] = cell_list[face_cell_no - 1][2];
5618 }
5619 else if (face_cell_face_no == 2)
5620 {
5621 quad_node_list[0] = cell_list[face_cell_no - 1][2];
5622 quad_node_list[1] = cell_list[face_cell_no - 1][3];
5623 }
5624 else if (face_cell_face_no == 3)
5625 {
5626 quad_node_list[0] = cell_list[face_cell_no - 1][3];
5627 quad_node_list[1] = cell_list[face_cell_no - 1][4];
5628 }
5629 else if (face_cell_face_no == 4)
5630 {
5631 quad_node_list[0] = cell_list[face_cell_no - 1][4];
5632 quad_node_list[1] = cell_list[face_cell_no - 1][1];
5633 }
5634 else
5635 {
5636 AssertThrow(face_cell_face_no <= 4,
5637 ExcMessage("Invalid face number in 2d"));
5638 }
5639 }
5640 else if (dim == 3)
5641 {
5642 if (face_cell_face_no == 1)
5643 {
5644 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5645 quad_node_list[1] = cell_list[face_cell_no - 1][4];
5646 quad_node_list[2] = cell_list[face_cell_no - 1][3];
5647 quad_node_list[3] = cell_list[face_cell_no - 1][2];
5648 }
5649 else if (face_cell_face_no == 2)
5650 {
5651 quad_node_list[0] = cell_list[face_cell_no - 1][5];
5652 quad_node_list[1] = cell_list[face_cell_no - 1][8];
5653 quad_node_list[2] = cell_list[face_cell_no - 1][7];
5654 quad_node_list[3] = cell_list[face_cell_no - 1][6];
5655 }
5656 else if (face_cell_face_no == 3)
5657 {
5658 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5659 quad_node_list[1] = cell_list[face_cell_no - 1][2];
5660 quad_node_list[2] = cell_list[face_cell_no - 1][6];
5661 quad_node_list[3] = cell_list[face_cell_no - 1][5];
5662 }
5663 else if (face_cell_face_no == 4)
5664 {
5665 quad_node_list[0] = cell_list[face_cell_no - 1][2];
5666 quad_node_list[1] = cell_list[face_cell_no - 1][3];
5667 quad_node_list[2] = cell_list[face_cell_no - 1][7];
5668 quad_node_list[3] = cell_list[face_cell_no - 1][6];
5669 }
5670 else if (face_cell_face_no == 5)
5671 {
5672 quad_node_list[0] = cell_list[face_cell_no - 1][3];
5673 quad_node_list[1] = cell_list[face_cell_no - 1][4];
5674 quad_node_list[2] = cell_list[face_cell_no - 1][8];
5675 quad_node_list[3] = cell_list[face_cell_no - 1][7];
5676 }
5677 else if (face_cell_face_no == 6)
5678 {
5679 quad_node_list[0] = cell_list[face_cell_no - 1][1];
5680 quad_node_list[1] = cell_list[face_cell_no - 1][5];
5681 quad_node_list[2] = cell_list[face_cell_no - 1][8];
5682 quad_node_list[3] = cell_list[face_cell_no - 1][4];
5683 }
5684 else
5685 {
5686 AssertThrow(face_cell_no <= 6,
5687 ExcMessage("Invalid face number in 3d"));
5688 }
5689 }
5690 else
5691 {
5692 AssertThrow(dim == 2 || dim == 3, ExcNotImplemented());
5693 }
5694
5695 return quad_node_list;
5696 }
5697
5698 template <int dim, int spacedim>
5699 void
5700 Abaqus_to_UCD<dim, spacedim>::write_out_avs_ucd(std::ostream &output) const
5701 {
5702 // References:
5703 // http://dealii.org/developer/doxygen/deal.II/structGeometryInfo.html
5704 // http://people.scs.fsu.edu/~burkardt/data/ucd/ucd.html
5705
5706 AssertThrow(output.fail() == false, ExcIO());
5707
5708 // save old formatting options
5709 const boost::io::ios_base_all_saver formatting_saver(output);
5710
5711 // Write out title - Note: No other commented text can be inserted below
5712 // the title in a UCD file
5713 output << "# Abaqus to UCD mesh conversion" << std::endl;
5714 output << "# Mesh type: AVS UCD" << std::endl;
5715
5716 // ========================================================
5717 // ASCII UCD File Format
5718 // The input file cannot contain blank lines or lines with leading blanks.
5719 // Comments, if present, must precede all data in the file.
5720 // Comments within the data will cause read errors.
5721 // The general order of the data is as follows:
5722 // 1. Numbers defining the overall structure, including the number of
5723 // nodes,
5724 // the number of cells, and the length of the vector of data associated
5725 // with the nodes, cells, and the model.
5726 // e.g. 1:
5727 // <num_nodes> <num_cells> <num_ndata> <num_cdata> <num_mdata>
5728 // e.g. 2:
5729 // n_elements = n_hex_cells + n_bc_quads + n_quad_cells +
5730 // n_bc_edges outfile.write(str(n_nodes) + " " + str(n_elements) +
5731 // " 0 0 0\n")
5732 // 2. For each node, its node id and the coordinates of that node in
5733 // space.
5734 // Node-ids must be integers, but any number including non sequential
5735 // numbers can be used. Mid-edge nodes are treated like any other node.
5736 // 3. For each cell: its cell-id, material, cell type (hexahedral,
5737 // pyramid,
5738 // etc.), and the list of node-ids that correspond to each of the
5739 // cell's vertices. The below table specifies the different cell types
5740 // and the keyword used to represent them in the file.
5741
5742 // Write out header
5743 output << node_list.size() << "\t" << (cell_list.size() + face_list.size())
5744 << "\t0\t0\t0" << std::endl;
5745
5746 output.width(16);
5747 output.precision(8);
5748
5749 // Write out node numbers
5750 // Loop over all nodes
5751 for (const auto &node : node_list)
5752 {
5753 // Node number
5754 output << node[0] << "\t";
5755
5756 // Node coordinates
5757 output.setf(std::ios::scientific, std::ios::floatfield);
5758 for (unsigned int jj = 1; jj < spacedim + 1; ++jj)
5759 {
5760 // invoke tolerance -> set points close to zero equal to zero
5761 if (std::abs(node[jj]) > tolerance)
5762 output << static_cast<double>(node[jj]) << "\t";
5763 else
5764 output << 0.0 << "\t";
5765 }
5766 if (spacedim == 2)
5767 output << 0.0 << "\t";
5768
5769 output << std::endl;
5770 output.unsetf(std::ios::floatfield);
5771 }
5772
5773 // Write out cell node numbers
5774 for (unsigned int ii = 0; ii < cell_list.size(); ++ii)
5775 {
5776 output << ii + 1 << "\t" << cell_list[ii][0] << "\t"
5777 << (dim == 2 ? "quad" : "hex") << "\t";
5778 for (unsigned int jj = 1; jj < GeometryInfo<dim>::vertices_per_cell + 1;
5779 ++jj)
5780 output << cell_list[ii][jj] << "\t";
5781
5782 output << std::endl;
5783 }
5784
5785 // Write out quad node numbers
5786 for (unsigned int ii = 0; ii < face_list.size(); ++ii)
5787 {
5788 output << ii + 1 << "\t" << face_list[ii][0] << "\t"
5789 << (dim == 2 ? "line" : "quad") << "\t";
5790 for (unsigned int jj = 1; jj < GeometryInfo<dim>::vertices_per_face + 1;
5791 ++jj)
5792 output << face_list[ii][jj] << "\t";
5793
5794 output << std::endl;
5795 }
5796 }
5797} // namespace
5798
5799
5800// explicit instantiations
5801#include "grid/grid_in.inst"
5802
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
@ Default
Use GridIn::default_format stored in this object.
Definition grid_in.h:212
@ vtu
Use read_vtu().
Definition grid_in.h:230
@ xda
Use read_xda().
Definition grid_in.h:222
@ tecplot
Use read_tecplot().
Definition grid_in.h:226
@ ugrid
Use read_ugrid().
Definition grid_in.h:236
@ msh
Use read_msh().
Definition grid_in.h:224
@ ucd
Use read_ucd().
Definition grid_in.h:216
@ dbmesh
Use read_dbmesh().
Definition grid_in.h:220
@ abaqus
Use read_abaqus().
Definition grid_in.h:218
@ assimp
Use read_assimp().
Definition grid_in.h:232
@ unv
Use read_unv().
Definition grid_in.h:214
@ exodusii
Use read_exodusii().
Definition grid_in.h:234
@ vtk
Use read_vtk().
Definition grid_in.h:228
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:4804
void read_partitioned_msh(const std::string &file_prefix, const std::string &file_suffix="msh")
Definition grid_in.cc:3163
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:4018
void read_ugrid(std::istream &in)
Definition grid_in.cc:4173
std::map< std::string, Vector< double > > cell_data
Definition grid_in.h:1039
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:5112
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:4834
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:2193
static Format parse_format(const std::string &format_name)
Definition grid_in.cc:5149
void read_vtu(std::istream &in)
Definition grid_in.cc:773
void read_tecplot(std::istream &in)
Definition grid_in.cc:4009
Format default_format
Definition grid_in.h:1028
ExodusIIData read_exodusii(const std::string &filename, const bool apply_all_indicators_to_manifolds=false)
Definition grid_in.cc:4639
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
ObserverPointer< Triangulation< dim, spacedim >, GridIn< dim, spacedim > > tria
Definition grid_in.h:963
void read(std::istream &in, Format format=Default)
Definition grid_in.cc:5041
static void debug_output_grid(const std::vector< CellData< dim > > &cells, const std::vector< Point< spacedim > > &vertices, std::ostream &out)
Definition grid_in.cc:4860
static std::string get_format_names()
Definition grid_in.cc:5209
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:3541
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 size_type size() const 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_FALLTHROUGH
Definition config.h:273
const unsigned int DoFAccessor< structdim, dim, spacedim, level_dof_access >::dimension
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
constexpr unsigned int GeometryInfo< dim >::vertices_per_cell
IteratorRange< active_cell_iterator > active_cell_iterators() const
static ::ExceptionBase & ExcIO()
static ::ExceptionBase & ExcInvalidDBMeshFormat()
static ::ExceptionBase & ExcFileNotOpen(std::string arg1)
static ::ExceptionBase & ExcNeedsAssimp()
static ::ExceptionBase & ExcUnknownIdentifier(std::string arg1)
static ::ExceptionBase & ExcInvalidGMSHInput(std::string arg1)
static ::ExceptionBase & ExcNotImplemented()
static ::ExceptionBase & ExcInvalidDBMESHInput(std::string arg1)
static ::ExceptionBase & ExcDBMESHWrongDimension(int arg1)
#define Assert(cond, exc)
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
static ::ExceptionBase & ExcInvalidVertexIndex(int arg1, int arg2)
static ::ExceptionBase & ExcNeedsGMSHAPI()
static ::ExceptionBase & ExcNeedsExodusII()
static ::ExceptionBase & ExcNoTriangulationSelected()
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
static ::ExceptionBase & ExcUnknownElementType(int arg1)
#define AssertThrowExodusII(error_code)
static ::ExceptionBase & ExcGmshNoCellInformation(unsigned int arg1, unsigned int arg2)
static ::ExceptionBase & ExcInvalidVertexIndexGmsh(int arg1, int arg2, int arg3)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcUnknownSectionType(int arg1)
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)
static ::ExceptionBase & ExcGmshUnsupportedGeometry(int arg1)
#define AssertThrow(cond, exc)
void consistently_order_cells(std::vector< CellData< dim > > &cells)
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
constexpr ReferenceCell< 3 > Hexahedron
constexpr ReferenceCell< 2 > Quadrilateral
ReferenceCell< dim > n_vertices_to_reference_cell(const unsigned int n_vertices)
constexpr ReferenceCell< 1 > Line
constexpr ReferenceCell< 2 > Triangle
constexpr unsigned int max_n_vertices()
constexpr ReferenceCell< 3 > Tetrahedron
constexpr unsigned int max_n_faces()
constexpr ReferenceCell< dim > Invalid
constexpr ReferenceCell< 3 > Pyramid
constexpr const ReferenceCell< dim > & get_hypercube()
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:843
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:751
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:828
std::string decompress(const std::string &compressed_input)
Definition utilities.cc:403
constexpr unsigned int invalid_unsigned_int
Definition types.h:236
constexpr types::boundary_id internal_face_boundary_id
Definition types.h:327
constexpr types::boundary_id invalid_boundary_id
Definition types.h:307
constexpr types::manifold_id flat_manifold_id
Definition types.h:340
constexpr types::material_id invalid_material_id
Definition types.h:292
constexpr types::geometric_orientation default_geometric_orientation
Definition types.h:350
::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
unsigned int boundary_id
Definition types.h:159
unsigned int manifold_id
Definition types.h:171
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 constexpr unsigned int vertices_per_cell
static constexpr std::array< unsigned int, vertices_per_cell > ucd_to_deal
static constexpr unsigned int vertices_per_face
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