Amesos2 - Direct Sparse Solver Interfaces Version of the Day
Amesos2_cuSOLVER_def.hpp
1// @HEADER
2// *****************************************************************************
3// Amesos2: Templated Direct Sparse Solver Package
4//
5// Copyright 2011 NTESS and the Amesos2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef AMESOS2_CUSOLVER_DEF_HPP
11#define AMESOS2_CUSOLVER_DEF_HPP
12
13#include <Teuchos_Tuple.hpp>
14#include <Teuchos_ParameterList.hpp>
15#include <Teuchos_StandardParameterEntryValidators.hpp>
16
18#include "Amesos2_cuSOLVER_decl.hpp"
19
20namespace Amesos2 {
21namespace Impl {
22
23// Standalone functor for scattering CSR entries into a dense column-major matrix.
24// Must live outside the private member function to satisfy CUDA extended-lambda rules.
25template<class MatrixView, class RowPtrView, class ColIndView, class NzView>
26struct CsrToDenseFunctor {
27 MatrixView matrix;
28 RowPtrView row_ptr;
29 ColIndView col_ind;
30 NzView nzvals;
31
32 CsrToDenseFunctor(MatrixView m, RowPtrView r, ColIndView c, NzView v)
33 : matrix(m), row_ptr(r), col_ind(c), nzvals(v) {}
34
35 KOKKOS_INLINE_FUNCTION
36 void operator()(int row) const {
37 for(int j = row_ptr(row); j < row_ptr(row+1); ++j)
38 matrix(row, col_ind(j)) = nzvals(j);
39 }
40};
41
42// Functor to fill a square matrix with the identity.
43template<class MatrixView>
44struct SetIdentityFunctor {
45 MatrixView matrix;
46 SetIdentityFunctor(MatrixView m) : matrix(m) {}
47
48 KOKKOS_INLINE_FUNCTION
49 void operator()(int i) const {
50 typedef typename MatrixView::value_type val_t;
51 const int n = static_cast<int>(matrix.extent(1));
52 for(int j = 0; j < n; ++j)
53 matrix(i, j) = (i == j) ? val_t(1) : val_t(0);
54 }
55};
56
57} // namespace Impl
58
59template <class Matrix, class Vector>
61 Teuchos::RCP<const Matrix> A,
62 Teuchos::RCP<Vector> X,
63 Teuchos::RCP<const Vector> B )
64 : SolverCore<Amesos2::cuSOLVER,Matrix,Vector>(A, X, B)
65{
66 auto status = cusolverDnCreate(&data_.dn_handle);
67 TEUCHOS_TEST_FOR_EXCEPTION( status != CUSOLVER_STATUS_SUCCESS,
68 std::runtime_error, "cusolverDnCreate failed");
69 status = cusolverSpCreate(&data_.sp_handle);
70 TEUCHOS_TEST_FOR_EXCEPTION( status != CUSOLVER_STATUS_SUCCESS,
71 std::runtime_error, "cusolverSpCreate failed");
72 status = cusolverSpCreateCsrcholInfo(&data_.chol_info);
73 TEUCHOS_TEST_FOR_EXCEPTION( status != CUSOLVER_STATUS_SUCCESS,
74 std::runtime_error, "cusolverSpCreateCsrcholInfo failed");
75 auto sparse_status = cusparseCreateMatDescr(&data_.desc);
76 TEUCHOS_TEST_FOR_EXCEPTION( sparse_status != CUSPARSE_STATUS_SUCCESS,
77 std::runtime_error, "cusparseCreateMatDescr failed");
78 auto blas_status = cublasCreate(&data_.blas_handle);
79 TEUCHOS_TEST_FOR_EXCEPTION( blas_status != CUBLAS_STATUS_SUCCESS,
80 std::runtime_error, "cublasCreate failed");
81 // Run GEMM on the same stream as Kokkos so copies and multiply are
82 // serialized without cross-stream synchronization gaps.
83 cublasSetStream(data_.blas_handle,
84 Kokkos::DefaultExecutionSpace().cuda_stream());
85}
86
87template <class Matrix, class Vector>
89{
90 cublasDestroy(data_.blas_handle);
91 cusparseDestroyMatDescr(data_.desc);
92 cusolverSpDestroyCsrcholInfo(data_.chol_info);
93 cusolverSpDestroy(data_.sp_handle);
94 cusolverDnDestroy(data_.dn_handle);
95}
96
97template<class Matrix, class Vector>
98int
100{
101#ifdef HAVE_AMESOS2_TIMERS
102 Teuchos::TimeMonitor preOrderTimer(this->timers_.preOrderTime_);
103#endif
104 if(do_optimization() &&
105 this->globalNumRows_ > data_.small_matrix_threshold) {
106 this->matrixA_->returnRowPtr_kokkos_view(device_row_ptr_view_);
107 this->matrixA_->returnColInd_kokkos_view(device_cols_view_);
108
109 // reorder to optimize cuSolver
110 if(data_.bReorder) {
111 Amesos2::Util::reorder(
112 device_row_ptr_view_, device_cols_view_,
113 device_perm_, device_peri_, sorted_nnz,
114 true);
115 }
116 }
117
118 return 0;
119}
120
121template <class Matrix, class Vector>
122int
124{
125#ifdef HAVE_AMESOS2_TIMERS
126 Teuchos::TimeMonitor symFactTimer(this->timers_.symFactTime_);
127#endif
128
129 int err = 0;
130 if ( this->root_ ) {
131 const int n = this->globalNumRows_;
132
133 if(n > data_.small_matrix_threshold) {
134 if(device_row_ptr_view_.extent(0) == 0) {
135 this->matrixA_->returnRowPtr_kokkos_view(device_row_ptr_view_);
136 this->matrixA_->returnColInd_kokkos_view(device_cols_view_);
137 }
138 const int nnz = device_cols_view_.size(); // reorder may have changed this
139 const int * colIdx = device_cols_view_.data();
140 const int * rowPtr = device_row_ptr_view_.data();
141 auto status = cusolverSpXcsrcholAnalysis(
142 data_.sp_handle, n, nnz, data_.desc, rowPtr, colIdx, data_.chol_info);
143 err = (status != CUSOLVER_STATUS_SUCCESS) ? 1 : 0;
144 } else {
145 // Allocate dense matrix, pivot array, info scalar, and inverse.
146 if((int)device_matrix_.extent(0) != n) {
147 device_matrix_ = device_value_type_matrix(
148 Kokkos::ViewAllocateWithoutInitializing("cusolver_dense"), n, n);
149 device_ipiv_ = Kokkos::View<int*, device_type>(
150 Kokkos::ViewAllocateWithoutInitializing("cusolver_ipiv"), n);
151 device_info_ = Kokkos::View<int, device_type>("cusolver_info");
152 }
153 if((int)device_inverse_.extent(0) != n) {
154 device_inverse_ = device_value_type_matrix(
155 Kokkos::ViewAllocateWithoutInitializing("cusolver_inverse"), n, n);
156 }
157
158 // Query factorization workspace size
159 int lwork = 0;
160 auto status = function_map::bufferInfo(
161 data_.dn_handle, n, device_matrix_.data(), n, &lwork);
162 if(status == CUSOLVER_STATUS_SUCCESS) {
163 if((size_t)lwork > buffer_.extent(0)) {
164 buffer_ = device_value_type_array(
165 Kokkos::ViewAllocateWithoutInitializing("cusolver_buf"), lwork);
166 }
167 }
168 err = (status != CUSOLVER_STATUS_SUCCESS) ? 1 : 0;
169 }
170 }
171
172 Teuchos::broadcast(*(this->getComm()), 0, &err);
173 TEUCHOS_TEST_FOR_EXCEPTION(err != 0,
174 std::runtime_error, "Amesos2 cuSolver symbolic failed.");
175
176 return err;
177}
178
179template <class Matrix, class Vector>
180int
182{
183#ifdef HAVE_AMESOS2_TIMERS
184 Teuchos::TimeMonitor numFactTimer(this->timers_.numFactTime_);
185#endif
186
187 int err = 0;
188 if(do_optimization()) {
189 const int n = this->globalNumRows_;
190
191 if(n > data_.small_matrix_threshold) {
192 this->matrixA_->returnValues_kokkos_view(device_nzvals_view_);
193
194 // reorder to optimize cuSolver
195 if(data_.bReorder) {
196 // must have original row and cols - maybe cache this from 1st symbolic setup
197 // this setup exists to support the refactor option
198 device_size_type_array orig_device_row_ptr_view;
199 device_ordinal_type_array orig_device_cols_view;
200 this->matrixA_->returnRowPtr_kokkos_view(orig_device_row_ptr_view);
201 this->matrixA_->returnColInd_kokkos_view(orig_device_cols_view);
202 Amesos2::Util::reorder_values(
203 device_nzvals_view_, orig_device_row_ptr_view, device_row_ptr_view_,
204 orig_device_cols_view, device_perm_, device_peri_, sorted_nnz);
205 }
206
207 const int nnz = device_cols_view_.size(); // reorder may have changed this
208 const cusolver_type * values = device_nzvals_view_.data();
209 const int * colIdx = device_cols_view_.data();
210 const int * rowPtr = device_row_ptr_view_.data();
211
212 size_t internalDataInBytes, workspaceInBytes;
213 auto status = function_map::sparseBufferInfo(
214 data_.sp_handle, n, nnz, data_.desc,
215 values, rowPtr, colIdx, data_.chol_info,
216 &internalDataInBytes, &workspaceInBytes);
217
218 if(status == CUSOLVER_STATUS_SUCCESS) {
219 const size_t buffer_size = workspaceInBytes / sizeof(cusolver_type);
220 if(buffer_size > buffer_.extent(0)) {
221 buffer_ = device_value_type_array(
222 Kokkos::ViewAllocateWithoutInitializing("cusolver_buf"), buffer_size);
223 }
224 status = function_map::sparseNumeric(
225 data_.sp_handle, n, nnz, data_.desc,
226 values, rowPtr, colIdx, data_.chol_info, buffer_.data());
227 }
228 err = (status != CUSOLVER_STATUS_SUCCESS) ? 1 : 0;
229 Teuchos::broadcast(*(this->getComm()), 0, &err);
230 TEUCHOS_TEST_FOR_EXCEPTION(err != 0,
231 std::runtime_error, "Amesos2 cuSolver numeric failed.");
232
233 return err;
234 }
235
236 // Extract CSR views from matrix (views into matrix internal storage)
237 device_value_type_array nzvals;
238 device_size_type_array row_ptr;
239 device_ordinal_type_array col_ind;
240 this->matrixA_->returnValues_kokkos_view(nzvals);
241 this->matrixA_->returnRowPtr_kokkos_view(row_ptr);
242 this->matrixA_->returnColInd_kokkos_view(col_ind);
243
244 // Zero-fill dense matrix then scatter sparse entries
245 Kokkos::deep_copy(device_matrix_,
246 Teuchos::ScalarTraits<cusolver_type>::zero());
247 Impl::CsrToDenseFunctor<device_value_type_matrix,
248 device_size_type_array,
249 device_ordinal_type_array,
250 device_value_type_array>
251 scatter(device_matrix_, row_ptr, col_ind, nzvals);
252 Kokkos::parallel_for("Amesos2_cuSOLVER_csr_to_dense",
253 Kokkos::RangePolicy<typename device_type::execution_space>(0, n),
254 scatter);
255
256 // LU factorization in-place
257 auto status = function_map::numeric(
258 data_.dn_handle, n, device_matrix_.data(), n,
259 buffer_.data(), device_ipiv_.data(), device_info_.data());
260
261 if(status == CUSOLVER_STATUS_SUCCESS) {
262 // Fill device_inverse_ with identity, then solve LU * inv = I, inv = A^{-1}.
263 Impl::SetIdentityFunctor<device_value_type_matrix> set_id(device_inverse_);
264 Kokkos::parallel_for("Amesos2_cuSOLVER_set_identity",
265 Kokkos::RangePolicy<typename device_type::execution_space>(0, n),
266 set_id);
267
268 status = function_map::solveLU(
269 data_.dn_handle, CUBLAS_OP_N, n, n,
270 device_matrix_.data(), n, device_ipiv_.data(),
271 device_inverse_.data(), n, device_info_.data());
272 }
273
274 if(status == CUSOLVER_STATUS_SUCCESS) {
275 auto host_info = Kokkos::create_mirror_view_and_copy(
276 Kokkos::HostSpace(), device_info_);
277 err = (host_info() != 0) ? 1 : 0;
278 } else {
279 err = 1;
280 }
281 }
282
283 Teuchos::broadcast(*(this->getComm()), 0, &err);
284 TEUCHOS_TEST_FOR_EXCEPTION(err != 0,
285 std::runtime_error, "Amesos2 cuSolver numeric failed.");
286
287 return err;
288}
289
290template <class Matrix, class Vector>
291int
293 const Teuchos::Ptr<MultiVecAdapter<Vector> > X,
294 const Teuchos::Ptr<const MultiVecAdapter<Vector> > B) const
295{
296 int err = 0;
297 const int n = this->globalNumRows_;
298
299 if(n > data_.small_matrix_threshold) {
300 TEUCHOS_TEST_FOR_EXCEPTION(this->control_.useTranspose_,
301 std::runtime_error,
302 "Amesos2 cuSolver sparse Cholesky path does not support transpose solves.");
303
304 const global_size_type ld_rhs = this->root_ ? X->getGlobalLength() : 0;
305 const ordinal_type nrhs = X->getGlobalNumVectors();
306
307 bool bAssignedX;
308 { // Get values from RHS B
309#ifdef HAVE_AMESOS2_TIMERS
310 Teuchos::TimeMonitor mvConvTimer(this->timers_.vecConvTime_);
311#endif
312
313 const bool initialize_data = true;
314 const bool do_not_initialize_data = false;
315 Util::get_1d_copy_helper_kokkos_view<MultiVecAdapter<Vector>,
316 device_solve_array_t>::do_get(initialize_data, B, this->bValues_,
317 Teuchos::as<size_t>(ld_rhs), ROOTED, this->rowIndexBase_);
318
319 bAssignedX = Util::get_1d_copy_helper_kokkos_view<MultiVecAdapter<Vector>,
320 device_solve_array_t>::do_get(do_not_initialize_data, X, this->xValues_,
321 Teuchos::as<size_t>(ld_rhs), ROOTED, this->rowIndexBase_);
322 }
323
324 if ( this->root_ ) { // Do solve!
325#ifdef HAVE_AMESOS2_TIMERS
326 Teuchos::TimeMonitor solveTimer(this->timers_.solveTime_);
327#endif
328
329 if(data_.bReorder) {
330 Amesos2::Util::apply_reorder_permutation(
331 this->bValues_, this->permute_result_, this->device_perm_);
332 }
333 else {
334 this->permute_result_ = this->bValues_; // no permutation
335 }
336
337 for(ordinal_type rhs = 0; rhs < nrhs; ++rhs) {
338 const cusolver_type * b = this->permute_result_.data() + rhs * n;
339 cusolver_type * x = this->xValues_.data() + rhs * n;
340 auto status = function_map::sparseSolve(
341 data_.sp_handle, n, b, x, data_.chol_info, buffer_.data());
342 err = (status != CUSOLVER_STATUS_SUCCESS) ? 1 : 0;
343 if(err != 0) {
344 break;
345 }
346 }
347
348 if(data_.bReorder && err == 0) {
349 Amesos2::Util::apply_reorder_permutation(
350 this->xValues_, this->permute_result_, this->device_peri_);
351 Kokkos::deep_copy(this->xValues_, this->permute_result_);
352 }
353 }
354
355 if(!bAssignedX) {
356#ifdef HAVE_AMESOS2_TIMERS
357 Teuchos::TimeMonitor redistTimer(this->timers_.vecRedistTime_);
358#endif
359
360 Util::template put_1d_data_helper_kokkos_view<
361 MultiVecAdapter<Vector>,device_solve_array_t>::do_put(X, xValues_,
362 Teuchos::as<size_t>(ld_rhs), ROOTED, this->rowIndexBase_);
363 }
364
365 Teuchos::broadcast(*(this->getComm()), 0, &err);
366 TEUCHOS_TEST_FOR_EXCEPTION(err != 0,
367 std::runtime_error, "Amesos2 cuSolver solve failed.");
368
369 return err;
370 }
371
372 const global_size_type ld_rhs = this->root_ ? X->getGlobalLength() : 0;
373 bool bAssignedX;
374 {
375#ifdef HAVE_AMESOS2_TIMERS
376 Teuchos::TimeMonitor mvConvTimer(this->timers_.vecConvTime_);
377#endif
378
379 // The adapter assigns compatible device storage directly. Otherwise it
380 // stages B and X in the CUDA views below, so cuBLAS never receives host
381 // pointers from a serial Kokkos node.
382 const bool initialize_data = true;
383 const bool do_not_initialize_data = false;
384 Util::get_1d_copy_helper_kokkos_view<MultiVecAdapter<Vector>,
385 device_solve_array_t>::do_get(initialize_data, B, this->bValues_,
386 Teuchos::as<size_t>(ld_rhs), ROOTED, this->rowIndexBase_);
387 bAssignedX = Util::get_1d_copy_helper_kokkos_view<MultiVecAdapter<Vector>,
388 device_solve_array_t>::do_get(do_not_initialize_data, X, this->xValues_,
389 Teuchos::as<size_t>(ld_rhs), ROOTED, this->rowIndexBase_);
390 }
391
392 if(this->root_) {
393#ifdef HAVE_AMESOS2_TIMERS
394 Teuchos::TimeMonitor solveTimer(this->timers_.solveTime_);
395#endif
396 const int nrhs = static_cast<int>(X->getGlobalNumVectors());
397 const int ldb = std::max(n, static_cast<int>(this->bValues_.stride(1)));
398 const int ldx = std::max(n, static_cast<int>(this->xValues_.stride(1)));
399
400 const cublasOperation_t trans =
401 this->control_.useTranspose_ ? CUBLAS_OP_C : CUBLAS_OP_N;
402
403 auto blas_status = function_map::solve(
404 data_.blas_handle, trans, n, nrhs,
405 device_inverse_.data(), n,
406 this->bValues_.data(), ldb, this->xValues_.data(), ldx);
407
408 err = (blas_status != CUBLAS_STATUS_SUCCESS) ? 1 : 0;
409 }
410
411 Teuchos::broadcast(*(this->getComm()), 0, &err);
412 TEUCHOS_TEST_FOR_EXCEPTION(err != 0,
413 std::runtime_error, "Amesos2 cuSolver solve failed.");
414
415 if(!bAssignedX) {
416#ifdef HAVE_AMESOS2_TIMERS
417 Teuchos::TimeMonitor redistTimer(this->timers_.vecRedistTime_);
418#endif
419
420 Util::template put_1d_data_helper_kokkos_view<
421 MultiVecAdapter<Vector>, device_solve_array_t>::do_put(
422 X, this->xValues_, Teuchos::as<size_t>(ld_rhs), ROOTED,
423 this->rowIndexBase_);
424 }
425
426 return err;
427}
428
429template <class Matrix, class Vector>
430bool
432{
433 return( this->matrixA_->getGlobalNumRows() == this->matrixA_->getGlobalNumCols() );
434}
435
436template <class Matrix, class Vector>
437void
438cuSOLVER<Matrix,Vector>::setParameters_impl(const Teuchos::RCP<Teuchos::ParameterList> & parameterList )
439{
440 using Teuchos::RCP;
441 using Teuchos::ParameterEntryValidator;
442
443 RCP<const Teuchos::ParameterList> valid_params = getValidParameters_impl();
444
445 if( parameterList->isParameter("Reorder") ){
446 RCP<const ParameterEntryValidator> reorder_validator = valid_params->getEntry("Reorder").validator();
447 parameterList->getEntry("Reorder").setValidator(reorder_validator);
448 }
449 if( parameterList->isParameter("small matrix threshold") ){
450 RCP<const ParameterEntryValidator> threshold_validator =
451 valid_params->getEntry("small matrix threshold").validator();
452 parameterList->getEntry("small matrix threshold").setValidator(threshold_validator);
453 }
454 data_.bReorder = parameterList->get<bool>("Reorder",
455#ifdef HAVE_AMESOS2_METIS
456 true
457#else
458 false
459#endif
460 );
461 data_.small_matrix_threshold = parameterList->get<int>(
462 "small matrix threshold", 2500);
463}
464
465template <class Matrix, class Vector>
466Teuchos::RCP<const Teuchos::ParameterList>
468{
469 static Teuchos::RCP<const Teuchos::ParameterList> valid_params;
470
471 if (is_null(valid_params)) {
472 Teuchos::RCP<Teuchos::ParameterList> pl = Teuchos::parameterList();
473
474#ifdef HAVE_AMESOS2_METIS
475 pl->set("Reorder", true, "Whether GIDs contiguous");
476#else
477 pl->set("Reorder", false, "Whether GIDs contiguous");
478#endif
479 pl->set("small matrix threshold", 2500,
480 "Use explicit inverse for matrices with rows less than or equal to this value");
481 valid_params = pl;
482 }
483 return valid_params;
484}
485
486template <class Matrix, class Vector>
487bool
489 return (this->root_ && (this->matrixA_->getComm()->getSize() == 1));
490}
491
492template <class Matrix, class Vector>
493bool
495{
496 if(current_phase == SOLVE) {
497 return(false);
498 }
499
500 if(!do_optimization()) {
501 TEUCHOS_TEST_FOR_EXCEPTION( true, std::runtime_error,
502 "cuSolver is only implemented for serial.");
503 }
504
505 return true;
506}
507
508template <class Matrix, class Vector>
509void
510cuSOLVER<Matrix,Vector>::describe_impl(Teuchos::FancyOStream &out,
511 const Teuchos::EVerbosityLevel verbLevel) const
512{
513 out << " cuSOLVER: sparse Cholesky with dense inverse for small matrices" << std::endl;
514 out << " > Reorder = " << (data_.bReorder ? "YES" : "NO") << std::endl;
515 out << " > small matrix threshold = "
516 << data_.small_matrix_threshold << std::endl;
517}
518
519template<class Matrix, class Vector>
520const char* cuSOLVER<Matrix,Vector>::name = "cuSOLVER";
521
522} // end namespace Amesos2
523
524#endif // AMESOS2_CUSOLVER_DEF_HPP
@ ROOTED
Definition Amesos2_TypeDecl.hpp:93
Amesos2::SolverCore: A templated interface for interaction with third-party direct sparse solvers.
Definition Amesos2_SolverCore_decl.hpp:72
Amesos2 interface to cuSOLVER sparse Cholesky and dense inverse solves.
Definition Amesos2_cuSOLVER_decl.hpp:30
void setParameters_impl(const Teuchos::RCP< Teuchos::ParameterList > &parameterList)
Definition Amesos2_cuSOLVER_def.hpp:438
void describe_impl(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel) const
Prints the status information about the current solver with some level of verbosity.
Definition Amesos2_cuSOLVER_def.hpp:510
int preOrdering_impl()
Performs pre-ordering on the matrix to increase efficiency.
Definition Amesos2_cuSOLVER_def.hpp:99
bool loadA_impl(EPhase current_phase)
Reads matrix data into internal structures.
Definition Amesos2_cuSOLVER_def.hpp:494
cuSOLVER(Teuchos::RCP< const Matrix > A, Teuchos::RCP< Vector > X, Teuchos::RCP< const Vector > B)
Initialize from Teuchos::RCP.
Definition Amesos2_cuSOLVER_def.hpp:60
bool matrixShapeOK_impl() const
Determines whether the shape of the matrix is OK for this solver.
Definition Amesos2_cuSOLVER_def.hpp:431
int solve_impl(const Teuchos::Ptr< MultiVecAdapter< Vector > > X, const Teuchos::Ptr< const MultiVecAdapter< Vector > > B) const
cuSOLVER specific solve.
Definition Amesos2_cuSOLVER_def.hpp:292
Teuchos::RCP< const Teuchos::ParameterList > getValidParameters_impl() const
Definition Amesos2_cuSOLVER_def.hpp:467
int symbolicFactorization_impl()
Perform symbolic factorization of the matrix using cuSOLVER.
Definition Amesos2_cuSOLVER_def.hpp:123
bool do_optimization() const
can we optimize size_type and ordinal_type for straight pass through
Definition Amesos2_cuSOLVER_def.hpp:488
int numericFactorization_impl()
cuSOLVER specific numeric factorization
Definition Amesos2_cuSOLVER_def.hpp:181
~cuSOLVER()
Destructor.
Definition Amesos2_cuSOLVER_def.hpp:88
EPhase
Used to indicate a phase in the direct solution.
Definition Amesos2_TypeDecl.hpp:31
A templated MultiVector class adapter for Amesos2.
Definition Amesos2_MultiVecAdapter_decl.hpp:142