1 #ifndef MAXWELL_HPP_INCLUDED
2 #define MAXWELL_HPP_INCLUDED
4 #include <Eigen/Sparse>
11 #include <unsupported/Eigen/src/IterativeSolvers/Scaling.h>
34 typedef Eigen::SparseLU<SystemMatrixType,Eigen::COLAMDOrdering<int> >
SolverType;
36 static constexpr std::string_view
SolverName =
"SparseLU";
41 bool use_threads =
true ,
42 std::array<int,2>
const* dqr =
nullptr ,
43 std::ostream &output = std::cout );
70 void assembleRHS(Eigen::Ref<const Eigen::VectorXd>
const & rho ,
71 Eigen::Ref<const Eigen::VectorXd>
const & J ,
72 Eigen::Ref<const Eigen::VectorXd>
const & EOld ,
73 Eigen::Ref<const Eigen::VectorXd>
const & BOld );
80 Eigen::VectorXd
solve();
82 Eigen::VectorXd
solve(
const Eigen::VectorXd &rhs);
88 template<
typename Derived>
89 double norm(Eigen::MatrixBase<Derived>
const &E ,
95 template<
typename Derived>
96 double normd(Eigen::MatrixBase<Derived>
const &E ,
100 void assembleLocalContribution(Eigen::Ref<const Eigen::MatrixXd>
const & A,
size_t iT,
size_t kL,
size_t kR, std::forward_list<Eigen::Triplet<double>> * triplets)
const;
101 void assembleLocalContributionH(Eigen::Ref<const Eigen::MatrixXd>
const & A,
size_t iT,
size_t k, std::forward_list<Eigen::Triplet<double>> * triplets)
const;
102 void assembleLocalContribution(Eigen::Ref<const Eigen::VectorXd>
const & R,
size_t iT,
size_t k);
104 void assembleSystem();
110 std::ostream & _output;
112 Eigen::VectorXd _rhs;
114 Eigen::VectorXd _interpOne;
115 std::vector<Eigen::MatrixXd> _ALoc00;
116 std::vector<Eigen::MatrixXd> _ALoc11;
117 std::vector<Eigen::MatrixXd> _ALoc22;
118 Eigen::VectorXd _scalingL, _scalingR;
124 :
u(maxwell.dimensionSystem()),
sizeG(maxwell.dimensionG()),
sizeE(maxwell.dimensionE()),
sizeB(maxwell.dimensionB()),
137 Eigen::VectorBlock<Eigen::VectorXd>
h()
142 Eigen::VectorBlock<Eigen::VectorXd>
G()
147 Eigen::VectorBlock<Eigen::VectorXd>
E()
152 Eigen::VectorBlock<Eigen::VectorXd>
B()
163 : _ddrcore(mesh,degree,use_threads,dqr, output), _dt(dt), _use_threads(use_threads), _output(output), _rhs(dimensionSystem())
165 auto oneFunc = [](size_t ,
const Eigen::Vector<double,2> &)->Eigen::Vector<double,1> {
166 return Eigen::Vector<double,1>{1.};
168 _interpOne = _ddrcore.template interpolate<0>(oneFunc,dqr);
173 void MaxwellProblem::assembleLocalContribution(Eigen::Ref<const Eigen::MatrixXd>
const & A,
size_t iT,
size_t kL,
size_t kR,
174 std::forward_list<Eigen::Triplet<double>> * triplets)
const
194 for (
size_t iDL = 0; iDL <= 2; ++iDL) {
195 const size_t nbLDofsL = _ddrcore.
dofspace(kL).numLocalDofs(iDL);
196 if (nbLDofsL == 0)
continue;
197 std::vector<size_t>
const & boundariesL = _ddrcore.
mesh()->get_boundary(iDL,2,iT);
198 for (
size_t iDR = 0; iDR <= 2; ++iDR) {
199 const size_t nbLDofsR = _ddrcore.
dofspace(kR).numLocalDofs(iDR);
200 if (nbLDofsR == 0)
continue;
201 std::vector<size_t>
const & boundariesR = _ddrcore.
mesh()->get_boundary(iDR,2,iT);
202 for (
size_t iFL = 0; iFL < boundariesL.size(); ++iFL) {
203 const size_t offsetL = gOffsetL + _ddrcore.
dofspace(kL).globalOffset(iDL,boundariesL[iFL]);
204 const size_t lOffsetL = _ddrcore.
dofspace(kL).localOffset(iDL,2,iFL,iT);
205 for (
size_t iFR = 0; iFR < boundariesR.size(); ++iFR) {
206 const size_t offsetR = gOffsetR + _ddrcore.
dofspace(kR).globalOffset(iDR,boundariesR[iFR]);
207 const size_t lOffsetR = _ddrcore.
dofspace(kR).localOffset(iDR,2,iFR,iT);
208 for (
size_t iRow = 0; iRow < nbLDofsL; ++iRow) {
209 for (
size_t iCol = 0; iCol < nbLDofsR; ++iCol) {
210 triplets->emplace_front(offsetL + iRow,offsetR + iCol,A(lOffsetL + iRow,lOffsetR + iCol));
219 void MaxwellProblem::assembleLocalContributionH(Eigen::Ref<const Eigen::MatrixXd>
const & A,
size_t iT,
size_t k, std::forward_list<Eigen::Triplet<double>> * triplets)
const
221 assert(A.cols() == 1 &&
"Expected only 1 harmonic form");
232 for (
size_t iD = 0; iD <= 2; ++iD) {
233 const size_t nbLDofs = _ddrcore.
dofspace(k).numLocalDofs(iD);
234 if (nbLDofs == 0)
continue;
235 std::vector<size_t>
const & boundaries = _ddrcore.
mesh()->get_boundary(iD,2,iT);
236 for (
size_t iF = 0; iF < boundaries.size(); ++iF) {
237 const size_t offset = gOffset + _ddrcore.
dofspace(k).globalOffset(iD,boundaries[iF]);
238 const size_t lOffset = _ddrcore.
dofspace(k).localOffset(iD,2,iF,iT);
239 for (
size_t iRow = 0; iRow < nbLDofs; ++iRow) {
240 triplets->emplace_front(offset+iRow,0,A(lOffset+iRow,0));
241 triplets->emplace_front(0,offset+iRow,A(lOffset+iRow,0));
247 void MaxwellProblem::assembleLocalContribution(Eigen::Ref<const Eigen::VectorXd>
const & R,
size_t iT,
size_t k)
259 for (
size_t iD = 0; iD <= 2; ++iD) {
260 const size_t nbLDofs = _ddrcore.
dofspace(k).numLocalDofs(iD);
261 if (nbLDofs == 0)
continue;
262 std::vector<size_t>
const & boundaries = _ddrcore.
mesh()->get_boundary(iD,2,iT);
263 for (
size_t iF = 0; iF < boundaries.size(); ++iF) {
264 const size_t offset = gOffset + _ddrcore.
dofspace(k).globalOffset(iD,boundaries[iF]);
265 const size_t lOffset = _ddrcore.
dofspace(k).localOffset(iD,2,iF,iT);
266 for (
size_t iRow = 0; iRow < nbLDofs; ++iRow) {
267 std::atomic_ref<double> atomicRHS(_rhs[offset+iRow]);
268 atomicRHS.fetch_add(R(lOffset+iRow),std::memory_order_relaxed);
274 void MaxwellProblem::assembleSystem()
276 const size_t nb_cell = _ddrcore.
mesh()->n_cells(2);
278 std::vector<Eigen::MatrixXd> ALoc0h(nb_cell);
279 std::vector<Eigen::MatrixXd> ALoc01(nb_cell);
281 std::vector<Eigen::MatrixXd> ALoc12(nb_cell);
283 _ALoc00.resize(nb_cell);
285 _ALoc11.resize(nb_cell);
286 _ALoc22.resize(nb_cell);
287 std::function<void(
size_t start,
size_t end)> assemble_local = [&](
size_t start,
size_t end)->
void {
288 for (
size_t iT = start; iT < end; iT++) {
295 Eigen::MatrixXd loc0h = loc00*_ddrcore.
dofspace(0).restrict(2,iT,_interpOne);
296 Eigen::MatrixXd loc01 = _ddrcore.
compose_diff(0,2,iT).transpose()*loc11;
298 Eigen::MatrixXd loc12 = _ddrcore.
compose_diff(1,2,iT).transpose()*loc22;
311 _output<<
"[MaxwellProblem] Assembling local contributions..."<<std::flush;
313 _output<<
"\r[MaxwellProblem] Assembled local contributions "<<std::endl;
315 auto batch_local_assembly = [&](
size_t start,
size_t end, std::forward_list<Eigen::Triplet<double>> * triplets)->
void {
316 for (
size_t iT = start; iT < end; iT++) {
318 assembleLocalContributionH(ALoc0h[iT],iT,0,triplets);
319 assembleLocalContribution(ALoc01[iT],iT,0,1,triplets);
320 assembleLocalContribution(ALoc01[iT].transpose(),iT,1,0,triplets);
322 assembleLocalContribution(0.5*_dt*ALoc12[iT],iT,1,2,triplets);
323 assembleLocalContribution(0.5*_dt*ALoc12[iT].transpose(),iT,2,1,triplets);
324 assembleLocalContribution(-1.*_ALoc11[iT],iT,1,1,triplets);
325 assembleLocalContribution(_ALoc22[iT],iT,2,2,triplets);
328 _output<<
"[MaxwellProblem] Assembling global system from local contributions..."<<std::flush;
330 batch_local_assembly,_use_threads);
331 _output<<
"\r[MaxwellProblem] Assembled global system from local contributions "<<std::endl;
334 void MaxwellProblem::assembleRHS(Eigen::Ref<const Eigen::VectorXd>
const & rho, Eigen::Ref<const Eigen::VectorXd>
const & J,Eigen::Ref<const Eigen::VectorXd>
const & EOld, Eigen::Ref<const Eigen::VectorXd>
const & BOld)
336 auto batch_local_assembly = [&](
size_t start,
size_t end)->
void {
337 for (
size_t iT = start; iT < end; ++iT) {
341 assert(rho.size() ==
dimensionG() &&
"Incorrect size of rho");
342 loc = -_ALoc00[iT]*_ddrcore.
dofspace(0).restrict(2,iT,rho);
343 assembleLocalContribution(loc,iT,0);
346 assert(J.size() ==
dimensionE() &&
"Incorrect size of J");
347 assert(EOld.size() ==
dimensionE() &&
"Incorrect size of EOld");
348 loc = _ALoc11[iT]*_ddrcore.
dofspace(1).restrict(2,iT,_dt*J - EOld);
349 loc -= 0.5*_dt*_ddrcore.
compose_diff(1,2,iT).transpose()*_ALoc22[iT]*_ddrcore.
dofspace(2).restrict(2,iT,BOld);
350 assembleLocalContribution(loc,iT,1);
352 assert(BOld.size() ==
dimensionB() &&
"Incorrect size of BOld");
353 loc = _ALoc22[iT]*_ddrcore.
dofspace(2).restrict(2,iT,BOld);
354 loc -= 0.5*_dt*_ALoc22[iT]*_ddrcore.
compose_diff(1,2,iT)*_ddrcore.
dofspace(1).restrict(2,iT,EOld);
355 assembleLocalContribution(loc,iT,2);
358 _output<<
"[MaxwellProblem] Assembling RHS..."<<std::flush;
360 parallel_for(_ddrcore.
mesh()->n_cells(2),batch_local_assembly,_use_threads);
361 _output<<
"\r[MaxwellProblem] Assembled RHS "<<std::endl;
366 double const err = (_system*u - _rhs).
norm()/_rhs.norm();
368 std::cerr<<
"[MaxwellProblem] The relative error on the solution is "<<err<<
". This could indicate that the solver silently failed"<<std::endl;
374 _output <<
"[MaxwellProblem] Setting solver "<<
SolverName<<
" with "<<
dimensionSystem()<<
" degrees of freedom"<<std::endl;
376 Eigen::IterScaling<SystemMatrixType> iterScaling;
377 iterScaling.computeRef(systemScaled);
378 _scalingL = iterScaling.LeftScaling();
379 _scalingR = iterScaling.RightScaling();
380 _solver.compute(systemScaled);
381 if (_solver.info() != Eigen::Success) {
382 std::cerr <<
"[MaxwellProblem] Failed to factorize the system" << std::endl;
383 throw std::runtime_error(
"Factorization failed");
387 Eigen::VectorXd scaledRhs = _rhs.cwiseProduct(_scalingR);
388 Eigen::VectorXd u = _solver.solve(scaledRhs);
389 u = u.cwiseProduct(_scalingL);
390 if (_solver.info() != Eigen::Success) {
391 std::cerr <<
"[MaxwellProblem] Failed to solve the system" << std::endl;
392 throw std::runtime_error(
"Solver failed");
399 Eigen::VectorXd scaledRhs = rhs.cwiseProduct(_scalingR);
400 Eigen::VectorXd u = _solver.solve(scaledRhs);
401 u = u.cwiseProduct(_scalingL);
402 if (_solver.info() != Eigen::Success) {
403 std::cerr <<
"[MaxwellProblem] Failed to solve the system" << std::endl;
404 throw std::runtime_error(
"Solver failed");
410 template<
typename Derived>
413 assert(E.size() == _ddrcore.
dofspace(k).dimensionMesh() &&
"Mismatched dimension, wrong form degree?");
414 const size_t nb_cell = _ddrcore.
mesh()->n_cells(2);
415 std::vector<double> accErr(nb_cell);
416 std::function<void(
size_t start,
size_t end)> compute_local = [&](
size_t start,
size_t end)->
void {
417 for (
size_t iT = start; iT < end; ++iT){
418 Eigen::VectorXd locE = _ddrcore.
dofspace(k).restrict(2,iT,E);
419 Eigen::MatrixXd matL2;
435 accErr[iT] = locE.dot(matL2*locE);
440 for (
double err : accErr) {
443 return std::sqrt(acc);
446 template<
typename Derived>
449 if (k == 2)
return 0.;
450 assert(E.size() == _ddrcore.
dofspace(k).dimensionMesh() &&
"Mismatched dimension, wrong form degree?");
451 const size_t nb_cell = _ddrcore.
mesh()->n_cells(2);
452 std::vector<double> accErr(nb_cell);
453 std::function<void(
size_t start,
size_t end)> compute_local = [&](
size_t start,
size_t end)->
void {
454 for (
size_t iT = start; iT < end; ++iT){
456 Eigen::MatrixXd matL2;
469 accErr[iT] = locE.dot(matL2*locE);
474 for (
double err : accErr) {
477 return std::sqrt(acc);
Eigen::MatrixXd computeL2Product(size_t k, size_t i_cell) const
Compute the local product (including the cell boundary)
Definition: ddr_spaces.cpp:272
const Mesh< dimension > * mesh() const
Return the Mesh associated with the class.
Definition: ddr_spaces.hpp:72
DOFSpace< dimension > const & dofspace(size_t k) const
Return DOFSpace associated to k forms.
Definition: ddr_spaces.hpp:68
Eigen::MatrixXd compose_diff(size_t k, size_t d, size_t i_cell) const
Compute the projected differential operator.
Definition: ddr_spaces.cpp:248
2 dimensional Maxwell equation on a manifold without boundary
Definition: maxwell.hpp:29
void compute()
Setup the solver.
Definition: maxwell.hpp:373
double normd(Eigen::MatrixBase< Derived > const &E, size_t k) const
Compute the discrete norm of the differential of a -form.
Definition: maxwell.hpp:447
static constexpr std::string_view SolverName
Name of the solver.
Definition: maxwell.hpp:36
size_t dimensionG() const
Return the number of unknowns attached to the space of 0 forms.
Definition: maxwell.hpp:55
Eigen::VectorXd solve()
Solve the system and return the solution.
Definition: maxwell.hpp:386
void assembleRHS(Eigen::Ref< const Eigen::VectorXd > const &rho, Eigen::Ref< const Eigen::VectorXd > const &J, Eigen::Ref< const Eigen::VectorXd > const &EOld, Eigen::Ref< const Eigen::VectorXd > const &BOld)
Assemble the Right-Hand side from the given interpolates.
Definition: maxwell.hpp:334
size_t dimensionH() const
Return the number of unknowns attached to the space of harmonic 0 forms.
Definition: maxwell.hpp:49
Eigen::SparseLU< SystemMatrixType, Eigen::COLAMDOrdering< int > > SolverType
Type used to store the solver.
Definition: maxwell.hpp:34
Eigen::SparseMatrix< double > SystemMatrixType
Type used to store the system.
Definition: maxwell.hpp:32
size_t dimensionE() const
Return the number of unknowns attached to the space of 1 forms.
Definition: maxwell.hpp:58
size_t dimensionB() const
Return the number of unknowns attached to the space of 2 forms.
Definition: maxwell.hpp:60
size_t dimensionSystem() const
Return the total number of unknowns.
Definition: maxwell.hpp:62
double timeStep() const
Return the time-step.
Definition: maxwell.hpp:67
double norm(Eigen::MatrixBase< Derived > const &E, size_t k) const
Compute the discrete norm of a -form.
Definition: maxwell.hpp:411
DDR_Spaces< 2 > const & ddrcore() const
Return the associated DDR_Spaces.
Definition: maxwell.hpp:65
bool validateSolution(Eigen::Ref< const Eigen::VectorXd > const &u) const
Check if the vector u if a solution up to a given relative accuracy.
Definition: maxwell.hpp:364
MaxwellProblem(const Mesh< 2 > &mesh, int degree, double timestep, bool use_threads=true, std::array< int, 2 > const *dqr=nullptr, std::ostream &output=std::cout)
Definition: maxwell.hpp:162
Main data structure for the mesh.
Definition: mesh.hpp:54
Discrete operators for DDR-PEC.
Definition: maxwell.hpp:21
static void parallel_for(unsigned nb_elements, std::function< void(size_t start, size_t end)> functor, bool use_threads=true)
Generic function to execute threaded processes.
Definition: parallel_for.hpp:42
Eigen::SparseMatrix< double > parallel_assembly(size_t nb_elements, std::pair< size_t, size_t > systemSize, FType localAssembly, bool use_threads=true)
Function to assemble a global sparse matrix from a procedure that compute local contributions.
Definition: parallel_for.hpp:74
Helper to parallelize operations using pthreads.
Construct a convenient wrapper around a vector of global unknowns to manipulate individual components...
Definition: maxwell.hpp:122
int offsetG
Definition: maxwell.hpp:158
Eigen::VectorBlock< Eigen::VectorXd > E()
Return the electric part.
Definition: maxwell.hpp:147
MaxwellVector & operator=(const MaxwellVector &other)
Copy another MaxwellVector.
Definition: maxwell.hpp:127
int offsetE
Definition: maxwell.hpp:158
int sizeE
Definition: maxwell.hpp:157
Eigen::VectorXd u
Definition: maxwell.hpp:156
int sizeB
Definition: maxwell.hpp:157
int sizeG
Definition: maxwell.hpp:157
Eigen::VectorBlock< Eigen::VectorXd > B()
Return the magnetic part.
Definition: maxwell.hpp:152
Eigen::VectorBlock< Eigen::VectorXd > h()
Return the harmonic part.
Definition: maxwell.hpp:137
int offsetB
Definition: maxwell.hpp:158
MaxwellVector(MaxwellProblem const &maxwell)
Definition: maxwell.hpp:123
Eigen::VectorBlock< Eigen::VectorXd > G()
Return the ghost part.
Definition: maxwell.hpp:142