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 end
elseif 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 end
elseif 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 end
end
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.