libMesh
Loading...
Searching...
No Matches
distributed_vector.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
19
20// Local includes
21#include "libmesh/distributed_vector.h"
22
23// libMesh includes
24#include "libmesh/dense_vector.h"
25#include "libmesh/dense_subvector.h"
26#include "libmesh/int_range.h"
27#include "libmesh/libmesh_common.h"
28#include "libmesh/tensor_tools.h"
29
30// TIMPI includes
32#include "timpi/parallel_sync.h"
33
34// C++ includes
35#include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC
36#include <cmath> // for std::abs
37#include <limits> // std::numeric_limits<T>::min()
38
39
40namespace libMesh
41{
42
43
44
45//--------------------------------------------------------------------------
46// DistributedVector methods
47template <typename T>
49{
50 // This function must be run on all processors at once
51 parallel_object_only();
52
54 libmesh_assert_equal_to (_values.size(), _local_size);
55 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
56
57 T local_sum = 0.;
58
59 for (auto & val : _values)
60 local_sum += val;
61
62 this->comm().sum(local_sum);
63
64 return local_sum;
65}
66
67
68
69template <typename T>
71{
72 // This function must be run on all processors at once
73 parallel_object_only();
74
76 libmesh_assert_equal_to (_values.size(), _local_size);
77 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
78
79 Real local_l1 = 0.;
80
81 for (auto & val : _values)
82 local_l1 += std::abs(val);
83
84 this->comm().sum(local_l1);
85
86 return local_l1;
87}
88
89
90
91template <typename T>
93{
94 // This function must be run on all processors at once
95 parallel_object_only();
96
98 libmesh_assert_equal_to (_values.size(), _local_size);
99 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
100
101 Real local_l2 = 0.;
102
103 for (auto & val : _values)
104 local_l2 += TensorTools::norm_sq(val);
105
106 this->comm().sum(local_l2);
107
108 return std::sqrt(local_l2);
109}
110
111
112
113template <typename T>
115{
116 // This function must be run on all processors at once
117 parallel_object_only();
118
119 libmesh_assert (this->initialized());
120 libmesh_assert_equal_to (_values.size(), _local_size);
121 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
122
123 Real local_linfty = 0.;
124
125 for (auto & val : _values)
126 local_linfty = std::max(local_linfty,
127 static_cast<Real>(std::abs(val))
128 ); // Note we static_cast so that both
129 // types are the same, as required
130 // by std::max
131
132 this->comm().max(local_linfty);
133
134 return local_linfty;
135}
136
137
138
139template <typename T>
141{
142 libmesh_assert (this->closed());
143 libmesh_assert (this->initialized());
144 libmesh_assert_equal_to (_values.size(), _local_size);
145 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
146
147 add(1., v);
148
149 return *this;
150}
151
152
153
154template <typename T>
156{
157 libmesh_assert (this->closed());
158 libmesh_assert (this->initialized());
159 libmesh_assert_equal_to (_values.size(), _local_size);
160 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
161
162 add(-1., v);
163
164 return *this;
165}
166
167
168
169template <typename T>
171{
172 libmesh_assert_equal_to(size(), v.size());
173
174 const DistributedVector<T> & v_vec = cast_ref<const DistributedVector<T> &>(v);
175
176 for (auto i : index_range(_values))
177 _values[i] *= v_vec._values[i];
178
179 return *this;
180}
181
182
183
184template <typename T>
186{
187 libmesh_assert_equal_to(size(), v.size());
188
189 const DistributedVector<T> & v_vec = cast_ref<const DistributedVector<T> &>(v);
190
191 for (auto i : index_range(_values))
192 _values[i] /= v_vec._values[i];
193
194 return *this;
195}
196
197
198
199
200template <typename T>
202{
203 for (auto & val : _values)
204 {
205 // Don't divide by zero
206 libmesh_assert_not_equal_to (val, T(0));
207
208 val = 1. / val;
209 }
210}
211
212
213
214
215template <typename T>
217{
218 // Replace values by complex conjugate
219 for (auto & val : _values)
220 val = libmesh_conj(val);
221}
222
223
224
225
226
227template <typename T>
229{
230 libmesh_assert (this->initialized());
231 libmesh_assert_equal_to (_values.size(), _local_size);
232 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
233
234 for (auto & val : _values)
235 val += v;
236}
237
238
239
240template <typename T>
242{
243 libmesh_assert (this->initialized());
244 libmesh_assert_equal_to (_values.size(), _local_size);
245 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
246
247 add (1., v);
248}
249
250
251
252template <typename T>
253void DistributedVector<T>::add (const T a, const NumericVector<T> & v_in)
254{
255 libmesh_assert (this->initialized());
256 libmesh_assert_equal_to (_values.size(), _local_size);
257 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
258
259 // Make sure the NumericVector passed in is really a DistributedVector
260 const DistributedVector<T> * v = cast_ptr<const DistributedVector<T> *>(&v_in);
261 libmesh_error_msg_if(!v, "Cannot add different types of NumericVectors.");
262
263 for (auto i : index_range(_values))
264 _values[i] += a * v->_values[i];
265}
266
267
268
269template <typename T>
270void DistributedVector<T>::scale (const T factor)
271{
272 libmesh_assert (this->initialized());
273 libmesh_assert_equal_to (_values.size(), _local_size);
274 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
275
276 for (auto & val : _values)
277 val *= factor;
278}
279
280template <typename T>
282{
283 libmesh_assert (this->initialized());
284 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
285
286 for (auto & val : _values)
287 val = std::abs(val);
288}
289
290
291
292
293
294template <typename T>
296{
297 // This function must be run on all processors at once
298 parallel_object_only();
299
300 // Make sure the NumericVector passed in is really a DistributedVector
301 const DistributedVector<T> * v = cast_ptr<const DistributedVector<T> *>(&V);
302
303 // Make sure that the two vectors are distributed in the same way.
304 libmesh_assert_equal_to ( this->first_local_index(), v->first_local_index() );
305 libmesh_assert_equal_to ( this->last_local_index(), v->last_local_index() );
306
307 // The result of dotting together the local parts of the vector.
308 T local_dot = 0;
309
310 for (auto i : index_range(_values))
311 local_dot += this->_values[i] * v->_values[i];
312
313 // The local dot products are now summed via MPI
314 this->comm().sum(local_dot);
315
316 return local_dot;
317}
318
319
320
321template <typename T>
324{
325 libmesh_assert (this->initialized());
326 libmesh_assert_equal_to (_values.size(), _local_size);
327 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
328
329 for (auto & val : _values)
330 val = s;
331
332 return *this;
333}
334
335
336
337template <typename T>
340{
341 // Make sure the NumericVector passed in is really a DistributedVector
342 const DistributedVector<T> * v = cast_ptr<const DistributedVector<T> *>(&v_in);
343
344 *this = *v;
345
346 return *this;
347}
348
349
350
351template <typename T>
354{
355 this->_is_initialized = v._is_initialized;
356 this->_is_closed = v._is_closed;
357
358 _global_size = v._global_size;
359 _local_size = v._local_size;
360 _first_local_index = v._first_local_index;
361 _last_local_index = v._last_local_index;
362
363 if (v.local_size() == this->local_size())
364 _values = v._values;
365
366 else
367 libmesh_error_msg("v.local_size() = " << v.local_size() << " must be equal to this->local_size() = " << this->local_size());
368
369 return *this;
370}
371
372
373
374template <typename T>
376DistributedVector<T>::operator = (const std::vector<T> & v)
377{
378 libmesh_assert (this->initialized());
379 libmesh_assert_equal_to (_values.size(), _local_size);
380 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
381
382 if (v.size() == local_size())
383 _values = v;
384
385 else if (v.size() == size())
386 for (auto i : index_range(*this))
387 _values[i-first_local_index()] = v[i];
388
389 else
390 libmesh_error_msg("Incompatible sizes in DistributedVector::operator=");
391
392 return *this;
393}
394
395
396
397template <typename T>
398inline
400{
401 libmesh_assert (this->initialized());
402
403 parallel_object_only();
404
405 bool have_remote_values = !_remote_values.empty();
406 this->comm().max(have_remote_values);
407
408 if (have_remote_values)
409 {
410 bool someone_is_setting = (_unclosed_state == SET_VALUES);
411 this->comm().max(someone_is_setting);
412
413#ifndef NDEBUG
414 // If *I* have remote values, I must know what to do with them.
415 if (!_remote_values.empty())
416 libmesh_assert(_unclosed_state == SET_VALUES ||
417 _unclosed_state == ADD_VALUES);
418
419 // If *anyone* had remote values, we must be doing something,
420 // and all ranks must agree on what we're doing.
421 bool someone_is_adding = (_unclosed_state == ADD_VALUES);
422 this->comm().max(someone_is_adding);
423
424 libmesh_assert_not_equal_to(someone_is_setting, someone_is_adding);
425#endif
426
427 // We want to traverse in id order later, but we can't compare
428 // values with default < in the case where Number==complex.
429 std::sort(_remote_values.begin(), _remote_values.end(),
430 [](auto a, auto b)
431 { return a.first < b.first; });
432
433 std::vector<numeric_index_type> last_local_indices;
434 this->comm().allgather(_last_local_index, last_local_indices);
435
436 std::map<processor_id_type, std::vector<std::pair<numeric_index_type,T>>>
437 updates_to_send;
438
439 processor_id_type p = 0;
440 for (auto [i, v] : _remote_values)
441 {
442 while (i >= last_local_indices[p])
443 {
444 libmesh_assert_less(p, this->n_processors());
445 ++p;
446 }
447 updates_to_send[p].emplace_back(i, v);
448 }
449 _remote_values.clear();
450
451 auto action_functor =
452 [this, someone_is_setting]
454 const std::vector<std::pair<numeric_index_type,T>> & incoming_values)
455 {
456 for (auto [i, v] : incoming_values)
457 {
458 libmesh_assert_greater_equal(i, _first_local_index);
459 libmesh_assert_less(i, _last_local_index);
460 if (someone_is_setting)
461 _values[i-_first_local_index] = v;
462 else
463 _values[i-_first_local_index] += v;
464 }
465 };
466
468 (this->comm(), updates_to_send, action_functor);
469
470 _unclosed_state = DO_NOTHING;
471 }
472
473 this->_is_closed = true;
474}
475
476
477
478template <typename T>
480
481{
482 libmesh_assert (this->initialized());
483 libmesh_assert_equal_to (_values.size(), _local_size);
484 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
485
486 DistributedVector<T> * v_local = cast_ptr<DistributedVector<T> *>(&v_local_in);
487
488 v_local->_first_local_index = 0;
489
490 v_local->_global_size =
491 v_local->_local_size =
492 v_local->_last_local_index = size();
493
494 v_local->_is_initialized =
495 v_local->_is_closed = true;
496
497 // Call localize on the vector's values. This will help
498 // prevent code duplication
499 localize (v_local->_values);
500
501#ifndef LIBMESH_HAVE_MPI
502
503 libmesh_assert_equal_to (local_size(), size());
504
505#endif
506}
507
508
509
510template <typename T>
512 const std::vector<numeric_index_type> &) const
513{
514 libmesh_assert (this->initialized());
515 libmesh_assert_equal_to (_values.size(), _local_size);
516 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
517
518 // TODO: We don't yet support the send list; this is inefficient:
519 localize (v_local_in);
520}
521
522
523
524template <typename T>
525void DistributedVector<T>::localize (std::vector<T> & v_local,
526 const std::vector<numeric_index_type> & indices) const
527{
528 // Resize v_local so there is enough room to hold all the local values.
529 v_local.resize(indices.size());
530
531 // We need to know who has the values we want, so get everyone's _local_size
532 std::vector<numeric_index_type> local_sizes;
533 this->comm().allgather (_local_size, local_sizes);
534
535 // Make a vector of partial sums of local sizes
536 std::vector<numeric_index_type> local_size_sums(this->n_processors());
537 local_size_sums[0] = local_sizes[0];
538 for (auto i : IntRange<numeric_index_type>(1, local_sizes.size()))
539 local_size_sums[i] = local_size_sums[i-1] + local_sizes[i];
540
541 // We now fill in 'requested_ids' based on the indices. Also keep
542 // track of the local index (in the indices vector) for each of
543 // these, since we need that when unpacking.
544 std::map<processor_id_type, std::vector<numeric_index_type>>
545 requested_ids, local_requested_ids;
546
547 // We'll use this typedef a couple of times below.
548 typedef typename std::vector<numeric_index_type>::iterator iter_t;
549
550 // For each index in indices, determine which processor it is on.
551 // This is an O(N*log(p)) algorithm that uses std::upper_bound().
552 // Note: upper_bound() returns an iterator to the first entry which is
553 // greater than the given value.
554 for (auto i : index_range(indices))
555 {
556 iter_t ub = std::upper_bound(local_size_sums.begin(),
557 local_size_sums.end(),
558 indices[i]);
559
560 processor_id_type on_proc = cast_int<processor_id_type>
561 (std::distance(local_size_sums.begin(), ub));
562
563 requested_ids[on_proc].push_back(indices[i]);
564 local_requested_ids[on_proc].push_back(i);
565 }
566
567 auto gather_functor =
568 [this]
569 (processor_id_type, const std::vector<dof_id_type> & ids,
570 std::vector<T> & values)
571 {
572 // The first send/receive we did was for indices, the second one will be
573 // for corresponding floating point values, so create storage for that now...
574 const std::size_t ids_size = ids.size();
575 values.resize(ids_size);
576
577 for (std::size_t i=0; i != ids_size; i++)
578 {
579 // The index of the requested value
580 const numeric_index_type requested_index = ids[i];
581
582 // Transform into local numbering, and get requested value.
583 values[i] = _values[requested_index - _first_local_index];
584 }
585 };
586
587 auto action_functor =
588 [& v_local, & local_requested_ids]
590 const std::vector<dof_id_type> &,
591 const std::vector<T> & values)
592 {
593 // Now write the received values to the appropriate place(s) in v_local
594 for (auto i : index_range(values))
595 {
596 libmesh_assert(local_requested_ids.count(pid));
597 libmesh_assert_less(i, local_requested_ids[pid].size());
598
599 // Get the index in v_local where this value needs to be inserted.
600 const numeric_index_type local_requested_index =
601 local_requested_ids[pid][i];
602
603 // Actually set the value in v_local
604 v_local[local_requested_index] = values[i];
605 }
606 };
607
608 const T * ex = nullptr;
610 (this->comm(), requested_ids, gather_functor, action_functor, ex);
611}
612
613
614
615template <typename T>
617 const numeric_index_type last_local_idx,
618 const std::vector<numeric_index_type> & send_list)
619{
620 // Only good for serial vectors
621 libmesh_assert_equal_to (this->size(), this->local_size());
622 libmesh_assert_greater (last_local_idx, first_local_idx);
623 libmesh_assert_less_equal (send_list.size(), this->size());
624 libmesh_assert_less (last_local_idx, this->size());
625
626 const numeric_index_type my_size = this->size();
627 const numeric_index_type my_local_size = (last_local_idx - first_local_idx + 1);
628
629 // Don't bother for serial cases
630 if ((first_local_idx == 0) &&
631 (my_local_size == my_size))
632 return;
633
634
635 // Build a parallel vector, initialize it with the local
636 // parts of (*this)
637 DistributedVector<T> parallel_vec(this->comm());
638
639 parallel_vec.init (my_size, my_local_size, true, PARALLEL);
640
641 // Copy part of *this into the parallel_vec
642 for (numeric_index_type i=first_local_idx; i<=last_local_idx; i++)
643 parallel_vec._values[i-first_local_idx] = _values[i];
644
645 // localize like normal
646 parallel_vec.localize (*this, send_list);
647}
648
649
650
651template <typename T>
652void DistributedVector<T>::localize (std::vector<T> & v_local) const
653{
654 // This function must be run on all processors at once
655 parallel_object_only();
656
657 libmesh_assert (this->initialized());
658 libmesh_assert_equal_to (_values.size(), _local_size);
659 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
660
661 v_local = this->_values;
662
663 this->comm().allgather (v_local);
664
665#ifndef LIBMESH_HAVE_MPI
666 libmesh_assert_equal_to (local_size(), size());
667#endif
668}
669
670
671
672template <typename T>
673void DistributedVector<T>::localize_to_one (std::vector<T> & v_local,
674 const processor_id_type pid) const
675{
676 // This function must be run on all processors at once
677 parallel_object_only();
678
679 libmesh_assert (this->initialized());
680 libmesh_assert_equal_to (_values.size(), _local_size);
681 libmesh_assert_equal_to ((_last_local_index - _first_local_index), _local_size);
682
683 v_local = this->_values;
684
685 this->comm().gather (pid, v_local);
686
687#ifndef LIBMESH_HAVE_MPI
688 libmesh_assert_equal_to (local_size(), size());
689#endif
690}
691
692
693
694template <typename T>
696 const NumericVector<T> &)
697//void DistributedVector<T>::pointwise_mult (const NumericVector<T> & vec1,
698// const NumericVector<T> & vec2)
699{
700 libmesh_not_implemented();
701}
702
703template <typename T>
705 const NumericVector<T> &)
706{
707 libmesh_not_implemented();
708}
709
710//--------------------------------------------------------------
711// Explicit instantiations
712template class LIBMESH_EXPORT DistributedVector<Number>;
713
714} // namespace libMesh
This class provides a simple parallel, distributed vector datatype which is specific to libmesh.
virtual void localize(std::vector< T > &v_local) const override
Creates a copy of the global vector in the local vector v_local.
virtual numeric_index_type local_size() const override
virtual void pointwise_divide(const NumericVector< T > &vec1, const NumericVector< T > &vec2) override
Computes (summation not implied) i.e.
DistributedVector & operator=(const DistributedVector &)
Copy assignment operator.
virtual NumericVector< T > & operator*=(const NumericVector< T > &v) override
Computes the component-wise multiplication of this vector's entries by another's, .
std::vector< T > _values
Actual vector datatype to hold vector entries.
numeric_index_type _first_local_index
The first component stored locally.
virtual NumericVector< T > & operator/=(const NumericVector< T > &v) override
Computes the component-wise division of this vector's entries by another's, .
virtual NumericVector< T > & operator-=(const NumericVector< T > &v) override
Subtracts v from *this, .
numeric_index_type _last_local_index
The last component (+1) stored locally.
virtual Real l1_norm() const override
virtual void init(const numeric_index_type N, const numeric_index_type n_local, const bool fast=false, const ParallelType ptype=AUTOMATIC) override
Change the dimension of the vector to n.
virtual void reciprocal() override
Computes the component-wise reciprocal, .
virtual void conjugate() override
Negates the imaginary component of each entry in the vector.
virtual void localize_to_one(std::vector< T > &v_local, const processor_id_type proc_id=0) const override
Creates a local copy of the global vector in v_local only on processor proc_id.
virtual Real l2_norm() const override
virtual void scale(const T factor) override
Scale each element of the vector by the given factor.
virtual void add(const numeric_index_type i, const T value) override
Adds value to the vector entry specified by i.
virtual Real linfty_norm() const override
virtual numeric_index_type first_local_index() const override
numeric_index_type _global_size
The global vector size.
virtual NumericVector< T > & operator+=(const NumericVector< T > &v) override
Adds v to *this, .
virtual void close() override
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
virtual T sum() const override
virtual T dot(const NumericVector< T > &V) const override
numeric_index_type _local_size
The local vector size.
virtual void pointwise_mult(const NumericVector< T > &vec1, const NumericVector< T > &vec2) override
Computes (summation not implied) i.e.
virtual void abs() override
Sets for each entry in the vector.
virtual numeric_index_type last_local_index() const override
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition int_range.h:54
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
bool _is_initialized
true once init() has been called.
virtual numeric_index_type size() const =0
bool _is_closed
Flag which tracks whether the vector's values are consistent on all processors after insertion or add...
static const Real b
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
auto norm_sq(const T &a)
The libMesh namespace provides an interface to certain functionality in the library.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
bool closed()
Checks that the library has been closed.
Definition libmesh.C:331
libmesh_assert(ctx)
dof_id_type numeric_index_type
Definition id_types.h:99
bool initialized()
Checks that library initialization has been done.
Definition libmesh.C:324
T libmesh_conj(T a)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
uint8_t processor_id_type
Definition id_types.h:104