deal.II version GIT relicensing-2173-gae8fc9d14b 2024-11-24 06:40:00+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
coupled_laplace_problem.h
Go to the documentation of this file.
1
126 *  
127 * @endcode
128 *
129 * The included deal.II header files are the same as in the other example
130 * programs:
131 *
132 * @code
133 *   #include <deal.II/base/function.h>
134 *   #include <deal.II/base/logstream.h>
135 *   #include <deal.II/base/quadrature_lib.h>
136 *  
137 *   #include <deal.II/dofs/dof_accessor.h>
138 *   #include <deal.II/dofs/dof_handler.h>
139 *   #include <deal.II/dofs/dof_tools.h>
140 *  
141 *   #include <deal.II/fe/fe_q.h>
142 *   #include <deal.II/fe/fe_values.h>
143 *  
144 *   #include <deal.II/grid/grid_generator.h>
145 *   #include <deal.II/grid/tria.h>
146 *   #include <deal.II/grid/tria_accessor.h>
147 *   #include <deal.II/grid/tria_iterator.h>
148 *  
149 *   #include <deal.II/lac/dynamic_sparsity_pattern.h>
150 *   #include <deal.II/lac/full_matrix.h>
151 *   #include <deal.II/lac/precondition.h>
152 *   #include <deal.II/lac/solver_cg.h>
153 *   #include <deal.II/lac/sparse_matrix.h>
154 *   #include <deal.II/lac/vector.h>
155 *  
156 *   #include <deal.II/numerics/data_out.h>
157 *   #include <deal.II/numerics/matrix_tools.h>
158 *   #include <deal.II/numerics/vector_tools.h>
159 * @endcode
160 *
161 * In addition to the deal.II header files, we include the preCICE API in order
162 * to obtain access to preCICE specific functionality
163 *
164 * @code
165 *   #include <precice/precice.hpp>
166 *  
167 *   #include <fstream>
168 *   #include <iostream>
169 *  
170 *   using namespace dealii;
171 *  
172 * @endcode
173 *
174 * Configuration parameters
175 *
176
177 *
178 * We set up a simple hard-coded struct containing all names we need for
179 * external coupling. The struct includes the name of the preCICE
180 * configuration file as well as the name of the simulation participant, the
181 * name of the coupling mesh and the name of the exchanged data. The last three
182 * names you also find in the preCICE configuration file. For real application
183 * cases, these names are better handled by a parameter file.
184 *
185 * @code
186 *   struct CouplingParamters
187 *   {
188 *   const std::string config_file = "precice-config.xml";
189 *   const std::string participant_name = "laplace-solver";
190 *   const std::string mesh_name = "dealii-mesh";
191 *   const std::string read_data_name = "boundary-data";
192 *   };
193 *  
194 *  
195 * @endcode
196 *
197 * The Adapter class
198 *
199
200 *
201 * The Adapter class handles all functionalities to couple the deal.II solver
202 * code to other solvers with preCICE, i.e., data structures are set up and all
203 * relevant information is passed to preCICE.
204 *
205
206 *
207 *
208 * @code
209 *   template <int dim, typename ParameterClass>
210 *   class Adapter
211 *   {
212 *   public:
213 *   Adapter(const ParameterClass & parameters,
214 *   const types::boundary_id dealii_boundary_interface_id);
215 *  
216 *   void
217 *   initialize(const DoFHandler<dim> & dof_handler,
218 *   std::map<types::global_dof_index, double> &boundary_data,
219 *   const MappingQGeneric<dim> & mapping);
220 *  
221 *   void
222 *   read_data(double relative_read_time,
223 *   std::map<types::global_dof_index, double> &boundary_data);
224 *  
225 *   void
226 *   advance(const double computed_timestep_length);
227 *  
228 * @endcode
229 *
230 * public precCICE solver interface
231 *
232 * @code
233 *   precice::Participant precice;
234 *  
235 * @endcode
236 *
237 * Boundary ID of the deal.II triangulation, associated with the coupling
238 * interface. The variable is defined in the constructor of this class and
239 * intentionally public so that it can be used during the grid generation and
240 * system assembly. The only thing, one needs to make sure is that this ID is
241 * unique for a particular triangulation.
242 *
243 * @code
244 *   const unsigned int dealii_boundary_interface_id;
245 *  
246 *   private:
247 * @endcode
248 *
249 * preCICE related initializations
250 * These variables are specified in and read from a parameter file, which is
251 * in this simple tutorial program the CouplingParameter struct already
252 * introduced in the beginning.
253 *
254 * @code
255 *   const std::string mesh_name;
256 *   const std::string read_data_name;
257 *  
258 * @endcode
259 *
260 * The node IDs are filled by preCICE during the initialization and associated to
261 * the spherical vertices we pass to preCICE
262 *
263 * @code
264 *   int n_interface_nodes;
265 *  
266 * @endcode
267 *
268 * DoF IndexSet, containing relevant coupling DoF indices at the coupling
269 * boundary
270 *
271 * @code
272 *   IndexSet coupling_dofs;
273 *  
274 * @endcode
275 *
276 * Data containers which are passed to preCICE in an appropriate preCICE
277 * specific format
278 *
279 * @code
280 *   std::vector<int> interface_nodes_ids;
281 *   std::vector<double> read_data_buffer;
282 *  
283 * @endcode
284 *
285 * The MPI rank and total number of MPI ranks is required by preCICE when the
286 * Participant is created. Since this tutorial runs only in serial mode we
287 * define the variables manually in this class instead of using the regular
288 * MPI interface.
289 *
290 * @code
291 *   static constexpr int this_mpi_process = 0;
292 *   static constexpr int n_mpi_processes = 1;
293 *  
294 * @endcode
295 *
296 * Function to transform the obtained data from preCICE into an appropriate
297 * map for Dirichlet boundary conditions
298 *
299 * @code
300 *   void
301 *   format_precice_to_dealii(
302 *   std::map<types::global_dof_index, double> &boundary_data) const;
303 *   };
304 *  
305 *  
306 *  
307 * @endcode
308 *
309 * In the constructor of the Adapter class, we set up the preCICE
310 * Participant. We need to tell preCICE our name as participant of the
311 * simulation and the name of the preCICE configuration file. Both have already
312 * been specified in the CouplingParameter class above. Thus, we pass the class
313 * directly to the constructor and read out all relevant information. As a
314 * second parameter, we need to specify the boundary ID of our triangulation,
315 * which is associated with the coupling interface.
316 *
317 * @code
318 *   template <int dim, typename ParameterClass>
319 *   Adapter<dim, ParameterClass>::Adapter(
320 *   const ParameterClass & parameters,
321 *   const types::boundary_id deal_boundary_interface_id)
322 *   : precice(parameters.participant_name,
323 *   parameters.config_file,
324 *   this_mpi_process,
325 *   n_mpi_processes)
326 *   , dealii_boundary_interface_id(deal_boundary_interface_id)
327 *   , mesh_name(parameters.mesh_name)
328 *   , read_data_name(parameters.read_data_name)
329 *   {}
330 *  
331 *  
332 *  
333 * @endcode
334 *
335 * This function initializes preCICE (e.g. establishes communication channels
336 * and allocates memory) and passes all relevant data to preCICE. For surface
337 * coupling, relevant data is in particular the location of the data points at
338 * the associated interface(s). The `boundary_data` is an empty map, which is
339 * filled by preCICE, i.e., information of the other participant. Throughout
340 * the system assembly, the map can directly be used in order to apply the
341 * Dirichlet boundary conditions in the linear system.
342 *
343 * @code
344 *   template <int dim, typename ParameterClass>
345 *   void
346 *   Adapter<dim, ParameterClass>::initialize(
347 *   const DoFHandler<dim> & dof_handler,
348 *   std::map<types::global_dof_index, double> &boundary_data,
349 *   const MappingQGeneric<dim> & mapping)
350 *   {
351 *   Assert(dim > 1, ExcNotImplemented());
352 *   AssertDimension(dim, precice.getMeshDimensions(mesh_name));
353 *  
354 * @endcode
355 *
356 * Afterwards, we extract the number of interface nodes and the coupling DoFs
357 * at the coupling interface from our deal.II solver via
359 *
360 * @code
361 *   std::set<types::boundary_id> couplingBoundary;
362 *   couplingBoundary.insert(dealii_boundary_interface_id);
363 *  
364 * @endcode
365 *
366 * The `ComponentMask()` might be important in case we deal with vector valued
367 * problems, because vector valued problems have a DoF for each component.
368 *
369 * @code
370 *   coupling_dofs = DoFTools::extract_boundary_dofs(dof_handler,
371 *   ComponentMask(),
372 *   couplingBoundary);
373 *  
374 * @endcode
375 *
376 * The coupling DoFs are used to set up the `boundary_data` map. At the end,
377 * we associate here each DoF with a respective boundary value.
378 *
379 * @code
380 *   for (const auto i : coupling_dofs)
381 *   boundary_data[i] = 0.0;
382 *  
383 * @endcode
384 *
385 * Since we deal with a scalar problem, the number of DoFs at the particular
386 * interface corresponds to the number of interface nodes.
387 *
388 * @code
389 *   n_interface_nodes = coupling_dofs.n_elements();
390 *  
391 *   std::cout << "\t Number of coupling nodes: " << n_interface_nodes
392 *   << std::endl;
393 *  
394 * @endcode
395 *
396 * Now, we need to tell preCICE the coordinates of the interface nodes. Hence,
397 * we set up a std::vector to pass the node positions to preCICE. Each node is
398 * specified only once.
399 *
400 * @code
401 *   std::vector<double> interface_nodes_positions;
402 *   interface_nodes_positions.reserve(dim * n_interface_nodes);
403 *  
404 * @endcode
405 *
406 * Set up the appropriate size of the data container needed for data
407 * exchange. Here, we deal with a scalar problem, so that only a scalar value
408 * is read/written per interface node.
409 *
410 * @code
411 *   read_data_buffer.resize(n_interface_nodes);
412 * @endcode
413 *
414 * The IDs are filled by preCICE during the initializations.
415 *
416 * @code
417 *   interface_nodes_ids.resize(n_interface_nodes);
418 *  
419 * @endcode
420 *
421 * The node location is obtained using `map_dofs_to_support_points()`.
422 *
423 * @code
424 *   std::map<types::global_dof_index, Point<dim>> support_points;
425 *   DoFTools::map_dofs_to_support_points(mapping, dof_handler, support_points);
426 *  
427 * @endcode
428 *
429 * `support_points` contains now the coordinates of all DoFs. In the next
430 * step, the relevant coordinates are extracted using the IndexSet with the
431 * extracted coupling_dofs.
432 *
433 * @code
434 *   for (const auto element : coupling_dofs)
435 *   for (int i = 0; i < dim; ++i)
436 *   interface_nodes_positions.push_back(support_points[element][i]);
437 *  
438 * @endcode
439 *
440 * Now we have all information to define the coupling mesh and pass the
441 * information to preCICE.
442 *
443 * @code
444 *   precice.setMeshVertices(mesh_name,
445 *   interface_nodes_positions,
446 *   interface_nodes_ids);
447 *  
448 * @endcode
449 *
450 * Then, we initialize preCICE internally calling the API function
451 * `initialize()`
452 *
453 * @code
454 *   precice.initialize();
455 *   }
456 *  
457 *   template <int dim, typename ParameterClass>
458 *   void
459 *   Adapter<dim, ParameterClass>::read_data(
460 *   double relative_read_time,
461 *   std::map<types::global_dof_index, double> &boundary_data)
462 *   {
463 * @endcode
464 *
465 * here, we obtain data, i.e. the boundary condition, from another
466 * participant. We have already vertex IDs and just need to convert our
467 * obtained data to the deal.II compatible 'boundary map' , which is done in
468 * the format_deal_to_precice function.
469 *
470 * @code
471 *   precice.readData(mesh_name,
472 *   read_data_name,
473 *   interface_nodes_ids,
474 *   relative_read_time,
475 *   read_data_buffer);
476 *  
477 * @endcode
478 *
479 * After receiving the coupling data in `read_data_buffer`, we convert it to
480 * the std::map `boundary_data` which is later needed in order to apply
481 * Dirichlet boundary conditions
482 *
483 * @code
484 *   format_precice_to_dealii(boundary_data);
485 *   }
486 *  
487 *  
488 * @endcode
489 *
490 * The function `advance()` is called in the main time loop after the
491 * computation in each time step. Here, preCICE exchanges the coupling data
492 * internally and computes mappings as well as acceleration methods.
493 *
494 * @code
495 *   template <int dim, typename ParameterClass>
496 *   void
497 *   Adapter<dim, ParameterClass>::advance(const double computed_timestep_length)
498 *   {
499 * @endcode
500 *
501 * We specify the computed time-step length and pass it to preCICE.
502 *
503 * @code
504 *   precice.advance(computed_timestep_length);
505 *   }
506 *  
507 *  
508 *  
509 * @endcode
510 *
511 * This function takes the std::vector obtained by preCICE in `read_data_buffer` and
512 * inserts the values to the right position in the boundary map used throughout
513 * our deal.II solver for Dirichlet boundary conditions. The function is only
514 * used internally in the Adapter class and not called in the solver itself. The
515 * order, in which preCICE sorts the data in the `read_data_buffer` vector is exactly
516 * the same as the order of the initially passed vertices coordinates.
517 *
518 * @code
519 *   template <int dim, typename ParameterClass>
520 *   void
521 *   Adapter<dim, ParameterClass>::format_precice_to_dealii(
522 *   std::map<types::global_dof_index, double> &boundary_data) const
523 *   {
524 * @endcode
525 *
526 * We already stored the coupling DoF indices in the `boundary_data` map, so
527 * that we can simply iterate over all keys in the map.
528 *
529 * @code
530 *   auto dof_component = boundary_data.begin();
531 *   for (int i = 0; i < n_interface_nodes; ++i)
532 *   {
533 *   AssertIndexRange(i, read_data_buffer.size());
534 *   boundary_data[dof_component->first] = read_data_buffer[i];
535 *   ++dof_component;
536 *   }
537 *   }
538 *  
539 *  
540 * @endcode
541 *
542 * The solver class is essentially the same as in @ref step_4 "step-4". We only extend the
543 * stationary problem to a time-dependent problem and introduced the coupling.
544 * Comments are added at any point, where the workflow differs from @ref step_4 "step-4".
545 *
546 * @code
547 *   template <int dim>
548 *   class CoupledLaplaceProblem
549 *   {
550 *   public:
551 *   CoupledLaplaceProblem();
552 *  
553 *   void
554 *   run();
555 *  
556 *   private:
557 *   void
558 *   make_grid();
559 *   void
560 *   setup_system();
561 *   void
562 *   assemble_system();
563 *   void
564 *   solve();
565 *   void
566 *   output_results() const;
567 *  
569 *   FE_Q<dim> fe;
570 *   DoFHandler<dim> dof_handler;
571 *   MappingQ1<dim> mapping;
572 *  
573 *   SparsityPattern sparsity_pattern;
574 *   SparseMatrix<double> system_matrix;
575 *  
576 *   Vector<double> solution;
577 *   Vector<double> old_solution;
578 *   Vector<double> system_rhs;
579 *  
580 * @endcode
581 *
582 * We allocate all structures required for the preCICE coupling: The map
583 * is used to apply Dirichlet boundary conditions and filled in the Adapter
584 * class with data from the other participant. The CouplingParameters hold the
585 * preCICE configuration as described above. The interface boundary ID is the
586 * ID associated to our coupling interface and needs to be specified, when we
587 * set up the Adapter class object, because we pass it directly to the
588 * Constructor of this class.
589 *
590 * @code
591 *   std::map<types::global_dof_index, double> boundary_data;
592 *   CouplingParamters parameters;
593 *   const types::boundary_id interface_boundary_id;
594 *   Adapter<dim, CouplingParamters> adapter;
595 *  
596 * @endcode
597 *
598 * The time-step size delta_t is the actual time-step size used for all
599 * computations. The preCICE time-step size is obtained by preCICE in order to
600 * ensure a synchronization at all coupling time steps. The solver time
601 * step-size is the desired time-step size of our individual solver. In more
602 * sophisticated computations, it might be determined adaptively. The
603 * `time_step` counter is just used for the time-step number.
604 *
605 * @code
606 *   double delta_t;
607 *   double precice_delta_t;
608 *   const double solver_delta_t = 0.1;
609 *   unsigned int time_step = 0;
610 *   };
611 *  
612 *  
613 *  
614 *   template <int dim>
615 *   class RightHandSide : public Function<dim>
616 *   {
617 *   public:
618 *   virtual double
619 *   value(const Point<dim> &p, const unsigned int component = 0) const override;
620 *   };
621 *  
622 *  
623 *  
624 *   template <int dim>
625 *   class BoundaryValues : public Function<dim>
626 *   {
627 *   public:
628 *   virtual double
629 *   value(const Point<dim> &p, const unsigned int component = 0) const override;
630 *   };
631 *  
632 *   template <int dim>
633 *   double
634 *   RightHandSide<dim>::value(const Point<dim> &p,
635 *   const unsigned int /*component*/) const
636 *   {
637 *   double return_value = 0.0;
638 *   for (unsigned int i = 0; i < dim; ++i)
639 *   return_value += 4.0 * std::pow(p(i), 4.0);
640 *  
641 *   return return_value;
642 *   }
643 *  
644 *  
645 *   template <int dim>
646 *   double
647 *   BoundaryValues<dim>::value(const Point<dim> &p,
648 *   const unsigned int /*component*/) const
649 *   {
650 *   return p.square();
651 *   }
652 *  
653 *  
654 *  
655 *   template <int dim>
656 *   CoupledLaplaceProblem<dim>::CoupledLaplaceProblem()
657 *   : fe(1)
658 *   , dof_handler(triangulation)
659 *   , interface_boundary_id(1)
660 *   , adapter(parameters, interface_boundary_id)
661 *   {}
662 *  
663 *  
664 *   template <int dim>
665 *   void
666 *   CoupledLaplaceProblem<dim>::make_grid()
667 *   {
669 *   triangulation.refine_global(4);
670 *  
671 *   for (const auto &cell : triangulation.active_cell_iterators())
672 *   for (const auto &face : cell->face_iterators())
673 *   {
674 * @endcode
675 *
676 * We choose the boundary in positive x direction for the
677 * interface coupling.
678 *
679 * @code
680 *   if (face->at_boundary() && (face->center()[0] == 1))
681 *   face->set_boundary_id(interface_boundary_id);
682 *   }
683 *  
684 *   std::cout << " Number of active cells: " << triangulation.n_active_cells()
685 *   << std::endl
686 *   << " Total number of cells: " << triangulation.n_cells()
687 *   << std::endl;
688 *   }
689 *  
690 *  
691 *   template <int dim>
692 *   void
693 *   CoupledLaplaceProblem<dim>::setup_system()
694 *   {
695 *   dof_handler.distribute_dofs(fe);
696 *  
697 *   std::cout << " Number of degrees of freedom: " << dof_handler.n_dofs()
698 *   << std::endl;
699 *  
700 *   DynamicSparsityPattern dsp(dof_handler.n_dofs());
701 *   DoFTools::make_sparsity_pattern(dof_handler, dsp);
702 *   sparsity_pattern.copy_from(dsp);
703 *  
704 *   system_matrix.reinit(sparsity_pattern);
705 *  
706 *   solution.reinit(dof_handler.n_dofs());
707 *   old_solution.reinit(dof_handler.n_dofs());
708 *   system_rhs.reinit(dof_handler.n_dofs());
709 *   }
710 *  
711 *  
712 *  
713 *   template <int dim>
714 *   void
715 *   CoupledLaplaceProblem<dim>::assemble_system()
716 *   {
717 * @endcode
718 *
719 * Reset global structures
720 *
721 * @code
722 *   system_rhs = 0;
723 *   system_matrix = 0;
724 * @endcode
725 *
726 * Update old solution values
727 *
728 * @code
729 *   old_solution = solution;
730 *  
731 *   QGauss<dim> quadrature_formula(fe.degree + 1);
732 *  
733 *   RightHandSide<dim> right_hand_side;
734 *  
735 *   FEValues<dim> fe_values(fe,
736 *   quadrature_formula,
739 *  
740 *   const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
741 *  
742 *   FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
743 *   Vector<double> cell_rhs(dofs_per_cell);
744 *   std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
745 * @endcode
746 *
747 * The solution values from previous time steps are stored for each quadrature
748 * point
749 *
750 * @code
751 *   std::vector<double> local_values_old_solution(fe_values.n_quadrature_points);
752 *  
753 *   for (const auto &cell : dof_handler.active_cell_iterators())
754 *   {
755 *   fe_values.reinit(cell);
756 *   cell_matrix = 0;
757 *   cell_rhs = 0;
758 * @endcode
759 *
760 * Get the local values from the `fe_values' object
761 *
762 * @code
763 *   fe_values.get_function_values(old_solution, local_values_old_solution);
764 *  
765 * @endcode
766 *
767 * The system matrix contains additionally a mass matrix due to the time
768 * discretization. The RHS has contributions from the old solution values.
769 *
770 * @code
771 *   for (const unsigned int q_index : fe_values.quadrature_point_indices())
772 *   for (const unsigned int i : fe_values.dof_indices())
773 *   {
774 *   for (const unsigned int j : fe_values.dof_indices())
775 *   cell_matrix(i, j) +=
776 *   ((fe_values.shape_value(i, q_index) * // phi_i(x_q)
777 *   fe_values.shape_value(j, q_index)) + // phi_j(x_q)
778 *   (delta_t * // delta t
779 *   fe_values.shape_grad(i, q_index) * // grad phi_i(x_q)
780 *   fe_values.shape_grad(j, q_index))) * // grad phi_j(x_q)
781 *   fe_values.JxW(q_index); // dx
782 *  
783 *   const auto x_q = fe_values.quadrature_point(q_index);
784 *   const auto &local_value = local_values_old_solution[q_index];
785 *   cell_rhs(i) += ((delta_t * // delta t
786 *   fe_values.shape_value(i, q_index) * // phi_i(x_q)
787 *   right_hand_side.value(x_q)) + // f(x_q)
788 *   fe_values.shape_value(i, q_index) *
789 *   local_value) * // phi_i(x_q)*val
790 *   fe_values.JxW(q_index); // dx
791 *   }
792 *  
793 * @endcode
794 *
795 * Copy local to global
796 *
797 * @code
798 *   cell->get_dof_indices(local_dof_indices);
799 *   for (const unsigned int i : fe_values.dof_indices())
800 *   {
801 *   for (const unsigned int j : fe_values.dof_indices())
802 *   system_matrix.add(local_dof_indices[i],
803 *   local_dof_indices[j],
804 *   cell_matrix(i, j));
805 *  
806 *   system_rhs(local_dof_indices[i]) += cell_rhs(i);
807 *   }
808 *   }
809 *   {
810 * @endcode
811 *
812 * At first, we apply the Dirichlet boundary condition from @ref step_4 "step-4", as
813 * usual.
814 *
815 * @code
816 *   std::map<types::global_dof_index, double> boundary_values;
817 *   VectorTools::interpolate_boundary_values(dof_handler,
818 *   0,
819 *   BoundaryValues<dim>(),
820 *   boundary_values);
821 *   MatrixTools::apply_boundary_values(boundary_values,
822 *   system_matrix,
823 *   solution,
824 *   system_rhs);
825 *   }
826 *   {
827 * @endcode
828 *
829 * Afterwards, we apply the coupling boundary condition. The `boundary_data`
830 * has already been filled by preCICE.
831 *
832 * @code
833 *   MatrixTools::apply_boundary_values(boundary_data,
834 *   system_matrix,
835 *   solution,
836 *   system_rhs);
837 *   }
838 *   }
839 *  
840 *  
841 *  
842 *   template <int dim>
843 *   void
844 *   CoupledLaplaceProblem<dim>::solve()
845 *   {
846 *   SolverControl solver_control(1000, 1e-12);
847 *   SolverCG<Vector<double>> solver(solver_control);
848 *   solver.solve(system_matrix, solution, system_rhs, PreconditionIdentity());
849 *  
850 *   std::cout << " " << solver_control.last_step()
851 *   << " CG iterations needed to obtain convergence." << std::endl;
852 *   }
853 *  
854 *  
855 *  
856 *   template <int dim>
857 *   void
858 *   CoupledLaplaceProblem<dim>::output_results() const
859 *   {
860 *   DataOut<dim> data_out;
861 *  
862 *   data_out.attach_dof_handler(dof_handler);
863 *   data_out.add_data_vector(solution, "solution");
864 *  
865 *   data_out.build_patches(mapping);
866 *  
867 *   std::ofstream output("solution-" + std::to_string(time_step) + ".vtk");
868 *   data_out.write_vtk(output);
869 *   }
870 *  
871 *  
872 *  
873 *   template <int dim>
874 *   void
875 *   CoupledLaplaceProblem<dim>::run()
876 *   {
877 *   std::cout << "Solving problem in " << dim << " space dimensions."
878 *   << std::endl;
879 *  
880 *   make_grid();
881 *   setup_system();
882 *  
883 * @endcode
884 *
885 * After we set up the system, we initialize preCICE and the adapter using the
886 * functionalities of the Adapter.
887 *
888 * @code
889 *   adapter.initialize(dof_handler, boundary_data, mapping);
890 *  
891 * @endcode
892 *
893 * preCICE steers the coupled simulation: `isCouplingOngoing` is
894 * used to synchronize the end of the simulation with the coupling partner
895 *
896 * @code
897 *   while (adapter.precice.isCouplingOngoing())
898 *   {
899 * @endcode
900 *
901 * The time step number is solely used to generate unique output files
902 *
903 * @code
904 *   ++time_step;
905 * @endcode
906 *
907 * preCICE returns the maximum admissible time-step size,
908 * which needs to be compared to our desired solver time-step size.
909 *
910 * @code
911 *   precice_delta_t = adapter.precice.getMaxTimeStepSize();
912 *   delta_t = std::min(precice_delta_t, solver_delta_t);
913 * @endcode
914 *
915 * Next we read data. Since we use a fully backward Euler method, we want
916 * the data to be associated to the end of the current time-step (delta_t)
917 * Time-interpolation methods in preCICE allow to get readData at any
918 * point in time, if the coupling scheme allows it
919 *
920 * @code
921 *   adapter.read_data(delta_t, boundary_data);
922 *  
923 * @endcode
924 *
925 * In the time loop, we assemble the coupled system and solve it as
926 * usual.
927 *
928 * @code
929 *   assemble_system();
930 *   solve();
931 *  
932 * @endcode
933 *
934 * After we solved the system, we advance the coupling to the next time
935 * level. In a bi-directional coupled simulation, we would pass our
936 * calculated data to preCICE
937 *
938 * @code
939 *   adapter.advance(delta_t);
940 *  
941 * @endcode
942 *
943 * Write an output file if the time step is completed. In case of an
944 * implicit coupling, where individual time steps are computed more than
945 * once, the function `isTimeWindowCompleted` prevents unnecessary result
946 * writing. For this simple tutorial configuration (explicit coupling),
947 * the function returns always `true`.
948 *
949 * @code
950 *   if (adapter.precice.isTimeWindowComplete())
951 *   output_results();
952 *   }
953 *   }
954 *  
955 *  
956 *  
957 *   int
958 *   main()
959 *   {
960 *   CoupledLaplaceProblem<2> laplace_problem;
961 *   laplace_problem.run();
962 *  
963 *   return 0;
964 *   }
965 * @endcode
966
967
968<a name="ann-fancy_boundary_condition.cc"></a>
969<h1>Annotated version of fancy_boundary_condition.cc</h1>
970 *
971 *
972 *
973 *
974 * @code
975 *   /* -----------------------------------------------------------------------------
976 *   *
977 *   * SPDX-License-Identifier: LGPL-2.1-or-later
978 *   * Copyright (C) 2020 by David Schneider
979 *   * Copyright (C) 2020 by Benjamin Uekermann
980 *   *
981 *   * This file is part of the deal.II code gallery.
982 *   *
983 *   * -----------------------------------------------------------------------------
984 *   */
985 *  
986 * @endcode
987 *
988 * This program does not use any deal.II functionality and depends only on
989 * preCICE and the standard libraries.
990 *
991 * @code
992 *   #include <precice/precice.hpp>
993 *  
994 *   #include <iostream>
995 *   #include <sstream>
996 *   #include <vector>
997 *  
998 * @endcode
999 *
1000 * The program computes a time-varying parabolic boundary condition, which is
1001 * passed to preCICE and serves as Dirichlet boundary condition for the other
1002 * coupling participant.
1003 *
1004
1005 *
1006 * Function to generate boundary values in each time step
1007 *
1008 * @code
1009 *   void
1010 *   define_boundary_values(std::vector<double> &boundary_data,
1011 *   const double time,
1012 *   const double end_time)
1013 *   {
1014 * @endcode
1015 *
1016 * Scale the current time value
1017 *
1018 * @code
1019 *   const double relative_time = time / end_time;
1020 * @endcode
1021 *
1022 * Define the amplitude. Values run from -0.5 to 0.5
1023 *
1024 * @code
1025 *   const double amplitude = (relative_time - 0.5);
1026 * @endcode
1027 *
1028 * Specify the actual data we want to pass to the other participant. Here, we
1029 * choose a parabola with boundary values 2 in order to enforce continuity
1030 * to adjacent boundaries.
1031 *
1032 * @code
1033 *   const double n_elements = boundary_data.size();
1034 *   const double right_zero = boundary_data.size() - 1;
1035 *   const double left_zero = 0;
1036 *   const double offset = 2;
1037 *   for (uint i = 0; i < n_elements; ++i)
1038 *   boundary_data[i] =
1039 *   -amplitude * ((i - left_zero) * (i - right_zero)) + offset;
1040 *   }
1041 *  
1042 *  
1043 *   int
1044 *   main()
1045 *   {
1046 *   std::cout << "Boundary participant: starting... \n";
1047 *  
1048 * @endcode
1049 *
1050 * Configuration
1051 *
1052 * @code
1053 *   const std::string configFileName("precice-config.xml");
1054 *   const std::string solverName("boundary-participant");
1055 *   const std::string meshName("boundary-mesh");
1056 *   const std::string dataWriteName("boundary-data");
1057 *  
1058 * @endcode
1059 *
1060 * Adjust to MPI rank and size for parallel computation
1061 *
1062 * @code
1063 *   const int commRank = 0;
1064 *   const int commSize = 1;
1065 *  
1066 *   precice::Participant precice(solverName, configFileName, commRank, commSize);
1067 *  
1068 *   const int dimensions = precice.getMeshDimensions(meshName);
1069 *   const int numberOfVertices = 6;
1070 *  
1071 * @endcode
1072 *
1073 * Set up data structures
1074 *
1075 * @code
1076 *   std::vector<double> writeData(numberOfVertices);
1077 *   std::vector<int> vertexIDs(numberOfVertices);
1078 *   std::vector<double> vertices(numberOfVertices * dimensions);
1079 *  
1080 * @endcode
1081 *
1082 * Define a boundary mesh
1083 *
1084 * @code
1085 *   std::cout << "Boundary participant: defining boundary mesh \n";
1086 *   const double length = 2;
1087 *   const double xCoord = 1;
1088 *   const double deltaY = length / (numberOfVertices - 1);
1089 *   for (int i = 0; i < numberOfVertices; ++i)
1090 *   for (int j = 0; j < dimensions; ++j)
1091 *   {
1092 *   const unsigned int index = dimensions * i + j;
1093 * @endcode
1094 *
1095 * The x-coordinate is always 1, i.e., the boundary is parallel to the
1096 * y-axis. The y-coordinate is descending from 1 to -1.
1097 *
1098 * @code
1099 *   if (j == 0)
1100 *   vertices[index] = xCoord;
1101 *   else
1102 *   vertices[index] = 1 - deltaY * i;
1103 *   }
1104 *  
1105 * @endcode
1106 *
1107 * Pass the vertices to preCICE
1108 *
1109 * @code
1110 *   precice.setMeshVertices(meshName, vertices, vertexIDs);
1111 *  
1112 * @endcode
1113 *
1114 * Variables for the time
1115 *
1116 * @code
1117 *   const double end_time = 1;
1118 *   double time = 0;
1119 *  
1120 * @endcode
1121 *
1122 * Not used in the configuration by default
1123 *
1124 * @code
1125 *   if (precice.requiresInitialData())
1126 *   {
1127 *   std::cout << "Boundary participant: writing initial data \n";
1128 *   define_boundary_values(writeData, time, end_time);
1129 *   precice.writeData(meshName, dataWriteName, vertexIDs, writeData);
1130 *   }
1131 *  
1132 * @endcode
1133 *
1134 * initialize the Participant
1135 *
1136 * @code
1137 *   precice.initialize();
1138 *  
1139 * @endcode
1140 *
1141 * Start time loop
1142 *
1143 * @code
1144 *   while (precice.isCouplingOngoing())
1145 *   {
1146 *   double dt = precice.getMaxTimeStepSize();
1147 *   time += dt;
1148 *  
1149 * @endcode
1150 *
1151 * Generate new boundary data
1152 *
1153 * @code
1154 *   define_boundary_values(writeData, time, end_time);
1155 *  
1156 *   std::cout << "Boundary participant: writing coupling data \n";
1157 *   precice.writeData(meshName, dataWriteName, vertexIDs, writeData);
1158 *  
1159 *   std::cout << "Boundary participant: advancing in time\n";
1160 *   precice.advance(dt);
1161 *   }
1162 *  
1163 *   std::cout << "Boundary participant: closing...\n";
1164 *  
1165 *   return 0;
1166 *   }
1167 * @endcode
1168
1169
1170*/
Definition fe_q.h:554
virtual RangeNumberType value(const Point< dim > &p, const unsigned int component=0) const
Definition point.h:111
Point< 2 > second
Definition grid_out.cc:4624
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
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:564
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)
@ update_values
Shape function values.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
std::vector< index_type > data
Definition mpi.cc:735
std::size_t size
Definition mpi.cc:734
IndexSet extract_boundary_dofs(const DoFHandler< dim, spacedim > &dof_handler, const ComponentMask &component_mask={}, const std::set< types::boundary_id > &boundary_ids={})
Definition dof_tools.cc:616
void map_dofs_to_support_points(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof_handler, std::vector< Point< spacedim > > &support_points, const ComponentMask &mask={})
void hyper_cube(Triangulation< dim, spacedim > &tria, const double left=0., const double right=1., const bool colorize=false)
void cell_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const FEValuesBase< dim > &fetest, const ArrayView< const std::vector< double > > &velocity, const double factor=1.)
Definition advection.h:74
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:191
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
void apply(const Kokkos::TeamPolicy< MemorySpace::Default::kokkos_space::execution_space >::member_type &team_member, const Kokkos::View< Number *, MemorySpace::Default::kokkos_space > shape_data, const ViewTypeIn in, ViewTypeOut out)
constexpr ReturnType< rank, T >::value_type & extract(T &t, const ArrayType &indices)
VectorType::value_type * end(VectorType &V)
std::vector< unsigned int > serial(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:92
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:107
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 reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
void transform(const InputIterator &begin_in, const InputIterator &end_in, OutputIterator out, const Function &function, const unsigned int grainsize)
Definition parallel.h:165
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
void advance(std::tuple< I1, I2 > &t, const unsigned int n)