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"
using namespace std;
void solve2d_laplace()
{
typedef DiscreteExteriorCalculus<2, 2, EigenLinearAlgebraBackend> Calculus;
typedef DiscreteExteriorCalculusFactory<EigenLinearAlgebraBackend> CalculusFactory;
Calculus
calculus = CalculusFactory::createFromDigitalSet(generateRingSet(
domain));
trace.
info() <<
"laplace = " << laplace << endl;
{
Board2D board;
board << dirac;
board.saveSVG("solve_laplace_calculus.svg");
}
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, DUAL, 0, DUAL> Solver;
Solver solver;
solver.compute(laplace);
Calculus::DualForm0 solution = solver.solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
Board2D board;
board << solution;
board.saveSVG("solve_laplace_simplicial_llt.svg");
}
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, DUAL, 0, DUAL> Solver;
Solver solver;
solver.compute(laplace);
Calculus::DualForm0 solution = solver.solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
Board2D board;
board << solution;
board.saveSVG("solve_laplace_simplicial_ldlt.svg");
}
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, DUAL, 0, DUAL> Solver;
Solver solver;
solver.compute(laplace);
Calculus::DualForm0 solution = solver.solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
Board2D board;
board << solution;
board.saveSVG("solve_laplace_conjugate_gradient.svg");
}
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, DUAL, 0, DUAL> Solver;
Solver solver;
solver.compute(laplace);
Calculus::DualForm0 solution = solver.solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
Board2D board;
board << solution;
board.saveSVG("solve_laplace_bicgstab.svg");
}
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, DUAL, 0, DUAL> Solver;
Solver solver;
solver.compute(laplace);
Calculus::DualForm0 solution = solver.solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
Board2D board;
board << solution;
board.saveSVG("solve_laplace_sparse_lu.svg");
}
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, DUAL, 0, DUAL> Solver;
Solver solver;
solver.compute(-laplace);
Calculus::DualForm0 solution = -solver.solve(dirac);
trace.
info() << solver.isValid() <<
" " << solver.myLinearAlgebraSolver.info() << endl;
Board2D board;
board << solution;
board.saveSVG("solve_laplace_sparse_qr.svg");
}
}
void solve2d_dual_decomposition()
{
trace.
beginBlock(
"2d discrete exterior calculus solve dual helmoltz decomposition");
typedef DiscreteExteriorCalculus<2, 2, EigenLinearAlgebraBackend> Calculus;
typedef DiscreteExteriorCalculusFactory<EigenLinearAlgebraBackend> CalculusFactory;
Calculus
calculus = CalculusFactory::createFromDigitalSet(generateDoubleRingSet(
domain));
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 LinearOperator<Calculus, 1, DUAL, 0, DUAL> ad1 = h2p * d1p * h1;
const LinearOperator<Calculus, 2, DUAL, 1, DUAL> ad2 = h1p * d0p * h2;
Calculus::DualVectorField input_vector_field(
calculus);
{
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;
{
Board2D board;
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);
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, DUAL, 0, DUAL> Solver;
Solver solver;
solver.compute(ad1 * d0);
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;
}
{
Board2D board;
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);
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 2, DUAL, 2, DUAL> Solver;
Solver solver;
solver.compute(d1 * ad2);
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;
}
{
Board2D board;
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;
{
Board2D board;
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");
typedef DiscreteExteriorCalculus<2, 2, EigenLinearAlgebraBackend> Calculus;
typedef DiscreteExteriorCalculusFactory<EigenLinearAlgebraBackend> CalculusFactory;
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 LinearOperator<Calculus, 1, PRIMAL, 0, PRIMAL> ad1 = h2p * d1p * h1;
const LinearOperator<Calculus, 2, PRIMAL, 1, PRIMAL> ad2 = h1p * d0p * h2;
Calculus::PrimalVectorField input_vector_field(
calculus);
{
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;
{
Board2D board;
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);
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> Solver;
Solver solver;
solver.compute(ad1 * d0);
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;
}
{
Board2D board;
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);
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 2, PRIMAL, 2, PRIMAL> Solver;
Solver solver;
solver.compute(d1 * ad2);
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;
}
{
Board2D board;
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;
{
Board2D board;
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");
typedef DiscreteExteriorCalculus<3, 3, EigenLinearAlgebraBackend> Calculus;
for (int kk=2; kk<=18; kk++)
for (int ll=4; ll<=36; ll++)
{
{
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::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::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::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;
}
}
}
for (int kk=2; kk<=18; kk++)
for (int ll=16; ll<=24; ll++)
{
{
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::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::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::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;
}
}
}
for (int kk=4; kk<=36; kk++)
for (int ll=0; ll<=12; ll++)
{
{
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::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::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::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;
}
}
}
for (int kk=0; kk<=12; kk++)
for (int ll=16; ll<=24; ll++)
{
{
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::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::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::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;
}
}
}
{
typedef Viewer3D<Z3i::Space, Z3i::KSpace>
Viewer;
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 LinearOperator<Calculus, 1, PRIMAL, 0, PRIMAL> ad1 = h3p * d2p * h1;
const LinearOperator<Calculus, 2, PRIMAL, 1, PRIMAL> ad2 = h2p * d1p * h2;
{
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);
{
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;
{
typedef Viewer3D<Z3i::Space, Z3i::KSpace>
Viewer;
viewer->setWindowTitle("input vector field");
}
Calculus::PrimalForm0 solution_curl_free(
calculus);
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 0, PRIMAL, 0, PRIMAL> Solver;
Solver solver;
solver.compute(ad1 * d0);
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;
}
{
typedef Viewer3D<Z3i::Space, Z3i::KSpace>
Viewer;
viewer->setWindowTitle("curl free solution");
}
Calculus::PrimalForm2 solution_div_free(
calculus);
{
typedef DiscreteExteriorCalculusSolver<Calculus, LinearAlgebraSolver, 2, PRIMAL, 2, PRIMAL> Solver;
Solver solver;
solver.compute(d1 * ad2);
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;
}
{
typedef Viewer3D<Z3i::Space, Z3i::KSpace>
Viewer;
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;
{
typedef Viewer3D<Z3i::Space, Z3i::KSpace>
Viewer;
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();
}
Structure representing an RGB triple with alpha component.
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...
Space::RealPoint RealPoint
HyperRectDomain< Space > Domain
HyperRectDomain< Space > Domain
Space::RealPoint RealPoint
DGtal is the top-level namespace which contains all DGtal functions and types.
DGtal::uint32_t Dimension
static void draw(Display3D< Space, KSpace > &display, const DGtal::DiscreteExteriorCalculus< dimEmbedded, dimAmbient, TLinearAlgebraBackend, TInteger > &calculus)
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
int main(int argc, char **argv)
unsigned int dim(const Vector &z)