652 *
double return_value = 0.0;
653 *
for (
unsigned int i = 0; i < dim; ++i)
654 * return_value += 4.0 *
std::pow(p(i), 4.0);
656 *
return return_value;
662 * BoundaryValues<dim>::value(
const Point<dim> &p,
663 *
const unsigned int )
const
671 * CoupledLaplaceProblem<dim>::CoupledLaplaceProblem()
674 * , interface_boundary_id(1)
675 * , adapter(parameters, interface_boundary_id)
681 * CoupledLaplaceProblem<dim>::make_grid()
686 *
for (
const auto &cell :
triangulation.active_cell_iterators())
687 * for (const auto &face : cell->face_iterators())
691 * We choose the boundary in
positive x direction for the
692 * interface coupling.
695 * if (face->at_boundary() && (face->
center()[0] == 1))
696 * face->set_boundary_id(interface_boundary_id);
699 * std::cout <<
" Number of active cells: " <<
triangulation.n_active_cells()
708 * CoupledLaplaceProblem<dim>::setup_system()
710 * dof_handler.distribute_dofs(fe);
712 * std::cout <<
" Number of degrees of freedom: " << dof_handler.n_dofs()
717 * sparsity_pattern.copy_from(dsp);
719 * system_matrix.reinit(sparsity_pattern);
721 * solution.reinit(dof_handler.n_dofs());
722 * old_solution.reinit(dof_handler.n_dofs());
723 * system_rhs.reinit(dof_handler.n_dofs());
730 * CoupledLaplaceProblem<dim>::assemble_system()
734 * Reset global structures
741 * Update old solution
values
744 * old_solution = solution;
748 * RightHandSide<dim> right_hand_side;
751 * quadrature_formula,
755 *
const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
759 * std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
762 * The solution
values from previous time steps are stored
for each quadrature
766 * std::vector<double> local_values_old_solution(fe_values.n_quadrature_points);
768 *
for (
const auto &cell : dof_handler.active_cell_iterators())
775 * Get the local
values from the `fe_values
' object
778 * fe_values.get_function_values(old_solution, local_values_old_solution);
782 * The system matrix contains additionally a mass matrix due to the time
783 * discretization. The RHS has contributions from the old solution values.
786 * for (const unsigned int q_index : fe_values.quadrature_point_indices())
787 * for (const unsigned int i : fe_values.dof_indices())
789 * for (const unsigned int j : fe_values.dof_indices())
790 * cell_matrix(i, j) +=
791 * ((fe_values.shape_value(i, q_index) * // phi_i(x_q)
792 * fe_values.shape_value(j, q_index)) + // phi_j(x_q)
793 * (delta_t * // delta t
794 * fe_values.shape_grad(i, q_index) * // grad phi_i(x_q)
795 * fe_values.shape_grad(j, q_index))) * // grad phi_j(x_q)
796 * fe_values.JxW(q_index); // dx
798 * const auto x_q = fe_values.quadrature_point(q_index);
799 * const auto &local_value = local_values_old_solution[q_index];
800 * cell_rhs(i) += ((delta_t * // delta t
801 * fe_values.shape_value(i, q_index) * // phi_i(x_q)
802 * right_hand_side.value(x_q)) + // f(x_q)
803 * fe_values.shape_value(i, q_index) *
804 * local_value) * // phi_i(x_q)*val
805 * fe_values.JxW(q_index); // dx
810 * Copy local to global
813 * cell->get_dof_indices(local_dof_indices);
814 * for (const unsigned int i : fe_values.dof_indices())
816 * for (const unsigned int j : fe_values.dof_indices())
817 * system_matrix.add(local_dof_indices[i],
818 * local_dof_indices[j],
819 * cell_matrix(i, j));
821 * system_rhs(local_dof_indices[i]) += cell_rhs(i);
827 * At first, we apply the Dirichlet boundary condition from @ref step_4 "step-4", as
831 * std::map<types::global_dof_index, double> boundary_values;
832 * VectorTools::interpolate_boundary_values(dof_handler,
834 * BoundaryValues<dim>(),
836 * MatrixTools::apply_boundary_values(boundary_values,
844 * Afterwards, we apply the coupling boundary condition. The `boundary_data`
845 * has already been filled by preCICE.
848 * MatrixTools::apply_boundary_values(boundary_data,
859 * CoupledLaplaceProblem<dim>::solve()
861 * SolverControl solver_control(1000, 1e-12);
862 * SolverCG<Vector<double>> solver(solver_control);
863 * solver.solve(system_matrix, solution, system_rhs, PreconditionIdentity());
865 * std::cout << " " << solver_control.last_step()
866 * << " CG iterations needed to obtain convergence." << std::endl;
873 * CoupledLaplaceProblem<dim>::output_results() const
875 * DataOut<dim> data_out;
877 * data_out.attach_dof_handler(dof_handler);
878 * data_out.add_data_vector(solution, "solution");
880 * data_out.build_patches(mapping);
882 * std::ofstream output("solution-" + std::to_string(time_step) + ".vtk");
883 * data_out.write_vtk(output);
890 * CoupledLaplaceProblem<dim>::run()
892 * std::cout << "Solving problem in " << dim << " space dimensions."
900 * After we set up the system, we initialize preCICE using the functionalities
901 * of the Adapter. preCICE returns the maximum admissible time-step size,
902 * which needs to be compared to our desired solver time-step size.
905 * precice_delta_t = adapter.initialize(dof_handler, boundary_data, mapping);
906 * delta_t = std::min(precice_delta_t, solver_delta_t);
910 * preCICE steers the coupled simulation: `isCouplingOngoing` is
911 * used to synchronize the end of the simulation with the coupling partner
914 * while (adapter.precice.isCouplingOngoing())
918 * The time step number is solely used to generate unique output files
924 * In the time loop, we assemble the coupled system and solve it as
933 * After we solved the system, we advance the coupling to the next time
934 * level. In a bi-directional coupled simulation, we would pass our
935 * calculated data to and obtain new data from preCICE. Here, we simply
936 * obtain new data from preCICE, so from the other participant. As before,
937 * we obtain a maximum time-step size and compare it against the desired
938 * solver time-step size.
941 * precice_delta_t = adapter.advance(boundary_data, delta_t);
942 * delta_t = std::min(precice_delta_t, solver_delta_t);
946 * Write an output file if the time step is completed. In case of an
947 * implicit coupling, where individual time steps are computed more than
948 * once, the function `isTimeWindowCompleted` prevents unnecessary result
949 * writing. For this simple tutorial configuration (explicit coupling),
950 * the function returns always `true`.
953 * if (adapter.precice.isTimeWindowComplete())
963 * CoupledLaplaceProblem<2> laplace_problem;
964 * laplace_problem.run();
971<a name="ann-fancy_boundary_condition.cc"></a>
972<h1>Annotated version of fancy_boundary_condition.cc</h1>
976 * This program does not use any deal.II functionality and depends only on
977 * preCICE and the standard libraries.
980 * #include <precice/SolverInterface.hpp>
982 * #include <iostream>
987 * The program computes a time-varying parabolic boundary condition, which is
988 * passed to preCICE and serves as Dirichlet boundary condition for the other
989 * coupling participant.
993 * Function to generate boundary values in each time step
997 * define_boundary_values(std::vector<double> &boundary_data,
999 * const double end_time)
1003 * Scale the current time value
1006 * const double relative_time = time / end_time;
1009 * Define the amplitude. Values run from -0.5 to 0.5
1012 * const double amplitude = (relative_time - 0.5);
1015 * Specify the actual data we want to pass to the other participant. Here, we
1016 * choose a parabola with boundary values 2 in order to enforce continuity
1017 * to adjacent boundaries.
1020 * const double n_elements = boundary_data.size();
1021 * const double right_zero = boundary_data.size() - 1;
1022 * const double left_zero = 0;
1023 * const double offset = 2;
1024 * for (uint i = 0; i < n_elements; ++i)
1025 * boundary_data[i] =
1026 * -amplitude * ((i - left_zero) * (i - right_zero)) + offset;
1033 * std::cout << "Boundary participant: starting... \n";
1040 * const std::string configFileName("precice-config.xml");
1041 * const std::string solverName("boundary-participant");
1042 * const std::string meshName("boundary-mesh");
1043 * const std::string dataWriteName("boundary-data");
1047 * Adjust to MPI rank and size for parallel computation
1050 * const int commRank = 0;
1051 * const int commSize = 1;
1053 * precice::SolverInterface precice(solverName,
1058 * const int meshID = precice.getMeshID(meshName);
1059 * const int dimensions = precice.getDimensions();
1060 * const int numberOfVertices = 6;
1062 * const int dataID = precice.getDataID(dataWriteName, meshID);
1066 * Set up data structures
1069 * std::vector<double> writeData(numberOfVertices);
1070 * std::vector<int> vertexIDs(numberOfVertices);
1071 * std::vector<double> vertices(numberOfVertices * dimensions);
1075 * Define a boundary mesh
1078 * std::cout << "Boundary participant: defining boundary mesh \n";
1079 * const double length = 2;
1080 * const double xCoord = 1;
1081 * const double deltaY = length / (numberOfVertices - 1);
1082 * for (int i = 0; i < numberOfVertices; ++i)
1083 * for (int j = 0; j < dimensions; ++j)
1085 * const unsigned int index = dimensions * i + j;
1088 * The x-coordinate is always 1, i.e., the boundary is parallel to the
1089 * y-axis. The y-coordinate is descending from 1 to -1.
1093 * vertices[index] = xCoord;
1095 * vertices[index] = 1 - deltaY * i;
1100 * Pass the vertices to preCICE
1103 * precice.setMeshVertices(meshID,
1106 * vertexIDs.data());
1110 * initialize the Solverinterface
1113 * double dt = precice.initialize();
1120 * const double end_time = 1;
1122 * while (precice.isCouplingOngoing())
1126 * Generate new boundary data
1129 * define_boundary_values(writeData, time, end_time);
1132 * std::cout << "Boundary participant: writing coupling data \n";
1133 * precice.writeBlockScalarData(dataID,
1136 * writeData.data());
1139 * dt = precice.advance(dt);
1140 * std::cout << "Boundary participant: advancing in time\n";
1145 * std::cout << "Boundary participant: closing...\n";
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints=AffineConstraints< number >(), 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.
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.)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation