[Fortran] 포트란을 활용한 배열 : 개요, 정합성 요구, 배열 작성자 (constructor), 마스크된 배열 할당 (where문), 동적배열, 배열함수

 정보

  • 업무명     : 포트란을 활용한 배열 : 개요, 정합성 요구, 배열 작성자 (constructor), 마스크된 배열 할당 (where문), 동적배열, 배열함수

  • 작성자     : 이상호

  • 작성일     : 2020-11-26

  • 설   명      :

  • 수정이력 :

 

 내용

[개요]

  • 안녕하세요? 웹 개발 및 연구 개발을 담당하고 있는 해솔입니다.

  • Fortran (포트란)은 수식 (Formular) 변환기 (Translator)의 약자로 과학 계산용으로 주고 사용되는 언어입니다.

  • 복잡한 계산 수행 성능이 뛰어나 공학과 자연과학 등 특정분야에 주로 사용되며 기상 데이터 처리를 위해 널리 사용되고 있습니다.

  • 오늘 포스팅은 포트란을 활용한 배열을 소개합니다.

 

 

[특징]

  • 기상 데이터 처리를 위해서 포트란 (Fortran)기술이 요구되며 이 프로그램은 이러한 목적을 달성하기 위한 기술서

 

[기능]

  • 개 요

  • 정합성 요구

  • 배열 작성자 (constructor)

  • 마스크된 배열 할당 (where문)

  • 동적배열

  • 배열함수

 

[활용 자료]

  • 없음

 

[자료 처리 방안 및 활용 분석 기법]

  • 없음

 

[사용법]

  • 없음

 

[사용 OS]

  • Linux (CentOS v7.8)

 

[사용 언어]

  • gfortran v4.8.5

 

 세부 내용

개요
  • 프로그램을 개발하는 이유 중 하나는 많은 양의 데이터를 동일한 방법으로 빠르게 처리하기 쉽게 하기 위한 위함이 있습니다.

  • 예를 들면 수 많은 숫자 세트들 중 평균 값을 찾기 위해, 또는 방대한 양의 숫자 또는 이름 목록을 정렬하기 위하여, 또는 많은 학생들의 시험 결과를 분석하는 등 입니다.

  • 이러한 데이터를 처리 하기 위하여 우리는 배열을 이용할 수 있습니다.

  • 배열은 하나의 이름으로 참조할 수 있는 같은 타입의 변수나 상수의 집합이라고 할 수 있습니다.

  • 이 집합은 메모리 상에서 연속적인 위치를 점유하게 됩니다.

  • 연속적인 위치에 있기에 같은 반복적인 연산은 DO문을 이용하여 수 많은 데이터에 적용 할 수 있습니다.

  • 혹은 특정 조건에 해당하는 경우만 WHERE문을 통해 계산 할 수 있습니다.

  • 포트란에서는 조건만 만족한다면 배열끼리 연산도 가능합니다.

  • 배열을 사용할 때 기본적인 다음 사항을 지키는 습관이 중요합니다.

    • 배열 사용 전 에는 반드시 초기화

      • 초기화를 하지 않을 경우 쓰레기(의도치 않은)값이 저장되어 잘못된 계산을 유발합니다.

         

    • 배열 크기를 파라미터를 이용하여 선언하라

      • 배열 크기를 항상 유의 해야 합니다. 의도했던 크기보다 넘어갈 경우 에러가 발생하거나 위와 비슷한 상황이 발생합니다.

 

정합성 요구
  • 포트란에서는 반드시 스칼라로 제한되어 사용되는 소수의 연산을 제외하고는 어떤 연산도 배열을 피연산수로 가질 수 있으며 그 결과도 배열이 될 수 있습니다.

  • 거의 모든 표현에 배열을 직접 사용할 수 있도록 함으로써 데이터 병렬을 지원하고 있습니다.

REAL, DIMENSION(100) :: a, b 
a=2.0 
b=b*a

 

  • 배열 연산에서의 정합성 요구는 연산에 참여하는 배열들의 모양이 서로 일치해야 함을 의미합니다. 

  • 배열 연산에서 사용되는 피연산수 즉 배열은 서로 같은 차원을 가져야 하며 각 차원마다 원소의 개수가 같아야 한다는 것 입니다.

  • 물론 연산의 결과 역시 연산에 참여한 배열들과 같은 모양을 가지게 될 것입니다.

  • 간단하게 수학에서 배운 행렬연산을 생각하시면 이해가 쉬울 듯 합니다.

 

 

  • 이와 같은 연산의 정의로 인해 연산에 참여하는 각 배열의 모양이 같아야 한다는 정합성 요구를 이해할

    수 있습니다. 

  • 그러나 한가지 예외 사항이 있는데 피연산수 중 하나로 스칼라가 올 때입니다. 배열 B에 대해 B+2는 유효한 연산입니다.

 

 

배열 작성자 (constructor)

[암시적 DO 구문]

  • 포트란에서는 1차원 배열을 명시적으로 구성 하기 위하여 배열 작성자(array constructor)를 이용할수 있습니다. 

  • 배열 작성자는 (/1, 2, 3, 4/)와 같이 (/…/)내에 콤마로 배열의 원소를 구분해 나열하는 것을 말합니다. 

  • 일반적으로 배열 작성자의 각 원소는 어떤 스칼라 표현도 가능합니다.

  • 스칼라 표현과 더불어 배열 작성자의 원소를 나타내는 방법으로 암시적 DO구문과 배열표현이 있습니다.

  • 암시적 DO 구문은 다음과 같은 형식을 가집니다.

(/ expression-list, index-variable=first-value, last-value[,increment]/)

 

  • 표현식 (/(k, k=1,n)/)는 원소가 1,2,3, …,n으로 주어지는 1차원 벡터를 생성하며 n=4 이면 (/1,2,3,4/)가 됩니다.

(/1,0,1,0,1,...../) = (/(1,0, j=1,500000)/) ! 1과 0이 반복되는 100만개의 원소로 구성 되는 벡터
(/ ((i + j, i = 1, 3), j = 1, 2) /) ! = (/ 2, 3, 4, 3, 4, 5 /) ! 암시적 DO 구문을 대응되는 스칼라 표현

 

[배열표현]

  • 배열 표현은 임의 차원의 배열을 이용해 배열 작성자를 구성하는 것입니다.

  • 배열 A 가 1000ⅹ1000의 2차원 배열일 때 (/A+1.3/)은 100만개의 원소를 가지는 배열 작성자가 됩니다.

  • 각 원소는 배열 A의 각 원소에 1.3을 더한 값이 되며, 1열 1000개, 2 열 1000개, … 와 같이 열-우선 순으로 나열돼 하나의 배열 작성자를 구성합니다.

(/A+1.3/) = (/ ( ( A(j,k)+1.3, j=1,1000 ), k=1,1000 ) /)

 

  • 여기서 A+1.3의 원소를 열-우선 순으로 나열하지 않고 행-우선 순으로 나열된 배열 작성자를 원한다면다음과 같이 암시적 DO구문을 이용합니다.

(/ ( ( A(j,k)+1.3, k=1,1000 ), j=1,1000 ) /)

 

[RESHAPE 함수]

  • 1차원으로 정의되는 배열 작성자를 이용해 원하는 모양의 배열을 구성하기 위해서 내부함수 RESHAPE를 사용할 수 있습니다. RESHAPE 함수는 다음과 같은 형태로 사용합니다.

RESHAPE (array-constructor, shape-vector)

 

  • shape-vector는 원하는 배열 모양에 대한 각 차원의 크기를 지정하는 값을 (/ …/)로 묶어 나타냅니다.

REAL, DIMENSION (3,2) :: ra 
ra = RESHAPE((/ ((i+j,i=1,3),j=1,2)/), SHAPE=(/3,2/))

 

  • 다음은 1000ⅹ1000크기의 실수타입 단위 행렬을 배열 작성자와 RESHAPE 함수를 이용해 Ident_1000이라는 이름을 가지는 배열 상수로 정의한 것입니다.

REAL, PARAMETER, DIMENSION(1000,1000) :: Ident_1000 = & 
RESHAPE( ( /(1.0,(0.0,k=1,1000),j=1,999),1.0/ ), (/1000,1000/))

 

마스크된 배열 할당 (where문)

[WHERE 문]

  • 마스크(mask)란 배열자료값을 이용한 논리형 표현식으로 마스크된 배열 연산은 배열의 각 원소에 병렬로 수행되는 연산이 일부 원소에만 적용되도록 logical 타입의 마스크를 사용하는 것입니다.

  • 마스크를 통하여 배열에 값을 할당하기 위해 WHERE 문을 사용합니다.

WHERE (mask) array-assignment-statement

 

  • WHERE 구문의 마스크 원소가 .TRUE.일 때 이에 대응되는 배열 원소에 값이 할당 되며, .FALSE.가 되면 값이 할당되지 않습니다.

  • 다음은 마스크를 이용해 배열의 일부 원소에 값을 할당하는 예제 입니다.

WHERE (C .NE. 0) A = B/C

 

  • 여러 개의 할당에 하나의 마스크를 적용할 수 있으며 이때 WHERE 문은 다음과 같이 블록화 됩니다.

WHERE (mask) 
array-assignment-1 
array-assignment-2 
......... 
END WHERE

 

  • 또한 블록화된 WHERE 문에 ELSE WHERE 옵션을 덧붙여 사용하면 마스크 원소가 .FALSE.일 때도 배열에 값을 할당하도록 할 수 있습니다.

WHERE (C .NE. 0) 
A = B/C 
ELSE WHERE 
A = B 
END WHERE

 

동적배열

[Automatic Array]

  • 포트란이 지원하는 동적 배열은 프로그램 실행 중에 메모리가 할당되며 배열의 크기를 프로그램에서 계산되는 값 또는 입력 값으로 결정할 수 있습니다.

  • 동적 배열은 automatic array, allocatable array, 그리고 pointer array 이렇게 세 가지 형태가 있습니다.

  • Automatic array는 크기가 dummy 인수에 의해 결정되는 지역 배열입니다.

  • 프로시저 내에서 자동 생성되고 프로시저의 종료와 함께 자동으로 소멸됩니다.

  • 크기가 프로시저의 dummy 인수에 의해 결정되므로 프로시저가 호출될 때 마다 그 크기는 달라 질 수 있습니다.

  • 다음 예에서 배열 work1 과 work2 는 automatic array의 예입니다.

SUBROUTINE sub(n, a) 
  IMPLICIT NONE 
  INTEGER :: n 
  REAL, DIMENSION(n, n) :: a 
  REAL, DIMENSION (n, n) :: work1 
  REAL, DIMENSION (SIZE(a, 1)) :: work2 
END SUBROUTINE sub

 

[Allocatable Arrays]

  • 포트란에서는 ALLOCATE 문을 이용해 메모리를 동적으로 할당할 수 있습니다.

  • Allocatable array 는 다음과 같이 ALLOCATABLE attribute 로 선언하여 사용합니다.

PROGRAM simulate 
  IMPLICIT NONE 
  INTEGER :: n 
  INTEGER, DIMENSION(:,:), ALLOCATABLE :: a 
END PROGRAM simulate

 

  • 배열의 이름과 차원은 위와 같이 미리 결정되어 있어야 하지만 그 크기는 다음과 같이 입력 값으로부터받아오거나 프로그램 실행 중에 계산되는 값에 의해 결정됩니다.

PRINT *,'Enter n:' 
READ *,n 
IF(.NOT.ALLOCATED(a)) ALLOCATE( a(n,2*n) ) 
........ 
DEALLOCATE(a)

 

  • Allocatable array 는 프로시저 내에서 지역적으로 사용될 수도 있고 프로그램 전체에서 global 하게 사용될 수도 있습니다.

  • allocatable array를 더 이상 사용할 필요가 없다면 DEALLOCATE 문을 이용해 해제 시켜야 합니다. 

  • 그렇지만 , 프로시저 내에서 지역적으로 사용된 allocatable array는 SAVE로 명시해 두지 않은 경우 프로시저의 종료와 함께 자동으로 소멸됩니다.

 

[Pointer Arrays]

  • pointer array는 ALLOCATE 문을 이용해 명시적으로 할당되고 실행 중에 결정되는 크기를 가지며DEALLOCATE 문을 이용해 명시적으로 할당 해제 된다는 점에서 allocatable array와 유사합니다.

  • pointer array는 target으로 명시적으로 선언된 다른 배열과 배열 부분에 대한 aliasing 을 위해 사용됩니다.

REAL, TARGET :: B(100,100) ! 배열 B 는 target 속성을 가진다 . 
REAL, POINTER :: U(:,:),V(:),W(:,:) ! 3 개의 포인터 배열 선언
... 
U => B(I:I+2,J:J+2) ! U 는 B 의 3X3 부분을 point 
ALLOCATE ( W(M,N) ) ! 크기가 MXN 인 W 를 동적할당
V => B(:,J) ! V는 B 의 J 번째 열을 point 
V => W(I-1,1:N:2) ! V는 W 의 I-1 번째 열의 일부를 point 하도록 바꿈

 

  • allocatable 로 선언하는 대신 포인터 로 선언

  • 다른 배열 또는 배열 부분으로의 대체(aliasing) 을 위해 사용

  • allocatable array의 모든 기능을 포함하지만 상대적으로 복잡하여 효율적인 면에서 좋지 않음

  • 지역적으로 계산되는 값에 의존하는 크기를 가지는 동적 배열의 정의를 위해 일반적으로 allocatable array를 많이 사용합니다.

  • 그러나 알고리즘에서 동적 대체(aliasing)가 필요한 경우에 pointer array를 사용하게 됩니다.

  • 관련하여 보다 자세한 내용은 포인터와 같이 다시 한번 이야기 하겠습니다.

 

 참고 문헌

 문의사항

[기상학/프로그래밍 언어]

  • sangho.lee.1990@gmail.com

[해양학/천문학/빅데이터]

  • saimang0804@gmail.com