GridFormat 0.6.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
dolfinx.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
9#ifndef GRIDFORMAT_TRAITS_DOLFINX_HPP_
10#define GRIDFORMAT_TRAITS_DOLFINX_HPP_
11
12#include <array>
13#include <ranges>
14#include <cstdint>
15#include <utility>
16#include <memory>
17#include <algorithm>
18#include <concepts>
19#include <span>
20#include <vector>
21
22#include <dolfinx/io/cells.h>
23#include <dolfinx/io/vtk_utils.h>
24#include <dolfinx/mesh/cell_types.h>
25#include <dolfinx/mesh/Mesh.h>
26#include <dolfinx/fem/Function.h>
27#include <dolfinx/fem/FunctionSpace.h>
28
29#include <gridformat/common/ranges.hpp>
30#include <gridformat/common/exceptions.hpp>
31#include <gridformat/common/precision.hpp>
32#include <gridformat/grid/cell_type.hpp>
33#include <gridformat/grid/traits.hpp>
35
36
37namespace GridFormat {
38
39namespace DolfinX {
40
41struct Cell { std::int32_t index; };
42struct Point { std::int32_t index; };
43
44#ifndef DOXYGEN
45namespace Detail {
46 CellType cell_type(dolfinx::mesh::CellType ct) {
47 // since dolfinx supports higher-order cells, let's simply return the lagrange variants always
48 switch (ct) {
49 case dolfinx::mesh::CellType::point: return GridFormat::CellType::vertex;
50 case dolfinx::mesh::CellType::interval: return GridFormat::CellType::lagrange_segment;
51 case dolfinx::mesh::CellType::triangle: return GridFormat::CellType::lagrange_triangle;
52 case dolfinx::mesh::CellType::quadrilateral: return GridFormat::CellType::lagrange_quadrilateral;
53 case dolfinx::mesh::CellType::tetrahedron: return GridFormat::CellType::lagrange_tetrahedron;
54 case dolfinx::mesh::CellType::pyramid: break;
55 case dolfinx::mesh::CellType::prism: break;
56 case dolfinx::mesh::CellType::hexahedron: return GridFormat::CellType::lagrange_hexahedron;
57 }
58 throw NotImplemented("Support for dolfinx cell type '" + dolfinx::mesh::to_string(ct) + "'");
59 }
60
61 template<std::floating_point T>
62 bool is_cellwise_constant(const dolfinx::fem::FunctionSpace<T>& space) {
63 return space.dofmap()->element_dof_layout().num_dofs() == 1;
64 }
65
66 template<typename T, std::floating_point U>
67 bool is_cellwise_constant(const dolfinx::fem::Function<T, U>& f) {
68 assert(f.function_space());
69 return is_cellwise_constant(*f.function_space());
70 }
71
73 template<std::floating_point T>
74 std::span<const std::int32_t> cell_nodes(const dolfinx::mesh::Mesh<T>& mesh, std::int32_t cell) {
75 const auto dofmap = mesh.geometry().dofmaps().front();
76 const auto num_nodes = dofmap.extent(1);
77 return {dofmap.data_handle() + static_cast<std::size_t>(cell)*num_nodes, num_nodes};
78 }
79} // namespace Detail
80#endif // DOXYGEN
81
82} // namespace DolfinX
83
84namespace Traits {
85
86template<std::floating_point T>
87struct Cells<dolfinx::mesh::Mesh<T>> {
88 static std::ranges::range auto get(const dolfinx::mesh::Mesh<T>& mesh) {
89 auto map = mesh.topology()->index_map(mesh.topology()->dim());
90 if (!map) throw GridFormat::ValueError("Cell index map not available");
91 return std::views::iota(0, map->size_local()) | std::views::transform([] (std::int32_t i) {
92 return DolfinX::Cell{i};
93 });
94 }
95};
96
97template<std::floating_point T>
98struct CellType<dolfinx::mesh::Mesh<T>, DolfinX::Cell> {
99 static GridFormat::CellType get(const dolfinx::mesh::Mesh<T>& mesh, const DolfinX::Cell&) {
100 return DolfinX::Detail::cell_type(mesh.topology()->cell_type());
101 }
102};
103
104template<std::floating_point T>
105struct CellPoints<dolfinx::mesh::Mesh<T>, DolfinX::Cell> {
106 static std::ranges::range auto get(const dolfinx::mesh::Mesh<T>& mesh, const DolfinX::Cell& cell) {
107 const auto nodes = DolfinX::Detail::cell_nodes(mesh, cell.index);
108 auto permutation = dolfinx::io::cells::transpose(
109 dolfinx::io::cells::perm_vtk(mesh.topology()->cell_type(), nodes.size())
110 );
111 return std::views::iota(std::size_t{0}, nodes.size())
112 | std::views::transform([perm = std::move(permutation), nodes=nodes] (std::size_t i) {
113 return DolfinX::Point{nodes[perm[i]]};
114 }
115 );
116 }
117};
118
119template<std::floating_point T>
120struct Points<dolfinx::mesh::Mesh<T>> {
121 static std::ranges::range auto get(const dolfinx::mesh::Mesh<T>& mesh) {
122 const auto num_points = mesh.geometry().x().size()/3;
123 return std::views::iota(std::size_t{0}, num_points) | std::views::transform([] (std::size_t i) {
124 return DolfinX::Point{static_cast<std::int32_t>(i)};
125 });
126 }
127};
128
129template<std::floating_point T>
130struct PointCoordinates<dolfinx::mesh::Mesh<T>, DolfinX::Point> {
131 static std::ranges::range auto get(const dolfinx::mesh::Mesh<T>& mesh, const DolfinX::Point& point) {
132 const auto x = mesh.geometry().x();
133 return std::array{x[point.index*3], x[point.index*3 + 1], x[point.index*3 + 2]};
134 }
135};
136
137template<std::floating_point T>
138struct PointId<dolfinx::mesh::Mesh<T>, DolfinX::Point> {
139 static std::integral auto get(const dolfinx::mesh::Mesh<T>&, const DolfinX::Point& point) {
140 return point.index;
141 }
142};
143
144template<std::floating_point T>
145struct NumberOfPoints<dolfinx::mesh::Mesh<T>> {
146 static std::integral auto get(const dolfinx::mesh::Mesh<T>& mesh) {
147 return mesh.geometry().x().size()/3;
148 }
149};
150
151template<std::floating_point T>
152struct NumberOfCells<dolfinx::mesh::Mesh<T>> {
153 static std::integral auto get(const dolfinx::mesh::Mesh<T>& mesh) {
154 auto map = mesh.topology()->index_map(mesh.topology()->dim());
155 if (!map) throw GridFormat::ValueError("Cell index map not available");
156 return map->size_local();
157 }
158};
159
160template<std::floating_point T>
161struct NumberOfCellPoints<dolfinx::mesh::Mesh<T>, DolfinX::Cell> {
162 static std::integral auto get(const dolfinx::mesh::Mesh<T>& mesh, const DolfinX::Cell&) {
163 return mesh.geometry().dofmaps().front().extent(1);
164 }
165};
166
167} // namespace Traits
168
169namespace DolfinX {
170
177template<std::floating_point T>
178class LagrangePolynomialGrid {
179 public:
180 LagrangePolynomialGrid() = default;
181 LagrangePolynomialGrid(const dolfinx::fem::FunctionSpace<T>& space) {
182 if (!space.mesh() || !space.element())
183 throw ValueError("Cannot construct mesh from space without mesh or element");
184 // dolfinx does not check this precondition of vtk_mesh_from_space and fails with undefined behaviour
185 if (Detail::is_cellwise_constant(space))
186 throw ValueError("Cannot construct mesh from a space with cell-wise constant (P0) functions");
187
188 _cell_type = space.mesh()->topology()->cell_type();
189 _mesh = space.mesh();
190 _element = space.element();
191
192 auto [x, xshape, xids, _ghosts, cells, cells_shape] = dolfinx::io::vtk_mesh_from_space(space);
193 _node_coords = std::move(x);
194 _node_coords_shape = std::move(xshape);
195 _node_ids = std::move(xids);
196 _cells = std::move(cells);
197 _cells_shape = std::move(cells_shape);
198 _set = true;
199 }
200
201 void update(const dolfinx::fem::FunctionSpace<T>& space) {
202 *this = LagrangePolynomialGrid{space};
203 }
204
205 void clear() {
206 _mesh = nullptr;
207 _element = nullptr;
208 _node_coords.clear();
209 _node_ids.clear();
210 _cells.clear();
211 _set = false;
212 }
213
214 static LagrangePolynomialGrid from(const dolfinx::fem::FunctionSpace<T>& space) {
215 return {space};
216 }
217
218 std::integral auto number_of_points() const { return _node_coords_shape[0]; }
219 std::integral auto number_of_cells() const { return _cells_shape[0]; }
220 std::integral auto number_of_cell_points() const { return _cells_shape[1]; }
221
222 std::int64_t id(const Point& p) const {
223 return _node_ids[p.index];
224 }
225
226 auto position(const Point& p) const {
227 assert(_node_coords_shape[1] == 3);
228 return std::array{
229 _node_coords[p.index*3],
230 _node_coords[p.index*3 + 1],
231 _node_coords[p.index*3 + 2]
232 };
233 }
234
235 std::ranges::range auto points() const {
236 _check_built();
237 return std::views::iota(std::size_t{0}, _node_coords_shape[0])
238 | std::views::transform([] (std::size_t i) {
239 return Point{static_cast<std::int32_t>(i)};
240 });
241 }
242
243 std::ranges::range auto points(const Cell& cell) const {
244 return std::views::iota(std::size_t{0}, _cells_shape[1])
245 | std::views::transform([&, i=cell.index, ncorners=_cells_shape[1]] (std::size_t local_point_index) {
246 return Point{static_cast<std::int32_t>(_cells[i*ncorners + local_point_index])};
247 });
248 }
249
250 std::ranges::range auto cells() const {
251 _check_built();
252 return std::views::iota(std::size_t{0}, _cells_shape[0])
253 | std::views::transform([] (std::size_t i) {
254 return Cell{static_cast<std::int32_t>(i)};
255 });
256 }
257
258 template<int rank = 0, int dim = 3, Concepts::Scalar V>
259 auto evaluate(const dolfinx::fem::Function<V, T>& f, const Cell& c) const {
260 assert(is_compatible(f));
261 return _evaluate<rank, dim>(f, c.index);
262 }
263
264 template<int rank = 0, int dim = 3, Concepts::Scalar V>
265 auto evaluate(const dolfinx::fem::Function<V, T>& f, const Point& p) const {
266 assert(is_compatible(f));
267 return _evaluate<rank, dim>(f, p.index);
268 }
269
270 auto cell_type() const {
271 return _cell_type;
272 }
273
274 template<Concepts::Scalar V>
275 bool is_compatible(const dolfinx::fem::Function<V, T>& f) const {
276 if (!_set) return false;
277 if (!f.function_space()->mesh()) return false;
278 if (f.function_space()->mesh() != _mesh) return false;
279 if (!Detail::is_cellwise_constant(f)) {
280 if (!f.function_space()->element()) return false;
281 if (*f.function_space()->element() != *_element) return false;
282 }
283 return true;
284 }
285
286 private:
287 void _check_built() const {
288 if (!_set)
289 throw InvalidState("Mesh has not been built");
290 }
291
292 template<int rank = 0, int dim = 3, Concepts::Scalar V>
293 auto _evaluate(const dolfinx::fem::Function<V, T>& f, const std::integral auto i) const {
294 const auto f_components = f.function_space()->element()->block_size();
295 const auto& values = f.x()->array();
296 assert(f.function_space()->element()->value_shape().size() == rank);
297 assert(values.size() >= static_cast<std::size_t>(i*f_components + f_components));
298
299 if constexpr (rank == 0)
300 return values[i*f_components];
301 else if constexpr (rank == 1) {
302 auto result = Ranges::filled_array<dim>(V{0});
303 std::copy_n(
304 values.data() + i*f_components,
305 std::min(dim, f_components),
306 result.begin()
307 );
308 return result;
309 } else {
310 throw NotImplemented("Tensor evaluation");
311 return std::array<std::array<V, dim>, dim>{}; // for return type deduction
312 }
313 }
314
315 dolfinx::mesh::CellType _cell_type;
316 std::shared_ptr<const dolfinx::mesh::Mesh<T>> _mesh{nullptr};
317 std::shared_ptr<const dolfinx::fem::FiniteElement<T>> _element{nullptr};
318
319 std::vector<T> _node_coords;
320 std::array<std::size_t, 2> _node_coords_shape;
321 std::vector<std::int64_t> _node_ids;
322 std::vector<std::int64_t> _cells;
323 std::array<std::size_t, 2> _cells_shape;
324 bool _set = false;
325};
326
327template<std::floating_point T>
328LagrangePolynomialGrid(const dolfinx::fem::FunctionSpace<T>&) -> LagrangePolynomialGrid<T>;
329
334template<std::floating_point T>
335LagrangePolynomialGrid<T> make_lagrange_grid(const dolfinx::fem::FunctionSpace<T>& space) {
336 return LagrangePolynomialGrid<T>{space};
337}
338
347template<typename Writer, Concepts::Scalar T, std::floating_point U, Concepts::Scalar P = T>
348void set_point_function(const dolfinx::fem::Function<T, U>& f,
349 Writer& writer,
350 std::string name = "",
351 const Precision<P>& prec = {}) {
352 if (!writer.grid().is_compatible(f))
353 throw ValueError("Grid passed to writer is incompatible with the given function");
354 if (Detail::is_cellwise_constant(f))
355 throw ValueError("Given function is not node-based");
356 if (name.empty())
357 name = f.name;
358 const auto block_size = f.function_space()->element()->block_size();
359 const auto dim = f.function_space()->mesh()->geometry().dim();
360 if (block_size == 1)
361 writer.set_point_field(name, [&] (const auto p) { return writer.grid().template evaluate<0>(f, p); }, prec);
362 else if (dim >= block_size)
363 writer.set_point_field(name, [&] (const auto p) { return writer.grid().template evaluate<1>(f, p); }, prec);
364 else
365 writer.set_point_field(name, [&] (const auto p) { return writer.grid().template evaluate<2>(f, p); }, prec);
366}
367
376template<typename Writer, Concepts::Scalar T, std::floating_point U, Concepts::Scalar P = T>
377void set_cell_function(const dolfinx::fem::Function<T, U>& f,
378 Writer& writer,
379 std::string name = "",
380 const Precision<P>& prec = {}) {
381 if (!writer.grid().is_compatible(f))
382 throw ValueError("Grid passed to writer is incompatible with the given function");
383 if (!Detail::is_cellwise_constant(f))
384 throw ValueError("Given function is not constant per grid cell");
385 if (name.empty())
386 name = f.name;
387 const auto block_size = f.function_space()->element()->block_size();
388 const auto dim = f.function_space()->mesh()->geometry().dim();
389 if (block_size == 1)
390 writer.set_cell_field(name, [&] (const auto p) { return writer.grid().template evaluate<0>(f, p); }, prec);
391 else if (dim >= block_size)
392 writer.set_cell_field(name, [&] (const auto p) { return writer.grid().template evaluate<1>(f, p); }, prec);
393 else
394 writer.set_cell_field(name, [&] (const auto p) { return writer.grid().template evaluate<2>(f, p); }, prec);
395}
396
405template<typename Writer, Concepts::Scalar T, std::floating_point U, Concepts::Scalar P = T>
406void set_function(const dolfinx::fem::Function<T, U>& f,
407 Writer& writer,
408 const std::string& name = "",
409 const Precision<P>& prec = {}) {
410 if (Detail::is_cellwise_constant(f))
411 set_cell_function(f, writer, name, prec);
412 else
413 set_point_function(f, writer, name, prec);
414}
415
416} // namespace DolfinX
417
418namespace Traits {
419
420template<std::floating_point T>
421struct Cells<DolfinX::LagrangePolynomialGrid<T>> {
422 static std::ranges::range auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh) {
423 return mesh.cells();
424 }
425};
426
427template<std::floating_point T>
428struct CellType<DolfinX::LagrangePolynomialGrid<T>, DolfinX::Cell> {
429 static GridFormat::CellType get(const DolfinX::LagrangePolynomialGrid<T>& mesh, const DolfinX::Cell&) {
430 return DolfinX::Detail::cell_type(mesh.cell_type());
431 }
432};
433
434template<std::floating_point T>
435struct CellPoints<DolfinX::LagrangePolynomialGrid<T>, DolfinX::Cell> {
436 static std::ranges::range auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh, const DolfinX::Cell& cell) {
437 return mesh.points(cell);
438 }
439};
440
441template<std::floating_point T>
442struct Points<DolfinX::LagrangePolynomialGrid<T>> {
443 static std::ranges::range auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh) {
444 return mesh.points();
445 }
446};
447
448template<std::floating_point T>
449struct PointCoordinates<DolfinX::LagrangePolynomialGrid<T>, DolfinX::Point> {
450 static std::ranges::range auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh, const DolfinX::Point& point) {
451 return mesh.position(point);
452 }
453};
454
455template<std::floating_point T>
456struct PointId<DolfinX::LagrangePolynomialGrid<T>, DolfinX::Point> {
457 static std::integral auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh, const DolfinX::Point& point) {
458 return mesh.id(point);
459 }
460};
461
462template<std::floating_point T>
463struct NumberOfPoints<DolfinX::LagrangePolynomialGrid<T>> {
464 static std::integral auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh) {
465 return mesh.number_of_points();
466 }
467};
468
469template<std::floating_point T>
470struct NumberOfCells<DolfinX::LagrangePolynomialGrid<T>> {
471 static std::integral auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh) {
472 return mesh.number_of_cells();
473 }
474};
475
476template<std::floating_point T>
477struct NumberOfCellPoints<DolfinX::LagrangePolynomialGrid<T>, DolfinX::Cell> {
478 static std::integral auto get(const DolfinX::LagrangePolynomialGrid<T>& mesh, const DolfinX::Cell&) {
479 return mesh.number_of_cell_points();
480 }
481};
482
483} // namespace Traits
484} // namespace GridFormat
485
486#endif // GRIDFORMAT_TRAITS_DOLFINX_HPP_
Wrapper around a nodal dolfinx::FunctionSpace, exposing it as a mesh composed of lagrange elements wi...
Definition dolfinx.hpp:178
Interface to the writers for all supported file formats. Depending on the chosen format,...
Definition writer.hpp:90
void set_point_field(const std::string &name, Field &&field)
Set a point data field to be added to the output.
Definition writer.hpp:210
const Grid & grid() const
Return a reference to the underlying grid.
Definition writer.hpp:326
void set_cell_field(const std::string &name, Field &&field)
Set a cell data field to be added to the output.
Definition writer.hpp:239
Base classes for grid data writers.
void set_function(const dolfinx::fem::Function< T, U > &f, Writer &writer, const std::string &name="", const Precision< P > &prec={})
Insert the given function into the writer as field.
Definition dolfinx.hpp:406
void set_cell_function(const dolfinx::fem::Function< T, U > &f, Writer &writer, std::string name="", const Precision< P > &prec={})
Insert the given function into the writer as cell field.
Definition dolfinx.hpp:377
LagrangePolynomialGrid< T > make_lagrange_grid(const dolfinx::fem::FunctionSpace< T > &space)
Create a LagrangePolynomialGrid from the given function space.
Definition dolfinx.hpp:335
void set_point_function(const dolfinx::fem::Function< T, U > &f, Writer &writer, std::string name="", const Precision< P > &prec={})
Insert the given function into the writer as point field.
Definition dolfinx.hpp:348
Definition dolfinx.hpp:41
Definition dolfinx.hpp:42