HArD::Core3D
Hybrid Arbitrary Degree::Core 3D - Library to implement 3D schemes with vertex, edge, face and cell polynomials as unknowns
HHO_MHD.hpp
Go to the documentation of this file.
1 
2 #include <Eigen/Dense>
3 #include <Eigen/Sparse>
4 
5 #include "MHDTests.hpp"
6 #include "mesh_builder.hpp"
7 #include "vtu_writer.hpp"
8 #include <basis.hpp>
9 #include <chrono>
10 #include <elementquad.hpp>
11 #include <hybridcore.hpp>
12 #include <mesh.hpp>
13 #include <parallel_for.hpp>
14 
16 #include <TestCase/TestCase.hpp>
17 
18 #include <boost/program_options.hpp>
19 #include <boost/timer/timer.hpp>
20 
21 #ifdef WITH_MKL
22 #include <Eigen/PardisoSupport>
23 #endif
24 
31 using namespace HArDCore3D;
32 
33 #ifndef _HHO_MHD_HPP
34 #define _HHO_MHD_HPP
35 
43 {
44 public:
45  // SolutionVector() : m_cell_deg(0), m_face_deg(0)
46  // {
47  // }
48 
49  SolutionVector(Eigen::VectorXd values,
50  const Mesh *mesh_ptr,
51  const size_t cell_deg,
52  const size_t face_deg
53  )
54  : m_values(values), m_mesh_ptr(mesh_ptr), m_cell_deg(cell_deg),
55  m_face_deg(face_deg)
56  {
57  assert((size_t)(m_values.size()) == n_total_dofs);
58  }
59 
61  inline Eigen::VectorXd asVectorXd() const { return m_values; }
63  inline void set_values(Eigen::VectorXd values) const { m_values = values; }
64 
66  inline const size_t get_cell_deg() const { return m_cell_deg; }
67 
69  inline const size_t get_face_deg() const { return m_face_deg; }
70 
71  Eigen::VectorXd velocity_values() const
72  {
73  return m_values.head(n_total_cell_velocity_dofs +
74  n_total_face_velocity_dofs);
75  }
76 
77  Eigen::VectorXd pressure_values() const
78  {
79  return m_values.segment(n_total_cell_velocity_dofs + n_total_face_velocity_dofs, n_total_pressure_dofs);
80  }
81 
82  Eigen::VectorXd magnetic_values() const
83  {
84  return m_values.segment( n_total_cell_velocity_dofs + n_total_face_velocity_dofs + n_total_pressure_dofs, n_total_cell_magnetic_dofs + n_total_face_magnetic_dofs);
85  }
86 
87  Eigen::VectorXd lagrange_values() const
88  {
89  return m_values.tail(n_total_lagrange_dofs);
90  }
91 
92  Eigen::VectorXd velocity_restr(size_t iT) const
93  {
94  Eigen::VectorXd XTF = Eigen::VectorXd::Zero(n_local_velocity_dofs(iT));
95 
96  XTF.head(n_local_cell_velocity_dofs) = m_values.segment( iT * n_local_cell_velocity_dofs, n_local_cell_velocity_dofs);
97  for (size_t iTF = 0; iTF < m_mesh_ptr->cell(iT)->n_faces(); iTF++)
98  {
99  size_t global_offset = n_total_cell_velocity_dofs + m_mesh_ptr->cell(iT)->face(iTF)->global_index() * n_local_face_velocity_dofs;
100  size_t local_offset = n_local_cell_velocity_dofs + iTF * n_local_face_velocity_dofs;
101  XTF.segment(local_offset, n_local_face_velocity_dofs) = m_values.segment(global_offset, n_local_face_velocity_dofs);
102  }
103 
104  return XTF;
105  }
106 
107  Eigen::VectorXd pressure_restr(size_t iT) const
108  {
109  size_t offset = n_total_cell_velocity_dofs + n_total_face_velocity_dofs + iT * n_local_pressure_dofs;
110 
111  return m_values.segment(offset, n_local_pressure_dofs);
112  }
113 
114  Eigen::VectorXd magnetic_restr(size_t iT) const
115  {
116  Eigen::VectorXd XTF = Eigen::VectorXd::Zero(n_local_magnetic_dofs(iT));
117 
118  size_t offset = n_total_cell_velocity_dofs + n_total_face_velocity_dofs + n_total_pressure_dofs;
119 
120  XTF.head(n_local_cell_magnetic_dofs) = m_values.segment( offset + iT * n_local_cell_magnetic_dofs, n_local_cell_magnetic_dofs);
121  for (size_t iTF = 0; iTF < m_mesh_ptr->cell(iT)->n_faces(); iTF++)
122  {
123  size_t global_offset = offset + n_total_cell_magnetic_dofs + m_mesh_ptr->cell(iT)->face(iTF)->global_index() * n_local_face_magnetic_dofs;
124  size_t local_offset = n_local_cell_magnetic_dofs + iTF * n_local_face_magnetic_dofs;
125  XTF.segment(local_offset, n_local_face_magnetic_dofs) = m_values.segment(global_offset, n_local_face_magnetic_dofs);
126  }
127 
128  return XTF;
129  }
130 
131  Eigen::VectorXd lagrange_restr(size_t iT) const
132  {
133  size_t offset = n_total_cell_velocity_dofs + n_total_face_velocity_dofs + n_total_pressure_dofs + n_total_cell_magnetic_dofs + n_total_face_magnetic_dofs + iT * n_local_lagrange_dofs;
134 
135  return m_values.segment(offset, n_local_lagrange_dofs);
136  }
137 
138  Eigen::VectorXd restr(size_t iT) const
139  {
140  Eigen::VectorXd XTF = Eigen::VectorXd::Zero( n_local_velocity_dofs(iT) + n_local_pressure_dofs + n_local_magnetic_dofs(iT) + n_local_lagrange_dofs);
141 
142  XTF.head(n_local_velocity_dofs(iT)) = this->velocity_restr(iT);
143  XTF.segment(n_local_velocity_dofs(iT), n_local_pressure_dofs) = this->pressure_restr(iT);
144  XTF.segment(n_local_velocity_dofs(iT) + n_local_pressure_dofs, n_local_magnetic_dofs(iT)) = this->magnetic_restr(iT);
145  XTF.tail(n_local_lagrange_dofs) = this->lagrange_restr(iT);
146 
147  return XTF;
148  }
149 
152  {
153  assert(m_cell_deg == b.get_cell_deg() || m_face_deg == b.get_face_deg());
154  return SolutionVector(m_values + b.asVectorXd(), m_mesh_ptr, m_cell_deg, m_face_deg);
155  }
156 
159  {
160  assert(m_cell_deg == b.get_cell_deg() || m_face_deg == b.get_face_deg());
161  return SolutionVector(m_values - b.asVectorXd(), m_mesh_ptr, m_cell_deg, m_face_deg);
162  }
163 
165  double operator()(size_t index) const { return m_values(index); }
166 
167  // //Assignment operator
168  // SolutionVector &operator=(const SolutionVector &a);
169 
170 private:
171  mutable Eigen::VectorXd m_values;
172  const Mesh *m_mesh_ptr;
173 
174  const size_t m_cell_deg;
175  const size_t m_face_deg;
176 
177  static const size_t dim = 3;
178 
179  const size_t n_cells = m_mesh_ptr->n_cells();
180  const size_t n_faces = m_mesh_ptr->n_faces();
181 
182  const size_t n_local_cell_velocity_dofs = dim * DimPoly<Cell>(m_cell_deg);
183  const size_t n_local_cell_magnetic_dofs = dim * DimPoly<Cell>(m_cell_deg);
184  const size_t n_local_face_velocity_dofs = dim * DimPoly<Face>(m_face_deg);
185  const size_t n_local_face_magnetic_dofs = dim * DimPoly<Face>(m_face_deg);
186  const size_t n_local_pressure_dofs = DimPoly<Cell>(m_cell_deg);
187  const size_t n_local_lagrange_dofs = DimPoly<Cell>(m_cell_deg);
188 
189  const size_t n_total_cell_velocity_dofs = n_cells * n_local_cell_velocity_dofs;
190  const size_t n_total_cell_magnetic_dofs = n_cells * n_local_cell_magnetic_dofs;
191  const size_t n_total_face_velocity_dofs = n_faces * n_local_face_velocity_dofs;
192  const size_t n_total_face_magnetic_dofs = n_faces * n_local_face_magnetic_dofs;
193  const size_t n_total_pressure_dofs = n_cells * n_local_pressure_dofs;
194  const size_t n_total_lagrange_dofs = n_cells * n_local_lagrange_dofs;
195 
196  inline size_t n_local_velocity_dofs(const size_t iT) const
197  {
198  return n_local_cell_velocity_dofs + m_mesh_ptr->cell(iT)->n_faces() * n_local_face_velocity_dofs;
199  }
200 
201  inline size_t n_local_magnetic_dofs(const size_t iT) const
202  {
203  return n_local_cell_magnetic_dofs + m_mesh_ptr->cell(iT)->n_faces() * n_local_face_magnetic_dofs;
204  }
205 };
206 
207 class MHDModel
208 {
209 public:
210  MHDModel(HybridCore &, size_t, size_t, char, char);
211 
212  SolutionVector solve_with_static_cond(FType<VectorRd> f_source, FType<VectorRd> g_source, double visc, double diff, double tol, bool threading = true);
213  SolutionVector global_interpolant(FType<VectorRd> velocity, FType<double> pressure, FType<VectorRd> magnetic);
214  std::vector<double> compute_errors(SolutionVector intepolant, SolutionVector discrete, double visc, double diff);
215 
216 private:
217  Eigen::MatrixXd gradient_term(Cell *cell, ElementQuad &elquad);
218  Eigen::MatrixXd stabilisation_term(Cell *cell, ElementQuad &elquad);
219  Eigen::MatrixXd divergence_term(Cell *cell, ElementQuad &elquad);
220  Eigen::VectorXd source_term(FType<VectorRd> f_vec, Cell *cell, ElementQuad &elquad);
221  Eigen::MatrixXd convective_term(Cell *cell, std::vector<Eigen::MatrixXd> tplus_k, std::vector<Eigen::MatrixXd> tminus_k, Eigen::VectorXd u);
222  Eigen::MatrixXd th_ijk_plus_th_ikj(Cell *cell, ElementQuad &elquad, size_t k);
223  Eigen::MatrixXd th_ijk_minus_th_ikj(Cell *cell, ElementQuad &elquad, size_t k);
224 
225  Eigen::VectorXd local_interpolant(FType<VectorRd> func, Cell *cell, ElementQuad &elquad);
226  Eigen::VectorXd scalar_l2_projection(FType<double> func, Cell *cell, ElementQuad &elquad);
227 
228  HybridCore &m_hho;
229  size_t m_L;
230  size_t m_K;
231  char m_u_bc;
232  char m_b_bc;
233 
234  static const size_t dim = 3;
235 
236  const Mesh *mesh_ptr = m_hho.get_mesh();
237 
238  const size_t n_cells = mesh_ptr->n_cells();
239  const size_t n_faces = mesh_ptr->n_faces();
240  const size_t n_bdry_faces = mesh_ptr->n_b_faces();
241 
242  const size_t n_local_cell_scalar_dofs = DimPoly<Cell>(m_L);
243  const size_t n_local_face_scalar_dofs = DimPoly<Face>(m_K);
244  const size_t n_local_highorder_scalar_dofs = DimPoly<Cell>(m_K + 1);
245 
246  const size_t n_local_cell_vector_dofs = dim * DimPoly<Cell>(m_L);
247  const size_t n_local_face_vector_dofs = dim * DimPoly<Face>(m_K);
248  const size_t n_local_highorder_vector_dofs = dim * DimPoly<Cell>(m_K + 1);
249 
250  const size_t n_total_cell_scalar_dofs = n_local_cell_scalar_dofs * n_cells;
251  // const size_t n_total_face_scalar_dofs = n_local_face_scalar_dofs * n_faces;
252 
253  const size_t n_total_cell_vector_dofs = n_local_cell_vector_dofs * n_cells;
254  const size_t n_total_face_vector_dofs = n_local_face_vector_dofs * n_faces;
255  // const size_t n_total_vector_dofs = n_total_cell_vector_dofs +
256  // n_total_face_vector_dofs;
257 
258  Eigen::SparseMatrix<double> inv_LTll_LTlg;
259  Eigen::SparseMatrix<double> LTgl;
260  Eigen::SparseMatrix<double> LTgg;
261 
262  Eigen::VectorXd inv_LTll_rTl;
263 
264  std::vector<Eigen::MatrixXd> AT;
265  std::vector<Eigen::MatrixXd> RT;
266 };
267 
268 std::vector<double> MHDModel::compute_errors(SolutionVector intepolant, SolutionVector discrete, double visc, double diff)
269 {
270 
271  double vel_energy_difference = 0.0;
272  double vel_energy_exact = 0.0;
273 
274  double mag_energy_difference = 0.0;
275  double mag_energy_exact = 0.0;
276 
277  double pressure_energy_difference = 0.0;
278  double pressure_energy_exact = 0.0;
279 
280  double lagrange_energy_difference = 0.0;
281  double lagrange_energy_exact = 0.0;
282 
283  double vel_l2_difference = 0.0;
284  double vel_l2_exact = 0.0;
285 
286  double mag_l2_difference = 0.0;
287  double mag_l2_exact = 0.0;
288 
289  // Construct the local matrices using multithreading if use_threads is true
290  // std::function<void(size_t, size_t)> compute_local_errors = [&](size_t
291  // start, size_t end) -> void
292  // {
293  // for (size_t iT = start; iT < end; ++iT)
294  // {
295  for (size_t iT = 0; iT < n_cells; ++iT)
296  {
297  Eigen::VectorXd local_vel_difference = (intepolant - discrete).velocity_restr(iT);
298  Eigen::VectorXd local_vel_exact = intepolant.velocity_restr(iT);
299  Eigen::VectorXd local_mag_difference = (intepolant - discrete).magnetic_restr(iT);
300  Eigen::VectorXd local_mag_exact = intepolant.magnetic_restr(iT);
301 
302  vel_energy_difference += local_vel_difference.transpose() * AT[iT] * local_vel_difference;
303  mag_energy_difference += local_mag_difference.transpose() * AT[iT] * local_mag_difference;
304  vel_energy_exact += local_vel_exact.transpose() * AT[iT] * local_vel_exact;
305  mag_energy_exact += local_mag_exact.transpose() * AT[iT] * local_mag_exact;
306 
307  ElementQuad elquad(m_hho, iT, m_L + m_L, m_K + m_K);
308 
309  QuadratureRule quadT = elquad.get_quadT();
310 
311  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
312  BasisQuad<VectorRd> dphiT_quadT = elquad.get_dphiT_quadT();
313 
314  Eigen::MatrixXd MTT = compute_gram_matrix(phiT_quadT, phiT_quadT, quadT, n_local_cell_scalar_dofs, n_local_cell_scalar_dofs, "sym");
315 
316  Eigen::MatrixXd vec_MTT = Eigen::MatrixXd::Zero(n_local_cell_vector_dofs, n_local_cell_vector_dofs);
317 
318  for (size_t d = 0; d < dim; d++)
319  {
320  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
321  {
322  for (size_t j = 0; j < n_local_cell_scalar_dofs; j++)
323  {
324  vec_MTT(dim * i + d, dim * j + d) = MTT(i, j);
325  }
326  }
327  }
328 
329  vel_l2_difference += local_vel_difference.head(n_local_cell_vector_dofs).transpose() * vec_MTT * local_vel_difference.head(n_local_cell_vector_dofs);
330  mag_l2_difference += local_mag_difference.head(n_local_cell_vector_dofs).transpose() * vec_MTT * local_mag_difference.head(n_local_cell_vector_dofs);
331  vel_l2_exact += local_vel_exact.head(n_local_cell_vector_dofs).transpose() * vec_MTT * local_vel_exact.head(n_local_cell_vector_dofs);
332  mag_l2_exact += local_mag_exact.head(n_local_cell_vector_dofs).transpose() * vec_MTT * local_mag_exact.head(n_local_cell_vector_dofs);
333 
334  Cell *cell = mesh_ptr->cell(iT);
335 
336  size_t n_local_bdry_vector_dofs = cell->n_faces() * n_local_face_vector_dofs;
337  Eigen::MatrixXd M_BDRY_BDRY = Eigen::MatrixXd::Zero(n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
338 
339  for (size_t iTF = 0; iTF < cell->n_faces(); iTF++)
340  {
341  const size_t offset_F = iTF * n_local_face_vector_dofs;
342 
343  QuadratureRule quadF = elquad.get_quadF(iTF);
344  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
345 
346  BasisQuad<VectorRd> vec_phiF_quadF(
347  boost::extents[n_local_face_vector_dofs][quadF.size()]);
348 
349  for (size_t d = 0; d < dim; d++)
350  {
351  VectorRd eF = VectorRd::Zero();
352 
353  // Sort face unknowns into perpendicular, then paralell components
354  if (d == 0)
355  {
356  eF = cell->face(iTF)->normal();
357  }
358  else if (d == 1)
359  {
360  eF = cell->face(iTF)->edge(0)->tangent();
361  }
362  else if (d == 2)
363  {
364  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
365  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
366  }
367  else
368  {
369  assert(false); // should never reach here
370  }
371 
372  for (size_t iqn = 0; iqn < quadF.size(); iqn++)
373  {
374  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
375  {
376  vec_phiF_quadF[dim * i + d][iqn] = phiF_quadF[i][iqn] * eF;
377  }
378  }
379  }
380 
381  M_BDRY_BDRY.block(offset_F, offset_F, n_local_face_vector_dofs, n_local_face_vector_dofs) = compute_gram_matrix(vec_phiF_quadF, vec_phiF_quadF, quadF, n_local_face_vector_dofs, n_local_face_vector_dofs, "sym");
382  }
383 
384  double hT = cell->diam();
385  vel_l2_difference += hT * local_vel_difference.tail(n_local_bdry_vector_dofs).transpose() * M_BDRY_BDRY * local_vel_difference.tail(n_local_bdry_vector_dofs);
386  mag_l2_difference += hT * local_mag_difference.tail(n_local_bdry_vector_dofs).transpose() * M_BDRY_BDRY * local_mag_difference.tail(n_local_bdry_vector_dofs);
387  vel_l2_exact += hT * local_vel_exact.tail(n_local_bdry_vector_dofs).transpose() * M_BDRY_BDRY * local_vel_exact.tail(n_local_bdry_vector_dofs);
388  mag_l2_exact += hT * local_mag_exact.tail(n_local_bdry_vector_dofs).transpose() * M_BDRY_BDRY * local_mag_exact.tail(n_local_bdry_vector_dofs);
389 
390  Eigen::VectorXd local_pressure_difference = (intepolant - discrete).pressure_restr(iT);
391  Eigen::VectorXd local_pressure_exact = intepolant.pressure_restr(iT);
392  Eigen::VectorXd local_lagrange_difference = (intepolant - discrete).lagrange_restr(iT);
393  Eigen::VectorXd local_lagrange_exact = intepolant.lagrange_restr(iT);
394 
395  pressure_energy_difference += local_pressure_difference.transpose() * MTT * local_pressure_difference;
396  lagrange_energy_difference += local_lagrange_difference.transpose() * MTT * local_lagrange_difference;
397  pressure_energy_exact += local_pressure_exact.transpose() * MTT * local_pressure_exact;
398  lagrange_energy_exact += local_lagrange_exact.transpose() * MTT * local_lagrange_exact;
399  }
400  // };
401 
402  // // Running the local constructions in parallel
403  // parallel_for(n_cells, compute_local_errors, threading);
404 
405  // If exact is zero, give absolute rather than relative errors
406  if (vel_energy_exact < 1E-12)
407  {
408  vel_energy_exact = 1.0;
409  }
410  if (mag_energy_exact < 1E-12)
411  {
412  mag_energy_exact = 1.0;
413  }
414  if (pressure_energy_exact < 1E-12)
415  {
416  pressure_energy_exact = 1.0;
417  }
418  if (lagrange_energy_exact < 1E-12)
419  {
420  lagrange_energy_exact = 1.0;
421  }
422  if (vel_l2_exact < 1E-12)
423  {
424  vel_l2_exact = 1.0;
425  }
426  if (mag_l2_exact < 1E-12)
427  {
428  mag_l2_exact = 1.0;
429  }
430 
431  // double velocity_energy_error = std::pow(visc, 0.5) * std::sqrt(vel_energy_difference / vel_energy_exact);
432  // double magnetic_energy_error = std::pow(diff, 0.5) * std::sqrt(mag_energy_difference / mag_energy_exact);
433 
434  double velocity_energy_error = std::sqrt(visc * vel_energy_difference / vel_energy_exact);
435  double magnetic_energy_error = std::sqrt(diff * mag_energy_difference / mag_energy_exact);
436  double pressure_energy_error = std::sqrt(pressure_energy_difference / pressure_energy_exact);
437  double lagrange_energy_error = std::sqrt(lagrange_energy_difference / lagrange_energy_exact);
438  double velocity_l2_error = std::sqrt(vel_l2_difference / vel_l2_exact);
439  double magnetic_l2_error = std::sqrt(mag_l2_difference / mag_l2_exact);
440 
441  return std::vector<double>{velocity_energy_error, magnetic_energy_error, pressure_energy_error, lagrange_energy_error, velocity_l2_error, magnetic_l2_error};
442  // return std::vector<double>{velocity_energy_error, magnetic_energy_error,
443  // pressure_energy_error, lagrange_energy_error};
444 }
445 
447 {
448  Eigen::VectorXd values = Eigen::VectorXd::Zero(2 * (n_total_cell_vector_dofs + n_total_face_vector_dofs + n_total_cell_scalar_dofs));
449  for (size_t iT = 0; iT < n_cells; ++iT)
450  {
451  ElementQuad elquad(m_hho, iT, 14, 14);
452  Cell *cell = mesh_ptr->cell(iT);
453 
454  Eigen::VectorXd local_Ikv = local_interpolant(velocity, cell, elquad);
455  Eigen::VectorXd local_Ikm = local_interpolant(magnetic, cell, elquad);
456  Eigen::VectorXd local_pikp = scalar_l2_projection(pressure, cell, elquad);
457 
458  values.segment(iT * n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_Ikv.head(n_local_cell_vector_dofs);
459  for (size_t iTF = 0; iTF < cell->n_faces(); ++iTF)
460  {
461  values.segment(n_total_cell_vector_dofs + cell->face(iTF)->global_index() * n_local_face_vector_dofs, n_local_face_vector_dofs) = local_Ikv.segment(n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs, n_local_face_vector_dofs); // inefficient -- rewriting on internal // faces
462  }
463  values.segment(n_total_cell_vector_dofs + n_total_face_vector_dofs + iT * n_local_cell_scalar_dofs, n_local_cell_scalar_dofs) = local_pikp;
464 
465  size_t magnetic_offset = n_total_cell_vector_dofs + n_total_face_vector_dofs + n_total_cell_scalar_dofs;
466  values.segment(magnetic_offset + iT * n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_Ikm.head(n_local_cell_vector_dofs);
467  for (size_t iTF = 0; iTF < cell->n_faces(); ++iTF)
468  {
469  values.segment(magnetic_offset + n_total_cell_vector_dofs + cell->face(iTF)->global_index() * n_local_face_vector_dofs, n_local_face_vector_dofs) = local_Ikm.segment(n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs, n_local_face_vector_dofs); // inefficient -- rewriting on internal // faces
470  }
471  }
472  return SolutionVector(values, mesh_ptr, m_L, m_K);
473 }
474 
475 MHDModel::MHDModel(HybridCore &hho, size_t L, size_t K, char u_bc, char b_bc) : m_hho(hho), m_L(L), m_K(K), m_u_bc(u_bc), m_b_bc(b_bc)
476 {
477  AT.resize(n_cells);
478  RT.resize(n_cells);
479 }
480 
481 Eigen::VectorXd MHDModel::scalar_l2_projection(FType<double> func, Cell *cell, ElementQuad &elquad)
482 {
483  QuadratureRule quadT = elquad.get_quadT();
484 
485  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
486 
487  Eigen::MatrixXd MTT = compute_gram_matrix(phiT_quadT, phiT_quadT, quadT, n_local_cell_scalar_dofs, n_local_cell_scalar_dofs, "sym");
488 
489  Eigen::VectorXd RHS = integrate(func, phiT_quadT, quadT, n_local_cell_scalar_dofs);
490 
491  return (MTT.ldlt()).solve(RHS);
492 }
493 
494 Eigen::VectorXd MHDModel::local_interpolant(FType<VectorRd> func, Cell *cell, ElementQuad &elquad)
495 {
496  const size_t n_local_faces = cell->n_faces();
497  const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces * n_local_face_vector_dofs;
498 
499  Eigen::VectorXd ITk = Eigen::VectorXd::Zero(n_local_vector_dofs);
500 
501  FType<double> func1 = [&](const VectorRd x) -> double { return func(x)(0); };
502  FType<double> func2 = [&](const VectorRd x) -> double { return func(x)(1); };
503  FType<double> func3 = [&](const VectorRd x) -> double { return func(x)(2); };
504 
505  Eigen::VectorXd piTk_func1 = scalar_l2_projection(func1, cell, elquad);
506  Eigen::VectorXd piTk_func2 = scalar_l2_projection(func2, cell, elquad);
507  Eigen::VectorXd piTk_func3 = scalar_l2_projection(func3, cell, elquad);
508 
509  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
510  {
511  ITk(dim * i + 0) = piTk_func1(i);
512  ITk(dim * i + 1) = piTk_func2(i);
513  ITk(dim * i + 2) = piTk_func3(i);
514  }
515 
516  for (size_t iTF = 0; iTF < n_local_faces; iTF++)
517  {
518  const size_t offset_F = n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs;
519 
520  QuadratureRule quadF = elquad.get_quadF(iTF);
521  size_t n_quadF = quadF.size();
522 
523  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
524 
525  BasisQuad<VectorRd> vec_phiF_quadF(boost::extents[n_local_face_vector_dofs][n_quadF]);
526 
527  for (size_t k = 0; k < dim; k++)
528  {
529  VectorRd eF = VectorRd::Zero();
530 
531  // Sort face unknowns into perpendicular, then paralell components
532  if (k == 0)
533  {
534  eF = cell->face(iTF)->normal();
535  }
536  else if (k == 1)
537  {
538  eF = cell->face(iTF)->edge(0)->tangent();
539  }
540  else if (k == 2)
541  {
542  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
543  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
544  }
545  else
546  {
547  assert(false); // should never reach here
548  }
549 
550  for (size_t iqn = 0; iqn < n_quadF; iqn++)
551  {
552  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
553  {
554  vec_phiF_quadF[dim * i + k][iqn] = phiF_quadF[i][iqn] * eF;
555  }
556  }
557  }
558 
559  Eigen::MatrixXd MFF = compute_gram_matrix(vec_phiF_quadF, vec_phiF_quadF, quadF, n_local_face_vector_dofs, n_local_face_vector_dofs, "sym");
560  Eigen::VectorXd RHS = integrate(func, vec_phiF_quadF, quadF, n_local_face_vector_dofs);
561 
562  ITk.segment(offset_F, n_local_face_vector_dofs) = (MFF.ldlt()).solve(RHS);
563  }
564 
565  return ITk;
566 }
567 
568 Eigen::MatrixXd MHDModel::divergence_term(Cell *cell, ElementQuad &elquad)
569 {
570  const size_t n_local_faces = cell->n_faces();
571  const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces * n_local_face_vector_dofs;
572 
573  QuadratureRule quadT = elquad.get_quadT();
574  size_t n_quadT = quadT.size();
575 
576  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
577  BasisQuad<VectorRd> dphiT_quadT = elquad.get_dphiT_quadT();
578 
579  BasisQuad<double> div_vec_phiT_quadT(boost::extents[n_local_cell_vector_dofs][n_quadT]);
580 
581  for (size_t k = 0; k < dim; k++)
582  {
583  // VectorRd ek = VectorRd::Zero();
584  // ek(k) = 1;
585  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
586  {
587  for (size_t iqn = 0; iqn < n_quadT; iqn++)
588  {
589  div_vec_phiT_quadT[dim * i + k][iqn] = dphiT_quadT[i][iqn](k);
590  }
591  }
592  }
593 
594  Eigen::MatrixXd DT_RHS = Eigen::MatrixXd::Zero(n_local_cell_scalar_dofs, n_local_vector_dofs);
595 
596  DT_RHS.topLeftCorner(n_local_cell_scalar_dofs, n_local_cell_vector_dofs) = compute_gram_matrix(phiT_quadT, div_vec_phiT_quadT, quadT, n_local_cell_scalar_dofs, n_local_cell_vector_dofs);
597 
598  for (size_t iTF = 0; iTF < n_local_faces; iTF++)
599  {
600  const size_t offset_F = n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs;
601  const VectorRd &nTF = cell->face_normal(iTF);
602 
603  QuadratureRule quadF = elquad.get_quadF(iTF);
604  size_t n_quadF = quadF.size();
605 
606  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
607  BasisQuad<double> phiT_quadF = elquad.get_phiT_quadF(iTF);
608 
609  BasisQuad<double> vec_phiF_quadF_dot_nTF(boost::extents[n_local_face_vector_dofs][n_quadF]);
610  BasisQuad<double> vec_phiT_quadF_dot_nTF(boost::extents[n_local_cell_vector_dofs][n_quadF]);
611 
612  for (size_t k = 0; k < dim; k++)
613  {
614  VectorRd eT = VectorRd::Zero();
615  eT(k) = 1;
616 
617  VectorRd eF = VectorRd::Zero();
618 
619  // Sort face unknowns into perpendicular, then paralell components
620  if (k == 0)
621  {
622  eF = cell->face(iTF)->normal();
623  }
624  else if (k == 1)
625  {
626  eF = cell->face(iTF)->edge(0)->tangent();
627  }
628  else if (k == 2)
629  {
630  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
631  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
632  }
633  else
634  {
635  assert(false); // should never reach here
636  }
637 
638  for (size_t iqn = 0; iqn < n_quadF; iqn++)
639  {
640  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
641  {
642  vec_phiF_quadF_dot_nTF[dim * i + k][iqn] = phiF_quadF[i][iqn] * eF.dot(nTF);
643  }
644  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
645  {
646  vec_phiT_quadF_dot_nTF[dim * i + k][iqn] = phiT_quadF[i][iqn] * eT.dot(nTF);
647  }
648  }
649  }
650 
651  DT_RHS.topLeftCorner(n_local_cell_scalar_dofs, n_local_cell_vector_dofs) -= compute_gram_matrix(phiT_quadF, vec_phiT_quadF_dot_nTF, quadF, n_local_cell_scalar_dofs, n_local_cell_vector_dofs);
652 
653  DT_RHS.block(0, offset_F, n_local_cell_scalar_dofs, n_local_face_vector_dofs) = compute_gram_matrix(phiT_quadF, vec_phiF_quadF_dot_nTF, quadF, n_local_cell_scalar_dofs, n_local_face_vector_dofs);
654  }
655 
656  return -DT_RHS;
657 }
658 
659 Eigen::MatrixXd MHDModel::gradient_term(Cell *cell, ElementQuad &elquad)
660 {
661  const size_t n_local_faces = cell->n_faces();
662  const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces * n_local_face_vector_dofs;
663 
664  QuadratureRule quadT = elquad.get_quadT();
665 
666  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
667  BasisQuad<VectorRd> dphiT_quadT = elquad.get_dphiT_quadT();
668 
669  Eigen::MatrixXd RT_RHS = Eigen::MatrixXd::Zero(n_local_highorder_vector_dofs, n_local_vector_dofs);
670 
671  Eigen::MatrixXd MTT = compute_gram_matrix(phiT_quadT, phiT_quadT, quadT, n_local_cell_scalar_dofs, n_local_highorder_scalar_dofs, "sym");
672  Eigen::MatrixXd StiffT = compute_gram_matrix(dphiT_quadT, dphiT_quadT, quadT, "sym");
673 
674  Eigen::MatrixXd vec_MTT = Eigen::MatrixXd::Zero(n_local_cell_vector_dofs, n_local_highorder_vector_dofs);
675 
676  Eigen::MatrixXd vec_StiffT = Eigen::MatrixXd::Zero(n_local_highorder_vector_dofs, n_local_highorder_vector_dofs);
677 
678  for (size_t k = 0; k < dim; k++)
679  {
680  for (size_t i = 0; i < n_local_highorder_scalar_dofs; i++)
681  {
682  for (size_t j = 0; j < n_local_highorder_scalar_dofs; j++)
683  {
684  if (i < n_local_cell_scalar_dofs)
685  {
686  vec_MTT(dim * i + k, dim * j + k) = MTT(i, j);
687  }
688  vec_StiffT(dim * i + k, dim * j + k) = StiffT(i, j);
689  }
690  }
691  }
692 
693  Eigen::MatrixXd LT = (vec_MTT.topRightCorner(dim, n_local_highorder_vector_dofs)).transpose(); // need both components of the constant term
694  Eigen::MatrixXd LTtLT = LT * (LT.transpose());
695 
696  double scalT = vec_StiffT.norm() / LTtLT.norm();
697 
698  RT_RHS.topLeftCorner(n_local_highorder_vector_dofs, n_local_cell_vector_dofs) = vec_StiffT.topLeftCorner(n_local_highorder_vector_dofs, n_local_cell_vector_dofs) + scalT * LTtLT.topLeftCorner(n_local_highorder_vector_dofs, n_local_cell_vector_dofs);
699 
700  for (size_t iTF = 0; iTF < n_local_faces; iTF++)
701  {
702  const size_t offset_F = n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs;
703  const VectorRd &nTF = cell->face_normal(iTF);
704 
705  QuadratureRule quadF = elquad.get_quadF(iTF);
706  size_t n_quadF = quadF.size();
707 
708  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
709  BasisQuad<double> phiT_quadF = elquad.get_phiT_quadF(iTF);
710  BasisQuad<VectorRd> dphiT_quadF = elquad.get_dphiT_quadF(iTF);
711 
712  BasisQuad<VectorRd> vec_phiF_quadF(boost::extents[n_local_face_vector_dofs][n_quadF]);
713  BasisQuad<VectorRd> vec_phiT_quadF(boost::extents[n_local_cell_vector_dofs][n_quadF]);
714 
715  BasisQuad<VectorRd> nTF_doT_grad_vec_phiT_quadF(boost::extents[n_local_highorder_vector_dofs][n_quadF]);
716 
717  for (size_t k = 0; k < dim; k++)
718  {
719  VectorRd eT = VectorRd::Zero();
720  eT(k) = 1;
721 
722  VectorRd eF = VectorRd::Zero();
723 
724  // Sort face unknowns into perpendicular, then paralell components
725  if (k == 0)
726  {
727  eF = cell->face(iTF)->normal();
728  }
729  else if (k == 1)
730  {
731  eF = cell->face(iTF)->edge(0)->tangent();
732  }
733  else if (k == 2)
734  {
735  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
736  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
737  }
738  else
739  {
740  assert(false); // should never reach here
741  }
742 
743  for (size_t iqn = 0; iqn < n_quadF; iqn++)
744  {
745  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
746  {
747  vec_phiF_quadF[dim * i + k][iqn] = phiF_quadF[i][iqn] * eF;
748  }
749  for (size_t i = 0; i < n_local_highorder_scalar_dofs; i++)
750  {
751  if (i < n_local_cell_scalar_dofs)
752  {
753  vec_phiT_quadF[dim * i + k][iqn] = phiT_quadF[i][iqn] * eT;
754  }
755  nTF_doT_grad_vec_phiT_quadF[dim * i + k][iqn] = (dphiT_quadF[i][iqn].dot(nTF)) * eT;
756  }
757  }
758  }
759 
760  RT_RHS.topLeftCorner(n_local_highorder_vector_dofs, n_local_cell_vector_dofs) -= compute_gram_matrix(nTF_doT_grad_vec_phiT_quadF, vec_phiT_quadF, quadF, n_local_highorder_vector_dofs, n_local_cell_vector_dofs);
761 
762  RT_RHS.block(0, offset_F, n_local_highorder_vector_dofs, n_local_face_vector_dofs) = compute_gram_matrix(nTF_doT_grad_vec_phiT_quadF, vec_phiF_quadF, quadF, n_local_highorder_vector_dofs, n_local_face_vector_dofs);
763  }
764 
765  RT[cell->global_index()] = (vec_StiffT + scalT * LTtLT).ldlt().solve(RT_RHS);
766 
767  return RT[cell->global_index()].transpose() * vec_StiffT * RT[cell->global_index()];
768 }
769 
770 Eigen::MatrixXd MHDModel::th_ijk_plus_th_ikj(Cell *cell, ElementQuad &elquad, size_t k)
771 {
772  const size_t n_local_faces = cell->n_faces();
773  const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces * n_local_face_vector_dofs;
774 
775  Eigen::MatrixXd T = Eigen::MatrixXd::Zero(n_local_vector_dofs, n_local_vector_dofs);
776 
777  assert(k < 2 * n_local_vector_dofs);
778  if (k >= n_local_vector_dofs)
779  {
780  k -= n_local_vector_dofs;
781  }
782 
783  if (k < n_local_cell_vector_dofs)
784  {
785  QuadratureRule quadT = elquad.get_quadT();
786 
787  size_t k_row = (k % dim);
788  size_t k_scalar = (k / dim); // integer division
789 
790  VectorRd eK = VectorRd::Zero();
791  eK(k_row) = 1.0;
792 
793  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
794  BasisQuad<VectorRd> dphiT_quadT = elquad.get_dphiT_quadT();
795 
796  BasisQuad<VectorRd> vec_phiT_quadT(boost::extents[n_local_cell_vector_dofs][quadT.size()]);
797  BasisQuad<VectorRd> phiT_quadT_dot_grad_basis_k(boost::extents[n_local_cell_vector_dofs][quadT.size()]);
798  BasisQuad<VectorRd> basis_k_dot_grad_phiT_quadT(boost::extents[n_local_cell_vector_dofs][quadT.size()]);
799  BasisQuad<MatrixRd> grad_phiT_quadT(boost::extents[n_local_cell_vector_dofs][quadT.size()]);
800  BasisQuad<MatrixRd> basis_k_tensor_phiT_quadT(boost::extents[n_local_cell_vector_dofs][quadT.size()]);
801 
802  for (size_t d = 0; d < dim; ++d)
803  {
804  VectorRd eT = VectorRd::Zero();
805  eT(d) = 1.0;
806  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
807  {
808  for (size_t iqn = 0; iqn < quadT.size(); iqn++)
809  {
810  vec_phiT_quadT[dim * i + d][iqn] = phiT_quadT[i][iqn] * eT;
811  basis_k_dot_grad_phiT_quadT[dim * i + d][iqn] = phiT_quadT[k_scalar][iqn] * eK.dot(dphiT_quadT[i][iqn]) * eT;
812  phiT_quadT_dot_grad_basis_k[dim * i + d][iqn] = phiT_quadT[i][iqn] * eT.dot(dphiT_quadT[k_scalar][iqn]) * eK;
813  grad_phiT_quadT[dim * i + d][iqn] = MatrixRd::Zero();
814  grad_phiT_quadT[dim * i + d][iqn].row(d) = dphiT_quadT[i][iqn].transpose();
815  // grad_phiT_quadT[dim * i + d][iqn].col(d) = dphiT_quadT[i][iqn];
816  basis_k_tensor_phiT_quadT[dim * i + d][iqn] = MatrixRd::Zero();
817  basis_k_tensor_phiT_quadT[dim * i + d][iqn](k_row, d) = phiT_quadT[k_scalar][iqn] * phiT_quadT[i][iqn];
818  // basis_k_tensor_phiT_quadT[dim * i + d][iqn](d, k_row) =
819  // phiT_quadT[k_scalar][iqn] * phiT_quadT[i][iqn];
820  }
821  }
822  }
823 
824  Eigen::MatrixXd uT_dot_grad_wT_dot_vT = compute_gram_matrix(vec_phiT_quadT, basis_k_dot_grad_phiT_quadT, quadT, n_local_cell_vector_dofs, n_local_cell_vector_dofs);
825 
826  T.topLeftCorner(n_local_cell_vector_dofs, n_local_cell_vector_dofs) = compute_gram_matrix(vec_phiT_quadT, phiT_quadT_dot_grad_basis_k, quadT, n_local_cell_vector_dofs, n_local_cell_vector_dofs) + uT_dot_grad_wT_dot_vT - compute_gram_matrix(grad_phiT_quadT, basis_k_tensor_phiT_quadT, quadT, n_local_cell_vector_dofs, n_local_cell_vector_dofs) - uT_dot_grad_wT_dot_vT.transpose();
827  // T.topLeftCorner(n_local_cell_vector_dofs, n_local_cell_vector_dofs) =
828  // compute_gram_matrix(vec_phiT_quadT, phiT_quadT_dot_grad_basis_k,
829  // quadT).transpose() + uT_dot_grad_wT_dot_vT.transpose() -
830  // compute_gram_matrix(basis_k_tensor_phiT_quadT, grad_phiT_quadT,
831  // quadT).transpose() - uT_dot_grad_wT_dot_vT;
832 
833  for (size_t iTF = 0; iTF < n_local_faces; ++iTF)
834  {
835  const size_t offset_F = n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs;
836  VectorRd nTF = cell->face_normal(iTF);
837 
838  QuadratureRule quadF = elquad.get_quadF(iTF);
839 
840  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
841  BasisQuad<double> phiT_quadF = elquad.get_phiT_quadF(iTF);
842 
843  BasisQuad<VectorRd> basis_k_dot_nTF_phiT_quadF(boost::extents[n_local_cell_vector_dofs][quadF.size()]);
844  BasisQuad<double> phiT_quadF_dot_nTF(boost::extents[n_local_cell_vector_dofs][quadF.size()]);
845 
846  BasisQuad<double> basis_k_dot_phiF_quadF(boost::extents[n_local_face_vector_dofs][quadF.size()]);
847  BasisQuad<VectorRd> vec_phiF_quadF(boost::extents[n_local_face_vector_dofs][quadF.size()]);
848 
849  for (size_t d = 0; d < dim; ++d)
850  {
851  // VectorRd eF = ((d == 0) ? cell->face(iTF)->normal() :
852  // cell->face(iTF)->tangent());
853 
854  VectorRd eF = VectorRd::Zero();
855 
856  // Sort face unknowns into perpendicular, then paralell components
857  if (d == 0)
858  {
859  eF = cell->face(iTF)->normal();
860  }
861  else if (d == 1)
862  {
863  eF = cell->face(iTF)->edge(0)->tangent();
864  }
865  else if (d == 2)
866  {
867  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
868  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
869  }
870  else
871  {
872  assert(false); // should never reach here
873  }
874 
875  VectorRd eT = VectorRd::Zero();
876  eT(d) = 1.0;
877 
878  for (size_t iqn = 0; iqn < quadF.size(); iqn++)
879  {
880  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
881  {
882  basis_k_dot_nTF_phiT_quadF[dim * i + d][iqn] = phiT_quadF[k_scalar][iqn] * eK.dot(nTF) * phiT_quadF[i][iqn] * eT;
883  phiT_quadF_dot_nTF[dim * i + d][iqn] = phiT_quadF[i][iqn] * eT.dot(nTF);
884  }
885  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
886  {
887  basis_k_dot_phiF_quadF[dim * i + d][iqn] = phiT_quadF[k_scalar][iqn] * phiF_quadF[i][iqn] * eK.dot(eF);
888  vec_phiF_quadF[dim * i + d][iqn] = phiF_quadF[i][iqn] * eF;
889  }
890  }
891  }
892 
893  Eigen::MatrixXd uT_dot_nTF_vT_dot_wF = compute_gram_matrix(basis_k_dot_nTF_phiT_quadF, vec_phiF_quadF, quadF);
894 
895  T.block(0, offset_F, n_local_cell_vector_dofs, n_local_face_vector_dofs) = uT_dot_nTF_vT_dot_wF;
896  T.block(offset_F, 0, n_local_face_vector_dofs, n_local_cell_vector_dofs) = -(compute_gram_matrix(basis_k_dot_phiF_quadF, phiT_quadF_dot_nTF, quadF) + uT_dot_nTF_vT_dot_wF.transpose());
897  }
898 
899  return 0.5 * T;
900  }
901 
902  // const size_t iT = cell->global_index();
903  // const size_t n_local_faces = cell->n_faces();
904  // const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces
905  // * n_local_face_vector_dofs;
906 
907  assert(k < n_local_vector_dofs);
908 
909  size_t iTF = (k - n_local_cell_vector_dofs) / n_local_face_vector_dofs; // integer division
910  size_t k_basis = (k - n_local_cell_vector_dofs) % n_local_face_vector_dofs;
911  size_t k_scalar = k_basis / dim; // integer division
912 
913  VectorRd eF = VectorRd::Zero();
914 
915  // Sort face unknowns into perpendicular, then paralell components
916  if (k_basis % dim == 0)
917  {
918  eF = cell->face(iTF)->normal();
919  }
920  else if (k_basis % dim == 1)
921  {
922  eF = cell->face(iTF)->edge(0)->tangent();
923  }
924  else if (k_basis % dim == 2)
925  {
926  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
927  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
928  }
929  else
930  {
931  assert(false); // should never reach here
932  }
933 
934  VectorRd nTF = cell->face_normal(iTF);
935 
936  QuadratureRule quadF = elquad.get_quadF(iTF);
937 
938  BasisQuad<double> phiT_quadF = elquad.get_phiT_quadF(iTF);
939  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
940 
941  BasisQuad<double> phiT_quadF_dot_nTF(boost::extents[n_local_cell_vector_dofs][quadF.size()]);
942  BasisQuad<double> phiT_quadF_dot_basis_k(boost::extents[n_local_cell_vector_dofs][quadF.size()]);
943 
944  for (size_t d = 0; d < dim; ++d)
945  {
946  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
947  {
948  for (size_t iqn = 0; iqn < quadF.size(); iqn++)
949  {
950  phiT_quadF_dot_nTF[dim * i + d][iqn] = phiT_quadF[i][iqn] * nTF(d); // eT.dot(nTF)
951  phiT_quadF_dot_basis_k[dim * i + d][iqn] = phiT_quadF[i][iqn] * phiF_quadF[k_scalar][iqn] * eF(d); // eT.dot(eF)
952  }
953  }
954  }
955 
956  // Eigen::MatrixXd T = Eigen::MatrixXd::Zero(n_local_vector_dofs,
957  // n_local_vector_dofs);
958  T.topLeftCorner(n_local_cell_vector_dofs, n_local_cell_vector_dofs) = 0.5 * compute_gram_matrix(phiT_quadF_dot_basis_k, phiT_quadF_dot_nTF, quadF, n_local_cell_vector_dofs, n_local_cell_vector_dofs);
959  return T;
960 
961  // return 0.5 * compute_gram_matrix(phiT_quadF_dot_basis_k,
962  // phiT_quadF_dot_nTF, quadF, n_local_cell_vector_dofs,
963  // n_local_cell_vector_dofs);
964 }
965 
966 Eigen::MatrixXd MHDModel::th_ijk_minus_th_ikj(Cell *cell, ElementQuad &elquad, size_t k)
967 {
968  const size_t n_local_faces = cell->n_faces();
969  const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces * n_local_face_vector_dofs;
970 
971  Eigen::MatrixXd T = Eigen::MatrixXd::Zero(n_local_vector_dofs, n_local_vector_dofs);
972 
973  assert(k < 2 * n_local_vector_dofs);
974  if (k >= n_local_vector_dofs)
975  {
976  k -= n_local_vector_dofs;
977  }
978 
979  if (k < n_local_cell_vector_dofs)
980  {
981  QuadratureRule quadT = elquad.get_quadT();
982 
983  size_t k_row = (k % dim);
984  size_t k_scalar = (k / dim); // integer division
985 
986  VectorRd eK = VectorRd::Zero();
987  eK(k_row) = 1.0;
988 
989  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
990  BasisQuad<VectorRd> dphiT_quadT = elquad.get_dphiT_quadT();
991 
992  BasisQuad<VectorRd> vec_phiT_quadT( boost::extents[n_local_cell_vector_dofs][quadT.size()]);
993  BasisQuad<VectorRd> phiT_quadT_dot_grad_basis_k( boost::extents[n_local_cell_vector_dofs][quadT.size()]);
994  BasisQuad<VectorRd> basis_k_dot_grad_phiT_quadT( boost::extents[n_local_cell_vector_dofs][quadT.size()]);
995  BasisQuad<MatrixRd> grad_phiT_quadT( boost::extents[n_local_cell_vector_dofs][quadT.size()]);
996  BasisQuad<MatrixRd> basis_k_tensor_phiT_quadT( boost::extents[n_local_cell_vector_dofs][quadT.size()]);
997 
998  for (size_t d = 0; d < dim; ++d)
999  {
1000  VectorRd eT = VectorRd::Zero();
1001  eT(d) = 1.0;
1002  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
1003  {
1004  for (size_t iqn = 0; iqn < quadT.size(); iqn++)
1005  {
1006  vec_phiT_quadT[dim * i + d][iqn] = phiT_quadT[i][iqn] * eT;
1007  basis_k_dot_grad_phiT_quadT[dim * i + d][iqn] = phiT_quadT[k_scalar][iqn] * eK.dot(dphiT_quadT[i][iqn]) * eT;
1008  phiT_quadT_dot_grad_basis_k[dim * i + d][iqn] = phiT_quadT[i][iqn] * eT.dot(dphiT_quadT[k_scalar][iqn]) * eK;
1009  grad_phiT_quadT[dim * i + d][iqn] = MatrixRd::Zero();
1010  grad_phiT_quadT[dim * i + d][iqn].row(d) = dphiT_quadT[i][iqn].transpose();
1011  basis_k_tensor_phiT_quadT[dim * i + d][iqn] = MatrixRd::Zero();
1012  basis_k_tensor_phiT_quadT[dim * i + d][iqn](k_row, d) = phiT_quadT[k_scalar][iqn] * phiT_quadT[i][iqn];
1013  }
1014  }
1015  }
1016 
1017  Eigen::MatrixXd uT_dot_grad_wT_dot_vT = compute_gram_matrix(vec_phiT_quadT, basis_k_dot_grad_phiT_quadT, quadT);
1018 
1019  T.topLeftCorner(n_local_cell_vector_dofs, n_local_cell_vector_dofs) = -compute_gram_matrix(vec_phiT_quadT, phiT_quadT_dot_grad_basis_k, quadT) + uT_dot_grad_wT_dot_vT + compute_gram_matrix(grad_phiT_quadT, basis_k_tensor_phiT_quadT, quadT) - uT_dot_grad_wT_dot_vT.transpose();
1020 
1021  for (size_t iTF = 0; iTF < n_local_faces; ++iTF)
1022  {
1023  const size_t offset_F = n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs;
1024  VectorRd nTF = cell->face_normal(iTF);
1025 
1026  QuadratureRule quadF = elquad.get_quadF(iTF);
1027 
1028  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
1029  BasisQuad<double> phiT_quadF = elquad.get_phiT_quadF(iTF);
1030 
1031  BasisQuad<VectorRd> basis_k_dot_nTF_phiT_quadF( boost::extents[n_local_cell_vector_dofs][quadF.size()]);
1032  BasisQuad<double> phiT_quadF_dot_nTF( boost::extents[n_local_cell_vector_dofs][quadF.size()]);
1033 
1034  BasisQuad<double> basis_k_dot_phiF_quadF( boost::extents[n_local_face_vector_dofs][quadF.size()]);
1035  BasisQuad<VectorRd> vec_phiF_quadF( boost::extents[n_local_face_vector_dofs][quadF.size()]);
1036 
1037  for (size_t d = 0; d < dim; ++d)
1038  {
1039  VectorRd eF = VectorRd::Zero();
1040 
1041  // Sort face unknowns into perpendicular, then paralell components
1042  if (d == 0)
1043  {
1044  eF = cell->face(iTF)->normal();
1045  }
1046  else if (d == 1)
1047  {
1048  eF = cell->face(iTF)->edge(0)->tangent();
1049  }
1050  else if (d == 2)
1051  {
1052  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
1053  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
1054  }
1055  else
1056  {
1057  assert(false); // should never reach here
1058  }
1059 
1060  VectorRd eT = VectorRd::Zero();
1061  eT(d) = 1.0;
1062 
1063  for (size_t iqn = 0; iqn < quadF.size(); iqn++)
1064  {
1065  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
1066  {
1067  basis_k_dot_nTF_phiT_quadF[dim * i + d][iqn] = phiT_quadF[k_scalar][iqn] * eK.dot(nTF) * phiT_quadF[i][iqn] * eT;
1068  phiT_quadF_dot_nTF[dim * i + d][iqn] = phiT_quadF[i][iqn] * eT.dot(nTF);
1069  }
1070  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
1071  {
1072  basis_k_dot_phiF_quadF[dim * i + d][iqn] = phiT_quadF[k_scalar][iqn] * phiF_quadF[i][iqn] * eK.dot(eF);
1073  vec_phiF_quadF[dim * i + d][iqn] = phiF_quadF[i][iqn] * eF;
1074  }
1075  }
1076  }
1077 
1078  Eigen::MatrixXd uT_dot_nTF_vT_dot_wF = compute_gram_matrix( basis_k_dot_nTF_phiT_quadF, vec_phiF_quadF, quadF);
1079 
1080  T.block(0, offset_F, n_local_cell_vector_dofs, n_local_face_vector_dofs) = uT_dot_nTF_vT_dot_wF;
1081  T.block(offset_F, 0, n_local_face_vector_dofs, n_local_cell_vector_dofs) = -uT_dot_nTF_vT_dot_wF.transpose() + compute_gram_matrix(basis_k_dot_phiF_quadF, phiT_quadF_dot_nTF, quadF);
1082  }
1083 
1084  return 0.5 * T;
1085  }
1086 
1087  assert(k < n_local_vector_dofs);
1088 
1089  size_t iTF = (k - n_local_cell_vector_dofs) / n_local_face_vector_dofs; // integer division
1090  size_t k_basis = (k - n_local_cell_vector_dofs) % n_local_face_vector_dofs;
1091  size_t k_scalar = k_basis / dim; // integer division
1092 
1093  VectorRd eF = VectorRd::Zero();
1094 
1095  // Sort face unknowns into perpendicular, then paralell components
1096  if (k_basis % dim == 0)
1097  {
1098  eF = cell->face(iTF)->normal();
1099  }
1100  else if (k_basis % dim == 1)
1101  {
1102  eF = cell->face(iTF)->edge(0)->tangent();
1103  }
1104  else if (k_basis % dim == 2)
1105  {
1106  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
1107  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
1108  }
1109  else
1110  {
1111  assert(false); // should never reach here
1112  }
1113  VectorRd nTF = cell->face_normal(iTF);
1114 
1115  QuadratureRule quadF = elquad.get_quadF(iTF);
1116 
1117  BasisQuad<double> phiT_quadF = elquad.get_phiT_quadF(iTF);
1118  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
1119 
1120  BasisQuad<double> phiT_quadF_dot_nTF( boost::extents[n_local_cell_vector_dofs][quadF.size()]);
1121  BasisQuad<double> phiT_quadF_dot_basis_k( boost::extents[n_local_cell_vector_dofs][quadF.size()]);
1122 
1123  for (size_t d = 0; d < dim; ++d)
1124  {
1125  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
1126  {
1127  for (size_t iqn = 0; iqn < quadF.size(); iqn++)
1128  {
1129  phiT_quadF_dot_nTF[dim * i + d][iqn] = phiT_quadF[i][iqn] * nTF(d); // eT.dot(nTF)
1130  phiT_quadF_dot_basis_k[dim * i + d][iqn] = phiT_quadF[i][iqn] * phiF_quadF[k_scalar][iqn] * eF(d); // eT.dot(eF)
1131  }
1132  }
1133  }
1134 
1135  // Eigen::MatrixXd T = Eigen::MatrixXd::Zero(n_local_vector_dofs,
1136  // n_local_vector_dofs);
1137  T.topLeftCorner(n_local_cell_vector_dofs, n_local_cell_vector_dofs) = -0.5 * compute_gram_matrix(phiT_quadF_dot_basis_k, phiT_quadF_dot_nTF, quadF, n_local_cell_vector_dofs, n_local_cell_vector_dofs);
1138  return T;
1139 }
1140 
1141 Eigen::MatrixXd MHDModel::convective_term(Cell *cell, std::vector<Eigen::MatrixXd> tplus_k, std::vector<Eigen::MatrixXd> tminus_k, Eigen::VectorXd u)
1142 {
1143  const size_t n_local_faces = cell->n_faces();
1144  const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces * n_local_face_vector_dofs;
1145  const size_t n_local_dofs = 2 * n_local_vector_dofs;
1146  assert((size_t)u.size() == n_local_dofs);
1147 
1148  Eigen::MatrixXd T = Eigen::MatrixXd::Zero(n_local_dofs, n_local_dofs);
1149 
1150  for (size_t k = 0; k < n_local_vector_dofs; ++k)
1151  {
1152  T.topLeftCorner(n_local_vector_dofs, n_local_vector_dofs) += (u(k) + u(k + n_local_vector_dofs)) * tplus_k[k];
1153  T.topRightCorner(n_local_vector_dofs, n_local_vector_dofs) += (u(k) + u(k + n_local_vector_dofs)) * -tplus_k[k];
1154  T.bottomRightCorner(n_local_vector_dofs, n_local_vector_dofs) += (u(k) + u(k + n_local_vector_dofs)) * tminus_k[k];
1155  T.bottomLeftCorner(n_local_vector_dofs, n_local_vector_dofs) += (u(k) + u(k + n_local_vector_dofs)) * -tminus_k[k];
1156  }
1157 
1158  return T;
1159 }
1160 
1161 Eigen::MatrixXd MHDModel::stabilisation_term(Cell *cell, ElementQuad &elquad)
1162 {
1163  const size_t n_local_faces = cell->n_faces();
1164  const size_t n_local_bdry_vector_dofs = n_local_faces * n_local_face_vector_dofs;
1165 
1166  QuadratureRule quadT = elquad.get_quadT();
1167 
1168  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
1169  BasisQuad<VectorRd> dphiT_quadT = elquad.get_dphiT_quadT();
1170 
1171  Eigen::MatrixXd MTT = compute_gram_matrix(phiT_quadT, phiT_quadT, quadT, n_local_cell_scalar_dofs, n_local_highorder_scalar_dofs, "sym");
1172  Eigen::MatrixXd StiffT = compute_gram_matrix(dphiT_quadT, dphiT_quadT, quadT, n_local_cell_scalar_dofs, n_local_cell_scalar_dofs, "sym");
1173 
1174  Eigen::MatrixXd vec_MTT = Eigen::MatrixXd::Zero(n_local_cell_vector_dofs, n_local_highorder_vector_dofs);
1175  Eigen::MatrixXd vec_StiffT = Eigen::MatrixXd::Zero(n_local_cell_vector_dofs, n_local_cell_vector_dofs);
1176 
1177  for (size_t k = 0; k < dim; k++)
1178  {
1179  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
1180  {
1181  for (size_t j = 0; j < n_local_highorder_scalar_dofs; j++)
1182  {
1183  if (j < n_local_cell_scalar_dofs)
1184  {
1185  vec_StiffT(dim * i + k, dim * j + k) = StiffT(i, j);
1186  }
1187  vec_MTT(dim * i + k, dim * j + k) = MTT(i, j);
1188  }
1189  }
1190  }
1191 
1192  Eigen::MatrixXd diffT = (vec_MTT.topLeftCorner(n_local_cell_vector_dofs, n_local_cell_vector_dofs).ldlt()).solve(vec_MTT) * RT[cell->global_index()];
1193  diffT.topLeftCorner(n_local_cell_vector_dofs, n_local_cell_vector_dofs) -= Eigen::MatrixXd::Identity(n_local_cell_vector_dofs, n_local_cell_vector_dofs);
1194 
1195  std::vector<Eigen::Triplet<double>> triplets_M_BDRY_BDRY;
1196  std::vector<Eigen::Triplet<double>> triplets_M_BDRY_T;
1197  std::vector<Eigen::Triplet<double>> triplets_SCALE_MAT;
1198 
1199  Eigen::SparseMatrix<double> M_BDRY_BDRY(n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
1200  Eigen::SparseMatrix<double> M_BDRY_T(n_local_bdry_vector_dofs, n_local_highorder_vector_dofs);
1201  Eigen::SparseMatrix<double> BDRY_SCALE_MAT(n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
1202 
1203  for (size_t iTF = 0; iTF < n_local_faces; iTF++)
1204  {
1205  const size_t offset_F = iTF * n_local_face_vector_dofs;
1206 
1207  QuadratureRule quadF = elquad.get_quadF(iTF);
1208  size_t n_quadF = quadF.size();
1209 
1210  BasisQuad<double> phiF_quadF = elquad.get_phiF_quadF(iTF);
1211  BasisQuad<double> phiT_quadF = elquad.get_phiT_quadF(iTF);
1212 
1213  BasisQuad<VectorRd> vec_phiF_quadF( boost::extents[n_local_face_vector_dofs][n_quadF]);
1214  BasisQuad<VectorRd> vec_phiT_quadF( boost::extents[n_local_highorder_vector_dofs][n_quadF]);
1215 
1216  for (size_t d = 0; d < dim; d++)
1217  {
1218  VectorRd eT = VectorRd::Zero();
1219  eT(d) = 1;
1220 
1221  VectorRd eF = VectorRd::Zero();
1222 
1223  // Sort face unknowns into perpendicular, then paralell components
1224  if (d == 0)
1225  {
1226  eF = cell->face(iTF)->normal();
1227  }
1228  else if (d == 1)
1229  {
1230  eF = cell->face(iTF)->edge(0)->tangent();
1231  }
1232  else if (d == 2)
1233  {
1234  // eF = mesh_ptr->cell(iT)->face(iTF)->edge(1)->tangent();
1235  eF = (cell->face(iTF)->normal()).cross(cell->face(iTF)->edge(0)->tangent());
1236  }
1237  else
1238  {
1239  assert(false); // should never reach here
1240  }
1241 
1242  for (size_t iqn = 0; iqn < n_quadF; iqn++)
1243  {
1244  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
1245  {
1246  vec_phiF_quadF[dim * i + d][iqn] = phiF_quadF[i][iqn] * eF;
1247  }
1248  for (size_t i = 0; i < n_local_highorder_scalar_dofs; i++)
1249  {
1250  vec_phiT_quadF[dim * i + d][iqn] = phiT_quadF[i][iqn] * eT;
1251  }
1252  }
1253  }
1254 
1255  Eigen::MatrixXd vec_MFF = compute_gram_matrix(vec_phiF_quadF, vec_phiF_quadF, quadF, "sym");
1256  Eigen::MatrixXd vec_MFT = compute_gram_matrix(vec_phiF_quadF, vec_phiT_quadF, quadF, n_local_face_vector_dofs, n_local_highorder_vector_dofs);
1257 
1258  Eigen::MatrixXd LOCAL_SCALING = (1.0 / (cell->diam())) * Eigen::MatrixXd::Identity(n_local_face_vector_dofs, n_local_face_vector_dofs);
1259 
1260  for (size_t i = 0; i < n_local_face_vector_dofs; ++i)
1261  {
1262  for (size_t j = 0; j < n_local_face_vector_dofs; ++j)
1263  {
1264  triplets_M_BDRY_BDRY.emplace_back(offset_F + i, offset_F + j, vec_MFF(i, j));
1265  triplets_SCALE_MAT.emplace_back(offset_F + i, offset_F + j, LOCAL_SCALING(i, j));
1266  }
1267  for (size_t k = 0; k < n_local_highorder_vector_dofs; ++k)
1268  {
1269  triplets_M_BDRY_T.emplace_back(offset_F + i, k, vec_MFT(i, k));
1270  }
1271  }
1272  }
1273 
1274  M_BDRY_BDRY.setFromTriplets(std::begin(triplets_M_BDRY_BDRY), std::end(triplets_M_BDRY_BDRY));
1275  M_BDRY_T.setFromTriplets(std::begin(triplets_M_BDRY_T), std::end(triplets_M_BDRY_T));
1276  BDRY_SCALE_MAT.setFromTriplets(std::begin(triplets_SCALE_MAT), std::end(triplets_SCALE_MAT));
1277 
1278  Eigen::BiCGSTAB<Eigen::SparseMatrix<double>> solver;
1279  solver.compute(M_BDRY_BDRY);
1280 
1281  Eigen::MatrixXd inv_M_BDRY_BDRY_M_BDRY_T = solver.solve(M_BDRY_T);
1282 
1283  // Eigen::MatrixXd diffBDRY = solver.solve(M_BDRY_T) * RT[cell->global_index()];
1284  Eigen::MatrixXd diffBDRY = inv_M_BDRY_BDRY_M_BDRY_T * RT[cell->global_index()];
1285  diffBDRY.bottomRightCorner(n_local_bdry_vector_dofs, n_local_bdry_vector_dofs) -= Eigen::MatrixXd::Identity(n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
1286 
1287  return diffT.transpose() * vec_StiffT * diffT + diffBDRY.transpose() * BDRY_SCALE_MAT * M_BDRY_BDRY * diffBDRY;
1288  // return diffBDRY.transpose() * BDRY_SCALE_MAT * M_BDRY_BDRY * diffBDRY;
1289 
1290  // Eigen::MatrixXd diffT_on_bdry = inv_M_BDRY_BDRY_M_BDRY_T.topLeftCorner(n_local_bdry_vector_dofs, n_local_cell_vector_dofs) * diffT;
1291 
1292  // return (diffT_on_bdry - diffBDRY).transpose() * BDRY_SCALE_MAT * M_BDRY_BDRY * (diffT_on_bdry - diffBDRY);
1293 }
1294 
1295 Eigen::VectorXd MHDModel::source_term(FType<VectorRd> f_vec, Cell *cell, ElementQuad &elquad)
1296 {
1297  const size_t n_local_faces = cell->n_faces();
1298  const size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_faces * n_local_face_vector_dofs;
1299 
1300  // get quadrature rule and basis function on quadrature nodes
1301  QuadratureRule quadT = elquad.get_quadT();
1302  BasisQuad<double> phiT_quadT = elquad.get_phiT_quadT();
1303  BasisQuad<VectorRd> vec_phiT_quadT(boost::extents[n_local_highorder_vector_dofs][quadT.size()]);
1304 
1305  Eigen::VectorXd source_vec = Eigen::VectorXd::Zero(n_local_vector_dofs);
1306 
1307  for (size_t k = 0; k < dim; k++)
1308  {
1309  VectorRd eT = VectorRd::Zero();
1310  eT(k) = 1;
1311 
1312  for (size_t iqn = 0; iqn < quadT.size(); iqn++)
1313  {
1314  for (size_t i = 0; i < n_local_cell_scalar_dofs; i++)
1315  {
1316  vec_phiT_quadT[dim * i + k][iqn] = phiT_quadT[i][iqn] * eT;
1317  }
1318  }
1319  }
1320 
1321  // Integrate source term with basis functions
1322  source_vec.head(n_local_cell_vector_dofs) = integrate(f_vec, vec_phiT_quadT, quadT, n_local_cell_vector_dofs);
1323  return source_vec;
1324 }
1325 
1326 SolutionVector MHDModel::solve_with_static_cond(FType<VectorRd> f_source, FType<VectorRd> g_source, double visc, double diff, double tol, bool threading)
1327 {
1328 
1329  std::vector<Eigen::VectorXd> LTf(n_cells);
1330  std::vector<Eigen::VectorXd> LTg(n_cells);
1331  std::vector<Eigen::MatrixXd> DT(n_cells);
1332  // std::vector<std::vector<Eigen::MatrixXd>> TTk(n_cells);
1333  std::vector<std::vector<Eigen::MatrixXd>> Tplus_k(n_cells);
1334  std::vector<std::vector<Eigen::MatrixXd>> Tminus_k(n_cells);
1335 
1336  std::cout << " Assembling local matrices\n";
1337  boost::timer::cpu_timer assembly_timer;
1338  assembly_timer.start();
1339 
1340  // Construct the local matrices using multithreading if use_threads is true
1341  std::function<void(size_t, size_t)> construct_all_local_contributions = [&](size_t start, size_t end) -> void
1342  {
1343  for (size_t iT = start; iT < end; ++iT)
1344  {
1345  // ElementQuad elquad(m_hho, iT, m_K + 1 + m_K + 1 + 2, m_K + m_K + 1);
1346 
1347  ElementQuad elquad(m_hho, iT, std::max(3 * m_L, m_L + m_K + 1) + 1, std::max(3 * m_K, m_K + m_K + 1) + 1);
1348  // ElementQuad elquad(m_hho, iT, 15, 15);
1349  Cell *cell = mesh_ptr->cell(iT);
1350 
1351  AT[iT] = gradient_term(cell, elquad);
1352  AT[iT] = AT[iT] + stabilisation_term(cell, elquad);
1353  LTf[iT] = (source_term(f_source, cell, elquad).head(n_local_cell_vector_dofs));
1354  LTg[iT] = (source_term(g_source, cell, elquad).head(n_local_cell_vector_dofs));
1355  DT[iT] = (divergence_term(cell, elquad));
1356  size_t n_local_vector_dofs = n_local_cell_vector_dofs + cell->n_faces() * n_local_face_vector_dofs;
1357  // for (size_t k = 0; k < 2 * n_local_vector_dofs; ++k)
1358  for (size_t k = 0; k < n_local_vector_dofs; ++k)
1359  {
1360  // TTk[iT].push_back(T_ijk_plus_T_ikj(cell, elquad, k));
1361  Tplus_k[iT].push_back(th_ijk_plus_th_ikj(cell, elquad, k));
1362  Tminus_k[iT].push_back(th_ijk_minus_th_ikj(cell, elquad, k));
1363  }
1364  }
1365  };
1366 
1367  // Running the local constructions in parallel
1368  parallel_for(n_cells, construct_all_local_contributions, threading);
1369 
1370  double exact_rhs_norm = 0.0;
1371  for (size_t iT = 0; iT < n_cells; ++iT)
1372  {
1373  exact_rhs_norm += LTf[iT].squaredNorm() + LTg[iT].squaredNorm();
1374  }
1375  exact_rhs_norm = std::sqrt(exact_rhs_norm);
1376 
1377  assembly_timer.stop();
1378  std::cout << " Assembly time = " << double(assembly_timer.elapsed().wall) * 1E-9 << "\n";
1379 
1380  size_t n_magnetic_dofs = n_total_cell_vector_dofs + n_total_face_vector_dofs + n_total_cell_scalar_dofs;
1381  size_t n_velocity_dofs = n_total_cell_vector_dofs + n_total_face_vector_dofs + n_total_cell_scalar_dofs;
1382 
1383  size_t n_total_dofs = n_magnetic_dofs + n_velocity_dofs;
1384 
1385  size_t n_velocity_unknowns = n_total_face_vector_dofs + n_cells; // face dofs plus one scalar dof per cell
1386  size_t n_magnetic_unknowns = n_total_face_vector_dofs + n_cells; // face dofs plus one scalar dof per cell
1387 
1388  size_t n_implicit_velocity_dofs = n_total_cell_vector_dofs + n_cells * (n_local_cell_scalar_dofs - 1);
1389  size_t n_implicit_magnetic_dofs = n_total_cell_vector_dofs + n_cells * (n_local_cell_scalar_dofs - 1);
1390 
1391  size_t n_unknowns = n_velocity_unknowns + n_magnetic_unknowns;
1392  size_t n_implicit_dofs = n_implicit_velocity_dofs + n_implicit_magnetic_dofs;
1393 
1394  SolutionVector SOL(Eigen::VectorXd::Zero(n_total_dofs), mesh_ptr, m_L, m_K);
1395 
1396  std::cout << " Solving scheme using Newton method\n";
1397 
1398  boost::timer::cpu_timer solving_timer;
1399  solving_timer.start();
1400 
1401  double residual_error = 0.0;
1402  int i = 0;
1403 
1404  do
1405  {
1406  std::vector<Eigen::VectorXd> local_RHS(n_cells);
1407 
1408  std::vector<Eigen::MatrixXd> local_inv_LTll_LTlg(n_cells);
1409  std::vector<Eigen::MatrixXd> local_LTgl(n_cells);
1410  std::vector<Eigen::MatrixXd> local_LTgg(n_cells);
1411 
1412  Eigen::VectorXd inv_LTll_rTl = Eigen::VectorXd::Zero(n_implicit_dofs);
1413 
1414  // Construct the local matrices using multithreading if use_threads is true
1415  std::function<void(size_t, size_t)> build_local_mats = [&](size_t start, size_t end) -> void
1416  {
1417  for (size_t iT = start; iT < end; ++iT)
1418  {
1419  Cell *cell = mesh_ptr->cell(iT);
1420  size_t n_local_faces = cell->n_faces();
1421  size_t n_local_bdry_vector_dofs = n_local_faces * n_local_face_vector_dofs;
1422  size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_bdry_vector_dofs;
1423  size_t n_local_unknowns = 2 * (n_local_vector_dofs + n_local_cell_scalar_dofs);
1424  size_t magnetic_offset = n_local_vector_dofs + n_local_cell_scalar_dofs;
1425 
1426  Eigen::VectorXd uT = SOL.restr(iT);
1427  Eigen::VectorXd vector_terms = Eigen::VectorXd::Zero(2 * n_local_vector_dofs);
1428  vector_terms.head(n_local_vector_dofs) = uT.head(n_local_vector_dofs);
1429  vector_terms.tail(n_local_vector_dofs) = uT.segment(magnetic_offset, n_local_vector_dofs);
1430 
1431  Eigen::MatrixXd A = Eigen::MatrixXd::Zero(n_local_unknowns, n_local_unknowns);
1432 
1433  A.block(0, 0, n_local_vector_dofs, n_local_vector_dofs) = visc * AT[iT];
1434  A.block(0, n_local_vector_dofs, n_local_vector_dofs, n_local_cell_scalar_dofs) = DT[iT].transpose();
1435  A.block(n_local_vector_dofs, 0, n_local_cell_scalar_dofs, n_local_vector_dofs) = -DT[iT];
1436 
1437  A.block(magnetic_offset, magnetic_offset, n_local_vector_dofs, n_local_vector_dofs) = diff * AT[iT];
1438  A.block(magnetic_offset, magnetic_offset + n_local_vector_dofs, n_local_vector_dofs, n_local_cell_scalar_dofs) = DT[iT].transpose();
1439  A.block(magnetic_offset + n_local_vector_dofs, magnetic_offset, n_local_cell_scalar_dofs, n_local_vector_dofs) = -DT[iT];
1440 
1441  // Eigen::MatrixXd T = convective_term(cell, TTk[iT], vector_terms);
1442  Eigen::MatrixXd T = convective_term(cell, Tplus_k[iT], Tminus_k[iT], vector_terms);
1443  Eigen::MatrixXd BMAT = Eigen::MatrixXd::Zero(n_local_unknowns, n_local_unknowns);
1444  BMAT.block(0, 0, n_local_vector_dofs, n_local_vector_dofs) = T.topLeftCorner(n_local_vector_dofs, n_local_vector_dofs);
1445  BMAT.block(magnetic_offset, 0, n_local_vector_dofs, n_local_vector_dofs) = T.bottomLeftCorner(n_local_vector_dofs, n_local_vector_dofs);
1446  BMAT.block(0, magnetic_offset, n_local_vector_dofs, n_local_vector_dofs) = T.topRightCorner(n_local_vector_dofs, n_local_vector_dofs);
1447  BMAT.block(magnetic_offset, magnetic_offset, n_local_vector_dofs, n_local_vector_dofs) = T.bottomRightCorner(n_local_vector_dofs, n_local_vector_dofs);
1448 
1449  Eigen::MatrixXd local_DG = A + BMAT;
1450  Eigen::MatrixXd GMAT = A + 0.5 * BMAT;
1451 
1452  Eigen::VectorXd G = GMAT * uT;
1453  G.segment(0, n_local_cell_vector_dofs) -= LTf[iT];
1454  G.segment(magnetic_offset, n_local_cell_vector_dofs) -= LTg[iT];
1455 
1456  local_RHS[iT] = -G;
1457 
1458  // local_DG.block(0, 0, n_local_cell_vector_dofs, n_local_cell_vector_dofs) += std::pow(deltaN, -1) * vec_MTT[iT];
1459  // local_DG.block(magnetic_offset, magnetic_offset, n_local_cell_vector_dofs, n_local_cell_vector_dofs) += std::pow(deltaN, -1) * vec_MTT[iT];
1460 
1461  // local_DG.block(n_local_vector_dofs, n_local_vector_dofs, n_local_cell_scalar_dofs, n_local_cell_scalar_dofs) += std::pow(deltaN, -1) * MTT[iT];
1462  // local_DG.block(n_local_vector_dofs + magnetic_offset, n_local_vector_dofs + magnetic_offset, n_local_cell_scalar_dofs, n_local_cell_scalar_dofs) += std::pow(deltaN, -1) * MTT[iT];
1463 
1464  size_t n_local_condensed_velocity_dofs = n_local_bdry_vector_dofs + 1;
1465  size_t n_local_condensed_magnetic_dofs = n_local_bdry_vector_dofs + 1;
1466 
1467  size_t n_local_condensed_dofs = n_local_condensed_velocity_dofs + n_local_condensed_magnetic_dofs;
1468 
1469  size_t n_local_implicit_velocity_dofs = n_local_cell_vector_dofs + n_local_cell_scalar_dofs - 1;
1470  size_t n_local_implicit_magnetic_dofs = n_local_cell_vector_dofs + n_local_cell_scalar_dofs - 1;
1471 
1472  size_t n_local_implicit_dofs = n_local_implicit_velocity_dofs + n_local_implicit_magnetic_dofs;
1473 
1474  size_t local_magnetic_offset = n_local_vector_dofs + n_local_cell_scalar_dofs;
1475  size_t local_condensed_magnetic_offset = n_local_bdry_vector_dofs + 1;
1476  size_t local_implicit_magnetic_offset = n_local_cell_vector_dofs + n_local_cell_scalar_dofs - 1;
1477 
1478  Eigen::MatrixXd local_LTll = Eigen::MatrixXd::Zero(n_local_implicit_dofs, n_local_implicit_dofs);
1479 
1480  // velocity-velocity dofs
1481  local_LTll.block(0, 0, n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(0, 0, n_local_cell_vector_dofs, n_local_cell_vector_dofs);
1482  local_LTll.block(0, n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1) = local_DG.block(0, n_local_vector_dofs + 1, n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1);
1483  local_LTll.block(n_local_cell_vector_dofs, 0, n_local_cell_scalar_dofs - 1, n_local_cell_vector_dofs) = local_DG.block(n_local_vector_dofs + 1, 0, n_local_cell_scalar_dofs - 1, n_local_cell_vector_dofs);
1484 
1485  // velocity pressure-pressure dofs (relaxation)
1486  // local_LTll.block(n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1, n_local_cell_scalar_dofs - 1) = local_DG.block(n_local_vector_dofs + 1, n_local_vector_dofs + 1, n_local_cell_scalar_dofs - 1, n_local_cell_scalar_dofs - 1);
1487 
1488  // velocity-magnetic dofs
1489  local_LTll.block(0, local_implicit_magnetic_offset, n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(0, local_magnetic_offset, n_local_cell_vector_dofs, n_local_cell_vector_dofs);
1490 
1491  // magnetic-velocity dofs
1492  local_LTll.block(local_implicit_magnetic_offset, 0, n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(local_magnetic_offset, 0, n_local_cell_vector_dofs, n_local_cell_vector_dofs);
1493 
1494  // magnetic-magnetic dofs
1495  local_LTll.block(local_implicit_magnetic_offset, local_implicit_magnetic_offset, n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(local_magnetic_offset, local_magnetic_offset, n_local_cell_vector_dofs, n_local_cell_vector_dofs);
1496  local_LTll.block(local_implicit_magnetic_offset, local_implicit_magnetic_offset + n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1) = local_DG.block(local_magnetic_offset, local_magnetic_offset + n_local_vector_dofs + 1, n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1);
1497  local_LTll.block(local_implicit_magnetic_offset + n_local_cell_vector_dofs, local_implicit_magnetic_offset, n_local_cell_scalar_dofs - 1, n_local_cell_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_vector_dofs + 1, local_magnetic_offset, n_local_cell_scalar_dofs - 1, n_local_cell_vector_dofs);
1498 
1499  // magnetic pressure-pressure dofs (relaxation)
1500  // local_LTll.block(local_implicit_magnetic_offset + n_local_cell_vector_dofs, local_implicit_magnetic_offset + n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1, n_local_cell_scalar_dofs - 1) = local_DG.block(local_magnetic_offset + n_local_vector_dofs + 1, local_magnetic_offset + n_local_vector_dofs + 1, n_local_cell_scalar_dofs - 1, n_local_cell_scalar_dofs - 1);
1501 
1502  Eigen::MatrixXd local_LTlg = Eigen::MatrixXd::Zero(n_local_implicit_dofs, n_local_condensed_dofs);
1503 
1504  // velocity-velocity dofs
1505  local_LTlg.block(0, 0, n_local_cell_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(0, n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_bdry_vector_dofs);
1506  local_LTlg.block(n_local_cell_vector_dofs, 0, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs) = local_DG.block(n_local_vector_dofs + 1, n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs);
1507 
1508  // velocity-magnetic dofs
1509  local_LTlg.block(0, local_condensed_magnetic_offset, n_local_cell_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(0, local_magnetic_offset + n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_bdry_vector_dofs);
1510  // local_LTlg.block(0 + n_local_cell_vector_dofs, local_condensed_magnetic_offset, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs) = local_DG.block(0 + n_local_vector_dofs + 1, local_magnetic_offset + n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs);
1511 
1512  // magnetic-velocity dofs
1513  local_LTlg.block(local_implicit_magnetic_offset, 0, n_local_cell_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset, 0 + n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_bdry_vector_dofs);
1514  // local_LTlg.block(local_implicit_magnetic_offset + n_local_cell_vector_dofs, 0, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_vector_dofs + 1, 0 + n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs);
1515 
1516  // magnetic-magnetic dofs
1517  local_LTlg.block(local_implicit_magnetic_offset, local_condensed_magnetic_offset, n_local_cell_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset, local_magnetic_offset + n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_bdry_vector_dofs);
1518  local_LTlg.block(local_implicit_magnetic_offset + n_local_cell_vector_dofs, local_condensed_magnetic_offset, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_vector_dofs + 1, local_magnetic_offset + n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1, n_local_bdry_vector_dofs);
1519 
1520  local_LTgl[iT] = Eigen::MatrixXd::Zero(n_local_condensed_dofs, n_local_implicit_dofs);
1521 
1522  // velocity-velocity dofs
1523  local_LTgl[iT].block(0, 0, n_local_bdry_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(n_local_cell_vector_dofs, 0, n_local_bdry_vector_dofs, n_local_cell_vector_dofs);
1524  local_LTgl[iT].block(0, n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1) = local_DG.block(n_local_cell_vector_dofs, n_local_vector_dofs + 1, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1);
1525 
1526  // velocity-magnetic dofs
1527  local_LTgl[iT].block(0, local_implicit_magnetic_offset, n_local_bdry_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(0 + n_local_cell_vector_dofs, local_magnetic_offset, n_local_bdry_vector_dofs, n_local_cell_vector_dofs);
1528  // local_LTgl.block(0, local_implicit_magnetic_offset + n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1) = local_DG.block(0 + n_local_cell_vector_dofs, local_magnetic_offset + n_local_vector_dofs + 1, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1);
1529 
1530  // magnetic-velocity dofs
1531  local_LTgl[iT].block(local_condensed_magnetic_offset, 0, n_local_bdry_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, 0, n_local_bdry_vector_dofs, n_local_cell_vector_dofs);
1532  // local_LTgl.block(local_condensed_magnetic_offset, 0 + n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, 0 + n_local_vector_dofs + 1, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1);
1533 
1534  // magnetic-magnetic dofs
1535  local_LTgl[iT].block(local_condensed_magnetic_offset, local_implicit_magnetic_offset, n_local_bdry_vector_dofs, n_local_cell_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, local_magnetic_offset, n_local_bdry_vector_dofs, n_local_cell_vector_dofs);
1536  local_LTgl[iT].block(local_condensed_magnetic_offset, local_implicit_magnetic_offset + n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, local_magnetic_offset + n_local_vector_dofs + 1, n_local_bdry_vector_dofs, n_local_cell_scalar_dofs - 1);
1537 
1538  local_LTgg[iT] = Eigen::MatrixXd::Zero(n_local_condensed_dofs, n_local_condensed_dofs);
1539 
1540  // velocity-velocity dofs
1541  local_LTgg[iT].block(0, 0, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(n_local_cell_vector_dofs, n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
1542  local_LTgg[iT].block(0, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1) = local_DG.block(n_local_cell_vector_dofs, n_local_cell_vector_dofs + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1);
1543  local_LTgg[iT].block(n_local_bdry_vector_dofs, 0, 1, n_local_bdry_vector_dofs) = local_DG.block(n_local_cell_vector_dofs + n_local_bdry_vector_dofs, n_local_cell_vector_dofs, 1, n_local_bdry_vector_dofs);
1544 
1545  // velocity pressure-pressure dofs (relaxation)
1546  // local_LTgg[iT](n_local_bdry_vector_dofs, n_local_bdry_vector_dofs) = local_DG(n_local_cell_vector_dofs + n_local_bdry_vector_dofs, n_local_cell_vector_dofs + n_local_bdry_vector_dofs);
1547 
1548  // velocity-magnetic dofs
1549  local_LTgg[iT].block(0, local_condensed_magnetic_offset, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(0 + n_local_cell_vector_dofs, local_magnetic_offset + n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
1550  // local_LTgg[iT].block(0, local_condensed_magnetic_offset + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1) = local_DG.block(0 + n_local_cell_vector_dofs, local_magnetic_offset + n_local_cell_vector_dofs + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1);
1551  // local_LTgg[iT].block(0 + n_local_bdry_vector_dofs, local_condensed_magnetic_offset, 1, n_local_bdry_vector_dofs) = local_DG.block(0 + n_local_cell_vector_dofs + n_local_bdry_vector_dofs, local_magnetic_offset + n_local_cell_vector_dofs, 1, n_local_bdry_vector_dofs);
1552 
1553  // magnetic-velocity dofs
1554  local_LTgg[iT].block(local_condensed_magnetic_offset, 0, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, 0 + n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
1555  // local_LTgg[iT].block(local_condensed_magnetic_offset, 0 + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, 0 + n_local_cell_vector_dofs + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1);
1556  // local_LTgg[iT].block(local_condensed_magnetic_offset + n_local_bdry_vector_dofs, 0, 1, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs + n_local_bdry_vector_dofs, 0 + n_local_cell_vector_dofs, 1, n_local_bdry_vector_dofs);
1557 
1558  // magnetic-magnetic dofs
1559  local_LTgg[iT].block(local_condensed_magnetic_offset, local_condensed_magnetic_offset, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, local_magnetic_offset + n_local_cell_vector_dofs, n_local_bdry_vector_dofs, n_local_bdry_vector_dofs);
1560  local_LTgg[iT].block(local_condensed_magnetic_offset, local_condensed_magnetic_offset + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs, local_magnetic_offset + n_local_cell_vector_dofs + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs, 1);
1561  local_LTgg[iT].block(local_condensed_magnetic_offset + n_local_bdry_vector_dofs, local_condensed_magnetic_offset, 1, n_local_bdry_vector_dofs) = local_DG.block(local_magnetic_offset + n_local_cell_vector_dofs + n_local_bdry_vector_dofs, local_magnetic_offset + n_local_cell_vector_dofs, 1, n_local_bdry_vector_dofs);
1562 
1563  // magnetic pressure-pressure dofs (relaxation)
1564  // local_LTgg[iT](local_condensed_magnetic_offset + n_local_bdry_vector_dofs, local_condensed_magnetic_offset + n_local_bdry_vector_dofs) = local_DG(local_magnetic_offset + n_local_cell_vector_dofs + n_local_bdry_vector_dofs, local_magnetic_offset + n_local_cell_vector_dofs + n_local_bdry_vector_dofs);
1565 
1566  Eigen::PartialPivLU<Eigen::MatrixXd> solver;
1567  solver.compute(local_LTll);
1568 
1569  Eigen::VectorXd local_rTl = Eigen::VectorXd::Zero(n_local_implicit_dofs);
1570  local_rTl.head(n_local_cell_vector_dofs) = local_RHS[iT].head(n_local_cell_vector_dofs);
1571  local_rTl.segment(local_implicit_magnetic_offset, n_local_cell_vector_dofs) = local_RHS[iT].segment(local_magnetic_offset, n_local_cell_vector_dofs);
1572 
1573  Eigen::VectorXd local_inv_LTll_rTl = solver.solve(local_rTl);
1574  local_inv_LTll_LTlg[iT] = solver.solve(local_LTlg);
1575 
1576  inv_LTll_rTl.segment(iT * n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_inv_LTll_rTl.segment(0, n_local_cell_vector_dofs);
1577  inv_LTll_rTl.segment(n_total_cell_vector_dofs + iT * (n_local_cell_scalar_dofs - 1), n_local_cell_scalar_dofs - 1) = local_inv_LTll_rTl.segment(n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1);
1578 
1579  inv_LTll_rTl.segment(n_implicit_velocity_dofs + iT * n_local_cell_vector_dofs, n_local_cell_vector_dofs) = local_inv_LTll_rTl.segment(local_implicit_magnetic_offset, n_local_cell_vector_dofs);
1580  inv_LTll_rTl.segment(n_implicit_velocity_dofs + n_total_cell_vector_dofs + iT * (n_local_cell_scalar_dofs - 1), n_local_cell_scalar_dofs - 1) = local_inv_LTll_rTl.segment(local_implicit_magnetic_offset + n_local_cell_vector_dofs, n_local_cell_scalar_dofs - 1);
1581  }
1582  };
1583 
1584  // Running the local constructions in parallel
1585  parallel_for(n_cells, build_local_mats, threading);
1586 
1587  Eigen::SparseMatrix<double> inv_LTll_LTlg(n_implicit_dofs, n_unknowns + 2);
1588  Eigen::SparseMatrix<double> LTgl(n_unknowns + 2, n_implicit_dofs);
1589  Eigen::SparseMatrix<double> LTgg(n_unknowns + 2, n_unknowns + 2);
1590 
1591  std::vector<Eigen::Triplet<double>> triplets_inv_LTll_LTlg;
1592  std::vector<Eigen::Triplet<double>> triplets_LTgl;
1593  std::vector<Eigen::Triplet<double>> triplets_LTgg;
1594 
1595  Eigen::VectorXd rTg = Eigen::VectorXd::Zero(n_unknowns + 2);
1596 
1597  for (size_t iT = 0; iT < n_cells; ++iT)
1598  {
1599  Cell *cell = mesh_ptr->cell(iT);
1600  size_t n_local_faces = cell->n_faces();
1601  size_t n_local_bdry_vector_dofs = n_local_faces * n_local_face_vector_dofs;
1602 
1603  size_t local_velocity_cell_offset = 0;
1604  size_t local_magnetic_cell_offset = n_local_cell_vector_dofs + n_local_cell_scalar_dofs - 1;
1605  size_t global_velocity_cell_offset = iT * n_local_cell_vector_dofs;
1606  size_t global_magnetic_cell_offset = n_implicit_velocity_dofs + iT * n_local_cell_vector_dofs;
1607 
1608  size_t local_velocity_pressure_offset = n_local_cell_vector_dofs;
1609  size_t local_magnetic_pressure_offset = n_local_cell_vector_dofs + n_local_cell_scalar_dofs - 1 + n_local_cell_vector_dofs;
1610  size_t global_velocity_pressure_offset = n_total_cell_vector_dofs + iT * (n_local_cell_scalar_dofs - 1);
1611  size_t global_magnetic_pressure_offset = n_implicit_velocity_dofs + n_total_cell_vector_dofs + iT * (n_local_cell_scalar_dofs - 1);
1612 
1613  for (size_t iTF = 0; iTF < n_local_faces; iTF++)
1614  {
1615  size_t iF = cell->face(iTF)->global_index();
1616 
1617  size_t local_velocity_face_iTF_offset = iTF * n_local_face_vector_dofs;
1618  size_t global_velocity_face_iTF_offset = iF * n_local_face_vector_dofs;
1619  size_t local_magnetic_face_iTF_offset = n_local_bdry_vector_dofs + 1 + iTF * n_local_face_vector_dofs;
1620  size_t global_magnetic_face_iTF_offset = n_velocity_unknowns + iF * n_local_face_vector_dofs;
1621 
1622  rTg.segment(global_velocity_face_iTF_offset, n_local_face_vector_dofs) += local_RHS[iT].segment(n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs, n_local_face_vector_dofs);
1623  rTg.segment(global_magnetic_face_iTF_offset, n_local_face_vector_dofs) += local_RHS[iT].segment(n_local_cell_vector_dofs + n_local_bdry_vector_dofs + n_local_cell_scalar_dofs + n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs, n_local_face_vector_dofs);
1624 
1625  for (size_t i = 0; i < n_local_face_vector_dofs; i++)
1626  {
1627  for (size_t j = 0; j < n_local_cell_vector_dofs; j++)
1628  {
1629  triplets_LTgl.emplace_back(global_velocity_face_iTF_offset + i, global_velocity_cell_offset + j, local_LTgl[iT](local_velocity_face_iTF_offset + i, local_velocity_cell_offset + j));
1630  triplets_LTgl.emplace_back(global_velocity_face_iTF_offset + i, global_magnetic_cell_offset + j, local_LTgl[iT](local_velocity_face_iTF_offset + i, local_magnetic_cell_offset + j));
1631  triplets_LTgl.emplace_back(global_magnetic_face_iTF_offset + i, global_velocity_cell_offset + j, local_LTgl[iT](local_magnetic_face_iTF_offset + i, local_velocity_cell_offset + j));
1632  triplets_LTgl.emplace_back(global_magnetic_face_iTF_offset + i, global_magnetic_cell_offset + j, local_LTgl[iT](local_magnetic_face_iTF_offset + i, local_magnetic_cell_offset + j));
1633 
1634  triplets_inv_LTll_LTlg.emplace_back(global_velocity_cell_offset + j, global_velocity_face_iTF_offset + i, local_inv_LTll_LTlg[iT](local_velocity_cell_offset + j, local_velocity_face_iTF_offset + i));
1635  triplets_inv_LTll_LTlg.emplace_back(global_magnetic_cell_offset + j, global_velocity_face_iTF_offset + i, local_inv_LTll_LTlg[iT](local_magnetic_cell_offset + j, local_velocity_face_iTF_offset + i));
1636  triplets_inv_LTll_LTlg.emplace_back(global_velocity_cell_offset + j, global_magnetic_face_iTF_offset + i, local_inv_LTll_LTlg[iT](local_velocity_cell_offset + j, local_magnetic_face_iTF_offset + i));
1637  triplets_inv_LTll_LTlg.emplace_back(global_magnetic_cell_offset + j, global_magnetic_face_iTF_offset + i, local_inv_LTll_LTlg[iT](local_magnetic_cell_offset + j, local_magnetic_face_iTF_offset + i));
1638  }
1639  for (size_t j = 0; j < n_local_cell_scalar_dofs - 1; j++)
1640  {
1641  triplets_LTgl.emplace_back(global_velocity_face_iTF_offset + i, global_velocity_pressure_offset + j, local_LTgl[iT](local_velocity_face_iTF_offset + i, local_velocity_pressure_offset + j));
1642  triplets_LTgl.emplace_back(global_magnetic_face_iTF_offset + i, global_magnetic_pressure_offset + j, local_LTgl[iT](local_magnetic_face_iTF_offset + i, local_magnetic_pressure_offset + j));
1643 
1644  triplets_inv_LTll_LTlg.emplace_back(global_velocity_pressure_offset + j, global_velocity_face_iTF_offset + i, local_inv_LTll_LTlg[iT](local_velocity_pressure_offset + j, local_velocity_face_iTF_offset + i));
1645  triplets_inv_LTll_LTlg.emplace_back(global_magnetic_pressure_offset + j, global_magnetic_face_iTF_offset + i, local_inv_LTll_LTlg[iT](local_magnetic_pressure_offset + j, local_magnetic_face_iTF_offset + i));
1646  }
1647  for (size_t jTF = 0; jTF < n_local_faces; jTF++)
1648  {
1649  size_t jF = cell->face(jTF)->global_index();
1650 
1651  size_t local_velocity_face_jTF_offset = jTF * n_local_face_vector_dofs;
1652  size_t global_velocity_face_jTF_offset = jF * n_local_face_vector_dofs;
1653  size_t local_magnetic_face_jTF_offset = n_local_bdry_vector_dofs + 1 + jTF * n_local_face_vector_dofs;
1654  size_t global_magnetic_face_jTF_offset = n_velocity_unknowns + jF * n_local_face_vector_dofs;
1655  for (size_t j = 0; j < n_local_face_vector_dofs; j++)
1656  {
1657  triplets_LTgg.emplace_back(global_velocity_face_iTF_offset + i, global_velocity_face_jTF_offset + j, local_LTgg[iT](local_velocity_face_iTF_offset + i, local_velocity_face_jTF_offset + j));
1658  triplets_LTgg.emplace_back(global_velocity_face_iTF_offset + i, global_magnetic_face_jTF_offset + j, local_LTgg[iT](local_velocity_face_iTF_offset + i, local_magnetic_face_jTF_offset + j));
1659  triplets_LTgg.emplace_back(global_magnetic_face_iTF_offset + i, global_velocity_face_jTF_offset + j, local_LTgg[iT](local_magnetic_face_iTF_offset + i, local_velocity_face_jTF_offset + j));
1660  triplets_LTgg.emplace_back(global_magnetic_face_iTF_offset + i, global_magnetic_face_jTF_offset + j, local_LTgg[iT](local_magnetic_face_iTF_offset + i, local_magnetic_face_jTF_offset + j));
1661  }
1662  }
1663 
1664  triplets_LTgg.emplace_back(global_velocity_face_iTF_offset + i, n_total_face_vector_dofs + iT, local_LTgg[iT](local_velocity_face_iTF_offset + i, n_local_bdry_vector_dofs));
1665  triplets_LTgg.emplace_back(n_total_face_vector_dofs + iT, global_velocity_face_iTF_offset + i, local_LTgg[iT](n_local_bdry_vector_dofs, local_velocity_face_iTF_offset + i));
1666 
1667  triplets_LTgg.emplace_back(global_magnetic_face_iTF_offset + i, n_velocity_unknowns + n_total_face_vector_dofs + iT, local_LTgg[iT](local_magnetic_face_iTF_offset + i, n_local_bdry_vector_dofs + 1 + n_local_bdry_vector_dofs));
1668  triplets_LTgg.emplace_back(n_velocity_unknowns + n_total_face_vector_dofs + iT, global_magnetic_face_iTF_offset + i, local_LTgg[iT](n_local_bdry_vector_dofs + 1 + n_local_bdry_vector_dofs, local_magnetic_face_iTF_offset + i));
1669  }
1670  }
1671 
1672  triplets_LTgg.emplace_back(n_total_face_vector_dofs + iT, n_unknowns, std::sqrt(cell->measure()));
1673  triplets_LTgg.emplace_back(n_unknowns, n_total_face_vector_dofs + iT, -std::sqrt(cell->measure()));
1674  triplets_LTgg.emplace_back(n_velocity_unknowns + n_total_face_vector_dofs + iT, n_unknowns + 1, std::sqrt(cell->measure()));
1675  triplets_LTgg.emplace_back(n_unknowns + 1, n_velocity_unknowns + n_total_face_vector_dofs + iT, -std::sqrt(cell->measure()));
1676 
1677 
1678  // triplets_LTgg.emplace_back(n_unknowns, n_unknowns, std::sqrt(cell->measure()));
1679  // triplets_LTgg.emplace_back(n_unknowns + 1, n_unknowns + 1, std::sqrt(cell->measure()));
1680  // triplets_LTgg.emplace_back(n_unknowns, n_unknowns, 1.0);
1681  // triplets_LTgg.emplace_back(n_unknowns + 1, n_unknowns + 1, 1.0);
1682 
1683  // triplets_LTgg.emplace_back(n_total_face_vector_dofs + iT, n_unknowns, 1.0);
1684  // triplets_LTgg.emplace_back(n_unknowns, n_total_face_vector_dofs + iT, -1.0);
1685  // triplets_LTgg.emplace_back(n_velocity_unknowns + n_total_face_vector_dofs + iT, n_unknowns + 1, 1.0);
1686  // triplets_LTgg.emplace_back(n_unknowns + 1, n_velocity_unknowns + n_total_face_vector_dofs + iT, -1.0);
1687 
1688  // triplets_LTgg.emplace_back(n_total_face_vector_dofs + iT, n_unknowns, 1.0);
1689 
1690  triplets_LTgg.emplace_back(n_total_face_vector_dofs + iT, n_total_face_vector_dofs + iT, std::pow((cell->diam() / double(m_K + 1)), m_K + 2));
1691  triplets_LTgg.emplace_back(n_velocity_unknowns + n_total_face_vector_dofs + iT, n_velocity_unknowns + n_total_face_vector_dofs + iT, std::pow((cell->diam() / double(m_K + 1)), m_K + 2));
1692 
1693  // triplets_LTgg.emplace_back(n_total_face_vector_dofs + iT, n_total_face_vector_dofs + iT, local_LTgg[iT](n_local_bdry_vector_dofs, n_local_bdry_vector_dofs));
1694  // triplets_LTgg.emplace_back(n_velocity_unknowns + n_total_face_vector_dofs + iT, n_velocity_unknowns + n_total_face_vector_dofs + iT, local_LTgg[iT](n_local_bdry_vector_dofs + 1 + n_local_bdry_vector_dofs, n_local_bdry_vector_dofs + 1 + n_local_bdry_vector_dofs));
1695  }
1696 
1697  inv_LTll_LTlg.setFromTriplets(std::begin(triplets_inv_LTll_LTlg), std::end(triplets_inv_LTll_LTlg));
1698  LTgl.setFromTriplets(std::begin(triplets_LTgl), std::end(triplets_LTgl));
1699  LTgg.setFromTriplets(std::begin(triplets_LTgg), std::end(triplets_LTgg));
1700 
1701  for (size_t ibF = 0; ibF < mesh_ptr->n_b_faces(); ++ibF)
1702  {
1703  size_t iF = mesh_ptr->n_i_faces() + ibF;
1704  size_t velocity_offset = iF * n_local_face_vector_dofs;
1705  size_t magnetic_offset = n_velocity_unknowns + iF * n_local_face_vector_dofs;
1706  for (size_t i = 0; i < n_local_face_vector_dofs; ++i)
1707  {
1708  int vel_index = int(velocity_offset + i);
1709  int mag_index = int(magnetic_offset + i);
1710 
1711  if ((m_u_bc == 'D') || ((m_u_bc == 'H') && i % dim == 0))
1712  {
1713  LTgg.row(vel_index) *= 0.0;
1714  LTgg.col(vel_index) *= 0.0;
1715  LTgg.coeffRef(vel_index, vel_index) = 1.0;
1716 
1717  LTgl.row(vel_index) *= 0.0;
1718  inv_LTll_LTlg.col(vel_index) *= 0.0;
1719  }
1720  if ((m_b_bc == 'D') || ((m_b_bc == 'H') && i % dim == 0))
1721  {
1722  LTgg.row(mag_index) *= 0.0;
1723  LTgg.col(mag_index) *= 0.0;
1724  LTgg.coeffRef(mag_index, mag_index) = 1.0;
1725 
1726  LTgl.row(mag_index) *= 0.0;
1727  inv_LTll_LTlg.col(mag_index) *= 0.0;
1728  }
1729  }
1730  }
1731  LTgg.prune(0.0);
1732  inv_LTll_LTlg.prune(0.0);
1733  LTgl.prune(0.0);
1734 
1735  LTgg.row(n_unknowns) *= visc;
1736  LTgg.col(n_unknowns) *= visc;
1737  LTgg.row(n_unknowns + 1) *= diff;
1738  LTgg.col(n_unknowns + 1) *= diff;
1739 
1740  Eigen::SparseMatrix<double> condensed_SysMat = LTgg - LTgl * inv_LTll_LTlg;
1741 
1742  // Eigen::SparseLU<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>> condensed_solver;
1743  // condensed_solver.analyzePattern(condensed_SysMat);
1744  // condensed_solver.factorize(condensed_SysMat);
1745 
1746  Eigen::PardisoLU<Eigen::SparseMatrix<double>> condensed_solver;
1747  condensed_solver.compute(condensed_SysMat);
1748 
1749  Eigen::VectorXd RHS = rTg - LTgl * inv_LTll_rTl;
1750 
1751  Eigen::VectorXd condensed_terms = condensed_solver.solve(RHS);
1752  double residual_of_linear_system = (condensed_SysMat * condensed_terms - RHS).norm();
1753  // std::cout << "Residual of linear system = " << (condensed_SysMat * condensed_terms - RHS).norm() << "\n";
1754 
1755  Eigen::VectorXd implicit_terms = inv_LTll_rTl - inv_LTll_LTlg * condensed_terms;
1756 
1757  Eigen::VectorXd deltaU = Eigen::VectorXd::Zero(n_total_dofs);
1758 
1759  deltaU.segment(0, n_total_cell_vector_dofs) = implicit_terms.segment(0, n_total_cell_vector_dofs);
1760  deltaU.segment(n_total_cell_vector_dofs, n_total_face_vector_dofs) = condensed_terms.segment(0, n_total_face_vector_dofs);
1761 
1762  deltaU.segment(n_velocity_dofs, n_total_cell_vector_dofs) = implicit_terms.segment(n_implicit_velocity_dofs, n_total_cell_vector_dofs);
1763  deltaU.segment(n_velocity_dofs + n_total_cell_vector_dofs, n_total_face_vector_dofs) = condensed_terms.segment(n_velocity_unknowns, n_total_face_vector_dofs);
1764 
1765  for (size_t iT = 0; iT < n_cells; ++iT)
1766  {
1767  deltaU(n_total_cell_vector_dofs + n_total_face_vector_dofs + iT * n_local_cell_scalar_dofs) = condensed_terms(n_total_face_vector_dofs + iT);
1768  deltaU(n_velocity_dofs + n_total_cell_vector_dofs + n_total_face_vector_dofs + iT * n_local_cell_scalar_dofs) = condensed_terms(n_velocity_unknowns + n_total_face_vector_dofs + iT);
1769 
1770  deltaU.segment(n_total_cell_vector_dofs + n_total_face_vector_dofs + iT * n_local_cell_scalar_dofs + 1, n_local_cell_scalar_dofs - 1) = implicit_terms.segment(n_total_cell_vector_dofs + iT * (n_local_cell_scalar_dofs - 1), n_local_cell_scalar_dofs - 1);
1771  deltaU.segment(n_velocity_dofs + n_total_cell_vector_dofs + n_total_face_vector_dofs + iT * n_local_cell_scalar_dofs + 1, n_local_cell_scalar_dofs - 1) = implicit_terms.segment(n_implicit_velocity_dofs + n_total_cell_vector_dofs + iT * (n_local_cell_scalar_dofs - 1), n_local_cell_scalar_dofs - 1);
1772  }
1773 
1774  size_t internal_vector_dofs = n_total_cell_vector_dofs + n_total_face_vector_dofs - mesh_ptr->n_b_faces() * n_local_face_vector_dofs;
1775  size_t bdry_vector_dofs = mesh_ptr->n_b_faces() * n_local_face_vector_dofs;
1776 
1777  // set boundary dofs
1778 
1779  if (m_u_bc == 'D')
1780  {
1781  deltaU.segment(internal_vector_dofs, bdry_vector_dofs) = Eigen::VectorXd::Zero(bdry_vector_dofs); // Homogeneous Dirchlet BCs
1782  }
1783  else
1784  {
1785  assert(m_u_bc == 'H');
1786  for (size_t ibF = 0; ibF < mesh_ptr->n_b_faces(); ++ibF)
1787  {
1788  size_t offset = internal_vector_dofs + ibF * n_local_face_vector_dofs;
1789 
1790  // set normal components to zero
1791  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
1792  {
1793  deltaU(offset + dim * i) = 0.0; // homogenous bcs
1794  }
1795  }
1796  }
1797 
1798  if (m_b_bc == 'D')
1799  {
1800  deltaU.segment(n_total_cell_vector_dofs + n_total_face_vector_dofs + n_total_cell_scalar_dofs + internal_vector_dofs, bdry_vector_dofs) = Eigen::VectorXd::Zero(bdry_vector_dofs);
1801  }
1802  else
1803  {
1804  assert(m_b_bc == 'H');
1805  for (size_t ibF = 0; ibF < mesh_ptr->n_b_faces(); ++ibF)
1806  {
1807  size_t offset = n_total_cell_vector_dofs + n_total_face_vector_dofs + n_total_cell_scalar_dofs + internal_vector_dofs + ibF * n_local_face_vector_dofs;
1808 
1809  // set normal components to zero
1810  for (size_t i = 0; i < n_local_face_scalar_dofs; i++)
1811  {
1812  deltaU(offset + dim * i) = 0.0; // homogenous bcs
1813  }
1814  }
1815  }
1816 
1817  // SOL.set_values(SOL.asVectorXd() + relax * deltaU);
1818  SOL.set_values(SOL.asVectorXd() + deltaU);
1819  Eigen::VectorXd residual = Eigen::VectorXd::Zero(n_total_dofs);
1820 
1821  std::function<void(size_t, size_t)> compute_residual = [&](size_t start, size_t end) -> void
1822  {
1823  for (size_t iT = start; iT < end; ++iT)
1824  {
1825  Cell *cell = mesh_ptr->cell(iT);
1826  size_t n_local_faces = cell->n_faces();
1827  size_t n_local_bdry_vector_dofs = n_local_faces * n_local_face_vector_dofs;
1828  size_t n_local_vector_dofs = n_local_cell_vector_dofs + n_local_bdry_vector_dofs;
1829  size_t n_local_unknowns = 2 * (n_local_vector_dofs + n_local_cell_scalar_dofs);
1830  size_t local_magnetic_offset = n_local_vector_dofs + n_local_cell_scalar_dofs;
1831 
1832  Eigen::VectorXd uT = SOL.restr(iT);
1833  Eigen::VectorXd vector_terms = Eigen::VectorXd::Zero(2 * n_local_vector_dofs);
1834  vector_terms.head(n_local_vector_dofs) = uT.head(n_local_vector_dofs);
1835  vector_terms.tail(n_local_vector_dofs) = uT.segment(local_magnetic_offset, n_local_vector_dofs);
1836 
1837  Eigen::MatrixXd A = Eigen::MatrixXd::Zero(n_local_unknowns, n_local_unknowns);
1838 
1839  A.block(0, 0, n_local_vector_dofs, n_local_vector_dofs) = visc * AT[iT];
1840  A.block(0, n_local_vector_dofs, n_local_vector_dofs, n_local_cell_scalar_dofs) = DT[iT].transpose();
1841  A.block(n_local_vector_dofs, 0, n_local_cell_scalar_dofs, n_local_vector_dofs) = -DT[iT];
1842 
1843  A.block(local_magnetic_offset, local_magnetic_offset, n_local_vector_dofs, n_local_vector_dofs) = diff * AT[iT];
1844  A.block(local_magnetic_offset, local_magnetic_offset + n_local_vector_dofs, n_local_vector_dofs, n_local_cell_scalar_dofs) = DT[iT].transpose();
1845  A.block(local_magnetic_offset + n_local_vector_dofs, local_magnetic_offset, n_local_cell_scalar_dofs, n_local_vector_dofs) = -DT[iT];
1846 
1847  // Eigen::MatrixXd T = convective_term(cell, TTk[iT], vector_terms);
1848  Eigen::MatrixXd T = convective_term(cell, Tplus_k[iT], Tminus_k[iT], vector_terms);
1849  Eigen::MatrixXd BMAT = Eigen::MatrixXd::Zero(n_local_unknowns, n_local_unknowns);
1850  BMAT.block(0, 0, n_local_vector_dofs, n_local_vector_dofs) = T.topLeftCorner(n_local_vector_dofs, n_local_vector_dofs);
1851  BMAT.block(local_magnetic_offset, 0, n_local_vector_dofs, n_local_vector_dofs) = T.bottomLeftCorner(n_local_vector_dofs, n_local_vector_dofs);
1852  BMAT.block(0, local_magnetic_offset, n_local_vector_dofs, n_local_vector_dofs) = T.topRightCorner(n_local_vector_dofs, n_local_vector_dofs);
1853  BMAT.block(local_magnetic_offset, local_magnetic_offset, n_local_vector_dofs, n_local_vector_dofs) = T.bottomRightCorner(n_local_vector_dofs, n_local_vector_dofs);
1854 
1855  Eigen::MatrixXd GMAT = A + 0.5 * BMAT;
1856 
1857  Eigen::VectorXd G = GMAT * uT;
1858  G.segment(0, n_local_cell_vector_dofs) -= LTf[iT];
1859  G.segment(local_magnetic_offset, n_local_cell_vector_dofs) -= LTg[iT];
1860 
1861  residual.segment(iT * n_local_cell_vector_dofs, n_local_cell_vector_dofs) = G.head(n_local_cell_vector_dofs);
1862  // residual.segment(n_total_cell_vector_dofs + n_total_face_vector_dofs + iT * n_local_cell_scalar_dofs, n_local_cell_scalar_dofs) = G.segment(n_local_vector_dofs, n_local_cell_scalar_dofs);
1863 
1864  size_t global_magnetic_offset = n_total_cell_vector_dofs + n_total_face_vector_dofs + n_total_cell_scalar_dofs;
1865 
1866  residual.segment(global_magnetic_offset + iT * n_local_cell_vector_dofs, n_local_cell_vector_dofs) = G.segment(local_magnetic_offset, n_local_cell_vector_dofs);
1867  // residual.segment(global_magnetic_offset + n_total_cell_vector_dofs + n_total_face_vector_dofs + iT * n_local_cell_scalar_dofs, n_local_cell_scalar_dofs) = G.tail(n_local_cell_scalar_dofs);
1868 
1869  // for (size_t iTF = 0; iTF < n_local_faces; ++iTF)
1870  // {
1871  // if (cell->face(iTF)->is_boundary())
1872  // {
1873  // continue;
1874  // }
1875  // size_t iF = cell->face(iTF)->global_index();
1876  // residual.segment(n_total_cell_vector_dofs + iF * n_local_face_vector_dofs, n_local_face_vector_dofs) += G.segment(n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs, n_local_face_vector_dofs);
1877  // residual.segment(global_magnetic_offset + n_total_cell_vector_dofs + iF * n_local_face_vector_dofs, n_local_face_vector_dofs) += G.segment(local_magnetic_offset + n_local_cell_vector_dofs + iTF * n_local_face_vector_dofs, n_local_face_vector_dofs);
1878  // }
1879  }
1880  };
1881 
1882  // Running the local constructions in parallel
1883  parallel_for(n_cells, compute_residual, threading);
1884 
1885  residual_error = residual.norm() / exact_rhs_norm;
1886 
1887  ++i;
1888 
1889  std::cout << " It. " << i << ":";
1890  std::cout << " Res = " << residual_error << ", Solver error = " << residual_of_linear_system << "\n";
1891 
1892  if (i > 500)
1893  {
1894  std::cout << "Failed to converge\n";
1895  // std::cout << "Failed to converge, best residual = " << best_residual << "\n";
1896  // SOL.set_values(best_guess);
1897  break;
1898  }
1899  } while (residual_error > tol);
1900 
1901  solving_timer.stop();
1902  std::cout << " Solving time = " << double(solving_timer.elapsed().wall) * 1E-9 << "\n";
1903 
1904  return SOL;
1905 }
1906 
1907 #endif
Definition: elementquad.hpp:55
Definition: hybridcore.hpp:174
Definition: HHO_MHD.hpp:208
Class to describe a mesh.
Definition: MeshND.hpp:17
boost::multi_array< T, 2 > BasisQuad
type for bases evaluated on quadrature nodes
Definition: basis.hpp:60
std::function< T(const VectorRd &)> FType
type for function of point. T is the return type of the function
Definition: basis.hpp:54
Eigen::VectorXd integrate(const FType< T > &f, const BasisQuad< T > &B, const QuadratureRule &qr, size_t n_rows=0)
Computes the vector of integrals (f, phi_i)
Definition: basis.hpp:2591
Eigen::MatrixXd compute_gram_matrix(const boost::multi_array< VectorRd, 2 > &B1, const boost::multi_array< double, 2 > &B2, const QuadratureRule &qr)
Compute the Gram-like matrix given a family of vector-valued and one of scalar-valued functions by te...
Definition: basis.cpp:339
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:58
size_t L
Definition: HHO_DiffAdvecReac.hpp:46
size_t K
Definition: HHO_DiffAdvecReac.hpp:46
Eigen::VectorXd lagrange_restr(size_t iT) const
Definition: HHO_MHD.hpp:131
MHDModel(HybridCore &, size_t, size_t, char, char)
Definition: HHO_MHD.hpp:475
void set_values(Eigen::VectorXd values) const
Return the values as an Eigen vector.
Definition: HHO_MHD.hpp:63
const size_t get_face_deg() const
Return the face degree.
Definition: HHO_MHD.hpp:69
Eigen::VectorXd pressure_restr(size_t iT) const
Definition: HHO_MHD.hpp:107
SolutionVector operator-(const SolutionVector &b) const
Overloads the subtraction: subtracts the coefficients.
Definition: HHO_MHD.hpp:158
Eigen::VectorXd magnetic_restr(size_t iT) const
Definition: HHO_MHD.hpp:114
Eigen::VectorXd lagrange_values() const
Definition: HHO_MHD.hpp:87
Eigen::VectorXd pressure_values() const
Definition: HHO_MHD.hpp:77
SolutionVector(Eigen::VectorXd values, const Mesh *mesh_ptr, const size_t cell_deg, const size_t face_deg)
Definition: HHO_MHD.hpp:49
Eigen::VectorXd velocity_values() const
Definition: HHO_MHD.hpp:71
std::vector< double > compute_errors(SolutionVector intepolant, SolutionVector discrete, double visc, double diff)
Definition: HHO_MHD.hpp:268
Eigen::VectorXd magnetic_values() const
Definition: HHO_MHD.hpp:82
SolutionVector solve_with_static_cond(FType< VectorRd > f_source, FType< VectorRd > g_source, double visc, double diff, double tol, bool threading=true)
Definition: HHO_MHD.hpp:1326
SolutionVector operator+(const SolutionVector &b) const
Overloads the addition: adds the coefficients.
Definition: HHO_MHD.hpp:151
const size_t get_cell_deg() const
Return the cell degree.
Definition: HHO_MHD.hpp:66
double operator()(size_t index) const
Overloads the (): returns the corresponding coefficient.
Definition: HHO_MHD.hpp:165
Eigen::VectorXd velocity_restr(size_t iT) const
Definition: HHO_MHD.hpp:92
Eigen::VectorXd asVectorXd() const
Return the values as an Eigen vector.
Definition: HHO_MHD.hpp:61
SolutionVector global_interpolant(FType< VectorRd > velocity, FType< double > pressure, FType< VectorRd > magnetic)
Definition: HHO_MHD.hpp:446
Eigen::VectorXd restr(size_t iT) const
Definition: HHO_MHD.hpp:138
const boost::multi_array< VectorRd, 2 > & get_dphiT_quadF(size_t ilF) const
Returns values of gradients of cell basis functions at face quadrature nodes, for face with local num...
Definition: elementquad.hpp:103
const boost::multi_array< double, 2 > & get_phiT_quadF(size_t ilF) const
Returns values of cell basis functions at face quadrature nodes, for face with local number ilF.
Definition: elementquad.hpp:91
const boost::multi_array< VectorRd, 2 > & get_dphiT_quadT() const
Returns values of gradients of cell basis functions at cell quadrature nodes.
Definition: elementquad.hpp:85
const size_t DimPoly< Face >(const int m)
Compute the size of the basis of 2-variate polynomials up to degree m.
Definition: hybridcore.hpp:68
const boost::multi_array< double, 2 > get_phiF_quadF(size_t ilF) const
Returns values of face basis functions at face quadrature nodes, for face with local number ilF.
Definition: elementquad.hpp:97
const QuadratureRule & get_quadF(size_t ilF) const
Returns quadrature rules on face with local number ilF.
Definition: elementquad.hpp:73
const boost::multi_array< double, 2 > & get_phiT_quadT() const
Returns values of cell basis functions at cell quadrature nodes.
Definition: elementquad.hpp:79
const size_t DimPoly< Cell >(const int m)
Compute the size of the basis of 3-variate polynomials up to degree m.
Definition: hybridcore.hpp:61
const QuadratureRule & get_quadT() const
Returns quadrature rules in cell.
Definition: elementquad.hpp:67
const Mesh * get_mesh() const
Returns a pointer to the mesh.
Definition: hybridcore.hpp:198
std::size_t n_faces() const
number of faces in the mesh.
Definition: MeshND.hpp:59
std::size_t n_cells() const
number of cells in the mesh.
Definition: MeshND.hpp:60
std::size_t n_b_faces() const
number of boundary faces in the mesh.
Definition: MeshND.hpp:77
Cell< dimension > * cell(std::size_t index) const
get a constant pointer to a cell using its global index
Definition: MeshND.hpp:197
std::size_t n_i_faces() const
number of internal faces in the mesh.
Definition: MeshND.hpp:82
std::vector< QuadratureNode > QuadratureRule
Definition: quadraturerule.hpp:61
Definition: ddr-magnetostatics.hpp:40
MeshND::VectorRd< 2 > VectorRd
Definition: Mesh2D.hpp:14
MeshND::Cell< 2 > Cell
Definition: Mesh2D.hpp:13
Definition: HHO_MHD.hpp:43