Conceptos básicos

Nextflow enables scalable and reproducible scientific workflows using software containers. It allows the adaptation of pipelines written in the most common scripting languages.
Its fluent DSL simplifies the implementation and the deployment of complex parallel and reactive workflows on clouds and clusters.

En el artículo anterior vimos una introducción a Nextflow. En este vamos a profundizar un poco más en los conceptos básicos de este lenguaje

Pipeline

Podemos definir el pipeline como el conjunto de tareas que tiene que realizar Nextflow. Básicamente es asociarlo con el fichero (y sus includes) a ejecutar

Process

El proceso, process, es la unidad básica a ejecutar. Podemos asociarlo con el comando de sistema a ejecutar para completar una tarea. Dicho comando tendrá unos parámetros de entrada y generará una salida.

Así mismo los procesos son independientes y no comparten un estado común sino que se comunican entre sí mediante colas (o canales)

Channel

Un canal es el mecanismo por el cual los procesos se intercambian datos. Cuando definimos un proceso declaramos su input pero no especificamos de donde leer este input. Es mediante los canales (y sus operadores) donde realizamos esta interconexión o dependencia

Flujo de trabajo

Como ya he mencionado, los procesos son unidades "atómicas" e independientes que lo único que necesitan para su ejecución es disponer de los datos de entrada que definan (y producir unos datos de salida también definidos por el propio proceso).

Así pues, podemos pensar que al inicio de nuestro pipeline todos los procesos se encuentran creados y a la espera de "ser alimentados". Dicha alimentación se realiza a través de los diferentes operadores que ofrece un Channel:

hola.nf
process goodbye {
  input:
    path name
  output:
    stdout

    """
    cat $name
    """
}

process hi {
  input:
    val name

  output:
    path "name.txt"

    """
    echo Hi $name > name.txt
    """
}

workflow {
   Channel.of( 'Jorge' ) | hi | goodbye | view
}

En este ejemplo podemos ver dos procesos definidos (goodbye y hi)

  • goodbye recibe un fichero como entrada (path) y en su contexto lo vamos a referenciar como name. Su ejecución va a consistir en volcar (`cat) dicho fichero por la consola, emitiendo la salida

  • hi por su parte recibe una cadena (val) y en su contexto la vamos a referenciar como name (no confundir con el de goodbye). Dicho proceso va a concatenar una cadena fija con el parámetro de entrada para generar un fichero de trabajo name.txt y emitirá la ruta de este fichero

Por último definimos el flujo de trabajo:

  • Un canal emitirá una cadena fija, que será enviada al proceso hi. La salida de este será enviada por el canal al proceso goodbye y por ultimo la salida de este será volcada por consola (view)

    Este ejemplo, aunque no hace nada de utilidad, nos muestra los diferentes componentes de un pipeline.

Como podemos intuir, el orden de ejecución de los procesos no viene determinado por su posición en el fichero sino por la composición del flujo de trabajo. De esta manera Nextflow nos permitirá definir (y reutilizar) procesos en diferentes ficheros y componerlos a nuestro antojo

En los siguientes artículos iremos viendo, entre otras cosas, cómo ejecutar procesos en paralelo

Executor

Mientras que el proceso define el comando a ejecutar, el executor es el que define cómo se ejecutará dicho comando.

El executor por defecto es la máquina donde se está ejecutando el pipeline pero, sin modificar este, podemos indicarle a Nextflow qué executor usar como por ejemplo un cluster SLURM, un container de Amazon o de Google, etc

Así por ejemplo, cuando le indicamos a Nextflow que un pipeline lo ejecute, por ejemplo, en AWS él se encargará de aprovisionar la instancia, esperar a que se encuentre lista y ejecutar el script en esta (en realidad usando las APIs del executor en concreto)

Programación

La idea principal de Nextflow es que con la sintaxis que ofrece se disponga prácticamente de todas las herramientas para definir pipelines complejos, pero aun así permite incluir scripts con toda la complejidad que se desee.

Al estar desarrollado en Groovy es posible incluir scripts en el propio pipeline que nos ayuden a ampliar el lenguaje. Así mismo permite la inclusión de librerías Java (jar) simplemente colocándolas en la carpeta lib

random.nf
def cadenaRandom(){
   new Random().with {(1..9).collect {(('a'..'z')).join()[ nextInt((('a'..'z')).join().length())]}.join()}
}


process hi {
  input:
    val name

  output:
    stdout

    """
    echo $name is a random string
    """
}

workflow {
   Channel.of( cadenaRandom() ) | hi | view
}
nextflow run ./random.nf
N E X T F L O W  ~  version 22.04.5
Launching `./dos.nf` [friendly_fourier] DSL2 - revision: d0b1349f31
executor >  local (1)
[65/ee7786] process > hi (1) [100%] 1 of 1 ✔
lswwkzafv is a random string

Conclusion

En este artículo hemos visto las piezas básicas que componen un flujo de trabajo así como una breve introducción a la definición y ejecución de los procesos

Follow comments at Telegram group Or subscribe to the Channel Telegram channel

2019 - 2022 | Mixed with Bootstrap | Baked with JBake v2.6.7