domingo, 3 de novembro de 2019

Building Python OpenCV Contrib with Conda on Linux/Ubuntu

Building OpenCV form the source code can be a pain in the ass when working with Conda environment. I haven't found a working conda package, and the few tutorials i found didn't workout of the box, most likely because of different versions of anaconda, ubuntu and environment.

This post is based on these tutorials,which have enough information to get the build working:

https://alexsm.com/building-opencv-with-conda/ https://docs.opencv.org/master/d2/de6/tutorial_py_setup_in_ubuntu.html


Dependencies

First, install the dependencies need for opencv (more information on the opencv tutotrial above):

>sudo apt-get install cmake
>sudo apt-get install gcc g++
>sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev
>sudo apt-get install libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev
>sudo apt-get install libgtk2.0-dev
>sudo apt-get install libgtk-3-dev


Creating conda environment


Create and enable a new conda environment with the wanted python version and numpy:

>conda create -n opencv_build python=3.6 numpy
>conda activate opencv_build


Get source code

Make a folder and clone the opencv and contrib git:

>mkdir opencv_install
>cd opencv_install
>cd opencv_install
>git clone https://github.com/opencv/opencv.git
>git clone https://github.com/opencv/opencv_contrib.git

If you want some specific version do the checkout, otherwise skip the commands bellow:

>cd opencv; git checkout 3.4.1 >cd ../opencv_contrib; git checkout 3.4.1
>cd .. # in opencv_install


Check paths inside conda


It is good to check the correct environment path before building. With the environment activated:

>conda info

You should see something like this:

     active environment : opencv_build
    active env location : /home/jairo/anaconda3/envs/opencv_build
            shell level : 1
       user config file : /home/jairo/.condarc
 populated config files : /home/jairo/.condarc
          conda version : 4.6.11
    conda-build version : 3.17.8
         python version : 3.7.3.final.0
       base environment : /home/jairo/anaconda3  (writable)
           channel URLs : https://repo.anaconda.com/pkgs/main/linux-64
                          https://repo.anaconda.com/pkgs/main/noarch
                          https://repo.anaconda.com/pkgs/free/linux-64
                          https://repo.anaconda.com/pkgs/free/noarch
                          https://repo.anaconda.com/pkgs/r/linux-64
                          https://repo.anaconda.com/pkgs/r/noarch
          package cache : /home/jairo/anaconda3/pkgs
                          /home/jairo/.conda/pkgs
       envs directories : /home/jairo/anaconda3/envs
                          /home/jairo/.conda/envs
               platform : linux-64
             user-agent : conda/4.6.11 requests/2.21.0 CPython/3.7.3 Linux/4.15.0-66-generic ubuntu/16.04.6 glibc/2.23
                UID:GID : 1000:1000
             netrc file : None
           offline mode : False

You need to take note of the active env location. Is this case is /home/jairo/anaconda3/envs/opencv_build.

Check the path for python executable in the active enviroment::

>which python

/home/jairo/anaconda3/envs/opencv_build/bin/python

Take note of these 2 paths.


Create make file

Create the xcmake.sh file in the opencv_install folder. Remember to update the CONDA_ENV_PATH and CONDA_PYTHON_PATH with the information above:

CONDA_ENV_PATH=/home/jairo/anaconda3/envs/opencv_build
CONDA_PYTHON_PATH=/home/jairo/anaconda3/envs/opencv_build/bin/python
WHERE_OPENCV=../opencv
WHERE_OPENCV_CONTRIB=../opencv_contrib
cmake -D CMAKE_BUILD_TYPE=RELEASE \
        -D CMAKE_INSTALL_PREFIX=/usr/local \
        -D PYTHON3_EXECUTABLE=$CONDA_PYTHON_PATH \
        -D INSTALL_C_EXAMPLES=ON \
        -D INSTALL_PYTHON_EXAMPLES=ON \
        -D OPENCV_EXTRA_MODULES_PATH=$WHERE_OPENCV_CONTRIB/modules \
        -D BUILD_EXAMPLES=ON $WHERE_OPENCV


Prepare for building


Create the folder build inside the opencv_install:

>mkdir build
>cd build
>../xcmake.sh

You should get something like this if everything is correct:

--   Python 2:
--     Interpreter:                 /usr/bin/python2.7 (ver 2.7.12)
--     Libraries:                   /usr/lib/x86_64-linux-gnu/libpython2.7.so (ver 2.7.12)
--     numpy:                       /home/jairo/.local/lib/python2.7/site-packages/numpy/core/include (ver 1.16.5)
--     install path:                lib/python2.7/dist-packages/cv2/python-2.7
--
--   Python 3:
--     Interpreter:                 /home/jairo/anaconda3/envs/opencv_build/bin/python (ver 3.6.9)
--     Libraries:                   /home/jairo/anaconda3/envs/opencv_build/lib/libpython3.6m.so (ver 3.6.9)
--     numpy:                       /home/jairo/.local/lib/python3.6/site-packages/numpy/core/include (ver 1.17.3)
--     install path:                lib/python3.6/site-packages/cv2/python-3.6
--
--   Python (for build):            /home/jairo/anaconda3/envs/opencv_build/bin/python
--
--   Java:                         
--     ant:                         NO
--     JNI:                         NO
--     Java wrappers:               NO
--     Java tests:                  NO
--
--   Install to:                    /home/jairo/anaconda3/envs/opencv_build
-- -----------------------------------------------------------------

You want to make sure the Python(for build) and Install to folders are correct and inside your conda environment.


Build it

If everything is fine, make it. In the build folder:

>make -j4

-j4 is a flag for paralel processing. You can remove it if you see any problem.


Install it



>sudo make install
>sudo ldconfig


Test it


Run python and type:


>python
>import cv2
>cv2.__version__

You should see the opencv version installed.
In case of problem you need to check the paths, usually the problem is wrong paths. Check the paths where python is looking for modules (always with the environment activated):

Run python and type:

>python
>import sys
>print ('\n'.join(sys.path))

/home/jairo/anaconda3/envs/opencv_build/lib/python36.zip
/home/jairo/anaconda3/envs/opencv_build/lib/python3.6
/home/jairo/anaconda3/envs/opencv_build/lib/python3.6/lib-dynload
/home/jairo/.local/lib/python3.6/site-packages
/home/jairo/anaconda3/envs/opencv_build/lib/python3.6/site-packages

This will show the folders python is looking for the modules. If they are different from the specified on the build process the installation will fail.


Using the binary in another environment


To use this binary in other environment you should copy the cv2 folder to the respective folder. In this case the folder is located at /home/jairo/anaconda3/envs/opencv_build/lib/python3.6/sites-packages/cv2, and you should copy it to the new environment (change the opencv_build to the name of the new environment already create).

quarta-feira, 25 de setembro de 2019

Rede neural para Mega-sena em Colab + Keras

Como tudo começou

Resolvi aprender um pouco sobre implementação de redes neurais, e um bom projeto para começar é montar uma rede para prever os resultados da mega-sena. Os mais racionais vão dizer que isso é perda de tempo pois, o sorteio sendo aleatório, não é possível a rede aprender nenhum comportamento ou padrão. Bom, isso não importa, só de conseguir montar a rede e não gerar erros na execução já está de bom tamanho para começar.

Começei como acho que todo mundo começa: com linux e python. Baixei os pacotes com pip, e tudo ficou uma merda, com zilhões de versões diferentes, um monte dependencia incompreenssível e sem solução do tipo pacote A precisa do pacote B, que precisa do pacote C que não é compativel com o pacote A. O começo é difícil.

Ai conheci  Anaconda. Isso facilita muito a vida de quem esta começando. Vem tudo bunitinho e a maioria das coisas funciona de primeira. O Jupyter notebook vem instalado e funcionando, assim como os framework de inteligencia artificial como o tensorflow funcionam com facilidade. Começo a rodar diferentes exemplos e percebo que a minha máquina é muito ruim, e serve só para fazer fofoca do facebook e ver youtuber mané. Para rede neural falta memória, falta velocidade e qualquer aplicação boba leva muito tempo para processar.

Encontrando o Colab

Então, por acaso, no meu caminho, aparece o Colab, uma plataforma do Google. As máquinas virtuais alí tem mais memoria RAM que todos meus computadores juntos, e placas GPUs que preciso vender o carro para comprar uma. E tudo de graça. Fora que a conexão de internet dessas máquinas é muito rápida, assim posso baixar qualquer coisa da internet rapidamente na máquina virtual. Beleza, posso continuar com meu computador vagabundo e fazer minhas redes no Colab. Meu computador virou um mero terminal ;-)

O link para o Colab é esse aqui:

https://colab.research.google.com/


E Keras?

Junto com o Colab, descobri o Keras. De inicio achei que Keras era coisa de vagabundo preguiçoso que não queria se importar com os detalhes mais profundos de programação da rede neurais. Coisa de chefe: só fala oque quer, e os outros que se virem para conseguir fazer o negócio funcionar. E era isso mesmo!!! Keras é fácil, funciona e remove boa parte da complicação que vc não quer ter enquanto aprende algo novo. Seria o similar do seguinte: Python é muito facil, e C tem um monte de detalhes que vc precisa saber para utilizar. Keras é o Python, e tensorflow é o C. E Keras já vem instalado dentro da máquino do Colab, dentro do pacote do tensorflow. Nada mais fácil que isso.

E a Mega? Eu quero grana...

Como aprendizado montei um tutorial rápido de como utilizar o Colab, baixar arquivo de resultados da mega-sena no site da Caixa, processar e testar diferentes redes neurais em Keras. O arquivo esta no link abaixo, e pode ser aberto no Colab, procure por um link na página

https://github.com/JairoRotava/Colab_studies/blob/master/megasena_keras.ipynb

Mas não vá acreditar que isso vai em algum momento acertar os resultados da mega. Esse estudo também pode conter erros teóricos, não fazer sentido. Mas é uma boa forma de começar e aprender a sintaxe dos comandos em python e keras.

Boa sorte


sábado, 21 de setembro de 2019

Controle remoto para Linux utilizando um Arduino

Bateu um tédio, pouco serviço pra fazer, e oque precisa ser feito é muito chato. Resolvi fazer um projeto que estava muito tempo na fila: utilizar um controle remoto para controlar um computador que uso no rádio e tv. Da muita preguiça chato ter ir até o computador para mudar de música, pausar o filme no netflix, ou outra coisa qualquer.

O projeto é simples, utiliza um arduino UNO, um controle remoto que estava sobrando - da propria TV, uma panasonic, e um receptor de IR manjado ( TSOP4838 ). O arduino interpreta o sinal vindo do TSOP4838, comunica pelo USB com o computador, e um programa em python converte isso para comandos em Linux.


Receptor IR - Arduino

O hardware não tem nada demais, é o receptor de IR espetado no arduino. Coloquei tudo dentro de um copo do starbuck para não ficar dando curto com o resto dos equipamentos que estão no rack da tv.

O programa utilizado no Arduino é baseado num dos exemplos disponíves com a biblioteca IRremote. Foi adicionado código para lidar coma alimentação do sensor (pino 12 = GND e pino 13 = VCC), e tudo oque era descenessário foi retirado. No meu caso tive sorte do protocolo do controle remoto que eu tinha ser suportado pela biblioteca. No meu caso o protocolo utilizado é o PANASONIC.

#include <IRremote.h>

int RECV_PIN = 11;
int RECV_VCC_PIN = 13;
int RECV_GND_PIN = 12;

IRrecv irrecv(RECV_PIN);

decode_results results;

void setup()
{
  digitalWrite(RECV_GND_PIN, LOW);
  digitalWrite(RECV_VCC_PIN, HIGH);

  pinMode(RECV_GND_PIN, OUTPUT);
  pinMode(RECV_VCC_PIN, OUTPUT);
  
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
}

void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    irrecv.resume(); // Receive the next value
}

Esse programa retorna um número hexadecimal para cada tecla do controle. Mapeando a função com cada código retornado é feito um mapa do controle remoto. Neste controle da Panasonic algumas teclas mandam o mesmo código 3 vezes, geralmente as teclas que vc não que que fique repetindo, como play/pause e outras. As demais teclas com volume ficam mandando o código periodicamente enquanto o botão é apertado.



Interpretador em python

Programa python para recepção dos dados. Utiliza biblioteca pySerial e a ferramenta xdotool. Utilizo o python3. O programa acabou ficando mais complicado que o previsto inicialmente porque o receptor pega um pouco de ruído e envia alguns códigos incorretos. O software também gera uma movimentação não linear para o ponteiro do mouse, ou seja, começa devagar e vai movendo cada vez mais rápido, isso torna o controle pelo controle remoto mais fluido. O código esta bastante comentado.

# Recepção de control remoto
import time
import serial 
import os

VERSION = "0.1"
SERIAL_PORT = "/dev/ttyACM0"
BAUD_RATE = 9600

REPEATABLE = True
NOT_REPEATABLE = False
UNKNOW_COMMAND = '??'
NO_COMMAND = ''
NO_DESCRIPTION = ''
NO_IR_CODE = -1
#Lista de relacao entre codigo IR e comando linux. Formato [Codigo IR, Comando Linux, Descricao, Pode Repetir?]
command_list = [
 ['E17AF807','amixer -q -D pulse sset Master toggle','mute',NOT_REPEATABLE],
 ['BE3BB37','xdotool key XF86AudioRaiseVolume','aumenta volume',REPEATABLE],
 ['D4D9F2A7','xdotool key XF86AudioLowerVolume','baixa volume',REPEATABLE],
 ['21035431','xdotool mousemove_relative -- %MOUSE_MOVE 0','seta para direita',REPEATABLE],
 ['983AB4C1','xdotool mousemove_relative -- -%MOUSE_MOVE 0','seta para esquerda',REPEATABLE],
 ['C20370A1','xdotool mousemove_relative -- 0 -%MOUSE_MOVE','seta para cima',REPEATABLE],
 ['81930A09','xdotool mousemove_relative -- 0 %MOUSE_MOVE','seta para baixo',REPEATABLE],
 ['BB0ED9E1','xdotool click 1; sleep 0.2','ok',NOT_REPEATABLE],
 ['AA7B2167','xdotool key XF86AudioPlay','play',NOT_REPEATABLE],
 ['2E8C2A89','xdotool key XF86AudioNext','fwd',NOT_REPEATABLE],
 ['1F6E01C9','xdotool key XF86AudioPrev','rew',NOT_REPEATABLE],
 ['1D15F4D7','xdotool key XF86AudioStop','stop',NOT_REPEATABLE],
 ['406A954D','systemctl poweroff -i','desliga',NOT_REPEATABLE],
 ['E17A24DB','xset dpms force off; sleep 1','apaga tv',NOT_REPEATABLE],
 ['E17A18E7','xdotool key C; sleep 1','apaga tv',NOT_REPEATABLE],
 ['E17A7887','xdotool getwindowfocus getwindowname;sleep 1','pega nome janela em foco',NOT_REPEATABLE],
 ['A26409C9','pkill --oldest chrome; google-chrome-stable deezer.com/br/ & sleep 1','Abre pagina deezer',NOT_REPEATABLE],
 ['240C9161','pkill --oldest chrome; google-chrome-stable www.netflix.com & sleep 1','Abre pagina netflix',NOT_REPEATABLE],
 ['68E839F1','pkill --oldest chrome; google-chrome-stable www.youtube.com & sleep 1','Abre pagina youtube',NOT_REPEATABLE],
 ['ABBE1086','xdotool key Escape','esc',NOT_REPEATABLE], 
 ['68E69B7F','xdotool click 4','scrool up',REPEATABLE],
 ['223C02A7','xdotool click 5','scrool down',REPEATABLE]
]

#Codigo q corresponde a tecla repetida do IR. Tecla ainda esta apertada
button_down = 'FFFFFFFF'
#inicializa para evitar possivel chamada dessa variavel sem inicilizacao
linux_command = NO_COMMAND
last_command = NO_COMMAND
description = NO_DESCRIPTION
allow_repeat = NOT_REPEATABLE
repetition_count=0
last_ir_code = NO_IR_CODE
ir_code = NO_IR_CODE
#tempo max para interpretar que a tecla foi solta
IR_TIMEOUT = 0.3
#sinaliza q tecla esta pressionada desde ultima vez
pressed = False
#Maximo deslocamento do mouse
max_mouse = 35

# Hello message
print("Controle Remoto Arduino/Linux TabaJairo para controle Panasonic")
print("Versao: " + VERSION);


# Tenta conectar a serial
serial_connected = False
print("Procurando porta serial...")
while (not serial_connected):
 try:
  ser = serial.Serial(port=SERIAL_PORT,baudrate=BAUD_RATE, timeout=1)
  serial_connected = True
  print("yeah...achei")
 except:
  print("buuu...ainda não achei")
  time.sleep(1)
  

#Loop infinito - ctrl+c para terminar
while 1 :
 #Recebe ir code. Aguarda enquanto nao tem nada na serial
 last_ir_code = ir_code
 t = 0
 while ser.inWaiting() == 0:
  time.sleep(0.1)
  t = t + 0.1
  if t > IR_TIMEOUT:
   t=0
   #limpa ultimo codigo pois estourou timeout
   last_ir_code = NO_IR_CODE
 out = ser.readline().rstrip()
 ir_code = out.decode('utf-8')


 #Procura comando na lista
 #Coloca NO_COMMAND em linux_command para indicar q nao foi encontrado
 linux_command = NO_COMMAND
 for command in command_list:
  if ir_code == command[0]:
   linux_command = command[1]
   description = command[2]
   allow_repeat = command[3]

 # Se teve timeout na recepcao do codigo IR entao o ultimo comando eh vazio
 if last_ir_code == NO_IR_CODE:
  last_command = NO_COMMAND
  
 pressed = False
 #Verifica se eh comando repetido (tecla pressionada)
 if (linux_command == last_command) and (last_ir_code != NO_IR_CODE):
  pressed = True
  
 # Aqui tem um truque para melhor responsividade: se o comando atual nao eh reconhecido e 
 #  nao teve timeout na recepcap de IR supoe que houve problema na recepcao do codigo:
 #  o codigo eh diferente mas corresponde a ultima tecla pressionada
 if (linux_command == NO_COMMAND) and (last_ir_code != NO_IR_CODE) and (last_command != NO_COMMAND):
  pressed = True
  linux_command = last_command
  print("Codigo IR invalido! Provavelmente eh o ultimo comando, vamos repetir..."); 
 
 # Incrementa contador de botao pressionado
 if pressed:
  repetition_count+=1
 else:
  repetition_count=0

 # Atualiza last command. A posicao da linha eh importante para o codigo funcionar  
 last_command = linux_command
  
 #subsitui wildcard %MOUSE_MOVE do movimento do mouse para fazer aceleracao
 mouse_move = repetition_count*repetition_count+1;
 if mouse_move > max_mouse:
  mouse_move = max_mouse
 linux_command = linux_command.replace("%MOUSE_MOVE",str(mouse_move)) 

 #executa comando se for conhecido. Verifica se ele pode ser repetido ou nao.
 # Nao queremos o play/pause repetindo, nem o mute por exemplo
 if linux_command != NO_COMMAND:
  if repetition_count == 0 or  (repetition_count > 0 and allow_repeat):
   print(ir_code + ' ' +  str(repetition_count) + ' --> ' + description + ' --> ' + linux_command); 
   os.system(linux_command)
  else:
   print(ir_code + ' ' +  str(repetition_count) + ' --> ' + description + ' --> nao pode repetir');
 else:
  print(ir_code + ' ' +  str(repetition_count) + ' --> buuu, nao conheco esse comando ');


O programa monitor a porta serial /dev/ttyACM0 na qual o arduino esta conectada. Sempre que recebe um comando serial ele dispara o correspondente no linux. A ferramente xdotool permite simular o mouse e teclado, e com isso é possivel enviar comandos como aumentar/baixa volume pelas teclas especiais de volume, play/pause etc. Também é possível utilizar diretamente outros comandos do linux, como por exemplo desligar a tela ou fazer shutdown da máquina.
No exemplo acima estão comandos diretos no linux, comandos que simulam teclado ( xdotool key ... ) e comandos que simulam mouse (xdotool mousemove_relative e click ) .


Na figura acima esta uma amostra do programa recebendo os códugos. O primeiro campo é o código recebido pela IR, depois o numero de repetições, uma descrição do código, e o comando executado no linux.
Para algumas teclas do controle é simulado o uso de teclas de multimedia (volume, play, stop por exemplo), barra de rolagem do mouse, ou é executado algum comando específico, como desligar o sistema.

Ajuste do navegador

Foi encontrado um problema inesperado durante esse projeto: os navegadores de internet de forma geral não aceitam as teclas de acesso rápido multimidia (play, pause, stop...). Para fazer isso funcionar é necessário instalar um plugin no navegador. Não tive sucesso ao utilizar isso no firefox - o plugin não funcionava. No chrome o plugin funcionou corretamente e agora consigo controlar sites de musica, youtube e netflix com o controle remoto.

Falta deixar claro que estamos simulando tecla de atalho multimidia, que é possivel encontra em alguns teclado reais. Existe o padrão XF86 que são as telcas multimidia. Existe diferentes funções que podem ser acessadas com essas teclas.

Recepção Arduino

A comunicação com o Arduino é feita pela USB. Se o sistema não estiver encontrado o arduino verifique o nome da porta que o arduino esta instalado e corrija no programa de recepção em python. Também verifique se o acesso a USB esta liberado. Falta de acesso é um problema comum com o Arduino, existe bastane informação sobre isso na internet.


Como compilar SWAT 2018 rev670 no Gfortran Linux

Instalação do GFortran

Para instalar o GFortran no Ubuntu utilize os comandos:

sudo apt update
sudo apt install gfortran

Modificações do código original

Pode ser necessário alterar os arquivos de código fonte para atendenrem o padrão do fortran. O compilador da intel é mais premissivo quanto ao padrão do arrquivo, enquanto o gfortran é mais chato, requer o código no padrão fixed ou free from, e não lida bem com a mistura dos dois dentro do mesmo arquivo. Os códigos mais recentes do SWAT já tem corrigido essa padronização, mas os antigos podem precisar de bastante modificação.
O SWAT é compilado originalmente com ifortran (intel), e o compilador da intel é mais permissivo que o compilador GFortran para questões de compatibilidade de formato de código antigo (.f) e novo (.f90) . O fortran passou por várias revisões com o tempo, e o código de swat tem arquivos com padrões antigos e novos, as vezes misturados dentro de um mesmo arquivo. O codigo original do SWAT não compila com GFortran, pois ele é mais chato com relação a formatação dos arquivos. Além disso, no SWAT também são utilizadas estruturas que são diferentes das recomendadas pelo padrão fortran, mas que o compilador da intel aceita, pois facilitam o uso da linguangem.
Modificações necessárias para compilar:
  1. Comentar a linha !! include 'modparm.f' no main.f
  2. É necessário alterar o comando "use" em alguns arquivos, para "use parm, except_this_one => tair", onde tair deve ser substituido pelo nome da função do arquivo com erro.
O erro ao compilar caso o comando "use" não seja corrigido é este:
       use parm
         1
Error: ‘regres’ of module ‘parm’, imported at (1), is also the name of the current program unit 
 Os arquivos que precisam ser corrigidos são:

  1. tair.f
  2. layersplit.f
  3. vbl.f
  4. ndenit.f
  5. atri.f
  6. rsedaa.f
  7. regres.h
  8. HQDAV.f90
Para corrigir altere a linha com “use parm” para “use parm, except_this_one => tair”. O nome na exceção deve ser ser o nome da função atual.
Essa modificações devem tornar o código compativel com o GFortran, sem atrapalhar o IFortran. Os os compiladores devem funcionar corretamente.


Arquivo makefile

Foi criado um makefile para facilitar a compilação do sistema. O makefile verifica se o módulo esta disponível e compila, e depois constroi o programa. A versão release e debug estão em diretórios separados. Para compilar executar: make all, make release ou make debug. Make clean limpa os builds antigos e make prep cria os diretórios.
Baixe o arquivo do link disponível no final do post e copie no mesmo diretório do código fonte.


Compilando

Para compilar execute o comando "make all" no diretório do código fonte. Isso vai criar as pastas dos executáveis. O binario está é salvo na pasta /release com o nome swat


Flags do compilador

Aqui é apresentado uma pequena descrição das flags utilizadas no compilador. Voce não precisa entender oque acontece aqui. Elas são apresentadas somente como curiosidade e para ajudar e resolver algum tipo de problema.

Para compilador com sucesso é necessário utilizar algumas flags do compilador. Elas são:
  1. release: -c -O3 -static -ffixed-line-length-none -ffree-line-length-none -std=gnu -ffpe-trap=invalid,zero,overflow -fno-automatic
  2. debug: -c -O0 -static -ffixed-line-length-none -ffree-line-length-none -std=gnu -ffpe-trap=invalid,zero,overflow -fcheck=all -fbacktrace -fno-automatic
Uma breve descrição das flags são:
  1. -O0 ou -O3: nível de otimização do código.
  2. -static: compila bibliotecas estaticamente no código.
  3. -ffixed-line-length-none: informa ao compilador para ler a linha inteira dos arquivos de código que estão no formato fixo. Pode ser necessário retirar essa flag caso o compilador apresente erro ao ler os arquivos.
  4. -ffree-line-length-none: informa ao compilador para ler a linha inteira dos arquivos de código que estão no formato livre. Pode ser necessário retirar essa flag caso o compilador apresente erro ao ler os arquivos.
  5. std=gnu: padrão do fortran utilizado.
  6. -ffpe-trap=invalid,zero,overflow: ativa exceções para capturar erro de overflow, número inválido e divisão por zero. O erro de underflow não é capturado, o número é aproximado para zero.
  7. -fno-automatic: trata variáveis locais como estáticas, ou seja, elas são salvas e mantém o mesmo valor entre as chamadas da função. O fortran inicialmente tratava as variáveis locais como estáticas, mas nas versões mais novas elas não mantém mais o valor entre chamadas. O código swat assume que as variáveis mantém o valor entre as chamadas, por iso precisa dessa flag.
  8. -fcheck=all: verifica por todos erros possíveis durante execução. Pode deixar o programa mais lento. Utilizado somente no debug.
  9. -fbacktrace: faz um rastreamento para a função que gera erro. Utilizado somente no debug.


Sobre os formatos dos arquivos fortran

Os código fontes do fortran seguiam o padrão fixed form (.f), e os mais atuais seguem o free form (.f90). O código do SWAT parece misturar os dois formatos, oque pode gerar erros na compilação com o gfortran.
Os arquivos do SWAT parecem ser todos escritos em fixed form, mas alguns arquivos utilizam a quebra de linha do formato free form. O compilador informa um erro quando isso acontece.
O erro mais comum nos códigos do SWAT é utilizar a extensão .f, e utilizar para quebra de linha o caracter & (que é do padrão .f90). Isso gera um erro. A solução é alterar a extensão do arquivo para .f90, ou corrigir a quebra de linha.
Como regra geral: se o fim da linha tiver um &, a extensão do arquivo deve ser .f90. Se a linha de baixo tiver o & ou outro caracter na coluna 6 então deve ser .f.
Extensão .f → fixed form: primeira cinco colunas em branco. A continuação de linha é incluir algum caracter na coluna 6 da próxima linha. Esse é o primeiro padrão do fortran – F77. F77 le somente até coluna 72 por padrão.
Extensão .f90 → free form: é a versão mais moderna. A continuação de linha é com & no fim a linha atual e no inicio da segunda linha.
É recomendável utilizar .f90.
Alguns arquivos de algumas versões do SWAT tem as duas formas de quebra de linha no mesmo arquivo, oque torna impossível compilar sem corrigir o código para um dos dois padrões definidos.


Link para arquivos

Os arquivos originais do SWAT podem ser obtidos nesse link: https://swat.tamu.edu/software/swat-executables/

Os arquivos já modificados para GFortran e com o arquivo makefile podem ser obtidos no github: https://github.com/JairoRotava/SWAT-2018-GFortran

Vale a pena verificar se os resultados gerados com o binário compilado pelo GFortran é o mesmo que o gerado pelo binário gerado pelo fornecido no site.