diff --git a/include/aspect/simulator.h b/include/aspect/simulator.h index 628a0676e47..abb4b5fe10a 100644 --- a/include/aspect/simulator.h +++ b/include/aspect/simulator.h @@ -1337,6 +1337,16 @@ namespace aspect void interpolate_onto_velocity_system(const TensorFunction<1,dim> &func, LinearAlgebra::Vector &vec) const; + /** + * Perform a Newton line search to determine the optimal step length + * along the search direction. + * + * This function is implemented in + * source/simulator/helper_functions.cc + */ + double perform_line_search(const DefectCorrectionResiduals &dcr, + const bool use_picard, + LinearAlgebra::BlockVector &search_direction); /** * Add constraints to the given @p constraints object that are required diff --git a/source/simulator/helper_functions.cc b/source/simulator/helper_functions.cc index 2316dac7dd6..ece09c818d9 100644 --- a/source/simulator/helper_functions.cc +++ b/source/simulator/helper_functions.cc @@ -2629,6 +2629,92 @@ namespace aspect return initial_newton_residual; } + /** + * Performs a Newton line search for the do_one_defect_correction_Stokes_step function + */ + template + double Simulator::perform_line_search(const DefectCorrectionResiduals &dcr, + const bool use_picard, + LinearAlgebra::BlockVector &search_direction) + { + LinearAlgebra::BlockVector backup_linearization_point(introspection.index_sets.stokes_partitioning, mpi_communicator); + double step_length_factor = 1.0; + unsigned int line_search_iteration = 0; + // Many parts of the solver depend on the block layout (velocity = 0, + // pressure = 1). For example the linearized_stokes_initial_guess vector or the StokesBlock matrix + // wrapper. Let us make sure that this holds (and shorten their names): + const unsigned int pressure_block_index = (parameters.include_melt_transport) ? + introspection.variable("fluid pressure").block_index + : introspection.block_indices.pressure; + const unsigned int velocity_block_index = introspection.block_indices.velocities; + Assert(velocity_block_index == 0, ExcNotImplemented()); + Assert(pressure_block_index == 1, ExcNotImplemented()); + (void) pressure_block_index; + // Do the loop for the line search at least once with the full step length. + // If line search is disabled we will exit the loop in the first iteration. + + do + { + if (line_search_iteration == 0) + { + // backup our starting point for the line search + backup_linearization_point.block(pressure_block_index) = current_linearization_point.block(pressure_block_index); + backup_linearization_point.block(velocity_block_index) = current_linearization_point.block(velocity_block_index); + } + else + { + // undo the last iteration and try again with smaller step length + current_linearization_point.block(pressure_block_index) = backup_linearization_point.block(pressure_block_index); + current_linearization_point.block(velocity_block_index) = backup_linearization_point.block(velocity_block_index); + search_direction.block(pressure_block_index) *= step_length_factor; + search_direction.block(velocity_block_index) *= step_length_factor; + } + + // Update the current linearization point with the search direction + current_linearization_point.block(pressure_block_index) += search_direction.block(pressure_block_index); + current_linearization_point.block(velocity_block_index) += search_direction.block(velocity_block_index); + + // Rebuild the rhs to determine the new residual. + assemble_newton_stokes_matrix = rebuild_stokes_preconditioner = false; + rebuild_stokes_matrix = !boundary_velocity_manager.get_prescribed_boundary_velocity_indicators().empty(); + + assemble_stokes_system(); + + const double test_velocity_residual = system_rhs.block(velocity_block_index).l2_norm(); + const double test_pressure_residual = system_rhs.block(pressure_block_index).l2_norm(); + const double test_residual = std::sqrt(test_velocity_residual * test_velocity_residual + + test_pressure_residual * test_pressure_residual); + + // Determine if the residual has decreased sufficiently. + const double alpha = 1e-4; + if (test_residual < (1.0 - alpha * step_length_factor) * dcr.residual + || + line_search_iteration >= newton_handler->parameters.max_newton_line_search_iterations + || + use_picard) + { + pcout << " Newton system information: Norm of the rhs: " << test_residual + << ", Derivative scaling factor: " << newton_handler->parameters.newton_derivative_scaling_factor << std::endl; + return test_residual; + } + else + { + pcout << " Line search iteration " << line_search_iteration << ", with norm of the rhs " + << test_residual << " and going to " << (1.0 - alpha * step_length_factor) * dcr.residual + << ", relative residual: " << test_residual/dcr.initial_residual << std::endl; + + // The current search direction has not decreased the residual + // enough, so we take a smaller step and try again. + // TODO: make a parameter out of this. + step_length_factor = 2.0/3.0; + } + + ++line_search_iteration; + Assert(line_search_iteration <= newton_handler->parameters.max_newton_line_search_iterations, + ExcInternalError()); + } + while (true); + } template @@ -2779,6 +2865,9 @@ namespace aspect template void Simulator::restore_outflow_boundary_ids(const unsigned int boundary_id_offset); \ template void Simulator::check_consistency_of_boundary_conditions() const; \ template double Simulator::compute_initial_newton_residual(); \ + template double Simulator::perform_line_search(const DefectCorrectionResiduals &dcr, \ + const bool use_picard, \ + LinearAlgebra::BlockVector &search_direction); \ template double Simulator::compute_Eisenstat_Walker_linear_tolerance(const bool EisenstatWalkerChoiceOne, \ const double maximum_linear_stokes_solver_tolerance, \ const double linear_stokes_solver_tolerance, \ diff --git a/source/simulator/solver_schemes.cc b/source/simulator/solver_schemes.cc index 1104ff0aedb..dc68c5b12bd 100644 --- a/source/simulator/solver_schemes.cc +++ b/source/simulator/solver_schemes.cc @@ -530,6 +530,7 @@ namespace aspect } + template void Simulator::do_one_defect_correction_Stokes_step(DefectCorrectionResiduals &dcr, const bool use_picard) @@ -702,7 +703,9 @@ namespace aspect dcr.residual, dcr.residual_old); - pcout << " The linear solver tolerance is set to " << parameters.linear_stokes_solver_tolerance << std::endl; + pcout << " The linear solver tolerance is set to " + << parameters.linear_stokes_solver_tolerance + << ". "; } } @@ -737,79 +740,11 @@ namespace aspect dcr.residual_old = dcr.residual; // We have computed an update. Prepare for a line search. - LinearAlgebra::BlockVector backup_linearization_point(introspection.index_sets.stokes_partitioning, mpi_communicator); - - double step_length_factor = 1.0; - unsigned int line_search_iteration = 0; - - // Do the loop for the line search at least once with the full step length. - // If line search is disabled we will exit the loop in the first iteration. - do - { - if (line_search_iteration == 0) - { - // backup our starting point for the line search - backup_linearization_point.block(pressure_block_index) = current_linearization_point.block(pressure_block_index); - backup_linearization_point.block(velocity_block_index) = current_linearization_point.block(velocity_block_index); - } - else - { - // undo the last iteration and try again with smaller step length - current_linearization_point.block(pressure_block_index) = backup_linearization_point.block(pressure_block_index); - current_linearization_point.block(velocity_block_index) = backup_linearization_point.block(velocity_block_index); - search_direction.block(pressure_block_index) *= step_length_factor; - search_direction.block(velocity_block_index) *= step_length_factor; - } - - // Update the current linearization point with the search direction - current_linearization_point.block(pressure_block_index) += search_direction.block(pressure_block_index); - current_linearization_point.block(velocity_block_index) += search_direction.block(velocity_block_index); - - // Rebuild the rhs to determine the new residual. - assemble_newton_stokes_matrix = rebuild_stokes_preconditioner = false; - rebuild_stokes_matrix = !boundary_velocity_manager.get_prescribed_boundary_velocity_indicators().empty(); - - assemble_stokes_system(); - - const double test_velocity_residual = system_rhs.block(velocity_block_index).l2_norm(); - const double test_pressure_residual = system_rhs.block(pressure_block_index).l2_norm(); - const double test_residual = std::sqrt(test_velocity_residual * test_velocity_residual - + test_pressure_residual * test_pressure_residual); - - // Determine if the residual has decreased sufficiently. - const double alpha = 1e-4; - if (test_residual < (1.0 - alpha * step_length_factor) * dcr.residual - || - line_search_iteration >= newton_handler->parameters.max_newton_line_search_iterations - || - use_picard) - { - pcout << " Newton system information: Norm of the rhs: " << test_residual - << ", Derivative scaling factor: " << newton_handler->parameters.newton_derivative_scaling_factor << std::endl; - dcr.residual = test_residual; - break; - } - else - { - pcout << " Line search iteration " << line_search_iteration << ", with norm of the rhs " - << test_residual << " and going to " << (1.0 - alpha * step_length_factor) * dcr.residual - << ", relative residual: " << test_residual/dcr.initial_residual << std::endl; - - // The current search direction has not decreased the residual - // enough, so we take a smaller step and try again. - // TODO: make a parameter out of this. - step_length_factor = 2.0/3.0; - } - - ++line_search_iteration; - Assert(line_search_iteration <= newton_handler->parameters.max_newton_line_search_iterations, - ExcInternalError()); - } - while (true); + dcr.residual = perform_line_search(dcr, use_picard, search_direction); } - if (use_picard == true) + if (use_picard) { // When we are using (defect corrected) Picard, keep the // newton_derivative_scaling_factor at zero. The newton_derivative_scaling_factor