Reference documentation for deal.II version GIT relicensing-233-g802318d791 2024-03-28 20:20:02+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-29.h
Go to the documentation of this file.
1,
408 *   Vector<double> &values) const override
409 *   {
410 *   AssertDimension(values.size(), 2);
411 *  
412 *   values(0) = 1;
413 *   values(1) = 0;
414 *   }
415 *  
416 *   virtual void
417 *   vector_value_list(const std::vector<Point<dim>> &points,
418 *   std::vector<Vector<double>> &value_list) const override
419 *   {
420 *   AssertDimension(value_list.size(), points.size());
421 *  
422 *   for (unsigned int p = 0; p < points.size(); ++p)
423 *   DirichletBoundaryValues<dim>::vector_value(points[p], value_list[p]);
424 *   }
425 *   };
426 *  
427 * @endcode
428 *
429 *
430 * <a name="step_29-ThecodeParameterReadercodeclass"></a>
431 * <h3>The <code>ParameterReader</code> class</h3>
432 *
433
434 *
435 * The next class is responsible for preparing the ParameterHandler object
436 * and reading parameters from an input file. It includes a function
437 * <code>declare_parameters</code> that declares all the necessary
438 * parameters and a <code>read_parameters</code> function that is called
439 * from outside to initiate the parameter reading process.
440 *
441 * @code
442 *   class ParameterReader : public Subscriptor
443 *   {
444 *   public:
445 *   ParameterReader(ParameterHandler &);
446 *   void read_parameters(const std::string &);
447 *  
448 *   private:
449 *   void declare_parameters();
450 *   ParameterHandler &prm;
451 *   };
452 *  
453 * @endcode
454 *
455 * The constructor stores a reference to the ParameterHandler object that is
456 * passed to it:
457 *
458 * @code
459 *   ParameterReader::ParameterReader(ParameterHandler &paramhandler)
460 *   : prm(paramhandler)
461 *   {}
462 *  
463 * @endcode
464 *
465 *
466 * <a name="step_29-codeParameterReaderdeclare_parameterscode"></a>
467 * <h4><code>ParameterReader::declare_parameters</code></h4>
468 *
469
470 *
471 * The <code>declare_parameters</code> function declares all the parameters
472 * that our ParameterHandler object will be able to read from input files,
473 * along with their types, range conditions and the subsections they appear
474 * in. We will wrap all the entries that go into a section in a pair of
475 * braces to force the editor to indent them by one level, making it simpler
476 * to read which entries together form a section:
477 *
478 * @code
479 *   void ParameterReader::declare_parameters()
480 *   {
481 * @endcode
482 *
483 * Parameters for mesh and geometry include the number of global
484 * refinement steps that are applied to the initial coarse mesh and the
485 * focal distance @f$d@f$ of the transducer lens. For the number of refinement
486 * steps, we allow integer values in the range @f$[0,\infty)@f$, where the
487 * omitted second argument to the Patterns::Integer object denotes the
488 * half-open interval. For the focal distance any number greater than
489 * zero is accepted:
490 *
491 * @code
492 *   prm.enter_subsection("Mesh & geometry parameters");
493 *   {
494 *   prm.declare_entry("Number of refinements",
495 *   "6",
496 *   Patterns::Integer(0),
497 *   "Number of global mesh refinement steps "
498 *   "applied to initial coarse grid");
499 *  
500 *   prm.declare_entry("Focal distance",
501 *   "0.3",
502 *   Patterns::Double(0),
503 *   "Distance of the focal point of the lens "
504 *   "to the x-axis");
505 *   }
506 *   prm.leave_subsection();
507 *  
508 * @endcode
509 *
510 * The next subsection is devoted to the physical parameters appearing in
511 * the equation, which are the frequency @f$\omega@f$ and wave speed
512 * @f$c@f$. Again, both need to lie in the half-open interval @f$[0,\infty)@f$
513 * represented by calling the Patterns::Double class with only the left
514 * end-point as argument:
515 *
516 * @code
517 *   prm.enter_subsection("Physical constants");
518 *   {
519 *   prm.declare_entry("c", "1.5e5", Patterns::Double(0), "Wave speed");
520 *  
521 *   prm.declare_entry("omega", "5.0e7", Patterns::Double(0), "Frequency");
522 *   }
523 *   prm.leave_subsection();
524 *  
525 *  
526 * @endcode
527 *
528 * Last but not least we would like to be able to change some properties
529 * of the output, like filename and format, through entries in the
530 * configuration file, which is the purpose of the last subsection:
531 *
532 * @code
533 *   prm.enter_subsection("Output parameters");
534 *   {
535 *   prm.declare_entry("Output filename",
536 *   "solution",
537 *   Patterns::Anything(),
538 *   "Name of the output file (without extension)");
539 *  
540 * @endcode
541 *
542 * Since different output formats may require different parameters for
543 * generating output (like for example, postscript output needs
544 * viewpoint angles, line widths, colors etc), it would be cumbersome if
545 * we had to declare all these parameters by hand for every possible
546 * output format supported in the library. Instead, each output format
547 * has a <code>FormatFlags::declare_parameters</code> function, which
548 * declares all the parameters specific to that format in an own
549 * subsection. The following call of
551 * <code>declare_parameters</code> for all available output formats, so
552 * that for each format an own subsection will be created with
553 * parameters declared for that particular output format. (The actual
554 * value of the template parameter in the call, <code>@<1@></code>
555 * above, does not matter here: the function does the same work
556 * independent of the dimension, but happens to be in a
557 * template-parameter-dependent class.) To find out what parameters
558 * there are for which output format, you can either consult the
559 * documentation of the DataOutBase class, or simply run this program
560 * without a parameter file present. It will then create a file with all
561 * declared parameters set to their default values, which can
562 * conveniently serve as a starting point for setting the parameters to
563 * the values you desire.
564 *
565 * @code
567 *   }
568 *   prm.leave_subsection();
569 *   }
570 *  
571 * @endcode
572 *
573 *
574 * <a name="step_29-codeParameterReaderread_parameterscode"></a>
575 * <h4><code>ParameterReader::read_parameters</code></h4>
576 *
577
578 *
579 * This is the main function in the ParameterReader class. It gets called
580 * from outside, first declares all the parameters, and then reads them from
581 * the input file whose filename is provided by the caller. After the call
582 * to this function is complete, the <code>prm</code> object can be used to
583 * retrieve the values of the parameters read in from the file :
584 *
585 * @code
586 *   void ParameterReader::read_parameters(const std::string &parameter_file)
587 *   {
588 *   declare_parameters();
589 *  
590 *   prm.parse_input(parameter_file);
591 *   }
592 *  
593 *  
594 *  
595 * @endcode
596 *
597 *
598 * <a name="step_29-ThecodeComputeIntensitycodeclass"></a>
599 * <h3>The <code>ComputeIntensity</code> class</h3>
600 *
601
602 *
603 * As mentioned in the introduction, the quantity that we are really after
604 * is the spatial distribution of the intensity of the ultrasound wave,
605 * which corresponds to @f$|u|=\sqrt{v^2+w^2}@f$. Now we could just be content
606 * with having @f$v@f$ and @f$w@f$ in our output, and use a suitable visualization
607 * or postprocessing tool to derive @f$|u|@f$ from the solution we
608 * computed. However, there is also a way to output data derived from the
609 * solution in deal.II, and we are going to make use of this mechanism here.
610 *
611
612 *
613 * So far we have always used the DataOut::add_data_vector function to add
614 * vectors containing output data to a DataOut object. There is a special
615 * version of this function that in addition to the data vector has an
616 * additional argument of type DataPostprocessor. What happens when this
617 * function is used for output is that at each point where output data is to
618 * be generated, the DataPostprocessor::evaluate_scalar_field() or
619 * DataPostprocessor::evaluate_vector_field() function of the
620 * specified DataPostprocessor object is invoked to compute the output
621 * quantities from the values, the gradients and the second derivatives of
622 * the finite element function represented by the data vector (in the case
623 * of face related data, normal vectors are available as well). Hence, this
624 * allows us to output any quantity that can locally be derived from the
625 * values of the solution and its derivatives. Of course, the ultrasound
626 * intensity @f$|u|@f$ is such a quantity and its computation doesn't even
627 * involve any derivatives of @f$v@f$ or @f$w@f$.
628 *
629
630 *
631 * In practice, the DataPostprocessor class only provides an interface to
632 * this functionality, and we need to derive our own class from it in order
633 * to implement the functions specified by the interface. In the most
634 * general case one has to implement several member functions but if the
635 * output quantity is a single scalar then some of this boilerplate code can
636 * be handled by a more specialized class, DataPostprocessorScalar and we
637 * can derive from that one instead. This is what the
638 * <code>ComputeIntensity</code> class does:
639 *
640 * @code
641 *   template <int dim>
642 *   class ComputeIntensity : public DataPostprocessorScalar<dim>
643 *   {
644 *   public:
645 *   ComputeIntensity();
646 *  
647 *   virtual void evaluate_vector_field(
648 *   const DataPostprocessorInputs::Vector<dim> &inputs,
649 *   std::vector<Vector<double>> &computed_quantities) const override;
650 *   };
651 *  
652 * @endcode
653 *
654 * In the constructor, we need to call the constructor of the base class
655 * with two arguments. The first denotes the name by which the single scalar
656 * quantity computed by this class should be represented in output files. In
657 * our case, the postprocessor has @f$|u|@f$ as output, so we use "Intensity".
658 *
659
660 *
661 * The second argument is a set of flags that indicate which data is needed
662 * by the postprocessor in order to compute the output quantities. This can
663 * be any subset of update_values, update_gradients and update_hessians
664 * (and, in the case of face data, also update_normal_vectors), which are
665 * documented in UpdateFlags. Of course, computation of the derivatives
666 * requires additional resources, so only the flags for data that are really
667 * needed should be given here, just as we do when we use FEValues objects.
668 * In our case, only the function values of @f$v@f$ and @f$w@f$ are needed to
669 * compute @f$|u|@f$, so we're good with the update_values flag.
670 *
671 * @code
672 *   template <int dim>
673 *   ComputeIntensity<dim>::ComputeIntensity()
674 *   : DataPostprocessorScalar<dim>("Intensity", update_values)
675 *   {}
676 *  
677 *  
678 * @endcode
679 *
680 * The actual postprocessing happens in the following function. Its input is
681 * an object that stores values of the function (which is here vector-valued)
682 * representing the data vector given to DataOut::add_data_vector, evaluated
683 * at all evaluation points where we generate output, and some tensor objects
684 * representing derivatives (that we don't use here since @f$|u|@f$ is computed
685 * from just @f$v@f$ and @f$w@f$). The derived quantities are returned in the
686 * <code>computed_quantities</code> vector. Remember that this function may
687 * only use data for which the respective update flag is specified by
688 * <code>get_needed_update_flags</code>. For example, we may not use the
689 * derivatives here, since our implementation of
690 * <code>get_needed_update_flags</code> requests that only function values
691 * are provided.
692 *
693 * @code
694 *   template <int dim>
695 *   void ComputeIntensity<dim>::evaluate_vector_field(
696 *   const DataPostprocessorInputs::Vector<dim> &inputs,
697 *   std::vector<Vector<double>> &computed_quantities) const
698 *   {
699 *   AssertDimension(computed_quantities.size(), inputs.solution_values.size());
700 *  
701 * @endcode
702 *
703 * The computation itself is straightforward: We iterate over each
704 * entry in the output vector and compute @f$|u|@f$ from the
705 * corresponding values of @f$v@f$ and @f$w@f$. We do this by creating a
706 * complex number @f$u@f$ and then calling `std::abs()` on the
707 * result. (One may be tempted to call `std::norm()`, but in a
708 * historical quirk, the C++ committee decided that `std::norm()`
709 * should return the <i>square</i> of the absolute value --
710 * thereby not satisfying the properties mathematicians require of
711 * something called a "norm".)
712 *
713 * @code
714 *   for (unsigned int p = 0; p < computed_quantities.size(); ++p)
715 *   {
716 *   AssertDimension(computed_quantities[p].size(), 1);
717 *   AssertDimension(inputs.solution_values[p].size(), 2);
718 *  
719 *   const std::complex<double> u(inputs.solution_values[p](0),
720 *   inputs.solution_values[p](1));
721 *  
722 *   computed_quantities[p](0) = std::abs(u);
723 *   }
724 *   }
725 *  
726 *  
727 * @endcode
728 *
729 *
730 * <a name="step_29-ThecodeUltrasoundProblemcodeclass"></a>
731 * <h3>The <code>UltrasoundProblem</code> class</h3>
732 *
733
734 *
735 * Finally here is the main class of this program. It's member functions
736 * are very similar to the previous examples, in particular @ref step_4 "step-4", and the
737 * list of member variables does not contain any major surprises either.
738 * The ParameterHandler object that is passed to the constructor is stored
739 * as a reference to allow easy access to the parameters from all functions
740 * of the class. Since we are working with vector valued finite elements,
741 * the FE object we are using is of type FESystem.
742 *
743 * @code
744 *   template <int dim>
745 *   class UltrasoundProblem
746 *   {
747 *   public:
748 *   UltrasoundProblem(ParameterHandler &);
749 *   void run();
750 *  
751 *   private:
752 *   void make_grid();
753 *   void setup_system();
754 *   void assemble_system();
755 *   void solve();
756 *   void output_results() const;
757 *  
758 *   ParameterHandler &prm;
759 *  
761 *   DoFHandler<dim> dof_handler;
762 *   FESystem<dim> fe;
763 *  
764 *   SparsityPattern sparsity_pattern;
765 *   SparseMatrix<double> system_matrix;
766 *   Vector<double> solution, system_rhs;
767 *   };
768 *  
769 *  
770 *  
771 * @endcode
772 *
773 * The constructor takes the ParameterHandler object and stores it in a
774 * reference. It also initializes the DoF-Handler and the finite element
775 * system, which consists of two copies of the scalar @f$Q_1@f$ field, one for
776 * @f$v@f$ and one for @f$w@f$. In other words, we want the finite element space
777 * @f$Q_1\times Q_1 = Q_1^2@f$, which is easily constructed and passed as the
778 * constructor argument to the FESystem class (i.e., the type of the `fe`
779 * member being initialized here):
780 *
781 * @code
782 *   template <int dim>
783 *   UltrasoundProblem<dim>::UltrasoundProblem(ParameterHandler &param)
784 *   : prm(param)
785 *   , dof_handler(triangulation)
786 *   , fe(FE_Q<dim>(1) ^ 2)
787 *   {}
788 *  
789 * @endcode
790 *
791 *
792 * <a name="step_29-codeUltrasoundProblemmake_gridcode"></a>
793 * <h4><code>UltrasoundProblem::make_grid</code></h4>
794 *
795
796 *
797 * Here we set up the grid for our domain. As mentioned in the exposition,
798 * the geometry is just a unit square (in 2d) with the part of the boundary
799 * that represents the transducer lens replaced by a sector of a circle.
800 *
801 * @code
802 *   template <int dim>
803 *   void UltrasoundProblem<dim>::make_grid()
804 *   {
805 * @endcode
806 *
807 * First we generate some logging output and start a timer so we can
808 * compute execution time when this function is done:
809 *
810 * @code
811 *   std::cout << "Generating grid... ";
812 *   Timer timer;
813 *  
814 * @endcode
815 *
816 * Then we query the values for the focal distance of the transducer lens
817 * and the number of mesh refinement steps from our ParameterHandler
818 * object:
819 *
820 * @code
821 *   prm.enter_subsection("Mesh & geometry parameters");
822 *  
823 *   const double focal_distance = prm.get_double("Focal distance");
824 *   const unsigned int n_refinements = prm.get_integer("Number of refinements");
825 *  
826 *   prm.leave_subsection();
827 *  
828 * @endcode
829 *
830 * Next, two points are defined for position and focal point of the
831 * transducer lens, which is the center of the circle whose segment will
832 * form the transducer part of the boundary. Notice that this is the only
833 * point in the program where things are slightly different in 2d and 3d.
834 * Even though this tutorial only deals with the 2d case, the necessary
835 * additions to make this program functional in 3d are so minimal that we
836 * opt for including them:
837 *
838 * @code
839 *   const Point<dim> transducer =
840 *   (dim == 2) ? Point<dim>(0.5, 0.0) : Point<dim>(0.5, 0.5, 0.0);
841 *   const Point<dim> focal_point = (dim == 2) ?
842 *   Point<dim>(0.5, focal_distance) :
843 *   Point<dim>(0.5, 0.5, focal_distance);
844 *  
845 *  
846 * @endcode
847 *
848 * As initial coarse grid we take a simple unit square with 5 subdivisions
849 * in each direction. The number of subdivisions is chosen so that the
850 * line segment @f$[0.4,0.6]@f$ that we want to designate as the transducer
851 * boundary is spanned by a single face. Then we step through all cells to
852 * find the faces where the transducer is to be located, which in fact is
853 * just the single edge from 0.4 to 0.6 on the x-axis. This is where we
854 * want the refinements to be made according to a circle shaped boundary,
855 * so we mark this edge with a different manifold indicator. Since we will
856 * Dirichlet boundary conditions on the transducer, we also change its
857 * boundary indicator.
858 *
859 * @code
861 *  
862 *   for (auto &cell : triangulation.cell_iterators())
863 *   for (const auto &face : cell->face_iterators())
864 *   if (face->at_boundary() &&
865 *   ((face->center() - transducer).norm_square() < 0.01))
866 *   {
867 *   face->set_boundary_id(1);
868 *   face->set_manifold_id(1);
869 *   }
870 * @endcode
871 *
872 * For the circle part of the transducer lens, a SphericalManifold object
873 * is used (which, of course, in 2d just represents a circle), with center
874 * computed as above.
875 *
876 * @code
877 *   triangulation.set_manifold(1, SphericalManifold<dim>(focal_point));
878 *  
879 * @endcode
880 *
881 * Now global refinement is executed. Cells near the transducer location
882 * will be automatically refined according to the circle shaped boundary
883 * of the transducer lens:
884 *
885 * @code
886 *   triangulation.refine_global(n_refinements);
887 *  
888 * @endcode
889 *
890 * Lastly, we generate some more logging output. We stop the timer and
891 * query the number of CPU seconds elapsed since the beginning of the
892 * function:
893 *
894 * @code
895 *   timer.stop();
896 *   std::cout << "done (" << timer.cpu_time() << "s)" << std::endl;
897 *  
898 *   std::cout << " Number of active cells: " << triangulation.n_active_cells()
899 *   << std::endl;
900 *   }
901 *  
902 *  
903 * @endcode
904 *
905 *
906 * <a name="step_29-codeUltrasoundProblemsetup_systemcode"></a>
907 * <h4><code>UltrasoundProblem::setup_system</code></h4>
908 *
909
910 *
911 * Initialization of the system matrix, sparsity patterns and vectors are
912 * the same as in previous examples and therefore do not need further
913 * comment. As in the previous function, we also output the run time of what
914 * we do here:
915 *
916 * @code
917 *   template <int dim>
918 *   void UltrasoundProblem<dim>::setup_system()
919 *   {
920 *   std::cout << "Setting up system... ";
921 *   Timer timer;
922 *  
923 *   dof_handler.distribute_dofs(fe);
924 *  
925 *   DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
926 *   DoFTools::make_sparsity_pattern(dof_handler, dsp);
927 *   sparsity_pattern.copy_from(dsp);
928 *  
929 *   system_matrix.reinit(sparsity_pattern);
930 *   system_rhs.reinit(dof_handler.n_dofs());
931 *   solution.reinit(dof_handler.n_dofs());
932 *  
933 *   timer.stop();
934 *   std::cout << "done (" << timer.cpu_time() << "s)" << std::endl;
935 *  
936 *   std::cout << " Number of degrees of freedom: " << dof_handler.n_dofs()
937 *   << std::endl;
938 *   }
939 *  
940 *  
941 * @endcode
942 *
943 *
944 * <a name="step_29-codeUltrasoundProblemassemble_systemcode"></a>
945 * <h4><code>UltrasoundProblem::assemble_system</code></h4>
946 *
947
948 *
949 * As before, this function takes care of assembling the system matrix and
950 * right hand side vector:
951 *
952 * @code
953 *   template <int dim>
954 *   void UltrasoundProblem<dim>::assemble_system()
955 *   {
956 *   std::cout << "Assembling system matrix... ";
957 *   Timer timer;
958 *  
959 * @endcode
960 *
961 * First we query wavespeed and frequency from the ParameterHandler object
962 * and store them in local variables, as they will be used frequently
963 * throughout this function.
964 *
965
966 *
967 *
968 * @code
969 *   prm.enter_subsection("Physical constants");
970 *  
971 *   const double omega = prm.get_double("omega"), c = prm.get_double("c");
972 *  
973 *   prm.leave_subsection();
974 *  
975 * @endcode
976 *
977 * As usual, for computing integrals ordinary Gauss quadrature rule is
978 * used. Since our bilinear form involves boundary integrals on
979 * @f$\Gamma_2@f$, we also need a quadrature rule for surface integration on
980 * the faces, which are @f$dim-1@f$ dimensional:
981 *
982 * @code
983 *   QGauss<dim> quadrature_formula(fe.degree + 1);
984 *   QGauss<dim - 1> face_quadrature_formula(fe.degree + 1);
985 *  
986 *   const unsigned int n_q_points = quadrature_formula.size(),
987 *   n_face_q_points = face_quadrature_formula.size(),
988 *   dofs_per_cell = fe.n_dofs_per_cell();
989 *  
990 * @endcode
991 *
992 * The FEValues objects will evaluate the shape functions for us. For the
993 * part of the bilinear form that involves integration on @f$\Omega@f$, we'll
994 * need the values and gradients of the shape functions, and of course the
995 * quadrature weights. For the terms involving the boundary integrals,
996 * only shape function values and the quadrature weights are necessary.
997 *
998 * @code
999 *   FEValues<dim> fe_values(fe,
1000 *   quadrature_formula,
1001 *   update_values | update_gradients |
1002 *   update_JxW_values);
1003 *  
1004 *   FEFaceValues<dim> fe_face_values(fe,
1005 *   face_quadrature_formula,
1006 *   update_values | update_JxW_values);
1007 *  
1008 * @endcode
1009 *
1010 * As usual, the system matrix is assembled cell by cell, and we need a
1011 * matrix for storing the local cell contributions as well as an index
1012 * vector to transfer the cell contributions to the appropriate location
1013 * in the global system matrix after.
1014 *
1015 * @code
1016 *   FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
1017 *   std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
1018 *  
1019 *   for (const auto &cell : dof_handler.active_cell_iterators())
1020 *   {
1021 * @endcode
1022 *
1023 * On each cell, we first need to reset the local contribution matrix
1024 * and request the FEValues object to compute the shape functions for
1025 * the current cell:
1026 *
1027 * @code
1028 *   cell_matrix = 0;
1029 *   fe_values.reinit(cell);
1030 *  
1031 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
1032 *   {
1033 *   for (unsigned int j = 0; j < dofs_per_cell; ++j)
1034 *   {
1035 * @endcode
1036 *
1037 * At this point, it is important to keep in mind that we are
1038 * dealing with a finite element system with two
1039 * components. Due to the way we constructed this FESystem,
1040 * namely as the Cartesian product of two scalar finite
1041 * element fields, each shape function has only a single
1042 * nonzero component (they are, in deal.II lingo, @ref
1043 * GlossPrimitive "primitive"). Hence, each shape function
1044 * can be viewed as one of the @f$\phi@f$'s or @f$\psi@f$'s from the
1045 * introduction, and similarly the corresponding degrees of
1046 * freedom can be attributed to either @f$\alpha@f$ or @f$\beta@f$.
1047 * As we iterate through all the degrees of freedom on the
1048 * current cell however, they do not come in any particular
1049 * order, and so we cannot decide right away whether the DoFs
1050 * with index @f$i@f$ and @f$j@f$ belong to the real or imaginary part
1051 * of our solution. On the other hand, if you look at the
1052 * form of the system matrix in the introduction, this
1053 * distinction is crucial since it will determine to which
1054 * block in the system matrix the contribution of the current
1055 * pair of DoFs will go and hence which quantity we need to
1056 * compute from the given two shape functions. Fortunately,
1057 * the FESystem object can provide us with this information,
1058 * namely it has a function
1059 * FESystem::system_to_component_index(), that for each local
1060 * DoF index returns a pair of integers of which the first
1061 * indicates to which component of the system the DoF
1062 * belongs. The second integer of the pair indicates which
1063 * index the DoF has in the scalar base finite element field,
1064 * but this information is not relevant here. If you want to
1065 * know more about this function and the underlying scheme
1066 * behind primitive vector valued elements, take a look at
1067 * @ref step_8 "step-8" or the @ref vector_valued module, where these topics
1068 * are explained in depth.
1069 *
1070 * @code
1071 *   if (fe.system_to_component_index(i).first ==
1072 *   fe.system_to_component_index(j).first)
1073 *   {
1074 * @endcode
1075 *
1076 * If both DoFs @f$i@f$ and @f$j@f$ belong to same component,
1077 * i.e. their shape functions are both @f$\phi@f$'s or both
1078 * @f$\psi@f$'s, the contribution will end up in one of the
1079 * diagonal blocks in our system matrix, and since the
1080 * corresponding entries are computed by the same formula,
1081 * we do not bother if they actually are @f$\phi@f$ or @f$\psi@f$
1082 * shape functions. We can simply compute the entry by
1083 * iterating over all quadrature points and adding up
1084 * their contributions, where values and gradients of the
1085 * shape functions are supplied by our FEValues object.
1086 *
1087
1088 *
1089 *
1090 * @code
1091 *   for (unsigned int q_point = 0; q_point < n_q_points;
1092 *   ++q_point)
1093 *   cell_matrix(i, j) +=
1094 *   (((fe_values.shape_value(i, q_point) *
1095 *   fe_values.shape_value(j, q_point)) *
1096 *   (-omega * omega) +
1097 *   (fe_values.shape_grad(i, q_point) *
1098 *   fe_values.shape_grad(j, q_point)) *
1099 *   c * c) *
1100 *   fe_values.JxW(q_point));
1101 *  
1102 * @endcode
1103 *
1104 * You might think that we would have to specify which
1105 * component of the shape function we'd like to evaluate
1106 * when requesting shape function values or gradients from
1107 * the FEValues object. However, as the shape functions
1108 * are primitive, they have only one nonzero component,
1109 * and the FEValues class is smart enough to figure out
1110 * that we are definitely interested in this one nonzero
1111 * component.
1112 *
1113 * @code
1114 *   }
1115 *   }
1116 *   }
1117 *  
1118 *  
1119 * @endcode
1120 *
1121 * We also have to add contributions due to boundary terms. To this
1122 * end, we loop over all faces of the current cell and see if first it
1123 * is at the boundary, and second has the correct boundary indicator
1124 * associated with @f$\Gamma_2@f$, the part of the boundary where we have
1125 * absorbing boundary conditions:
1126 *
1127 * @code
1128 *   for (const auto face_no : cell->face_indices())
1129 *   if (cell->face(face_no)->at_boundary() &&
1130 *   (cell->face(face_no)->boundary_id() == 0))
1131 *   {
1132 * @endcode
1133 *
1134 * These faces will certainly contribute to the off-diagonal
1135 * blocks of the system matrix, so we ask the FEFaceValues
1136 * object to provide us with the shape function values on this
1137 * face:
1138 *
1139 * @code
1140 *   fe_face_values.reinit(cell, face_no);
1141 *  
1142 *  
1143 * @endcode
1144 *
1145 * Next, we loop through all DoFs of the current cell to find
1146 * pairs that belong to different components and both have
1147 * support on the current face_no:
1148 *
1149 * @code
1150 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
1151 *   for (unsigned int j = 0; j < dofs_per_cell; ++j)
1152 *   if ((fe.system_to_component_index(i).first !=
1153 *   fe.system_to_component_index(j).first) &&
1154 *   fe.has_support_on_face(i, face_no) &&
1155 *   fe.has_support_on_face(j, face_no))
1156 * @endcode
1157 *
1158 * The check whether shape functions have support on a
1159 * face is not strictly necessary: if we don't check for
1160 * it we would simply add up terms to the local cell
1161 * matrix that happen to be zero because at least one of
1162 * the shape functions happens to be zero. However, we can
1163 * save that work by adding the checks above.
1164 *
1165
1166 *
1167 * In either case, these DoFs will contribute to the
1168 * boundary integrals in the off-diagonal blocks of the
1169 * system matrix. To compute the integral, we loop over
1170 * all the quadrature points on the face and sum up the
1171 * contribution weighted with the quadrature weights that
1172 * the face quadrature rule provides. In contrast to the
1173 * entries on the diagonal blocks, here it does matter
1174 * which one of the shape functions is a @f$\psi@f$ and which
1175 * one is a @f$\phi@f$, since that will determine the sign of
1176 * the entry. We account for this by a simple conditional
1177 * statement that determines the correct sign. Since we
1178 * already checked that DoF @f$i@f$ and @f$j@f$ belong to
1179 * different components, it suffices here to test for one
1180 * of them to which component it belongs.
1181 *
1182 * @code
1183 *   for (unsigned int q_point = 0; q_point < n_face_q_points;
1184 *   ++q_point)
1185 *   cell_matrix(i, j) +=
1186 *   ((fe.system_to_component_index(i).first == 0) ? -1 :
1187 *   1) *
1188 *   fe_face_values.shape_value(i, q_point) *
1189 *   fe_face_values.shape_value(j, q_point) * c * omega *
1190 *   fe_face_values.JxW(q_point);
1191 *   }
1192 *  
1193 * @endcode
1194 *
1195 * Now we are done with this cell and have to transfer its
1196 * contributions from the local to the global system matrix. To this
1197 * end, we first get a list of the global indices of the this cells
1198 * DoFs...
1199 *
1200 * @code
1201 *   cell->get_dof_indices(local_dof_indices);
1202 *  
1203 *  
1204 * @endcode
1205 *
1206 * ...and then add the entries to the system matrix one by one:
1207 *
1208 * @code
1209 *   for (unsigned int i = 0; i < dofs_per_cell; ++i)
1210 *   for (unsigned int j = 0; j < dofs_per_cell; ++j)
1211 *   system_matrix.add(local_dof_indices[i],
1212 *   local_dof_indices[j],
1213 *   cell_matrix(i, j));
1214 *   }
1215 *  
1216 *  
1217 * @endcode
1218 *
1219 * The only thing left are the Dirichlet boundary values on @f$\Gamma_1@f$,
1220 * which is characterized by the boundary indicator 1. The Dirichlet
1221 * values are provided by the <code>DirichletBoundaryValues</code> class
1222 * we defined above:
1223 *
1224 * @code
1225 *   std::map<types::global_dof_index, double> boundary_values;
1226 *   VectorTools::interpolate_boundary_values(dof_handler,
1227 *   1,
1228 *   DirichletBoundaryValues<dim>(),
1229 *   boundary_values);
1230 *  
1231 *   MatrixTools::apply_boundary_values(boundary_values,
1232 *   system_matrix,
1233 *   solution,
1234 *   system_rhs);
1235 *  
1236 *   timer.stop();
1237 *   std::cout << "done (" << timer.cpu_time() << "s)" << std::endl;
1238 *   }
1239 *  
1240 *  
1241 *  
1242 * @endcode
1243 *
1244 *
1245 * <a name="step_29-codeUltrasoundProblemsolvecode"></a>
1246 * <h4><code>UltrasoundProblem::solve</code></h4>
1247 *
1248
1249 *
1250 * As already mentioned in the introduction, the system matrix is neither
1251 * symmetric nor definite, and so it is not quite obvious how to come up
1252 * with an iterative solver and a preconditioner that do a good job on this
1253 * matrix. (For more on this topic, see also the
1254 * <a href="#extensions">Possibilities for extensions</a> section below.)
1255 * We chose instead to go a different way and solve the linear
1256 * system with the sparse LU decomposition provided by UMFPACK. This is
1257 * often a good first choice for 2d problems and works reasonably well even
1258 * for moderately large numbers of DoFs. The deal.II interface to UMFPACK
1259 * is implemented in the SparseDirectUMFPACK class, which is very easy to
1260 * use and allows us to solve our linear system with just 3 lines of code.
1261 *
1262
1263 *
1264 * Note again that for compiling this example program, you need to have the
1265 * deal.II library built with UMFPACK support.
1266 *
1267 * @code
1268 *   template <int dim>
1269 *   void UltrasoundProblem<dim>::solve()
1270 *   {
1271 *   std::cout << "Solving linear system... ";
1272 *   Timer timer;
1273 *  
1274 * @endcode
1275 *
1276 * The code to solve the linear system is short: First, we allocate an
1277 * object of the right type. The following call to
1278 * SparseDirectUMFPACK::solve() takes as argument the matrix to decompose,
1279 * and a vector that upon input equals the right hand side of the linear
1280 * system to be solved, and upon output contains the solution of the linear
1281 * system. To satisfy this input/output requirement, we first assign the
1282 * right hand side vector to the `solution` variable.
1283 *
1284 * @code
1285 *   SparseDirectUMFPACK A_direct;
1286 *  
1287 *   solution = system_rhs;
1288 *   A_direct.solve(system_matrix, solution);
1289 *  
1290 *   timer.stop();
1291 *   std::cout << "done (" << timer.cpu_time() << "s)" << std::endl;
1292 *   }
1293 *  
1294 *  
1295 *  
1296 * @endcode
1297 *
1298 *
1299 * <a name="step_29-codeUltrasoundProblemoutput_resultscode"></a>
1300 * <h4><code>UltrasoundProblem::output_results</code></h4>
1301 *
1302
1303 *
1304 * Here we output our solution @f$v@f$ and @f$w@f$ as well as the derived quantity
1305 * @f$|u|@f$ in the format specified in the parameter file. Most of the work for
1306 * deriving @f$|u|@f$ from @f$v@f$ and @f$w@f$ was already done in the implementation of
1307 * the <code>ComputeIntensity</code> class, so that the output routine is
1308 * rather straightforward and very similar to what is done in the previous
1309 * tutorials.
1310 *
1311 * @code
1312 *   template <int dim>
1313 *   void UltrasoundProblem<dim>::output_results() const
1314 *   {
1315 *   std::cout << "Generating output... ";
1316 *   Timer timer;
1317 *  
1318 * @endcode
1319 *
1320 * Define objects of our <code>ComputeIntensity</code> class and a DataOut
1321 * object:
1322 *
1323 * @code
1324 *   ComputeIntensity<dim> intensities;
1325 *   DataOut<dim> data_out;
1326 *  
1327 *   data_out.attach_dof_handler(dof_handler);
1328 *  
1329 * @endcode
1330 *
1331 * Next we query the output-related parameters from the ParameterHandler.
1332 * The DataOut::parse_parameters call acts as a counterpart to the
1333 * DataOutInterface<1>::declare_parameters call in
1334 * <code>ParameterReader::declare_parameters</code>. It collects all the
1335 * output format related parameters from the ParameterHandler and sets the
1336 * corresponding properties of the DataOut object accordingly.
1337 *
1338 * @code
1339 *   prm.enter_subsection("Output parameters");
1340 *  
1341 *   const std::string output_filename = prm.get("Output filename");
1342 *   data_out.parse_parameters(prm);
1343 *  
1344 *   prm.leave_subsection();
1345 *  
1346 * @endcode
1347 *
1348 * Now we put together the filename from the base name provided by the
1349 * ParameterHandler and the suffix which is provided by the DataOut class
1350 * (the default suffix is set to the right type that matches the one set
1351 * in the .prm file through parse_parameters()):
1352 *
1353 * @code
1354 *   const std::string filename = output_filename + data_out.default_suffix();
1355 *  
1356 *   std::ofstream output(filename);
1357 *  
1358 * @endcode
1359 *
1360 * The solution vectors @f$v@f$ and @f$w@f$ are added to the DataOut object in the
1361 * usual way:
1362 *
1363 * @code
1364 *   std::vector<std::string> solution_names;
1365 *   solution_names.emplace_back("Re_u");
1366 *   solution_names.emplace_back("Im_u");
1367 *  
1368 *   data_out.add_data_vector(solution, solution_names);
1369 *  
1370 * @endcode
1371 *
1372 * For the intensity, we just call <code>add_data_vector</code> again, but
1373 * this with our <code>ComputeIntensity</code> object as the second
1374 * argument, which effectively adds @f$|u|@f$ to the output data:
1375 *
1376 * @code
1377 *   data_out.add_data_vector(solution, intensities);
1378 *  
1379 * @endcode
1380 *
1381 * The last steps are as before. Note that the actual output format is now
1382 * determined by what is stated in the input file, i.e. one can change the
1383 * output format without having to re-compile this program:
1384 *
1385 * @code
1386 *   data_out.build_patches();
1387 *   data_out.write(output);
1388 *  
1389 *   timer.stop();
1390 *   std::cout << "done (" << timer.cpu_time() << "s)" << std::endl;
1391 *   }
1392 *  
1393 *  
1394 *  
1395 * @endcode
1396 *
1397 *
1398 * <a name="step_29-codeUltrasoundProblemruncode"></a>
1399 * <h4><code>UltrasoundProblem::run</code></h4>
1400 *
1401
1402 *
1403 * Here we simply execute our functions one after the other:
1404 *
1405 * @code
1406 *   template <int dim>
1407 *   void UltrasoundProblem<dim>::run()
1408 *   {
1409 *   make_grid();
1410 *   setup_system();
1411 *   assemble_system();
1412 *   solve();
1413 *   output_results();
1414 *   }
1415 *   } // namespace Step29
1416 *  
1417 *  
1418 * @endcode
1419 *
1420 *
1421 * <a name="step_29-Thecodemaincodefunction"></a>
1422 * <h4>The <code>main</code> function</h4>
1423 *
1424
1425 *
1426 * Finally the <code>main</code> function of the program. It has the same
1427 * structure as in almost all of the other tutorial programs. The only
1428 * exception is that we define ParameterHandler and
1429 * <code>ParameterReader</code> objects, and let the latter read in the
1430 * parameter values from a textfile called <code>@ref step_29 "step-29".prm</code>. The
1431 * values so read are then handed over to an instance of the UltrasoundProblem
1432 * class:
1433 *
1434 * @code
1435 *   int main()
1436 *   {
1437 *   try
1438 *   {
1439 *   using namespace dealii;
1440 *   using namespace Step29;
1441 *  
1442 *   ParameterHandler prm;
1443 *   ParameterReader param(prm);
1444 *   param.read_parameters("step-29.prm");
1445 *  
1446 *   UltrasoundProblem<2> ultrasound_problem(prm);
1447 *   ultrasound_problem.run();
1448 *   }
1449 *   catch (std::exception &exc)
1450 *   {
1451 *   std::cerr << std::endl
1452 *   << std::endl
1453 *   << "----------------------------------------------------"
1454 *   << std::endl;
1455 *   std::cerr << "Exception on processing: " << std::endl
1456 *   << exc.what() << std::endl
1457 *   << "Aborting!" << std::endl
1458 *   << "----------------------------------------------------"
1459 *   << std::endl;
1460 *   return 1;
1461 *   }
1462 *   catch (...)
1463 *   {
1464 *   std::cerr << std::endl
1465 *   << std::endl
1466 *   << "----------------------------------------------------"
1467 *   << std::endl;
1468 *   std::cerr << "Unknown exception!" << std::endl
1469 *   << "Aborting!" << std::endl
1470 *   << "----------------------------------------------------"
1471 *   << std::endl;
1472 *   return 1;
1473 *   }
1474 *   return 0;
1475 *   }
1476 * @endcode
1477<a name="step_29-Results"></a><h1>Results</h1>
1478
1479
1480The current program reads its run-time parameters from an input file
1481called <code>step-29.prm</code> that looks like this:
1482@code
1483subsection Mesh & geometry parameters
1484 # Distance of the focal point of the lens to the x-axis
1485 set Focal distance = 0.3
1486
1487 # Number of global mesh refinement steps applied to initial coarse grid
1488 set Number of refinements = 5
1489end
1490
1491
1492subsection Physical constants
1493 # Wave speed
1494 set c = 1.5e5
1495
1496 # Frequency
1497 set omega = 3.0e7
1498end
1499
1500
1501subsection Output parameters
1502 # Name of the output file (without extension)
1503 set Output file = solution
1504
1505 # A name for the output format to be used
1506 set Output format = vtu
1507end
1508@endcode
1509
1510As can be seen, we set
1511@f$d=0.3@f$, which amounts to a focus of the transducer lens
1512at @f$x=0.5@f$, @f$y=0.3@f$. The coarse mesh is refined 5 times,
1513resulting in 160x160 cells, and the output is written in vtu
1514format. The parameter reader understands many more parameters
1515pertaining in particular to the generation of output, but we
1516need none of these parameters here and therefore stick with
1517their default values.
1518
1519Here's the console output of the program in debug mode:
1520
1521@code
1522> make run
1523[ 66%] Built target @ref step_29 "step-29"
1524[100%] Run @ref step_29 "step-29" with Debug configuration
1525Generating grid... done (0.820449s)
1526 Number of active cells: 25600
1527Setting up system... done (1.18392s)
1528 Number of degrees of freedom: 51842
1529Assembling system matrix... done (2.33291s)
1530Solving linear system... done (1.34837s)
1531Generating output... done (2.05782s)
1532[100%] Built target run
1533@endcode
1534
1535(Of course, execution times will differ if you run the program
1536locally.) The fact that most of the time is spent on assembling
1537the system matrix and generating output is due to the many assertions
1538that need to be checked in debug mode. In release mode these parts
1539of the program run much faster whereas solving the linear system is
1540hardly sped up at all:
1541
1542@code
1543> make run
1544[ 66%] Built target @ref step_29 "step-29"
1545Scanning dependencies of target run
1546[100%] Run @ref step_29 "step-29" with Release configuration
1547DEAL::Generating grid... done (0.0144960s)
1548DEAL:: Number of active cells: 25600
1549DEAL::Setting up system... done (0.0356880s)
1550DEAL:: Number of degrees of freedom: 51842
1551DEAL::Assembling system matrix... done (0.0436570s)
1552DEAL::Solving linear system... done (1.54733s)
1553DEAL::Generating output... done (0.720528s)
1554[100%] Built target run
1555@endcode
1556
1557The graphical output of the program looks as follows:
1558
1559
1560<table align="center" class="doxtable">
1561 <tr>
1562 <td>
1563 <img src="https://www.dealii.org/images/steps/developer/step-29.v.png" alt="v = Re(u)">
1564 </td>
1565 <td>
1566 <img src="https://www.dealii.org/images/steps/developer/step-29.w.png" alt="w = Im(u)">
1567 </td>
1568 </tr>
1569 <tr>
1570 <td colspan="2">
1571 <img src="https://www.dealii.org/images/steps/developer/step-29.intensity.png" alt="|u|">
1572 </td>
1573 </tr>
1574</table>
1575
1576The first two pictures show the real and imaginary parts of
1577@f$u@f$, whereas the last shows the intensity @f$|u|@f$. One can clearly
1578see that the intensity is focused around the focal point of the
1579lens (0.5, 0.3), and that the focus
1580is rather sharp in @f$x@f$-direction but more blurred in @f$y@f$-direction, which is a
1581consequence of the geometry of the focusing lens, its finite aperture,
1582and the wave nature of the problem.
1583
1584Because colorful graphics are always fun, and to stress the focusing
1585effects some more, here is another set of images highlighting how well
1586the intensity is actually focused in @f$x@f$-direction:
1587
1588<table align="center" class="doxtable">
1589 <tr>
1590 <td>
1591 <img src="https://www.dealii.org/images/steps/developer/step-29.surface.png" alt="|u|">
1592 </td>
1593 <td>
1594 <img src="https://www.dealii.org/images/steps/developer/step-29.contours.png" alt="|u|">
1595 </td>
1596 </tr>
1597</table>
1598
1599
1600As a final note, the structure of the program makes it easy to
1601determine which parts of the program scale nicely as the mesh is
1602refined and which parts don't. Here are the run times for 5, 6, and 7
1603global refinements:
1604
1605@code
1606> make run
1607[ 66%] Built target @ref step_29 "step-29"
1608[100%] Run @ref step_29 "step-29" with Release configuration
1609DEAL::Generating grid... done (0.0135260s)
1610DEAL:: Number of active cells: 25600
1611DEAL::Setting up system... done (0.0213910s)
1612DEAL:: Number of degrees of freedom: 51842
1613DEAL::Assembling system matrix... done (0.0414300s)
1614DEAL::Solving linear system... done (1.56621s)
1615DEAL::Generating output... done (0.729605s)
1616[100%] Built target run
1617
1618> make run
1619[ 66%] Built target @ref step_29 "step-29"
1620[100%] Run @ref step_29 "step-29" with Release configuration
1621DEAL::Generating grid... done (0.0668490s)
1622DEAL:: Number of active cells: 102400
1623DEAL::Setting up system... done (0.109694s)
1624DEAL:: Number of degrees of freedom: 206082
1625DEAL::Assembling system matrix... done (0.160784s)
1626DEAL::Solving linear system... done (7.86577s)
1627DEAL::Generating output... done (2.89320s)
1628[100%] Built target run
1629
1630> make run
1631[ 66%] Built target @ref step_29 "step-29"
1632[100%] Run @ref step_29 "step-29" with Release configuration
1633DEAL::Generating grid... done (0.293154s)
1634DEAL:: Number of active cells: 409600
1635DEAL::Setting up system... done (0.491301s)
1636DEAL:: Number of degrees of freedom: 821762
1637DEAL::Assembling system matrix... done (0.605386s)
1638DEAL::Solving linear system... done (45.1989s)
1639DEAL::Generating output... done (11.2292s)
1640[100%] Built target run
1641@endcode
1642
1643Each time we refine the mesh once, so the number of cells and degrees
1644of freedom roughly quadruples from each step to the next. As can be seen,
1645generating the grid, setting up degrees of freedom, assembling the
1646linear system, and generating output scale pretty closely to linear,
1647whereas solving the linear system is an operation that requires 8
1648times more time each time the number of degrees of freedom is
1649increased by a factor of 4, i.e. it is @f${\cal O}(N^{3/2})@f$. This can
1650be explained by the fact that (using optimal ordering) the
1651bandwidth of a finite element matrix is @f$B={\cal O}(N^{(dim-1)/dim})@f$,
1652and the effort to solve a banded linear system using LU decomposition
1653is @f${\cal O}(BN)@f$. This also explains why the program does run in 3d
1654as well (after changing the dimension on the
1655<code>UltrasoundProblem</code> object), but scales very badly and
1656takes extraordinary patience before it finishes solving the linear
1657system on a mesh with appreciable resolution, even though all the
1658other parts of the program scale very nicely.
1659
1660
1661
1662<a name="step-29-extensions"></a>
1663<a name="step_29-Possibilitiesforextensions"></a><h3>Possibilities for extensions</h3>
1664
1665
1666An obvious possible extension for this program is to run it in 3d
1667&mdash; after all, the world around us is three-dimensional, and
1668ultrasound beams propagate in three-dimensional media. You can try
1669this by simply changing the template parameter of the principal class
1670in <code>main()</code> and running it. This won't get you very far,
1671though: certainly not if you do 5 global refinement steps as set in
1672the parameter file. You'll simply run out of memory as both the mesh
1673(with its @f$(2^5)^3 \cdot 5^3=2^{15}\cdot 125 \approx 4\cdot 10^6@f$ cells)
1674and in particular the sparse direct solver take too much memory. You
1675can solve with 3 global refinement steps, however, if you have a bit
1676of time: in early 2011, the direct solve takes about half an
1677hour. What you'll notice, however, is that the solution is completely
1678wrong: the mesh size is simply not small enough to resolve the
1679solution's waves accurately, and you can see this in plots of the
1680solution. Consequently, this is one of the cases where adaptivity is
1681indispensable if you don't just want to throw a bigger (presumably
1682%parallel) machine at the problem.
1683 *
1684 *
1685<a name="step_29-PlainProg"></a>
1686<h1> The plain program</h1>
1687@include "step-29.cc"
1688*/
static void declare_parameters(ParameterHandler &prm)
void add_data_vector(const VectorType &data, const std::vector< std::string > &names, const DataVectorType type=type_automatic, const std::vector< DataComponentInterpretation::DataComponentInterpretation > &data_component_interpretation={})
virtual void evaluate_vector_field(const DataPostprocessorInputs::Vector< dim > &input_data, std::vector< Vector< double > > &computed_quantities) const
virtual void evaluate_scalar_field(const DataPostprocessorInputs::Scalar< dim > &input_data, std::vector< Vector< double > > &computed_quantities) const
Definition fe_q.h:550
void enter_subsection(const std::string &subsection, const bool create_path_if_needed=true)
Definition point.h:111
Definition timer.h:117
Point< 3 > center
Point< 2 > second
Definition grid_out.cc:4614
Point< 2 > first
Definition grid_out.cc:4613
unsigned int level
Definition grid_out.cc:4616
__global__ void set(Number *val, const Number s, const size_type N)
#define AssertDimension(dim1, dim2)
void loop(IteratorType begin, std_cxx20::type_identity_t< IteratorType > end, DOFINFO &dinfo, INFOBOX &info, const std::function< void(DOFINFO &, typename INFOBOX::CellInfo &)> &cell_worker, const std::function< void(DOFINFO &, typename INFOBOX::CellInfo &)> &boundary_worker, const std::function< void(DOFINFO &, DOFINFO &, typename INFOBOX::CellInfo &, typename INFOBOX::CellInfo &)> &face_worker, AssemblerType &assembler, const LoopControl &lctrl=LoopControl())
Definition loop.h:442
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
UpdateFlags
@ update_hessians
Second derivatives of shape functions.
@ update_values
Shape function values.
@ update_normal_vectors
Normal vectors.
@ update_gradients
Shape function gradients.
const Event initial
Definition event.cc:64
void subdivided_hyper_cube(Triangulation< dim, spacedim > &tria, const unsigned int repetitions, const double left=0., const double right=1., const bool colorize=false)
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
spacedim const Point< spacedim > & p
Definition grid_tools.h:980
const std::vector< bool > & used
spacedim & mesh
Definition grid_tools.h:979
if(marked_vertices.size() !=0) for(auto it
for(unsigned int j=best_vertex+1;j< vertices.size();++j) if(vertices_to_use[j])
@ matrix
Contents is actually a matrix.
@ diagonal
Matrix is diagonal.
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:191
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > w(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
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)
bool check(const ConstraintKinds kind_in, const unsigned int dim)
int(&) functions(const void *v1, const void *v2)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
const InputIterator OutputIterator out
Definition parallel.h:167
const Iterator const std_cxx20::type_identity_t< Iterator > & end
Definition parallel.h:610
const InputIterator OutputIterator const Function & function
Definition parallel.h:168
STL namespace.
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
Definition types.h:32
unsigned int boundary_id
Definition types.h:144
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation