Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/dolfinx/v0.9.0/cpp/doxygen/d6/dbe/Vector_8h_source.html
DOLFINx 0.6.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
Vector.h
1// Copyright (C) 2020 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 <complex>
11#include <dolfinx/common/IndexMap.h>
12#include <dolfinx/common/Scatterer.h>
13#include <limits>
14#include <memory>
15#include <numeric>
16#include <span>
17#include <vector>
18
19namespace dolfinx::la
20{
21
23
24template <typename T, class Allocator = std::allocator<T>>
25class Vector
26{
27public:
29 using value_type = T;
30
32 using allocator_type = Allocator;
33
35 Vector(std::shared_ptr<const common::IndexMap> map, int bs,
36 const Allocator& alloc = Allocator())
37 : _map(map), _scatterer(std::make_shared<common::Scatterer<>>(*_map, bs)),
38 _bs(bs), _buffer_local(_scatterer->local_buffer_size(), alloc),
39 _buffer_remote(_scatterer->remote_buffer_size(), alloc),
40 _x(bs * (map->size_local() + map->num_ghosts()), alloc)
41 {
42 }
43
45 Vector(const Vector& x)
46 : _map(x._map), _scatterer(x._scatterer), _bs(x._bs),
47 _request(1, MPI_REQUEST_NULL), _buffer_local(x._buffer_local),
48 _buffer_remote(x._buffer_remote), _x(x._x)
49 {
50 }
51
54 : _map(std::move(x._map)), _scatterer(std::move(x._scatterer)),
55 _bs(std::move(x._bs)),
56 _request(std::exchange(x._request, {MPI_REQUEST_NULL})),
57 _buffer_local(std::move(x._buffer_local)),
58 _buffer_remote(std::move(x._buffer_remote)), _x(std::move(x._x))
59 {
60 }
61
62 // Assignment operator (disabled)
63 Vector& operator=(const Vector& x) = delete;
64
66 Vector& operator=(Vector&& x) = default;
67
70 void set(T v) { std::fill(_x.begin(), _x.end(), v); }
71
75 {
76 const std::int32_t local_size = _bs * _map->size_local();
77 std::span<const T> x_local(_x.data(), local_size);
78
79 auto pack = [](const auto& in, const auto& idx, auto& out)
80 {
81 for (std::size_t i = 0; i < idx.size(); ++i)
82 out[i] = in[idx[i]];
83 };
84 pack(x_local, _scatterer->local_indices(), _buffer_local);
85
86 _scatterer->scatter_fwd_begin(std::span<const T>(_buffer_local),
87 std::span<T>(_buffer_remote),
88 std::span<MPI_Request>(_request));
89 }
90
94 {
95 const std::int32_t local_size = _bs * _map->size_local();
96 const std::int32_t num_ghosts = _bs * _map->num_ghosts();
97 std::span<T> x_remote(_x.data() + local_size, num_ghosts);
98 _scatterer->scatter_fwd_end(std::span<MPI_Request>(_request));
99
100 auto unpack = [](const auto& in, const auto& idx, auto& out, auto op)
101 {
102 for (std::size_t i = 0; i < idx.size(); ++i)
103 out[idx[i]] = op(out[idx[i]], in[i]);
104 };
105
106 unpack(_buffer_remote, _scatterer->remote_indices(), x_remote,
107 [](auto /*a*/, auto b) { return b; });
108 }
109
113 {
114 this->scatter_fwd_begin();
115 this->scatter_fwd_end();
116 }
117
121 {
122 const std::int32_t local_size = _bs * _map->size_local();
123 const std::int32_t num_ghosts = _bs * _map->num_ghosts();
124 std::span<T> x_remote(_x.data() + local_size, num_ghosts);
125
126 auto pack = [](const auto& in, const auto& idx, auto& out)
127 {
128 for (std::size_t i = 0; i < idx.size(); ++i)
129 out[i] = in[idx[i]];
130 };
131 pack(x_remote, _scatterer->remote_indices(), _buffer_remote);
132
133 _scatterer->scatter_rev_begin(std::span<const T>(_buffer_remote),
134 std::span<T>(_buffer_local), _request);
135 }
136
143 template <class BinaryOperation>
144 void scatter_rev_end(BinaryOperation op)
145 {
146 const std::int32_t local_size = _bs * _map->size_local();
147 std::span<T> x_local(_x.data(), local_size);
148 _scatterer->scatter_rev_end(_request);
149
150 auto unpack = [](const auto& in, const auto& idx, auto& out, auto op)
151 {
152 for (std::size_t i = 0; i < idx.size(); ++i)
153 out[idx[i]] = op(out[idx[i]], in[i]);
154 };
155 unpack(_buffer_local, _scatterer->local_indices(), x_local, op);
156 }
157
163 template <class BinaryOperation>
164 void scatter_rev(BinaryOperation op)
165 {
166 this->scatter_rev_begin();
167 this->scatter_rev_end(op);
168 }
169
171 std::shared_ptr<const common::IndexMap> map() const { return _map; }
172
174 constexpr int bs() const { return _bs; }
175
177 std::span<const T> array() const { return std::span<const T>(_x); }
178
180 std::span<T> mutable_array() { return std::span(_x); }
181
183 constexpr allocator_type allocator() const { return _x.get_allocator(); }
184
185private:
186 // Map describing the data layout
187 std::shared_ptr<const common::IndexMap> _map;
188
189 // Scatter for managing MPI communication
190 std::shared_ptr<const common::Scatterer<>> _scatterer;
191
192 // Block size
193 int _bs;
194
195 // MPI request handle
196 std::vector<MPI_Request> _request = {MPI_REQUEST_NULL};
197
198 // Buffers for ghost scatters
199 std::vector<T, Allocator> _buffer_local, _buffer_remote;
200
201 // Vector data
202 std::vector<T, Allocator> _x;
203};
204
211template <typename T, class Allocator>
213{
214 const std::int32_t local_size = a.bs() * a.map()->size_local();
215 if (local_size != b.bs() * b.map()->size_local())
216 throw std::runtime_error("Incompatible vector sizes");
217 std::span<const T> x_a = a.array().subspan(0, local_size);
218 std::span<const T> x_b = b.array().subspan(0, local_size);
219
220 const T local = std::transform_reduce(
221 x_a.begin(), x_a.end(), x_b.begin(), static_cast<T>(0), std::plus{},
222 [](T a, T b) -> T
223 {
224 if constexpr (std::is_same<T, std::complex<double>>::value
225 or std::is_same<T, std::complex<float>>::value)
226 {
227 return std::conj(a) * b;
228 }
229 else
230 return a * b;
231 });
232
233 T result;
234 MPI_Allreduce(&local, &result, 1, dolfinx::MPI::mpi_type<T>(), MPI_SUM,
235 a.map()->comm());
236 return result;
237}
238
241template <typename T, class Allocator>
243{
244 T result = inner_product(a, a);
245 return std::real(result);
246}
247
252template <typename T, class Allocator>
253auto norm(const Vector<T, Allocator>& a, Norm type = Norm::l2)
254{
255 switch (type)
256 {
257 case Norm::l2:
258 return std::sqrt(squared_norm(a));
259 case Norm::linf:
260 {
261 const std::int32_t size_local = a.bs() * a.map()->size_local();
262 std::span<const T> x_a = a.array().subspan(0, size_local);
263 auto max_pos = std::max_element(x_a.begin(), x_a.end(),
264 [](T a, T b)
265 { return std::norm(a) < std::norm(b); });
266 auto local_linf = std::abs(*max_pos);
267 decltype(local_linf) linf = 0;
268 MPI_Allreduce(&local_linf, &linf, 1, MPI::mpi_type<decltype(linf)>(),
269 MPI_MAX, a.map()->comm());
270 return linf;
271 }
272 default:
273 throw std::runtime_error("Norm type not supported");
274 }
275}
276
282template <typename T, typename U>
283void orthonormalize(std::span<Vector<T, U>> basis, double tol = 1.0e-10)
284{
285 // Loop over each vector in basis
286 for (std::size_t i = 0; i < basis.size(); ++i)
287 {
288 // Orthogonalize vector i with respect to previously orthonormalized
289 // vectors
290 for (std::size_t j = 0; j < i; ++j)
291 {
292 // basis_i <- basis_i - dot_ij basis_j
293 T dot_ij = inner_product(basis[i], basis[j]);
294 std::transform(basis[j].array().begin(), basis[j].array().end(),
295 basis[i].array().begin(), basis[i].mutable_array().begin(),
296 [dot_ij](auto xj, auto xi) { return xi - dot_ij * xj; });
297 }
298
299 // Normalise basis function
300 double norm = la::norm(basis[i], la::Norm::l2);
301 if (norm < tol)
302 {
303 throw std::runtime_error(
304 "Linear dependency detected. Cannot orthogonalize.");
305 }
306 std::transform(basis[i].array().begin(), basis[i].array().end(),
307 basis[i].mutable_array().begin(),
308 [norm](auto x) { return x / norm; });
309 }
310}
311
316template <typename T, typename U>
317bool is_orthonormal(std::span<const Vector<T, U>> basis, double tol = 1.0e-10)
318{
319 for (std::size_t i = 0; i < basis.size(); i++)
320 {
321 for (std::size_t j = i; j < basis.size(); j++)
322 {
323 const double delta_ij = (i == j) ? 1.0 : 0.0;
324 T dot_ij = inner_product(basis[i], basis[j]);
325 if (std::abs(delta_ij - dot_ij) > tol)
326 return false;
327 }
328 }
329
330 return true;
331}
332
333} // namespace dolfinx::la
Distributed vector.
Definition: Vector.h:26
std::span< const T > array() const
Get local part of the vector (const version)
Definition: Vector.h:177
void scatter_fwd_begin()
Begin scatter of local data from owner to ghosts on other ranks.
Definition: Vector.h:74
Allocator allocator_type
The allocator type.
Definition: Vector.h:32
Vector(std::shared_ptr< const common::IndexMap > map, int bs, const Allocator &alloc=Allocator())
Create a distributed vector.
Definition: Vector.h:35
constexpr int bs() const
Get block size.
Definition: Vector.h:174
std::shared_ptr< const common::IndexMap > map() const
Get IndexMap.
Definition: Vector.h:171
void set(T v)
Set all entries (including ghosts)
Definition: Vector.h:70
std::span< T > mutable_array()
Get local part of the vector.
Definition: Vector.h:180
Vector & operator=(Vector &&x)=default
Move Assignment operator.
void scatter_fwd()
Scatter local data to ghost positions on other ranks.
Definition: Vector.h:112
Vector(Vector &&x)
Move constructor.
Definition: Vector.h:53
Vector(const Vector &x)
Copy constructor.
Definition: Vector.h:45
void scatter_fwd_end()
End scatter of local data from owner to ghosts on other ranks.
Definition: Vector.h:93
void scatter_rev_begin()
Start scatter of ghost data to owner.
Definition: Vector.h:120
constexpr allocator_type allocator() const
Get the allocator associated with the container.
Definition: Vector.h:183
void scatter_rev_end(BinaryOperation op)
End scatter of ghost data to owner. This process may receive data from more than one process,...
Definition: Vector.h:144
void scatter_rev(BinaryOperation op)
Scatter ghost data to owner. This process may receive data from more than one process,...
Definition: Vector.h:164
T value_type
The value type.
Definition: Vector.h:29
Linear algebra interface.
Definition: sparsitybuild.h:15
Norm
Norm types.
Definition: utils.h:17
void orthonormalize(std::span< Vector< T, U > > basis, double tol=1.0e-10)
Orthonormalize a set of vectors.
Definition: Vector.h:283
T inner_product(const Vector< T, Allocator > &a, const Vector< T, Allocator > &b)
Compute the inner product of two vectors. The two vectors must have the same parallel layout.
Definition: Vector.h:212
auto squared_norm(const Vector< T, Allocator > &a)
Compute the squared L2 norm of vector.
Definition: Vector.h:242
bool is_orthonormal(std::span< const Vector< T, U > > basis, double tol=1.0e-10)
Test if basis is orthonormal.
Definition: Vector.h:317
auto norm(const Vector< T, Allocator > &a, Norm type=Norm::l2)
Compute the norm of the vector.
Definition: Vector.h:253