libMesh
Loading...
Searching...
No Matches
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
28namespace libMesh
29{
30// compute a solution indexable at quadrature points composed from the local degree of freedom
31// solution vector and associated basis functions
32template <typename SolnType, typename PhiType>
33void
34compute_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
53HDGProblem::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
62HDGProblem::~HDGProblem() = default;
63
64void
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
104void
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
117void
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
130void
131HDGProblem::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
144void
145HDGProblem::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
160void
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
177HDGProblem::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
187HDGProblem::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
204void
205HDGProblem::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
237void
238HDGProblem::scalar_volume_jacobian(const unsigned int vel_component,
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
278void
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
306void
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
340void
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
352void
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
371HDGProblem::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
395void
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
407void
408HDGProblem::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
420void
421HDGProblem::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
429void
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
440void
441HDGProblem::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
478void
479HDGProblem::scalar_dirichlet_jacobian(const unsigned int vel_component,
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
505void
506HDGProblem::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
548void
549HDGProblem::scalar_face_jacobian(const unsigned int vel_component,
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
612void
613HDGProblem::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
654void
655HDGProblem::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
711void
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();
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
863
864 // qv, v, lm_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
885void
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
895void
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
boundary_id_type get_id_by_name(std::string_view name) const
static const boundary_id_type invalid_id
Number used for internal use.
Defines a dense matrix for use in Finite Element-type computations.
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Defines a dense vector for use in Finite Element-type computations.
void resize(const unsigned int n)
Resize the vector.
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition dof_map.C:2201
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
StaticCondensation * sc
Definition hdg_problem.h:45
const USoln u_true_soln
Definition hdg_problem.h:69
boundary_id_type bottom_bnd
Definition hdg_problem.h:60
void scalar_volume_residual(const std::vector< Gradient > &vel_gradient, const unsigned int vel_component, std::vector< Gradient > &sigma, DenseVector< Number > &R)
std::vector< Number > qv_dof_values
boundary_id_type right_bnd
Definition hdg_problem.h:59
std::vector< Number > qu_dof_values
HDGProblem(const Real nu_in, const bool cavity_in)
Definition hdg_problem.C:53
std::vector< Number > v_dof_values
std::vector< Gradient > sigma_u
std::vector< Number > p_dof_values
const std::vector< std::vector< Real > > * scalar_phi_face
std::vector< Gradient > sigma_v
const std::vector< std::vector< RealVectorValue > > * vector_phi
const std::vector< Real > * JxW
void vector_dirichlet_residual(const unsigned int vel_component, DenseVector< Number > &R)
std::unique_ptr< FEVectorBase > vector_fe_face
Definition hdg_problem.h:51
const std::vector< std::vector< Real > > * div_vector_phi
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)
const std::vector< Point > * q_point
std::vector< Number > lm_v_sol
const PSoln p_true_soln
Definition hdg_problem.h:71
void pressure_dirichlet_residual(DenseVector< Number > &R)
const std::vector< Real > * JxW_face
const std::vector< std::vector< Real > > * scalar_phi
void pressure_face_residual(DenseVector< Number > &R)
const MeshBase * mesh
Definition hdg_problem.h:43
std::vector< Number > p_sol
void scalar_dirichlet_residual(const std::vector< Gradient > &vector_sol, const std::vector< Number > &scalar_sol, const unsigned int vel_component, DenseVector< Number > &R)
void vector_face_residual(const std::vector< Number > &lm_sol, DenseVector< Number > &R)
virtual void jacobian(const NumericVector< Number > &X, SparseMatrix< Number > &, NonlinearImplicitSystem &S) override
Jacobian function.
std::unique_ptr< FEBase > scalar_fe
Definition hdg_problem.h:49
const std::vector< Point > * normals
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)
const DofMap * dof_map
Definition hdg_problem.h:44
void scalar_volume_jacobian(const unsigned int vel_component, DenseMatrix< Number > &Jsq, DenseMatrix< Number > &Jsp, DenseMatrix< Number > &Jsu, DenseMatrix< Number > &Jsv)
const Elem * neigh
std::unique_ptr< QBase > qrule
Definition hdg_problem.h:50
std::unordered_map< unsigned int, std::vector< dof_id_type > > dof_indices
std::size_t scalar_n_dofs
std::vector< Number > v_sol
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
std::vector< Gradient > qv_sol
std::vector< Number > lm_u_sol
const std::vector< std::vector< RealVectorValue > > * vector_phi_face
boundary_id_type left_bnd
Definition hdg_problem.h:57
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
RealVectorValue get_dirichlet_velocity(const unsigned int qp) const
boundary_id_type top_bnd
Definition hdg_problem.h:58
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)
std::unique_ptr< QBase > qface
Definition hdg_problem.h:54
std::vector< Number > u_sol
const std::vector< std::vector< RealVectorValue > > * grad_scalar_phi
std::size_t global_lm_n_dofs
boundary_id_type current_bnd
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)
std::vector< Number > lm_u_dof_values
void pressure_volume_jacobian(DenseMatrix< Number > &Jpu, DenseMatrix< Number > &Jpv, DenseMatrix< Number > &Jpglm, DenseMatrix< Number > &Jglmp)
const VSoln v_true_soln
Definition hdg_problem.h:70
static constexpr Real tau
std::vector< Number > u_dof_values
std::size_t lm_n_dofs
const std::vector< std::vector< Real > > * lm_phi_face
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)
void vector_face_jacobian(DenseMatrix< Number > &Jqlm)
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)
std::size_t vector_n_dofs
virtual void residual(const NumericVector< Number > &X, NumericVector< Number > &R, NonlinearImplicitSystem &S) override
Residual function.
void scalar_dirichlet_jacobian(const unsigned int vel_component, DenseMatrix< Number > &Jsq, DenseMatrix< Number > &Jsp, DenseMatrix< Number > &Jss)
std::vector< Gradient > qu_sol
const std::vector< Point > * qface_point
void compute_stress(const std::vector< Gradient > &vel_gradient, const unsigned int vel_component, std::vector< Gradient > &sigma)
std::unique_ptr< FEBase > scalar_fe_face
Definition hdg_problem.h:52
void vector_volume_jacobian(DenseMatrix< Number > &Jqq, DenseMatrix< Number > &Jqs)
void add_matrix(NonlinearImplicitSystem &sys, const unsigned int ivar_num, const unsigned int jvar_num, const DenseMatrix< Number > &elem_mat)
std::vector< Number > lm_v_dof_values
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)
std::unique_ptr< FEBase > lm_fe_face
Definition hdg_problem.h:53
void pressure_face_jacobian(DenseMatrix< Number > &Jplm_u, DenseMatrix< Number > &Jplm_v)
void pressure_volume_residual(DenseVector< Number > &Rp, DenseVector< Number > &Rglm)
std::size_t p_n_dofs
const SparseMatrix< Number > & get_system_matrix() const
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and non-linear solv...
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
virtual void get(const std::vector< numeric_index_type > &index, T *values) const
Access multiple components at once.
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].
virtual void zero()=0
Set all entries to zero.
Real forcing(const Point &p) const override
Definition exact_soln.h:148
The QBase class provides the basic functionality from which various quadrature rules can be derived.
Definition quadrature.h:62
unsigned int n_points() const
Definition quadrature.h:131
Generic sparse matrix.
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.
virtual void add(const numeric_index_type i, const numeric_index_type j, const T value)=0
Add value to the element (i,j).
void set_current_elem(const Elem &elem)
Set the current element.
unsigned int variable_number(std::string_view var) const
Definition system.C:1398
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
The libMesh namespace provides an interface to certain functionality in the library.
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
libmesh_assert(ctx)
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
VectorValue< Real > RealVectorValue
Useful typedefs to allow transparent switching between Real and Complex data types.
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
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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