Parte 3: Joint calling su una coorte¶
Nella Parte 2 avete costruito una pipeline di variant calling per campione che processava i dati di ciascun campione in modo indipendente. Ora la estenderemo per implementare il joint variant calling, come trattato nella Parte 1.
Compito¶
In questa parte del corso estenderemo il flusso di lavoro per fare quanto segue:
- Generare un file indice per ciascun file BAM di input utilizzando Samtools
- Eseguire GATK HaplotypeCaller su ciascun file BAM di input per generare un GVCF di chiamate di varianti genomiche per campione
- Raccogliere tutti i GVCF e combinarli in un data store GenomicsDB
- Eseguire il joint genotyping sui dati GVCF combinati per produrre un VCF a livello di coorte
Questa parte si basa direttamente sul flusso di lavoro prodotto nella Parte 2.
Come iniziare da questa sezione
Questa sezione del corso presuppone che abbiate completato la Parte 2: Variant calling per campione e abbiate una pipeline genomics.nf funzionante.
Se non avete completato la Parte 2 o volete ripartire da zero per questa parte, potete utilizzare la soluzione della Parte 2 come punto di partenza.
Eseguite questi comandi dall'interno della directory nf4-science/genomics/:
cp solutions/part2/genomics-2.nf genomics.nf
cp solutions/part2/nextflow.config .
cp solutions/part2/modules/* modules/
Questo vi fornisce un flusso di lavoro completo di variant calling per campione. Potete testare che funzioni correttamente eseguendo il seguente comando:
Piano della lezione¶
Abbiamo suddiviso questo in due passaggi:
- Modificare lo step di variant calling per campione per produrre un GVCF. Questo copre l'aggiornamento dei comandi e degli output del processo.
- Aggiungere uno step di joint genotyping che combini e genotipi i GVCF per campione.
Questo introduce l'operatore
collect(), le closure Groovy per la costruzione della riga di comando e i processi multi-comando.
Note
Assicuratevi di essere nella directory di lavoro corretta:
cd /workspaces/training/nf4-science/genomics
1. Modificare lo step di variant calling per campione per produrre un GVCF¶
La pipeline della Parte 2 produce file VCF, ma il joint calling richiede file GVCF. Dobbiamo attivare la modalità di variant calling GVCF e aggiornare l'estensione del file di output.
Richiamiamo il comando di variant calling GVCF dalla Parte 1:
gatk HaplotypeCaller \
-R /data/ref/ref.fasta \
-I /data/bam/reads_mother.bam \
-O reads_mother.g.vcf \
-L /data/ref/intervals.bed \
-ERC GVCF
Rispetto al comando base HaplotypeCaller che abbiamo incapsulato nella Parte 2, le differenze sono il parametro -ERC GVCF e l'estensione di output .g.vcf.
1.1. Dire a HaplotypeCaller di emettere un GVCF e aggiornare l'estensione di output¶
Aprite il file del modulo modules/gatk_haplotypecaller.nf per apportare due modifiche:
- Aggiungere il parametro
-ERC GVCFal comando GATK HaplotypeCaller; - Aggiornare il percorso del file di output per utilizzare l'estensione
.g.vcfcorrispondente, come da convenzione GATK.
Assicuratevi di aggiungere un backslash (\) alla fine della riga precedente quando aggiungete -ERC GVCF.
Dobbiamo anche aggiornare il blocco output per corrispondere alla nuova estensione del file.
Poiché abbiamo cambiato l'output del comando da .vcf a .g.vcf, il blocco output: del processo deve riflettere la stessa modifica.
1.2. Aggiornare l'estensione del file di output nel blocco outputs del processo¶
Dobbiamo anche aggiornare la configurazione di publish e output del flusso di lavoro per riflettere i nuovi output GVCF.
1.3. Aggiornare i target di publish per i nuovi output GVCF¶
Poiché ora stiamo producendo GVCF invece di VCF, dovremmo aggiornare la sezione publish: del flusso di lavoro per utilizzare nomi più descrittivi.
Organizzeremo anche i file GVCF nella loro subdirectory per maggiore chiarezza.
Ora aggiorniamo il blocco output per corrispondere.
1.4. Aggiornare il blocco output per la nuova struttura di directory¶
Dobbiamo anche aggiornare il blocco output per inserire i file GVCF in una subdirectory gvcf.
Con il modulo, i target di publish e il blocco output tutti aggiornati, possiamo testare le modifiche.
1.5. Eseguire la pipeline¶
Eseguiamo il flusso di lavoro per verificare che le modifiche funzionino.
Output del comando
L'output di Nextflow appare uguale a prima, ma i file .g.vcf e i loro file indice sono ora organizzati in subdirectory.
Directory contents (symlink abbreviati)
results/
├── gvcf/
│ ├── reads_father.bam.g.vcf -> */27/0d7eb9*/reads_father.bam.g.vcf
│ ├── reads_father.bam.g.vcf.idx -> */27/0d7eb9*/reads_father.bam.g.vcf.idx
│ ├── reads_mother.bam.g.vcf -> */e4/4ed55e*/reads_mother.bam.g.vcf
│ ├── reads_mother.bam.g.vcf.idx -> */e4/4ed55e*/reads_mother.bam.g.vcf.idx
│ ├── reads_son.bam.g.vcf -> */08/e95962*/reads_son.bam.g.vcf
│ └── reads_son.bam.g.vcf.idx -> */08/e95962*/reads_son.bam.g.vcf.idx
└── indexed_bam/
├── reads_father.bam -> */9a/c7a873*/reads_father.bam
├── reads_father.bam.bai -> */9a/c7a873*/reads_father.bam.bai
├── reads_mother.bam -> */f1/8d8486*/reads_mother.bam
├── reads_mother.bam.bai -> */f1/8d8486*/reads_mother.bam.bai
├── reads_son.bam -> */cc/fbc705*/reads_son.bam
└── reads_son.bam.bai -> */cc/fbc705*/reads_son.bam.bai
Se aprite uno dei file GVCF e scorrete al suo interno, potete verificare che GATK HaplotypeCaller abbia prodotto file GVCF come richiesto.
Takeaway¶
Quando modificate il nome del file di output di un comando di uno strumento, il blocco output: del processo e la configurazione publish/output devono essere aggiornati di conseguenza.
Cosa c'è dopo?¶
Impariamo a raccogliere i contenuti di un canale e passarli al processo successivo come singolo input.
2. Aggiungere uno step di joint genotyping¶
Ora dobbiamo raccogliere i GVCF per campione, combinarli in un data store GenomicsDB ed eseguire il joint genotyping per produrre un VCF a livello di coorte.
Come trattato nella Parte 1, questa è un'operazione che utilizza due strumenti: GenomicsDBImport combina i GVCF, poi GenotypeGVCFs produce le chiamate di varianti finali.
Incapsuleremo entrambi gli strumenti in un singolo processo chiamato GATK_JOINTGENOTYPING.
Richiamiamo i due comandi dalla Parte 1:
gatk GenomicsDBImport \
-V reads_mother.g.vcf \
-V reads_father.g.vcf \
-V reads_son.g.vcf \
-L /data/ref/intervals.bed \
--genomicsdb-workspace-path family_trio_gdb
Il primo comando prende i GVCF per campione e un file di intervalli, e produce un data store GenomicsDB.
Il secondo prende quel data store, un genoma di riferimento, e produce il VCF finale a livello di coorte.
L'URI del container è lo stesso di HaplotypeCaller: community.wave.seqera.io/library/gatk4:4.5.0.0--730ee8817e436867.
2.1. Configurare gli input¶
Il processo di joint genotyping necessita di due tipi di input che non abbiamo ancora: un nome arbitrario per la coorte e gli output GVCF raccolti da tutti i campioni raggruppati insieme.
2.1.1. Aggiungere un parametro cohort_name¶
Dobbiamo fornire un nome arbitrario per la coorte.
Più avanti nella serie di formazione imparerete come utilizzare i metadati dei campioni per questo tipo di cose, ma per ora dichiariamo semplicemente un parametro CLI utilizzando params e gli diamo un valore predefinito per comodità.
2.1.2. Raccogliere gli output di HaplotypeCaller attraverso i campioni¶
Se collegassimo direttamente il canale di output da GATK_HAPLOTYPECALLER al nuovo processo, Nextflow chiamerebbe il processo su ciascun GVCF del campione separatamente.
Vogliamo raggruppare tutti e tre i GVCF (e i loro file indice) in modo che Nextflow li passi tutti insieme a una singola chiamata del processo.
Possiamo farlo utilizzando l'operatore di canale collect().
Aggiungete le seguenti righe al corpo del workflow, subito dopo la chiamata a GATK_HAPLOTYPECALLER:
Analizziamo questo:
- Prendiamo il canale di output da
GATK_HAPLOTYPECALLERutilizzando la proprietà.out. - Poiché abbiamo nominato gli output usando
emit:nella sezione 1, possiamo selezionare i GVCF con.vcfe i file indice con.idx. Senza output nominati, dovremmo usare.out[0]e.out[1]. - L'operatore
collect()raggruppa tutti i file in un singolo elemento, quindiall_gvcfs_chcontiene tutti e tre i GVCF insieme, eall_idxs_chcontiene tutti e tre i file indice insieme.
Possiamo raccogliere i GVCF e i loro file indice separatamente (invece di mantenerli insieme in tuple) perché Nextflow posizionerà tutti i file di input insieme per l'esecuzione, quindi i file indice saranno presenti accanto ai GVCF.
Tip
Potete utilizzare l'operatore view() per ispezionare i contenuti dei canali prima e dopo l'applicazione degli operatori di canale.
2.2. Scrivere il processo di joint genotyping e chiamarlo nel flusso di lavoro¶
Seguendo lo stesso schema che abbiamo utilizzato nella Parte 2, scriveremo la definizione del processo in un file modulo, lo importeremo nel flusso di lavoro e lo chiameremo sugli input che abbiamo appena preparato.
2.2.1. Costruire una stringa per dare a ciascun GVCF un argomento -V¶
Prima di iniziare a compilare la definizione del processo, c'è una cosa da capire.
Il comando GenomicsDBImport si aspetta un argomento -V separato per ciascun file GVCF, così:
gatk GenomicsDBImport \
-V reads_mother.bam.g.vcf \
-V reads_father.bam.g.vcf \
-V reads_son.bam.g.vcf \
...
Se scrivessimo -V ${all_gvcfs_ch}, Nextflow concatenerebbe semplicemente i nomi dei file e quella parte del comando apparirebbe così:
Ma dobbiamo che la stringa appaia così:
Cosa importante, dobbiamo costruire questa stringa dinamicamente da qualunque file sia nel canale raccolto. Nextflow (tramite Groovy) fornisce un modo conciso per farlo:
Analizziamo questo:
all_gvcfs.collect { gvcf -> "-V ${gvcf}" }itera su ciascun percorso di file e antepone-Vad esso, producendo["-V A.g.vcf", "-V B.g.vcf", "-V C.g.vcf"]..join(' ')li concatena con spazi:"-V A.g.vcf -V B.g.vcf -V C.g.vcf".- Il risultato viene assegnato a una variabile locale
gvcfs_line(definita condef), che possiamo interpolare nel template del comando.
Questa riga va all'interno del blocco script: del processo, prima del template del comando.
Potete inserire codice Groovy arbitrario tra script: e le """ di apertura del template del comando.
Poi sarete in grado di riferirvi a quell'intera stringa come gvcfs_line nel blocco script: del processo.
2.2.2. Compilare il modulo per il processo di joint genotyping¶
Ora possiamo affrontare la scrittura del processo completo.
Aprite modules/gatk_jointgenotyping.nf ed esaminate la struttura della definizione del processo.
Procedete e compilate la definizione del processo utilizzando le informazioni fornite sopra, quindi verificate il vostro lavoro confrontandolo con la soluzione nella scheda "Dopo" qui sotto.
Ci sono diverse cose che vale la pena evidenziare qui.
Come in precedenza, diversi input sono elencati anche se i comandi non vi fanno riferimento direttamente: all_idxs, ref_index e ref_dict.
Elencarli assicura che Nextflow posizioni questi file nella directory di lavoro accanto ai file che appaiono nei comandi, che GATK si aspetta di trovare in base alle convenzioni di denominazione.
La variabile gvcfs_line utilizza la closure Groovy descritta sopra per costruire gli argomenti -V per GenomicsDBImport.
Questo processo esegue due comandi in serie, proprio come fareste nel terminale.
GenomicsDBImport combina i GVCF per campione in un data store, poi GenotypeGVCFs legge quel data store e produce il VCF finale a livello di coorte.
Il data store GenomicsDB (${cohort_name}_gdb) è un artefatto intermedio utilizzato solo all'interno del processo; non appare nel blocco output.
Una volta completato questo, il processo è pronto all'uso. Per utilizzarlo nel flusso di lavoro, dovrete importare il modulo e aggiungere una chiamata al processo.
2.2.3. Importare il modulo¶
Aggiungete l'istruzione import a genomics.nf, sotto le istruzioni import esistenti:
Il processo è ora disponibile nello scope del flusso di lavoro.
2.2.4. Aggiungere la chiamata al processo¶
Aggiungete la chiamata a GATK_JOINTGENOTYPING nel corpo del flusso di lavoro, dopo le righe collect():
Il processo è ora completamente collegato. Successivamente, configuriamo come vengono pubblicati gli output.
2.3. Configurare la gestione degli output¶
Dobbiamo pubblicare gli output del joint VCF. Aggiungete target di publish ed entry del blocco output per i risultati del joint genotyping.
2.3.1. Aggiungere target di publish per il joint VCF¶
Aggiungete il joint VCF e il suo indice alla sezione publish: del flusso di lavoro:
Ora aggiorniamo il blocco output per corrispondere.
2.3.2. Aggiungere entry del blocco output per il joint VCF¶
Aggiungete entry per i file joint VCF. Li metteremo alla radice della directory results poiché questo è l'output finale.
Con il processo, i target di publish e il blocco output tutti al loro posto, possiamo testare il flusso di lavoro completo.
2.4. Eseguire il flusso di lavoro¶
Eseguiamo il flusso di lavoro per verificare che tutto funzioni.
Output del comando
I primi due step sono in cache dall'esecuzione precedente, e il nuovo step GATK_JOINTGENOTYPING viene eseguito una volta sugli input raccolti da tutti e tre i campioni.
Il file di output finale, family_trio.joint.vcf (e il suo indice), sono nella directory results.
Directory contents (symlink abbreviati)
results/
├── family_trio.joint.vcf -> */a6/7cc8ed*/family_trio.joint.vcf
├── family_trio.joint.vcf.idx -> */a6/7cc8ed*/family_trio.joint.vcf.idx
├── gvcf/
│ ├── reads_father.bam.g.vcf -> */27/0d7eb9*/reads_father.bam.g.vcf
│ ├── reads_father.bam.g.vcf.idx -> */27/0d7eb9*/reads_father.bam.g.vcf.idx
│ ├── reads_mother.bam.g.vcf -> */e4/4ed55e*/reads_mother.bam.g.vcf
│ ├── reads_mother.bam.g.vcf.idx -> */e4/4ed55e*/reads_mother.bam.g.vcf.idx
│ ├── reads_son.bam.g.vcf -> */08/e95962*/reads_son.bam.g.vcf
│ └── reads_son.bam.g.vcf.idx -> */08/e95962*/reads_son.bam.g.vcf.idx
└── indexed_bam/
├── reads_father.bam -> */9a/c7a873*/reads_father.bam
├── reads_father.bam.bai -> */9a/c7a873*/reads_father.bam.bai
├── reads_mother.bam -> */f1/8d8486*/reads_mother.bam
├── reads_mother.bam.bai -> */f1/8d8486*/reads_mother.bam.bai
├── reads_son.bam -> */cc/fbc705*/reads_son.bam
└── reads_son.bam.bai -> */cc/fbc705*/reads_son.bam.bai
Se aprite il file joint VCF, potete verificare che il flusso di lavoro abbia prodotto le chiamate di varianti attese.
Ora avete un flusso di lavoro di joint variant calling automatizzato e completamente riproducibile!
Note
Tenete presente che i file di dati che vi abbiamo fornito coprono solo una piccola porzione del cromosoma 20. La dimensione reale di un callset di varianti sarebbe contata in milioni di varianti. Ecco perché utilizziamo solo piccoli sottoinsiemi di dati per scopi di formazione!
Takeaway¶
Sapete come raccogliere output da un canale e raggrupparli come singolo input per un altro processo. Sapete anche come costruire una riga di comando utilizzando closure Groovy e come eseguire comandi multipli in un singolo processo.
Cosa c'è dopo?¶
Celebrate il vostro successo e prendetevi una pausa ben meritata.
Nella prossima parte di questo corso, imparerete come eseguire una pipeline di variant calling production-ready da nf-core e confrontarla con la pipeline che avete costruito manualmente.