Skip to content

Fortran Folha de referência

Linguagem pioneira para computação científica e numérica.

01

Básico & Estrutura do Programa

Estrutura do Programa & Hello World

Todo programa Fortran começa com 'program NAME' e termina com 'end program NAME'. 'implicit none' é OBRIGATÓRIO em Fortran moderno — força a declaração explícita de todas as variáveis (sem ele, o Fortran usa tipagem implícita onde variáveis começando com i-n são integer, outras real, o que é uma grande fonte de bugs). O bloco 'contains' separa código executável de procedimentos internos (subroutines/functions definidos dentro do programa). Comentários começam com '!'. Free source form (Fortran 90+) usa extensão .f90; colunas não importam. Compile com gfortran/ifort.

fortran
program hello
  ! A complete Fortran program structure
  implicit none
  ! declarations go here
  integer :: status = 0

  print *, "Hello, World!"   ! list-directed output to stdout

  ! executable statements
  call do_work(status)

  print *, "Exit status: ", status
contains
  subroutine do_work(st)
    integer, intent(out) :: st
    st = 0
    print *, "Working..."
  end subroutine do_work
end program hello

! Compile: gfortran hello.f90 -o hello
! Run:     ./hello

Variáveis & Tipos Intrínsecos

Fortran tem 5 tipos intrínsecos: integer, real, complex, character, logical. 'kind' seleciona precisão/tamanho — use kind=8 para 64-bit (ou melhor, use selected_real_kind/iso_fortran_env para portabilidade). Literais real precisam de um sufixo kind: 3.14_8 (não apenas 3.14). Double precision é a sintaxe legada para real(kind=8). Literais complex usam a forma (real, imag). Valores logical são .true. / .false. (com pontos). Strings character têm um 'len' fixo a menos que declaradas com len=: e allocatable (deferred-length, Fortran 2003+). Sempre inicialize com o sufixo kind correspondente para evitar perda silenciosa de precisão.

fortran
program variables
  implicit none
  ! Integer types
  integer :: count = 0
  integer(kind=8) :: big = 9223372036854775807_8   ! 64-bit
  ! Real types
  real :: x = 3.14              ! default (often 32-bit)
  real(kind=8) :: y = 2.718281828459045_8   ! double precision
  double precision :: z = 1.0d0
  ! Complex
  complex :: c = (1.0, 2.0)    ! 1 + 2i
  complex(kind=8) :: cw = (1.0_8, 2.0_8)
  ! Character
  character(len=20) :: name = "Alice"
  character(len=:), allocatable :: flexible   ! deferred length
  ! Logical
  logical :: flag = .true.
  ! Print all
  print *, count, big
  print *, x, y, z
  print *, c, cw
  print *, name, flag
end program variables

Constantes & Parâmetros

Constantes usam o atributo 'parameter' e devem ser inicializadas na declaração. Elas não podem ser modificadas — o compilador pode otimizá-las e inseri-las inline. Use SCREAMING_SNAKE_CASE por convenção. character(*) significa 'pegue o comprimento do inicializador' (útil para constantes de string). Parâmetros são comumente usados para tamanhos de arrays, constantes físicas e códigos integer semelhantes a enum. Fortran 2003+ também tem tipos ENUM próprios, mas integers parameter permanecem a escolha idiomática. Parâmetros podem ser usados em declarações de dimensão de array e outros contextos de expressão constante.

fortran
program constants
  implicit none
  ! Named constants via 'parameter' attribute
  integer, parameter :: MAX_SIZE = 100
  real, parameter :: PI = 3.14159265
  real, parameter :: E = 2.718281828
  character(*), parameter :: APP_NAME = "MyApp"
  ! Using parameters
  real :: arr(MAX_SIZE)
  arr = 0.0
  print *, APP_NAME, " size=", MAX_SIZE
  print *, "Circumference: ", 2.0 * PI * 5.0

  ! Enum-like via parameter
  integer, parameter :: SUNDAY = 1, MONDAY = 2, TUESDAY = 3
  integer :: day = MONDAY
  print *, "Day code: ", day
end program constants

Operadores & Expressões

Operadores do Fortran: aritméticos (+ - * / **), com ** para exponenciação (exclusivo do Fortran). Divisão integer trunca em direção a zero — use real(a)/b para divisão verdadeira. Duas sintaxes relacionais: moderna (< > == /= <= >=) e legada (.lt. .gt. .eq. .ne. .le. .ge.). Lógicos: .and. .or. .not. .eqv. (equivalência) .neqv. (ou exclusivo). Concatenação de strings usa //; trim() remove espaços à direita (Fortran preenche strings de comprimento fixo com espaços). mod vs modulo: mod segue sinal de divisão truncada, modulo segue divisão com floor — eles diferem para operandos negativos.

fortran
program operators
  implicit none
  integer :: a = 17, b = 5
  real :: x = 2.0
  ! Arithmetic
  print *, a + b, a - b, a * b    ! 22 12 85
  print *, a / b                   ! 3 (integer division!)
  print *, real(a) / b             ! 3.4 (cast to real)
  print *, a ** 2                  ! 289 (exponentiation)
  print *, mod(a, b)               ! 2 (modulo)
  print *, modulo(a, b)            ! 2 (differs for negatives)
  ! Relational (both forms work)
  print *, a > b, a < b            ! T F
  print *, a .gt. b, a .lt. b      ! T F (old form)
  print *, a == b, a /= b          ! F T
  ! Logical
  print *, (a > 0) .and. (b > 0)   ! T
  print *, (a > 0) .or. (b < 0)    ! T
  print *, .not. (a > 0)           ! F
  print *, (a > 0) .eqv. (b > 0)   ! T (equivalence)
  ! String concatenation
  character(10) :: s1 = "Hello", s2 = "World"
  print *, trim(s1) // " " // trim(s2)   ! Hello World
end program operators

Funções Intrínsecas & Matemática

Fortran tem um conjunto rico de funções intrínsecas (built-in). Matemática: abs, sqrt, exp, log (natural), log10, sin/cos/tan/asin/acos/atan/atan2, sinh/cosh/tanh. Arredondamento: int (trunca), nint (mais próximo), floor, ceiling. Conversão: real(), int(), cmplx(). Inquiry: size, shape, huge (valor máximo), tiny (mínimo positivo), kind. Todas as funções trigonométricas recebem radianos. atan2(y, x) retorna o ângulo no quadrante correto (diferente de atan). Use huge/tiny para verificar limites de intervalo. As intrínsecas são elementais — elas funcionam em arrays elemento a elemento automaticamente.

fortran
program intrinsics
  implicit none
  real :: x = -3.7, y = 2.5
  ! Math functions
  print *, abs(x)         ! 3.7
  print *, sqrt(2.0)      ! 1.414...
  print *, exp(1.0)       ! 2.718... (e^x)
  print *, log(2.0)       ! 0.693... (natural log)
  print *, log10(1000.0)  ! 3.0
  print *, sin(3.14159/2) ! 1.0
  print *, cos(0.0)       ! 1.0
  print *, atan2(1.0,1.0) ! 0.785... (pi/4)
  ! Rounding
  print *, int(x)         ! -3 (truncate toward zero)
  print *, nint(x)        ! -4 (nearest integer)
  print *, floor(x)       ! -4 (toward -inf)
  print *, ceiling(x)     ! -3 (toward +inf)
  print *, abs(x), max(x, y), min(x, y)   ! 3.7 2.5 -3.7
  ! Type conversion
  print *, real(5)        ! 5.0
  print *, int(3.9)       ! 3
  ! Inquiry
  real :: arr(10)
  print *, size(arr)      ! 10
  print *, huge(1)        ! 2147483647
  print *, tiny(1.0)      ! smallest positive real
end program intrinsics
02

Fluxo de Controle

If...Then...Else

Block IF: 'if (cond) then ... else if (cond) then ... else ... end if'. Cada branch precisa de 'then' (exceto o else final). Logical IF é uma linha única: 'if (cond) statement' (sem 'then'/'end if'). Condições usam operadores relacionais (< > == /= <= >= ou .lt. .gt. .eq. .ne. .le. .ge.) combinados com .and. .or. .not. 'stop' termina o programa (opcionalmente com uma mensagem/código). O arithmetic IF (if (x) label1, label2, label3) foi removido no Fortran 2018 — nunca o use. Sempre use 'implicit none' para que variáveis não declaradas sejam capturadas.

fortran
program if_demo
  implicit none
  integer :: score = 85
  character(1) :: grade

  ! Multi-branch if/else if/else
  if (score >= 90) then
    grade = 'A'
  else if (score >= 80) then
    grade = 'B'
  else if (score >= 70) then
    grade = 'C'
  else if (score >= 60) then
    grade = 'D'
  else
    grade = 'F'
  end if

  print *, "Score ", score, " -> Grade ", grade

  ! Logical if (single statement, no 'then')
  if (score < 0 .or. score > 100) stop "Invalid score"

  ! Arithmetic if (OBSOLETE - avoid)
  ! if (x) 10, 20, 30   ! jump to label based on sign
end program if_demo

Select Case (Switch)

select case é a declaração switch do Fortran. Cases podem ser valores únicos (case (3)), listas (case (1, 3, 5)) ou intervalos (case (4:5) significa 4 a 5 inclusive). case default é o fallback. Diferente do C, NÃO há fall-through — cada branch é independente e apenas um roda. Funciona com tipos integer, character e logical (NÃO real). Intervalos de character usam ordenação ASCII ('A':'Z'). Para comparações de ponto flutuante, use if/else. select case é mais eficiente do que longas cadeias if/else if para dispatch de integer/char (o compilador pode usar jump tables).

fortran
program case_demo
  implicit none
  integer :: day = 3
  character(1) :: ch = 'A'
  character(10) :: day_name

  ! Integer select case
  select case (day)
  case (1)
    day_name = "Monday"
  case (2)
    day_name = "Tuesday"
  case (3)
    day_name = "Wednesday"
  case (4:5)
    day_name = "Thu/Fri"
  case (6:7)
    day_name = "Weekend"
  case default
    day_name = "Invalid"
  end select
  print *, day_name

  ! Character select case (case-insensitive via pre-upper)
  select case (ch)
  case ('A':'Z')
    print *, "Uppercase letter"
  case ('a':'z')
    print *, "Lowercase letter"
  case ('0':'9')
    print *, "Digit"
  case default
    print *, "Other"
  end select

  ! Logical select case
  select case (day > 5)
  case (.true.)
    print *, "Weekend!"
  case (.false.)
    print *, "Weekday"
  end select
end program case_demo

Loops Do (Contados)

Loop DO contado: 'do var = start, end, step' (step padrão é 1). O loop roda enquanto var <= end (para step positivo) ou var >= end (para step negativo). var é incrementado APÓS cada iteração. Construtos implied-do [(expr, var=start,end)] são poderosos para inicialização de arrays e listas de I/O. Loops nomeados (outer: do ... end do outer) permitem direcionar cycle/exit a um nível específico de aninhamento. A variável de loop é automaticamente definida; no Fortran ela retém seu valor final após o loop. Evite modificar a variável de loop dentro do corpo do loop.

fortran
program do_loops
  implicit none
  integer :: i, j, total

  ! Basic counted loop: do var = start, end [, step]
  do i = 1, 5
    print *, i          ! 1 2 3 4 5
  end do

  ! With step
  do i = 10, 1, -1      ! countdown
    print *, i
  end do

  do i = 0, 100, 25     ! 0 25 50 75 100
    print *, i
  end do

  ! Implied-do (inline, for array init / I/O)
  integer :: arr(5) = [(i**2, i=1,5)]   ! 1 4 9 16 25
  print *, arr
  print *, (i, i=1,3)                   ! 1 2 3

  ! Nested loops with labels (for cycle/exit targeting)
  total = 0
  outer: do i = 1, 3
    inner: do j = 1, 3
      total = total + i*j
    end do inner
  end do outer
  print *, "Total: ", total
end program do_loops

Do While & Loops Infinitos

do while (cond) ... end do é um loop pré-teste (verifica a condição antes de cada iteração; pode rodar zero vezes). Para comportamento pós-teste, use do ... if (cond) exit ... end do. O 'do ... end do' puro é um loop infinito — você DEVE ter uma declaração exit (caso contrário, infinito). 'exit' sai do loop mais interno (ou de um loop nomeado). Loops nomeados (factorial_loop:) permitem que exit direcione um loop externo. Use do while quando a contagem de iterações é desconhecida e depende de uma condição; use do contado quando a contagem é conhecida de antemão.

fortran
program while_demo
  implicit none
  integer :: n, count
  real :: x, sum

  ! do while: pre-test loop
  n = 1024
  count = 0
  do while (n > 1)
    n = n / 2
    count = count + 1
  end do
  print *, "log2(1024) = ", count   ! 10

  ! Infinite loop with exit
  sum = 0.0
  do
    read(*, *) x
    if (x < 0) exit          ! leave loop
    sum = sum + x
  end do
  print *, "Sum: ", sum

  ! do ... end do with conditional exit
  n = 1
  factorial_loop: do
    if (n > 10) exit factorial_loop
    print *, n, factorial(n)
    n = n + 1
  end do factorial_loop

contains
  recursive function factorial(n) result(f)
    integer, intent(in) :: n
    integer :: f
    if (n <= 1) then
      f = 1
    else
      f = n * factorial(n-1)
    end if
  end function factorial
end program while_demo

Cycle, Exit & Controle de Loop

cycle pula o resto da iteração atual e pula para a próxima (como 'continue' em C/Python). exit sai do loop completamente (como 'break'). Ambos têm como alvo o loop mais interno por padrão, mas com loops nomeados (search: do ... end do search) você pode direcionar um loop externo: 'exit search' ou 'cycle search'. Isso é essencial para sair de loops aninhados de forma limpa. Use cycle para filtragem (pular iterações indesejadas) e exit para terminação antecipada (busca encontrada, erro detectado). Loops nomeados tornam o fluxo de controle aninhado explícito e legível.

fortran
program loop_control
  implicit none
  integer :: i, j

  ! cycle: skip to next iteration (like 'continue' in C)
  do i = 1, 10
    if (mod(i, 2) == 0) cycle   ! skip even numbers
    print *, i                  ! 1 3 5 7 9
  end do

  ! exit: break out of loop (like 'break' in C)
  do i = 1, 100
    if (i * i > 50) then
      print *, "Stopped at i=", i
      exit
    end if
  end do

  ! Named loops: cycle/exit can target outer loops
  search: do i = 1, 5
    do j = 1, 5
      if (i + j == 7) then
        print *, "Found: ", i, "+", j, "= 7"
        exit search             ! break out of OUTER loop
      end if
    end do
  end do search

  ! Early exit from a search
  integer :: arr(10) = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
  do i = 1, size(arr)
    if (arr(i) == 9) then
      print *, "Found 9 at index ", i
      exit
    end if
  end do
end program loop_control
03

Arrays & Operações com Vetores

Declaração & Inicialização de Arrays

Arrays Fortran são 1-indexed por padrão (limite inferior = 1), mas você pode especificar um limite inferior personalizado: a(0:4) tem índices 0..4. Arrays multidimensionais usam ordenação (rows, cols) — armazenamento column-major (primeiro índice varia mais rápido na memória). Inicialize com construtores de array [1,2,3] ou implied-do [(expr, i=start,end)]. reshape preenche um array multi-dimensional a partir de uma lista 1D. size() retorna o total de elementos; lbound/ubound retornam limites inferior/superior. shape() retorna a forma como um array 1D. Arrays são 'whole-array' — você pode atribuir e operar neles sem loops explícitos.

fortran
program array_decl
  implicit none
  ! Declaration with dimension
  integer :: a(5)                  ! 1D, indices 1..5
  integer :: b(0:4)                ! 1D, indices 0..4 (custom lower bound)
  real :: c(3, 4)                  ! 2D, 3 rows x 4 cols
  real, dimension(10) :: d         ! using dimension attribute

  ! Initialization at declaration
  integer :: x(5) = [1, 2, 3, 4, 5]
  integer :: y(5) = [(i*2, i=1,5)] ! implied-do: 2 4 6 8 10
  integer :: z(5) = 0              ! all zeros
  real :: m(2,2) = reshape([1,2,3,4], [2,2])

  ! Allocation later
  print *, size(x), lbound(x), ubound(x)   ! 5 1 5
  print *, size(c, dim=1)                  ! 3 (rows)
  print *, size(c, dim=2)                  ! 4 (cols)

  ! Array of characters
  character(10) :: names(3) = ["Alice", "Bob", "Carol"]
  print *, names(2)                        ! Bob
end program array_decl

Seções de Array & Subscripts de Vetor

Seções de array (slicing) usam a sintaxe a(start:end:stride) — todas as partes opcionais. Stride pode ser negativo (reverso). Subscripts de vetor permitem gather/scatter com um array de índices: a(idx) retorna [a(idx(1)), a(idx(2)), ...]. Seções podem ser atribuídas: a(2:4) = [99,98,97]. O construto 'where' é atribuição condicional em nível de array (como numpy where). Operações de array do Fortran são vetorizadas — sem loops explícitos para operações elemento a elemento. Esse é o recurso matador do Fortran para código numérico: sintaxe limpa, matemática, que os compiladores vetorizam automaticamente.

fortran
program array_sections
  implicit none
  integer :: a(10) = [(i, i=1,10)]
  integer :: b(5)
  integer :: idx(3) = [2, 5, 7]

  ! Array sections (slicing): a(start:end[:stride])
  print *, a(3:7)         ! 3 4 5 6 7
  print *, a(1:10:2)      ! 1 3 5 7 9 (stride 2)
  print *, a(:5)          ! 1 2 3 4 5 (start defaults to 1)
  print *, a(6:)          ! 6 7 8 9 10 (end defaults to ubound)
  print *, a(::3)         ! 1 4 7 10
  print *, a(10:1:-1)     ! 10 9 8 ... 1 (reverse)

  ! Vector subscript (gather)
  b = a(idx)              ! b = [a(2), a(5), a(7)] = [2, 5, 7]
  print *, b

  ! Assigning to a section
  a(2:4) = [99, 98, 97]
  print *, a(1:5)         ! 1 99 98 97 5

  ! 2D sections
  real :: m(4,4) = 0.0
  m(2:3, 2:3) = 1.0       ! set 2x2 sub-block
  print *, m(2,2), m(3,3) ! 1.0 1.0

  ! Where construct (masked array assignment)
  where (a > 50) a = 0    ! set elements > 50 to 0
end program array_sections

Arrays Multidimensionais & Matrizes

Fortran armazena arrays em column-major (primeiro índice varia mais rápido na memória) — oposto ao C. reshape preenche em ordem column-major, então reshape([1,2,3,4],[2,2]) dá [[1,3],[2,4]]. matmul(A,B) é multiplicação verdadeira de matrizes (álgebra linear); A*B é elemento a elemento (Hadamard) — eles NÃO são a mesma coisa! transpose(A) retorna a transposta. Reduções: sum, product, maxval, minval, maxloc, minloc, count — todas suportam dim= para reduzir ao longo de um eixo. Para alto desempenho, escreva loops em ordem column-major (loop mais interno sobre o primeiro índice) para ser cache-friendly.

fortran
program matrices
  implicit none
  real :: A(3,3), B(3,3), C(3,3)
  integer :: i, j

  ! Initialize with implied-do
  A = reshape([(real(i), i=1,9)], [3,3])
  ! A = 1 4 7
  !     2 5 8
  !     3 6 9   (column-major fill!)

  ! Identity matrix
  B = 0.0
  do i = 1, 3
    B(i,i) = 1.0
  end do

  ! Matrix multiplication (intrinsic)
  C = matmul(A, B)        ! A * I = A
  print *, C(1,1), C(2,2) ! 1.0 5.0

  ! Transpose
  print *, transpose(A)(1,:)   ! 1 2 3 (first row of A^T)

  ! Element-wise operations
  C = A + B               ! element-wise add
  C = A * 2.0             ! scalar multiply
  C = A * B               ! element-wise (NOT matmul!)

  ! Array reduction along a dimension
  print *, sum(A, dim=1)   ! column sums: 6 15 24
  print *, sum(A, dim=2)   ! row sums: 12 15 18
  print *, maxval(A)       ! 9.0
  print *, maxloc(A)       ! 3 3 (location of max)

  ! Reshape
  integer :: flat(6) = [1,2,3,4,5,6]
  integer :: mat(2,3)
  mat = reshape(flat, [2,3])
end program matrices

Arrays Allocatable (Dinâmicos)

Arrays allocatable são a maneira moderna de fazer memória dinâmica no Fortran — mais seguros que ponteiros (sem vazamentos de memória, desalocação automática na saída do escopo). Declare com o atributo allocatable e uma forma diferida (:, (:,:), etc.). allocate() com stat= captura erros (sempre verifique!). deallocate() libera explicitamente. Fortran 2003+ suporta realocação automática na atribuição: flex = [flex, 4] cresce o array. allocated() verifica se está atualmente alocado. Allocatables são preferidos em vez de ponteiros para arrays dinâmicos porque o compilador rastreia e libera-os automaticamente — sem vazamentos, sem ponteiros pendentes.

fortran
program alloc_demo
  implicit none
  integer, allocatable :: arr(:), matrix(:,:)
  integer :: n, m, i, stat

  ! Get size from user
  print *, "Enter size:"
  read(*, *) n
  m = n * 2

  ! Allocate
  allocate(arr(n), matrix(n, m), stat=stat)
  if (stat /= 0) then
    print *, "Allocation failed!"
    stop 1
  end if

  ! Use the arrays
  arr = [(i, i=1, n)]
  matrix = 0.0
  do i = 1, n
    matrix(i, :) = i
  end do

  print *, size(arr), size(matrix, dim=2)
  print *, allocated(arr)   ! T

  ! Deallocate (or let it auto-deallocate at scope exit)
  deallocate(arr, matrix)
  print *, allocated(arr)   ! F

  ! Automatic reallocation on assignment (Fortran 2003)
  integer, allocatable :: flex(:)
  flex = [1, 2, 3]          ! auto-allocates to size 3
  flex = [flex, 4, 5]       ! reallocates to size 5: 1 2 3 4 5
  print *, flex
  deallocate(flex)
end program alloc_demo

Funções Intrínsecas de Array

As intrínsecas de array do Fortran são seu superpoder. Reduções: sum, product, maxval, minval, maxloc (índice do máximo), minloc, count (contagem de true), any (existe), all (todos). Todas suportam mask= para redução condicional. pack() reúne elementos onde a mask é true (como numpy compress); unpack() faz scatter. cshift/eoshift rotacionam arrays (circular vs end-off). merge(a, b, mask) faz seleção elemento a elemento. Nota: Fortran NÃO tem sort built-in — você deve escrever um (ou usar uma biblioteca). Essas intrínsecas são elementais e vetorizáveis, tornando o código Fortran tanto limpo quanto rápido.

fortran
program array_funcs
  implicit none
  integer :: a(5) = [3, 1, 4, 1, 5, 9, 2, 6]
  ! Wait, let's fix size
  integer :: b(8) = [3, 1, 4, 1, 5, 9, 2, 6]
  integer :: c(5) = [10, 20, 30, 40, 50]
  logical :: mask(5) = [.true., .false., .true., .false., .true.]

  ! Inquiry
  print *, size(b)        ! 8
  print *, shape(b)       ! 8
  print *, lbound(b), ubound(b)   ! 1 8

  ! Reductions
  print *, sum(b)         ! 31
  print *, product(c)     ! 120000000
  print *, maxval(b), minval(b)   ! 9 1
  print *, maxloc(b)      ! 6 (index of max)
  print *, minloc(b)      ! 2 (index of min, first occurrence)
  print *, count(b > 3)   ! 5 (number of true elements)
  print *, any(b > 8)     ! T (at least one)
  print *, all(b > 0)     ! T (all of them)

  ! With mask
  print *, sum(b, mask=b > 3)     ! sum of elements > 3
  print *, pack(b, b > 3)         ! compact array of elements > 3
  print *, unpack([1,2], mask, 0) ! spread values per mask

  ! Manipulation
  print *, cshift(b, 2)   ! circular shift left by 2
  print *, eoshift(b, 2)  ! end-off shift left by 2 (fills 0)
  print *, merge(b, c, mask)  ! element-wise: b where mask true, else c

  ! Sorting (Fortran 2003+)
  integer :: sorted(8)
  sorted = b
  call sort_array(sorted)   ! custom sort (no built-in sort)
end program array_funcs
04

Strings & Manipulação de Caracteres

Declaração de Character & Comprimento

Strings Fortran são de COMPRIMENTO FIXO por padrão — strings mais curtas são preenchidas com espaços para preencher o comprimento declarado. character(N) ou character(len=N) declara comprimento N. character(*) pega o comprimento do contexto (inicializador de parâmetro ou argumento dummy). character(:), allocatable habilita strings dinâmicas de comprimento diferido (Fortran 2003+) — a string é realocada na atribuição. len() retorna o comprimento declarado; len_trim() retorna o comprimento sem espaços à direita. trim() retorna a string sem espaços à direita (mas o resultado ainda é de comprimento fixo no contexto). Para processamento de texto de comprimento variável, use strings allocatable de comprimento diferido.

fortran
program char_decl
  implicit none
  ! Fixed-length strings
  character(10) :: s1 = "Hello"
  character(len=20) :: s2 = "World"
  character(20) :: s3          ! len= keyword optional

  ! Deferred-length (allocatable) - Fortran 2003+
  character(:), allocatable :: flex
  flex = "Dynamic"             ! len=7
  flex = "Now longer string"   ! reallocates to len=18
  print *, len(flex)           ! 18

  ! Array of strings
  character(15) :: names(3) = ["Alice", "Bob", "Carol"]

  ! Single character
  character(1) :: ch = 'A'
  character :: ch2 = 'B'       ! len=1 default

  print *, s1                  ! "Hello     " (padded to 10)
  print *, trim(s1)            ! "Hello" (no padding)
  print *, len(s1), len_trim(s1)   ! 10 5
  print *, names(2)            ! "Bob" (padded)
end program char_decl

Concatenação de Strings & Operações

Concatenação de strings usa o operador //. repeat(s, n) repete uma string n vezes. Substrings usam s(start:end) — 1-indexed, INCLUSIVO em ambas as extremidades (diferente do Python). s(8:) significa da posição 8 ao fim; s(:5) significa do início à posição 5. index(s, sub) retorna a posição da primeira ocorrência de sub (0 se não encontrada, sensível a maiúsculas/minúsculas). scan(s, set) retorna a posição do primeiro caractere NO conjunto; verify(s, set) retorna o primeiro caractere NÃO no conjunto. adjustl/adjustr deslocam espaços à esquerda/direita. Strings Fortran NÃO são terminadas com null como em C — o comprimento é rastreado separadamente.

fortran
program string_ops
  implicit none
  character(20) :: first = "John", last = "Doe"
  character(50) :: full

  ! Concatenation with //
  full = first // " " // last   ! "John Doe"
  print *, trim(full)

  ! Repeat
  print *, repeat("-", 30)      ! 30 dashes

  ! Substring (1-indexed, inclusive)
  character(20) :: s = "Hello, World!"
  print *, s(1:5)               ! "Hello"
  print *, s(8:12)              ! "World"
  print *, s(8:)                ! "World!"  (to end)
  print *, s(:5)                ! "Hello"   (from start)

  ! Index (find substring)
  print *, index(s, "World")    ! 8 (position, 0 if not found)
  print *, index(s, "world")    ! 0 (case-sensitive)
  print *, scan(s, "aeiou")     ! 2 (first vowel position)
  print *, verify(s, "abcdefg") ! 1 (first char NOT in set)

  ! Length
  print *, len_trim(s)          ! 13
  print *, adjustl(s)           ! left-justify
  print *, adjustr(s)           ! right-justify
end program string_ops

Funções Intrínsecas de String

iachar(c) retorna o código ASCII de um caractere; achar(i) é o inverso. (ichar/char são dependentes do processador — prefira iachar/achar para portabilidade.) Fortran NÃO tem conversão de maiúsculas/minúsculas built-in — use iachar/achar manualmente (A-Z é 65-90, a-z é 97-122, a diferença é 32). Comparação lexicográfica: lge/lgt/lle/llt (lexicamente maior/menor que) tratam strings de comprimentos diferentes graciosamente. I/O interno (read/write para uma string em vez de um arquivo) é a maneira idiomática de converter entre strings e números: read(str, *) num e write(str, fmt) num. O formato I0 fornece inteiros de largura mínima (sem preenchimento).

fortran
program string_funcs
  implicit none
  character(20) :: s = "Hello World"
  integer :: i
  character(1) :: ch

  ! Character code conversion
  print *, iachar('A')          ! 65 (ASCII code)
  print *, achar(66)            ! 'B' (from ASCII code)
  print *, ichar('A')           ! processor-dependent (use iachar for ASCII)

  ! Case conversion (manual - no built-in)
  do i = 1, len_trim(s)
    ch = s(i:i)
    if (ch >= 'A' .and. ch <= 'Z') then
      s(i:i) = achar(iachar(ch) + 32)   ! to lowercase
    end if
  end do
  print *, s                    ! "hello world"

  ! Comparison
  print *, lge("apple", "banana")   ! F (lexicographic >=)
  print *, lgt("zebra", "apple")    ! T
  print *, lle("abc", "abcd")       ! T (<=)
  print *, llt("abc", "abd")        ! T (<)

  ! String to number conversion (internal read)
  character(20) :: num_str = "3.14159"
  real :: pi_val
  read(num_str, *) pi_val
  print *, pi_val * 2           ! 6.28318

  ! Number to string (internal write)
  character(20) :: out_str
  integer :: n = 42
  write(out_str, '(I0)') n      ! I0 = minimal-width integer
  print *, "Number: " // trim(out_str)
end program string_funcs

Formatação de Strings

Especificações de formato vivem em uma string: '(I5, F8.2, A)'. I=integer, F=ponto fixo real, E=exponencial, ES=científico (mantissa 1-10), A=character, X=espaço, /=nova linha. A largura vem primeiro (I5 = largura 5), então .m opcional para dígitos mínimos (I5.3). I0 significa largura mínima (sem preenchimento). ES fornece notação científica adequada (1.23E+6) vs E (0.12E+7). Repetições: 3I4 = três inteiros cada um com largura 4. Literais de string em formato: '("text")'. O formato pode ser uma variável string, um * (list-directed, o compilador escolhe) ou um label (número de statement). Para saída limpa, prefira I0 para inteiros e F ou ES para reais.

fortran
program format_demo
  implicit none
  integer :: n = 42
  real :: pi = 3.14159265
  real :: big = 1234567.89
  character(20) :: name = "Alice"

  ! Format specifiers
  ! Iw     - integer, width w
  ! Iw.m   - integer, width w, at least m digits
  ! Fw.d   - fixed-point real, width w, d decimals
  ! Ew.d   - exponential, width w, d decimals
  ! Aw     - character, width w
  ! A      - character, default width
  ! nX     - n spaces
  ! /      - newline

  write(*, '(I5)') n            ! "   42"
  write(*, '(I5.3)') n          ! "  042"
  write(*, '(I0)') n            ! "42" (minimal)
  write(*, '(F10.4)') pi        ! "    3.1416"
  write(*, '(E12.4)') big       ! " 0.1235E+07"
  write(*, '(ES12.4)') big      ! " 1.2346E+06" (scientific)
  write(*, '(A10)') name        ! "     Alice"
  write(*, '(A, I3)') "n=", n   ! "n= 42"

  ! Multiple items
  write(*, '(A, I3, A, F8.4)') "n=", n, " pi=", pi

  ! Repeated format: 3I4 = three integers width 4
  write(*, '(3I4)') 1, 2, 3     ! "   1   2   3"

  ! Newline and spacing
  write(*, '("Name: ", A, /, "Age: ", I3)') name, n

  ! List-directed (default format)
  print *, name, n, pi
end program format_demo

Parsing & Tokenização

Fortran NÃO tem split/tokenize built-in — você deve escrevê-lo manualmente usando index() e substrings. O padrão: encontre o delimitador com index, extraia o token com substring, avance além do delimitador, repita. Para pares key=value, encontre '=' com index e divida em key (antes) e value (depois). trim() remove espaços à direita; adjustl() remove espaços à esquerda. Para parsing robusto, também trate tokens vazios e whitespace. Alternativamente, use reads internos com especificadores de formato para dados estruturados, ou leia de uma string como se fosse um arquivo. Bibliotecas como split() existem em alguns frameworks Fortran, mas não são padrão.

fortran
program parse_demo
  implicit none
  character(100) :: line = "name=Bob,age=30,city=NYC"
  character(50) :: token
  integer :: pos, start, end

  ! Split by comma
  start = 1
  do
    end = index(line(start:), ",")
    if (end == 0) then
      ! last token
      token = line(start:)
      call process(trim(token))
      exit
    end if
    token = line(start:start+end-2)
    call process(trim(token))
    start = start + end
  end do

contains
  subroutine process(t)
    character(*), intent(in) :: t
    integer :: eq_pos
    eq_pos = index(t, "=")
    if (eq_pos > 0) then
      print *, "Key: ", trim(t(:eq_pos-1)), &
               " Value: ", trim(t(eq_pos+1:))
    end if
  end subroutine process
end program parse_demo

! Output:
! Key: name Value: Bob
! Key: age Value: 30
! Key: city Value: NYC
05

Procedimentos: Funções & Subroutines

Funções

Funções retornam um valor e são usadas em expressões (como funções matemáticas). Sintaxe moderna: 'function name(args) result(var)' — a variável de resultado é o que é retornado. intent(in) marca argumentos somente leitura (o compilador impõe isso). Funções podem retornar escalares OU arrays (use size() da entrada para dimensionar a saída). Funções devem ser PURAS (sem efeitos colaterais) — não modifique estado global nem faça I/O em uma função. Procedimentos internos (no bloco 'contains') têm acesso às variáveis do host (host association). Para procedimentos externos, use um bloco interface para especificar a assinatura.

fortran
program func_demo
  implicit none
  ! Function declared in interface or as external
  print *, add(3, 4)         ! 7
  print *, square(5)         ! 25
  print *, distance(0.0, 0.0, 3.0, 4.0)   ! 5.0

contains
  ! Basic function with result clause
  function add(a, b) result(c)
    integer, intent(in) :: a, b
    integer :: c
    c = a + b
  end function add

  ! Function with result named same as function (legacy)
  function square(x) result(y)
    integer, intent(in) :: x
    integer :: y
    y = x * x
  end function square

  ! Real function
  function distance(x1, y1, x2, y2) result(d)
    real, intent(in) :: x1, y1, x2, y2
    real :: d
    d = sqrt((x2-x1)**2 + (y2-y1)**2)
  end function distance

  ! Array-valued function
  function reverse(arr) result(rev)
    integer, intent(in) :: arr(:)
    integer :: rev(size(arr))
    integer :: i, n
    n = size(arr)
    do i = 1, n
      rev(i) = arr(n-i+1)
    end do
  end function reverse
end program func_demo

Subroutines & Intent

Subroutines são chamadas com 'call' e não retornam um valor — elas modificam argumentos in place. Use subroutines quando: (1) você precisa modificar múltiplos argumentos, (2) a operação é um 'comando' não uma 'computação', (3) retornar um resultado array-valued é awkward. Atributos intent documentam e impõem a direção do argumento: intent(in) = somente leitura (erro de compilação se você atribuir a ele), intent(out) = somente escrita (indefinido na entrada, deve ser definido antes do retorno), intent(inout) = leitura-escrita. Sempre especifique intent — captura bugs e habilita otimização. Subroutines podem modificar arrays passados a elas (sem cópia se forem contíguos).

fortran
program sub_demo
  implicit none
  integer :: x = 10, y = 20
  real :: arr(5) = [1.0, 2.0, 3.0, 4.0, 5.0]

  ! Subroutines are called with 'call'
  call swap(x, y)
  print *, x, y              ! 20 10

  call scale_array(arr, 2.0)
  print *, arr               ! 2 4 6 8 10

  call fill_zero(arr)
  print *, arr               ! 0 0 0 0 0

contains
  subroutine swap(a, b)
    integer, intent(inout) :: a, b   ! read AND write
    integer :: tmp
    tmp = a
    a = b
    b = tmp
  end subroutine swap

  subroutine scale_array(a, factor)
    real, intent(inout) :: a(:)
    real, intent(in) :: factor
    a = a * factor           ! whole-array operation
  end subroutine scale_array

  subroutine fill_zero(a)
    real, intent(out) :: a(:)        ! write-only (output)
    a = 0.0
  end subroutine fill_zero
end program sub_demo

Funções Pure & Elemental

Funções pure NÃO têm efeitos colaterais: sem I/O, sem modificação de variável global, sem stop, só podem chamar outros procedimentos pure. Elas habilitam otimização do compilador (paralelização, eliminação de subexpressões comuns) e são exigidas em alguns contextos (ex.: DO CONCURRENT). Funções elemental são escritas para ESCALARES mas automaticamente funcionam em arrays elemento a elemento — escreva uma vez, use para ambos. pure elemental combina ambos. Use pure para qualquer função que seja realmente uma função matemática (sem efeitos colaterais). Use elemental quando a operação se aplica naturalmente elemento a elemento a arrays (funções matemáticas, conversões). O compilador pode vetorizar automaticamente chamadas elemental em arrays.

fortran
program pure_demo
  implicit none
  real :: a(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
  real :: b(5)
  integer :: i

  ! Pure function: no side effects, no I/O
  b = square_arr(a)
  print *, b                 ! 1 4 9 16 25

  ! Elemental function: works on scalars AND arrays automatically
  b = cube(a)                ! applies cube() element-wise
  print *, b                 ! 1 8 27 64 125

  print *, cube(2.0)         ! also works on scalar: 8.0

contains
  ! Pure: no side effects, no I/O, no stop, only pure calls
  pure function square_arr(x) result(y)
    real, intent(in) :: x(:)
    real :: y(size(x))
    y = x * x
  end function square_arr

  ! Elemental: scalar signature, but works on arrays too
  elemental function cube(x) result(y)
    real, intent(in) :: x
    real :: y
    y = x * x * x
  end function cube

  ! Pure elemental: both
  pure elemental double precision function sq(x) result(y)
    double precision, intent(in) :: x
    double precision :: y
    y = x * x
  end function sq
end program pure_demo

Argumentos Opcionais & por Palavra-chave

Argumentos opcionais permitem que chamadores os omitam. Use present(arg) dentro do procedimento para verificar se um argumento foi fornecido — acessar um optional ausente é comportamento indefinido. Argumentos por palavra-chave (name="value") permitem passar argumentos em qualquer ordem e tornam as chamadas autodocumentadas. Depois de usar uma palavra-chave, todos os argumentos subsequentes também devem usar palavras-chave. Argumentos opcionais devem vir após todos os obrigatórios na assinatura. Valores padrão são implementados via verificações present() (Fortran não tem sintaxe padrão built-in). Palavra-chave + opcional juntos habilitam APIs flexíveis: chamadores especificam apenas o que precisam.

fortran
program optional_demo
  implicit none
  ! All arguments after the first optional must also be optional
  print *, greet("Alice")                    ! Hello, Alice!
  print *, greet("Bob", "Hi")                ! Hi, Bob!
  print *, greet("Carol", greeting="Hey")    ! keyword argument
  print *, greet(greeting="Welcome", name="Dave")  ! all keywords

  ! With present() check
  call log_msg("Starting up")
  call log_msg("Error!", level=2)
  call log_msg("Debug info", level=0, file="debug.log")

contains
  function greet(name, greeting) result(msg)
    character(*), intent(in) :: name
    character(*), intent(in), optional :: greeting
    character(50) :: msg
    character(20) :: g

    if (present(greeting)) then
      g = greeting
    else
      g = "Hello"
    end if
    msg = trim(g) // ", " // name // "!"
  end function greet

  subroutine log_msg(message, level, file)
    character(*), intent(in) :: message
    integer, intent(in), optional :: level
    character(*), intent(in), optional :: file
    integer :: lvl
    lvl = 1
    if (present(level)) lvl = level
    print *, "[L", lvl, "] ", trim(message)
  end subroutine log_msg
end program optional_demo

Procedimentos Internos & Recursivos

Procedimentos internos (dentro de 'contains') têm host association — eles podem ler E modificar as variáveis do programa host (como closures). Use-os para helpers que precisam do estado do host. Procedimentos recursivos devem ser declarados com o prefixo 'recursive' (Fortran 90/2003); Fortran 2018 torna recursão o padrão. Para recursão mútua, use um bloco interface para declarar a referência forward. Recursão é elegante, mas pode ser lenta (overhead de chamada de função) e arriscada (stack overflow para recursão profunda). Para factorial/fibonacci, versões iterativas são mais rápidas e seguras. Use recursão para problemas naturalmente recursivos (traversal de árvore, divide-and-conquer) com profundidade limitada.

fortran
program nested_demo
  implicit none
  integer :: counter = 0   ! host variable

  ! Internal procedures (in contains) access host variables
  call increment
  call increment
  print *, counter          ! 2

  ! Recursive function
  print *, factorial(5)     ! 120
  print *, fib(10)          ! 55

  ! Mutual recursion (needs forward declaration)
  print *, is_even(4)       ! T

contains
  subroutine increment
    counter = counter + 1   ! modifies host's counter
  end subroutine increment

  recursive function factorial(n) result(f)
    integer, intent(in) :: n
    integer :: f
    if (n <= 1) then
      f = 1
    else
      f = n * factorial(n-1)
    end if
  end function factorial

  recursive function fib(n) result(f)
    integer, intent(in) :: n
    integer :: f
    if (n < 2) then
      f = n
    else
      f = fib(n-1) + fib(n-2)
    end if
  end function fib

  ! Mutual recursion with interface
  recursive function is_even(n) result(r)
    integer, intent(in) :: n
    logical :: r
    interface
      recursive function is_odd(m) result(ro)
        integer, intent(in) :: m
        logical :: ro
      end function is_odd
    end interface
    if (n == 0) then
      r = .true.
    else
      r = is_odd(n-1)
    end if
  end function is_even
end program nested_demo
06

Módulos & Encapsulação

Básico de Módulos & Use

Módulos são o principal mecanismo de encapsulação do Fortran (substituindo common blocks e procedimentos externos). Um arquivo de módulo contém: (1) declarações (constantes, variáveis, tipos derivados), (2) um bloco 'contains' com procedimentos. Use 'use module_name' para importar; 'use module_name, only: x, y' importa apenas entidades específicas (recomendado — evita poluição de namespace). Variáveis de módulo são persistentes (static) e compartilhadas entre todos os procedimentos que usam o módulo. Módulos fornecem interfaces explícitas (o compilador verifica tipos de argumentos), diferente de procedimentos externos. Sempre compile arquivos de módulo antes dos arquivos que os usam. 'implicit none' em um módulo propaga para todos os seus procedimentos.

fortran
! geometry.f90 - module file
module geometry
  implicit none
  private                      ! default: everything private
  public :: circle_area, circle_perimeter, PI

  ! Module-level constants (persistent)
  real, parameter :: PI = 3.14159265

contains
  function circle_area(r) result(a)
    real, intent(in) :: r
    real :: a
    a = PI * r * r
  end function circle_area

  function circle_perimeter(r) result(p)
    real, intent(in) :: r
    real :: p
    p = 2.0 * PI * r
  end function circle_perimeter
end module geometry

! main.f90 - using the module
program use_module
  use geometry, only: circle_area, circle_perimeter, PI
  implicit none
  real :: r = 5.0
  print *, "Area: ", circle_area(r)         ! 78.5398
  print *, "Perimeter: ", circle_perimeter(r) ! 31.4159
  print *, "PI: ", PI
end program use_module

! Compile: gfortran geometry.f90 main.f90 -o main

Controle de Acesso (Public/Private)

Controle de acesso: 'private' torna entidades internas ao módulo; 'public' as exporta. O padrão pode ser definido em nível de módulo ('private' então seletivamente 'public :: ...') — esta é a melhor prática (interface explícita). Componentes de tipos derivados podem ser private mesmo se o tipo em si for public — chamadores podem usar o tipo, mas não acessar internals diretamente; eles devem passar por procedimentos. 'save' torna variáveis de módulo persistentes (elas retêm valores entre chamadas) — variáveis de módulo são salvas por padrão. 'final' define um destrutor (chamado quando o objeto sai do escopo). Essa encapsulação habilita verdadeiro OOP com invariantes impostos através de procedimentos.

fortran
module bank_account
  implicit none
  private                       ! default: everything private
  ! Explicitly export:
  public :: account_t, deposit, withdraw, get_balance

  ! Derived type - can expose type but hide internals
  type :: account_t
    private                    ! components are private
    real :: balance = 0.0
    integer :: id = 0
  contains
    procedure :: balance => get_bal   ! type-bound procedure
    final :: cleanup                  ! destructor
  end type account_t

  ! Module-level counter (private, not exported)
  integer, save :: next_id = 1000

contains
  ! Constructor (factory function)
  function create_account(initial) result(acc)
    type(account_t) :: acc
    real, intent(in) :: initial
    acc%balance = initial
    acc%id = next_id
    next_id = next_id + 1
  end function create_account

  subroutine deposit(acc, amount)
    type(account_t), intent(inout) :: acc
    real, intent(in) :: amount
    acc%balance = acc%balance + amount
  end subroutine deposit

  function get_bal(acc) result(b)
    class(account_t), intent(in) :: acc
    real :: b
    b = acc%balance
  end function get_bal

  subroutine cleanup(acc)
    type(account_t) :: acc
    ! cleanup code (e.g., log closure)
  end subroutine cleanup
end module bank_account

Tipos Derivados em Módulos

Tipos derivados definidos em módulos podem ter: procedimentos type-bound (procedure :: name => impl), construtores (via interface overloaded com o nome do tipo) e componentes allocatable. O 'class(keyword)' em procedimentos type-bound habilita polimorfismo (o tipo real pode ser uma subclasse). Procedimentos type-bound são chamados como obj%method(args) — sintaxe OOP. Sobrecarregar o nome do tipo como um interface permite ter múltiplos construtores (vector_from_array, vector_from_size). Componentes allocatable são automaticamente alocados/desalocados. Este é OOP moderno em Fortran: encapsulação, métodos, construtores e polimorfismo, todos dentro do sistema de módulos.

fortran
module vector_mod
  implicit none
  private
  public :: vector_t, vector_add, vector_scale

  ! Derived type with type parameters (Fortran 2003)
  type :: vector_t
    real, allocatable :: data(:)
    integer :: length = 0
  contains
    procedure :: norm => vector_norm
    procedure :: print => vector_print
  end type vector_t

  ! Constructor interface (overloaded)
  interface vector_t
    procedure vector_from_array
    procedure vector_from_size
  end interface vector_t

contains
  function vector_from_array(arr) result(v)
    real, intent(in) :: arr(:)
    type(vector_t) :: v
    v%data = arr
    v%length = size(arr)
  end function

  function vector_from_size(n, fill) result(v)
    integer, intent(in) :: n
    real, intent(in) :: fill
    type(vector_t) :: v
    allocate(v%data(n))
    v%data = fill
    v%length = n
  end function

  function vector_norm(self) result(n)
    class(vector_t), intent(in) :: self
    real :: n
    n = sqrt(sum(self%data**2))
  end function

  subroutine vector_print(self)
    class(vector_t), intent(in) :: self
    print *, "Vector(len=", self%length, "): ", self%data
  end subroutine

  function vector_add(a, b) result(c)
    type(vector_t), intent(in) :: a, b
    type(vector_t) :: c
    c%data = a%data + b%data
    c%length = a%length
  end function

  function vector_scale(a, s) result(c)
    type(vector_t), intent(in) :: a
    real, intent(in) :: s
    type(vector_t) :: c
    c%data = a%data * s
    c%length = a%length
  end function
end module vector_mod

Procedimentos Genéricos & Sobrecarga

Interfaces genéricas fornecem polimorfismo ad-hoc (sobrecarga): um nome despacha para diferentes procedimentos específicos com base nos tipos de argumentos. O bloco 'interface name / module procedure proc1, proc2 / end interface' lista todos os procedimentos específicos. O compilador escolhe o correspondente por tipo/rank do argumento em tempo de compilação. Todos os procedimentos específicos em um genérico devem ter assinaturas DISTINTAS (distinguíveis pelos tipos de argumentos) — caso contrário, ambiguidade. É assim que o Fortran faz sobrecarga de operadores/funções sem templates. O dispatch genérico é resolvido em tempo de compilação (sem overhead em runtime). Use genéricos para fornecer uma API uniforme entre tipos.

fortran
module generics_mod
  implicit none
  private
  public :: print_value, add

  ! Generic interface: one name, multiple specific procedures
  interface print_value
    module procedure print_int
    module procedure print_real
    module procedure print_str
    module procedure print_int_array
  end interface print_value

  interface add
    module procedure add_int
    module procedure add_real
    module procedure add_arrays
  end interface add

contains
  subroutine print_int(x)
    integer, intent(in) :: x
    print *, "Integer: ", x
  end subroutine

  subroutine print_real(x)
    real, intent(in) :: x
    print *, "Real: ", x
  end subroutine

  subroutine print_str(s)
    character(*), intent(in) :: s
    print *, "String: ", trim(s)
  end subroutine

  subroutine print_int_array(arr)
    integer, intent(in) :: arr(:)
    print *, "Array: ", arr
  end subroutine

  function add_int(a, b) result(c)
    integer, intent(in) :: a, b
    integer :: c
    c = a + b
  end function

  function add_real(a, b) result(c)
    real, intent(in) :: a, b
    real :: c
    c = a + b
  end function

  function add_arrays(a, b) result(c)
    real, intent(in) :: a(:), b(:)
    real :: c(size(a))
    c = a + b
  end function
end module generics_mod

program use_generics
  use generics_mod
  implicit none
  call print_value(42)              ! Integer: 42
  call print_value(3.14)            ! Real: 3.14
  call print_value("Hello")         ! String: Hello
  call print_value([1,2,3])         ! Array: 1 2 3
  print *, add(2, 3)                ! 5
  print *, add(2.5, 3.5)            ! 6.0
end program use_generics

Sobrecarga de Operadores

Sobrecarga de operadores permite definir como +, -, *, /, ==, etc. funcionam em seus tipos derivados. interface operator(+) / module procedure vec_add / end interface vincula o operador + a uma função. Para operadores binários, você pode sobrecarregar ambas as ordens (vec*scalar e scalar*vec) com procedimentos separados. assignment(=) sobrecarrega o operador de atribuição (o procedimento é uma subroutine com LHS intent(out) e RHS intent(in)). Isso habilita sintaxe matemática: c = a + b em vez de c = vec_add(a, b). Use sobrecarga de operadores para tipos matemáticos (vetores, matrizes, números complexos) onde melhora a legibilidade. Evite sobrecarregar para semânticas não óbvias. O construtor de estrutura vec3(x,y,z) é built-in para tipos derivados.

fortran
module vec_ops
  implicit none
  private
  public :: vec3, operator(+), operator(*), assignment(=)

  type :: vec3
    real :: x, y, z
  end type vec3

  ! Overload the + operator for vec3 + vec3
  interface operator(+)
    module procedure vec_add
  end interface

  ! Overload * for vec3 * scalar and scalar * vec3
  interface operator(*)
    module procedure vec_scale_r
    module procedure vec_scale_l
    module procedure vec_dot
  end interface

  ! Overload = for array-to-vec assignment
  interface assignment(=)
    module procedure arr_to_vec
  end interface

contains
  function vec_add(a, b) result(c)
    type(vec3), intent(in) :: a, b
    type(vec3) :: c
    c = vec3(a%x+b%x, a%y+b%y, a%z+b%z)
  end function

  function vec_scale_r(v, s) result(r)
    type(vec3), intent(in) :: v
    real, intent(in) :: s
    type(vec3) :: r
    r = vec3(v%x*s, v%y*s, v%z*s)
  end function

  function vec_scale_l(s, v) result(r)
    real, intent(in) :: s
    type(vec3), intent(in) :: v
    type(vec3) :: r
    r = vec3(v%x*s, v%y*s, v%z*s)
  end function

  ! vec3 * vec3 = dot product (scalar)
  function vec_dot(a, b) result(d)
    type(vec3), intent(in) :: a, b
    real :: d
    d = a%x*b%x + a%y*b%y + a%z*b%z
  end function

  subroutine arr_to_vec(v, arr)
    type(vec3), intent(out) :: v
    real, intent(in) :: arr(3)
    v = vec3(arr(1), arr(2), arr(3))
  end subroutine
end module vec_ops

program use_ops
  use vec_ops
  implicit none
  type(vec3) :: a, b, c
  a = vec3(1.0, 2.0, 3.0)
  b = vec3(4.0, 5.0, 6.0)
  c = a + b              ! vec3 addition
  print *, c%x, c%y, c%z ! 5 7 9
  print *, a * 2.0       ! scale: 2 4 6
  print *, 3.0 * b       ! scale: 12 15 18
  print *, a * b         ! dot product: 32
end program use_ops
07

Tipos Derivados (Structs) & OOP

Definindo Tipos Derivados

Tipos derivados são os structs do Fortran (tipos compostos definidos pelo usuário). Defina com 'type :: Name ... end type Name'. Componentes acessados com % (NÃO . — isso é para números complexos). Construtor de estrutura: Name(val1, val2) cria uma instância. Componentes podem ter valores padrão (= value na declaração). Atribuição de todo o tipo copia todos os componentes (cópia profunda para componentes allocatable). Arrays de tipos derivados são suportados. Tipos derivados são a fundação para OOP no Fortran (com procedimentos type-bound, herança, polimorfismo). Use % para acesso a componentes: obj%field, obj%method(). A sintaxe de construtor Name(args) é automática, a menos que você a sobrescreva com um interface.

fortran
program derived_types
  implicit none
  ! Basic derived type (struct)
  type :: Point
    real :: x, y
  end type Point

  ! Type with default initialization
  type :: Person
    character(20) :: name = "Unknown"
    integer :: age = 0
    logical :: active = .true.
  end type Person

  ! Declare and construct
  type(Point) :: p1, p2
  type(Person) :: alice, bob

  ! Structure constructor
  p1 = Point(3.0, 4.0)
  p2 = Point(0.0, 0.0)
  alice = Person("Alice", 30, .true.)
  bob = Person("Bob", 25)        ! uses default for 'active'

  ! Access components with %
  print *, p1%x, p1%y            ! 3.0 4.0
  print *, alice%name, alice%age ! Alice 30
  print *, bob%active            ! T (default)

  ! Modify components
  p1%x = p1%x + 1.0
  alice%age = 31

  ! Whole-type assignment (component-wise copy)
  p2 = p1
  print *, p2%x                  ! 4.0

  ! Array of derived types
  type(Point) :: points(3)
  points(1) = Point(1.0, 1.0)
  points(2) = Point(2.0, 2.0)
  points(3) = Point(3.0, 3.0)
  print *, points(2)%y           ! 2.0
end program derived_types

Componentes de Tipo & Construtores

Tipos derivados podem ter: componentes allocatable (memória gerenciada automaticamente), componentes com inicialização padrão, procedimentos type-bound (métodos) e finalizers (destrutores). O 'interface TypeName / module procedure custom_init / end interface' sobrecarrega o construtor de estrutura com uma função factory personalizada. 'class(ClassName)' (vs 'type(ClassName)') em procedimentos type-bound habilita polimorfismo (o tipo real pode ser uma subclasse). Procedimentos 'final' rodam quando um objeto sai do escopo (destrutor) — use-os para liberar recursos. Componentes allocatable são automaticamente desalocados na finalização, mas finalizers explícitos são mais claros para limpeza complexa. Construtos block permitem declarar variáveis no meio do código (Fortran 2008).

fortran
program type_components
  implicit none
  ! Type with various component kinds
  type :: Student
    integer :: id
    character(20) :: name
    real, allocatable :: grades(:)    ! allocatable component
    integer :: num_grades = 0
  contains
    procedure :: add_grade
    procedure :: average => student_avg
    final :: student_finalize
  end type Student

  ! Overloaded constructor
  interface Student
    module procedure student_init
  end interface Student

  type(Student) :: s

  ! Use custom constructor
  s = Student(101, "Alice")
  call s%add_grade(85.0)
  call s%add_grade(92.0)
  call s%add_grade(78.0)
  print *, s%average()    ! 85.0

contains
  function student_init(id, name) result(s)
    integer, intent(in) :: id
    character(*), intent(in) :: name
    type(Student) :: s
    s%id = id
    s%name = name
    s%num_grades = 0
  end function

  subroutine add_grade(self, g)
    class(Student), intent(inout) :: self
    real, intent(in) :: g
    integer :: n
    n = self%num_grades
    if (n == 0) then
      allocate(self%grades(1))
    else
      ! grow array
      block
        real, allocatable :: tmp(:)
        tmp = self%grades
        deallocate(self%grades)
        allocate(self%grades(n+1))
        self%grades(1:n) = tmp
      end block
    end if
    self%num_grades = n + 1
    self%grades(n+1) = g
  end subroutine

  function student_avg(self) result(avg)
    class(Student), intent(in) :: self
    real :: avg
    if (self%num_grades > 0) then
      avg = sum(self%grades) / self%num_grades
    else
      avg = 0.0
    end if
  end function

  subroutine student_finalize(self)
    type(Student) :: self
    if (allocated(self%grades)) deallocate(self%grades)
  end subroutine
end program type_components

Procedimentos Type-Bound (Métodos)

Procedimentos type-bound são os métodos do Fortran: 'procedure :: method_name => implementation'. Chame-os como obj%method(args) — sintaxe OOP. O primeiro argumento é 'self' (o objeto), declarado como 'class(TypeName)' (polimórfico) ou 'type(TypeName)' (concreto). 'class' permite herança/polimorfismo; 'type' é para tipos não extensíveis. O '=> implementation' mapeia o nome do método a um procedimento específico (permite renomeação). final é o destrutor. Este exemplo implementa uma stack dinâmica com array de crescimento automático. Procedimentos type-bound dão verdadeiro OOP: encapsulação (dados + métodos juntos), sintaxe de passagem de mensagens (obj%method) e polimorfismo (via class). Sempre use class() para procedimentos type-bound para habilitar herança futura.

fortran
module stack_mod
  implicit none
  private
  public :: stack_t

  type :: stack_t
    integer, allocatable :: data(:)
    integer :: top = 0
    integer :: capacity = 0
  contains
    procedure :: push => stack_push
    procedure :: pop => stack_pop
    procedure :: peek => stack_peek
    procedure :: is_empty => stack_is_empty
    procedure :: size => stack_size
    procedure :: clear => stack_clear
    final :: stack_finalize
  end type stack_t

  interface stack_t
    module procedure stack_init
  end interface

contains
  function stack_init(initial_cap) result(s)
    integer, intent(in), optional :: initial_cap
    type(stack_t) :: s
    integer :: cap
    cap = 16
    if (present(initial_cap)) cap = initial_cap
    allocate(s%data(cap))
    s%capacity = cap
    s%top = 0
  end function

  subroutine stack_push(self, val)
    class(stack_t), intent(inout) :: self
    integer, intent(in) :: val
    if (self%top >= self%capacity) then
      ! grow
      block
        integer, allocatable :: tmp(:)
        tmp = self%data
        deallocate(self%data)
        allocate(self%data(self%capacity * 2))
        self%data(1:self%capacity) = tmp
        self%capacity = self%capacity * 2
      end block
    end if
    self%top = self%top + 1
    self%data(self%top) = val
  end subroutine

  function stack_pop(self) result(val)
    class(stack_t), intent(inout) :: self
    integer :: val
    if (self%top == 0) stop "Stack underflow"
    val = self%data(self%top)
    self%top = self%top - 1
  end function

  function stack_peek(self) result(val)
    class(stack_t), intent(in) :: self
    integer :: val
    val = self%data(self%top)
  end function

  logical function stack_is_empty(self) result(r)
    class(stack_t), intent(in) :: self
    r = (self%top == 0)
  end function

  integer function stack_size(self) result(n)
    class(stack_t), intent(in) :: self
    n = self%top
  end function

  subroutine stack_clear(self)
    class(stack_t), intent(inout) :: self
    self%top = 0
  end subroutine

  subroutine stack_finalize(self)
    type(stack_t) :: self
    if (allocated(self%data)) deallocate(self%data)
  end subroutine
end module stack_mod

program use_stack
  use stack_mod
  implicit none
  type(stack_t) :: s
  s = stack_t(8)         ! initial capacity 8
  call s%push(10)
  call s%push(20)
  call s%push(30)
  print *, s%size()      ! 3
  print *, s%pop()       ! 30
  print *, s%pop()       ! 20
  print *, s%is_empty()  ! F
end program use_stack

Herança & Polimorfismo

OOP do Fortran: 'type, extends(Parent) :: Child' cria uma subclasse (herança). 'type, abstract :: Name' com 'procedure(...), deferred :: method' define uma base abstrata (como classe abstrata Java / C++ pure virtual). 'class(Base)' é polimórfico — pode segurar qualquer subclasse. Dispatch polimórfico: chamar obj%method() invoca a sobrescrita da subclasse. 'select type (var => expr) / type is (ConcreteType) / end select' faz verificação de tipo em runtime (downcasting). Aloque com 'allocate(TypeName::var)' para criar um objeto polimórfico de um tipo concreto específico. Isso é OOP completo: herança, polimorfismo, tipos abstratos e dispatch em runtime — comparável a Java/C++.

fortran
module shapes
  implicit none
  private
  public :: shape, circle, rectangle, shape_ptr

  ! Base type (abstract)
  type, abstract :: shape
    character(20) :: name
  contains
    procedure(area_if), deferred :: area
    procedure(describe_if), deferred :: describe
    procedure :: get_name => shape_get_name
  end type shape

  ! Abstract interface (must be implemented by subclasses)
  abstract interface
    function area_if(self) result(a)
      import :: shape
      class(shape), intent(in) :: self
      real :: a
    end function area_if
    subroutine describe_if(self)
      import :: shape
      class(shape), intent(in) :: self
    end subroutine describe_if
  end interface

  ! Derived type: circle extends shape
  type, extends(shape) :: circle
    real :: radius
  contains
    procedure :: area => circle_area
    procedure :: describe => circle_describe
  end type circle

  ! Derived type: rectangle extends shape
  type, extends(shape) :: rectangle
    real :: width, height
  contains
    procedure :: area => rect_area
    procedure :: describe => rect_describe
  end type rectangle

  ! Class pointer for polymorphism
  type :: shape_ptr
    class(shape), allocatable :: ptr
  end type shape_ptr

contains
  function shape_get_name(self) result(n)
    class(shape), intent(in) :: self
    character(20) :: n
    n = self%name
  end function

  function circle_area(self) result(a)
    class(circle), intent(in) :: self
    real :: a
    a = 3.14159265 * self%radius**2
  end function

  subroutine circle_describe(self)
    class(circle), intent(in) :: self
    print *, "Circle '", trim(self%name), "' r=", self%radius
  end subroutine

  function rect_area(self) result(a)
    class(rectangle), intent(in) :: self
    real :: a
    a = self%width * self%height
  end function

  function rect_describe(self) result(s)
    class(rectangle), intent(in) :: self
    character(50) :: s
    write(s, '("Rectangle ", F6.2, "x", F6.2)') self%width, self%height
  end function
end module shapes

program use_polymorphism
  use shapes
  implicit none
  type(shape_ptr) :: shapes_arr(2)
  ! Allocate concrete types into polymorphic container
  allocate(circle::shapes_arr(1)%ptr)
  select type(s => shapes_arr(1)%ptr)
  type is (circle)
    s%radius = 5.0
    s%name = "C1"
  end select

  allocate(rectangle::shapes_arr(2)%ptr)
  select type(s => shapes_arr(2)%ptr)
  type is (rectangle)
    s%width = 3.0
    s%height = 4.0
    s%name = "R1"
  end select

  ! Polymorphic dispatch
  block
    integer :: i
    do i = 1, 2
      call shapes_arr(i)%ptr%describe
      print *, "Area: ", shapes_arr(i)%ptr%area()
    end do
  end block
end program use_polymorphism

Tipos Aninhados & Arrays de Tipos

Tipos derivados podem ser aninhados (composição): um tipo pode ter componentes de outros tipos derivados. Acesse componentes aninhados com % encadeado: emp%home%city. Construtores de estrutura aninham naturalmente: Employee(id, name, Address(...), salary). Arrays de tipos derivados são suportados: type(Employee) :: emps(N). Você pode extrair um array de um único componente: emps(:)%id dá um array de inteiros. Componentes allocatable permitem coleções de tamanho dinâmico (ex.: um departamento com um número variável de funcionários). Esse modelo de composição é a fundação para construir estruturas de dados complexas (árvores, grafos, listas) no Fortran. Use composição (tem-um) em vez de herança (é-um) quando não há uma relação clara de subtipo.

fortran
program nested_types
  implicit none
  ! Nested derived types
  type :: Address
    character(50) :: street
    character(30) :: city
    character(10) :: zip
  end type Address

  type :: Employee
    integer :: id
    character(30) :: name
    type(Address) :: home      ! nested type
    real :: salary
  end type Employee

  type :: Department
    character(30) :: name
    type(Employee), allocatable :: employees(:)   ! array of types
    integer :: count = 0
  end type Department

  ! Construct with nested structure constructor
  type(Employee) :: emp
  emp = Employee(101, "Alice", &
        Address("123 Main St", "Springfield", "12345"), 75000.0)

  ! Access nested components
  print *, emp%name                      ! Alice
  print *, emp%home%city                 ! Springfield
  print *, emp%home%zip                  ! 12345

  ! Department with array of employees
  type(Department) :: dept
  dept%name = "Engineering"
  allocate(dept%employees(3))
  dept%employees(1) = emp
  dept%employees(2) = Employee(102, "Bob", &
                        Address("456 Oak Ave", "Shelbyville", "54321"), 68000.0)
  dept%count = 2

  ! Iterate over array of types
  block
    integer :: i
    do i = 1, dept%count
      print *, dept%employees(i)%id, trim(dept%employees(i)%name), &
               dept%employees(i)%home%city
    end do
  end block

  ! Array of derived type components
  print *, dept%employees(1:2)%id        ! array of id field
end program nested_types
08

I/O de Arquivos & Formatação

Abrindo & Fechando Arquivos

open() conecta um arquivo a um número de unit. newunit=u deixa o compilador escolher uma unit única (evita conflitos) — sempre prefira isso em vez de números de unit fixos no código. status: 'old' (o arquivo deve existir), 'new' (não deve existir), 'replace' (excluir + criar), 'scratch' (temporário, auto-excluído ao fechar). action: 'read', 'write', 'readwrite'. position: 'rewind' (início), 'append' (fim), 'asis' (onde estiver). Sempre verifique iostat após open e read — não-zero significa erro (negativo = EOF, positivo = erro). iomsg dá uma mensagem de erro descritiva. close() desconecta. Para tratamento robusto de arquivos, sempre verifique iostat e trate erros graciosamente.

fortran
program file_open
  implicit none
  integer :: u, ios
  character(100) :: msg

  ! newunit: compiler picks a unique unit number (Fortran 2008)
  ! status: 'old' (must exist), 'new' (must not exist),
  !         'replace' (overwrite), 'scratch' (temporary)
  ! action: 'read', 'write', 'readwrite'
  ! position: 'rewind', 'append', 'asis'
  open(newunit=u, file="data.txt", status="replace", &
       action="write", iostat=ios, iomsg=msg)
  if (ios /= 0) then
    print *, "Open failed: ", trim(msg)
    stop 1
  end if

  write(u, *) "First line"
  write(u, *) "Second line"
  close(u)

  ! Append to existing file
  open(newunit=u, file="data.txt", status="old", &
       action="write", position="append", iostat=ios)
  if (ios == 0) then
    write(u, *) "Appended line"
    close(u)
  end if

  ! Scratch file (auto-deleted on close)
  open(newunit=u, status="scratch", action="readwrite")
  write(u, *) "Temporary data"
  rewind(u)
  ! read back...
  close(u)   ! file disappears

  ! Reading with end-of-file detection
  open(newunit=u, file="data.txt", status="old", action="read")
  do
    read(u, '(A)', iostat=ios) msg
    if (ios /= 0) exit   ! EOF or error
    print *, trim(msg)
  end do
  close(u)
end program file_open

I/O Formatado

Especificadores de formato controlam I/O: '(A, I0, F8.2)'. A=character, I0=integer largura mínima, F8.2=real largura 8 com 2 decimais. write(unit, fmt) escreve; read(unit, fmt) lê. unit=* significa stdout/stdin. Para arquivos, use a unit de open(). I/O list-directed (*) é flexível: read(u, *) a, b, c lê valores separados por vírgula/espaço automaticamente. Para CSV, list-directed read funciona se os valores forem separados por vírgula. Strings de formato podem ser reusadas: '(3I4)' aplica I4 três vezes. Sempre corresponda o formato ao tipo de dado — formatos incompatíveis causam erros de runtime. Para texto misto + números, leia como string e depois faça parse, ou use formatos explícitos.

fortran
program formatted_io
  implicit none
  integer :: u, n = 42
  real :: pi = 3.14159265
  character(20) :: name = "Alice"

  ! Write formatted data to file
  open(newunit=u, file="output.txt", status="replace")
  write(u, '(A, I0)') "Count: ", n
  write(u, '(A, F8.4)') "Pi: ", pi
  write(u, '(A, A)') "Name: ", trim(name)
  write(u, '(3(I0, 1X))') 1, 2, 3   ! "1 2 3 "
  close(u)

  ! Read formatted data back
  open(newunit=u, file="output.txt", status="old", action="read")
  block
    character(100) :: line
    integer :: i
    do i = 1, 5
      read(u, '(A)') line
      print *, trim(line)
    end do
  end block
  close(u)

  ! Reading structured data
  open(newunit=u, file="data.csv", status="replace")
  write(u, '(I0, ",", I0, ",", I0)') 1, 10, 100
  write(u, '(I0, ",", I0, ",", I0)') 2, 20, 200
  close(u)

  ! Read back as numbers
  open(newunit=u, file="data.csv", status="old", action="read")
  block
    integer :: a, b, c
    ! List-directed read handles commas as separators
    read(u, *) a, b, c
    print *, a, b, c   ! 1 10 100
    read(u, *) a, b, c
    print *, a, b, c   ! 2 20 200
  end block
  close(u)
end program formatted_io

I/O Não-formatado (Binário)

I/O não-formatado (binário) é mais rápido e compacto que I/O formatado (texto) — sem conversão de string. form='unformatted' o habilita. access='stream' (Fortran 2003) dá acesso byte-stream (como I/O de arquivo C, sem record markers). access='sequential' (padrão) usa record markers (cada write/read é um record com prefixos de comprimento) — portável dentro do Fortran, mas não para outras linguagens. Para interoperabilidade com C/Python, use stream access. Arquivos binários não são legíveis por humanos, mas ideais para grandes conjuntos de dados numéricos. Sempre escreva metadados (tamanhos de array, informações de tipo) antes dos dados para que você possa lê-los de volta corretamente. I/O não-formatado preserva precisão total (sem arredondamento da conversão de texto).

fortran
program binary_io
  implicit none
  integer :: u, i
  real :: arr(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
  real :: arr_in(5)
  integer :: n

  ! Write binary (unformatted) - faster, no format conversion
  open(newunit=u, file="data.bin", status="replace", &
       form="unformatted", access="stream")
  write(u) size(arr)        ! write the count first
  write(u) arr              ! write the whole array
  close(u)

  ! Read binary back
  open(newunit=u, file="data.bin", status="old", &
       form="unformatted", access="stream", action="read")
  read(u) n                 ! read the count
  print *, "Count: ", n
  read(u) arr_in            ! read the array
  print *, arr_in           ! 1 2 3 4 5
  close(u)

  ! Sequential unformatted (default) - with record markers
  open(newunit=u, file="data_seq.bin", status="replace", &
       form="unformatted")   ! access defaults to "sequential"
  do i = 1, 5
    write(u) i, arr(i)      ! each write is a "record"
  end do
  close(u)

  ! Read sequentially
  open(newunit=u, file="data_seq.bin", status="old", &
       form="unformatted", action="read")
  do
    read(u, iostat=i) n, arr_in(1)
    if (i /= 0) exit
    print *, n, arr_in(1)
  end do
  close(u)
end program binary_io

Namelist (I/O Agrupado)

namelist agrupa variáveis para I/O de texto estruturado — como JSON/YAML, mas nativo do Fortran. Defina com 'namelist /name/ var1, var2, ...'. write(u, nml=name) produz no formato &NAME var=val, var=val, /. read(u, nml=name) faz parse de volta. Namelist é perfeito para arquivos de configuração: usuários editam um arquivo de texto, o programa o lê. As variáveis mantêm seus valores declarados como padrão; apenas as que estão no arquivo são sobrescritas. O formato é tolerante (insensível a whitespace, vírgulas opcionais). Namelist suporta todos os tipos intrínsecos e arrays. iostat captura erros de parse. Esta é a maneira mais fácil de tornar um programa Fortran configurável sem escrever um parser personalizado.

fortran
program namelist_demo
  implicit none
  ! Namelist groups variables for easy I/O
  integer :: max_iter = 100
  real :: tolerance = 1.0e-6
  logical :: verbose = .true.
  character(20) :: method = "newton"
  real :: params(3) = [0.1, 0.2, 0.3]

  ! Define a namelist group
  namelist /config/ max_iter, tolerance, verbose, method, params

  ! Write namelist to file
  block
    integer :: u
    open(newunit=u, file="config.nml", status="replace")
    write(u, nml=config)
    close(u)
  end block

  ! The file looks like:
  ! &CONFIG
  !  MAX_ITER=100,
  !  TOLERANCE=1.0000000E-06,
  !  VERBOSE=T,
  !  METHOD="newton",
  !  PARAMS=0.100000, 0.200000, 0.300000,
  ! /

  ! Read namelist from file (overrides defaults)
  block
    integer :: u, ios
    open(newunit=u, file="config.nml", status="old", action="read")
    read(u, nml=config, iostat=ios)
    close(u)
    if (ios /= 0) then
      print *, "Error reading config"
    else
      print *, "max_iter=", max_iter
      print *, "tolerance=", tolerance
      print *, "method=", trim(method)
      print *, "params=", params
    end if
  end block

  ! User can edit config.nml in a text editor
  ! then re-run to pick up new values
end program namelist_demo

Arquivos Internos & Tratamento de Erros

Arquivos internos permitem ler de / escrever para uma string de caracteres em vez de um arquivo — sprintf/sscanf do Fortran. write(str, fmt) formata para uma string; read(str, fmt) faz parse de uma string. Esta é a maneira padrão de converter entre strings e números. Sempre use iostat para tratamento de erros: 0 = sucesso, negativo = EOF, positivo = erro. Os labels legados end= e err= funcionam, mas iostat é mais limpo (sem goto). Para parse robusto, verifique iostat após cada read. I/O interno é ótimo para: construir strings de saída, fazer parse de entrada do usuário, converter valores de arquivos de configuração. A string age como um 'arquivo interno' — mesmas declarações de I/O, apenas um destino de string.

fortran
program internal_io
  implicit none
  character(100) :: buffer
  integer :: n = 42
  real :: x = 3.14159
  integer :: ios

  ! Internal write: format to a string (like sprintf)
  write(buffer, '(A, I0, A, F8.4)') "n=", n, " x=", x
  print *, trim(buffer)   ! n=42 x=  3.1416

  ! Internal read: parse from a string (like sscanf)
  character(50) :: input = "100 3.14 hello"
  integer :: a
  real :: b
  character(20) :: c
  read(input, *, iostat=ios) a, b, c
  if (ios == 0) print *, a, b, trim(c)   ! 100 3.14 hello

  ! Robust number parsing with error handling
  character(20) :: num_str = "3.14abc"
  real :: val
  read(num_str, *, iostat=ios) val
  if (ios /= 0) then
    print *, "Parse error: '", trim(num_str), "' is not a number"
  else
    print *, "Value: ", val
  end if

  ! End-of-file and error handling on file reads
  block
    integer :: u
    character(100) :: line
    open(newunit=u, file="data.txt", status="old", action="read")
    do
      read(u, '(A)', iostat=ios) line
      if (ios < 0) then
        print *, "End of file"
        exit
      else if (ios > 0) then
        print *, "Read error at line"
        exit
      end if
      print *, trim(line)
    end do
    close(u)
  end block

  ! err= and end= labels (legacy style)
  block
    integer :: u, val
    open(newunit=u, file="nums.txt", status="old", action="read")
    do
      read(u, *, end=100, err=200) val
      print *, val
    end do
100 print *, "Reached EOF"
    close(u)
    goto 300
200 print *, "Read error!"
    close(u)
300 continue
  end block
end program internal_io
09

Computação Numérica

Parâmetros Kind & Precisão

Parâmetros kind controlam precisão/tamanho. Maneira moderna e portátil: use iso_fortran_env (int32/int64, real32/real64/real128). Legado: selected_real_kind(digits, exponent_range). real32 ≈ 7 dígitos significativos, real64 ≈ 15 dígitos (double), real128 ≈ 33 dígitos (quad). CRÍTICO: sempre anexe o sufixo kind a literais (3.14_dp, não 3.14) — caso contrário, o literal é parseado como precisão simples ENTÃO convertido, perdendo dígitos. precision() retorna dígitos significativos; range() retorna intervalo de expoente decimal. epsilon() dá o epsilon da máquina (menor incremento distinguível). tiny/huge dão min/max. Para computação científica, use real64 (double precision) por padrão.

fortran
program precision_demo
  use iso_fortran_env, only: int32, int64, real32, real64, real128
  implicit none
  ! Portable kind selection via iso_fortran_env
  integer(kind=int32) :: i32 = 100
  integer(kind=int64) :: i64 = 9223372036854775807_int64
  real(kind=real32) :: r32 = 3.14159265_real32   ! single (~7 digits)
  real(kind=real64) :: r64 = 3.14159265358979_real64  ! double (~15 digits)
  real(kind=real128) :: r128 = 3.14159265358979_real128 ! quad (~33 digits)

  ! Legacy: selected_real_kind (still works)
  integer, parameter :: dp = selected_real_kind(15, 307)  ! double
  integer, parameter :: sp = selected_real_kind(6, 37)    ! single
  real(kind=dp) :: pi = 3.14159265358979_dp

  print *, "real32 precision: ", precision(r32), " range: ", range(r32)
  print *, "real64 precision: ", precision(r64), " range: ", range(r64)
  print *, "real128 precision: ", precision(r128)

  ! Epsilon and tiny
  print *, "epsilon(r32): ", epsilon(r32)   ! ~1.19e-7
  print *, "epsilon(r64): ", epsilon(r64)   ! ~2.22e-16
  print *, "tiny(r64): ", tiny(r64)         ! smallest positive
  print *, "huge(i32): ", huge(i32)         ! 2147483647
  print *, "huge(i64): ", huge(i64)

  ! Always use _kind suffix on literals
  ! WRONG: real(kind=dp) :: x = 3.14  (loses precision!)
  ! RIGHT: real(kind=dp) :: x = 3.14_dp
end program precision_demo

Álgebra Linear (matmul, solve, BLAS)

Fortran tem álgebra linear built-in: matmul (multiplicação matriz-matriz/matriz-vetor), dot_product, transpose. Para resolver sistemas lineares (Ax=b), autovalores, SVD, etc., use LAPACK (a biblioteca Fortran padrão da indústria): dgesv resolve Ax=b, dgesvd faz SVD, dsyev faz decomposição de autovalores. Link com -llapack -lblas. O exemplo mostra eliminação Gaussiana manual para um sistema 3x3 — para trabalho real, use LAPACK (mais rápido, mais preciso com pivoteamento, lida com qualquer tamanho). O armazenamento column-major do Fortran corresponde às expectativas do LAPACK nativamente (sem transposição necessária). matmul é otimizado, mas para matrizes grandes, BLAS dgemm é mais rápido. Sempre verifique o número de condição para estabilidade numérica.

fortran
program linalg
  implicit none
  real(8) :: A(3,3), B(3,3), C(3,3)
  real(8) :: v(3), w(3), x(3)
  real(8) :: det
  integer :: i

  ! Initialize matrices
  A = reshape([1,2,3, 4,5,6, 7,8,10], [3,3])  ! column-major fill
  B = reshape([1,0,0, 0,1,0, 0,0,1], [3,3])   ! identity

  ! Matrix-matrix multiplication
  C = matmul(A, B)        ! A * I = A
  print *, "A * I = A? ", all(abs(C - A) < 1e-10)

  ! Matrix-vector multiplication
  v = [1.0, 2.0, 3.0]
  w = matmul(A, v)
  print *, "A * v = ", w    ! 14 32 53

  ! Dot product
  print *, "v . v = ", dot_product(v, v)   ! 14

  ! Outer product
  C = 0.0
  do i = 1, 3
    C(:,i) = v * v(i)
  end do

  ! Transpose
  C = transpose(A)

  ! Solve linear system Ax = b (need LAPACK or custom)
  ! Using LAPACK dgesv:
  !   call dgesv(n, nrhs, A, lda, ipiv, b, ldb, info)
  ! For demo, manual Gaussian elimination:
  x = solve_3x3(A, v)
  print *, "Solution: ", x

contains
  function solve_3x3(A, b) result(x)
    real(8), intent(inout) :: A(3,3)
    real(8), intent(in) :: b(3)
    real(8) :: x(3), M(3,4), factor
    integer :: k, j
    M(:,1:3) = A
    M(:,4) = b
    ! Forward elimination
    do k = 1, 2
      do i = k+1, 3
        factor = M(i,k) / M(k,k)
        M(i,:) = M(i,:) - factor * M(k,:)
      end do
    end do
    ! Back substitution
    x(3) = M(3,4) / M(3,3)
    x(2) = (M(2,4) - M(2,3)*x(3)) / M(2,2)
    x(1) = (M(1,4) - M(1,2)*x(2) - M(1,3)*x(3)) / M(1,1)
  end function
end program linalg

Números Aleatórios

call random_number(x) preenche x com reais uniformes [0,1) — funciona em escalares ou arrays. call random_seed(size=n) obtém o tamanho da seed; random_seed(put=seed) define a seed para reprodutibilidade (essencial para teste/debug). Para inteiros em [a,b]: a + int(r * (b-a+1)). Para números aleatórios Gaussianos (normais), use a transformação Box-Muller (mostrada) ou o método polar. Fortran não tem gerador de distribuição normal built-in — implemente ou use uma biblioteca. Para simulações de Monte Carlo, defina a seed para reprodutibilidade, então rode muitos testes. random_number NÃO é criptograficamente seguro — use uma biblioteca crypto para segurança. Para código paralelo, cada imagem precisa de uma seed distinta.

fortran
program random_demo
  implicit none
  real :: r
  integer :: i, n = 10
  real :: arr(10)
  integer :: seed_size
  integer, allocatable :: seed(:)

  ! Initialize random seed (Fortran 2003: random_seed with no args
  ! uses a processor-dependent seed; for reproducibility, set it)
  call random_seed(size=seed_size)
  allocate(seed(seed_size))
  ! Set a fixed seed for reproducibility
  seed = [(12345 + i*6789, i=1, seed_size)]
  call random_seed(put=seed)

  ! Generate uniform [0,1) random reals
  call random_number(r)
  print *, "Single: ", r

  call random_number(arr)   ! fills entire array
  print *, "Array: ", arr

  ! Generate integers in [a, b]
  block
    integer :: a = 1, b = 6
    integer :: dice
    do i = 1, 5
      call random_number(r)
      dice = a + int(r * (b - a + 1))   ! [1, 6]
      print *, "Dice roll: ", dice
    end do
  end block

  ! Normal (Gaussian) via Box-Muller transform
  block
    real :: u1, u2, z1, z2
    integer :: j
    do j = 1, 5
      call random_number(u1)
      call random_number(u2)
      u1 = max(u1, 1e-10)   ! avoid log(0)
      z1 = sqrt(-2.0 * log(u1)) * cos(2.0 * 3.14159265 * u2)
      z2 = sqrt(-2.0 * log(u1)) * sin(2.0 * 3.14159265 * u2)
      print *, "Gaussian: ", z1, z2
    end do
  end block

  ! Monte Carlo: estimate pi
  block
    integer :: inside = 0, total = 100000
    real :: x, y
    do i = 1, total
      call random_number(x)
      call random_number(y)
      if (x*x + y*y <= 1.0) inside = inside + 1
    end do
    print *, "Pi estimate: ", 4.0 * real(inside) / total
  end block
end program random_demo

Integração Numérica & Busca de Raízes

Integração numérica: a regra de Simpson é mais precisa que a regra do trapézio (erro O(h^4) vs O(h^2)). Passe funções como argumentos usando um bloco interface. Busca de raízes: bisseção é robusta (sempre converge se o sinal muda) mas lenta (convergência linear); o método de Newton é rápido (convergência quadrática) mas precisa da derivada e pode divergir. Para trabalho de produção, use QUADPACK (integração) ou MINPACK (busca de raízes) — bibliotecas Fortran testadas em batalha. O bloco interface é essencial ao passar funções como argumentos — diz ao compilador a assinatura da função. Sempre defina uma contagem máxima de iterações para evitar loops infinitos. Verifique convergência com tolerâncias tanto do valor da função quanto do tamanho do passo.

fortran
program numerical
  implicit none
  real(8) :: a, b, result
  integer :: n

  ! Numerical integration: Simpson's rule
  ! Integrate f(x) = x^2 from 0 to 2 (exact: 8/3 ≈ 2.6667)
  a = 0.0; b = 2.0; n = 1000
  result = simpson(f_sq, a, b, n)
  print *, "Integral of x^2 from 0 to 2: ", result   ! ~2.6667

  ! Integrate sin(x) from 0 to pi (exact: 2.0)
  result = simpson(f_sin, 0.0_8, 3.14159265358979_8, 1000)
  print *, "Integral of sin(x) from 0 to pi: ", result  ! ~2.0

  ! Root finding: bisection method
  ! Find root of f(x) = x^2 - 2 (i.e., sqrt(2) ≈ 1.4142)
  result = bisection(f_x2_minus_2, 0.0_8, 2.0_8, 1e-12_8)
  print *, "sqrt(2) = ", result   ! ~1.41421356

  ! Newton's method (needs derivative)
  result = newton(f_x2_minus_2, fp_x2_minus_2, 1.0_8, 1e-12_8)
  print *, "sqrt(2) via Newton: ", result

contains
  ! Function to integrate: x^2
  function f_sq(x) result(y)
    real(8), intent(in) :: x
    real(8) :: y
    y = x * x
  end function

  function f_sin(x) result(y)
    real(8), intent(in) :: x
    real(8) :: y
    y = sin(x)
  end function

  ! Simpson's rule: integral of f from a to b with n intervals
  function simpson(f, a, b, n) result(integral)
    interface
      function f(x) result(y)
        import
        real(8), intent(in) :: x
        real(8) :: y
      end function
    end interface
    real(8), intent(in) :: a, b
    integer, intent(in) :: n
    real(8) :: integral, h, x
    integer :: i
    h = (b - a) / n
    integral = f(a) + f(b)
    do i = 1, n-1
      x = a + i * h
      if (mod(i, 2) == 0) then
        integral = integral + 2.0 * f(x)
      else
        integral = integral + 4.0 * f(x)
      end if
    end do
    integral = integral * h / 3.0
  end function

  function f_x2_minus_2(x) result(y)
    real(8), intent(in) :: x
    real(8) :: y
    y = x*x - 2.0
  end function

  function fp_x2_minus_2(x) result(y)
    real(8), intent(in) :: x
    real(8) :: y
    y = 2.0 * x
  end function

  ! Bisection: find root in [a, b] (f(a) and f(b) must have opposite signs)
  function bisection(f, a, b, tol) result(root)
    interface
      function f(x) result(y)
        import
        real(8), intent(in) :: x
        real(8) :: y
      end function
    end interface
    real(8), intent(in) :: a, b, tol
    real(8) :: root, fa, fb, mid, fmid
    integer :: iter, maxiter = 100
    fa = f(a); fb = f(b)
    if (fa * fb > 0) stop "No sign change in interval"
    do iter = 1, maxiter
      mid = (a + b) / 2.0
      fmid = f(mid)
      if (abs(fmid) < tol .or. (b - a)/2.0 < tol) then
        root = mid
        return
      end if
      if (fa * fmid < 0) then
        b = mid; fb = fmid
      else
        a = mid; fa = fmid
      end if
    end do
    root = (a + b) / 2.0
  end function

  ! Newton's method: x_{n+1} = x_n - f(x)/f'(x)
  function newton(f, fp, x0, tol) result(root)
    interface
      function f(x) result(y)
        import
        real(8), intent(in) :: x
        real(8) :: y
      end function
      function fp(x) result(y)
        import
        real(8), intent(in) :: x
        real(8) :: y
      end function
    end interface
    real(8), intent(in) :: x0, tol
    real(8) :: root, x, fx, fpx
    integer :: iter, maxiter = 100
    x = x0
    do iter = 1, maxiter
      fx = f(x); fpx = fp(x)
      if (abs(fx) < tol) then
        root = x
        return
      end if
      x = x - fx / fpx
    end do
    root = x
  end function
end program numerical

Aritmética IEEE & Exceções

O módulo ieee_arithmetic (Fortran 2003) fornece suporte a IEEE 754: Infinity, NaN (Not a Number), NaN sinalizador/silencioso, flags de exceção e modos de arredondamento. NaN NUNCA é igual a nada (incluindo a si mesmo) — use ieee_is_nan() para testar. Infinity resulta de overflow ou divisão por zero. Flags de exceção (ieee_divide_by_zero, ieee_overflow, ieee_underflow, ieee_inexact, ieee_invalid) rastreiam se ocorreram exceções — verifique com ieee_get_flag, limpe com ieee_set_flag. ieee_set_halting_mode controla se uma exceção para o programa. Modos de arredondamento afetam operações de ponto flutuante. Use isso para código numérico robusto: detecte NaN/Inf, trate exceções graciosamente e controle a precisão. Nota: -ffast-math no gfortran quebra a conformidade IEEE (não use para código que dependa desses recursos).

fortran
program ieee_demo
  use ieee_arithmetic
  implicit none
  real :: a, b, c
  logical :: flag

  ! IEEE special values
  a = ieee_value(a, ieee_positive_inf)   ! +Infinity
  b = ieee_value(b, ieee_negative_inf)   ! -Infinity
  c = ieee_value(c, ieee_quiet_nan)      ! NaN

  print *, "Infinity: ", a                ! Infinity
  print *, "NaN: ", c                     ! NaN
  print *, "Inf > 1e30: ", a > 1e30       ! T
  print *, "NaN == NaN: ", c == c         ! F (NaN is never equal!)

  ! Check for special values
  print *, "is_nan(c): ", ieee_is_nan(c)         ! T
  print *, "is_finite(1.0): ", ieee_is_finite(1.0)  ! T
  print *, "is_inf(a): ", ieee_is_finite(a)      ! F

  ! Operations producing special values
  print *, "1.0/0.0: ", 1.0/0.0           ! Infinity (if -ffast-math off)
  print *, "0.0/0.0: ", 0.0/0.0           ! NaN
  print *, "sqrt(-1.0): ", sqrt(-1.0)     ! NaN

  ! IEEE exception flags
  call ieee_set_halting_mode(ieee_divide_by_zero, .false.)  ! don't halt
  b = 1.0 / 0.0   ! sets divide_by_zero flag, returns Inf
  call ieee_get_flag(ieee_divide_by_zero, flag)
  print *, "Divide by zero occurred: ", flag   ! T

  ! Check and clear flags
  call ieee_set_flag(ieee_all, .false.)   ! clear all flags
  b = 1.0 / 0.0
  call ieee_get_flag(ieee_divide_by_zero, flag)
  print *, "Flag after division: ", flag   ! T
  call ieee_set_flag(ieee_all, .false.)   ! clear

  ! Rounding modes
  call ieee_set_rounding_mode(ieee_nearest)   ! default
  call ieee_set_rounding_mode(ieee_down)      ! round toward -inf
  call ieee_set_rounding_mode(ieee_up)        ! round toward +inf
  call ieee_set_rounding_mode(ieee_to_zero)   ! truncate

  ! Comparing NaN-safe
  if (ieee_unordered(c, 1.0)) print *, "c is unordered (NaN)"
end program ieee_demo
10

Coarrays & Paralelo

Declaração básica de coarray

Coarrays são o modelo paralelo built-in do Fortran (F2008). Cada 'image' é um processo paralelo. Declare com sufixo [*]. this_image() retorna o rank; num_images() a contagem. Acesse remoto com x[k]. sync all é uma barreira. Compiladores: gfortran (com -fcoarray=lib), ifort, Cray.

fortran
program coarray_hello
  use iso_fortran_env, only: real64
  implicit none
  real(real64) :: x[*]  ! coarray — one copy per image
  integer :: me

  me = this_image()
  x = real(me, real64)  ! local assignment

  call co_sum(x, result_image=1)  ! reduce sum to image 1
  if (me == 1) print *, 'Sum =', x

  sync all  ! barrier
end program

Acesso remoto e sincronização

Acesse coarray remoto com sufixo [k] — comunicação one-sided. Leituras e escritas são não-bloqueantes até sync. sync all é barreira global; sync images([1,2]) espera por images específicas. Seções críticas: lock/unlock com critical...end critical. Evite deadlock ordenando syncs de forma consistente.

fortran
program remote_access
  implicit none
  integer :: val[*], neighbor
  integer :: me, n

  me = this_image()
  n = num_images()
  val = me * 10

  sync all  ! ensure all writes complete

  ! read from neighbor (circular)
  neighbor = merge(1, me + 1, me == n)
  print *, 'Image', me, 'sees neighbor', neighbor, 'value', val[neighbor]

  ! write to image 1 from all
  if (me /= 1) val[1] = val[me]
  sync all
  if (me == 1) print *, 'Image 1 received:', val
end program

Operações coletivas

Coletivos: co_sum, co_min, co_max, co_broadcast. Eles operam em coarrays e reduzem/broadcast através de images. result_image especifica quem obtém a resposta (padrão: todos). source_image para broadcast. Sempre sincronize antes de ler resultados coletivos em outras images. Mais rápido que loops manuais com syncs.

fortran
program collectives
  use iso_fortran_env, only: real64
  implicit none
  real(real64) :: local_sum, global_sum[*]
  integer :: i

  local_sum = 0.0_real64
  do i = 1, 100
    local_sum = local_sum + real(i * this_image(), real64)
  end do

  global_sum = local_sum
  call co_sum(global_sum, result_image=1)
  if (this_image() == 1) print *, 'Total:', global_sum

  ! other collectives: co_min, co_max, co_broadcast
  call co_broadcast(global_sum, source_image=1)
end program

Tipos derivados coarray

Tipos derivados podem ser coarrays — todo componente é replicado por image. Acesse componentes remotos com p[k]%field. Componentes allocatable em coarrays requerem Fortran 2018+ (alguns compiladores podem não suportar). Para arrays de partículas, use type(particle), allocatable :: particles(:)[:].

fortran
program coarray_types
  implicit none
  type :: particle
    real :: x, y, z
    real :: mass
  end type
  type(particle) :: p[*]
  integer :: me

  me = this_image()
  p%mass = real(me)
  p%x = real(me) * 0.5

  sync all
  ! access component of remote coarray
  if (me == 1) print *, 'Image 2 mass:', p[2]%mass
end program

Coarrays allocatable e teams

Coarrays allocatable são alocados em todas as images simultaneamente com sufixo [*]. Teams (F2018) dividem images em grupos independentes — cada um tem seu próprio this_image/num_images. form team cria teams; change team entra no escopo. Útil para paralelismo hierárquico. Suporte do compilador varia (ifort, gfortran 9+).

fortran
program alloc_coarrays
  use iso_fortran_env, only: team_type
  implicit none
  integer, allocatable :: data(:)[:]
  type(team_type) :: odd_team, even_team
  integer :: me

  me = this_image()
  allocate(data(100)[*])  ! allocate on all images

  ! F2018 teams — split images into groups
  form team(merge(1, 2, mod(me, 2) == 1), odd_team, even_team)
  if (mod(me, 2) == 1) then
    change team(odd_team)
      ! this_image() and num_images() now refer to team
      data(this_image()) = me
    end team
  end if

  deallocate(data)
end program
11

Interoperabilidade com C

Básico de ISO_C_BINDING

iso_c_binding fornece kinds compatíveis com C (c_int, c_double, c_char, etc.). bind(C, name='...') expõe Fortran para C com um nome de símbolo específico. Strings C precisam de terminador c_null_char. O bloco interface declara a assinatura da função C. Compile C e Fortran separadamente, link juntos.

fortran
program c_interop
  use iso_c_binding, only: c_int, c_double, c_char, c_null_char
  implicit none
  interface
    subroutine c_print(msg) bind(C, name='c_print')
      import :: c_char
      character(kind=c_char), dimension(*) :: msg
    end subroutine
  end interface

  call c_print('Hello from Fortran' // c_null_char)
end program

! Corresponding C:
!   void c_print(const char* msg) { printf("%s\n", msg); }

Passando arrays para C

Passe arrays via c_loc (obtém ponteiro C) e tipo c_ptr. Use atributo 'value' para argumentos escalares C (passados por valor, não referência). Arrays Fortran são column-major; C é row-major — transpose arrays 2D ou documente a convenção. c_f_pointer converte ponteiros C de volta para ponteiros Fortran.

fortran
program array_interop
  use iso_c_binding, only: c_double, c_int, c_loc, c_f_pointer
  implicit none
  real(c_double), target :: arr(10)
  integer(c_int) :: n
  type(c_ptr) :: ptr

  arr = [(real(i), i=1,10)]
  ptr = c_loc(arr(1))  ! get C pointer

  ! call C function: void process(double* arr, int n);
  call process_c(ptr, size(arr, kind=c_int))

  interface
    subroutine process_c(arr, n) bind(C, name='process')
      import :: c_double, c_int, c_ptr
      type(c_ptr), value :: arr
      integer(c_int), value :: n
    end subroutine
  end interface
end program

Tipos interoperáveis com C

Tipos com bind(C) têm layout de memória compatível com C — necessário para passar structs para C. Apenas kinds compatíveis com C são permitidos (sem real/integer padrão). Sem componentes allocatable/pointer. Arrays char de comprimento fixo emulam strings C. A ordem importa — Fortran pode reordenar componentes sem bind(C).

fortran
module data_types
  use iso_c_binding, only: c_double, c_int, c_char
  implicit none

  type, bind(C) :: point
    real(c_double) :: x, y
    integer(c_int) :: id
  end type

  type, bind(C) :: string_holder
    character(kind=c_char, len=1) :: name(64)
  end type
end module

! C equivalent:
!   struct point { double x, y; int id; };
!   struct string_holder { char name[64]; };

Chamando Fortran a partir de C

Subroutines com bind(C, name='...') são chamáveis a partir de C por aquele nome. Use 'value' para escalares que C passa por valor; arrays são passados por referência (sem value). Resultados de função devem ser escalares compatíveis com C. Use nomes de binding C para evitar name mangling do compilador (underscores, mudanças de caso).

fortran
! Fortran:
module fmod
  use iso_c_binding, only: c_double
  implicit none
contains
  subroutine square_array(arr, n) bind(C, name='square_array')
    integer, value :: n
    real(c_double), intent(inout) :: arr(n)
    arr = arr ** 2
  end subroutine
end module

! C caller:
!   extern void square_array(double* arr, int n);
!   double data[5] = {1, 2, 3, 4, 5};
!   square_array(data, 5);

Ponteiros de função C e callbacks

abstract interface declara uma assinatura de função C. procedure(iface) aceita uma função correspondente como argumento. Passe ponteiros de função C diretamente. c_funloc obtém o endereço C de um procedimento Fortran; c_f_procpointer converte um c_funptr de volta para um procedimento Fortran. Útil para callbacks estilo qsort.

fortran
module callbacks
  use iso_c_binding, only: c_funloc, c_funptr, c_int
  implicit none

  abstract interface
    function comparator(a, b) bind(C)
      import :: c_int
      integer(c_int), value :: a, b
      integer(c_int) :: comparator
    end function
  end interface

contains
  subroutine sort_with_c(arr, n, cmp) bind(C)
    integer(c_int), value :: n
    integer(c_int), intent(inout) :: arr(n)
    procedure(comparator) :: cmp
    ! ... use cmp(a, b) to compare ...
  end subroutine
end module

! C side:
!   int descending(int a, int b) { return b - a; }
!   sort_with_c(arr, n, descending);
12

Orientação a Objetos (extends/final)

Extensão de tipo (herança)

Extensão de tipo = herança. 'extends(parent)' declara uma subclasse. 'class(T)' é polimórfico (aceita T ou qualquer extensão); 'type(T)' é exato. 'abstract' + 'deferred' = método abstrato (deve ser sobrescrito). 'contains' introduz procedimentos type-bound. Sobrescreva redeclarando o procedimento com o mesmo nome.

fortran
module shapes
  implicit none
  type, abstract :: shape
    real :: x = 0, y = 0
  contains
    procedure(area_iface), deferred :: area
    procedure :: move => shape_move
  end type

  abstract interface
    function area_iface(this) result(a)
      import :: shape
      class(shape), intent(in) :: this
      real :: a
    end function
  end interface

  type, extends(shape) :: circle
    real :: radius
  contains
    procedure :: area => circle_area
  end type

contains
  subroutine shape_move(this, dx, dy)
    class(shape), intent(inout) :: this
    real, intent(in) :: dx, dy
    this%x = this%x + dx
    this%y = this%y + dy
  end subroutine

  function circle_area(this) result(a)
    class(circle), intent(in) :: this
    real :: a
    a = 3.14159 * this%radius ** 2
  end function
end module

Polimorfismo e SELECT TYPE

select type faz discriminação de tipo em runtime em variáveis polimórficas. 'type is (T)' corresponde ao tipo exato; 'class is (T)' corresponde a T e extensões. Dentro do bloco, a variável é tratada como o tipo correspondido (acessa componentes específicos). Arrays polimórficos seguram tipos mistos via class(shape) — mas a alocação deve ser por elemento.

fortran
module polymorph
  use shapes, only: shape, circle, rectangle
  implicit none
contains
  subroutine describe(s)
    class(shape), intent(in) :: s
    select type(s)
    type is (circle)
      print *, 'Circle radius:', s%radius
    type is (rectangle)
      print *, 'Rectangle:', s%width, 'x', s%height
    class is (shape)
      print *, 'Some shape at:', s%x, s%y
    class default
      print *, 'Unknown type'
    end select
  end subroutine

  subroutine process_all(shapes)
    class(shape), intent(in) :: shapes(:)
    integer :: i
    do i = 1, size(shapes)
      call describe(shapes(i))
      print *, 'Area:', shapes(i)%area()
    end do
  end subroutine
end module

Finalizers e destrutores

Procedimentos final rodam automaticamente quando uma variável sai do escopo — como destrutores C++. Defina com 'final :: name'. Deve ser uma subroutine pegando TYPE (não class) — sem polimorfismo. Um tipo pode ter múltiplos finalizers (sobrecarregados por rank). Use para fechar arquivos, liberar memória, liberar recursos. Não pode falhar/levantar exceção.

fortran
module resources
  implicit none
  type :: file_handle
    integer :: unit = -1
  contains
    final :: close_file
    procedure :: open
  end type
contains
  subroutine open(this, filename)
    class(file_handle), intent(inout) :: this
    character(*), intent(in) :: filename
    open(newunit=this%unit, file=filename, status='old')
  end subroutine

  subroutine close_file(this)
    type(file_handle), intent(inout) :: this
    if (this%unit /= -1) then
      close(this%unit)
      this%unit = -1
    end if
  end subroutine
end module

program demo
  use resources
  type(file_handle) :: f
  call f%open('data.txt')
  ! ... use file ...
end program  ! f goes out of scope -> close_file called automatically

Construtores e alocação

Interface genérico com o nome do tipo age como um construtor personalizado — sobrecarrega o construtor de estrutura padrão. Múltiplos procedimentos permitem conjuntos de argumentos diferentes. O construtor padrão (point(x=..., y=...)) ainda está disponível, a menos que seja sobrescrito. Alocação polimórfica: allocate(circle :: shape_var) cria um circle em uma variável class(shape).

fortran
module points
  implicit none
  type :: point
    real :: x, y
  contains
    procedure :: norm
  end type
  interface point
    procedure new_point
    procedure new_point_polar
  end interface
contains
  function new_point(x, y) result(p)
    real, intent(in) :: x, y
    type(point) :: p
    p%x = x
    p%y = y
  end function

  function new_point_polar(r, theta) result(p)
    real, intent(in) :: r, theta
    type(point) :: p
    p%x = r * cos(theta)
    p%y = r * sin(theta)
  end function

  function norm(this) result(n)
    class(point), intent(in) :: this
    real :: n
    n = sqrt(this%x**2 + this%y**2)
  end function
end module

program use_points
  use points
  type(point) :: a, b
  a = point(1.0, 2.0)         ! cartesian
  b = point(3.0, 0.5)         ! polar (same name, different args)
  print *, a%norm(), b%norm()
end program

Tipos abstratos e templates

Tipos abstratos não podem ser instanciados — apenas estendidos. Procedimentos deferred devem ser sobrescritos em subclasses concretas. class(*) é polimórfico ilimitado — segura qualquer tipo (use select type para recuperar). Esse padrão implementa classes base abstratas e interfaces. Containers concretos (list, stack, queue) estendem e implementam os procedimentos deferred.

fortran
module container
  implicit none
  type, abstract :: container
  contains
    procedure(add_iface), deferred :: add
    procedure(size_iface), deferred :: size
    procedure :: is_empty => container_is_empty
  end type

  abstract interface
    subroutine add_iface(this, item)
      import :: container
      class(container), intent(inout) :: this
      class(*), intent(in) :: item
    end subroutine
    function size_iface(this) result(n)
      import :: container
      class(container), intent(in) :: this
      integer :: n
    end function
  end interface
contains
  function container_is_empty(this) result(e)
    class(container), intent(in) :: this
    logical :: e
    e = (this%size() == 0)
  end function
end module

! Concrete subclass implements add/size
! class(*) allows storing any type (unlimited polymorphic)
13

Tipos Derivados Parametrizados

Declaração básica de PDT

Parameterized Derived Types (PDTs, F2003) são como templates C++. Parâmetros 'kind' são compile-time (fixos por instância); parâmetros 'len' são runtime (podem ser diferidos com ':'). Aloque tipos deferred-length com allocate(type(params) :: var). PDTs habilitam containers genéricos type-safe sem truques de pré-processador.

fortran
module pdt_types
  implicit none
  type :: matrix(k, n, m)
    integer, kind :: k = kind(1.0)
    integer, len :: n, m
    real(k) :: data(n, m)
  end type

  ! kind parameters: compile-time (like template params)
  ! len parameters: runtime (like allocatable dimensions)
end module

program use_pdt
  use pdt_types
  implicit none
  type(matrix(kind(1.0), 3, 3)) :: small  ! fixed 3x3
  type(matrix(kind(1.d0), :, :)), allocatable :: big  ! deferred

  small%data = 0.0
  allocate(matrix(kind(1.d0), 100, 100) :: big)
  big%data = 0.d0
  deallocate(big)
end program

PDT com parâmetro kind

Tipos parametrizados por kind permitem escrever um tipo que funciona para múltiplas precisões. O tipo de retorno da função usa this%k para corresponder. Note a sintaxe class(array_t(k=*)) para o procedimento — necessário para tipos kind-param. Suporte do compilador: gfortran 9+, ifort. Útil para bibliotecas que suportam precisão simples/dupla/quad.

fortran
module typed_array
  implicit none
  type :: array_t(k)
    integer, kind :: k = kind(1.0)
    real(k), allocatable :: data(:)
  contains
    procedure :: sum_values
  end type
contains
  function sum_values(this) result(s)
    class(array_t(k=*)), intent(in) :: this
    real(this%k) :: s
    s = sum(this%data)
  end function
end module

program test
  use typed_array
  type(array_t(kind(1.0)))  :: float_arr
  type(array_t(kind(1.d0))) :: double_arr

  allocate(float_arr%data(10))
  allocate(double_arr%data(10))
  float_arr%data = 1.0
  double_arr%data = 1.d0

  print *, float_arr%sum_values()
  print *, double_arr%sum_values()
end program

PDT com parâmetro length

Tipos parametrizados por length carregam o tamanho como um parâmetro de tipo. fstr(5) e fstr(6) são tipos diferentes. Procedimentos usam fstr(*) para aceitar qualquer length. O tipo de resultado pode depender dos lengths de entrada (c tem length a%n + b%n). Útil para buffers de tamanho fixo, arrays de tamanho estático. Suporte de compilador limitado — teste completamente.

fortran
module fixed_string
  implicit none
  type :: fstr(n)
    integer, len :: n
    character(len=n) :: str
  end type
contains
  function concat(a, b) result(c)
    type(fstr(*)), intent(in) :: a, b
    type(fstr(a%n + b%n)) :: c
    c%str = a%str // b%str
  end function
end module

program test
  use fixed_string
  type(fstr(5))  :: a = fstr(5)('Hello')
  type(fstr(6))  :: b = fstr(6)(' World')
  type(fstr(11)) :: c

  c = concat(a, b)
  print *, c%str
end program

Arrays allocatable em PDT

PDTs podem conter componentes allocatable. O parâmetro kind propaga para tipos de componente (real(this%k)). move_alloc transfere alocação eficientemente (sem cópia). Use class(stack(k=*)) em procedimentos para aceitar qualquer kind. PDTs com componentes allocatable combinam tipagem genérica com sizing dinâmico.

fortran
module stack_t
  implicit none
  type :: stack(k)
    integer, kind :: k = kind(1.0)
    real(k), allocatable :: data(:)
    integer :: top = 0
  contains
    procedure :: push
    procedure :: pop
    procedure :: is_empty
  end type
contains
  subroutine push(this, val)
    class(stack(k=*)), intent(inout) :: this
    real(this%k), intent(in) :: val
    if (this%top == size(this%data)) then
      block
        real(this%k), allocatable :: tmp(:)
        tmp = [this%data, val]
        call move_alloc(tmp, this%data)
      end block
    else
      this%top = this%top + 1
      this%data(this%top) = val
    end if
  end subroutine
end module

Limitações de PDT e workarounds

Suporte a PDT varia — parâmetros kind são amplamente suportados; parâmetros length menos. Para sizing em runtime, prefira componentes allocatable em vez de parâmetros len. Arrays PDT (type(matrix(4,4)) :: arr(10)) podem não funcionar em todos os compiladores. Teste no seu compilador alvo. Para máxima portabilidade, use pré-processador (#define) ou interfaces genéricas.

fortran
! Limitations of PDTs:
! - Not all compilers fully support len parameters
! - I/O of PDTs may not work as expected
! - Some F2003 features (e.g., PDT arrays) have spotty support

! Workaround: use allocatable components instead of len params
module alt_matrix
  implicit none
  type :: matrix_alt(k)
    integer, kind :: k = kind(1.0)
    real(k), allocatable :: data(:,:)
  end type
contains
  function make_matrix(k_, n, m) result(mat)
    integer, intent(in) :: n, m
    ! kind must be compile-time constant — can't pass dynamically
    ! workaround: separate constructors per kind
  end function
end module

! Best practice: use kind params (well-supported), avoid len params
! Use allocatable components for runtime sizing instead
14

Submodules

Estrutura básica de submodule

Submodules (F2008) separam interface de implementação. O módulo declara a interface; o submodule fornece o corpo. Mudanças no corpo do submodule não acionam recompilação de dependentes — apenas mudanças de interface o fazem. Use 'module procedure' para implementar. Ótimo para bibliotecas grandes com compilação cara.

fortran
! parent_module.f90
module math_lib
  implicit none
  interface
    module function integrate(f, a, b, n) result(s)
      abstract interface
        real function func_t(x)
          real, intent(in) :: x
        end function
      end interface
      procedure(func_t) :: f
      real, intent(in) :: a, b
      integer, intent(in) :: n
      real :: s
    end function
  end interface
end module

! sub_math.f90
submodule(math_lib) math_impl
  implicit none
contains
  module procedure integrate
    integer :: i
    real :: dx, x
    dx = (b - a) / n
    s = 0.5 * (f(a) + f(b))
    do i = 1, n-1
      x = a + i * dx
      s = s + f(x)
    end do
    s = s * dx
  end procedure
end submodule

Múltiplos submodules por módulo

Um módulo pode ter múltiplos submodules — divida implementações entre arquivos. Cada submodule começa com 'submodule(parent) name'. Procedimentos no mesmo módulo parent podem se chamar. Isso habilita compilação incremental: edite um submodule, recompile apenas ele e link. Útil para módulos muito grandes.

fortran
! big_lib.f90
module big_lib
  implicit none
  interface
    module subroutine sort(arr)
      real, intent(inout) :: arr(:)
    end subroutine
    module function mean(arr) result(m)
      real, intent(in) :: arr(:)
      real :: m
    end function
    module function stddev(arr) result(s)
      real, intent(in) :: arr(:)
      real :: s
    end function
  end interface
end module

! sub_sort.f90 — one submodule
submodule(big_lib) sort_impl
contains
  module procedure sort
    ! quicksort implementation
  end procedure
end submodule

! sub_stats.f90 — another submodule
submodule(big_lib) stats_impl
contains
  module procedure mean
    m = sum(arr) / size(arr)
  end procedure
  module procedure stddev
    block
      real :: mu
      mu = mean(arr)  ! can call sibling module procedures
      s = sqrt(sum((arr - mu)**2) / (size(arr) - 1))
    end block
  end procedure
end submodule

Procedimentos internos apenas em submodule

Submodules podem conter procedimentos helper privados não visíveis através da interface do módulo parent. Isso esconde detalhes de implementação enquanto os mantém co-localizados. Apenas implementações 'module procedure' são acessíveis via parent. Helpers permanecem internos — melhor encapsulamento do que colocar tudo no bloco contains do módulo.

fortran
module sparse_ops
  implicit none
  interface
    module function sparse_multiply(a, b) result(c)
      ! ... sparse matrix types ...
      real, allocatable :: c(:,:)
    end function
  end interface
end module

submodule(sparse_ops) sparse_impl
  implicit none
contains
  module procedure sparse_multiply
    ! call helper — invisible to module users
    call validate_dimensions(a, b)
    c = multiply_kernel(a, b)
  end procedure

  ! private helper — not exposed via parent module
  subroutine validate_dimensions(a, b)
    ! ...
  end subroutine

  function multiply_kernel(a, b) result(c)
    ! ...
  end function
end submodule

Herdando variáveis de módulo

Submodules têm acesso às variáveis e tipos do módulo parent via host association. Eles podem ler e modificar estado em nível de módulo. Isso é útil para configuração que afeta o comportamento da implementação. Mudanças em variáveis de módulo ainda requerem recompilação do submodule (e dependentes).

fortran
module config
  implicit none
  integer :: verbosity = 0
  interface
    module subroutine log_message(msg)
      character(*), intent(in) :: msg
    end subroutine
  end interface
end module

submodule(config) config_impl
  implicit none
  ! submodule can access parent module's variables
contains
  module procedure log_message
    if (verbosity > 0) then
      print *, '[LOG]', msg
    end if
  end procedure
end submodule

! Usage:
!   use config
!   verbosity = 1
!   call log_message('Starting up')

Benefícios de compilação do submodule

O benefício principal: mudanças de implementação em um submodule não acionam recompilação de código que apenas 'usa' o módulo parent. Apenas o submodule em si recompila. Isso acelera dramaticamente builds incrementais para projetos Fortran grandes. Reestruture implementações que mudam frequentemente em submodules; mantenha interfaces estáveis no parent.

fortran
! Without submodules:
!   module big_mod
!     ... 5000 lines of implementation ...
!   end module
!   ! Any change -> recompile big_mod + all users

! With submodules:
!   module big_mod
!     ! just interfaces (~200 lines)
!   end module
!   submodule(big_mod) impl
!     ! 5000 lines of implementation
!   end submodule
!   ! Change impl body -> recompile only submodule
!   ! Change interface -> recompile everything (unavoidable)

! Build system example (Makefile):
!   big_mod.o: big_mod.f90
!   impl.o: impl.f90 big_mod.o
!   user.o: user.f90 big_mod.o  ! NOT impl.o
!   app: user.o impl.o big_mod.o
!   	$(FC) -o app user.o impl.o big_mod.o
15

Ponto Flutuante IEEE

Exceções e flags IEEE

ieee_exceptions fornece acesso a flags de exceção IEEE (overflow, underflow, divide_by_zero, invalid, inexact). ieee_get_flag lê; ieee_set_flag limpa. ieee_set_halting_mode controla se exceções param o programa. ieee_arithmetic fornece ieee_is_nan, ieee_is_finite, ieee_is_negative, etc. Sempre verifique flags após computações críticas.

fortran
program ieee_exceptions
  use ieee_exceptions
  use ieee_arithmetic, only: ieee_is_nan, ieee_is_finite
  implicit none
  real :: x, y
  logical :: overflow_flag

  call ieee_set_halting_mode(ieee_overflow, .false.)
  x = huge(x) * 10  ! overflow -> Inf, no halt

  call ieee_get_flag(ieee_overflow, overflow_flag)
  print *, 'Overflow occurred:', overflow_flag

  y = 0.0 / 0.0  ! NaN
  print *, 'x is finite:', ieee_is_finite(x)
  print *, 'y is NaN:', ieee_is_nan(y)

  call ieee_set_flag(ieee_all, .false.)  ! clear all flags
end program

Tratamento de NaN e Inf

Inf e NaN são valores especiais IEEE. NaN != NaN (use ieee_is_nan para testar). Inf propaga através de aritmética. Operações que produzem NaN: 0/0, Inf-Inf, 0*Inf, sqrt(-1). Compile com -ffpe-trap=invalid,zero,overflow para parar nesses casos (debugging). Código de produção deve verificar NaN após operações arriscadas.

fortran
program nan_inf
  use ieee_arithmetic, only: ieee_value, ieee_nan, &
                             ieee_positive_inf, ieee_negative_inf
  implicit none
  real :: pos_inf, neg_inf, nan_val

  pos_inf = ieee_value(0.0, ieee_positive_inf)
  neg_inf = ieee_value(0.0, ieee_negative_inf)
  nan_val = ieee_value(0.0, ieee_nan)

  print *, 'Inf - Inf =', pos_inf - pos_inf  ! NaN
  print *, '0 * Inf =', 0.0 * pos_inf         ! NaN
  print *, 'Inf > 1e30:', pos_inf > 1e30       ! T
  print *, 'NaN == NaN:', nan_val == nan_val   ! F (always!)

  ! generate via arithmetic
  pos_inf = 1.0 / 0.0  ! may halt without -fno-trapping-math
end program

Modos de arredondamento

IEEE suporta 4 modos de arredondamento: nearest (padrão), down, up, to zero. ieee_set_rounding_mode o altera em runtime. Útil para aritmética de intervalos (computar limites superior/inferior arredondando para cima/baixo). Afeta todas as operações FP subsequentes até ser alterado. Restaure para nearest quando terminar. Alguns compiladores otimizam assumindo nearest — use com cuidado.

fortran
program rounding_modes
  use ieee_arithmetic, only: ieee_set_rounding_mode, &
                             ieee_nearest, ieee_down, ieee_up, ieee_to_zero
  implicit none
  real :: x, y, result

  x = 1.0 / 3.0
  print *, 'Nearest:', x

  call ieee_set_rounding_mode(ieee_down)
  result = 1.0 / 3.0
  print *, 'Down:', result

  call ieee_set_rounding_mode(ieee_up)
  result = 1.0 / 3.0
  print *, 'Up:', result

  call ieee_set_rounding_mode(ieee_to_zero)
  result = 1.0 / 3.0
  print *, 'To zero:', result

  call ieee_set_rounding_mode(ieee_nearest)  ! restore default
end program

Inquiry de recursos IEEE

ieee_features e funções de inquiry do ieee_arithmetic permitem verificar quais recursos IEEE o compilador/plataforma suporta. ieee_support_nan, ieee_support_inf, ieee_support_rounding, ieee_support_datatype, etc. Útil para código portátil — degrace graciosamente em sistemas não-IEEE. A maioria dos sistemas modernos suporta todos os recursos.

fortran
program ieee_inquiry
  use ieee_features
  use ieee_arithmetic, only: ieee_support_nan, ieee_support_inf, &
                             ieee_support_rounding, ieee_nearest
  implicit none

  if (ieee_support_nan(1.0)) print *, 'NaN supported'
  if (ieee_support_inf(1.0)) print *, 'Inf supported'
  if (ieee_support_rounding(ieee_nearest, 1.0)) &
    print *, 'Nearest rounding supported'

  ! Check halting mode
  block
    use ieee_exceptions
    logical :: halting
    call ieee_get_halting_mode(ieee_overflow, halting)
    print *, 'Overflow halts:', halting
  end block
end program

Controlando o ambiente FP

Controle o comportamento FP por seção: desabilite halting para código arriscado, verifique flags depois, restaure. ieee_all corresponde a todas as exceções. Padrão: limpe flags, rode computação, verifique flags, trate erros. Para produção, prefira verificações explícitas (ieee_is_nan) em vez de detecção baseada em flags — flags podem ser definidas por código não relacionado.

fortran
program fp_env
  use ieee_arithmetic
  use ieee_exceptions
  implicit none
  real :: a, b, c

  ! Save/restore FP state
  block
    use iso_fortran_env, only: real32
    real(real32) :: saved_state
    ! (no portable save/restore in standard — use compiler intrinsics)
  end block

  ! Disable halting for a risky section
  call ieee_set_halting_mode(ieee_all, .false.)
  call ieee_set_flag(ieee_all, .false.)

  a = sqrt(-1.0)  ! NaN, no halt
  b = 1.0 / 0.0   ! Inf, no halt

  if (ieee_is_nan(a)) then
    print *, 'Got NaN, using fallback'
    a = 0.0
  end if

  ! Re-enable halting
  call ieee_set_halting_mode(ieee_divide_by_zero, .true.)
  call ieee_set_halting_mode(ieee_invalid, .true.)
end program
16

Desempenho & Otimização

Ordenação de arrays e contiguidade

Arrays Fortran são column-major: a(i,j) e a(i+1,j) são adjacentes na memória. A ordem do loop importa: o loop mais interno deve iterar o primeiro índice. Ordem errada causa cache misses — desaceleração de 10x+. A sintaxe de array (sum, matmul) deixa o compilador otimizar. Use arrays contíguos (não ponteiros) para o melhor desempenho.

fortran
program array_perf
  implicit none
  real, allocatable :: a(:,:), b(:,:)
  integer :: i, j, n
  real :: s

  n = 1000
  allocate(a(n,n), b(n,n))

  ! GOOD: column-major access (Fortran is column-major)
  do j = 1, n
    do i = 1, n
      s = s + a(i,j)
    end do
  end do

  ! BAD: row-major access (cache misses)
  do i = 1, n
    do j = 1, n
      s = s + a(i,j)
    end do
  end do

  ! BEST: array syntax (compiler optimizes)
  s = sum(a)
end program

Procedimentos pure e elemental

Procedimentos pure não têm efeitos colaterais — o compilador pode otimizar, paralelizar e reordenar chamadas. Procedimentos elemental funcionam tanto em escalares quanto em arrays (auto-vetorizados). pure elemental combina ambos. Use-os para funções matemáticas. Restrições: sem I/O, sem modificação global, intent(in) para todas as entradas. Habilita melhor otimização que procedimentos regulares.

fortran
module math_ops
  implicit none
contains
  ! pure: no side effects, enables optimization
  pure function square(x) result(y)
    real, intent(in) :: x
    real :: y
    y = x * x
  end function

  ! elemental: works on scalars and arrays element-wise
  pure elemental function clamp(x, lo, hi) result(y)
    real, intent(in) :: x, lo, hi
    real :: y
    y = max(lo, min(x, hi))
  end function
end module

program use_math
  use math_ops
  implicit none
  real :: arr(100), scalar

  arr = clamp(arr, 0.0, 1.0)  ! applies to each element
  scalar = clamp(1.5, 0.0, 1.0)  ! also works on scalar
  arr = square(arr)  ! elemental too (if declared)
end program

Flags de otimização do compilador

Comece com -O2 -march=native para produção. -O3 pode ajudar ou prejudicar — faça benchmark. -Ofast quebra conformidade IEEE (não use se NaN/Inf importam). -flto habilita inlining entre arquivos (compilação mais lenta, execução mais rápida). Use -fcheck=all e -ffpe-trap em builds de debug para capturar erros. Otimização guiada por perfil (-fprofile-use) dá 5-15% de speedup.

fortran
! gfortran optimization flags:
!   -O0   no optimization (debug)
!   -O1   basic optimization
!   -O2   standard optimization (recommended)
!   -O3   aggressive (may increase code size)
!   -Ofast  -O3 + -ffast-math (breaks IEEE compliance)
!   -march=native  use CPU's full instruction set
!   -flto  link-time optimization (cross-file inlining)
!   -fopenmp  enable OpenMP pragmas
!   -fprofile-generate/use  profile-guided optimization

! Example Makefile:
!   FC = gfortran
!   FFLAGS = -O2 -march=native -Wall -fcheck=all
!   FFLAGS_RELEASE = -O3 -march=native -flto -fno-trapping-math
!   FFLAGS_DEBUG = -O0 -g -fcheck=all -fbacktrace -ffpe-trap=invalid,zero

! ifort equivalents:
!   -O2, -O3, -xHost (= -march=native), -ipo (= -flto)

Paralelismo OpenMP

OpenMP adiciona paralelismo via diretivas (!$omp). 'parallel do' paraleliza o próximo loop. 'reduction(+:s)' lida com somas com segurança. Compile com -fopenmp (gfortran) ou -qopenmp (ifort). Defina a contagem de threads via variável de ambiente OMP_NUM_THREADS. Melhor para loops CPU-bound com iterações independentes. Cuidado com false sharing e desequilíbrio de carga.

fortran
program openmp_demo
  use omp_lib
  implicit none
  real, allocatable :: a(:), b(:), c(:)
  integer :: i, n, nthreads

  n = 10000000
  allocate(a(n), b(n), c(n))
  a = 1.0; b = 2.0

  !$omp parallel
  if (omp_get_thread_num() == 0) then
    nthreads = omp_get_num_threads()
    print *, 'Threads:', nthreads
  end if
  !$omp end parallel

  !$omp parallel do
  do i = 1, n
    c(i) = a(i) + b(i)
  end do
  !$omp end parallel do

  ! reduction
  block
    real :: s
    s = 0.0
    !$omp parallel do reduction(+:s)
    do i = 1, n
      s = s + c(i)
    end do
    !$omp end parallel do
    print *, 'Sum:', s
  end block
end program

Profiling e detecção de hotspots

system_clock dá timing portátil. Para profiling sério, use gprof (compile com -pg), perf (Linux) ou Intel VTune. Faça profile antes de otimizar — surpresas são comuns. Concentre-se em hotspots (os 20% do código que tomam 80% do tempo). Otimize os loops mais internos primeiro. Sempre faça benchmark antes/depois das mudanças — intuição costuma estar errada.

fortran
! Compile with profiling:
!   gfortran -pg -O2 prog.f90 -o prog
!   ./prog  (generates gmon.out)
!   gprof prog gmon.out > profile.txt

! Or use perf (Linux):
!   perf record ./prog
!   perf report

! Or gprofng (modern):
!   gprofng collect app ./prog
!   gprofng analyze

! Manual timing:
program timing
  use iso_fortran_env, only: real64, int64
  implicit none
  integer(int64) :: start, finish, rate
  real(real64) :: elapsed
  real, allocatable :: a(:)
  integer :: i

  allocate(a(10000000))
  call system_clock(start, rate)
  do i = 1, 100
    a = a + 1.0
  end do
  call system_clock(finish)
  elapsed = real(finish - start, real64) / real(rate, real64)
  print *, 'Elapsed:', elapsed, 'sec'
end program
17

Programação em Linguagem Mista

Fortran chamando biblioteca C

Envolva funções de biblioteca C em um módulo de interface Fortran. Use bind(C, name='...') para linkar ao símbolo C exato. Passe ponteiros como c_ptr com 'value'. Para argumentos de ponteiro de função (como o comparador do qsort), use uma abstract interface. Isso permite chamar qualquer função C — incluindo libc, BLAS, system calls.

fortran
module c_glue
  use iso_c_binding
  implicit none
  interface
    function c_malloc(size) bind(C, name='malloc')
      import :: c_ptr, c_size_t
      integer(c_size_t), value :: size
      type(c_ptr) :: c_malloc
    end function

    subroutine c_free(ptr) bind(C, name='free')
      import :: c_ptr
      type(c_ptr), value :: ptr
    end subroutine

    subroutine c_qsort(base, nmemb, size, compar) bind(C, name='qsort')
      import :: c_ptr, c_size_t, c_int
      type(c_ptr), value :: base
      integer(c_size_t), value :: nmemb, size
      abstract interface
        function compar_fn(a, b) bind(C)
          import :: c_ptr
          type(c_ptr), value :: a, b
          integer(c_int) :: compar_fn
        end function
      end interface
      procedure(compar_fn) :: compar
    end subroutine
  end interface
end module

Interoperação C++ via extern C

C++ faz name-mangling de símbolos, então envolva funções C++ em blocos extern "C". A interface Fortran então se vincula ao nome não mangled. Link com -lstdc++ (gfortran) ou use um linker C++. Isso permite usar bibliotecas C++ (STL, Boost, Qt) a partir do Fortran. Para classes C++, escreva um wrapper de API C flat, então chame a partir do Fortran.

fortran
! C++ side (wrapper.cpp):
!   extern "C" {
!     void cpp_process(double* data, int n) {
!       std::vector<double> v(data, data+n);
!       std::sort(v.begin(), v.end());
!       std::copy(v.begin(), v.end(), data);
!     }
!   }

! Fortran side:
module cpp_glue
  use iso_c_binding
  implicit none
  interface
    subroutine cpp_process(data, n) bind(C, name='cpp_process')
      import :: c_double, c_int
      real(c_double), intent(inout) :: data(*)
      integer(c_int), value :: n
    end subroutine
  end interface
end module

program use_cpp
  use cpp_glue
  implicit none
  real(c_double) :: arr(10) = [5.d0, 3.d0, 8.d0, 1.d0, 9.d0, &
                                2.d0, 7.d0, 4.d0, 6.d0, 0.d0]
  call cpp_process(arr, size(arr, kind=c_int))
  print *, arr
end program

! Build: gfortran use_cpp.f90 wrapper.cpp -lstdc++ -o app

Interoperação Python via f2py

f2py (parte do numpy) gera bindings Python automaticamente para Fortran. Ele lê atributos intent e cria interfaces Python/numpy adequadas. intent(out) torna-se um valor de retorno. Arrays mapeiam para arrays numpy (zero-copy quando contíguos). Ótimo para código numérico crítico de desempenho chamado a partir do Python. Suporte limitado para recursos F2003+.

fortran
! Fortran module (myfuncs.f90):
module myfuncs
  implicit none
contains
  subroutine compute(arr, n, result)
    integer, intent(in) :: n
    real(8), intent(in) :: arr(n)
    real(8), intent(out) :: result
    result = sum(arr**2)
  end subroutine

  function fast_sin(x) result(y)
    real(8), intent(in) :: x
    real(8) :: y
    y = sin(x)
  end function
end module

! Build Python extension:
!   f2py -c myfuncs.f90 -m myfuncs
!   (creates myfuncs.cpython-*.so)

! Python usage:
!   import numpy as np
!   import myfuncs
!   arr = np.array([1.0, 2.0, 3.0])
!   result = myfuncs.myfuncs.compute(arr)
!   y = myfuncs.myfuncs.fast_sin(1.5)

Biblioteca compartilhada e carregamento dinâmico

Use dlopen/dlsym (POSIX) ou LoadLibrary/GetProcAddress (Windows) para plugins carregados em runtime. Envolva em interfaces iso_c_binding. Converta c_ptr para ponteiro de procedimento com c_f_procpointer. Isso habilita arquiteturas de plugins — carregue diferentes implementações em runtime. Cross-platform: use #ifdef para loaders específicos de SO.

fortran
module dynamic_loader
  use iso_c_binding
  implicit none
  interface
    function dlopen(filename, flag) bind(C, name='dlopen')
      import :: c_ptr, c_char, c_int
      character(kind=c_char), dimension(*) :: filename
      integer(c_int), value :: flag
      type(c_ptr) :: dlopen
    end function

    function dlsym(handle, name) bind(C, name='dlsym')
      import :: c_ptr, c_char
      type(c_ptr), value :: handle
      character(kind=c_char), dimension(*) :: name
      type(c_ptr) :: dlsym
    end function

    function dlclose(handle) bind(C, name='dlclose')
      import :: c_ptr, c_int
      type(c_ptr), value :: handle
      integer(c_int) :: dlclose
    end function
  end interface
end module

program plugin
  use dynamic_loader
  implicit none
  type(c_ptr) :: lib, sym
  integer, parameter :: RTLD_NOW = 2

  lib = dlopen('./plugin.so' // c_null_char, RTLD_NOW)
  if (.not. c_associated(lib)) then
    print *, 'Failed to load library'
    stop 1
  end if

  sym = dlsym(lib, 'init' // c_null_char)
  ! call init via c_f_procpointer...
  print *, dlclose(lib)
end program

Integração com sistema de build

CMake lida bem com builds de linguagem mista. Declare todas as linguagens em project(). Defina flags por linguagem. Link bibliotecas na ordem de dependência. CMake rastreia dependências de módulos Fortran automaticamente (sem ordenação manual de .mod). Para linking da biblioteca padrão C++, use a flag certa por plataforma. Use Fortran como linker principal se o runtime Fortran for necessário.

fortran
# CMakeLists.txt for mixed Fortran/C/C++ project:
cmake_minimum_required(VERSION 3.18)
project(mixed LANGUAGES Fortran C CXX)

set(CMAKE_Fortran_FLAGS "-O2 -fopenmp")
set(CMAKE_C_FLAGS "-O2")
set(CMAKE_CXX_FLAGS "-O2 -std=c++17")

add_library(fortran_lib STATIC mymod.f90)
add_library(c_lib STATIC helper.c)
add_library(cpp_lib STATIC wrapper.cpp)

# Mixed-language executable
add_executable(app main.f90)
target_link_libraries(app fortran_lib c_lib cpp_lib)

# Fortran needs C runtime
if(APPLE)
  target_link_libraries(app "-lc++")
else()
  target_link_libraries(app stdc++)
endif()

# Module dependency tracking (CMake handles automatically)
#   fortran_lib depends on its own .mod files
#   app depends on fortran_lib's .mod files
18

I/O Moderno

Newunit e tratamento seguro de arquivos

newunit= evita colisões de número de unit — o compilador escolhe um número livre. Sempre verifique iostat após open/read/write — não-zero significa erro. iomsg dá uma mensagem de erro legível por humanos. action= ('read', 'write', 'readwrite') previne uso acidental. status= ('old', 'new', 'replace', 'scratch', 'unknown') controla criação de arquivo.

fortran
program safe_io
  implicit none
  integer :: u, ios
  character(256) :: msg

  ! newunit: compiler picks an unused unit number
  open(newunit=u, file='data.txt', status='old', action='read', &
       iostat=ios, iomsg=msg)
  if (ios /= 0) then
    print *, 'Open failed:', trim(msg)
    stop 1
  end if

  ! read with error handling
  read(u, *, iostat=ios, iomsg=msg) some_value
  if (ios /= 0) then
    print *, 'Read failed:', trim(msg)
  end if

  close(u)
contains
  integer function some_value()
    some_value = 0
  end function
end program

I/O Stream (binário)

Stream access (F2003) dá I/O orientado a bytes como C — sem record markers. access='stream' o habilita. pos= lê/escreve em um offset de byte específico. form='unformatted' para binário. Ótimo para arquivos binários interoperáveis (leia arquivos escritos em Fortran a partir de C/Python). Sequential access padrão usa record markers (não portátil).

fortran
program stream_io
  implicit none
  integer :: u, i
  real, allocatable :: data(:)

  ! stream access = byte-oriented (like C fread/fwrite)
  open(newunit=u, file='data.bin', access='stream', &
       form='unformatted', status='replace')

  data = [(real(i), i=1,100)]
  write(u) data  ! binary, no record markers
  close(u)

  ! read back
  allocate(data(100))
  open(newunit=u, file='data.bin', access='stream', &
       form='unformatted', status='old', action='read')
  read(u) data
  close(u)

  ! position-based access
  open(newunit=u, file='data.bin', access='stream', &
       form='unformatted', status='old')
  read(u, pos=21) data(1)  ! read 5th real (4 bytes each)
  close(u)
end program

I/O de tipo derivado

User-Defined Derived Type I/O (F2003) permite controlar como tipos são lidos/escritos. Defina uma subroutine com a assinatura específica e vincule-a via 'generic :: write(formatted)'. O código de formato DT a dispara. Habilita serialização personalizada (CSV, JSON-like, binário). iotype é 'LISTDIRECTED', 'NAMELIST' ou 'DT' para I/O formatado.

fortran
module person_type
  implicit none
  type :: person
    character(20) :: name
    integer :: age
    real :: height
  end type
contains
  ! custom formatted I/O via DT format
  subroutine write_person(dtv, unit, iotype, v_list, iostat, iomsg)
    class(person), intent(in) :: dtv
    integer, intent(in) :: unit
    character(*), intent(in) :: iotype
    integer, intent(in) :: v_list(:)
    integer, intent(out) :: iostat
    character(*), intent(inout) :: iomsg
    write(unit, '(a,",",i0,",",f0.2)', iostat=iostat) &
      trim(dtv%name), dtv%age, dtv%height
  end subroutine
end module

program use_dtv
  use person_type
  implicit none
  type(person) :: p = person('Alice', 30, 5.7)

  ! DT format triggers custom writer
  print "(DT)", p  ! calls write_person
end program

I/O Namelist

Namelist fornece I/O legível por humanos e baseado em nome para grupos de variáveis. Formato: &group_name var=value, ... /. A leitura apenas atualiza variáveis presentes no arquivo — outras mantêm seus valores. Ótimo para arquivos de configuração — usuários editam texto, sem parser necessário. Limitações: sem comentários em alguns compiladores, suporte de tipo limitado.

fortran
program namelist_demo
  implicit none
  integer :: n_iterations = 100
  real :: tolerance = 1.0e-6
  character(50) :: output_file = 'results.txt'
  logical :: verbose = .true.

  namelist /config/ n_iterations, tolerance, output_file, verbose

  ! write namelist
  open(newunit=u, file='config.nml', status='replace')
  write(u, nml=config)
  close(u)

  ! read namelist (only specified vars are updated)
  open(newunit=u, file='config.nml', status='old', action='read')
  read(u, nml=config)
  close(u)

  print *, 'Iterations:', n_iterations
end program

! config.nml format:
! &config
!   n_iterations = 500
!   tolerance = 1.0e-8
!   verbose = .false.
! /

I/O assíncrono

I/O assíncrono (F2003) sobrepõe I/O com computação. write(..., asynchronous='yes') inicia uma operação não-bloqueante. wait(unit) bloqueia até completar. Útil para grandes conjuntos de dados — comece a escrever enquanto computa o próximo pedaço. Suporte do compilador varia. Use inquire(unit=u, pending=...) para verificar status. Combine com double-buffering para pipelines.

fortran
program async_io
  implicit none
  integer :: u1, u2, ios
  real, allocatable :: a(:), b(:)

  allocate(a(1000000), b(1000000))
  a = 1.0; b = 2.0

  open(newunit=u1, file='a.bin', access='stream', form='unformatted', &
       asynchronous='yes')
  open(newunit=u2, file='b.bin', access='stream', form='unformatted', &
       asynchronous='yes')

  ! non-blocking writes
  write(u1, asynchronous='yes') a
  write(u2, asynchronous='yes') b

  ! do other work while I/O proceeds...

  ! wait for completion
  wait(u1)
  wait(u2)

  close(u1); close(u2)
end program
19

Depuração & Profiling

Flags de depuração em tempo de compilação

Use -fcheck=all para verificação de limites (captura erros off-by-one). -ffpe-trap para em NaN/Inf/overflow — inestimável para código numérico. -finit-real=nan torna variáveis não inicializadas visíveis (elas propagam como NaN). -fbacktrace imprime um stack trace em crash. Sempre depure com essas flags; remova para builds de produção (elas desaceleram o código).

fortran
! gfortran debug build:
!   gfortran -O0 -g -fcheck=all -fbacktrace -ffpe-trap=invalid,zero,overflow \
!            -Wall -Wextra -Wpedantic -finit-real=nan prog.f90

! Flag explanations:
!   -O0          no optimization (easier debugging)
!   -g           debug symbols (for gdb)
!   -fcheck=all  bounds, pointer, recursion checks
!   -fbacktrace  print stack trace on error
!   -ffpe-trap   halt on FP exceptions (invalid, zero, overflow)
!   -finit-real=nan  initialize reals to NaN (catch uninitialized)
!   -finit-integer=-9999  initialize integers to sentinel
!   -Wall -Wextra  more warnings

! Runtime error message:
!   At line 42 of file prog.f90
!   Fortran runtime error: Array bound out of bounds for dimension 1

Tratamento de erros com iostat

iostat: negativo = EOF, zero = sucesso, positivo = erro. iomsg dá detalhes. Leia em uma string primeiro, então faça parse — separa erros de I/O de erros de parse. Rastreie números de linha para mensagens de erro úteis. Sempre trate erros explicitamente — falhas silenciosas são difíceis de depurar. Use stop com um código não-zero para sinalizar falha a scripts.

fortran
program robust_io
  implicit none
  integer :: u, ios, line_num
  character(256) :: msg, line
  real :: value

  open(newunit=u, file='data.txt', status='old', action='read', &
       iostat=ios, iomsg=msg)
  if (ios /= 0) call error_exit('Open: ' // trim(msg))

  line_num = 0
  do
    line_num = line_num + 1
    read(u, '(a)', iostat=ios) line
    if (ios < 0) exit  ! EOF
    if (ios > 0) then
      print *, 'Read error at line', line_num
      cycle
    end if

    read(line, *, iostat=ios, iomsg=msg) value
    if (ios /= 0) then
      print *, 'Parse error at line', line_num, ':', trim(msg)
      cycle
    end if
    ! process value
  end do
  close(u)
contains
  subroutine error_exit(m)
    character(*), intent(in) :: m
    print *, 'ERROR:', m
    stop 1
  end subroutine
end program

GDB para Fortran

GDB suporta Fortran: array slices, componentes de tipo derivado, procedimentos de módulo (formato de nome: modname__procname). Use -g -O0 para a melhor experiência de depuração. 'display' auto-imprime variáveis em cada parada — útil para observar loops. 'info locals' mostra todas as locais. Para variáveis de módulo, use 'print modname::varname'.

fortran
! Compile: gfortran -g -O0 prog.f90 -o prog
! Start: gdb ./prog

! Common GDB commands for Fortran:
!   (gdb) break main           break at main
!   (gdb) break prog.f90:42     break at line 42
!   (gdb) break mymod__my_sub   break at subroutine (note double underscore)
!   (gdb) run                   start program
!   (gdb) next                  step over
!   (gdb) step                  step into
!   (gdb) print arr(5)          print array element
!   (gdb) print arr(1:10)       print array slice
!   (gdb) print mat(2,3)        print 2D array element
!   (gdb) print p%name          print derived type component
!   (gdb) display x             watch variable (auto-print on stop)
!   (gdb) backtrace             show call stack
!   (gdb) info locals           show all local variables
!   (gdb) continue              resume execution

Profiling com gprof

gprof amostra o program counter durante a execução. Compile TODOS os arquivos-fonte com -pg para profiles completos. O flat profile mostra onde o tempo é gasto; o call graph mostra a hierarquia de chamadas. Concentre-se em funções com alto tempo 'self' — é onde a otimização ajuda. Nota: -pg altera o timing — código profile-like pode se comportar diferente em produção.

fortran
! Compile with profiling:
!   gfortran -pg -O2 prog.f90 -o prog
! Run:
!   ./prog
!   (creates gmon.out)
! Analyze:
!   gprof prog gmon.out > profile.txt
!   gprof prog gmon.out | less

! profile.txt sections:
!   - Flat profile: time per function (self + cumulative)
!   - Call graph: who called whom, how many times
!   - Index: function cross-references

! Key columns:
!   %time  percentage of total time
!   cumulative  running total
!   self    seconds in function (excluding children)
!   calls   number of calls
!   self/call  average time per call (self)

! For multi-threaded: use gprofng (modern) or perf

Benchmarking de desempenho

Faça benchmark adequadamente: warmup primeiro (efeitos de cache), rode múltiplas iterações, pegue o mínimo (menos ruído). system_clock com count_rate dá wall time. Reporte throughput (elementos/seg) para operações de array. Compare implementações na mesma máquina com as mesmas flags. Cuidado: o compilador pode otimizar resultados 'não usados' — use o resultado (ex.: imprima a soma) para prevenir isso.

fortran
program benchmark
  use iso_fortran_env, only: real64, int64
  implicit none
  integer, parameter :: n = 1000000
  integer, parameter :: niter = 100
  real(real64), allocatable :: a(:), b(:), c(:)
  integer(int64) :: start, finish, rate
  real(real64) :: t_start, t_end, min_time
  integer :: i, iter

  allocate(a(n), b(n), c(n))
  a = 1.0_real64; b = 2.0_real64

  ! warmup (cache, JIT-like effects)
  do i = 1, n
    c(i) = a(i) + b(i)
  end do

  min_time = huge(min_time)
  call system_clock(count_rate=rate)
  do iter = 1, niter
    call system_clock(start)
    do i = 1, n
      c(i) = a(i) + b(i)
    end do
    call system_clock(finish)
    t_start = real(start, real64) / rate
    t_end = real(finish, real64) / rate
    min_time = min(min_time, t_end - t_start)
  end do

  print *, 'Best time:', min_time * 1e6, 'us'
  print *, 'Throughput:', real(n) / min_time / 1e9, 'G elem/s'
end program
20

Fortran Moderno

Free Form

Fortran moderno (90+) usa free form: sem restrições de coluna. Comentários começam com !. Statements podem abranger múltiplas linhas com &. Implicit none é obrigatório para segurança de tipo. Muito mais legível que fixed-form Fortran 77.

fortran
program modern
    implicit none
    integer :: i
    do i = 1, 10
        print *, "Value:", i
    end do
end program modern
! Free form: no column restrictions
! Comments start with !

Modules

Módulos agrupam procedimentos e dados relacionados. use importa um módulo. contains separa declarações em nível de módulo de procedimentos. Módulos fornecem interfaces explícitas, habilitando verificação de tipos. Prefira módulos a procedimentos externos.

fortran
module math_utils
    implicit none
    contains
    function square(x) result(y)
        real, intent(in) :: x
        real :: y
        y = x * x
    end function square
end module math_utils

program test
    use math_utils
    print *, square(3.0)  ! 9.0
end program test

Derived Types

Tipos derivados são estruturas de dados definidas pelo usuário (structs). % acessa componentes. Procedimentos type-bound habilitam OOP. Construtores criam instâncias. Tipos derivados podem ter valores padrão e estender outros tipos (herança). Use para modelagem de dados complexos.

fortran
type :: Point
    real :: x, y
end type Point
type(Point) :: p
p = Point(1.0, 2.0)  ! Constructor
p%x = 3.0  ! Component access
print *, p%x, p%y
! Type-bound procedures (OOP)
type :: Circle
    real :: radius
contains
    procedure :: area => circle_area
end type

Intent Attributes

Parâmetros intent(in) são somente leitura (não podem ser modificados). intent(out) é somente escrita (definido pelo procedimento). intent(inout) é leitura-escrita. O compilador verifica violações de intent. Melhora clareza do código e habilita otimizações. Sempre especifique intent.

fortran
subroutine process(input, output, inout)
    integer, intent(in) :: input    ! Read-only
    integer, intent(out) :: output  ! Write-only
    integer, intent(inout) :: inout ! Read-write
    output = input * 2
    inout = inout + 1
end subroutine

Pure & Elemental

Funções pure não têm efeitos colaterais (sem I/O, sem estado mutável). O compilador pode otimizá-las. Funções elemental funcionam tanto em escalares quanto em arrays automaticamente. Elas são pure por padrão. Ideais para operações matemáticas. Habilita execução paralela.

fortran
pure function square(x) result(y)
    real, intent(in) :: x
    real :: y
    y = x * x
end function
! Elemental: works on scalars and arrays
elemental function double_it(x) result(y)
    real, intent(in) :: x
    real :: y
    y = 2.0 * x
end function
! double_it([1,2,3]) returns [2,4,6]
21

Arrays

Declaração de Array

Arrays Fortran são 1-indexed por padrão. Limites inferiores personalizados com (0:). Ordem column-major (primeiro índice varia mais rápido). Arrays allocatable são heap-allocated e devem ser deallocated. Constantes de array usam [ ]. Arrays Fortran são mais eficientes que arrays C devido a descriptors.

fortran
! Static arrays
real :: a(10)           ! 1D, indices 1-10
real :: b(0:9)          ! 1D, indices 0-9
real :: c(3, 4)         ! 2D, 3 rows x 4 cols
! Allocatable (dynamic)
real, allocatable :: d(:)
allocate(d(100))        ! Allocate
deallocate(d)           ! Free
! Array constants
integer :: nums(5) = [1, 2, 3, 4, 5]

Operações de Array

Fortran suporta operações whole-array: +, -, *, /, **. Sem loops necessários para matemática elemento a elemento. Funções intrínsecas: sum, product, maxval, minval, any, all, count. Muito mais rápido que loops devido à vetorização. Esta é a força do Fortran para computação numérica.

fortran
real :: a(5) = [1, 2, 3, 4, 5]
real :: b(5)
b = a * 2          ! Element-wise: [2,4,6,8,10]
b = a + 1          ! [2,3,4,5,6]
print *, sum(a)    ! 15
print *, maxval(a) ! 5
print *, any(a > 3)  ! .true.
print *, count(a > 2) ! 3

Seções de Array

Seções de array usam sintaxe (start:end:stride). :: significa padrão (1 ao fim, stride 1). Strides negativos revertem. Seções multi-dimensionais funcionam em qualquer dimensão. Seções podem ser passadas a procedimentos. Muito poderoso para slicing sem cópia.

fortran
real :: a(10) = [(i, i=1,10)]
print *, a(3:7)     ! Elements 3 to 7
print *, a(::2)     ! Every other: [1,3,5,7,9]
print *, a(2:8:2)   ! Stride 2: [2,4,6,8]
real :: m(3,3)
m(:, 2)             ! Second column
m(1, :)             ! First row

Construto where

where é atribuição condicional em nível de array. Como um if vetorizado. Elsewhere trata o caso falso. Mais eficiente que loops porque pode ser vetorizado. Use para operações condicionais elemento a elemento em arrays.

fortran
real :: a(5) = [1, -2, 3, -4, 5]
where (a > 0)
    a = a * 2       ! Double positives
elsewhere
    a = 0           ! Zero negatives
end where
! Result: [2, 0, 6, 0, 10]
! Equivalent to a loop with if

Alocação Dinâmica

Arrays allocatable são dimensionados dinamicamente. allocate cria, deallocate libera. Fortran 2003+ auto-desaloca no fim do escopo. Verifique status de alocação com allocated(). Arrays allocatable são automaticamente realocados na atribuição (Fortran 2003+). Muito mais seguro que C malloc/free.

fortran
real, allocatable :: matrix(:,:)
integer :: n
n = 100
allocate(matrix(n, n))
matrix = 0.0  ! Initialize all to 0
! ... use matrix ...
deallocate(matrix)
! Automatic deallocation at end of scope
! (Fortran 2003+)
22

Operações de I/O

Saída Formatada

Strings de formato controlam a saída. I5 = integer largura 5. F8.3 = float largura 8, 3 decimais. I0 = largura mínima. A = string. X = espaço. / = nova linha. write(*,...) é o mesmo que print, mas mais flexível. Use strings de formato para saída alinhada.

fortran
integer :: i = 42
real :: x = 3.14159
print "(I5, F8.3)", i, x    ! "   42    3.142"
print "(A, I0)", "Count=", i  ! "Count=42"
write(*, "(3F6.2)") 1.0, 2.0, 3.0
! Format specifiers:
! I: integer, F: float, E: exponential
! A: string, X: space, /: newline

I/O de Arquivo

open conecta um arquivo a uma unit. newunit atribui um número de unit livre. status: old (deve existir), new (não deve existir), replace. iostat retorna não-zero em erro ou EOF. Sempre verifique iostat para evitar crashes. close desconecta o arquivo.

fortran
integer :: unit, ios
open(newunit=unit, file="data.txt", status="old", action="read")
do
    read(unit, *, iostat=ios) value
    if (ios /= 0) exit
    print *, value
end do
close(unit)
! status: old, new, replace, scratch
! action: read, write, readwrite

Namelist

namelist agrupa variáveis para I/O. O formato do arquivo é &config n=10, x=3.14 /. Útil para arquivos de configuração. Variáveis podem estar em qualquer ordem. Apenas variáveis listadas são lidas/escritas. Muito mais fácil que fazer parse de formatos personalizados.

fortran
integer :: n = 10
real :: x = 3.14
namelist /config/ n, x
! Write namelist
open(1, file="config.nml")
write(1, nml=config)
close(1)
! Read namelist
open(1, file="config.nml")
read(1, nml=config)
close(1)

Arquivos Internos

Arquivos internos usam strings de caracteres como units de I/O. write para uma string converte valores para texto. read de uma string faz parse de texto. Útil para conversão de tipo e formatação. trim remove espaços à direita. Muito mais simples que C sprintf/sscanf.

fortran
character(20) :: str
integer :: num = 42
! Integer to string
write(str, "(I0)") num
print *, trim(str)  ! "42"
! String to integer
read(str, *) num
! Internal files use character variables as units

I/O Binário

I/O não-formatado escreve dados binários crus. Mais rápido e compacto que texto. access="stream" para acesso em nível de byte (Fortran 2008). Não portável entre arquiteturas (endianness). Use para grandes conjuntos de dados científicos. I/O formatado é para dados legíveis por humanos.

fortran
! Unformatted (binary) I/O
open(1, file="data.bin", form="unformatted", &
     access="stream")
write(1) array  ! No format, raw bytes
read(1) array2
close(1)
! Faster than formatted I/O
! Smaller file size
! Not portable across architectures
23

Programação Paralela

OpenMP

OpenMP paraleliza loops com diretivas. !$omp parallel do distribui iterações entre threads. private: cada thread tem sua própria cópia. reduction: combina resultados. Compile com -fopenmp. Maneira fácil de paralelizar código numérico.

fortran
!$omp parallel do private(i) reduction(+:sum)
do i = 1, n
    sum = sum + a(i) * b(i)
end do
!$omp end parallel do
! Compile: gfortran -fopenmp program.f90
! Environment: OMP_NUM_THREADS=4

Coarrays

Coarrays (Fortran 2008) são arrays paralelos built-in. Cada image (processo) tem sua própria cópia. [N] acessa dados de outra image. sync all é uma barreira. this_image() retorna o número da image. Built-in na linguagem, sem biblioteca necessária.

fortran
program coarray_example
    implicit none
    integer :: me[*]  ! Coarray: one per image
    me = this_image()
    sync all  ! Barrier
    if (this_image() == 1) then
        print *, "Image 2 has:", me[2]  ! Remote access
    end if
end program
! Compile: gfortran -fcoarray=single program.f90

Básico de MPI

MPI (Message Passing Interface) é o padrão para paralelismo distribuído. mpi_init/finalize começam e terminam. comm_rank dá o ID do processo. comm_size dá o total de processos. Send/recv para comunicação. Escala para milhares de cores. Use para clusters.

fortran
program mpi_example
    use mpi
    integer :: rank, size, ierr
    call mpi_init(ierr)
    call mpi_comm_rank(MPI_COMM_WORLD, rank, ierr)
    call mpi_comm_size(MPI_COMM_WORLD, size, ierr)
    print *, "I am rank", rank, "of", size
    call mpi_finalize(ierr)
end program
! Compile: mpifort program.f90

do concurrent

do concurrent (Fortran 2008) indica que iterações de loop são independentes. O compilador pode paralelizar automaticamente. local declara variáveis privadas. Mais seguro que OpenMP: o compilador verifica independência. Use para loops embarrassingly parallel.

fortran
do concurrent (i = 1:n) local(tmp)
    tmp = expensive_compute(a(i))
    b(i) = tmp * 2
end do
! Tells compiler iterations are independent
! Can be parallelized automatically
! local: private variable per iteration

Padrão de Redução

Reduções combinam resultados parciais de cada thread. Comuns: sum, product, max, min. Cada thread computa um resultado parcial local. O runtime combina-os no final. Evita data races. Essencial para algoritmos numéricos paralelos.

fortran
!$omp parallel do reduction(+:total)
do i = 1, n
    total = total + a(i)
end do
!$omp end parallel do
! Common reductions: +, *, max, min, .and., .or.
! Each thread has a private copy
! Combined at the end
24

Métodos Numéricos

Álgebra Linear

Fortran tem operações de matriz built-in. matmul multiplica matrizes. dot_product computa produto escalar. transpose transpõe. Essas são altamente otimizadas (nível BLAS). Para produção, use LAPACK. Fortran é a linguagem de escolha para computação numérica de alto desempenho.

fortran
! Matrix multiplication
do i = 1, n
    do j = 1, n
        c(i,j) = sum(a(i,:) * b(:,j))
    end do
end do
! Or use matmul intrinsic
c = matmul(a, b)
! Dot product
dot = dot_product(a, b)
! Transpose
at = transpose(a)

Resolvendo ODEs

O método de Euler é o solver de ODE mais simples: y(n+1) = y(n) + dt*f(t,y). Para precisão, use Runge-Kutta (RK4). O bloco interface passa funções como argumentos. Fortran é ideal para computação científica devido a operações de array e desempenho.

fortran
! Euler method: dy/dt = f(t, y)
subroutine euler(f, t0, y0, dt, n, t, y)
    interface
        real function f(t, y)
            real, intent(in) :: t, y
        end function
    end interface
    real, intent(in) :: t0, y0, dt
    integer, intent(in) :: n
    real, intent(out) :: t(n+1), y(n+1)
    integer :: i
    t(1) = t0; y(1) = y0
    do i = 1, n
        t(i+1) = t(i) + dt
        y(i+1) = y(i) + dt * f(t(i), y(i))
    end do
end subroutine

Números Aleatórios

random_number gera reais uniformes [0,1). random_seed inicializa o gerador. Para inteiros, escale e converta. Box-Muller transforma uniforme em distribuição normal. Para trabalho sério, use uma biblioteca (ex.: Mersenne Twister). Sempre faça seed para reprodutibilidade.

fortran
call random_seed()  ! Seed from system
call random_number(x)  ! x in [0, 1)
! Array of randoms
real :: arr(100)
call random_number(arr)
! Integer in range [1, 6]
integer :: dice
call random_number(r)
dice = int(r * 6) + 1
! Normal distribution (Box-Muller)
call random_number(u1)
call random_number(u2)
z = sqrt(-2*log(u1)) * cos(2*PI*u2)

Interpolação

Interpolação linear estima valores entre pontos conhecidos. Encontre o intervalo, então interpole. Para resultados mais suaves, use interpolação cubic spline. As operações de array do Fortran tornam isso conciso. Sempre verifique limites para evitar erros de extrapolação.

fortran
function interp(x, xs, ys) result(y)
    real, intent(in) :: x, xs(:), ys(:)
    real :: y
    integer :: i
    ! Find interval
    i = 1
    do while (i < size(xs) .and. x > xs(i+1))
        i = i + 1
    end do
    ! Linear interpolation
    y = ys(i) + (ys(i+1) - ys(i)) * &
        (x - xs(i)) / (xs(i+1) - xs(i))
end function

Integração Numérica

A regra do trapézio aproxima integrais: soma de trapézios. Mais preciso: regra de Simpson. Para dimensões maiores, use quadratura Gaussiana. Fortran se destaca em integração numérica devido ao desempenho. Sempre valide com soluções analíticas conhecidas.

fortran
! Trapezoidal rule
function trapezoid(f, a, b, n) result(integral)
    interface
        real function f(x)
            real, intent(in) :: x
        end function
    end interface
    real, intent(in) :: a, b
    integer, intent(in) :: n
    real :: integral, h
    integer :: i
    h = (b - a) / n
    integral = (f(a) + f(b)) / 2
    do i = 1, n-1
        integral = integral + f(a + i*h)
    end do
    integral = integral * h
end function
25

Armadilhas Comuns

Indexação baseada em 1

Arrays Fortran são 1-indexed por padrão, diferente de C/Python (0-indexed). Isso causa erros off-by-one ao portar código. Limites inferiores personalizados (0:9) são permitidos. Seja consistente dentro de um projeto. Verifique limites de array com a flag de compilador -fcheck=bounds.

fortran
! Fortran arrays are 1-based by default
real :: a(10)  ! Indices 1 to 10
! a(0) = 1.0  ! Error: out of bounds
a(1) = 1.0    ! OK
! Custom bounds
real :: b(0:9)  ! Indices 0 to 9
b(0) = 1.0    ! OK

Tipagem Implícita

Sem implicit none, variáveis começando com i-n são integer, outras real. Isso causa bugs sutis (erro de digitação cria uma nova variável). Sempre use implicit none. Fortran moderno (2018+) pode defini-lo globalmente com -fimplicit-none. Esta é a prática recomendada mais importante do Fortran.

fortran
! BAD: implicit typing (Fortran 77 style)
program bad
    ! i-n start with integer by default
    i = 1      ! integer (implicit)
    x = 3.14   ! real (implicit)
end program
! GOOD: explicit typing
program good
    implicit none  ! Force explicit declaration
    integer :: i
    real :: x
end program

Ordem Column-Major

Fortran armazena arrays em column-major: m(1,1), m(2,1), m(3,1), m(1,2), ... Acessar coluna-por-coluna é cache-friendly. Ordem errada de loop causa cache misses e desacelera em 10x+. Sempre corresponda a ordem do loop ao layout de memória. Oposto do C (row-major).

fortran
! Fortran is column-major (first index varies fastest)
real :: m(3, 3)
! Efficient: iterate over first index in inner loop
do j = 1, 3
    do i = 1, 3
        m(i, j) = 0.0  ! Cache-friendly
    end do
end do
! Inefficient: row-major access
do i = 1, 3
    do j = 1, 3
        m(i, j) = 0.0  ! Cache misses
    end do
end do

Passagem por Referência

Fortran passa argumentos por referência (como ponteiros C). Subroutines podem modificar variáveis do chamador a menos que intent(in) seja especificado. Sem intent, modificações acidentais causam bugs. Sempre especifique intent. intent(out) sinaliza que o procedimento definirá o valor.

fortran
subroutine modify(x)
    integer, intent(inout) :: x
    x = 99  ! Modifies the caller variable
end subroutine
! Fortran passes by reference by default
! intent(in) prevents modification
! Without intent, modification is allowed (dangerous)

Precisão de Ponto Flutuante

real padrão é precisão simples (~7 dígitos), frequentemente insuficiente. Use double precision para computação científica. kind(1.0d0) ou selected_real_kind(15) define double. Sempre adicione sufixo a literais: 3.14_dp. Misturar precisões causa truncamento silencioso. Use iso_fortran_env para kinds portáveis.

fortran
! Single precision (default)
real :: x = 3.14159  ! ~7 digits
! Double precision
real(kind=8) :: y = 3.14159d0  ! ~15 digits
! Or use kind parameter
integer, parameter :: dp = kind(1.0d0)
real(dp) :: z = 3.14159_dp
! Always use _dp or d0 for double literals

Was this helpful?