Neste curso usaremos Linux para programar em Fortran. Então precisamos estudar um pouco de Linux e de Fortran.
Índice:
Alguns links:
- Site do Eric
- Site da Tereza
- WikiBook de Fortran
- Dicas de Fortran
- Cartão de referência de Fortran90
- Outro cartão de referência de Fortran90
- Lista de funcões intrínsecas do gfortran
- Wikibook de LaTeX
- Curso de LaTeX
Alguns comandos de Linux são importantes e eles rodam no terminal:
-
cd
Esse comando muda de diretório. Por exemplo, estou na pasta Music e quero entrar na pasta Iron Maiden:cd Iron\ Maiden
E tem que ter essa barra invertida antes do espaco. Isso significa que o espaco faz parte do nome da pasta. Se não tiver a barra, o
cd
acha que você quer entrar em duas pastas, uma chamada Iron e outra Maiden, e aí dá erro. Outra opção é usar áspas:cd "Iron Maiden"
. -
ls
Esse comando lista o conteúdo de uma pasta. Por exemplo, estou na pasta Iron Maiden/Dance of Death:ls '01 - Wildest Dreams.mp3' '03 - No More Lies.mp3' '05 - Dance Of Death.mp3' '07 - New Frontier.mp3' '09 - Face In The Sand.mp3' '11 - Journeyman.mp3' '02 - Rainmaker.mp3' '04 - Montsegur.mp3' '06 - Gates Of Tomorrow.mp3' '08 - Paschendale.mp3' '10 - Age Of Innocence.mp3'
-
mkdir
Esse comando cria uma pasta. Por exemplo:mkdir FisComp17 cd FisComp17 mkdir Aula1 ls 'Aula1'
-
pwd
Esse comando fala em qual diretório você está. Por exemplo:pwd /home/h/Music/Iron Maiden/Dance Of Death
-
mv
Esse comando move arquivos de lugar. Por exemplo: você acabou de baixar o álbum Powerslave, de 1984 do Iron Maiden, e descompactou em sua pasta pessoal (/home/voce/
) e quer mover para a pasta/home/voce/Music/IronMaiden
:cd /home/voce mv Powerslave /home/voce/Music/IronMaiden
-
rm
Esse comando deleta arquivos. Por exemplo, você quer deletar o arquivoWesleySafadao.zip
:rm WesleySafadao.zip
Mas se você quiser deletar a pasta
Wesley
, o comando é diferente:rm -r Wesley
Aquele
-r
significafaça isso de modo recursivo
, ou seja, para pastas. Para copiar diretórios com o comandocp
, tem que usar o-r
também. -
gfortran
: esse é o compilador de Fortran90 (detalhes disso mais pra baixo). Ao contrário de Python, que é só escrever o código e rodar, em Fortran temos que compilar o código antes. Isso significa: escrever o código e em seguida rodar um programa (compilador) que cria um executável. Por exemplo, quero compilar o código 01.f90:gfortran 01.f90 -o 01
Cada coisa (se chamam argumentos) depois do
gfortran
significa algo:01.f90
: esse é o nome do código fonte a ser compilado.-o 01
: isso significa: "Compilador, produza como saída (o
deoutput
e não o número zero!) um arquivo binário (executável) chamado 01". Uma coisa legal é que se você não escrever esse-o 01
, ogfortran
vai gerar um aquivo chamadoa.out
. E aqui dá pra perceber uma coisa interessante: não importa a extensão do arquivo! Você até pode chamar ele deprograminha.bonitinho
que funciona do mesmo jeito!
Existem alguns outros argumentos que são interessantes para usar tbm, mas são opcionais:
-Wall -Wextra -Wconversion-extra
: isso faz com que o compilador te fale coisas que podem estar erradas no seu código!-O3
: isso faz com que o compilador otimize o código gerado, para ficar mais rápido. Essa é a letraO
maiúscula e não o número zero! E depois da letraO
tem o número 3.
-
./01
Isso executa o programa 01 que acabamos de compilar!
Caso queira aprender mais sobre bash, aqui tem um guia muito bom! Recomendo!
VIM (Vi IMproved
) é um editor de texto maravilindo!
Ele funciona no terminal e faz MUITA coisa. Um modo de aprender a usar ele é
pelo comando vimtutor
. Basta abrir um terminal e digitar esse comando. Uma
alternativa mais coloridinha é o jogo online
VIM Adventures.
Precisamos instalar um compilador para compilar os códigos. Recomendo o gfortran, o compilador de Fortran do projeto GNU. Esse compilador é livre, gratuito e open source.
Se você está no maravilindo mundo do Linux, instalar é super simples:
- no Ubuntu & Debian:
sudo apt-get install gfortran
- no ArchLinux:
sudo pacman -Sy gcc-fortran
Se você gosta de sofrer, pode sofrer com Fortran no Windows também! Para instalar, adquira o instalador na seção de downloads oficiais para Windows e instale.
Até isso é mais simples que Windows:
brew install gcc
Vamos fazer um programa em Fortran que escreva algum blablabla no terminal. Para isso, abra seu editor de texto favorito (gedit, vim, emacs, atom, nano qualquer um mesmo) e cole isso:
program ola
write(*, *) "666, the number of the beast"
end program ola
Agora salve esse arquivo como oi.f90
(o nome nao importa, mas tem que
terminar em .f90) e compile:
gfortran -Wall -Wextra -Wconversion-extra -O3 oi.f90 -o oi
E vamos rodar o programa:
./oi
666, the number of the beast
Legal, né?
Fortran é bem simples mas tem algumas frescuras.. Todo códifo Fortran comeca com program NomeDoPrograma e termina com end program NomeDoPrograma e tudo o que estiver entre essas linhas é o programa.
Aquela linha ali no meio, write(*, *) "666, the number of the beast"
escreve
aquela string, que está entre áspas. Parece python, né? Vamos ver mais para
frente o que são aqueles asteríscos entre parênteses.
Em Fortran, as variáveis tem tipo e isso não pode mudar. Os tipos básicos de variáveis são:
INTEGER
Apenas para números inteiros.REAL
Nesse tipo, os números podem ter casas decimais.COMPLEX
Isso é para números complexos.LOGICAL
Isso é o famoso booleano, tem dois valores possíveis: verdadeiro e falsoCHARACTER
Isso é para fazer textão.
E se você simplesmente sair usando as variáveis, o compilador vai decidir o tipo delas pelo nome da variável! Isso pode bugar seu programa e pode ficar muito difícil achar esse erro. Isso se chama nomes implícitos das variáveis (ou algo assim). e podemos desligar isso no código, e falar pro compilador o que queremos para cada variável:
program ola
implicit none
integer :: N
character(30) :: textao
N = 666
textao = "O número da besta eh"
write(*, *) textao, N
end program ola
Vamos compilar e executar:
gfortran -Wall -Wextra -Wconversion-extra -O3 ola.f90 -o ola
./ola
O número da besta eh 666
Aquele implicit none
tira essa funcionalidade do fortran, e assim podemos
declarar o tipo de nossas variáveis.
O character(30)
significa que a string tem 30 caracteres. Se você salvar
na variável um texto com mais de 30 caracteres, como a letra toda de uma
música, só os primeiros 30 vão ficar salvos.
Para escrever várias variáveis com a funcão write
, separe-as por vírgulas.
Em Fortran, comentários são identificados pelo ponto de exclamacão !
e tudo o
que vier depois dele na mesma linha é ignorado. Vamos comentar o exemplo
anterior:
program ola
! Iron Maiden é uma banda muito foda!
implicit none ! nada de variáveis implícitas
integer :: N ! uma variável inteira chamada N
character(30) :: textao
N = 666
textao = "O número da besta eh"
write(*, *) textao, N
end program ola
Não precisa indentar um código Fortran. Mas ele fica mais bonitinho e facilita a leitura.
As funcões read
e write
são bem parecidas. Uma lê alguma coisa e salva numa
variável e a outra escreve o conteúdo de uma variável em algum lugar. Esse
lugar pode ser um arquivo ou na tela.
Por exemplo:
program oi
implicit none
character(80) :: banda
write(*, *) "Que banda eh a melhor: IronMaiden ou JudasPriest?"
read(*, *) banda
write(*, *) "Voce respondeu: ", banda
end program oi
Se você compilar e rodar esse programa, vai aparecer no terminal o texto Que banda eh a melhor: IronMaiden ou JudasPriest?
e o programa vai esperar você
dar uma resposta. Depois que você responder, o programa imprime na tela a sua
resposta.
Em alguns casos, o programa precisa tomar uma decisão baseada em algum resultado. Para isso existe a estrura de condicionais. Por exemplo:
program oi
implicit none
character(80) :: banda
write(*, *) "Que banda eh a melhor: IronMaiden ou JudasPriest?"
read(*, *) banda
if (banda .eq. "IronMaiden") then
write(*, *) "\m/"
else if (banda == "JudasPriest") then
write(*, *) "Resposta quase certa..."
else
write(*, *) "bye bye..."
endif
end program oi
Agora as coisas ficam mais interessantes: dependendo da resposta que você der, o programa tem uma saída diferente.
Existem dois modos de comparar o valor das variáveis: usando os símbolos matemáticos ou escrevendo as coisas. Vejamos:
símbolo | < | <= | == | /= | > | >= |
---|---|---|---|---|---|---|
texto | .LT. | .LE. | .EQ. | .NE. | .GT. | .GE. |
Obviamente nem todas essas operacões fazem sentido para texto...
Em alguns casos, queremos fazer alguma coisa várias vezes, para isso usa-se um laço (loop em inglês). Em Fortran isso é assim:
do i = inicio, fim, [incremento]
faz algo aqui várias vezes
enddo
Por exemplo, se você quiser escrever um texto várias vezes na tela, pode usar esse código aqui:
program oi
implicit none
integer :: N
integer :: i
write(*, *) "Me diga um numero:"
read(*, *) N
do i = 1, N
write(*, *) "666, the number of the beast!"
enddo
end program oi
Ele pede um número, depois escreve na tela 666, the number of the beast!
N
vezes!
Se você nao especificar um valor para o incremento, o fortran assume que é 1. Mas pode ser qualquer valor. Tome muito cuidado com isso: Se você fizer um loop assim:
do i = 1, 10, -1
write(*, *) "666, the number of the beast!"
enddo
O programa nunca vai parar! Porque a variável i
vai ser cada vez menor:
1, 0, -1, -2, -3...
e isso vai sempre ser menor que 10
.
Mas laços também podem ser assim:
do while (condição)
blab lab lá. ...
end do
Por exemplo:
Program uhul
Integer :: I
I = 13
Do while (I < 42)
Write(*, *) "666"
I = I + 1
End do
end program uhul
Vamos fazer uma calculadora que converte temperatura em graus célsius para farenheit. A fórmula para isso é:
F = (C * 9/5) + 32
Para isso precisamos ler a temperatura em Célsius, checar se o valor é válido, fazer a conta, e mostrar o resultado.
Agora vem uma coisa nova: números reais. Também chamados de ponto flutuantes. Nesse curso, vamos usar precisão dupla sempre. Para isso, a variável deve ser declarada assim:
real(8) :: T_c, T_f
Aquele 8 alí é o número de bytes usado para salvar a informacão. Os valores
válidos são 4, 8, 16
; e 4
é o padrão (precisão simples).
O programa então fica assim:
program temperatura
real(8) :: T_c, T_f
write(*, *) "Qual a temperatura em Celsius?"
read(*, *) T_c
if (T_c < -273.15d0) then
write(*, *) "Essa temperatura não existe!"
else
T_f = (T_c * 9.d0 / 5.d0) + 32
write(*, *) T_c, "C = ", T_f, "F"
endif
end program temperatura
E sempre que fizermos alguma conta com números, precisamos colocar o d0
depois, para o compilador saber que é precisão dupla.
Esse programa salva na variável T_c
o valor da temperatura. Depois checa se
está acima de zero absoluto, se não estiver, não converte para fahrenheit. Se
for uma temperatura válida, converte e escreve na tela.
O primeiro argumento das funcões read
e write
é qual arquivo é usado para
ler ou escrever. Se tiver um asterísco, é o teclado ou a tela.
Para abrir um arquivo, usamos uma funcão chamada open
. Ela precisa de uma
variável inteira e do nome do arquivo:
program escreve
implicit none
integer :: numero = 10
open(numero, file = 'NumberOfTheBeast.txt')
write(numero, *) 666
close(numero)
end program escreve
Vejamos linha a linha o que acontece:
- primeiro vem o nome do programa:
escreve
implicit none
: não queremos variáveis com tipos implícitosinteger :: numero = 10
: declaramos uma variável do tipo integer, chamada numero e com valor 10open(numero, file = 'NumberOfTheBeast.txt')
: abre (cria) um arquivo, chamado NumberOfTheBeast.txt, identificado pornumero
write(numero, *) 666
: escreve no arquivo identificado pornumero
o valor 666close(numero)
: fecha o arquivo. Isso é importante para garantir que será salvo.end program escreve
: programa acaba aqui
Dica: nunca use os numeros 5
e 6
para trabalhar com arquivos, eles são numeros especiais:
um é o teclado e o outro é a tela. Sempre use números maiores que 10
para evitar problemas.
Também podemos fazer funções em Fortran! E também existem subrotinas! A diferença entre elas:
função
em Fortran retorna um valor.subrotina
em Fortran não retorna um valor.
Mas ambas podem modificar as variáveis de entrada!!
Um exemplo de função:
function NumberOfBeast(N)
integer :: NumberOfBeast ! aqui definimos o tipo de retorno da funcao
real(8) :: N ! aqui definimos que o tipo do argumento N é um real(8)
if (N > 0) Then
write(*, *) "666"
end if
NumberOfBeast = 666 ! a funcão retorna o que estiver salvo no nome dela
N = N + 666 ! A variável de entrada agora muda de valor!!
end function NumberOfBeast
Veja que temos que falar qual o tipo de cada argumento no corpo da funcão, assim como especificar qual o tipo de retorno. A funcão retorna o que estiver salvo numa variável com o mesmo nome dela.
Um exemplo de um código completo usando essa função acima:
program bla
implicit none
real(8) :: A
A = -13
write(*, *) NumberOfBeast(A), A
contains ! as funcoes sao definidas aqui
function NumberOfBeast(N)
integer :: NumberOfBeast ! aqui definimos o tipo de retorno da funcao
real(8) :: N ! aqui definimos que o tipo do argumento N é um real(8)
if (N > 0) Then
write(*, *) "666"
end if
NumberOfBeast = 666 ! a funcão retorna o que estiver salvo no nome dela
N = N + 666 ! A variável de entrada agora muda de valor!!
end function NumberOfBeast
end program bla
Se executarmos esse código:
gfortran -O bla.f90 -o bla
./bla
666 653.0000000000000
Você entende o por quê?
Agora um exemplo de subrotina, é quase igual:
program blu
implicit none
real(8) :: A, B
integer :: C
A = 13
B = 42
C = 137
write(*, *) "antes da subrotina", A, B, C
call NumberOfBeast(A, B, C) ! note a diferença de uso de função e subrotina!!!!
write(*, *) "depois da subrotina", A, B, C
contains
subroutine NumberOfBeast(M, N, P)
! temos que falar que tipos de argumentos M, N, P são
real(8) :: M, N
integer :: P
if (M > 0) Then
N = 666
else
N = -666
end if
P = M + N
end subroutine NumberOfBeast
end program blu
E a saída:
gfortran -O blu.f90 -o blu
./blu
antes da subrotina 13.000000000000000 42.000000000000000 137
depois da subrotina 13.000000000000000 666.00000000000000 679
Se você tem uma função f(x)
, para usá-la basta chamar f(x)
no seu código.
Mas, se você tem uma subrotina s(y)
, para usá-la você precisa [literalmente]
chamá-la: call s(y)
.
É possível mudar o valor dos argumentos dentro das funcões/subrotinas! Tome muito cuidado com isso! Tem como desligar isso, e isso está explicado aqui.
Podemos definir vetores em Fortran da seguinte maneira:
real(8), dimension(666) :: bla
E assim temos um vetor de 666 elementos reais! Os vetores comecam por padrão na posicão um e nào na posicão zero!!!!!
Vamos criar um vetor e atribuir alguns valores:
real(8), dimension(666) :: bla
bla(1) = 42
bla(2) = 137
bla(3) = 666
E se quisermos uma matriz?
real(8), dimension(666, 42) :: blatriz
blatriz(1, 1) = 6.66d0
blatriz(2, 2) = 6.d0
blatriz(137, 42) = 642.d0
HA!
Mas e se não soubermos o tamanho dos vetores e matrizes na criação do código fonte? Nesse caso temos que usar a chamada alocação dinâmica, que é o processo de criar dinamicamente um vetor.
Para isso, precisamos declarar nossa variável com um atributo de alocável
e
um indicando qual a dimensão dela - se é um vetor, tem 1 dimensão; se é uma
matriz, tem 2. Depois precisamos dizer a dimensão. E depois limpar a variável:
program dinamico
implicit none
real, allocatable, dimension(:) :: vetorzinho
real, allocatable, dimension(:,:) :: matrizinha
allocate(vetorzinho(42)) ! nosso vetorzinho tem 42 posições :)
allocate(matrizinha(137,666)) ! nossa matriz tem tamanho 137x666 \o/
! aqui a gente faz alguma coisa com esse vetor/matriz
deallocate(vetorzinho, matrizinha) ! dessa linha pra baixo, **nao** podemos
! usar nem o vetorzinho nem a matrizinha
end program dinamico
Na verdade, pseudo aleatórios.
Suponha que, por algum motivo, você quer produzir uma sequência de números que parecem ser totalmente aleatórios entre zero e um: 0 ≤ x < 1.
Para fazer isso em Fortran, use a subrotina random_number(harvest)
. Você tem
que passar para ela uma variável real. Essa subrotina vai fazer uma colheita
(harvest em inglês) de um número pseudo aleatório e salvar nessa variável. Se
você passar um vetor de reais, a subrotina vai preencher todas as posições com
números aleatórios.
E o segredo de uma boa colheita é uma boa semente! Para ter uma boa semente,
chame a subrotina random_seed()
no começo do seu código.
Um exemplo de como fazer isso tudo está aqui:
program teste
implicit none
! numeros reais
real(4) :: r4
real(8) :: r8
real(16) :: r16
! uma matriz 4x4 de reais
real(8) :: muitos(4,4)
! inicia o gerador de numerinhos
call random_seed()
! todo mundo recebe um número pseudoaleatório
call random_number(r16)
call random_number(r8)
call random_number(r4)
call random_number(muitos)
! olha só o resultado:
print *, r16, r8, r4
print *, muitos(1,:)
print *, muitos(2,:)
print *, muitos(3,:)
print *, muitos(4,:)
end program teste
E se compilarmos e executarmos:
$ gfortran aleat.f90 -o aleat
$ ./aleat
0.874676781539981845053480738391677618 0.57505145031418081 0.541935444
0.73207031044638016 0.28057229020748109 1.9748846033733503E-002 0.35994364391829448
0.32109422766730178 0.85784435637327083 1.1146918705856490E-002 0.12327309451436308
0.68715436105949601 0.97579903334737772 0.69876248466784485 0.78320390097181725
0.45095051262175967 0.74165725644654046 0.86718512447000606 9.9055518599761361E-002
Se você executar esse código, provavelmente vai ter outra saída.
gnuplot é um software para fazer gráficos, bem simples de usar. Tem o manual em pdf online no site dele, mas pode clicar aqui tbm para acessar a documentação.
Para usar o gnuplot, abra um terminal e execute o comando gnuplot
.
Suponha que você queira fazer um gráfico com as funções:
- e^(-x^2)
- sin(x)
- tanh(x)
Então abra o gnuplot e execute esse comando:
plot exp(-x**2), sin(x), tanh(x)
Pois é. Mas e se quiser graficar um arquivo de dados?
Se você tem um arquivo.txt
com duas colunas de dados, sendo que cada linha é
um conjunto de pontos (x, y)
:
plot "arquivo.txt"
Se quiser saber como definir textos de título, eixos, legenda, etc, veja a próxima seção.
Para fazer um gráfico em PDF, precisa mudar o padrão do gnuplot de fazer o gráfico na tela para fazer num pdf, e qual o nome do arquivo:
set term pdfcairo color enhanced
# aqui define o nome do arquivo de saída
set output "bla.pdf"
E depois disso, cada comando plot
vai criar uma página nova com apenas aquele
gráfico. Por exemplo:
# magia negra
set encoding utf8
set term pdfcairo enhanced color
# aqui define o nome do arquivo de saída
set output "bla.pdf"
# primeiro gráfico: título e eixos
set title "Gráfico da página 1"
set xlabel "dist (m)"
set ylabel "bla (s)"
plot sin(x)/x title "Xablaucius"
# segundo gráfico: título e eixos
set title "Gráfico da página 2"
set xlabel "bla (ble)"
set ylabel "bli (blo)"
plot atan(sin(x)**2/x) with impulses title "Nota dos hellatórios", \
real(sin(x)**besj0(x))/(2*x) title "vontade de corrigir"
# terceirográfico: título e eixos
set title "Gráfico da página 3"
set xlabel "Curintia"
set ylabel "Sorvete de morango"
# intervalo no eixo y
set yrange [-500:15000]
# muda a legenda de lugar
set key left box
plot exp(x) title "expectativaa"
Eu ia colocar o resultado desse script aqui, mas não ficou legal mostrar nessa página. Então copia isso aí e roda vc mesmo.
Este material está disponibilizado sob uma licensa Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.