Beispiel #1
0
// ====================================================================== 
void Eig(const Operator& Op, MultiVector& ER, MultiVector& EI)
{
  int ierr;
  if (Op.GetDomainSpace() != Op.GetRangeSpace())
    ML_THROW("Matrix is not square", -1);

  ER.Reshape(Op.GetDomainSpace());
  EI.Reshape(Op.GetDomainSpace());

  Epetra_LinearProblem Problem;
  Problem.SetOperator(const_cast<Epetra_RowMatrix*>(Op.GetRowMatrix()));
  Amesos_Lapack Lapack(Problem);

  Epetra_Vector ER_Epetra(Op.GetRowMatrix()->RowMatrixRowMap());
  Epetra_Vector EI_Epetra(Op.GetRowMatrix()->RowMatrixRowMap());

  ierr = Lapack.GEEV(ER_Epetra, EI_Epetra);

  if (ierr)
    ML_THROW("GEEV returned error code = " + GetString(ierr), -1);
  
  for (int i = 0 ; i < ER.GetMyLength() ; ++i) {
    ER(i) = ER_Epetra[i];
    EI(i) = EI_Epetra[i];
  }
}
// ====================================================================== 
Operator GetJacobiIterationOperator(const Operator& Amat, double Damping)
{

  struct ML_AGG_Matrix_Context* widget = new struct ML_AGG_Matrix_Context;
  widget->near_bdry = 0;
  widget->aggr_info = 0;
  widget->drop_tol  = 0.0;

  widget->Amat = Amat.GetML_Operator();
  widget->omega = Damping;

  ML_Operator* tmp_ML = ML_Operator_Create(GetML_Comm());
  ML_Operator_Set_ApplyFuncData(tmp_ML, widget->Amat->invec_leng,
                                widget->Amat->outvec_leng, widget,
                                widget->Amat->matvec->Nrows, NULL, 0);

  tmp_ML->data_destroy = widget_destroy;

  ML_Operator_Set_Getrow(tmp_ML, widget->Amat->getrow->Nrows, 
                         ML_AGG_JacobiSmoother_Getrows);

  // Creates a new copy of pre_comm, so that the old pre_comm
  // can be destroyed without worry
  ML_CommInfoOP_Clone(&(tmp_ML->getrow->pre_comm),
                      widget->Amat->getrow->pre_comm);

  Operator tmp(Amat.GetDomainSpace(), Amat.GetRangeSpace(), tmp_ML, true,
               Amat.GetRCPOperatorBox());

  return(tmp);
}
Beispiel #3
0
// ====================================================================== 
// FIXME: Add List
void Eigs(const Operator& A, int NumEigenvalues, 
          MultiVector& ER, MultiVector& EI)
{

  if (A.GetDomainSpace() != A.GetRangeSpace())
    ML_THROW("Input Operator is not square", -1);

  double time;

  time = GetClock();

  int length = NumEigenvalues;
  double tol = 1e-3;
  int restarts = 1;
  int output = 10;
  bool PrintStatus = true;

  // 1.- set parameters for Anasazi
  Teuchos::ParameterList AnasaziList;
  // MatVec should be either "A" or "ML^{-1}A"
  AnasaziList.set("eigen-analysis: matrix operation", "A");
  AnasaziList.set("eigen-analysis: use diagonal scaling", false);
  AnasaziList.set("eigen-analysis: symmetric problem", false);
  AnasaziList.set("eigen-analysis: length", length);
  AnasaziList.set("eigen-analysis: block-size",1);
  AnasaziList.set("eigen-analysis: tolerance", tol);
  AnasaziList.set("eigen-analysis: restart", restarts);
  AnasaziList.set("eigen-analysis: output", output);
  AnasaziList.get("eigen-analysis: print current status",PrintStatus);

  // data to hold real and imag for eigenvalues and eigenvectors
  Space ESpace(-1, NumEigenvalues);
  ER.Reshape(ESpace);
  EI.Reshape(ESpace);

  // this is the starting value -- random
  Epetra_MultiVector EigenVectors(A.GetRowMatrix()->OperatorDomainMap(),
                                  NumEigenvalues);
  EigenVectors.Random();
#ifdef HAVE_ML_ANASAxI
  //int NumRealEigenvectors, NumImagEigenvectors;
#endif

  AnasaziList.set("eigen-analysis: action", "LM");

#ifdef HAVE_ML_ANASAxI
  ML_THROW("fixme...", -1);
  /* FIXME
  ML_Anasazi::Interface(A.GetRowMatrix(),EigenVectors,ER.GetValues(),
			EI.GetValues(), AnasaziList, 0, 0,
			&NumRealEigenvectors, &NumImagEigenvectors, 0);
                        */
#else
  ML_THROW("Anasazi is no longer supported", -1);
#endif

  return;
}
// ====================================================================== 
Operator GetTranspose(const Operator& A, const bool byrow = true) 
{
  ML_Operator* ML_transp;
  ML_transp = ML_Operator_Create(GetML_Comm());
  if (byrow)
    ML_Operator_Transpose_byrow(A.GetML_Operator(),ML_transp);
  else
    ML_Operator_Transpose(A.GetML_Operator(),ML_transp);

  Operator transp(A.GetRangeSpace(),A.GetDomainSpace(), ML_transp,true);
  return(transp);
}
Beispiel #5
0
int main(int argc, char *argv[])
{

#ifdef HAVE_MPI
  MPI_Init(&argc,&argv);
#endif

  Init();

  try {

    int NX = 10;         // number of nodes on the X-axis
    int NY = 10;         // number of nodes on the Y-axis
    double conv = 1.0;   // convection coefficient
    double diff = 1e-5;  // diffusion coefficient

    Operator A = GetRecirc2D(NX, NY, conv, diff);

    Teuchos::ParameterList List;
    List.set("smoother: type", "symmetric Gauss-Seidel");
    List.set("smoother: sweeps", 1);
    List.set("aggregation: damping factor", 0.0);
    List.set("coarse: max size", 32);

    MultiLevelNonSymmetricSA Prec(A, List);

    MultiVector LHS(A.GetRangeSpace());
    MultiVector RHS(A.GetDomainSpace());

    LHS.Random();
    RHS = 0.0;

    List.set("krylov: type", "gmres");
    Krylov(A, LHS, RHS, Prec, List);

    Finalize(); 

  }
  catch (const int e) {
    cerr << "Caught integer exception, code = " << e << endl;
  }
  catch (...) {
    cerr << "Caught exception..." << endl;
  }

#ifdef HAVE_MPI
  MPI_Finalize() ;
#endif

  return(0);

}
// ====================================================================== 
Operator GetScaledOperator(const Operator& A, const double alpha) 
{
  ML_Operator* ScaledA = 0;
  ScaledA = ML_Operator_ExplicitlyScale(A.GetML_Operator(),
                                        (double)alpha);

  if (ScaledA == 0)
    ML_THROW("ML_Operator_ExplicitlyScale returned 0", -1);

  Operator res;
  res.Reshape(A.GetDomainSpace(), A.GetRangeSpace(), ScaledA,
              true, A.GetRCPOperatorBox());
    
  return(res);
}
// ====================================================================== 
MultiVector GetDiagonal(const Operator& A, const int offset)
{
  // FIXME
  if (A.GetDomainSpace() != A.GetRangeSpace())
    ML_THROW("Currently only square matrices are supported", -1);

  MultiVector D(A.GetDomainSpace());
  D = 0.0;
  
  ML_Operator* matrix = A.GetML_Operator();

  if (matrix->getrow == NULL)
    ML_THROW("getrow() not set!", -1);

  int row_length;
  int allocated = 128;
  int*    bindx = (int    *)  ML_allocate(allocated*sizeof(int   ));
  double* val   = (double *)  ML_allocate(allocated*sizeof(double));

  for (int i = 0 ; i < matrix->getrow->Nrows; i++) {
    int GlobalRow = A.GetGRID(i);
    ML_get_matrix_row(matrix, 1, &i, &allocated, &bindx, &val,
                      &row_length, 0);
    for  (int j = 0; j < row_length; j++) {
      D(i) = 0.0;
      if (A.GetGCID(bindx[j]) == GlobalRow + offset) {
        D(i) = val[j];
        break;
      }
    }
  }

  ML_free(val);
  ML_free(bindx);
  return (D);

}
Beispiel #8
0
// ======================================================================
void GetPtent(const Operator& A, Teuchos::ParameterList& List,
              const MultiVector& ThisNS,
              Operator& Ptent, MultiVector& NextNS)
{
  std::string CoarsenType     = List.get("aggregation: type", "Uncoupled");
  /* old version
  int    NodesPerAggr    = List.get("aggregation: per aggregate", 64);
  */
  double Threshold       = List.get("aggregation: threshold", 0.0);
  int    NumPDEEquations = List.get("PDE equations", 1);

  ML_Aggregate* agg_object;
  ML_Aggregate_Create(&agg_object);
  ML_Aggregate_Set_MaxLevels(agg_object,2);
  ML_Aggregate_Set_StartLevel(agg_object,0);
  ML_Aggregate_Set_Threshold(agg_object,Threshold);
  //agg_object->curr_threshold = 0.0;

  ML_Operator* ML_Ptent = 0;
  ML_Ptent = ML_Operator_Create(GetML_Comm());

  if (ThisNS.GetNumVectors() == 0)
    ML_THROW("zero-dimension null space", -1);

  int size = ThisNS.GetMyLength();

  double* null_vect = 0;
  ML_memory_alloc((void **)(&null_vect), sizeof(double) * size * ThisNS.GetNumVectors(), "ns");

  int incr = 1;
  for (int v = 0 ; v < ThisNS.GetNumVectors() ; ++v)
    DCOPY_F77(&size, (double*)ThisNS.GetValues(v), &incr,
              null_vect + v * ThisNS.GetMyLength(), &incr);


  ML_Aggregate_Set_NullSpace(agg_object, NumPDEEquations,
                             ThisNS.GetNumVectors(), null_vect,
                             ThisNS.GetMyLength());

  if (CoarsenType == "Uncoupled")
    agg_object->coarsen_scheme = ML_AGGR_UNCOUPLED;
  else if (CoarsenType == "Uncoupled-MIS")
    agg_object->coarsen_scheme = ML_AGGR_HYBRIDUM;
  else if (CoarsenType == "MIS") {
   /* needed for MIS, otherwise it sets the number of equations to
    * the null space dimension */
    agg_object->max_levels  = -7;
    agg_object->coarsen_scheme = ML_AGGR_MIS;
  }
  else if (CoarsenType == "METIS")
    agg_object->coarsen_scheme = ML_AGGR_METIS;
  else {
    ML_THROW("Requested aggregation scheme (" + CoarsenType +
             ") not recognized", -1);
  }

  int NextSize = ML_Aggregate_Coarsen(agg_object, A.GetML_Operator(),
                                      &ML_Ptent, GetML_Comm());

  /* This is the old version
  int NextSize;

  if (CoarsenType == "Uncoupled") {
    NextSize = ML_Aggregate_CoarsenUncoupled(agg_object, A.GetML_Operator(),
  }
  else if (CoarsenType == "MIS") {
    NextSize = ML_Aggregate_CoarsenMIS(agg_object, A.GetML_Operator(),
                                       &ML_Ptent, GetML_Comm());
  }
  else if (CoarsenType == "METIS") {
    ML ml_object;
    ml_object.ML_num_levels = 1; // crap for line below
    ML_Aggregate_Set_NodesPerAggr(&ml_object,agg_object,0,NodesPerAggr);
    NextSize = ML_Aggregate_CoarsenMETIS(agg_object, A.GetML_Operator(),
                                         &ML_Ptent, GetML_Comm());
  }
  else {
    ML_THROW("Requested aggregation scheme (" + CoarsenType +
             ") not recognized", -1);
  }
  */

  ML_Operator_ChangeToSinglePrecision(ML_Ptent);

  int NumMyElements = NextSize;
  Space CoarseSpace(-1,NumMyElements);
  Ptent.Reshape(CoarseSpace,A.GetRangeSpace(),ML_Ptent,true);

  assert (NextSize * ThisNS.GetNumVectors() != 0);

  NextNS.Reshape(CoarseSpace, ThisNS.GetNumVectors());

  size = NextNS.GetMyLength();
  for (int v = 0 ; v < NextNS.GetNumVectors() ; ++v)
    DCOPY_F77(&size, agg_object->nullspace_vect + v * size, &incr,
              NextNS.GetValues(v), &incr);

  ML_Aggregate_Destroy(&agg_object);
  ML_memory_free((void**)(&null_vect));
}
int main(int argc, char *argv[])
{

#ifdef HAVE_MPI
  MPI_Init(&argc,&argv);
#endif

  if (argc != 2) {
    fprintf(stderr, "Usage: `%s InputFile'\n", argv[0]);
    fprintf(stderr, "An example of input file is reported\n");
    fprintf(stderr, "in the source of this example\n");
    exit(EXIT_SUCCESS);
  }

  string InputFile = argv[1];

  // Initialize the workspace and set the output level
  Init();

  try {

    int         NumPDEEqns;
    Operator    A;

    ReadSAMISMatrix("mtx.dat", A, NumPDEEqns);

    Teuchos::ParameterList List = ReadParameterList(InputFile.c_str());
    int  MaxLevels            = List.get("max levels", 10);
    int  AdditionalCandidates = List.get("additional candidates", 2);
    int  limKer               = List.get("limit kernel", -1);

    if (AdditionalCandidates == 0 && limKer == 0)
      limKer = -1;

    // create multilevel preconditioner, do not compute hierarchy
    MultiLevelAdaptiveSA Prec(A, List, NumPDEEqns);

    if (limKer) {
      MultiVector NS;
      ReadSAMISKernel("ker.dat", NS, limKer);
      Prec.SetNullSpace(NS);
      Prec.AdaptCompute(true, AdditionalCandidates);
    }
    else {
      Prec.AdaptCompute(false, AdditionalCandidates);
    }

    MultiVector LHS(A.GetDomainSpace());
    MultiVector RHS(A.GetRangeSpace());

    LHS.Random();
    RHS = 0.0;

    // Set krylov: type unless specified in the config. file
    if (List.isParameter("krylov: type") == 0)
        List.set("krylov: type","cg_condnum");

    Krylov(A, LHS, RHS, Prec, List);

    Finalize(); 

  }
  catch (const int e) {
    cerr << "Caught integer exception, code = " << e << endl;
  }
  catch (...) {
    cerr << "Caught exception..." << endl;
  }

#ifdef HAVE_MPI
  MPI_Finalize() ;
#endif

  return(0);

}
/*----------------------------------------------------------------------*
 |  compute the preconditioner (public)                      m.gee 03/06|
 *----------------------------------------------------------------------*/
bool MOERTEL::Mortar_ML_Preconditioner::Compute()
{

    iscomputed_ = false;

    MLAPI::Init();

    // get parameters
    int     maxlevels     = mlparams_.get("max levels",10);
    int     maxcoarsesize = mlparams_.get("coarse: max size",10);
    double* nullspace     = mlparams_.get("null space: vectors",(double*)NULL);
    int     nsdim         = mlparams_.get("null space: dimension",1);
    int     numpde        = mlparams_.get("PDE equations",1);
    double  damping       = mlparams_.get("aggregation: damping factor",1.33);
    std::string  eigenanalysis = mlparams_.get("eigen-analysis: type", "Anorm");
    std::string  smoothertype  = mlparams_.get("smoother: type","symmetric Gauss-Seidel");
    std::string  coarsetype    = mlparams_.get("coarse: type","Amesos-KLU");
    std::string  ptype         = mlparams_.get("prolongator: type","mod_full");

    // create the missing rowmap Arrmap_
    Arrmap_ = Teuchos::rcp(MOERTEL::SplitMap(A_->RowMap(),*Annmap_));
    Teuchos::RCP<Epetra_Map> map1 = Arrmap_;
    Teuchos::RCP<Epetra_Map> map2 = Annmap_;

    // split Atilde
    //
    //  Atilde11 Atilde12
    //  Atilde21 Atilde22
    //
    Teuchos::RCP<Epetra_CrsMatrix> Atilde11;
    Teuchos::RCP<Epetra_CrsMatrix> Atilde12;
    Teuchos::RCP<Epetra_CrsMatrix> Atilde21;
    Teuchos::RCP<Epetra_CrsMatrix> Atilde22;
    MOERTEL::SplitMatrix2x2(Atilde_,map1,map2,Atilde11,Atilde12,Atilde21,Atilde22);

    // build Atildesplit
    //
    //  Atilde11  0
    //  0         I
    //
    Atildesplit_ = Teuchos::rcp(new Epetra_CrsMatrix(Copy,A_->RowMap(),50,false));
    MOERTEL::MatrixMatrixAdd(*Atilde11,false,1.0,*Atildesplit_,0.0);
    Teuchos::RCP<Epetra_CrsMatrix> tmp = Teuchos::rcp(MOERTEL::PaddedMatrix(*map2,1.0,1));
    tmp->FillComplete();
    MOERTEL::MatrixMatrixAdd(*tmp,false,1.0,*Atildesplit_,1.0);
    Atildesplit_->FillComplete();
    Atildesplit_->OptimizeStorage();

    // split A
    //
    //  A11 A12
    //  A21 A22
    //
    Teuchos::RCP<Epetra_CrsMatrix> A11;
    Teuchos::RCP<Epetra_CrsMatrix> A12;
    Teuchos::RCP<Epetra_CrsMatrix> A21;
    Teuchos::RCP<Epetra_CrsMatrix> A22;
    MOERTEL::SplitMatrix2x2(A_,map1,map2,A11,A12,A21,A22);

    // build Asplit_
    //
    //  A11  0
    //  0    A22
    //
    Asplit_ = Teuchos::rcp(new Epetra_CrsMatrix(Copy,A_->RowMap(),50,false));
    MOERTEL::MatrixMatrixAdd(*A11,false,1.0,*Asplit_,0.0);
    MOERTEL::MatrixMatrixAdd(*A22,false,1.0,*Asplit_,1.0);
    Asplit_->FillComplete();
    Asplit_->OptimizeStorage();

    // build BWT (padded to full size)
    //
    //  0   Mr Dinv
    //  0    I
    //
    Teuchos::RCP<Epetra_CrsMatrix> BWT = Teuchos::rcp(MOERTEL::MatMatMult(*B_,false,*WT_,false,10));
    tmp = Teuchos::rcp(MOERTEL::PaddedMatrix(BWT->RowMap(),0.0,25));
    MOERTEL::MatrixMatrixAdd(*BWT,false,1.0,*tmp,0.0);
    tmp->FillComplete(BWT->DomainMap(),BWT->RangeMap());
    BWT = tmp;
    tmp = Teuchos::null;

    // split BWT to obtain M = Mr Dinv
    Teuchos::RCP<Epetra_CrsMatrix> Zero11;
    Teuchos::RCP<Epetra_CrsMatrix> M;
    Teuchos::RCP<Epetra_CrsMatrix> Zero21;
    Teuchos::RCP<Epetra_CrsMatrix> I22;
    MOERTEL::SplitMatrix2x2(BWT,map1,map2,Zero11,M,Zero21,I22);


    // build matrix Ahat11 = Atilde11 - M Atilde22 M^T
    Teuchos::RCP<Epetra_CrsMatrix> Ahat11 = Teuchos::rcp(new Epetra_CrsMatrix(Copy,*map1,50,false));
    MOERTEL::MatrixMatrixAdd(*Atilde11,false,1.0,*Ahat11,0.0);
    Teuchos::RCP<Epetra_CrsMatrix> tmp1 = Teuchos::rcp(MOERTEL::MatMatMult(*Atilde22,false,*M,true,10));
    Teuchos::RCP<Epetra_CrsMatrix> tmp2 = Teuchos::rcp(MOERTEL::MatMatMult(*M,false,*tmp1,false,10));
    MOERTEL::MatrixMatrixAdd(*tmp2,false,-1.0,*Ahat11,1.0);
    Ahat11->FillComplete();
    Ahat11->OptimizeStorage();

    // build matrix Ahat
    //
    //  Ahat11   0   =   Atilde11 - M Atilde22 M^T   0
    //  0        0       0                           0
    //
    Ahat_ = Teuchos::rcp(MOERTEL::PaddedMatrix(A_->RowMap(),0.0,25));
    MOERTEL::MatrixMatrixAdd(*Ahat11,false,1.0,*Ahat_,0.0);
    Ahat_->FillComplete();
    Ahat_->OptimizeStorage();


    // build mlapi objects
    Space space(A_->RowMatrixRowMap());
    Operator mlapiAsplit(space,space,Asplit_.get(),false);
    Operator mlapiAtildesplit(space,space,Atildesplit_.get(),false);
    Operator mlapiAhat(space,space,Ahat_.get(),false);
    Operator mlapiBWT(space,space,BWT.get(),false);
    Operator mlapiBWTcoarse;
    Operator ImBWTfine = GetIdentity(space,space) - mlapiBWT;
    Operator ImBWTcoarse;
    Operator Ptent;
    Operator P;
    Operator Pmod;
    Operator Rtent;
    Operator R;
    Operator Rmod;
    Operator IminusA;
    Operator C;
    InverseOperator S;

    mlapiAtildesplit_.resize(maxlevels);
    mlapiAhat_.resize(maxlevels);
    mlapiImBWT_.resize(maxlevels);
    mlapiImWBT_.resize(maxlevels);
    mlapiRmod_.resize(maxlevels);
    mlapiPmod_.resize(maxlevels);
    mlapiS_.resize(maxlevels);

    mlapiAtildesplit_[0] = mlapiAtildesplit;
    mlapiAhat_[0]        = mlapiAhat;
    mlapiImBWT_[0]       = ImBWTfine;
    mlapiImWBT_[0]       = GetTranspose(ImBWTfine);


    // build nullspace
    MultiVector NS;
    MultiVector NextNS;
    NS.Reshape(mlapiAsplit.GetRangeSpace(),nsdim);
    if (nullspace)
    {
        for (int i=0; i<nsdim; ++i)
            for (int j=0; j<NS.GetMyLength(); ++j)
                NS(j,i) = nullspace[i*NS.GetMyLength()+j];
    }
    else
    {
        if (numpde==1) NS = 1.0;
        else
        {
            NS = 0.0;
            for (int i=0; i<NS.GetMyLength(); ++i)
                for (int j=0; j<numpde; ++j)
                    if ( i % numpde == j)
                        NS(i,j) = 1.0;
        }
    }

    double lambdamax;

    // construct the hierarchy
    int level=0;
    for (level=0; level<maxlevels-1; ++level)
    {
        // this level's smoothing operator
        mlapiAtildesplit = mlapiAtildesplit_[level];

        // build smoother
        if (Comm().MyPID()==0)
        {
            ML_print_line("-", 78);
            std::cout << "MOERTEL/ML : creating smoother level " << level << std::endl;
            fflush(stdout);
        }
        S.Reshape(mlapiAtildesplit,smoothertype,mlparams_);

        if (level) mlparams_.set("PDE equations", NS.GetNumVectors());

        if (Comm().MyPID()==0)
        {
            ML_print_line("-", 80);
            std::cout << "MOERTEL/ML : creating level " << level+1 << std::endl;
            ML_print_line("-", 80);
            fflush(stdout);
        }
        mlparams_.set("workspace: current level",level);

        // get tentative prolongator based on decoupled original system
        GetPtent(mlapiAsplit,mlparams_,NS,Ptent,NextNS);
        NS = NextNS;

        // do prolongator smoothing
        if (damping)
        {
            if (eigenanalysis == "Anorm")
                lambdamax = MaxEigAnorm(mlapiAsplit,true);
            else if (eigenanalysis == "cg")
                lambdamax = MaxEigCG(mlapiAsplit,true);
            else if (eigenanalysis == "power-method")
                lambdamax = MaxEigPowerMethod(mlapiAsplit,true);
            else ML_THROW("incorrect parameter (" + eigenanalysis + ")", -1);

            IminusA = GetJacobiIterationOperator(mlapiAsplit,damping/lambdamax);
            P       = IminusA * Ptent;
            R       = GetTranspose(P);
            Rtent   = GetTranspose(Ptent);
        }
        else
        {
            P     = Ptent;
            Rtent = GetTranspose(Ptent);
            R     = Rtent;
            lambdamax = -1.0;
        }

        // do variational coarse grid of split original matrix Asplit
        C = GetRAP(R,mlapiAsplit,P);

        // compute the mortar projections on coarse grid
        mlapiBWTcoarse = GetRAP(Rtent,mlapiBWT,Ptent);
        ImBWTcoarse    = GetIdentity(C.GetDomainSpace(),C.GetRangeSpace());
        ImBWTcoarse    = ImBWTcoarse - mlapiBWTcoarse;

        // do modified prolongation and restriction
        if (ptype=="mod_full")
            Rmod = ImBWTcoarse * ( R * ImBWTfine ) + mlapiBWTcoarse * ( R * mlapiBWT );
        else if (ptype=="mod_middle")
            Rmod = ImBWTcoarse * ( R * ImBWTfine );
        else if (ptype=="mod_simple")
            Rmod = R * ImBWTfine;
        else if (ptype=="original")
            Rmod = R;
        else
            ML_THROW("incorrect parameter ( " + ptype + " )", -1);
        Pmod = GetTranspose(Rmod);

        // store matrix for construction of next level
        mlapiAsplit = C;

        // make coarse smoothing operator
        // make coarse residual operator
        mlapiAtildesplit_[level+1] = GetRAP(Rmod,mlapiAtildesplit,Pmod);
        mlapiAhat_[level+1]        = GetRAP(Rmod,mlapiAhat_[level],Pmod);
        mlapiImBWT_[level]         = ImBWTfine;
        mlapiImBWT_[level+1]       = ImBWTcoarse;
        mlapiImWBT_[level]         = GetTranspose(ImBWTfine);
        mlapiImWBT_[level+1]       = GetTranspose(ImBWTcoarse);
        mlapiRmod_[level]          = Rmod;
        mlapiPmod_[level]          = Pmod;
        mlapiS_[level]             = S;

        // prepare for next level
        mlapiBWT  = mlapiBWTcoarse;
        ImBWTfine = ImBWTcoarse;

        // break if coarsest level is below specified size
        if (mlapiAsplit.GetNumGlobalRows() <= maxcoarsesize)
        {
            ++level;
            break;
        }

    } // for (level=0; level<maxlevels-1; ++level)

    // do coarse smoother
    S.Reshape(mlapiAtildesplit_[level],coarsetype,mlparams_);
    mlapiS_[level] = S;

    // store max number of levels
    maxlevels_ = level+1;

    iscomputed_ = true;
    return true;
}