| 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 |