DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
Vector.h
1// Copyright (C) 2020-2025 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/Scatterer.h>
15#include <dolfinx/common/types.h>
16#include <limits>
17#include <memory>
18#include <numeric>
19#include <span>
20#include <type_traits>
21#include <vector>
22
23namespace dolfinx::la
24{
26template <class F, class Container, class ScatterContainer>
27concept VectorPackKernel = requires(F f, ScatterContainer idx, Container x) {
28 f(idx.cbegin(), idx.cend(), x.cbegin(), x.begin());
29};
30
32template <class GetPtr, class U, class T>
33concept GetPtrConcept = requires(GetPtr f, U x) {
34 { f(x) } -> std::same_as<T*>;
35} and requires(GetPtr f, const U x) {
36 { f(x) } -> std::same_as<const T*>;
37};
38
47template <typename T, typename Container = std::vector<T>,
48 typename ScatterContainer = std::vector<std::int32_t>>
49class Vector
50{
51 static_assert(std::is_same_v<typename Container::value_type, T>);
52
53 template <typename, typename, typename>
54 friend class Vector;
55
56private:
61 auto get_pack()
62 {
63 return [](typename ScatterContainer::const_iterator idx_first,
64 typename ScatterContainer::const_iterator idx_last,
65 const auto in_first, auto out_first)
66 {
67 // out[i] = in[idx[i]]
68 std::transform(idx_first, idx_last, out_first,
69 [in_first](auto p) { return *std::next(in_first, p); });
70 };
71 }
72
77 auto get_unpack()
78 {
79 return [](typename ScatterContainer::const_iterator idx_first,
80 typename ScatterContainer::const_iterator idx_last,
81 const auto in_first, auto out_first)
82 {
83 // out[idx[i]] = in[i]
84 for (typename ScatterContainer::const_iterator idx = idx_first;
85 idx != idx_last; ++idx)
86 {
87 std::size_t d = std::distance(idx_first, idx);
88 *std::next(out_first, *idx) = *std::next(in_first, d);
89 }
90 };
91 }
92
98 template <typename BinaryOp>
99 auto get_unpack_op(BinaryOp op)
100 {
101 return [op](typename ScatterContainer::const_iterator idx_first,
102 typename ScatterContainer::const_iterator idx_last,
103 const auto in_first, auto out_first)
104 {
105 // out[idx[i]] = op(out[idx[i]], in[i])
106 for (typename ScatterContainer::const_iterator idx = idx_first;
107 idx != idx_last; ++idx)
108 {
109 std::size_t d = std::distance(idx_first, idx);
110 auto& out = *std::next(out_first, *idx);
111 out = op(out, *std::next(in_first, d));
112 }
113 };
114 }
115
116public:
118 using container_type = Container;
119
121 using value_type = container_type::value_type;
122
123 static_assert(std::is_same_v<value_type, typename container_type::value_type>,
124 "Scalar type and container value type must be the same.");
125
131 Vector(std::shared_ptr<const common::IndexMap> map, int bs)
132 : _map(map), _bs(bs), _x(bs * (map->size_local() + map->num_ghosts())),
133 _scatterer(
134 std::make_shared<common::Scatterer<ScatterContainer>>(*_map, bs)),
135 _buffer_local(_scatterer->local_indices().size()),
136 _buffer_remote(_scatterer->remote_indices().size())
137 {
138 }
139
141 Vector(const Vector& x) = default;
142
144 Vector(Vector&& x) = default;
145
146private:
156 std::shared_ptr<const common::Scatterer<ScatterContainer>>
157 scatter_ptr(auto sc) const
158 {
159 using SC = typename std::remove_cv<
160 typename decltype(sc)::element_type>::type::container_type;
161 if constexpr (std::is_same_v<ScatterContainer, SC>)
162 return sc; // Scatters use same container
163 else // Scatters use different containers, so copy
164 return std::make_shared<common::Scatterer<ScatterContainer>>(*sc);
165 }
166
167public:
177 template <typename T0, typename Container0, typename ScatterContainer0>
178 explicit Vector(const Vector<T0, Container0, ScatterContainer0>& x)
179 : _map(x.index_map()), _bs(x.bs()), _x(x._x.begin(), x._x.end()),
180 _scatterer(scatter_ptr(x._scatterer)), _request(MPI_REQUEST_NULL),
181 _buffer_local(_scatterer->local_indices().size()),
182 _buffer_remote(_scatterer->remote_indices().size())
183 {
184 }
185
186 // Assignment operator (disabled)
187 Vector& operator=(const Vector& x) = delete;
188
190 Vector& operator=(Vector&& x) = default;
191
196 [[deprecated("Use std::ranges::fill(u.array(), v) instead.")]] void
198 {
199 std::ranges::fill(_x, v);
200 }
201
216 template <typename U, typename GetPtr>
219 void scatter_fwd_begin(U pack, GetPtr get_ptr)
220 {
221 pack(_scatterer->local_indices().begin(), _scatterer->local_indices().end(),
222 _x.begin(), _buffer_local.begin());
223 _scatterer->scatter_fwd_begin(get_ptr(_buffer_local),
224 get_ptr(_buffer_remote), _request);
225 }
226
236 requires requires(Container c) {
237 { c.data() } -> std::same_as<T*>;
238 }
239 {
240 scatter_fwd_begin(get_pack(), [](auto&& x) { return x.data(); });
241 }
242
254 template <typename U>
255 requires VectorPackKernel<U, container_type, ScatterContainer>
256 void scatter_fwd_end(U unpack)
257 {
258 _scatterer->scatter_end(_request);
259 unpack(_scatterer->remote_indices().begin(),
260 _scatterer->remote_indices().end(), _buffer_remote.begin(),
261 std::next(_x.begin(), _bs * _map->size_local()));
262 }
263
273 requires requires(Container c) {
274 { c.data() } -> std::same_as<T*>;
275 }
276 {
277 this->scatter_fwd_end(get_unpack());
278 }
279
290 requires requires(Container c) {
291 { c.data() } -> std::same_as<T*>;
292 }
293 {
294 this->scatter_fwd_begin(get_pack(), [](auto&& x) { return x.data(); });
295 this->scatter_fwd_end(get_unpack());
296 }
297
311 template <typename U, typename GetPtr>
312 requires VectorPackKernel<U, container_type, ScatterContainer>
313 and GetPtrConcept<GetPtr, Container, T>
314 void scatter_rev_begin(U pack, GetPtr get_ptr)
315 {
316 std::int32_t local_size = _bs * _map->size_local();
317 pack(_scatterer->remote_indices().begin(),
318 _scatterer->remote_indices().end(), std::next(_x.begin(), local_size),
319 _buffer_remote.begin());
320 _scatterer->scatter_rev_begin(get_ptr(_buffer_remote),
321 get_ptr(_buffer_local), _request);
322 }
323
333 requires requires(Container c) {
334 { c.data() } -> std::same_as<T*>;
335 }
336 {
337 scatter_rev_begin(get_pack(), [](auto&& x) { return x.data(); });
338 }
339
347 template <typename U>
348 requires VectorPackKernel<U, container_type, ScatterContainer>
349 void scatter_rev_end(U unpack)
350 {
351 _scatterer->scatter_end(_request);
352 unpack(_scatterer->local_indices().begin(),
353 _scatterer->local_indices().end(), _buffer_local.begin(),
354 _x.begin());
355 }
356
368 template <class BinaryOperation>
369 requires requires(Container c) {
370 { c.data() } -> std::same_as<T*>;
371 }
372 void scatter_rev(BinaryOperation op)
373 {
374 this->scatter_rev_begin();
375 this->scatter_rev_end(get_unpack_op(op));
376 }
377
379 std::shared_ptr<const common::IndexMap> index_map() const { return _map; }
380
382 constexpr int bs() const { return _bs; }
383
387 container_type& array() { return _x; }
388
392 const container_type& array() const { return _x; }
393
396 [[deprecated("Use array() instead.")]] container_type& mutable_array()
397 {
398 return _x;
399 }
400
401private:
402 // Map describing the data layout
403 std::shared_ptr<const common::IndexMap> _map;
404
405 // Block size
406 int _bs;
407
408 // Vector data
410
411 // Scatter for managing MPI communication
412 std::shared_ptr<const common::Scatterer<ScatterContainer>> _scatterer;
413
414 // MPI request handle
415 MPI_Request _request = MPI_REQUEST_NULL;
416
417 // Buffers for ghost scatters
418 container_type _buffer_local, _buffer_remote;
419}; // namespace dolfinx::la
420
431template <class V>
432auto inner_product(const V& a, const V& b)
433{
434 using T = typename V::value_type;
435 const std::int32_t local_size = a.bs() * a.index_map()->size_local();
436 if (local_size != b.bs() * b.index_map()->size_local())
437 throw std::runtime_error("Incompatible vector sizes");
438
439 const T local = std::transform_reduce(
440 a.array().begin(), std::next(a.array().begin(), local_size),
441 b.array().begin(), static_cast<T>(0), std::plus{},
442 [](T a, T b) -> T
443 {
444 if constexpr (std::is_same<T, std::complex<double>>::value
445 or std::is_same<T, std::complex<float>>::value)
446 {
447 return std::conj(a) * b;
448 }
449 else
450 return a * b;
451 });
452
453 T result;
454 MPI_Allreduce(&local, &result, 1, dolfinx::MPI::mpi_t<T>, MPI_SUM,
455 a.index_map()->comm());
456 return result;
457}
458
462template <class V>
463auto squared_norm(const V& a)
464{
465 using T = typename V::value_type;
466 T result = inner_product(a, a);
467 return std::real(result);
468}
469
476template <class V>
477auto norm(const V& x, Norm type = Norm::l2)
478{
479 using T = typename V::value_type;
480 switch (type)
481 {
482 case Norm::l1:
483 {
484 std::int32_t size_local = x.bs() * x.index_map()->size_local();
485 using U = typename dolfinx::scalar_value_t<T>;
486 U local_l1 = std::accumulate(
487 x.array().begin(), std::next(x.array().begin(), size_local), U(0),
488 [](auto norm, auto x) { return norm + std::abs(x); });
489 U l1(0);
490 MPI_Allreduce(&local_l1, &l1, 1, MPI::mpi_t<U>, MPI_SUM,
491 x.index_map()->comm());
492 return l1;
493 }
494 case Norm::l2:
495 return std::sqrt(squared_norm(x));
496 case Norm::linf:
497 {
498 std::int32_t size_local = x.bs() * x.index_map()->size_local();
499 auto max_pos = std::max_element(
500 x.array().begin(), std::next(x.array().begin(), size_local),
501 [](T a, T b) { return std::norm(a) < std::norm(b); });
502 auto local_linf = std::abs(*max_pos);
503 decltype(local_linf) linf = 0;
504 MPI_Allreduce(&local_linf, &linf, 1, MPI::mpi_t<decltype(linf)>, MPI_MAX,
505 x.index_map()->comm());
506 return linf;
507 }
508 default:
509 throw std::runtime_error("Norm type not supported");
510 }
511}
512
519template <class V>
520void orthonormalize(std::vector<std::reference_wrapper<V>> basis)
521{
522 using T = typename V::value_type;
523 using U = typename dolfinx::scalar_value_t<T>;
524
525 // Loop over each vector in basis
526 for (std::size_t i = 0; i < basis.size(); ++i)
527 {
528 // Orthogonalize vector i with respect to previously orthonormalized
529 // vectors
530 V& bi = basis[i].get();
531 for (std::size_t j = 0; j < i; ++j)
532 {
533 const V& bj = basis[j].get();
534
535 // basis_i <- basis_i - dot_ij basis_j
536 auto dot_ij = inner_product(bi, bj);
537 std::ranges::transform(bj.array(), bi.array(), bi.array().begin(),
538 [dot_ij](auto xj, auto xi)
539 { return xi - dot_ij * xj; });
540 }
541
542 // Normalise basis function
543 auto norm = la::norm(bi, la::Norm::l2);
544 if (norm * norm < std::numeric_limits<U>::epsilon())
545 {
546 throw std::runtime_error(
547 "Linear dependency detected. Cannot orthogonalize.");
548 }
549 std::ranges::transform(bi.array(), bi.array().begin(),
550 [norm](auto x) { return x / norm; });
551 }
552}
553
562template <class V>
564 std::vector<std::reference_wrapper<const V>> basis,
565 dolfinx::scalar_value_t<typename V::value_type> eps = std::numeric_limits<
566 dolfinx::scalar_value_t<typename V::value_type>>::epsilon())
567{
568 using T = typename V::value_type;
569 for (std::size_t i = 0; i < basis.size(); i++)
570 {
571 for (std::size_t j = i; j < basis.size(); ++j)
572 {
573 T delta_ij = (i == j) ? T(1) : T(0);
574 auto dot_ij = inner_product(basis[i].get(), basis[j].get());
575 if (std::norm(delta_ij - dot_ij) > eps)
576 return false;
577 }
578 }
579
580 return true;
581}
582
583} // namespace dolfinx::la
A vector that can be distributed across processes.
Definition Vector.h:50
container_type & mutable_array()
Get local part of the vector.
Definition Vector.h:396
container_type & array()
Get the process-local part of the vector.
Definition Vector.h:387
container_type::value_type value_type
Scalar type.
Definition Vector.h:121
const container_type & array() const
Get the process-local part of the vector (const version).
Definition Vector.h:392
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:314
Vector(const Vector< T0, Container0, ScatterContainer0 > &x)
Copy-convert vector, possibly using to different container types.
Definition Vector.h:178
constexpr int bs() const
Get block size.
Definition Vector.h:382
Vector(std::shared_ptr< const common::IndexMap > map, int bs)
Create a distributed vector.
Definition Vector.h:131
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:372
void scatter_fwd_end(U unpack)
End scatter (send) of local data values that are ghosted on other processes.
Definition Vector.h:256
void scatter_rev_end(U unpack)
End scatter of ghost data to owner and update owned entries.
Definition Vector.h:349
Vector & operator=(Vector &&x)=default
Move assignment operator.
std::shared_ptr< const common::IndexMap > index_map() const
Get IndexMap.
Definition Vector.h:379
void scatter_fwd_end()
End scatter (send) of local data values that are ghosted on other processes (simplified CPU version).
Definition Vector.h:272
void scatter_fwd()
Scatter (send) of local data values that are ghosted on other processes and update ghost entry values...
Definition Vector.h:289
Container container_type
Container type.
Definition Vector.h:118
void scatter_fwd_begin(U pack, GetPtr get_ptr)
Begin scatter (send) of local data that is ghosted on other processes.
Definition Vector.h:219
void set(value_type v)
Set all entries (including ghosts).
Definition Vector.h:197
Vector(Vector &&x)=default
Move constructor.
void scatter_fwd_begin()
Begin scatter (send) of local data that is ghosted on other processes (simplified CPU version).
Definition Vector.h:235
Vector(const Vector &x)=default
Copy constructor.
void scatter_rev_begin()
Start scatter (send) of ghost entry data to the owning process of an index (simplified CPU version).
Definition Vector.h:332
Access to pointer function concept.
Definition Vector.h:33
la::Vector scatter pack/unpack function concept.
Definition Vector.h:27
MPI_Datatype mpi_t
Retrieves the MPI data type associated to the provided type.
Definition MPI.h:280
Miscellaneous classes, functions and types.
Definition dolfinx_common.h:8
Linear algebra interface.
Definition sparsitybuild.h:15
void orthonormalize(std::vector< std::reference_wrapper< V > > basis)
Orthonormalize a set of vectors.
Definition Vector.h:520
auto squared_norm(const V &a)
Compute the squared L2 norm of vector.
Definition Vector.h:463
auto norm(const V &x, Norm type=Norm::l2)
Compute the norm of the vector.
Definition Vector.h:477
auto inner_product(const V &a, const V &b)
Compute the inner product of two vectors.
Definition Vector.h:432
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:563
Norm
Norm types.
Definition utils.h:17