10#include "cell_types.h"
19#include <dolfinx/graph/ordering.h>
20#include <dolfinx/graph/partition.h>
43template <std::
floating_po
int T>
44std::tuple<std::vector<T>, std::vector<std::int64_t>>
45create_interval_cells(std::array<T, 2> p, std::int64_t n);
47template <std::
floating_po
int T>
48Mesh<T> build_tri(MPI_Comm comm, std::array<std::array<T, 2>, 2> p,
49 std::array<std::int64_t, 2> n,
54template <std::
floating_po
int T>
55Mesh<T> build_quad(MPI_Comm comm, std::array<std::array<T, 2>, 2> p,
56 std::array<std::int64_t, 2> n,
61template <std::
floating_po
int T>
62std::vector<T> create_geom(MPI_Comm comm, std::array<std::array<T, 3>, 2> p,
63 std::array<std::int64_t, 3> n);
65template <std::
floating_po
int T>
66Mesh<T> build_tet(MPI_Comm comm, MPI_Comm subcomm,
67 std::array<std::array<T, 3>, 2> p,
68 std::array<std::int64_t, 3> n,
72template <std::
floating_po
int T>
73Mesh<T> build_hex(MPI_Comm comm, MPI_Comm subcomm,
74 std::array<std::array<T, 3>, 2> p,
75 std::array<std::int64_t, 3> n,
79template <std::
floating_po
int T>
81build_prism(MPI_Comm comm, MPI_Comm subcomm, std::array<std::array<T, 3>, 2> p,
82 std::array<std::int64_t, 3> n,
92Mesh<typename std::remove_reference_t<typename U::value_type>> finalize_mesh(
93 MPI_Comm comm, MPI_Comm commx, std::span<const std::int64_t> cells,
95 typename std::remove_reference_t<typename U::value_type>>& element,
96 const U& x, std::array<std::size_t, 2> xshape,
100 return create_mesh(comm, commx, cells, element, commx, x, xshape,
130template <std::
floating_po
int T =
double>
132 std::array<std::array<T, 3>, 2> p,
133 std::array<std::int64_t, 3> n,
CellType celltype,
138 if (std::ranges::any_of(n, [](
auto e) {
return e < 1; }))
139 throw std::runtime_error(
"At least one cell is required.");
141 for (int32_t i = 0; i < 3; i++)
143 if (p[0][i] >= p[1][i])
144 throw std::runtime_error(
"It must hold p[0] < p[1].");
152 case CellType::tetrahedron:
153 return impl::build_tet<T>(comm, subcomm, p, n, partitioner, ghost_mode,
155 case CellType::hexahedron:
156 return impl::build_hex<T>(comm, subcomm, p, n, partitioner, ghost_mode,
158 case CellType::prism:
159 return impl::build_prism<T>(comm, subcomm, p, n, partitioner, ghost_mode,
162 throw std::runtime_error(
"Generate box mesh. Wrong cell type");
185template <std::
floating_po
int T =
double>
187 std::array<std::int64_t, 3> n,
CellType celltype,
192 return create_box<T>(comm, comm, p, n, celltype, partitioner, ghost_mode,
218template <std::
floating_po
int T =
double>
220 std::array<std::int64_t, 2> n,
CellType celltype,
223 int gdim = 2,
GhostMode ghost_mode = GhostMode::none,
227 if (gdim < 2 || gdim > 3)
228 throw std::runtime_error(
"2 <= gdim <= 3 for rectangle mesh.");
229 if (std::ranges::any_of(n, [](
auto e) {
return e < 1; }))
230 throw std::runtime_error(
"At least one cell per dimension is required.");
232 for (int32_t i = 0; i < 2; i++)
234 if (p[0][i] >= p[1][i])
235 throw std::runtime_error(
"It must hold p[0] < p[1].");
243 case CellType::triangle:
244 return impl::build_tri<T>(comm, p, n, partitioner, diagonal, ghost_mode,
246 case CellType::quadrilateral:
247 return impl::build_quad<T>(comm, p, n, partitioner, ghost_mode, reorder_fn,
250 throw std::runtime_error(
"Generate rectangle mesh. Wrong cell type.");
270template <std::
floating_po
int T =
double>
272 std::array<std::int64_t, 2> n,
CellType celltype,
296template <std::
floating_po
int T =
double>
303 if (gdim < 1 || gdim > 3)
304 throw std::runtime_error(
"1 <= gdim <= 3 for interval mesh.");
306 throw std::runtime_error(
"At least one cell per dimension is required.");
308 const auto [a, b] = p;
310 throw std::runtime_error(
"It must hold p[0] < p[1].");
311 if (std::abs(a - b) < std::numeric_limits<T>::epsilon())
313 throw std::runtime_error(
314 "Length of interval is zero. Check your dimensions.");
320 fem::CoordinateElement<T> element(CellType::interval, 1);
323 auto [x1d, cells] = impl::create_interval_cells<T>(p, n);
324 std::size_t npts = x1d.size();
327 return impl::finalize_mesh(comm, MPI_COMM_SELF, cells, element, x1d,
328 {npts, 1}, partitioner, ghost_mode,
331 std::vector<T> x(npts * gdim, T(0));
332 for (std::size_t i = 0; i < npts; i++)
333 x[i * gdim] = x1d[i];
334 return impl::finalize_mesh(comm, MPI_COMM_SELF, cells, element, x,
335 {npts,
static_cast<std::size_t
>(gdim)},
336 partitioner, ghost_mode, reorder_fn);
340 return impl::finalize_mesh(comm, MPI_COMM_NULL, {}, element,
342 {0,
static_cast<std::size_t
>(gdim)}, partitioner,
343 ghost_mode, reorder_fn);
350template <std::
floating_po
int T>
351std::tuple<std::vector<T>, std::vector<std::int64_t>>
352create_interval_cells(std::array<T, 2> p, std::int64_t n)
354 const auto [a, b] = p;
356 const T
h = (b - a) /
static_cast<T
>(n);
359 std::vector<T> x(n + 1);
360 std::ranges::generate(x, [i = std::int64_t(0), a,
h]()
mutable
361 {
return a +
h *
static_cast<T
>(i++); });
364 std::vector<std::int64_t>
cells(2 * n);
365 for (std::size_t ix = 0; ix <
cells.size() / 2; ++ix)
368 cells[2 * ix + 1] = ix + 1;
371 return {std::move(x), std::move(cells)};
374template <std::
floating_po
int T>
375std::vector<T> create_geom(MPI_Comm comm, std::array<std::array<T, 3>, 2> p,
376 std::array<std::int64_t, 3> n)
380 const auto [nx, ny, nz] = n;
382 assert(std::ranges::all_of(n, [](
auto e) {
return e >= 1; }));
386 const std::array<T, 3> extents = {
387 (p1[0] - p0[0]) /
static_cast<T
>(nx),
388 (p1[1] - p0[1]) /
static_cast<T
>(ny),
389 (p1[2] - p0[2]) /
static_cast<T
>(nz),
392 if (std::ranges::any_of(
394 {
return std::abs(e) < 2.0 * std::numeric_limits<T>::epsilon(); }))
396 throw std::runtime_error(
397 "Box seems to have zero width, height or depth. Check dimensions");
400 const std::int64_t n_points = (nx + 1) * (ny + 1) * (nz + 1);
405 geom.reserve((range_end - range_begin) * 3);
406 const std::int64_t sqxy = (nx + 1) * (ny + 1);
407 for (std::int64_t v = range_begin; v < range_end; ++v)
410 const std::int64_t p = v % sqxy;
411 std::array<std::int64_t, 3> idx = {p % (nx + 1), p / (nx + 1), v / sqxy};
414 for (std::size_t i = 0; i < idx.size(); i++)
415 geom.push_back(p0[i] +
static_cast<T
>(idx[i]) * extents[i]);
421template <std::
floating_po
int T>
422Mesh<T> build_tet(MPI_Comm comm, MPI_Comm subcomm,
423 std::array<std::array<T, 3>, 2> p,
424 std::array<std::int64_t, 3> n,
428 common::Timer timer(
"Build BoxMesh (tetrahedra)");
430 std::vector<std::int64_t>
cells;
431 fem::CoordinateElement<T> element(CellType::tetrahedron, 1);
432 if (subcomm != MPI_COMM_NULL)
434 x = create_geom<T>(subcomm, p, n);
436 const auto [nx, ny, nz] = n;
437 const std::int64_t n_cells = nx * ny * nz;
441 cells.reserve(6 * (range_c[1] - range_c[0]) * 4);
444 for (std::int64_t i = range_c[0]; i < range_c[1]; ++i)
446 const std::int64_t iz = i / (nx * ny);
447 const std::int64_t j = i % (nx * ny);
448 const std::int64_t iy = j / nx;
449 const std::int64_t ix = j % nx;
450 const std::int64_t v0 = iz * (nx + 1) * (ny + 1) + iy * (nx + 1) + ix;
451 const std::int64_t v1 = v0 + 1;
452 const std::int64_t v2 = v0 + (nx + 1);
453 const std::int64_t v3 = v1 + (nx + 1);
454 const std::int64_t v4 = v0 + (nx + 1) * (ny + 1);
455 const std::int64_t v5 = v1 + (nx + 1) * (ny + 1);
456 const std::int64_t v6 = v2 + (nx + 1) * (ny + 1);
457 const std::int64_t v7 = v3 + (nx + 1) * (ny + 1);
461 {v0, v1, v3, v7, v0, v1, v7, v5, v0, v5, v7, v4,
462 v0, v3, v2, v7, v0, v6, v4, v7, v0, v2, v6, v7});
466 return finalize_mesh(comm, subcomm, cells, element, x, {x.size() / 3, 3},
467 partitioner, ghost_mode, reorder_fn);
470template <std::
floating_po
int T>
472build_hex(MPI_Comm comm, MPI_Comm subcomm, std::array<std::array<T, 3>, 2> p,
473 std::array<std::int64_t, 3> n,
475 const std::function<std::vector<std::int32_t>(
476 const graph::AdjacencyList<std::int32_t>&)>& reorder_fn)
478 common::Timer timer(
"Build BoxMesh (hexahedra)");
480 std::vector<std::int64_t>
cells;
481 fem::CoordinateElement<T> element(CellType::hexahedron, 1);
482 if (subcomm != MPI_COMM_NULL)
484 x = create_geom<T>(subcomm, p, n);
487 const auto [nx, ny, nz] = n;
488 const std::int64_t n_cells = nx * ny * nz;
491 cells.reserve((range_c[1] - range_c[0]) * 8);
492 for (std::int64_t i = range_c[0]; i < range_c[1]; ++i)
494 const std::int64_t iz = i / (nx * ny);
495 const std::int64_t j = i % (nx * ny);
496 const std::int64_t iy = j / nx;
497 const std::int64_t ix = j % nx;
499 const std::int64_t v0 = (iz * (ny + 1) + iy) * (nx + 1) + ix;
500 const std::int64_t v1 = v0 + 1;
501 const std::int64_t v2 = v0 + (nx + 1);
502 const std::int64_t v3 = v1 + (nx + 1);
503 const std::int64_t v4 = v0 + (nx + 1) * (ny + 1);
504 const std::int64_t v5 = v1 + (nx + 1) * (ny + 1);
505 const std::int64_t v6 = v2 + (nx + 1) * (ny + 1);
506 const std::int64_t v7 = v3 + (nx + 1) * (ny + 1);
507 cells.insert(
cells.end(), {v0, v1, v2, v3, v4, v5, v6, v7});
511 return finalize_mesh(comm, subcomm, cells, element, x, {x.size() / 3, 3},
512 partitioner, ghost_mode, reorder_fn);
515template <std::
floating_po
int T>
516Mesh<T> build_prism(MPI_Comm comm, MPI_Comm subcomm,
517 std::array<std::array<T, 3>, 2> p,
518 std::array<std::int64_t, 3> n,
523 std::vector<std::int64_t>
cells;
524 fem::CoordinateElement<T> element(CellType::prism, 1);
525 if (subcomm != MPI_COMM_NULL)
527 x = create_geom<T>(subcomm, p, n);
529 const std::int64_t nx = n[0];
530 const std::int64_t ny = n[1];
531 const std::int64_t nz = n[2];
532 const std::int64_t n_cells = nx * ny * nz;
535 const std::int64_t cell_range = range_c[1] - range_c[0];
538 cells.reserve(2 * cell_range * 6);
539 for (std::int64_t i = range_c[0]; i < range_c[1]; ++i)
541 const std::int64_t iz = i / (nx * ny);
542 const std::int64_t j = i % (nx * ny);
543 const std::int64_t iy = j / nx;
544 const std::int64_t ix = j % nx;
546 const std::int64_t v0 = (iz * (ny + 1) + iy) * (nx + 1) + ix;
547 const std::int64_t v1 = v0 + 1;
548 const std::int64_t v2 = v0 + (nx + 1);
549 const std::int64_t v3 = v1 + (nx + 1);
550 const std::int64_t v4 = v0 + (nx + 1) * (ny + 1);
551 const std::int64_t v5 = v1 + (nx + 1) * (ny + 1);
552 const std::int64_t v6 = v2 + (nx + 1) * (ny + 1);
553 const std::int64_t v7 = v3 + (nx + 1) * (ny + 1);
554 cells.insert(
cells.end(), {v0, v1, v2, v4, v5, v6});
555 cells.insert(
cells.end(), {v1, v2, v3, v5, v6, v7});
559 return finalize_mesh(comm, subcomm, cells, element, x, {x.size() / 3, 3},
560 partitioner, ghost_mode, reorder_fn);
563template <std::
floating_po
int T>
564Mesh<T> build_tri(MPI_Comm comm, std::array<std::array<T, 2>, 2> p,
565 std::array<std::int64_t, 2> n,
570 fem::CoordinateElement<T> element(CellType::triangle, 1);
571 if (gdim < 2 || gdim > 3)
572 throw std::runtime_error(
"2 <= gdim <= 3 for tri mesh.");
576 const auto [p0, p1] = p;
577 const auto [nx, ny] = n;
579 const auto [a, c] = p0;
580 const auto [b, d] = p1;
582 const T ab = (b - a) /
static_cast<T
>(nx);
583 const T cd = (d - c) /
static_cast<T
>(ny);
584 if (std::abs(b - a) < std::numeric_limits<T>::epsilon()
585 or std::abs(d - c) < std::numeric_limits<T>::epsilon())
587 throw std::runtime_error(
"Rectangle seems to have zero width, height or "
588 "depth. Check dimensions");
595 case DiagonalType::crossed:
596 nv = (nx + 1) * (ny + 1) + nx * ny;
600 nv = (nx + 1) * (ny + 1);
606 std::vector<std::int64_t>
cells;
607 cells.reserve(nc * 3);
610 for (std::int64_t iy = 0; iy <= ny; iy++)
612 T x1 = c + cd *
static_cast<T
>(iy);
613 for (std::int64_t ix = 0; ix <= nx; ix++)
614 x.insert(x.end(), {a + ab * static_cast<T>(ix), x1});
620 case DiagonalType::crossed:
621 for (std::int64_t iy = 0; iy < ny; iy++)
623 T x1 = c + cd * (
static_cast<T
>(iy) + 0.5);
624 for (std::int64_t ix = 0; ix < nx; ix++)
626 T x0 = a + ab * (
static_cast<T
>(ix) + 0.5);
627 x.insert(x.end(), {x0, x1});
638 case DiagonalType::crossed:
640 for (std::int64_t iy = 0; iy < ny; iy++)
642 for (std::int64_t ix = 0; ix < nx; ix++)
644 std::int64_t v0 = iy * (nx + 1) + ix;
645 std::int64_t v1 = v0 + 1;
646 std::int64_t v2 = v0 + (nx + 1);
647 std::int64_t v3 = v1 + (nx + 1);
648 std::int64_t vmid = (nx + 1) * (ny + 1) + iy * nx + ix;
651 cells.insert(
cells.end(), {v0, v1, vmid, v0, v2, vmid, v1, v3, vmid,
660 for (std::int64_t iy = 0; iy < ny; iy++)
665 case DiagonalType::right_left:
667 local_diagonal = DiagonalType::right;
669 local_diagonal = DiagonalType::left;
671 case DiagonalType::left_right:
673 local_diagonal = DiagonalType::left;
675 local_diagonal = DiagonalType::right;
680 for (std::int64_t ix = 0; ix < nx; ix++)
682 std::int64_t v0 = iy * (nx + 1) + ix;
683 std::int64_t v1 = v0 + 1;
684 std::int64_t v2 = v0 + (nx + 1);
685 std::int64_t v3 = v1 + (nx + 1);
686 switch (local_diagonal)
688 case DiagonalType::left:
690 cells.insert(
cells.end(), {v0, v1, v2, v1, v2, v3});
691 if (diagonal == DiagonalType::right_left
692 or diagonal == DiagonalType::left_right)
694 local_diagonal = DiagonalType::right;
700 cells.insert(
cells.end(), {v0, v1, v3, v0, v2, v3});
701 if (diagonal == DiagonalType::right_left
702 or diagonal == DiagonalType::left_right)
704 local_diagonal = DiagonalType::left;
713 std::size_t npts = x.size() / 2;
716 return finalize_mesh(comm, MPI_COMM_SELF, cells, element, x, {npts, 2},
717 partitioner, ghost_mode, reorder_fn);
719 std::vector<T> xg(npts * gdim, T(0));
720 for (std::size_t i = 0; i < npts; i++)
722 xg[i * gdim] = x[2 * i];
723 xg[i * gdim + 1] = x[2 * i + 1];
725 return finalize_mesh(comm, MPI_COMM_SELF, cells, element, xg,
726 {npts,
static_cast<std::size_t
>(gdim)}, partitioner,
727 ghost_mode, reorder_fn);
731 return finalize_mesh(comm, MPI_COMM_NULL, {}, element, std::vector<T>{},
732 {0,
static_cast<std::size_t
>(gdim)}, partitioner,
733 ghost_mode, reorder_fn);
737template <std::
floating_po
int T>
738Mesh<T> build_quad(MPI_Comm comm, std::array<std::array<T, 2>, 2> p,
739 std::array<std::int64_t, 2> n,
744 if (gdim < 2 || gdim > 3)
745 throw std::runtime_error(
"2 <= gdim <= 3 for quad mesh.");
747 fem::CoordinateElement<T> element(CellType::quadrilateral, 1);
750 const auto [nx, ny] = n;
751 const auto [a, c] = p[0];
752 const auto [b, d] = p[1];
754 const T ab = (b - a) /
static_cast<T
>(nx);
755 const T cd = (d - c) /
static_cast<T
>(ny);
759 x.reserve((nx + 1) * (ny + 1) * 2);
760 for (std::int64_t ix = 0; ix <= nx; ix++)
762 T x0 = a + ab *
static_cast<T
>(ix);
763 for (std::int64_t iy = 0; iy <= ny; iy++)
764 x.insert(x.end(), {x0, c + cd * static_cast<T>(iy)});
768 std::vector<std::int64_t>
cells;
769 cells.reserve(nx * ny * 4);
770 for (std::int64_t ix = 0; ix < nx; ix++)
772 for (std::int64_t iy = 0; iy < ny; iy++)
774 std::int64_t i0 = ix * (ny + 1);
775 cells.insert(
cells.end(), {i0 + iy, i0 + iy + 1, i0 + iy + ny + 1,
780 std::size_t npts = x.size() / 2;
783 return finalize_mesh(comm, MPI_COMM_SELF, cells, element, x, {npts, 2},
784 partitioner, ghost_mode, reorder_fn);
786 std::vector<T> xg(npts * gdim, T(0));
787 for (std::size_t i = 0; i < npts; i++)
789 xg[i * gdim] = x[2 * i];
790 xg[i * gdim + 1] = x[2 * i + 1];
792 return finalize_mesh(comm, MPI_COMM_SELF, cells, element, xg,
793 {npts,
static_cast<std::size_t
>(gdim)}, partitioner,
794 ghost_mode, reorder_fn);
798 return finalize_mesh(comm, MPI_COMM_NULL, {}, element, std::vector<T>{},
799 {0,
static_cast<std::size_t
>(gdim)}, partitioner,
800 ghost_mode, reorder_fn);
Definition CoordinateElement.h:38
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Small, foundational mesh types (enums, etc.) with minimal dependencies.
Functions supporting mesh operations.
int size(MPI_Comm comm)
Definition MPI.cpp:81
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:73
constexpr std::array< std::int64_t, 2 > local_range(int index, std::int64_t N, int size)
Partition a global range [0, N - 1] across callers into non-overlapping sub-partitions of almost equa...
Definition local_range.h:26
void cells(la::SparsityPattern &pattern, const std::pair< R0, R1 > &cells, std::array< std::reference_wrapper< const DofMap >, 2 > dofmaps)
Iterate over cells and insert entries into sparsity pattern.
Definition sparsitybuild.h:37
bool has_partitioner(const AnyPartitionFunction &partitioner)
Whether an AnyPartitionFunction holds a callable partitioner.
Definition partition.cpp:130
std::vector< std::int32_t > reorder_rcm(const graph::AdjacencyList< std::int32_t > &graph)
Re-order a graph using the Reverse Cuthill-McKee algorithm.
Definition ordering.cpp:149
AdjacencyList< std::int32_t > partition_graph(MPI_Comm comm, int nparts, const AdjacencyList< std::int64_t > &local_graph, std::optional< std::span< const std::int32_t > > node_weights, std::optional< std::span< const std::int32_t > > edge_weights, bool ghosting)
Partition graph across processes using the default graph partitioner.
Definition partition.cpp:136
std::variant< partition_fn, geom_partition_fn, hybrid_partition_fn > AnyPartitionFunction
Any of the three partitioning function shapes that mesh::create_mesh accepts: partition_fn,...
Definition partition.h:118
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
DiagonalType
Enum for different diagonal types.
Definition generation.h:32
Mesh< T > create_rectangle(MPI_Comm comm, std::array< std::array< T, 2 >, 2 > p, std::array< std::int64_t, 2 > n, CellType celltype, graph::AnyPartitionFunction partitioner, DiagonalType diagonal=DiagonalType::right, int gdim=2, GhostMode ghost_mode=GhostMode::none, const CellReorderFunction &reorder_fn=graph::reorder_rcm)
Create a uniform mesh::Mesh over the rectangle spanned by the two points p.
Definition generation.h:219
std::function< std::vector< std::int32_t >( const graph::AdjacencyList< std::int32_t > &)> CellReorderFunction
Function that reorders (locally) cells that are owned by this process. It takes the local mesh dual g...
Definition utils.h:202
CellType
Cell type identifier.
Definition cell_types.h:22
std::vector< T > h(const Mesh< T > &mesh, std::span< const std::int32_t > entities, int dim)
Compute greatest distance between any two vertices of the mesh entities (h).
Definition utils.h:285
Mesh< T > create_box(MPI_Comm comm, MPI_Comm subcomm, std::array< std::array< T, 3 >, 2 > p, std::array< std::int64_t, 3 > n, CellType celltype, graph::AnyPartitionFunction partitioner={}, GhostMode ghost_mode=GhostMode::none, const CellReorderFunction &reorder_fn=graph::reorder_rcm)
Create a uniform mesh::Mesh over rectangular prism spanned by the two points p.
Definition generation.h:131
Mesh< T > create_interval(MPI_Comm comm, std::int64_t n, std::array< T, 2 > p, mesh::GhostMode ghost_mode=mesh::GhostMode::none, graph::AnyPartitionFunction partitioner={}, int gdim=1, const CellReorderFunction &reorder_fn=graph::reorder_rcm)
Interval mesh of the 1D line [a, b].
Definition generation.h:298
Mesh< typename std::remove_reference_t< typename U::value_type > > create_mesh(MPI_Comm comm, MPI_Comm commt, std::vector< std::span< const std::int64_t > > cells, const std::vector< fem::CoordinateElement< typename std::remove_reference_t< typename U::value_type > > > &elements, MPI_Comm commg, const U &x, std::array< std::size_t, 2 > xshape, const graph::Partitioner &partitioner, GhostMode ghost_mode, std::optional< std::int32_t > max_facet_to_cell_links, int num_threads, const CellReorderFunction &reorder_fn=graph::reorder_rcm)
Create a distributed mesh::Mesh from mesh data and using the provided graph partitioning function for...
Definition utils.h:1246
GhostMode
Enum for different partitioning ghost modes.
Definition types.h:19
An AnyPartitionFunction together with the node weights it should be called with, if any.
Definition partition.h:156