// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at // the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights // reserved. See file COPYRIGHT for details. // // This file is part of the MFEM library. For more information and source code // availability see http://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the GNU Lesser General Public License (as published by the Free // Software Foundation) version 2.1 dated February 1999. // Implementation of data type vector #include "../general/okina.hpp" #include "device.hpp" #include "vector.hpp" #if defined(MFEM_USE_SUNDIALS) && defined(MFEM_USE_MPI) #include #include #endif #include #include #include #include #include #include namespace mfem { void Vector::Push() const { mm::push(data, size*sizeof(double)); } void Vector::Pull() const { mm::pull(data, size*sizeof(double)); } Vector::Vector(const Vector &v) { int s = v.Size(); if (s > 0) { MFEM_ASSERT(v.data, "invalid source vector"); allocsize = size = s; data = mm::malloc(s); mm::memcpy(data, v.data, sizeof(double)*s); } else { allocsize = size = 0; data = NULL; } } void Vector::Load(std::istream **in, int np, int *dim) { int i, j, s; s = 0; for (i = 0; i < np; i++) { s += dim[i]; } SetSize(s); int p = 0; for (i = 0; i < np; i++) for (j = 0; j < dim[i]; j++) { *in[i] >> data[p++]; } } void Vector::Load(std::istream &in, int Size) { SetSize(Size); for (int i = 0; i < size; i++) { in >> data[i]; } } double &Vector::Elem(int i) { return operator()(i); } const double &Vector::Elem(int i) const { return operator()(i); } double Vector::operator*(const double *v) const { double prod = Dot(size, data, v); return prod; } double Vector::operator*(const Vector &v) const { #ifdef MFEM_DEBUG if (v.size != size) { mfem_error("Vector::operator*(const Vector &) const"); } #endif return operator*(v.data); } Vector &Vector::operator=(const double *v) { if (data != v) { MFEM_ASSERT(data + size <= v || v + size <= data, "Vectors overlap!"); mm::memcpy(data, v, sizeof(double)*size); } return *this; } Vector &Vector::operator=(const Vector &v) { SetSize(v.Size()); return operator=(v.data); } Vector &Vector::operator=(double value) { DeviceVector y(data, size); MFEM_FORALL(i, size, y[i] = value;); return *this; } Vector &Vector::operator*=(double c) { DeviceVector y(data, size); MFEM_FORALL(i, size, y[i] *= c;); return *this; } Vector &Vector::operator/=(double c) { const double m = 1.0/c; DeviceVector y(data, size); MFEM_FORALL(i, size, y[i] *= m;); return *this; } Vector &Vector::operator-=(double c) { DeviceVector y(data, size); MFEM_FORALL(i, size, y[i] -= c;); return *this; } Vector &Vector::operator-=(const Vector &v) { #ifdef MFEM_DEBUG if (size != v.size) { mfem_error("Vector::operator-=(const Vector &)"); } #endif const int N = size; DeviceVector y(data, N); const DeviceVector x(v, N); MFEM_FORALL(i, N, y[i] -= x[i];); return *this; } Vector &Vector::operator+=(const Vector &v) { #ifdef MFEM_DEBUG if (size != v.size) { mfem_error("Vector::operator+=(const Vector &)"); } #endif const int N = size; DeviceVector y(data, N); const DeviceVector x(v, N); MFEM_FORALL(i, N, y[i] += x[i];); return *this; } Vector &Vector::Add(const double a, const Vector &Va) { #ifdef MFEM_DEBUG if (size != Va.size) { mfem_error("Vector::Add(const double, const Vector &)"); } #endif if (a != 0.0) { const int N = size; DeviceVector y(data, N); const DeviceVector x(Va, N); MFEM_FORALL(i, N, y[i] += a * x[i];); } return *this; } Vector &Vector::Set(const double a, const Vector &Va) { #ifdef MFEM_DEBUG if (size != Va.size) { mfem_error("Vector::Set(const double, const Vector &)"); } #endif const int N = size; DeviceVector y(data, N); const DeviceVector x(Va, N); MFEM_FORALL(i, N, y[i] = a * x[i];); return *this; } void Vector::SetVector(const Vector &v, int offset) { MFEM_GPU_CANNOT_PASS; int vs = v.Size(); double *vp = v.data, *p = data + offset; #ifdef MFEM_DEBUG if (offset+vs > size) { mfem_error("Vector::SetVector(const Vector &, int)"); } #endif for (int i = 0; i < vs; i++) { p[i] = vp[i]; } } void Vector::Neg() { DeviceVector y(data, size); MFEM_FORALL(i, size, y[i] = -y[i];); } void add(const Vector &v1, const Vector &v2, Vector &v) { #ifdef MFEM_DEBUG if (v.size != v1.size || v.size != v2.size) { mfem_error("add(Vector &v1, Vector &v2, Vector &v)"); } #endif #ifdef MFEM_USE_OPENMP MFEM_GPU_CANNOT_PASS; #pragma omp parallel for for (int i = 0; i < v.size; i++) { v.data[i] = v1.data[i] + v2.data[i]; } #else const int N = v.size; DeviceVector y(v, N); const DeviceVector x1(v1, N); const DeviceVector x2(v2, N); MFEM_FORALL(i, N, y[i] = x1[i] + x2[i];); #endif } void add(const Vector &v1, double alpha, const Vector &v2, Vector &v) { #ifdef MFEM_DEBUG if (v.size != v1.size || v.size != v2.size) { mfem_error ("add(Vector &v1, double alpha, Vector &v2, Vector &v)"); } #endif if (alpha == 0.0) { v = v1; } else if (alpha == 1.0) { add(v1, v2, v); } else { const double *v1p = v1.data, *v2p = v2.data; double *vp = v.data; const int s = v.size; #ifdef MFEM_USE_OPENMP MFEM_GPU_CANNOT_PASS; #pragma omp parallel for for (int i = 0; i < s; i++) { vp[i] = v1p[i] + alpha*v2p[i]; } #endif const int N = s; DeviceVector d_z(vp, N); const DeviceVector d_x(v1p, N); const DeviceVector d_y(v2p, N); MFEM_FORALL(i, N, d_z[i] = d_x[i] + alpha * d_y[i];); } } void add(const double a, const Vector &x, const Vector &y, Vector &z) { #ifdef MFEM_DEBUG if (x.size != y.size || x.size != z.size) mfem_error ("add(const double a, const Vector &x, const Vector &y," " Vector &z)"); #endif if (a == 0.0) { z = 0.0; } else if (a == 1.0) { add(x, y, z); } else { const double *xp = x.data; const double *yp = y.data; double *zp = z.data; const int s = x.size; #ifdef MFEM_USE_OPENMP #pragma omp parallel for for (int i = 0; i < s; i++) { zp[i] = a * (xp[i] + yp[i]); } #endif DeviceVector z(zp, s); const DeviceVector x(xp, s); const DeviceVector y(yp, s); MFEM_FORALL(i, s, z[i] = a * (x[i] + y[i]);); } } void add(const double a, const Vector &x, const double b, const Vector &y, Vector &z) { #ifdef MFEM_DEBUG if (x.size != y.size || x.size != z.size) mfem_error("add(const double a, const Vector &x,\n" " const double b, const Vector &y, Vector &z)"); #endif if (a == 0.0) { z.Set(b, y); } else if (b == 0.0) { z.Set(a, x); } else if (a == 1.0) { add(x, b, y, z); } else if (b == 1.0) { add(y, a, x, z); } else if (a == b) { add(a, x, y, z); } else { const double *xp = x.data; const double *yp = y.data; double *zp = z.data; const int s = x.size; #ifdef MFEM_USE_OPENMP #pragma omp parallel for for (int i = 0; i < s; i++) { zp[i] = a * xp[i] + b * yp[i]; } #endif DeviceVector z(zp, s); const DeviceVector x(xp, s); const DeviceVector y(yp, s); MFEM_FORALL(i, s, z[i] = a * x[i] + b * y[i];); } } void subtract(const Vector &x, const Vector &y, Vector &z) { #ifdef MFEM_DEBUG if (x.size != y.size || x.size != z.size) { mfem_error ("subtract(const Vector &, const Vector &, Vector &)"); } #endif const double *xp = x.data; const double *yp = y.data; double *zp = z.data; const int s = x.size; #ifdef MFEM_USE_OPENMP MFEM_GPU_CANNOT_PASS; #pragma omp parallel for for (int i = 0; i < s; i++) { zp[i] = xp[i] - yp[i]; } #endif DeviceVector zd(zp, s); const DeviceVector xd(xp, s); const DeviceVector yd(yp, s); MFEM_FORALL(i, s, zd[i] = xd[i] - yd[i];); } void subtract(const double a, const Vector &x, const Vector &y, Vector &z) { #ifdef MFEM_DEBUG if (x.size != y.size || x.size != z.size) mfem_error("subtract(const double a, const Vector &x," " const Vector &y, Vector &z)"); #endif if (a == 0.) { z = 0.; } else if (a == 1.) { subtract(x, y, z); } else { const double *xp = x.data; const double *yp = y.data; double *zp = z.data; const int s = x.size; #ifdef MFEM_USE_OPENMP #pragma omp parallel for for (int i = 0; i < s; i++) { zp[i] = a * (xp[i] - yp[i]); } #endif DeviceVector zd(zp, s); const DeviceVector xd(xp, s); const DeviceVector yd(yp, s); MFEM_FORALL(i, s, zd[i] = a * (xd[i] - yd[i]);); } } void Vector::median(const Vector &lo, const Vector &hi) { const int N = size; DeviceVector v(data, N); const DeviceVector l(lo, N); const DeviceVector h(hi, N); MFEM_FORALL(i, N, { if (v[i] < l[i]) { v[i] = l[i]; } else if (v[i] > h[i]) { v[i] = h[i]; } }); } static void GetSubvector(const int N, double *y, const double *x, const int* dofs) { DeviceVector d_y(y, N); const DeviceVector d_x(x, N); const DeviceArray d_dofs(dofs, N); MFEM_FORALL(i, N, { const int dof_i = d_dofs[i]; d_y[i] = dof_i >= 0 ? d_x[dof_i] : -d_x[-dof_i-1]; }); } void Vector::GetSubVector(const Array &dofs, Vector &elemvect) const { const int n = dofs.Size(); elemvect.SetSize(n); mfem::GetSubvector(n, elemvect, data, dofs); } void Vector::GetSubVector(const Array &dofs, double *elem_data) const { mfem::GetSubvector(dofs.Size(), elem_data, data,dofs); } static void SetSubvector(const int N, double* y, const double d, const int* dofs) { DeviceVector d_y(y,N); const DeviceArray d_dofs(dofs,N); MFEM_FORALL(i, N, { const int j = d_dofs[i]; if (j >= 0) { d_y[j] = d; } else { d_y[-1-j] = -d; } }); } static void SetSubvector(const int N, double *y, const double *x, const int* dofs) { DeviceVector d_y(y,N); const DeviceVector d_x(x,N); const DeviceArray d_dofs(dofs,N); MFEM_FORALL(i, N, { const int dof_i = d_dofs[i]; if (dof_i >= 0) { d_y[dof_i] = d_x[i]; } else { d_y[-1-dof_i] = -d_x[i]; } }); } void Vector::SetSubVector(const Array &dofs, const double value) { mfem::SetSubvector(dofs.Size(), data, value, dofs); } void Vector::SetSubVector(const Array &dofs, const Vector &elemvect) { mfem::SetSubvector(dofs.Size(), data, elemvect, dofs); } void Vector::SetSubVector(const Array &dofs, double *elem_data) { mfem::SetSubvector(dofs.Size(), data, elem_data, dofs); } static void AddElement(const int N, const int *dofs, const double *x, double *y) { DeviceVector d_y(y,N); const DeviceVector d_x(x,N); const DeviceArray d_dofs(dofs,N); MFEM_FORALL(i, N, { const int j = d_dofs[i]; if (j >= 0) d_y[j] += d_x[i]; else { d_y[-1-j] -= d_x[i]; } }); } void Vector::AddElementVector(const Array &dofs, const Vector &elemvect) { MFEM_ASSERT(dofs.Size() == elemvect.Size(), "Size mismatch: " "length of dofs is " << dofs.Size() << ", length of elemvect is " << elemvect.Size()); mfem::AddElement(dofs.Size(), dofs, elemvect.GetData(), data); } void Vector::AddElementVector(const Array &dofs, double *elem_data) { mfem::AddElement(dofs.Size(), dofs, elem_data, data); } void Vector::AddElementVector(const Array &dofs, const double a, const Vector &elemvect) { const int N = dofs.Size(); const double alpha = a; DeviceVector d_y(data, N); const DeviceVector d_x(elemvect, N); const DeviceArray d_dofs(dofs, N); MFEM_FORALL(i, N, { const int j = d_dofs[i]; if (j >= 0) d_y[j] += alpha * d_x[i]; else { d_y[-1-j] -= alpha * d_x[i]; } }); } void Vector::SetSubVectorComplement(const Array &dofs, const double val) { Vector dofs_vals; GetSubVector(dofs, dofs_vals); operator=(val); SetSubVector(dofs, dofs_vals); } void Vector::Print(std::ostream &out, int width) const { if (!size) { return; } Pull(); for (int i = 0; 1; ) { out << data[i]; i++; if (i == size) { break; } if ( i % width == 0 ) { out << '\n'; } else { out << ' '; } } out << '\n'; } void Vector::Print_HYPRE(std::ostream &out) const { int i; std::ios::fmtflags old_fmt = out.flags(); out.setf(std::ios::scientific); std::streamsize old_prec = out.precision(14); out << size << '\n'; // number of rows for (i = 0; i < size; i++) { out << data[i] << '\n'; } out.precision(old_prec); out.flags(old_fmt); } void Vector::Randomize(int seed) { MFEM_GPU_CANNOT_PASS; // static unsigned int seed = time(0); const double max = (double)(RAND_MAX) + 1.; if (seed == 0) { seed = (int)time(0); } // srand(seed++); srand((unsigned)seed); for (int i = 0; i < size; i++) { data[i] = std::abs(rand()/max); } } double Vector::Norml2() const { MFEM_GPU_CANNOT_PASS; // Scale entries of Vector on the fly, using algorithms from // std::hypot() and LAPACK's drm2. This scaling ensures that the // argument of each call to std::pow is <= 1 to avoid overflow. if (0 == size) { return 0.0; } // end if 0 == size if (1 == size) { return std::abs(data[0]); } // end if 1 == size double scale = 0.0; double sum = 0.0; for (int i = 0; i < size; i++) { if (data[i] != 0.0) { const double absdata = std::abs(data[i]); if (scale <= absdata) { const double sqr_arg = scale / absdata; sum = 1.0 + sum * (sqr_arg * sqr_arg); scale = absdata; continue; } // end if scale <= absdata const double sqr_arg = absdata / scale; sum += (sqr_arg * sqr_arg); // else scale > absdata } // end if data[i] != 0 } return scale * std::sqrt(sum); } double Vector::Normlinf() const { MFEM_GPU_CANNOT_PASS; double max = 0.0; for (int i = 0; i < size; i++) { max = std::max(std::abs(data[i]), max); } return max; } double Vector::Norml1() const { MFEM_GPU_CANNOT_PASS; double sum = 0.0; for (int i = 0; i < size; i++) { sum += std::abs(data[i]); } return sum; } double Vector::Normlp(double p) const { MFEM_GPU_CANNOT_PASS; MFEM_ASSERT(p > 0.0, "Vector::Normlp"); if (p == 1.0) { return Norml1(); } if (p == 2.0) { return Norml2(); } if (p < infinity()) { // Scale entries of Vector on the fly, using algorithms from // std::hypot() and LAPACK's drm2. This scaling ensures that the // argument of each call to std::pow is <= 1 to avoid overflow. if (0 == size) { return 0.0; } // end if 0 == size if (1 == size) { return std::abs(data[0]); } // end if 1 == size double scale = 0.0; double sum = 0.0; for (int i = 0; i < size; i++) { if (data[i] != 0.0) { const double absdata = std::abs(data[i]); if (scale <= absdata) { sum = 1.0 + sum * std::pow(scale / absdata, p); scale = absdata; continue; } // end if scale <= absdata sum += std::pow(absdata / scale, p); // else scale > absdata } // end if data[i] != 0 } return scale * std::pow(sum, 1.0/p); } // end if p < infinity() return Normlinf(); // else p >= infinity() } double Vector::Max() const { MFEM_GPU_CANNOT_PASS; double max = data[0]; for (int i = 1; i < size; i++) if (data[i] > max) { max = data[i]; } return max; } double Vector::Min() const { MFEM_GPU_CANNOT_PASS; double min = data[0]; for (int i = 1; i < size; i++) if (data[i] < min) { min = data[i]; } return min; } double Vector::Sum() const { MFEM_GPU_CANNOT_PASS; double sum = 0.0; for (int i = 0; i < size; i++) { sum += data[i]; } return sum; } #ifdef __NVCC__ static __global__ void cuKernelMin(const int N, double *gdsr, const double *x) { __shared__ double s_min[MFEM_BLOCKS]; const int n = blockDim.x*blockIdx.x + threadIdx.x; if (n>=N) { return; } const int bid = blockIdx.x; const int tid = threadIdx.x; const int bbd = bid*blockDim.x; const int rid = bbd+tid; s_min[tid] = x[n]; for (int workers=blockDim.x>>1; workers>0; workers>>=1) { __syncthreads(); if (tid >= workers) { continue; } if (rid >= N) { continue; } const int dualTid = tid + workers; if (dualTid >= N) { continue; } const int rdd = bbd+dualTid; if (rdd >= N) { continue; } if (dualTid >= blockDim.x) { continue; } s_min[tid] = fmin(s_min[tid], s_min[dualTid]); } if (tid==0) { gdsr[bid] = s_min[0]; } } static double cuVectorMin(const int N, const double *X) { const DeviceVector x(X, N); const int tpb = MFEM_BLOCKS; const int blockSize = MFEM_BLOCKS; const int gridSize = (N+blockSize-1)/blockSize; const int min_sz = (N%tpb)==0? (N/tpb) : (1+N/tpb); const int bytes = min_sz*sizeof(double); static double *h_min = NULL; if (!h_min) { h_min = (double*)calloc(min_sz,sizeof(double)); } static CUdeviceptr gdsr = (CUdeviceptr) NULL; if (!gdsr) { ::cuMemAlloc(&gdsr,bytes); } cuKernelMin<<>>(N, (double*)gdsr, x); cuCheck(cudaGetLastError()); ::cuMemcpy((CUdeviceptr)h_min,(CUdeviceptr)gdsr,bytes); double min = std::numeric_limits::infinity(); for (int i=0; i=N) { return; } const int bid = blockIdx.x; const int tid = threadIdx.x; const int bbd = bid*blockDim.x; const int rid = bbd+tid; s_dot[tid] = x[n] * y[n]; for (int workers=blockDim.x>>1; workers>0; workers>>=1) { __syncthreads(); if (tid >= workers) { continue; } if (rid >= N) { continue; } const int dualTid = tid + workers; if (dualTid >= N) { continue; } const int rdd = bbd+dualTid; if (rdd >= N) { continue; } if (dualTid >= blockDim.x) { continue; } s_dot[tid] += s_dot[dualTid]; } if (tid==0) { gdsr[bid] = s_dot[0]; } } static double cuVectorDot(const int N, const double *X, const double *Y) { const DeviceVector x(X, N); const DeviceVector y(Y, N); static int dot_block_sz = 0; const int tpb = MFEM_BLOCKS; const int blockSize = MFEM_BLOCKS; const int gridSize = (N+blockSize-1)/blockSize; const int dot_sz = (N%tpb)==0? (N/tpb) : (1+N/tpb); const int bytes = dot_sz*sizeof(double); static double *h_dot = NULL; if (!h_dot or dot_block_sz!=dot_sz) { if (h_dot) { free(h_dot); } h_dot = (double*)calloc(dot_sz,sizeof(double)); } static CUdeviceptr gdsr = (CUdeviceptr) NULL; if (!gdsr or dot_block_sz!=dot_sz) { if (gdsr) { cuCheck(::cuMemFree(gdsr)); } cuCheck(::cuMemAlloc(&gdsr,bytes)); } if (dot_block_sz!=dot_sz) { dot_block_sz = dot_sz; } cuKernelDot<<>>(N, (double*)gdsr, x, y); cuCheck(cudaGetLastError()); cuCheck(::cuMemcpy((CUdeviceptr)h_dot,(CUdeviceptr)gdsr,bytes)); double dot = 0.0; for (int i=0; i::infinity(); for (int i=0; ilocal_vector; SetDataAndSize(hpv_local->data, hpv_local->size); break; } #endif default: MFEM_ABORT("N_Vector type " << nvid << " is not supported"); } } void Vector::ToNVector(N_Vector &nv) { MFEM_ASSERT(nv, "N_Vector handle is NULL"); N_Vector_ID nvid = N_VGetVectorID(nv); switch (nvid) { case SUNDIALS_NVEC_SERIAL: MFEM_ASSERT(NV_OWN_DATA_S(nv) == SUNFALSE, "invalid serial N_Vector"); NV_DATA_S(nv) = data; NV_LENGTH_S(nv) = size; break; #ifdef MFEM_USE_MPI case SUNDIALS_NVEC_PARALLEL: MFEM_ASSERT(NV_OWN_DATA_P(nv) == SUNFALSE, "invalid parallel N_Vector"); NV_DATA_P(nv) = data; NV_LOCLENGTH_P(nv) = size; break; case SUNDIALS_NVEC_PARHYP: { hypre_Vector *hpv_local = N_VGetVector_ParHyp(nv)->local_vector; MFEM_ASSERT(hpv_local->owns_data == false, "invalid hypre N_Vector"); hpv_local->data = data; hpv_local->size = size; break; } #endif default: MFEM_ABORT("N_Vector type " << nvid << " is not supported"); } } #endif // MFEM_USE_SUNDIALS }