Ifpack2 Templated Preconditioning Package Version 1.0
Ifpack2_Krylov_def.hpp
00001 /*@HEADER
00002 // ***********************************************************************
00003 //
00004 //       Ifpack2: Tempated Object-Oriented Algebraic Preconditioner Package
00005 //                 Copyright (2009) Sandia Corporation
00006 //
00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00008 // license for use of this work by or on behalf of the U.S. Government.
00009 //
00010 // Redistribution and use in source and binary forms, with or without
00011 // modification, are permitted provided that the following conditions are
00012 // met:
00013 //
00014 // 1. Redistributions of source code must retain the above copyright
00015 // notice, this list of conditions and the following disclaimer.
00016 //
00017 // 2. Redistributions in binary form must reproduce the above copyright
00018 // notice, this list of conditions and the following disclaimer in the
00019 // documentation and/or other materials provided with the distribution.
00020 //
00021 // 3. Neither the name of the Corporation nor the names of the
00022 // contributors may be used to endorse or promote products derived from
00023 // this software without specific prior written permission.
00024 //
00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00036 //
00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
00038 //
00039 // ***********************************************************************
00040 //@HEADER
00041 */
00042 
00043 #ifndef IFPACK2_KRYLOV_DEF_HPP
00044 #define IFPACK2_KRYLOV_DEF_HPP
00045 
00046 #include "Ifpack2_Krylov_decl.hpp"
00047 
00048 namespace Ifpack2 {
00049 
00050 template <class MatrixType>
00051 Krylov<MatrixType>::
00052 Krylov (const Teuchos::RCP<const row_matrix_type>& A) :
00053   A_ (A),
00054   // Default values
00055   iterationType_ ("GMRES"),
00056   numIters_ (5),
00057   resTol_ (0.001), // FIXME (mfh 17 Jan 2014) Make a function of STS::eps()
00058   BlockSize_ (1),
00059   ZeroStartingSolution_ (true),
00060   PreconditionerType_ (1),
00061   // General
00062   Condest_ (-STM::one ()),
00063   IsInitialized_ (false),
00064   IsComputed_ (false),
00065   NumInitialize_ (0),
00066   NumCompute_ (0),
00067   NumApply_ (0),
00068   InitializeTime_ (0.0),
00069   ComputeTime_ (0.0),
00070   ApplyTime_ (0.0)
00071 {}
00072 
00073 
00074 template <class MatrixType>
00075 Krylov<MatrixType>::~Krylov() {}
00076 
00077 
00078 template <class MatrixType>
00079 void Krylov<MatrixType>::setMatrix (const Teuchos::RCP<const row_matrix_type>& A)
00080 {
00081   // Check in serial or one-process mode if the matrix is square.
00082   TEUCHOS_TEST_FOR_EXCEPTION(
00083     ! A.is_null () && A->getComm ()->getSize () == 1 &&
00084     A->getNodeNumRows () != A->getNodeNumCols (),
00085     std::runtime_error, "Ifpack2::Krylov::setMatrix: If A's communicator only "
00086     "contains one process, then A must be square.  Instead, you provided a "
00087     "matrix A with " << A->getNodeNumRows () << " rows and "
00088     << A->getNodeNumCols () << " columns.");
00089 
00090   // It's legal for A to be null; in that case, you may not call
00091   // initialize() until calling setMatrix() with a nonnull input.
00092   // Regardless, setting the matrix invalidates any previous
00093   // factorization.
00094   IsInitialized_ = false;
00095   IsComputed_ = false;
00096   Condest_ = -STM::one ();
00097 
00098   A_ = A;
00099 }
00100 
00101 
00102 template <class MatrixType>
00103 void Krylov<MatrixType>::setParameters (const Teuchos::ParameterList& plist)
00104 {
00105   using Teuchos::as;
00106   using Teuchos::ParameterList;
00107   using Teuchos::Exceptions::InvalidParameterName;
00108   using Teuchos::Exceptions::InvalidParameterType;
00109 
00110   ParameterList params = plist;
00111 
00112   // Get the current parameters' values.  We don't assign to the
00113   // instance data directly until we've gotten all the parameters.
00114   // This ensures "transactional" semantics, so that if attempting to
00115   // get some parameter throws an exception, the class' state doesn't
00116   // change.
00117   magnitude_type resTol = resTol_;
00118   int numIters = numIters_;
00119   std::string iterType = iterationType_;
00120   int blockSize = BlockSize_;
00121   bool zeroStartingSolution = ZeroStartingSolution_;
00122   int precType = PreconditionerType_;
00123 
00124   //
00125   // Get the "krylov: iteration type" parameter.
00126   //
00127   // We prefer std::string (name of Belos solver), but allow int
00128   // ("enum" value) for backwards compatibility.
00129   //
00130   bool gotIterType = false;
00131   try {
00132     iterType = params.get<std::string> ("krylov: iteration type");
00133     gotIterType = true;
00134   }
00135   catch (InvalidParameterName) {
00136     gotIterType = true; // the parameter is not there, so don't try to get it anymore
00137   }
00138   catch (InvalidParameterType) {
00139     // Perhaps the user specified it as int.
00140   }
00141   // If it's not string, it has to be int.
00142   // We've already checked whether the name exists.
00143   if (! gotIterType) {
00144     const int iterTypeInt = params.get<int> ("krylov: iteration type");
00145     gotIterType = true;
00146 
00147     if (iterTypeInt == 1) {
00148       iterType = "GMRES";
00149     } else if (iterTypeInt == 2) {
00150       iterType = "CG";
00151     } else {
00152       TEUCHOS_TEST_FOR_EXCEPTION(
00153         true, std::invalid_argument, "Ifpack2::Krylov::setParameters: Invalid "
00154         "\"krylov: iteration type\" value " << iterTypeInt << ".  Valid int "
00155         "values are 1 (GMRES) and 2 (CG).  Please prefer setting this "
00156         "parameter as a string (the name of the Krylov solver to use).");
00157     }
00158   }
00159 
00160   resTol = params.get ("krylov: residual tolerance", resTol);
00161   numIters = params.get ("krylov: number of iterations", numIters);
00162   blockSize = params.get ("krylov: block size", blockSize);
00163   zeroStartingSolution = params.get ("krylov: zero starting solution",
00164                                      zeroStartingSolution);
00165   precType = params.get ("krylov: preconditioner type", precType);
00166 
00167   // Separate preconditioner parameters into another list
00168   //
00169   // FIXME (mfh 17 Jan 2014) Inner preconditioner's parameters should
00170   // be a sublist, not part of the main list!!!
00171   if (PreconditionerType_ == 1) {
00172     precParams_.set ("relaxation: sweeps",
00173                      params.get ("relaxation: sweeps", 1));
00174     precParams_.set ("relaxation: damping factor",
00175                      params.get ("relaxation: damping factor", (scalar_type) 1.0));
00176     precParams_.set ("relaxation: min diagonal value",
00177                      params.get ("relaxation: min diagonal value", STS::one ()));
00178     precParams_.set ("relaxation: zero starting solution",
00179                      params.get ("relaxation: zero starting solution", true));
00180     precParams_.set ("relaxation: backward mode",
00181                      params.get ("relaxation: backward mode", false));
00182   }
00183   // FIXME (mfh 17 Jan 2014) AdditiveSchwarz's ParameterList no longer
00184   // takes parameters for its subdomain solver!  You have to pass them
00185   // into a sublist.
00186   if (PreconditionerType_ == 2 || PreconditionerType_ == 3) {
00187     // FIXME (mfh 17 Jan 2014) should be an integer, given how ILUT
00188     // works!  Furthermore, this parameter does not mean what you
00189     // think it means.
00190     precParams_.set ("fact: ilut level-of-fill",
00191                      params.get ("fact: ilut level-of-fill", (double) 1.0));
00192     precParams_.set ("fact: iluk level-of-fill",
00193                      params.get ("fact: iluk level-of-fill", (double) 1.0));
00194     // FIXME (mfh 17 Jan 2014) scalar_type or magnitude_type? not
00195     // sure, but double is definitely wrong.
00196     precParams_.set ("fact: absolute threshold",
00197                      params.get ("fact: absolute threshold", (double) 0.0));
00198     // FIXME (mfh 17 Jan 2014) scalar_type or magnitude_type? not
00199     // sure, but double is definitely wrong.
00200     precParams_.set ("fact: relative threshold",
00201                      params.get("fact: relative threshold", (double) 1.0));
00202     // FIXME (mfh 17 Jan 2014) scalar_type or magnitude_type? not
00203     // sure, but double is definitely wrong.
00204     precParams_.set ("fact: relax value",
00205                      params.get ("fact: relax value", (double) 0.0));
00206   }
00207 
00208   // "Commit" the new values to the instance data.
00209   iterationType_ = iterType;
00210   numIters_ = numIters;
00211   resTol_ = resTol;
00212   BlockSize_ = blockSize;
00213   ZeroStartingSolution_ = zeroStartingSolution;
00214   PreconditionerType_ = precType;
00215 }
00216 
00217 
00218 template <class MatrixType>
00219 Teuchos::RCP<const Teuchos::Comm<int> >
00220 Krylov<MatrixType>::getComm () const {
00221   TEUCHOS_TEST_FOR_EXCEPTION(
00222     A_.is_null (), std::runtime_error, "Ifpack2::Krylov::getComm: "
00223     "The input matrix A is null.  Please call setMatrix() with a nonnull "
00224     "input matrix before calling this method.");
00225   return A_->getComm ();
00226 }
00227 
00228 
00229 template <class MatrixType>
00230 Teuchos::RCP<const Tpetra::RowMatrix<typename MatrixType::scalar_type,typename MatrixType::local_ordinal_type,typename MatrixType::global_ordinal_type,typename MatrixType::node_type> >
00231 Krylov<MatrixType>::getMatrix () const {
00232   return A_;
00233 }
00234 
00235 
00236 template <class MatrixType>
00237 Teuchos::RCP<const Tpetra::Map<typename MatrixType::local_ordinal_type,typename MatrixType::global_ordinal_type,typename MatrixType::node_type> >
00238 Krylov<MatrixType>::getDomainMap () const
00239 {
00240   TEUCHOS_TEST_FOR_EXCEPTION(
00241     A_.is_null (), std::runtime_error, "Ifpack2::Krylov::getDomainMap: "
00242     "The input matrix A is null.  Please call setMatrix() with a nonnull "
00243     "input matrix before calling this method.");
00244   return A_->getDomainMap ();
00245 }
00246 
00247 
00248 template <class MatrixType>
00249 Teuchos::RCP<const Tpetra::Map<typename MatrixType::local_ordinal_type,typename MatrixType::global_ordinal_type,typename MatrixType::node_type> >
00250 Krylov<MatrixType>::getRangeMap () const
00251 {
00252   TEUCHOS_TEST_FOR_EXCEPTION(
00253     A_.is_null (), std::runtime_error, "Ifpack2::Krylov::getRangeMap: "
00254     "The input matrix A is null.  Please call setMatrix() with a nonnull "
00255     "input matrix before calling this method.");
00256   return A_->getRangeMap ();
00257 }
00258 
00259 
00260 template <class MatrixType>
00261 bool Krylov<MatrixType>::hasTransposeApply () const {
00262   // FIXME (mfh 17 Jan 2014) apply() does not currently work with mode
00263   // != NO_TRANS, so it's correct to return false here.
00264   return false;
00265 }
00266 
00267 
00268 template <class MatrixType>
00269 int Krylov<MatrixType>::getNumInitialize () const {
00270   return NumInitialize_;
00271 }
00272 
00273 
00274 template <class MatrixType>
00275 int Krylov<MatrixType>::getNumCompute () const {
00276   return NumCompute_;
00277 }
00278 
00279 
00280 template <class MatrixType>
00281 int Krylov<MatrixType>::getNumApply () const {
00282   return NumApply_;
00283 }
00284 
00285 
00286 template <class MatrixType>
00287 double Krylov<MatrixType>::getInitializeTime () const {
00288   return InitializeTime_;
00289 }
00290 
00291 
00292 template <class MatrixType>
00293 double Krylov<MatrixType>::getComputeTime () const {
00294   return ComputeTime_;
00295 }
00296 
00297 
00298 template <class MatrixType>
00299 double Krylov<MatrixType>::getApplyTime () const {
00300   return ApplyTime_;
00301 }
00302 
00303 
00304 template <class MatrixType>
00305 typename Krylov<MatrixType>::magnitude_type
00306 Krylov<MatrixType>::
00307 computeCondEst (CondestType CT,
00308                 local_ordinal_type MaxIters,
00309                 magnitude_type Tol,
00310                 const Teuchos::Ptr<const row_matrix_type>& matrix)
00311 {
00312   if (! isComputed ()) { // cannot compute right now
00313     return -STM::one ();
00314   }
00315   // NOTE: this is computing the *local* condest
00316   if (Condest_ == -STM::one ()) {
00317     Condest_ = Ifpack2::Condest (*this, CT, MaxIters, Tol, matrix);
00318   }
00319   return Condest_;
00320 }
00321 
00322 
00323 template <class MatrixType>
00324 void Krylov<MatrixType>::initialize ()
00325 {
00326   using Teuchos::ParameterList;
00327   using Teuchos::RCP;
00328   using Teuchos::rcp;
00329   typedef Tpetra::MultiVector<scalar_type, local_ordinal_type,
00330                               global_ordinal_type, node_type> TMV;
00331   typedef Tpetra::Operator<scalar_type, local_ordinal_type,
00332                            global_ordinal_type, node_type> TOP;
00333 
00334   TEUCHOS_TEST_FOR_EXCEPTION(
00335     A_.is_null (), std::runtime_error, "Ifpack2::Krylov::initialize: "
00336     "The input matrix A is null.  Please call setMatrix() with a nonnull "
00337     "input matrix before calling this method.");
00338 
00339   // clear any previous allocation
00340   IsInitialized_ = false;
00341   IsComputed_ = false;
00342 
00343   Teuchos::Time timer ("initialize");
00344   { // The body of code to time
00345     Teuchos::TimeMonitor timeMon (timer);
00346 
00347     // Belos parameter list
00348     RCP<ParameterList> belosList = rcp (new ParameterList ("GMRES"));
00349     belosList->set ("Maximum Iterations", numIters_);
00350     belosList->set ("Convergence Tolerance", resTol_);
00351 
00352     // FIXME (17 Jan 2014) This whole "preconditioner type" thing is
00353     // not how we want Krylov to initialize its inner preconditioner.
00354     // Krylov should be initialized like AdditiveSchwarz: the Factory
00355     // should create it, in order to avoid circular dependencies.
00356 
00357     if (PreconditionerType_ == 0) {
00358       // no preconditioner
00359     }
00360     else if (PreconditionerType_==1) {
00361       ifpack2_prec_=rcp (new Relaxation<MatrixType> (A_));
00362     }
00363     else if (PreconditionerType_==2) {
00364       ifpack2_prec_=rcp (new ILUT<MatrixType> (A_));
00365     }
00366     else if (PreconditionerType_==3) {
00367       ifpack2_prec_ = rcp (new RILUK<MatrixType> (A_));
00368     }
00369     else if (PreconditionerType_==4) {
00370       ifpack2_prec_ = rcp (new Chebyshev<MatrixType> (A_));
00371     }
00372     if (PreconditionerType_>0) {
00373       ifpack2_prec_->initialize();
00374       ifpack2_prec_->setParameters(precParams_);
00375     }
00376     belosProblem_ = rcp (new Belos::LinearProblem<belos_scalar_type,TMV,TOP> ());
00377     belosProblem_->setOperator (A_);
00378 
00379     if (iterationType_ == "GMRES") {
00380       belosSolver_ =
00381         rcp (new Belos::BlockGmresSolMgr<belos_scalar_type,TMV,TOP> (belosProblem_, belosList));
00382     }
00383     else {
00384       belosSolver_ =
00385         rcp (new Belos::BlockCGSolMgr<belos_scalar_type,TMV,TOP> (belosProblem_, belosList));
00386     }
00387 
00388   }
00389   IsInitialized_ = true;
00390   ++NumInitialize_;
00391   InitializeTime_ += timer.totalElapsedTime ();
00392 }
00393 
00394 
00395 template <class MatrixType>
00396 void Krylov<MatrixType>::compute ()
00397 {
00398   TEUCHOS_TEST_FOR_EXCEPTION(
00399     A_.is_null (), std::runtime_error, "Ifpack2::Krylov::compute: "
00400     "The input matrix A is null.  Please call setMatrix() with a nonnull "
00401     "input matrix before calling this method.");
00402 
00403   // Don't time the initialize(); that gets timed separately.
00404   if (! isInitialized ()) {
00405     initialize ();
00406   }
00407 
00408   Teuchos::Time timer ("compute");
00409   { // The body of code to time
00410     Teuchos::TimeMonitor timeMon (timer);
00411     if (PreconditionerType_ > 0) {
00412       ifpack2_prec_->compute ();
00413       belosProblem_->setLeftPrec (ifpack2_prec_);
00414     }
00415   }
00416   IsComputed_ = true;
00417   ++NumCompute_;
00418   ComputeTime_ += timer.totalElapsedTime ();
00419 }
00420 
00421 
00422 template <class MatrixType>
00423 void Krylov<MatrixType>::
00424 apply (const Tpetra::MultiVector<typename MatrixType::scalar_type,
00425        typename MatrixType::local_ordinal_type,
00426        typename MatrixType::global_ordinal_type,
00427        typename MatrixType::node_type>& X,
00428        Tpetra::MultiVector<typename MatrixType::scalar_type,
00429                            typename MatrixType::local_ordinal_type,
00430                            typename MatrixType::global_ordinal_type,
00431                            typename MatrixType::node_type>& Y,
00432        Teuchos::ETransp mode,
00433        typename MatrixType::scalar_type alpha,
00434        typename MatrixType::scalar_type beta) const
00435 {
00436   using Teuchos::RCP;
00437   using Teuchos::rcp;
00438   using Teuchos::rcpFromRef;
00439   typedef Tpetra::MultiVector<scalar_type, local_ordinal_type,
00440                               global_ordinal_type, node_type> MV;
00441   TEUCHOS_TEST_FOR_EXCEPTION(
00442     ! isComputed (), std::runtime_error,
00443     "Ifpack2::Krylov::apply: You must call compute() before you may call apply().");
00444   TEUCHOS_TEST_FOR_EXCEPTION(
00445     X.getNumVectors () != Y.getNumVectors (), std::invalid_argument,
00446     "Ifpack2::Krylov::apply: The MultiVector inputs X and Y do not have the "
00447     "same number of columns.  X.getNumVectors() = " << X.getNumVectors ()
00448     << " != Y.getNumVectors() = " << Y.getNumVectors () << ".");
00449 
00450   // Catch unimplemented cases: alpha != 1, beta != 0, mode != NO_TRANS.
00451   TEUCHOS_TEST_FOR_EXCEPTION(
00452     alpha != STS::one (), std::logic_error,
00453     "Ifpack2::Krylov::apply: alpha != 1 has not been implemented.");
00454   TEUCHOS_TEST_FOR_EXCEPTION(
00455     beta != STS::zero (), std::logic_error,
00456     "Ifpack2::Krylov::apply: zero != 0 has not been implemented.");
00457   TEUCHOS_TEST_FOR_EXCEPTION(
00458     mode != Teuchos::NO_TRANS, std::logic_error,
00459     "Ifpack2::Krylov::apply: mode != Teuchos::NO_TRANS has not been implemented.");
00460 
00461   Teuchos::Time timer ("apply");
00462   { // The body of code to time
00463     Teuchos::TimeMonitor timeMon (timer);
00464 
00465     // If X and Y are pointing to the same memory location,
00466     // we need to create an auxiliary vector, Xcopy
00467     RCP<const MV> Xcopy;
00468     if (X.getLocalMV ().getValues () == Y.getLocalMV ().getValues ()) {
00469       Xcopy = rcp (new MV (createCopy(X)));
00470     } else {
00471       Xcopy = rcpFromRef (X);
00472     }
00473 
00474     RCP<MV> Ycopy = rcpFromRef (Y);
00475     if (ZeroStartingSolution_) {
00476       Ycopy->putScalar (STS::zero ());
00477     }
00478 
00479     // Set left and right hand sides for Belos
00480     belosProblem_->setProblem (Ycopy, Xcopy);
00481     belosSolver_->solve (); // solve the linear system
00482   }
00483   ++NumApply_;
00484   ApplyTime_ += timer.totalElapsedTime ();
00485 }
00486 
00487 
00488 template <class MatrixType>
00489 std::string Krylov<MatrixType>::description () const
00490 {
00491   std::ostringstream os;
00492 
00493   // Output is a valid YAML dictionary in flow style.  If you don't
00494   // like everything on a single line, you should call describe()
00495   // instead.
00496   os << "\"Ifpack2::Krylov\": {";
00497   if (this->getObjectLabel () != "") {
00498     os << "Label: \"" << this->getObjectLabel () << "\", ";
00499   }
00500   os << "Initialized: " << (isInitialized () ? "true" : "false") << ", "
00501      << "Computed: " << (isComputed () ? "true" : "false") << ", ";
00502 
00503   if (A_.is_null ()) {
00504     os << "Matrix: null";
00505   }
00506   else {
00507     os << "Matrix: not null"
00508        << ", Global matrix dimensions: ["
00509        << A_->getGlobalNumRows () << ", " << A_->getGlobalNumCols () << "]";
00510   }
00511 
00512   os << "}";
00513   return os.str ();
00514 }
00515 
00516 
00517 template <class MatrixType>
00518 void Krylov<MatrixType>::
00519 describe (Teuchos::FancyOStream &out,
00520           const Teuchos::EVerbosityLevel verbLevel) const
00521 {
00522   using std::endl;
00523   using std::setw;
00524   using Teuchos::VERB_DEFAULT;
00525   using Teuchos::VERB_NONE;
00526   using Teuchos::VERB_LOW;
00527   using Teuchos::VERB_MEDIUM;
00528   using Teuchos::VERB_HIGH;
00529   using Teuchos::VERB_EXTREME;
00530 
00531   const Teuchos::EVerbosityLevel vl =
00532     (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
00533 
00534   if (vl != VERB_NONE) {
00535     // describe() always starts with a tab by convention.
00536     Teuchos::OSTab tab0 (out);
00537     out << "\"Ifpack2::Krylov\":";
00538 
00539     Teuchos::OSTab tab1 (out);
00540     if (this->getObjectLabel () != "") {
00541       out << "Label: " << this->getObjectLabel () << endl;
00542     }
00543     out << "Initialized: " << (isInitialized () ? "true" : "false") << endl
00544         << "Computed: " << (isComputed () ? "true" : "false") << endl
00545         << "Global number of rows: " << A_->getGlobalNumRows () << endl
00546         << "Global number of columns: " << A_->getGlobalNumCols () << endl
00547         << "Matrix:";
00548     if (A_.is_null ()) {
00549       out << " null" << endl;
00550     } else {
00551       A_->describe (out, vl);
00552     }
00553   }
00554 }
00555 
00556 } // namespace Ifpack2
00557 
00558 #define IFPACK2_KRYLOV_INSTANT(S,LO,GO,N) \
00559   template class Ifpack2::Krylov< Tpetra::CrsMatrix<S, LO, GO, N> >;
00560 
00561 #endif /* IFPACK2_KRYLOV_DEF_HPP */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends