diff --git a/opm/simulators/linalg/HyprePreconditioner.hpp b/opm/simulators/linalg/HyprePreconditioner.hpp index d1f9cfe30c..e36f3d870a 100644 --- a/opm/simulators/linalg/HyprePreconditioner.hpp +++ b/opm/simulators/linalg/HyprePreconditioner.hpp @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include @@ -36,6 +38,9 @@ #include #include +#include +#include +#include namespace Hypre { @@ -49,7 +54,7 @@ namespace Hypre { * @tparam X The type of the update vector. * @tparam Y The type of the defect vector. */ -template +template class HyprePreconditioner : public Dune::PreconditionerWithUpdate { public: @@ -61,15 +66,30 @@ public: * @param A The matrix for which the preconditioner is constructed. * @param prm The property tree containing configuration parameters. */ - HyprePreconditioner (const M& A, const Opm::PropertyTree prm) - : A_(A) + // template , ::Opm::PropertyTree>> + // HyprePreconditioner (const M& A, const Prm& prm) + // : HyprePreconditioner(A, prm, Dune::Amg::SequentialInformation()) + // { + // //NB if this is used comm_ can never be used + // } + + HyprePreconditioner (const M& A, const Opm::PropertyTree prm,const Comm& comm) + : A_(A),comm_(comm) { OPM_TIMEBLOCK(prec_construct); - int size; - MPI_Comm_size(MPI_COMM_WORLD, &size); + int rank; + MPI_Comm mpi_comm; + if constexpr (std::is_same_v){ + mpi_comm = MPI_COMM_SELF; + }else{ + mpi_comm = comm.communicator(); + } + MPI_Comm_size(mpi_comm, &size); + MPI_Comm_rank(mpi_comm, &rank); if (size > 1) { - OPM_THROW(std::runtime_error, "HyprePreconditioner is currently only implemented for sequential runs"); + assert(size == comm.communicator().size()); + assert(rank == comm.communicator().rank()); } use_gpu_ = prm.get("use_gpu", false); @@ -121,17 +141,16 @@ public: } // Create Hypre vectors - N_ = A_.N(); - nnz_ = A_.nonzeroes(); - HYPRE_IJVectorCreate(MPI_COMM_SELF, 0, N_-1, &x_hypre_); - HYPRE_IJVectorCreate(MPI_COMM_SELF, 0, N_-1, &b_hypre_); + setupHypreParallelInfo(comm_);//create all need information for setting up the matrix + HYPRE_IJVectorCreate(mpi_comm, dof_offset_, dof_offset_ +(N_-1), &x_hypre_); + HYPRE_IJVectorCreate(mpi_comm, dof_offset_, dof_offset_ +(N_-1), &b_hypre_); HYPRE_IJVectorSetObjectType(x_hypre_, HYPRE_PARCSR); HYPRE_IJVectorSetObjectType(b_hypre_, HYPRE_PARCSR); HYPRE_IJVectorInitialize(x_hypre_); HYPRE_IJVectorInitialize(b_hypre_); // Create indices vector indices_.resize(N_); - std::iota(indices_.begin(), indices_.end(), 0); + std::iota(indices_.begin(), indices_.end(), dof_offset_); if (use_gpu_) { indices_device_ = hypre_CTAlloc(HYPRE_BigInt, N_, HYPRE_MEMORY_DEVICE); hypre_TMemcpy(indices_device_, indices_.data(), HYPRE_BigInt, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); @@ -141,7 +160,10 @@ public: } // Create Hypre matrix - HYPRE_IJMatrixCreate(MPI_COMM_SELF, 0, N_-1, 0, N_-1, &A_hypre_); + HYPRE_IJMatrixCreate(mpi_comm, + dof_offset_, dof_offset_ +(N_-1), + dof_offset_, dof_offset_ +(N_-1), + &A_hypre_); HYPRE_IJMatrixSetObjectType(A_hypre_, HYPRE_PARCSR); HYPRE_IJMatrixInitialize(A_hypre_); @@ -189,7 +211,11 @@ public: void update() override { OPM_TIMEBLOCK(prec_update); copyMatrixToHypre(); - HYPRE_BoomerAMGSetup(solver_, parcsr_A_, par_b_, par_x_); + { + OPM_TIMEBLOCK(hypre_bommer_amg_setup); + HYPRE_BoomerAMGSetup(solver_, parcsr_A_, par_b_, par_x_); + } + } /** @@ -200,7 +226,8 @@ public: * @param v The update vector. * @param d The defect vector. */ - void pre(X& /*v*/, Y& /*d*/) override { + void pre(X& v, Y& /*d*/) override { + comm_.copyOwnerToAll(v,v); // From dune: make dirichlet values consistent ?? } /** @@ -222,6 +249,9 @@ public: // Copy result back copyVectorFromHypre(v); + // NB do we need ot sync values to get correct values since a preconditioner + // consistent result (a operator apply should give unique). + comm_.copyOwnerToAll(v,v); } /** @@ -240,7 +270,8 @@ public: * @return The solver category, which is sequential. */ Dune::SolverCategory::Category category() const override { - return Dune::SolverCategory::sequential; + return std::is_same_v ? + Dune::SolverCategory::sequential : Dune::SolverCategory::overlapping; } /** @@ -257,71 +288,271 @@ public: } private: - /** - * @brief Sets up the sparsity pattern for the Hypre matrix. - * - * Allocates and initializes arrays required by Hypre. - */ - void setupSparsityPattern() { - // Allocate arrays required by Hypre - ncols_.resize(N_); - rows_.resize(N_); - cols_.resize(nnz_); - // Setup arrays and fill column indices - int pos = 0; + /** + * @brief Save all elements which should be copied to hypre in a continuous memery + */ + + void makeContinuousMatrixEntries() + { + + assert(continuous_matrix_entries_.size() == nnz_); + int nnz = 0; for (auto row = A_.begin(); row != A_.end(); ++row) { const int rowIdx = row.index(); - rows_[rowIdx] = rowIdx; - ncols_[rowIdx] = row->size(); - for (auto col = row->begin(); col != row->end(); ++col) { - cols_[pos++] = col.index(); + if (!(local_dune_to_local_hypre_[rowIdx] < 0)) { + const auto& value = *col; + continuous_matrix_entries_[nnz] = value[0][0]; + nnz++; + } } } - if (use_gpu_) { - // Allocate device arrays - ncols_device_ = hypre_CTAlloc(HYPRE_Int, N_, HYPRE_MEMORY_DEVICE); - rows_device_ = hypre_CTAlloc(HYPRE_BigInt, N_, HYPRE_MEMORY_DEVICE); - cols_device_ = hypre_CTAlloc(HYPRE_BigInt, nnz_, HYPRE_MEMORY_DEVICE); - values_device_ = hypre_CTAlloc(HYPRE_Real, nnz_, HYPRE_MEMORY_DEVICE); - - // Copy to device - hypre_TMemcpy(ncols_device_, ncols_.data(), HYPRE_Int, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); - hypre_TMemcpy(rows_device_, rows_.data(), HYPRE_BigInt, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); - hypre_TMemcpy(cols_device_, cols_.data(), HYPRE_BigInt, nnz_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); - } } /** - * @brief Copies the matrix values to the Hypre matrix. - * - * This method transfers the matrix data from the host to the Hypre matrix. - * It assumes that the values of the matrix are stored in a contiguous array. - * If GPU is used, the data is transferred to the device. + * @brief Set up helping structure for hypre in serial case */ - void copyMatrixToHypre() { - OPM_TIMEBLOCK(prec_copy_matrix); - // Get pointer to matrix values array - const HYPRE_Real* values = &(A_[0][0][0][0]); - // Indexing explanation: - // A_[0] - First row of the matrix - // [0] - First block in that row - // [0] - First row within the 1x1 block - // [0] - First column within the 1x1 block - if (use_gpu_) { - hypre_TMemcpy(values_device_, values, HYPRE_Real, nnz_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); - HYPRE_IJMatrixSetValues(A_hypre_, N_, ncols_device_, rows_device_, cols_device_, values_device_); - } - else { - HYPRE_IJMatrixSetValues(A_hypre_, N_, ncols_.data(), rows_.data(), cols_.data(), values); - } - HYPRE_IJMatrixAssemble(A_hypre_); - HYPRE_IJMatrixGetObject(A_hypre_, reinterpret_cast(&parcsr_A_)); + void setupHypreParallelInfo(const Dune::Amg::SequentialInformation& /*comm*/) + { + N_ = A_.N(); + nnz_ = A_.nonzeroes(); + + local_dune_to_local_hypre_.resize(N_); + local_dune_to_global_hypre_.resize(N_); + local_hypre_to_local_dune_.resize(N_); + for (int i = 0; i < N_; ++i) { + local_dune_to_local_hypre_[i] = i; + local_hypre_to_local_dune_[i] = i; + local_dune_to_global_hypre_[i] = i; + } + dof_offset_ = 0; + owner_first_ = true; } + + /** + * @brief Set up helping structure for hypre in parallel case + */ + + template + void setupHypreParallelInfo(const Commun& comm) + { + + const auto& collective_comm = comm.communicator(); + // make global numbering and mapping for matrix + + size_t count = 0; + local_dune_to_local_hypre_.resize(comm.indexSet().size(), -1); + local_dune_to_global_hypre_.resize(comm.indexSet().size(), -1); + + + // in case index set is not full dune we fix it + if(!(A_.N() == comm.indexSet().size())){ + // in OPM this will likely not be trigged + // ensure no holes in index sett + const_cast(comm).buildGlobalLookup(A_.N());// need? + Dune::Amg::MatrixGraph graph(const_cast(A_));// do not know why not const ref is sufficient + Dune::fillIndexSetHoles(graph, const_cast(comm)); + assert(A_.N() == comm.indexSet().size()); + } + // NB iterations in index set is not in order of indexes + // first find owners + for (const auto& ind : comm.indexSet()) { + int local_ind = ind.local().local(); + if (ind.local().attribute() == Dune::OwnerOverlapCopyAttributeSet::owner) { + local_dune_to_local_hypre_[local_ind] = 1;//count; + count += 1; + + } else { + local_dune_to_local_hypre_[local_ind] = -1; + } + } + N_ = count; + + // make order of variables + //NB owner first will fail for cprw... + bool owner_first = true; + bool visited_copy = false; + count = 0; + for(size_t i=0; i < local_dune_to_local_hypre_.size(); ++i) { + if (local_dune_to_local_hypre_[i] < 0) { + visited_copy = true; + assert(local_dune_to_local_hypre_[i] == -1); + } else { + local_dune_to_local_hypre_[i] = count; + local_hypre_to_local_dune_.push_back(i); + owner_first = owner_first && !visited_copy; + count +=1; + } + } + + //owner first need other copying of data + owner_first_ = owner_first; + if(!owner_first){// only need this storage if not owner first + continuous_vector_values_.resize(N_); + } + + // Gather the number of DOFs from each process + std::vector dof_counts_per_process(collective_comm.size()); + collective_comm.allgather(&N_, 1, dof_counts_per_process.data()); + + // Calculate our process's offset (sum of DOFs in processes before us) + dof_offset_ = std::accumulate(dof_counts_per_process.begin(), + dof_counts_per_process.begin() + collective_comm.rank(), 0); + + // Create global DOF indices by adding offset to local indices + for (size_t i = 0; i < local_dune_to_local_hypre_.size(); ++i) { + if (local_dune_to_local_hypre_[i] >= 0) { + local_dune_to_global_hypre_[i] = local_dune_to_local_hypre_[i] + dof_offset_; + }else{ + local_dune_to_global_hypre_[i] = -1; + } + } + + if(collective_comm.rank() > 0) { + assert(dof_offset_>0); + } + // communicate global numbering of dofs + comm.copyOwnerToAll(local_dune_to_global_hypre_, local_dune_to_global_hypre_); + + // calculate matrix propeties which can be used to optimize memmory + int nnz = 0; + for (auto row = A_.begin(); row != A_.end(); ++row) { + const int rowIdx = row.index(); + for (auto col = row->begin(); col != row->end(); ++col) { + if (!(local_dune_to_local_hypre_[rowIdx] < 0) ){ + nnz++; + } + } + } + nnz_ = nnz; + if(!owner_first){ + continuous_matrix_entries_.resize(nnz_); + } + } + + /** + * @brief Mapping from local dofs to hypre global dofs + * + */ + + int localToHypreGlobal(int index) + { + return local_dune_to_global_hypre_[index]; + } + + /** + * @brief Sets up the sparsity pattern for the Hypre matrix. + * + * Allocates and initializes arrays required by Hypre + * + */ + + void setupSparsityPattern() + { + // Allocate arrays required by Hypre + ncols_.resize(N_); + rows_.resize(N_); + cols_.resize(nnz_); + + // Setup arrays and fill column indices + int pos = 0; + int rowpos = 0; + for (auto row = A_.begin(); row != A_.end(); ++row) { + const int rind = row.index(); + if (local_dune_to_local_hypre_[rind] < 0) { + // This is a ghost dof, skip it + continue; + } + + const int local_rowIdx = local_dune_to_local_hypre_[rind]; + const int global_rowIdx = localToHypreGlobal(rind); + assert(local_rowIdx == rowpos); + if (owner_first_) { + assert(rind == local_rowIdx); // valid for owner first + } + assert(global_rowIdx >= 0); + rows_[local_rowIdx] = global_rowIdx; + ncols_[local_rowIdx] = row->size(); + + for (auto col = row->begin(); col != row->end(); ++col) { + const int colIdx = localToHypreGlobal(col.index()); + assert(colIdx >= 0); + cols_[pos++] = colIdx; + } + rowpos++; + } + if (use_gpu_) { + // Allocate device arrays + ncols_device_ = hypre_CTAlloc(HYPRE_Int, N_, HYPRE_MEMORY_DEVICE); + rows_device_ = hypre_CTAlloc(HYPRE_BigInt, N_, HYPRE_MEMORY_DEVICE); + cols_device_ = hypre_CTAlloc(HYPRE_BigInt, nnz_, HYPRE_MEMORY_DEVICE); + values_device_ = hypre_CTAlloc(HYPRE_Real, nnz_, HYPRE_MEMORY_DEVICE); + + // Copy to device + hypre_TMemcpy(ncols_device_, ncols_.data(), HYPRE_Int, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + hypre_TMemcpy(rows_device_, rows_.data(), HYPRE_BigInt, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + hypre_TMemcpy(cols_device_, cols_.data(), HYPRE_BigInt, nnz_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + } + } + + /** + * @brief Copies the matrix values to the Hypre matrix. + * + * This method transfers the matrix data from the host to the Hypre matrix. + * It assumes that the values of the matrix are stored in a contiguous array. + * If GPU is used, the data is transferred to the device. + */ + void copyMatrixToHypre() + { + OPM_TIMEBLOCK(prec_copy_matrix); + // Get pointer to matrix values array + // Indexing explanation: + // A_[0] - First row of the matrix + // [0] - First block in that row + // [0] - First row within the 1x1 block + // [0] - First column within the 1x1 block + + if (use_gpu_) { + if (owner_first_) { + const HYPRE_Real* values = &(A_[0][0][0][0]); + hypre_TMemcpy(values_device_, values, HYPRE_Real, nnz_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + }else{ + // need a temp storage since matrix elements which should be copied is not continous in memory + this->makeContinuousMatrixEntries(); + const HYPRE_Real* values = &(continuous_matrix_entries_[0]); + hypre_TMemcpy(values_device_, values, HYPRE_Real, nnz_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + } + HYPRE_IJMatrixSetValues(A_hypre_, N_, ncols_device_, rows_device_, cols_device_, values_device_); + } else { + if (owner_first_) { + const HYPRE_Real* values = &(A_[0][0][0][0]); + HYPRE_IJMatrixSetValues(A_hypre_, N_, ncols_.data(), rows_.data(), cols_.data(), values); + } else { + this->makeContinuousMatrixEntries(); + const HYPRE_Real* values = &(continuous_matrix_entries_[0]); + HYPRE_IJMatrixSetValues(A_hypre_, N_, ncols_.data(), rows_.data(), cols_.data(), values); + } + } + + HYPRE_IJMatrixAssemble(A_hypre_); + HYPRE_IJMatrixGetObject(A_hypre_, reinterpret_cast(&parcsr_A_)); + } + + + void setContinuousVectorForHypre(const X& v) + { + assert(continuous_vector_values_.size() == N_); + // set v values solution vectors + for (size_t i = 0; i < local_hypre_to_local_dune_.size(); ++i) { + continuous_vector_values_[i] = v[local_hypre_to_local_dune_[i]][0]; + } + } + + /** * @brief Copies vectors to the Hypre format. * @@ -331,21 +562,41 @@ private: * @param v The update vector. * @param d The defect vector. */ - void copyVectorsToHypre(const X& v, const Y& d) { + void copyVectorsToHypre(const X& v, const Y& d) + { OPM_TIMEBLOCK(prec_copy_vectors_to_hypre); - const HYPRE_Real* x_vals = &(v[0][0]); - const HYPRE_Real* b_vals = &(d[0][0]); - if (use_gpu_) { - hypre_TMemcpy(x_values_device_, x_vals, HYPRE_Real, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); - hypre_TMemcpy(b_values_device_, b_vals, HYPRE_Real, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); - + if (owner_first_) { + const HYPRE_Real* x_vals = &(v[0][0]); + const HYPRE_Real* b_vals = &(d[0][0]); + hypre_TMemcpy(x_values_device_, x_vals, HYPRE_Real, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + hypre_TMemcpy(b_values_device_, b_vals, HYPRE_Real, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + } else { + // continuous_vector_values_ as continuous storage + this->setContinuousVectorForHypre(v); + const HYPRE_Real* x_vals = &(continuous_vector_values_[0]); + hypre_TMemcpy(x_values_device_, x_vals, HYPRE_Real, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + this->setContinuousVectorForHypre(d); + const HYPRE_Real* b_vals = &(continuous_vector_values_[0]); + hypre_TMemcpy(b_values_device_, b_vals, HYPRE_Real, N_, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST); + } HYPRE_IJVectorSetValues(x_hypre_, N_, indices_device_, x_values_device_); HYPRE_IJVectorSetValues(b_hypre_, N_, indices_device_, b_values_device_); - } - else { - HYPRE_IJVectorSetValues(x_hypre_, N_, indices_.data(), x_vals); - HYPRE_IJVectorSetValues(b_hypre_, N_, indices_.data(), b_vals); + } else { + if (owner_first_) { + const HYPRE_Real* x_vals = &(v[0][0]); + const HYPRE_Real* b_vals = &(d[0][0]); + HYPRE_IJVectorSetValues(x_hypre_, N_, indices_.data(), x_vals); + HYPRE_IJVectorSetValues(b_hypre_, N_, indices_.data(), b_vals); + } else { + // continuous_vector_values_ as continuous storage + this->setContinuousVectorForHypre(v);// setting continuous_vector_values_ + const HYPRE_Real* x_vals = &(continuous_vector_values_[0]); + HYPRE_IJVectorSetValues(x_hypre_, N_, indices_.data(), x_vals); + this->setContinuousVectorForHypre(d);// setting continuous_vector_values_ + const HYPRE_Real* b_vals = &(continuous_vector_values_[0]); + HYPRE_IJVectorSetValues(b_hypre_, N_, indices_.data(), b_vals); + } } HYPRE_IJVectorAssemble(x_hypre_); @@ -354,6 +605,18 @@ private: HYPRE_IJVectorGetObject(b_hypre_, reinterpret_cast(&par_b_)); } + /** + * @brief Set dune vector from continous vector storage + */ + + void setDuneVectorFromContinuousVector(X& v) + { + for (size_t i = 0; i < local_hypre_to_local_dune_.size(); ++i) { + v[local_hypre_to_local_dune_[i]][0] = continuous_vector_values_[i]; + } + } + + /** * @brief Copies the solution vector from Hypre. * @@ -362,21 +625,47 @@ private: * * @param v The update vector. */ - void copyVectorFromHypre(X& v) { + void copyVectorFromHypre(X& v) + { OPM_TIMEBLOCK(prec_copy_vector_from_hypre); - HYPRE_Real* values = &(v[0][0]); + if (use_gpu_) { - HYPRE_IJVectorGetValues(x_hypre_, N_, indices_device_, x_values_device_); - hypre_TMemcpy(values, x_values_device_, HYPRE_Real, N_, HYPRE_MEMORY_HOST, HYPRE_MEMORY_DEVICE); - } - else { - HYPRE_IJVectorGetValues(x_hypre_, N_, indices_.data(), values); + if (owner_first_) { + HYPRE_Real* values = &(v[0][0]); + HYPRE_IJVectorGetValues(x_hypre_, N_, indices_device_, x_values_device_); + hypre_TMemcpy(values, x_values_device_, HYPRE_Real, N_, HYPRE_MEMORY_HOST, HYPRE_MEMORY_DEVICE); + } else { + HYPRE_Real* values = &(continuous_vector_values_[0]); + HYPRE_IJVectorGetValues(x_hypre_, N_, indices_device_, x_values_device_); + hypre_TMemcpy(values, x_values_device_, HYPRE_Real, N_, HYPRE_MEMORY_HOST, HYPRE_MEMORY_DEVICE); + this->setDuneVectorFromContinuousVector(v); + } + } else { + if (owner_first_) { + HYPRE_Real* values = &(v[0][0]); + HYPRE_IJVectorGetValues(x_hypre_, N_, indices_.data(), values); + } else { + HYPRE_Real* values = &(continuous_vector_values_[0]); + HYPRE_IJVectorGetValues(x_hypre_, N_, indices_.data(), values); + this->setDuneVectorFromContinuousVector(v); + } } } + const M& A_; //!< The matrix for which the preconditioner is constructed. + const Comm& comm_; //!< The communication object for parallel operations. bool use_gpu_ = false; //!< Flag indicating whether to use GPU acceleration. + // mappings between dune and hypre use int to have negative values as not owned + std::vector local_dune_to_local_hypre_; + std::vector local_dune_to_global_hypre_; + std::vector local_hypre_to_local_dune_;// will only be needed if not owner first + std::vector continuous_matrix_entries_; + std::vector continuous_vector_values_; + int dof_offset_; + bool owner_first_; + HYPRE_Solver solver_ = nullptr; //!< The Hypre solver object. HYPRE_IJMatrix A_hypre_ = nullptr; //!< The Hypre matrix object. HYPRE_ParCSRMatrix parcsr_A_ = nullptr; //!< The parallel CSR matrix object. diff --git a/opm/simulators/linalg/StandardPreconditioners_mpi.hpp b/opm/simulators/linalg/StandardPreconditioners_mpi.hpp index 6de64358de..2c0297b2b4 100644 --- a/opm/simulators/linalg/StandardPreconditioners_mpi.hpp +++ b/opm/simulators/linalg/StandardPreconditioners_mpi.hpp @@ -247,8 +247,18 @@ struct StandardPreconditioners OPM_THROW(std::invalid_argument, "Properties: No smoother with name " + smoother + "."); } }); +#if HAVE_HYPRE + if constexpr (M::block_type::rows == 1 && M::block_type::cols == 1 + && std::is_same_v) { + F::addCreator( + "hypre", [](const O& op, const P& prm, const std::function&, std::size_t, const C& comm) { + return std::make_shared>(op.getmat(), prm, comm); + }); + } +#endif } + F::addCreator("cpr", [](const O& op, const P& prm, diff --git a/opm/simulators/linalg/StandardPreconditioners_serial.hpp b/opm/simulators/linalg/StandardPreconditioners_serial.hpp index 0bc05e5a68..a380efc5ef 100644 --- a/opm/simulators/linalg/StandardPreconditioners_serial.hpp +++ b/opm/simulators/linalg/StandardPreconditioners_serial.hpp @@ -174,8 +174,8 @@ struct StandardPreconditioners) { - F::addCreator("hypre", [](const O& op, const P& prm, const std::function&, std::size_t) { - return std::make_shared>(op.getmat(), prm); + F::addCreator("hypre", [](const O& op, const P& prm, const std::function&, std::size_t) { + return std::make_shared>(op.getmat(), prm, Dune::Amg::SequentialInformation()); }); } #endif diff --git a/tests/HyprePreconditionerTestHelper.hpp b/tests/HyprePreconditionerTestHelper.hpp index af817b4bf9..2a58cc02b5 100644 --- a/tests/HyprePreconditionerTestHelper.hpp +++ b/tests/HyprePreconditionerTestHelper.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -102,7 +103,7 @@ inline void testHyprePreconditioner(bool use_gpu) prm.put("use_gpu", use_gpu ? 1 : 0); // Create preconditioner - auto prec = std::make_shared>(matrix, prm); + auto prec = std::make_shared>(matrix, prm, Dune::Amg::SequentialInformation()); // Create solver double reduction = 1e-8;