Skip to content

Commit 02096a9

Browse files
terzaghi1D completed
1 parent 2cce658 commit 02096a9

1 file changed

Lines changed: 186 additions & 29 deletions

File tree

examples/matlab/terzaghi1D.m

Lines changed: 186 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@
5454
mu = 1e-3; % Dynamic viscosity [Pa·s]
5555
rho = 1000; % Fluid density [kg/m^3]
5656
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
5760

5861
%% Numerical (MOLE) Solution
5962
L = lap(k, m, dx); % Mimetic Laplacian operator for diffusion
@@ -62,71 +65,145 @@
6265
% Boundary modifications to Laplacian
6366
L(1,:) = 0; L(end,:) = 0;
6467

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
6772

6873
% Loop over each specified final time
6974
for i = 1:length(times_sec)
7075
t_final = times_sec(i);
71-
dt = 0.03;
76+
dt = 1.0;
7277
nsteps = round(t_final / dt);
7378

7479
% Uniform Initial Condition : p(x,0) = P0
75-
p = P0 * ones(size(xgrid))';
80+
p = P0 * ones(size(xgrid))';
7681

7782
% Time integration using Forward Euler
7883
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
8287
end
8388

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.
8592

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
8799
dpdx = G * p;
88100
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
90116

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)
92129
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');
95132
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
97140
end
98141
end
99142

143+
100144
%% Analytical Solution
101145
N_max = 100; % Number of Fourier series terms
102146
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));
104151

105152
% Loop over all time snapshots
106153
for i = 1:length(times_sec)
107154
t = times_sec(i);
108155
p = zeros(size(xgrid));
109156

110-
% Compute analytical solution using truncated Fourier sine series
157+
% Compute analytical pressure using Fourier series
111158
for N = 0:N_max
112-
n = 2*N + 1; % Odd terms only (satisfies boundary conditions)
159+
n = 2*N + 1;
113160
term = (1/n) * sin(n*pi*xgrid/(2*l)) .* exp(-(n^2)*(pi^2)*cf*t/(4*l^2));
114161
p = p + term;
115162
end
116163
p = (4/pi) * P0 * p;
117164
p_analytical(:,i) = p;
118165

119-
% Compute Darcy flux (analytical)
120-
dpdx = gradient(p, dx); % basic gradient
166+
% Compute Darcy flux
167+
dpdx = gradient(p, dx);
121168
q = - (K / mu) * (dpdx - rho * g);
122169
q_analytical(:,i) = q(1:m+1);
123170

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
125191
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');
128194
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);
130207
end
131208
end
132209

@@ -159,17 +236,97 @@
159236
legend('Location', 'southeast');
160237
grid on;
161238

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+
162299
%% Print relative L2 errors
163300
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+
166304
for i = 1:length(times_hr)
167305
% L2 error for pressure
168306
rel_err_p = norm(p_numerical(:,i) - p_analytical(:,i)) / norm(p_analytical(:,i));
169-
307+
170308
% L2 error for flux
171309
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
175332
end

0 commit comments

Comments
 (0)