diff --git a/unidad6/fortran/clase2/README.md b/unidad6/fortran/clase2/README.md index 754e99c..8c36363 100644 --- a/unidad6/fortran/clase2/README.md +++ b/unidad6/fortran/clase2/README.md @@ -1,12 +1,376 @@ -# Fortran: Clase 2 +--- +title: "Fortran: calidad, empaquetamiento e interfaz con otros lenguajes" +theme: Luebeck +header-includes: + - \usepackage{setspace} +--- -## Contenidos +# Empaquetamiento -### Primer mitad -- Gestor de paquetes: fpm -- Calidad: - - Linters - - Testing +### Contenido -### Segunda mitad -- Interfaz con Python +- Compilación. +- Makefiles. +- fpm. + +## Compilación + +### Compilación: Pasos básicos + +Fortran es un lenguaje compilado. + +```bash +# Compilación básica +gfortran source.f90 -o main +``` + +### Compilación: Flags importantes + +\small +``` +-Wall -Wextra ; Muchas warnings +-fcheck=all ; checks durante runtime +-ON (N=0, 1, 2, 3, fast); grado de optimización +-funroll-loops ; "desarmar" los loops +-fimplicit-none ; forzar implicit none +-fdefault-real-8 ; +-freal-4-real-8 ; +``` +\normalsize + +\tiny +[https://gcc.gnu.org/onlinedocs/gfortran/Fortran-Dialect-Options.html](https://gcc.gnu.org/onlinedocs/gfortran/Fortran-Dialect-Options.html) +[https://gcc.gnu.org/onlinedocs/gfortran/Code-Gen-Options.html](https://gcc.gnu.org/onlinedocs/gfortran/Code-Gen-Options.html) +\normalsize + +### Makefiles. +Una forma de simplificar el proceso de compilación es mediante +un Makefile. + +- Forma estructurada de definir dependencias. +- Automatización de comandos. +- Cambiar el proceso de compilación con variables. + +### Makefiles: Ejemplo básico + +- Ejemplo + +Algo mejor: + +\tiny +> [https://aoterodelaroza.github.io/devnotes/modern-fortran-makefiles/](https://aoterodelaroza.github.io/devnotes/modern-fortran-makefiles/) +\normalsize + + +## fpm + +### fpm: Fortran Package Manager + +Gestor de paquetes desarrollado por fortran-lang, basado en el gestor cargo +de Rust. + +[https://fpm.fortran-lang.org/](https://fpm.fortran-lang.org/) + +### fpm: Funcionamiento + +![fpm](figs/fpm-1.png) +Fuente: [https://tcevents.chem.uzh.ch/event/14/contributions/68/](https://tcevents.chem.uzh.ch/event/14/contributions/68/) + +### fpm: Estructura de archivos + +![](figs/fpm-2.png) + +### fpm: Configuración + +::: columns + +::: column + +Un paquete de Fortran distribuido mediante fpm se configura mediante un archivo +`fpm.toml` + +Contiene + +- Metadata del proyecto +- Dependencias (separando dependencias de desarrollo de release) +- Librerías de sistema que se utilizan + +::: + +::: column + +![fpm.toml](figs/fpm-3.png) + +::: + +::: + +# Calidad + +## Análisis estático +### Análisis estático: Linters + +- fprettify. +- flinter. +- fortran-linter. + +### Linters: fprettify + +- No es un linter, es un formateador +- Solo hace tres cosas (que es más o menos lo mismo) + - Alinea indentaciones. + - Acomoda whitespace entre operadores. + - Trata de limitar el ancho de filas. Tira un warning si no puede + +```bash +fprettify --whitespace 4 -l 80 file.f90 +``` + +![](figs/ugly.png){height=40%}\ ![](figs/fprettify.png){height=40%} + +### Linters: fortran-linter +Este si es un linter. + +- Sigue siendo más limitado que `flake8` +- Tira un set de warnings y también tiene la posibilidad de modificar el código + +\tiny +```bash +# Mostrar que tan mal estamos +fortran-linter --syntax-only --line-length 80 file.f90 +# Modificar el archivo +fortran-linter --line-length 80 --inplace file.f90 +``` +\normalsize + +Si se modifica el archivo se genera un backup `file.f90.orig` + +### Linters: flinter +Por último, también está `flinter` + +- Es un poco más avanzado. +- Tiene un archivo de configuración para las regex. +- Le da un puntaje al código. + +```bash +pip install flinter +flint lint file.f90 +flint lint folder +flint tree folder #> Está bueno para saber donde atacar con más prioridad +``` + +## Testing +### Testing +Existen varios frameworks de testing: + +- FUnit +- veggies +- test-drive + +Yo uso test-drive así que vamos a ver ese. + +### Testing: test-drive + +Los tests se separan en módulos que luego se colectan desde un runner +principal. + +- Puede agregarse como una dependencia en `fpm`. +- Es un único archivo que también puede agregarse a la fuente. + +### test-drive: test + +\tiny +```fortran +subroutine test_cosa(error) + type(error_type), allocatable, intent(out) :: error + + call check(error, 1 + 2 == 3) + if (allocated(error)) return + + ! equivalent to the above + call check(error, 1 + 2, 3) + if (allocated(error)) return +end subroutine test_cosa +``` +\normalsize + +### test-drive: collector de tests + +\tiny +```fortran +!> Collect all exported unit tests +subroutine collect_suite(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + testsuite = [ & + new_unittest("valid", test_valid), & + new_unittest("invalid", test_invalid, should_fail=.true.) & + ] + +end subroutine collect_suite +``` +\normalsize + +### test-drive: runner + +\tiny +```fortran +program tester + ... + use test_suite, only : collect_suite + ... + type(testsuite_type), allocatable :: testsuites(:) + + stat = 0 + + testsuites = [ & + new_testsuite("suite1", collect_suite), & + ... + ] + + do is = 1, size(testsuites) + write(error_unit, fmt) "Testing:", testsuites(is)%name + call run_testsuite(testsuites(is)%collect, error_unit, stat) + end do + + if (stat > 0) then + write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" + error stop + end if + +end program tester +``` +\normalsize + + +## Coverage + +### Coverage +Se pueden generar archivos de coverage compilando con la flag `--coverage` +(usando `gfortran`). + +- Con `fpm` simplemente es necesario correr: + +> `fpm test --flag "--coverage"` + +- `gcovr`: Es un paquete en python que warpea la herramienta `gcov` y brinda +una funcionalidad similar a `coverage.py` + + - Importante aclararle de donde no incluir coverage. + +\tiny +```bash +gcovr --exclude "build" \ + --exclude "example" \ + --exclude "app" --fail-under-branch 90 +``` +\normalsize + +## Documentación +### Documentación: Ford +La generación de documentación se puede hacer mediante `Ford` + +Genera automáticamente un html a partir del código. + +También permite agregar páginas estáticas, ya sean html o markdown. + +### Ford: Archivo de configuración +`Ford` se maneja con un único archivo de configuración donde se especifican los +detalles de qué documentar y cómo. + +# Interfaces con otros lenguajes + +## Python + +### f2py + +El principal método de interfacear Fortran con Python es mediante `f2py` + +\tiny +- Genera módulos de extensión (extension modules) para Python a partir de código Fortran. +- Utilizar subrutinas, datos en bloques COMMON y variables en módulos de FORTRAN 77 o Fortran 90/95 desde Python. +- Llamar funciones de Python desde Fortran (callbacks). +- Manejar automáticamente la diferencia entre arrays NumPy-contiguos (esto es, C-contiguos) y Fortran-contiguos. +- Fue creado en 1999 por Pearu Peterson mientras era estudiante de doctorado en la Universidad Técnica de Tallin, y en 2005 después de varias versiones estables quedó incluido dentro de NumPy. +\normalsize + +### f2py: Instalación + +- Viene instalado con numpy, así que no hace falta nada +- Hace falta tener instalado un compilador de fortran (preferentemente `gfortran`) + +#### Uso +Se puede usar desde la terminal como: + +```bash +f2py -c -m modulo_python archivo_fortran.f90 +``` + +### f2py: Lo malo + +#### Incompatibilidad +f2py está limitado a las características básicas de Fortran. Objetos (types) +no funcionan. + +\hrule + +\tiny +```fortran +subroutine sub_noanda(x, y) + type(mi_objeto) :: x + real :: y + !cosas +end subroutine +``` +\normalsize + +#### Solución + +Hacer subroutinas/funciones wrappers que tomen la información básica + +### f2py: lo malo + +\tiny +```fortran +subroutine sub_noanda(x, y) + type(mi_objeto), intent(in) :: x + real, intent(out) :: y + !cosas +end subroutine +``` +\normalsize + +\tiny +```fortran +subroutine sub_estasi(n, x, y) + integer :: n + real, intent(in) :: x(n) + real, intent(out) :: y + + type(mi_objeto) :: in_obj + + in_obj%x = x + call sub_noanda(in_obj, y) +end subroutine +``` +\normalsize + +\ + +\Tiny + +> Si el código ya es extremadamente complicado lo mejor es armar un par de +> procedimientos que llamen a lo importante y solo se trabaje con eso, +> compilando previamente el código principal como una librería. + +\normalsize + + +### f2py: Packaging +Muy lindo compilarlo desde la terminal, pero y para automatizarlo en un paquete? + +Es necesario utilizar CMake. + +- Ejemplo gchop. +- Ejemplo pff. diff --git a/unidad6/fortran/clase2/f2py/complicado.py b/unidad6/fortran/clase2/f2py/complicado.py new file mode 100644 index 0000000..b0864e7 --- /dev/null +++ b/unidad6/fortran/clase2/f2py/complicado.py @@ -0,0 +1,7 @@ +import fsub + +x = [1, 2, 3] + +y = fsub.sub(x) + +print(y) diff --git a/unidad6/fortran/clase2/f2py/noanda.f90 b/unidad6/fortran/clase2/f2py/noanda.f90 new file mode 100644 index 0000000..ac5f6f4 --- /dev/null +++ b/unidad6/fortran/clase2/f2py/noanda.f90 @@ -0,0 +1,6 @@ +subroutine sub_alloc(x, y) + real, allocatable, intent(in) :: x(:) + real, intent(out) :: y + + y = sum(x) +end subroutine diff --git a/unidad6/fortran/clase2/f2py/noandaposta.f90 b/unidad6/fortran/clase2/f2py/noandaposta.f90 new file mode 100644 index 0000000..ee74f53 --- /dev/null +++ b/unidad6/fortran/clase2/f2py/noandaposta.f90 @@ -0,0 +1,27 @@ +module noanda + type :: obj + real, allocatable :: att(:) + end type obj + +contains + + subroutine noanda_sub(x, y) + type(obj) :: x + real :: y + + y = sum(x%att) + end subroutine noanda_sub + + subroutine sianda_sub(n, x, y) + integer :: n + real, intent(in) :: x(n) + real, intent(out) :: y + + type(obj) :: in_obj + + in_obj%att = x + + call noanda_sub(in_obj, y) + end subroutine sianda_sub + +end module noanda diff --git a/unidad6/fortran/clase2/f2py/subrutinacomplicada.f90 b/unidad6/fortran/clase2/f2py/subrutinacomplicada.f90 new file mode 100644 index 0000000..400bb31 --- /dev/null +++ b/unidad6/fortran/clase2/f2py/subrutinacomplicada.f90 @@ -0,0 +1,7 @@ +subroutine sub(n, x, y) + integer :: n + real, intent(in) :: x(n) + real, intent(out) :: y + + y = sum(x) +end subroutine sub diff --git a/unidad6/fortran/clase2/figs/fpm-1.png b/unidad6/fortran/clase2/figs/fpm-1.png new file mode 100755 index 0000000..2a3f67f Binary files /dev/null and b/unidad6/fortran/clase2/figs/fpm-1.png differ diff --git a/unidad6/fortran/clase2/figs/fpm-2.png b/unidad6/fortran/clase2/figs/fpm-2.png new file mode 100755 index 0000000..e282bbe Binary files /dev/null and b/unidad6/fortran/clase2/figs/fpm-2.png differ diff --git a/unidad6/fortran/clase2/figs/fpm-3.png b/unidad6/fortran/clase2/figs/fpm-3.png new file mode 100755 index 0000000..a6426a5 Binary files /dev/null and b/unidad6/fortran/clase2/figs/fpm-3.png differ diff --git a/unidad6/fortran/clase2/figs/fprettify.png b/unidad6/fortran/clase2/figs/fprettify.png new file mode 100755 index 0000000..f83f70c Binary files /dev/null and b/unidad6/fortran/clase2/figs/fprettify.png differ diff --git a/unidad6/fortran/clase2/figs/ugly.png b/unidad6/fortran/clase2/figs/ugly.png new file mode 100755 index 0000000..7859a26 Binary files /dev/null and b/unidad6/fortran/clase2/figs/ugly.png differ diff --git a/unidad6/fortran/clase2/linters/bad.f90 b/unidad6/fortran/clase2/linters/bad.f90 new file mode 100644 index 0000000..ba984e4 --- /dev/null +++ b/unidad6/fortran/clase2/linters/bad.f90 @@ -0,0 +1,17 @@ +program main + !comment + implicit none + real*8 x + real*4 y(3) + integer i + + x = 2.3 + y =[2, 3, 1] + + x = x + x + x + 2 + 3 + 2 + 1 + 2 + 4 + 2341 + x + x + 123 + 4 + 5 + 2 + x + 1 + 3 + 1 + 2 + 5 + 2 + 10 + + do i = 1 , 3 + y( i) = 2 * i ** 2 + end do + +end program main diff --git a/unidad6/fortran/clase2/linters/show.sh b/unidad6/fortran/clase2/linters/show.sh new file mode 100644 index 0000000..f759645 --- /dev/null +++ b/unidad6/fortran/clase2/linters/show.sh @@ -0,0 +1 @@ +fprettify -s -w 4 -l 80 bad.f90 | bat --language "Fortran (Modern)" diff --git a/unidad6/fortran/clase2/makefiles/Makefile b/unidad6/fortran/clase2/makefiles/Makefile new file mode 100644 index 0000000..4bfabba --- /dev/null +++ b/unidad6/fortran/clase2/makefiles/Makefile @@ -0,0 +1,39 @@ +# compiler +FC = gfortran + +# compiler flags +# standard +CFLAGS = -std=f2008ts +# warning flags +CFLAGS += -Wall +# debugging options +CFLAGS += -fPIC -fcheck=all -fbacktrace -fmax-errors=3 +# GDB flag +CFLAGS += -g + +# lib flags +# LFLAGS = -pthread -I/usr/lib/x86_64-linux-gnu/openmpi/lib -L/usr/lib/x86_64-linux-gnu/openmpi/lib -lmpi_usempif08 -lmpi_usempi_ignore_tkr -lmpi_mpifh -lmpi + +# source files +SRCS = mod_1 main +OBJS = $(SRCS:=.o) + +# executable +MAIN = main + +# compile project +all : $(MAIN) + @echo Ya compiló todo + +$(MAIN) : $(OBJS) + @echo Compilando ejecutable + $(FC) $(CFLAGS) -o $(MAIN) $(OBJS) $(LFLAGS) + +.SUFFIXES : .o .f90 + +.f90.o : + @echo Compilando $< + $(FC) $(CFLAGS) $(LFLAGS) -c $< + +clean : + $(RM) *.o *.so *.mod $(MAIN) diff --git a/unidad6/fortran/clase2/makefiles/main.f90 b/unidad6/fortran/clase2/makefiles/main.f90 new file mode 100644 index 0000000..f1a02b1 --- /dev/null +++ b/unidad6/fortran/clase2/makefiles/main.f90 @@ -0,0 +1,13 @@ +program main + use mod_1, only: sub + + real :: x + real :: y + + x = 2 + y = 3 + + call sub(x, y) + + print *, x, y +end program main diff --git a/unidad6/fortran/clase2/makefiles/mod_1.f90 b/unidad6/fortran/clase2/makefiles/mod_1.f90 new file mode 100644 index 0000000..f5452ff --- /dev/null +++ b/unidad6/fortran/clase2/makefiles/mod_1.f90 @@ -0,0 +1,9 @@ +module mod_1 + contains + subroutine sub(x, y) + real, intent(in) :: x + real, intent(out) :: y + + y = 2 * x + end subroutine +end module diff --git a/unidad6/fortran/clase2/slides.pdf b/unidad6/fortran/clase2/slides.pdf new file mode 100644 index 0000000..87f6fa1 Binary files /dev/null and b/unidad6/fortran/clase2/slides.pdf differ