포트란 90/95
개인 열람용 포스팅.
컴파일 할 때
1. 오브젝트 만들어 컴파일(컴파일 할 게 많아지면 각각 오브젝트를 만든 뒤 컴파일 하는 방식으로 사용)
gfortran -c test1.f90 test2.f90
gfortran -o main.x test1.o test2.o
2. 바로 컴파일
gfortran -o main.x test1.f90 test2.f90
1. 기본사항
(1) 대소문자 구분 없음
(2) _ (underscore) = 변수가 차지하는 메모리 바이트수
별도의 선언 없이 메모리 지정, 아무거나 되는 게 아니라 컴파일 조건에 맞야 됨
2. 연산
(1) 관계 연산
==
/=
>
<
>=
<=
(2) 논리 연산 (. 점을 잘 찍자...)
(논리값 .TURE, T, .FALSE, F 로 표현)
.AND.
.OR.
.NOT.
.EQV.
.NEQV.
(3) 수식 연산
+ - * /
** = 거듭제곱
// 연속된 문자열을 합침
'와 "는 기능이 동일, '와 "를 혼용하여 내부 묶음을 표현 가능
ex) 'ab'cd'ef' -> (x) -> "ab'cd'ef"
3. 특수 문자 기능들
! = 주석 행
& = 연속기호 (문장이 긴 경우 사용 가능) (cf. bash는 \ 기호로)
; = 한 줄에 여러 코드 작성
4. 구성
(1) 프로시저
PROGRAM, SUBROUTINE, FUNCTION, MODULE, BLOCK DATA
단, BLOCK DATA는 사용하지 말 것을 권고
외부 프로시저
내부 프로시저 cf) CONTANINS
내장 프로시저 (이미 내장된 것)
MODULE 프로시저 cf) USE
module은 별도의 파일로 구성하는 것이 좋다.
이상하게도 bash로 gfortran을 이용해서 컴파일 하면 되는 게
다른 컴파일러에서는 모듈 파일이 따로 없다고 오류가 나기도 한다.
ex)
MODULE EngineSet
IMPLICIT NONE
INTEGER :: i
REAL(KIND=16) :: a, b
!!!
END MODULE Engine
PROGRAM MainProcedure
USE EngineSet
IMPLICIT NONE
INTEGER :: j
i=1;j=2
CALL InternalProc(b) 기
CALL ExternalProc(c)
WRITE(*,*) i, b
CONTAINS
SUBROUTINE InternalProc(x)
USE EngineSet
IMPLICIT NONE
REAL(KIND=16) :: x
x = a + i
END SUBROUTINE InternalProc
SUBROUTINE InternalProc2
!!!
END SUBROUTINE
END PROGRAM MainProcedure
SUBROUTINE ExternalProc(y)
USE EngineSet
IMPLICIT NONE
INTEGER :: y
y=i+j
END SUBROUTINE ExternalProc
!!!
REAL FUNCTION TotalSum(x, n) RESULT(sum)
IMPLICIT NONE
INTEGER :: k, n
REAL :: x(*)
sum=0
DO k=1,n
sum=sum+x(k)
END DO
END FUNCTION TotalSum
!!!
(5) implicit none
포트란에서는 i,j,k,l,m,n 으로 시작하는 변수는 암묵적으로 정수형으로 간주되므로
코딩 오류를 피하기 위해 implicit none을 문두에 써서 선언되지 않은 변수의 사용을 막는다.
(6) contains
프로시저 내에 포함되는 프로시저를 기술하기 위해 쓰는 구문
해당 프로시저의 마지막 부분에 기술한다.
end contains는 없다....
explicitly interface를 선언해줄 때 필요할 때가 있지만 각 프로시저에 contains을 쓰는 건 난잡하므로
module을 하나 만들고 거기에 contains을 쓴 다음 각종 프로시저를 코딩한 뒤
main에서 use module_name 을 해주면 편하다.
(7) interface
프로시저들이 있음을 알려주는 역할
명칭이 없는 경우는 여러 개의 프로시저를 입력 가능하고
명칭이 있는 경우는 ambiguity가 없는 한에서 입력 가능하다.
C의 오버로딩 같은 느낌.
정의 연산자
interface operator(operator)
인수가 1개 prefix 연산자
인수가 2개 infix 연산자
그외는 불가
(가인수 속성에 intent(in)을 해줘야 한다.)
연산자 오버로딩이라고 생각하면 된다.
정의 할당문
interface assignment(=)
2개의 가인수를 지녀야 한다.
첫 번째는 intent(out 또는 inout), 두 번째는 intent(in)
등호의 좌변은 첫 번째 인수, 우변은 두 번째 인수가 된다.
할당문에 오버로딩한 거라고 생각하면 된다.
(8) 재귀 호출
재귀 호출을 하고자 할 때
subprocedure 앞에 recursive 라고 붙여줘야 한다.
ex)
RECURSIVE INTEGER RFUNCTION Factorial(n) RESULT(fac)
(9) CALL
서브루틴 호출에 사용
2. 변수 관련
(1) 변수
real/complex/character/logical/
기본 바이트(선택 가능)
정수4(1,2,4,8)
실수4(4,8,16)
복소수4(4,8,16)
(kind=#)로 지정 ex) integer(kind=8) :: a
문자는 (len=#)으로 지정
속성
변수 형 옆에 콤마 쓰고 이어서 쓰면 됨
DIMENSION(행, 열)
이때 1:3:2 같은 표현을 쓰면 1부터 3까지 +2 단위로라는 뜻이다.
포트란은 7차원 배열까지 가능
ex) CHARACTER(LEN=5), DIMENSION(5,5) :: str
str(3:4,:) (2:4) = 'abc' !이렇게 하면 str(3:4,1:5) 변수들에 2:4자리에 'abc'를 할당하게 된다.
기본적으로 배열은 0 이 아닌 1부터 시작한다.
정할 경우 음의 정수도 가능하다.
배열에 값을 할당할 때는 RESHAPE 함수를 사용
ex) integer, dimension(3,2) :: a=reshape((/11,21,31,12/), (/3,2/), PAD=(/0/))
!a=1행에 11 12, 2행에 21, 0 3행에 31, 0을 가진다.
근데 어쩌피 write(*,*) a를 하면 11 21 31 12 0 0을 출력함...앞 자리부터 변함
배열에 관한 내장 함수가 많이 있으니 따로 찾아볼 것
그냥 변수 오른쪽에 괄호 치고 배열 선언해줘도 된다.
integer, dimension(2,4)::a
integer :: b(2,4)
integer :: c
dimension c(2,4)
ALLOCATABLE = 동적 배열
ex) INTEGER, ALLOCATABLE, DIMENSION(:,:) :: a
ALLOCATE(a(3,2)) !여기에 allocate(a(3,2), b(2), c(3,2)) 처럼 사용 가능하다.
DEALLOCATE(a) !마찬가지로 deallocate(a,b,c)
그 외 많은 배열 방법이 있으니 참고
서브프로시져에서 배열을 규정하는 배열 방법도 있고...
allocatable하게 정의된 배열, 포인터, 타겟 등에 대해
allocate와 deallocate를 통해 메모리를 동적으로 주었다가 해제할 수 있다.
(allocate(a(10,5)), b(5,2), c(5)), deallocate(a,b,c))
deallocate를 한 변수에 접근을 하면 오류가 난다. 다시 allocate할 때까지는 못 쓴다.
PARAMETER
상수로 정의
ex) integer, parameter :: number=4
INTRINSIC
내장 함수임을 밝히고 내장 함수를 변수처럼 인수로 전달하거나 사용할 때 씀
ex) ###
intrinsic :: sin
call sub(a, b, sin)
###
subroutine sub(a,b,c)
real :: a,b,c
write(*,*) a, b, c(a+b)
end subroutine
EXTERNAL
외부 함수임을 밝히고 실인수로 사용할 때...라는데
도통 사용법을 알 수가 없다.
아무래도 fortran 70에서 쓰이다가 fortran 90에서 interface로 대체된 것이지 않을까 싶다.
INTENT
fortran은 기본적으로 참조 방식으로 인수를 전달한다.
(C언어나 베이직은 값을 전달하는 방식이고 참조로 하려면 포인터나 byref 등을 이용한다)
그런고로 실수가 일어나는 것을 막기 위해
intent(in)을 쓰면 프로시저 안에서 변수 수정이 일어나면 컴파일러가 에러를 낸다.
그러나 intent(in)으로 받은 변수를 또 다른 프로시저로 넘겨주면 수정이 가능하다는 것이 함정.
아무것도 안 쓰면 기본적으로 intent(inout)이 되며... 컴파일러 전용 코드로 무의미...
몇몇 컴파일러에서는 %val로 값으로 인수를 전달하기도 한다는데 gfortran은 안 되는 듯?
PUBLIC/PRIVATE
module에서 쓰이는 속성으로 전역/지역 변수
module에서는 묵시적으로 전역변수이다.
public만 쓰고 줄을 넘기면 해당 프로시저의 모든 변수가 전역 변수가 되고
public :: 변수로 해주면 해당 변수만 전역 변수가 된다.
OPTIONAL
해당 인수를 선택적으로 받는다.
해당 인수의 유효성을 검사하기 위해 present(a)를 이용해 검사한다.
optional 속성은 explicit한 interface에서만 사용 가능하므로 contains 된 프로시저이거나
아니면 module을 use 또는 interface 처리를 해줘야 한다.
interface에서 변수에 optional 속성을 써 주는 것이 필수이다.
만약 변수를 전달받지 못한 경우 전달받지 못한 변수를 건드리게 되면 오류가 난다.
(default 값 설정조차 함부로 해서는 안 된다.)
컴파일은 정상적으로 되어도 전달 받지 못한 변수를 전달 받은 것처럼 하여 연산을 하면
이상한 주소를 참조하게 되므로 오류가 나게 된다.
이때 해결 방법은 그 인수를 값으로 받으면 된다.
이는 attribute에 value를 써주면 해결된다.
이 경우 참조 성격으로 변수를 받는 것이 아니라 값을 전달받아 처리하게 된다.
즉, value 속성으로 처리된 변수는 해당 프로시저에서 값을 바꾸어도 해당 프로시저를 나가면 아무런 변화가 없는 것이 된다.
(이 경우는 default 값 설정하는 코드를 짜도 문제가 없다.)
(서브 루틴엔 value를 쓰고 interface 부분에는 value를 안 써도 정상 작동된다.)
다만, value 속성은 fortran 2003 부터 도입되었다.
POINTER/TARGET
포인터 설명할 때 다시..
SAVE
베이직의 static 같은 표현.
프로시저에서 사용한 변수를 프로시저를 나가더라도 기록에 남겨두어 다시 프로시저로 들어왔을 때 이어서 사용가능하게 함
SEQUENCE
TYPE을 이용한 선언에서 변수들이 순차적으로 접근 가능함을 선언해준다.
sequence 없이도 암묵적으로 순차접근 속성을 가진다.
ex)
type custom1
sequence
integer :: a, b
real :: re
character :: ch
end type
###
type(custom1) :: object1
read(*,*) object1 !object1%a, object1%b, object1%re, object1%ch 순을 값을 받음
(2) 문자열 함수
TRIM(char) = 뒤 공백 및 스페이스 제거
ADJUSTL(char) = 앞 공백 및 스페이스 제거
ADJUSTR(char) = 뒤 공백 및 스페이스 제거
INDEX(charA, charB) = A에서 B의 위치를 반환
단, adjustl/adjustr에서 스페이스 값도 정렬하는지 아니면 사라지는지는 추후에 확인
(3) 자료형 변환
작은 집합에서 큰 집합으로 가는 건 =로도 해결 가능
큰 집합에서 작은 집합으로 가는 건
REAL(a)
FLOAT(a)
DBLE(a)
INT(r)
CMPLX(r)
CMPLX(r,r)
로 해결
INT 소수점 버림, NINT 반올림하여 정수 반환
ANINT 실수의 소수점 이하를 반올림하여 실수 반환
AINT 실수의 소수점 이하를 버려 실수 반환
FLOOR(r) r 보다 작은 최대 정수
CEILING(r) r보다 큰 최소 정수
수학적으로
int(r), aint(r) = sgn(r)*int(abs(r))
nint(r) anint(r) = sgn(r)*int(abs(r)+0.5)
참고.
abs, man, min, sign(부호변환), dim(잉여값), modulo(floor로 계산한 나머지), mod(int로 계산한 나머지)
인데 정확한 것은 각 인수별로 다시 확인해봐야 정확할 듯.
(실수가 인수로 가거나 음수일 경우 출력값에 애매하므로...)
(4) 사용자 정의 구조체 TYPE
구조체 선언
TYPE name (또는 type :: name)
###변수들
END TYPE name
선언 방법
TYPE(name) :: object1
접근 방법
object1%component ! %를 이용해 접근한다.
한 번에 값을 전달할 때
object1 = name(a, b, re, ch)
3. 제어문
(1) if
if ()괄호쓰고 조건식 then
###
else if (==) then
###
else
###
end if
한줄 표현은
if () ###
베이직하고 달리 한 줄로 쓸때는 then을 쓰지 않음
조건식 검사 방식은 C 언어와 유사.(basic 과 달리..)
즉, A and B 에서 A가 거짓이면 B는 검사하지 않음
A or B에서 A가 참이면 B는 검사하지 않음
(2) merge
C언어의
Result = (condition)? expression(T) : expression(F);
와 동일하다.
result = merge(expression(T), expression(F), condition)
(3) where
where (배열 조건식) 배열 할당문
[name] where ()
elsewhere () [name]
end where [name]
ex)
integer, dimension(3) :: a=(/1,3,5/), b=(/1,9,2/)
where (a>b) a=-1
write(*,*) a
!출력 1, 3, -1
(4) forall
forall ( i=1:n, j=1:n, j>i) a(i,j)=a(j,i)
forall ()
###
end forall
(5) select case
select case (괄호쓰고 expression)
case (4) ! 정수, 논리, 문자형 상수 selector
case (1,3,5)
case (:-1) ! -1 이하
case (100:) 100 이상
case (50:60)
case default
end select
(6) do
DO
END DO
[name :] do i=ini,limit,step
###
exit [name] ! c언어 break
cycle [name] ! c언어 continue, fortran에서 continue는 goto와 함께 쓰이므로 주의
end do [name]
===
do while (condition)
###
end do
cycle 및 exit에 name을 지정해서 여러 루프를 빠져나갈 수 있는지 추후에 확인해볼 것
(7) goto
goto label ! label은 정수형 상수
###
label continue
goto (10, 20, 30) n
goto (10, 20, 30), n
의 경우 n 값이 1,2,3에 따라 10, 20, 30으로 이동
goto 변수 는 사용할 수 없고
integer :: n
assign 10 to n
goto n
10 continue
로 이용할 수는 있다.
(8) 각종 제어문
pause
실행을 잠시 멈추고 press enter to continue를 띄운다.
return !c언어의 return이 아니다.
goto와 함께 쓰지 말 것을 권고 받는다...
stop
프로그램 종료(이제는 필요 없어진 명령어...)
ex) stop 'end' ! end를 출력하고 종료
4. 입출력
(1) 입력
read (unitnumber, format, ...) a, b, c
read(*,*) a,b,c
콤마나 공백으로 구분하여 읽음
(2) 출력
print format(expression||lable||*|assignedalbel), output
write (*,*) output
open(3, 'input.txt')
open(4, 'output.txt')
read(3, FMT="E10.0") x
print *, "abc"
print 100, 'a=' a
100 format(A, 2X)
write(4,*) a
write(4, FMT="(' x=', 3X,F7.4)") x
줄 변경하지 않고 출력하기
write(*, "(A)" , ADVANCE="NO") a
default는 advance="yes"이다.
(3) 파일 입출력
open(i/o unit number, FILE='text.txt',status='new')
status=''
old 존재하는 파일 읽기
new 파일이 새로 만들어졌음을 의미, 이미 존재하면 에러(overwrite 방지에 이용가능)
scratch 임시파일, 프로그램 종료 또는 close 실행시 삭제됨
replace
unknow (default) 있으면 읽고 없으면 만들어 읽고
action=''
read, write, readwrite(default)
iostat=ierror (integer로 선언하고 사용)
success->0, fail->positive number
ACCESS= sequential(default), direct
FORM= formatted, unformatted(default)
RECL= direct open의 경우 record 길이
err= 에러나면 해당 label을 실행
close(unit=#) 또는 close(#) 또는 close(unit=#, status=....)\
status
keep (default)
delete
rewind([unit=], err=, iostat=)
순차접근 비서식파일에 주로 사용하여 작업하다가 파일의 처음 부분으로 읽고쓰는 위치를 이동
iostat
오류 - 양수, 파일 또는 레코드 끝 - 음수, 그외 - 0 (read 도 마찬가지 이다)
read 에서 end=# 도 사용할 수 있는데 파일 끝을 알려준다.
backspace([unit=], err=, iostat=)
현재 기록위치에서 이전 레코드 앞으로 이동. 주로 다시 읽을 필요가 있을 때 사용
endfile #
파일 끝을 알리는 레코드 기록
read/print/write
READ( [ UNIT=] u [, [ FMT=] f ] [, IOSTAT= ios ] [, REC= rn ] [, END= s ] [, ERR= s ] ) iolist
READ f [, iolist ]
READ([UNIT=] u, [NML=] grname [,IOSTAT=ios ] [,END=s ] [,ERR=s ] )
s = 에러 날 경우 코드에서 이동할 위치 지정(goto 문 역할)
iso = 파일이 안 열린 경우도 음수, 뭐,, 읽어올 데이터가 없으면 음수... 인듯
read 정리
read 문장 하나당 레코드 하나가 기본
레코드를 넘는 read를 한 경우 자동으로 다음 레코드 정보를 읽어옴
read 문장이 끝나면 레코드를 한 줄 넘김
read 문장이 끝나도 레코드를 넘기지 않게 하기 위해서는 format에 $를 넣어주면 됨
다만 이 경우 레코드를 넘어서까지 $를 사용할 경우
end of record 까지 읽고 넘어가므로 레코드를 넘어가기 전에 원치 않는 정보가 읽히기 된다.
inquire
파일 상태 확인
recl
rec
direct read...
(4) 포멧
label FORMAT([item])
그때 그때 필요한 것 찾아 쓸 것.
너무 많음...
변수형, 서식, 위치조정제어, 부호출력, 공백처리, 레코드 제어(/ 줄 바꾸기),
커서 넘기지 않기($)
반복(! rIn, rFw 등 5(F5.2, 2X)로 5번 형힉 반복으로 활용)
(5) NAMELIST
namelist를 이용한 입출력
선언된 변수들을 namelist를 이용해 그룹명으로 묶는다.(배열도 묶을 수 있음)
NAMELIST / group1 / a, b, c
read(1, nml=group1)
write(1, nml=group1)
input, output 파일은
&gruop1
SREAL = 1.,
SINTEGER = 2,
SCOMPLEX = (3.,4.),
SCHAR = 'namelist',
SBOOL = T/
&array1
AREAL = 1. 1. 2. 3.,
AINTEGER = 2 2 3 4,
ACOMPLEX = (3.,4.) (3.,4.) (5.,6.) (7.,7.),
ACHAR = 'namelist' 'namelist' 'array' ' the lot',
ABOOL = T T F F/
와 같은 형식으로 주어진다.
이때 데이터를 받는 것은 명칭으로 받으므로 순서는 상관없으며 optional하게 받을 수도 있다.
5. 기타 프로시저
(1) 재귀호출
1. 마지막에 설명
(2) entry
서브프로시저에 새로운 입력점을 추가하여 사용
subroutine quartic(a)
a=a*a
entry square(a)
a=a*a
end subroutine quartic
(3) statement function
프로시저 중간에
real :: x, y,z
additionfn (x,y) = x+y
라고 쓰고 나중에 프로시저 내에서 사용 가능하게 하는 방법
z=2*additionfn(z,z)
(4)
6. 포인터
(1) 정의
C 언어에서는
int num;
int* p;
p = # //포인터에 변수 주소 대입
printf("%d", *p);//p 주소에 위치한 값 출력
printf("%d", p);//p가 가르키는 주소 출력
이지만
포트란은 좀 다르다.
integer, pointer :: p1, ㅔ2
integer, target :: tg=100
p1 => tg !포인터 p1에 tg 주소 할당
p2 => p1 !포인터 p2에 p1 주소 할당
포인터로 받을 수 있는 것은 pointer나 target 속성을 지닌 변수로 한정된다.
p1=33
print *, p1, p2, tg ! 모두 33이 나온다.
즉, 포인터와 타겟을 선언해야하고
포인터에 =>을 이용하여 타겟 주소를 준다.
포인터나 타겟이나 값을 읽을 때는 일반적인 변수처럼 사용 가능하다.
즉, 주소 전달은 =>, 값 전달은 =이다.
(2) 관련 함수
nullify(p) !널 포인터로 만든다
associated(p) !연결 상태를 논리값으로 알려준다.
nullify한 포인터에 값을 건드리는 연산은 오류를 야기한다.
값은 0으로 출력되므로 접근을 못하는 것은 아니지만
다시 주소를 할당해줄 때까지 값을 참조하는 행위는 불가능하다.
'etc. > etc. com' 카테고리의 다른 글
크롬 다운로드 버블 끄기 (v 119.0.6045.106) (0) | 2023.11.05 |
---|---|
ubuntu 기본 명령어 정리 (0) | 2017.06.26 |
[티스토리 블로그] 줄 바꿈 기준 변경하기 (0) | 2014.05.18 |