libMesh
numeric_vector.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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 // Local Includes
20 #include "libmesh/numeric_vector.h"
21 #include "libmesh/distributed_vector.h"
22 #include "libmesh/laspack_vector.h"
23 #include "libmesh/eigen_sparse_vector.h"
24 #include "libmesh/petsc_vector.h"
25 #include "libmesh/trilinos_epetra_vector.h"
26 #include "libmesh/shell_matrix.h"
27 #include "libmesh/tensor_tools.h"
28 #include "libmesh/enum_solver_package.h"
29 #include "libmesh/int_range.h"
30 
31 // gzstream for reading/writing compressed files as a stream
32 #ifdef LIBMESH_HAVE_GZSTREAM
33 # include "gzstream.h"
34 #endif
35 
36 // C++ includes
37 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC
38 #include <cmath> // for std::abs
39 #include <fstream>
40 #include <iostream>
41 #include <sstream>
42 #include <limits>
43 #include <memory>
44 
45 #ifdef LIBMESH_HAVE_CXX11_REGEX
46 #include <regex>
47 #endif
48 
49 
50 namespace libMesh
51 {
52 
53 
54 
55 //------------------------------------------------------------------
56 // NumericVector methods
57 
58 // Full specialization for Real datatypes
59 template <typename T>
60 std::unique_ptr<NumericVector<T>>
62  SolverPackage solver_package,
63  ParallelType parallel_type)
64 {
65  // Build the appropriate vector
66  switch (solver_package)
67  {
68 
69 #ifdef LIBMESH_HAVE_LASPACK
70  case LASPACK_SOLVERS:
71  return std::make_unique<LaspackVector<T>>(comm, parallel_type);
72 #endif
73 
74 #ifdef LIBMESH_HAVE_PETSC
75  case PETSC_SOLVERS:
76  return std::make_unique<PetscVector<T>>(comm, parallel_type);
77 #endif
78 
79 #ifdef LIBMESH_TRILINOS_HAVE_EPETRA
80  case TRILINOS_SOLVERS:
81  return std::make_unique<EpetraVector<T>>(comm, parallel_type);
82 #endif
83 
84 #ifdef LIBMESH_HAVE_EIGEN
85  case EIGEN_SOLVERS:
86  return std::make_unique<EigenSparseVector<T>>(comm, parallel_type);
87 #endif
88 
89  default:
90  return std::make_unique<DistributedVector<T>>(comm, parallel_type);
91  }
92 }
93 
94 
95 
96 template <typename T>
98 {
99  // Check for no-op
100  if (_type == t)
101  return;
102 
103  // If the NumericVector is not yet initialized, then it is generally
104  // safe to change the ParallelType, with minor restrictions.
105  if (!this->initialized())
106  {
107  // If ghosted vectors are not enabled and the user requested a
108  // GHOSTED vector, fall back on SERIAL.
109 #ifndef LIBMESH_ENABLE_GHOSTED
110  if (t == GHOSTED)
111  {
112  _type = SERIAL;
113  return;
114  }
115 #endif
116 
117  _type = t;
118  return;
119  }
120 
121  // If we made it here, then the NumericVector was already
122  // initialized and we don't currently allow the ParallelType to be
123  // changed, although this could potentially be added later.
124  libmesh_not_implemented();
125 }
126 
127 template <typename T>
128 void NumericVector<T>::insert (const T * v,
129  const std::vector<numeric_index_type> & dof_indices)
130 {
131  libmesh_assert (v);
132 
133  for (auto i : index_range(dof_indices))
134  this->set (dof_indices[i], v[i]);
135 }
136 
137 
138 
139 template <typename T>
141  const std::vector<numeric_index_type> & dof_indices)
142 {
143  libmesh_assert_equal_to (V.size(), dof_indices.size());
144  libmesh_assert (V.readable());
145 
146  for (auto i : index_range(dof_indices))
147  this->set (dof_indices[i], V(i));
148 }
149 
150 
151 
152 template <typename T>
153 int NumericVector<T>::compare (const NumericVector<T> & other_vector,
154  const Real threshold) const
155 {
156  libmesh_assert(this->compatible(other_vector));
157 
158  int first_different_i = std::numeric_limits<int>::max();
159  numeric_index_type i = first_local_index();
160 
161  while (first_different_i==std::numeric_limits<int>::max()
162  && i<last_local_index())
163  {
164  if (std::abs((*this)(i) - other_vector(i)) > threshold)
165  first_different_i = i;
166  else
167  i++;
168  }
169 
170  // Find the correct first differing index in parallel
171  this->comm().min(first_different_i);
172 
173  if (first_different_i == std::numeric_limits<int>::max())
174  return -1;
175 
176  return first_different_i;
177 }
178 
179 
180 template <typename T>
182  const Real threshold) const
183 {
184  libmesh_assert(this->compatible(other_vector));
185 
186  int first_different_i = std::numeric_limits<int>::max();
187  numeric_index_type i = first_local_index();
188 
189  do
190  {
191  if (std::abs((*this)(i) - other_vector(i)) > threshold *
192  std::max(std::abs((*this)(i)), std::abs(other_vector(i))))
193  first_different_i = i;
194  else
195  i++;
196  }
197  while (first_different_i==std::numeric_limits<int>::max()
198  && i<last_local_index());
199 
200  // Find the correct first differing index in parallel
201  this->comm().min(first_different_i);
202 
203  if (first_different_i == std::numeric_limits<int>::max())
204  return -1;
205 
206  return first_different_i;
207 }
208 
209 
210 template <typename T>
212  const Real threshold) const
213 {
214  libmesh_assert(this->compatible(other_vector));
215 
216  int first_different_i = std::numeric_limits<int>::max();
217  numeric_index_type i = first_local_index();
218 
219  const Real my_norm = this->linfty_norm();
220  const Real other_norm = other_vector.linfty_norm();
221  const Real abs_threshold = std::max(my_norm, other_norm) * threshold;
222 
223  do
224  {
225  if (std::abs((*this)(i) - other_vector(i) ) > abs_threshold)
226  first_different_i = i;
227  else
228  i++;
229  }
230  while (first_different_i==std::numeric_limits<int>::max()
231  && i<last_local_index());
232 
233  // Find the correct first differing index in parallel
234  this->comm().min(first_different_i);
235 
236  if (first_different_i == std::numeric_limits<int>::max())
237  return -1;
238 
239  return first_different_i;
240 }
241 
242 
243 
244 template <typename T>
245 void NumericVector<T>::print_matlab(const std::string & filename) const
246 {
247  parallel_object_only();
248 
249  libmesh_assert (this->initialized());
250 
251  const numeric_index_type first_dof = this->first_local_index(),
252  end_dof = this->last_local_index();
253 
254  // We'll print the vector from processor 0 to make sure
255  // it's serialized properly
256  if (this->processor_id() == 0)
257  {
258  std::unique_ptr<std::ofstream> file;
259 
260  if (filename != "")
261  file = std::make_unique<std::ofstream>(filename.c_str());
262 
263  std::ostream & os = (filename == "") ? libMesh::out : *file;
264 
265  // Print a header similar to PETSC_VIEWER_ASCII_MATLAB
266  os << "%Vec Object: () " << this->n_processors() << " MPI processes\n"
267  << "% type: " << (this->n_processors() > 1 ? "mpi" : "seq") << "\n"
268  << "Vec = [\n";
269 
270  for (numeric_index_type i : make_range(end_dof))
271  os << (*this)(i) << '\n';
272 
273  std::vector<T> cbuf;
274  for (auto p : IntRange<processor_id_type>(1, this->n_processors()))
275  {
276  this->comm().receive(p, cbuf);
277 
278  for (auto c : cbuf)
279  os << c << '\n';
280  }
281 
282  os << "];\n" << std::endl;
283  }
284  else
285  {
286  std::vector<T> cbuf(end_dof - first_dof);
287 
288  for (numeric_index_type i : make_range(end_dof - first_dof))
289  cbuf[i] = (*this)(first_dof+i);
290  this->comm().send(0,cbuf);
291  }
292 }
293 
294 
295 
296 template <typename T>
297 void NumericVector<T>::read_matlab(const std::string & filename)
298 {
299  LOG_SCOPE("read_matlab()", "NumericVector");
300 
301 #ifndef LIBMESH_HAVE_CXX11_REGEX
302  libmesh_not_implemented(); // What is your compiler?!? Email us!
303  libmesh_ignore(filename);
304 #else
305  parallel_object_only();
306 
307  const bool gzipped_file = (filename.rfind(".gz") == filename.size() - 3);
308 
309  // If we don't already have this size, we'll need to reinit, and
310  // determine which entries each processor is in charge of.
311  std::vector<numeric_index_type> first_entries, end_entries;
312 
313  numeric_index_type first_entry = 0,
314  end_entry = 0,
315  n = 0;
316 
317  // We'll use an istream here; it might be an ifstream if we're
318  // opening a raw ASCII file or a gzstream if we're opening a
319  // compressed one.
320  std::unique_ptr<std::istream> file;
321 
322  // First read through the file, saving size and entry data
323  std::vector<T> entries;
324 
325  {
326  // We'll read the vector on processor 0 rather than try to juggle
327  // parallel I/O.
328  if (this->processor_id() == 0)
329  {
330  // We'll be using regular expressions to make ourselves slightly
331  // more robust to formatting.
332  const std::regex start_regex // assignment like "Vec_0x84000002_1 = ["
333  ("\\s*\\w+\\s*=\\s*\\[");
334  const std::regex end_regex // end of assignment
335  ("^[^%]*\\]");
336 
337  if (gzipped_file)
338  {
339 #ifdef LIBMESH_HAVE_GZSTREAM
340  auto inf = std::make_unique<igzstream>();
341  libmesh_assert(inf);
342  inf->open(filename.c_str(), std::ios::in);
343  file = std::move(inf);
344 #else
345  libmesh_error_msg("ERROR: need gzstream to handle .gz files!!!");
346 #endif
347  }
348  else
349  {
350  auto inf = std::make_unique<std::ifstream>();
351  libmesh_assert(inf);
352 
353  std::string new_name = Utility::unzip_file(filename);
354 
355  inf->open(new_name.c_str(), std::ios::in);
356  file = std::move(inf);
357  }
358 
359  const std::string whitespace = " \t";
360 
361  bool have_started = false;
362  bool have_ended = false;
363 
364  for (std::string line; std::getline(*file, line);)
365  {
366  std::smatch sm;
367 
368  // First, try to match an entry. This is the most common
369  // case so we won't rely on slow std::regex for it.
370  // stringstream is at least an improvement over that.
371  std::istringstream l(line);
372  T value;
373  l >> value;
374 
375  if (!l.fail())
376  {
377  libmesh_error_msg_if
378  (!have_started, "Confused by premature entries in vector file " << filename);
379 
380  entries.push_back(value);
381  ++n;
382  }
383 
384  else if (std::regex_search(line, start_regex))
385  have_started = true;
386 
387  else if (std::regex_search(line, end_regex))
388  {
389  have_ended = true;
390  break;
391  }
392  }
393 
394  libmesh_error_msg_if
395  (!have_started, "Confused by missing assignment-beginning in vector file " << filename);
396 
397  libmesh_error_msg_if
398  (!have_ended, "Confused by missing assignment-ending in vector file " << filename);
399  }
400 
401  this->comm().broadcast(n);
402 
403  if (this->initialized() &&
404  n == this->size())
405  {
406  first_entry = this->first_local_index(),
407  end_entry = this->last_local_index();
408  }
409  else
410  {
411  // Determine which rows/columns each processor will be in charge of
412  first_entry = this->processor_id() * n / this->n_processors(),
413  end_entry = (this->processor_id()+1) * n / this->n_processors();
414  }
415 
416  this->comm().gather(0, first_entry, first_entries);
417  this->comm().gather(0, end_entry, end_entries);
418 
419  } // Done reading entry data and broadcasting vector size
420 
421  // If we're not already initialized compatibly with the file then
422  // we'll initialize here
423  bool need_init = !this->initialized() ||
424  (this->size() != n) ||
425  (this->local_size() != end_entry - first_entry);
426 
427  this->comm().max(need_init);
428 
429  if (need_init)
430  this->init(n, end_entry - first_entry);
431 
432  // Set the vector values last. The iota call here is inefficient
433  // but it's probably better than calling a single virtual function
434  // per index.
435  if (this->processor_id() == 0)
436  for (auto p : make_range(this->n_processors()))
437  {
438  const numeric_index_type first_entry_p = first_entries[p];
439  const numeric_index_type n_local = end_entries[p] - first_entry_p;
440  std::vector<numeric_index_type> indices(n_local);
441  std::iota(indices.begin(), indices.end(), first_entry_p);
442  this->insert(entries.data() + first_entries[p],
443  indices);
444  }
445 
446  this->close();
447 #endif
448 }
449 
450 
451 /*
452 // Full specialization for float datatypes (DistributedVector wants this)
453 
454 template <>
455 int NumericVector<float>::compare (const NumericVector<float> & other_vector,
456 const Real threshold) const
457 {
458 libmesh_assert (this->initialized());
459 libmesh_assert (other_vector.initialized());
460 libmesh_assert_equal_to (this->first_local_index(), other_vector.first_local_index());
461 libmesh_assert_equal_to (this->last_local_index(), other_vector.last_local_index());
462 
463 int rvalue = -1;
464 numeric_index_type i = first_local_index();
465 
466 do
467 {
468 if (std::abs((*this)(i) - other_vector(i) ) > threshold)
469 rvalue = i;
470 else
471 i++;
472 }
473 while (rvalue==-1 && i<last_local_index());
474 
475 return rvalue;
476 }
477 
478 // Full specialization for double datatypes
479 template <>
480 int NumericVector<double>::compare (const NumericVector<double> & other_vector,
481 const Real threshold) const
482 {
483 libmesh_assert (this->initialized());
484 libmesh_assert (other_vector.initialized());
485 libmesh_assert_equal_to (this->first_local_index(), other_vector.first_local_index());
486 libmesh_assert_equal_to (this->last_local_index(), other_vector.last_local_index());
487 
488 int rvalue = -1;
489 numeric_index_type i = first_local_index();
490 
491 do
492 {
493 if (std::abs((*this)(i) - other_vector(i) ) > threshold)
494 rvalue = i;
495 else
496 i++;
497 }
498 while (rvalue==-1 && i<last_local_index());
499 
500 return rvalue;
501 }
502 
503 #ifdef LIBMESH_DEFAULT_TRIPLE_PRECISION
504 // Full specialization for long double datatypes
505 template <>
506 int NumericVector<long double>::compare (const NumericVector<long double> & other_vector,
507 const Real threshold) const
508 {
509 libmesh_assert (this->initialized());
510 libmesh_assert (other_vector.initialized());
511 libmesh_assert_equal_to (this->first_local_index(), other_vector.first_local_index());
512 libmesh_assert_equal_to (this->last_local_index(), other_vector.last_local_index());
513 
514 int rvalue = -1;
515 numeric_index_type i = first_local_index();
516 
517 do
518 {
519 if (std::abs((*this)(i) - other_vector(i) ) > threshold)
520 rvalue = i;
521 else
522 i++;
523 }
524 while (rvalue==-1 && i<last_local_index());
525 
526 return rvalue;
527 }
528 #endif
529 
530 
531 // Full specialization for Complex datatypes
532 template <>
533 int NumericVector<Complex>::compare (const NumericVector<Complex> & other_vector,
534 const Real threshold) const
535 {
536 libmesh_assert (this->initialized());
537 libmesh_assert (other_vector.initialized());
538 libmesh_assert_equal_to (this->first_local_index(), other_vector.first_local_index());
539 libmesh_assert_equal_to (this->last_local_index(), other_vector.last_local_index());
540 
541 int rvalue = -1;
542 numeric_index_type i = first_local_index();
543 
544 do
545 {
546 if ((std::abs((*this)(i).real() - other_vector(i).real()) > threshold) || (std::abs((*this)(i).imag() - other_vector(i).imag()) > threshold))
547 rvalue = i;
548 else
549 i++;
550 }
551 while (rvalue==-1 && i<this->last_local_index());
552 
553 return rvalue;
554 }
555 */
556 
557 
558 template <class T>
559 Real NumericVector<T>::subset_l1_norm (const std::set<numeric_index_type> & indices) const
560 {
561  libmesh_assert (this->readable());
562 
563  const NumericVector<T> & v = *this;
564 
565  Real norm = 0;
566 
567  for (const auto & index : indices)
568  norm += std::abs(v(index));
569 
570  this->comm().sum(norm);
571 
572  return norm;
573 }
574 
575 template <class T>
576 Real NumericVector<T>::subset_l2_norm (const std::set<numeric_index_type> & indices) const
577 {
578  libmesh_assert (this->readable());
579 
580  const NumericVector<T> & v = *this;
581 
582  Real norm = 0;
583 
584  for (const auto & index : indices)
585  norm += TensorTools::norm_sq(v(index));
586 
587  this->comm().sum(norm);
588 
589  return std::sqrt(norm);
590 }
591 
592 template <class T>
593 Real NumericVector<T>::subset_linfty_norm (const std::set<numeric_index_type> & indices) const
594 {
595  libmesh_assert (this->readable());
596 
597  const NumericVector<T> & v = *this;
598 
599  Real norm = 0;
600 
601  for (const auto & index : indices)
602  {
603  Real value = std::abs(v(index));
604  if (value > norm)
605  norm = value;
606  }
607 
608  this->comm().max(norm);
609 
610  return norm;
611 }
612 
613 
614 
615 template <class T>
617 {
618  libmesh_assert(this->compatible(v));
619 
620  Real norm = 0;
621  for (const auto i : make_range(this->first_local_index(), this->last_local_index()))
622  norm += TensorTools::norm_sq((*this)(i) - v(i));
623 
624  this->comm().sum(norm);
625 
626  return std::sqrt(norm);
627 }
628 
629 
630 
631 template <class T>
633 {
634  libmesh_assert(this->compatible(v));
635 
636  Real norm = 0;
637  for (const auto i : make_range(this->first_local_index(), this->last_local_index()))
638  norm += libMesh::l1_norm_diff((*this)(i), v(i));
639 
640  this->comm().sum(norm);
641 
642  return norm;
643 }
644 
645 
646 
647 template <typename T>
649  const std::vector<numeric_index_type> & dof_indices)
650 {
651  libmesh_assert(v);
652 
653  for (auto i : index_range(dof_indices))
654  this->add (dof_indices[i], v[i]);
655 }
656 
657 
658 
659 template <typename T>
661  const std::vector<numeric_index_type> & dof_indices)
662 {
664 
665  const std::size_t n = dof_indices.size();
666  libmesh_assert_equal_to(v.size(), n);
667  for (numeric_index_type i=0; i != n; i++)
668  this->add (dof_indices[i], v(i));
669 }
670 
671 
672 
673 template <typename T>
675  const ShellMatrix<T> & a)
676 {
677  libmesh_assert(this->compatible(v));
678 
679  a.vector_mult_add(*this,v);
680 }
681 
682 
683 
684 template <typename T>
686 {
687  return this->initialized() && this->closed();
688 }
689 
690 
691 template <typename T>
693 {
694  return this->readable() && v.readable() &&
695  this->size() == v.size() &&
696  this->local_size() == v.local_size() &&
697  this->first_local_index() == v.first_local_index() &&
698  this->last_local_index() == v.last_local_index();
699 }
700 
701 
702 //------------------------------------------------------------------
703 // Explicit instantiations
704 template class LIBMESH_EXPORT NumericVector<Number>;
705 
706 } // namespace libMesh
virtual void insert(const T *v, const std::vector< numeric_index_type > &dof_indices)
Inserts the entries of v in *this at the locations specified by v.
bool closed()
Checks that the library has been closed.
Definition: libmesh.C:283
virtual Real subset_l2_norm(const std::set< numeric_index_type > &indices) const
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
Definition: int_range.h:53
virtual numeric_index_type size() const =0
virtual void add_vector(const T *v, const std::vector< numeric_index_type > &dof_indices)
Computes , where v is a pointer and each dof_indices[i] specifies where to add value v[i]...
Provides a uniform interface to vector storage schemes for different linear algebra libraries...
Definition: vector_fe_ex5.C:44
The libMesh namespace provides an interface to certain functionality in the library.
virtual Real subset_linfty_norm(const std::set< numeric_index_type > &indices) const
virtual Real subset_l1_norm(const std::set< numeric_index_type > &indices) const
virtual void vector_mult_add(NumericVector< T > &dest, const NumericVector< T > &arg) const =0
Multiplies the matrix with arg and adds the result to dest.
void iota(ForwardIter first, ForwardIter last, T value)
Utility::iota was created back when std::iota was just an SGI STL extension.
Definition: utility.h:229
Real l2_norm_diff(const NumericVector< T > &other_vec) const
void libmesh_ignore(const Args &...)
dof_id_type numeric_index_type
Definition: id_types.h:99
auto l1_norm_diff(const NumericVector< T > &vec1, const NumericVector< T > &vec2)
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
std::string unzip_file(std::string_view name)
Create an unzipped copy of a bz2 or xz file, returning the name of the now-unzipped file that can be ...
Definition: utility.C:164
libmesh_assert(ctx)
virtual void read_matlab(const std::string &filename)
Read the contents of the vector from the Matlab-script format used by PETSc.
bool compatible(const NumericVector< T > &v) const
virtual int global_relative_compare(const NumericVector< T > &other_vector, const Real threshold=TOLERANCE) const
auto norm(const T &a) -> decltype(std::abs(a))
Definition: tensor_tools.h:74
Real l1_norm_diff(const NumericVector< T > &other_vec) const
virtual numeric_index_type first_local_index() const =0
virtual int local_relative_compare(const NumericVector< T > &other_vector, const Real threshold=TOLERANCE) const
virtual void print_matlab(const std::string &filename="") const
Print the contents of the vector in Matlab&#39;s sparse matrix format.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual int compare(const NumericVector< T > &other_vector, const Real threshold=TOLERANCE) const
auto norm_sq(const T &a) -> decltype(std::norm(a))
Definition: tensor_tools.h:104
OStreamProxy out
virtual numeric_index_type local_size() const =0
static const bool value
Definition: xdr_io.C:54
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
static std::unique_ptr< NumericVector< T > > build(const Parallel::Communicator &comm, SolverPackage solver_package=libMesh::default_solver_package(), ParallelType parallel_type=AUTOMATIC)
Builds a NumericVector on the processors in communicator comm using the linear solver package specifi...
bool initialized()
Checks that library initialization has been done.
Definition: libmesh.C:276
SolverPackage
Defines an enum for various linear solver packages.
Generic shell matrix, i.e.
virtual numeric_index_type last_local_index() const =0
virtual Real linfty_norm() const =0
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:117
void set_type(ParallelType t)
Allow the user to change the ParallelType of the NumericVector under some circumstances.
ParallelType
Defines an enum for parallel data structure types.