F90 – Resumão – IF – UFRGS

F90 – Resumão – IF – UFRGS

Resumo adaptado do original: (IF-UFRGS – Origem: Wikipédia, a enciclopédia livre.).


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,
  • Fortran 90, 95, 2003 e 2008

Olá Mundo

Exemplos de código típico e mínimo para testar se o Fortran 90 está instalado (há 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, no terminal linux por exemplo, com cat linha por linha ou com um editor de texto qualquer, por exemplo no linux (vi, nano, emacs, etc…)

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 (executar)

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

./ é uma espécie de “comando” que deve ir na frente do nome do programa, necessário para que o intérprete de comandos (shell) do terminal (ou sistema) linux saiba que o programa está no diretório de trabalho. Ao ser executado será exibido na tela do computador o seguinte:

olá Mundo

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.

Código fonte do programa Olá Mundo na versão padrão de formato-sintaxe: program ... end program:

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

Esta versão está no estilo Fortran 90, mesmo que com declarações opcionais como program nome (terminada com end program) é útil quando temos códigos longos com subrotinas (programas chamados pelo programa principal)

NOTA: A título de curiosidade, a única declaração obrigatória em Fortran é o comando end.


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.

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 primitivos de variáveis:

Tipo Primitivo Descrição
INTEGER inteira, exemplo: 0, 1, 100 -34
REAL real ou de ponto flutuante, exemplo: 3.14, -0.003, 3.2E-10, -5.02E100 (notação científica)
COMPLEX complexa, exemplo:
\[(2,3 + 0i):\] (2.3, 0)
\[(-i):\] (0, -1.0)
\[(1,2 \times 10^{-5} – 1 \times 10^{-3}i):\] (1.2E-5, -0.001)
LOGICAL logica, exemplo: .TRUE. .FALSE.
CHARACTER caractere, exemplo: “ola”, “Entre seu nome!”

No programa a definição de variáveis, ou declaração de variáveis, é feita no início e depois seguem as instruções de execução, veja a forma no seguinte exemplo:

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

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 binary digit 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 (7 bits) 1001100 representa o número decimal 76.

1 0 0 1 1 0 0
6 5 4 3 2 1 0
\[1 \times 2^6\] \[0 \times 2^5\] \[0 \times 2^4\] \[1 \times 2^3\] \[1 \times 2^2\] \[0 \times 2^1\] \[0 \times 2^0\]
64 0 0 8 4 0 0

\[64+8+4 = 76\]

(Mais informações: 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 padrão (standard)

Se quisermos ler ou gravar num arquivo:

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

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

 CLOSE(1)
 CLOSE(2)

Operadores matemáticos

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

Exemplo usando todas as operações

Equação:

\[z = 3,14 + \frac{\sqrt(x)}{3} – 4,3\times10^{-6}\]

z = 3.14+x**(1./2)/3 - 4.3E-6

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

Lista das funções intrínsecas mais comuns

Function        Generic  Specific Data type
                name     name     Arg   Res

Square root     SQRT     SQRT     R     R
                         DSQRT    D     D
                         CSQRT    C     C

Exponential     EXP      EXP      R     R
                         DEXP     D     D
                         CEXP     C     C

Natural         LOG      ALOG     R     R
 logarithm               DLOG     D     D
                         CLOG     C     C

Common          LOG10    ALOG10   R     R
 logarithm               DLOG10   D     D

Sine            SIN      SIN      R     R
                         DSIN     D     D
                         CSIN     C     C

Cosine          COS      COS      R     R
                         DCOS     D     D
                         CCOS     C     C

Tangent         TAN      TAN      R     R
                         DTAN     D     D

Arcsine         ASIN     ASIN     R     R
                         DASIN    D     D

Arccosine       ACOS     ACOS     R     R
                         DCOS     D     D

Arctangent      ATAN     ATAN     R     R
                         DATAN    D     D
                ATAN2    ATAN2   2R     R
                         DATAN2  2D     D

Hyperbolic      SINH     SINH     R     R
 sine                    DSINH    D     D

Hyperbolic      COSH     COSH     R     R
 cosine                  DCOSH    D     D

Hyperbolic       TANH    TANH     R     R
 tangent                 DTANH    D     D

Ciclos ou laços (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

há diferenciação no 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 preferencialmente 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) (unidimensional) de um objeto com aceleração constante:

\[x(t) = x_0 + v_0t + \frac{at^2}{2}\]

 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

Desafio: este programa contém um erro que você deve consegue descobrir? Se sim comente no formulário no final do post. :o)

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 análogo Função
== .eq. igual
/= .ne. diferente
> .gt. maior
>= .ge. maior ou igual
< .lt. menor
<= .le. menor ou igual
.and. .and. E lógico
.or. .or. OU lógico
.not. .not. NÃO lógico

Eles são usados junto com as instruçõ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ções 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

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 infinitamente. Exemplo:

do
   print*, "seja aproximadamente exato, não exatamente errado!"
enddo

o programa ficará rodando infinitamente, fazendo a frase ser escrita na tela em um laço infinito, que só poderá ser cancelado teclando CTRL C no terminal.

Para que um ciclo infinito tenha utilidade prática, em algum momento (lógico) do programa é preciso lançar mão do comando 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 (que não de erro por entrada de dados) podemos fazer uma pequena modificação:

Na qual foi usado 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 .le. 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 .lt. 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 (:: quadrupleto) entre atributos e variáveis é mandatório sempre que houver atribuição inicial na declaração.

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 .gt. 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, implementada em Fortran, 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:

\[g(x)=x^{2} sin(x)\]

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 .le. 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 função h(x)
program exemplo_uso_funcao
implicit None
real, external :: h(x) ! external pq está depois do end program
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)
enddo

end program exemplo_uso_funcao

! aqui a definicao de funcao h(x)
real function h(x)
real x
   h = exp(-x**2)/2.0
end function h

Subrotina (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, dimension(3,3) :: A=0., B=0.

  call matriz(A,B)
  print*,"Matriz A"
  print'(3(f5.3,1x))',A
  
  print*,"Matriz B"
  print 100,B

100 format (3(f5.3,1x))

stop("Programa finalizado!")
end program main

subroutine matriz(A,B)
real, dimension(3,3) :: A,B

    A = B + 1

end subroutine matriz

Resultado:

Matriz A                                                  
1.000 1.000 1.000
1.000 1.000 1.000
1.000 1.000 1.000   
Matriz B                   
0.000 0.000 0.000
0.000 0.000 0.000
0.000 0.000 0.000 

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 Subrotina sabe de a,b,N
implicit none
...
  h = (a-b)/N           !e pode usá-las com os valores que tinham quando foi chamada
...
end subroutine

IDPGPT-1890

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

*