deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
step-35.h
Go to the documentation of this file.
1 ((n % vel_update_prec == 0) || (n == 2)));
1411 *   verbose_cout << " Projection Step" << std::endl;
1412 *   projection_step(/* reinit_prec= */ (n == 2));
1413 *   verbose_cout << " Updating the Pressure" << std::endl;
1414 *   update_pressure(/* reinit_prec= */ (n == 2));
1415 *   vel_exact.advance_time(dt);
1416 *   }
1417 *   output_results(n_steps);
1418 *   }
1419 *  
1420 *  
1421 *  
1422 *   template <int dim>
1423 *   void NavierStokesProjection<dim>::interpolate_velocity()
1424 *   {
1425 *   for (unsigned int d = 0; d < dim; ++d)
1426 *   {
1427 *   u_star[d].equ(2., u_n[d]);
1428 *   u_star[d] -= u_n_minus_1[d];
1429 *   }
1430 *   }
1431 *  
1432 *  
1433 * @endcode
1434 *
1435 *
1436 * <a name="step_35-codeNavierStokesProjectiondiffusion_stepcode"></a>
1437 * <h4><code>NavierStokesProjection::diffusion_step</code></h4>
1438 *
1439
1440 *
1441 * The implementation of a diffusion step. Note that the expensive operation
1442 * is the diffusion solve at the end of the function, which we have to do once
1443 * for each velocity component. To accelerate things a bit, we allow to do
1444 * this in %parallel, using the Threads::new_task function which makes sure
1445 * that the <code>dim</code> solves are all taken care of and are scheduled to
1446 * available processors: if your machine has more than one processor core and
1447 * no other parts of this program are using resources currently, then the
1448 * diffusion solves will run in %parallel. On the other hand, if your system
1449 * has only one processor core then running things in %parallel would be
1450 * inefficient (since it leads, for example, to cache congestion) and things
1451 * will be executed sequentially.
1452 *
1453 * @code
1454 *   template <int dim>
1455 *   void NavierStokesProjection<dim>::diffusion_step(const bool reinit_prec)
1456 *   {
1457 *   pres_tmp.equ(-1., pres_n);
1458 *   pres_tmp.add(-4. / 3., phi_n, 1. / 3., phi_n_minus_1);
1459 *  
1460 *   assemble_advection_term();
1461 *  
1462 *   for (unsigned int d = 0; d < dim; ++d)
1463 *   {
1464 *   force[d] = 0.;
1465 *   v_tmp.equ(2. / dt, u_n[d]);
1466 *   v_tmp.add(-.5 / dt, u_n_minus_1[d]);
1467 *   vel_Mass.vmult_add(force[d], v_tmp);
1468 *  
1469 *   pres_Diff[d].vmult_add(force[d], pres_tmp);
1470 *   u_n_minus_1[d] = u_n[d];
1471 *  
1472 *   vel_it_matrix[d].copy_from(vel_Laplace_plus_Mass);
1473 *   vel_it_matrix[d].add(1., vel_Advection);
1474 *  
1475 *   vel_exact.set_component(d);
1476 *   boundary_values.clear();
1477 *   for (const auto &boundary_id : boundary_ids)
1478 *   {
1479 *   switch (boundary_id)
1480 *   {
1481 *   case 1:
1483 *   dof_handler_velocity,
1484 *   boundary_id,
1485 *   Functions::ZeroFunction<dim>(),
1486 *   boundary_values);
1487 *   break;
1488 *   case 2:
1489 *   VectorTools::interpolate_boundary_values(dof_handler_velocity,
1490 *   boundary_id,
1491 *   vel_exact,
1492 *   boundary_values);
1493 *   break;
1494 *   case 3:
1495 *   if (d != 0)
1497 *   dof_handler_velocity,
1498 *   boundary_id,
1500 *   boundary_values);
1501 *   break;
1502 *   case 4:
1504 *   dof_handler_velocity,
1505 *   boundary_id,
1507 *   boundary_values);
1508 *   break;
1509 *   default:
1511 *   }
1512 *   }
1513 *   MatrixTools::apply_boundary_values(boundary_values,
1514 *   vel_it_matrix[d],
1515 *   u_n[d],
1516 *   force[d]);
1517 *   }
1518 *  
1519 *  
1521 *   for (unsigned int d = 0; d < dim; ++d)
1522 *   {
1523 *   if (reinit_prec)
1524 *   prec_velocity[d].initialize(vel_it_matrix[d],
1526 *   vel_diag_strength, vel_off_diagonals));
1527 *   tasks += Threads::new_task(
1528 *   &NavierStokesProjection<dim>::diffusion_component_solve, *this, d);
1529 *   }
1530 *   tasks.join_all();
1531 *   }
1532 *  
1533 *  
1534 *  
1535 *   template <int dim>
1536 *   void
1537 *   NavierStokesProjection<dim>::diffusion_component_solve(const unsigned int d)
1538 *   {
1539 *   SolverControl solver_control(vel_max_its, vel_eps * force[d].l2_norm());
1541 *   solver_control,
1542 *   SolverGMRES<Vector<double>>::AdditionalData(vel_Krylov_size));
1543 *   gmres.solve(vel_it_matrix[d], u_n[d], force[d], prec_velocity[d]);
1544 *   }
1545 *  
1546 *  
1547 * @endcode
1548 *
1549 *
1550 * <a name="step_35-codeNavierStokesProjectionassemble_advection_termcode"></a>
1551 * <h4> <code>NavierStokesProjection::assemble_advection_term</code> </h4>
1552 *
1553
1554 *
1555 * The following few functions deal with assembling the advection terms, which
1556 * is the part of the system matrix for the diffusion step that changes at
1557 * every time step. As mentioned above, we will run the assembly loop over all
1558 * cells in %parallel, using the WorkStream class and other
1559 * facilities as described in the documentation topic on @ref threads.
1560 *
1561 * @code
1562 *   template <int dim>
1563 *   void NavierStokesProjection<dim>::assemble_advection_term()
1564 *   {
1565 *   vel_Advection = 0.;
1566 *   AdvectionPerTaskData data(fe_velocity.n_dofs_per_cell());
1567 *   AdvectionScratchData scratch(fe_velocity,
1568 *   quadrature_velocity,
1572 *   dof_handler_velocity.begin_active(),
1573 *   dof_handler_velocity.end(),
1574 *   *this,
1575 *   &NavierStokesProjection<dim>::assemble_one_cell_of_advection,
1576 *   &NavierStokesProjection<dim>::copy_advection_local_to_global,
1577 *   scratch,
1578 *   data);
1579 *   }
1580 *  
1581 *  
1582 *  
1583 *   template <int dim>
1584 *   void NavierStokesProjection<dim>::assemble_one_cell_of_advection(
1585 *   const typename DoFHandler<dim>::active_cell_iterator &cell,
1586 *   AdvectionScratchData &scratch,
1587 *   AdvectionPerTaskData &data)
1588 *   {
1589 *   scratch.fe_val.reinit(cell);
1590 *   cell->get_dof_indices(data.local_dof_indices);
1591 *   for (unsigned int d = 0; d < dim; ++d)
1592 *   {
1593 *   scratch.fe_val.get_function_values(u_star[d], scratch.u_star_tmp);
1594 *   for (unsigned int q = 0; q < scratch.nqp; ++q)
1595 *   scratch.u_star_local[q][d] = scratch.u_star_tmp[q];
1596 *   }
1597 *  
1598 *   for (unsigned int d = 0; d < dim; ++d)
1599 *   {
1600 *   scratch.fe_val.get_function_gradients(u_star[d], scratch.grad_u_star);
1601 *   for (unsigned int q = 0; q < scratch.nqp; ++q)
1602 *   {
1603 *   if (d == 0)
1604 *   scratch.u_star_tmp[q] = 0.;
1605 *   scratch.u_star_tmp[q] += scratch.grad_u_star[q][d];
1606 *   }
1607 *   }
1608 *  
1609 *   data.local_advection = 0.;
1610 *   for (unsigned int q = 0; q < scratch.nqp; ++q)
1611 *   for (unsigned int i = 0; i < scratch.dpc; ++i)
1612 *   for (unsigned int j = 0; j < scratch.dpc; ++j)
1613 *   data.local_advection(i, j) += (scratch.u_star_local[q] *
1614 *   scratch.fe_val.shape_grad(j, q) *
1615 *   scratch.fe_val.shape_value(i, q)
1616 *   +
1617 *   0.5 *
1618 *   scratch.u_star_tmp[q] *
1619 *   scratch.fe_val.shape_value(i, q) *
1620 *   scratch.fe_val.shape_value(j, q))
1621 *   * scratch.fe_val.JxW(q);
1622 *   }
1623 *  
1624 *  
1625 *  
1626 *   template <int dim>
1627 *   void NavierStokesProjection<dim>::copy_advection_local_to_global(
1628 *   const AdvectionPerTaskData &data)
1629 *   {
1630 *   for (unsigned int i = 0; i < fe_velocity.n_dofs_per_cell(); ++i)
1631 *   for (unsigned int j = 0; j < fe_velocity.n_dofs_per_cell(); ++j)
1632 *   vel_Advection.add(data.local_dof_indices[i],
1633 *   data.local_dof_indices[j],
1634 *   data.local_advection(i, j));
1635 *   }
1636 *  
1637 *  
1638 *  
1639 * @endcode
1640 *
1641 *
1642 * <a name="step_35-codeNavierStokesProjectionprojection_stepcode"></a>
1643 * <h4><code>NavierStokesProjection::projection_step</code></h4>
1644 *
1645
1646 *
1647 * This implements the projection step:
1648 *
1649 * @code
1650 *   template <int dim>
1651 *   void NavierStokesProjection<dim>::projection_step(const bool reinit_prec)
1652 *   {
1653 *   pres_iterative.copy_from(pres_Laplace);
1654 *  
1655 *   pres_tmp = 0.;
1656 *   for (unsigned d = 0; d < dim; ++d)
1657 *   pres_Diff[d].Tvmult_add(pres_tmp, u_n[d]);
1658 *  
1659 *   phi_n_minus_1 = phi_n;
1660 *  
1661 *   static std::map<types::global_dof_index, double> bval;
1662 *   if (reinit_prec)
1663 *   VectorTools::interpolate_boundary_values(dof_handler_pressure,
1664 *   3,
1666 *   bval);
1667 *  
1668 *   MatrixTools::apply_boundary_values(bval, pres_iterative, phi_n, pres_tmp);
1669 *  
1670 *   if (reinit_prec)
1671 *   prec_pres_Laplace.initialize(pres_iterative,
1673 *   vel_diag_strength, vel_off_diagonals));
1674 *  
1675 *   SolverControl solvercontrol(vel_max_its, vel_eps * pres_tmp.l2_norm());
1676 *   SolverCG<Vector<double>> cg(solvercontrol);
1677 *   cg.solve(pres_iterative, phi_n, pres_tmp, prec_pres_Laplace);
1678 *  
1679 *   phi_n *= 1.5 / dt;
1680 *   }
1681 *  
1682 *  
1683 * @endcode
1684 *
1685 *
1686 * <a name="step_35-codeNavierStokesProjectionupdate_pressurecode"></a>
1687 * <h4> <code>NavierStokesProjection::update_pressure</code> </h4>
1688 *
1689
1690 *
1691 * This is the pressure update step of the projection method. It implements
1692 * the standard formulation of the method, that is @f[ p^{n+1} = p^n +
1693 * \phi^{n+1}, @f] or the rotational form, which is @f[ p^{n+1} = p^n +
1694 * \phi^{n+1} - \frac{1}{Re} \nabla\cdot u^{n+1}. @f]
1695 *
1696 * @code
1697 *   template <int dim>
1698 *   void NavierStokesProjection<dim>::update_pressure(const bool reinit_prec)
1699 *   {
1700 *   pres_n_minus_1 = pres_n;
1701 *   switch (type)
1702 *   {
1703 *   case RunTimeParameters::Method::standard:
1704 *   pres_n += phi_n;
1705 *   break;
1706 *   case RunTimeParameters::Method::rotational:
1707 *   if (reinit_prec)
1708 *   prec_mass.initialize(pres_Mass);
1709 *   pres_n = pres_tmp;
1710 *   prec_mass.solve(pres_n);
1711 *   pres_n.sadd(1. / Re, 1., pres_n_minus_1);
1712 *   pres_n += phi_n;
1713 *   break;
1714 *   default:
1716 *   };
1717 *   }
1718 *  
1719 *  
1720 * @endcode
1721 *
1722 *
1723 * <a name="step_35-codeNavierStokesProjectionoutput_resultscode"></a>
1724 * <h4> <code>NavierStokesProjection::output_results</code> </h4>
1725 *
1726
1727 *
1728 * This method plots the current solution. The main difficulty is that we want
1729 * to create a single output file that contains the data for all velocity
1730 * components, the pressure, and also the vorticity of the flow. On the other
1731 * hand, velocities and the pressure live on separate DoFHandler objects, and
1732 * so can't be written to the same file using a single DataOut object. As a
1733 * consequence, we have to work a bit harder to get the various pieces of data
1734 * into a single DoFHandler object, and then use that to drive graphical
1735 * output.
1736 *
1737
1738 *
1739 * We will not elaborate on this process here, but rather refer to @ref step_32 "step-32",
1740 * where a similar procedure is used (and is documented) to create a joint
1741 * DoFHandler object for all variables.
1742 *
1743
1744 *
1745 * Let us also note that we here compute the vorticity as a scalar quantity in
1746 * a separate function, using the @f$L^2@f$ projection of the quantity
1747 * @f$\text{curl} u@f$ onto the finite element space used for the components of
1748 * the velocity. In principle, however, we could also have computed it as a
1749 * pointwise quantity from the velocity, and do so through the
1750 * DataPostprocessor mechanism discussed in @ref step_29 "step-29" and @ref step_33 "step-33".
1751 *
1752 * @code
1753 *   template <int dim>
1754 *   void NavierStokesProjection<dim>::output_results(const unsigned int step)
1755 *   {
1756 *   assemble_vorticity((step == 1));
1757 *   const FESystem<dim> joint_fe(fe_velocity ^ dim, fe_pressure, fe_velocity);
1758 *   DoFHandler<dim> joint_dof_handler(triangulation);
1759 *   joint_dof_handler.distribute_dofs(joint_fe);
1760 *   Assert(joint_dof_handler.n_dofs() ==
1761 *   ((dim + 1) * dof_handler_velocity.n_dofs() +
1762 *   dof_handler_pressure.n_dofs()),
1763 *   ExcInternalError());
1764 *   Vector<double> joint_solution(joint_dof_handler.n_dofs());
1765 *   std::vector<types::global_dof_index> loc_joint_dof_indices(
1766 *   joint_fe.n_dofs_per_cell()),
1767 *   loc_vel_dof_indices(fe_velocity.n_dofs_per_cell()),
1768 *   loc_pres_dof_indices(fe_pressure.n_dofs_per_cell());
1769 *   typename DoFHandler<dim>::active_cell_iterator
1770 *   joint_cell = joint_dof_handler.begin_active(),
1771 *   joint_endc = joint_dof_handler.end(),
1772 *   vel_cell = dof_handler_velocity.begin_active(),
1773 *   pres_cell = dof_handler_pressure.begin_active();
1774 *   for (; joint_cell != joint_endc; ++joint_cell, ++vel_cell, ++pres_cell)
1775 *   {
1776 *   joint_cell->get_dof_indices(loc_joint_dof_indices);
1777 *   vel_cell->get_dof_indices(loc_vel_dof_indices);
1778 *   pres_cell->get_dof_indices(loc_pres_dof_indices);
1779 *   for (unsigned int i = 0; i < joint_fe.n_dofs_per_cell(); ++i)
1780 *   switch (joint_fe.system_to_base_index(i).first.first)
1781 *   {
1782 *   case 0:
1783 *   Assert(joint_fe.system_to_base_index(i).first.second < dim,
1784 *   ExcInternalError());
1785 *   joint_solution(loc_joint_dof_indices[i]) =
1786 *   u_n[joint_fe.system_to_base_index(i).first.second](
1787 *   loc_vel_dof_indices[joint_fe.system_to_base_index(i)
1788 *   .second]);
1789 *   break;
1790 *   case 1:
1791 *   Assert(joint_fe.system_to_base_index(i).first.second == 0,
1792 *   ExcInternalError());
1793 *   joint_solution(loc_joint_dof_indices[i]) =
1794 *   pres_n(loc_pres_dof_indices[joint_fe.system_to_base_index(i)
1795 *   .second]);
1796 *   break;
1797 *   case 2:
1798 *   Assert(joint_fe.system_to_base_index(i).first.second == 0,
1799 *   ExcInternalError());
1800 *   joint_solution(loc_joint_dof_indices[i]) = rot_u(
1801 *   loc_vel_dof_indices[joint_fe.system_to_base_index(i).second]);
1802 *   break;
1803 *   default:
1804 *   DEAL_II_ASSERT_UNREACHABLE();
1805 *   }
1806 *   }
1807 *   std::vector<std::string> joint_solution_names(dim, "v");
1808 *   joint_solution_names.emplace_back("p");
1809 *   joint_solution_names.emplace_back("rot_u");
1810 *   DataOut<dim> data_out;
1811 *   data_out.attach_dof_handler(joint_dof_handler);
1812 *   std::vector<DataComponentInterpretation::DataComponentInterpretation>
1813 *   component_interpretation(
1814 *   dim + 2, DataComponentInterpretation::component_is_part_of_vector);
1815 *   component_interpretation[dim] =
1816 *   DataComponentInterpretation::component_is_scalar;
1817 *   component_interpretation[dim + 1] =
1818 *   DataComponentInterpretation::component_is_scalar;
1819 *   data_out.add_data_vector(joint_solution,
1820 *   joint_solution_names,
1821 *   DataOut<dim>::type_dof_data,
1822 *   component_interpretation);
1823 *   data_out.build_patches(deg + 1);
1824 *   std::ofstream output("solution-" + Utilities::int_to_string(step, 5) +
1825 *   ".vtk");
1826 *   data_out.write_vtk(output);
1827 *   }
1828 *  
1829 *  
1830 *  
1831 * @endcode
1832 *
1833 * Following is the helper function that computes the vorticity by projecting
1834 * the term @f$\text{curl} u@f$ onto the finite element space used for the
1835 * components of the velocity. The function is only called whenever we
1836 * generate graphical output, so not very often, and as a consequence we
1837 * didn't bother parallelizing it using the WorkStream concept as we do for
1838 * the other assembly functions. That should not be overly complicated,
1839 * however, if needed. Moreover, the implementation that we have here only
1840 * works for 2d, so we bail if that is not the case.
1841 *
1842 * @code
1843 *   template <int dim>
1844 *   void NavierStokesProjection<dim>::assemble_vorticity(const bool reinit_prec)
1845 *   {
1846 *   Assert(dim == 2, ExcNotImplemented());
1847 *   if (reinit_prec)
1848 *   prec_vel_mass.initialize(vel_Mass);
1849 *  
1850 *   FEValues<dim> fe_val_vel(fe_velocity,
1851 *   quadrature_velocity,
1853 *   update_values);
1854 *   const unsigned int dpc = fe_velocity.n_dofs_per_cell(),
1855 *   nqp = quadrature_velocity.size();
1856 *   std::vector<types::global_dof_index> ldi(dpc);
1857 *   Vector<double> loc_rot(dpc);
1858 *  
1859 *   std::vector<Tensor<1, dim>> grad_u1(nqp), grad_u2(nqp);
1860 *   rot_u = 0.;
1861 *  
1862 *   for (const auto &cell : dof_handler_velocity.active_cell_iterators())
1863 *   {
1864 *   fe_val_vel.reinit(cell);
1865 *   cell->get_dof_indices(ldi);
1866 *   fe_val_vel.get_function_gradients(u_n[0], grad_u1);
1867 *   fe_val_vel.get_function_gradients(u_n[1], grad_u2);
1868 *   loc_rot = 0.;
1869 *   for (unsigned int q = 0; q < nqp; ++q)
1870 *   for (unsigned int i = 0; i < dpc; ++i)
1871 *   loc_rot(i) += (grad_u2[q][0] - grad_u1[q][1]) *
1872 *   fe_val_vel.shape_value(i, q) *
1873 *   fe_val_vel.JxW(q);
1874 *  
1875 *   for (unsigned int i = 0; i < dpc; ++i)
1876 *   rot_u(ldi[i]) += loc_rot(i);
1877 *   }
1878 *  
1879 *   prec_vel_mass.solve(rot_u);
1880 *   }
1881 *   } // namespace Step35
1882 *  
1883 *  
1884 * @endcode
1885 *
1886 *
1887 * <a name="step_35-Themainfunction"></a>
1888 * <h3> The main function </h3>
1889 *
1890
1891 *
1892 * The main function looks very much like in all the other tutorial programs, so
1893 * there is little to comment on here:
1894 *
1895 * @code
1896 *   int main()
1897 *   {
1898 *   try
1899 *   {
1900 *   using namespace Step35;
1901 *  
1902 *   RunTimeParameters::Data_Storage data;
1903 *   data.read_data("parameter-file.prm");
1904 *  
1905 *   deallog.depth_console(data.verbose ? 2 : 0);
1906 *  
1907 *   NavierStokesProjection<2> test(data);
1908 *   test.run(data.verbose, data.output_interval);
1909 *   }
1910 *   catch (std::exception &exc)
1911 *   {
1912 *   std::cerr << std::endl
1913 *   << std::endl
1914 *   << "----------------------------------------------------"
1915 *   << std::endl;
1916 *   std::cerr << "Exception on processing: " << std::endl
1917 *   << exc.what() << std::endl
1918 *   << "Aborting!" << std::endl
1919 *   << "----------------------------------------------------"
1920 *   << std::endl;
1921 *   return 1;
1922 *   }
1923 *   catch (...)
1924 *   {
1925 *   std::cerr << std::endl
1926 *   << std::endl
1927 *   << "----------------------------------------------------"
1928 *   << std::endl;
1929 *   std::cerr << "Unknown exception!" << std::endl
1930 *   << "Aborting!" << std::endl
1931 *   << "----------------------------------------------------"
1932 *   << std::endl;
1933 *   return 1;
1934 *   }
1935 *   std::cout << "----------------------------------------------------"
1936 *   << std::endl
1937 *   << "Apparently everything went fine!" << std::endl
1938 *   << "Don't forget to brush your teeth :-)" << std::endl
1939 *   << std::endl;
1940 *   return 0;
1941 *   }
1942 * @endcode
1943<a name="step_35-Results"></a><h1>Results</h1>
1944
1945
1946<a name="step_35-Re100"></a><h3> Re = 100 </h3>
1947
1948
1949We run the code with the following <code>parameter-file.prm</code>, which can be found in the
1950same directory as the source:
1951@verbatim
1952 # First a global definition
1953 # the type of method we want to use
1954 set Method_Form = rotational
1955
1956 subsection Physical data
1957 # In this subsection we declare the physical data
1958 # The initial and final time, and the Reynolds number
1959 set initial_time = 0.
1960 set final_time = 25.
1961 set Reynolds = 100
1962 end
1963
1964 subsection Time step data
1965 # In this subsection we declare the data that is to be used for time discretization,
1966 # i.e. the time step dt
1967 set dt = 5e-3
1968 end
1969
1970 subsection Space discretization
1971 # In this subsection we declare the data that is relevant to the space discretization
1972 # we set the number of global refines the triangulation must have
1973 # and the degree k of the pair Q_(k+1)--Q_k of velocity--pressure finite element spaces
1974 set n_of_refines = 3
1975 set pressure_fe_degree = 1
1976 end
1977
1978 subsection Data solve velocity
1979 # In this section we declare the parameters that are going to control the solution process
1980 # for the velocity.
1981 set max_iterations = 1000 # maximal number of iterations that GMRES must make
1982 set eps = 1e-6 # stopping criterion
1983 set Krylov_size = 30 # size of the Krylov subspace to be used in GMRES
1984 set off_diagonals = 60 # number of off diagonals that ILU must compute
1985 set diag_strength = 0.01 # diagonal strengthening value
1986 set update_prec = 10 # this number indicates how often the preconditioner must be updated
1987 end
1988
1989 #The output frequency
1990 set output_interval = 50
1991
1992 #Finally we set the verbosity level
1993 set verbose = false
1994@endverbatim
1995
1996Since the <code>verbose</code> parameter is set to <code>false</code>,
1997we do not get any kind of output besides the number of the time step
1998the program is currently working on.
1999If we were to set it to <code>true</code> we would get information on what the program is doing and
2000how many steps each iterative process had to make to converge, etc.
2001
2002Let us plot the obtained results for @f$t=1,5,12,20,25@f$ (i.e. time steps
2003200, 1000, 2400, 4000, and 5000), where in the left column we show the
2004vorticity and in the right the velocity field:
2005
2006<table>
2007 <tr>
2008 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.vorticity.0.9.3.png" alt="" width="400"> </td>
2009 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.velocity.0.9.3.png" alt="" width="400"> </td>
2010 </tr>
2011 <tr>
2012 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.vorticity.1.9.3.png" alt="" width="400"> </td>
2013 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.velocity.1.9.3.png" alt="" width="400"> </td>
2014 </tr>
2015 <tr>
2016 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.vorticity.2.9.3.png" alt="" width="400"> </td>
2017 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.velocity.2.9.3.png" alt="" width="400"> </td>
2018 </tr>
2019 <tr>
2020 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.vorticity.3.9.3.png" alt="" width="400"> </td>
2021 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.velocity.3.9.3.png" alt="" width="400"> </td>
2022 </tr>
2023 <tr>
2024 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.vorticity.4.9.3.png" alt="" width="400"> </td>
2025 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_100.velocity.4.9.3.png" alt="" width="400"> </td>
2026 </tr>
2027</table>
2028
2029The images show nicely the development and extension of a vortex chain
2030behind the obstacles, with the sign of the vorticity indicating
2031whether this is a left or right turning vortex.
2032
2033
2034<a name="step_35-Re500"></a><h3> Re = 500 </h3>
2035
2036
2037We can change the Reynolds number, @f$Re@f$, in the parameter file to a
2038value of @f$500@f$. Doing so, and reducing the time step somewhat as well,
2039yields the following images at times @f$t=20,40@f$:
2040
2041<table>
2042 <tr>
2043 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_500.vorticity.0.9.3.png" alt="" width="400"> </td>
2044 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_500.velocity.0.9.3.png" alt="" width="400"> </td>
2045 </tr>
2046 <tr>
2047 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_500.vorticity.1.9.3.png" alt="" width="400"> </td>
2048 <td> <img src="https://dealii.org/images/steps/developer/step-35.Re_500.velocity.1.9.3.png" alt="" width="400"> </td>
2049 </tr>
2050</table>
2051
2052For this larger Reynolds number, we observe unphysical oscillations, especially
2053for the vorticity. The discretization scheme has now difficulties in correctly
2054resolving the flow, which should still be laminar and well-organized.
2055These phenomena are typical of discretization schemes that lack robustness
2056in under-resolved scenarios, where under-resolved means that the Reynolds
2057number computed with the mesh size instead of the physical dimensions of
2058the geometry is large. We look at a zoom at the region behind the obstacle, and
2059the mesh size we have there:
2060
2061
2062<img src="https://dealii.org/images/steps/developer/step-35.Re_500.zoom.9.3.png" alt="" width="400">
2063
2064We can easily test our hypothesis by re-running the simulation with one more
2065mesh refinement set in the parameter file :
2066
2067<img src="https://dealii.org/images/steps/developer/step-35.Re_500.zoom_2.9.3.png" alt="" width="400">
2068
2069Indeed, the vorticity field now looks much smoother. While we can expect that
2070further refining the mesh will suppress the remaining oscillations as well,
2071one should take measures to obtain a robust scheme in the limit of coarse
2072resolutions, as described below.
2073
2074
2075<a name="step-35-extensions"></a>
2076<a name="step_35-Possibilitiesforextensions"></a><h3> Possibilities for extensions </h3>
2077
2078
2079This program can be extended in the following directions:
2080<ul>
2081 <li> Adaptive mesh refinement: As we have seen, we computed everything on a single fixed mesh.
2082 Using adaptive mesh refinement can lead to increased accuracy while not significantly increasing the
2083 computational time.
2084
2085 <li> Adaptive time-stepping: Although there apparently is currently no theory about
2086 projection methods with variable time step,
2087 practice shows that they perform very well.
2088
2089 <li> High Reynolds %numbers: As we can see from the results, increasing the Reynolds number changes significantly
2090 the behavior of the discretization scheme. Using well-known stabilization techniques we could be able to
2091 compute the flow in this, or many other problems, when the Reynolds number is very large and where computational
2092 costs demand spatial resolutions for which the flow is only marginally resolved, especially for 3D turbulent
2093 flows.
2094
2095 <li> Variable density incompressible flows: There are projection-like methods for the case of incompressible
2096 flows with variable density. Such flows play a role if fluids of different
2097 density mix, for example fresh water and salt water, or alcohol and water.
2098
2099 <li> Compressible Navier-Stokes equations: These equations are relevant for
2100 cases where
2101 velocities are high enough so that the fluid becomes compressible, but not
2102 fast enough that we get into a regime where viscosity becomes negligible
2103 and the Navier-Stokes equations need to be replaced by the hyperbolic Euler
2104 equations of gas dynamics. Compressibility starts to become a factor if the
2105 velocity becomes greater than about one third of the speed of sound, so it
2106 is not a factor for almost all terrestrial vehicles. On the other hand,
2107 commercial jetliners fly at about 85 per cent of the speed of sound, and
2108 flow over the wings becomes significantly supersonic, a regime in which the
2109 compressible Navier-Stokes equations are not applicable any more
2110 either. There are significant applications for the range in between,
2111 however, such as for small aircraft or the fast trains in many European and
2112 East Asian countries.
2113</ul>
2114 *
2115 *
2116<a name="step_35-PlainProg"></a>
2117<h1> The plain program</h1>
2118@include "step-35.cc"
2119*/
*  iterator end()
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  *  int main(int argc, char **argv)
*const unsigned int n_steps
*  x_component_mask set(0, true)
*  *  *  struct InterferenceTaperTransform *  
unsigned int depth_console(const unsigned int n)
Definition logstream.cc:349
typename SparseLUDecomposition< number >::AdditionalData AdditionalData
Definition sparse_ilu.h:77
#define DEAL_II_NOT_IMPLEMENTED()
#define Assert(cond, exc)
typename ActiveSelector::active_cell_iterator active_cell_iterator
void loop(IteratorType begin, std_cxx20::type_identity_t< IteratorType > end, DOFINFO &dinfo, INFOBOX &info, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &cell_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &)> &boundary_worker, const std::function< void(std_cxx20::type_identity_t< DOFINFO > &, std_cxx20::type_identity_t< DOFINFO > &, typename INFOBOX::CellInfo &, typename INFOBOX::CellInfo &)> &face_worker, AssemblerType &assembler, const LoopControl &lctrl=LoopControl())
Definition loop.h:562
@ update_values
Shape function values.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
Task< RT > new_task(const std::function< RT()> &function)
LogStream deallog
Definition logstream.cc:36
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
std::vector< value_type > l2_norm(const typename ::Triangulation< dim, spacedim >::cell_iterator &parent, const value_type parent_value)
Expression sign(const Expression &x)
@ matrix
Contents is actually a matrix.
@ diagonal
Matrix is diagonal.
constexpr types::blas_int one
void apply_boundary_values(const std::map< types::global_dof_index, number > &boundary_values, SparseMatrix< number > &matrix, Vector< number > &solution, Vector< number > &right_hand_side, const bool eliminate_columns=true)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
*  *  *  ScaleZFunction< dim, Number, components >::ScaleZFunction *  component(component)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
void interpolate_boundary_values(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const std::map< types::boundary_id, const Function< spacedim, number > * > &function_map, std::map< types::global_dof_index, number > &boundary_values, const ComponentMask &component_mask={})
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
void run(const std::vector< std::vector< Iterator > > &colored_iterators, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
int(&) functions(const void *v1, const void *v2)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
unsigned int boundary_id
Definition types.h:159