Example of primal and dual Helmoltz decomposition in 2D and 3D using Discrete Exterior Calculus.
#include <string>
#include <QApplication>
#include "DECExamplesCommon.h"
#include "DGtal/math/linalg/EigenSupport.h"
#include "DGtal/dec/DiscreteExteriorCalculus.h"
#include "DGtal/dec/DiscreteExteriorCalculusSolver.h"
#include "DGtal/dec/DiscreteExteriorCalculusFactory.h"
#include "DGtal/io/viewers/Viewer3D.h"
#include "DGtal/io/boards/Board2D.h"
#include "DGtal/io/readers/GenericReader.h"
void solve2d_laplace()
{
trace.
beginBlock(
"2d discrete exterior calculus solve laplace");
Calculus calculus = CalculusFactory::createFromDigitalSet(generateRingSet(
domain));
trace.
info() << calculus << endl;
Calculus::DualIdentity0 laplace = calculus.laplace<DUAL>() + 0.01 * calculus.identity<0, DUAL>();
trace.
info() <<
"laplace = " << laplace << endl;
Calculus::DualForm0 dirac(calculus);
dirac.myContainer(calculus.getCellIndex( calculus.myKSpace.uSpel(
Z2i::Point(2,5))) ) = 1;
{
board << dirac;
board.
saveSVG(
"solve_laplace_calculus.svg");
}
{
Solver solver;
Calculus::DualForm0 solution = solver.
solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() << solution << endl;
board << solution;
board.
saveSVG(
"solve_laplace_simplicial_llt.svg");
}
{
Solver solver;
Calculus::DualForm0 solution = solver.
solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() << solution << endl;
board << solution;
board.
saveSVG(
"solve_laplace_simplicial_ldlt.svg");
}
{
Solver solver;
Calculus::DualForm0 solution = solver.
solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() << solution << endl;
board << solution;
board.
saveSVG(
"solve_laplace_conjugate_gradient.svg");
}
{
trace.
beginBlock(
"biconjugate gradient stabilized (bicgstab)");
Solver solver;
Calculus::DualForm0 solution = solver.
solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() << solution << endl;
board << solution;
board.
saveSVG(
"solve_laplace_bicgstab.svg");
}
{
Solver solver;
Calculus::DualForm0 solution = solver.
solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() << solution << endl;
board << solution;
board.
saveSVG(
"solve_laplace_sparse_lu.svg");
}
{
Solver solver;
Calculus::DualForm0 solution = -solver.
solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() << solution << endl;
board << solution;
board.
saveSVG(
"solve_laplace_sparse_qr.svg");
}
}
void solve2d_dual_decomposition()
{
trace.
beginBlock(
"2d discrete exterior calculus solve dual helmoltz decomposition");
Calculus calculus = CalculusFactory::createFromDigitalSet(generateDoubleRingSet(
domain));
trace.
info() << calculus << endl;
const Calculus::DualDerivative0 d0 = calculus.derivative<0, DUAL>();
const Calculus::DualDerivative1 d1 = calculus.derivative<1, DUAL>();
const Calculus::PrimalDerivative0 d0p = calculus.derivative<0, PRIMAL>();
const Calculus::PrimalDerivative1 d1p = calculus.derivative<1, PRIMAL>();
const Calculus::DualHodge1 h1 = calculus.hodge<1, DUAL>();
const Calculus::DualHodge2 h2 = calculus.hodge<2, DUAL>();
const Calculus::PrimalHodge1 h1p = calculus.hodge<1, PRIMAL>();
const Calculus::PrimalHodge2 h2p = calculus.hodge<2, PRIMAL>();
Calculus::DualVectorField input_vector_field(calculus);
for (Calculus::Index ii=0; ii<input_vector_field.length(); ii++)
{
input_vector_field.myCoordinates(ii, 0) = cos(-.5*cell_center[0]+ .3*cell_center[1]);
input_vector_field.myCoordinates(ii, 1) = cos(.4*cell_center[0]+ .8*cell_center[1]);
}
const Calculus::DualForm1 input_one_form = calculus.flat(input_vector_field);
const Calculus::DualForm0 input_one_form_anti_derivated = ad1 * input_one_form;
const Calculus::DualForm2 input_one_form_derivated = d1 * input_one_form;
{
board << calculus;
board <<
CustomStyle(
"KForm",
new KFormStyle2D(-1, 1));
board << input_one_form;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(.75));
board << input_vector_field;
board.
saveSVG(
"solve_2d_dual_decomposition_calculus.svg");
}
Calculus::DualForm0 solution_curl_free(calculus);
{
Solver solver;
solution_curl_free = solver.
solve(input_one_form_anti_derivated);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() <<
"min=" << solution_curl_free.myContainer.minCoeff() <<
" max=" << solution_curl_free.myContainer.maxCoeff() << endl;
}
{
board << calculus;
board << solution_curl_free;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(.75));
board << calculus.sharp(d0*solution_curl_free);
board.
saveSVG(
"solve_2d_dual_decomposition_curl_free.svg");
}
Calculus::DualForm2 solution_div_free(calculus);
{
Solver solver;
solution_div_free = solver.
solve(input_one_form_derivated);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() <<
"min=" << solution_div_free.myContainer.minCoeff() <<
" max=" << solution_div_free.myContainer.maxCoeff() << endl;
}
{
board << calculus;
board << solution_div_free;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(1.5));
board << calculus.sharp(ad2*solution_div_free);
board.
saveSVG(
"solve_2d_dual_decomposition_div_free.svg");
}
const Calculus::DualForm1 solution_harmonic = input_one_form - d0*solution_curl_free - ad2*solution_div_free;
trace.
info() <<
"min=" << solution_harmonic.myContainer.minCoeff() <<
" max=" << solution_harmonic.myContainer.maxCoeff() << endl;
{
board << calculus;
board << solution_harmonic;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(20));
board << calculus.sharp(solution_harmonic);
board.
saveSVG(
"solve_2d_dual_decomposition_harmonic.svg");
}
}
void solve2d_primal_decomposition()
{
trace.
beginBlock(
"2d discrete exterior calculus solve primal helmoltz decomposition");
Calculus calculus = CalculusFactory::createFromDigitalSet(generateDoubleRingSet(
domain));
const Calculus::PrimalDerivative0 d0 = calculus.derivative<0,
PRIMAL>();
const Calculus::PrimalDerivative1 d1 = calculus.derivative<1,
PRIMAL>();
const Calculus::DualDerivative0 d0p = calculus.derivative<0,
DUAL>();
const Calculus::DualDerivative1 d1p = calculus.derivative<1,
DUAL>();
const Calculus::PrimalHodge1 h1 = calculus.hodge<1,
PRIMAL>();
const Calculus::PrimalHodge2 h2 = calculus.hodge<2,
PRIMAL>();
const Calculus::DualHodge1 h1p = calculus.hodge<1,
DUAL>();
const Calculus::DualHodge2 h2p = calculus.hodge<2,
DUAL>();
Calculus::PrimalVectorField input_vector_field(calculus);
for (Calculus::Index ii=0; ii<input_vector_field.length(); ii++)
{
input_vector_field.myCoordinates(ii, 0) = cos(-.5*cell_center[0]+ .3*cell_center[1]);
input_vector_field.myCoordinates(ii, 1) = cos(.4*cell_center[0]+ .8*cell_center[1]);
}
const Calculus::PrimalForm1 input_one_form = calculus.flat(input_vector_field);
const Calculus::PrimalForm0 input_one_form_anti_derivated = ad1 * input_one_form;
const Calculus::PrimalForm2 input_one_form_derivated = d1 * input_one_form;
{
board << calculus;
board << input_one_form;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(.75));
board << input_vector_field;
board.
saveSVG(
"solve_2d_primal_decomposition_calculus.svg");
}
Calculus::PrimalForm0 solution_curl_free(calculus);
{
Solver solver;
solution_curl_free = solver.
solve(input_one_form_anti_derivated);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() <<
"min=" << solution_curl_free.myContainer.minCoeff() <<
" max=" << solution_curl_free.myContainer.maxCoeff() << endl;
}
{
board << calculus;
board << solution_curl_free;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(.75));
board << calculus.sharp(d0*solution_curl_free);
board.
saveSVG(
"solve_2d_primal_decomposition_curl_free.svg");
}
Calculus::PrimalForm2 solution_div_free(calculus);
{
Solver solver;
solution_div_free = solver.
solve(input_one_form_derivated);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() <<
"min=" << solution_div_free.myContainer.minCoeff() <<
" max=" << solution_div_free.myContainer.maxCoeff() << endl;
}
{
board << calculus;
board << solution_div_free;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(1.5));
board << calculus.sharp(ad2*solution_div_free);
board.
saveSVG(
"solve_2d_primal_decomposition_div_free.svg");
}
const Calculus::PrimalForm1 solution_harmonic = input_one_form - d0*solution_curl_free - ad2*solution_div_free;
trace.
info() <<
"min=" << solution_harmonic.myContainer.minCoeff() <<
" max=" << solution_harmonic.myContainer.maxCoeff() << endl;
{
board << calculus;
board << solution_harmonic;
board <<
CustomStyle(
"VectorField",
new VectorFieldStyle2D(30));
board << calculus.sharp(solution_harmonic);
board.
saveSVG(
"solve_2d_primal_decomposition_harmonic.svg");
}
}
void solve3d_decomposition()
{
trace.
beginBlock(
"3d discrete exterior calculus solve helmoltz decomposition");
Calculus calculus;
for (int kk=2; kk<=18; kk++)
for (int ll=4; ll<=36; ll++)
{
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,4,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::NEG;
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,36,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::POS;
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(4,ll,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = ( *calculus.myKSpace.uDirs(cell) == 2 ? Calculus::KSpace::NEG : Calculus::KSpace::POS );
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(36,ll,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = ( *calculus.myKSpace.uDirs(cell) == 2 ? Calculus::KSpace::POS : Calculus::KSpace::NEG );
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
}
for (int kk=2; kk<=18; kk++)
for (int ll=16; ll<=24; ll++)
{
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,16,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = ( *calculus.myKSpace.uDirs(cell) == 0 ? Calculus::KSpace::NEG : Calculus::KSpace::POS );
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,24,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = ( *calculus.myKSpace.uDirs(cell) == 0 ? Calculus::KSpace::POS : Calculus::KSpace::NEG );
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(16,ll,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::POS;
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(24,ll,kk));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::NEG;
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
}
for (int kk=4; kk<=36; kk++)
for (int ll=0; ll<=12; ll++)
{
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(4+ll,kk,2));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::POS;
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(4+ll,kk,18));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::NEG;
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(24+ll,kk,2));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::POS;
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(24+ll,kk,18));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::NEG;
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
}
for (int kk=0; kk<=12; kk++)
for (int ll=16; ll<=24; ll++)
{
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,4+kk,2));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::POS;
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,4+kk,18));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::NEG;
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,24+kk,2));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::POS;
break;
case 2:
sign = Calculus::KSpace::NEG;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
{
const Calculus::Cell cell = calculus.myKSpace.uCell(
Z3i::Point(ll,24+kk,18));
Calculus::KSpace::Sign sign = Calculus::KSpace::POS;
{
case 0:
sign = Calculus::KSpace::POS;
break;
case 1:
sign = Calculus::KSpace::NEG;
break;
case 2:
sign = Calculus::KSpace::POS;
break;
default:
break;
}
calculus.insertSCell( calculus.myKSpace.signs(cell, sign) );
}
}
calculus.updateIndexes();
{
viewer->setWindowTitle("structure");
}
const Calculus::PrimalDerivative0 d0 = calculus.derivative<0,
PRIMAL>();
const Calculus::PrimalDerivative1 d1 = calculus.derivative<1,
PRIMAL>();
const Calculus::DualDerivative1 d1p = calculus.derivative<1,
DUAL>();
const Calculus::DualDerivative2 d2p = calculus.derivative<2,
DUAL>();
const Calculus::PrimalHodge1 h1 = calculus.hodge<1,
PRIMAL>();
const Calculus::PrimalHodge2 h2 = calculus.hodge<2,
PRIMAL>();
const Calculus::DualHodge2 h2p = calculus.hodge<2,
DUAL>();
const Calculus::DualHodge3 h3p = calculus.hodge<3,
DUAL>();
{
const Calculus::PrimalIdentity0 laplace = calculus.laplace<
PRIMAL>();
const Eigen::VectorXd laplace_diag = laplace.myContainer.diagonal();
boost::array<int, 7> degrees;
std::fill(degrees.begin(), degrees.end(), 0);
for (int kk=0; kk<laplace_diag.rows(); kk++)
{
const int degree = laplace_diag[kk];
ASSERT( degree >= 0 );
ASSERT( static_cast<unsigned int>(degree) < degrees.size() );
degrees[degree] ++;
}
for (int kk=0; kk<7; kk++)
trace.
info() << kk <<
" " << degrees[kk] << endl;
}
Calculus::PrimalVectorField input_vector_field(calculus);
for (Calculus::Index ii=0; ii<input_vector_field.length(); ii++)
{
input_vector_field.myCoordinates(ii, 0) = -cos(-.3*cell_center[0] + .6*cell_center[1] + .8*cell_center[2]);
input_vector_field.myCoordinates(ii, 1) = sin(.8*cell_center[0] + .3*cell_center[1] - .4*cell_center[2]);
input_vector_field.myCoordinates(ii, 2) = -cos(cell_center[2]*.5);
}
const Calculus::PrimalForm1 input_one_form = calculus.flat(input_vector_field);
const Calculus::PrimalForm0 input_one_form_anti_derivated = ad1 * input_one_form;
const Calculus::PrimalForm2 input_one_form_derivated = d1 * input_one_form;
{
viewer->setWindowTitle("input vector field");
}
Calculus::PrimalForm0 solution_curl_free(calculus);
{
Solver solver;
solution_curl_free = solver.
solve(input_one_form_anti_derivated);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() <<
"min=" << solution_curl_free.myContainer.minCoeff() <<
" max=" << solution_curl_free.myContainer.maxCoeff() << endl;
}
{
viewer->setWindowTitle("curl free solution");
}
Calculus::PrimalForm2 solution_div_free(calculus);
{
Solver solver;
solution_div_free = solver.
solve(input_one_form_derivated);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
trace.
info() <<
"min=" << solution_div_free.myContainer.minCoeff() <<
" max=" << solution_div_free.myContainer.maxCoeff() << endl;
}
{
viewer->setWindowTitle("div free solution");
}
const Calculus::PrimalForm1 solution_harmonic = input_one_form - d0*solution_curl_free - ad2*solution_div_free;
trace.
info() <<
"min=" << solution_harmonic.myContainer.minCoeff() <<
" max=" << solution_harmonic.myContainer.maxCoeff() << endl;
{
viewer->setWindowTitle("harmonic");
}
}
int main(
int argc,
char* argv[])
{
QApplication app(argc,argv);
solve2d_laplace();
solve2d_dual_decomposition();
solve2d_primal_decomposition();
solve3d_decomposition();
return app.exec();
}
Aim: This class specializes a 'Board' class so as to display DGtal objects more naturally (with <<)....
Structure representing an RGB triple with alpha component.
Aim: This class provides static members to create DEC structures from various other DGtal structures.
Aim: This wraps a linear algebra solver around a discrete exterior calculus.
SolutionKForm solve(const InputKForm &input_kform) const
DiscreteExteriorCalculusSolver & compute(const Operator &linear_operator)
Aim: DiscreteExteriorCalculus represents a calculus in the dec package. This is the main structure in...
Aim: LinearOperator represents discrete linear operator between discrete kforms in the DEC package.
void beginBlock(const std::string &keyword="")
virtual void show()
Overload QWidget method in order to add a call to updateList() method (to ensure that the lists are w...
void saveSVG(const char *filename, PageSize size=Board::BoundingBox, double margin=10.0) const
DGtal is the top-level namespace which contains all DGtal functions and types.
DGtal::uint32_t Dimension
Factory for GPL Display3D:
Eigen::SimplicialLLT< SparseMatrix > SolverSimplicialLLT
Solvers on sparse matrices.
Eigen::SimplicialLDLT< SparseMatrix > SolverSimplicialLDLT
Eigen::BiCGSTAB< SparseMatrix > SolverBiCGSTAB
Eigen::SparseQR< SparseMatrix, Eigen::COLAMDOrdering< SparseMatrix::Index > > SolverSparseQR
Eigen::SparseLU< SparseMatrix > SolverSparseLU
Eigen::ConjugateGradient< SparseMatrix > SolverConjugateGradient