- 追加された行はこの色です。
- 削除された行はこの色です。
// 現在(&lastmod;)作成中です。
// 既に書いている内容も&color(#ff0000){大幅に変わる};可能性が高いので注意。
-------
神戸大学 大学院システム情報学研究科 計算科学専攻 陰山 聡
-------
【2010.07.08: 講義後追記】 変更箇所
+ スクリプトでgnuplotを使う場合、グラフがグラフが一瞬描かれた直後に終了してしまうという問題があった。これを避けるにはgnuplotスクリプトの最後の一行に
pause -1
と書く必要がある。
以下のコンテンツではこの点に修正を加えた。
+ ソースコードheat1_animation_x_prof.f90が抜けていた問題を修正した。
-------
【目次】
#contents
-------
* 可視化の必要性 [#m523fb8a]
上の例では、MPIプロセスがもつ配列uの値をテキストデータとして書き出すことで、
プロセス間のMPI_SENDRECVによる通信の様子が分かりやすく表示された。
このように人間の視覚を使って情報を引き出す操作を一般に可視化という。
科学技術計算においては、可視化が様々な局面で重要な役割を果たす。
特に、計算結果として出力される数値データに潜む情報を引き出すための可視化―これをデータ可視化という―は、
不可欠と言ってよい。
データ可視化の身近な例は天気図であろう。
NKHラジオの「気象通報」という番組を知っているだろうか?
各地の風向、風力、気圧などの数字をアナウンサーが次々に読み上げていくラジオ番組である。
これをただ耳で聞いているだけでは何もわからない。
しかしペンと地図を手にし、番組を聞きながら読み上げられる各地点に気圧などを書き込み(専用の地図を売っている)、
最後に同じ気圧になりそうな点を曲線(等高線)でつなげていくと、
低気圧や高気圧が図―天気図―としてあらわれる。
知識のある人が天気図を見れば、 明日の天気がだいたいわかる。
この天気図に入っている情報は、
もともとラジオのアナウンサーが読み上げる数値データに全て含まれているものである。
だが、そのような数値データの羅列をいくら注意深く聞いても、
あるいはそれを数表にして紙の上に書き、それをじっと睨んでみても、
明日の天気を知るのはほとんど不可能である。
紙の上に天気図という図にしてみてはじめてデータに隠されていた情報を引き出すことができる。
これが可視化である。
* 可視化の例 [#see76733]
データの可視化には可視化の目的や、対象データの種類の応じて様々な方法が開発されている。
その中で、ここでは、スカラー場の可視化について簡単に説明する。
** 1次元グラフ [#cd543b20]
xのある関数f(x)を直感的に理解するためには、グラフを書くのが一番である。
例えばxのx乗の関数、つまりf(x)=x^xはどんな形だろうか?
下のグラフを見ればわかる。
#ref(graph_1d.png)
** 2次元等高線 [#t263310f]
xとyの関数、つまり2次元の関数f(x,y)の形を理解するには、等高線を描くのがよい。
地表面での大気の圧力pの分布p(x,y)の等高線は天気図でおなじみである。
#ref(graph_2d.png)
上の図では等高線の高さを色で表示してさらに分かりやすくしている。
ここで等高線の描画アルゴリズムを紹介しよう。
計算格子点上に定義されたデータから一本の等高線を描くには、下の図のように短い線分をつなげていけばよい。
#ref(contour_line01.png)
一つの線分は4つの計算格子点で定義された長方形領域(セル)の中で直線を描く。
例えばf(x,y)=1.0の値の等高線を描く場合を考えよう。
あるセルの4つの頂点におけるfの値が全て1.0よりも大きいか、あるいは1.0未満であれば、f=1.0の等高線はこのセルを通らないのは明らかである。
セルを周囲の4つの辺、それぞれの両端の頂点でのfの値が1.0を「挟めば」その辺を等高線が通る。
辺上のどの位置を等高線が横切るかは、線形補間をすればよい。
下の図はちょうど中点を通る例である。
#ref(contour_line02.png)
このアルゴリズムはmarching squaresと呼ばれる。
等高線による可視化とは2次平面上に分布するスカラー場f(x,y)を
等高線という曲線を見ることで理解する方法である。
** 3次元等値面 [#ba511110]
等値面の「3次元版」も考えられる。
この場合は3次元空間中の曲面の形状を見ることで3次元空間に分布する関数f(x,y,z)の分布を理解する。
たとえば、f(x,y,z)がある値(例えば1.0)をとる点の集合は、曲面の方程式 f(x,y,z)=1.0 で定義される。
下の図の白い矢印で示している物体は等値面の例である。
これは核融合実験装置(LHD)内部のプラズマの圧力分布の等値面である。
#ref(isosurface_sample.png)
このような等値面を描くためのアルゴリズムとしてMarching Cubesがある。
#ref(marching_cubes.png)
このMarching Cubesアルゴリズムは、
1985年に米国で特許となったが、20年を経過したので、現在では自由に利用できる。
* gnuplot入門 [#y577d51e]
このような可視化アルゴリズムを実装したソフトウェアは既に各種開発されており、可視化ソフトウェアとかグラフソフトなどと呼ばれる。
この演習では、そのなかで無償で利用出来るgnuplotを利用する。
(なお、gnuplotはGNUのソフトウェアではない。)
/usr/bin/gnuplot
または
/mnt/nfs/usr/bin/gnuplot
というコマンド打てばgnuplotが立ち上がる。
gnuplotコマンドプロンプトに
plot sin(x)
と打ち込んでみよう。sin関数がプロットされるはずである。
上に例として示したxのx乗のグラフは
plot x**xとすれば見える。
gnuplotの終了はコマンドプロンプトでquitと打つ。
gnuplotではこのように解析的に定義された関数をターミナルに打ち込んでそのグラフを描く機能がある。
それに加えて、ファイルに書き込まれた離散データを読み込み、それをグラフにする機能もある。
この機能を使ってheat1.f90で計算した正方形領域の中心点での温度が、
ヤコビ法によって次第に収束して行く様子を可視化してみよう。
* &color(#0000ff){【演習】1次元グラフ1}; [#ldf93d80]
(1) heat1.f90の出力(標準出力)をファイルtest.dataという名前に保存せよ。
- コンパイルはpgf95 heat1.f90
- ./a.out > test.data でリダイレクトされる。
(2) test.dataの中身を確認せよ
- エディタで開くよりもheadコマンドやtailコマンドで見る方が早い。
(3) gnuplotを立ち上げ、コマンドプロンプトに
plot 'test.data' w lp
と入れよ。
lpはlinespointsの略で、線(line)と点(point)を表示することを意味する。
(w linespointsと書いてもよい。)
** 結果例 [#sfc0090b]
#ref(central_temp_converge.png)
* &color(#0000ff){【演習】1次元グラフ2}; [#v1cfc1e5]
(1) 上の図の中で文字とグラフが重なって見にくいので、文字を消す。
gnuplotのコマンドプロンプトで
unset key
と入れ、それに続いて
replot
と打て。
(2) 縦軸の表示範囲を調整をする。
gnuplotのコマンドプロンプトで
set yrange [0:0.35]
replot
と打て。
(3) 図全体のタイトルと、x軸、y軸の説明を入れる。
set title "Temperatute at the Center"
set xlabel "iterations"
set ylabel "temperature"
replot
と打て。
** 結果例 [#y4850b7e]
#ref(central_temp_converge_rev.png)
* &color(#0000ff){【演習】1次元グラフ3}; [#mcc1d44b]
heat1.f90で計算された最終的な平衡温度分布をgnuplotで見てみよう。
正方形を真ん中で横に切るy=0.5の線上での温度のx分布をグラフにする。
(1) これからの演習でデータファイルを多数生成するので、データファイル出力先のディレクトリを用意しよう。
例えばいまソースコードの置いていあるディレクトリと同じ高さに
mkdir ../data
などとしてdataディレクトリを作成すること。別の場所、名前でも構わないが、以後のサンプルコードではそれに応じて適宜dataディレクトリの名前を変更すること。
(2) 以下のプログラムをheat1_print_final_x_prof.f90
という名前のソースコードにコピーし、コンパイル・実行せよ。
program heat1_print_final_x_prof
implicit none
integer, parameter :: m=31, nmax=5000
integer :: i,j,n
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
real(DP), dimension(:,:), allocatable :: u, un
real(DP) :: h, heat=1.0_DP, heat_hsq
allocate(u(0:m+1,0:m+1))
allocate(un(m,m))
h = 1.0_DP/(m+1)
heat_hsq = heat*h*h
u(:,:) = 0.0_DP
open(10,file='../data/temp.final_profile_x')
do n = 1 , nmax
do j = 1 , m
do i = 1 , m
! un(i,j)=(u(i-1,j)+u(i+1,j)+u(i,j-1)+u(i,j+1))/4.0_DP+heat*h*h
un(i,j)=(u(i-1,j)+u(i+1,j)+u(i,j-1)+u(i,j+1))*0.25_DP+heat_hsq
end do
end do
u(1:m,1:m)=un(1:m,1:m)
end do
do i = 0 , m+1
write(10,*) i, u(i,m/2+1)
end do
deallocate(u,un)
close(10)
end program heat1_print_final_x_prof
ディレクトリ../dataにtemp.final_profile_xという名前のファイルが出来ているはずである。
* &color(#0000ff){【演習】1次元グラフ4}; [#rfdd8b3e]
../data/temp.final_profile_xのデータをgnuplotでグラフにする。
(1) このデータの中身をエディタまたはheadコマンド、tailコマンドで確認せよ。
(2) gnuplotではコマンドプロンプトに入力する内容をスクリプトファイルから読み込ませることが出来る。
以下の内容をheat1_print_final_x_prof.gpという名前のファイルに保存せよ。
#
# heat1_print_final_x_prof.gp
#
# final temperature profile at y=0.5 as a function of x
#
set xrange [0:32]
set yrange [0:0.5]
set xlabel 'i'
set ylabel 'temp at y=0.5'
plot '../data/temp.final_profile_x' w lp
pause -1
(3) gnuplotがまだ立ち上がっていたらquitコマンドで終了し、改めてshellで
gnuplot heat1_print_final_x_prof.gp
と打て。
** 結果例 [#o4e0c43d]
#ref(temp_final_x_profile.png)
(4) ファイルheat1_print_final_x_prof.gpを自由に変更してグラフがどう変わるか試せ。
なお、gnuplotのヘルプはコマンドプロンプトでhelpと打てば得られる。
* アニメーション用データの生成 [#w228ba14]
次にアニメーションによって収束の様子を確認しよう。
そのためのデータ(連番つきファイル群)を書き出すためのプログラム
heat_animation_x_prof.f90
を以下に示す。
このプログラムをpgf90コマンドでコンパイルし、
実行すればdataディレクトリに連番ファイルが出力される。
module constants
implicit none
integer, parameter :: m=61, nmax=5000
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
end module constants
module print
use constants
implicit none
contains
subroutine print__profile(f)
real(DP), dimension(0:m+1), intent(in) :: f
integer :: i
integer :: counter = 0 ! automatic save attribute
character(len=*), parameter :: base = "../data/temp.j=middle."
character(len=4) :: serial_num
write(serial_num,'(i4.4)') counter
open(10,file=base//serial_num)
do i = 0 , m+1
write(10,*) i, f(i)
end do
close(10)
counter = counter + 1
end subroutine print__profile
end module print
program heat1_animation_x_prof
use constants
use print
implicit none
integer :: i,j,n
real(DP), dimension(:,:), allocatable :: u, un
real(DP) :: h, heat=1.0_DP
real(DP) :: random
allocate(u(0:m+1,0:m+1))
allocate(un(m,m))
h = 1.0_DP/(m+1)
u(:,:) = 0.0_DP
call print__profile(u(:,m/2+1))
do n=1, nmax
do j=1, m
do i=1, m
un(i,j)=(u(i-1,j)+u(i+1,j)+u(i,j-1)+u(i,j+1))/4.0_DP+heat*h*h
end do
end do
u(1:m,1:m)=un(1:m,1:m)
if (mod(n,100)==0) call print__profile(u(:,m/2+1))
end do
deallocate(u,un)
end program heat1_animation_x_prof
* gnuplotによるアニメーション [#s79cd920]
gnuplotのスクリプトを使えばアニメーションも簡単にできる。
ただし、コマ数が多くなると手でスクリプトを書くのは難しくなるので、
「gnuplotスクリプト自動生成プログラム」を作ったほうがよい。
以下はその例である。
* &color(#0000ff){【演習】1次元グラフ アニメーション}; [#e7336907]
以下のコードを
heat1_animation_x_prof_gp_generator.f90
というファイルに保存し、ソースコードの冒頭にあるUsageに従ってgnuplotによる可視化アニメーションを作れ。
!
! heat1_animation_x_prof_gp_generator.f90
!
! by Akira Kageyama
! at Kobe University
! on 2010.07.07
! for data visualization of heat1_animation_x_prof.f90
! in the Lecture "Computational Science" (2010)
!
! Usage:
! (1) check the mesh size m and the counter_end.
! (2) pgf95 this_code
! (3) ./a.out > anyname
! (4) gnuplot anyname
!
program heat1_animation_x_prof_gp_generator
implicit none
integer, parameter :: m = 61
integer :: counter, counter_end = 50
character(len=*), parameter :: base='../data/temp.j=middle.'
character(len=4) :: serial_num
print *, "#"
print *, "# gnuplot script generated by heat1_animation_x_prof_gp_generator.f90"
print *, "#"
print *, " "
print *, "set xlabel 'j' # x-axis"
print *, "set ylabel 'temperature' # y-axis"
print *, "set xrange[0:", m+1, "] # j-grid min & max"
print *, "set yrange[0.0:0.5] # temperature min & max"
do counter = 0 , counter_end
write(serial_num,'(i4.4)') counter
print *, "plot '"//base//serial_num//"' w lp"
if ( counter==0) then
print *, "pause 5"
else
print *, "pause 1"
end if
end do
print *, "pause -1'
end program heat1_animation_x_prof_gp_generator
* 熱伝導問題コードのリファクタリング [#pf7cac38]
1次元的な可視化(グラフ)はこのように簡単である。
これ以降、次に正方形領域の温度場全体を表示する2次元的な可視化を行う。
ここでもやはりgnuplotを使うが、可視化機能の追加だけでなく、これからの演習では、これまで学んだ
y方向のみの分割による並列化(1次元領域分割による並列化)よりももう少し実際的な2次元領域分割による並列化と、さらには、3次元熱伝導問題を解く問題にも挑戦したい。
したがって、今後扱うコードは少しづつ複雑化していくので、ここでまずはheat2.f90(並列版コード)のリファクタリングをしておこう。
以下のコードをthermal_diffusion.f90という名前で保存せよ。
!
! thermal_diffusion.f90
!
module constants
implicit none
integer, parameter :: SP = kind(1.0)
integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
integer, parameter :: MESH_SIZE = 61
integer, parameter :: LOOP_MAX = 10000
end module constants
module parallel
use constants
use mpi
implicit none
private
public :: parallel__initialize, &
parallel__finalize, &
parallel__sendrecv, &
parallel__set_prof_1d, &
parallel__set_prof_2d, &
parallel__tellme
integer :: nprocs
integer :: myrank, left, right
integer :: jstart, jend
contains
subroutine parallel__finalize
integer :: ierr
call mpi_finalize(ierr)
end subroutine parallel__finalize
subroutine parallel__initialize
integer :: ierr
call mpi_init(ierr)
call mpi_comm_size(MPI_COMM_WORLD, nprocs, ierr)
call mpi_comm_rank(MPI_COMM_WORLD, myrank, ierr)
left = myrank - 1
right = myrank + 1
if ( myrank==0 ) left = nprocs-1
if ( myrank==nprocs-1 ) right = 0
jstart = MESH_SIZE * myrank / nprocs + 1
jend = MESH_SIZE * (myrank+1) / nprocs
end subroutine parallel__initialize
subroutine parallel__sendrecv(send_vector,rank_target, &
recv_vector,rank_source)
real(DP), dimension(:), intent(in) :: send_vector
integer, intent(in) :: rank_target
real(DP), dimension(:), intent(out) :: recv_vector
integer, intent(in) :: rank_source
integer, dimension(MPI_STATUS_SIZE) :: istat
integer :: tag_dummy = 100
integer :: n, ierr
n = size(send_vector,dim=1)
call mpi_sendrecv(send_vector, &
n, &
MPI_DOUBLE_PRECISION, &
rank_target, &
tag_dummy, &
recv_vector, &
n, &
MPI_DOUBLE_PRECISION, &
rank_source, &
tag_dummy, &
MPI_COMM_WORLD, &
istat, &
ierr)
end subroutine parallel__sendrecv
function parallel__set_prof_1d(jstt_,jend_,myprof) result(prof_1d)
integer, intent(in) :: jstt_
integer, intent(in) :: jend_
real(DP), dimension(jstt_:jend_), intent(in) :: myprof
real(DP), dimension(0:MESH_SIZE+1) :: prof_1d
real(DP), dimension(0:MESH_SIZE+1) :: work
integer :: ierr
work(:) = 0.0_DP
work(jstt_:jend_) = myprof(jstt_:jend_)
call mpi_allreduce(work, & ! source
prof_1d, & ! target
MESH_SIZE+2, &
MPI_DOUBLE_PRECISION, &
MPI_SUM, &
MPI_COMM_WORLD, &
ierr)
end function parallel__set_prof_1d
function parallel__set_prof_2d(jstt_,jend_,myprof) result(prof_2d)
integer, intent(in) :: jstt_
integer, intent(in) :: jend_
real(DP), dimension(0:MESH_SIZE+1,jstt_:jend_), &
intent(in) :: myprof
real(DP), dimension(0:MESH_SIZE+1,0:MESH_SIZE+1) :: prof_2d
real(DP), dimension(0:MESH_SIZE+1,0:MESH_SIZE+1) :: work
integer :: ierr
integer :: meshsize_p1_sq = (MESH_SIZE+1)**2
work(:,:) = 0.0_DP
work(:,jstt_:jend_) = myprof(:,jstt_:jend_)
call mpi_allreduce(work(1,1), & ! source
prof_2d(1,1), & ! target
meshsize_p1_sq, &
MPI_DOUBLE_PRECISION, &
MPI_SUM, &
MPI_COMM_WORLD, &
ierr)
end function parallel__set_prof_2d
function parallel__tellme(which) result(val)
character(len=*), intent(in) :: which
integer :: val
select case (which)
case ('jend')
val = jend
case ('jstart')
val = jstart
case ('left')
val = left
case ('right')
val = right
case ('myrank')
val = myrank
case ('nprocs')
val = nprocs
end select
end function parallel__tellme
end module parallel
module temperature
use constants
use parallel
implicit none
private
public :: temperature__initialize, &
temperature__finalize, &
temperature__output_1d_profile, &
temperature__output_2d_profile, &
temperature__update
real(DP), allocatable, dimension(:,:) :: temp
real(DP), allocatable, dimension(:,:) :: work
real(DP), parameter :: SIDE = 1.0_DP
real(DP) :: h = SIDE / (MESH_SIZE+1)
real(DP) :: heat
integer :: jstart, jend
integer :: myrank, right, left, nprocs
!
! |<----------- SIDE ---------->|
! | |
! |<-h->| |
! 0 1 2 ... MESH_SIZE+1
! jend+1 +-----+-----+-----+-----+-----+
! | | | | | |
! | | | | | |
! jend +-----+-----+-----+-----+-----+
! | | | | | |
! .
! . temp(0:MESH_SIZE,0:jend+1)
! .
! | | | | | |
! jstart +-----+-----+-----+-----+-----+
! | | | | | |
! | | | | | |
! jstart-1 +-----+-----+-----+-----+-----+
!
contains
!----------------
!--- private ---
!----------------
subroutine set_jstart_and_jend(jstart_, jend_)
integer, intent(out) :: jstart_, jend_
if ( myrank==0 ) then
jstart_ = 0
jend_ = jend
else if ( myrank==nprocs-1 ) then
jstart_ = jstart
jend_ = MESH_SIZE+1
else
jstart_ = jstart
jend_ = jend
end if
end subroutine set_jstart_and_jend
!--------------
!--- public ---
!--------------
subroutine temperature__initialize
real(DP) :: heat_source = 4.0
jstart = parallel__tellme('jstart')
jend = parallel__tellme('jend')
myrank = parallel__tellme('myrank')
right = parallel__tellme('right')
left = parallel__tellme('left')
nprocs = parallel__tellme('nprocs')
allocate(temp(0:MESH_SIZE+1,jstart-1:jend+1))
allocate(work( MESH_SIZE, jstart:jend) )
heat = (heat_source/4) * h * h
temp(:,:) = 0.0_DP ! initial condition
end subroutine temperature__initialize
subroutine temperature__finalize
deallocate(work,temp)
end subroutine temperature__finalize
subroutine temperature__output_1d_profile
real(DP), dimension(0:MESH_SIZE+1) :: prof
integer :: counter = 0 ! saved
integer :: ierr ! use for MPI
integer :: jstart_, jend_
character(len=4) :: serial_num ! put on file name
character(len=*), parameter :: base = "../data/temp.i=middle."
integer, parameter :: icut = MESH_SIZE/2+1 ! center
! nprocs = 3
! MESH_SIZE = 9
!
! 0 1 2 3 4 5 6 7 8 9 10
! +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
! |<=====rank0=====>| |<==rank1==>| |<=====rank2=====>|
! jstart_ | jend_ | | | | |
! | jstart jend jstart_ jend_ | | |
! | | | jstart jend jstart_ | jend_
! | | | | | jstart jend |
! | | | | | | | |
! +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
!
integer :: j
call set_jstart_and_jend(jstart_,jend_)
write(serial_num,'(i4.4)') counter
prof(:) = parallel__set_prof_1d(jstart_,jend_,temp(icut,jstart_:jend_))
if ( myrank==0 ) then
open(10,file=base//serial_num)
do j = 0 , MESH_SIZE+1
write(10,*) j, prof(j)
end do
close(10)
end if
counter = counter + 1
end subroutine temperature__output_1d_profile
subroutine temperature__output_2d_profile
real(DP), dimension(0:MESH_SIZE+1, &
0:MESH_SIZE+1) :: prof
integer :: counter = 0 ! saved
integer :: ierr ! use for MPI
integer :: jstart_, jend_
character(len=4) :: serial_num ! put on file name
character(len=*), parameter :: base = "../data/temp.2d."
integer :: i, j
call set_jstart_and_jend(jstart_,jend_)
write(serial_num,'(i4.4)') counter
prof(:,:) = parallel__set_prof_2d(jstart_,jend_,temp(:,jstart_:jend_))
if ( myrank==0 ) then
open(10,file=base//serial_num)
do j = 0 , MESH_SIZE+1
do i = 0 , MESH_SIZE+1
write(10,*) i, j, prof(i,j)
end do
write(10,*)' ' ! gnuplot requires a blank line here.
end do
close(10)
end if
counter = counter + 1
end subroutine temperature__output_2d_profile
subroutine temperature__update
integer :: i, j
call parallel__sendrecv(temp(1:MESH_SIZE,jend), right, &
temp(1:MESH_SIZE,jstart-1), left )
call parallel__sendrecv(temp(1:MESH_SIZE,jstart), left, &
temp(1:MESH_SIZE,jend+1), right )
if ( myrank==0 ) temp(1:MESH_SIZE,0 ) = 0.0_DP
if ( myrank==nprocs-1 ) temp(1:MESH_SIZE,MESH_SIZE+1) = 0.0_DP
!
! Time development form of the thermal diffusion equation is
! \partial T(x,y,t) / \partial t = \nabla^2 T(x,y,t) + heat_source.
! In the stationary state,
! \nabla^2 T(x,y) + heat_source = 0.
! The finite difference method with grid spacings dx and dy leads to
! (T(i+1,j)-2*T(i,j)+T(i-1,j))/dx^2
! + (T(i,j+1)-2*T(i,j)+T(i,j-1))/dy^2 + heat_source = 0.
! When dx=dy=h,
! T(i,j) = (T(i+1,j)+T(i-1,j)+T(i,j+1)+T(i,j-1))/4+heat_source*h^2/4.
! This suggests a relaxation method by the following
! update procedure called Jacobi method;
do j = jstart, jend
do i = 1, MESH_SIZE
! work(i,j) = (temp(i-1,j)+temp(i+1,j)+temp(i,j-1)+temp(i,j+1))/4.0_DP &
! + heat_source*h*h ! Operation time is saved in below.
work(i,j) = (temp(i-1,j)+temp(i+1,j)+temp(i,j-1)+temp(i,j+1))*0.25_DP &
+ heat
end do
end do
temp(1:MESH_SIZE,jstart:jend) = work(1:MESH_SIZE,jstart:jend)
end subroutine temperature__update
end module temperature
program thermal_diffusion
use constants
use parallel
use temperature
implicit none
integer :: loop
call parallel__initialize
call temperature__initialize
call temperature__output_1d_profile
call temperature__output_2d_profile
do loop = 1 , LOOP_MAX
call temperature__update
if ( mod(loop,100)==0 ) call temperature__output_1d_profile
if ( mod(loop,100)==0 ) call temperature__output_2d_profile
end do
call temperature__finalize
call parallel__finalize
end program thermal_diffusion
読みやすくなるように書き換えただけで、コードの内容は本質的にはheat2.f90と同じである。
(変数名も一部変えている。)
ただし、可視化するためのデータ書き出しルーチン
call temperature__output_1d_profile
call temperature__output_2d_profile
を二つ追加している。(main プログラムのdo-loopの中。)
サブルーチンtemperature__output_1d_profileでは正方形を真ん中で縦に切る線上(x=1/2)での温度の分布を出力する。
つまり、yが0から1.0までの温度を出力するわけであるが、このコード(もともとのheat2.f90と同じ)ではy方向に領域分割して並列化をしているので、
温度のデータは各プロセスが個別の持っている。
それを集約してprof(:)という1次元配列(添字の範囲はj=0からMESH_SIZE+1まで)に収めている。
ここでMPI_ALLREDUCE関数を利用している。
参考までにこのジョブを計算機「scalar」に投入するためのジョブスクリプトのサンプルを示す:
#PBS -l cputim_job=00:05:00
#PBS -l memsz_job=2gb
#PBS -l cpunum_job=1
#PBS -T vltmpi
#PBS -b 16
#PBS -q PCL-A
cd /home/users/YournID/YourDirectory
mpirun_rsh -np 16 ${NQSII_MPIOPTS} ./a.out
このジョブスクリプトをthermal_diffusion.jsというファイルに保存すれば、
qsub thermal_diffusion.js
でscalarに投入できる。
* 宿題 [#uac45d4e]
上記 thermal_diffusion.f90コードを理解しておくこと。(レポート提出必要なし。)
* 授業アンケート [#x8e7e151]
今回の演習内容はどうでしたか?(どれか一つ、一度だけ押してください。)
#vote(簡単すぎた[0], 難しすぎた[1], ちょうどよかった[3])
* 質問、コメントなど自由にどうぞ [#b77967bf]
「お名前」欄は空欄で可。
#pcomment
------------------------------
as of &_now; (&counter;)