Xpetra - a short introduction

Philosophy

  • Goal: write algorithms independent from {E,T}petra which easily allow to work on underlying data
  • Xpetra provides an abstract interface-wrapper for both Epetra and Tpetra
  • Xpetra follows the Tpetra user interface standards
  • Xpetra extends the logic of {E,T}petra by blocked objects (e.g., for multiphysics problems)
  • Xpetra is Thyra compatible
    • in contrast to Thyra, easy-access to underlying data
    • Xpetra ideal to write algorithms that operate on the data
  • Xpetra has a Kokkos interface (including an Epetra compatibility layer)

Basic design concept

  • For standard linear algebra objects (e.g. Map, MultiVector, CrsMatrix,...)
    • Define abstract class and implement specializations for both {E,T}petra
    • Provide a Factory class to create new instances of the linear algebra objects without writing {E,T}petra specific code
  • Further classes for blocked linear algebra objects (e.g., BlockedMap, BlockedMultiVector,...)

Instructions for writing algorithms using Xpetra

  • write algorithms using Xpetra only
  • by all means avoid Epetra/Tpetra specific code
  • use Factory classes to generate new objects
  • The factory classes "automatically" choose the right linear algebra framework underneath

Example: how to transform an Epetra_CrsMatrix to a Xpetra::Matrix?

Declare location of include headers


In [1]:
.I /opt/install/do-conf-ep-serial/include/


Out[1]:

Load Trilinos libraries


In [2]:
.L libepetra


Out[2]:

1. Create a tridiagonal matrix of size $20\times 20$ and store it in an Epetra_CrsMatrix object

Create a serial communicator...


In [3]:
#include "Epetra_SerialComm.h"


Out[3]:


In [4]:
Epetra_SerialComm Comm;


Out[4]:
(Epetra_SerialComm &) @0x7f850ea31018

... and define the matrix size.


In [5]:
int NumMyElements = 20;


Out[5]:
(int) 20

Create a new map and fill the matrix.


In [6]:
#include "Epetra_Map.h"
#include "Epetra_CrsMatrix.h"


Out[6]:


In [7]:
Epetra_Map Map(-1, NumMyElements, 0, Comm);
int NumGlobalElements = Map.NumGlobalElements();

Epetra_CrsMatrix A(Copy, Map, 3);

double negOne = -1.0;
double posTwo = 2.0;
for (int i=0; i<NumMyElements; i++) {
    int GlobalRow = A.GRID(i); int RowLess1 = GlobalRow - 1; int RowPlus1 = GlobalRow + 1;

    if (RowLess1!=-1) A.InsertGlobalValues(GlobalRow, 1, &negOne, &RowLess1);
    if (RowPlus1!=NumGlobalElements) A.InsertGlobalValues(GlobalRow, 1, &negOne, &RowPlus1);
    A.InsertGlobalValues(GlobalRow, 1, &posTwo, &GlobalRow);
};

A.FillComplete();


Out[7]:
(int) 0

2. Wrap it into an Xpetra object

The following code demonstrates how to wrap the Epetra_CrsMatrix object into an Xpetra::Matrix object that serves as abstract input class for most of the algebraic multigrid algorithms in MueLu.

Even though the example uses Epetra, the procedure for Tpetra is similar.


In [8]:
.L libxpetra


Out[8]:

Package-specific wrapper class: {E,T}petraCrsMatrix

First, we have to include some header files:


In [9]:
#include <Kokkos_DefaultNode.hpp>


Out[9]:

Next, we declare some typedefs:


In [10]:
typedef double Scalar;
typedef int LocalOrdinal;
typedef int GlobalOrdinal;
typedef Kokkos::Compat::KokkosSerialWrapperNode EpetraNode;
typedef EpetraNode Node;


Out[10]:

If you know Tpetra you are certainly familiar with most of the template parameters. Xpetra is templated on the exact same template parameters. Therefore, we have to provide compatible template parameters for the Epetra branch in Xpetra.

Please note the EpetraNode typedef which can be either the SerialNode or OpenMPNode depending on the configuration of Epetra on your machine. This notebook only contains a standard Epetra installation using GO=int on the SerialNode.

To improve the readability of the source code, you should include the Xpetra_UseShortNames.hpp file after the typedefs. It contains typedefs of all Xpetra classes with the corresponding template parameters defined above. This allows us to write, e.g., Map instead of Xpetra::Map<LocalOrdinal,GlobalOrdinal,Node> later in the code. However, this only works if your code lives in the Xpetra namespace. This is not the case in this notebook. So, we have to write the long version. At least, we can shorten the template parameters using SC, LO, GO, NO. The corresponding typedefs come with the inclusion of Xpetra_UseShortNames.hpp


In [11]:
#include "Xpetra_UseShortNames.hpp"


Out[11]:

Xpetra_EpetraCrsMatrix.hpp defines the class which encapsulates an Epetra_CrsMatrix object in Xpetra.


In [12]:
#include "Xpetra_EpetraCrsMatrix.hpp"


Out[12]:

The constructor of Xpetra::EpetraCrsMatrix only accepts an RCP pointer to an Epetra_CrsMatrix object. So, let's wrap A into an rcp pointer:


In [13]:
#include "Teuchos_RCP.hpp"


Out[13]:


In [14]:
Teuchos::RCP<Epetra_CrsMatrix> rcpA = Teuchos::rcpFromRef(A);
std::cout << rcpA << std::endl;


Teuchos::RCP<Epetra_CrsMatrix>{ptr=0x7f850ea31060,node=0x861d510,strong_count=1,weak_count=0}
Out[14]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

Then call the constructor


In [15]:
Xpetra::EpetraCrsMatrixT<GO,NO> xA(rcpA);


Out[15]:
(Xpetra::EpetraCrsMatrixT<GO, NO> &) @0x7f850ea311c0

In [16]:
std::cout << *rcpA << std::endl;


Epetra::CrsMatrix
Number of Global Rows        = 20
Number of Global Cols        = 20
Number of Global Diagonals   = 20
Number of Global Nonzeros    = 58
Global Maximum Num Entries   = 3





Number of My Rows        = 20
Number of My Cols        = 20
Number of My Diagonals   = 20
Number of My Nonzeros    = 58
My Maximum Num Entries   = 3

   Processor    Row Index    Col Index           Value     
       0             0             0                       2    
       0             0             1                      -1    
       0             1             0                      -1    
       0             1             1                       2    
       0             1             2                      -1    
       0             2             1                      -1    
       0             2             2                       2    
       0             2             3                      -1    
       0             3             2                      -1    
       0             3             3                       2    
       0             3             4                      -1    
       0             4             3                      -1    
       0             4             4                       2    
       0             4             5                      -1    
       0             5             4                      -1    
       0             5             5                       2    
       0             5             6                      -1    
       0             6             5                      -1    
       0             6             6                       2    
       0             6             7                      -1    
       0             7             6                      -1    
       0             7             7                       2    
       0             7             8                      -1    
       0             8             7                      -1    
       0             8             8                       2    
       0             8             9                      -1    
       0             9             8                      -1    
       0             9             9                       2    
       0             9            10                      -1    
       0            10             9                      -1    
       0            10            10                       2    
       0            10            11                      -1    
       0            11            10                      -1    
       0            11            11                       2    
       0            11            12                      -1    
       0            12            11                      -1    
       0            12            12                       2    
       0            12            13                      -1    
       0            13            12                      -1    
       0            13            13                       2    
       0            13            14                      -1    
       0            14            13                      -1    
       0            14            14                       2    
       0            14            15                      -1    
       0            15            14                      -1    
       0            15            15                       2    
       0            15            16                      -1    
       0            16            15                      -1    
       0            16            16                       2    
       0            16            17                      -1    
       0            17            16                      -1    
       0            17            17                       2    
       0            17            18                      -1    
       0            18            17                      -1    
       0            18            18                       2    
       0            18            19                      -1    
       0            19            18                      -1    
       0            19            19                       2    

Out[16]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

The {E,T}petraCrsMatrix class are package-specific wrappers for Epetra and Tpetra which encapsulate an Epetra Epetra_CrsMatrix or an Tpetra Tpetra::CrsMatrix object.

The EpetraCrsMatrix object is templated on the GlobalOrdinal and NodeType. GlobalOrdinal can be either int (default) or long long depending on the Epetra configuration. In this container we only support GO=int. The NodeType template parameter should be Xpetra::EpetraNode which translates to the serial or OpenMP node, depending on the configuration of Epetra. Note, that Epetra can only be compiled for one choice of GlobalOrdinal and NodeType.

The TpetraCrsMatrix object is templated on the Tpetra template parameters, which include Scalar, LocalOrdinal, GlobalOrdinal and Node.

Xpetra consequently follows the Tpetra style and naming conventions for functions.

Wrap the CrsMatrix class

Next, one has to wrap the Xpetra::CrsMatrix object into an Xpetra::Matrix object. This can be done with the Xpetra::CrsMatrixWrap class.


In [17]:
#include "Xpetra_CrsMatrixWrap.hpp"
#include "Xpetra_CrsMatrix.hpp"


Out[17]:

Again, we have to use RCP pointers from the Teuchos package.


In [18]:
Teuchos::RCP<Xpetra::CrsMatrix<SC,LO,GO,NO> > xCrsMat = Teuchos::rcpFromRef(xA);


Out[18]:
(Teuchos::RCP<Xpetra::CrsMatrix<SC, LO, GO, NO> > &) @0x7f850ea31200

In [19]:
Teuchos::RCP<Xpetra::Matrix<SC,LO,GO,NO> > xMat = Teuchos::rcp(new Xpetra::CrsMatrixWrap<SC,LO,GO,NO>(xCrsMat));
GO numRows = xMat->getGlobalNumRows();
auto numEntries = xMat->getNodeNumEntries();
std::cout << "The matrix has " << numRows << " rows and " << numEntries << " nonzeros." << std::endl;


The matrix has 20 rows and 58 nonzeros.
Out[19]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

The object xMat is an RCP pointer to a Xpetra::Matrix object that can be used as (abstract) input to MueLu.

It supports the Tpetra style interface, i.e., you can basically use it like an Tpetra object.

3. How can i access the underlying {E,T}petra CrsMatrix object?

First of all: you never should need that!

Xpetra is meant to provide a common wrapper for {E,T}petra which allows you to write algorithms independent from the concrete implementation of {E,T}petra, but providing a Petra like API! This is a big advantage compared to, e.g., Thyra, which has a similar intention, but is too abstract for writing algorithms which operate on the low-level data (like matrix entries).

Nevertheless, here is how you can access the underlying Epetra_CrsMatrix object given an Xpetra::Matrix object:


In [20]:
Teuchos::RCP<Xpetra::CrsMatrixWrap<SC,LO,GO,NO> > xCrsMatWrap = Teuchos::rcp_dynamic_cast<Xpetra::CrsMatrixWrap<SC,LO,GO,NO> >(xMat);
Teuchos::RCP<Xpetra::CrsMatrix<SC,LO,GO,NO> > xCrsMatTmp = xCrsMatWrap->getCrsMatrix();
Teuchos::RCP<Xpetra::EpetraCrsMatrixT<GO,NO> > xEpCrsMatTmp = Teuchos::rcp_dynamic_cast<Xpetra::EpetraCrsMatrixT<GO,NO>>(xCrsMatTmp);
Teuchos::RCP<const Epetra_CrsMatrix> epMat = xEpCrsMatTmp->getEpetra_CrsMatrix();
std::cout << "The Epetra matrix has " << epMat->NumGlobalRows()<< " rows." << std::endl;


The Epetra matrix has 20 rows.
Out[20]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

Again: you should not need this. As you can see from the last code snippet, it introduces {E,T}petra specific code which is against the Xpetra philosophy. You use Xpetra to write code independent from {E,T}petra!

Example: how to wrap a (Multi)Vector?

Again, let's assume we have an Epetra_Vector:


In [21]:
Epetra_Vector x(Map);
x.Random();


Out[21]:
(int) 0

We can wrap it into a Xpetra::EpetraVectorT class using


In [22]:
#include "Xpetra_EpetraVector.hpp"


Out[22]:


In [23]:
Xpetra::EpetraVectorT<GO,NO> xx(Teuchos::rcpFromRef(x));


Out[23]:
(Xpetra::EpetraVectorT<GO, NO> &) @0x7f850ea31360

Let's print the content of the vector. It contains the random values of the underlying Epetra vector.


In [24]:
std::cout << xx << std::endl;


     MyPID           GID               Value  
         0             0               -0.630518
         0             1                0.879819
         0             2               -0.877245
         0             3                0.150899
         0             4                0.165755
         0             5               -0.151011
         0             6              -0.0357566
         0             7               -0.961937
         0             8                0.722493
         0             9                0.934016
         0            10              0.00486903
         0            11                -0.16616
         0            12               -0.657289
         0            13                0.946171
         0            14                0.291264
         0            15               -0.727955
         0            16               -0.743651
         0            17               -0.542321
         0            18               -0.792288
         0            19               0.0205202

Out[24]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

The Xpetra::EpetraVectorT class is derived from the Xpetra::Vector class:


In [25]:
Teuchos::RCP<Xpetra::Vector<SC,LO,GO,NO> > xv = Teuchos::rcpFromRef(xx);
std::cout << *xv << std::endl;


     MyPID           GID               Value  
         0             0               -0.630518
         0             1                0.879819
         0             2               -0.877245
         0             3                0.150899
         0             4                0.165755
         0             5               -0.151011
         0             6              -0.0357566
         0             7               -0.961937
         0             8                0.722493
         0             9                0.934016
         0            10              0.00486903
         0            11                -0.16616
         0            12               -0.657289
         0            13                0.946171
         0            14                0.291264
         0            15               -0.727955
         0            16               -0.743651
         0            17               -0.542321
         0            18               -0.792288
         0            19               0.0205202

Out[25]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

To wrap multivectors for {E,T}petra requires a similar procedure.

How to create new Xpetra::Vector objects in an algorithm independent from {E,T}petra?

If you write an algorithm in Xpetra (independent from the concrete {E,T}petra implementation), you may need to create objects such as (multi)vectors or matrices. You cannot use the {E,T}petra wrappers given by, e.g., Xpetra::EpetraMultiVector or Xpetra::TpetraCrsMatrix, since this would make your code {E,T}petra specific.

Instead, you use the Xpetra factory classes to create new instances of maps, vectors, multivectors, matrices, etc...

Create a new Map


In [26]:
#include "Xpetra_MapFactory.hpp"
#include "Xpetra_Map.hpp"
#include "Teuchos_DefaultSerialComm.hpp"


Out[26]:

Xpetra uses the communicator object from Teuchos:


In [27]:
Teuchos::RCP<const Teuchos::Comm<int> > comm = Xpetra::toXpetra(Comm);


Out[27]:
(Teuchos::RCP<const Teuchos::Comm<int> > &) @0x7f850ea313c8

The factory classes have Build routines which accept a flag Xpetra::Use{E,T}petra and user parameters for the constructor of the Map object. The code, e.g., could look like:


In [28]:
Xpetra::UnderlyingLib lib = Xpetra::UseEpetra;


Out[28]:
(Xpetra::UnderlyingLib) (Xpetra::UnderlyingLib::UseEpetra) : (unsigned int) 0

In [29]:
Teuchos::RCP<Xpetra::Map<LO,GO,NO> > xm = Xpetra::MapFactory<LO,GO,NO>::Build(lib,10,0,comm);


Out[29]:
(Teuchos::RCP<Xpetra::Map<LO, GO, NO> > &) @0x7f850ea313e8

By consequently using lib when creating map objects through Xpetra factories one can quickly switch from an Epetra to a Tpetra implementation by setting lib = Xpetra::UseTpetra. No further code changes necessary.


In [30]:
std::cout << *xm <<std::endl;


 
 Number of Global Entries = 10
 Maximum of all GIDs      = 9
 Minimum of all GIDs      = 0
 Index Base               = 0
 

Out[30]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

Create a new MultiVector based on Map

There is a Xpetra::MultiVectorFactory which works the same way:


In [31]:
#include "Xpetra_MultiVector.hpp"
#include "Xpetra_Vector.hpp"
#include "Xpetra_EpetraMultiVector.hpp"
#include "Xpetra_BlockedMultiVector.hpp"
#include "Xpetra_BlockedVector.hpp"


Out[31]:


In [32]:
#include "Xpetra_MultiVectorFactory.hpp"


Out[32]:

The underlying implementation (Epetra or Tpetra) is already specified by the type of the map.


In [33]:
Teuchos::RCP<Xpetra::MultiVector<SC,LO,GO,NO> > xmv = Xpetra::MultiVectorFactory<SC,LO,GO,NO>::Build(xm, 3);
xmv->putScalar(42.0);
std::cout << *xmv << std::endl;


     MyPID           GID               Value               Value               Value  
         0             0                      42                  42                  42
         0             1                      42                  42                  42
         0             2                      42                  42                  42
         0             3                      42                  42                  42
         0             4                      42                  42                  42
         0             5                      42                  42                  42
         0             6                      42                  42                  42
         0             7                      42                  42                  42
         0             8                      42                  42                  42
         0             9                      42                  42                  42

Out[33]:
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7f8506776700

In [ ]: