포트란 90, 기본 by 바죠


컴퓨터 언어 포트란 90이 필요한 이유: 간단하게 말하면 실행속도 때문이다. 포트란 컴퓨터 언어는 계산용으로 특화되어 있다. 장차 병렬 컴퓨팅에서도 유리하다. 병렬 컴퓨팅은 해당 순차 프로그램의 실행 속도가 느리기 때문에 취하는 방편이다. 따라서 계산이 느린 컴퓨터 언어를 이용해서 병렬 컴퓨팅을 한다는 것은 앞뒤가 맞지 않는 상황이다. 계산속도에 관한한 포트란, C 언어를 빼놓고 이야기 할 수 없다. 이것은 움직이지 않는 사실이다. 엄청난 계산량과 관련되어 있는 프로그램들이 모두 C언어, 포트란 언어로 되어 있는 이유이다. 과학기술용 킬러 응용 프로그램들이 대부분 C 언어 또는 포트란으로 개발된 이유이다. 슈퍼컴퓨팅으로 가기 위해서는 어쩔수 없다. 슈퍼컴퓨터에서 실제로 실행되고 있는 대부분의 생산 프로그램들은 포트란, C 언어에 기반하고 있고 병렬화되어 사용되어지고 있다. 그렇지 않을 수도 있지만, 대부분이 그렇다는 뜻이다. 응용 영역에 따라서 항상 가장 적합한 컴퓨터 언어를 정할 수 있다. 이것은 사실이다. 왜냐하면, 특정 언어는 특정한 일에 특화되어 개발되기 때문이다.

예를 들어, 계산량이 방대한 경우에 해당하는 양자화학, 전자구조 계산 프로그램들은 대부분 포트란 90 또는 C 언어로 만들어져 있다. 이들 대부분의 경우, MPI 기반의 병렬화까지 지원된다. 포트란 또는 C 언어를 사용하지 않으면 실행 속도에서 너무나도 큰 차이가 발생하게 된다. 

포트란 90에서 가능한 문법들과 매우 편리한 계산 방법들을 몇 가지 나열했다. 이것은 기본 키워드들의 나열과 비슷한 양태를 나타내고 있다. 계산을 위한 많은 직관적인 장치들이 많이 도입되었다. 포트란 90 에서는 특별히, 벡터, 행렬들의 취급이 매우 용이해졌다고 볼 수 있다. 포트란 90에는 행렬, 벡터 전체를 한꺼번에 취급하는 기능이 있다. 포트란 90은 수학적 의미 전달이 쉽게 되는 컴퓨터 언어이다. 포트란 77 형식의 프로그래밍 기법과 비교하여 do 문이 확실히 줄어 들 수 있음을 알 수 있다. 왜나하면 많은 do 루프들이 행렬, 벡터 표현 형식으로 바뀌기 때문이다. 매우 간결해진 수식 표현들을 사용함으로써 가독성을 높일 수 있고 관련된 실수들을 줄일 수 있다. 아울러, 복소수 취급이 간결하고 직관적이다. 당연히, 행렬, 벡터에도 직관적인 복소수 계산이 가능하다.  

포트란 77을 사용할 때 보다, 포트란 90을 사용하면 응용 소스코드의 길이가 확실히 줄어듬을 알 수 있다. 훨씬 간결하게 명확하게 일들을 수행할 수 있다. 물론, 어처구니 없는 말도 안되는 실수도 할 수 있겠다. 포트란 90은 계산에 더욱더 특화되어 있다. 프로그래머가 계산과 관련된 일을 최대한 간결하게 처리할 수 있게 해준다. 다양한 기본 함수들이 이미 개발되어 언어의 일부로 제공된다. 또한, 배열, 행렬, 변수 (실수, 복소수) 등에 자동으로 적용되어 사용이된다. 수 십년간 개발 완료된 포트란 77 서브루틴들은 고스란히 포트란 90에서 사용할 수 있다. 전혀 바꿀 필요가 없다. 그대로 사용 가능하다. 이것은 포트란 언어의 엄청난 장점이다. 생산성 향상으로 이어진다.  


이러한 기본적인 계산방식의 습득과 모듈을 활용한 프로그래밍 기법을 소화할 수 있다면, 포트란 90 프로그래밍을 이해했다고 할 수 있다. http://incredible.egloos.com/2950367

포트란 90을 활용한 프로그래밍의 핵심은 모듈을 활용하는 것이다. 이러한 모듈의 활용은 장기적으로 활용도 높은 프로그래밍을 구축할 수 있게 해준다. 그 다음은 너무도 간단한 포트란 90만의 간결한 수식 표현 방식을 채택하는 것이다. 사실 모듈의 활용에서는 변수들의 선언, 동적인 배열들의 선언을 포함하기가 자연스러우며 모듈속의 변수들과 배열들을 전적으로 관리하는 서브루틴들 함수들을 일반적으로 포함한다. 아래는 통상적인 모듈의 한 예를 도식적으로 나타내었다.

모듈은 포트란 77에서 사용하는 common문과 성격이 비슷하다고 일단 생각할 수 있다. named common 문은 좀더 여러개로 나누어진 common 문이다. 특별한 무리들의 변수들을 특정한 이름하에서 공동으로 사용할 수 있게 한 것이 named common 문이다. 이러한 named common문을 가장 현명하게 사용하는 방법은 include 를 사용하여 한 파일에서 named common문을 작성하고 이 파일을 관련된 프로그램에서 포함시켜서 사용하는 것이였다.

모듈에서는 특정한 무리들의 변수들 뿐만 아니라 이들 변수들과 관련된 계산들을 수행하는 부프로그램들을 동시에 관리한다. 특정 무리의 변수들과 관련된 작업만을 전문적으로 모듈 내부에서 수행할 수 있게 때문에 자연스럽게 해당 프로그램이 간결해지고 특정 작업에만 집중할 수 있게 된다. 우선 공통으로 사용되는 변수들은 모듈의 상단에서 선언되기 때문에 모듈내에서 존재하는 키워드 contains 아래쪽에 배치된 부프로그램들에서 쉽게 참조할 수 있다. 공통으로 선언된 변수들 중에 가변배열이 있다면 이들 가변배열들을 활성화 또는 소멸시키는 부프로그램들도 자연스럽게 만들 수 있다. contains 아래에 나오는 부프로그램들도 그 부프로그램 안에서만 사용되는 지역변수들을 당연히 둘 수 있다. 

모듈 외부에서 참조/변형이 가능한 변수들은 public 성질을 가져야 한다. 이들은 public 하다고 선언을 해줘야 한다. 사실은 public 이 디폴트이다. 하지만, 모든것이 private이라고 선언하고 직접적으로 선발해서 public 으로 구체적으로 적어 주는것이 더 명확하다고 볼 수 있다. 물론, 간단한 경우에는 public, private라는 용어 자체를 사용하지 않음으로써 모두 public이라는 것을 간단히 표시할 수 있다. public, 이게 디폴트이니까 그렇다.

모듈 단위로 프로그램이 완성되고 테스트를 마쳤다면, 기존의 다른 프로그램과 연동될 때, 예기치 않은 상황들을 사전에 차단할 수 있다.  단순히 접함을 시도하면 너무 많은 변수들과 함수들의 혼재로 굉장히 세밀하게 들여다 봐야한다. 하지만, USE, ONLY를 사용하면 실제로 필요한 정보들만 정확히 전달 되는가를 확인하면된다. 그 이외의 것들은 모두 숨겨져 있어서 '안전'하다. 각각의 독립성을 유지할 수 있다. 차후 추가적인 작업들도 독립된 모듈 내에서만 진행할 수 있다.

module abcd_mod
implicit none
private
save
real*8, allocatable :: dynamv(:,:), dynamw(:)
integer, allocatable :: jjmm(:)
integer ixyz, jin
real*8 qzz
public :: intial_ab, final_ab, opera_ab, dynamv  ! 참조 가능한 부프로그램들 이름. 참조/변형가능한 변수들

contains

subroutine initial_ab(....)  ! 예를 들어, 메인 프로그램에서 참조가능한 부프로그램이라면 메인에서 인수들을 받을 수 있다. 인수가 없는 것도 가능하다.
implicit none

contains위에 선언된 변수들은 아래의 모듈 내에 존재하는 다른 부프로그램들에서와 마찬가지로 그냥 참조/변형가능하다.
.......
end subroutine initial_ab

subroutine final_ab(....)
implicit none
자체 지역 변수를 선언하여 사용할 수 있다.
.............
end subroutine final_ab

..........
real*8 function abcd(...)
implicit none
함수도 부프로그램이다.
............
end function abcd

......
..............
......................
end module abcd_mod
module constants
  implicit none
  real, parameter :: pi=3.141592e0
  real, parameter :: e=2.71828183
  integer, parameter :: answer=42
  real, parameter :: earthRadiusMeters=6.38e6
end module constants

program test
! Option #1:  blanket "use constants"
!  use constants
! Option #2:  Specify EACH variable you wish to use.
  use constants, only : pi,e,answer,earthRadiusMeters
  implicit none

  write(6,*) "Hello world.  Here are some constants:"
  write(6,*) pi, e, answer, earthRadiusMeters
end program test



모듈, 프로그램, 부프로그램, 함수들에서 제일 중요한 것은 IMPLICIT NONE위에 USE 표현들이 온다는 것이다. USE가 여러개 일 수도 있다. 이렇게 사용된 USE와 관련된 모듈들은 지금의 모듈, 프로그램 보다 더 먼저 컴파일되어야 한다는 점에서 왜 현재 프로그램에 있는 implicit none 보다 먼저 선언되는지를 이야기 한다고 볼 수도 있다.

통상 모듈에서필요한 USE관련된 것들의 선언이 끝나면, implicit none, private, save로 이어진다고 보면된다. 늘 이렇게 적는 것이 편리하다.
그 다음, 원하는 선언들을 죽죽하고 마지막에 public이라는 키워드로, 현 모듈 밖에서 참조/변형 가능한 변수들, 부프로그램들의 리스트를 작성해 둔다. 너무 길면, public이라는 키워드를 두, 세번 사용해서 선언하면 된다.

외부 부프로그램, 프로그램, 모듈에서 함수나 변수를 참조할 때:

USE abc_mod, ONLY: var_name1, vari_na

처럼 ONLY를 사용하는 것이 좋다.

그냥 단순히

USE abc_mod

을 사용하는 것 보다는. 훨씬 구체적으로 접근하고 있다는 것이다. 프로그램을 작성할 때, 읽을 때에도 많은 도움이 된다. 이렇게 함으로써 더욱더 프로그램의 구조와 역할 분담이 명확해진다. 프로그램 개발과정에서 프로그램 설계자체를 바꿀수 있게 도와주기도 한다.

자, 그럼 기본 함수들을 한번 볼까요?
!234567890
       PROGRAM xxx_1
       IMPLICIT NONE
       INTEGER, PARAMETER :: idp=KIND(0.d0)  ! 이후에 사용될 변수들의 정밀도를 정하는 데 활용될 것이다.
!      CHARACTER*8 fnnd ; CHARACTER*10 fnnt
       CHARACTER(8) fnnd ; CHARACTER(10) fnnt
       LOGICAL ll1,ll2
       INTEGER ii,jj,i,nn
       INTEGER :: kk,npt
!
       REAL*8 aa,pi                                   ! 기존의 포트란77 프로그램들과 연동될 때 이용하면 아주 자연스럽다.
       REAL(8) bb,dx,xx
       REAL(idp) cc
       REAL(KIND=idp) dd
       REAL(KIND=idp) :: ee
       REAL(8) ff
!
       COMPLEX(idp) zz1,zz2,zz3             ! 복소수의 double precision이라고 볼 수 있다.
       COMPLEX(idp) :: zz4
!
       REAL(8), ALLOCATABLE :: xvector(:),xmatrix(:,:)   ! 프로그램 실행중에 생성될 실수형 배열들이다. 차원은 미리 정해 줘야 한다.
       REAL(8), ALLOCATABLE :: yvector(:)
       REAL(8), ALLOCATABLE :: ymatrix(:,:)
       REAL(8), ALLOCATABLE :: qvector(:)
!
       REAL(8) :: sma(3)
       REAL(8) smb(3)
!
       CALL DATE_AND_TIME(DATE=fnnd,TIME=fnnt)             ! 실행 년/월/일/시간을 알아내는 데 활용됨.
       WRITE(6,*) '____________ ',' date ',fnnd,' time ',fnnt,'____________'
!
       INQUIRE(FILE='input_file',EXIST=ll1)     ! 파일명이 input_file 인것이 존재하는지를 물어보는 표현, 존재하면 ll1값이 .true. 가 될것이다.
!
       pi=4.0d0*atan(1.0d0)
       WRITE(6,*) 'pi ',pi
       pi=acos(-1.0d0)
       WRITE(6,*) 'pi ',pi
     
       IF(ll1)THEN
       WRITE(6,*) 'file is present'
       ELSE
       WRITE(6,*) 'file is absent'
       ENDIF
 
       WRITE(6,*) idp,' idp'
       ll1=.TRUE.
       ll2=.FALSE.
       WRITE(6,*) ll1,' ll1'
       WRITE(6,*) ll2,' ll2'
       ii=0
       jj=1
       kk=-1
       ll1= (1 == 2)
       ll2= (1 /= 2)
       IF(ii >= jj) ll1=.TRUE.
       IF(ii == jj) ll2=.FALSE.
       WRITE(6,*) ii,' ii'
       WRITE(6,*) jj,' jj'
       aa=0.0_idp                    ! note
       bb=0.0d0
       cc=0.0d0
       dd=0.0d0
       zz1=0.0_idp
       zz2=CMPLX(1.0_idp,2.0_idp)    ! note
       zz3=zz1+zz2
       zz3=zz1-zz2
       zz3=zz1*zz2
       zz3=zz1/zz2
       zz3=zz1**2
       aa=REAL(zz3)
       bb=IMAG(zz3)
       cc=ABS(zz3)
       zz4=CONJG(zz3)
       WRITE(6,*) aa,' aa'
       WRITE(6,*) bb,' bb'
       WRITE(6,*) cc,' cc'
       WRITE(6,*) dd,' dd'
       WRITE(6,*) zz1,' zz1'
       WRITE(6,*) zz2,' zz2'
       OPEN(1,FILE='output1',FORM='FORMATTED')
       WRITE(1,'(i8)') ii
       WRITE(1,'(2i8)') ii,jj
       WRITE(1,'(i8,2x,i8)') ii,jj
       WRITE(1,'(i8,2x,i8,3x,a5)') ii,jj,'ii,jj'
       WRITE(1,*) aa,' aa'
       WRITE(1,*) bb,' bb'
       WRITE(1,*) cc,' cc'
       WRITE(1,*) dd,' dd'
       WRITE(1,*) zz1,' zz1'
       WRITE(1,*) zz2,' zz2'
       CLOSE(1)
       OPEN(2,FILE='output2',FORM='FORMATTED')
       WRITE(2,'(i8)') ii
       WRITE(2,'(2i8)') ii,jj
       CLOSE(2)
       sma=0.0d0
       smb=1.0d0
       ii=10
       jj=10
       nn=ii
       ALLOCATE(xvector(nn)) ; ALLOCATE(yvector(nn))
       ALLOCATE(xmatrix(nn,nn))
       ALLOCATE(ymatrix(nn,nn))
       ALLOCATE(qvector(0:nn))  !  시작하는 인수와 끝나는 인수가 각각 0, nn 인 경우이다.
       IF(ALLOCATED(xvector))THEN
       WRITE(6,*) 'xvector is allocated'
       WRITE(6,*) 'size ', SIZE(xvector)
       DO i=1,nn
       xvector(i)=FLOAT(i)
       ENDDO
       yvector=-xvector    ! 각 성분들에 -1.을 곱한 다음 그대로 복사한다.
       ENDIF
       WRITE(6,*) 'sum ', SUM(xvector)    ! 성분들의 합을 반환한다.
       WRITE(6,*) 'sum ', SUM(yvector)
       yvector=SQRT(ABS(xvector))           ! 각 성분들의 절대값에 0.5 승을 계산하여 새로운 벡터의 성분으로 대입.

       IF(ALLOCATED(xmatrix))THEN
       WRITE(6,*) 'xmatrix is allocated'
       WRITE(6,*) 'size ', SIZE(xmatrix)
       ENDIF
       xvector=0.0d0
       yvector=0.0d0
       xmatrix=0.0d0   ! 모든 행렬 성분이 0.d0으로 초기화 됨.
       ymatrix=TRANSPOSE(xmatrix)
       ymatrix(:,1)=yvector(:)
       yvector=MATMUL(xmatrix,xvector)   ! 행렬, 벡터 곱하기, 통상적인 M1 * M2도 당연히 된다.
       dd=DOT_PRODUCT(xvector,yvector) ! 통상적인 내적, inner product
       dd=MAXVAL(xvector)                      ! 벡터 성분중에서 가장 큰 값을 반환
       ee=MINVAL(yvector)                       ! 가장 작은 성분을 반환
       dd=MAXVAL(ABS(xvector))             ! 절대값 기준으로 가장 큰 성분을 반환
       dd=MAXVAL(ABS(xvector-yvector)) ! 두 벡터의 차이 벡터를 계산하고 그 벡터의 절대값 기준 인수 중에서 최대값을 반환
       dd=MAXVAL(ABS(xvector-yvector))/(MAXVAL(ABS(xvector))+MAXVAL(ABS(yvector))+1.0d-8)
       aa=SUM(xvector)/FLOAT(SIZE(xvector))
       bb=PRODUCT(yvector)   ! 모든 성분들의 연이은 곱하기 가 반환됨
       DEALLOCATE(xvector) ; DEALLOCATE(yvector)
       DEALLOCATE(xmatrix)     ! 할당된 변수를 메모리 상에서 없애 버림. 따라서, 해당 변수는 더이상 사용할 수 없다.
       DEALLOCATE(ymatrix)
       DEALLOCATE(qvector)
       INQUIRE(FILE='del',EXIST=ll1)
       IF(LL1)THEN
       WRITE(6,*) 'del is present'
       OPEN(11,FILE='del')
       CLOSE(11,STATUS='DELETE')   ! 파일 지우기를 실행함.
              ENDIF
       aa=0.0d0
       bb=1.0d0
       npt=100000000
       dx=(bb-aa)/float(npt-1)
       cc=0.0d0
       DO i=1,npt
       xx=aa+float(i-1)*dx
       cc=cc+ff(xx)
       ENDDO
       cc=cc*dx
       write(6,*) cc,' cc'
       STOP
       END PROGRAM xxx_1
       FUNCTION ff(x)
       REAL(8) ff
       REAL(8) x
       ff=x*x
       RETURN
       END FUNCTION ff

  ----------------------------------------

재미 있는 DOT_PRODUCT의 성질:
아래의 예제에서와 같이 DOT_PRODUCT(vec1,vec2)는 SUM(vec1*vec2)와 같다. 이 때, vec1, vec2는 길이가 같은 실수형 벡터이다.  vec1(1)*vec2(1)+vec1(2)*vec2(2)+vec1(3)*vec2(3)+......
zvec1,zvec2가 각각 complex형일 경우는, DOT_PRODUCT(zvec1,zvec2)는 SUM(CONJG(zvec1)*zvec2) 와 같다 물론, 복소수 형으로 계산값이 반환된다. DOT_PRODUCT 이 친구 약간 오버하는 경향이 있다. 누가 conjg를 취하라고 했어?

그럼, lvec1, lvec2처럼 logical 자료형일 경우는 어떻게 될까?
(l1(1) .and. l2(1)) .or. (l1(2) .and. l2(2)) .or. ............이 그 정답이다.
       implicit none
       integer j, k,i
       real*8 r1(3),r2(3),r3(3)
       complex*16 z1(3),z2(3)
       logical l1(3),l2(3)

       r1(1)=1.d0
       r1(2)=1.d0
       r1(3)=1.d0
       l1(1)=.true.
       l1(2)=.false.
       l1(3)=.false.
       l2=(.not. l1)
       r2=-r1
       z1(1)=cmplx(0.0d0,1.0d0)
       z1(2)=cmplx(0.0d0,1.0d0)
       z1(3)=cmplx(0.0d0,1.0d0)

       z2=conjg(z1)
       write(6,*) sum(r1*r2) ,'SUM(r1*r2)'
       write(6,*) dot_product(r1,r2) ,' r1 dot r2'
       write(6,*) dot_product(z1,z1),' z1 dot z1'
       write(6,*) dot_product(z2,z2),' z2 dot z2'
       write(6,*) dot_product(z1,z2) ,' z1 dot z2'
       write(6,*) sum(conjg(z1)*z2) ,'SUM(conjg(z1)*z2)'
       write(6,*) dot_product(l1,l2) ,' l1 dot l2'
       write(6,*) (l1(1) .and. l2(1)) .or.  (l1(2) .and. l2(2)) .or.  (l1(3) .and. l2(3))
       stop
       end

   -3.000000000000000      SUM(r1*r2)
   -3.000000000000000       r1 dot r2
 (3.000000000000000,0.0000000000000000)  z1 dot z1
 (3.000000000000000,0.0000000000000000)  z2 dot z2
 (-3.000000000000000,0.0000000000000000)  z1 dot z2
 (-3.000000000000000,0.0000000000000000) SUM(conjg(z1)*z2)
  F  l1 dot l2
  F
FORTRAN STOP

       implicit none
       integer j,i
       integer narr,mref
       real*8, allocatable ::  xarr(:,:)
       real*8, allocatable ::  yarr(:,:)
       real*8, allocatable ::  zarr(:,:)
       real*8, allocatable ::  warr(:,:)
       logical lexist

       narr=3
       mref=3

       allocate(xarr(narr,mref),yarr(narr,mref),zarr(narr,mref))
       allocate(warr(narr,mref))

       do i=1,narr
       do j=1,mref
       xarr(i,j)=float(i*j)
       yarr(i,j)=1.d0/float(i*j)
       enddo
       enddo

       zarr=xarr*yarr                  ! 통상적인 행렬 곱하기가 아니다, 각 성분별로 곱하기를 시행한다.
       warr=
matmul(xarr,yarr)     ! 통상적인 행렬 곱하기, 정말 간결한 표현이다.

       write(6,*) zarr
       write(6,*)
       write(6,*) warr


       deallocate(xarr,yarr,zarr)
       deallocate(warr)
       stop
       end

    1.000000000000000         1.000000000000000         1.000000000000000     
    1.000000000000000         1.000000000000000         1.000000000000000     
    1.000000000000000         1.000000000000000         1.000000000000000    

    3.000000000000000         6.000000000000000         9.000000000000000     
    1.500000000000000         3.000000000000000         4.500000000000000     
    1.000000000000000         2.000000000000000         3.000000000000000    
FORTRAN STOP

----------------------------------------

또 하나 포트란 90에서 흥미로운 것이 배열을 다룰 때, 사용하는 : 의 용도이다. aaa(:) 는 모든 성분을 지칭한다. aaa(7:9) 는 성분, 7,8,9를 표시한다. aaa(7:9)=1.0d0 이런식으로 사용될 수 있다. aaa(7:) 는 무슨뜻일까? 성분들 7,8,9,...끝까지를 표시한다. aaa(:10)  성분으로 보았을 때, 처음부터 10까지 aaa(::2)는 처음부터 끝까지 stride를 2로 해서 aaa(3:9)와 aaa(3:9:1)는 같은 것이다. 마지막 1 이 stride이다.

         program test
         implicit none
         real*8 avec(100,3,0:100)
         real*8 bvec(100,3,0:100)
         avec=1.0d0
         bvec=0.0d0

         bvec(:,:,1:99)=avec(:,:,1:99)
        
         write(6,*) avec(1,1,0)
         write(6,*) avec(1,1,100)
         write(6,*) bvec(1,1,0)
         write(6,*) bvec(1,1,100)

          end
    1.000000000000000    
    1.000000000000000    
   0.0000000000000000    
   0.0000000000000000   

아래와 같은 응용도 가능하다.

rtilde(:,:,2:np-1)= rtilde(:,:,2:np-1)+gam*xfor(:,:,2:np-1)
 

----------------------------------------

 선언문에서
         implicit none
을 사용하는 것이 여러 가지로 유리하다.
하지만, 굳이 아래와 같이
         implicit real*8 (a-h,o-z)
를 사용한다면
         allocatable aaa(:), bbb(:,:,:), ijk(:,:)
등으로도 사용가능하다.
여기서, aaa,bbb는 실수형이고 ijk는 정수형 가변 배열이 선언된 것이다.

가장 복잡하게는 아래와 같이도 된다.
REAL*8, DIMENSION(:), ALLOCATABLE  :: aaa

 

-----------------------------------------

순차 프로그램에서 보다도 병렬 프로그램에서 allocated()의 유용성이 빛난다. 병렬 프로그램의 경우, 선언된 변수들이 특정 노드에서만 allocate되고 deallocate될 필요가 있다. 물론, 어떤 배열들은 모든 노드들에서 allocate되고 deallocate돌 수도 있다.  노드 마다 allocate되 었는지를 확인하고 deallocate시킬 때, 프로그램에 간단하게 allocated문을 사용하면 된다.
if(allocated(abcd)) deallocate(abcd)
위와 같이 프로그램 해 두면, 각 노드의 상황에 따라서 논리적으로 처리된다. 각 노드에서 판단한다. allocate되었으면 deallocate하고 그렇지 않으면 통과.

포트란에서 부프로그램을 부를 때, call abcxd(zz,bb,xx,0.1d0) 처럼 인수에 직접 숫자를 넣어서 부를 수도 있다. 문제는 0.1 과 0.1d0 이 다르다는 것이다. subroutine abcxd에서 선언된 대응 변수의 자료형에 충실하게 맞추어서 사용해야한다. 숫자를 사용할 때.

>=             .ge.

<=              .le.

==              .eq.

/=             .ne.

에서 주의할 점은 >=은 있어도 =>는 없다. 항상 =이 뒤에 나온다고 생각하면 될듯. 물론, >, < 도 필요하면 사용할 수 있다. .gt. .lt.
 

----------------------------------------

컴퓨터 언어를 사용하여 프로그래밍을 한다는 것은 컴퓨터와의 대화이다. 이 대화가 원만히 진행되기 위해서는 컴퓨터가 내 말을 알아 들어야 한다. 당연히, 나도 컴퓨터의 말을 알아 들어야 한다. 이러한 대화를 위해서는 여러 가지 출력물을 컴퓨터로 부터 받아 볼 필요가 있다. 이것이 공식 채널이다. 여러 가지 채널을 가지는 것이 좋다. 당연히 이렇게 계산할 줄 알았지만, 그렇게 계산하지 않고 있는 경우가 허다하다. 대화가 필요하다.
 
정확히 영어가 서툰 상태에서 미국 사람과 대화하는 상황과 같다. 영어가 완벽하지 않을 경우, 주변 상황이 대화의 주제를 잘 설명해 주는 상황이면 전체의 대화는 생각 보다 잘 진행될 수 있다. 다시 말해서, 대화 중간에 대화와 관련된 다양한 정보가 필요하다.

프로그래머 자신이 한 일들이 100 % 옳다고 생각하면, 그 프로그래머가 진행하는 프로그래밍의 진도는 처지기 마련이다. 더 이상의 진도는 없다고 봐야한다. 내가 틀릴 수도 있다는 가정을 늘 해야한다. 이게 디버깅이다. 그래서, 프린트도 해보고, 하나 하나 체크해 나가야 한다. 확실하다고 생각하는것 부터 체크해야 한다. 대개 문제는 예측하지 못하는 곳에서 생기기 마련이다. 당연히 그래야 하는데, 그렇지 못한 경우가 많기 때문이다. 프로그래머가 겸손해져야 프로그래밍의 진도가 나간다. 좋은 프로그래머는 자신이 실수할 수 있다는 가능성을 열어 두는 프로그래머이다.

프로그램의 완성은 여러 가지 단계들을 요구한다. 결코 한번에 이루어지지 않는다. 어쩌면 시작 단계가 가장 쉬울수도 있다. 하지만, 단계 단계를 거치지 않고는 결코 완전한 코드가 완성될 수 없다. 많은 검정의 단계를 거쳐야한다.

아무리 뛰어난 프로그램밍 기술을 가지고 있다고 해도 실수는 할 수 있다. 그리고 간단한 프로그램이 점점 복잡해지고 규모가 커지기 마련이다. 이렇게 시간이 흐르다 보면, 정말 프로그램을 분석할 수 있는 능력에 한계를 느끼게된다. 그리고, 한 달 뒤 1년 뒤에 프로그램을 다시 본다고 생각하면 아찔한 정도가 아니라 황당하기 까지 할 것이다. 이러한 어려움을 해결하기 위해서는 구조적으로, 체계적으로 단계 단계를 밟아나가면서 프로그램을 완성해야 한다. 즉, 중간중간 테스트도 모듈별로 하는 것이 좋다. 체계적인 개발이며 동시에 미래를 위한 투자이다.

프로그램하다 보면 별 희한한 에러들도 존재함을 알게 된다. 초보자들의 경우 타이포에러를 비롯하여 여러 가지 에러들이 있다. 컴파일이 되는 상황에서 에러를 말한다. 차라리 신텍스 에러이면 오히려 반가운것 아닌가? 컴파일이 되고 난 다음에 생각지도 못한 에러들은 많은 시간과 노동력을 요구한다. 그래서 두려운것이다. 계산 중간 단계에서 다양한 출력물을 만들어 볼 필요가 있다.

확실하다고 생각하는 부분을 하나씩 하나씩 점검해 나가는 것이다. 이렇게 하기 위해서는 프로그램으로 부터의 정보의 출력이 필수적이다. 여러개의 파일들을 동시에 이용하는 것이 유리하다. 여러 가지 부류들을 각기 다른 파일에 출력하는 것이 유리하다.

뒤돌아 보면, 과거에는 implicit none도 사실은 포트란에 적용되질 않았었다. implicit none을 선언하면 모든 변수들을 C언어에서 처럼 정의해야한다. 하지만, 최근 포트란에서도 implicit none은 거의 업계 표준처럼 되어 있다. 여전히, i,j,k,l,m,n으로 시작하는 변수는 정수형으로 사용하려고 노력한다. 왜냐하면, 프로그래머가 편하기 때문이다. 나쁘지 않은 습관으로 받아들여진다.

과거에는, implicit real*8 (a-h,o-z)를 열심히 사용했다. 이전에 작성된 라이버러리들을 보면 이러한 양식이 많이 있다. 이것을 이해할 수 있어야 한다. 구체적으로 선언하지 않는한 a로 시작하는 변수들,b로 시작하는 변수들,...h로 시작하는 변수들, ...o로 시작하는 변수들, p로 시작하는 변수들, ...z로 시작하는 변수들은 기본적으로 real*8 의 정밀도를 가지는 실수형 변수들이다. 그렇지 않은 변수들은 구체적으로 선언을 해야한다. 예를 들어, complex*16 zz1, character*8 ffx, logical lob

일면 편리한점이 있다. 하지만, implicit none이 도입된 이상 이러한 시도는 좋은 시도가 아니다. 프로그램을 어렵게 만드는 유형의 프로그램밍으로 보인다. 늘 그렇지는 않지만. 어처구니 없는 타이포를 잡아내지 못한다.

포트란에서는 기본출력 단위는 6번이다. write(6,*)에서 처럼 unit=6 이 기본출력이다.

open(1,file='abcd',form='formatted') 처럼 선언하고 읽거나, 적을 수 있다. read(1,*), write(1,*) 처럼 사용하면 된다.

 

----------------------------------------
fortran library
전문가들이 만들어 놓은 프로그램들을 한 번 볼 필요가 있다.
좋은 작가가 되려면 좋은 작가의 작품들을 많이 보아야 한다.
----------------------------------------
많은 경우, 대부분의 경우, 이미 많은 자료들이 인터넷을 통해서 얻을 수 있다.
이들을 잘 이해하는 것 또한 매우 중요하다. 왜냐하면, 많은 경우 매우 오랜기간 동안 개발된 것이 대부분이다.
많은 테스트를 거쳐서 공개된 것들이기 때문이기도 하다.
----------------------------------------

 


두 개의 포트란 90 튜토리얼을 모셨다. 매우 훌륭한 튜토리얼이라고 생각한다. 서로 다른 스타일로 포트란 90을 설명하고 있어서 동시에 읽어 보면 굉장히 도움이 됨을 알 수 있다.
f90_coursenotes_.pdf
f90_studentnotes_.pdf

KISTI 제공 포트란 책자:
F90_book.pdf




참고 자료:  포트란 90 모듈
http://incredible.egloos.com/2950367



포트란 코딩 폼:

fortran_coding_form.pdf

전설의 포트란 코딩 용지:http://en.wikipedia.org/wiki/Image:FortranCodingForm.agr.jpg



 


A succinct, free guide written by Clive G. Page of the University of Leicester. prof77_fortran77.pdf

Introducing Fortran 95 with coverage of ISO TR 15580 and TR 15581

Program examples

http://www.kcl.ac.uk/kis/support/cit/fortran/f95_book_examples/index.html

--------------------------------------------------------------------------------------------------------------------
http://www.personal.psu.edu/faculty/h/d/hdk/fortran.html
Fortran Resources
and
Fortran 77/90/95 Compilers for Windows and Linux
--------------------------------------------------------------------------------------------------------------------
http://www.dmoz.org/Computers/Programming/Languages/Fortran/

--------------------------------------------------------------------------------------------------------------------http://flash.uchicago.edu/~tomek/htmls/num_meth.html
Numerical methods for Fortran programmers

포트란 컴파일러 (Windows): 
 http://ftp.g95.org/g95-MinGW.exe
SilverFrost FTN95 (Windows):
http://www.silverfrost.com/11/ftn95/ftn95_fortran_95_for_windows.aspx



-----------------------------------------------------------------------
대부분의 경우, 아래와 같은 방식의 프로그램 단위들의 조합이 가능하다. 새로운 프로그램을 개발함에 있어서 이러한 조합이 매우 유용하다. 이렇게 함으로써 한 가지 종류의 일을 전문적으로 처리하는 모듈을 만들고 동시에 테스트할 수 있다. 만드는 것은 테스트하는 것이다. 그냥 만들 수 없다. 테스트하면서 만들어야 진짜 만드는 것이다.

실제 가능한 데이터를 이용하여 실제 계산들을 하나 하나 테스트해야 한다. 테스트가 끝이나면 프로그램이 완성된다. 그 단계가 지나면, 이 프로그램, 모듈 부분은 다른 프로그램에서 쉽게 사용되어 질 수 있다.  그때에는, 다른 프로그램의 한 부분과의 접합에 신경을 써야한다. 그 접합 부분 위주로 또 다른 테스트가 들어가야 할 것이다.

아래와 같이 특정한 일들을 하는 데 필요한 준비, 그 일에 특화 된 배열, 변수등 을 동시에 정의하고 프로그램을 만든다. 아울러, 실제 이 모듈이 사용될 때를 가정하고 실제 데이터를 메인 프로그램에서 제공하여 실제 계산들을 검정한다. 물론, 하나의 모듈은 다른 모듈을 불러서 사용할 수 있다. 하나일 필요가 없다. 여러개의 모듈이 동시에 불려져서 사용될 수 있다.

module abcd
implicit none
private
save
integer
real*8
integer, allocatable ::
real*8, allocatable ::
모듈은 변수들과 연관된 함수들의 집합이다. 특정한 일에 관련된 변수, 배열들을 공통으로 사용할 수 있도록 정의한다. 가변 배열도 여기에서 정의된다. 가변 배열이 접근 가능한 곳에서는 메모리 할당, 메모리 해제가 가능하다. 물론, 접근가능한 곳에서는 변수로 사용할 수 있다. 가변 배열도, 일반 변수들과 마찬가지로 다른 세상밖으로 나갈 필요가 없을 수도 있다. 이 모듈 안에서만 필요한 경우가 있다. 그렇지 않을 수도 있다. 잘 분류할 필요가 있다.  특별하지 않은 경우에는 데이터, 루틴들이 이 곳에서 다른 세상으로 나가지 못한다. 그 변수에 새로운 값들이 들어 올 수도 없다.  하나의 모듈안에서 공통으로 사용하는 변수들은 인수를 통해서 전달 될 필요가 없고 모듈내에서는 공통으로 사용된다. 따라서, 최대한 분업화하여 작업을 하는 것을 장려할 수 있다. 선언된 변수들은 모듈내에 존재하는 함수들에게 공개되고 쉽게 접근될 수 있기 때문에 분업화를 최대한으로 추구할 수 있다.

public ::
 다른 세상으로 나갈 수 있는 루틴, 배열, 변수들의 리스트를 명시적으로 적어 준다.
contains

본격적으로 하고자 하는 일에 대한 준비 작업이 이루어 질 수 있다. 특정 데이터가 들어오고, 필요한 배열들에 대한 메모리 확보 작업들이  실행 될 수 있다. 외부로부터 데이터를 유입한다.
subroutine abcd_init()
implicit none
integer
real*8

end subroutine abcd_init

특정한 일들이 종료가 될 때 마지막으로 필요한 작업들을 수행한다. 출력, 메모리 할당 부분에 대한 정리가 이루어질 수 있다.
subroutine abcd_fin()
implicit none

end subroutine abcd_fin

본격적인 작업이 이루어 질 수 있다.
이 또한 다른 루틴들을 불러서 작업을 할 수 있다. 물론, 외부로 부터 새로운 데이터를 유입할 수 있다.
subroutine abcd_do()
implicit none
integer
real*8

end subroutine abcd_do

end module abcd

program abc_test
USE abcd, ONLY :
implicit none
integer
real*8

end program abc_test
-----------------------------------------------------------------------

abs(-5.0) : 5.0
ceiling(–5.5) : –5.0
cmplx(5.0, –5.0) : 5.0–5.0i
conjg(c)
cos(3.14159) ~ -1.0
exp(1.0)
floor(–5.5) : –6.0
int(4.99) : 4
max(–4,–5) : –4
min(–4,–5,–6) : –6
mod(8,5) : 3
nint(5.5) : 6
real(5) : 5.0
call random_number(tmp) : tmp 

               
index('hi there', 'there') : 4
len('hi there') : 8
len_trim(' hi there ') : 8
repeat('-', 4) : '----'
scan('hello', 'oe') : 2
trim(' hi ') : 'hi'

               
r = dot_product(vec1, vec2)
vec2 = matmul(matrix, vec1)
trans = transpose(matrix)

-----------------------------------------------------------------------
-----------------------------------------------------------------------


-----------------------------------------------------------------------

-----------------------------------------------------------------------

-----------------------------------------------------------------------

-----------------------------------------------------------------------



핑백

덧글

댓글 입력 영역

최근 포토로그



MathJax