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/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 <utility>
22#include <vector>
23
24namespace dolfinx::la
25{
27template <class F, class Container, class ScatterContainer>
28concept VectorPackKernel = requires(F f, ScatterContainer idx, Container x) {
29 f(idx.cbegin(), idx.cend(), x.cbegin(), x.begin());
30};
31
33template <class GetPtr, class U, class T>
34concept GetPtrConcept = requires(GetPtr f, U x) {
35 { f(x) } -> std::same_as<T*>;
36} and requires(GetPtr f, const U x) {
37 { f(x) } -> std::same_as<const T*>;
38};
39
48template <typename T, typename Container = std::vector<T>,
49 typename ScatterContainer = std::vector<std::int32_t>>
50class Vector
51{
52 static_assert(std::is_same_v<typename Container::value_type, T>);
53
54 template <typename, typename, typename>
55 friend class Vector;
56
57private:
65 template <class F>
66 static void dispatch_bs(int bs, F&& f)
67 {
68 switch (bs)
69 {
70 case 1:
71 return f(std::integral_constant<int, 1>{});
72 case 2:
73 return f(std::integral_constant<int, 2>{});
74 case 3:
75 return f(std::integral_constant<int, 3>{});
76 default:
77 return f(bs);
78 }
79 }
80
85 auto get_pack()
86 {
87 return [bs = _bs](typename ScatterContainer::const_iterator idx_first,
88 typename ScatterContainer::const_iterator idx_last,
89 const auto in_first, auto out_first)
90 {
91 // out[i * bs + j] = in[idx[i] * bs + j]
92 dispatch_bs(bs,
93 [&](auto B)
94 {
95 auto out = out_first;
96 for (auto idx = idx_first; idx != idx_last; ++idx)
97 {
98 auto in = std::next(in_first, (*idx) * B);
99 for (int j = 0; j < B; ++j, ++in, ++out)
100 *out = *in;
101 }
102 });
103 };
104 }
105
110 auto get_unpack()
111 {
112 // Assignment is accumulation with an operation that keeps the
113 // received value
114 return get_unpack_op([](auto, auto received) { return received; });
115 }
116
122 template <typename BinaryOp>
123 auto get_unpack_op(BinaryOp op)
124 {
125 return [op, bs = _bs](typename ScatterContainer::const_iterator idx_first,
126 typename ScatterContainer::const_iterator idx_last,
127 const auto in_first, auto out_first)
128 {
129 // out[idx[i] * bs + j] = op(out[idx[i] * bs + j], in[i * bs + j])
130 dispatch_bs(bs,
131 [&](auto B)
132 {
133 auto in = in_first;
134 for (auto idx = idx_first; idx != idx_last; ++idx)
135 {
136 auto out = std::next(out_first, (*idx) * B);
137 for (int j = 0; j < B; ++j, ++in, ++out)
138 *out = op(*out, *in);
139 }
140 });
141 };
142 }
143
144public:
146 using container_type = Container;
147
149 using value_type = container_type::value_type;
150
151 static_assert(std::is_same_v<value_type, typename container_type::value_type>,
152 "Scalar type and container value type must be the same.");
153
166 Vector(std::shared_ptr<const common::IndexMap> map, int bs)
167 : Vector(map, bs,
168 std::make_shared<common::Scatterer<ScatterContainer>>(*map))
169 {
170 }
171
178 Vector(std::shared_ptr<const common::IndexMap> map, int bs,
179 std::shared_ptr<const common::Scatterer<ScatterContainer>> scatterer)
180 : _map(std::move(map)), _bs(bs),
181 _x(bs * (_map->size_local() + _map->num_ghosts())),
182 _scatterer(std::move(scatterer)),
183 _buffer_local(bs * _scatterer->local_indices_block().size()),
184 _buffer_remote(bs * _scatterer->remote_indices_block().size())
185 {
186 }
187
189 Vector(const Vector& x) = default;
190
192 Vector(Vector&& x) = default;
193
194private:
204 std::shared_ptr<const common::Scatterer<ScatterContainer>>
205 scatter_ptr(auto sc) const
206 {
207 using SC = typename std::remove_cv<
208 typename decltype(sc)::element_type>::type::container_type;
209 if constexpr (std::is_same_v<ScatterContainer, SC>)
210 return sc; // Scatters use same container
211 else // Scatters use different containers, so copy
212 return std::make_shared<common::Scatterer<ScatterContainer>>(*sc);
213 }
214
215public:
235 template <typename T0, typename Container0, typename ScatterContainer0>
236 explicit Vector(const Vector<T0, Container0, ScatterContainer0>& x)
237 : _map(x.index_map()), _bs(x.bs()), _x(x._x.begin(), x._x.end()),
238 _scatterer(scatter_ptr(x._scatterer)), _request(MPI_REQUEST_NULL),
239 _buffer_local(_bs * _scatterer->local_indices_block().size()),
240 _buffer_remote(_bs * _scatterer->remote_indices_block().size())
241 {
242 }
243
244 // Assignment operator (disabled)
245 Vector& operator=(const Vector& x) = delete;
246
248 Vector& operator=(Vector&& x) = default;
249
254 [[deprecated("Use std::ranges::fill(u.array(), v) instead.")]] void
256 {
257 std::ranges::fill(_x, v);
258 }
259
275 template <typename U, typename GetPtr>
278 void scatter_fwd_begin(U pack, GetPtr get_ptr)
279 {
280 pack(_scatterer->local_indices_block().begin(),
281 _scatterer->local_indices_block().end(), _x.begin(),
282 _buffer_local.begin());
283 _scatterer->scatter_fwd_begin(get_ptr(_buffer_local),
284 get_ptr(_buffer_remote), _bs, _request);
285 }
286
297 requires requires(Container c) {
298 { c.data() } -> std::same_as<T*>;
299 }
300 {
301 scatter_fwd_begin(get_pack(), [](auto&& x) { return x.data(); });
302 }
303
318 template <typename U>
319 requires VectorPackKernel<U, container_type, ScatterContainer>
320 void scatter_fwd_end(U unpack)
321 {
322 _scatterer->scatter_fwd_end(_request);
323 unpack(_scatterer->remote_indices_block().begin(),
324 _scatterer->remote_indices_block().end(), _buffer_remote.begin(),
325 std::next(_x.begin(), _bs * _map->size_local()));
326 }
327
339 requires requires(Container c) {
340 { c.data() } -> std::same_as<T*>;
341 }
342 {
343 this->scatter_fwd_end(get_unpack());
344 }
345
357 requires requires(Container c) {
358 { c.data() } -> std::same_as<T*>;
359 }
360 {
361 this->scatter_fwd_begin(get_pack(), [](auto&& x) { return x.data(); });
362 this->scatter_fwd_end(get_unpack());
363 }
364
379 template <typename U, typename GetPtr>
380 requires VectorPackKernel<U, container_type, ScatterContainer>
381 and GetPtrConcept<GetPtr, Container, T>
382 void scatter_rev_begin(U pack, GetPtr get_ptr)
383 {
384 std::int32_t local_size = _bs * _map->size_local();
385 pack(_scatterer->remote_indices_block().begin(),
386 _scatterer->remote_indices_block().end(),
387 std::next(_x.begin(), local_size), _buffer_remote.begin());
388 _scatterer->scatter_rev_begin(get_ptr(_buffer_remote),
389 get_ptr(_buffer_local), _bs, _request);
390 }
391
402 requires requires(Container c) {
403 { c.data() } -> std::same_as<T*>;
404 }
405 {
406 scatter_rev_begin(get_pack(), [](auto&& x) { return x.data(); });
407 }
408
420 template <typename U>
421 requires VectorPackKernel<U, container_type, ScatterContainer>
422 void scatter_rev_end(U unpack)
423 {
424 _scatterer->scatter_rev_end(_request);
425 unpack(_scatterer->local_indices_block().begin(),
426 _scatterer->local_indices_block().end(), _buffer_local.begin(),
427 _x.begin());
428 }
429
442 template <class BinaryOperation>
443 requires requires(Container c) {
444 { c.data() } -> std::same_as<T*>;
445 }
446 void scatter_rev(BinaryOperation op)
447 {
448 this->scatter_rev_begin();
449 this->scatter_rev_end(get_unpack_op(op));
450 }
451
453 std::shared_ptr<const common::IndexMap> index_map() const noexcept
454 {
455 return _map;
456 }
457
460 std::shared_ptr<const common::Scatterer<ScatterContainer>>
461 scatterer() const noexcept
462 {
463 return _scatterer;
464 }
465
467 constexpr int bs() const noexcept { return _bs; }
468
472 container_type& array() noexcept { return _x; }
473
477 const container_type& array() const noexcept { return _x; }
478
481 [[deprecated("Use array() instead.")]] container_type&
482 mutable_array() noexcept
483 {
484 return _x;
485 }
486
487private:
488 // Map describing the data layout
489 std::shared_ptr<const common::IndexMap> _map;
490
491 // Block size
492 int _bs;
493
494 // Vector data
496
497 // Scatter for managing MPI communication
498 std::shared_ptr<const common::Scatterer<ScatterContainer>> _scatterer;
499
500 // MPI request handle
501 MPI_Request _request = MPI_REQUEST_NULL;
502
503 // Buffers for ghost scatters
504 container_type _buffer_local, _buffer_remote;
505}; // namespace dolfinx::la
506
517template <class V>
518auto inner_product(const V& a, const V& b)
519{
520 using T = typename V::value_type;
521 const std::int32_t local_size = a.bs() * a.index_map()->size_local();
522 if (local_size != b.bs() * b.index_map()->size_local())
523 throw std::runtime_error("Incompatible vector sizes");
524
525 const T local = std::transform_reduce(
526 a.array().begin(), std::next(a.array().begin(), local_size),
527 b.array().begin(), static_cast<T>(0), std::plus{},
528 [](T a, T b) -> T
529 {
530 if constexpr (std::is_same<T, std::complex<double>>::value
531 or std::is_same<T, std::complex<float>>::value)
532 {
533 return std::conj(a) * b;
534 }
535 else
536 return a * b;
537 });
538
539 T result;
540 MPI_Allreduce(&local, &result, 1, dolfinx::MPI::mpi_t<T>, MPI_SUM,
541 a.index_map()->comm());
542 return result;
543}
544
548template <class V>
549auto squared_norm(const V& a)
550{
551 using T = typename V::value_type;
552 T result = inner_product(a, a);
553 return std::real(result);
554}
555
562template <class V>
563auto norm(const V& x, Norm type = Norm::l2)
564{
565 using T = typename V::value_type;
566 switch (type)
567 {
568 case Norm::l1:
569 {
570 std::int32_t size_local = x.bs() * x.index_map()->size_local();
571 using U = typename dolfinx::scalar_value_t<T>;
572 U local_l1 = std::accumulate(
573 x.array().begin(), std::next(x.array().begin(), size_local), U(0),
574 [](auto norm, auto x) { return norm + std::abs(x); });
575 U l1(0);
576 MPI_Allreduce(&local_l1, &l1, 1, MPI::mpi_t<U>, MPI_SUM,
577 x.index_map()->comm());
578 return l1;
579 }
580 case Norm::l2:
581 return std::sqrt(squared_norm(x));
582 case Norm::linf:
583 {
584 std::int32_t size_local = x.bs() * x.index_map()->size_local();
585 auto max_pos = std::max_element(
586 x.array().begin(), std::next(x.array().begin(), size_local),
587 [](T a, T b) { return std::norm(a) < std::norm(b); });
588 auto local_linf = std::abs(*max_pos);
589 decltype(local_linf) linf = 0;
590 MPI_Allreduce(&local_linf, &linf, 1, MPI::mpi_t<decltype(linf)>, MPI_MAX,
591 x.index_map()->comm());
592 return linf;
593 }
594 default:
595 throw std::runtime_error("Norm type not supported");
596 }
597}
598
605template <class V>
606void orthonormalize(std::vector<std::reference_wrapper<V>> basis)
607{
608 using T = typename V::value_type;
609 using U = typename dolfinx::scalar_value_t<T>;
610
611 // Loop over each vector in basis
612 for (std::size_t i = 0; i < basis.size(); ++i)
613 {
614 // Orthogonalize vector i with respect to previously orthonormalized
615 // vectors
616 V& bi = basis[i].get();
617 for (std::size_t j = 0; j < i; ++j)
618 {
619 const V& bj = basis[j].get();
620
621 // basis_i <- basis_i - dot_ij basis_j
622 auto dot_ij = inner_product(bi, bj);
623 std::ranges::transform(bj.array(), bi.array(), bi.array().begin(),
624 [dot_ij](auto xj, auto xi)
625 { return xi - dot_ij * xj; });
626 }
627
628 // Normalise basis function
629 auto norm = la::norm(bi, la::Norm::l2);
630 if (norm * norm < std::numeric_limits<U>::epsilon())
631 {
632 throw std::runtime_error(
633 "Linear dependency detected. Cannot orthogonalize.");
634 }
635 std::ranges::transform(bi.array(), bi.array().begin(),
636 [norm](auto x) { return x / norm; });
637 }
638}
639
648template <class V>
650 std::vector<std::reference_wrapper<const V>> basis,
651 dolfinx::scalar_value_t<typename V::value_type> eps = std::numeric_limits<
652 dolfinx::scalar_value_t<typename V::value_type>>::epsilon())
653{
654 using T = typename V::value_type;
655 for (std::size_t i = 0; i < basis.size(); i++)
656 {
657 for (std::size_t j = i; j < basis.size(); ++j)
658 {
659 T delta_ij = (i == j) ? T(1) : T(0);
660 auto dot_ij = inner_product(basis[i].get(), basis[j].get());
661 if (std::norm(delta_ij - dot_ij) > eps)
662 return false;
663 }
664 }
665
666 return true;
667}
668
669} // namespace dolfinx::la
A Scatterer supports the scattering and gathering of distributed data that is associated with a commo...
Definition Scatterer.h:84
A vector that can be distributed across processes.
Definition Vector.h:51
std::shared_ptr< const common::IndexMap > index_map() const noexcept
Get IndexMap.
Definition Vector.h:453
container_type::value_type value_type
Scalar type.
Definition Vector.h:149
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:382
Vector(const Vector< T0, Container0, ScatterContainer0 > &x)
Create a vector by copying and converting another vector.
Definition Vector.h:236
container_type & mutable_array() noexcept
Get local part of the vector.
Definition Vector.h:482
Vector(std::shared_ptr< const common::IndexMap > map, int bs)
Create a distributed vector.
Definition Vector.h:166
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:446
constexpr int bs() const noexcept
Get block size.
Definition Vector.h:467
void scatter_fwd_end(U unpack)
End scatter (send) of local data values that are ghosted on other processes.
Definition Vector.h:320
container_type & array() noexcept
Get the process-local part of the vector.
Definition Vector.h:472
void scatter_rev_end(U unpack)
End scatter of ghost data to owner and update owned entries.
Definition Vector.h:422
Vector & operator=(Vector &&x)=default
Move assignment operator.
void scatter_fwd_end()
End scatter (send) of local data values that are ghosted on other processes (simplified CPU version).
Definition Vector.h:338
void scatter_fwd()
Scatter (send) of local data values that are ghosted on other processes and update ghost entry values...
Definition Vector.h:356
Container container_type
Container type.
Definition Vector.h:146
void scatter_fwd_begin(U pack, GetPtr get_ptr)
Begin scatter (send) of local data that is ghosted on other processes.
Definition Vector.h:278
void set(value_type v)
Set all entries (including ghosts).
Definition Vector.h:255
Vector(Vector &&x)=default
Move constructor.
const container_type & array() const noexcept
Get the process-local part of the vector (const version).
Definition Vector.h:477
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:178
std::shared_ptr< const common::Scatterer< ScatterContainer > > scatterer() const noexcept
Get the scatterer used for halo communication.
Definition Vector.h:461
void scatter_fwd_begin()
Begin scatter (send) of local data that is ghosted on other processes (simplified CPU version).
Definition Vector.h:296
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:401
Access to pointer function concept.
Definition Vector.h:34
la::Vector scatter pack/unpack function concept.
Definition Vector.h:28
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:606
auto squared_norm(const V &a)
Compute the squared L2 norm of vector.
Definition Vector.h:549
auto norm(const V &x, Norm type=Norm::l2)
Compute the norm of the vector.
Definition Vector.h:563
auto inner_product(const V &a, const V &b)
Compute the inner product of two vectors.
Definition Vector.h:518
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:649
Norm
Norm types.
Definition utils.h:17