├── Time_Integrators ├── KS_1_1.mat ├── forcing_profile_20.mat ├── forcing_profile_40.mat ├── KS_Integrator_Trefethen.m ├── RD_Integrator_Rudy.m ├── initialise_sim_params_fdso_np.m └── integrate_2dns_fdso_np.m ├── NatComm2021 ├── weight_full.m ├── weight_harm.m ├── weight_poly.m ├── SINDy.m ├── weight_full_harm.m ├── ParEst_wf_KS.m ├── ParEst_WF_LambdaOmegaRD.m └── ParEst_WF_Kolmo.m ├── Chaos2019 ├── weight_poly.m ├── find_coeffs.m ├── weight_full.m ├── KS_integrate.m └── ParEst_WF_KS.m └── README.md /Time_Integrators/KS_1_1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SchatzLabGT/SymbolicRegression/HEAD/Time_Integrators/KS_1_1.mat -------------------------------------------------------------------------------- /Time_Integrators/forcing_profile_20.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SchatzLabGT/SymbolicRegression/HEAD/Time_Integrators/forcing_profile_20.mat -------------------------------------------------------------------------------- /Time_Integrators/forcing_profile_40.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SchatzLabGT/SymbolicRegression/HEAD/Time_Integrators/forcing_profile_40.mat -------------------------------------------------------------------------------- /NatComm2021/weight_full.m: -------------------------------------------------------------------------------- 1 | function W = weight_full(k,p,x,t) 2 | %{ 3 | Assemble the 1D weight functions into the full weight 4 | 5 | k = [kx,ky,kt]: order of derivative(s) 6 | p = [px,py,pt]: exponents of weight polynomials 7 | %} 8 | 9 | if length(k) == 3 10 | wx = weight_poly(x,p(1),k(1)); 11 | wy = weight_poly(x,p(2),k(2)); 12 | wt = weight_poly(t,p(3),k(3)); 13 | [wX,wY,wT] = meshgrid(wx,wy,wt); 14 | W = wX.*wY.*wT; 15 | elseif length(k) == 2 16 | wx = weight_poly(x,p(1),k(1)); 17 | wt = weight_poly(t,p(2),k(2)); 18 | [wT,wX] = meshgrid(wt,wx); 19 | W = wX.*wT; 20 | end 21 | 22 | end -------------------------------------------------------------------------------- /NatComm2021/weight_harm.m: -------------------------------------------------------------------------------- 1 | %% ----------------------------------------------------------------------- 2 | % WEIGHT FUNCTION HARMONICS 3 | % ------------------------------------------------------------------------ 4 | function g = weight_harm(x,k,q) 5 | % g_j = sin(q*pi*x) 6 | if mod(k,2) % odd k 7 | n = (1/2)*(k - 1); 8 | g = (-1)^n*(pi*q)^k*cos(pi*q*x); 9 | else % even k 10 | n = k/2; 11 | g = (-1)^n*(pi*q)^k*sin(pi*q*x); 12 | end 13 | if q == 0 && k == 0 14 | g = ones(size(x)); 15 | elseif q == 0 && k > 0 16 | g = zeros(size(x)); 17 | end 18 | end -------------------------------------------------------------------------------- /NatComm2021/weight_poly.m: -------------------------------------------------------------------------------- 1 | function p = weight_poly(x,m,k) 2 | %{ 3 | Polynomial piece of weighting function used to satisfy BC 4 | 5 | A = d^k/dx^k[ (x^2 - 1)^m ] 6 | 7 | x: independent variable 8 | m: power of base function 9 | k: order of derivative 10 | %} 11 | a = zeros(m*2 + 1,1); % initial coefficent vector 12 | for l = 0:m 13 | a(2*l+1) = (-1)^(m-l)*nchoosek(m,l); % set polynomial coefficients 14 | end 15 | 16 | c = zeros(2*m+1,1); % final coefficient vector 17 | for n = 0:(2*m - k) 18 | c(n+1) = a(n+1+k)*factorial(n+k)/factorial(n); 19 | end 20 | 21 | p = 0; 22 | for n = 0:(2*m-k) 23 | p = p + c(n+1)*x.^n; % final windowing function 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /Chaos2019/weight_poly.m: -------------------------------------------------------------------------------- 1 | function p = weight_poly(x,m,k) 2 | %{ 3 | Polynomial piece of weighting function used to satisfy BC 4 | 5 | A = d^k/dx^k[ (x^2 - 1)^m ] 6 | 7 | x: independent variable 8 | m: power of base function 9 | k: order of derivative 10 | %} 11 | if m==0 12 | p = (k==0)*x.^0; 13 | else 14 | a = zeros(m*2 + 1,1); % initial coefficent vector 15 | for l = 0:m 16 | a(2*l+1) = (-1)^(m-l)*nchoosek(m,l); % set polynomial coefficients 17 | end 18 | 19 | c = zeros(2*m+1,1); % final coefficient vector 20 | for n = 0:(2*m - k) 21 | c(n+1) = a(n+1+k)*factorial(n+k)/factorial(n); 22 | end 23 | 24 | p = 0; 25 | for n = 0:(2*m-k) 26 | p = p + c(n+1)*x.^n; % final windowing function 27 | end 28 | end -------------------------------------------------------------------------------- /NatComm2021/SINDy.m: -------------------------------------------------------------------------------- 1 | %% --------------------------------------------------------------- SINDy() 2 | function Xi = SINDy (Theta, dXdt) 3 | %{ 4 | Compute sparse regression on dX = Theta * Xi 5 | Regression technique used: sequential least squares 6 | 7 | Modified procedure from: 8 | S. H. Rudy, S. L. Brunton, J. L. Proctor, J. N. Kutz, Data-driven 9 | discovery of partial differential equations. Sci. Adv. 3, e1602614 (2017) 10 | %} 11 | 12 | Xi = Theta \ dXdt; 13 | 14 | gamma = 0.05; 15 | lambda = gamma*mean(abs(dXdt)); 16 | 17 | for i = 1:5 18 | 19 | product = zeros(size(Xi)); 20 | [~,w] = size(Theta); 21 | for p_ind = 1:w 22 | product(p_ind) = mean(abs(Xi(p_ind)*Theta(:,p_ind))); 23 | end 24 | 25 | smallinds = product < lambda; 26 | Xi(smallinds) = 0; % set negligible terms to 0 27 | for ind = 1:size(dXdt,2) 28 | biginds = ~smallinds(:,ind); 29 | Xi(biginds,ind) = Theta(:,biginds) \ dXdt(:,ind); 30 | end 31 | end 32 | 33 | end -------------------------------------------------------------------------------- /NatComm2021/weight_full_harm.m: -------------------------------------------------------------------------------- 1 | %% ----------------------------------------------------------------------- 2 | % FULL 3D WINDOWING FUNCTION 3 | % ------------------------------------------------------------------------ 4 | function W = weight_full_harm(k,p,q,x,y,t) 5 | %{ 6 | Combine base windows and harmonics in 1D, then assemble into 3D 7 | k = [kx,ky,kt]: order of derivative(s) 8 | p = [px,py,pt] : weight function exponents 9 | q = [q,r,s] : harmonic numbers 10 | %} 11 | 12 | % General Liebniz Rule 13 | wx = 0; 14 | for n = 0:k(1) 15 | f = weight_poly(x,p(1),k(1)-n); 16 | g = weight_harm(x,n,q(1)); 17 | wx = wx + nchoosek(k(1),n)*f.*g; 18 | end 19 | wy = 0; 20 | for n = 0:k(2) 21 | f = weight_poly(y,p(2),k(2)-n); 22 | g = weight_harm(y,n,q(2)); 23 | wy = wy + nchoosek(k(2),n)*f.*g; 24 | end 25 | wt = 0; 26 | for n = 0:k(3) 27 | f = weight_poly(t,p(3),k(3)-n); 28 | g = weight_harm(t,n,q(3)); 29 | wt = wt + nchoosek(k(3),n)*f.*g; 30 | end 31 | 32 | [wX,wY,wT] = meshgrid(wx,wy,wt); % Make 1D components 3D 33 | 34 | W = wX.*wY.*wT; % combine all components 35 | 36 | end -------------------------------------------------------------------------------- /Chaos2019/find_coeffs.m: -------------------------------------------------------------------------------- 1 | function Xi = find_coeffs(Theta, gamma) 2 | % compute sparse regression on Theta * Xi = 0 3 | % first column is (usually) dx/dt term 4 | % gamma : sparsification parameter - higher gamma means more pruning 5 | [h,w] = size(Theta); 6 | [~,~,V] = svd(Theta); 7 | Xi = V(:,end); % first estimate of coefficients (not sparse) 8 | lambda = norm(Theta*Xi); % residual as threshold 9 | smallinds = zeros(w,1); 10 | for i=1:w 11 | % product of the coefficient and characteristic size of library function 12 | for p_ind = 1:w 13 | product(p_ind) = norm(Xi(p_ind)*Theta(:,p_ind)./sqrt(sum(Theta(:,:).^2,2))); 14 | end 15 | product(smallinds==1)=Inf; 16 | [Y,I] = min(product); % find next term to eliminate 17 | smallinds(I) = 1; 18 | if sum(smallinds==0)==0 % no more terms to remove 19 | break; 20 | end 21 | Xi_old = Xi; 22 | Xi(smallinds==1) = 0; % set coefficients of negligible terms to 0 23 | [~,~,V] = svd(Theta(:,smallinds==0)); 24 | Xi(smallinds==0) = V(:,end); % find new coefficients 25 | lambda_old = lambda; 26 | lambda = norm(Theta*Xi); 27 | if (lambda/lambda_old > gamma) || nnz(Xi)==0 % condition to end iteration 28 | Xi = Xi_old; 29 | break 30 | end 31 | end 32 | end -------------------------------------------------------------------------------- /Time_Integrators/KS_Integrator_Trefethen.m: -------------------------------------------------------------------------------- 1 | % kursiv.m - solution of Kuramoto-Sivashinsky equation by ETDRK4 scheme 2 | % 3 | % u_t = -u*u_x - u_xx - u_xxxx, periodic BCs on [0,32*pi] 4 | % computation is based on v = fft(u), so linear term is diagonal 5 | % compare p27.m in Trefethen, "Spectral Methods in MATLAB", SIAM 2000 6 | % AK Kassam and LN Trefethen, July 2002 7 | 8 | % Spatial grid and initial condition: 9 | N = 128*8; 10 | x = 32*pi*(1:N)'/N; 11 | u = cos(x/16).*(1+sin(x/16)); 12 | v = fft(u); 13 | 14 | % Precompute various ETDRK4 scalar quantities: 15 | h = 2/10; % time step 16 | k = [0:N/2-1 0 -N/2+1:-1]'/16; % wave numbers 17 | L = k.^2 - k.^4; % Fourier multipliers 18 | E = exp(h*L); E2 = exp(h*L/2); 19 | M = 16; % no. of points for complex means 20 | r = exp(1i*pi*((1:M)-.5)/M); % roots of unity 21 | LR = h*L(:,ones(M,1)) + r(ones(N,1),:); 22 | Q = h*real(mean( (exp(LR/2)-1)./LR ,2)); 23 | f1 = h*real(mean( (-4-LR+exp(LR).*(4-3*LR+LR.^2))./LR.^3 ,2)); 24 | f2 = h*real(mean( (2+LR+exp(LR).*(-2+LR))./LR.^3 ,2)); 25 | f3 = h*real(mean( (-4-3*LR-LR.^2+exp(LR).*(4-LR))./LR.^3 ,2)); 26 | 27 | % Main time-stepping loop: 28 | uu=u; tt=0; 29 | tmax = 100; % Lt, total integration time 30 | nmax = round(tmax/h); % number of integration time steps 31 | nplt = (tmax/250)/h; % snapshot time step 32 | g = -0.5i*k; 33 | for n = 1:nmax 34 | t = n*h; 35 | Nv = g.*fft(real(ifft(v)).^2); 36 | a = E2.*v + Q.*Nv; 37 | Na = g.*fft(real(ifft(a)).^2); 38 | b = E2.*v + Q.*Na; 39 | Nb = g.*fft(real(ifft(b)).^2); 40 | c = E2.*a + Q.*(2*Nb-Nv); 41 | Nc = g.*fft(real(ifft(c)).^2); 42 | v = E.*v + Nv.*f1 + 2*(Na+Nb).*f2 + Nc.*f3; 43 | if mod(n,nplt) == 0 44 | u = real(ifft(v)); 45 | uu = [uu,u]; 46 | tt = [tt,t]; 47 | end 48 | end 49 | 50 | % Save Results 51 | dx = x(2) - x(1); 52 | dt = tt(2) - tt(1); 53 | save('Kuramoto_Sivashinsky.mat','tt','uu','dx','dt','-v7.3') 54 | 55 | % Plot results: 56 | surf(tt,x,uu), shading interp, lighting phong, axis tight 57 | view([-90 90]), colormap(autumn); set(gca,'zlim',[-5 50]) 58 | light('color',[1 1 0],'position',[-1,2,2]) 59 | material([0.30 0.60 0.60 40.00 1.00]); 60 | -------------------------------------------------------------------------------- /Time_Integrators/RD_Integrator_Rudy.m: -------------------------------------------------------------------------------- 1 | %%%%%% Rudy's R-D Integrator %%%%%% 2 | 3 | %{ 4 | Acquired from 5 | 6 | https://github.com/snagcliffs/PDE-FIND/tree/master/Datasets 7 | 8 | through the paper 9 | 10 | Samuel H. Rudy, Steven L. Brunton, Joshua L. Proctor, J. Nathan Kutz 11 | Science Advances26 Apr 2017 : e1602614 12 | 13 | %} 14 | 15 | % lambda-omega reaction-diffusion system 16 | % u_t = lam(A) u - ome(A) v + d1*(u_xx + u_yy) = 0 17 | % v_t = ome(A) u + lam(A) v + d2*(v_xx + v_yy) = 0 18 | % 19 | % A^2 = u^2 + v^2 and 20 | % lam(A) = 1 - A^2 21 | % ome(A) = -beta*A^2 22 | 23 | 24 | t=0:0.05:2; 25 | d1=0.1; d2=0.1; beta=1.0; 26 | L=20; n=512; N=n*n; 27 | x2=linspace(-L/2,L/2,n+1); x=x2(1:n); y=x; 28 | kx=(2*pi/L)*[0:(n/2-1) -n/2:-1]; ky=kx; 29 | 30 | % INITIAL CONDITIONS 31 | 32 | [X,Y]=meshgrid(x,y); 33 | [KX,KY]=meshgrid(kx,ky); 34 | K2=KX.^2+KY.^2; K22=reshape(K2,N,1); 35 | 36 | m=2; % number of spirals 37 | 38 | U_t = zeros(length(x),length(y),length(t)); 39 | V_t = zeros(length(x),length(y),length(t)); 40 | 41 | U_t(:,:,1)=tanh(sqrt(X.^2+Y.^2)).*cos(m*angle(X+i*Y)-(sqrt(X.^2+Y.^2))); 42 | V_t(:,:,1)=tanh(sqrt(X.^2+Y.^2)).*sin(m*angle(X+i*Y)-(sqrt(X.^2+Y.^2))); 43 | 44 | % REACTION-DIFFUSION 45 | uvt=[reshape(fft2(U_t(:,:,1)),1,N) reshape(fft2(V_t(:,:,1)),1,N)].'; 46 | [t,uvsol]=ode45(@(t,uvt) reaction_diffusion_rhs(t,uvt,[],K22,d1,d2,beta,n,N),t,uvt); 47 | 48 | 49 | for j=1:length(t)-1 50 | ut=reshape((uvsol(j,1:N).'),n,n); 51 | vt=reshape((uvsol(j,(N+1):(2*N)).'),n,n); 52 | U_t(:,:,j+1)=real(ifft2(ut)); 53 | V_t(:,:,j+1)=real(ifft2(vt)); 54 | 55 | figure(1) 56 | pcolor(x,y,V_t(:,:,j+1)); shading interp; colormap(hot); colorbar; drawnow; 57 | end 58 | 59 | dx = x(2) - x(1); 60 | dt = t(2) - t(1); 61 | save('reaction_diffusion_big.mat','t','x','y','U_t','V_t','dx','dt','-v7.3') 62 | 63 | %% 64 | load reaction_diffusion_big 65 | pcolor(x,y,U_t(:,:,end)); shading interp; colormap(hot) 66 | 67 | 68 | function rhs = reaction_diffusion_rhs(t,uvt,dummy,K22,d1,d2,beta,n,N) 69 | 70 | % Calculate u and v terms 71 | ut=reshape((uvt(1:N)),n,n); 72 | vt=reshape((uvt((N+1):(2*N))),n,n); 73 | u=real(ifft2(ut)); v=real(ifft2(vt)); 74 | 75 | % Reaction Terms 76 | u3=u.^3; v3=v.^3; u2v=(u.^2).*v; uv2=u.*(v.^2); 77 | utrhs=reshape((fft2(u-u3-uv2+beta*u2v+beta*v3)),N,1); 78 | vtrhs=reshape((fft2(v-u2v-v3-beta*u3-beta*uv2)),N,1); 79 | 80 | rhs=[-d1*K22.*uvt(1:N)+utrhs 81 | -d2*K22.*uvt(N+1:end)+vtrhs]; 82 | 83 | end -------------------------------------------------------------------------------- /Chaos2019/weight_full.m: -------------------------------------------------------------------------------- 1 | function W = weight_full(k,F,o) 2 | global var 3 | %{ 4 | Assemble 1D weight functions into (a derivative of) the full weight 5 | Third entries in arguments optional (only used for 3D PDEs) 6 | 7 | k = [kx,ky,kt]: order of derivative(s) 8 | F = [Fx,Fy,Ft]: power of (x^2-1) term 9 | w = [ox,oy,ot]: frequency of sin/cos 10 | %} 11 | s = pi; 12 | ifsinx = mod(o(1),2); 13 | o1 = s*floor((o(1)+1)/2); 14 | for i=0:k(1) 15 | if k(1)==0 16 | coeff = 1; 17 | else 18 | coeff = nchoosek(k(1),i); 19 | end 20 | wxi(:,i+1) = coeff*weight_poly(var.x,F(1),k(1)-i); 21 | sign = (-1)^(floor((i+1-ifsinx)/2)); 22 | if mod(i+ifsinx,2)==0 23 | trxi(:,i+1)=sign*o1^i*cos(o1*var.x); 24 | else 25 | trxi(:,i+1)=sign*o1^i*sin(o1*var.x); 26 | end 27 | end 28 | if length(k)==3 29 | ifsiny = mod(o(2),2); 30 | o2 = s*floor((o(2)+1)/2); 31 | for i=0:k(2) 32 | if k(2)==0 33 | coeff = 1; 34 | else 35 | coeff = nchoosek(k(2),i); 36 | end 37 | wyi(:,i+1) = coeff*weight_poly(var.x,F(2),k(2)-i); 38 | sign = (-1)^(floor((i+1-ifsiny)/2)); 39 | if mod(i+ifsiny,2)==0 40 | tryi(:,i+1)=sign*o2^i*cos(o2*var.x); 41 | else 42 | tryi(:,i+1)=sign*o2^i*sin(o2*var.x); 43 | end 44 | end 45 | ifsint = mod(o(3),2); 46 | o3 = s*floor((o(3)+1)/2); 47 | for i=0:k(3) 48 | if k(3)==0 49 | coeff = 1; 50 | else 51 | coeff = nchoosek(k(3),i); 52 | end 53 | wti(:,i+1) = coeff*weight_poly(var.t,F(3),k(3)-i); 54 | sign = (-1)^(floor((i+1-ifsint)/2)); 55 | if mod(i+ifsint,2)==0 56 | trti(:,i+1)=sign*o3^i*cos(o3*var.t); 57 | else 58 | trti(:,i+1)=sign*o3^i*sin(o3*var.t); 59 | end 60 | end 61 | wx = sum(wxi.*trxi,2); 62 | wy = sum(wyi.*tryi,2); 63 | wt = sum(wti.*trti,2); 64 | [wX,wY,wT] = meshgrid(wx,wy,wt); 65 | W = wX.*wY.*wT; 66 | else 67 | ifsint = mod(o(2),2); 68 | o2 = s*floor((o(2)+1)/2); 69 | for i=0:k(2) 70 | if k(2)==0 71 | coeff = 1; 72 | else 73 | coeff = nchoosek(k(2),i); 74 | end 75 | wti(:,i+1) = coeff*weight_poly(var.t,F(2),k(2)-i); 76 | sign = (-1)^(floor((i+1-ifsint)/2)); 77 | if mod(i+ifsint,2)==0 78 | trti(:,i+1)=sign*o2^i*cos(o2*var.t); 79 | else 80 | trti(:,i+1)=sign*o2^i*sin(o2*var.t); 81 | end 82 | end 83 | wx = sum(wxi.*trxi,2); 84 | wt = sum(wti.*trti,2); 85 | [wX,wT] = meshgrid(wx,wt); 86 | W = wX'.*wT'; 87 | end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /Chaos2019/KS_integrate.m: -------------------------------------------------------------------------------- 1 | % kursiv.m - solution of Kuramoto-Sivashinsky equation by ETDRK4 scheme 2 | % 3 | % u_t = -u*u_x - u_xx - u_xxxx, periodic BCs on [0,32*pi] 4 | % computation is based on v = fft(u), so linear term is diagonal 5 | % compare p27.m in Trefethen, "Spectral Methods in MATLAB", SIAM 2000 6 | % AK Kassam and LN Trefethen, July 2002 7 | 8 | function [uu,tt] = KS_integrate(Nx,Nt,par,if_plot,if_save) 9 | % Spatial grid and initial condition: 10 | Lx = 32*pi; % physical length of domain 11 | x = Lx*(1:Nx)'/Nx; 12 | dx = x(2) - x(1); 13 | u = cos(x/16).*(1+sin(x/16)); % initial condition 14 | v = fft(u); 15 | % Precompute various ETDRK4 scalar quantities: 16 | h = 5/100; % time-step 17 | k = [0:Nx/2-1 0 -Nx/2+1:-1]'/16; % wave numbers 18 | L = par(1)*k.^2 - par(2)*k.^4; % Fourier multipliers 19 | E = exp(h*L); E2 = exp(h*L/2); 20 | M = 16; % no. of points for complex means 21 | r = exp(1i*pi*((1:M)-.5)/M); % roots of unity 22 | LR = h*L(:,ones(M,1)) + r(ones(Nx,1),:); 23 | Q = h*real(mean( (exp(LR/2)-1)./LR ,2)); 24 | f1 = h*real(mean( (-4-LR+exp(LR).*(4-3*LR+LR.^2))./LR.^3 ,2)); 25 | f2 = h*real(mean( (2+LR+exp(LR).*(-2+LR))./LR.^3 ,2)); 26 | f3 = h*real(mean( (-4-3*LR-LR.^2+exp(LR).*(4-LR))./LR.^3 ,2)); 27 | 28 | % Main time-stepping loop: 29 | uu = u; tt = 0; 30 | tskip = 50; 31 | tmax = 550; % time-length 32 | dt = 0.25; 33 | nmax = round(tmax/h); nplt = floor((tmax/Nt)/h*dt); 34 | g = -0.5i*k; 35 | for n = 1:nmax 36 | t = n*h; 37 | Nv = g.*fft(real(ifft(v)).^2); 38 | a = E2.*v + Q.*Nv; 39 | Na = g.*fft(real(ifft(a)).^2); 40 | b = E2.*v + Q.*Na; 41 | Nb = g.*fft(real(ifft(b)).^2); 42 | c = E2.*a + Q.*(2*Nb-Nv); 43 | Nc = g.*fft(real(ifft(c)).^2); 44 | v = E.*v + Nv.*f1 + 2*(Na+Nb).*f2 + Nc.*f3; 45 | if mod(n,nplt)==0 46 | u = real(ifft(v)); 47 | uu = [uu,u]; tt = [tt,t]; 48 | end 49 | end 50 | %dt = tt(2)-tt(1); 51 | uu = uu(:,floor(tskip/dt)+1:end-1); 52 | tt = tt(:,floor(tskip/dt)+1:end-1); 53 | uu = uu(:,1:2000); 54 | tt = tt(:,1:2000); 55 | % Plot results: 56 | if(if_plot) 57 | surf(tt,x,uu), shading interp, lighting phong, axis tight 58 | view([-90 90]), colormap(autumn); set(gca,'zlim',[-5 50]) 59 | light('color',[1 1 0],'position',[-1,2,2]) 60 | material([0.30 0.60 0.60 40.00 1.00]); 61 | end 62 | 63 | % Save results 64 | if(if_save) 65 | 66 | [Nx,Nt] = size(uu); 67 | save_name = ['KS_',num2str(par(1)),'_',num2str(par(2)),'.mat']; 68 | save(save_name,'tt','uu','par','Nx','Nt','dx','dt','-v7.3') 69 | 70 | end 71 | 72 | end 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PDE_Discovery_Weak_Formulation 2 | MATLAB codes for papers: 3 | ## Robust and Optimal Sparse Regression for Nonlinear PDE Models 4 | References and authorship are attributed both in the MATLAB codes (provided in the Chaos2019 directory), as well in the paper for which this directory was created. Summary of included files below: 5 | 6 | KS_Integrate.m: Integrator for Kuramoto-Sivashinsky equation by Trefethen. 7 | 8 | ParEst_WF_KS.m: Implementation of the weak formulation method for the Kuramoto-Sivashinsky equation. 9 | 10 | find_coeffs.m: A general solver for sparse regression from the linear system ΘX = 0. 11 | 12 | weight_full.m: Helper method for generating weight functions and their derivatives. 13 | 14 | weight_poly.m: Helper method for generating weight functions' components in 1D. 15 | 16 | ## Using Noisy or Incomplete Data to Discover Models of Spatiotemporal Dynamics 17 | 18 | In Time_Integrators all MATLAB codes used to generate data sets can be found, and the data set for the Kuramoto-Sivashinsky equation is given directly (in addition to its integrator). References and authorship are attributed both in the MATLAB codes, as well in the paper for which this directory was created. Brief instructions for use are given below: 19 | 20 | KS_Integrator_Trefethen.m : Running this directly should generate the data set in the same directory; some modification to the variable "nplt" on line 31 is necessary for different time resolutions of the stored trajectory. 21 | 22 | RD_Integrator_Rudy.m : The length of the trajectory and the desired temporal resolution can be changed in the variable "t" on line 24. Otherwise, running directly should yield the same dataset used in the paper. 23 | 24 | initialise_sim_params_fdso_np.m : Run this first to create the necessary files required to run the main integrator. There are two grid resolutions available, 20 grid points per unit length and 40 grid points per unit length, and the forcing profiles for these associated grid resolutions are stored in the files "forcing_profile_20.mat" and "forcing_profile_40.mat", respectively. This code is where spatial grid resolution, integration time-step, as well as physical parameters are determined. 25 | 26 | integrate_2dns_fdso_np.m : Main integrator for the 2D Navier-Stokes equation with Kolmogorov forcing. Running this will create a trajectory in the directory that the initialization code creates. This code is where the length of the trajectory and the temporal sampling rate are determined. 27 | 28 | In the other folder, Weak_Formulation_Codes, are the functions used to determine the governing equations for the systems detailed in the paper. The titles indicate for which system the function is intended for. Note that the principle input for these MATLAB codes is a string containing the filename for the .mat file that contains the trajectory and relevant parameters (usually only dx & dt, the spatial spacing and time between snapshots, respectively). 29 | 30 | **Important Note 1** 31 | The trajectories must be saved with the switch '-v7.3', which enables them to be loaded in pieces with the matfile formalism in MATLAB. This should be handled automatically in the integrators. 32 | 33 | ParEst_wf_KS.m : System identifier for the Kuramoto-Sivashinsky equation. Comes with symbolic regression functionality, though it is not used in the paper. 34 | 35 | ParEst_WF_Kolmo.m : System identifier for the 2D Navier-Stokes data. 36 | 37 | ParEst_WF_RD.m : System identifier for the lambda-omega reaction diffusion system. Comes with symbolic regression functionality. 38 | -------------------------------------------------------------------------------- /NatComm2021/ParEst_wf_KS.m: -------------------------------------------------------------------------------- 1 | %%%% Integral Method Kuramoto-Sivashinsky Parameter Estimation %%%% 2 | 3 | %% PSEUDO-CODE 4 | %{ 5 | 6 | INPUTS 7 | ------ 8 | filename : string for path to -v7.3 .mat file containing data 9 | N_d : number of integration domains 10 | D = [fr, Dt] : size of integration domain 11 | 12 | OUTPUTS 13 | ------- 14 | par : estimated parameters in physical units 15 | ksi : estimated parameters in non-dimensinoal units 16 | res : residual of estimation (utility unclear) 17 | 18 | Initialize 19 | -size of local domain 20 | -any tracking variables 21 | -library and target 22 | 23 | Fill terms 24 | -advection term 25 | -laplacian term 26 | -rayleigh term 27 | -time-derivative term 28 | 29 | Regression 30 | -invert library onto target 31 | -calculate norm of b-\Theta\xi 32 | 33 | Reminders 34 | -disp to-do list 35 | 36 | weight_full(k) 37 | -inputs: k = [kx,ky,kt], order of derivative(s) 38 | -output: 3D array corresponding to local sub-domain 39 | 40 | weight_poly(x,m,k) 41 | -inputs: absiscae, polynomial order, derivative order 42 | -output: additive windowing polynomial 43 | 44 | %} 45 | %% 46 | function [ksi,res,Q,q0] = ParEst_wf_KS(filename,N_d,D,if_track,if_symreg) 47 | 48 | %% INITIALIZE 49 | % Define matfile 50 | traj = matfile(filename); 51 | 52 | % Get size of velocity fields 53 | [Lx,Lt] = size(traj,'uu'); 54 | 55 | % Grid densities 56 | dt = traj.dt; 57 | dx = traj.dx; 58 | 59 | % % Size of local domain 60 | clearvars -global 61 | Dx = D(1); 62 | Dt = D(2); 63 | 64 | % Create subdomain 65 | x = linspace(-1,1,Dx+1); 66 | t = linspace(-1,1,Dt+1); 67 | 68 | % Define variable conversions 69 | S_x = 2/(dx*Dx); 70 | S_t = 2/(dt*Dt); 71 | 72 | % Time sampling scheme 73 | P = zeros(2,N_d); 74 | P(1,:) = randi([1,Lx-Dx],N_d,1); 75 | P(2,:) = randi([1,Lt-Dt],N_d,1); 76 | 77 | % Initialize Target and Library 78 | q0 = zeros(N_d,1); 79 | Q = zeros(length(q0),8); 80 | 81 | %% FILL LIBRARY AND TARGET 82 | n_lib = 0; 83 | n_track = 10; 84 | 85 | % Pre-make derivatives of windowing functions 86 | p = [4,3]; % weight function exponents 87 | dA00 = weight_full([0,0],p,x,t); 88 | dA01 = weight_full([0,1],p,x,t); 89 | dA10 = weight_full([1,0],p,x,t); 90 | dA20 = weight_full([2,0],p,x,t); 91 | dA40 = weight_full([4,0],p,x,t); 92 | dA30 = weight_full([3,0],p,x,t); 93 | 94 | for np = 1:N_d 95 | 96 | n_lib = n_lib + 1; 97 | 98 | if if_track && n_lib == n_track 99 | if n_lib < 100 100 | disp(['Library Row # : ',num2str(n_lib)]) 101 | n_track = n_track + 10; 102 | elseif n_lib < 1000 103 | disp(['Library Row # : ',num2str(n_lib)]) 104 | n_track = n_track + 100; 105 | else 106 | disp(['Library Row # : ',num2str(n_lib)]) 107 | n_track = n_track + 1000; 108 | end 109 | end 110 | 111 | % Indices for integration domain 112 | rx = P(1,np):(P(1,np)+Dx); 113 | rt = P(2,np):(P(2,np)+Dt); 114 | 115 | % Velocity fields on integration domain 116 | U = traj.uu(rx,rt); 117 | 118 | % Target 119 | B = U.*dA01*S_t; 120 | q0(n_lib,1) = trapz(x,trapz(t,B,2),1); 121 | 122 | % Advection Term 123 | th1 = -(1/2)*U.^2.*dA10*S_x; 124 | Q(n_lib,1) = trapz(x,trapz(t,th1,2),1); 125 | 126 | % Laplacian Term 127 | th2 = U.*dA20*S_x^2; 128 | Q(n_lib,2) = trapz(x,trapz(t,th2,2),1); 129 | 130 | % Biharmonic Term 131 | th3 = U.*dA40*S_x^4; 132 | Q(n_lib,3) = trapz(x,trapz(t,th3,2),1); 133 | 134 | % Linear Term 135 | th4 = U.*dA00; 136 | Q(n_lib,4) = trapz(x,trapz(t,th4,2),1); 137 | 138 | % First Order Derivative 139 | th5 = U.*dA10*S_x; 140 | Q(n_lib,5) = trapz(x,trapz(t,th5,2),1); 141 | 142 | % Third Order Derivative 143 | th6 = U.*dA30*S_x^3; 144 | Q(n_lib,6) = trapz(x,trapz(t,th6,2),1); 145 | 146 | % Quadratic Term 147 | th7 = U.^2.*dA00; 148 | Q(n_lib,7) = trapz(x,trapz(t,th7,2),1); 149 | 150 | % Cubic Term 151 | th8 = U.^3.*dA00; 152 | Q(n_lib,8) = trapz(x,trapz(t,th8,2),1); 153 | 154 | end 155 | 156 | %% REGRESSION 157 | % Parameters 158 | if if_symreg 159 | ksi = SINDy(Q, q0); % sparsify library 160 | res = norm(q0 - Q*ksi); 161 | else 162 | ksi = Q(:,1:3) \ q0; 163 | res = norm(q0 - Q(1:3)*ksi); 164 | end 165 | 166 | end 167 | -------------------------------------------------------------------------------- /Chaos2019/ParEst_WF_KS.m: -------------------------------------------------------------------------------- 1 | %%%% Weak Formulation of Kuramoto-Sivashinsky Parameter Estimation %%%% 2 | 3 | %{ 4 | 5 | INPUTS 6 | ------ 7 | filename : string for path to -v7.3 .mat file containing data 8 | N_d : number of integration domains 9 | F : weight function powers [alpha, beta] 10 | wts : frequencies of weight functions used in x and t 11 | (0 : frequency 0, 1-2 : sin/cos of frequency pi, etc.) 12 | D = [Dx, Dt] : size of integration domain 13 | if_track : enable output during running 14 | sig : size of artificial noise 15 | seed : random seed to fix realization of noise and integration domains 16 | 17 | OUTPUTS 18 | ------- 19 | ksi : estimated parameters in physical units 20 | res : residual of estimation 21 | Q : library matrix 22 | 23 | %} 24 | %% 25 | function [ksi,res,Q] = ParEst_WF_KS(filename,N_d,F,wts,D,if_track,sig,seed) 26 | 27 | %% INITIALIZE 28 | % Define matfile 29 | traj = matfile(filename); 30 | 31 | % Get size of velocity fields 32 | [Lx,Lt] = size(traj,'uu'); 33 | 34 | % Grid densities 35 | dt = traj.dt; 36 | dx = traj.dx; 37 | 38 | % % Size of local domain 39 | clearvars -global 40 | global var 41 | %var.Dx = D(1); 42 | %var.Dt = D(2); 43 | var.Dx = D(1)-1; 44 | var.Dt = D(2)-1; 45 | 46 | var.harX = length(wts{1}); 47 | var.harT = length(wts{2}); 48 | 49 | % Create subdomain 50 | var.x = linspace(-1,1,var.Dx+1); 51 | var.t = linspace(-1,1,var.Dt+1); 52 | 53 | % Define variable conversions 54 | S_x = 2/(dx*var.Dx); 55 | S_t = 2/(dt*var.Dt); 56 | 57 | % Set random number generator state 58 | if nargin>7 59 | rng(seed); 60 | end 61 | 62 | % Time sampling scheme 63 | P = zeros(2,N_d); 64 | P(1,:) = randi([1,Lx-var.Dx],N_d,1); 65 | P(2,:) = randi([1,Lt-var.Dt],N_d,1); 66 | 67 | % Initialize Target and Library 68 | Q = zeros(N_d*var.harX*var.harT,10); %4 69 | 70 | %% FILL TERMS 71 | 72 | n_lib = 0; 73 | n_track = 10; 74 | 75 | U_full = traj.uu; 76 | 77 | % Add noise to u 78 | if sig > 0 79 | U_full = U_full + sig*std(U_full(:))*randn(size(U_full)); % Gaussian 80 | %U_full = U_full + sig*std(U_full(:))*sqrt(3)*(-1+2.*rand(size(U_full))); % uniform 81 | end 82 | 83 | for i = 1:length(wts{1}) 84 | for j = 1:length(wts{2}) 85 | 86 | end 87 | end 88 | 89 | % Compute each library row 90 | for i = 1:length(wts{1}) 91 | for j = 1:length(wts{2}) 92 | % Pre-compute weight functions and their derivatives 93 | ox = wts{1}(i); 94 | ot = wts{2}(j); 95 | dA00{i}{j} = weight_full([0,0],F,[ox,ot]); 96 | dA01{i}{j} = weight_full([0,1],F,[ox,ot]); 97 | dA10{i}{j} = weight_full([1,0],F,[ox,ot]); 98 | dA20{i}{j} = weight_full([2,0],F,[ox,ot]); 99 | dA40{i}{j} = weight_full([4,0],F,[ox,ot]); 100 | dA30{i}{j} = weight_full([3,0],F,[ox,ot]); 101 | 102 | for np = 1:N_d 103 | 104 | n_lib = n_lib + 1; 105 | 106 | % Output 107 | if if_track && n_lib == n_track 108 | if n_lib < 100 109 | disp(['Library Row # : ',num2str(n_lib)]) 110 | n_track = n_track + 10; 111 | elseif n_lib < 1000 112 | disp(['Library Row # : ',num2str(n_lib)]) 113 | n_track = n_track + 100; 114 | else 115 | disp(['Library Row # : ',num2str(n_lib)]) 116 | n_track = n_track + 1000; 117 | end 118 | end 119 | 120 | % Indices for integration domain 121 | rx = P(1,np):(P(1,np)+var.Dx); 122 | rt = P(2,np):(P(2,np)+var.Dt); 123 | 124 | % u field on integration domain 125 | U = U_full(rx,rt); 126 | 127 | % Time Derivative Term 128 | B = -U.*dA01{i}{j}*S_t; 129 | 130 | Q(n_lib,1) = trapz(var.x,trapz(var.t,B,2),1); 131 | 132 | % Advection Term 133 | th1 = -(1/2)*U.^2.*dA10{i}{j}*S_x; 134 | Q(n_lib,2) = trapz(var.x,trapz(var.t,th1,2),1); 135 | 136 | % Laplacian Term 137 | th2 = U.*dA20{i}{j}*S_x^2; 138 | Q(n_lib,3) = trapz(var.x,trapz(var.t,th2,2),1); 139 | 140 | % Biharmonic Term 141 | th3 = U.*dA40{i}{j}*S_x^4; 142 | Q(n_lib,4) = trapz(var.x,trapz(var.t,th3,2),1); 143 | 144 | % Linear Term 145 | th4 = U.*dA00{i}{j}; 146 | Q(n_lib,5) = trapz(var.x,trapz(var.t,th4,2),1); 147 | 148 | % First Order Derivative 149 | th5 = -U.*dA10{i}{j}*S_x; 150 | Q(n_lib,6) = trapz(var.x,trapz(var.t,th5,2),1); 151 | 152 | % Third Order Derivative 153 | th6 = -U.*dA30{i}{j}*S_x^3; 154 | Q(n_lib,7) = trapz(var.x,trapz(var.t,th6,2),1); 155 | 156 | % Quadratic Term 157 | th7 = U.^2.*dA00{i}{j}; 158 | Q(n_lib,8) = trapz(var.x,trapz(var.t,th7,2),1); 159 | 160 | % Cubic Term 161 | th8 = U.^3.*dA00{i}{j}; 162 | Q(n_lib,9) = trapz(var.x,trapz(var.t,th8,2),1); 163 | 164 | % Constant Term 165 | th9 = dA00{i}{j}; 166 | Q(n_lib,10) = trapz(var.x,trapz(var.t,th9,2),1); 167 | 168 | end 169 | 170 | end 171 | end 172 | 173 | % Sparsification parameter 174 | gamma = 1.4; 175 | 176 | % Model parameters 177 | ksi = find_coeffs(Q(:,:),gamma); 178 | 179 | % Normalize with respect to du/dt term 180 | if ksi(1)~=0 181 | ksi = ksi/ksi(1); 182 | end 183 | 184 | % Compute residual (relative) 185 | res = norm(Q(:,:)*ksi)/norm(Q(:,:))/norm(ksi); 186 | 187 | end -------------------------------------------------------------------------------- /NatComm2021/ParEst_WF_LambdaOmegaRD.m: -------------------------------------------------------------------------------- 1 | %%%% Lambda-Omega RD Weak Formulation System Identification %%%%% 2 | function [ksi,res,Q,q0] = ParEst_WF_LambdaOmegaRD(filename,N_d,D,if_track) 3 | %{ 4 | INPUTS: 5 | ------- 6 | filename -- string containing path to matfile w/ data 7 | N_d -- number of integration domains to saple 8 | D = [Dx,Dt] -- size of integration domain in space/time 9 | if_track -- bool for showing progress in command window 10 | 11 | OUTPUTS: 12 | -------- 13 | ksi -- non dimensional estimated parameters 14 | res -- residual = mean(abs(Q*ksi-q0)) 15 | Q -- Assembled library 16 | q0 -- time derivative term 17 | %} 18 | 19 | %% INITIALIZE 20 | % Define matfile 21 | traj = matfile(filename); 22 | 23 | % Get size of velocity fields 24 | [~,Lx,Lt] = size(traj,'U_t'); 25 | 26 | % Grid densities 27 | dt = traj.dt; 28 | dx = traj.dx; 29 | 30 | % % Size of local domain 31 | clearvars -global 32 | Dx = D(1); 33 | Dt = D(2); 34 | 35 | % Create subdomain 36 | x = linspace(-1,1,Dx+1); 37 | t = linspace(-1,1,Dt+1); 38 | 39 | % Define variable conversions 40 | S_x = 2/(dx*Dx); 41 | S_t = 2/(dt*Dt); 42 | 43 | % Initialize integration domain location 44 | P = zeros(3,1); 45 | 46 | % Initialize Target and Library 47 | q0 = zeros(N_d,2); 48 | Q = zeros(length(q0),10,2); 49 | 50 | %% FILL TERMS 51 | n_lib = 0; 52 | n_track = 10; 53 | 54 | % Pre-make derivatives of weight functions 55 | p = [2,2,1]; % weight function exponents 56 | dA000 = weight_full([0,0,0],p,x,t); 57 | dA001 = weight_full([0,0,1],p,x,t); 58 | dA200 = weight_full([2,0,0],p,x,t); 59 | dA020 = weight_full([0,2,0],p,x,t); 60 | 61 | for np = 1:N_d 62 | 63 | n_lib = n_lib + 1; 64 | 65 | if if_track && n_lib == n_track 66 | if n_lib < 100 67 | disp(['Library Row # : ',num2str(n_lib)]) 68 | n_track = n_track + 10; 69 | elseif n_lib < 1000 70 | disp(['Library Row # : ',num2str(n_lib)]) 71 | n_track = n_track + 100; 72 | else 73 | disp(['Library Row # : ',num2str(n_lib)]) 74 | n_track = n_track + 1000; 75 | end 76 | end 77 | 78 | % Choose random point 79 | P(1) = randi([1,Lx-Dx]); 80 | P(2) = randi([1,Lx-Dx]); 81 | P(3) = randi([1,Lt-Dt]); 82 | 83 | % Indices for integration domain 84 | rx = P(1):(P(1) + Dx); 85 | ry = P(2):(P(2) + Dx); 86 | rt = P(3):(P(3) + Dt); 87 | 88 | % U in integration domain 89 | U = traj.U_t(ry,rx,rt); 90 | V = traj.V_t(ry,rx,rt); 91 | 92 | %%%%% U %%%%% 93 | % du/dt 94 | B = -U.*dA001*S_t; 95 | q0(n_lib,1) = trapz(x,trapz(x,trapz(t,B,3),2),1); 96 | 97 | % \nabla^2 u 98 | th1 = U.*(dA020 + dA200)*S_x^2; 99 | Q(n_lib,1,1) = trapz(x,trapz(x,trapz(t,th1,3),2),1); 100 | 101 | % u 102 | th2 = U.*dA000; 103 | Q(n_lib,2,1) = trapz(x,trapz(x,trapz(t,th2,3),2),1); 104 | 105 | % u^2 106 | th3 = U.^2.*dA000; 107 | Q(n_lib,3,1) = trapz(x,trapz(x,trapz(t,th3,3),2),1); 108 | 109 | % u^3 110 | th4 = U.^3.*dA000; 111 | Q(n_lib,4,1) = trapz(x,trapz(x,trapz(t,th4,3),2),1); 112 | 113 | % v 114 | th5 = V.*dA000; 115 | Q(n_lib,5,1) = trapz(x,trapz(x,trapz(t,th5,3),2),1); 116 | 117 | % v^2 118 | th6 = V.^2.*dA000; 119 | Q(n_lib,6,1) = trapz(x,trapz(x,trapz(t,th6,3),2),1); 120 | 121 | % v^3 122 | th7 = V.^3.*dA000; 123 | Q(n_lib,7,1) = trapz(x,trapz(x,trapz(t,th7,3),2),1); 124 | 125 | % uv 126 | th8 = U.*V.*dA000; 127 | Q(n_lib,8,1) = trapz(x,trapz(x,trapz(t,th8,3),2),1); 128 | 129 | % u^2v 130 | th9 = U.^2.*V.*dA000; 131 | Q(n_lib,9,1) = trapz(x,trapz(x,trapz(t,th9,3),2),1); 132 | 133 | % uv^2 134 | th10 = U.*V.^2.*dA000; 135 | Q(n_lib,10,1) = trapz(x,trapz(x,trapz(t,th10,3),2),1); 136 | 137 | %%%%% V %%%%% 138 | % dv/dt 139 | B = -V.*dA001*S_t; 140 | q0(n_lib,2) = trapz(x,trapz(x,trapz(t,B,3),2),1); 141 | 142 | % \nabla^2 v 143 | th1 = V.*(dA020 + dA200)*S_x^2; 144 | Q(n_lib,1,2) = trapz(x,trapz(x,trapz(t,th1,3),2),1); 145 | 146 | % u 147 | th2 = U.*dA000; 148 | Q(n_lib,2,2) = trapz(x,trapz(x,trapz(t,th2,3),2),1); 149 | 150 | % u^2 151 | th3 = U.^2.*dA000; 152 | Q(n_lib,3,2) = trapz(x,trapz(x,trapz(t,th3,3),2),1); 153 | 154 | % u^3 155 | th4 = U.^3.*dA000; 156 | Q(n_lib,4,2) = trapz(x,trapz(x,trapz(t,th4,3),2),1); 157 | 158 | % v 159 | th5 = V.*dA000; 160 | Q(n_lib,5,2) = trapz(x,trapz(x,trapz(t,th5,3),2),1); 161 | 162 | % v^2 163 | th6 = V.^2.*dA000; 164 | Q(n_lib,6,2) = trapz(x,trapz(x,trapz(t,th6,3),2),1); 165 | 166 | % v^3 167 | th7 = V.^3.*dA000; 168 | Q(n_lib,7,2) = trapz(x,trapz(x,trapz(t,th7,3),2),1); 169 | 170 | % uv 171 | th8 = U.*V.*dA000; 172 | Q(n_lib,8,2) = trapz(x,trapz(x,trapz(t,th8,3),2),1); 173 | 174 | % u^2v 175 | th9 = U.^2.*V.*dA000; 176 | Q(n_lib,9,2) = trapz(x,trapz(x,trapz(t,th9,3),2),1); 177 | 178 | % uv^2 179 | th10 = U.*V.^2.*dA000; 180 | Q(n_lib,10,2) = trapz(x,trapz(x,trapz(t,th10,3),2),1); 181 | 182 | end % np 183 | 184 | %% REGRESSION 185 | % Parameters 186 | ksi_u = SINDy(Q(:,:,1), q0(:,1)); 187 | ksi_v = SINDy(Q(:,:,2), q0(:,2)); 188 | 189 | fields = ["Laplacian";"u";"u2";"u3";"v";"v2";"v3";"uv";"u2v";"uv2"]; 190 | 191 | ksi.u = cell2struct(num2cell(ksi_u),fields); 192 | ksi.v = cell2struct(num2cell(ksi_v),fields); 193 | 194 | % How good was the estimation 195 | res_u = mean(abs(q0(:,1) - Q(:,:,1)*ksi_u)); 196 | res_v = mean(abs(q0(:,2) - Q(:,:,2)*ksi_v)); 197 | 198 | res = [res_u, res_v]; 199 | 200 | end 201 | 202 | -------------------------------------------------------------------------------- /NatComm2021/ParEst_WF_Kolmo.m: -------------------------------------------------------------------------------- 1 | %%%% Integral Method Q2D-NS Parameter Estimation %%%% 2 | %% ---------------------------------------------------------------------- 3 | % PREAMBLE 4 | % ----------------------------------------------------------------------- 5 | %{ 6 | INPUTS 7 | ------ 8 | filename : string for path to -v7.3 .mat file containing data 9 | - U_t,V_t : non-dim velocity fields 10 | - dx : non-dim spatial grid spacing 11 | - dt_mks : dimensional temporal grid spacing (seconds) 12 | - Ts_mks : time-scale in seconds 13 | N_d : number of integration domains 14 | N_h = [Q R S] 15 | - S : max harmonic in time (>=1) 16 | - Q,R : max harmonic in x,y resp. (>=0) 17 | D = [fr, Dt] 18 | - fr : spatial-area of int domain as fraction of full area 19 | - Dt : time-length of integration domain in grid points 20 | track : boolean to track library filling in cmd window 21 | 22 | OUTPUTS 23 | ------- 24 | ksi : estimated parameters in non-dimensinoal units 25 | res : residual of estimation (utility unclear) 26 | Q : matrix containing integral evaluations of gov eq terms 27 | q0 : vector containing integral evaluation of time-derivative term 28 | P : 3xN_d matrix containing integration domain locations 29 | 30 | SECTIONS 31 | -------- 32 | Initialize 33 | -size of local domain 34 | -how many harmonics to sample 35 | -any tracking variables 36 | -library and target 37 | 38 | Construct Library 39 | -advection term 40 | -laplacian term 41 | -rayleigh term 42 | -time-derivative term 43 | 44 | Regression 45 | -invert library onto target or use SINDy 46 | -calculate norm of b-\Theta\xi 47 | 48 | Reminders 49 | -disp to-do list 50 | 51 | FUNCTIONS 52 | --------- 53 | Construct 3D Weight Function 54 | - use General Liebniz Rule to find the k^th oder product rule of base 55 | weight and harmonics 56 | - use meshgrid to combine 3 1D composite fields into final 3D weight 57 | function 58 | 59 | Construct 1D Base Weight Function 60 | - find base poly coefficients based on exponent m 61 | - convert poly coefficients based on derivative order k 62 | - compute full polynomial 63 | 64 | Construct 1D Weight harmonics 65 | - Calculate derivatives of sin(pi*q*x) in 1D 66 | 67 | %} 68 | %% 69 | function [ksi,res,Q,q0,P] = ParEst_WF_Kolmo(filename,N_d,N_h,D,track) 70 | %% ---------------------------------------------------------------------- 71 | % INITIALIZE 72 | % ----------------------------------------------------------------------- 73 | % Define matfile 74 | traj = matfile(filename); 75 | 76 | % Get size of velocity fields 77 | [Ly,Lx,Lt] = size(traj,'U_t'); 78 | 79 | % Grid densities 80 | dt = traj.dt; 81 | dx = traj.dx; 82 | 83 | % Size of local domain 84 | fr = D(1); 85 | Dx = round(fr*Lx); % sizes of the integration domain 86 | Dy = round(fr*Lx); 87 | Dt = D(2); 88 | 89 | % Sampling scheme 90 | P = zeros(3,N_d); % Starting corner of integration domain 91 | P(1,:) = randi([1,Lx-Dx],N_d,1); 92 | P(2,:) = randi([1,Ly-Dy],N_d,1); 93 | P(3,:) = randi([1,Lt-Dt],N_d,1); 94 | 95 | % Define derivative conversions 96 | S_x = 2/(dx*Dx); 97 | S_y = 2/(dx*Dy); 98 | S_t = 2/(dt*Dt); 99 | 100 | % Create subdomain 101 | x = linspace(-1,1,Dx); 102 | y = linspace(-1,1,Dy); 103 | t = linspace(-1,1,Dt); 104 | 105 | % Initialize Target and Library 106 | q0 = zeros(N_d*(N_h(1)+1)*(N_h(2)+1)*(N_h(3)+1),1); 107 | q1 = q0; q2 = q0; q3 = q0; 108 | 109 | %% ---------------------------------------------------------------------- 110 | % CONSTRUCT LIBRARY 111 | % ----------------------------------------------------------------------- 112 | n_lib = 0; 113 | n_track = 10; 114 | p = [3,3,0]; % weight function exponents 115 | 116 | for np = 1:N_d 117 | 118 | % Indices for integration domain 119 | rx = P(1,np):(P(1,np)+Dx-1); 120 | ry = P(2,np):(P(2,np)+Dy-1); 121 | rt = P(3,np):(P(3,np)+Dt-1); 122 | 123 | % Velocity fields on integration domain 124 | U = traj.U_t(ry,rx,rt); 125 | V = traj.V_t(ry,rx,rt); 126 | 127 | for q = 0:N_h(1) 128 | for r = 0:N_h(2) 129 | for s = 1:N_h(3) 130 | 131 | n_lib = n_lib + 1; 132 | 133 | if track && n_lib == n_track 134 | if n_lib < 100 135 | disp(['Library Row # : ',num2str(n_lib)]) 136 | n_track = n_track + 10; 137 | elseif n_lib < 1000 138 | disp(['Library Row # : ',num2str(n_lib)]) 139 | n_track = n_track + 100; 140 | else 141 | disp(['Library Row # : ',num2str(n_lib)]) 142 | n_track = n_track + 1000; 143 | end 144 | end 145 | 146 | % Make wave numbers global for use in weight_full() 147 | q_vec = [q,r,s]; 148 | 149 | % Make derivatives of windowing functions 150 | dA100 = weight_full_harm([1,0,0],p,q_vec,x,y,t); 151 | dA010 = weight_full_harm([0,1,0],p,q_vec,x,y,t); 152 | dA101 = weight_full_harm([1,0,1],p,q_vec,x,y,t); 153 | dA011 = weight_full_harm([0,1,1],p,q_vec,x,y,t); 154 | dA110 = weight_full_harm([1,1,0],p,q_vec,x,y,t); 155 | dA020 = weight_full_harm([0,2,0],p,q_vec,x,y,t); 156 | dA200 = weight_full_harm([2,0,0],p,q_vec,x,y,t); 157 | dA210 = weight_full_harm([2,1,0],p,q_vec,x,y,t); 158 | dA120 = weight_full_harm([1,2,0],p,q_vec,x,y,t); 159 | dA030 = weight_full_harm([0,3,0],p,q_vec,x,y,t); 160 | dA300 = weight_full_harm([3,0,0],p,q_vec,x,y,t); 161 | % --> the 3 digits correspond to derivatives, NOT to harmonics 162 | 163 | % Time-derivative 164 | b = (V.*dA101*S_x - U.*dA011*S_y)*S_t; 165 | q0(n_lib,1) = trapz(x,trapz(y,trapz(t,b,3))); 166 | 167 | % Advection Term (incompressible) 168 | th1 = U.*V.*(dA020*S_y^2 - dA200*S_x^2) + ... 169 | (U.^2 - V.^2).*dA110*S_x*S_y; 170 | q1(n_lib,1) = trapz(x,trapz(y,trapz(t,th1,3))); 171 | 172 | % Laplacian Term 173 | th2 = U.*(dA210*S_x^2*S_y + dA030*S_y^3) - ... 174 | V.*(dA300*S_x^3 + dA120*S_x*S_y^2); 175 | q2(n_lib,1) = trapz(x,trapz(y,trapz(t,th2,3))); 176 | 177 | % Rayleigh Term 178 | th3 = V.*dA100*S_x - U.*dA010*S_y; 179 | q3(n_lib,1) = trapz(x,trapz(y,trapz(t,th3,3))); 180 | 181 | end % s 182 | end % r 183 | end % q 184 | end % np 185 | 186 | % Assemble Library 187 | Q = [q1 q2 q3]; 188 | 189 | %% ---------------------------------------------------------------------- 190 | % REGRESSION 191 | % ----------------------------------------------------------------------- 192 | % Compute parameters 193 | ksi = Q \ q0; 194 | 195 | % Compute residual 196 | res = mean(abs(q0 - Q*ksi)); 197 | 198 | end 199 | -------------------------------------------------------------------------------- /Time_Integrators/initialise_sim_params_fdso_np.m: -------------------------------------------------------------------------------- 1 | function initialise_sim_params_fdso_np(I_mks,dt_mks,n,monopole_sense) 2 | %% The code was originally written by Balachandra Suri 3 | %{ 4 | 5 | INPUT: 6 | ------ 7 | I_mks - Driving current in Amps 8 | dt_mks - Integration time-step 9 | n - grid points per unit length (grid spacing: dx = 1/n) 10 | monopole_sense - forcing profile orientation 11 | 12 | OUTPUT: 13 | ------- 14 | Directory containing necessary files for integrator to function properly 15 | 16 | To recreate PNAS data set: 17 | I_mks = 19.5/1000 18 | dt_mks = 1/32 19 | n = 40 (and then sub-sampled to 10 post creation) 20 | monopole_sense = 'ccw' 21 | 22 | NOTE: Using n = 20 subsampled down to n = 10 should yield comparable 23 | results as the n = 40 sub-sampled used in PNAS paper. Use n = 20 for faster 24 | results. 25 | 26 | %} 27 | %% 28 | disp('Initialising Simulation Parameters') 29 | 30 | % Load forcing profile 31 | if n == 20 32 | load('forcing_profile_20.mat','F0x') 33 | elseif n == 40 34 | load('forcing_profile_40.mat','F0x') 35 | else 36 | error(['No forcing profile for the grid spacing n = ',num2str(n)]) 37 | end 38 | 39 | % Create directory for desired forcing current 40 | dir_name = sprintf('%05.2fmA',I_mks*1000); 41 | if (~exist(dir_name,'dir')) 42 | mkdir(dir_name); 43 | end 44 | cd(dir_name); 45 | 46 | 47 | %% Experimental Parameters Specification in MKS 48 | % Constants 49 | inch = 2.54/100; %since we use inch a lot in our length scales 50 | 51 | % Fluid Properties 52 | rho_mks = 959.4; % Depth Averaged Density 53 | mu_mks = 0.0031; % effective dynamic viscosity 54 | nu_mks = mu_mks/rho_mks; % kinematic Viscosity 55 | rayfric_mks = 0.0645; % Rayleigh friction 56 | advpf = 0.826; % advection prefactor 57 | 58 | % here we specitfy the number of magnet pairs 59 | Nmp = 7; 60 | 61 | % Basic length scales of the domain 62 | % width of the aluminium box; 63 | Lx_mks = 6*inch + 1*inch; 64 | % height of aluminium box 65 | Ly_mks = Nmp*inch + 2*inch; 66 | % length scale of forcing 67 | L0y_mks = 1/2*inch; 68 | 69 | %% here we set the sense of monopole for global circulation 70 | if(strcmp(monopole_sense,'ccw')) 71 | sense = +1; 72 | elseif(strcmp(monopole_sense,'cw')) 73 | sense = -1; 74 | end 75 | %% define length, velocity and time scales 76 | % F_mks = get_forcing_info_np(I_mks,sense,'amp'); 77 | % A = F_mks/rho_mks; 78 | A = compute_A(I_mks,rho_mks); 79 | Ls_mks = L0y_mks; 80 | Us_mks = sqrt(A*Ls_mks); 81 | Ts_mks = Ls_mks/Us_mks; 82 | 83 | %% Non Dimensionalization 84 | % Length scales 85 | 86 | % length of box 87 | Lx = Lx_mks/Ls_mks; 88 | % height of box 89 | Ly = Ly_mks/Ls_mks; 90 | 91 | % Non dimensional simulation parameters 92 | % viscous Reynolds number 93 | Re_nu = Us_mks*Ls_mks/nu_mks; 94 | % friction Reynolds Number 95 | Re_rf = Us_mks/Ls_mks/rayfric_mks; 96 | % non-dimensional time 97 | dt = dt_mks/Ts_mks; 98 | 99 | %% Defining a Grid 100 | 101 | % Define the x and y axes. 102 | % the number of intervals we define a unit length into 103 | n0 = n; 104 | % bins along x direction 105 | nx = round(Lx*n0); 106 | % bins along y direction 107 | ny = round(Ly*n0); 108 | % x axis coordinates 109 | x = linspace(0,Lx,nx+1); 110 | % y axis coordinates 111 | y = linspace(0,Ly,ny+1); 112 | dx = Lx/nx; 113 | dy = Ly/ny; 114 | 115 | [X,Y] = meshgrid(x,y); 116 | 117 | % Define the coordinates of grid , i.e xu, xv etc. 118 | 119 | xu = linspace(0,Lx,nx+1); 120 | yu = linspace(-dy/2,Ly+dy/2,ny+2); 121 | 122 | xv = linspace(-dx/2,Lx+dx/2,nx+2); 123 | yv = linspace(0,Ly,ny+1); 124 | 125 | xp = linspace(-dx/2,Lx+dx/2,nx+2); 126 | yp = linspace(-dy/2,Ly+dy/2,ny+2); 127 | 128 | % Define U, V and P matrices: Using interior points only 129 | 130 | U = zeros(ny,nx-1); 131 | V = zeros(ny-1,nx); 132 | P = zeros(ny,nx); 133 | 134 | % Here we save the size of each matrix 135 | sizeU = size(U); 136 | sizeV = size(V); 137 | sizeP = size(P); 138 | 139 | % Define the extended matrices: Including boundary points 140 | 141 | Ue = zeros(ny+2,nx+1); 142 | Ve = zeros(ny+1,nx+2); 143 | 144 | %% Laplacian Definitions 145 | LapU = compute_Laplacian(ny,nx-1,-2,-3,dx,dy); 146 | LapV = compute_Laplacian(ny-1,nx,-3,-2,dx,dy); 147 | LapP = compute_Laplacian(ny,nx,-1,-1,dx,dy); 148 | 149 | %% Crank Nicholson LHS matrices 150 | 151 | LHSU = (1+1/2*dt/Re_rf)*speye(size(LapU)) - 1/2*dt/Re_nu*LapU; 152 | LHSV = (1+1/2*dt/Re_rf)*speye(size(LapV)) - 1/2*dt/Re_nu*LapV; 153 | LHSP = -LapP; % the negative sign is necessary for cholsky decomposition 154 | LHSP(1,1) = 3/2*LHSP(1,1); 155 | 156 | 157 | %% Rearrange the rows and columns to maximize sparse zeros 158 | 159 | permU = symamd(LHSU); 160 | permV = symamd(LHSV); 161 | permP = symamd(LHSP); 162 | 163 | %% We perform the Cholskey decomposirion here 164 | 165 | UTcholU = chol(LHSU(permU,permU)); 166 | LTcholU = UTcholU'; 167 | UTcholV = chol(LHSV(permV,permV)); 168 | LTcholV = UTcholV'; 169 | UTcholP = chol(LHSP(permP,permP)); 170 | LTcholP = UTcholP'; 171 | 172 | save('sim_params.mat','Nmp','I_mks','dt_mks','advpf','rayfric_mks','nu_mks','dt',... 173 | 'A','Ls_mks','Us_mks','Ts_mks','Re_nu','Re_rf','monopole_sense'); 174 | save('grid_params.mat','Lx','Ly','Ls_mks','n0','nx','ny','dx','dy','dt','sizeU','sizeV','sizeP',... 175 | 'x','y','X','Y','xu','xv','yu','yv','xp','yp'); 176 | save('differential_ops.mat','U','V','P','Ue','Ve','permU','permV','permP','LapU','LapV','LapP',... 177 | 'UTcholU','UTcholV','UTcholP','LTcholU','LTcholV','LTcholP'); 178 | 179 | save('differential_ops.mat','F0x','-append'); 180 | fprintf(' Simulation Parameters\n nu = %05.02f cSt. \n rayfric = %05.3f 1/s \n advpf = %05.3f',nu_mks*10E5,rayfric_mks,advpf); 181 | pause(3); 182 | clear 183 | cd .. 184 | end 185 | %% In this function we compute the laplacian matrices 186 | function [Laplacian] = compute_Laplacian(nrows,ncols,xbcval,ybcval,dx,dy) 187 | % here we compute the x derivate part of lapacian 188 | % we create a tri diagonal sparse matrix with coefficients for one column 189 | d2Qdx2 = spdiags(ones(ncols,1)*[1, -2, 1],[-1,0,1],ncols,ncols); 190 | % we use implicit boundary conditions here, and hence this requires setting 191 | % the end of the diagonal elements to match boundary conditions 192 | d2Qdx2(1,1) = xbcval; d2Qdx2(ncols,ncols) = xbcval; 193 | % here we create the full matrix for as many rows and columns 194 | d2Qdx2 = (1/dx^2)*kron(d2Qdx2,speye(nrows)); 195 | 196 | % here we compute the y derivate part of lapacian 197 | % we create a tri diagonal sparse matrix with coefficients for one row 198 | d2Qdy2 = spdiags(ones(nrows,1)*[1, -2, 1],[-1,0,1],nrows,nrows); 199 | d2Qdy2(1,1) = ybcval; d2Qdy2(nrows,nrows) = ybcval; 200 | d2Qdy2 = (1/dy^2)*kron(speye(ncols),d2Qdy2); 201 | 202 | Laplacian = d2Qdx2 + d2Qdy2; 203 | end 204 | %% RAVI's computation of A 205 | function A = compute_A(I_mks,rho_mks) 206 | inch = 2.54/100; 207 | hc = 0.003; 208 | hd = 0.003; 209 | B0 = .2515-25.71*0.9e-3; 210 | B1 = -25.71; 211 | Le = (6.0*inch+1.0*inch); 212 | B_mean = (B0 + B1*hd + B1*hc/2)*hc/(hc+hd); 213 | J = I_mks/Le/hc; 214 | A = B_mean*J*(1.0/rho_mks); 215 | end 216 | -------------------------------------------------------------------------------- /Time_Integrators/integrate_2dns_fdso_np.m: -------------------------------------------------------------------------------- 1 | function integrate_2dns_fdso_np(I_mks,ti_mks,tf_mks,if_continue,save_for_rec,save_rate,if_plot) 2 | %{ 3 | The code was written by Balachandra Suri 4 | Crank Nicholson for the Linear term 5 | 2nd order Adam's Bashforth for the NonLinear term 6 | %} 7 | 8 | %% Input Arguments 9 | % ti_mks is the initial time 10 | % tf_mks is how long we want to simulate for, usually 1000 seconds 11 | % if_continue 0: not continuing 1:continuing from previous data 12 | % save_for_rec: 0: don't save along the way 1: save 13 | % save_rate: times per second to save 14 | % if_plot: 0:don't plot 1: plot results 15 | 16 | % SAMPLE: integrate_2dns_fdso_np(2000,2100,1,1,4,0) 17 | % -> start from t=2000s from previous run, and continue until 2100s, saving 18 | % every 1/4 s. 19 | 20 | %% Some matlab commands to clear display etc... 21 | clc 22 | close all 23 | pause(2); 24 | 25 | %% Check directory for desired current 26 | 27 | dir_name = sprintf('%05.2fmA',I_mks*1000); 28 | if (~exist(dir_name,'dir')) 29 | error(['Directory, ' dir_name ', does not exist. Initialize with' ... 30 | ' initialise_sim_params_fdso_np.m']) 31 | end 32 | cd(dir_name) 33 | 34 | %% Here we initialise some variables 35 | U = []; V = []; P = []; F0x = []; X = []; Y = []; omega_t = []; dt = []; 36 | U_t = []; V_t = []; P_t = []; 37 | 38 | %% here we load sim_params.mat 39 | if(if_continue == 1) 40 | file_name = input('enter file name: ','s'); 41 | load(file_name,'U','V','P'); 42 | % we set the arbitraty constant on pressure to zero 43 | P = P-mean(P(:)); 44 | elseif(if_continue == 0) 45 | display('starting a simulation from random initial conditions'); 46 | load('differential_ops.mat', 'U','V','P'); 47 | P = P-mean(P(:)); 48 | end 49 | load('sim_params.mat','Ts_mks','dt_mks','advpf','Ls_mks','Us_mks','Re_nu','Re_rf','I_mks'); 50 | dt = dt_mks/Ts_mks; 51 | load('grid_params.mat','dx','dy','X','Y','Ly','Lx'); 52 | x = 0:dx:Lx; 53 | y = 0:dy:Ly; 54 | load('differential_ops.mat','Ue','Ve','permU','permV','permP',... 55 | 'UTcholU','UTcholV','UTcholP','LTcholU','LTcholV','LTcholP','F0x'); 56 | 57 | %% Here we set the time variable for integration 58 | nt = round((tf_mks-ti_mks)/dt_mks); 59 | tf = (ti_mks+nt*dt_mks)/Ts_mks; 60 | 61 | %% Here we initialise some counter variables 62 | gamma_cnt = 0; 63 | % rate at which we want toe vorticity field to be saved 64 | omega_save_fps = save_rate; 65 | % total number of omega that are to be saved 66 | omega_save_n = round(tf_mks-ti_mks)*omega_save_fps; 67 | % one in how many omega do we intend to save. in terms of numbers 68 | omega_save_dn = round(1/dt_mks/omega_save_fps); 69 | % lets us know how many omegas have been saved 70 | omega_save_cnt = 0; 71 | % marks when is the next instant to save omega 72 | omega_save_next = omega_save_dn; 73 | 74 | %% Allocate Additional memory for temporary variables 75 | u = zeros(size(U(:))); 76 | v = zeros(size(V(:))); 77 | phi = zeros(size(P(:))); 78 | Phie = zeros(size(P)+[2,2]); 79 | 80 | [lU,wU] = size(U); 81 | [lV,wV] = size(V); 82 | [lP,wP] = size(P); 83 | 84 | if(save_for_rec) 85 | omega_t = zeros(round(Ly/dy)+1,round(Lx/dx)+1,omega_save_n); 86 | U_t = zeros(lU,wU,omega_save_n); 87 | V_t = zeros(lV,wV,omega_save_n); 88 | P_t = zeros(lP,wP,omega_save_n); 89 | end 90 | rms_u = zeros(nt,1); 91 | rms_v = zeros(nt,1); 92 | err_u = zeros(nt,1); 93 | err_v = zeros(nt,1); 94 | gamma_arr = zeros(nt,1); 95 | 96 | %% This is the loop where we step forward in time 97 | 98 | for k = 1:nt 99 | 100 | %% Extend the Matrices to include boundary points too 101 | % we read the interior values into the extended matrices 102 | Ue(2:end-1,2:end-1) = U; 103 | Ve(2:end-1,2:end-1) = V; 104 | 105 | % we now impose the boundary conditions on Ue using boundary points 106 | Ue(:,1) = 0; 107 | Ue(:,end) = 0; 108 | Ue(1,:) = -Ue(2,:); 109 | Ue(end,:) = -Ue(end-1,:); 110 | 111 | % we now impose the boundary conditions on Ve using boundary points 112 | Ve(:,1) = -Ve(:,2); 113 | Ve(:,end) = -Ve(:,end-1); 114 | Ve(1,:) = 0; 115 | Ve(end,:) = 0; 116 | 117 | %% here we choose the transaition parameter for hybrid difference 118 | % set gamma = 0 for central difference 119 | % gamma = min(1.2*dt*max([max(abs(U(:)))/dx,max(abs(V(:)))/dy]),1); 120 | gamma = 0; 121 | if(gamma==0 && gamma_cnt == 0) 122 | disp('Using a central difference scheme for NLT'); 123 | gamma_cnt = 1; 124 | elseif(gamma_cnt == 0) 125 | disp('Using a hybrid difference scheme for NLT'); 126 | gamma_cnt = 1; 127 | end 128 | gamma_arr(k) = gamma; 129 | 130 | %% Here we compute the Nonlinear terms for U,V 131 | Uax = (Ue(:,2:end)+Ue(:,1:end-1))/2; 132 | Uay = (Ue(2:end,:)+Ue(1:end-1,:))/2; 133 | Vax = (Ve(:,2:end)+Ve(:,1:end-1))/2; 134 | Vay = (Ve(2:end,:)+Ve(1:end-1,:))/2; 135 | 136 | % here we compute the x and y gradients 137 | dUx = (Ue(:,2:end)-Ue(:,1:end-1))/2; 138 | dUy = (Ue(2:end,:)-Ue(1:end-1,:))/2; 139 | dVx = (Ve(:,2:end)-Ve(:,1:end-1))/2; 140 | dVy = (Ve(2:end,:)-Ve(1:end-1,:))/2; 141 | 142 | % here we compute the nonlinear terms for U equation 143 | %U advecting U along x 144 | UadvU = Uax.^2 - gamma*abs(Uax).*dUx; 145 | dU2dx = (UadvU(:,2:end) - UadvU(:,1:end-1))/dx; 146 | %V advecting U along y 147 | VadvU = Vax.*Uay - gamma*abs(Vax).*dUy; 148 | dVUdy = (VadvU(2:end,:)-VadvU(1:end-1,:))/dy; 149 | 150 | % add both parts of nltx using interior points only 151 | nltU = advpf*(dU2dx(2:end-1,:) + dVUdy(:,2:end-1)); 152 | 153 | % here we compute the nonlinear terms for V equation 154 | % V advecting V along y 155 | VadvV = Vay.^2 - gamma*abs(Vay).*dVy; 156 | dV2dy = (VadvV(2:end,:) - VadvV(1:end-1,:))/dy; 157 | % U advectng V along x 158 | UadvV = Uay.*Vax - gamma*abs(Uay).*dVx; 159 | dUVdx = (UadvV(:,2:end)-UadvV(:,1:end-1))/dx; 160 | 161 | % add both parts of nltV using only interior points 162 | nltV = advpf*(dV2dy(:,2:end-1) + dUVdx(2:end-1,:)); 163 | 164 | % This would run only for the first Adam Bashforth iteration 165 | % since the estimate for the previous step is not available 166 | if(~exist('nltU_prev','var')) 167 | nltU_prev = nltU; 168 | end 169 | if(~exist('nltV_prev','var')) 170 | nltV_prev = nltV; 171 | end 172 | 173 | %% Here we compute the Linear term, Pressure term and force of RHS 174 | % This is the linear term for the U, V components 175 | 176 | % The following lines compute the terms of laplacian 177 | d2Udx2 = (Ue(2:end-1,3:end)+Ue(2:end-1,1:end-2)-2*Ue(2:end-1,2:end-1))/dx^2; 178 | d2Udy2 = (Ue(3:end,2:end-1)+Ue(1:end-2,2:end-1)-2*Ue(2:end-1,2:end-1))/dy^2; 179 | d2Vdx2 = (Ve(2:end-1,3:end)+Ve(2:end-1,1:end-2)-2*Ve(2:end-1,2:end-1))/dx^2; 180 | d2Vdy2 = (Ve(3:end,2:end-1)+Ve(1:end-2,2:end-1)-2*Ve(2:end-1,2:end-1))/dy^2; 181 | 182 | ltU = 1/Re_nu/2*(d2Udx2(:)+d2Udy2(:)) - 1/Re_rf/2*U(:); 183 | ltV = 1/Re_nu/2*(d2Vdx2(:)+d2Vdy2(:)) - 1/Re_rf/2*V(:); 184 | 185 | if(save_for_rec && k == omega_save_next) 186 | omega_save_cnt = omega_save_cnt+1; 187 | dUdy = (Ue(2:end,:)-Ue(1:end-1,:))/dy; 188 | dVdx = (Ve(:,2:end)-Ve(:,1:end-1))/dx; 189 | % omega_t(:,:,omega_save_cnt) = (dVdx-dUdy)/Ts_mks; 190 | U_t(:,:,omega_save_cnt) = U(:,:); 191 | V_t(:,:,omega_save_cnt) = V(:,:); 192 | P_t(:,:,omega_save_cnt) = P(:,:); 193 | omega_save_next = omega_save_next + omega_save_dn; 194 | end 195 | 196 | % here we compute the Gradient of the pressure 197 | dPdx = (P(:,2:end) - P(:,1:end-1))/dx; 198 | dPdy = (P(2:end,:) - P(1:end-1,:))/dy; 199 | 200 | % here we compute the rhs of both the U,V equations 201 | rhsu = U(:) + dt*(ltU(:) - 3/2*nltU(:) + 1/2*nltU_prev(:) - dPdx(:) + F0x(:)); 202 | rhsv = V(:) + dt*(ltV(:) - 3/2*nltV(:) + 1/2*nltV_prev(:) - dPdy(:)); 203 | 204 | %here we solve the linear equations using matrix inversion 205 | u(permU) = UTcholU\(LTcholU\rhsu(permU)); 206 | v(permV) = UTcholV\(LTcholV\rhsv(permV)); 207 | 208 | % here we reshape the vectors back into matrix form 209 | U = reshape(u,size(U)); 210 | V = reshape(v,size(V)); 211 | 212 | if(isnan(rms(U(:))) || isnan(rms(V(:)))) 213 | display('Simulation diverged to NAN, try reducing dt'); 214 | break 215 | end 216 | %% Here we re-form the extended matrices and impose boundary conditions 217 | % we read the interior values into the extended matrices 218 | Ue(2:end-1,2:end-1) = U; 219 | Ve(2:end-1,2:end-1) = V; 220 | 221 | % we now impose the boundary conditions on Ue using boundary points 222 | Ue(:,1) = 0; 223 | Ue(:,end) = 0; 224 | Ue(1,:) = -Ue(2,:); 225 | Ue(end,:) = -Ue(end-1,:); 226 | 227 | % we now impose the boundary conditions on Ve using boundary points 228 | Ve(:,1) = -Ve(:,2); 229 | Ve(:,end) = -Ve(:,end-1); 230 | Ve(1,:) = 0; 231 | Ve(end,:) = 0; 232 | 233 | %% here we compute the pressure correction, the inverse laplacian of div.Vel 234 | dUdx = (Ue(2:end-1,2:end)-Ue(2:end-1,1:end-1))/dx; 235 | dVdy = (Ve(2:end,2:end-1)-Ve(1:end-1,2:end-1))/dy; 236 | DivVel = dUdx+dVdy; 237 | rhsp = -DivVel(:)/dt; 238 | phi(permP) = UTcholP\(LTcholP\rhsp(permP)); 239 | % This sets the arbitrary constant in pressure computation to zero 240 | Phi = reshape(phi,size(P))-mean(phi(:)); 241 | 242 | % we compute the gradient of the pressure correction 243 | dPhidx = (Phi(:,2:end) - Phi(:,1:end-1))/dx; 244 | dPhidy = (Phi(2:end,:) - Phi(1:end-1,:))/dy; 245 | 246 | % here we compute the pressure correction 247 | Phie(2:end-1,2:end-1) = Phi; 248 | Phie(1,:) = Phie(2,:); 249 | Phie(end,:) = Phie(end-1,:); 250 | Phie(:,1) = Phie(:,2); 251 | Phie(:,end) = Phie(:,end-1); 252 | 253 | % The following lines compute the terms of laplacian of Phi 254 | d2Phidx2 = (Phie(2:end-1,3:end)+Phie(2:end-1,1:end-2)-2*Phie(2:end-1,2:end-1))/dx^2; 255 | d2Phidy2 = (Phie(3:end,2:end-1)+Phie(1:end-2,2:end-1)-2*Phie(2:end-1,2:end-1))/dy^2; 256 | 257 | %% Here we update the final correct velocity and the pressure 258 | U = U - dt*dPhidx; 259 | V = V - dt*dPhidy; 260 | P = P + Phi - dt/2/Re_nu*(d2Phidx2+d2Phidy2) + dt/2/Re_rf*Phi; 261 | 262 | %% here we save the non-linear term computed using old velocity 263 | nltU_prev = nltU; 264 | nltV_prev = nltV; 265 | % we just store the value of the norm to be sure of time dependence 266 | rms_v(k) = rms(V(:)); 267 | rms_u(k) = rms(U(:)); 268 | end 269 | 270 | % we read the interior values into the extrended matrices 271 | Ue(2:end-1,2:end-1) = U; 272 | Ve(2:end-1,2:end-1) = V; 273 | 274 | % we now impose the boundary conditions on Ue 275 | Ue(:,1) = 0; 276 | Ue(:,end) = 0; 277 | Ue(1,:) = -Ue(2,:); 278 | Ue(end,:) = -Ue(end-1,:); 279 | 280 | % we now impose the boundary conditions on Ve 281 | Ve(:,1) = -Ve(:,2); 282 | Ve(:,end) = -Ve(:,end-1); 283 | Ve(1,:) = 0; 284 | Ve(end,:) = 0; 285 | 286 | dUdy = (Ue(2:end,:)-Ue(1:end-1,:))/dy; 287 | dVdx = (Ve(:,2:end)-Ve(:,1:end-1))/dx; 288 | omega = (dVdx-dUdy)/Ts_mks; 289 | 290 | if(save_for_rec && k == omega_save_next) 291 | omega_save_cnt = omega_save_cnt+1; 292 | % omega_t(:,:,omega_save_cnt) = (dVdx-dUdy)/Ts_mks; 293 | U_t(:,:,omega_save_cnt) = Ue; 294 | V_t(:,:,omega_save_cnt) = Ve; 295 | P_t(:,:,omega_save_cnt) = P; 296 | end 297 | 298 | U0 = (Ue(2:end,:)+Ue(1:end-1,:))/2*Us_mks; 299 | V0 = (Ve(:,2:end)+Ve(:,1:end-1))/2*Us_mks; 300 | 301 | dt = save_rate/Ts_mks; % time-step between saved snapshot (non-dim) 302 | dt_int_mks = dt_mks; % integration time-step (seconds) 303 | file_name = sprintf('state_%05.2fmA_%.4d.mat',I_mks*1000,tf_mks); 304 | save(file_name,'-v7.3','omega','U','V','P','U0','V0','Us_mks','Ts_mks','Ls_mks','dx','dt','dt_int_mks','tf_mks','ti_mks',... 305 | 'I_mks','rms_u','rms_v','gamma_arr','x','y','nltU_prev','nltV_prev','omega','err_u','err_v','F0x'); 306 | if(save_for_rec) 307 | save(file_name,'U_t','V_t','P_t','omega_save_dn','advpf','Re_nu','Re_rf','-append'); 308 | end 309 | 310 | if(if_plot) 311 | max_omega = max(omega(:)); 312 | min_omega = min(omega(:)); 313 | hfig = figure(1); 314 | 315 | contourf(X,Y,omega,20,'LineStyle','None'); 316 | daspect([1 1 1]); 317 | caxis([min_omega,max_omega]); 318 | colorbar 319 | hold on 320 | quiver(X(1:4:end,1:4:end),Y(1:4:end,1:4:end),U0(1:4:end,1:4:end),V0(1:4:end,1:4:end)) 321 | xlabel({'X'},'Fontsize',12,'FontWeight','bold','FontName','Times'); 322 | ylabel({'Y'},'Fontsize',12,'FontWeight','bold','FontName','Times'); 323 | title({sprintf('Simulation at %05.2fmA',I_mks*1000)},'Fontsize',12,'FontWeight','bold','FontName','Times'); 324 | set(hfig, 'Position', [50 50 750 fix(Ly/Lx*750)]); 325 | export_fig(sprintf('%05.2fmA_flow.pdf',I_mks*1000),'-transparent'); 326 | close(hfig) 327 | 328 | hfig = figure(2); 329 | plot((1:nt)*dt_mks,rms_u*Us_mks,'b*-'); 330 | hold on 331 | plot((1:nt)*dt_mks,rms_v*Us_mks,'r*-'); 332 | title({sprintf('Root Mean Square of U (blue) and V (red), I = %05.2f mA',I_mks*1000)},'Fontsize',12,'FontWeight','bold','FontName','Times'); 333 | xlabel({'t (seconds)'},'Fontsize',12,'FontWeight','bold','FontName','Times'); 334 | ylabel({'rms velocity (m/s)'},'Fontsize',12,'FontWeight','bold','FontName','Times'); 335 | set(hfig, 'Position', [50 50 750 fix(Ly/Lx*750)]); 336 | export_fig(sprintf('%05.2fmA_norm.pdf',I_mks*1000),'-transparent'); 337 | close(hfig) 338 | 339 | hfig = figure(3); 340 | plot((1:nt)*dt_mks,gamma_arr,'k*-'); 341 | title({sprintf('CFL condition for I = %05.2f mA and 1/dt = %03d',I_mks*1000,1/dt_mks)},'Fontsize',12,'FontWeight','bold','FontName','Times'); 342 | xlabel({'t (seconds)'},'Fontsize',12,'FontWeight','bold','FontName','Times'); 343 | ylabel({'(1/dt)(dx)U_{max}'},'Fontsize',12,'FontWeight','bold','FontName','Times'); 344 | set(hfig, 'Position', [50 50 800 300]); 345 | export_fig(sprintf('%05.2fmA_cfl.pdf',I_mks*1000),'-transparent'); 346 | close(hfig) 347 | 348 | end 349 | cd .. 350 | clear all 351 | end 352 | --------------------------------------------------------------------------------