DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
Vector.h
1// Copyright (C) 2020-2026 Garth N. Wells
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include "utils.h"
10#include <algorithm>
11#include <cmath>
12#include <complex>
13#include <dolfinx/common/IndexMap.h>
14#include <dolfinx/common/MPI.h>
15#include <dolfinx/common/Scatterer.h>
16#include <dolfinx/common/types.h>
17#include <limits>
18#include <memory>
19#include <numeric>
20#include <span>
21#include <stdexcept>
22#include <type_traits>
23#include <utility>
24#include <vector>
25
26namespace dolfinx::la
27{
29template <class F, class Container, class ScatterContainer>
30concept VectorPackKernel = requires(F f, ScatterContainer idx, Container x) {
31 f(idx.cbegin(), idx.cend(), x.cbegin(), x.begin());
32};
33
35template <class GetPtr, class U, class T>
36concept GetPtrConcept = requires(GetPtr f, U x) {
37 { f(x) } -> std::same_as<T*>;
38} and requires(GetPtr f, const U x) {
39 { f(x) } -> std::same_as<const T*>;
40};
41
50template <typename T, typename Container = std::vector<T>,
51 typename ScatterContainer = std::vector<std::int32_t>>
52class Vector
53{
54 static_assert(std::is_same_v<typename Container::value_type, T>);
55
56 template <typename, typename, typename>
57 friend class Vector;
58
59private:
67 template <class F>
68 static void dispatch_bs(int bs, F&& f)
69 {
70 switch (bs)
71 {
72 case 1:
73 return f(std::integral_constant<int, 1>{});
74 case 2:
75 return f(std::integral_constant<int, 2>{});
76 case 3:
77 return f(std::integral_constant<int, 3>{});
78 default:
79 return f(bs);
80 }
81 }
82
87 auto get_pack()
88 {
89 return [bs = _bs](typename ScatterContainer::const_iterator idx_first,
90 typename ScatterContainer::const_iterator idx_last,
91 const auto in_first, auto out_first)
92 {
93 // out[i * bs + j] = in[idx[i] * bs + j]
94 dispatch_bs(bs,
95 [&](auto B)
96 {
97 auto out = out_first;
98 for (auto idx = idx_first; idx != idx_last; ++idx)
99 {
100 auto in = std::next(in_first, (*idx) * B);
101 for (int j = 0; j < B; ++j, ++in, ++out)
102 *out = *in;
103 }
104 });
105 };
106 }
107
112 auto get_unpack()
113 {
114 // Assignment is accumulation with an operation that keeps the
115 // received value
116 return get_unpack_op([](auto, auto received) { return received; });
117 }
118
124 template <typename BinaryOp>
125 auto get_unpack_op(BinaryOp op)
126 {
127 return [op, bs = _bs](typename ScatterContainer::const_iterator idx_first,
128 typename ScatterContainer::const_iterator idx_last,
129 const auto in_first, auto out_first)
130 {
131 // out[idx[i] * bs + j] = op(out[idx[i] * bs + j], in[i * bs + j])
132 dispatch_bs(bs,
133 [&](auto B)
134 {
135 auto in = in_first;
136 for (auto idx = idx_first; idx != idx_last; ++idx)
137 {
138 auto out = std::next(out_first, (*idx) * B);
139 for (int j = 0; j < B; ++j, ++in, ++out)
140 *out = op(*out, *in);
141 }
142 });
143 };
144 }
145
146public:
148 using container_type = Container;
149
151 using value_type = container_type::value_type;
152
153 static_assert(std::is_same_v<value_type, typename container_type::value_type>,
154 "Scalar type and container value type must be the same.");
155
168 Vector(std::shared_ptr<const common::IndexMap> map, int bs)
169 : Vector(map, bs,
170 std::make_shared<common::Scatterer<ScatterContainer>>(*map))
171 {
172 }
173
180 Vector(std::shared_ptr<const common::IndexMap> map, int bs,
181 std::shared_ptr<const common::Scatterer<ScatterContainer>> scatterer)
182 : _map(std::move(map)), _bs(bs),
183 _x(bs * (_map->size_local() + _map->num_ghosts())),
184 _scatterer(std::move(scatterer)),
185 _buffer_local(bs * _scatterer->local_indices_block().size()),
186 _buffer_remote(bs * _scatterer->remote_indices_block().size())
187 {
188 }
189
193 Vector(const Vector& x) = default;
194
198 Vector(Vector&& x) = default;
199
200private:
210 std::shared_ptr<const common::Scatterer<ScatterContainer>>
211 scatter_ptr(auto sc) const
212 {
213 using SC = typename std::remove_cv<
214 typename decltype(sc)::element_type>::type::container_type;
215 if constexpr (std::is_same_v<ScatterContainer, SC>)
216 return sc; // Scatters use same container
217 else // Scatters use different containers, so copy
218 return std::make_shared<common::Scatterer<ScatterContainer>>(*sc);
219 }
220
221public:
241 template <typename T0, typename Container0, typename ScatterContainer0>
242 explicit Vector(const Vector<T0, Container0, ScatterContainer0>& x)
243 : _map(x.index_map()), _bs(x.bs()), _x(x._x.begin(), x._x.end()),
244 _scatterer(scatter_ptr(x._scatterer)),
245 _buffer_local(_bs * _scatterer->local_indices_block().size()),
246 _buffer_remote(_bs * _scatterer->remote_indices_block().size())
247 {
248 }
249
250 // Assignment operator (disabled)
251 Vector& operator=(const Vector& x) = delete;
252
258 Vector& operator=(Vector&& x) = default;
259
264 [[deprecated("Use std::ranges::fill(u.array(), v) instead.")]] void
266 {
267 std::ranges::fill(_x, v);
268 }
269
285 template <typename U, typename GetPtr>
288 void scatter_fwd_begin(U pack, GetPtr get_ptr)
289 {
290 pack(_scatterer->local_indices_block().begin(),
291 _scatterer->local_indices_block().end(), _x.begin(),
292 _buffer_local.begin());
293 _scatterer->scatter_fwd_begin(get_ptr(_buffer_local),
294 get_ptr(_buffer_remote), _bs,
295 _request.request());
296 }
297
308 requires requires(Container c) {
309 { c.data() } -> std::same_as<T*>;
310 }
311 {
312 scatter_fwd_begin(get_pack(), [](auto&& x) { return x.data(); });
313 }
314
329 template <typename U>
330 requires VectorPackKernel<U, container_type, ScatterContainer>
331 void scatter_fwd_end(U unpack)
332 {
333 _scatterer->scatter_fwd_end(_request.request());
334 unpack(_scatterer->remote_indices_block().begin(),
335 _scatterer->remote_indices_block().end(), _buffer_remote.begin(),
336 std::next(_x.begin(), _bs * _map->size_local()));
337 }
338
350 requires requires(Container c) {
351 { c.data() } -> std::same_as<T*>;
352 }
353 {
354 this->scatter_fwd_end(get_unpack());
355 }
356
368 requires requires(Container c) {
369 { c.data() } -> std::same_as<T*>;
370 }
371 {
372 this->scatter_fwd_begin(get_pack(), [](auto&& x) { return x.data(); });
373 this->scatter_fwd_end(get_unpack());
374 }
375
390 template <typename U, typename GetPtr>
391 requires VectorPackKernel<U, container_type, ScatterContainer>
392 and GetPtrConcept<GetPtr, Container, T>
393 void scatter_rev_begin(U pack, GetPtr get_ptr)
394 {
395 std::int32_t local_size = _bs * _map->size_local();
396 pack(_scatterer->remote_indices_block().begin(),
397 _scatterer->remote_indices_block().end(),
398 std::next(_x.begin(), local_size), _buffer_remote.begin());
399 _scatterer->scatter_rev_begin(get_ptr(_buffer_remote),
400 get_ptr(_buffer_local), _bs,
401 _request.request());
402 }
403
414 requires requires(Container c) {
415 { c.data() } -> std::same_as<T*>;
416 }
417 {
418 scatter_rev_begin(get_pack(), [](auto&& x) { return x.data(); });
419 }
420
432 template <typename U>
433 requires VectorPackKernel<U, container_type, ScatterContainer>
434 void scatter_rev_end(U unpack)
435 {
436 _scatterer->scatter_rev_end(_request.request());
437 unpack(_scatterer->local_indices_block().begin(),
438 _scatterer->local_indices_block().end(), _buffer_local.begin(),
439 _x.begin());
440 }
441
454 template <class BinaryOperation>
455 requires requires(Container c) {
456 { c.data() } -> std::same_as<T*>;
457 }
458 void scatter_rev(BinaryOperation op)
459 {
460 this->scatter_rev_begin();
461 this->scatter_rev_end(get_unpack_op(op));
462 }
463
465 std::shared_ptr<const common::IndexMap> index_map() const noexcept
466 {
467 return _map;
468 }
469
472 std::shared_ptr<const common::Scatterer<ScatterContainer>>
473 scatterer() const noexcept
474 {
475 return _scatterer;
476 }
477
479 constexpr int bs() const noexcept { return _bs; }
480
484 container_type& array() noexcept { return _x; }
485
489 const container_type& array() const noexcept { return _x; }
490
493 [[deprecated("Use array() instead.")]] container_type&
494 mutable_array() noexcept
495 {
496 return _x;
497 }
498
499private:
500 // Map describing the data layout
501 std::shared_ptr<const common::IndexMap> _map;
502
503 // Block size
504 int _bs;
505
506 // Vector data
508
509 // Scatter for managing MPI communication
510 std::shared_ptr<const common::Scatterer<ScatterContainer>> _scatterer;
511
512 // Request for a scatter in flight. Transfers on move, so that the
513 // moved-from vector does not name the target's request.
514 dolfinx::MPI::Request _request;
515
516 // Buffers for ghost scatters
517 container_type _buffer_local, _buffer_remote;
518}; // namespace dolfinx::la
519
530template <class V>
531auto inner_product(const V& a, const V& b)
532{
533 using T = typename V::value_type;
534 const std::int32_t local_size = a.bs() * a.index_map()->size_local();
535 if (local_size != b.bs() * b.index_map()->size_local())
536 throw std::invalid_argument("Incompatible vector sizes");
537
538 const T local = std::transform_reduce(
539 a.array().begin(), std::next(a.array().begin(), local_size),
540 b.array().begin(), static_cast<T>(0), std::plus{},
541 [](T a, T b) -> T
542 {
543 if constexpr (std::is_same<T, std::complex<double>>::value
544 or std::is_same<T, std::complex<float>>::value)
545 {
546 return std::conj(a) * b;
547 }
548 else
549 return a * b;
550 });
551
552 T result;
553 MPI_Allreduce(&local, &result, 1, dolfinx::MPI::mpi_t<T>, MPI_SUM,
554 a.index_map()->comm());
555 return result;
556}
557
561template <class V>
562auto squared_norm(const V& a)
563{
564 using T = typename V::value_type;
565 T result = inner_product(a, a);
566 return std::real(result);
567}
568
575template <class V>
576auto norm(const V& x, Norm type = Norm::l2)
577{
578 using T = typename V::value_type;
579 switch (type)
580 {
581 case Norm::l1:
582 {
583 std::int32_t size_local = x.bs() * x.index_map()->size_local();
584 using U = typename dolfinx::scalar_value_t<T>;
585 U local_l1 = std::accumulate(
586 x.array().begin(), std::next(x.array().begin(), size_local), U(0),
587 [](auto norm, auto x) { return norm + std::abs(x); });
588 U l1(0);
589 MPI_Allreduce(&local_l1, &l1, 1, MPI::mpi_t<U>, MPI_SUM,
590 x.index_map()->comm());
591 return l1;
592 }
593 case Norm::l2:
594 return std::sqrt(squared_norm(x));
595 case Norm::linf:
596 {
597 std::int32_t size_local = x.bs() * x.index_map()->size_local();
598 auto max_pos = std::max_element(
599 x.array().begin(), std::next(x.array().begin(), size_local),
600 [](T a, T b) { return std::norm(a) < std::norm(b); });
601 auto local_linf = std::abs(*max_pos);
602 decltype(local_linf) linf = 0;
603 MPI_Allreduce(&local_linf, &linf, 1, MPI::mpi_t<decltype(linf)>, MPI_MAX,
604 x.index_map()->comm());
605 return linf;
606 }
607 default:
608 throw std::invalid_argument("Norm type not supported");
609 }
610}
611
618template <class V>
619void orthonormalize(std::vector<std::reference_wrapper<V>> basis)
620{
621 using T = typename V::value_type;
622 using U = typename dolfinx::scalar_value_t<T>;
623
624 // Loop over each vector in basis
625 for (std::size_t i = 0; i < basis.size(); ++i)
626 {
627 // Orthogonalize vector i with respect to previously orthonormalized
628 // vectors
629 V& bi = basis[i].get();
630 for (std::size_t j = 0; j < i; ++j)
631 {
632 const V& bj = basis[j].get();
633
634 // basis_i <- basis_i - dot_ij basis_j
635 auto dot_ij = inner_product(bi, bj);
636 std::ranges::transform(bj.array(), bi.array(), bi.array().begin(),
637 [dot_ij](auto xj, auto xi)
638 { return xi - dot_ij * xj; });
639 }
640
641 // Normalise basis function
642 auto norm = la::norm(bi, la::Norm::l2);
643 if (norm * norm < std::numeric_limits<U>::epsilon())
644 {
645 throw std::runtime_error(
646 "Linear dependency detected. Cannot orthogonalize.");
647 }
648 std::ranges::transform(bi.array(), bi.array().begin(),
649 [norm](auto x) { return x / norm; });
650 }
651}
652
661template <class V>
663 std::vector<std::reference_wrapper<const V>> basis,
664 dolfinx::scalar_value_t<typename V::value_type> eps = std::numeric_limits<
665 dolfinx::scalar_value_t<typename V::value_type>>::epsilon())
666{
667 using T = typename V::value_type;
668 for (std::size_t i = 0; i < basis.size(); i++)
669 {
670 for (std::size_t j = i; j < basis.size(); ++j)
671 {
672 T delta_ij = (i == j) ? T(1) : T(0);
673 auto dot_ij = inner_product(basis[i].get(), basis[j].get());
674 if (std::norm(delta_ij - dot_ij) > eps)
675 return false;
676 }
677 }
678
679 return true;
680}
681
682} // namespace dolfinx::la
Holder for the request of a non-blocking operation.
Definition MPI.h:421
A Scatterer supports the scattering and gathering of distributed data that is associated with a commo...
Definition Scatterer.h:87
A vector that can be distributed across processes.
Definition Vector.h:53
std::shared_ptr< const common::IndexMap > index_map() const noexcept
Get IndexMap.
Definition Vector.h:465
container_type::value_type value_type
Scalar type.
Definition Vector.h:151
void scatter_rev_begin(U pack, GetPtr get_ptr)
Start scatter (send) of ghost entry data to the owning process of an index.
Definition Vector.h:393
Vector(const Vector< T0, Container0, ScatterContainer0 > &x)
Create a vector by copying and converting another vector.
Definition Vector.h:242
container_type & mutable_array() noexcept
Get local part of the vector.
Definition Vector.h:494
Vector(std::shared_ptr< const common::IndexMap > map, int bs)
Create a distributed vector.
Definition Vector.h:168
void scatter_rev(BinaryOperation op)
Scatter (send) of ghost data values to the owning process and assign/accumulate into the owned data e...
Definition Vector.h:458
constexpr int bs() const noexcept
Get block size.
Definition Vector.h:479
void scatter_fwd_end(U unpack)
End scatter (send) of local data values that are ghosted on other processes.
Definition Vector.h:331
container_type & array() noexcept
Get the process-local part of the vector.
Definition Vector.h:484
void scatter_rev_end(U unpack)
End scatter of ghost data to owner and update owned entries.
Definition Vector.h:434
Vector & operator=(Vector &&x)=default
void scatter_fwd_end()
End scatter (send) of local data values that are ghosted on other processes (simplified CPU version).
Definition Vector.h:349
void scatter_fwd()
Scatter (send) of local data values that are ghosted on other processes and update ghost entry values...
Definition Vector.h:367
Container container_type
Container type.
Definition Vector.h:148
void scatter_fwd_begin(U pack, GetPtr get_ptr)
Begin scatter (send) of local data that is ghosted on other processes.
Definition Vector.h:288
void set(value_type v)
Set all entries (including ghosts).
Definition Vector.h:265
Vector(Vector &&x)=default
const container_type & array() const noexcept
Get the process-local part of the vector (const version).
Definition Vector.h:489
Vector(std::shared_ptr< const common::IndexMap > map, int bs, std::shared_ptr< const common::Scatterer< ScatterContainer > > scatterer)
Create a distributed vector using an existing scatterer.
Definition Vector.h:180
std::shared_ptr< const common::Scatterer< ScatterContainer > > scatterer() const noexcept
Get the scatterer used for halo communication.
Definition Vector.h:473
void scatter_fwd_begin()
Begin scatter (send) of local data that is ghosted on other processes (simplified CPU version).
Definition Vector.h:307
Vector(const Vector &x)=default
void scatter_rev_begin()
Start scatter (send) of ghost entry data to the owning process of an index (simplified CPU version).
Definition Vector.h:413
Access to pointer function concept.
Definition Vector.h:36
la::Vector scatter pack/unpack function concept.
Definition Vector.h:30
MPI_Datatype mpi_t
Retrieves the MPI data type associated to the provided type.
Definition MPI.h:326
Miscellaneous classes, functions and types.
Definition dolfinx_common.h:8
Linear algebra interface.
Definition dolfinx_la.h:7
void orthonormalize(std::vector< std::reference_wrapper< V > > basis)
Orthonormalize a set of vectors.
Definition Vector.h:619
auto squared_norm(const V &a)
Compute the squared L2 norm of vector.
Definition Vector.h:562
auto norm(const V &x, Norm type=Norm::l2)
Compute the norm of the vector.
Definition Vector.h:576
auto inner_product(const V &a, const V &b)
Compute the inner product of two vectors.
Definition Vector.h:531
bool is_orthonormal(std::vector< std::reference_wrapper< const V > > basis, dolfinx::scalar_value_t< typename V::value_type > eps=std::numeric_limits< dolfinx::scalar_value_t< typename V::value_type > >::epsilon())
Test if basis is orthonormal.
Definition Vector.h:662
Norm
Norm types.
Definition utils.h:22