1#ifndef DUNE_COPASI_SOLVER_ISTL_UMFPACK_HH
2#define DUNE_COPASI_SOLVER_ISTL_UMFPACK_HH
4#if !HAVE_SUITESPARSE_UMFPACK
5#error Trying to use umfpack wrapper without including umfpack
10#include <dune/istl/solver.hh>
19#if DUNE_COPASI_ENABLE_PARALLEL_SORT
20#if __cpp_lib_execution >= 201603L
23#elif __has_include(<oneapi/dpl/execution>)
24#include <oneapi/dpl/execution>
25#include <oneapi/dpl/algorithm>
31template<Concept::AssembledLinearOperator O,
class Alloc = std::allocator<
void>>
32class UMFPackWapper final :
public InverseOperator<typename O::domain_type, typename O::range_type>
34 using Matrix =
typename O::matrix_type;
35 using X =
typename O::domain_type;
36 using Y =
typename O::range_type;
37 using MI = PDELab::MultiIndex<std::size_t, blockLevel<Matrix>()>;
39 static const inline std::map<std::string, double> umf_strategy_map = {
40 {
"AUTO", UMFPACK_STRATEGY_AUTO },
41 {
"UNSYMMETRIC", UMFPACK_STRATEGY_UNSYMMETRIC },
42 {
"UMFPACK_STRATEGY_SYMMETRIC", UMFPACK_STRATEGY_SYMMETRIC },
45 static const inline std::map<std::string, double> umf_ordering_map = {
46 {
"CHOLMOD", UMFPACK_ORDERING_CHOLMOD },
47 {
"AMD", UMFPACK_ORDERING_AMD },
48 {
"METIS", UMFPACK_ORDERING_METIS },
49 {
"BEST", UMFPACK_ORDERING_BEST },
50 {
"NONE", UMFPACK_ORDERING_NONE },
51#if defined(UMFPACK_VER) && (UMFPACK >= UMFPACK_VER_CODE(6, 0))
52 {
"METIS_GUARD", UMFPACK_ORDERING_METIS_GUARD },
57 using Int64Alloc =
typename std::allocator_traits<Alloc>::template rebind_alloc<int64_t>;
58 using DoubleAlloc =
typename std::allocator_traits<Alloc>::template rebind_alloc<double>;
60 struct SymbolicDeleter
62 void operator()(
void* ptr)
const { umfpack_dl_free_symbolic(&ptr); }
66 void operator()(
void* ptr)
const { umfpack_dl_free_numeric(&ptr); }
73 const ParameterTree& config,
74 const Alloc& alloc = Alloc())
89 auto set_config = [&](
auto id,
auto key) {
92 set_config(UMFPACK_PRL,
"verbosity");
93 set_config(UMFPACK_DENSE_ROW,
"dense_row");
94 set_config(UMFPACK_DENSE_COL,
"dense_col");
95 set_config(UMFPACK_PIVOT_TOLERANCE,
"pivot_tolerance");
96 set_config(UMFPACK_BLOCK_SIZE,
"block_size");
97 set_config(UMFPACK_DENSE_ROW,
"dense_row");
98 set_config(UMFPACK_ALLOC_INIT,
"alloc_init");
99 set_config(UMFPACK_IRSTEP,
"irstep");
100 set_config(UMFPACK_FIXQ,
"fixq");
101 set_config(UMFPACK_AMD_DENSE,
"amd_dense");
102 set_config(UMFPACK_SYM_PIVOT_TOLERANCE,
"sym_pivot_tolerance");
103 set_config(UMFPACK_SCALE,
"scale");
104 set_config(UMFPACK_FRONT_ALLOC_INIT,
"front_alloc_init");
105 set_config(UMFPACK_DROPTOL,
"droptol");
106 set_config(UMFPACK_AGGRESSIVE,
"aggressive");
107 set_config(UMFPACK_SINGLETONS,
"singletons");
108 _umf_control[UMFPACK_STRATEGY] = umf_strategy_map.at(config.get(
"strategy",
"AUTO"));
109 _umf_control[UMFPACK_ORDERING] = umf_ordering_map.at(config.get(
"ordering",
"CHOLMOD"));
111 if (config.get(
"report_control",
false))
114 storeFlatMatrix(op->getmat());
124 void forEachMatrixEntry(M&& mat,
const auto& call_back, MI row = MI(), MI col = MI())
126 using MD = std::decay_t<M>;
127 if constexpr (IsNumber<MD>::value) {
128 std::invoke(call_back, mat, row, col);
130 for (
auto row_it = mat.begin(); row_it != mat.end(); ++row_it) {
131 auto this_row = push_back(row, row_it.index());
132 for (
auto col_it = row_it->begin(); col_it != row_it->end(); ++col_it) {
133 forEachMatrixEntry(*col_it, call_back, this_row, push_back(col, col_it.index()));
139 void storeFlatMatrix(
const Matrix& mat)
148 using EntryAlloc =
typename std::allocator_traits<Alloc>::template rebind_alloc<Entry>;
149 EntryAlloc entryalloc(
_alloc);
153 if (blockLevel<Matrix>() > 1) {
154 forEachMatrixEntry(mat, [&](
auto&&,
auto&&,
auto&&) { ++nnz; });
156 nnz = mat.nonzeroes();
159 auto entries = std::span<Entry>(std::allocator_traits<EntryAlloc>::allocate(entryalloc, nnz), nnz);
161 forEachMatrixEntry(mat, [&](
double val, MI row, MI col) {
162 std::allocator_traits<EntryAlloc>::construct(entryalloc, &entries[nnz++], val, row, col);
166 if (blockLevel<Matrix>() > 1) {
168 auto row_compare = [](
auto lhs,
auto rhs) {
170 return std::ranges::lexicographical_compare(lhs.row, rhs.row);
174#
if DUNE_COPASI_ENABLE_PARALLEL_SORT and (__cpp_lib_execution >= 201603L)
176#elif DUNE_COPASI_ENABLE_PARALLEL_SORT and __has_include(<oneapi/dpl/execution>)
177 oneapi::dpl::execution::par,
185 std::size_t flat_row = -1;
186 for (
auto& [val, row, col] : entries) {
188 if (row != std::exchange(last_row, row))
200 auto col_row_compare = [](
auto lhs,
auto rhs) {
201 return std::ranges::lexicographical_compare(std::array{ lhs.col, lhs.row },
202 std::array{ rhs.col, rhs.row },
203 std::ranges::lexicographical_compare);
208#
if DUNE_COPASI_ENABLE_PARALLEL_SORT and (__cpp_lib_execution >= 201603L)
210#elif DUNE_COPASI_ENABLE_PARALLEL_SORT and __has_include(<oneapi/dpl/execution>)
211 oneapi::dpl::execution::par,
227 for (std::size_t i = 0; i != entries.size(); ++i) {
228 auto [val, row, col] = entries[i];
230 if (col != std::exchange(last_col, col))
235 _umf_rows.push_back(
static_cast<int64_t
>(row[0]));
236 std::allocator_traits<EntryAlloc>::destroy(entryalloc, &entries[i]);
241 std::allocator_traits<EntryAlloc>::deallocate(entryalloc, entries.data(), entries.size());
251 void* symbolic_ptr =
nullptr;
252 umfpack_dl_symbolic(
_umf_n,
277 umfpack_dl_free_symbolic(&symbolic_ptr);
281 void apply(X& x, Y& b, InverseOperatorResult& res)
override
291 std::size_t flat_i = 0;
292 PDELab::forEachContainerEntry(b, [&](
auto val) {
_umf_b[flat_i++] = val; });
298 int status = umfpack_dl_wsolve(UMFPACK_A,
315 PDELab::forEachContainerEntry(x, [&](
auto& val) { val =
_umf_x[flat_i++]; });
319 res.converged = (status == 0);
322 void apply(X& x, Y& b, [[maybe_unused]]
double reduction, InverseOperatorResult& res)
override
324 this->
apply(x, b, res);
329 return SolverCategory::Category::sequential;
334 return [](
const std::shared_ptr<LinearOperator<X, Y>>& op,
335 const ParameterTree& config,
336 const Alloc& alloc) -> std::shared_ptr<InverseOperator<X, Y>> {
338 using OpAlloc =
typename std::allocator_traits<Alloc>::template rebind_alloc<UMFPackWapper>;
339 OpAlloc opalloc(alloc);
342 auto aop = std::dynamic_pointer_cast<O>(op);
344 throw format_exception(InvalidStateException{},
"Linear operator does not hold a matrix!");
346 return std::allocate_shared<UMFPackWapper>(opalloc, aop, config, alloc);
Definition: umfpack.hh:33
~UMFPackWapper() override
Definition: umfpack.hh:117
double _umf_info[UMFPACK_INFO]
Definition: umfpack.hh:361
void apply(X &x, Y &b, InverseOperatorResult &res) override
Definition: umfpack.hh:281
double _umf_control[UMFPACK_CONTROL]
Definition: umfpack.hh:360
std::vector< int64_t, Int64Alloc > _umf_offsets
Definition: umfpack.hh:352
int _umf_m
Definition: umfpack.hh:350
std::vector< int64_t, Int64Alloc > _umf_iworkspace
Definition: umfpack.hh:354
bool _report_numeric
Definition: umfpack.hh:363
bool _report_info
Definition: umfpack.hh:364
UMFPackWapper(const std::shared_ptr< const O > &op, const ParameterTree &config, const Alloc &alloc=Alloc())
Definition: umfpack.hh:72
void * _umf_numeric_ptr
Definition: umfpack.hh:359
std::vector< double, DoubleAlloc > _umf_values
Definition: umfpack.hh:356
std::vector< double, DoubleAlloc > _umf_b
Definition: umfpack.hh:358
SolverCategory::Category category() const override
Definition: umfpack.hh:327
static auto factory()
Definition: umfpack.hh:332
Alloc _alloc
Definition: umfpack.hh:351
int _umf_n
Definition: umfpack.hh:350
void apply(X &x, Y &b, double reduction, InverseOperatorResult &res) override
Definition: umfpack.hh:322
std::vector< double, DoubleAlloc > _umf_x
Definition: umfpack.hh:357
std::vector< int64_t, Int64Alloc > _umf_rows
Definition: umfpack.hh:353
bool _report_symbolic
Definition: umfpack.hh:362
std::vector< double, DoubleAlloc > _umf_dworkspace
Definition: umfpack.hh:355
Definition: block_jacobi.hh:14
auto format_exception(Exception &&e, fmt::format_string< Args... > format, Args &&... args)
Definition: exceptions.hh:23