chaos-polymtl / lethe

Repository for the open-source lethe CFD/DEM/CFD-DEM project
https://chaos-polymtl.github.io/lethe/index.html
Apache License 2.0
278 stars 59 forks source link

Unify SUPG/PSPG and GLS matrix-free operators #1053

Closed lpsaavedra closed 8 months ago

lpsaavedra commented 8 months ago

Description of the problem

There was a lot of code duplication introduced in #1049 since the GLS operator only adds two additional terms to the SUPG/PSPG operator.

Description of the solution

This PR unifies the SUPG/PSPG and GLS steady operators, and the SUPG/PSPG and GLS transient operators, in only one operator that uses the stabilization type and the is_bdf function to identify what terms to take into account.

How Has This Been Tested?

All the matrix-free application tests pass without any issue.

peterrum commented 8 months ago

Personally, I would go one step further by merging the steady and transient operator. The diff is minor:

index 195e210..42d1ac2 100644
--- a/a.cc
+++ b/b.cc
@@ -1,18 +1,24 @@
+
+
+template <int dim, typename number>
+NavierStokesTransientOperator<dim, number>::NavierStokesTransientOperator() =
+  default;
+
 /**
  * The expressions calculated in this cell integral are:
- * (q,∇δu) + (v,(u·∇)δu) + (v,(δu·∇)u) - (∇·v,δp) + ν(∇v,∇δu) (Weak form
- * Jacobian),
+ * (q,∇δu) + (v,∂t δu) + (v,(u·∇)δu) + (v,(δu·∇)u) - (∇·v,δp) + ν(∇v,∇δu) (Weak
+ * form Jacobian),
  * plus three additional terms in the case of SUPG-PSPG stabilization:
- * \+ ((u·∇)δu + (δu·∇)u + ∇δp - ν∆δu)τ·∇q (PSPG Jacobian)
- * \+ ((u·∇)δu + (δu·∇)u + ∇δp - ν∆δu)τu·∇v (SUPG Jacobian Part 1)
- * \+ ((u·∇)u + ∇p - ν∆u - f )τδu·∇v (SUPG Jacobian Part 2),
+ * \+ (∂t δu +(u·∇)δu + (δu·∇)u + ∇δp - ν∆δu)τ·∇q (PSPG Jacobian)
+ * \+ (∂t δu +(u·∇)δu + (δu·∇)u + ∇δp - ν∆δu)τu·∇v (SUPG Jacobian Part 1)
+ * \+ (∂t u +(u·∇)u + ∇p - ν∆u - f )τδu·∇v (SUPG Jacobian Part 2),
  * plus two additional terms in the case of full gls stabilization:
- * \+ ((u·∇)δu + (δu·∇)u + ∇δp - ν∆δu)τ(−ν∆v) (GLS Jacobian)
+ * \+ (∂t δu +(u·∇)δu + (δu·∇)u + ∇δp - ν∆δu)τ(−ν∆v) (GLS Jacobian)
  * \+ (∇·δu)τ'(∇·v) (LSIC Jacobian).
  */
 template <int dim, typename number>
 void
-NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(
+NavierStokesTransientOperator<dim, number>::do_cell_integral_local(
   FECellIntegrator &integrator) const
 {
   integrator.evaluate(EvaluationFlags::values | EvaluationFlags::gradients |
@@ -22,6 +28,15 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(

   const auto h = integrator.read_cell_data(this->get_element_size());

+  // Vector for the BDF coefficients
+  const Vector<double> &bdf_coefs =
+    this->simulation_control->get_bdf_coefficients();
+  const auto time_steps_vector =
+    this->simulation_control->get_time_steps_vector();
+  const double dt  = time_steps_vector[0];
+  const double sdt = 1. / dt;
+
+
   for (const auto q : integrator.quadrature_point_indices())
     {
       // Evaluate source term function
@@ -50,15 +65,20 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(
       auto previous_hessian_diagonal =
         this->nonlinear_previous_hessian_diagonal(cell, q);

+      // Time derivatives of previous solutions
+      auto previous_time_derivatives =
+        this->time_derivatives_previous_solutions(cell, q);
+
       // Calculate tau
       VectorizedArray<number> u_mag_squared = 1e-12;
       for (unsigned int k = 0; k < dim; ++k)
         u_mag_squared += Utilities::fixed_power<2>(previous_values[k]);

       const auto tau =
-        1. / std::sqrt(4. * u_mag_squared / h / h +
-                       9. * Utilities::fixed_power<2>(
-                              4. * this->kinematic_viscosity / (h * h)));
+        1. /
+        std::sqrt(Utilities::fixed_power<2>(sdt) + 4. * u_mag_squared / h / h +
+                  9. * Utilities::fixed_power<2>(
+                         4. * this->kinematic_viscosity / (h * h)));

       const auto tau_lsic = std::sqrt(u_mag_squared) * h * 0.5;

@@ -78,6 +98,8 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(
               value_result[i] += gradient[i][k] * previous_values[k] +
                                  previous_gradient[i][k] * value[k];
             }
+          // +(v,∂t δu)
+          value_result[i] += bdf_coefs[0] * value[i];
         }

       // PSPG Jacobian
@@ -91,6 +113,8 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(
                        gradient[i][k] * previous_values[k] +
                        previous_gradient[i][k] * value[k]);
             }
+          // +(∂t δu)·τ∇q
+          gradient_result[dim][i] += tau * bdf_coefs[0] * value[i];
         }
       // (∇δp)τ·∇q
       gradient_result[dim] += tau * gradient[dim];
@@ -110,24 +134,27 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(
                      previous_gradient[i][l] * value[l] -
                      this->kinematic_viscosity * hessian_diagonal[i][l]);
                 }
-
-              // +(∇δp)τ(u·∇)v
+              // +(∇δp + ∂t δu)τ(u·∇)v
               gradient_result[i][k] +=
-                tau * previous_values[k] * gradient[dim][i];
+                tau * previous_values[k] *
+                (gradient[dim][i] + bdf_coefs[0] * value[i]);

               // Part 2
               for (unsigned int l = 0; l < dim; ++l)
                 {
-                  // +((u·∇)u -ν∆u)τ(δu·∇)v
+                  // +((u·∇)u - ν∆u)τ(δu·∇)v
                   gradient_result[i][k] +=
                     tau * value[k] *
                     (previous_gradient[i][l] * previous_values[l] -
                      this->kinematic_viscosity *
                        previous_hessian_diagonal[i][l]);
                 }
-              // +(∇p - f)τ(δu·∇)v
+              // +(∇p - f + ∂t u)τ(δu·∇)v
               gradient_result[i][k] +=
-                tau * value[k] * (previous_gradient[dim][i] - source_value[i]);
+                tau * value[k] *
+                (previous_gradient[dim][i] - source_value[i] +
+                 bdf_coefs[0] * previous_values[i] +
+                 previous_time_derivatives[i]);
             }
         }

@@ -149,9 +176,10 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(
                          this->kinematic_viscosity * hessian_diagonal[i][l]);
                     }

-                  // +(∇δp)τ(−ν∆v)
+                  // +(∇δp + ∂t δu)τ(−ν∆v)
                   hessian_result[i][k][k] +=
-                    tau * -this->kinematic_viscosity * gradient[dim][i];
+                    tau * -this->kinematic_viscosity *
+                    (gradient[dim][i] + bdf_coefs[0] * value[i]);

                   // LSIC term
                   // (∇·δu)τ'(∇·v)
@@ -160,7 +188,6 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(
             }
         }

-
       integrator.submit_gradient(gradient_result, q);
       integrator.submit_value(value_result, q);
       integrator.submit_hessian(hessian_result, q);
@@ -172,17 +199,17 @@ NavierStokesSteadyOperator<dim, number>::do_cell_integral_local(

 /**
  * The expressions calculated in this cell integral are:
- * (q, ∇·u) + (v,(u·∇)u) - (∇·v,p) + ν(∇v,∇u) - (v,f) (Weak form),
+ * (q, ∇·u) + (v,∂t u) + (v,(u·∇)u) - (∇·v,p) + ν(∇v,∇u) - (v,f) (Weak form),
  * plus two additional terms in the case of SUPG-PSPG stabilization:
- * \+ ((u·∇)u + ∇p - ν∆u - f)τ∇·q (PSPG term)
- * \+ ((u·∇)u + ∇p - ν∆u - f)τu·∇v (SUPG term),
+ * \+ (∂t u +(u·∇)u + ∇p - ν∆u - f)τ∇·q (PSPG term)
+ * \+ (∂t u +(u·∇)u + ∇p - ν∆u - f)τu·∇v (SUPG term),
  * plus two additional terms in the case of full gls stabilization:
- * \+ ((u·∇)u + ∇p - ν∆u - f)τ(−ν∆v) (GLS term)
+ * \+ (∂t u +(u·∇)u + ∇p - ν∆u - f)τ(−ν∆v) (GLS term)
  * \+ (∇·u)τ'(∇·v) (LSIC term).
  */
 template <int dim, typename number>
 void
-NavierStokesSteadyOperator<dim, number>::local_evaluate_residual(
+NavierStokesTransientOperator<dim, number>::local_evaluate_residual(
   const MatrixFree<dim, number>               &matrix_free,
   VectorType                                  &dst,
   const VectorType                            &src,
@@ -199,6 +226,14 @@ NavierStokesSteadyOperator<dim, number>::local_evaluate_residual(

       const auto h = integrator.read_cell_data(this->get_element_size());

+      // Vector for the BDF coefficients
+      const Vector<double> &bdf_coefs =
+        this->simulation_control->get_bdf_coefficients();
+      const auto time_steps_vector =
+        this->simulation_control->get_time_steps_vector();
+      const double dt  = time_steps_vector[0];
+      const double sdt = 1. / dt;
+
       for (const auto q : integrator.quadrature_point_indices())
         {
           // Evaluate source term function
@@ -216,13 +251,18 @@ NavierStokesSteadyOperator<dim, number>::local_evaluate_residual(
           typename FECellIntegrator::gradient_type hessian_diagonal =
             integrator.get_hessian_diagonal(q);

+          // Time derivatives of previoussolutions
+          auto previous_time_derivatives =
+            this->time_derivatives_previous_solutions(cell, q);
+
           // Calculate tau
           VectorizedArray<number> u_mag_squared = 1e-12;
           for (unsigned int k = 0; k < dim; ++k)
             u_mag_squared += Utilities::fixed_power<2>(value[k]);

           const auto tau =
-            1. / std::sqrt(4. * u_mag_squared / h / h +
+            1. / std::sqrt(Utilities::fixed_power<2>(sdt) +
+                           4. * u_mag_squared / h / h +
                            9. * Utilities::fixed_power<2>(
                                   4. * this->kinematic_viscosity / (h * h)));

@@ -240,8 +280,9 @@ NavierStokesSteadyOperator<dim, number>::local_evaluate_residual(
               gradient_result[i] = this->kinematic_viscosity * gradient[i];
               // -(∇·v,p)
               gradient_result[i][i] += -value[dim];
-              // -(v,f)
-              value_result[i] = -source_value[i];
+              // +(v,-f + ∂t u)
+              value_result[i] = -source_value[i] + bdf_coefs[0] * value[i] +
+                                previous_time_derivatives[i];
               // +(q,∇·u)
               value_result[dim] += gradient[i][i];

@@ -255,16 +296,17 @@ NavierStokesSteadyOperator<dim, number>::local_evaluate_residual(
           // PSPG term
           for (unsigned int i = 0; i < dim; ++i)
             {
-              // (-f)·τ∇q
-              gradient_result[dim][i] += -tau * source_value[i];
-
               for (unsigned int k = 0; k < dim; ++k)
                 {
-                  //(-ν∆u + (u·∇)u)·τ∇q
+                  // (-ν∆u + (u·∇)u)·τ∇q
                   gradient_result[dim][i] +=
                     tau * (-this->kinematic_viscosity * hessian_diagonal[i][k] +
                            gradient[i][k] * value[k]);
                 }
+              // +(-f + ∂t u)·τ∇q
+              gradient_result[dim][i] +=
+                tau * (-source_value[i] + bdf_coefs[0] * value[i] +
+                       previous_time_derivatives[i]);
             }
           // +(∇p)τ∇·q
           gradient_result[dim] += tau * gradient[dim];
@@ -276,15 +318,20 @@ NavierStokesSteadyOperator<dim, number>::local_evaluate_residual(
                 {
                   for (unsigned int l = 0; l < dim; ++l)
                     {
-                      // (-ν∆u + (u·∇)u)τ(u·∇)v
+                      // (-ν∆u)τ(u·∇)v
+                      gradient_result[i][k] +=
+                        -tau * this->kinematic_viscosity * value[k] *
+                        hessian_diagonal[i][l];
+
+                      // + ((u·∇)u)τ(u·∇)v
                       gradient_result[i][k] +=
-                        tau * value[k] *
-                        (-this->kinematic_viscosity * hessian_diagonal[i][l] +
-                         gradient[i][l] * value[l]);
+                        tau * value[k] * gradient[i][l] * value[l];
                     }
-                  // + (∇p - f)τ(u·∇)v
+                  // + (∇p - f + ∂t u)τ(u·∇)v
                   gradient_result[i][k] +=
-                    tau * value[k] * (gradient[dim][i] - source_value[i]);
+                    tau * value[k] *
+                    (gradient[dim][i] - source_value[i] +
+                     bdf_coefs[0] * value[i] + previous_time_derivatives[i]);
                 }
             }

@@ -305,10 +352,12 @@ NavierStokesSteadyOperator<dim, number>::local_evaluate_residual(
                                hessian_diagonal[i][l] +
                              gradient[i][l] * value[l]);
                         }
-                      // + (∇p - f)τ(−ν∆v)
+                      // + (∇p - f + ∂t u)τ(−ν∆v)
                       hessian_result[i][k][k] +=
                         tau * -this->kinematic_viscosity *
-                        (gradient[dim][i] - source_value[i]);
+                        (gradient[dim][i] - source_value[i] +
+                         +bdf_coefs[0] * value[i] +
+                         previous_time_derivatives[i]);

                       // LSIC term
                       // (∇·u)τ'(∇·v)
peterrum commented 8 months ago

I would try to avoid using template arguments. The overhead of the if-statements should be hardy measurements, in particular, the branch can be perfectly predicted. Once all the operators are merged, we can test this.

However instead of templating the class, I would rather only template the relevant functions and convert the runtime argument to template arguments here:

https://github.com/lethe-cfd/lethe/blob/3568eaa0d82a45601338fc2ac4b62c3fd0b5314a/source/solvers/mf_navier_stokes_operators.cc#L509-L510

@blaisb What do you think?

blaisb commented 8 months ago

I would try to avoid using template arguments. The overhead of the if-statements should be hardy measurements, in particular, the branch can be perfectly predicted. Once all the operators are merged, we can test this.

However instead of templating the class, I would rather only template the relevant functions and convert the runtime argument to template arguments here:

https://github.com/lethe-cfd/lethe/blob/3568eaa0d82a45601338fc2ac4b62c3fd0b5314a/source/solvers/mf_navier_stokes_operators.cc#L509-L510

@blaisb What do you think?

Okidoke You are right about the branch prediction. Let's give it a try and measure

lpsaavedra commented 8 months ago

I have finally merged the steady-state and transient operators... this is ready for a re-review

blaisb commented 8 months ago

Once @peterrum comments are addressed this will be ready for a merge