Amesos2 - Direct Sparse Solver Interfaces  Version of the Day
Amesos2_TpetraMultiVecAdapter_def.hpp
Go to the documentation of this file.
1 // @HEADER
2 //
3 // ***********************************************************************
4 //
5 // Amesos2: Templated Direct Sparse Solver Package
6 // Copyright 2011 Sandia Corporation
7 //
8 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9 // the U.S. Government retains certain rights in this software.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
39 //
40 // ***********************************************************************
41 //
42 // @HEADER
43 
53 #ifndef AMESOS2_TPETRA_MULTIVEC_ADAPTER_DEF_HPP
54 #define AMESOS2_TPETRA_MULTIVEC_ADAPTER_DEF_HPP
55 
57 
58 
59 namespace Amesos2 {
60 
61  using Tpetra::MultiVector;
62 
63  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node >
64  MultiVecAdapter<
65  MultiVector<Scalar,
66  LocalOrdinal,
67  GlobalOrdinal,
68  Node> >::MultiVecAdapter( const Teuchos::RCP<multivec_t>& m )
69  : mv_(m)
70  {}
71 
72 
73  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node >
74  typename MultiVecAdapter<
75  MultiVector<Scalar,
76  LocalOrdinal,
77  GlobalOrdinal,
78  Node> >::multivec_t::impl_scalar_type *
79  MultiVecAdapter<
80  MultiVector<Scalar,
81  LocalOrdinal,
82  GlobalOrdinal,
83  Node> >::getMVPointer_impl() const
84  {
85  TEUCHOS_TEST_FOR_EXCEPTION( this->getGlobalNumVectors() != 1,
86  std::invalid_argument,
87  "Amesos2_TpetraMultiVectorAdapter: getMVPointer_impl should only be called for case with a single vector and single MPI process" );
88 
89  typedef typename multivec_t::dual_view_type dual_view_type;
90  typedef typename dual_view_type::host_mirror_space host_execution_space;
91  mv_->template sync<host_execution_space> ();
92  auto contig_local_view_2d = mv_->template getLocalView<host_execution_space>();
93  auto contig_local_view_1d = Kokkos::subview (contig_local_view_2d, Kokkos::ALL (), 0);
94  return contig_local_view_1d.data();
95  }
96 
97  // TODO Proper type handling:
98  // Consider a MultiVectorTraits class
99  // typedef Tpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node> multivector_type
100  // NOTE: In this class, above already has a typedef multivec_t
101  // typedef typename multivector_type::impl_scalar_type return_scalar_type; // this is the POD type the dual_view_type is templated on
102  // Traits class needed to do this generically for the general MultiVectorAdapter interface
103 
104 
105  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node >
106  void
107  MultiVecAdapter<
108  MultiVector<Scalar,
109  LocalOrdinal,
110  GlobalOrdinal,
111  Node> >::get1dCopy(const Teuchos::ArrayView<scalar_t>& av,
112  size_t lda,
113  Teuchos::Ptr<
114  const Tpetra::Map<LocalOrdinal,
115  GlobalOrdinal,
116  Node> > distribution_map,
117  EDistribution distribution) const
118  {
119  using Teuchos::as;
120  using Teuchos::RCP;
121  typedef Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node> map_type;
122  const size_t num_vecs = getGlobalNumVectors ();
123 
124  TEUCHOS_TEST_FOR_EXCEPTION(
125  distribution_map.getRawPtr () == NULL, std::invalid_argument,
126  "Amesos2::MultiVecAdapter::get1dCopy: distribution_map argument is null.");
127  TEUCHOS_TEST_FOR_EXCEPTION(
128  mv_.is_null (), std::logic_error,
129  "Amesos2::MultiVecAdapter::get1dCopy: mv_ is null.");
130  // Check mv_ before getMap(), because the latter calls mv_->getMap().
131  TEUCHOS_TEST_FOR_EXCEPTION(
132  this->getMap ().is_null (), std::logic_error,
133  "Amesos2::MultiVecAdapter::get1dCopy: this->getMap() returns null.");
134 
135 #ifdef HAVE_AMESOS2_DEBUG
136  const size_t requested_vector_length = distribution_map->getNodeNumElements ();
137  TEUCHOS_TEST_FOR_EXCEPTION(
138  lda < requested_vector_length, std::invalid_argument,
139  "Amesos2::MultiVecAdapter::get1dCopy: On process " <<
140  distribution_map->getComm ()->getRank () << " of the distribution Map's "
141  "communicator, the given stride lda = " << lda << " is not large enough "
142  "for the local vector length " << requested_vector_length << ".");
143  TEUCHOS_TEST_FOR_EXCEPTION(
144  as<size_t> (av.size ()) < as<size_t> ((num_vecs - 1) * lda + requested_vector_length),
145  std::invalid_argument, "Amesos2::MultiVector::get1dCopy: MultiVector "
146  "storage not large enough given leading dimension and number of vectors." );
147 #endif // HAVE_AMESOS2_DEBUG
148 
149  // Special case when number vectors == 1 and single MPI process
150  if ( num_vecs == 1 && this->getComm()->getRank() == 0 && this->getComm()->getSize() == 1 ) {
151  mv_->get1dCopy (av, lda);
152  }
153  else {
154 
155  // (Re)compute the Export object if necessary. If not, then we
156  // don't need to clone distribution_map; we can instead just get
157  // the previously cloned target Map from the Export object.
158  RCP<const map_type> distMap;
159  if (exporter_.is_null () ||
160  ! exporter_->getSourceMap ()->isSameAs (* (this->getMap ())) ||
161  ! exporter_->getTargetMap ()->isSameAs (* distribution_map)) {
162 
163  // Since we're caching the Export object, and since the Export
164  // needs to keep the distribution Map, we have to make a copy of
165  // the latter in order to ensure that it will stick around past
166  // the scope of this function call. (Ptr is not reference
167  // counted.) Map's clone() method suffices, even though it only
168  // makes a shallow copy of some of Map's data, because Map is
169  // immutable and those data are reference-counted (e.g.,
170  // ArrayRCP or RCP).
171  distMap = distribution_map->template clone<Node> (distribution_map->getNode ());
172 
173  // (Re)create the Export object.
174  exporter_ = rcp (new export_type (this->getMap (), distMap));
175  }
176  else {
177  distMap = exporter_->getTargetMap ();
178  }
179 
180  multivec_t redist_mv (distMap, num_vecs);
181 
182  // Redistribute the input (multi)vector.
183  redist_mv.doExport (*mv_, *exporter_, Tpetra::REPLACE);
184 
185  if ( distribution != CONTIGUOUS_AND_ROOTED ) {
186  // Do this if GIDs contiguous - existing functionality
187  // Copy the imported (multi)vector's data into the ArrayView.
188  redist_mv.get1dCopy (av, lda);
189  }
190  else {
191  // Do this if GIDs not contiguous...
192  // sync is needed for example if mv was updated on device, but will be passed through Amesos2 to solver running on host
193  typedef typename multivec_t::dual_view_type dual_view_type;
194  typedef typename dual_view_type::host_mirror_space host_execution_space;
195  redist_mv.template sync < host_execution_space > ();
196 
197  auto contig_local_view_2d = redist_mv.template getLocalView<host_execution_space>();
198  if ( redist_mv.isConstantStride() ) {
199  for ( size_t j = 0; j < num_vecs; ++j) {
200  auto av_j = av(lda*j, lda);
201  for ( size_t i = 0; i < lda; ++i ) {
202  av_j[i] = contig_local_view_2d(i,j); //lda may not be correct if redist_mv is not constant stride...
203  }
204  }
205  }
206  else {
207  // ... lda should come from Teuchos::Array* allocation,
208  // not the MultiVector, since the MultiVector does NOT
209  // have constant stride in this case.
210  // TODO lda comes from X->getGlobalLength() in solve_impl - should this be changed???
211  const size_t lclNumRows = redist_mv.getLocalLength();
212  for (size_t j = 0; j < redist_mv.getNumVectors(); ++j) {
213  auto av_j = av(lda*j, lclNumRows);
214  auto X_j = redist_mv.getVector(j);
215  auto X_lcl_j_2d = redist_mv.template getLocalView<host_execution_space> ();
216  auto X_lcl_j_1d = Kokkos::subview (X_lcl_j_2d, Kokkos::ALL (), j);
217  for ( size_t i = 0; i < lclNumRows; ++i ) {
218  av_j[i] = X_lcl_j_1d(i);
219  }
220  }
221  }
222 
223  auto global_contiguous_size = distMap->getGlobalNumElements(); //maybe use getGlobalLength() from the mv
224  auto local_contiguous_size = (distMap->getComm()->getRank() == 0) ? global_contiguous_size : 0;
225  RCP<const map_type> contigMap = rcp( new map_type(global_contiguous_size, local_contiguous_size, 0, distMap->getComm() ));
226 
227  typedef Tpetra::Export<local_ordinal_t, global_ordinal_t, node_t> contiguous_export_t;
228  RCP<contiguous_export_t> contig_exporter = rcp( new contiguous_export_t(redist_mv.getMap(), contigMap) );
229  multivec_t contig_mv( contigMap, num_vecs);
230  contig_mv.doExport(redist_mv, *contig_exporter, Tpetra::INSERT);
231  }
232  }
233  }
234 
235  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node >
236  Teuchos::ArrayRCP<Scalar>
237  MultiVecAdapter<
238  MultiVector<Scalar,
239  LocalOrdinal,
240  GlobalOrdinal,
241  Node> >::get1dViewNonConst (bool local)
242  {
243  // FIXME (mfh 22 Jan 2014) When I first found this routine, all of
244  // its code was commented out, and it didn't return anything. The
245  // latter is ESPECIALLY dangerous, given that the return value is
246  // an ArrayRCP. Thus, I added the exception throw below.
247  TEUCHOS_TEST_FOR_EXCEPTION(
248  true, std::logic_error, "Amesos2::MultiVecAdapter::get1dViewNonConst: "
249  "Not implemented.");
250 
251  // if( local ){
252  // this->localize();
253  // /* Use the global element list returned by
254  // * mv_->getMap()->getNodeElementList() to get a subCopy of mv_ which we
255  // * assign to l_l_mv_, then return get1dViewNonConst() of l_l_mv_
256  // */
257  // if(l_l_mv_.is_null() ){
258  // Teuchos::ArrayView<const GlobalOrdinal> nodeElements_go
259  // = mv_->getMap()->getNodeElementList();
260  // Teuchos::Array<size_t> nodeElements_st(nodeElements_go.size());
261 
262  // // Convert the node element to a list of size_t type objects
263  // typename Teuchos::ArrayView<const GlobalOrdinal>::iterator it_go;
264  // Teuchos::Array<size_t>::iterator it_st = nodeElements_st.begin();
265  // for( it_go = nodeElements_go.begin(); it_go != nodeElements_go.end(); ++it_go ){
266  // *(it_st++) = Teuchos::as<size_t>(*it_go);
267  // }
268 
269  // // To be consistent with the globalize() function, get a view of the local mv
270  // l_l_mv_ = l_mv_->subViewNonConst(nodeElements_st);
271 
272  // return(l_l_mv_->get1dViewNonConst());
273  // } else {
274  // // We need to re-import values to the local, since changes may have been
275  // // made to the global structure that are not reflected in the local
276  // // view.
277  // Teuchos::ArrayView<const GlobalOrdinal> nodeElements_go
278  // = mv_->getMap()->getNodeElementList();
279  // Teuchos::Array<size_t> nodeElements_st(nodeElements_go.size());
280 
281  // // Convert the node element to a list of size_t type objects
282  // typename Teuchos::ArrayView<const GlobalOrdinal>::iterator it_go;
283  // Teuchos::Array<size_t>::iterator it_st = nodeElements_st.begin();
284  // for( it_go = nodeElements_go.begin(); it_go != nodeElements_go.end(); ++it_go ){
285  // *(it_st++) = Teuchos::as<size_t>(*it_go);
286  // }
287 
288  // return l_l_mv_->get1dViewNonConst();
289  // }
290  // } else {
291  // if( mv_->isDistributed() ){
292  // this->localize();
293 
294  // return l_mv_->get1dViewNonConst();
295  // } else { // not distributed, no need to import
296  // return mv_->get1dViewNonConst();
297  // }
298  // }
299  }
300 
301 
302  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node>
303  void
304  MultiVecAdapter<
305  MultiVector<Scalar,
306  LocalOrdinal,
307  GlobalOrdinal,
308  Node> >::put1dData(const Teuchos::ArrayView<const scalar_t>& new_data,
309  size_t lda,
310  Teuchos::Ptr<
311  const Tpetra::Map<LocalOrdinal,
312  GlobalOrdinal,
313  Node> > source_map,
314  EDistribution distribution )
315  {
316  using Teuchos::RCP;
317  typedef Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node> map_type;
318 
319  TEUCHOS_TEST_FOR_EXCEPTION(
320  source_map.getRawPtr () == NULL, std::invalid_argument,
321  "Amesos2::MultiVecAdapter::put1dData: source_map argument is null.");
322  TEUCHOS_TEST_FOR_EXCEPTION(
323  mv_.is_null (), std::logic_error,
324  "Amesos2::MultiVecAdapter::put1dData: the internal MultiVector mv_ is null.");
325  // getMap() calls mv_->getMap(), so test first whether mv_ is null.
326  TEUCHOS_TEST_FOR_EXCEPTION(
327  this->getMap ().is_null (), std::logic_error,
328  "Amesos2::MultiVecAdapter::put1dData: this->getMap() returns null.");
329 
330  const size_t num_vecs = getGlobalNumVectors ();
331 
332  // Special case when number vectors == 1 and single MPI process
333  if ( num_vecs == 1 && this->getComm()->getRank() == 0 && this->getComm()->getSize() == 1 ) {
334  typedef typename multivec_t::dual_view_type::host_mirror_space host_execution_space;
335  // num_vecs = 1; stride does not matter
336  auto mv_view_to_modify_2d = mv_->template getLocalView<host_execution_space>();
337  for ( size_t i = 0; i < lda; ++i ) {
338  mv_view_to_modify_2d(i,0) = new_data[i];
339  }
340  }
341  else {
342 
343  // (Re)compute the Import object if necessary. If not, then we
344  // don't need to clone source_map; we can instead just get the
345  // previously cloned source Map from the Import object.
346  RCP<const map_type> srcMap;
347  if (importer_.is_null () ||
348  ! importer_->getSourceMap ()->isSameAs (* source_map) ||
349  ! importer_->getTargetMap ()->isSameAs (* (this->getMap ()))) {
350 
351  // Since we're caching the Import object, and since the Import
352  // needs to keep the source Map, we have to make a copy of the
353  // latter in order to ensure that it will stick around past the
354  // scope of this function call. (Ptr is not reference counted.)
355  // Map's clone() method suffices, even though it only makes a
356  // shallow copy of some of Map's data, because Map is immutable
357  // and those data are reference-counted (e.g., ArrayRCP or RCP).
358  srcMap = source_map->template clone<Node> (source_map->getNode ());
359  importer_ = rcp (new import_type (srcMap, this->getMap ()));
360  }
361  else {
362  srcMap = importer_->getSourceMap ();
363  }
364 
365  multivec_t redist_mv (srcMap, num_vecs);
366 
367  if ( distribution != CONTIGUOUS_AND_ROOTED ) {
368  // Do this if GIDs contiguous - existing functionality
369  // Redistribute the output (multi)vector.
370  const multivec_t source_mv (srcMap, new_data, lda, num_vecs);
371  mv_->doImport (source_mv, *importer_, Tpetra::REPLACE);
372  }
373  else {
374  typedef typename multivec_t::dual_view_type dual_view_type;
375  typedef typename dual_view_type::host_mirror_space host_execution_space;
376  redist_mv.template modify< host_execution_space > ();
377 
378  if ( redist_mv.isConstantStride() ) {
379  auto contig_local_view_2d = redist_mv.template getLocalView<host_execution_space>();
380  for ( size_t j = 0; j < num_vecs; ++j) {
381  auto av_j = new_data(lda*j, lda);
382  for ( size_t i = 0; i < lda; ++i ) {
383  contig_local_view_2d(i,j) = av_j[i];
384  }
385  }
386  }
387  else {
388  // ... lda should come from Teuchos::Array* allocation,
389  // not the MultiVector, since the MultiVector does NOT
390  // have constant stride in this case.
391  // TODO lda comes from X->getGlobalLength() in solve_impl - should this be changed???
392  const size_t lclNumRows = redist_mv.getLocalLength();
393  for (size_t j = 0; j < redist_mv.getNumVectors(); ++j) {
394  auto av_j = new_data(lda*j, lclNumRows);
395  auto X_j = redist_mv.getVector(j);
396  auto X_lcl_j_2d = redist_mv.template getLocalView<host_execution_space> ();
397  auto X_lcl_j_1d = Kokkos::subview (X_lcl_j_2d, Kokkos::ALL (), j);
398  for ( size_t i = 0; i < lclNumRows; ++i ) {
399  X_lcl_j_1d(i) = av_j[i];
400  }
401  }
402  }
403 
404  typedef typename multivec_t::node_type::memory_space memory_space;
405  redist_mv.template sync <memory_space> ();
406 
407  mv_->doImport (redist_mv, *importer_, Tpetra::REPLACE);
408  }
409  }
410 
411  }
412 
413 
414  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node >
415  std::string
416  MultiVecAdapter<
417  MultiVector<Scalar,
418  LocalOrdinal,
419  GlobalOrdinal,
420  Node> >::description() const
421  {
422  std::ostringstream oss;
423  oss << "Amesos2 adapter wrapping: ";
424  oss << mv_->description();
425  return oss.str();
426  }
427 
428 
429  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node >
430  void
431  MultiVecAdapter<
432  MultiVector<Scalar,
433  LocalOrdinal,
434  GlobalOrdinal,
435  Node> >::describe (Teuchos::FancyOStream& os,
436  const Teuchos::EVerbosityLevel verbLevel) const
437  {
438  mv_->describe (os, verbLevel);
439  }
440 
441 
442  template <typename Scalar, typename LocalOrdinal, typename GlobalOrdinal, class Node >
443  const char* MultiVecAdapter<
444  MultiVector<Scalar,
445  LocalOrdinal,
446  GlobalOrdinal,
447  Node> >::name = "Amesos2 adapter for Tpetra::MultiVector";
448 
449 } // end namespace Amesos2
450 
451 #endif // AMESOS2_TPETRA_MULTIVEC_ADAPTER_DEF_HPP
Definition: Amesos2_AbstractConcreteMatrixAdapter.hpp:48
Amesos2::MultiVecAdapter specialization for the Tpetra::MultiVector class.
EDistribution
Definition: Amesos2_TypeDecl.hpp:123