610 lines
19 KiB
C++
610 lines
19 KiB
C++
// Copyright (c) 2010-2020, 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"
|
|
#ifdef MFEM_USE_SUNDIALS
|
|
#include <nvector/nvector_serial.h>
|
|
#endif
|
|
#include <cmath>
|
|
#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 double *v, const int n);
|
|
|
|
/// Define a shortcut for std::numeric_limits<double>::infinity()
|
|
inline double infinity()
|
|
{
|
|
return std::numeric_limits<double>::infinity();
|
|
}
|
|
|
|
/// Vector data type.
|
|
class Vector
|
|
{
|
|
protected:
|
|
|
|
Memory<double> data;
|
|
int size;
|
|
|
|
public:
|
|
|
|
/// Default constructor for Vector. Sets size = 0 and data = NULL.
|
|
Vector() { data.Reset(); size = 0; }
|
|
|
|
/// Copy constructor. Allocates a new data array and copies the data.
|
|
Vector(const Vector &);
|
|
|
|
/// @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(double *_data, int _size)
|
|
{ data.Wrap(_data, _size, false); size = _size; }
|
|
|
|
/// Create a Vector of size @a size_ using MemoryType @a mt.
|
|
Vector(int size_, MemoryType mt)
|
|
: data(size_, mt), size(size_) { }
|
|
|
|
/// 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. */
|
|
void UseDevice(bool use_dev) const { data.UseDevice(use_dev); }
|
|
|
|
/// Return the device flag of the Memory object used by the Vector
|
|
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, Vector &v) { SetSize(s, v.GetMemory().GetMemoryType()); }
|
|
|
|
/// Set the Vector data.
|
|
/// @warning This method should be called only when OwnsData() is false.
|
|
void SetData(double *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(double *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(double *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.
|
|
@sa NewDataAndSize(). */
|
|
inline void NewMemoryAndSize(const Memory<double> &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();
|
|
|
|
/// 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 double *GetData() const
|
|
{ return const_cast<double*>((const double*)data); }
|
|
|
|
/// Conversion to `double *`.
|
|
/** @note This conversion function makes it possible to use [] for indexing
|
|
in addition to the overloaded operator()(int). */
|
|
inline operator double *() { return data; }
|
|
|
|
/// Conversion to `const double *`.
|
|
/** @note This conversion function makes it possible to use [] for indexing
|
|
in addition to the overloaded operator()(int). */
|
|
inline operator const double *() const { return data; }
|
|
|
|
/// Return a reference to the Memory object used by the Vector.
|
|
Memory<double> &GetMemory() { return data; }
|
|
|
|
/** @brief Return a reference to the Memory object used by the Vector, const
|
|
version. */
|
|
const Memory<double> &GetMemory() const { return data; }
|
|
|
|
/// Update the memory location of the vector to match @a v.
|
|
void SyncMemory(const Vector &v) { GetMemory().Sync(v.GetMemory()); }
|
|
|
|
/// Update the alias memory location of the vector to match @a v.
|
|
void SyncAliasMemory(const Vector &v)
|
|
{ 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(double **p)
|
|
{ *p = data; data.Reset(); size = 0; }
|
|
|
|
/// Changes the ownership of the data; after the call the Vector is empty
|
|
inline double *StealData() { double *p; StealData(&p); return p; }
|
|
|
|
/// Access Vector entries. Index i = 0 .. size-1.
|
|
double &Elem(int i);
|
|
|
|
/// Read only access to Vector entries. Index i = 0 .. size-1.
|
|
const double &Elem(int i) const;
|
|
|
|
/// Access Vector entries using () for 0-based indexing.
|
|
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
|
|
inline double &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 double &operator()(int i) const;
|
|
|
|
/// Dot product with a `double *` array.
|
|
double operator*(const double *) const;
|
|
|
|
/// Return the inner-product.
|
|
double operator*(const Vector &v) const;
|
|
|
|
/// Copy Size() entries from @a v.
|
|
Vector &operator=(const double *v);
|
|
|
|
/// Copy assignment.
|
|
/** @note Defining this method overwrites the implicitly defined copy
|
|
assignment operator. */
|
|
Vector &operator=(const Vector &v);
|
|
|
|
/// Redefine '=' for vector = constant.
|
|
Vector &operator=(double value);
|
|
|
|
Vector &operator*=(double c);
|
|
|
|
Vector &operator/=(double c);
|
|
|
|
Vector &operator-=(double c);
|
|
|
|
Vector &operator-=(const Vector &v);
|
|
|
|
Vector &operator+=(const Vector &v);
|
|
|
|
/// (*this) += a * Va
|
|
Vector &Add(const double a, const Vector &Va);
|
|
|
|
/// (*this) = a * x
|
|
Vector &Set(const double a, const Vector &x);
|
|
|
|
void SetVector(const Vector &v, int offset);
|
|
|
|
/// (*this) = -(*this)
|
|
void Neg();
|
|
|
|
/// 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, double alpha, const Vector &v2, Vector &v);
|
|
|
|
/// z = a * (x + y)
|
|
friend void add(const double a, const Vector &x, const Vector &y, Vector &z);
|
|
|
|
/// z = a * x + b * y
|
|
friend void add(const double a, const Vector &x,
|
|
const double 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 double a, const Vector &x,
|
|
const Vector &y, Vector &z);
|
|
|
|
/// 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, double *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 double 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, double *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, double *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 double 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 double 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;
|
|
|
|
/// Set random values in the vector.
|
|
void Randomize(int seed = 0);
|
|
/// Returns the l2 norm of the vector.
|
|
double Norml2() const;
|
|
/// Returns the l_infinity norm of the vector.
|
|
double Normlinf() const;
|
|
/// Returns the l_1 norm of the vector.
|
|
double Norml1() const;
|
|
/// Returns the l_p norm of the vector.
|
|
double Normlp(double p) const;
|
|
/// Returns the maximal element of the vector.
|
|
double Max() const;
|
|
/// Returns the minimal element of the vector.
|
|
double Min() const;
|
|
/// Return the sum of the vector entries
|
|
double Sum() const;
|
|
/// Compute the square of the Euclidean distance to another vector.
|
|
inline double DistanceSquaredTo(const double *p) const;
|
|
/// Compute the Euclidean distance to another vector.
|
|
inline double DistanceTo(const double *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(data, size); }
|
|
|
|
/// Destroys vector.
|
|
virtual ~Vector();
|
|
|
|
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
|
|
const double *Read(bool on_dev = true) const
|
|
{ return mfem::Read(data, size, on_dev); }
|
|
|
|
/// Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
|
|
const double *HostRead() const
|
|
{ return mfem::Read(data, size, false); }
|
|
|
|
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
|
|
double *Write(bool on_dev = true)
|
|
{ return mfem::Write(data, size, on_dev); }
|
|
|
|
/// Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
|
|
double *HostWrite()
|
|
{ return mfem::Write(data, size, false); }
|
|
|
|
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
|
|
double *ReadWrite(bool on_dev = true)
|
|
{ return mfem::ReadWrite(data, size, on_dev); }
|
|
|
|
/// Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
|
|
double *HostReadWrite()
|
|
{ return mfem::ReadWrite(data, size, false); }
|
|
|
|
#ifdef MFEM_USE_SUNDIALS
|
|
/// Construct a wrapper Vector from SUNDIALS N_Vector.
|
|
explicit Vector(N_Vector nv);
|
|
|
|
/// Return a new wrapper SUNDIALS N_Vector of type SUNDIALS_NVEC_SERIAL.
|
|
/** The returned N_Vector must be destroyed by the caller. */
|
|
virtual N_Vector ToNVector() { return N_VMake_Serial(Size(), GetData()); }
|
|
|
|
/** @brief Update an existing wrapper SUNDIALS N_Vector to point to this
|
|
Vector. */
|
|
virtual void ToNVector(N_Vector &nv);
|
|
#endif
|
|
};
|
|
|
|
// Inline methods
|
|
|
|
inline bool IsFinite(const double &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 double *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)
|
|
{
|
|
if (s > 0)
|
|
{
|
|
size = s;
|
|
data.New(s);
|
|
}
|
|
else
|
|
{
|
|
size = 0;
|
|
data.Reset();
|
|
}
|
|
}
|
|
|
|
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<double> &mem, int s,
|
|
bool own_mem)
|
|
{
|
|
data.Delete();
|
|
size = s;
|
|
data = mem;
|
|
if (!own_mem) { data.ClearOwnerFlags(); }
|
|
}
|
|
|
|
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 double &Vector::operator()(int i)
|
|
{
|
|
MFEM_ASSERT(data && i >= 0 && i < size,
|
|
"index [" << i << "] is out of range [0," << size << ")");
|
|
|
|
return data[i];
|
|
}
|
|
|
|
inline const double &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 double DistanceSquared(const double *x, const double *y, const int n)
|
|
{
|
|
double d = 0.0;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
d += (x[i]-y[i])*(x[i]-y[i]);
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
inline double Distance(const double *x, const double *y, const int n)
|
|
{
|
|
return std::sqrt(DistanceSquared(x, y, n));
|
|
}
|
|
|
|
inline double Vector::DistanceSquaredTo(const double *p) const
|
|
{
|
|
return DistanceSquared(data, p, size);
|
|
}
|
|
|
|
inline double Vector::DistanceTo(const double *p) const
|
|
{
|
|
return Distance(data, p, 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 double 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 double InnerProduct(MPI_Comm comm, const Vector &x, const Vector &y)
|
|
{
|
|
double loc_prod = x * y;
|
|
double glb_prod;
|
|
MPI_Allreduce(&loc_prod, &glb_prod, 1, MPI_DOUBLE, MPI_SUM, comm);
|
|
return glb_prod;
|
|
}
|
|
#endif
|
|
|
|
} // namespace mfem
|
|
|
|
#endif
|