1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ChatGPTに拡散方程式のプログラムを書いてと頼んだ。

Last updated at Posted at 2023-03-18

ChatGPT使ってみた

プログラムを書いてくれるというので拡散方程式の差分のFortranプログラムを書いてもらいました。

結論からいうと、合ってそうで正しくないコードが出てきました。質問を日本語で適当にしたからなのか、陰解法でとかめんどくさいことをいったからなのか。

最初のお願い

拡散方程式の空間を2次精度の中心差分,時間を1次精度の陰的Euler法で離散化するFortranのプログラム書いて

ちなみに想定される離散化した式が以下。

\frac{u^{n+1}_i - u^n_i}{\Delta t} = \alpha \frac{u^{n+1}_{i+1} - 2u^{n+1}_i + u^{n+1}_{i-1}}{\Delta x^2}

上の添字が時刻,下の添字が空間座標のインデックス。既知の$u^n$から次の時刻の$u^{n+1}$を計算します。行列でかくと

\begin{pmatrix}
  1+2\nu  & -\nu      & 0 \dots &       &        0                         \\
   -\nu      & 1+2\nu          &  -\nu      & 0 \dots &0 \\
         &     \ddots            & \ddots            & &         \\
        &-\nu &1+2\nu & -\nu & \\
       &  &  \ddots      & \ddots      &      \\
   0 \dots      & 0 & -\nu& 1+2\nu & -\nu \\
   0  \dots    &                &    0    &     -\nu      & 1+2\nu
\end{pmatrix}
\begin{pmatrix}
u^{n+1}_1\\
u^{n+1}_2 \\
\vdots\\
u^{n+1}_i\\
\vdots\\
u^{n+1}_{imax-1} \\
u^{n+1}_{imax}
\end{pmatrix} = 
\begin{pmatrix}
u^{n}_1\\
u^n_2\\
\vdots\\
u^{n}_i\\
\vdots\\
u^{n}_{imax-1} \\
u^{n}_{imax}
\end{pmatrix}

ここで$\nu=\alpha \Delta t/\Delta x^2$。この連立1次方程式を解いてくれればOKです。

回答

以下に、拡散方程式の空間を2次精度の中心差分、時間を1次精度の陰的Euler法で離散化するFortranのプログラムを示します。このプログラムは、1次元の場合を想定しています。

program diffusion
  implicit none
  integer, parameter :: nx = 100 ! 空間の分割数
  integer, parameter :: nt = 100 ! 時間ステップ数
  real, parameter :: dx = 0.01 ! 空間刻み幅
  real, parameter :: dt = 0.01 ! 時間刻み幅
  real, parameter :: alpha = 0.1 ! 拡散係数
  real, dimension(0:nx) :: u ! 解の配列
  integer :: i, j ! インデックス用変数
  
  ! 初期条件
  do i = 0, nx
    u(i) = 0.0
    if (i >= 45 .and. i <= 55) u(i) = 1.0
  end do
  
  ! 時間発展
  do j = 1, nt
    do i = 1, nx-1
      u(i) = u(i) + alpha * dt / dx**2 * (u(i+1) - 2*u(i) + u(i-1))
    end do
  end do
  
  ! 結果の出力
  do i = 0, nx
    write(*,*) i*dx, u(i)
  end do
end program diffusion

陰解法を頼んだのに陽解法。。。。よく見ると

u(i) = u(i) + alpha * dt / dx**2 * (u(i+1) - 2*u(i) + u(i-1))

だと,u(i-1)が次のタイムステップの量になって,計算がおかしくなる。

これは陽的Euler法のプログラムです。陰解法のプログラムを書いて下さい。

回答

陰的Euler法の場合、更新式が差分方程式を解く必要があります。一般的には、連立方程式を解く必要がありますが、今回は簡単のために反復法を用いたガウス・ザイデル法を使って、差分方程式を解きます。以下に、陰的Euler法による拡散方程式のFortranプログラムを示します。

program diffusion
  implicit none
  integer, parameter :: nx = 100 ! 空間の分割数
  integer, parameter :: nt = 100 ! 時間ステップ数
  real, parameter :: dx = 0.01 ! 空間刻み幅
  real, parameter :: dt = 0.01 ! 時間刻み幅
  real, parameter :: alpha = 0.1 ! 拡散係数
  real, dimension(0:nx) :: u ! 解の配列
  real, dimension(0:nx) :: u_new ! 新しい解の配列
  integer :: i, j, k ! インデックス用変数
  real :: error, tol ! 反復の収束判定用変数
  
  ! 初期条件
  do i = 0, nx
    u(i) = 0.0
    u_new(i) = 0.0
    if (i >= 45 .and. i <= 55) u(i) = 1.0
  end do
  
  ! 時間発展
  do j = 1, nt
    do i = 1, nx-1
      ! 初期値の設定
      u_new(i) = u(i)
      error = 1.0
      tol = 1.0e-6
      
      ! 反復法による解法
      do k = 1, 1000
        u_new(i) = (u(i+1) + u_new(i-1) + alpha * dt / dx**2 * u(i-1) + alpha * dt / dx**2 * u(i+1)) / (2.0*(1.0 + alpha*dt/dx**2))
        error = max(error, abs(u_new(i) - u(i)))
        u(i) = u_new(i)
        if (error < tol) exit
      end do
    end do
  end do
  
  ! 結果の出力
  do i = 0, nx
    write(*,*) i*dx, u(i)
  end do
end program diffusion

ガウス・ザイデル法ってどんなんだっけと思いながら,教科書見返したら,パット見あってそうだけど,よく見るとこれもヘン。ガウス・ザイデル法で離散化すると

(1+\frac{2\alpha \Delta t}{\Delta x^2})u^{(k+1)}_i = \frac{\alpha \Delta t}{\Delta x^2} u^{(k+1)}_{i-1} + \frac{\alpha \Delta t}{\Delta x^2} u^{(k)}_{i+1} +u^n_i

kがガウス・ザイデルの反復の回数。

u_new(i) = (u(i+1) + u_new(i-1) + alpha * dt / dx**2 * u(i-1) + alpha * dt / dx**2 * u(i+1)) / (2.0*(1.0 + alpha*dt/dx**2))

余計な項がある。これをもとにして書き直したコードがこれ。

program diffusion
  implicit none
  integer, parameter :: nx = 100 ! 空間の分割数
  integer, parameter :: nt = 30 ! 時間ステップ数
  real, parameter :: dx = 0.01 ! 空間刻み幅
  real, parameter :: dt = 0.01 ! 時間刻み幅
  real, parameter :: alpha = 0.1 ! 拡散係数
  real, dimension(0:nx) :: u ! 解の配列
  real, dimension(0:nx) :: u_new ! 新しい解の配列
  real, dimension(0:nx) :: u_pre ! 
  integer :: i, j, k ! インデックス用変数
  real :: error, tol ! 反復の収束判定用変数
  real :: nu
  
  ! 初期条件
  do i = 0, nx
     u(i) = 0.0
     u_new(i) = 0.0
     if (i >= 45 .and. i <= 55) u(i) = 1.0
  end do

  tol = 1.0e-6
  nu = alpha * dt / dx**2
  
  ! 時間発展
  do j = 1, nt
     do i = 1, nx-1
        ! 初期値の設定
        u_new(i) = u(i)
        u_pre(i) = u(i)
     enddo

     ! 反復法による解法
     do k = 1, 500
        error = -1.0
        do i = 1, nx-1
           
           u_new(i) = (u_new(i-1) *nu + u(i+1) *nu + u_pre(i))/(1.0+2.0*nu) 
           error = max(error, abs(u_new(i) - u(i)))
           u(i) = u_new(i)
           
        end do
        write(*,*) k, error
        if (error < tol) exit

     end do

     ! 結果の出力
     write(*,*) j
    
     do i = 0, nx
        write(10*j,*) i*dx, u(i)
     end do
         
  end do
end program diffusion

現状

流体とか差分のプログラムを書いてもらうとそれっぽいプログラムを書いてくれるが,正しいかを割とまじめにチェックする必要がありそう。

補足

英語でお願いしてみました。

Make a Fortran program for solving the one-dimensional diffusion equation. Please use Euler implicit method for time marching algorithm and second accuracy central differencing for spatial discretization. Please solve the linear algebra equation by Gauss-Sidel method.

できたコード

program diffusion
    implicit none

    ! Define parameters
    real(kind=8) :: L = 1.0        ! Length of domain
    integer :: Nx = 100           ! Number of spatial grid points
    real(kind=8) :: T = 1.0       ! Simulation time
    integer :: Nt = 1000          ! Number of time steps
    real(kind=8) :: D = 1.0       ! Diffusion coefficient
    real(kind=8) :: dx, dt       ! Spatial grid spacing, time step size
    real(kind=8), dimension(:), allocatable :: u, unew ! Solution arrays
    real(kind=8), dimension(:,:), allocatable :: A   ! Matrix for implicit method
    real(kind=8) :: r             ! Implicit method coefficient
    integer :: i, n, j            ! Loop variables

    ! Allocate arrays
    allocate(u(Nx), unew(Nx), A(Nx,Nx))

    ! Calculate grid spacing and time step size
    dx = L / real(Nx-1)
    dt = T / real(Nt)

    ! Set initial condition
    do i = 2, Nx-1
        u(i) = sin(real(i-1) * pi * dx)
    end do
    u(1) = 0.0
    u(Nx) = 0.0

    ! Set up matrix for implicit method
    r = D * dt / dx**2
    do i = 1, Nx
        do j = 1, Nx
            if (i == j) then
                A(i,j) = 1.0 + 2.0*r
            else if (i == j+1 .or. i == j-1) then
                A(i,j) = -r
            else
                A(i,j) = 0.0
            end if
        end do
    end do

    ! Time stepping loop
    do n = 1, Nt
        ! Solve linear algebra equation by Gauss-Sidel method
        do i = 1, Nx
            unew(i) = u(i)
        end do
        do i = 2, Nx-1
            u(i) = (unew(i-1) + unew(i+1) + r*u(i-1) + r*u(i+1)) / (1.0 + 2.0*r)
        end do
        u(1) = 0.0
        u(Nx) = 0.0
    end do

    ! Deallocate arrays
    deallocate(u, unew, A)

end program diffusion

係数行列を準備するとかいいじゃないかとおもってみたら,係数行列Aは定義するだけしておいて,使わない。依然として,配列の計算がおかしい。

ちなみにJuliaで作ってっていったら,係数行列をそのまま反転するというプログラムを作ってくれました。

Make a Julia program for solving the one-dimensional diffusion equation. Please use Euler implicit method for time marching algorithm and second accuracy central differencing for spatial discretization.

# Define parameters
L = 1.0        # Length of domain
Nx = 100       # Number of spatial grid points
T = 1.0        # Simulation time
Nt = 1000      # Number of time steps
D = 1.0        # Diffusion coefficient
dx = L / (Nx-1)
dt = T / Nt

# Create arrays for solution
u = zeros(Nx)
u[1] = 0.0
u[Nx] = 0.0
unew = zeros(Nx)

# Set initial condition
for i in 2:Nx-1
    x = (i-1) * dx
    u[i] = sin(pi*x)
end

# Set up matrix for implicit method
r = D * dt / dx^2
A = zeros(Nx,Nx)
A[1,1] = 1.0
A[Nx,Nx] = 1.0
for i in 2:Nx-1
    A[i,i] = 1 + 2*r
    A[i,i-1] = -r
    A[i,i+1] = -r
end

# Time stepping loop
for n in 1:Nt
    b = u
    unew = A \ b
    u = unew
end

# Plot solution
using Plots
x = range(0, L, length=Nx)
plot(x, u, xlabel="x", ylabel="u(x)", label="Numerical solution")
plot!(x, sin.(pi*x), label="Analytical solution")
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?