ExaDG
Loading...
Searching...
No Matches
convective_operator.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22#ifndef INCLUDE_EXADG_INCOMPRESSIBLE_NAVIER_STOKES_SPATIAL_DISCRETIZATION_OPERATORS_CONVECTIVE_OPERATOR_H_
23#define INCLUDE_EXADG_INCOMPRESSIBLE_NAVIER_STOKES_SPATIAL_DISCRETIZATION_OPERATORS_CONVECTIVE_OPERATOR_H_
24
25#include <exadg/incompressible_navier_stokes/spatial_discretization/operators/weak_boundary_conditions.h>
26#include <exadg/incompressible_navier_stokes/user_interface/parameters.h>
27#include <exadg/matrix_free/integrators.h>
28#include <exadg/operators/operator_base.h>
29
30namespace ExaDG
31{
32namespace IncNS
33{
34namespace Operators
35{
36struct ConvectiveKernelData
37{
38 ConvectiveKernelData()
39 : formulation(FormulationConvectiveTerm::DivergenceFormulation),
40 temporal_treatment(TreatmentOfConvectiveTerm::Implicit),
41 upwind_factor(1.0),
42 use_outflow_bc(false),
43 type_dirichlet_bc(TypeDirichletBCs::Mirror),
44 ale(false)
45 {
46 }
47
48 FormulationConvectiveTerm formulation;
49
50 TreatmentOfConvectiveTerm temporal_treatment;
51
52 double upwind_factor;
53
54 bool use_outflow_bc;
55
56 TypeDirichletBCs type_dirichlet_bc;
57
58 bool ale;
59};
60
61template<int dim, typename Number>
62class ConvectiveKernel
63{
64public:
65 ConvectiveKernel(){};
66
67
68private:
69 typedef dealii::VectorizedArray<Number> scalar;
70 typedef dealii::Tensor<1, dim, dealii::VectorizedArray<Number>> vector;
71 typedef dealii::Tensor<2, dim, dealii::VectorizedArray<Number>> tensor;
72
73 typedef dealii::LinearAlgebra::distributed::Vector<Number> VectorType;
74
75 typedef CellIntegrator<dim, dim, Number> IntegratorCell;
76 typedef FaceIntegrator<dim, dim, Number> IntegratorFace;
77
78public:
79 void
80 reinit(dealii::MatrixFree<dim, Number> const & matrix_free,
81 ConvectiveKernelData const & data,
82 unsigned int const dof_index,
83 unsigned int const quad_index_linearized,
84 bool const use_own_velocity_storage)
85 {
86 this->data = data;
87
88 // integrators for linearized problem
89 integrator_velocity =
90 std::make_shared<IntegratorCell>(matrix_free, dof_index, quad_index_linearized);
91 integrator_velocity_m =
92 std::make_shared<IntegratorFace>(matrix_free, true, dof_index, quad_index_linearized);
93 integrator_velocity_p =
94 std::make_shared<IntegratorFace>(matrix_free, false, dof_index, quad_index_linearized);
95
96 if(data.ale)
97 {
98 integrator_grid_velocity =
99 std::make_shared<IntegratorCell>(matrix_free, dof_index, quad_index_linearized);
100 integrator_grid_velocity_face =
101 std::make_shared<IntegratorFace>(matrix_free, true, dof_index, quad_index_linearized);
102 }
103
104 if(use_own_velocity_storage)
105 {
106 velocity.reset();
107 matrix_free.initialize_dof_vector(velocity.own(), dof_index);
108 }
109
110 if(data.ale)
111 {
112 matrix_free.initialize_dof_vector(grid_velocity.own(), dof_index);
113
114 AssertThrow(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation,
115 dealii::ExcMessage(
116 "ALE formulation can only be used in combination with ConvectiveFormulation"));
117 }
118 }
119
120 static MappingFlags
121 get_mapping_flags()
122 {
123 MappingFlags flags;
124
125 flags.cells = dealii::update_JxW_values | dealii::update_gradients;
126 flags.inner_faces = dealii::update_JxW_values | dealii::update_normal_vectors;
127 flags.boundary_faces = dealii::update_JxW_values | dealii::update_normal_vectors;
128
129 return flags;
130 }
131
132 /*
133 * IntegratorFlags valid for both the nonlinear convective operator and the linearized convective
134 * operator
135 */
137 get_integrator_flags() const
138 {
139 IntegratorFlags flags;
140
141 if(data.formulation == FormulationConvectiveTerm::DivergenceFormulation)
142 {
143 flags.cell_evaluate = dealii::EvaluationFlags::values;
144 flags.cell_integrate = dealii::EvaluationFlags::gradients;
145
146 flags.face_evaluate = dealii::EvaluationFlags::values;
147 flags.face_integrate = dealii::EvaluationFlags::values;
148 }
149 else if(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation)
150 {
151 flags.cell_evaluate = dealii::EvaluationFlags::values | dealii::EvaluationFlags::gradients;
152 flags.cell_integrate = dealii::EvaluationFlags::values;
153
154 flags.face_evaluate = dealii::EvaluationFlags::values;
155 flags.face_integrate = dealii::EvaluationFlags::values;
156 }
157 else
158 {
159 AssertThrow(false, dealii::ExcMessage("Not implemented."));
160 }
161
162 return flags;
163 }
164
166 get_data() const
167 {
168 return this->data;
169 }
170
171 VectorType const &
172 get_velocity() const
173 {
174 return *velocity;
175 }
176
177 void
178 set_velocity_copy(VectorType const & src)
179 {
180 velocity.own() = src;
181
182 velocity->update_ghost_values();
183 }
184
185 void
186 set_velocity_ptr(VectorType const & src)
187 {
188 velocity.reset(src);
189
190 velocity->update_ghost_values();
191 }
192
193 void
194 set_grid_velocity_ptr(VectorType const & src)
195 {
196 grid_velocity.reset(src);
197
198 grid_velocity->update_ghost_values();
199 }
200
201 VectorType const &
202 get_grid_velocity() const
203 {
204 return *grid_velocity;
205 }
206
207 inline DEAL_II_ALWAYS_INLINE //
208 vector
209 get_velocity_cell(unsigned int const q) const
210 {
211 return integrator_velocity->get_value(q);
212 }
213
214 inline DEAL_II_ALWAYS_INLINE //
215 tensor
216 get_velocity_gradient_cell(unsigned int const q) const
217 {
218 return integrator_velocity->get_gradient(q);
219 }
220
221 inline DEAL_II_ALWAYS_INLINE //
222 vector
223 get_velocity_m(unsigned int const q) const
224 {
225 return integrator_velocity_m->get_value(q);
226 }
227
228 inline DEAL_II_ALWAYS_INLINE //
229 vector
230 get_velocity_p(unsigned int const q) const
231 {
232 return integrator_velocity_p->get_value(q);
233 }
234
235 // grid velocity cell
236 inline DEAL_II_ALWAYS_INLINE //
237 vector
238 get_grid_velocity_cell(unsigned int const q) const
239 {
240 return integrator_grid_velocity->get_value(q);
241 }
242
243 // grid velocity face (the grid velocity is continuous
244 // so that we need only one function instead of minus and
245 // plus functions)
246 inline DEAL_II_ALWAYS_INLINE //
247 vector
248 get_grid_velocity_face(unsigned int const q) const
249 {
250 return integrator_grid_velocity_face->get_value(q);
251 }
252
253 // linearized operator
254 void
255 reinit_cell(unsigned int const cell) const
256 {
257 integrator_velocity->reinit(cell);
258
259 if(data.ale)
260 integrator_grid_velocity->reinit(cell);
261
262 if(data.formulation == FormulationConvectiveTerm::DivergenceFormulation)
263 {
264 integrator_velocity->gather_evaluate(*velocity, dealii::EvaluationFlags::values);
265 }
266 else if(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation)
267 {
268 integrator_velocity->gather_evaluate(*velocity,
269 dealii::EvaluationFlags::values |
270 dealii::EvaluationFlags::gradients);
271
272 if(data.ale)
273 integrator_grid_velocity->gather_evaluate(*grid_velocity, dealii::EvaluationFlags::values);
274 }
275 else
276 {
277 AssertThrow(false, dealii::ExcMessage("Not implemented."));
278 }
279 }
280
281 void
282 reinit_face(unsigned int const face) const
283 {
284 integrator_velocity_m->reinit(face);
285 integrator_velocity_m->gather_evaluate(*velocity, dealii::EvaluationFlags::values);
286
287 integrator_velocity_p->reinit(face);
288 integrator_velocity_p->gather_evaluate(*velocity, dealii::EvaluationFlags::values);
289
290 if(data.ale)
291 {
292 integrator_grid_velocity_face->reinit(face);
293 integrator_grid_velocity_face->gather_evaluate(*grid_velocity,
294 dealii::EvaluationFlags::values);
295 }
296 }
297
298 void
299 reinit_boundary_face(unsigned int const face) const
300 {
301 integrator_velocity_m->reinit(face);
302 integrator_velocity_m->gather_evaluate(*velocity, dealii::EvaluationFlags::values);
303
304 if(data.ale)
305 {
306 integrator_grid_velocity_face->reinit(face);
307 integrator_grid_velocity_face->gather_evaluate(*grid_velocity,
308 dealii::EvaluationFlags::values);
309 }
310 }
311
312 void
313 reinit_face_cell_based(unsigned int const cell,
314 unsigned int const face,
315 dealii::types::boundary_id const boundary_id) const
316 {
317 integrator_velocity_m->reinit(cell, face);
318 integrator_velocity_m->gather_evaluate(*velocity, dealii::EvaluationFlags::values);
319
320 if(data.ale)
321 {
322 integrator_grid_velocity_face->reinit(cell, face);
323 integrator_grid_velocity_face->gather_evaluate(*grid_velocity,
324 dealii::EvaluationFlags::values);
325 }
326
327 if(boundary_id == dealii::numbers::internal_face_boundary_id) // internal face
328 {
329 // TODO: Matrix-free implementation in deal.II does currently not allow to access data of
330 // the neighboring element in case of cell-based face loops.
331 // integrator_velocity_p->reinit(cell, face);
332 // integrator_velocity_p->gather_evaluate(velocity,dealii::EvaluationFlags::values);
333 }
334 }
335
336 /*
337 * Volume flux, i.e., the term occurring in the volume integral. The volume flux depends on the
338 * formulation used for the convective term, and is therefore implemented separately for the
339 * different formulations (divergence formulation vs. convective formulation). Note that these
340 * functions are called by the linearized or linearly implicit convective operator, but not by
341 * the nonlinear convective operator.
342 */
343 inline DEAL_II_ALWAYS_INLINE //
344 tensor
345 get_volume_flux_divergence_formulation(vector const & delta_u, unsigned int const q) const
346 {
347 // u denotes the point of linearization for the linearized problem (of the nonlinear implicit
348 // operator) or the transport velocity for the linearly implicit problem
349 vector u = get_velocity_cell(q);
350
351 // flux
352 tensor F;
353
354 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
355 {
356 // linearization of nonlinear convective term
357
358 F = outer_product(u, delta_u);
359 F = F + transpose(F);
360 }
361 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
362 {
363 F = outer_product(delta_u, u);
364 }
365 else
366 {
367 AssertThrow(false, dealii::ExcMessage("not implemented"));
368 }
369
370
371 // minus sign due to integration by parts
372 return -F;
373 }
374
375 inline DEAL_II_ALWAYS_INLINE //
376 vector
377 get_volume_flux_convective_formulation(vector const & delta_u,
378 tensor const & grad_delta_u,
379 unsigned int const q) const
380 {
381 // u denotes the point of linearization for the linearized problem (of the nonlinear implicit
382 // operator) or the transport velocity for the linearly implicit problem in case no ALE
383 // formulation is used.
384
385 // The velocity w also takes into account the grid velocity u_grid in case of an ALE
386 // formulation.
387
388 // w = u
389 vector w = get_velocity_cell(q);
390 tensor grad_u = get_velocity_gradient_cell(q);
391
392 // w = u - u_grid
393 if(data.ale)
394 w -= get_grid_velocity_cell(q);
395
396 // flux
397 vector F;
398
399 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
400 {
401 // linearization of nonlinear convective term
402
403 // plus sign since the strong formulation is used, i.e.
404 // integration by parts is performed twice
405 F = grad_u * delta_u + grad_delta_u * w;
406 }
407 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
408 {
409 // plus sign since the strong formulation is used, i.e.
410 // integration by parts is performed twice
411 F = grad_delta_u * w;
412 }
413 else
414 {
415 AssertThrow(false, dealii::ExcMessage("not implemented"));
416 }
417
418 return F;
419 }
420
421 /*
422 * Calculates the flux for nonlinear operator on interior faces. This function is needed for
423 * face-centric loops and the flux is therefore computed on both sides of an interior face. The
424 * interior flux (element m) is the first element in the tuple, the exterior flux (element p,
425 * neighbor) is the second element in the tuple.
426 */
427 inline DEAL_II_ALWAYS_INLINE //
428 std::tuple<vector, vector>
429 calculate_flux_nonlinear_interior_and_neighbor(vector const & uM,
430 vector const & uP,
431 vector const & normalM,
432 vector const & u_grid) const
433 {
434 vector flux_m, flux_p;
435
436 if(data.formulation == FormulationConvectiveTerm::DivergenceFormulation)
437 {
438 vector flux = calculate_lax_friedrichs_flux(uM, uP, normalM);
439
440 flux_m = flux;
441 flux_p = -flux; // opposite signs since n⁺ = - n⁻
442 }
443 else if(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation)
444 {
445 vector flux;
446 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
447 if(data.ale)
448 average_u_normal -= u_grid * normalM;
449
450 flux = calculate_upwind_flux(uM, uP, average_u_normal);
451
452 // a second term is needed since the strong formulation is implemented (integration by parts
453 // twice)
454 flux_m = flux - average_u_normal * uM;
455 flux_p = -flux + average_u_normal * uP; // opposite signs since n⁺ = - n⁻
456 }
457 else
458 {
459 AssertThrow(false, dealii::ExcMessage("Not implemented."));
460 }
461
462 return std::make_tuple(flux_m, flux_p);
463 }
464
465 /*
466 * Calculates the flux for nonlinear operator on boundary faces. The flux computation used on
467 * interior faces has to be "corrected" if a special outflow boundary condition is used on Neumann
468 * boundaries that is able to deal with backflow.
469 */
470 inline DEAL_II_ALWAYS_INLINE //
471 vector
472 calculate_flux_nonlinear_boundary(vector const & uM,
473 vector const & uP,
474 vector const & normalM,
475 vector const & u_grid,
476 BoundaryTypeU const & boundary_type) const
477 {
478 vector flux;
479
480 if(data.formulation == FormulationConvectiveTerm::DivergenceFormulation)
481 {
482 flux = calculate_lax_friedrichs_flux(uM, uP, normalM);
483
484 if(boundary_type == BoundaryTypeU::Neumann and data.use_outflow_bc == true)
485 apply_outflow_bc(flux, uM * normalM);
486 }
487 else if(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation)
488 {
489 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
490 if(data.ale)
491 average_u_normal -= u_grid * normalM;
492
493 flux = calculate_upwind_flux(uM, uP, average_u_normal);
494
495 if(boundary_type == BoundaryTypeU::Neumann and data.use_outflow_bc == true)
496 apply_outflow_bc(flux, average_u_normal);
497
498 // second term appears since the strong formulation is implemented (integration by parts
499 // is performed twice)
500 flux = flux - average_u_normal * uM;
501 }
502 else
503 {
504 AssertThrow(false, dealii::ExcMessage("Not implemented."));
505 }
506
507 return flux;
508 }
509
510 /*
511 * Calculates the flux for linear operator on interior faces. This function is needed for
512 * face-centric loops and the flux is therefore computed on both sides of an interior face. The
513 * interior flux (element m) is the first element in the tuple, the exterior flux (element p,
514 * neighbor) is the second element in the tuple.
515 */
516 inline DEAL_II_ALWAYS_INLINE //
517 std::tuple<vector, vector>
518 calculate_flux_linear_operator_interior_and_neighbor(vector const & uM,
519 vector const & uP,
520 vector const & delta_uM,
521 vector const & delta_uP,
522 vector const & normalM,
523 unsigned int const q) const
524 {
525 vector fluxM, fluxP;
526
527 if(data.formulation == FormulationConvectiveTerm::DivergenceFormulation)
528 {
529 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
530 {
531 // linearization of nonlinear convective term
532
533 fluxM = calculate_lax_friedrichs_flux_linearized(uM, uP, delta_uM, delta_uP, normalM);
534 fluxP = -fluxM;
535 }
536 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
537 {
538 fluxM = calculate_lax_friedrichs_flux_linear_transport(uM, uP, delta_uM, delta_uP, normalM);
539 fluxP = -fluxM;
540 }
541 else
542 {
543 AssertThrow(false, dealii::ExcMessage("not implemented"));
544 }
545 }
546 else if(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation)
547 {
548 vector u_grid;
549 if(data.ale)
550 u_grid = get_grid_velocity_face(q);
551
552 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
553 {
554 // linearization of nonlinear convective term
555
556 vector flux = calculate_upwind_flux_linearized(uM, uP, u_grid, delta_uM, delta_uP, normalM);
557
558 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
559 if(data.ale)
560 average_u_normal -= u_grid * normalM;
561
562 scalar average_delta_u_normal = 0.5 * (delta_uM + delta_uP) * normalM;
563
564 // second term appears since the strong formulation is implemented (integration by parts
565 // is performed twice)
566 fluxM = flux - average_u_normal * delta_uM - average_delta_u_normal * uM;
567 // opposite signs since n⁺ = - n⁻
568 fluxP = -flux + average_u_normal * delta_uP + average_delta_u_normal * uP;
569 }
570 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
571 {
572 // linearly implicit convective term
573
574 vector flux;
575 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
576 if(data.ale)
577 average_u_normal -= u_grid * normalM;
578
579 flux = calculate_upwind_flux(delta_uM, delta_uP, average_u_normal);
580
581 // a second term is needed since the strong formulation is implemented (integration by parts
582 // twice)
583 fluxM = flux - average_u_normal * delta_uM;
584 fluxP = -flux + average_u_normal * delta_uP; // opposite signs since n⁺ = - n⁻
585 }
586 else
587 {
588 AssertThrow(false, dealii::ExcMessage("not implemented"));
589 }
590 }
591 else
592 {
593 AssertThrow(false, dealii::ExcMessage("Not implemented."));
594 }
595
596 return std::make_tuple(fluxM, fluxP);
597 }
598
599 /*
600 * Calculates the flux for linear operator on interior faces. Only the flux on element e⁻ is
601 * computed.
602 */
603 inline DEAL_II_ALWAYS_INLINE //
604 vector
605 calculate_flux_linear_operator_interior(vector const & uM,
606 vector const & uP,
607 vector const & delta_uM,
608 vector const & delta_uP,
609 vector const & normalM,
610 unsigned int const q) const
611 {
612 vector flux;
613
614 if(data.formulation == FormulationConvectiveTerm::DivergenceFormulation)
615 {
616 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
617 {
618 // linearization of nonlinear convective term
619 flux = calculate_lax_friedrichs_flux_linearized(uM, uP, delta_uM, delta_uP, normalM);
620 }
621 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
622 {
623 flux = calculate_lax_friedrichs_flux_linear_transport(uM, uP, delta_uM, delta_uP, normalM);
624 }
625 else
626 {
627 AssertThrow(false, dealii::ExcMessage("not implemented"));
628 }
629 }
630 else if(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation)
631 {
632 vector u_grid;
633 if(data.ale)
634 u_grid = get_grid_velocity_face(q);
635
636 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
637 {
638 // linearization of nonlinear convective term
639
640 flux = calculate_upwind_flux_linearized(uM, uP, u_grid, delta_uM, delta_uP, normalM);
641
642 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
643
644 if(data.ale)
645 average_u_normal -= u_grid * normalM;
646
647 scalar average_delta_u_normal = 0.5 * (delta_uM + delta_uP) * normalM;
648
649 // second term appears since the strong formulation is implemented (integration by parts
650 // is performed twice)
651 flux = flux - average_u_normal * delta_uM - average_delta_u_normal * uM;
652 }
653 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
654 {
655 // linearly implicit convective term
656
657 vector flux;
658 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
659 if(data.ale)
660 average_u_normal -= u_grid * normalM;
661
662 flux = calculate_upwind_flux(delta_uM, delta_uP, average_u_normal);
663
664 // a second term is needed since the strong formulation is implemented (integration by parts
665 // twice)
666 flux = flux - average_u_normal * delta_uM;
667 }
668 else
669 {
670 AssertThrow(false, dealii::ExcMessage("not implemented"));
671 }
672 }
673 else
674 {
675 AssertThrow(false, dealii::ExcMessage("Not implemented."));
676 }
677
678 return flux;
679 }
680
681 /*
682 * Calculates the flux for linear operator on boundary faces. The only reason why this
683 * function has to be implemented separately is the fact that the flux computation used on
684 * interior faces has to be "corrected" if a special outflow boundary condition is used on Neumann
685 * boundaries that is able to deal with backflow.
686 */
687 inline DEAL_II_ALWAYS_INLINE //
688 vector
689 calculate_flux_linear_operator_boundary(vector const & uM,
690 vector const & uP,
691 vector const & delta_uM,
692 vector const & delta_uP,
693 vector const & normalM,
694 BoundaryTypeU const & boundary_type,
695 unsigned int const q) const
696 {
697 vector flux;
698
699 if(data.formulation == FormulationConvectiveTerm::DivergenceFormulation)
700 {
701 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
702 {
703 // linearization of nonlinear convective term
704 flux = calculate_lax_friedrichs_flux_linearized(uM, uP, delta_uM, delta_uP, normalM);
705
706 if(boundary_type == BoundaryTypeU::Neumann and data.use_outflow_bc == true)
707 apply_outflow_bc(flux, uM * normalM);
708 }
709 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
710 {
711 flux = calculate_lax_friedrichs_flux_linear_transport(uM, uP, delta_uM, delta_uP, normalM);
712
713 if(boundary_type == BoundaryTypeU::Neumann and data.use_outflow_bc == true)
714 apply_outflow_bc(flux, uM * normalM);
715 }
716 else
717 {
718 AssertThrow(false, dealii::ExcMessage("not implemented"));
719 }
720 }
721 else if(data.formulation == FormulationConvectiveTerm::ConvectiveFormulation)
722 {
723 vector u_grid;
724 if(data.ale)
725 u_grid = get_grid_velocity_face(q);
726
727 if(data.temporal_treatment == TreatmentOfConvectiveTerm::Implicit)
728 {
729 // linearization of nonlinear convective term
730 flux = calculate_upwind_flux_linearized(uM, uP, u_grid, delta_uM, delta_uP, normalM);
731
732 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
733 if(data.ale)
734 average_u_normal -= u_grid * normalM;
735
736 if(boundary_type == BoundaryTypeU::Neumann and data.use_outflow_bc == true)
737 apply_outflow_bc(flux, average_u_normal);
738
739 scalar average_delta_u_normal = 0.5 * (delta_uM + delta_uP) * normalM;
740
741 // second term appears since the strong formulation is implemented (integration by parts
742 // is performed twice)
743 flux = flux - average_u_normal * delta_uM - average_delta_u_normal * uM;
744 }
745 else if(data.temporal_treatment == TreatmentOfConvectiveTerm::LinearlyImplicit)
746 {
747 // linearly implicit convective term
748
749 vector flux;
750 scalar average_u_normal = 0.5 * (uM + uP) * normalM;
751 if(data.ale)
752 average_u_normal -= u_grid * normalM;
753
754 flux = calculate_upwind_flux(delta_uM, delta_uP, average_u_normal);
755
756 if(boundary_type == BoundaryTypeU::Neumann and data.use_outflow_bc == true)
757 apply_outflow_bc(flux, average_u_normal);
758
759 // a second term is needed since the strong formulation is implemented (integration by parts
760 // twice)
761 flux = flux - average_u_normal * delta_uM;
762 }
763 else
764 {
765 AssertThrow(false, dealii::ExcMessage("not implemented"));
766 }
767 }
768 else
769 {
770 AssertThrow(false, dealii::ExcMessage("Not implemented."));
771 }
772
773 return flux;
774 }
775
776 /*
777 * Divergence formulation:
778 * Lax-Friedrichs flux
779 * Calculation of lambda according to Shahbazi et al.:
780 * lambda = max ( max |lambda(flux_jacobian_M)| , max |lambda(flux_jacobian_P)| )
781 * = max ( | 2*(uM)^T*normal | , | 2*(uP)^T*normal | )
782 */
783 inline DEAL_II_ALWAYS_INLINE //
784 scalar
785 calculate_lambda(scalar const & uM_n, scalar const & uP_n) const
786 {
787 return data.upwind_factor * 2.0 * std::max(std::abs(uM_n), std::abs(uP_n));
788 }
789
790 /*
791 * Divergence formulation: Calculate Lax-Friedrichs flux for nonlinear operator.
792 */
793 inline DEAL_II_ALWAYS_INLINE //
794 vector
795 calculate_lax_friedrichs_flux(vector const & uM,
796 vector const & uP,
797 vector const & normalM) const
798 {
799 scalar uM_n = uM * normalM;
800 scalar uP_n = uP * normalM;
801
802 vector average_normal_flux =
803 dealii::make_vectorized_array<Number>(0.5) * (uM * uM_n + uP * uP_n);
804
805 vector jump_value = uM - uP;
806
807 scalar lambda = calculate_lambda(uM_n, uP_n);
808
809 return (average_normal_flux + 0.5 * lambda * jump_value);
810 }
811
812 /*
813 * Divergence formulation: Calculate Lax-Friedrichs flux for linearly implicit formulation
814 * (linear transport with transport velocity w).
815 */
816 inline DEAL_II_ALWAYS_INLINE //
817 vector
818 calculate_lax_friedrichs_flux_linear_transport(vector const & wM,
819 vector const & wP,
820 vector const & uM,
821 vector const & uP,
822 vector const & normalM) const
823 {
824 scalar wM_n = wM * normalM;
825 scalar wP_n = wP * normalM;
826
827 vector average_normal_flux =
828 dealii::make_vectorized_array<Number>(0.5) * (uM * wM_n + uP * wP_n);
829
830 vector jump_value = uM - uP;
831
832 // the function calculate_lambda() is for the nonlinear operator with quadratic nonlinearity. In
833 // case of linear transport, lambda is reduced by a factor of 2.
834 scalar lambda = 0.5 * calculate_lambda(wM_n, wP_n);
835
836 return (average_normal_flux + 0.5 * lambda * jump_value);
837 }
838
839 /*
840 * Divergence formulation: Calculate Lax-Friedrichs flux for linearized operator.
841 */
842 inline DEAL_II_ALWAYS_INLINE //
843 vector
844 calculate_lax_friedrichs_flux_linearized(vector const & uM,
845 vector const & uP,
846 vector const & delta_uM,
847 vector const & delta_uP,
848 vector const & normalM) const
849 {
850 scalar uM_n = uM * normalM;
851 scalar uP_n = uP * normalM;
852
853 scalar delta_uM_n = delta_uM * normalM;
854 scalar delta_uP_n = delta_uP * normalM;
855
856 vector average_normal_flux =
857 dealii::make_vectorized_array<Number>(0.5) *
858 (uM * delta_uM_n + delta_uM * uM_n + uP * delta_uP_n + delta_uP * uP_n);
859
860 vector jump_value = delta_uM - delta_uP;
861
862 scalar lambda = calculate_lambda(uM_n, uP_n);
863
864 return (average_normal_flux + 0.5 * lambda * jump_value);
865 }
866
867 /*
868 * Convective formulation: Calculate upwind flux given an average normal velocity. This function
869 * is used for the nonlinear operator and for the linearly implicit treatment of the convective
870 * term.
871 */
872 inline DEAL_II_ALWAYS_INLINE //
873 vector
874 calculate_upwind_flux(vector const & uM,
875 vector const & uP,
876 scalar const & average_normal_velocity) const
877 {
878 vector average_velocity = 0.5 * (uM + uP);
879
880 vector jump_value = uM - uP;
881
882 return (average_normal_velocity * average_velocity +
883 data.upwind_factor * 0.5 * std::abs(average_normal_velocity) * jump_value);
884 }
885
886 /*
887 * Convective formulation: Calculate upwind flux for linearized operator.
888 */
889 inline DEAL_II_ALWAYS_INLINE //
890 vector
891 calculate_upwind_flux_linearized(vector const & uM,
892 vector const & uP,
893 vector const & u_grid,
894 vector const & delta_uM,
895 vector const & delta_uP,
896 vector const & normalM) const
897 {
898 vector average_velocity = 0.5 * (uM + uP);
899 vector delta_average_velocity = 0.5 * (delta_uM + delta_uP);
900
901 scalar average_normal_velocity = average_velocity * normalM;
902 if(data.ale)
903 average_normal_velocity -= u_grid * normalM;
904
905 scalar delta_average_normal_velocity = delta_average_velocity * normalM;
906
907 vector jump_value = delta_uM - delta_uP;
908
909 return (average_normal_velocity * delta_average_velocity +
910 delta_average_normal_velocity * average_velocity +
911 data.upwind_factor * 0.5 * std::abs(average_normal_velocity) * jump_value);
912 }
913
914 /*
915 * outflow BC according to Gravemeier et al. (2012)
916 */
917 inline DEAL_II_ALWAYS_INLINE //
918 void
919 apply_outflow_bc(vector & flux, scalar const & normal_velocity) const
920 {
921 // we need a factor indicating whether we have inflow or outflow
922 // on the Neumann part of the boundary.
923 // outflow: factor = 1.0 (do nothing, neutral element of multiplication)
924 // inflow: factor = 0.0 (set convective flux to zero)
925 scalar outflow_indicator = dealii::make_vectorized_array<Number>(1.0);
926
927 for(unsigned int v = 0; v < dealii::VectorizedArray<Number>::size(); ++v)
928 {
929 if(normal_velocity[v] < 0.0) // backflow at outflow boundary
930 outflow_indicator[v] = 0.0;
931 }
932
933 // set flux to zero in case of backflow
934 flux = outflow_indicator * flux;
935 }
936
937private:
939
940 // linearization velocity for nonlinear problems or transport velocity for "linearly implicit
941 // formulation" of convective term
942 lazy_ptr<VectorType> velocity;
943
944 // grid velocity for ALE problems
945 lazy_ptr<VectorType> grid_velocity;
946
947 std::shared_ptr<IntegratorCell> integrator_velocity;
948 std::shared_ptr<IntegratorFace> integrator_velocity_m;
949 std::shared_ptr<IntegratorFace> integrator_velocity_p;
950
951 std::shared_ptr<IntegratorCell> integrator_grid_velocity;
952 std::shared_ptr<IntegratorFace> integrator_grid_velocity_face;
953};
954
955
956} // namespace Operators
957
958template<int dim>
959struct ConvectiveOperatorData : public OperatorBaseData
960{
961 ConvectiveOperatorData() : OperatorBaseData(), quad_index_nonlinear(0)
962 {
963 }
964
965 /*
966 * Needs ConvectiveKernelData since it is not possible to remove all kernel parameters from
967 * function do_cell_integral(), i.e. the parameter FormulationConvectiveTerm is explicitly needed
968 * by ConvectiveOperator.
969 */
971
972 /*
973 * In addition to the quad_index in OperatorBaseData (which denotes the quadrature index for the
974 * linearized problem), an additional quadrature index has to be specified for the convective
975 * operator evaluation in case of explicit time integration or nonlinear residual evaluation).
976 */
977 unsigned int quad_index_nonlinear;
978
979 std::shared_ptr<BoundaryDescriptorU<dim> const> bc;
980};
981
982
983
984template<int dim, typename Number>
985class ConvectiveOperator : public OperatorBase<dim, Number, dim>
986{
987public:
988 typedef dealii::VectorizedArray<Number> scalar;
989 typedef dealii::Tensor<1, dim, dealii::VectorizedArray<Number>> vector;
990 typedef dealii::Tensor<2, dim, dealii::VectorizedArray<Number>> tensor;
991
992 typedef ConvectiveOperator<dim, Number> This;
993
994 typedef OperatorBase<dim, Number, dim> Base;
995
996 typedef typename Base::VectorType VectorType;
997 typedef typename Base::Range Range;
998 typedef typename Base::IntegratorCell IntegratorCell;
999 typedef typename Base::IntegratorFace IntegratorFace;
1000
1001 ConvectiveOperator()
1002 {
1003 }
1004
1005 void
1006 set_velocity_copy(VectorType const & src) const;
1007
1008 void
1009 set_velocity_ptr(VectorType const & src) const;
1010
1011 dealii::LinearAlgebra::distributed::Vector<Number> const &
1012 get_velocity() const;
1013
1014 void
1015 initialize(dealii::MatrixFree<dim, Number> const & matrix_free,
1016 dealii::AffineConstraints<Number> const & affine_constraints,
1017 ConvectiveOperatorData<dim> const & data,
1018 std::shared_ptr<Operators::ConvectiveKernel<dim, Number>> kernel);
1019
1020 /*
1021 * Evaluate nonlinear operator.
1022 */
1023 void
1024 evaluate_nonlinear_operator(VectorType & dst, VectorType const & src, Number const time) const;
1025
1026 void
1027 evaluate_nonlinear_operator_add(VectorType & dst,
1028 VectorType const & src,
1029 Number const time) const;
1030
1031 // these functions are not implemented for the convective operator. They only have to exist
1032 // due to the definition of the base class.
1033 void
1034 evaluate(VectorType & dst, VectorType const & src) const final;
1035
1036 void
1037 evaluate_add(VectorType & dst, VectorType const & src) const final;
1038
1039private:
1040 /*
1041 * Evaluation of nonlinear operator.
1042 */
1043 void
1044 cell_loop_nonlinear_operator(dealii::MatrixFree<dim, Number> const & matrix_free,
1045 VectorType & dst,
1046 VectorType const & src,
1047 Range const & cell_range) const;
1048
1049 void
1050 face_loop_nonlinear_operator(dealii::MatrixFree<dim, Number> const & matrix_free,
1051 VectorType & dst,
1052 VectorType const & src,
1053 Range const & face_range) const;
1054
1055 void
1056 boundary_face_loop_nonlinear_operator(dealii::MatrixFree<dim, Number> const & matrix_free,
1057 VectorType & dst,
1058 VectorType const & src,
1059 Range const & face_range) const;
1060
1061 void
1062 do_cell_integral_nonlinear_operator(IntegratorCell & integrator,
1063 IntegratorCell & integrator_u_grid) const;
1064
1065 void
1066 do_face_integral_nonlinear_operator(IntegratorFace & integrator_m,
1067 IntegratorFace & integrator_p,
1068 IntegratorFace & integrator_grid_velocity) const;
1069
1070 void
1071 do_boundary_integral_nonlinear_operator(IntegratorFace & integrator,
1072 IntegratorFace & integrator_grid_velocity,
1073 dealii::types::boundary_id const & boundary_id) const;
1074
1075 /*
1076 * Linearized operator.
1077 */
1078
1079 // Note: this function can only be used for the linearized operator.
1080 void
1081 reinit_cell_derived(IntegratorCell & integrator, unsigned int const cell) const final;
1082
1083 // Note: this function can only be used for the linearized operator.
1084 void
1085 reinit_face_derived(IntegratorFace & integrator_m,
1086 IntegratorFace & integrator_p,
1087 unsigned int const face) const final;
1088
1089 // Note: this function can only be used for the linearized operator.
1090 void
1091 reinit_boundary_face_derived(IntegratorFace & integrator_m, unsigned int const face) const final;
1092
1093 // Note: this function can only be used for the linearized operator.
1094 void
1095 reinit_face_cell_based_derived(IntegratorFace & integrator_m,
1096 IntegratorFace & integrator_p,
1097 unsigned int const cell,
1098 unsigned int const face,
1099 dealii::types::boundary_id const boundary_id) const final;
1100
1101 // linearized operator
1102 void
1103 do_cell_integral(IntegratorCell & integrator) const final;
1104
1105 // linearized operator
1106 void
1107 do_face_integral(IntegratorFace & integrator_m, IntegratorFace & integrator_p) const final;
1108
1109 // linearized operator
1110 void
1111 do_face_int_integral(IntegratorFace & integrator_m, IntegratorFace & integrator_p) const final;
1112
1113 // linearized operator
1114
1115 // TODO
1116 // This function is currently only needed due to limitations of deal.II which do
1117 // currently not allow to access neighboring data in case of cell-based face loops.
1118 // Once this functionality is available, this function should be removed again.
1119 void
1120 do_face_int_integral_cell_based(IntegratorFace & integrator_m,
1121 IntegratorFace & integrator_p) const final;
1122
1123 // linearized operator
1124 void
1125 do_face_ext_integral(IntegratorFace & integrator_m, IntegratorFace & integrator_p) const final;
1126
1127 // linearized operator
1128 void
1129 do_boundary_integral(IntegratorFace & integrator,
1130 OperatorType const & operator_type,
1131 dealii::types::boundary_id const & boundary_id) const final;
1132
1133 ConvectiveOperatorData<dim> operator_data;
1134
1135 std::shared_ptr<Operators::ConvectiveKernel<dim, Number>> kernel;
1136};
1137
1138} // namespace IncNS
1139} // namespace ExaDG
1140
1141
1142#endif /* INCLUDE_EXADG_INCOMPRESSIBLE_NAVIER_STOKES_SPATIAL_DISCRETIZATION_OPERATORS_CONVECTIVE_OPERATOR_H_ \
1143 */
Definition convective_operator.h:63
Definition lazy_ptr.h:29
Definition driver.cpp:33
Definition convective_operator.h:960
Definition convective_operator.h:37
Definition integrator_flags.h:30
Definition mapping_flags.h:31