Dear all, I am currently trying to implement the PETSc SNES solver (https://petsc.org/release/docs/manual/snes/) into my code. As a starting point, I am trying to implement it into the step-15 from the tutorials.
Attached can be found a MWE of the current version of the code which follows an older implementation (https://github.com/gujans/dealii-snes/blob/master/step-15.cc). The code performs the following steps: 1 - create the mesh; 2 - initialize the PETScWrappers::MPI matrix/vectors; 3 - set the boundary values; 4 - initialize the SNES solver; 5 - assemble the FormFunction and FormJacobian using the assemble_rhs and assemble_system as functions to set the values to the system matrix and rhs; 6 - solve the problem using the SNES solver. At the moment the code runs but it produces a solution which is constant zero, moreover the SNES iteration count returns zero as well. Am I missing something while applying the boundary conditions? How can I pass the correct assembled matrices to the SNES Solver? To be noted that if I pass to the solver the PETScWrappers::MPI::Vector present_solution, which is initialized during the setup_system(), instead of a Vec test_x which i created just to store the solution from the solver, it returns the following error: [0]PETSC ERROR: ----- Error Message -------- [0]PETSC ERROR: Object is in wrong state [0]PETSC ERROR: Not for unassembled vector Lastly, I also found an old post in the group (https://groups.google.com/g/dealii/c/pg9bj_z8dbk/m/OTtfdCY0AAAJ) which hinted another possible approach to the one i am using, however i am not sure how to extract PETSc Vec object from PETScWrappers::MPI::Vector without passing from a PETScWrapper::VectorBase. Thanks in advance, Lorenzo -- The deal.II project is located at http://www.dealii.org/ For mailing list/forum options, see https://groups.google.com/d/forum/dealii?hl=en --- You received this message because you are subscribed to the Google Groups "deal.II User Group" group. To unsubscribe from this group and stop receiving emails from it, send an email to dealii+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/dealii/6a940fd0-b6af-4e1e-9746-e72100b586d5n%40googlegroups.com.
// Base step 15 include files #include <deal.II/base/quadrature_lib.h> #include <deal.II/base/function.h> #include <deal.II/base/logstream.h> #include <deal.II/base/utilities.h> #include <deal.II/lac/vector.h> #include <deal.II/lac/full_matrix.h> #include <deal.II/lac/sparse_matrix.h> #include <deal.II/lac/dynamic_sparsity_pattern.h> #include <deal.II/lac/solver_cg.h> #include <deal.II/lac/precondition.h> #include <deal.II/lac/affine_constraints.h> #include <deal.II/grid/tria.h> #include <deal.II/grid/grid_generator.h> #include <deal.II/grid/tria_accessor.h> #include <deal.II/grid/tria_iterator.h> #include <deal.II/grid/manifold_lib.h> #include <deal.II/grid/grid_refinement.h> #include <deal.II/dofs/dof_handler.h> #include <deal.II/dofs/dof_accessor.h> #include <deal.II/dofs/dof_tools.h> #include <deal.II/fe/fe_values.h> #include <deal.II/fe/fe_q.h> #include <deal.II/numerics/vector_tools.h> #include <deal.II/numerics/matrix_tools.h> #include <deal.II/numerics/data_out.h> #include <deal.II/numerics/error_estimator.h> #include <fstream> #include <iostream> // Added include files for the SNES version #include <deal.II/base/conditional_ostream.h> #include <deal.II/base/mpi.h> #include <deal.II/lac/petsc_vector.h> #include <deal.II/lac/petsc_sparse_matrix.h> #include <deal.II/lac/petsc_solver.h> #include <deal.II/lac/petsc_precondition.h> #include <deal.II/grid/grid_tools.h> #include <deal.II/grid/grid_out.h> #include <deal.II/dofs/dof_renumbering.h> #include <petscsnes.h> #include <deal.II/numerics/solution_transfer.h> #include <deal.II/distributed/solution_transfer.h> namespace Step15 { // Setup Classe using namespace dealii; template <int dim> class MinimalSurfaceProblem { public: MinimalSurfaceProblem(); ~MinimalSurfaceProblem(); void run(); private: void setup_system(const bool initial_step); void set_boundary_values(); void assemble_system(PETScWrappers::MPI::Vector &present_solution); void assemble_rhs(PETScWrappers::MPI::Vector &present_solution, PETScWrappers::MPI::Vector &system_rhs); void refine_mesh(); static PetscErrorCode FormFunction(SNES snes, Vec x, Vec f, void* ctx); static PetscErrorCode FormJacobian(SNES snes, Vec x, Mat jac, Mat B, void* ctx); MPI_Comm mpi_communicator; const unsigned int n_mpi_processes; const unsigned int this_mpi_process; IndexSet locally_owned_dofs; ConditionalOStream pcout; Triangulation<dim> triangulation; DoFHandler<dim> dof_handler; FE_Q<dim> fe; AffineConstraints<double> hanging_node_constraints; SparsityPattern sparsity_pattern; PETScWrappers::MPI::SparseMatrix system_matrix; PETScWrappers::MPI::Vector present_solution; PETScWrappers::MPI::Vector system_rhs; Vec test_x; }; // Costructor template <int dim> MinimalSurfaceProblem<dim>::MinimalSurfaceProblem() : mpi_communicator(MPI_COMM_WORLD) , n_mpi_processes(Utilities::MPI::n_mpi_processes(mpi_communicator)) , this_mpi_process(Utilities::MPI::this_mpi_process(mpi_communicator)) , pcout(std::cout, (this_mpi_process == 0)) , dof_handler(triangulation) , fe(2) {} // Destructor template <int dim> MinimalSurfaceProblem<dim>::~MinimalSurfaceProblem() { dof_handler.clear(); } // Boundary values template <int dim> class BoundaryValues : public Function<dim> { public: virtual double value(const Point<dim>& p, const unsigned int component = 0) const override; }; template <int dim> double BoundaryValues<dim>::value(const Point<dim>& p, const unsigned int /*component*/) const { return std::sin(2 * numbers::PI * (p[0] + p[1])); } // Function used to create the FormFunction Input for the SNESSetFunction template <int dim> PetscErrorCode MinimalSurfaceProblem<dim>::FormFunction(SNES snes, Vec x, Vec f, void* ctx) { auto p_ctx = reinterpret_cast<MinimalSurfaceProblem<dim>*>(ctx); PETScWrappers::VectorBase xx(x); PETScWrappers::VectorBase ff(f); PETScWrappers::MPI::Vector x_wrap(p_ctx->mpi_communicator, xx, xx.local_size()); PETScWrappers::MPI::Vector f_wrap(p_ctx->mpi_communicator, ff, ff.local_size()); p_ctx->assemble_rhs(x_wrap, f_wrap); return 0; } // Function used to create the FormFunction Input for the SNESSetJacobian template <int dim> PetscErrorCode MinimalSurfaceProblem<dim>::FormJacobian(SNES snes, Vec x, Mat jac, Mat B, void* ctx) { auto p_ctx = reinterpret_cast<MinimalSurfaceProblem<dim>*>(ctx); PETScWrappers::VectorBase xx(x); PETScWrappers::MPI::Vector x_wrap(p_ctx->mpi_communicator, xx, xx.local_size()); p_ctx->assemble_system(x_wrap); /* Assemble matrix */ PetscErrorCode ierr; ierr = MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY); CHKERRQ(ierr); ierr = MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY); CHKERRQ(ierr); if (jac != B) { ierr = MatAssemblyBegin(jac, MAT_FINAL_ASSEMBLY); CHKERRQ(ierr); ierr = MatAssemblyEnd(jac, MAT_FINAL_ASSEMBLY); CHKERRQ(ierr); } return 0; } // Setup System template <int dim> void MinimalSurfaceProblem<dim>::setup_system(const bool initial_step) { GridTools::partition_triangulation(n_mpi_processes, triangulation); if (initial_step) { dof_handler.distribute_dofs(fe); DoFRenumbering::subdomain_wise(dof_handler); const std::vector<IndexSet> locally_owned_dofs_per_proc = DoFTools::locally_owned_dofs_per_subdomain(dof_handler); locally_owned_dofs = locally_owned_dofs_per_proc[this_mpi_process]; present_solution.reinit(locally_owned_dofs, mpi_communicator); } const std::vector<IndexSet> locally_owned_dofs_per_proc = DoFTools::locally_owned_dofs_per_subdomain(dof_handler); locally_owned_dofs = locally_owned_dofs_per_proc[this_mpi_process]; hanging_node_constraints.clear(); DoFTools::make_hanging_node_constraints(dof_handler, hanging_node_constraints); VectorTools::interpolate_boundary_values(dof_handler, 0, ZeroFunction<dim>(), hanging_node_constraints); hanging_node_constraints.close(); system_rhs.reinit(locally_owned_dofs, mpi_communicator); DynamicSparsityPattern dsp(dof_handler.n_dofs()); DoFTools::make_sparsity_pattern(dof_handler, dsp, hanging_node_constraints, false); hanging_node_constraints.condense(dsp); sparsity_pattern.copy_from(dsp); system_matrix.reinit(locally_owned_dofs, locally_owned_dofs, sparsity_pattern, mpi_communicator); } // Assemble bilinear form template <int dim> void MinimalSurfaceProblem<dim>::assemble_system(PETScWrappers::MPI::Vector &present_solution) { const QGauss<dim> quadrature_formula(fe.degree + 1); system_matrix = 0; FEValues<dim> fe_values(fe, quadrature_formula, update_gradients | update_quadrature_points | update_JxW_values); const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell); std::vector<Tensor<1, dim>> old_solution_gradients(n_q_points); std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell); for (const auto& cell : dof_handler.active_cell_iterators()) if (cell->subdomain_id() == this_mpi_process) { cell_matrix = 0; fe_values.reinit(cell); fe_values.get_function_gradients(present_solution, old_solution_gradients); for (unsigned int q = 0; q < n_q_points; ++q) { const double coeff = 1.0 / std::sqrt(1 + old_solution_gradients[q] * old_solution_gradients[q]); for (unsigned int i = 0; i < dofs_per_cell; ++i) { for (unsigned int j = 0; j < dofs_per_cell; ++j) cell_matrix(i, j) += (((fe_values.shape_grad(i, q) // ((\nabla \phi_i * coeff // * a_n * fe_values.shape_grad(j, q)) // * \nabla \phi_j) - // - (fe_values.shape_grad(i, q) // (\nabla \phi_i * coeff * coeff * coeff // * a_n^3 * (fe_values.shape_grad(j, q) // * (\nabla \phi_j * old_solution_gradients[q]) // * \nabla u_n) * old_solution_gradients[q])) // * \nabla u_n))) * fe_values.JxW(q)); // * dx } } cell->get_dof_indices(local_dof_indices); hanging_node_constraints.distribute_local_to_global(cell_matrix, local_dof_indices, system_matrix); } system_matrix.compress(VectorOperation::add); std::map<types::global_dof_index, double> boundary_values; VectorTools::interpolate_boundary_values(dof_handler, 0, Functions::ZeroFunction<dim>(), boundary_values); MatrixTools::apply_boundary_values(boundary_values, system_matrix, present_solution, system_rhs); } // Assemble RHS template <int dim> void MinimalSurfaceProblem<dim>::assemble_rhs(PETScWrappers::MPI::Vector &present_solution, PETScWrappers::MPI::Vector &system_rhs) { const QGauss<dim> quadrature_formula(fe.degree + 1); system_rhs = 0; FEValues<dim> fe_values(fe, quadrature_formula, update_gradients | update_quadrature_points | update_JxW_values); const unsigned int dofs_per_cell = fe.dofs_per_cell; const unsigned int n_q_points = quadrature_formula.size(); FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell); Vector<double> cell_rhs(dofs_per_cell); std::vector<Tensor<1, dim>> old_solution_gradients(n_q_points); std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell); for (const auto& cell : dof_handler.active_cell_iterators()) if (cell->subdomain_id() == this_mpi_process) { cell_rhs = 0; fe_values.reinit(cell); fe_values.get_function_gradients(present_solution, old_solution_gradients); for (unsigned int q = 0; q < n_q_points; ++q) { const double coeff = 1.0 / std::sqrt(1 + old_solution_gradients[q] * old_solution_gradients[q]); for (unsigned int i = 0; i < dofs_per_cell; ++i) { cell_rhs(i) -= (fe_values.shape_grad(i, q) // \nabla \phi_i * coeff // * a_n * old_solution_gradients[q] // * u_n * fe_values.JxW(q)); // * dx } } cell->get_dof_indices(local_dof_indices); hanging_node_constraints.distribute_local_to_global(cell_rhs, local_dof_indices, system_rhs); } system_rhs.compress(VectorOperation::add); } // Setup boundary values template <int dim> void MinimalSurfaceProblem<dim>::set_boundary_values() { std::map<types::global_dof_index, double> boundary_values; VectorTools::interpolate_boundary_values(dof_handler, 0, BoundaryValues<dim>(), boundary_values); for (auto &boundary_value : boundary_values) present_solution(boundary_value.first) = boundary_value.second; } // Run template <int dim> void MinimalSurfaceProblem<dim>::run() { GridGenerator::hyper_ball(triangulation); triangulation.refine_global(2); setup_system(true); set_boundary_values(); // SNES solver SNES snes; PetscErrorCode ierr; ierr = SNESCreate(mpi_communicator, &snes); ierr = VecCreate(mpi_communicator,&test_x); ierr = VecSetSizes(test_x,PETSC_DECIDE,present_solution.size()); ierr = VecSetFromOptions(test_x); ierr = SNESSetFunction(snes, system_rhs, FormFunction, this); ierr = SNESSetJacobian(snes, system_matrix, system_matrix, FormJacobian, this); ierr = SNESSetFromOptions(snes); ierr = SNESSolve(snes, NULL, test_x); PETScWrappers::VectorBase test_xx(test_x); PETScWrappers::MPI::Vector output_solution(mpi_communicator, test_xx, test_xx.local_size()); PetscInt its; SNESGetIterationNumber(snes, &its); PetscPrintf(MPI_COMM_WORLD, "Number of SNES iterations = %D\n", its); ierr = SNESDestroy(&snes); ierr = VecDestroy(&test_x); DataOut<dim> data_out; data_out.attach_dof_handler(dof_handler); data_out.add_data_vector(output_solution, "solution"); data_out.build_patches(); const std::string filename = "solution.vtk"; std::ofstream output(filename.c_str()); data_out.write_vtk(output); } } // namespace Step15 // @sect4{The main function} int main(int argc, char** argv) { try { using namespace dealii; using namespace Step15; Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1); deallog.depth_console(0); MinimalSurfaceProblem<2> laplace_problem_2d; laplace_problem_2d.run(); } catch (std::exception &exc) { std::cerr << std::endl << std::endl << "----------------------------------------------------" << std::endl; std::cerr << "Exception on processing: " << std::endl << exc.what() << std::endl << "Aborting!" << std::endl << "----------------------------------------------------" << std::endl; return 1; } catch (...) { std::cerr << std::endl << std::endl << "----------------------------------------------------" << std::endl; std::cerr << "Unknown exception!" << std::endl << "Aborting!" << std::endl << "----------------------------------------------------" << std::endl; return 1; } return 0; }