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