We consider the Poisson boundary value problem (for simplicity here only with Dirichlet conditions):
\begin{eqnarray*}
-\Delta u &=& f \quad \text{in} \quad \Omega, \\
u &=& g \quad \text{on} \quad \partial\Omega.
\end{eqnarray*}
\[
\int_\Omega \nabla u \nabla v \text{ d} x = \int_\Omega f v \text{ d}x
\]
for all \( u\in V_0 \).
For a hierarchy of grids with levels \(\ell=0 \mbox{ (=coarsest)},
1,2,\ldots,L \mbox{ (=finest)} \), we have subspaces \( V_\ell \subset H^1(\Omega) \) and
\[
V_{\ell,g} = V_\ell \cap V_g
\quad\mbox{and}\quad
V_{\ell,0} = V_\ell \cap V_0.
\]
The relation between these spaces are realized for \( \ell = 0,1,\ldots,L-1 \) by transfer matrices
Having the matrix \( A_L \) on the finest grid level, we can set up the fine-grid matrices via the Galerkin principle:
Then, we extract the basis from the multi patch object to obtain a multi basis.
There are a few possibilities to modify the geometry, which can be activated using the command line and which can found in the full listings.
Since the multigrid solver also works for a discontinuous Galerkin (dG) setting, we allow to modify the discretizations in order to obtain non-matching discretizations at the interfaces as a more appropriate test case for a dG setting.
We set the spline degree as required at the command line. Moreover, we apply the given number of uniform refinement steps.
Here, we set up a vector that stores the transfer matrices. Moreover, we set up data structures that are needed for the subspace corrected mass smoother.
The function has to know the boundary conditions and the assembler options in order to know about the eliminated dofs (particularly on the Dirichlet boundary) and the coupled dofs on the interfaces between the patches.
In any case, we have to export the transfer matrices \( P_\ell \). The subspace corrected mass smoother also requires the gsMultiBasis objects for all grid levels.
If one wants to apply this to systems of PDEs, it is expected to have individual grid hierarchies for all of the variables.
We use a unnamed temporary object, which means that the object cleaned up immediately after exporting the data.
It is possible to provide the stiffness matrices for all grid levels, the prolongation operators, the restriction operators and the solver for the coarsest grid level, all of them as gsLinearOperator.
With the member setOptions, we provide various settings, like the number of presmoothing steps, the number of post smoothing steps, and the type of the cycle (1 = V-cycle and 2 = W-cycle). These options can also be set using the corresponding member functions.
If we do not provide a coarse solver explicitly, a LU solver will be set up automatically with the first multigrid cycle that is applied. Since we are solving an elliptic problem in this example file, we explicitly set up a Cholesky solver.
Here, mg->matrix(0) refers to the matrix on the coarsest grid level.
The smoothers have to be provided for the grid levels \( \ell=1, 2, \ldots, L \). Depending on the command line options, we use
We now provide the smoother with the respective options (like damping parameters).
Finally, we provide the smoother to the multigrid object.
We use a random vector as initial guess.
As a next step, we solve the linear system using either a conjugate gradient solver with multigrid preconditioner or by iterating multigrid itself. The latter is to be represented as preconditioned gradient (=Richardson) solver.
#include <ctime>
int main(int argc, char *argv[])
{
std::string geometry("domain2d/yeti_mp2.xml");
real_t stretchGeometry = 1;
bool nonMatching = false;
bool dg = false;
bool nitsche = false;
bool extrasmooth = false;
std::string smoother("GaussSeidel");
real_t damping = -1;
real_t scaling = 0.12;
std::string iterativeSolver("cg");
real_t tolerance = 1.e-8;
std::string boundaryConditions("d");
std::string out;
bool plot = false;
gsCmdLine cmd(
"Solves a PDE with an isogeometric discretization using a multigrid solver.");
cmd.addString("g", "Geometry", "Geometry file", geometry);
cmd.addInt ("", "SplitPatches", "Split every patch that many times in 2^d patches", splitPatches);
cmd.addReal ("", "StretchGeometry", "Stretch geometry in x-direction by the given factor", stretchGeometry);
cmd.addInt ("", "XRefine", "Refine in x-direction by adding that many knots to every knot span", xRefine);
cmd.addInt ("r", "Refinements", "Number of uniform h-refinement steps to perform before solving", refinements);
cmd.addInt ("p", "Degree", "Degree of the B-spline discretization space", degree);
cmd.addSwitch("", "NonMatching", "Set up a non-matching multi-patch discretization", nonMatching);
cmd.addSwitch("", "DG", "Use discontinuous Galerkin (SIPG) for coupling the patches", dg);
cmd.addSwitch("", "Nitsche", "Use Nitsche method for Dirichlet boundary conditions", nitsche);
cmd.addInt ("l", "MG.Levels", "Number of levels to use for multigrid iteration", levels);
cmd.addInt ("c", "MG.NumCycles", "Number of multi-grid cycles", cycles);
cmd.addInt ("", "MG.NumPreSmooth", "Number of pre-smoothing steps", presmooth);
cmd.addInt ("", "MG.NumPostSmooth", "Number of post-smoothing steps", postsmooth);
cmd.addSwitch("", "MG.Extrasmooth", "Doubles the number of smoothing steps for each coarser level", extrasmooth);
cmd.addString("s", "MG.Smoother", "Smoothing method", smoother);
cmd.addReal ("", "MG.Damping", "Damping factor for the smoother", damping);
cmd.addReal ("", "MG.Scaling", "Scaling factor for the subspace corrected mass smoother", scaling);
cmd.addString("i", "IterativeSolver", "Iterative solver: apply multigrid directly (d) or as a preconditioner for "
"conjugate gradient (cg)", iterativeSolver);
cmd.addReal ("t", "Solver.Tolerance", "Stopping criterion for linear solver", tolerance);
cmd.addInt ("", "Solver.MaxIterations", "Stopping criterion for linear solver", maxIterations);
cmd.addString("b", "BoundaryConditions", "Boundary conditions", boundaryConditions);
cmd.addString("", "out", "Write solution and used options to file", out);
cmd.addSwitch( "plot", "Plot the result with Paraview", plot);
try { cmd.getValues(argc,argv); } catch (int rv) { return rv; }
if (levels <0) { levels = refinements; cmd.setInt( "MG.Levels", levels ); }
if (damping<0) { cmd.remove( "MG.Damping" ); }
cmd.remove( "DG" );
cmd.addInt(
"MG.InterfaceStrategy",
"", (
index_t)( dg ? iFace::dg : iFace::conforming ) );
cmd.addInt(
"MG.DirichletStrategy",
"", (
index_t)( nitsche ? dirichlet::nitsche : dirichlet::elimination ) );
{
gsInfo <<
"Geometry file could not be found.\n";
return EXIT_FAILURE;
}
gsInfo <<
"Run multiGrid_example with options:\n" << cmd <<
"\n";
gsInfo <<
"Define geometry... " << std::flush;
if (!mpPtr)
{
gsInfo <<
"No geometry found in file " << geometry <<
".\n";
return EXIT_FAILURE;
}
for (
index_t i=0; i<splitPatches; ++i)
{
gsInfo <<
"split patches uniformly... " << std::flush;
mp = mp.uniformSplit();
}
if (stretchGeometry!=1)
{
gsInfo <<
"and stretch it... " << std::flush;
for (size_t i=0; i!=mp.nPatches(); ++i)
}
gsInfo <<
"Define boundary conditions... " << std::flush;
{
const index_t len = boundaryConditions.length();
for (gsMultiPatch<>::const_biterator it = mp.bBegin(); it < mp.bEnd(); ++it)
{
char b_local;
if ( len == 1 )
b_local = boundaryConditions[0];
else if ( i < len )
b_local = boundaryConditions[i];
else
{
gsInfo <<
"\nNot enough boundary conditions given.\n";
return EXIT_FAILURE;
}
if ( b_local == 'd' )
else if ( b_local == 'n' )
else
{
gsInfo <<
"\nInvalid boundary condition given; only 'd' (Dirichlet) and 'n' (Neumann) are supported.\n";
return EXIT_FAILURE;
}
++i;
}
if ( len > i )
gsInfo <<
"\nToo many boundary conditions have been specified. Ignoring the remaining ones.\n";
gsInfo <<
"done. "<<i<<
" boundary conditions set.\n";
}
if (xRefine)
{
if (mp.nPatches() > 1)
{
gsInfo <<
"\nThe option --XRefine is only available for single-patch geometries..\n";
return EXIT_FAILURE;
}
gsInfo <<
"Option --XRefine: Refine the grid in x-direction by interting " << xRefine
<< " knots in every knot span... " << std::flush;
mb[0].component(0).uniformRefine(xRefine,1);
}
gsInfo <<
"Setup bases and adjust degree... " << std::flush;
for ( size_t i = 0; i < mb.nBases(); ++ i )
mb[i].setDegreePreservingMultiplicity(degree);
for (
index_t i = 0; i < refinements; ++i )
mb.uniformRefine();
if (nonMatching)
{
gsInfo <<
"Option NonMatching: Make uniform refinement for every third patch... " << std::flush;
for (size_t i = 0; i < mb.nBases(); ++i)
if ( i%3 == 0 )
mb[i].uniformRefine();
--refinements;
gsInfo <<
"Option NonMatching: Increase spline degree for every other third patch... " << std::flush;
for (size_t i = 0; i < mb.nBases(); ++i)
if ( i%3 == 1 )
mb[i].setDegreePreservingMultiplicity(degree+1);
if (!dg)
{
gsInfo <<
"\nThe option --NonMatching does not allow a conforming discretization. Thus, option --DG is required.\n";
return EXIT_FAILURE;
}
}
gsInfo <<
"Setup assembler and assemble matrix... " << std::flush;
mp,
mb,
bc,
f,
(dirichlet::strategy) cmd.getInt("MG.DirichletStrategy"),
(iFace::strategy) cmd.getInt("MG.InterfaceStrategy")
);
assembler.assemble();
gsInfo <<
"Setup solver and solve... " << std::flush;
std::vector< gsSparseMatrix<real_t,RowMajor> > transferMatrices;
std::vector< gsMultiBasis<real_t> > multiBases;
std::vector<real_t> patchLocalDampingParameters;
{
if ( smoother == "Richardson" || smoother == "r" )
smootherOp = makeRichardsonOp(mg->
matrix(i));
else if ( smoother == "Jacobi" || smoother == "j" )
smootherOp = makeJacobiOp(mg->
matrix(i));
else if ( smoother == "GaussSeidel" || smoother == "gs" )
smootherOp = makeGaussSeidelOp(mg->
matrix(i));
else if ( smoother == "IncompleteLU" || smoother == "ilu" )
smootherOp = makeIncompleteLUOp(mg->
matrix(i));
else if ( smoother == "SubspaceCorrectedMassSmoother" || smoother == "scms" )
smootherOp = setupSubspaceCorrectedMassSmoother( i, mg->
numLevels(), mg->
matrix(i),
multiBases[i], bc, cmd.getGroup("MG"), patchLocalDampingParameters );
else if ( smoother == "Hybrid" || smoother == "hyb" )
makeGaussSeidelOp(mg->
matrix(i)),
multiBases[i], bc, cmd.getGroup("MG"), patchLocalDampingParameters )
);
else
{
gsInfo <<
"\n\nThe chosen smoother is unknown.\n\nKnown are:\n Richardson (r)\n Jacobi (j)\n GaussSeidel (gs)"
"\n IncompleteLU (ilu)\n SubspaceCorrectedMassSmoother (scms)\n Hybrid (hyb)\n\n";
return EXIT_FAILURE;
}
if (extrasmooth && i < mg->numLevels()-1)
{
}
}
x.setRandom( assembler.matrix().rows(), 1 );
if (iterativeSolver=="cg")
else if (iterativeSolver=="d")
else
{
gsInfo <<
"\n\nThe chosen iterative solver is unknown.\n\nKnown are:\n conjugate gradient (cg)\n direct (d)\n\n";
return EXIT_FAILURE;
}
const index_t iter = errorHistory.rows()-1;
const bool success = errorHistory(iter,0) < tolerance;
if (success)
gsInfo <<
"Reached desired tolerance after " << iter <<
" iterations:\n";
else
gsInfo <<
"Did not reach desired tolerance after " << iter <<
" iterations:\n";
if (errorHistory.rows() < 20)
gsInfo << errorHistory.transpose() <<
"\n\n";
else
gsInfo << errorHistory.topRows(5).transpose() <<
" ... " << errorHistory.bottomRows(5).transpose() <<
"\n\n";
if (!out.empty())
{
std::time_t time = std::time(NULL);
fd.add(cmd);
fd.add(x);
fd.addComment(std::string("multiGrid_example Timestamp:")+std::ctime(&time));
fd.save(out);
gsInfo <<
"Write solution to file " << out <<
"\n";
}
if (plot)
{
assembler.constructSolution(x, mpsol);
gsInfo <<
"Write Paraview data to file multiGrid_result.pvd\n";
gsWriteParaview<>(sol, "multiGrid_result", 1000);
}
if (!plot&&out.empty())
{
gsInfo <<
"Done. No output created, re-run with --plot to get a ParaView "
"file containing the solution or --out to write solution to xml file.\n";
}
return success ? EXIT_SUCCESS : EXIT_FAILURE;
}
std::vector<real_t>& patchLocalDampingParameters
)
{
const short_t dim = mb.topology().dim();
const iFace::strategy iFaceStrategy = (iFace::strategy)opt.
askInt(
"InterfaceStrategy", 1);
GISMO_ASSERT( iFaceStrategy == iFace::dg || iFaceStrategy == iFace::conforming,
"Unknown interface strategy." );
mb.getMapper(
(dirichlet::strategy)opt.
askInt(
"DirichletStrategy",11),
iFaceStrategy,
bc,
dm,
0
);
std::vector< std::vector<patchComponent> > components = mb.topology().allComponents(true);
const index_t nr_components = components.size();
if (patchLocalDampingParameters.size() == 0)
patchLocalDampingParameters.resize(nr_components);
for(
index_t ps=0; ps < 2*dim; ++ps )
std::vector< gsSparseMatrix<real_t,RowMajor> > transfers;
transfers.reserve(nr_components);
std::vector< gsLinearOperator<>::Ptr > ops;
ops.reserve(nr_components);
for (
index_t i=0; i<nr_components; ++i)
{
const index_t cdim = components[i][0].dim();
std::vector<gsBasis<>::uPtr> bases = mb.componentBasis_withIndices(components[i],dm,indices,true);
se.reserve(sz);
se.add(indices(j,0),j,(real_t)1);
transfer.setFrom(se);
if (sz>0)
{
if (cdim >= 2 && cdim >= dim-1)
{
real_t stiffFactor, massFactor;
if ( cdim == dim )
{
nrDifferentBases = 1;
stiffFactor = 1;
massFactor = 0;
}
else
{
if ( iFaceStrategy == iFace::dg && components[i].size() == 2 )
{
const index_t patch0 = components[i][0].patch(),
patch1 = components[i][1].patch();
const real_t h = math::min( mb[patch0].getMinCellLength(), mb[patch1].getMinCellLength() );
const index_t p = math::max( mb[patch0].maxDegree(), mb[patch1].maxDegree() );
nrDifferentBases = 2;
stiffFactor = h/p;
massFactor = p/h;
}
else
{
const index_t patch = components[i][0].patch();
const real_t h = mb[patch].getMinCellLength();
const index_t p = mb[patch].maxDegree();
nrDifferentBases = 1;
stiffFactor = h/p;
massFactor = p/h;
}
}
for (
index_t j=0; j!=nrDifferentBases; ++j)
{
*(bases[j]),
dir_bc,
massFactor,
stiffFactor
);
}
if ( nrDifferentBases > 1 )
{
for (
index_t j=0; j!=nrDifferentBases; ++j)
}
else
{
if (patchLocalDampingParameters[i] == 0)
{
bool saveLambda = (level > nrLevels - 5);
for (
index_t j=0; j!=bases[0]->dim(); ++j)
saveLambda &= bases[0]->component(j).numElements()
> static_cast<size_t>( bases[0]->component(j).maxDegree() );
if ( saveLambda )
patchLocalDampingParameters[i] = damping;
}
else
}
}
else
{
ops.push_back( makeSparseCholeskySolver(localMatrix) );
}
transfers.push_back(
give(transfer));
}
}
makeMatrixOp(matrix),
);
return result;
}
Generic preconditioner which applies an arbitrary linear operator to the residual.
Definition gsAdditiveOp.h:51
memory::unique_ptr< gsBlockOp< T > > uPtr
Unique pointer for gsBlockOp.
Definition gsBlockOp.h:51
static uPtr make(index_t nRows, index_t nCols)
Make function returning a smart pointer.
Definition gsBlockOp.h:60
void addOperator(index_t row, index_t col, const BasePtr &op)
Add a preconditioner to the block structure.
Definition gsBlockOp.hpp:30
Class containing a set of boundary conditions.
Definition gsBoundaryConditions.h:342
void addCondition(int p, boxSide s, condition_type::type t, gsFunctionSet< T > *f, short_t unknown=0, bool parametric=false, int comp=-1)
Adds another boundary condition.
Definition gsBoundaryConditions.h:650
Class for command-line argument parsing.
Definition gsCmdLine.h:57
The conjugate gradient method.
Definition gsConjugateGradient.h:30
gsConjugateGradient & setOptions(const gsOptionList &opt)
Set the options based on a gsOptionList.
Definition gsConjugateGradient.h:68
Class defining a globally constant function.
Definition gsConstantFunction.h:34
Maintains a mapping from patch-local dofs to global dof indices and allows the elimination of individ...
Definition gsDofMapper.h:69
index_t freeSize() const
Returns the number of free (not eliminated) dofs.
Definition gsDofMapper.h:436
A scalar of vector field defined on a m_parametric geometry.
Definition gsField.h:55
This class represents an XML data tree which can be read from or written to a (file) stream.
Definition gsFileData.h:34
static void open(const std::string &fn)
Opens the file fn using the preferred application of the OS.
Definition gsFileManager.cpp:688
static std::string getSearchPaths()
Get the defined search paths.
Definition gsFileManager.cpp:271
static bool fileExists(const std::string &name)
Checks if the file exists.
Definition gsFileManager.cpp:62
Class defining a multivariate (real or vector) function given by a string mathematical expression.
Definition gsFunctionExpr.h:52
Abstract base class representing a geometry map.
Definition gsGeometry.h:93
void scale(T s, int coord=-1)
Apply Scaling by factor s.
Definition gsGeometry.h:413
The gradient method.
Definition gsGradientMethod.h:30
gsGradientMethod & setOptions(const gsOptionList &opt)
Set the options based on a gsOptionList.
Definition gsGradientMethod.h:110
This wrapper class allows gsIterativeSolver to be used as gsLinearOperator.
Definition gsIterativeSolver.h:270
void solveDetailed(const VectorType &rhs, VectorType &x, VectorType &error_history)
Solves the linear system and stores the solution in x and records the error histroy.
Definition gsIterativeSolver.h:134
memory::unique_ptr< gsLinearOperator > uPtr
Unique pointer for gsLinearOperator.
Definition gsLinearOperator.h:36
memory::shared_ptr< gsLinearOperator > Ptr
Shared pointer for gsLinearOperator.
Definition gsLinearOperator.h:33
A matrix with arbitrary coefficient type and fixed or dynamic size.
Definition gsMatrix.h:41
Holds a set of patch-wise bases and their topology information.
Definition gsMultiBasis.h:37
index_t numLevels() const
Number of levels in the multigrid construction.
Definition gsMultiGrid.h:256
void setOptions(const gsOptionList &opt) override
Set the options based on a gsOptionList.
Definition gsMultiGrid.hpp:313
void setSmoother(index_t lvl, const PrecondPtr &sm)
Definition gsMultiGrid.hpp:284
const SpMatrix & matrix(index_t lvl) const
Stiffness matrix for given level.
Definition gsMultiGrid.hpp:291
void setCoarseSolver(const OpPtr &sol)
Set the solver for the coarsest problem (level 0)
Definition gsMultiGrid.h:302
OpPtr underlyingOp(index_t lvl) const
Underlying operator (=stiffness matrix) for given level.
Definition gsMultiGrid.h:269
Container class for a set of geometry patches and their topology, that is, the interface connections ...
Definition gsMultiPatch.h:100
Class which holds a list of parameters/options, and provides easy access to them.
Definition gsOptionList.h:33
Real getReal(const std::string &label) const
Reads value for option label from options.
Definition gsOptionList.cpp:44
index_t askInt(const std::string &label, const index_t &value=0) const
Reads value for option label from options.
Definition gsOptionList.cpp:117
static OpUPtr subspaceCorrectedMassSmootherOp(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions(), T sigma=(T)(12)/(T)(100), T alpha=0, T beta=1)
Definition gsPatchPreconditionersCreator.hpp:576
Implementation of an (multiple right-hand side) Poisson assembler.
Definition gsPoissonAssembler.h:37
void setOptions(const gsOptionList &opt)
Set options based on a gsOptionList object.
Definition gsPreconditioner.h:237
memory::shared_ptr< gsPreconditionerFromOp > Ptr
Shared pointer for gsLinearOperator.
Definition gsPreconditioner.h:156
void setDamping(const T tau)
Set damping parameter.
Definition gsPreconditioner.h:223
memory::unique_ptr< gsPreconditionerFromOp > uPtr
Unique pointer for gsLinearOperator.
Definition gsPreconditioner.h:159
static uPtr make(BasePtr underlying, BasePtr preconditioner, T tau=(T) 1)
Make function returning a smart pointer.
Definition gsPreconditioner.h:188
void setNumOfSweeps(index_t n)
Set the number of sweeps to be applied in the member function apply.
Definition gsPreconditioner.h:90
T estimateLargestEigenvalueOfPreconditionedSystem(index_t steps=10) const
Estimates the largest eigenvalue of .
Definition gsPreconditioner.h:121
virtual void setOptions(const gsOptionList &opt)
Set options based on a gsOptionList object.
Definition gsPreconditioner.h:111
Reads an object from a data file, if such the requested object exists in the file.
Definition gsReadFile.h:43
Class that provides a container for triplets (i,j,value) to be filled in a sparse matrix.
Definition gsSparseMatrix.h:34
Sparse matrix class, based on gsEigen::SparseMatrix.
Definition gsSparseMatrix.h:139
uPtr moveToPtr()
This function returns a smart pointer to the matrix. After calling it, the matrix object becomes empt...
Definition gsSparseMatrix.h:247
Main header to be included by clients using the G+Smo library.
#define short_t
Definition gsConfig.h:35
#define GISMO_ASSERT(cond, message)
Definition gsDebug.h:89
The G+Smo namespace, containing all definitions for the library.
@ dirichlet
Dirichlet type.
Definition gsBoundaryConditions.h:31
@ neumann
Neumann type.
Definition gsBoundaryConditions.h:33