Równania cząstkowe w MATLABie
Contents
Schematy różnicowe dla równania transportu
Rozwiązywanie numeryczne równań cząstkowych rozpoczniemy od prostego przykładu równania transportu, tzn.


Zauważmy, że musimy zadać jeszcze warunek brzegowy. Jednak to, który warunek brzegowy zadamy (by problem był dobrze określony) jest ściśle związane z samym równaniem: jeśli
, to musimy zadać warunek dla
, jeśli
, to dla
.
Przyjmiemy oznaczenie
, gdzie
to punkty na siatce (
,
). Stwórzmy funkcję NumericSolve, która dla zadanych parametrów równania będzie je rozwiązywała każdym z poniższych schematów:
- schemat forward-time forward-space:
, - schemat forward-time backward-space:
, - schemat forward-time central-space:
.
Zauważmy, że musimy pamiętać nie tylko o zaimplementowaniu warunku początkowego i brzegowego, ale także musimy uzupełnić (czasami) wartości na drugim brzegu. Tam, gdzie będziemy potrzebować tzw. numerycznego warunku brzegowego, użyjemy wartości odległej o 1 na siatce (czyli np.
).
Implementacja NumericSolve
function [u, X, T, M, N] = NumericSolve(a, u0, ht, t, x, lambda, h, method) % Równanie u_t + a*u_x = 0 z warunkiem początkowym u0 i warunkiem brzegowym % ht. Dla a>0, ht znajduje się na lewym brzegu, a dla a<0 na prawym. % t=[t0, t1] - przedział czasowy % x=[x0, x1] - przedział przestrzenny % h - krok przestrzenny, lambda = h/k, gdzie k - krok czasowy % Metody: % (1) forward-time forward-space % (2) forward-time backward-space % (3) forward-time central-space
k = lambda*h;
X = x(1):h:x(2); T = t(1):k:t(2); M = length(X); N = length(T);
u = zeros(M,N);
% uzupełnienie warunku początkowego: for i=1:M, u(i,1) = feval(u0,X(i)); end
% uzupełnienie warunku brzegowego: if a>0 for i=2:N, u(1,i) = feval(ht,T(i)); end else for i=2:N, u(M,i) = feval(ht,T(i)); end end
if strcmp(method,'forward-time forward-space') for n=2:N for m=2:(M-1) u(m,n) = u(m,n-1) - a*lambda*( u(m+1,n-1) - u(m,n-1) ); end
% uzupełnienie brzegowego warunku numerycznego
if a>0
u(M,n) = u(M-1,n);
else
u(1,n) = u(1,n-1) - a*lambda*( u(2,n-1) - u(1,n-1) );
end
endelseif strcmp(method,'forward-time backward-space') for n=2:N for m=2:(M-1) u(m,n) = u(m,n-1) - a*lambda*( u(m,n-1) - u(m-1,n-1) ); end
% uzupełnienie brzegowego warunku numerycznego
if a>0
u(M,n) = u(M,n-1) - a*lambda*( u(M,n-1) - u(M-1,n-1) );
else
u(1,n) = u(1+1,n);
end
endelseif strcmp(method,'forward-time central-space') for n=2:N for m=2:(M-1) u(m,n) = u(m,n-1) - a*lambda*( u(m+1,n-1) - u(m-1,n-1) )/2; end
% uzupełnienie brzegowego warunku numerycznego
if a>0
u(M,n) = u(M-1,n);
else
u(1,n) = u(1+1,n);
end
endend
Przetestowanie działania zaimplementowanych metod
Zajmijmy się na początek przykładem



gdzie
oraz

i przetestujmy działanie każdego schematu dla
oraz
,
,
.
a = 1; u0 = @(x) (abs(x)<=0.5).*(cos(pi*x)).^2; ht = @(t) 0; x = [-3,3]; t = [0,2.4]; h1 = 1/10; h2 = 1/20; h3 = 1/40; lambda = 0.8; method1 = 'forward-time forward-space'; method2 = 'forward-time backward-space'; method3 = 'forward-time central-space'; h = h3; [u1, X, T, M, N] = NumericSolve(a, u0, ht, t, x, lambda, h, method1); [u2] = NumericSolve(a, u0, ht, t, x, lambda, h, method2); [u3] = NumericSolve(a, u0, ht, t, x, lambda, h, method3); % for n = 1:N n = 20; plot(X,u1(:,n),'g') hold on; plot(X,u2(:,n),'b') plot(X,u3(:,n),'c') plot(X,u0((X)-a*T(n)),'-r') hold off; ylim([-0.1,1.6]) legend(method1,method2,method3,'real solution') xlabel('x'); ylabel('u(t,x)'); title(['time: ',num2str(T(n),'%.2f'),'s']) pause(0.2) % end
Zauważmy, że tylko metoda forward-time backward-space daje rozsądne wyniki. Spróbujmy zmodyfikować zagadnienie (niech
, wtedy warunek brzegowy zadany jest na drugim brzegu). Role schematów się odwracają - tym razem tylko metoda forward-time forward-space daje wyniki zbliżone do prawdziwych. Jest to związane z faktem, że dla
metoda forward- time backward-space jest zbieżna, a dla
nie (i analogicznie dla drugiego schematu). Widzimy też, że zmniejszenie kroku
znacząco poprawia własności schematów.
Stabilność metod
Wiemy, że dla
metoda forward-time backward-space jest zbieżna, ponadto z wykładu wiemy, że jest zgodna z równaniem (dla dowolnego
). Zbadajmy teraz stabilność tej metody. Wiemy (również z wykładu), że schemat jest stabilny, jeśli
. Potwierdźmy ten fakt doświadczalnie.
h = 1/10; lambda1 = 0.8; lambda2 = 1.6; method = 'forward-time backward-space'; [u4, X, T1, M, N1] = NumericSolve(a, u0, ht, t, x, lambda1, h, method); [u5, X, T2, M, N2] = NumericSolve(a, u0, ht, t, x, lambda2, h, method); % for n = 1:N2 n = 10; n2 = find(T1==T2(n)); plot(X,u4(:,n2),'b') hold on; plot(X,u5(:,n),'g') plot(X,u0((X)-a*T2(n)),'-r') hold off; ylim([-0.1,1.6]) legend('stable','unstable','real solution') xlabel('x'); ylabel('u(t,x)'); title(['time: ',num2str(T(n),'%.2f'),'s']) pause(0.2) % end
Warto sprawdzić przy tym, jak zmienia się norma L2 rozwiązania (w przypadku schematu niestabilnego powinna rosnąć nieograniczenie).
stability1 = zeros(size(T1)); stability2 = zeros(size(T2)); stability0 = sqrt(h)*norm(u0(X))*ones(size(t)); for n = 1:N1 stability1(n) = sqrt(h)*norm(u4(:,n)); end for n = 1:N2 stability2(n) = sqrt(h)*norm(u5(:,n)); end plot(T1,stability1,'b',T2,stability2,'g',t,stability0,'-r') ylim([-0.1,1.6]) legend('stable','unstable','real solution') xlabel('t'); ylabel('norm');
Podsumowanie
Przedstawione metody są zaledwie podstawowymi metodami jednokrokowymi. Jak widać ich własności nie są rewelacyjne - istnieje wiele schematów o dużo lepszych własnościach.