MATLBA官方给出的2D Allen-Cahn Matlab代码分析

该博客详细介绍了如何利用隐式/显式时间步长的伪谱法来解决二维Allen-Cahn方程。通过设置周期性边界条件和初始条件,利用快速傅里叶变换(FFT)处理空间导数,并通过迭代更新求解非线性项。博客通过MATLAB代码展示了整个计算过程,并在每个时间步长绘制了结果的表面图。
该文章已生成可运行项目,

PDE

%Solving 2D Allen-Cahn Eq using pseudo-spectral with Implicit/Explicit
%u_t= u_{xx}+u_{yy} + u - u^3 (偏微分方程)
%where u-u^3 is treated explicitly and u_{xx} and u_{yy} is treated implicitly
%BC = Periodic (周期性边界条件)
%IC=v=sin(2*pi*x)+0.001*cos(16*pi*x; (初值条件)
clear all; clc; %清除工作空间的所有变量,函数,和MEX文件。清空命令窗口中的内容。

%Grid (绘制网格)
N = 64; h = 1/N; x = h*(1:N);  %N是网格点个数,h是每个网格大小,x是网格点的空间序列
dt = .01; %dt是时间采样频谱

%x and y meshgrid
y=x'; %y是x的转置
[xx,yy]=meshgrid(x,y); % 表示区域网格设置,目的是为了让x,y成为矩阵格点
%initial conditions (初值条件)
v=sin(2*pi*xx)+0.001*cos(16*pi*xx);
epsilon=.01; %epsilon的值
%(ik) and (ik)^2 vectors in x and y direction
kx=(1i*[0:N/2-1 0 -N/2+1:-1]); %(0+1i)*[0:31 0 -31:-1]
ky=(1i*[0:N/2-1 0 -N/2+1:-1]');%kx的转置
k2x=kx.^2; %kx的平方
k2y=ky.^2; %ky的平方
ii=1:N; %ii=[1 2 3 ...64]
%(ik)^2 matricies in x and y direction
for m= 1:N
    uxx(m,:)= k2x(ii); %Second derivative in the x-direction (x 方向的u的二阶导数)
end
 
for j= 1:N 
    uyy(:,j)= k2y(ii); %Second derivative in the y-direction  (y 方向的u的二阶导数)
end  
for n = 1:500 %时间步长为0.01,跑500次时间步
    
    v_nl=v.^3;  %calculates nonlinear term in real space
    
    for m=1:N %FFT in x-direction on linear and nonlinear term
        v_nl(m,:) = fft(v_nl(m,:)); %对v_n的第m行进行快速Fourier变换
        v_hat(m,:)=fft(v(m,:)); %对v的第m行进行快速Fourier变换
    end
    
    for j=1:N %FFT in y-direction on linear and nonlinear term
        v_nl(:,j) = fft(v_nl(:,j)); %对v_nl的第j列进行快速Fourier变换
        v_hat(:,j)=fft(v_hat(:,j)); %对v_hat的第j列进行快速Fourier变换
    end
    
    vnew=(v_hat/dt-v_nl)./ ...
       (-(uxx+uyy)*epsilon+1/dt-1); %Implicit/Explicit timestepping(隐式/显式时间步长)
  
    for m=1:N %converts to real space in x-direction
    %跑完FFT后我们就得到了多项式乘积的点值表示,现在我们需要将点值表示转回系数表示,这个转换的过程被称为离散傅里叶逆变换(IDFT)
        v(m,:)=ifft(vnew(m,:)); %使用快速傅里叶变换算法计算 vnew的第m行的逆离散傅里叶变换
    end
    
    for j=1:N %converts to real space in y-direction
        v(:,j)=real(ifft(v(:,j)));  %返回对v的第j列进行逆变换的复数的实部数值
    end
    
   %Plots each timestep %画图
   surf(v); 
   title(num2str(n)); 
   axis([0 N 0 N -1 1]); 
   view(43,22); 
   drawnow;
     
end
本文章已经生成可运行项目
<think>我们要求解的是Allen-Cahn方程,这是一个典型的相场模型方程,常用于描述相分离过程。方程形式如下: ∂φ/∂t = M * (ε²∇²φ - f(φ)) 其中,f(φ) = φ³ - φ,M是迁移率,ε是界面能参数。 我们将使用有限差分法进行空间离散,使用显式欧拉法进行时间离散。注意,显式欧拉法由于稳定性限制可能需要很小的时间步长。对于二维问题,我们考虑一个矩形区域。 边界条件:我们假设周期性边界条件或诺伊曼边界条件(零通量)。这里我们采用周期性边界条件。 初始条件:我们可以设置一个随机扰动场。 步骤: 1. 定义参数:网格大小、时间步长、空间步长、迁移率M、参数ε。 2. 初始化场变量φ。 3. 时间循环:在每个时间步,计算拉普拉斯项和双阱势的导数项。 4. 更新φ。 5. 应用边界条件(周期性)。 6. 可选:每隔一定步数输出结果或可视化。 注意:显式方法稳定性要求较高,因此时间步长需要满足CFL条件。对于Allen-Cahn方程,时间步长需要满足 Δt < (Δx)^2 / (4*M*ε²) 左右。为了更好的稳定性,我们也可以采用半隐式方法,但这里我们先给出显式方法。 下面是使用显式欧拉法求解二维Allen-Cahn方程MATLAB代码: ```matlab % 求解二维Allen-Cahn方程MATLAB代码(显式欧拉法) clear; clc; close all; % 设置参数 Nx = 128; % x方向网格点数 Ny = 128; % y方向网格点数 dx = 0.5; % 空间步长(x方向) dy = 0.5; % 空间步长(y方向) M = 1.0; % 迁移率 epsilon = 0.1; % 界面能参数 dt = 0.01; % 时间步长 total_time = 50; % 总时间 nsteps = round(total_time / dt); % 时间步数 % 初始化场 phi = zeros(Ny, Nx); % 设置初始条件:均匀态叠加随机扰动 phi = 0.5 * rand(Ny, Nx) - 0.25; % 创建网格坐标 x = (0:Nx-1) * dx; y = (0:Ny-1) * dy; [X, Y] = meshgrid(x, y); % 用于可视化 figure; h = imagesc(x, y, phi); colormap(jet); colorbar; axis equal tight; title('时间: 0'); xlabel('x'); ylabel('y'); % 时间迭代 for n = 1:nsteps % 复制当前时间步的phi phi_old = phi; % 计算拉普拉斯项(使用周期性边界条件) % 使用中心差分法 laplacian = zeros(Ny, Nx); % 内部点(中心差分) for i = 1:Ny for j = 1:Nx % 周期性边界处理 ip = i + 1; if ip > Ny, ip = 1; end im = i - 1; if im < 1, im = Ny; end jp = j + 1; if jp > Nx, jp = 1; end jm = j - 1; if jm < 1, jm = Nx; end % 二阶中心差分 laplacian(i,j) = (phi_old(ip,j) + phi_old(im,j) - 2*phi_old(i,j)) / (dy^2) ... + (phi_old(i,jp) + phi_old(i,jm) - 2*phi_old(i,j)) / (dx^2); end end % 计算非线性项 f(φ) = φ^3 - φ, 则 -f(φ) = φ - φ^3 nonlin_term = phi_old - phi_old.^3; % 更新phi:显式欧拉法 phi = phi_old + dt * M * (epsilon^2 * laplacian - nonlin_term); % 更新时间 current_time = n * dt; % 每50步可视化一次 if mod(n,50)==0 set(h, 'CData', phi); title(sprintf('时间: %.2f', current_time)); drawnow; end end ``` ### 代码解释: 1. **参数设置**: - `Nx`, `Ny`:网格点数。 - `dx`, `dy`:空间步长。 - `M`:迁移率。 - `epsilon`:界面能参数,控制界面宽度。 - `dt`:时间步长,需要足够小以满足稳定性。 - `total_time`:模拟总时间。 2. **初始条件**: - 初始场为均匀态(0附近)加上随机扰动(-0.25到0.25之间)。 3. **拉普拉斯项计算**: - 使用五点差分格式(中心差分),并应用周期性边界条件- 对于边界上的点,通过取模或索引调整实现周期性。 4. **非线性项**: - 双阱势的导数项:`f(φ)=φ^3-φ`,所以方程中为 `-f(φ)=φ-φ^3`。 5. **时间更新**: - 显式欧拉法更新:`φ_new = φ_old + dt * M * (ε²∇²φ - (φ^3 - φ))`,注意非线性项是负号。 6. **可视化**: - 每隔一定步数更新图像,显示相场演化过程。 ### 稳定性注意: 由于显式方法,时间步长需要满足稳定性条件。通常要求: dt < (dx^2 * dy^2) / (2 * epsilon^2 * M * (dx^2 + dy^2)) 但更严格的约束可能来自非线性项。如果模拟发散,可尝试减小时间步长。 ### 改进方向: 1. **使用半隐式方法**:将线性部分(拉普拉斯项)隐式处理,提高稳定性。 2. **自适应时间步长**:根据解的变化调整时间步长。 3. **傅里叶谱方法**:利用FFT计算拉普拉斯项,提高精度和效率(特别适合周期性边界条件)。 下面是使用半隐式欧拉法的改进版本(将线性部分隐式处理): ```matlab % 半隐式欧拉法求解Allen-Cahn方程(周期性边界条件,使用FFT) clear; clc; close all; % 设置参数 Nx = 128; % x方向网格点数 Ny = 128; % y方向网格点数 Lx = 64; % 区域长度(x) Ly = 64; % 区域长度(y) dx = Lx / Nx; % 空间步长(x) dy = Ly / Ny; % 空间步长(y) M = 1.0; % 迁移率 epsilon = 0.5; % 界面能参数 dt = 0.1; % 时间步长(半隐式允许更大的时间步长) total_time = 100; nsteps = round(total_time / dt); % 波数矢量 kx = 2*pi/Lx * [0:Nx/2-1, -Nx/2:-1]; % 波数(x方向) ky = 2*pi/Ly * [0:Ny/2-1, -Ny/2:-1]; % 波数(y方向) [KX, KY] = meshgrid(kx, ky); K2 = KX.^2 + KY.^2; % 波数平方 % 初始化场 phi = 0.5 * rand(Ny, Nx) - 0.25; % 用于可视化 figure; h = imagesc(phi); colormap(jet); colorbar; axis equal tight; title('时间: 0'); xlabel('x'); ylabel('y'); % 时间迭代 for n = 1:nsteps % 计算非线性项(在实空间) nonlin_term = phi - phi.^3; % 非线性项的傅里叶变换 nonlin_hat = fft2(nonlin_term); % 当前场的傅里叶变换 phi_hat = fft2(phi); % 半隐式欧拉更新(隐式处理拉普拉斯项,显式处理非线性项) % 更新公式:φ_{n+1} = [φ_n + dt*M*( -nonlin_term )] / (1 + dt*M*epsilon^2*K2) % 在傅里叶空间中更新 phi_hat_new = (phi_hat - dt * M * nonlin_hat) ./ (1 + dt * M * epsilon^2 * K2); % 反变换回实空间 phi_new = real(ifft2(phi_hat_new)); phi = phi_new; % 更新时间 current_time = n * dt; % 可视化 if mod(n,10)==0 set(h, 'CData', phi); title(sprintf('时间: %.2f', current_time)); drawnow; end end ``` ### 半隐式方法说明: 1. **傅里叶谱方法**: - 利用FFT计算空间导数(拉普拉斯项在傅里叶空间为乘法运算)。 - 周期性边界条件自然满足。 2. **半隐式离散**: - 线性部分(拉普拉斯项)采用隐式处理:`(1 - dt*M*epsilon^2*∇²)φ^{n+1} = φ^n - dt*M*nonlin_term` - 在傅里叶空间写成:`(1 + dt*M*epsilon^2*K2) * φ_hat^{n+1} = φ_hat^n - dt*M*nonlin_hat` 3. **优点**: - 时间步长限制主要来自非线性项,通常可以比显式方法取更大的时间步长。 - 计算精度高(谱方法)。 ### 注意: - 初始条件需要添加随机扰动以打破对称性,否则均匀态不会分相。 - 半隐式代码中,由于采用了傅里叶变换,需要确保网格是均匀的。 §§相关问题§§: 1. 如何处理非周期性边界条件(如诺伊曼边界条件)的Allen-Cahn方程? 2. 如何将Allen-Cahn方程扩展到三维? 3. 在相场模拟中,如何定量计算相分离的特征尺度? 4. 如何结合流体流动(如Navier-Stokes方程)与Allen-Cahn方程模拟两相流? </think>以下是用MATLAB求解Allen-Cahn方程的完整代码实现。Allen-Cahn方程是一种描述相分离过程的非线性偏微分方程,常用于材料科学和流体力学中的界面演化模拟: ```matlab % Allen-Cahn方程求解器 - 使用半隐式分裂步谱方法 clear; clc; close all; % ========================= 参数设置 ========================= L = 20.0; % 区域长度 [-L/2, L/2] N = 256; % 空间离散点数 T = 100.0; % 总模拟时间 dt = 0.01; % 时间步长 epsilon = 0.05; % 界面厚度参数 mobility = 1.0; % 迁移率 delta = 0.1; % 热噪声强度 % ========================= 初始化 ========================= dx = L / N; % 空间步长 x = linspace(-L/2, L/2, N); % 空间网格 [xx, yy] = meshgrid(x, x); % 二维网格 % 初始条件:随机扰动 rng(42); % 固定随机种子以确保可重现性 phi0 = 0.1 * (2*rand(N,N) - 1); % 随机初始化 % 傅里叶波数向量 k = 2*pi/L * [0:N/2-1, -N/2:-1]; % 波数 [kx, ky] = meshgrid(k, k); k2 = kx.^2 + ky.^2; % 波数平方 % ========================= 半隐式求解器 ========================= phi = phi0; % 初始化相场变量 phi_old = phi; % 创建可视化窗口 figure('Position', [100, 100, 900, 700]); subplot(2,2,1); h_img = imagesc(x, x, phi); colorbar; axis square; title('瞬时相场分布'); xlabel('x'); ylabel('y'); colormap(jet); subplot(2,2,2); h_conv = plot(0, 0, 'b-', 'LineWidth', 2); xlabel('时间'); ylabel('能量密度'); title('自由能演化'); grid on; subplot(2,2,3); h_minmax = plot(0, 0, 'r-', 0, 0, 'b--', 'LineWidth', 1.5); xlabel('时间'); ylabel('相场值'); title('相场极值'); legend('最大值', '最小值'); grid on; subplot(2,2,4); h_energy = plot(0, 0, 'k-', 'LineWidth', 2); xlabel('时间'); ylabel('总自由能'); title('系统总自由能'); grid on; % 预分配绘图数据 t_vec = 0; min_phi = min(phi(:)); max_phi = max(phi(:)); free_energy_vec = zeros(1, ceil(T/dt)+1); free_energy_vec(1) = calculate_free_energy(phi, epsilon, dx); minmax_data = [max_phi; min_phi]; energy_data = free_energy_vec(1); % ========================= 时间迭代 ========================= nsteps = round(T/dt); for n = 1:nsteps t = n * dt; % 1. 非线性项处理 (显式部分) nonlinear_term = phi - phi.^3 + delta * randn(N,N); % 添加热噪声 % 2. 线性项处理 (隐式部分 - 谱方法) phi_hat = fft2(phi); % 傅里叶变换 % 应用半隐式格式 numerator = fft2(phi_old + dt * mobility * nonlinear_term); denominator = 1 + dt * mobility * epsilon^2 * k2; phi_hat_new = numerator ./ denominator; % 3. 反傅里叶变换 phi_new = real(ifft2(phi_hat_new)); % 4. 更新场变量 phi_old = phi; phi = phi_new; % ========================= 计算物理量 ========================= % 自由能密度函数 free_energy_density = 0.25*(phi.^2 - 1).^2 + 0.5*epsilon^2*... ((circshift(phi, [1,0]) - phi).^2 + (circshift(phi, [0,1]) - phi).^2)/dx^2; % 系统总自由能 total_energy = dx^2 * sum(free_energy_density(:)); % 相场极值 min_phi = min(phi(:)); max_phi = max(phi(:)); % ========================= 更新可视化 ========================= t_vec(end+1) = t; minmax_data(:, end+1) = [max_phi; min_phi]; free_energy_vec(end+1) = total_energy; energy_data(end+1) = total_energy; % 每50步更新一次图像 if mod(n, 50) == 0 set(h_img, 'CData', phi); title(h_img.Parent, sprintf('相场分布 (t = %.1f)', t)); % 自由能密度曲线 set(h_conv, 'XData', t_vec, 'YData', free_energy_vec); % 相场极值曲线 set(h_minmax(1), 'XData', t_vec, 'YData', minmax_data(1,:)); set(h_minmax(2), 'XData', t_vec, 'YData', minmax_data(2,:)); % 总自由能曲线 set(h_energy, 'XData', t_vec, 'YData', energy_data); drawnow; end % 输出进度 if mod(n, 500) == 0 fprintf('时间 %.2f / %.2f (%.1f%%)\n', t, T, 100*t/T); end end % ========================= 最终可视化 ========================= figure('Position', [100, 100, 1200, 400]); % 最终相场分布 subplot(1,3,1); imagesc(x, x, phi); axis square; colorbar; title(sprintf('最终相场分布 (t = %.1f)', T)); xlabel('x'); ylabel('y'); colormap(jet); % 界面轮廓 subplot(1,3,2); contourf(x, x, phi, [-0.99:0.1:0.99]); axis square; title('相场等高线'); xlabel('x'); ylabel('y'); colorbar; % 自由能演化 subplot(1,3,3); plot(t_vec, energy_data, 'k-', 'LineWidth', 2); xlabel('时间'); ylabel('总自由能'); title('自由能演化'); grid on; % ========================= 辅助函数 ========================= function F = calculate_free_energy(phi, epsilon, dx) % 计算自由能泛函 grad_x = (circshift(phi, [1,0]) - phi)/dx; grad_y = (circshift(phi, [0,1]) - phi)/dx; grad_sq = grad_x.^2 + grad_y.^2; F = dx^2 * sum(0.25*(phi.^2 - 1).^2 + 0.5*epsilon^2*grad_sq, 'all'); end ``` ### Allen-Cahn方程解析 Allen-Cahn方程描述了相分离过程的演化: ``` ∂φ/∂t = M [ε²∇²φ - f(φ)] ``` 其中: - φ 是相场变量(在-1和1之间变化) - M 是迁移率 - ε 是界面厚度参数 - f(φ) = φ³ - φ = dF/dφ (双阱势的导数) ### 数值求解方法:分裂步谱方法 1. **空间离散化**: ```matlab dx = L / N; % 空间步长 x = linspace(-L/2, L/2, N); % 空间网格 ``` 2. **傅里叶谱方法**: ```matlab k = 2*pi/L * [0:N/2-1, -N/2:-1]; % 波数向量 [kx, ky] = meshgrid(k, k); k2 = kx.^2 + ky.^2; % 波数平方 ``` 3. **半隐式时间离散**: ```matlab % 显式处理非线性项 nonlinear_term = phi - phi.^3 + delta * randn(N,N); % 隐式处理线性项(谱空间) numerator = fft2(phi_old + dt * mobility * nonlinear_term); denominator = 1 + dt * mobility * epsilon^2 * k2; phi_hat_new = numerator ./ denominator; % 反变换到物理空间 phi_new = real(ifft2(phi_hat_new)); ``` ### 关键物理量计算 1. **自由能泛函**: ```matlab function F = calculate_free_energy(phi, epsilon, dx) grad_x = (circshift(phi, [1,0]) - phi)/dx; grad_y = (circshift(phi, [0,1]) - phi)/dx; grad_sq = grad_x.^2 + grad_y.^2; F = dx^2 * sum(0.25*(phi.^2 - 1).^2 + 0.5*epsilon^2*grad_sq, 'all'); end ``` 自由能包含两部分: - 双阱势:0.25(φ²-1)² - 界面能:0.5ε²|∇φ|² 2. **相场演化特征**: - 系统从随机初始状态演化 - 相分离形成清晰界面 - 自由能随时间单调下降 - 相场值最终趋近于±1 ### 可视化说明 1. **实时可视化**: - 相场分布的热图 - 自由能密度演化 - 相场极值变化 - 系统总自由能 2. **最终结果**: - 相场分布图 - 界面等高线图 - 自由能演化曲线 ### 参数选择建议 1. **界面厚度参数ε**: - 控制界面宽度:ε ≈ 网格尺寸 - 典型值:ε = 0.02-0.1 2. **时间步长dt**: - 稳定性条件:dt < dx²/(4Mε²) - 典型值:dt = 0.001-0.1 3. **热噪声δ**: - 加速相分离过程 - 典型值:δ = 0.01-0.1 该实现采用高效的傅里叶谱方法,能够准确捕获相分离过程中的界面演化和能量变化。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值