|
6 | 6 |
|
7 | 7 | #include "HDF5Interface.h" |
8 | 8 | #include <algorithm> |
| 9 | +#include <basix/element-families.h> |
| 10 | +#include <basix/finite-element.h> |
9 | 11 | #include <concepts> |
10 | 12 | #include <dolfinx/common/IndexMap.h> |
| 13 | +#include <dolfinx/fem/Function.h> |
11 | 14 | #include <dolfinx/io/cells.h> |
| 15 | +#include <dolfinx/io/utils.h> |
12 | 16 | #include <dolfinx/mesh/Mesh.h> |
13 | 17 | #include <dolfinx/mesh/Topology.h> |
14 | 18 | #include <dolfinx/mesh/utils.h> |
@@ -298,6 +302,130 @@ void write_data(std::string point_or_cell, std::string filename, |
298 | 302 | hdf5::close_file(h5file); |
299 | 303 | } |
300 | 304 |
|
| 305 | +/// @brief Write a function to VTKHDF. |
| 306 | +/// |
| 307 | +/// Adds a function to an existing VTKHDF file, which already contains a mesh. |
| 308 | +/// |
| 309 | +/// @tparam U Scalar type. |
| 310 | +/// @param[in] filename File for output. |
| 311 | +/// @param[in] mesh Mesh, which must be the same as the original mesh |
| 312 | +/// used in the file. |
| 313 | +/// @param[in] u Function to write to file. |
| 314 | +/// @param[in] time Timestamp. |
| 315 | +/// |
| 316 | +/// @note Mesh must be written to file first using `VTKHDF::write_mesh`. |
| 317 | +/// @note Only one dataset "u" can be written per file at present, with |
| 318 | +/// multiple timesteps. |
| 319 | +/// @note Limited support for floating point types at present (no |
| 320 | +/// complex number support). This function only supports DG0 and CG1 functions. |
| 321 | +template <std::floating_point U> |
| 322 | +void write_function(std::string filename, const mesh::Mesh<U>& mesh, |
| 323 | + const fem::Function<U>& u, double time) |
| 324 | +{ |
| 325 | + auto dofmap = u.function_space()->dofmap(); |
| 326 | + assert(dofmap); |
| 327 | + const int bs = dofmap->bs(); |
| 328 | + |
| 329 | + auto map_c = mesh.topology()->index_map(mesh.topology()->dim()); |
| 330 | + assert(map_c); |
| 331 | + |
| 332 | + std::shared_ptr<const fem::FiniteElement<U>> element |
| 333 | + = u.function_space()->element(); |
| 334 | + assert(element); |
| 335 | + |
| 336 | + std::span<const std::size_t> value_shape = element->value_shape(); |
| 337 | + int rank = value_shape.size(); |
| 338 | + std::int32_t num_components = std::reduce( |
| 339 | + value_shape.begin(), value_shape.end(), 1, std::multiplies{}); |
| 340 | + |
| 341 | + std::span<const U> x = u.x()->array(); |
| 342 | + |
| 343 | + // Check that it is a Lagrange family element |
| 344 | + if (element->basix_element().family() != basix::element::family::P) |
| 345 | + { |
| 346 | + throw std::runtime_error("Only Lagrange finite elements are supported"); |
| 347 | + } |
| 348 | + |
| 349 | + // DG0 |
| 350 | + if (element->basix_element().degree() == 0) |
| 351 | + { |
| 352 | + const std::int32_t num_local_cells = map_c->size_local(); |
| 353 | + std::vector<U> data(num_local_cells * num_components); |
| 354 | + |
| 355 | + for (std::int32_t c = 0; c < num_local_cells; ++c) |
| 356 | + { |
| 357 | + auto dofs = dofmap->cell_dofs(c); |
| 358 | + assert(dofs.size() == 1); |
| 359 | + for (std::size_t i = 0; i < dofs.size(); ++i) |
| 360 | + { |
| 361 | + std::copy_n(std::cbegin(x) + bs * dofs[i], bs, |
| 362 | + std::begin(data) + num_components * c); |
| 363 | + } |
| 364 | + } |
| 365 | + |
| 366 | + io::VTKHDF::write_data<U>("Cell", filename, mesh, data, time); |
| 367 | + } |
| 368 | + // CG1 |
| 369 | + else if (element->basix_element().discontinuous() == false |
| 370 | + and element->basix_element().degree() == 1) |
| 371 | + { |
| 372 | + auto map_x = mesh.geometry().index_map(); |
| 373 | + assert(map_x); |
| 374 | + |
| 375 | + auto& geometry = mesh.geometry(); |
| 376 | + auto& cmap = geometry.cmap(); |
| 377 | + int cmap_dim = cmap.dim(); |
| 378 | + int cell_dim = element->space_dimension() / element->block_size(); |
| 379 | + if (cmap_dim != cell_dim) |
| 380 | + { |
| 381 | + throw std::runtime_error("Degree of output Function must be the same as " |
| 382 | + "mesh degree. Maybe the " |
| 383 | + "Function needs to be interpolated?"); |
| 384 | + } |
| 385 | + |
| 386 | + // Check that dofmap layouts are equal and check Lagrange variants |
| 387 | + if (dofmap->element_dof_layout() != cmap.create_dof_layout()) |
| 388 | + { |
| 389 | + throw std::runtime_error("Function and Mesh dof layouts do not match. " |
| 390 | + "Maybe the Function needs to be interpolated?"); |
| 391 | + } |
| 392 | + if (cmap.degree() > 2 |
| 393 | + and element->basix_element().lagrange_variant() != cmap.variant()) |
| 394 | + { |
| 395 | + throw std::runtime_error("Mismatch in Lagrange family. Maybe the " |
| 396 | + "Function needs to be interpolated?"); |
| 397 | + } |
| 398 | + |
| 399 | + std::int32_t num_cells = map_c->size_local() + map_c->num_ghosts(); |
| 400 | + std::int32_t num_local_points = map_x->size_local(); |
| 401 | + |
| 402 | + // Get dof array and pack into array (padded where appropriate) |
| 403 | + auto dofmap_x = geometry.dofmap(); |
| 404 | + std::vector<U> data(num_local_points * num_components); |
| 405 | + for (std::int32_t c = 0; c < num_cells; ++c) |
| 406 | + { |
| 407 | + auto dofs = dofmap->cell_dofs(c); |
| 408 | + auto dofs_x = md::submdspan(dofmap_x, c, md::full_extent); |
| 409 | + assert(dofs.size() == dofs_x.size()); |
| 410 | + for (std::size_t i = 0; i < dofs.size(); ++i) |
| 411 | + { |
| 412 | + if (dofs_x[i] < num_local_points) |
| 413 | + { |
| 414 | + std::copy_n(std::cbegin(x) + bs * dofs[i], bs, |
| 415 | + std::begin(data) + num_components * dofs_x[i]); |
| 416 | + } |
| 417 | + } |
| 418 | + } |
| 419 | + |
| 420 | + io::VTKHDF::write_data<U>("Point", filename, mesh, data, time); |
| 421 | + } |
| 422 | + else |
| 423 | + { |
| 424 | + throw std::runtime_error("Unsupported function space. Only DG0 and CG1 are " |
| 425 | + "supported at the moment."); |
| 426 | + } |
| 427 | +} |
| 428 | + |
301 | 429 | /// @brief Read a mesh from a VTKHDF format file. |
302 | 430 | /// |
303 | 431 | /// @tparam U Scalar type of mesh |
|
0 commit comments