FORTRAN

De Física Computacional
Ir para navegação Ir para pesquisar

A linguagem de programação Fortran foi desenvolvida na década de 1950 e continua a ser usada hoje em dia. O nome tem como origens a expressão "Formula Translator" (ou "Translation"). A linguagem Fortran é principalmente usada em ciências da computação e análise numérica.

O primeiro compilador de FORTRAN foi desenvolvido para o IBM 704 em 1954-57. O compilador era optimizado, pois os autores acreditavam que ninguém iria usar essa linguagem se a sua prestação não fosse comparável com a da linguagem assembly A linguagem Fortran foi largamente adoptada por cientistas para a escrita de programas numericamente intensivos, o que encorajou os produtores de compiladores a escrever compiladores que gerassem código mais rápido. A inclusão de um tipo de dados de número complexo na linguagem tornou a linguagem Fortran particularmente apta para a computação científica. Ao longo dos tempos foram-se dando algumas revisões da linguagem. Entre elas encontram-se:

  • FORTRAN IV (também conhecida como FORTRAN 66)
  • FORTRAN 77, 90, 95

--Origem: Wikipédia, a enciclopédia livre.

Olá Mundo

Exemplos de código típico e mínimo para testar se o Fortran90 está instalado (ha um equivalente para cada linguagem)

print*,"olá Mundo" 
end 

Editar

Essas duas linhas devem estar dentro de um arquivo (ex: ola.f90), isto pode ser feito com cat linha por linha ou com um editor de texto (jed ou emacs)

Compilar

ola.f90 deve ser compilado, isto é traduzido de linguagem de Fortran (código fonte) para Assembler (código objeto ou de maquina) com a seguinte instrução:

> f90 ola.f90 -o ola

f90 invoca o compilador que faz a dita conversão

-o nome é uma opção para nomear o código objeto (o programa executável) que no exemplo terá o nome ola

sem essa opção o executável criado terá o nome a.out

Rodar

ola.f90 não é executável, o computador não sabe o que fazer com ele, para isso o compilador traduz para una série de instruções que o computador sim sabe executar)

Para rodar fazemos: > ./ola

./ na frente é necessário para o interprete de comandos (shell) saber que o programa está no diretório de trabalho (veremos depois como configurar a sessão para não ter que digitar esses caracteres)

> olá Mundo  ! será a saída na tela

Desta forma completamos o pequeno ciclo de teste de existência de compilador Fortran 90 no computador que estamos usando e também de ter criado nosso primeiro código Fortran, compilado e executado o mesmo.

Olá Mundo (versão com estilo padrão: program ... end program)

program ola
print*,"olá Mundo"
end program ola

Esta versão está mais no estilo que usaremos daqui para frente, mesmo que opcional a declaração program nome (terminada com end program) é útil quando temos códigos longos com sub-rotinas (estes são programas chamados pelo programa principal


Variáveis

Em linguagem de programação "Variáveis" são nomes definidos pelo programador onde podem ser armazenados números ou texto para uso posterior, sobre os quais podem ser feitas operações matemáticas, lógicas ou de manipulação de caracteres, e que também podem ser alteradas ou substituídas por novos conteúdos dependendo das instruções do programa.

Podemos dizer que um programa é feito de constantes, variáveis e instruções de operações entre elas que definem uma tarefa a ser desenvolvida automaticamente pelo computador.

Em FORTRAN existem cinco tipos de variáveis:

INTEGER    -> inteira, exemplo: 0, 1, 100 -34
REAL       -> real o de ponto flutuante, exemplo: 3.14, -0.003, 3.2E-10, -5.02E100 (notação científica)
COMPLEX    -> complexa, exemplo: (2.3, 0), (0, -1.0), (1.2E-5, -0.001)
LOGICAL    -> logica, exemplo: .TRUE. .FALSE. (só essas)
CHARACTER  -> caracter, exemplo: "ola", "Entre seu nome!"

No programa a definição de variáveis é feita no início assim

Program test
Integer I, J, K
Real  a, x, velocidade, tempo_0, Temp
Logical prova
Character*10 Titulo, nome
...

São as chamadas declarações de variáveis, depois seguem as instruções de execução

Bytes

Em computação byte é uma unidade de medida de armazenagem de informação, tipicamente de oito bits. O bit, pela sua vez, é a mínima quantidade de informação possível, ou seja a menor unidade de medida de armazenagem de informação. É onde podemos guardar apenas dois estados: 0 ou 1, sim ou não, etc. Menos do que isso não é informação nenhuma, pois de fato, menos do que isso é um estado único que só pode ser sempre 0 ou 1 ou 2, mais é sempre igual, ele só pode tomar esse único valor e não mudar. Para ter informação então, no mínimo precisamos de dois estados: ora é um, ora é zero. Por isso alguém chamou o bit de ... a mínima diferença que faz a diferença.

A origem do bit é anterior ao computador, provavelmente de 1936, e o nome vem da contração de dígito binário em inglês. Os dígitos binários eram usados anteriormente e são apenas uma representação dos números como é a decimal, porem com base nas potências de 2. assim um número binário só pode estar composto de 0 e 1 (como um decimal esta só composto dos dígitos de 0 a 9). Como exemplo o numero binário 1001100 representa o número decimal 76. (texto em preparação--Sebas 12:42, 11 Setembro 2007 (BRT))

Byte Bit

Sistema binârio


Entrada e Saída

São as intruções READ e WRITE (ou PRINT) O READ permite entrar dados antes ou durante a execução do programa.

Com o WRITE podemos ver os resultado final da execução do código ou acompanhar o desenvolvimento do mesmo.

Exemplos:

READ (*,*) I, Velocidade
...
Write (*,*) Titulo, Temp

Esta última é equivalente a

PRINT*, Titulo, Temp

Todas essa representam entrada/saída pelo terminal Entrada pelo Teclado. Saída na Tela. São as chamadas entrada/saida standar

Se quisermos ler ou gravar num arquivo:

OPEN (1, FILE="entrada.dat")
READ(1,*) I, Velocidade

OPEN (2, FILE="entrada.dat")
WRITE (2,*) Titulo, Temp

CLOSE(1)
CLOSE(2)

Operadores matemáticos

Operador Função
. ponto decimal
+ soma
- resta
* multiplicação
/ divisão
** potência
( ) parênteses


Exemplo usando todas

z = 3.14*x**(-1.4)/2 - 4.3E-10

Funções intrínsecas

Estas são as funções pré-definidas no compilador, entre as quais encontramos a mais comummente utilizadas como raiz quadrada, trigonométricas, exponencial, etc: A sintaxe de uso é o nome da função (geralmente três letras) e o argumento entre parêntesis:

raiz quadrada: sqrt
trigonométricas: sin, cos, tan, asin
exponencial: exp
logaritmo natural: log
módulo: abs

Exemplos:

a = sqrt(2.)
b = sin(x)
Pi = 2.*asin(1.)  uma forma pratica de ter Pi como constante dentro de um programa
y = (x - exp(-lam*x**2))/sqrt(x)
z = x**(1./4.) Cuidado! Teste com 1/4 em lugar de 1./4. e verá a grande diferença

Ciclos (loop)

O grande lance dos programas (e do computador claro) é poder repetir operações sem ter que fazer uma de cada vez. Ou seja, utilizando a lógica para repetir tarefas que podem ter variações especificadas pelo programador dentro de um ciclo. Vejamos um exemplo usando a instrução Do I=... End Do:

...
Do I = 1, 100
   x = I*0.1;   y=x**2
   Print*, x,y
End Do
...

Nota a diferencia do shell onde maiúsculas e minúsculas são diferentes (A é diferente de a) no Fortran elas *SÃO* interpretadas da mesma forma (print = PRINT = PrInT=...). Porem, é recomendado, por uma questão de estilo, usar preferentemente minúsculas, reservando maiúsculas para aquilo que se quer chamar a atenção.

o Ponto e vírgula permite separar instruções diferentes em uma mesma linha

a interpretação do do é a seguinte:

   * comece com I=1
   * faca o que esta entre do ... end do
   * incremente I em 1 e veja se ultrapassou o valor máximo de I (no exemplo 100)
   * se a resposta e não faca de novo  com o novo valor de I
   * o ciclo se repete até o valor máximo de I (= 100 no exemplo)
   * quando I ultrapassa esse valor o laço é interrompido e o programa continua com a seguinte instrução depois de end do


Pergunta: Qual será no exemplo acima o valor da variável I finalizado o laço?

Exemplo: MRUA

Neste exemplo criamos um programa que gera a trajetória x(t) (uma dimensão) de um objeto com aceleração constante:

x(t) = x(0) + v(0)*t + 0.5*a*t²
program mrua
implicit none                ! isto nos obriga a declarar todas as variáveis que usaremos (recomendável)
real :: x0, x, v0, a, t, dt  ! variáveis reais (ponto flutuante)
integer :: I, N

print*, 'entre x0, v0, a, t, N'
read*, x0, v0, a, t, N
dt = t/N       ! passo de tempo: definido pelo tempo máximo e o numero de pontos da trajetória
t = 0

do i = 1, N
   x = x0 + v*t + 0.5*a*t**2
   print*, t, x
   t = t + dt
end do

end program mrua

Nota: este programa tem um erro que você deve descobrir

Controle de Fluxo (IF e CASE)

Possibilitam a escolha de rumos diferentes no programa dependendo de valores de variáveis ou condições entre elas.

IF ... THEN ...

A sintaxe é:

if (condição) then
     instruções executadas quando condição verdadeira
else
     instruções executadas quando condição falsa
end if

Para apreciar o poder dela primeiro vejamos:

Operadores relacionais e lógicos

Operador Função
== igual
/= diferente
> maior
>= maior ou igual
< menor
<= menor ou igual
.AND. E lógico
.OR. OU lógico
.NOR. NÃO lógico


Eles são usados junto com as instrucões de controlo como o IF (se), exemplo:

! exemplo de divisão a x b:
...
read*, a, b

if (b /= 0) then
    c = a/b
else
     stop 'b=0'
end if
print*, 'a/b=', c
...

CASE

Este serve para quando temos muitas opçõs ou escolhas para tomar rumos diferentes no programa.
Vejamos a sintaxe diretamente no exemplo:

Select Case (J)
    Case(1)
      print*, "caso 1"
    Case(2)
      print*, "caso 2"
    Case (4:10)
      print*, "caso 4-10"
    ....
    Case Default
      print*, "não foi nenhum dos casos contemplados"   
End Select

Ciclos (Avançado)

Ciclo infinito (Do ... end Do)

Se não especificamos com um índice o numero de vezes que o laço deve ser executado este será executado para sempre. Exemplo:

Do 
  print*, "All work and no play makes Jack a dull boy"
End Do

vai fazer que a tela do computador pareça ter sido controlado por um espírito maligno saído do filme O Iluminado. Só vamos poder cortar a loucura dela com CTRL C.

Para que seja de utilidade usamos o Exit, como no exemplo abaixo:

Do
    Print*, "Digite o número 1 ou 2"
    Read*, num
    Select Case (num)
    Case (1)
       Print*, "Você digitou 1"
       Exit
    Case (2)
       Print*, "Você digitou 2"
       Exit
    Case Default
       Print*, "Opção inválida"
    End Select
End Do

Se queremos um programa a prova de fogo (ie que não de erro por entrada de dados) podemos fazer uma pequena modificação:

Do
    Print*, "Digite o número 1 ou 2"
    Read(*,*,ERR=100) num
    Select Case (num)
    Case (1)
       Print*, "Você digitou 1";  Exit
    Case (2)
       Print*, "Você digitou 2";  Exit
    Case Default
100    Print*, "Opção inválida"
    End Select
End Do
End

Na qual usamos ERR=100 para o código se recuperar de dado inesperado, transferindo o controle do programa para a linha com esse número em caso de erro (texto em lugar de número por exemplo).

Ciclo condicional (Do While)

O Do While é executado enquanto uma condição lógica é satisfeita. Exemplo:

 I = 1
 Do While ( I <= 100 )
    I = I + 1; print*. I
 End do

Resulta equivalente a:

 Do I + 1, 100
    print*. I
 End do

Porem é de utilidade quando a condição não é trivial. Exemplo:

 fac = 1. + 1./3.;  Imax = 1.E6
 I = 1;  print*, I
 Do While ( I < Imax )
    I = fac*I;  print*, I 
 End do

Arranjos (Arrays)

Tecnicamente, são tipos de dados ou variáveis estruturadas. De outra forma, um arranjo é um conjunto de dados escalares, todos do mesmo tipo (REAL ou INTEGER, etc), organizados de forma regular de maneira que é fácil atribuir ou extrair valores deles.

Assim como um escalar real é definido como

Real A

um array real pode ser definido assim:

Real A(10)

No primeiro exemplo A pode conter apenas um valor por vez, enquanto no segundo, 10 diferentes valores podem ser armazenados em A, cada um dos quais pode ser referido via o índice assim

A(1) = 0;  A(2:7) = 4;  A(8) = -1;  A(9:10) = 2
 Do i =2, 10, 2
    print*, A(i) !imprimindo só as posições pares
 End do

Rodando esse programa teremos na tela:

  4.000000
  4.000000
  4.000000
 -1.000000
  2.000000

Arranjos Estáticos

Arranjo estático e aquele cuja dimensão (números de elementos do arranjo) é definido nas declarações (instruções no início do programa e que não são executadas durante o tempo de rodada), é um número fixo que não pode ser mais alterado durante a execução. Exemplos:

Real A(10), B(30)
Integer GG(10,10), ax(5,20), FG(6,10,5)

Também pode ser assim:

Character*10, Dimension (10,10) :: A, B, C, D

A dimensão é definida com Dimension(10,10) para todos os arrays (A,B,C,D), todos eles da mesma forma (shape).
NOTA: quando mais de um atributo é aplicado a uma ou mais variáveis (arrays ou escalares) os dois pares de pontos (::) entre atributos e variáveis é mandatório

Arranjos Dinâmicos

A diferença com os anteriores é que a dimensão destes é definida durante durante a execução do programa, pudendo até ser modificada durante o mesmo. Isto se consegue com o atributo ALLOCATABLE. Vejamos o exemplo:

Real, Allocatable :: A(:), B(:,:)
Integer, Allocatable :: K(:), AJ(:,:,:)

Nesses exemplos definimos A e K como arrays vetoriais, B matricial, e AJ tensorial.
O número de elementos deles é definido durante o tempo de execução com ALLOCATE. A seguir um exemplo que ilustra a vantagem de usar arranjos dinâmicos

Real, Allocatable :: A(:), B(:,:)
Integer I, N, M

Read*, N

Allocate (A(N), B(N,N))

Do I = 1, N
   A(I) = 2*I
End Do

Do i =1, N
   B(i,:) = A + i
End do

...
if (j > 0) then
  Deallocate (B)
else 
  Deallocate (A)
end if

...
Select Case (J)
 Case(-1)
   Allocate (A(N/2)
 Case(2) 
   Allocate (B(N,N/2))
End Select case

Funções e Subrotinas

São módulos independentes para fazer tarefas especificas definidas pelo programador. Com a declaração FUNCTION é possível definir funções matemáticas diferentes ou além do conjunto de funções intrínsecas do compilador. Elas podem ser de uma ou mais variáveis, porem devolvem um valor só.
ATENÇÂO a sintaxe: na declaração de função se escreve g(x) (primeira linha nos exemplos onde se declara o nome da função e das variáveis). Na definição da função se escreve:

g = ...

não se colocam os argumentos ou variáveis a esquerda, elas devem aparecer só a direita da instrução.


Quanto a subrotina ela e um procedimento mais geral que pode operar em um conjunto ou todas as variáveis que estão no programa principal, e no retorno ao programa não há limite nas variáveis que pode devolver. Vejamos a sintaxe de cada uma:

Function

A sintaxe é:

REAL FUNCTION NOME(VARIAVEL)
...
NOME = operações com VARIAVEL
END FUNCTION NOME

Vamos para o exemplo:

Real Function g(x)
Implicit None
Real x

  g = x**2 * sin(x)

End Function g

Exemplo de duas variáveis:

Real Function paraboloide(z,w)
Implicit None
Real z,w

  paraboloide = z**2 + w**2

End Function paraboloide 

É possível também definir funções não analíticas, como por exemplo:

...
if (x <= 0) then
    g = 0.
else
    g = 1.
end if
...

Logo na execução, dentro do programa principal, se chama como qualquer função intrínseca do FORTRAN. Vejamos um exemplo completo de programa com definição de função.

! este e o program principal que usa a funcao h(x)
Program exemplo_uso_funcao
Implicit None
Real, external :: h(x)
Real x, dx, y
Integer i,N

Print*, "programa para construir tabela de h(x)"
Print*, "entre o primeiro e ultimo x e o numero de pontos a graficar"
Read*, x0, xf, N

dx = (xf - x0)/N

Do i = 0, N
   x = x0 + i*dx
   print*, x, h(x)
End Do

End Program exemplo_uso_funca

! aqui a definicao de funcao h(x)
Real Function h(x)
Real x
   h = exp(-x**2)/2.0
End Function h

Subroutine

A sintaxe de subrotina e:

SUBROUTINE EXEMPLO (Argumentos)
...
END SUBROUTINE

No meio todas as instruções FORTRAN são validas, pois a subrotina é um programa. A idéia é que se um conjunto de operações se repete muito, então resulta conveniente colocá-las num módulo e chamá-lo cada vez que for necessário, assim:

PROGRAM Principal
...
  CALL EXEMPLO(Argumentos)
  ...
  DO
     ...
     CALL EXEMPLO(Argumentos)
     ...  
  END DO
 ...
END PROGRAM Principal

Exemplo:

Program Main
implicit None
Real A(3,3)

  Read*, B
  Call Matriz(A,B)

End Program Main

Subroutine Matriz(A,B)
Real A(3,3)

  A = B

End Subroutine Matriz

Module

Esta estrutura permite definir variáveis de uso comum entre o programa principal e as subrotinas sem se preocupar de passar elas como argumentos das mesmas. Vejamos um exemplo:

Module VARIAVEIS
  Real a, b
  Integer N
End Module

Depois no programa e subrotina

Program Principal
Use VARIAVEIS           !o main sabe agora de a,b,N
Implicit None
...
  Read (*,*) a, b, N    !estes valores de a,b,N serão passados para sub
  Call Sub
...
End Program

Subroutine Sub
Use VARIAVEIS           !a Subrutina sabe de a,b,N
Implicit None
...
  h = (a-b)/N           !e pode usa-las com os valores que tinham quando foi chamada
...
End Subroutine

Links

Erros sutis com F90