libMesh
hdg_problem.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 #include "hdg_problem.h"
19 
20 #include "libmesh/mesh_base.h"
21 #include "libmesh/dof_map.h"
22 #include "libmesh/static_condensation.h"
23 #include "libmesh/quadrature.h"
24 #include "libmesh/boundary_info.h"
25 #include "libmesh/numeric_vector.h"
26 #include "libmesh/sparse_matrix.h"
27 
28 namespace libMesh
29 {
30 // compute a solution indexable at quadrature points composed from the local degree of freedom
31 // solution vector and associated basis functions
32 template <typename SolnType, typename PhiType>
33 void
34 compute_qp_soln(std::vector<SolnType> & qp_vec,
35  const unsigned int n_qps,
36  const std::vector<std::vector<PhiType>> & phi,
37  const std::vector<Number> & dof_values)
38 {
39  libmesh_assert(dof_values.size() == phi.size());
40  qp_vec.resize(n_qps);
41  for (auto & val : qp_vec)
42  val = {};
43  for (const auto i : index_range(phi))
44  {
45  const auto & qp_phis = phi[i];
46  libmesh_assert(qp_phis.size() == n_qps);
47  const auto sol = dof_values[i];
48  for (const auto qp : make_range(n_qps))
49  qp_vec[qp] += qp_phis[qp] * sol;
50  }
51 }
52 
53 HDGProblem::HDGProblem(const Real nu_in, const bool cavity_in)
54  : nu(nu_in),
55  cavity(cavity_in),
56  u_true_soln(nu, cavity),
57  v_true_soln(nu, cavity),
58  p_true_soln(cavity)
59 {
60 }
61 
62 HDGProblem::~HDGProblem() = default;
63 
64 void
66 {
67  // Attach quadrature rules for the FE objects that we will reinit within the element "volume"
68  vector_fe->attach_quadrature_rule(qrule.get());
69  scalar_fe->attach_quadrature_rule(qrule.get());
70 
71  // Attach quadrature rules for the FE objects that we will reinit on the element faces
72  vector_fe_face->attach_quadrature_rule(qface.get());
73  scalar_fe_face->attach_quadrature_rule(qface.get());
74  lm_fe_face->attach_quadrature_rule(qface.get());
75 
76  // pre-request our required volumetric data
77  JxW = &vector_fe->get_JxW();
78  q_point = &vector_fe->get_xyz();
79  vector_phi = &vector_fe->get_phi();
80  scalar_phi = &scalar_fe->get_phi();
81  grad_scalar_phi = &scalar_fe->get_dphi();
82  div_vector_phi = &vector_fe->get_div_phi();
83 
84  // pre-request our required element face data
85  vector_phi_face = &vector_fe_face->get_phi();
86  scalar_phi_face = &scalar_fe_face->get_phi();
87  lm_phi_face = &lm_fe_face->get_phi();
88  JxW_face = &vector_fe_face->get_JxW();
89  qface_point = &vector_fe_face->get_xyz();
90  normals = &vector_fe_face->get_normals();
91 
92  const auto & bnd_info = mesh->get_boundary_info();
93  left_bnd = bnd_info.get_id_by_name("left");
94  top_bnd = bnd_info.get_id_by_name("top");
95  right_bnd = bnd_info.get_id_by_name("right");
96  bottom_bnd = bnd_info.get_id_by_name("bottom");
101  global_lm_n_dofs = cavity ? 1 : 0;
102 }
103 
104 void
106  const std::vector<Real> & JxW_local,
107  const std::vector<std::vector<Real>> & phi,
108  const std::vector<Number> & sol,
109  const std::size_t n_dofs,
111 {
112  for (const auto qp : make_range(quadrature.n_points()))
113  for (const auto i : make_range(n_dofs))
114  R(i) -= JxW_local[qp] * phi[i][qp] * sol[qp];
115 }
116 
117 void
119  const std::vector<Real> & JxW_local,
120  const std::vector<std::vector<Real>> & phi,
121  const std::size_t n_dofs,
123 {
124  for (const auto qp : make_range(quadrature.n_points()))
125  for (const auto i : make_range(n_dofs))
126  for (const auto j : make_range(n_dofs))
127  J(i, j) -= JxW_local[qp] * phi[i][qp] * phi[j][qp];
128 }
129 
130 void
131 HDGProblem::compute_stress(const std::vector<Gradient> & vel_gradient,
132  const unsigned int vel_component,
133  std::vector<Gradient> & sigma)
134 {
135  sigma.resize(qrule->n_points());
136  for (const auto qp : make_range(qrule->n_points()))
137  {
138  Gradient qp_p;
139  qp_p(vel_component) = p_sol[qp];
140  sigma[qp] = nu * vel_gradient[qp] - qp_p;
141  }
142 }
143 
144 void
145 HDGProblem::vector_volume_residual(const std::vector<Gradient> & vector_sol,
146  const std::vector<Number> & scalar_sol,
148 {
149  for (const auto qp : make_range(qrule->n_points()))
150  for (const auto i : make_range(vector_n_dofs))
151  {
152  // Vector equation dependence on vector dofs
153  R(i) += (*JxW)[qp] * ((*vector_phi)[i][qp] * vector_sol[qp]);
154 
155  // Vector equation dependence on scalar dofs
156  R(i) += (*JxW)[qp] * ((*div_vector_phi)[i][qp] * scalar_sol[qp]);
157  }
158 }
159 
160 void
162 {
163  for (const auto qp : make_range(qrule->n_points()))
164  for (const auto i : make_range(vector_n_dofs))
165  {
166  // Vector equation dependence on vector dofs
167  for (const auto j : make_range(vector_n_dofs))
168  Jqq(i, j) += (*JxW)[qp] * ((*vector_phi)[i][qp] * (*vector_phi)[j][qp]);
169 
170  // Vector equation dependence on scalar dofs
171  for (const auto j : make_range(scalar_n_dofs))
172  Jqs(i, j) += (*JxW)[qp] * ((*div_vector_phi)[i][qp] * (*scalar_phi)[j][qp]);
173  }
174 }
175 
177 HDGProblem::vel_cross_vel_residual(const std::vector<Number> & u_sol_local,
178  const std::vector<Number> & v_sol_local,
179  const unsigned int qp,
180  const unsigned int vel_component) const
181 {
182  const NumberVectorValue U(u_sol_local[qp], v_sol_local[qp]);
183  return U * U(vel_component);
184 }
185 
187 HDGProblem::vel_cross_vel_jacobian(const std::vector<Number> & u_sol_local,
188  const std::vector<Number> & v_sol_local,
189  const unsigned int qp,
190  const unsigned int vel_component,
191  const unsigned int vel_j_component,
192  const std::vector<std::vector<Real>> & phi,
193  const unsigned int j) const
194 {
195  const NumberVectorValue U(u_sol_local[qp], v_sol_local[qp]);
196  NumberVectorValue vector_phi_local;
197  vector_phi_local(vel_j_component) = phi[j][qp];
198  auto ret = vector_phi_local * U(vel_component);
199  if (vel_component == vel_j_component)
200  ret += U * phi[j][qp];
201  return ret;
202 }
203 
204 void
205 HDGProblem::scalar_volume_residual(const std::vector<Gradient> & vel_gradient,
206  const unsigned int vel_component,
207  std::vector<Gradient> & sigma,
209 {
210  compute_stress(vel_gradient, vel_component, sigma);
211  const auto & mms_info = vel_component == 0 ? static_cast<const ExactSoln &>(u_true_soln)
212  : static_cast<const ExactSoln &>(v_true_soln);
213  for (const auto qp : make_range(qrule->n_points()))
214  {
215  const auto vel_cross_vel = vel_cross_vel_residual(u_sol, v_sol, qp, vel_component);
216 
217  // Prepare forcing function
218  Real f = 0;
219  if (mms)
220  f = mms_info.forcing((*q_point)[qp]);
221 
222  for (const auto i : make_range(scalar_n_dofs))
223  {
224  // Scalar equation dependence on vector and pressure dofs
225  R(i) += (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * sigma[qp]);
226 
227  // Scalar equation dependence on scalar dofs
228  R(i) -= (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * vel_cross_vel);
229 
230  if (mms)
231  // Scalar equation RHS
232  R(i) -= (*JxW)[qp] * (*scalar_phi)[i][qp] * f;
233  }
234  }
235 }
236 
237 void
238 HDGProblem::scalar_volume_jacobian(const unsigned int vel_component,
239  DenseMatrix<Number> & Jsq,
240  DenseMatrix<Number> & Jsp,
241  DenseMatrix<Number> & Jsu,
242  DenseMatrix<Number> & Jsv)
243 {
244  for (const auto qp : make_range(qrule->n_points()))
245  for (const auto i : make_range(scalar_n_dofs))
246  {
247  // Scalar equation dependence on vector dofs
248  for (const auto j : make_range(vector_n_dofs))
249  Jsq(i, j) += (*JxW)[qp] * nu * ((*grad_scalar_phi)[i][qp] * (*vector_phi)[j][qp]);
250 
251  // Scalar equation dependence on pressure dofs
252  for (const auto j : make_range(p_n_dofs))
253  {
254  Gradient p_phi;
255  p_phi(vel_component) = (*scalar_phi)[j][qp];
256  Jsp(i, j) -= (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * p_phi);
257  }
258 
259  // Scalar equation dependence on scalar dofs
260  for (const auto j : make_range(scalar_n_dofs))
261  {
262  // derivatives wrt 0th component
263  {
264  const auto vel_cross_vel =
265  vel_cross_vel_jacobian(u_sol, v_sol, qp, vel_component, 0, (*scalar_phi), j);
266  Jsu(i, j) -= (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * vel_cross_vel);
267  }
268  // derivatives wrt 1th component
269  {
270  const auto vel_cross_vel =
271  vel_cross_vel_jacobian(u_sol, v_sol, qp, vel_component, 1, (*scalar_phi), j);
272  Jsv(i, j) -= (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * vel_cross_vel);
273  }
274  }
275  }
276 }
277 
278 void
280 {
281  for (const auto qp : make_range(qrule->n_points()))
282  {
283  // Prepare forcing function
284  Real f = 0;
285  if (mms)
286  f = p_true_soln.forcing((*q_point)[qp]);
287 
288  const Gradient vel(u_sol[qp], v_sol[qp]);
289  for (const auto i : make_range(p_n_dofs))
290  {
291  Rp(i) -= (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * vel);
292 
293  if (mms)
294  // Pressure equation RHS
295  Rp(i) -= (*JxW)[qp] * (*scalar_phi)[i][qp] * f;
296 
297  if (cavity)
298  Rp(i) -= (*JxW)[qp] * (*scalar_phi)[i][qp] * global_lm_dof_value;
299  }
300 
301  if (cavity)
302  Rglm(0) -= (*JxW)[qp] * p_sol[qp];
303  }
304 }
305 
306 void
308  DenseMatrix<Number> & Jpv,
309  DenseMatrix<Number> & Jpglm,
310  DenseMatrix<Number> & Jglmp)
311 {
312  for (const auto qp : make_range(qrule->n_points()))
313  {
314  for (const auto i : make_range(p_n_dofs))
315  {
316  for (const auto j : make_range(scalar_n_dofs))
317  {
318  {
319  const Gradient phi((*scalar_phi)[j][qp], Number(0));
320  Jpu(i, j) -= (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * phi);
321  }
322  {
323  const Gradient phi(Number(0), (*scalar_phi)[j][qp]);
324  Jpv(i, j) -= (*JxW)[qp] * ((*grad_scalar_phi)[i][qp] * phi);
325  }
326  }
327  if (cavity)
328  Jpglm(i, 0) -= (*JxW)[qp] * (*scalar_phi)[i][qp];
329  }
330 
331  if (cavity)
332  {
334  for (const auto j : make_range(p_n_dofs))
335  Jglmp(0, j) -= (*JxW)[qp] * (*scalar_phi)[j][qp];
336  }
337  }
338 }
339 
340 void
342 {
343  for (const auto qp : make_range(qface->n_points()))
344  {
345  const Gradient vel(lm_u_sol[qp], lm_v_sol[qp]);
346  const auto vdotn = vel * (*normals)[qp];
347  for (const auto i : make_range(p_n_dofs))
348  R(i) += (*JxW_face)[qp] * vdotn * (*scalar_phi_face)[i][qp];
349  }
350 }
351 
352 void
354 {
355  for (const auto qp : make_range(qface->n_points()))
356  for (const auto i : make_range(p_n_dofs))
357  for (const auto j : make_range(lm_n_dofs))
358  {
359  {
360  const Gradient phi((*lm_phi_face)[j][qp], Number(0));
361  Jplm_u(i, j) += (*JxW_face)[qp] * phi * (*normals)[qp] * (*scalar_phi_face)[i][qp];
362  }
363  {
364  const Gradient phi(Number(0), (*lm_phi_face)[j][qp]);
365  Jplm_v(i, j) += (*JxW_face)[qp] * phi * (*normals)[qp] * (*scalar_phi_face)[i][qp];
366  }
367  }
368 }
369 
371 HDGProblem::get_dirichlet_velocity(const unsigned int qp) const
372 {
373  if (mms)
375 
376  if (cavity)
377  {
378  if (current_bnd == top_bnd)
379  return RealVectorValue(1, 0);
380  else
381  return RealVectorValue(0, 0);
382  }
383  else // channel flow case
384  {
386 
387  if (current_bnd == left_bnd)
388  return RealVectorValue(1, 0);
389 
390  else
391  return RealVectorValue(0, 0);
392  }
393 }
394 
395 void
397 {
398  for (const auto qp : make_range(qface->n_points()))
399  {
400  const auto dirichlet_velocity = get_dirichlet_velocity(qp);
401  const auto vdotn = dirichlet_velocity * (*normals)[qp];
402  for (const auto i : make_range(p_n_dofs))
403  R(i) += (*JxW_face)[qp] * vdotn * (*scalar_phi_face)[i][qp];
404  }
405 }
406 
407 void
408 HDGProblem::vector_dirichlet_residual(const unsigned int vel_component, DenseVector<Number> & R)
409 {
410  for (const auto qp : make_range(qface->n_points()))
411  {
412  const auto scalar_value = get_dirichlet_velocity(qp)(vel_component);
413 
414  // External boundary -> Dirichlet faces -> Vector equation RHS
415  for (const auto i : make_range(vector_n_dofs))
416  R(i) -= (*JxW_face)[qp] * ((*vector_phi_face)[i][qp] * (*normals)[qp]) * scalar_value;
417  }
418 }
419 
420 void
421 HDGProblem::vector_face_residual(const std::vector<Number> & lm_sol, DenseVector<Number> & R)
422 {
423  for (const auto qp : make_range(qface->n_points()))
424  // Vector equation dependence on LM dofs
425  for (const auto i : make_range(vector_n_dofs))
426  R(i) -= (*JxW_face)[qp] * ((*vector_phi_face)[i][qp] * (*normals)[qp]) * lm_sol[qp];
427 }
428 
429 void
431 {
432  for (const auto qp : make_range(qface->n_points()))
433  // Vector equation dependence on LM dofs
434  for (const auto i : make_range(vector_n_dofs))
435  for (const auto j : make_range(lm_n_dofs))
436  Jqlm(i, j) -=
437  (*JxW_face)[qp] * ((*vector_phi_face)[i][qp] * (*normals)[qp]) * (*lm_phi_face)[j][qp];
438 }
439 
440 void
441 HDGProblem::scalar_dirichlet_residual(const std::vector<Gradient> & vector_sol,
442  const std::vector<Number> & scalar_sol,
443  const unsigned int vel_component,
445 {
446  for (const auto qp : make_range(qface->n_points()))
447  {
448  Gradient qp_p;
449  qp_p(vel_component) = p_sol[qp];
450 
451  const auto dirichlet_velocity = get_dirichlet_velocity(qp);
452  const auto scalar_value = dirichlet_velocity(vel_component);
453  ;
454 
455  for (const auto i : make_range(scalar_n_dofs))
456  {
457  // vector
458  R(i) -= (*JxW_face)[qp] * nu * (*scalar_phi_face)[i][qp] * (vector_sol[qp] * (*normals)[qp]);
459 
460  // pressure
461  R(i) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * (qp_p * (*normals)[qp]);
462 
463  // scalar from stabilization term
464  R(i) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * tau * scalar_sol[qp] * (*normals)[qp] *
465  (*normals)[qp];
466 
467  // dirichlet lm from stabilization term
468  R(i) -= (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * tau * scalar_value * (*normals)[qp] *
469  (*normals)[qp];
470 
471  // dirichlet lm from advection term
472  R(i) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * (dirichlet_velocity * (*normals)[qp]) *
473  scalar_value;
474  }
475  }
476 }
477 
478 void
479 HDGProblem::scalar_dirichlet_jacobian(const unsigned int vel_component,
480  DenseMatrix<Number> & Jsq,
481  DenseMatrix<Number> & Jsp,
482  DenseMatrix<Number> & Jss)
483 {
484  for (const auto qp : make_range(qface->n_points()))
485  for (const auto i : make_range(scalar_n_dofs))
486  {
487  for (const auto j : make_range(vector_n_dofs))
488  Jsq(i, j) -= (*JxW_face)[qp] * nu * (*scalar_phi_face)[i][qp] *
489  ((*vector_phi_face)[j][qp] * (*normals)[qp]);
490 
491  for (const auto j : make_range(p_n_dofs))
492  {
493  Gradient p_phi;
494  p_phi(vel_component) = (*scalar_phi_face)[j][qp];
495  // pressure
496  Jsp(i, j) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * (p_phi * (*normals)[qp]);
497  }
498 
499  for (const auto j : make_range(scalar_n_dofs))
500  Jss(i, j) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * tau * (*scalar_phi_face)[j][qp] *
501  (*normals)[qp] * (*normals)[qp];
502  }
503 }
504 
505 void
506 HDGProblem::scalar_face_residual(const std::vector<Gradient> & vector_sol,
507  const std::vector<Number> & scalar_sol,
508  const std::vector<Number> & lm_sol,
509  const unsigned int vel_component,
511 {
512  for (const auto qp : make_range(qface->n_points()))
513  {
514  Gradient qp_p;
515  qp_p(vel_component) = p_sol[qp];
516  const auto vel_cross_vel = vel_cross_vel_residual(lm_u_sol, lm_v_sol, qp, vel_component);
517 
518  for (const auto i : make_range(scalar_n_dofs))
519  {
520  if (neigh)
521  {
522  // vector
523  R(i) -=
524  (*JxW_face)[qp] * nu * (*scalar_phi_face)[i][qp] * (vector_sol[qp] * (*normals)[qp]);
525 
526  // pressure
527  R(i) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * (qp_p * (*normals)[qp]);
528 
529  // scalar from stabilization term
530  R(i) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * tau * scalar_sol[qp] *
531  (*normals)[qp] * (*normals)[qp];
532 
533  // lm from stabilization term
534  R(i) -= (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * tau * lm_sol[qp] * (*normals)[qp] *
535  (*normals)[qp];
536  }
537  else
538  libmesh_assert_msg(current_bnd == right_bnd,
539  "This method should only be called with an outflow boundary. "
540  "Dirichlet boundaries should call a different routine");
541 
542  // lm from convection term
543  R(i) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * vel_cross_vel * (*normals)[qp];
544  }
545  }
546 }
547 
548 void
549 HDGProblem::scalar_face_jacobian(const unsigned int vel_component,
550  DenseMatrix<Number> & Jsq,
551  DenseMatrix<Number> & Jsp,
552  DenseMatrix<Number> & Jss,
553  DenseMatrix<Number> & Jslm,
554  DenseMatrix<Number> & Js_lmu,
555  DenseMatrix<Number> & Js_lmv)
556 {
557  for (const auto qp : make_range(qface->n_points()))
558  for (const auto i : make_range(scalar_n_dofs))
559  {
560  if (neigh)
561  {
562  for (const auto j : make_range(vector_n_dofs))
563  Jsq(i, j) -= (*JxW_face)[qp] * nu * (*scalar_phi_face)[i][qp] *
564  ((*vector_phi_face)[j][qp] * (*normals)[qp]);
565 
566  for (const auto j : make_range(p_n_dofs))
567  {
568  Gradient p_phi;
569  p_phi(vel_component) = (*scalar_phi_face)[j][qp];
570  // pressure
571  Jsp(i, j) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * (p_phi * (*normals)[qp]);
572  }
573 
574  for (const auto j : make_range(scalar_n_dofs))
575  Jss(i, j) += (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * tau *
576  (*scalar_phi_face)[j][qp] * (*normals)[qp] * (*normals)[qp];
577  }
578  else
579  libmesh_assert_msg(current_bnd == right_bnd,
580  "This method should only be called with an outflow boundary. "
581  "Dirichlet boundaries should call a different routine");
582 
583  for (const auto j : make_range(lm_n_dofs))
584  {
585  if (neigh)
586  // from stabilization term
587  Jslm(i, j) -= (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * tau * (*lm_phi_face)[j][qp] *
588  (*normals)[qp] * (*normals)[qp];
589 
590  //
591  // from convection term
592  //
593 
594  // derivatives wrt 0th component
595  {
596  const auto vel_cross_vel =
597  vel_cross_vel_jacobian(lm_u_sol, lm_v_sol, qp, vel_component, 0, (*lm_phi_face), j);
598  Js_lmu(i, j) +=
599  (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * vel_cross_vel * (*normals)[qp];
600  }
601  // derivatives wrt 1th component
602  {
603  const auto vel_cross_vel =
604  vel_cross_vel_jacobian(lm_u_sol, lm_v_sol, qp, vel_component, 1, (*lm_phi_face), j);
605  Js_lmv(i, j) +=
606  (*JxW_face)[qp] * (*scalar_phi_face)[i][qp] * vel_cross_vel * (*normals)[qp];
607  }
608  }
609  }
610 }
611 
612 void
613 HDGProblem::lm_face_residual(const std::vector<Gradient> & vector_sol,
614  const std::vector<Number> & scalar_sol,
615  const std::vector<Number> & lm_sol,
616  const unsigned int vel_component,
618 {
619  for (const auto qp : make_range(qface->n_points()))
620  {
621  Gradient qp_p;
622  qp_p(vel_component) = p_sol[qp];
623  const auto vel_cross_vel = vel_cross_vel_residual(lm_u_sol, lm_v_sol, qp, vel_component);
624 
625  for (const auto i : make_range(lm_n_dofs))
626  {
627  // vector
628  R(i) -= (*JxW_face)[qp] * nu * (*lm_phi_face)[i][qp] * (vector_sol[qp] * (*normals)[qp]);
629 
630  // pressure
631  R(i) += (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * (qp_p * (*normals)[qp]);
632 
633  // scalar from stabilization term
634  R(i) += (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * tau * scalar_sol[qp] * (*normals)[qp] *
635  (*normals)[qp];
636 
637  // lm from stabilization term
638  R(i) -= (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * tau * lm_sol[qp] * (*normals)[qp] *
639  (*normals)[qp];
640 
641  // If we are an internal face we add the convective term. On the outflow boundary we do not
642  // zero out the convection term, e.g. we are going to set q + p + tau * (u - u_hat) to zero
643  if (neigh)
644  // lm from convection term
645  R(i) += (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * vel_cross_vel * (*normals)[qp];
646  else
647  libmesh_assert_msg(current_bnd == right_bnd,
648  "This method should only be called with an outflow boundary. "
649  "Dirichlet boundaries should call a different routine");
650  }
651  }
652 }
653 
654 void
655 HDGProblem::lm_face_jacobian(const unsigned int vel_component,
656  DenseMatrix<Number> & Jlmq,
657  DenseMatrix<Number> & Jlmp,
658  DenseMatrix<Number> & Jlms,
659  DenseMatrix<Number> & Jlmlm,
660  DenseMatrix<Number> & Jlm_lmu,
661  DenseMatrix<Number> & Jlm_lmv)
662 {
663  for (const auto qp : make_range(qface->n_points()))
664  for (const auto i : make_range(lm_n_dofs))
665  {
666  for (const auto j : make_range(vector_n_dofs))
667  Jlmq(i, j) -= (*JxW_face)[qp] * nu * (*lm_phi_face)[i][qp] *
668  ((*vector_phi_face)[j][qp] * (*normals)[qp]);
669 
670  for (const auto j : make_range(p_n_dofs))
671  {
672  Gradient p_phi;
673  p_phi(vel_component) = (*scalar_phi_face)[j][qp];
674  Jlmp(i, j) += (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * (p_phi * (*normals)[qp]);
675  }
676 
677  for (const auto j : make_range(scalar_n_dofs))
678  Jlms(i, j) += (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * tau * (*scalar_phi_face)[j][qp] *
679  (*normals)[qp] * (*normals)[qp];
680 
681  for (const auto j : make_range(lm_n_dofs))
682  {
683  // from stabilization term
684  Jlmlm(i, j) -= (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * tau * (*lm_phi_face)[j][qp] *
685  (*normals)[qp] * (*normals)[qp];
686  if (neigh)
687  {
688  // derivatives wrt 0th component
689  {
690  const auto vel_cross_vel =
691  vel_cross_vel_jacobian(lm_u_sol, lm_v_sol, qp, vel_component, 0, (*lm_phi_face), j);
692  Jlm_lmu(i, j) +=
693  (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * vel_cross_vel * (*normals)[qp];
694  }
695  // derivatives wrt 1th component
696  {
697  const auto vel_cross_vel =
698  vel_cross_vel_jacobian(lm_u_sol, lm_v_sol, qp, vel_component, 1, (*lm_phi_face), j);
699  Jlm_lmv(i, j) +=
700  (*JxW_face)[qp] * (*lm_phi_face)[i][qp] * vel_cross_vel * (*normals)[qp];
701  }
702  }
703  else
704  libmesh_assert_msg(current_bnd == right_bnd,
705  "This method should only be called with an outflow boundary. "
706  "Dirichlet boundaries should call a different routine");
707  }
708  }
709 }
710 
711 void
715 {
716  R.zero();
717 
718  const auto u_num = S.variable_number("vel_x");
719  const auto v_num = S.variable_number("vel_y");
720  const auto qu_num = S.variable_number("qu");
721  const auto qv_num = S.variable_number("qv");
722  const auto lm_u_num = S.variable_number("lm_u");
723  const auto lm_v_num = S.variable_number("lm_v");
724  const auto p_num = S.variable_number("pressure");
725  const auto global_lm_num = cavity ? S.variable_number("global_lm") : invalid_uint;
726 
727  auto & qu_dof_indices = dof_indices[qu_num];
728  auto & u_dof_indices = dof_indices[u_num];
729  auto & lm_u_dof_indices = dof_indices[lm_u_num];
730  auto & qv_dof_indices = dof_indices[qv_num];
731  auto & v_dof_indices = dof_indices[v_num];
732  auto & lm_v_dof_indices = dof_indices[lm_v_num];
733  auto & p_dof_indices = dof_indices[p_num];
734  static std::vector<dof_id_type> dummy_indices;
735  auto & global_lm_dof_indices = cavity ? dof_indices[global_lm_num] : dummy_indices;
736 
737  std::vector<boundary_id_type> boundary_ids;
738  const auto & boundary_info = mesh->get_boundary_info();
739  DenseVector<Number> Rqu, Rqv, Ru, Rv, Rlm_u, Rlm_v, Rp, Rglm;
740 
741  for (const auto & elem : mesh->active_local_element_ptr_range())
742  {
743  // Retrive our dof indices for all fields
744  dof_map->dof_indices(elem, qu_dof_indices, qu_num);
745  dof_map->dof_indices(elem, u_dof_indices, u_num);
746  dof_map->dof_indices(elem, lm_u_dof_indices, lm_u_num);
747  dof_map->dof_indices(elem, qv_dof_indices, qv_num);
748  dof_map->dof_indices(elem, v_dof_indices, v_num);
749  dof_map->dof_indices(elem, lm_v_dof_indices, lm_v_num);
750  dof_map->dof_indices(elem, p_dof_indices, p_num);
751  if (cavity)
752  {
753  dof_map->dof_indices(elem, global_lm_dof_indices, global_lm_num);
754  libmesh_assert(global_lm_dof_indices.size() == 1);
755  }
756 
757  vector_n_dofs = qu_dof_indices.size();
758  scalar_n_dofs = u_dof_indices.size();
759  lm_n_dofs = lm_u_dof_indices.size();
760  p_n_dofs = p_dof_indices.size();
762  Rqu.resize(vector_n_dofs);
763  Rqv.resize(vector_n_dofs);
764  Ru.resize(scalar_n_dofs);
765  Rv.resize(scalar_n_dofs);
766  Rlm_u.resize(lm_n_dofs);
767  Rlm_v.resize(lm_n_dofs);
768  Rp.resize(p_n_dofs);
769  Rglm.resize(global_lm_dof_indices.size());
770 
771  // Reinit our volume FE objects
772  vector_fe->reinit(elem);
773  scalar_fe->reinit(elem);
774 
775  libmesh_assert_equal_to(vector_n_dofs, vector_phi->size());
776  libmesh_assert_equal_to(scalar_n_dofs, scalar_phi->size());
777 
778  // Get our local element dof values
779  X.get(qu_dof_indices, qu_dof_values);
780  X.get(u_dof_indices, u_dof_values);
781  X.get(lm_u_dof_indices, lm_u_dof_values);
782  X.get(qv_dof_indices, qv_dof_values);
783  X.get(v_dof_indices, v_dof_values);
784  X.get(lm_v_dof_indices, lm_v_dof_values);
785  X.get(p_dof_indices, p_dof_values);
786  if (cavity)
787  global_lm_dof_value = X(global_lm_dof_indices[0]);
788 
789  // Compute volumetric local qp solutions
795 
796  //
797  // compute volumetric residuals and Jacobians
798  //
799 
800  // qu and u
803 
804  // qv and v
807 
808  // p
809  pressure_volume_residual(Rp, Rglm);
810 
811  for (auto side : elem->side_index_range())
812  {
813  neigh = elem->neighbor_ptr(side);
814 
815  // Reinit our face FE objects
816  vector_fe_face->reinit(elem, side);
817  scalar_fe_face->reinit(elem, side);
818  lm_fe_face->reinit(elem, side);
819 
820  // Compute face local qp solutions
828 
829  if (neigh == nullptr)
830  {
831  boundary_info.boundary_ids(elem, side, boundary_ids);
832  libmesh_assert(boundary_ids.size() == 1);
833  current_bnd = boundary_ids[0];
834  if (cavity || (current_bnd != right_bnd))
835  {
836  // qu, u
839 
840  // qv, v
843 
844  // p
846 
847  // Set the LMs on these Dirichlet boundary faces to 0
850 
851  continue;
852  }
853  }
854 
855  //
856  // if we got here, then we are on an internal face or an outlet face
857  //
858 
859  // qu, u, lm_u
862  lm_face_residual(qu_sol, u_sol, lm_u_sol, 0, Rlm_u);
863 
864  // qv, v, lm_v
867  lm_face_residual(qv_sol, v_sol, lm_v_sol, 1, Rlm_v);
868 
869  // p
871  }
872 
873  R.add_vector(Rqu, qu_dof_indices);
874  R.add_vector(Rqv, qv_dof_indices);
875  R.add_vector(Ru, u_dof_indices);
876  R.add_vector(Rv, v_dof_indices);
877  R.add_vector(Rlm_u, lm_u_dof_indices);
878  R.add_vector(Rlm_v, lm_v_dof_indices);
879  R.add_vector(Rp, p_dof_indices);
880  if (cavity)
881  R.add_vector(Rglm, global_lm_dof_indices);
882  }
883 }
884 
885 void
887  const unsigned int ivar_num,
888  const unsigned int jvar_num,
889  const DenseMatrix<Number> & elem_mat)
890 {
892  elem_mat, libmesh_map_find(dof_indices, ivar_num), libmesh_map_find(dof_indices, jvar_num));
893 }
894 
895 void
899 {
900  auto * J = &S.get_system_matrix();
901 
902  if (!sc)
903  // PETSc requires that the Jacobian matrix have a diagonal for some types of preconditioners
904  for (const auto i : make_range(J->row_start(), J->row_stop()))
905  J->add(i, i, 0);
906 
907  const auto u_num = S.variable_number("vel_x");
908  const auto v_num = S.variable_number("vel_y");
909  const auto qu_num = S.variable_number("qu");
910  const auto qv_num = S.variable_number("qv");
911  const auto lm_u_num = S.variable_number("lm_u");
912  const auto lm_v_num = S.variable_number("lm_v");
913  const auto p_num = S.variable_number("pressure");
914  const auto global_lm_num = cavity ? S.variable_number("global_lm") : invalid_uint;
915 
916  auto & qu_dof_indices = dof_indices[qu_num];
917  auto & u_dof_indices = dof_indices[u_num];
918  auto & lm_u_dof_indices = dof_indices[lm_u_num];
919  auto & qv_dof_indices = dof_indices[qv_num];
920  auto & v_dof_indices = dof_indices[v_num];
921  auto & lm_v_dof_indices = dof_indices[lm_v_num];
922  auto & p_dof_indices = dof_indices[p_num];
923  static std::vector<dof_id_type> dummy_indices;
924  auto & global_lm_dof_indices = cavity ? dof_indices[global_lm_num] : dummy_indices;
925 
926  std::vector<boundary_id_type> boundary_ids;
927  const auto & boundary_info = mesh->get_boundary_info();
928 
929  DenseMatrix<Number> Jqu_qu, Jqv_qv, Jqu_u, Jqv_v, Ju_qu, Jv_qv, Ju_p, Jv_p, Ju_u, Jv_u, Ju_v,
930  Jv_v, Jp_u, Jp_v, Jp_glm, Jglm_p, Jp_lmu, Jp_lmv, Jqu_lmu, Jqv_lmv, Ju_lmu, Jv_lmv, Jv_lmu,
931  Ju_lmv, Jlmu_qu, Jlmv_qv, Jlmu_p, Jlmv_p, Jlmu_u, Jlmv_v, Jlmu_lmu, Jlmv_lmu, Jlmu_lmv,
932  Jlmv_lmv;
933 
934  for (const auto & elem : mesh->active_local_element_ptr_range())
935  {
936  if (sc)
937  sc->set_current_elem(*elem);
938 
939  // Retrive our dof indices for all fields
940  dof_map->dof_indices(elem, qu_dof_indices, qu_num);
941  dof_map->dof_indices(elem, u_dof_indices, u_num);
942  dof_map->dof_indices(elem, lm_u_dof_indices, lm_u_num);
943  dof_map->dof_indices(elem, qv_dof_indices, qv_num);
944  dof_map->dof_indices(elem, v_dof_indices, v_num);
945  dof_map->dof_indices(elem, lm_v_dof_indices, lm_v_num);
946  dof_map->dof_indices(elem, p_dof_indices, p_num);
947  if (cavity)
948  {
949  dof_map->dof_indices(elem, global_lm_dof_indices, global_lm_num);
950  libmesh_assert(global_lm_dof_indices.size() == 1);
951  }
952 
953  vector_n_dofs = qu_dof_indices.size();
954  scalar_n_dofs = u_dof_indices.size();
955  lm_n_dofs = lm_u_dof_indices.size();
956  p_n_dofs = p_dof_indices.size();
958 
959  Jqu_qu.resize(qu_dof_indices.size(), qu_dof_indices.size());
960  Jqv_qv.resize(qv_dof_indices.size(), qv_dof_indices.size());
961  Jqu_u.resize(qu_dof_indices.size(), u_dof_indices.size());
962  Jqv_v.resize(qv_dof_indices.size(), v_dof_indices.size());
963  Ju_qu.resize(u_dof_indices.size(), qu_dof_indices.size());
964  Jv_qv.resize(v_dof_indices.size(), qv_dof_indices.size());
965  Ju_p.resize(u_dof_indices.size(), p_dof_indices.size());
966  Jv_p.resize(v_dof_indices.size(), p_dof_indices.size());
967  Ju_u.resize(u_dof_indices.size(), u_dof_indices.size());
968  Jv_u.resize(v_dof_indices.size(), u_dof_indices.size());
969  Ju_v.resize(u_dof_indices.size(), v_dof_indices.size());
970  Jv_v.resize(v_dof_indices.size(), v_dof_indices.size());
971  Jp_u.resize(p_dof_indices.size(), u_dof_indices.size());
972  Jp_v.resize(p_dof_indices.size(), v_dof_indices.size());
973  Jp_glm.resize(p_dof_indices.size(), global_lm_dof_indices.size());
974  Jglm_p.resize(global_lm_dof_indices.size(), p_dof_indices.size());
975  Jp_lmu.resize(p_dof_indices.size(), lm_u_dof_indices.size());
976  Jp_lmv.resize(p_dof_indices.size(), lm_v_dof_indices.size());
977  Jqu_lmu.resize(qu_dof_indices.size(), lm_u_dof_indices.size());
978  Jqv_lmv.resize(qv_dof_indices.size(), lm_v_dof_indices.size());
979  Ju_lmu.resize(u_dof_indices.size(), lm_u_dof_indices.size());
980  Jv_lmv.resize(v_dof_indices.size(), lm_v_dof_indices.size());
981  Jv_lmu.resize(v_dof_indices.size(), lm_u_dof_indices.size());
982  Ju_lmv.resize(u_dof_indices.size(), lm_v_dof_indices.size());
983  Jlmu_qu.resize(lm_u_dof_indices.size(), qu_dof_indices.size());
984  Jlmv_qv.resize(lm_v_dof_indices.size(), qv_dof_indices.size());
985  Jlmu_p.resize(lm_u_dof_indices.size(), p_dof_indices.size());
986  Jlmv_p.resize(lm_v_dof_indices.size(), p_dof_indices.size());
987  Jlmu_u.resize(lm_u_dof_indices.size(), u_dof_indices.size());
988  Jlmv_v.resize(lm_v_dof_indices.size(), v_dof_indices.size());
989  Jlmu_lmu.resize(lm_u_dof_indices.size(), lm_u_dof_indices.size());
990  Jlmv_lmu.resize(lm_v_dof_indices.size(), lm_u_dof_indices.size());
991  Jlmu_lmv.resize(lm_u_dof_indices.size(), lm_v_dof_indices.size());
992  Jlmv_lmv.resize(lm_v_dof_indices.size(), lm_v_dof_indices.size());
993 
994  // Reinit our volume FE objects
995  vector_fe->reinit(elem);
996  scalar_fe->reinit(elem);
997 
998  libmesh_assert_equal_to(vector_n_dofs, vector_phi->size());
999  libmesh_assert_equal_to(scalar_n_dofs, scalar_phi->size());
1000 
1001  // Get our local element dof values
1002  X.get(qu_dof_indices, qu_dof_values);
1003  X.get(u_dof_indices, u_dof_values);
1004  X.get(lm_u_dof_indices, lm_u_dof_values);
1005  X.get(qv_dof_indices, qv_dof_values);
1006  X.get(v_dof_indices, v_dof_values);
1007  X.get(lm_v_dof_indices, lm_v_dof_values);
1008  X.get(p_dof_indices, p_dof_values);
1009  if (cavity)
1010  global_lm_dof_value = X(global_lm_dof_indices[0]);
1011 
1012  // Compute volumetric local qp solutions
1018 
1019  //
1020  // compute volumetric residuals and Jacobians
1021  //
1022 
1023  // qu and u
1024  vector_volume_jacobian(Jqu_qu, Jqu_u);
1025  scalar_volume_jacobian(0, Ju_qu, Ju_p, Ju_u, Ju_v);
1026 
1027  // qv and v
1028  vector_volume_jacobian(Jqv_qv, Jqv_v);
1029  scalar_volume_jacobian(1, Jv_qv, Jv_p, Jv_u, Jv_v);
1030 
1031  // p
1032  pressure_volume_jacobian(Jp_u, Jp_v, Jp_glm, Jglm_p);
1033 
1034  for (auto side : elem->side_index_range())
1035  {
1036  neigh = elem->neighbor_ptr(side);
1037 
1038  // Reinit our face FE objects
1039  vector_fe_face->reinit(elem, side);
1040  scalar_fe_face->reinit(elem, side);
1041  lm_fe_face->reinit(elem, side);
1042 
1043  // Compute face local qp solutions
1051 
1052  if (neigh == nullptr)
1053  {
1054  boundary_info.boundary_ids(elem, side, boundary_ids);
1055  libmesh_assert(boundary_ids.size() == 1);
1056  current_bnd = boundary_ids[0];
1057  if (cavity || (current_bnd != right_bnd))
1058  {
1059  // qu, u
1060  scalar_dirichlet_jacobian(0, Ju_qu, Ju_p, Ju_u);
1061 
1062  // qv, v
1063  scalar_dirichlet_jacobian(1, Jv_qv, Jv_p, Jv_v);
1064 
1065  // Set the LMs on these Dirichlet boundary faces to 0
1068 
1069  continue;
1070  }
1071  }
1072 
1073  //
1074  // if we got here, then we are on an internal face or an outlet face
1075  //
1076 
1077  // qu, u, lm_u
1078  vector_face_jacobian(Jqu_lmu);
1079  scalar_face_jacobian(0, Ju_qu, Ju_p, Ju_u, Ju_lmu, Ju_lmu, Ju_lmv);
1080  lm_face_jacobian(0, Jlmu_qu, Jlmu_p, Jlmu_u, Jlmu_lmu, Jlmu_lmu, Jlmu_lmv);
1081 
1082  // qv, v, lm_v
1083  vector_face_jacobian(Jqv_lmv);
1084  scalar_face_jacobian(1, Jv_qv, Jv_p, Jv_v, Jv_lmv, Jv_lmu, Jv_lmv);
1085  lm_face_jacobian(1, Jlmv_qv, Jlmv_p, Jlmv_v, Jlmv_lmv, Jlmv_lmu, Jlmv_lmv);
1086 
1087  // p
1088  pressure_face_jacobian(Jp_lmu, Jp_lmv);
1089  }
1090 
1091  add_matrix(S, qu_num, qu_num, Jqu_qu);
1092  add_matrix(S, qv_num, qv_num, Jqv_qv);
1093  add_matrix(S, qu_num, u_num, Jqu_u);
1094  add_matrix(S, qv_num, v_num, Jqv_v);
1095  add_matrix(S, u_num, qu_num, Ju_qu);
1096  add_matrix(S, v_num, qv_num, Jv_qv);
1097  add_matrix(S, u_num, p_num, Ju_p);
1098  add_matrix(S, v_num, p_num, Jv_p);
1099  add_matrix(S, u_num, u_num, Ju_u);
1100  add_matrix(S, v_num, u_num, Jv_u);
1101  add_matrix(S, u_num, v_num, Ju_v);
1102  add_matrix(S, v_num, v_num, Jv_v);
1103  add_matrix(S, p_num, u_num, Jp_u);
1104  add_matrix(S, p_num, v_num, Jp_v);
1105  if (global_lm_num != invalid_uint)
1106  {
1107  add_matrix(S, p_num, global_lm_num, Jp_glm);
1108  add_matrix(S, global_lm_num, p_num, Jglm_p);
1109  }
1110  add_matrix(S, p_num, lm_u_num, Jp_lmu);
1111  add_matrix(S, p_num, lm_v_num, Jp_lmv);
1112  add_matrix(S, qu_num, lm_u_num, Jqu_lmu);
1113  add_matrix(S, qv_num, lm_v_num, Jqv_lmv);
1114  add_matrix(S, u_num, lm_u_num, Ju_lmu);
1115  add_matrix(S, v_num, lm_v_num, Jv_lmv);
1116  add_matrix(S, v_num, lm_u_num, Jv_lmu);
1117  add_matrix(S, u_num, lm_v_num, Ju_lmv);
1118  add_matrix(S, lm_u_num, qu_num, Jlmu_qu);
1119  add_matrix(S, lm_v_num, qv_num, Jlmv_qv);
1120  add_matrix(S, lm_u_num, p_num, Jlmu_p);
1121  add_matrix(S, lm_v_num, p_num, Jlmv_p);
1122  add_matrix(S, lm_u_num, u_num, Jlmu_u);
1123  add_matrix(S, lm_v_num, v_num, Jlmv_v);
1124  add_matrix(S, lm_u_num, lm_u_num, Jlmu_lmu);
1125  add_matrix(S, lm_v_num, lm_u_num, Jlmv_lmu);
1126  add_matrix(S, lm_u_num, lm_v_num, Jlmu_lmv);
1127  add_matrix(S, lm_v_num, lm_v_num, Jlmv_lmv);
1128  }
1129 
1130  J->close();
1131 }
1132 
1133 } // namespace libMesh
const std::vector< std::vector< Real > > * scalar_phi_face
Definition: hdg_problem.h:210
void scalar_face_jacobian(const unsigned int vel_component, DenseMatrix< Number > &Jsq, DenseMatrix< Number > &Jsp, DenseMatrix< Number > &Jss, DenseMatrix< Number > &Jslm, DenseMatrix< Number > &Js_lmu, DenseMatrix< Number > &Js_lmv)
Definition: hdg_problem.C:549
NumberVectorValue vel_cross_vel_residual(const std::vector< Number > &u_sol_local, const std::vector< Number > &v_sol_local, const unsigned int qp, const unsigned int vel_component) const
Definition: hdg_problem.C:177
boundary_id_type bottom_bnd
Definition: hdg_problem.h:60
std::unique_ptr< QBase > qface
Definition: hdg_problem.h:54
HDGProblem(const Real nu_in, const bool cavity_in)
Definition: hdg_problem.C:53
void create_identity_jacobian(const QBase &quadrature, const std::vector< Real > &JxW_local, const std::vector< std::vector< Real >> &phi, const std::size_t n_dofs, DenseMatrix< Number > &J)
Definition: hdg_problem.C:118
const std::vector< std::vector< Real > > * scalar_phi
Definition: hdg_problem.h:202
void vector_face_residual(const std::vector< Number > &lm_sol, DenseVector< Number > &R)
Definition: hdg_problem.C:421
const std::vector< std::vector< RealVectorValue > > * vector_phi_face
Definition: hdg_problem.h:209
void lm_face_jacobian(const unsigned int vel_component, DenseMatrix< Number > &Jlmq, DenseMatrix< Number > &Jlmp, DenseMatrix< Number > &Jlms, DenseMatrix< Number > &Jlmlm, DenseMatrix< Number > &Jlm_lmu, DenseMatrix< Number > &Jlm_lmv)
Definition: hdg_problem.C:655
void pressure_face_residual(DenseVector< Number > &R)
Definition: hdg_problem.C:341
std::vector< Gradient > sigma_u
Definition: hdg_problem.h:227
const std::vector< std::vector< RealVectorValue > > * vector_phi
Definition: hdg_problem.h:201
void scalar_face_residual(const std::vector< Gradient > &vector_sol, const std::vector< Number > &scalar_sol, const std::vector< Number > &lm_sol, const unsigned int vel_component, DenseVector< Number > &R)
Definition: hdg_problem.C:506
void set_current_elem(const Elem &elem)
Set the current element.
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:303
std::unique_ptr< FEVectorBase > vector_fe
Definition: hdg_problem.h:48
void vector_volume_residual(const std::vector< Gradient > &vector_sol, const std::vector< Number > &scalar_sol, DenseVector< Number > &R)
Definition: hdg_problem.C:145
boundary_id_type current_bnd
Definition: hdg_problem.h:251
virtual void get(const std::vector< numeric_index_type > &index, T *values) const
Access multiple components at once.
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition: dof_map.C:2201
VectorValue< Real > RealVectorValue
Useful typedefs to allow transparent switching between Real and Complex data types.
std::unique_ptr< FEBase > lm_fe_face
Definition: hdg_problem.h:53
std::vector< Number > v_sol
Definition: hdg_problem.h:222
void resize(const unsigned int n)
Resize the vector.
Definition: dense_vector.h:396
virtual void add_vector(const T *v, const std::vector< numeric_index_type > &dof_indices)
Computes , where v is a pointer and each dof_indices[i] specifies where to add value v[i]...
std::vector< Number > p_sol
Definition: hdg_problem.h:224
void compute_stress(const std::vector< Gradient > &vel_gradient, const unsigned int vel_component, std::vector< Gradient > &sigma)
Definition: hdg_problem.C:131
const DofMap * dof_map
Definition: hdg_problem.h:44
std::unique_ptr< FEBase > scalar_fe
Definition: hdg_problem.h:49
void scalar_volume_residual(const std::vector< Gradient > &vel_gradient, const unsigned int vel_component, std::vector< Gradient > &sigma, DenseVector< Number > &R)
Definition: hdg_problem.C:205
std::vector< Number > v_dof_values
Definition: hdg_problem.h:235
void scalar_volume_jacobian(const unsigned int vel_component, DenseMatrix< Number > &Jsq, DenseMatrix< Number > &Jsp, DenseMatrix< Number > &Jsu, DenseMatrix< Number > &Jsv)
Definition: hdg_problem.C:238
void pressure_volume_residual(DenseVector< Number > &Rp, DenseVector< Number > &Rglm)
Definition: hdg_problem.C:279
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
const Elem * neigh
Definition: hdg_problem.h:254
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:170
const std::vector< Real > * JxW_face
Definition: hdg_problem.h:207
virtual void zero()=0
Set all entries to zero.
const std::vector< Real > * JxW
Definition: hdg_problem.h:199
const VSoln v_true_soln
Definition: hdg_problem.h:70
unsigned int variable_number(std::string_view var) const
Definition: system.C:1398
const std::vector< std::vector< Real > > * div_vector_phi
Definition: hdg_problem.h:204
virtual void add(const numeric_index_type i, const numeric_index_type j, const T value)=0
Add value to the element (i,j).
const std::vector< Point > * q_point
Definition: hdg_problem.h:200
void create_identity_residual(const QBase &quadrature, const std::vector< Real > &JxW_local, const std::vector< std::vector< Real >> &phi, const std::vector< Number > &sol, const std::size_t n_dofs, DenseVector< Number > &R)
Definition: hdg_problem.C:105
virtual void residual(const NumericVector< Number > &X, NumericVector< Number > &R, NonlinearImplicitSystem &S) override
Definition: hdg_problem.C:712
virtual void add_matrix(const DenseMatrix< T > &dm, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols)=0
Add the full matrix dm to the SparseMatrix.
std::size_t lm_n_dofs
Definition: hdg_problem.h:243
static const boundary_id_type invalid_id
Number used for internal use.
boundary_id_type top_bnd
Definition: hdg_problem.h:58
std::vector< Number > lm_u_sol
Definition: hdg_problem.h:220
std::unordered_map< unsigned int, std::vector< dof_id_type > > dof_indices
Definition: hdg_problem.h:215
std::vector< Number > qv_dof_values
Definition: hdg_problem.h:234
std::size_t global_lm_n_dofs
Definition: hdg_problem.h:245
std::vector< Number > p_dof_values
Definition: hdg_problem.h:237
static constexpr Real tau
Definition: hdg_problem.h:248
std::unique_ptr< QBase > qrule
Definition: hdg_problem.h:50
const PSoln p_true_soln
Definition: hdg_problem.h:71
std::vector< Number > lm_v_sol
Definition: hdg_problem.h:223
const std::vector< std::vector< Real > > * lm_phi_face
Definition: hdg_problem.h:211
libmesh_assert(ctx)
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and non-linear solv...
void lm_face_residual(const std::vector< Gradient > &vector_sol, const std::vector< Number > &scalar_sol, const std::vector< Number > &lm_sol, const unsigned int vel_component, DenseVector< Number > &R)
Definition: hdg_problem.C:613
std::vector< Number > u_sol
Definition: hdg_problem.h:219
std::unique_ptr< FEBase > scalar_fe_face
Definition: hdg_problem.h:52
void add_matrix(NonlinearImplicitSystem &sys, const unsigned int ivar_num, const unsigned int jvar_num, const DenseMatrix< Number > &elem_mat)
Definition: hdg_problem.C:886
unsigned int n_points() const
Definition: quadrature.h:131
std::vector< Number > u_dof_values
Definition: hdg_problem.h:232
virtual void jacobian(const NumericVector< Number > &X, SparseMatrix< Number > &, NonlinearImplicitSystem &S) override
Definition: hdg_problem.C:896
void pressure_dirichlet_residual(DenseVector< Number > &R)
Definition: hdg_problem.C:396
RealVectorValue get_dirichlet_velocity(const unsigned int qp) const
Definition: hdg_problem.C:371
void scalar_dirichlet_jacobian(const unsigned int vel_component, DenseMatrix< Number > &Jsq, DenseMatrix< Number > &Jsp, DenseMatrix< Number > &Jss)
Definition: hdg_problem.C:479
std::vector< Gradient > qu_sol
Definition: hdg_problem.h:218
const std::vector< Point > * normals
Definition: hdg_problem.h:212
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2612
std::vector< Gradient > qv_sol
Definition: hdg_problem.h:221
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< Number > qu_dof_values
Definition: hdg_problem.h:231
StaticCondensation * sc
Definition: hdg_problem.h:45
Real forcing(const Point &p) const override
Definition: exact_soln.h:148
void vector_volume_jacobian(DenseMatrix< Number > &Jqq, DenseMatrix< Number > &Jqs)
Definition: hdg_problem.C:161
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Definition: dense_matrix.h:895
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:176
const MeshBase * mesh
Definition: hdg_problem.h:43
void pressure_face_jacobian(DenseMatrix< Number > &Jplm_u, DenseMatrix< Number > &Jplm_v)
Definition: hdg_problem.C:353
boundary_id_type left_bnd
Definition: hdg_problem.h:57
std::vector< Number > lm_u_dof_values
Definition: hdg_problem.h:233
const std::vector< Point > * qface_point
Definition: hdg_problem.h:208
Number global_lm_dof_value
Definition: hdg_problem.h:238
void pressure_volume_jacobian(DenseMatrix< Number > &Jpu, DenseMatrix< Number > &Jpv, DenseMatrix< Number > &Jpglm, DenseMatrix< Number > &Jglmp)
Definition: hdg_problem.C:307
void compute_qp_soln(std::vector< SolnType > &qp_vec, const unsigned int n_qps, const std::vector< std::vector< PhiType >> &phi, const std::vector< Number > &dof_values)
Definition: hdg_problem.C:34
const USoln u_true_soln
Definition: hdg_problem.h:69
std::size_t p_n_dofs
Definition: hdg_problem.h:244
void vector_face_jacobian(DenseMatrix< Number > &Jqlm)
Definition: hdg_problem.C:430
std::vector< Number > lm_v_dof_values
Definition: hdg_problem.h:236
const std::vector< std::vector< RealVectorValue > > * grad_scalar_phi
Definition: hdg_problem.h:203
std::size_t scalar_n_dofs
Definition: hdg_problem.h:242
std::size_t vector_n_dofs
Definition: hdg_problem.h:241
void vector_dirichlet_residual(const unsigned int vel_component, DenseVector< Number > &R)
Definition: hdg_problem.C:408
std::vector< Gradient > sigma_v
Definition: hdg_problem.h:228
boundary_id_type right_bnd
Definition: hdg_problem.h:59
const SparseMatrix< Number > & get_system_matrix() const
The QBase class provides the basic functionality from which various quadrature rules can be derived...
Definition: quadrature.h:61
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:153
std::unique_ptr< FEVectorBase > vector_fe_face
Definition: hdg_problem.h:51
void scalar_dirichlet_residual(const std::vector< Gradient > &vector_sol, const std::vector< Number > &scalar_sol, const unsigned int vel_component, DenseVector< Number > &R)
Definition: hdg_problem.C:441
NumberVectorValue vel_cross_vel_jacobian(const std::vector< Number > &u_sol_local, const std::vector< Number > &v_sol_local, const unsigned int qp, const unsigned int vel_component, const unsigned int vel_j_component, const std::vector< std::vector< Real >> &phi, const unsigned int j) const
Definition: hdg_problem.C:187