Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/v0.3.0/v0.9.0/cpp
DOLFINx  0.3.0
DOLFINx C++ interface
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 <limits>
13 #include <memory>
14 #include <numeric>
15 #include <vector>
16 #include <xtl/xspan.hpp>
17 
18 namespace dolfinx::la
19 {
20 
22 
23 template <typename T, class Allocator = std::allocator<T>>
24 class Vector
25 {
26 public:
28  Vector(const std::shared_ptr<const common::IndexMap>& map, int bs,
29  const Allocator& alloc = Allocator())
30  : _map(map), _bs(bs),
31  _x(bs * (map->size_local() + map->num_ghosts()), alloc)
32  {
33  if (bs == 1)
34  _datatype = MPI::mpi_type<T>();
35  else
36  {
37  MPI_Type_contiguous(bs, dolfinx::MPI::mpi_type<T>(), &_datatype);
38  MPI_Type_commit(&_datatype);
39  }
40  }
41 
43  Vector(const Vector& x)
44  : _map(x._map), _bs(x._bs), _request(MPI_REQUEST_NULL),
45  _buffer_send_fwd(x._buffer_send_fwd),
46  _buffer_recv_fwd(x._buffer_recv_fwd), _x(x._x)
47  {
48  MPI_Type_dup(x._datatype, &_datatype);
49  }
50 
53  : _map(std::move(x._map)), _bs(std::move(x._bs)),
54  _datatype(std::exchange(x._datatype, MPI_DATATYPE_NULL)),
55  _request(std::exchange(x._request, MPI_REQUEST_NULL)),
56  _buffer_send_fwd(std::move(x._buffer_send_fwd)),
57  _buffer_recv_fwd(std::move(x._buffer_recv_fwd)), _x(std::move(x._x))
58  {
59  }
60 
63  {
64  if (_bs != 1)
65  MPI_Type_free(&_datatype);
66  }
67 
68  // Assignment operator (disabled)
69  Vector& operator=(const Vector& x) = delete;
70 
72  Vector& operator=(Vector&& x) = default;
73 
77  {
78  assert(_map);
79 
80  // Pack send buffer
81  const std::vector<std::int32_t>& indices
82  = _map->scatter_fwd_indices().array();
83  _buffer_send_fwd.resize(_bs * indices.size());
84  for (std::size_t i = 0; i < indices.size(); ++i)
85  {
86  std::copy_n(std::next(_x.cbegin(), _bs * indices[i]), _bs,
87  std::next(_buffer_send_fwd.begin(), _bs * i));
88  }
89 
90  _buffer_recv_fwd.resize(_bs * _map->num_ghosts());
91  _map->scatter_fwd_begin(xtl::span<const T>(_buffer_send_fwd), _datatype,
92  _request, xtl::span<T>(_buffer_recv_fwd));
93  }
94 
98  {
99  assert(_map);
100  const std::int32_t local_size = _bs * _map->size_local();
101  xtl::span xremote(_x.data() + local_size, _map->num_ghosts() * _bs);
102  _map->scatter_fwd_end(_request);
103 
104  // Copy received data into ghost positions
105  const std::vector<std::int32_t>& scatter_fwd_ghost_pos
106  = _map->scatter_fwd_ghost_positions();
107  for (std::size_t i = 0; i < _map->num_ghosts(); ++i)
108  {
109  const int pos = scatter_fwd_ghost_pos[i];
110  std::copy_n(std::next(_buffer_recv_fwd.cbegin(), _bs * pos), _bs,
111  std::next(xremote.begin(), _bs * i));
112  }
113  }
114 
117  void scatter_fwd()
118  {
119  this->scatter_fwd_begin();
120  this->scatter_fwd_end();
121  }
122 
126  {
127  // Pack send buffer
128  const std::int32_t local_size = _bs * _map->size_local();
129  xtl::span<const T> xremote(_x.data() + local_size,
130  _map->num_ghosts() * _bs);
131  const std::vector<std::int32_t>& scatter_fwd_ghost_pos
132  = _map->scatter_fwd_ghost_positions();
133  _buffer_recv_fwd.resize(_bs * scatter_fwd_ghost_pos.size());
134  for (std::size_t i = 0; i < scatter_fwd_ghost_pos.size(); ++i)
135  {
136  const int pos = scatter_fwd_ghost_pos[i];
137  std::copy_n(std::next(xremote.cbegin(), _bs * i), _bs,
138  std::next(_buffer_recv_fwd.begin(), _bs * pos));
139  }
140 
141  // Resize receive buffer and begin scatter
142  _buffer_send_fwd.resize(_bs * _map->scatter_fwd_indices().array().size());
143  _map->scatter_rev_begin(xtl::span<const T>(_buffer_recv_fwd), _datatype,
144  _request, xtl::span<T>(_buffer_send_fwd));
145  }
146 
154  {
155  // Complete scatter
156  _map->scatter_rev_end(_request);
157 
158  // Copy/accumulate into owned part of the vector
159  const std::vector<std::int32_t>& shared_indices
160  = _map->scatter_fwd_indices().array();
161  switch (op)
162  {
163  case common::IndexMap::Mode::insert:
164  for (std::size_t i = 0; i < shared_indices.size(); ++i)
165  {
166  std::copy_n(std::next(_buffer_send_fwd.cbegin(), _bs * i), _bs,
167  std::next(_x.begin(), _bs * shared_indices[i]));
168  }
169  break;
170  case common::IndexMap::Mode::add:
171  for (std::size_t i = 0; i < shared_indices.size(); ++i)
172  for (int j = 0; j < _bs; ++j)
173  _x[shared_indices[i] * _bs + j] += _buffer_send_fwd[i * _bs + j];
174  break;
175  }
176  }
177 
184  {
185  this->scatter_rev_begin();
186  this->scatter_rev_end(op);
187  }
188 
192  T norm(la::Norm type = la::Norm::l2) const
193  {
194  switch (type)
195  {
196  case la::Norm::l2:
197  return std::sqrt(this->squared_norm());
198  case la::Norm::linf:
199  {
200  const std::int32_t size_local = _map->size_local();
201  double local_linf = 0.0;
202  if (size_local > 0)
203  {
204  auto local_max_entry = std::max_element(
205  _x.begin(), std::next(_x.begin(), size_local),
206  [](T a, T b) { return std::norm(a) < std::norm(b); });
207  local_linf = std::abs(*local_max_entry);
208  }
209 
210  double linf = 0.0;
211  MPI_Allreduce(&local_linf, &linf, 1, MPI_DOUBLE, MPI_MAX,
212  _map->comm(common::IndexMap::Direction::forward));
213  return linf;
214  }
215  default:
216  throw std::runtime_error("Norm type not supported");
217  }
218  }
219 
222  double squared_norm() const
223  {
224  const std::int32_t size_local = _map->size_local();
225  double result = std::transform_reduce(
226  _x.begin(), std::next(_x.begin(), size_local), 0.0, std::plus<double>(),
227  [](T val) { return std::norm(val); });
228  double norm2;
229  MPI_Allreduce(&result, &norm2, 1, MPI_DOUBLE, MPI_SUM,
230  _map->comm(common::IndexMap::Direction::forward));
231  return norm2;
232  }
233 
235  std::shared_ptr<const common::IndexMap> map() const { return _map; }
236 
238  constexpr int bs() const { return _bs; }
239 
241  const std::vector<T, Allocator>& array() const { return _x; }
242 
244  std::vector<T, Allocator>& mutable_array() { return _x; }
245 
246 private:
247  // Map describing the data layout
248  std::shared_ptr<const common::IndexMap> _map;
249 
250  // Block size
251  int _bs;
252 
253  // Data type and buffers for ghost scatters
254  MPI_Datatype _datatype = MPI_DATATYPE_NULL;
255  MPI_Request _request = MPI_REQUEST_NULL;
256  std::vector<T> _buffer_send_fwd, _buffer_recv_fwd;
257 
258  // Data
259  std::vector<T, Allocator> _x;
260 };
261 
268 template <typename T, class Allocator = std::allocator<T>>
270 {
271  const std::int32_t local_size = a.bs() * a.map()->size_local();
272  if (local_size != b.bs() * b.map()->size_local())
273  throw std::runtime_error("Incompatible vector sizes");
274  const std::vector<T>& x_a = a.array();
275  const std::vector<T>& x_b = b.array();
276 
277  const T local = std::transform_reduce(
278  x_a.begin(), x_a.begin() + local_size, x_b.begin(), static_cast<T>(0),
279  std::plus<T>(),
280  [](T a, T b) -> T
281  {
282  if constexpr (std::is_same<T, std::complex<double>>::value
283  or std::is_same<T, std::complex<float>>::value)
284  {
285  return std::conj(a) * b;
286  }
287  else
288  return a * b;
289  });
290 
291  T result;
292  MPI_Allreduce(&local, &result, 1, dolfinx::MPI::mpi_type<T>(), MPI_SUM,
293  a.map()->comm(common::IndexMap::Direction::forward));
294  return result;
295 }
296 
297 } // namespace dolfinx::la
Mode
Mode for reverse scatter operation.
Definition: IndexMap.h:53
Distributed vector.
Definition: Vector.h:25
constexpr int bs() const
Get block size.
Definition: Vector.h:238
void scatter_fwd_begin()
Begin scatter of local data from owner to ghosts on other ranks.
Definition: Vector.h:76
Vector(Vector &&x)
Move constructor.
Definition: Vector.h:52
void scatter_fwd()
Scatter local data to ghost positions on other ranks.
Definition: Vector.h:117
void scatter_rev(dolfinx::common::IndexMap::Mode op)
Scatter ghost data to owner. This process may receive data from more than one process,...
Definition: Vector.h:183
T norm(la::Norm type=la::Norm::l2) const
Compute the norm of the vector.
Definition: Vector.h:192
std::shared_ptr< const common::IndexMap > map() const
Get IndexMap.
Definition: Vector.h:235
void scatter_rev_end(common::IndexMap::Mode op)
End scatter of ghost data to owner. This process may receive data from more than one process,...
Definition: Vector.h:153
Vector & operator=(Vector &&x)=default
Move Assignment operator.
std::vector< T, Allocator > & mutable_array()
Get local part of the vector.
Definition: Vector.h:244
Vector(const std::shared_ptr< const common::IndexMap > &map, int bs, const Allocator &alloc=Allocator())
Create a distributed vector.
Definition: Vector.h:28
const std::vector< T, Allocator > & array() const
Get local part of the vector (const version)
Definition: Vector.h:241
Vector(const Vector &x)
Copy constructor.
Definition: Vector.h:43
void scatter_fwd_end()
End scatter of local data from owner to ghosts on other ranks.
Definition: Vector.h:97
double squared_norm() const
Compute the squared L2 norm of vector.
Definition: Vector.h:222
~Vector()
Destructor.
Definition: Vector.h:62
void scatter_rev_begin()
Start scatter of ghost data to owner.
Definition: Vector.h:125
Linear algebra interface.
Definition: sparsitybuild.h:13
Norm
Norm types.
Definition: utils.h:13
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:269