Xpetra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Xpetra_MapUtils.hpp
Go to the documentation of this file.
1 // @HEADER
2 //
3 // ***********************************************************************
4 //
5 // Xpetra: A linear algebra interface package
6 // Copyright 2012 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
39 // Jonathan Hu (jhu@sandia.gov)
40 // Andrey Prokopenko (aprokop@sandia.gov)
41 // Ray Tuminaro (rstumin@sandia.gov)
42 // Tobias Wiesner (tawiesn@sandia.gov)
43 //
44 // ***********************************************************************
45 //
46 // @HEADER
47 #ifndef PACKAGES_XPETRA_SUP_MAP_UTILS_HPP_
48 #define PACKAGES_XPETRA_SUP_MAP_UTILS_HPP_
49 
50 #include "Xpetra_ConfigDefs.hpp"
51 
52 #include "Xpetra_Exceptions.hpp"
53 #include "Xpetra_Map.hpp"
54 #include "Xpetra_MapFactory.hpp"
55 
56 namespace Xpetra {
57 
58 #ifndef DOXYGEN_SHOULD_SKIP_THIS
59 // forward declaration of BlockedMap, needed to prevent circular inclusions
60 template <class LO, class GO, class N>
61 class BlockedMap;
62 #endif
63 
71 template <class LocalOrdinal,
72  class GlobalOrdinal,
73  class Node = Tpetra::KokkosClassic::DefaultNode::DefaultNodeType>
74 class MapUtils {
75 #undef XPETRA_MAPUTILS_SHORT
77 
78  public:
93  static Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > concatenateMaps(const std::vector<Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > >& subMaps) {
94  // ToDo Resolve header issues to allow for using this routing in Xpetra::BlockedMap.
95 
96  // merge submaps to global map
97  std::vector<GlobalOrdinal> gids;
98  for (size_t tt = 0; tt < subMaps.size(); ++tt) {
99  Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > subMap = subMaps[tt];
100  Teuchos::ArrayView<const GlobalOrdinal> subMapGids = subMap->getLocalElementList();
101  gids.insert(gids.end(), subMapGids.begin(), subMapGids.end());
102  }
103 
104  const GlobalOrdinal INVALID = Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid();
105  // std::sort(gids.begin(), gids.end());
106  // gids.erase(std::unique(gids.begin(), gids.end()), gids.end());
107  Teuchos::ArrayView<GlobalOrdinal> gidsView(&gids[0], gids.size());
108  Teuchos::RCP<Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > fullMap = Xpetra::MapFactory<LocalOrdinal, GlobalOrdinal, Node>::Build(subMaps[0]->lib(), INVALID, gidsView, subMaps[0]->getIndexBase(), subMaps[0]->getComm());
109  return fullMap;
110  }
111 
127  static Teuchos::RCP<Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> >
130  TEUCHOS_TEST_FOR_EXCEPTION(nonOvlInput.getLocalNumElements() > input.getLocalNumElements(),
132  "Xpetra::MatrixUtils::shrinkMapGIDs: the non-overlapping map must not have more local ids than the overlapping map.")
133 
134  TEUCHOS_TEST_FOR_EXCEPTION(nonOvlInput.getMaxAllGlobalIndex() != input.getMaxAllGlobalIndex(),
136  "Xpetra::MatrixUtils::shrinkMapGIDs: the maximum GIDs of the overlapping and non-overlapping maps must be the same.")
137 
138  RCP<const Teuchos::Comm<int> > comm = input.getComm();
139 
140  // we expect input to be the potentially overlapping map associated with nonOvlInput as the non-overlapping
141  // map with the same GIDs over all processors (e.g. column map and domain map). We use the nonOvlInput map
142  // to determine which GIDs are owned by which processor.
143 
144  // calculate offset for new global Ids
145  std::vector<int> myGIDs(comm->getSize(), 0);
146  std::vector<int> numGIDs(comm->getSize(), 0);
147  myGIDs[comm->getRank()] = nonOvlInput.getLocalNumElements();
148  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, comm->getSize(), &myGIDs[0], &numGIDs[0]);
149  size_t gidOffset = 0;
150  for (int p = 0; p < comm->getRank(); p++) gidOffset += numGIDs[p];
151 
152  // we use nonOvlInput to assign the globally unique shrinked GIDs and communicate them to input.
153  std::map<const GlobalOrdinal, GlobalOrdinal> origGID2newGID;
154  for (size_t i = 0; i < nonOvlInput.getLocalNumElements(); i++) {
155  origGID2newGID[nonOvlInput.getGlobalElement(i)] = Teuchos::as<GlobalOrdinal>(i) + Teuchos::as<GlobalOrdinal>(gidOffset);
156  }
157  // build an overlapping version of mySpecialMap
158  Teuchos::Array<GlobalOrdinal> ovlUnknownStatusGids;
159  Teuchos::Array<GlobalOrdinal> ovlFoundStatusGids;
160  // loop over global column map of A and find all GIDs where it is not sure, whether they are special or not
161  for (size_t i = 0; i < input.getLocalNumElements(); i++) {
162  GlobalOrdinal gcid = input.getGlobalElement(i);
163  if (nonOvlInput.isNodeGlobalElement(gcid) == false) {
164  ovlUnknownStatusGids.push_back(gcid);
165  }
166  }
167 
168  // Communicate the number of DOFs on each processor
169  std::vector<int> myUnknownDofGIDs(comm->getSize(), 0);
170  std::vector<int> numUnknownDofGIDs(comm->getSize(), 0);
171  myUnknownDofGIDs[comm->getRank()] = ovlUnknownStatusGids.size();
172  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, comm->getSize(), &myUnknownDofGIDs[0], &numUnknownDofGIDs[0]);
173 
174  // create array containing all DOF GIDs
175  size_t cntUnknownDofGIDs = 0;
176  for (int p = 0; p < comm->getSize(); p++) cntUnknownDofGIDs += numUnknownDofGIDs[p];
177  std::vector<GlobalOrdinal> lUnknownDofGIDs(cntUnknownDofGIDs, 0); // local version to be filled
178  std::vector<GlobalOrdinal> gUnknownDofGIDs(cntUnknownDofGIDs, 0); // global version after communication
179  // calculate the offset and fill chunk of memory with local data on each processor
180  size_t cntUnknownOffset = 0;
181  for (int p = 0; p < comm->getRank(); p++) cntUnknownOffset += numUnknownDofGIDs[p];
182  for (size_t k = 0; k < Teuchos::as<size_t>(ovlUnknownStatusGids.size()); k++) {
183  lUnknownDofGIDs[k + cntUnknownOffset] = ovlUnknownStatusGids[k];
184  }
185  if (cntUnknownDofGIDs > 0) // only perform communication if there are unknown DOF GIDs
186  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, Teuchos::as<int>(cntUnknownDofGIDs), &lUnknownDofGIDs[0], &gUnknownDofGIDs[0]);
187  std::vector<GlobalOrdinal> lTranslatedDofGIDs(cntUnknownDofGIDs, 0); // local version to be filled
188  std::vector<GlobalOrdinal> gTranslatedDofGIDs(cntUnknownDofGIDs, 0); // global version after communication
189  // loop through all GIDs with unknown status
190  for (size_t k = 0; k < gUnknownDofGIDs.size(); k++) {
191  GlobalOrdinal curgid = gUnknownDofGIDs[k];
192  if (nonOvlInput.isNodeGlobalElement(curgid)) {
193  lTranslatedDofGIDs[k] = origGID2newGID[curgid]; // curgid is in special map (on this processor)
194  }
195  }
196  if (cntUnknownDofGIDs > 0) // only perform communication if there are unknown DOF GIDs
197  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, Teuchos::as<int>(cntUnknownDofGIDs), &lTranslatedDofGIDs[0], &gTranslatedDofGIDs[0]);
198 
199  for (size_t k = 0; k < Teuchos::as<size_t>(ovlUnknownStatusGids.size()); k++) {
200  origGID2newGID[ovlUnknownStatusGids[k]] = gTranslatedDofGIDs[k + cntUnknownOffset];
201  }
202  Teuchos::Array<GlobalOrdinal> ovlDomainMapArray;
203  for (size_t i = 0; i < input.getLocalNumElements(); i++) {
204  GlobalOrdinal gcid = input.getGlobalElement(i);
205  ovlDomainMapArray.push_back(origGID2newGID[gcid]);
206  }
207  RCP<Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > ovlDomainMap =
208  Xpetra::MapFactory<LocalOrdinal, GlobalOrdinal, Node>::Build(nonOvlInput.lib(), Teuchos::OrdinalTraits<GlobalOrdinal>::invalid(), ovlDomainMapArray(), 0, comm);
209  return ovlDomainMap;
210  }
211 
228  static Teuchos::RCP<Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > transformThyra2XpetraGIDs(
231  const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>& nonOvlReferenceInput) {
232  // TEUCHOS_TEST_FOR_EXCEPTION(nonOvlInput.getLocalNumElements() > input.getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::transformThyra2XpetraGIDs: the non-overlapping map must not have more local ids than the overlapping map.");
233  TEUCHOS_TEST_FOR_EXCEPTION(nonOvlInput.getLocalNumElements() != nonOvlReferenceInput.getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::transformThyra2XpetraGIDs: the number of local Xpetra reference GIDs and local Thyra GIDs of the non-overlapping maps must be the same!");
234  // TEUCHOS_TEST_FOR_EXCEPTION(nonOvlInput.getMaxAllGlobalIndex() != input.getMaxAllGlobalIndex(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::transformThyra2XpetraGIDs: the maximum GIDs of the overlapping and non-overlapping maps must be the same. nonOvlInput.getMaxAllGlobalIndex() = " << nonOvlInput.getMaxAllGlobalIndex() << " ovlInput.getMaxAllGlobalIndex() = " << input.getMaxAllGlobalIndex());
235 
236  RCP<const Teuchos::Comm<int> > comm = input.getComm();
237 
238  // fill translation map as far as possible
239  std::map<const GlobalOrdinal, GlobalOrdinal> thyra2xpetraGID;
240  for (size_t i = 0; i < nonOvlInput.getLocalNumElements(); i++) {
241  thyra2xpetraGID[nonOvlInput.getGlobalElement(i)] =
242  nonOvlReferenceInput.getGlobalElement(i);
243  }
244 
245  // find all GIDs of the overlapping Thyra map which are not owned by this proc
246  Teuchos::Array<GlobalOrdinal> ovlUnknownStatusGids;
247  // loop over global column map of A and find all GIDs where it is not sure, whether they are special or not
248  for (size_t i = 0; i < input.getLocalNumElements(); i++) {
249  GlobalOrdinal gcid = input.getGlobalElement(i);
250  if (nonOvlInput.isNodeGlobalElement(gcid) == false) {
251  ovlUnknownStatusGids.push_back(gcid);
252  }
253  }
254 
255  // Communicate the number of DOFs on each processor
256  std::vector<int> myUnknownDofGIDs(comm->getSize(), 0);
257  std::vector<int> numUnknownDofGIDs(comm->getSize(), 0);
258  myUnknownDofGIDs[comm->getRank()] = ovlUnknownStatusGids.size();
259  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, comm->getSize(), &myUnknownDofGIDs[0], &numUnknownDofGIDs[0]);
260 
261  // create array containing all DOF GIDs
262  size_t cntUnknownDofGIDs = 0;
263  for (int p = 0; p < comm->getSize(); p++) cntUnknownDofGIDs += numUnknownDofGIDs[p];
264  std::vector<GlobalOrdinal> lUnknownDofGIDs(cntUnknownDofGIDs, 0); // local version to be filled
265  std::vector<GlobalOrdinal> gUnknownDofGIDs(cntUnknownDofGIDs, 0); // global version after communication
266  // calculate the offset and fill chunk of memory with local data on each processor
267  size_t cntUnknownOffset = 0;
268  for (int p = 0; p < comm->getRank(); p++) cntUnknownOffset += numUnknownDofGIDs[p];
269  for (size_t k = 0; k < Teuchos::as<size_t>(ovlUnknownStatusGids.size()); k++) {
270  lUnknownDofGIDs[k + cntUnknownOffset] = ovlUnknownStatusGids[k];
271  }
272  if (cntUnknownDofGIDs > 0) // only perform communication if there are unknown DOF GIDs
273  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, Teuchos::as<int>(cntUnknownDofGIDs), &lUnknownDofGIDs[0], &gUnknownDofGIDs[0]);
274  std::vector<GlobalOrdinal> lTranslatedDofGIDs(cntUnknownDofGIDs, 0); // local version to be filled
275  std::vector<GlobalOrdinal> gTranslatedDofGIDs(cntUnknownDofGIDs, 0); // global version after communication
276  // loop through all GIDs with unknown status
277  for (size_t k = 0; k < gUnknownDofGIDs.size(); k++) {
278  GlobalOrdinal curgid = gUnknownDofGIDs[k];
279  if (nonOvlInput.isNodeGlobalElement(curgid)) {
280  lTranslatedDofGIDs[k] = thyra2xpetraGID[curgid];
281  }
282  }
283  if (cntUnknownDofGIDs > 0) // only perform communication if there are unknown DOF GIDs
284  Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, Teuchos::as<int>(cntUnknownDofGIDs), &lTranslatedDofGIDs[0], &gTranslatedDofGIDs[0]);
285 
286  for (size_t k = 0; k < Teuchos::as<size_t>(ovlUnknownStatusGids.size()); k++) {
287  thyra2xpetraGID[ovlUnknownStatusGids[k]] = gTranslatedDofGIDs[k + cntUnknownOffset];
288  }
289  Teuchos::Array<GlobalOrdinal> ovlDomainMapArray;
290  for (size_t i = 0; i < input.getLocalNumElements(); i++) {
291  GlobalOrdinal gcid = input.getGlobalElement(i);
292  ovlDomainMapArray.push_back(thyra2xpetraGID[gcid]);
293  }
294  RCP<Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > ovlDomainMap =
295  Xpetra::MapFactory<LocalOrdinal, GlobalOrdinal, Node>::Build(nonOvlInput.lib(), Teuchos::OrdinalTraits<GlobalOrdinal>::invalid(), ovlDomainMapArray(), 0, comm);
296 
297  TEUCHOS_TEST_FOR_EXCEPTION(input.getLocalNumElements() != ovlDomainMap->getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::transformThyra2XpetraGIDs: the number of local Thyra reference GIDs (overlapping) and local Xpetra GIDs (overlapping) must be the same!");
298  // TEUCHOS_TEST_FOR_EXCEPTION(nonOvlReferenceInput.getMaxAllGlobalIndex() != ovlDomainMap->getMaxAllGlobalIndex(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::transformThyra2XpetraGIDs: the maximum GIDs of the overlapping and non-overlapping Xpetra maps must be the same.");
299 
300  return ovlDomainMap;
301  }
302 
309  static Teuchos::RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > transformThyra2XpetraGIDs(
310  const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>& input, GlobalOrdinal offset) {
311  const GO INVALID = Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid();
312  RCP<const Teuchos::Comm<int> > comm = input.getComm();
313 
314  RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> > rcpInput = Teuchos::rcpFromRef(input);
315 
316  // check whether input map is a BlockedMap or a standard Map
317  RCP<const Xpetra::BlockedMap<LocalOrdinal, GlobalOrdinal, Node> > rcpBlockedInput = Teuchos::rcp_dynamic_cast<const Xpetra::BlockedMap<LocalOrdinal, GlobalOrdinal, Node> >(rcpInput);
318  if (rcpBlockedInput.is_null() == true) {
319  // create a new map with Xpetra GIDs (may start not from GID = 0)
320  std::vector<GlobalOrdinal> gids;
321  for (LocalOrdinal l = 0; l < Teuchos::as<LocalOrdinal>(rcpInput->getLocalNumElements()); ++l) {
322  GlobalOrdinal gid = rcpInput->getGlobalElement(l) + offset;
323  gids.push_back(gid);
324  }
325  Teuchos::ArrayView<GO> gidsView(&gids[0], gids.size());
326  RCP<Map> fullMap = MapFactory::Build(rcpInput->lib(), INVALID, gidsView, rcpInput->getIndexBase(), comm);
327  return fullMap;
328  }
329 
330  // SPECIAL CASE: input is a blocked map
331  // we have to recursively call this routine to get a BlockedMap containing (unique) Xpetra style GIDs
332 
333  size_t numMaps = rcpBlockedInput->getNumMaps();
334 
335  // first calucale GID offsets in submaps
336  // we need that for generating Xpetra GIDs
337  std::vector<GlobalOrdinal> gidOffsets(numMaps, 0);
338  for (size_t i = 1; i < numMaps; ++i) {
339  gidOffsets[i] = rcpBlockedInput->getMap(i - 1, true)->getMaxAllGlobalIndex() + gidOffsets[i - 1] + 1;
340  }
341 
342  std::vector<RCP<const Map> > mapsXpetra(rcpBlockedInput->getNumMaps(), Teuchos::null);
343  std::vector<RCP<const Map> > mapsThyra(rcpBlockedInput->getNumMaps(), Teuchos::null);
344  for (size_t b = 0; b < rcpBlockedInput->getNumMaps(); ++b) {
345  // extract sub map with Thyra style gids
346  // this can be an underlying Map or BlockedMap object
347  RCP<const Map> subMapThyra = rcpBlockedInput->getMap(b, true);
348  RCP<const Map> subMapXpetra = MapUtils::transformThyra2XpetraGIDs(*subMapThyra, gidOffsets[b] + offset); // recursive call
349  mapsXpetra[b] = subMapXpetra; // map can be of type Map or BlockedMap
350  mapsThyra[b] = subMapThyra; // map can be of type Map or BlockedMap
351  }
352 
353  Teuchos::RCP<Map> resultMap = Teuchos::rcp(new Xpetra::BlockedMap<LocalOrdinal, GlobalOrdinal, Node>(mapsXpetra, mapsThyra));
354  return resultMap;
355  }
356 };
357 
358 } // end namespace Xpetra
359 
360 #define XPETRA_MAPUTILS_SHORT
361 
362 #endif // PACKAGES_XPETRA_SUP_MAP_UTILS_HPP_
virtual Teuchos::RCP< const Teuchos::Comm< int > > getComm() const =0
Get this Map&#39;s Comm object.
virtual size_t getLocalNumElements() const =0
The number of elements belonging to the calling process.
static Teuchos::RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > concatenateMaps(const std::vector< Teuchos::RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > > &subMaps)
Helper function to concatenate several maps.
virtual bool isNodeGlobalElement(GlobalOrdinal globalIndex) const =0
Whether the given global index is valid for this Map on this process.
static Teuchos::RCP< Map< LocalOrdinal, GlobalOrdinal, Node > > Build(UnderlyingLib lib, global_size_t numGlobalElements, GlobalOrdinal indexBase, const Teuchos::RCP< const Teuchos::Comm< int >> &comm, LocalGlobal lg=Xpetra::GloballyDistributed)
Map constructor with Xpetra-defined contiguous uniform distribution.
static Teuchos::RCP< Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > transformThyra2XpetraGIDs(const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &input, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &nonOvlInput, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &nonOvlReferenceInput)
replace set of global ids by new global ids
virtual GlobalOrdinal getMaxAllGlobalIndex() const =0
The maximum global index over all processes in the communicator.
virtual GlobalOrdinal getGlobalElement(LocalOrdinal localIndex) const =0
The global index corresponding to the given local index.
static Teuchos::RCP< Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > shrinkMapGIDs(const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &input, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &nonOvlInput)
Helper function to shrink the GIDs and generate a standard map whith GIDs starting at 0...
virtual GlobalOrdinal getGlobalElement(LocalOrdinal localIndex) const
The global index corresponding to the given local index.
Exception throws to report incompatible objects (like maps).
static Teuchos::RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > transformThyra2XpetraGIDs(const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &input, GlobalOrdinal offset)
replace set of global ids by new global ids
virtual UnderlyingLib lib() const =0
Get the library used by this object (Tpetra or Epetra?)