Files
mfem/linalg/vector.hpp
T

727 lines
23 KiB
C++

// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_VECTOR
#define MFEM_VECTOR
#include "../general/array.hpp"
#ifdef MFEM_USE_ADIOS2
#include "../general/adios2stream.hpp"
#endif
#include "../general/globals.hpp"
#include "../general/mem_manager.hpp"
#include "../general/device.hpp"
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <limits>
#if defined(_MSC_VER) && (_MSC_VER < 1800)
#include <float.h>
#define isfinite _finite
#endif
#ifdef MFEM_USE_MPI
#include <mpi.h>
#endif
namespace mfem
{
/** Count the number of entries in an array of doubles for which isfinite
is false, i.e. the entry is a NaN or +/-Inf. */
inline int CheckFinite(const real_t *v, const int n);
/// Define a shortcut for std::numeric_limits<double>::infinity()
#ifndef __CYGWIN__
inline real_t infinity()
{
return std::numeric_limits<real_t>::infinity();
}
#else
// On Cygwin math.h defines a function 'infinity()' which will conflict with the
// above definition if we have 'using namespace mfem;' and try to use something
// like 'double a = infinity();'. This 'infinity()' function is non-standard and
// is defined by the Newlib C standard library implementation used by Cygwin,
// see https://en.wikipedia.org/wiki/Newlib, http://www.sourceware.org/newlib.
using ::infinity;
#endif
/// Generate a random `real_t` number in the interval [0,1) using rand().
inline real_t rand_real()
{
constexpr real_t max = (real_t)(RAND_MAX) + 1_r;
#if defined(MFEM_USE_SINGLE)
// Note: For RAND_MAX = 2^31-1, float(RAND_MAX) = 2^31, and
// max = float(RAND_MAX)+1.0f is equal to 2^31 too; this is the actual value
// that we want for max. However, this rounding behavior means that for a
// range of values of rand() close to RAND_MAX, the expression
// float(rand())/max will give 1.0f.
//
// Therefore, to ensure we return a number less than 1, we take the minimum
// of float(rand())/max and (1 - 2^(-24)), where the latter number is the
// largest float less than 1.
return std::fmin(real_t(rand())/max, 0.9999999403953552_r);
#else
return real_t(rand())/max;
#endif
}
/// Vector data type.
class Vector
{
protected:
Memory<real_t> data;
int size;
public:
/** Default constructor for Vector. Sets size = 0, and calls Memory::Reset on
data through Memory<double>'s default constructor. */
Vector(): size(0) { }
/// Copy constructor. Allocates a new data array and copies the data.
Vector(const Vector &);
/// Move constructor. "Steals" data from its argument.
Vector(Vector&& v);
/// @brief Creates vector of size s.
/// @warning Entries are not initialized to zero!
explicit Vector(int s);
/// Creates a vector referencing an array of doubles, owned by someone else.
/** The pointer @a data_ can be NULL. The data array can be replaced later
with SetData(). */
Vector(real_t *data_, int size_)
{ data.Wrap(data_, size_, false); size = size_; }
/** @brief Create a Vector referencing a sub-vector of the Vector @a base
starting at the given offset, @a base_offset, and size @a size_. */
Vector(Vector &base, int base_offset, int size_)
: data(base.data, base_offset, size_), size(size_) { }
/// Create a Vector of size @a size_ using MemoryType @a mt.
Vector(int size_, MemoryType mt)
: data(size_, mt), size(size_) { }
/** @brief Create a Vector of size @a size_ using host MemoryType @a h_mt and
device MemoryType @a d_mt. */
Vector(int size_, MemoryType h_mt, MemoryType d_mt)
: data(size_, h_mt, d_mt), size(size_) { }
/// Create a vector using a braced initializer list
template <int N, typename T = real_t>
explicit Vector(const T (&values)[N]) : Vector(N)
{ std::copy(values, values + N, GetData()); }
/// Enable execution of Vector operations using the mfem::Device.
/** The default is to use Backend::CPU (serial execution on each MPI rank),
regardless of the mfem::Device configuration.
When appropriate, MFEM functions and class methods will enable the use
of the mfem::Device for their Vector parameters.
Some derived classes, e.g. GridFunction, enable the use of the
mfem::Device by default. */
virtual void UseDevice(bool use_dev) const { data.UseDevice(use_dev); }
/// Return the device flag of the Memory object used by the Vector
virtual bool UseDevice() const { return data.UseDevice(); }
/// Reads a vector from multiple files
void Load(std::istream ** in, int np, int * dim);
/// Load a vector from an input stream.
void Load(std::istream &in, int Size);
/// Load a vector from an input stream, reading the size from the stream.
void Load(std::istream &in) { int s; in >> s; Load(in, s); }
/// @brief Resize the vector to size @a s.
/** If the new size is less than or equal to Capacity() then the internal
data array remains the same. Otherwise, the old array is deleted, if
owned, and a new array of size @a s is allocated without copying the
previous content of the Vector.
@warning In the second case above (new size greater than current one),
the vector will allocate new data array, even if it did not own the
original data! Also, new entries are not initialized! */
void SetSize(int s);
/// Resize the vector to size @a s using MemoryType @a mt.
void SetSize(int s, MemoryType mt);
/// Resize the vector to size @a s using the MemoryType of @a v.
void SetSize(int s, const Vector &v) { SetSize(s, v.GetMemory().GetMemoryType()); }
/// Set the Vector data.
/// @warning This method should be called only when OwnsData() is false.
void SetData(real_t *d) { data.Wrap(d, data.Capacity(), false); }
/// Set the Vector data and size.
/** The Vector does not assume ownership of the new data. The new size is
also used as the new Capacity().
@warning This method should be called only when OwnsData() is false.
@sa NewDataAndSize(). */
void SetDataAndSize(real_t *d, int s) { data.Wrap(d, s, false); size = s; }
/// Set the Vector data and size, deleting the old data, if owned.
/** The Vector does not assume ownership of the new data. The new size is
also used as the new Capacity().
@sa SetDataAndSize(). */
void NewDataAndSize(real_t *d, int s)
{
data.Delete();
SetDataAndSize(d, s);
}
/// Reset the Vector to use the given external Memory @a mem and size @a s.
/** If @a own_mem is false, the Vector will not own any of the pointers of
@a mem.
Note that when @a own_mem is true, the @a mem object can be destroyed
immediately by the caller but `mem.Delete()` should NOT be called since
the Vector object takes ownership of all pointers owned by @a mem.
@sa NewDataAndSize(). */
inline void NewMemoryAndSize(const Memory<real_t> &mem, int s, bool own_mem);
/// Reset the Vector to be a reference to a sub-vector of @a base.
inline void MakeRef(Vector &base, int offset, int size);
/** @brief Reset the Vector to be a reference to a sub-vector of @a base
without changing its current size. */
inline void MakeRef(Vector &base, int offset);
/// Set the Vector data (host pointer) ownership flag.
void MakeDataOwner() const { data.SetHostPtrOwner(true); }
/// Destroy a vector
void Destroy();
/** @brief Delete the device pointer, if owned. If @a copy_to_host is true
and the data is valid only on device, move it to host before deleting.
Invalidates the device memory. */
void DeleteDevice(bool copy_to_host = true)
{ data.DeleteDevice(copy_to_host); }
/// Returns the size of the vector.
inline int Size() const { return size; }
/// Return the size of the currently allocated data array.
/** It is always true that Capacity() >= Size(). */
inline int Capacity() const { return data.Capacity(); }
/// Return a pointer to the beginning of the Vector data.
/** @warning This method should be used with caution as it gives write access
to the data of const-qualified Vector%s. */
inline real_t *GetData() const
{ return const_cast<real_t*>((const real_t*)data); }
/// Conversion to `double *`. Deprecated.
MFEM_DEPRECATED inline operator real_t *() { return data; }
/// Conversion to `const double *`. Deprecated.
MFEM_DEPRECATED inline operator const real_t *() const { return data; }
/// STL-like begin.
inline real_t *begin() { return data; }
/// STL-like end.
inline real_t *end() { return data + size; }
/// STL-like begin (const version).
inline const real_t *begin() const { return data; }
/// STL-like end (const version).
inline const real_t *end() const { return data + size; }
/// Return a reference to the Memory object used by the Vector.
Memory<real_t> &GetMemory() { return data; }
/** @brief Return a reference to the Memory object used by the Vector, const
version. */
const Memory<real_t> &GetMemory() const { return data; }
/// Update the memory location of the vector to match @a v.
void SyncMemory(const Vector &v) const { GetMemory().Sync(v.GetMemory()); }
/// Update the alias memory location of the vector to match @a v.
void SyncAliasMemory(const Vector &v) const
{ GetMemory().SyncAlias(v.GetMemory(),Size()); }
/// Read the Vector data (host pointer) ownership flag.
inline bool OwnsData() const { return data.OwnsHostPtr(); }
/// Changes the ownership of the data; after the call the Vector is empty
inline void StealData(real_t **p)
{ *p = data; data.Reset(); size = 0; }
/// Changes the ownership of the data; after the call the Vector is empty
inline real_t *StealData() { real_t *p; StealData(&p); return p; }
/// Access Vector entries. Index i = 0 .. size-1.
real_t &Elem(int i);
/// Read only access to Vector entries. Index i = 0 .. size-1.
const real_t &Elem(int i) const;
/// Access Vector entries using () for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline real_t &operator()(int i);
/// Read only access to Vector entries using () for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline const real_t &operator()(int i) const;
/// Access Vector entries using [] for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline real_t &operator[](int i) { return (*this)(i); }
/// Read only access to Vector entries using [] for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline const real_t &operator[](int i) const { return (*this)(i); }
/// Dot product with a `double *` array.
real_t operator*(const real_t *) const;
/// Return the inner-product.
real_t operator*(const Vector &v) const;
/// Copy Size() entries from @a v.
Vector &operator=(const real_t *v);
/// Copy assignment.
/** @note Defining this method overwrites the implicitly defined copy
assignment operator. */
Vector &operator=(const Vector &v);
/// Move assignment
Vector &operator=(Vector&& v);
/// Redefine '=' for vector = constant.
Vector &operator=(real_t value);
Vector &operator*=(real_t c);
/// Component-wise scaling: (*this)(i) *= v(i)
Vector &operator*=(const Vector &v);
Vector &operator/=(real_t c);
/// Component-wise division: (*this)(i) /= v(i)
Vector &operator/=(const Vector &v);
Vector &operator-=(real_t c);
Vector &operator-=(const Vector &v);
Vector &operator+=(real_t c);
Vector &operator+=(const Vector &v);
/// (*this) += a * Va
Vector &Add(const real_t a, const Vector &Va);
/// (*this) = a * x
Vector &Set(const real_t a, const Vector &x);
void SetVector(const Vector &v, int offset);
void AddSubVector(const Vector &v, int offset);
/// (*this) = -(*this)
void Neg();
/// (*this)(i) = 1.0 / (*this)(i)
void Reciprocal();
/// Swap the contents of two Vectors
inline void Swap(Vector &other);
/// Set v = v1 + v2.
friend void add(const Vector &v1, const Vector &v2, Vector &v);
/// Set v = v1 + alpha * v2.
friend void add(const Vector &v1, real_t alpha, const Vector &v2, Vector &v);
/// z = a * (x + y)
friend void add(const real_t a, const Vector &x, const Vector &y, Vector &z);
/// z = a * x + b * y
friend void add(const real_t a, const Vector &x,
const real_t b, const Vector &y, Vector &z);
/// Set v = v1 - v2.
friend void subtract(const Vector &v1, const Vector &v2, Vector &v);
/// z = a * (x - y)
friend void subtract(const real_t a, const Vector &x,
const Vector &y, Vector &z);
/// Computes cross product of this vector with another 3D vector.
/// vout = this x vin.
void cross3D(const Vector &vin, Vector &vout) const;
/// v = median(v,lo,hi) entrywise. Implementation assumes lo <= hi.
void median(const Vector &lo, const Vector &hi);
/// Extract entries listed in @a dofs to the output Vector @a elemvect.
/** Negative dof values cause the -dof-1 position in @a elemvect to receive
the -val in from this Vector. */
void GetSubVector(const Array<int> &dofs, Vector &elemvect) const;
/// Extract entries listed in @a dofs to the output array @a elem_data.
/** Negative dof values cause the -dof-1 position in @a elem_data to receive
the -val in from this Vector. */
void GetSubVector(const Array<int> &dofs, real_t *elem_data) const;
/// Set the entries listed in @a dofs to the given @a value.
/** Negative dof values cause the -dof-1 position in this Vector to receive
the -value. */
void SetSubVector(const Array<int> &dofs, const real_t value);
/** @brief Set the entries listed in @a dofs to the values given in the @a
elemvect Vector. Negative dof values cause the -dof-1 position in this
Vector to receive the -val from @a elemvect. */
void SetSubVector(const Array<int> &dofs, const Vector &elemvect);
/** @brief Set the entries listed in @a dofs to the values given the @a ,
elem_data array. Negative dof values cause the -dof-1 position in this
Vector to receive the -val from @a elem_data. */
void SetSubVector(const Array<int> &dofs, real_t *elem_data);
/** @brief Add elements of the @a elemvect Vector to the entries listed in @a
dofs. Negative dof values cause the -dof-1 position in this Vector to add
the -val from @a elemvect. */
void AddElementVector(const Array<int> & dofs, const Vector & elemvect);
/** @brief Add elements of the @a elem_data array to the entries listed in @a
dofs. Negative dof values cause the -dof-1 position in this Vector to add
the -val from @a elem_data. */
void AddElementVector(const Array<int> & dofs, real_t *elem_data);
/** @brief Add @a times the elements of the @a elemvect Vector to the entries
listed in @a dofs. Negative dof values cause the -dof-1 position in this
Vector to add the -a*val from @a elemvect. */
void AddElementVector(const Array<int> & dofs, const real_t a,
const Vector & elemvect);
/// Set all vector entries NOT in the @a dofs Array to the given @a val.
void SetSubVectorComplement(const Array<int> &dofs, const real_t val);
/// Prints vector to stream out.
void Print(std::ostream &out = mfem::out, int width = 8) const;
#ifdef MFEM_USE_ADIOS2
/// Prints vector to stream out.
/// @param out adios2stream output
/// @param variable_name variable name associated with current Vector
void Print(adios2stream & out, const std::string& variable_name) const;
#endif
/// Prints vector to stream out in HYPRE_Vector format.
void Print_HYPRE(std::ostream &out) const;
/// Print the Vector size and hash of its data.
/** This is a compact text representation of the Vector contents that can be
used to compare vectors from different runs without the need to save the
whole vector. */
void PrintHash(std::ostream &out) const;
/// Set random values in the vector.
void Randomize(int seed = 0);
/// Returns the l2 norm of the vector.
real_t Norml2() const;
/// Returns the l_infinity norm of the vector.
real_t Normlinf() const;
/// Returns the l_1 norm of the vector.
real_t Norml1() const;
/// Returns the l_p norm of the vector.
real_t Normlp(real_t p) const;
/// Returns the maximal element of the vector.
real_t Max() const;
/// Returns the minimal element of the vector.
real_t Min() const;
/// Return the sum of the vector entries
real_t Sum() const;
/// Compute the square of the Euclidean distance to another vector.
inline real_t DistanceSquaredTo(const real_t *p) const;
/// Compute the square of the Euclidean distance to another vector.
inline real_t DistanceSquaredTo(const Vector &p) const;
/// Compute the Euclidean distance to another vector.
inline real_t DistanceTo(const real_t *p) const;
/// Compute the Euclidean distance to another vector.
inline real_t DistanceTo(const Vector &p) const;
/** @brief Count the number of entries in the Vector for which isfinite
is false, i.e. the entry is a NaN or +/-Inf. */
int CheckFinite() const { return mfem::CheckFinite(HostRead(), size); }
/// Destroys vector.
virtual ~Vector();
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
virtual const real_t *Read(bool on_dev = true) const
{ return mfem::Read(data, size, on_dev); }
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
virtual const real_t *HostRead() const
{ return mfem::Read(data, size, false); }
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
virtual real_t *Write(bool on_dev = true)
{ return mfem::Write(data, size, on_dev); }
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
virtual real_t *HostWrite()
{ return mfem::Write(data, size, false); }
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
virtual real_t *ReadWrite(bool on_dev = true)
{ return mfem::ReadWrite(data, size, on_dev); }
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
virtual real_t *HostReadWrite()
{ return mfem::ReadWrite(data, size, false); }
};
// Inline methods
template <typename T>
inline T ZeroSubnormal(T val)
{
return (std::fpclassify(val) == FP_SUBNORMAL) ? 0.0 : val;
}
inline bool IsFinite(const real_t &val)
{
// isfinite didn't appear in a standard until C99, and later C++11. It wasn't
// standard in C89 or C++98. PGI as of 14.7 still defines it as a macro.
#ifdef isfinite
return isfinite(val);
#else
return std::isfinite(val);
#endif
}
inline int CheckFinite(const real_t *v, const int n)
{
int bad = 0;
for (int i = 0; i < n; i++)
{
if (!IsFinite(v[i])) { bad++; }
}
return bad;
}
inline Vector::Vector(int s)
{
MFEM_ASSERT(s>=0,"Unexpected negative size.");
size = s;
if (s > 0)
{
data.New(s);
}
}
inline void Vector::SetSize(int s)
{
if (s == size)
{
return;
}
if (s <= data.Capacity())
{
size = s;
return;
}
// preserve a valid MemoryType and device flag
const MemoryType mt = data.GetMemoryType();
const bool use_dev = data.UseDevice();
data.Delete();
size = s;
data.New(s, mt);
data.UseDevice(use_dev);
}
inline void Vector::SetSize(int s, MemoryType mt)
{
if (mt == data.GetMemoryType())
{
if (s == size)
{
return;
}
if (s <= data.Capacity())
{
size = s;
return;
}
}
const bool use_dev = data.UseDevice();
data.Delete();
if (s > 0)
{
data.New(s, mt);
size = s;
}
else
{
data.Reset();
size = 0;
}
data.UseDevice(use_dev);
}
inline void Vector::NewMemoryAndSize(const Memory<real_t> &mem, int s,
bool own_mem)
{
data.Delete();
size = s;
if (own_mem)
{
data = mem;
}
else
{
data.MakeAlias(mem, 0, s);
}
}
inline void Vector::MakeRef(Vector &base, int offset, int s)
{
data.Delete();
size = s;
data.MakeAlias(base.GetMemory(), offset, s);
}
inline void Vector::MakeRef(Vector &base, int offset)
{
data.Delete();
data.MakeAlias(base.GetMemory(), offset, size);
}
inline void Vector::Destroy()
{
const bool use_dev = data.UseDevice();
data.Delete();
size = 0;
data.Reset();
data.UseDevice(use_dev);
}
inline real_t &Vector::operator()(int i)
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
inline const real_t &Vector::operator()(int i) const
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
inline void Vector::Swap(Vector &other)
{
mfem::Swap(data, other.data);
mfem::Swap(size, other.size);
}
/// Specialization of the template function Swap<> for class Vector
template<> inline void Swap<Vector>(Vector &a, Vector &b)
{
a.Swap(b);
}
inline Vector::~Vector()
{
data.Delete();
}
inline real_t DistanceSquared(const real_t *x, const real_t *y, const int n)
{
real_t d = 0.0;
for (int i = 0; i < n; i++)
{
d += (x[i]-y[i])*(x[i]-y[i]);
}
return d;
}
inline real_t Distance(const real_t *x, const real_t *y, const int n)
{
return std::sqrt(DistanceSquared(x, y, n));
}
inline real_t Distance(const Vector &x, const Vector &y)
{
return x.DistanceTo(y);
}
inline real_t Vector::DistanceSquaredTo(const real_t *p) const
{
return DistanceSquared(data, p, size);
}
inline real_t Vector::DistanceSquaredTo(const Vector &p) const
{
MFEM_ASSERT(p.Size() == Size(), "Incompatible vector sizes.");
return DistanceSquared(data, p.data, size);
}
inline real_t Vector::DistanceTo(const real_t *p) const
{
return Distance(data, p, size);
}
inline real_t Vector::DistanceTo(const Vector &p) const
{
MFEM_ASSERT(p.Size() == Size(), "Incompatible vector sizes.");
return Distance(data, p.data, size);
}
/// Returns the inner product of x and y
/** In parallel this computes the inner product of the local vectors,
producing different results on each MPI rank.
*/
inline real_t InnerProduct(const Vector &x, const Vector &y)
{
return x * y;
}
#ifdef MFEM_USE_MPI
/// Returns the inner product of x and y in parallel
/** In parallel this computes the inner product of the global vectors,
producing identical results on each MPI rank.
*/
inline real_t InnerProduct(MPI_Comm comm, const Vector &x, const Vector &y)
{
real_t loc_prod = x * y;
real_t glb_prod;
MPI_Allreduce(&loc_prod, &glb_prod, 1, MFEM_MPI_REAL_T, MPI_SUM, comm);
return glb_prod;
}
#endif
} // namespace mfem
#endif