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