πŸ’» Fortran Source Code Library

We currently offer 172 open-source, production-grade Fortran codes for offline testing. Run calculations locally on your own machine, view code structure, read technical explanations, and download compilation packages including sample input files.

Grid Peclet Number Solver

Core Numerical Engine in Fortran 90 β€’ 32 total downloads

peclet_number.f90
! =========================================================================
! Source File: peclet_number.f90
! =========================================================================

!==============================================================================
! ThermoFluidCalc β€” Calculator #22 : Numerical PΓ©clet Number
!==============================================================================
! Physics : The cell PΓ©clet number governs the ratio of convective to
!           diffusive transport within a single FVM cell:
!
!             Pe = ρ·uΒ·L / Ξ“   =   uΒ·L / Ξ±
!
!           where
!             u = characteristic velocity              (m/s)
!             L = cell length / face-to-face distance  (m)
!             Ξ± = thermal (or mass) diffusivity        (mΒ²/s)
!             Ξ“ = diffusion coefficient                (kg/(mΒ·s) or W/(mΒ·K))
!             ρ = density                              (kg/m³)
!
!           When |Pe| > 2 the Central Differencing Scheme (CDS) becomes
!           unbounded and alternative schemes (UDS, Hybrid, Power-Law,
!           QUICK) must be used.
!
! Reference : Patankar, "Numerical Heat Transfer and Fluid Flow", Ch. 5
!             Versteeg & Malalasekera, "An Introduction to CFD", Ch. 5
!
! Build:
!   gfortran -O2 -o peclet_number peclet_number.f90
!
! Modes:
!   1 = Single cell evaluation
!   2 = 1-D mesh sweep  (Pe vs cell size)
!   3 = Scheme comparison profiles
!
! Input (stdin):
!   Mode 1:  1  u  L  alpha  rho  gamma
!            (if alpha > 0 use it, else compute alpha = gamma/rho)
!   Mode 2:  2  u  alpha  rho  gamma  L_start  L_end  ncells
!   Mode 3:  3  Pe_value  npts
!==============================================================================
program peclet_number
  implicit none

  integer, parameter :: dp = selected_real_kind(15, 307)
  integer, parameter :: MAX_CELLS = 5000

  integer  :: mode, ncells, npts, i
  real(dp) :: u, L, alpha, rho, gamma_coeff
  real(dp) :: Pe, Pe_val
  real(dp) :: L_start, L_end, dL, Lc
  real(dp) :: x, dx, phi_exact, phi_cds, phi_uds
  real(dp) :: phi_hybrid, phi_power, phi_quick
  real(dp) :: Pe_abs, w_power
  character(len=60) :: diagnosis, recommendation

  ! ── Read mode ─────────────────────────────────────────────────────────────
  read(*,*) mode

  select case (mode)

  ! ========================================================================
  ! MODE 1 : Single cell
  ! ========================================================================
  case (1)
    backspace(5)
    read(*,*) mode, u, L, alpha, rho, gamma_coeff

    ! Compute alpha if not provided
    if (alpha <= 0.0_dp) then
      if (rho > 0.0_dp .and. gamma_coeff > 0.0_dp) then
        alpha = gamma_coeff / rho
      else
        write(*,'(A)') 'ERROR=Must provide either alpha>0 or both rho>0 and gamma>0.'
        stop
      end if
    end if

    if (L <= 0.0_dp) then
      write(*,'(A)') 'ERROR=Cell size L must be positive.'
      stop
    end if

    Pe = u * L / alpha

    ! Diagnosis
    Pe_abs = abs(Pe)
    if (Pe_abs < 0.1_dp) then
      diagnosis      = 'Pure diffusion'
      recommendation = 'CDS is ideal'
    else if (Pe_abs < 2.0_dp) then
      diagnosis      = 'Diffusion-dominated'
      recommendation = 'CDS is safe and 2nd-order accurate'
    else if (Pe_abs < 2.5_dp) then
      diagnosis      = 'Transition zone'
      recommendation = 'Consider Hybrid or QUICK scheme'
    else if (Pe_abs < 10.0_dp) then
      diagnosis      = 'Convection-dominated'
      recommendation = 'Use UDS, Hybrid, Power-Law, or QUICK'
    else
      diagnosis      = 'Strongly convective'
      recommendation = 'UDS or Power-Law; refine mesh to lower Pe'
    end if

    write(*,'(A,I1)')       'MODE=', mode
    write(*,'(A)')          'MODE_NAME=Single Cell'
    write(*,'(A,ES15.8)')   'VELOCITY=', u
    write(*,'(A,ES15.8)')   'CELL_SIZE=', L
    write(*,'(A,ES15.8)')   'ALPHA=', alpha
    write(*,'(A,ES15.8)')   'RHO=', rho
    write(*,'(A,ES15.8)')   'GAMMA=', gamma_coeff
    write(*,'(A,F15.6)')    'PE=', Pe
    write(*,'(A,F15.6)')    'PE_ABS=', Pe_abs
    write(*,'(A,A)')        'DIAGNOSIS=', trim(diagnosis)
    write(*,'(A,A)')        'RECOMMENDATION=', trim(recommendation)

  ! ========================================================================
  ! MODE 2 : 1-D mesh sweep
  ! ========================================================================
  case (2)
    backspace(5)
    read(*,*) mode, u, alpha, rho, gamma_coeff, L_start, L_end, ncells

    if (alpha <= 0.0_dp) then
      if (rho > 0.0_dp .and. gamma_coeff > 0.0_dp) then
        alpha = gamma_coeff / rho
      else
        write(*,'(A)') 'ERROR=Must provide either alpha>0 or both rho>0 and gamma>0.'
        stop
      end if
    end if

    if (ncells < 2) ncells = 2
    if (ncells > MAX_CELLS) ncells = MAX_CELLS
    if (L_start <= 0.0_dp .or. L_end <= 0.0_dp) then
      write(*,'(A)') 'ERROR=Cell sizes must be positive.'
      stop
    end if

    write(*,'(A,I1)')       'MODE=', mode
    write(*,'(A)')          'MODE_NAME=1-D Mesh Sweep'
    write(*,'(A,ES15.8)')   'VELOCITY=', u
    write(*,'(A,ES15.8)')   'ALPHA=', alpha
    write(*,'(A,ES15.8)')   'L_START=', L_start
    write(*,'(A,ES15.8)')   'L_END=', L_end
    write(*,'(A,I5)')       'NCELLS=', ncells

    dL = (L_end - L_start) / real(ncells - 1, dp)

    write(*,'(A)') 'DATA_START'
    do i = 0, ncells - 1
      Lc = L_start + real(i, dp) * dL
      Pe = u * Lc / alpha
      write(*,'(I5,A,ES15.8,A,F15.6)') i+1, ',', Lc, ',', Pe
    end do
    write(*,'(A)') 'DATA_END'

    ! Count flagged cells
    write(*,'(A,I5)') 'FLAGGED=', count_flagged(u, alpha, L_start, dL, ncells)

  ! ========================================================================
  ! MODE 3 : Scheme comparison profiles
  ! ========================================================================
  case (3)
    backspace(5)
    read(*,*) mode, Pe_val, npts

    if (npts < 10) npts = 10
    if (npts > MAX_CELLS) npts = MAX_CELLS

    write(*,'(A,I1)')      'MODE=', mode
    write(*,'(A)')         'MODE_NAME=Scheme Comparison'
    write(*,'(A,F15.6)')   'PE=', Pe_val
    write(*,'(A,I5)')      'NPTS=', npts

    Pe_abs = abs(Pe_val)
    dx = 1.0_dp / real(npts, dp)

    write(*,'(A)') 'DATA_START'
    do i = 0, npts
      x = real(i, dp) * dx

      ! Exact solution: phi = (exp(Pe*x) - 1) / (exp(Pe) - 1)
      if (abs(Pe_val) < 1.0e-12_dp) then
        phi_exact = x    ! linear when Pe→0
      else
        phi_exact = (exp(Pe_val * x) - 1.0_dp) / (exp(Pe_val) - 1.0_dp)
      end if

      ! CDS approximation (2nd order, central):
      ! For a uniform 1D grid the CDS discretisation gives the exact
      ! solution of the *discretised* equations.  The well-known
      ! wiggles appear when those discrete values overshoot/undershoot.
      ! We model the CDS discrete profile on npts cells:
      !   a_P phi_P = a_W phi_W + a_E phi_E
      !   with F = rho*u*A, D = Gamma*A/dx, Pe_cell = F/D
      ! For illustration we solve the tri-diagonal system directly.
      ! (computed below after the loop in a dedicated pass)

      ! For the per-point output we store exact only; CDS etc. come from
      ! the tri-diagonal solves.  Placeholder -999 will be replaced.
      write(*,'(F12.8,A,ES15.8,A,A,A,A,A,A,A,A)') x, ',', phi_exact, &
        ',', '0', ',', '0', ',', '0', ',', '0'
    end do
    write(*,'(A)') 'DATA_END'

    ! Now output the full discrete profiles via TDMA
    call output_discrete_profiles(Pe_val, npts)

  case default
    write(*,'(A)') 'ERROR=Invalid mode (must be 1-3).'
    stop
  end select

contains

  !------------------------------------------------------------------------
  integer function count_flagged(u, alpha, L0, dL, n)
    real(dp), intent(in) :: u, alpha, L0, dL
    integer, intent(in)  :: n
    integer :: j
    real(dp) :: Lj, Pej
    count_flagged = 0
    do j = 0, n-1
      Lj  = L0 + real(j, dp) * dL
      Pej = abs(u * Lj / alpha)
      if (Pej > 2.0_dp) count_flagged = count_flagged + 1
    end do
  end function

  !------------------------------------------------------------------------
  ! Solve 1-D convection-diffusion with different schemes via TDMA
  ! Domain [0,1], phi(0)=0, phi(1)=1, uniform grid, npts cells
  !------------------------------------------------------------------------
  subroutine output_discrete_profiles(Pe_global, n)
    real(dp), intent(in) :: Pe_global
    integer, intent(in)  :: n
    real(dp) :: dx_l, F, D, Pe_cell
    real(dp) :: aW, aE, aP, Su, Sp
    real(dp), allocatable :: phi_cds_a(:), phi_uds_a(:)
    real(dp), allocatable :: phi_hyb_a(:), phi_pow_a(:), phi_quick_a(:)
    real(dp), allocatable :: a(:), b(:), c(:), d_rhs(:), sol(:)
    integer :: j, scheme
    real(dp) :: aWq, aEq, aWW

    allocate(phi_cds_a(0:n), phi_uds_a(0:n))
    allocate(phi_hyb_a(0:n), phi_pow_a(0:n), phi_quick_a(0:n))
    allocate(a(n), b(n), c(n), d_rhs(n), sol(n))

    dx_l = 1.0_dp / real(n, dp)
    ! F = rho*u (per unit area), D = Gamma/dx
    ! Pe_cell = F*dx/Gamma = Pe_global * dx_l  (since Pe_global = u*L_total/alpha)
    ! But here L_total = 1, so Pe_cell = Pe_global / n * n ... = Pe_global * dx_l
    ! Actually Pe_global = u * 1 / alpha, Pe_cell = u * dx_l / alpha = Pe_global * dx_l
    F = Pe_global   ! normalised: F/D_ref
    D = 1.0_dp / dx_l
    Pe_cell = F / D  ! = Pe_global * dx_l

    ! ── Loop over 5 schemes ──────────────────────────────────────────────
    do scheme = 1, 5

      ! Build TDMA coefficients for interior cells j=1..n
      do j = 1, n
        select case (scheme)
        case (1) ! CDS
          aW = D + F / 2.0_dp
          aE = D - F / 2.0_dp
        case (2) ! UDS
          aW = D + max(F, 0.0_dp)
          aE = D + max(-F, 0.0_dp)
        case (3) ! Hybrid
          aW = max(F, D + F/2.0_dp, 0.0_dp)
          aE = max(-F, D - F/2.0_dp, 0.0_dp)
        case (4) ! Power-Law
          w_power = max(0.0_dp, (1.0_dp - 0.1_dp*abs(Pe_cell))**5)
          aW = D * w_power + max(F, 0.0_dp)
          aE = D * w_power + max(-F, 0.0_dp)
        case (5) ! QUICK (deferred correction simplified as UDS base)
          aW = D + max(F, 0.0_dp)
          aE = D + max(-F, 0.0_dp)
        end select

        Su = 0.0_dp
        Sp = 0.0_dp

        ! Boundary conditions
        if (j == 1) then
          ! West boundary: phi = 0
          aP = aW + aE + (aW) - Sp  ! extra aW from boundary
          Su = Su + (2.0_dp * D + F) * 0.0_dp  ! phi_boundary = 0
          aW = 0.0_dp
          aP = aE + (2.0_dp * D + F)
        else if (j == n) then
          ! East boundary: phi = 1
          aP = aW + aE + aE - Sp
          Su = Su + (2.0_dp * D - F) * 1.0_dp
          aE = 0.0_dp
          aP = aW + (2.0_dp * D - F)
        else
          aP = aW + aE - Sp
        end if

        ! TDMA arrays (a=sub, b=diag, c=super, d=rhs)
        a(j) = -aW
        b(j) = aP
        c(j) = -aE
        d_rhs(j) = Su
      end do

      ! ── TDMA solve ────────────────────────────────────────────────────
      ! Forward sweep
      do j = 2, n
        if (abs(b(j-1)) < 1.0e-30_dp) then
          b(j-1) = 1.0e-30_dp
        end if
        c(j-1) = c(j-1) / b(j-1)
        d_rhs(j-1) = d_rhs(j-1) / b(j-1)
        b(j) = b(j) - a(j) * c(j-1)
        d_rhs(j) = d_rhs(j) - a(j) * d_rhs(j-1)
      end do
      if (abs(b(n)) < 1.0e-30_dp) b(n) = 1.0e-30_dp
      sol(n) = d_rhs(n) / b(n)
      do j = n-1, 1, -1
        sol(j) = d_rhs(j) / b(j) - c(j) * sol(j+1)
      end do

      ! Store β€” cell centers at (j-0.5)*dx, plus boundaries
      select case (scheme)
      case (1)
        phi_cds_a(0) = 0.0_dp; phi_cds_a(n) = 1.0_dp
        do j = 1, n; phi_cds_a(j) = sol(j); end do  ! approximate: store at j index
      case (2)
        phi_uds_a(0) = 0.0_dp; phi_uds_a(n) = 1.0_dp
        do j = 1, n; phi_uds_a(j) = sol(j); end do
      case (3)
        phi_hyb_a(0) = 0.0_dp; phi_hyb_a(n) = 1.0_dp
        do j = 1, n; phi_hyb_a(j) = sol(j); end do
      case (4)
        phi_pow_a(0) = 0.0_dp; phi_pow_a(n) = 1.0_dp
        do j = 1, n; phi_pow_a(j) = sol(j); end do
      case (5)
        phi_quick_a(0) = 0.0_dp; phi_quick_a(n) = 1.0_dp
        do j = 1, n; phi_quick_a(j) = sol(j); end do
      end select

    end do  ! scheme loop

    ! ── Output discrete profiles ────────────────────────────────────────
    write(*,'(A)') 'PROFILES_START'
    do j = 0, n
      x = real(j, dp) * dx_l
      ! Exact
      if (abs(Pe_global) < 1.0e-12_dp) then
        phi_exact = x
      else
        phi_exact = (exp(Pe_global * x) - 1.0_dp) / (exp(Pe_global) - 1.0_dp)
      end if
      write(*,'(F12.8,5(A,ES15.8))') x, &
        ',', phi_exact, &
        ',', phi_cds_a(j), &
        ',', phi_uds_a(j), &
        ',', phi_hyb_a(j), &
        ',', phi_pow_a(j)
    end do
    write(*,'(A)') 'PROFILES_END'

    deallocate(phi_cds_a, phi_uds_a, phi_hyb_a, phi_pow_a, phi_quick_a)
    deallocate(a, b, c, d_rhs, sol)

  end subroutine output_discrete_profiles

end program peclet_number


Solver Description

Calculate local grid Peclet numbers to evaluate cell convection-diffusion numerical scheme limits.

Key Numerical Methods & Architecture

  • Input Redirection: Reads parameters sequentially from standard input (`stdin`) using Fortran sequential read (`read(*,*)`), ensuring modular integration.
  • Modular Design: Formulated using pure mathematical routines, separation of equations from output formatting, and precise numerical solvers (e.g. bisection, Newton-Raphson).
  • Standard Compliant: Written in clean, standards-compliant Fortran 90 to ensure cross-compiler compatibility.

πŸ› οΈ Local Compilation

To test this code on your machine, compile the source code file(s) using a standard Fortran compiler (e.g., `gfortran`).

Compilation Command:

gfortran -O3 peclet_number.f90 -o peclet_number

Execution Command:

Execute the program by feeding the sample input file into the program using stdin redirection:

peclet_number < input.txt

πŸ“₯ Downloads & Local Files

Preview of the required input file (input.txt):

! Velocity u (m/s)\nCell size L (m)\ndiffusivity (m/s) Ò€” or leave 0\ndensity (kg/m)\ndiffusion coeff (if =0)
2.0
! Parameter 2
0.01
! Parameter 3
1.5e-5
! Parameter 4
1.225
! Parameter 5
0.0