Matrizes#

Arrays are a central object in Fortran. The creation of dynamic sized arrays is discussed in the allocatable arrays section.

Para passar matrizes para processos há quatro formas disponíveis

  1. matrizes assumed-shape

  2. matrizes assumend-rank

  3. matrizes explicit-shape

  4. matrizes assumed-size

O modo indicado para passar matrizes para processos é por matrizes assumed-shape

subroutine f(r)
  real(dp), intent(out) :: r(:)
  integer :: n, i
  n = size(r)
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine f

Matrizes de dimensões maiores podem ser passadas de modo similar.

subroutine g(A)
  real(dp), intent(in) :: A(:, :)
  ...
end subroutine g

A matriz é simplesmente passada

real(dp) :: r(5)
call f(r)

Nesse caso nenhuma cópia de matriz é feita, o que tem a vantagem de que o formato e tamanho da informação é automaticamente passada para frente e checada em tempo de compilação e opcionalmente em tempo de execução. Similarmente, caminhos de matrizes podem ser passada sem requisição de cópia da matriz mas como um descritor assumed-shape:

real(dp) :: r(10)
call f(r(1:10:2))
call f(r(2:10:2))

Essa deve ser sempre o meio padrão de passar matrizes para dentro e para fora de subrotinas. Evite passar matriz como pedaços inteiros, uma vez que ofusca a intenção real do código:

real(dp) :: r(10)
call f(r(:))

No caso de precisar matrizes genéricas para um procedimento, a funcionalidade assumed-rank introduzida na especificação Fortran 2018 pode ser usada

subroutine h(r)
  real(dp), intent(in) :: r(..)
  select rank(r)
  rank(1)
  ! ...
  rank(2)
  ! ...
  end select
end subroutine h

A atual classificação pode ser consultada em tempo de execução usando o construto select rank. Isso facilmente permite criar funções mais genéricas que precisam lidar com diferentes classificações de matrizes.

Matrizes explicit-shape pode ser útil para retornar dados para funções. A maioria de suas funcionalidades pode ser fornecidas por assumed-shape e matrizes assumed-rank, mas encontram uso frequente para servir de interface com C ou em processos Fortran legado, portanto serão discutidos brevemente aqui.

Para usar matrizes explicit-shape, a dimensão tem que ser passada explicitamente como um argumento fictício como no exemplo abaixo

subroutine f(n, r)
  integer, intent(in) :: n
  real(dp), intent(out) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end subroutine

Para matrizes de altas dimensões índices adicionais tem que ser passados.

subroutine g(m, n, A)
  integer, intent(in) :: m, n
  real(dp), intent(in) :: A(m, n)
  ...
end subroutine

As rotinas podem ser invocadas por

real(dp) :: r(5), s(3, 4)
call f(size(r), r)
call g(size(s, 1), size(s, 2), s)

Note que o formato não é checado, assim o código legal à seguir potencialmente produzirá resultados incorretos:

real(dp) :: s(3, 4)
call g(size(s), 1, s)  ! s(12, 1) in g
call g(size(s, 2), size(s, 1), s)  ! s(4, 3) in g

Nesse caso a configuração da memória é preservada mas o formato será mudado. Além disso, matrizes explicit-shape requerem memória contínua e irão criar matrizes temporárias no caso de caminhos de array não-contínuos forem passados.

Para retornar uma matriz vinda de uma função com uso explicit-shape

function f(n) result(r)
  integer, intent(in) :: n
  real(dp) :: r(n)
  integer :: i
  do i = 1, n
    r(i) = 1.0_dp / i**2
  end do
end function

Finalmente, há matrizes assumed-size, que provém o menor tempo de compilação e checagem em tempo de execução e pode ser encontrado frequentemente em código legado. Eles devem ser evitados em favor de matrizes assumed-shape ou assumed-rank. Um argumento fictício de uma matriz assumed-size é identificada por um asterisco como última dimensão, isso desativa o uso dessa matriz com muitas funções intrínsecas, como size ou shape.

Para checar o tamanho e formato correto de uma matriz assumed-shape as funções intrínsecas size e shape podem ser usadas para consultas essas propriedades

if (size(r) /= 4) error stop "Incorrect size of 'r'"
if (any(shape(r) /= [2, 2])) error stop "Incorrect shape of 'r'"

Note que size retorna o tamanho total de todas as dimensões. Para obter o formato de uma dimensão específica adicione essa dimensão como segundo argumento da função.

Matrizes podem ser inicializadas usando construtor para matrizes

integer :: r(5)
r = [1, 2, 3, 4, 5]

O construtor de matriz pode ser anotado com o tipo da matriz construída

real(dp) :: r(5)
r = [real(dp) :: 1, 2, 3, 4, 5]

Laços implícitos «do» podem também ser usados dentro de um construtor de matriz

integer :: i
real(dp) :: r(5)
r = [(real(i**2, dp), i = 1, size(r))]

Para que a matriz inicie com índice diferente de 1, faça:

subroutine print_eigenvalues(kappa_min, lam)
  integer, intent(in) :: kappa_min
  real(dp), intent(in) :: lam(kappa_min:)

  integer :: kappa
  do kappa = kappa_min, ubound(lam, 1)
    print *, kappa, lam(kappa)
  end do
end subroutine print_eigenvalues