|
| 1 | +% ------------------------------------------------------------------------- |
| 2 | +% Terzaghi One-Dimensional Consolidation Example |
| 3 | +% |
| 4 | +% Consolidation is the process of transient fluid flow through a porous |
| 5 | +% medium that deforms over time. |
| 6 | +% |
| 7 | +% A constant compressive face load of P0 = 10 MPa is applied at the |
| 8 | +% left boundary (x = 0 m) of a saturated porous soil matrix. |
| 9 | +% |
| 10 | +% Zero displacement is assumed at the right boundary (x = L = 25 m), |
| 11 | +% representing a fixed wall or support. |
| 12 | +% |
| 13 | +% The matrix is assumed to be fully saturated, and fluid drainage is |
| 14 | +% permitted only at the loaded boundary (x = 0 m). |
| 15 | +% |
| 16 | +% The MOLE Laplacian operator is used to compute the excess pore pressure |
| 17 | +% p(x, t), satisfies a one-dimensional diffusion equation for pressure. |
| 18 | +% |
| 19 | +% The domain is defined on the interval x ∈ [0, L] meters. |
| 20 | +% |
| 21 | +% The simulation compares the MOLE-based numerical solution to an |
| 22 | +% analytical benchmark solution derived using Fourier series. |
| 23 | +% ------------------------------------------------------------------------- |
| 24 | + |
| 25 | +%% |
| 26 | +clc; |
| 27 | +close all; |
| 28 | + |
| 29 | +addpath('../../src/matlab'); % MOLE operator path |
| 30 | + |
| 31 | +%% Parameters |
| 32 | +P0 = 10e6; % Face load in Pascals |
| 33 | +cf = 1e-4; % Diffusion constant |
| 34 | +l = 25; % Domain length in meters |
| 35 | +k = 2; % MOLE operator order |
| 36 | +m = 50; % Number of cells |
| 37 | + |
| 38 | +% Spatial discretization |
| 39 | +a = 0; % Left boundary of the domain [m] |
| 40 | +b = l; % Right boundary of the domain [m] |
| 41 | +dx = (b - a)/m; % Cell width (uniform grid spacing) [m] |
| 42 | +xgrid = [a a+dx/2 : dx : b-dx/2 b]; % Staggered grid with ghost nodes at boundaries |
| 43 | +% xgrid has m+2 points: |
| 44 | +% - ghost cell at a (Dirichlet BC) |
| 45 | +% - m internal nodes |
| 46 | +% - ghost cell at b (Neumann BC) |
| 47 | + |
| 48 | +% Times to evaluate (in hours) |
| 49 | +times_hr = [1, 10, 40, 70]; % Time snapshots in hours for comparison |
| 50 | +times_sec = times_hr * 3600; % Convert time points to seconds for simulation |
| 51 | + |
| 52 | +% Fluid properties |
| 53 | +K = 1e-12; % Permeability [m^2] |
| 54 | +mu = 1e-3; % Dynamic viscosity [Pa·s] |
| 55 | +rho = 1000; % Fluid density [kg/m^3] |
| 56 | +g = 9.81; % Gravity [m/s^2] |
| 57 | + |
| 58 | +%% Numerical (MOLE) Solution |
| 59 | +L = lap(k, m, dx); % Mimetic Laplacian operator for diffusion |
| 60 | +G = grad(k, m, dx); % Mimetic gradient operator for Darcy flux |
| 61 | + |
| 62 | +% Boundary modifications to Laplacian |
| 63 | +L(1,:) = 0; L(end,:) = 0; |
| 64 | + |
| 65 | +p_numerical = zeros(length(xgrid), length(times_sec)); % Pressure field [Pa] |
| 66 | +q_numerical = zeros(m+1, length(times_sec)); % Darcy flux [m/s] (size = m+1) |
| 67 | + |
| 68 | +% Loop over each specified final time |
| 69 | +for i = 1:length(times_sec) |
| 70 | + t_final = times_sec(i); |
| 71 | + dt = 0.03; |
| 72 | + nsteps = round(t_final / dt); |
| 73 | + |
| 74 | + % Uniform Initial Condition : p(x,0) = P0 |
| 75 | + p = P0 * ones(size(xgrid))'; |
| 76 | + |
| 77 | + % Time integration using Forward Euler |
| 78 | + for step = 1:nsteps |
| 79 | + p = p + dt * cf * (L * p); |
| 80 | + p(1) = 0; % Dirichlet BC at x = 0 |
| 81 | + p(end) = p(end-1); % Neumann BC at x = L |
| 82 | + end |
| 83 | + |
| 84 | + p_numerical(:,i) = p; |
| 85 | + |
| 86 | + % Compute Darcy flux (numerical) |
| 87 | + dpdx = G * p; |
| 88 | + q = - (K / mu) * (dpdx - rho * g); |
| 89 | + q_numerical(:,i) = q(1:m+1); |
| 90 | + |
| 91 | + % Print numerical results |
| 92 | + fprintf('\nNumerical results at t = %.2f hr:\n', times_hr(i)); |
| 93 | + fprintf('| x (m) | Numerical p [Pa] | Darcy Flux [m/s] |\n'); |
| 94 | + fprintf('|---------------|------------------|------------------|\n'); |
| 95 | + for j = 1:m+1 |
| 96 | + fprintf('| %13.6f | %16.6e | %16.6e |\n', xgrid(j), p_numerical(j,i), q_numerical(j,i)); |
| 97 | + end |
| 98 | +end |
| 99 | + |
| 100 | +%% Analytical Solution |
| 101 | +N_max = 100; % Number of Fourier series terms |
| 102 | +p_analytical = zeros(length(xgrid), length(times_sec)); % Pressure field [Pa] |
| 103 | +q_analytical = zeros(m+1, length(times_sec)); % Darcy flux [m/s] (size = m+1) |
| 104 | + |
| 105 | +% Loop over all time snapshots |
| 106 | +for i = 1:length(times_sec) |
| 107 | + t = times_sec(i); |
| 108 | + p = zeros(size(xgrid)); |
| 109 | + |
| 110 | + % Compute analytical solution using truncated Fourier sine series |
| 111 | + for N = 0:N_max |
| 112 | + n = 2*N + 1; % Odd terms only (satisfies boundary conditions) |
| 113 | + term = (1/n) * sin(n*pi*xgrid/(2*l)) .* exp(-(n^2)*(pi^2)*cf*t/(4*l^2)); |
| 114 | + p = p + term; |
| 115 | + end |
| 116 | + p = (4/pi) * P0 * p; |
| 117 | + p_analytical(:,i) = p; |
| 118 | + |
| 119 | + % Compute Darcy flux (analytical) |
| 120 | + dpdx = gradient(p, dx); % basic gradient |
| 121 | + q = - (K / mu) * (dpdx - rho * g); |
| 122 | + q_analytical(:,i) = q(1:m+1); |
| 123 | + |
| 124 | + % Print analytical results |
| 125 | + fprintf('\nAnalytical results at t = %.2f hr:\n', times_hr(i)); |
| 126 | + fprintf('| x (m) | Analytical p [Pa] | Darcy Flux [m/s] |\n'); |
| 127 | + fprintf('|---------------|-------------------|------------------|\n'); |
| 128 | + for j = 1:m+1 |
| 129 | + fprintf('| %13.6f | %18.6e | %16.6e |\n', xgrid(j), p_analytical(j,i), q_analytical(j,i)); |
| 130 | + end |
| 131 | +end |
| 132 | + |
| 133 | +%% Combined Plot |
| 134 | +figure('Name','Terzaghi one-dimensional consolidation'); |
| 135 | +set(gcf,'Color','white'); |
| 136 | + |
| 137 | +% Top subplot: MOLE numerical |
| 138 | +subplot(2,1,1); |
| 139 | +hold on; |
| 140 | +for i = 1:length(times_sec) |
| 141 | + semilogy(xgrid, p_numerical(:,i)/1e6, '-o', 'DisplayName', [num2str(times_hr(i)) ' hr']); |
| 142 | +end |
| 143 | +title('MOLE Numerical Solution'); |
| 144 | +ylabel('Excess Pore Pressure p(x,t) [MPa]'); |
| 145 | +axis([0 l 1e-3 10]); |
| 146 | +legend('Location', 'southeast'); |
| 147 | +grid on; |
| 148 | + |
| 149 | +% Bottom subplot: Analytical |
| 150 | +subplot(2,1,2); |
| 151 | +hold on; |
| 152 | +for i = 1:length(times_sec) |
| 153 | + semilogy(xgrid, p_analytical(:,i)/1e6, '--s', 'DisplayName', [num2str(times_hr(i)) ' hr']); |
| 154 | +end |
| 155 | +title('Analytical Benchmark Solution'); |
| 156 | +xlabel('x (m)'); |
| 157 | +ylabel('Excess Pore Pressure p(x,t) [MPa]'); |
| 158 | +axis([0 l 1e-3 10]); |
| 159 | +legend('Location', 'southeast'); |
| 160 | +grid on; |
| 161 | + |
| 162 | +%% Print relative L2 errors |
| 163 | +fprintf('\nRelative L2 Errors (Numerical vs Analytical):\n'); |
| 164 | +fprintf('| Time [hr] | Pressure Error | Darcy Flux Error |\n'); |
| 165 | +fprintf('|------------------|--------------------|---------------------|\n'); |
| 166 | +for i = 1:length(times_hr) |
| 167 | + % L2 error for pressure |
| 168 | + rel_err_p = norm(p_numerical(:,i) - p_analytical(:,i)) / norm(p_analytical(:,i)); |
| 169 | + |
| 170 | + % L2 error for flux |
| 171 | + rel_err_q = norm(q_numerical(:,i) - q_analytical(:,i)) / norm(q_analytical(:,i)); |
| 172 | + |
| 173 | + % Print in table format |
| 174 | + fprintf('| %16.2f | %18.6e | %19.6e |\n', times_hr(i), rel_err_p, rel_err_q); |
| 175 | +end |
0 commit comments