|
54 | 54 | mu = 1e-3; % Dynamic viscosity [Pa·s] |
55 | 55 | rho = 1000; % Fluid density [kg/m^3] |
56 | 56 | g = 9.81; % Gravity [m/s^2] |
| 57 | +Ks = 1e8; % Bulk modulus [Pa] |
| 58 | +alpha = 1; % Biot coefficient |
| 59 | +Ss = 1e-5; % [1/Pa] Specific storage coefficient |
57 | 60 |
|
58 | 61 | %% Numerical (MOLE) Solution |
59 | 62 | L = lap(k, m, dx); % Mimetic Laplacian operator for diffusion |
|
62 | 65 | % Boundary modifications to Laplacian |
63 | 66 | L(1,:) = 0; L(end,:) = 0; |
64 | 67 |
|
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) |
| 68 | +p_numerical = zeros(length(xgrid), length(times_sec)); % Pressure field [Pa] |
| 69 | +q_numerical = zeros(m+1, length(times_sec)); % Darcy flux [m/s] (size = m+1) |
| 70 | +e_numerical = zeros(length(xgrid), length(times_sec)); % Strain field |
| 71 | +u_numerical = zeros(length(xgrid), length(times_sec)); % Displacement field |
67 | 72 |
|
68 | 73 | % Loop over each specified final time |
69 | 74 | for i = 1:length(times_sec) |
70 | 75 | t_final = times_sec(i); |
71 | | - dt = 0.03; |
| 76 | + dt = 1.0; |
72 | 77 | nsteps = round(t_final / dt); |
73 | 78 |
|
74 | 79 | % Uniform Initial Condition : p(x,0) = P0 |
75 | | - p = P0 * ones(size(xgrid))'; |
| 80 | + p = P0 * ones(size(xgrid))'; |
76 | 81 |
|
77 | 82 | % Time integration using Forward Euler |
78 | 83 | 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 |
| 84 | + p = p + dt * cf * (L * p); % Pressure diffusion |
| 85 | + p(1) = 0; % Dirichlet BC at x = 0 |
| 86 | + p(end) = p(end-1); % Neumann BC at x = L |
82 | 87 | end |
83 | 88 |
|
84 | | - p_numerical(:,i) = p; |
| 89 | + % Compute strain and displacement from final pressure |
| 90 | + e = (alpha * p - P0) / Ks; |
| 91 | + u = cumtrapz(xgrid, e); %cumulative integral using the trapezoidal rule. |
85 | 92 |
|
86 | | - % Compute Darcy flux (numerical) |
| 93 | + % Store results |
| 94 | + e_numerical(:,i) = e; |
| 95 | + u_numerical(:,i) = u; |
| 96 | + p_numerical(:,i) = p; |
| 97 | + |
| 98 | + % Compute darcy flux |
87 | 99 | dpdx = G * p; |
88 | 100 | q = - (K / mu) * (dpdx - rho * g); |
89 | | - q_numerical(:,i) = q(1:m+1); |
| 101 | + q_numerical(:,i) = q(1:m+1); |
| 102 | + |
| 103 | +end |
| 104 | + |
| 105 | +%% Mass Conservation Residual Evaluation |
| 106 | + |
| 107 | +% Compute dp/dt and de/dt using backward differences |
| 108 | +dpdt = zeros(size(p_numerical)); |
| 109 | +dedt = zeros(size(e_numerical)); |
| 110 | + |
| 111 | +for i = 2:length(times_sec) |
| 112 | + dt_local = times_sec(i) - times_sec(i-1); |
| 113 | + dpdt(:,i) = (p_numerical(:,i) - p_numerical(:,i-1)) / dt_local; |
| 114 | + dedt(:,i) = (e_numerical(:,i) - e_numerical(:,i-1)) / dt_local; |
| 115 | +end |
90 | 116 |
|
91 | | - % Print numerical results |
| 117 | +% Compute divergence of q using MOLE div() |
| 118 | +divq = zeros(size(p_numerical)); % same shape as pressure field |
| 119 | +for i = 1:length(times_sec) |
| 120 | + qx = q_numerical(:,i); % q has m+1 values |
| 121 | + divq(:,i) = div(k, m, dx) * qx; % returns m+2 values (staggered grid) |
| 122 | +end |
| 123 | + |
| 124 | +% Compute full mass balance residual |
| 125 | +residual = Ss * dpdt + alpha * dedt - divq; |
| 126 | + |
| 127 | +%% Combined Numerical Output |
| 128 | +for i = 1:length(times_sec) |
92 | 129 | 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'); |
| 130 | + fprintf('| x (m) | Numerical p [Pa] | Darcy Flux [m/s] | Residual [1/s] |\n'); |
| 131 | + fprintf('|---------------|------------------|------------------|------------------|\n'); |
95 | 132 | for j = 1:m+1 |
96 | | - fprintf('| %13.6f | %16.6e | %16.6e |\n', xgrid(j), p_numerical(j,i), q_numerical(j,i)); |
| 133 | + if i == 1 |
| 134 | + fprintf('| %13.6f | %16.6e | %16.6e | %16s |\n', ... |
| 135 | + xgrid(j), p_numerical(j,i), q_numerical(j,i), 'N/A'); |
| 136 | + else |
| 137 | + fprintf('| %13.6f | %16.6e | %16.6e | %16.6e |\n', ... |
| 138 | + xgrid(j), p_numerical(j,i), q_numerical(j,i), residual(j,i)); |
| 139 | + end |
97 | 140 | end |
98 | 141 | end |
99 | 142 |
|
| 143 | + |
100 | 144 | %% Analytical Solution |
101 | 145 | N_max = 100; % Number of Fourier series terms |
102 | 146 | 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) |
| 147 | +q_analytical = zeros(m+1, length(times_sec)); % Darcy flux [m/s] (size = m+1) |
| 148 | +e_analytical = zeros(length(xgrid), length(times_sec)); % Strain field |
| 149 | +u_analytical = zeros(length(xgrid), length(times_sec)); % Displacement field |
| 150 | +residual_analytical = zeros(length(xgrid), length(times_sec)); |
104 | 151 |
|
105 | 152 | % Loop over all time snapshots |
106 | 153 | for i = 1:length(times_sec) |
107 | 154 | t = times_sec(i); |
108 | 155 | p = zeros(size(xgrid)); |
109 | 156 |
|
110 | | - % Compute analytical solution using truncated Fourier sine series |
| 157 | + % Compute analytical pressure using Fourier series |
111 | 158 | for N = 0:N_max |
112 | | - n = 2*N + 1; % Odd terms only (satisfies boundary conditions) |
| 159 | + n = 2*N + 1; |
113 | 160 | term = (1/n) * sin(n*pi*xgrid/(2*l)) .* exp(-(n^2)*(pi^2)*cf*t/(4*l^2)); |
114 | 161 | p = p + term; |
115 | 162 | end |
116 | 163 | p = (4/pi) * P0 * p; |
117 | 164 | p_analytical(:,i) = p; |
118 | 165 |
|
119 | | - % Compute Darcy flux (analytical) |
120 | | - dpdx = gradient(p, dx); % basic gradient |
| 166 | + % Compute Darcy flux |
| 167 | + dpdx = gradient(p, dx); |
121 | 168 | q = - (K / mu) * (dpdx - rho * g); |
122 | 169 | q_analytical(:,i) = q(1:m+1); |
123 | 170 |
|
124 | | - % Print analytical results |
| 171 | + % Compute analytical strain and displacement |
| 172 | + e = (alpha * p - P0) / Ks; |
| 173 | + u = cumtrapz(xgrid, e); |
| 174 | + e_analytical(:,i) = e; |
| 175 | + u_analytical(:,i) = u; |
| 176 | + |
| 177 | + |
| 178 | + % Compute mass conservation residual (analytical) |
| 179 | + if i == 1 |
| 180 | + residual_analytical(:,i) = NaN(size(p_analytical(:,i))); % undefined at first time step |
| 181 | + else |
| 182 | + dt_local = times_sec(i) - times_sec(i-1); |
| 183 | + dpdt_ana = (p_analytical(:,i) - p_analytical(:,i-1)) / dt_local; |
| 184 | + dedt_ana = (e_analytical(:,i) - e_analytical(:,i-1)) / dt_local; |
| 185 | + divq_ana = div(k, m, dx) * q_analytical(:,i); |
| 186 | + residual_analytical(:,i) = Ss * dpdt_ana + alpha * dedt_ana - divq_ana; |
| 187 | + end |
| 188 | + |
| 189 | + |
| 190 | + % Print combined analytical output |
125 | 191 | 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'); |
| 192 | + fprintf('| x (m) | Analytical p [Pa] | Darcy Flux [m/s] | Residual [1/s] |\n'); |
| 193 | + fprintf('|---------------|-------------------|------------------|------------------|\n'); |
128 | 194 | for j = 1:m+1 |
129 | | - fprintf('| %13.6f | %18.6e | %16.6e |\n', xgrid(j), p_analytical(j,i), q_analytical(j,i)); |
| 195 | + if i == 1 |
| 196 | + fprintf('| %13.6f | %18.6e | %16.6e | %16s |\n', ... |
| 197 | + xgrid(j), p_analytical(j,i), q_analytical(j,i), 'N/A'); |
| 198 | + else |
| 199 | + fprintf('| %13.6f | %18.6e | %16.6e | %16.6e |\n', ... |
| 200 | + xgrid(j), p_analytical(j,i), q_analytical(j,i), residual_analytical(j,i)); |
| 201 | + end |
| 202 | + end |
| 203 | + |
| 204 | + if i > 1 |
| 205 | + max_res = max(abs(residual_analytical(2:end-1,i))); |
| 206 | + fprintf('| Max Residual (interior) at t = %.2f hr: %e |\n', times_hr(i), max_res); |
130 | 207 | end |
131 | 208 | end |
132 | 209 |
|
|
159 | 236 | legend('Location', 'southeast'); |
160 | 237 | grid on; |
161 | 238 |
|
| 239 | + |
| 240 | + |
| 241 | +%% Combined Displacement Plot (Numerical vs Analytical) |
| 242 | +figure('Name','Displacement Field (Numerical vs Analytical)'); |
| 243 | +set(gcf,'Color','white'); |
| 244 | + |
| 245 | +% Top subplot: Numerical displacement |
| 246 | +subplot(2,1,1); |
| 247 | +hold on; |
| 248 | +for i = 1:length(times_sec) |
| 249 | + plot(xgrid, u_numerical(:,i), '-o', 'DisplayName', [num2str(times_hr(i)) ' hr']); |
| 250 | +end |
| 251 | +xlabel('x (m)'); |
| 252 | +ylabel('Displacement u(x,t) [m]'); |
| 253 | +title('Numerical Displacement Field'); |
| 254 | +legend('Location','southwest'); |
| 255 | +grid on; |
| 256 | + |
| 257 | +% Bottom subplot: Analytical displacement |
| 258 | +subplot(2,1,2); |
| 259 | +hold on; |
| 260 | +for i = 1:length(times_sec) |
| 261 | + plot(xgrid, u_analytical(:,i), '--s', 'DisplayName', [num2str(times_hr(i)) ' hr']); |
| 262 | +end |
| 263 | +xlabel('x (m)'); |
| 264 | +ylabel('Displacement u(x,t) [m]'); |
| 265 | +title('Analytical Displacement Field'); |
| 266 | +legend('Location','southwest'); |
| 267 | +grid on; |
| 268 | + |
| 269 | + |
| 270 | +%% Residual Plot (Numerical vs Analytical) |
| 271 | +figure('Name','Mass Conservation Residuals (Numerical vs Analytical)'); |
| 272 | +set(gcf,'Color','white'); |
| 273 | + |
| 274 | +% Top subplot: Numerical residual |
| 275 | +subplot(2,1,1); |
| 276 | +hold on; |
| 277 | +for i = 2:length(times_sec) |
| 278 | + plot(xgrid(2:end-1), residual(2:end-1,i), '-o', 'DisplayName', [num2str(times_hr(i)) ' hr']); |
| 279 | +end |
| 280 | +title('Numerical Mass Conservation Residual'); |
| 281 | +xlabel('x (m)'); |
| 282 | +ylabel('Residual [1/s]'); |
| 283 | +legend('Location','northeast'); |
| 284 | +grid on; |
| 285 | + |
| 286 | +% Bottom subplot: Analytical residual |
| 287 | +subplot(2,1,2); |
| 288 | +hold on; |
| 289 | +for i = 2:length(times_sec) |
| 290 | + plot(xgrid(2:end-1), residual_analytical(2:end-1,i), '--s', 'DisplayName', [num2str(times_hr(i)) ' hr']); |
| 291 | +end |
| 292 | +title('Analytical Mass Conservation Residual'); |
| 293 | +xlabel('x (m)'); |
| 294 | +ylabel('Residual [1/s]'); |
| 295 | +legend('Location','northeast'); |
| 296 | +grid on; |
| 297 | + |
| 298 | + |
162 | 299 | %% Print relative L2 errors |
163 | 300 | fprintf('\nRelative L2 Errors (Numerical vs Analytical):\n'); |
164 | | -fprintf('| Time [hr] | Pressure Error | Darcy Flux Error |\n'); |
165 | | -fprintf('|------------------|--------------------|---------------------|\n'); |
| 301 | +fprintf('| Time [hr] | Pressure Error | Darcy Flux Error | Strain Error | Displacement Error | Residual Error |\n'); |
| 302 | +fprintf('|------------------|----------------|------------------|--------------|--------------------|----------------|\n'); |
| 303 | + |
166 | 304 | for i = 1:length(times_hr) |
167 | 305 | % L2 error for pressure |
168 | 306 | rel_err_p = norm(p_numerical(:,i) - p_analytical(:,i)) / norm(p_analytical(:,i)); |
169 | | - |
| 307 | + |
170 | 308 | % L2 error for flux |
171 | 309 | 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); |
| 310 | + |
| 311 | + % L2 error for strain |
| 312 | + rel_err_e = norm(e_numerical(:,i) - e_analytical(:,i)) / norm(e_analytical(:,i)); |
| 313 | + |
| 314 | + % L2 error for displacement |
| 315 | + rel_err_u = norm(u_numerical(:,i) - u_analytical(:,i)) / norm(u_analytical(:,i)); |
| 316 | + |
| 317 | + % L2 error for mass conservation residual |
| 318 | + if i == 1 |
| 319 | + rel_err_r = NaN; % Not defined for first time step |
| 320 | + else |
| 321 | + rel_err_r = norm(residual(:,i) - residual_analytical(:,i)) / norm(residual_analytical(:,i)); |
| 322 | + end |
| 323 | + |
| 324 | + % Print in formatted row |
| 325 | + if i == 1 |
| 326 | + fprintf('| %16.2f | %14.6e | %16.6e | %12.6e | %18.6e | %14s |\n', ... |
| 327 | + times_hr(i), rel_err_p, rel_err_q, rel_err_e, rel_err_u, 'N/A'); |
| 328 | + else |
| 329 | + fprintf('| %16.2f | %14.6e | %16.6e | %12.6e | %18.6e | %14.6e |\n', ... |
| 330 | + times_hr(i), rel_err_p, rel_err_q, rel_err_e, rel_err_u, rel_err_r); |
| 331 | + end |
175 | 332 | end |
0 commit comments