solve_pressure_correction Subroutine

private subroutine solve_pressure_correction(mesh, flow, bc, params, rhs, phi, stats)

Iteratively solves the Pressure Poisson system using PCG.

The solver uses a Diagonal Preconditioner to improve convergence speed on hexahedral meshes. For purely Neumann problems (closed boxes), the first cell is pinned to zero to remove the singularity.

Arguments

Type IntentOptional Attributes Name
type(mesh_t), intent(in) :: mesh
type(flow_mpi_t), intent(inout) :: flow
type(bc_set_t), intent(in) :: bc
type(case_params_t), intent(in) :: params
real(kind=rk), intent(in) :: rhs(:)
real(kind=rk), intent(inout) :: phi(:)
type(solver_stats_t), intent(inout) :: stats

Calls

proc~~solve_pressure_correction~~CallsGraph proc~solve_pressure_correction mod_flow_projection::solve_pressure_correction proc~ensure_pressure_operator_cache mod_flow_projection::ensure_pressure_operator_cache proc~solve_pressure_correction->proc~ensure_pressure_operator_cache proc~ensure_projection_workspace mod_flow_projection::ensure_projection_workspace proc~solve_pressure_correction->proc~ensure_projection_workspace proc~flow_exchange_cell_scalar mod_mpi_flow::flow_exchange_cell_scalar proc~solve_pressure_correction->proc~flow_exchange_cell_scalar proc~flow_global_dot_owned mod_mpi_flow::flow_global_dot_owned proc~solve_pressure_correction->proc~flow_global_dot_owned proc~flow_global_two_dots_owned mod_mpi_flow::flow_global_two_dots_owned proc~solve_pressure_correction->proc~flow_global_two_dots_owned proc~pressure_matvec mod_flow_projection::pressure_matvec proc~solve_pressure_correction->proc~pressure_matvec res res proc~solve_pressure_correction->res mpi_allreduce mpi_allreduce proc~ensure_pressure_operator_cache->mpi_allreduce proc~boundary_pressure_type mod_bc::boundary_pressure_type proc~ensure_pressure_operator_cache->proc~boundary_pressure_type proc~face_effective_neighbor mod_bc::face_effective_neighbor proc~ensure_pressure_operator_cache->proc~face_effective_neighbor proc~face_normal_distance mod_flow_projection::face_normal_distance proc~ensure_pressure_operator_cache->proc~face_normal_distance proc~fatal_error mod_kinds::fatal_error proc~ensure_pressure_operator_cache->proc~fatal_error proc~finalize_flow_projection_workspace mod_flow_projection::finalize_flow_projection_workspace proc~ensure_pressure_operator_cache->proc~finalize_flow_projection_workspace proc~patch_type_for_face mod_bc::patch_type_for_face proc~ensure_pressure_operator_cache->proc~patch_type_for_face proc~ensure_projection_workspace->proc~finalize_flow_projection_workspace mpi_irecv mpi_irecv proc~flow_exchange_cell_scalar->mpi_irecv mpi_isend mpi_isend proc~flow_exchange_cell_scalar->mpi_isend mpi_waitall mpi_waitall proc~flow_exchange_cell_scalar->mpi_waitall proc~check_mpi~2 mod_mpi_flow::check_mpi proc~flow_exchange_cell_scalar->proc~check_mpi~2 proc~profiler_start mod_profiler::profiler_start proc~flow_exchange_cell_scalar->proc~profiler_start proc~profiler_stop mod_profiler::profiler_stop proc~flow_exchange_cell_scalar->proc~profiler_stop proc~flow_global_dot_owned->mpi_allreduce proc~flow_global_dot_owned->proc~check_mpi~2 proc~flow_global_dot_owned->proc~profiler_start proc~flow_global_dot_owned->proc~profiler_stop proc~flow_global_two_dots_owned->mpi_allreduce proc~flow_global_two_dots_owned->proc~check_mpi~2 proc~flow_global_two_dots_owned->proc~profiler_start proc~flow_global_two_dots_owned->proc~profiler_stop proc~pressure_matvec->proc~ensure_pressure_operator_cache proc~pressure_matvec->proc~profiler_start proc~pressure_matvec->proc~profiler_stop proc~check_mpi~2->proc~fatal_error proc~is_periodic_face mod_bc::is_periodic_face proc~face_effective_neighbor->proc~is_periodic_face proc~face_normal_distance->proc~fatal_error proc~face_normal_distance->proc~patch_type_for_face proc~outward_normal mod_flow_projection::outward_normal proc~face_normal_distance->proc~outward_normal mpi_wtime mpi_wtime proc~profiler_start->mpi_wtime proc~find_or_create_timer mod_profiler::find_or_create_timer proc~profiler_start->proc~find_or_create_timer proc~profiler_stop->mpi_wtime proc~profiler_stop->proc~find_or_create_timer proc~record_edge mod_profiler::record_edge proc~profiler_stop->proc~record_edge proc~is_periodic_face->proc~patch_type_for_face

Called by

proc~~solve_pressure_correction~~CalledByGraph proc~solve_pressure_correction mod_flow_projection::solve_pressure_correction proc~advance_projection_step mod_flow_projection::advance_projection_step proc~advance_projection_step->proc~solve_pressure_correction program~lowmach_react_hex lowmach_react_hex program~lowmach_react_hex->proc~advance_projection_step

Source Code

   subroutine solve_pressure_correction(mesh, flow, bc, params, rhs, phi, stats)
      type(mesh_t), intent(in) :: mesh
      type(flow_mpi_t), intent(inout) :: flow
      type(bc_set_t), intent(in) :: bc
      type(case_params_t), intent(in) :: params
      real(rk), intent(in) :: rhs(:)
      real(rk), intent(inout) :: phi(:)
      type(solver_stats_t), intent(inout) :: stats

      real(rk) :: rr, rr_new
      real(rk) :: rz, rz_new
      real(rk) :: pap
      real(rk) :: alpha, beta
      integer :: iter
      integer :: c

      call ensure_projection_workspace(mesh)
      call ensure_pressure_operator_cache(mesh, flow, bc)

      associate( &
         r => projection_work%r, &
         z => projection_work%z, &
         pvec => projection_work%pvec, &
         ap => projection_work%ap, &
         local_ap => projection_work%local_ap)

         r = zero
         z = zero
         pvec = zero
         local_ap = zero

         ! The projection driver resets phi to zero before every pressure solve.
         ! Therefore A*phi is exactly zero for the initial PCG residual, so avoid
         ! one unnecessary halo exchange and pressure matvec per timestep.
         local_ap = zero

         do c = flow%first_cell, flow%last_cell
            r(c) = rhs(c)
         end do

         if (.not. pressure_cache%has_dirichlet_pressure) then
            r(1) = zero
            phi(1) = zero
         end if

         do c = flow%first_cell, flow%last_cell
            if (c == 1 .and. .not. pressure_cache%has_dirichlet_pressure) then
               z(c) = zero
            else
               z(c) = r(c) / max(pressure_cache%diag(c), tiny_safe)
            end if
         end do
          
         block
            real(rk) :: res(2)
            call flow_global_two_dots_owned(flow, r, r, r, z, res)
            rr = res(1); rz = res(2)
         end block

         pvec = z
         if (.not. pressure_cache%has_dirichlet_pressure) then
            pvec(1) = zero
         end if

         stats%pressure_iterations = 0
         stats%pressure_residual = sqrt(rr / max(real(mesh%ncells, rk), one))

         do iter = 1, params%pressure_max_iter
            if (stats%pressure_residual <= params%pressure_tol) exit

            ! a. matvec: q = A * p
            call flow_exchange_cell_scalar(flow, pvec)
            call pressure_matvec(mesh, flow, bc, pvec, local_ap)

            ! b. step length: alpha = (r, z) / (p, A*p)
            pap = flow_global_dot_owned(flow, pvec, local_ap)

            if (abs(pap) <= tiny_safe) exit

            alpha = rz / pap

            ! c. update solution and residual: phi = phi + alpha*p, r = r - alpha*q
            do c = flow%first_cell, flow%last_cell
               phi(c) = phi(c) + alpha * pvec(c)
               r(c) = r(c) - alpha * local_ap(c)
            end do

            if (.not. pressure_cache%has_dirichlet_pressure) then
               phi(1) = zero
               r(1) = zero
            end if

            do c = flow%first_cell, flow%last_cell
               if (c == 1 .and. .not. pressure_cache%has_dirichlet_pressure) then
                  z(c) = zero
               else
                  z(c) = r(c) / max(pressure_cache%diag(c), tiny_safe)
               end if
            end do

            block
               real(rk) :: res(2)
               call flow_global_two_dots_owned(flow, r, r, r, z, res)
               rr_new = res(1); rz_new = res(2)
            end block

            stats%pressure_residual = sqrt(rr_new / max(real(mesh%ncells, rk), one))
            stats%pressure_iterations = iter

            if (stats%pressure_residual <= params%pressure_tol) exit

            beta = rz_new / max(rz, tiny_safe)

            do c = flow%first_cell, flow%last_cell
               pvec(c) = z(c) + beta * pvec(c)
            end do
            if (.not. pressure_cache%has_dirichlet_pressure) then
               pvec(1) = zero
            end if

            rr = rr_new
            rz = rz_new
         end do

         if (.not. pressure_cache%has_dirichlet_pressure) then
            phi(1) = zero
         end if

         ! Accumulate pressure-solver iteration diagnostics.
         ! pressure_iterations remains the most recent solve count.
         stats%pressure_solve_count = stats%pressure_solve_count + 1
         stats%pressure_iterations_total = stats%pressure_iterations_total + stats%pressure_iterations
         stats%pressure_iterations_max = max(stats%pressure_iterations_max, stats%pressure_iterations)
         stats%pressure_iterations_avg = real(stats%pressure_iterations_total, rk) / &
                                         max(real(stats%pressure_solve_count, rk), one)

         call flow_exchange_cell_scalar(flow, phi)

      end associate
   end subroutine solve_pressure_correction