GCC Code Coverage Report


Directory: src/oiseau/
File: src/oiseau/io/gmsh_file.cpp
Date: 2025-05-24 01:28:39
Exec Total Coverage
Lines: 121 145 83.4%
Functions: 19 19 100.0%
Branches: 115 224 51.3%

Line Branch Exec Source
1 // Copyright (C) 2025 Tiago V. L. Amorim (@tiagovla)
2 //
3 // This file is part of oiseau (https://github.com/tiagovla/oiseau)
4 //
5 // SPDX-License-Identifier: GPL-3.0-or-later
6
7 #include "oiseau/io/gmsh_file.hpp"
8
9 #include <array>
10 #include <cstddef>
11 #include <istream>
12 #include <stdexcept>
13 #include <string>
14 #include <string_view>
15 #include <utility>
16 #include <vector>
17 #include <xtensor/containers/xadapt.hpp>
18
19 enum { PREFIX = '$' };
20
21 namespace oiseau::io {
22
23 namespace detail {
24 3 std::size_t gmsh_nodes_per_cell(const std::size_t s) {
25
2/9
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
3 switch (s) {
26 case 1:
27 return 2; // line
28 case 2:
29 return 3; // triangle
30 case 3:
31 return 4; // quad
32 2 case 4:
33 2 return 4; // tetra
34 1 case 5:
35 1 return 8; // hexahedron
36 case 6:
37 return 6; // prism?
38 case 7:
39 return 5; // wedge
40 case 15:
41 return 1; // vertex
42 default:
43 throw std::runtime_error("Unknown GMSH cell type: " + std::to_string(s));
44 }
45 }
46
47 template <class T>
48 concept Readable = std::is_convertible_v<T, std::string_view> || std::is_arithmetic_v<T>;
49
50 template <Readable T, int N>
51 186 std::array<T, N> from_file(std::istream& f, bool is_binary = false) {
52
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
190 std::array<T, N> arr{};
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
186 if (is_binary) {
54 f.read(reinterpret_cast<char*>(arr.data()), sizeof(T) * N);
55 } else {
56
6/7
✓ Branch 1 taken 82 times.
✓ Branch 2 taken 94 times.
✓ Branch 3 taken 29 times.
✓ Branch 4 taken 82 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 82 times.
✓ Branch 7 taken 64 times.
538 for (std::size_t i = 0; i < N; i++) f >> arr.at(i);
57 }
58 186 return arr;
59 }
60
61 template <Readable T>
62 30 std::vector<T> from_file(std::istream& f, std::size_t n, bool is_binary = false) {
63
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 std::vector<T> vec(n);
64
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
30 if (is_binary) {
65 f.read(reinterpret_cast<char*>(vec.data()), sizeof(T) * n);
66 } else {
67
4/6
✓ Branch 1 taken 67 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 67 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 67 times.
✓ Branch 7 taken 15 times.
164 for (std::size_t i = 0; i < n; i++) f >> vec.at(i);
68 }
69 30 return vec;
70 }
71
72 3 MeshFormatSection mesh_format_handler(std::istream& f_handler) {
73
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto [version] = from_file<double, 1>(f_handler);
74
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (version != 4.1) {
75 throw std::runtime_error(
76 "Unsupported GMSH version detected."
77 "Please ensure you are using version 4.1.");
78 }
79
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto [is_binary] = from_file<int, 1>(f_handler);
80
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto [size_t_size] = from_file<std::size_t, 1>(f_handler);
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (is_binary) {
82 f_handler.get(); // skip NF
83 auto [verify_one] = from_file<int, 1>(f_handler, true);
84 if (verify_one != 1) throw std::runtime_error("Invalid GMSH file");
85 if (size_t_size != sizeof(std::size_t)) throw std::runtime_error("Invalid GMSH file");
86 }
87 6 return {version, is_binary, size_t_size};
88 }
89
90 1 PhysicalNamesSection physical_names_handler(std::istream& f_handler) {
91
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 auto [num_phys_names] = from_file<int, 1>(f_handler);
92 1 std::vector<int> dimensions;
93 1 std::vector<int> physical_tags;
94 1 std::vector<std::string> names;
95
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 dimensions.reserve(num_phys_names);
96
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 physical_tags.reserve(num_phys_names);
97
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 names.reserve(num_phys_names);
98
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (int i = 0; i < num_phys_names; i++) {
99
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 auto [dim, tag] = from_file<int, 2>(f_handler);
100
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 auto [name] = from_file<std::string, 1>(f_handler);
101
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 dimensions.emplace_back(dim);
102
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 physical_tags.emplace_back(tag);
103
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 names.emplace_back(name);
104 2 }
105 2 return {num_phys_names, std::move(dimensions), std::move(physical_tags), std::move(names)};
106 1 }
107
108 2 EntitiesSection entities_handler(std::istream& f_handler, bool is_binary) {
109
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 auto quantity = from_file<std::size_t, 4>(f_handler, is_binary);
110 2 std::array<std::vector<EntityEntry>, 4> blocks;
111
5/8
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 8 times.
✓ Branch 10 taken 2 times.
10 for (std::size_t i = 0; i < 4; i++) blocks.at(i).reserve(quantity.at(i));
112
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 for (int d = 0; d < 4; d++) {
113
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 8 times.
13 for (std::size_t j = 0; j < quantity[d]; j++) {
114
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 auto [tag] = from_file<int, 1>(f_handler, is_binary);
115
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
5 auto bounding_coods = from_file<double>(f_handler, (d == 0) ? 3 : 6, is_binary);
116
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 auto [num_physicals] = from_file<std::size_t, 1>(f_handler, is_binary);
117
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 auto physical_tags = from_file<int>(f_handler, num_physicals, is_binary);
118
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (d > 0) {
119
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 auto [num_BREP] = from_file<std::size_t, 1>(f_handler, is_binary);
120
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 auto bounding_entities = from_file<int>(f_handler, num_BREP, is_binary);
121
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
4 blocks.at(d).emplace_back(tag, std::move(bounding_coods), std::move(physical_tags),
122 2 std::move(bounding_entities));
123 2 } else {
124
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
3 blocks.at(d).emplace_back(tag, std::move(bounding_coods), std::move(physical_tags));
125 }
126 5 }
127 }
128
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 return EntitiesSection(std::move(blocks));
129 2 }
130
131 3 NodesSection nodes_handler(std::istream& f_handler, bool is_binary) {
132 3 auto [num_entity_blocks, total_num_nodes, min_node_tag, max_node_tag] =
133
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 from_file<std::size_t, 4>(f_handler, is_binary);
134
135 3 std::vector<NodesBlock> blocks;
136
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 blocks.reserve(num_entity_blocks);
137
138
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 for (std::size_t i = 0; i < num_entity_blocks; i++) {
139
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 auto [dim, entity_tag, parametric] = from_file<int, 3>(f_handler, is_binary);
140
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 auto [quantity] = from_file<std::size_t, 1>(f_handler, is_binary);
141 5 std::vector<std::size_t> node_tags;
142 5 std::vector<double> node_coords;
143
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 node_tags.reserve(quantity);
144
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
5 node_coords.reserve(quantity * 3);
145
146
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for (std::size_t j = 0; j < quantity; j++) {
147
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 auto [tag] = from_file<std::size_t, 1>(f_handler, is_binary);
148
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 node_tags.emplace_back(tag);
149 }
150
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
27 for (std::size_t j = 0; j < quantity; j++) {
151
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 auto xyz = from_file<double, 3>(f_handler, is_binary);
152
1/2
✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
22 node_coords.insert(node_coords.end(), xyz.begin(), xyz.end());
153 }
154
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 blocks.emplace_back(dim, entity_tag, parametric, quantity, std::move(node_tags),
155 5 std::move(node_coords));
156 5 }
157 6 return {num_entity_blocks, total_num_nodes, min_node_tag, max_node_tag, std::move(blocks)};
158 3 }
159
160 2 ElementSection elements_handler(std::istream& f_handler, bool is_binary) {
161 2 auto [num_element_blocks, num_elements, min_element_tag, max_element_tag] =
162
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 from_file<std::size_t, 4>(f_handler, is_binary);
163 2 std::vector<ElementBlock> blocks;
164
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 blocks.reserve(num_element_blocks);
165
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 for (std::size_t i = 0; i < num_element_blocks; i++) {
166
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto [entity_dim, entity_tag, element_type] = from_file<int, 3>(f_handler, is_binary);
167
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto [num_elements_in_block] = from_file<std::size_t, 1>(f_handler, is_binary);
168
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 auto size = gmsh_nodes_per_cell(element_type);
169 auto tmp_conn =
170
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 from_file<std::size_t>(f_handler, (1 + size) * num_elements_in_block, is_binary);
171
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 blocks.emplace_back(entity_dim, entity_tag, element_type, num_elements_in_block,
172 3 std::move(tmp_conn));
173 3 }
174 4 return {num_element_blocks, num_elements, min_element_tag, max_element_tag, std::move(blocks)};
175 2 };
176
177 7 void skip_to_end_of_environment(std::istream& f_handler) {
178 7 std::string line;
179
3/6
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
14 while (getline(f_handler, line)) {
180
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 7 times.
14 if (line.starts_with(PREFIX)) break;
181 }
182 7 }
183 } // namespace detail
184
185 2 void GMSHFile::read(std::istream& f_handler) {
186 using namespace detail;
187
2/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
2 if (f_handler.fail()) throw std::runtime_error("Could not read file stream");
188 2 std::string line;
189 bool is_binary;
190
4/6
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 2 times.
11 while (getline(f_handler, line)) {
191
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if (line.starts_with(PREFIX)) {
192
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 line = line.substr(1);
193
3/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 5 times.
7 if (line == "MeshFormat") {
194
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 mesh_format_section = mesh_format_handler(f_handler);
195 2 is_binary = mesh_format_section.is_binary;
196
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 } else if (line == "PhysicalNames") {
197 physical_names_section = physical_names_handler(f_handler);
198
3/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
5 } else if (line == "Entities") {
199
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 entities_section = entities_handler(f_handler, is_binary);
200
3/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
4 } else if (line == "Nodes") {
201
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 nodes_section = nodes_handler(f_handler, is_binary);
202
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 } else if (line == "Elements") {
203
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 elements_section = elements_handler(f_handler, is_binary);
204 }
205
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 skip_to_end_of_environment(f_handler);
206 }
207 }
208 2 }
209 } // namespace oiseau::io
210