💻 Fortran Source Code Library
Run calculations locally on your own machine. View code structure, read technical explanations, and download compilation packages including sample input files.
Hardy-Cross Pipe Network Solver
Core Numerical Engine in Fortran 90 • 0 total downloads
! =========================================================================
! Source File: pipe_networks.f90
! =========================================================================
program pipe_networks
implicit none
integer, parameter :: max_pipes = 8
double precision :: rho, mu, nu, Q_total, dZ
double precision :: L(max_pipes), D(max_pipes), eps(max_pipes)
double precision :: Q(max_pipes), V(max_pipes), Re(max_pipes), f(max_pipes)
double precision :: dP(max_pipes), hf(max_pipes)
double precision :: rel_rough(max_pipes), Ac(max_pipes)
integer :: n_pipes, net_type ! 1=series, 2=parallel
double precision :: pi, g
! Hardy-Cross variables
double precision :: dQ, sum_num, sum_den
double precision :: f_old, Q_old(max_pipes)
integer :: i, j, iter, max_iter, converged
double precision :: tol
! Total results
double precision :: dP_total, hf_total, pump_power
pi = 3.14159265358979d0
g = 9.81d0
max_iter = 500
tol = 1.0d-8
! Read inputs
read(*,*) net_type ! 1=series, 2=parallel
read(*,*) n_pipes ! Number of pipes
read(*,*) rho ! Density [kg/m3]
read(*,*) mu ! Dynamic viscosity [Pa.s]
read(*,*) Q_total ! Total flow rate [m3/s]
read(*,*) dZ ! Elevation difference [m]
nu = mu / rho
do i = 1, n_pipes
read(*,*) L(i), D(i), eps(i)
! D and eps are in meters already (converted by PHP)
Ac(i) = pi * D(i)**2 / 4.0d0
rel_rough(i) = eps(i) / D(i)
end do
! ============================================
! HEADER
! ============================================
write(*,'(A)') '============================================================'
write(*,'(A)') ' PIPE NETWORK ANALYSIS'
write(*,'(A)') '============================================================'
write(*,*)
write(*,'(A)') '--- INPUT PARAMETERS ---'
if (net_type == 1) then
write(*,'(A)') ' Network Type = SERIES'
else
write(*,'(A)') ' Network Type = PARALLEL'
end if
write(*,'(A,I2)') ' Number of pipes = ', n_pipes
write(*,'(A,F12.2,A)') ' Fluid density ρ = ', rho, ' kg/m³'
write(*,'(A,ES12.4,A)') ' Dynamic viscosity μ = ', mu, ' Pa·s'
write(*,'(A,ES12.4,A)') ' Kinematic viscosity ν = ', nu, ' m²/s'
write(*,'(A,ES12.4,A)') ' Total flow rate Q = ', Q_total, ' m³/s'
write(*,'(A,F12.4,A)') ' Total flow rate = ', Q_total*1000.0d0*60.0d0, ' L/min'
write(*,'(A,F12.4,A)') ' Elevation difference = ', dZ, ' m'
write(*,*)
write(*,'(A)') '--- PIPE DATA ---'
write(*,'(A)') ' Pipe L [m] D [mm] ε [mm] ε/D A [m²]'
write(*,'(A)') ' ---- ------ ------ ------ ------ ------'
do i = 1, n_pipes
write(*,'(I5,2X,F8.2,2X,F8.2,2X,F8.4,3X,ES10.3,2X,ES10.4)') &
i, L(i), D(i)*1000, eps(i)*1000, rel_rough(i), Ac(i)
end do
write(*,*)
! ============================================
! SERIES NETWORK
! ============================================
if (net_type == 1) then
write(*,'(A)') '--- SERIES ANALYSIS ---'
write(*,'(A)') ' Same flow rate Q in all pipes.'
write(*,*)
dP_total = 0.0d0
hf_total = 0.0d0
do i = 1, n_pipes
Q(i) = Q_total
V(i) = Q(i) / Ac(i)
Re(i) = rho * V(i) * D(i) / mu
! Friction factor
call compute_friction(Re(i), rel_rough(i), f(i))
! Pressure drop (Darcy-Weisbach)
dP(i) = f(i) * (L(i) / D(i)) * rho * V(i)**2 / 2.0d0
hf(i) = dP(i) / (rho * g)
dP_total = dP_total + dP(i)
hf_total = hf_total + hf(i)
end do
! Add elevation head
dP_total = dP_total + rho * g * dZ
hf_total = hf_total + dZ
write(*,'(A)') '--- PER-PIPE RESULTS ---'
write(*,'(A)') ' Pipe Q [L/min] V [m/s] Re f ΔP [Pa] h_f [m]'
write(*,'(A)') ' ---- -------- ------ ---------- -------- --------- -------'
do i = 1, n_pipes
write(*,'(I5,2X,F10.2,2X,F8.4,3X,ES10.4,2X,ES10.4,2X,F10.2,3X,F8.4)') &
i, Q(i)*1000*60, V(i), Re(i), f(i), dP(i), hf(i)
end do
write(*,*)
pump_power = dP_total * Q_total
write(*,'(A)') '--- TOTAL SERIES RESULTS ---'
write(*,'(A,F12.2,A)') ' Total pressure drop = ', dP_total, ' Pa'
write(*,'(A,F12.4,A)') ' Total pressure drop = ', dP_total/1000, ' kPa'
write(*,'(A,F12.4,A)') ' Total head loss = ', hf_total, ' m'
write(*,'(A,F12.4,A)') ' Elevation component = ', rho*g*dZ, ' Pa'
write(*,'(A,F12.4,A)') ' Required pump power = ', pump_power, ' W'
write(*,*)
! ============================================
! PARALLEL NETWORK — HARDY-CROSS METHOD
! ============================================
else
write(*,'(A)') '--- PARALLEL ANALYSIS (HARDY-CROSS) ---'
write(*,'(A)') ' Same pressure drop across all branches.'
write(*,'(A)') ' Iterating to find flow distribution...'
write(*,*)
! Initial guess: distribute proportional to D^2.5
sum_num = 0.0d0
do i = 1, n_pipes
sum_num = sum_num + D(i)**2.5d0 / L(i)**0.5d0
end do
do i = 1, n_pipes
Q(i) = Q_total * (D(i)**2.5d0 / L(i)**0.5d0) / sum_num
end do
write(*,'(A)') '--- CONVERGENCE HISTORY ---'
write(*,'(A)') ' Iter ΔQ [m³/s] Max |Q change| Σ Q [m³/s]'
write(*,'(A)') ' ---- ---------- --------------- ----------'
converged = 0
do iter = 1, max_iter
! Save old values
do i = 1, n_pipes
Q_old(i) = Q(i)
end do
! Compute friction factors and head losses
do i = 1, n_pipes
V(i) = Q(i) / Ac(i)
Re(i) = rho * abs(V(i)) * D(i) / mu
if (Re(i) < 1.0d0) Re(i) = 1.0d0
call compute_friction(Re(i), rel_rough(i), f(i))
end do
! Hardy-Cross correction for parallel network
! For equal head loss: h_f1 = h_f2 = ... = h_fn
! Use head loss formulation: h_f = (8 f L Q|Q|) / (π² g D⁵)
! Correction: ΔQ = -Σ(R_i Q_i |Q_i|) / Σ(2 R_i |Q_i|)
! where R_i = 8 f_i L_i / (π² g D_i⁵)
! Actually for parallel, we equalize head losses
! Approach: adjust Q's so all have same head loss
! Using the Newton-like correction:
! Compute head losses
do i = 1, n_pipes
hf(i) = f(i) * L(i) * V(i) * abs(V(i)) / (2.0d0 * g * D(i))
end do
! Target: average head loss
sum_num = 0.0d0
do i = 1, n_pipes
sum_num = sum_num + hf(i)
end do
sum_num = sum_num / dble(n_pipes) ! average h_f
! Adjust each pipe's flow to equalize head loss
! h_f = f L V² / (2gD) = f L Q² / (2g D A²)
! dh_f/dQ = f L 2Q / (2g D A²) = 2 h_f / Q (approximately)
! ΔQ_i = -(h_f_i - h_f_avg) / (2 h_f_i / Q_i)
do i = 1, n_pipes
if (abs(hf(i)) > 1.0d-15 .and. abs(Q(i)) > 1.0d-15) then
dQ = -(hf(i) - sum_num) / (2.0d0 * hf(i) / Q(i))
Q(i) = Q(i) + dQ * 0.5d0 ! relaxation factor
end if
end do
! Enforce mass conservation: Σ Q_i = Q_total
sum_den = 0.0d0
do i = 1, n_pipes
sum_den = sum_den + Q(i)
end do
do i = 1, n_pipes
Q(i) = Q(i) * Q_total / sum_den
end do
! Check convergence
dQ = 0.0d0
do i = 1, n_pipes
if (abs(Q(i) - Q_old(i)) > dQ) dQ = abs(Q(i) - Q_old(i))
end do
if (mod(iter, 10) == 1 .or. iter <= 5 .or. dQ < tol) then
sum_den = 0.0d0
do i = 1, n_pipes
sum_den = sum_den + Q(i)
end do
write(*,'(I5,3X,ES14.6,3X,ES14.6,3X,ES12.4)') iter, dQ, dQ, sum_den
end if
if (dQ < tol) then
converged = 1
write(*,*)
write(*,'(A,I4,A)') ' ✓ Converged after ', iter, ' iterations'
exit
end if
end do
if (converged == 0) then
write(*,*)
write(*,'(A)') ' ⚠ WARNING: Did not converge within max iterations'
end if
write(*,*)
! Final results
do i = 1, n_pipes
V(i) = Q(i) / Ac(i)
Re(i) = rho * abs(V(i)) * D(i) / mu
call compute_friction(Re(i), rel_rough(i), f(i))
dP(i) = f(i) * (L(i) / D(i)) * rho * V(i)**2 / 2.0d0
hf(i) = dP(i) / (rho * g)
end do
write(*,'(A)') '--- PER-PIPE RESULTS ---'
write(*,'(A)') ' Pipe Q [L/min] V [m/s] Re f ΔP [Pa] h_f [m] Q/Q_t [%]'
write(*,'(A)') ' ---- -------- ------ ---------- -------- --------- ------- ---------'
do i = 1, n_pipes
write(*,'(I5,2X,F10.2,2X,F8.4,3X,ES10.4,2X,ES10.4,2X,F10.2,3X,F8.4,3X,F8.2)') &
i, Q(i)*1000*60, V(i), Re(i), f(i), dP(i), hf(i), (Q(i)/Q_total)*100
end do
write(*,*)
! Use first pipe's head loss as reference (all should be equal)
dP_total = dP(1)
hf_total = hf(1)
pump_power = dP_total * Q_total
write(*,'(A)') '--- TOTAL PARALLEL RESULTS ---'
write(*,'(A,F12.2,A)') ' Common pressure drop = ', dP_total, ' Pa'
write(*,'(A,F12.4,A)') ' Common pressure drop = ', dP_total/1000, ' kPa'
write(*,'(A,F12.4,A)') ' Common head loss = ', hf_total, ' m'
write(*,'(A,F12.4,A)') ' Required pump power = ', pump_power, ' W'
write(*,'(A,ES12.4,A)') ' Total flow rate check = ', Q_total, ' m³/s'
write(*,*)
end if
! ============================================
! CHART DATA
! ============================================
write(*,'(A)') '--- CHART_DATA ---'
do i = 1, n_pipes
write(*,'(I2,A,F12.6,A,F12.4,A,F12.2,A,F12.6)') &
i, ',', Q(i)*1000*60, ',', V(i), ',', dP(i), ',', hf(i)
end do
write(*,'(A)') '--- END_CHART_DATA ---'
write(*,*)
write(*,'(A)') '--- EQUATIONS USED ---'
write(*,'(A)') ' Darcy-Weisbach: ΔP = f(L/D)(ρV²/2)'
write(*,'(A)') ' Colebrook-White: 1/√f = -2log₁₀(ε/D/3.7 + 2.51/(Re√f))'
write(*,'(A)') ' Series: Q₁ = Q₂ = ... = Q_n; ΔP_total = Σ ΔP_i'
write(*,'(A)') ' Parallel: ΔP₁ = ΔP₂ = ... = ΔP_n; Q_total = Σ Q_i'
write(*,'(A)') ' Hardy-Cross: ΔQ = -Σ(RQ|Q|) / Σ(2R|Q|)'
contains
subroutine compute_friction(Re_val, eD, f_out)
double precision, intent(in) :: Re_val, eD
double precision, intent(out) :: f_out
double precision :: f_old_loc
integer :: k
if (Re_val < 2300.0d0) then
f_out = 64.0d0 / Re_val
else
! Swamee-Jain initial guess
f_out = 0.25d0 / (log10(eD/3.7d0 + 5.74d0/Re_val**0.9d0))**2
! Colebrook-White iteration
do k = 1, 100
f_old_loc = f_out
f_out = 1.0d0 / (-2.0d0 * log10(eD/3.7d0 + 2.51d0/(Re_val*sqrt(f_old_loc))))**2
if (abs(f_out - f_old_loc) / f_out < 1.0d-10) exit
end do
end if
end subroutine
end program pipe_networks
Solver Description
Solves steady pipe networks using the Hardy-Cross loop-balancing method. Iteratively solves for flow rates in loops by setting $\Delta Q = -\sum h_f / (2 \sum (h_f / Q))$ until mass conservation at nodes and energy conservation in loops are satisfied.
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:
Execution Command:
Execute the program by feeding the sample input file into the program using stdin redirection:
📥 Downloads & Local Files
Preview of the required input file (input.txt):
1000.0
! Fluid Viscosity ($\mu$) [Pa-s]
0.001
! Number of loops (N_loops)
2
! Boundary Flow Rate [L/min]
10.0
! Elevation Difference $\Delta Z$ [m]
0.0
! Pipe Index 1
1
! Pipe 1 Length [m]
100.0
! Pipe 1 Diameter [mm]
100.0
! Pipe 1 Roughness [mm]
0.045
! Pipe Index 2
2
! Pipe 2 Length [m]
80.0
! Pipe 2 Diameter [mm]
80.0
! Pipe 2 Roughness [mm]
0.15