Denovo bug fixes

This commit is contained in:
Neil Horner 2022-03-02 11:34:21 +00:00
parent b56cec9994
commit 03b8ba51f6
7 changed files with 225 additions and 186 deletions

View File

@ -115,13 +115,12 @@ For example:
* wf-isoforms-report.html * wf-isoforms-report.html
- Summary and plots of reads, alignments and the transcript assembly and annotation - Summary and plots of reads, alignments and the transcript assembly and annotation
- One file per run with results from each sample merged - One file per run with results from each sample merged
####Transcript isoform table Note transcript isoform table is currently only available for the reference-guilded assembly.
Note: Currently only available for the reference-guilded assembly
The report includes a searchable filterable table of transcript isoforms<br>
Note: If using a large dataset with 10s of thousands of predicted isoforms, to speed up searching, the table can be limited Note: If using a large dataset with 10s of thousands of predicted isoforms, to speed up searching, the table can be limited
by read coverage, which can be set with `transcript_table_cov_thresh = 50` (default 50x) by read coverage, which can be set with `transcript_table_cov_thresh = 50` (default 50x)
#### Output files
Each sample will also have it's own directory containing the following (dependent on assembly approach and input options) Each sample will also have it's own directory containing the following (dependent on assembly approach and input options)
* {sampleid}_gffcompare * {sampleid}_gffcompare
* Directory containing all output from gffcompare * Directory containing all output from gffcompare
@ -133,11 +132,8 @@ Each sample will also have it's own directory containing the following (dependen
- A transcriptome derived from the query reads + reference annotation - A transcriptome derived from the query reads + reference annotation
* merged_transcriptome.fas * merged_transcriptome.fas
-A transcriptome made from the combined query reads and reference annotation -A transcriptome made from the combined query reads and reference annotation
* final_polished_cds.fas (de novo only)
* CDS sequences derived from reads
## Useful links ## Useful links
* [nextflow](https://www.nextflow.io/) * [nextflow](https://www.nextflow.io/)

54
bin/merge_gff.py Executable file
View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
"""
Merge and fix gff files.
Merge multiple gff files into single file.
Rename gene and transcript ids to avoid attribute conflicts from
independently-created files.
"""
import argparse
from pathlib import Path
import re
from natsort import natsorted
def main(gff_files: str, outfile: str):
"""Entry point."""
regx_id = re.compile(r'gene_id "STRG\.(\d+)"')
start = 1
with open(outfile, 'w') as fh:
for gff in gff_files:
text = Path(gff).read_text()
if start != 1:
# Strip headers
text = [x for x in text.splitlines() if not x.startswith('#')]
text = '\n'.join(text)
ids = natsorted(set(re.findall(regx_id, text)))
new_gene_ids = list(range(start, start + len(ids)))
id_map = dict(zip(ids, new_gene_ids))
for old_id, new_id in id_map.items():
text = text.replace(
f'gene_id "STRG.{old_id}"',
f'gene_id "STRG.{new_id}"')
text = text.replace(
f'transcript_id "STRG.{old_id}.',
f'transcript_id "STRG.{new_id}.')
fh.write(text)
fh.write('\n')
start += len(ids)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--gff_files", help="gff files to merge",
required=True, nargs='+')
parser.add_argument("--out_file", help="where to save merged files",
required=True)
args = parser.parse_args()
main(args.gff_files, args.out_file)

View File

@ -6,7 +6,7 @@ from collections import Counter, defaultdict, OrderedDict
import math import math
from pathlib import Path from pathlib import Path
from aplanat import bars, lines from aplanat import bars, hist, lines
from aplanat.components import fastcat from aplanat.components import fastcat
from aplanat.components import simple as scomponents from aplanat.components import simple as scomponents
from aplanat.report import WFReport from aplanat.report import WFReport
@ -648,7 +648,8 @@ def transcript_table(report, df_tmaps, covr_threshold):
### Query transcript table ### Query transcript table
Low coverage transcripts are removed to speed up the table viewing. <br> Low coverage transcripts are removed to speed up the table viewing. <br>
This can be set with the parameter `args.min_isoform_cov` in the config. This can be set with the parameter `transcript_table_cov_thresh` in the
config.
''') ''')
section.plot(cov_plt) section.plot(cov_plt)
# Filter on converge threshold # Filter on converge threshold
@ -724,17 +725,9 @@ def tanscriptome_summary(report, gffs, sample_ids, denovo=False):
transcript_lens.append(tr_len) transcript_lens.append(tr_len)
num_bins = max(isoforms_per_gene) bar_isos = hist.histogram(
if num_bins > 12: [isoforms_per_gene], colors=[Colors.cerulean],
num_bins = 12 title="isoforms per gene")
hist_, bins = np.histogram(isoforms_per_gene, bins=num_bins)
bar_isos = bars.simple_bar([str(round(x_, 1)) for x_ in bins], hist_,
title="Isoforms per gene",
colors=Colors.cerulean,
x_axis_label='Num. isoforms',
y_axis_label='Num. genes')
bar_isos.xaxis.major_label_orientation = math.pi / 2.8 bar_isos.xaxis.major_label_orientation = math.pi / 2.8
plots.append(bar_isos) plots.append(bar_isos)

View File

@ -39,16 +39,13 @@ process dump_clusters {
process build_backbones { process build_backbones {
/* /*
This step can fail in what seems to at the racon stage giving an 'empty overlap error set' message Build coding
As a temporary fix, do racon_cmd || true to prevent it crashing the pipeline, and then move on to next cluster
This process needs some work
*/ */
label "isoforms" label "isoforms"
input: input:
tuple val(sample_id), path(cluster_fq) tuple val(sample_id), path(cluster_fq)
output: output:
tuple val(sample_id), path("*final_polished_cds.fa"), emit: polished_cds tuple val(sample_id), path("*final_polished_cds.fa"), emit: polished_cds, optional: true
script: script:
def cluster_fq_bl = new BlankSeparatedList(cluster_fq) def cluster_fq_bl = new BlankSeparatedList(cluster_fq)
""" """
@ -56,64 +53,70 @@ process build_backbones {
UNID=\$(echo ${cluster_fq_bl[1]} | grep -o -E '[0-9]+') UNID=\$(echo ${cluster_fq_bl[1]} | grep -o -E '[0-9]+')
for cluster in $cluster_fq_bl for cluster in $cluster_fq_bl
do do
clfq=`basename \$cluster` clfq=`basename \$cluster`
cln=\${clfq%.*} cln=\${clfq%.*}
echo Building backbone for cluster: \$cln echo Building backbone for cluster: \$cln
echo "\tSampling input reads for backbone construction." echo "\tSampling input reads for backbone construction."
sample=\${cln}_sample.fq sample=\${cln}_sample.fq
seqkit head --quiet -n 100 \$cluster > \$sample seqkit head --quiet -n 100 \$cluster > \$sample
seqkit sample --quiet -n 500 -2 -s 100 \$cluster >> \$sample seqkit sample --quiet -n 500 -2 -s 100 \$cluster >> \$sample
echo "\tConstructing spoa consensus." echo "\tConstructing spoa consensus."
spoa_cons=\${cln}_spoa.fa spoa_cons=\${cln}_spoa.fa
spoa -m 5 -n -4 -g -8 -e -6 -q -10 -c -15 -l 1 -r 0 \$sample > \$spoa_cons spoa -m 5 -n -4 -g -8 -e -6 -q -10 -c -15 -l 1 -r 0 \$sample > \$spoa_cons
echo "\tPolishing the consensus using racon." echo "\tPolishing the consensus using racon."
# polish 1 # polish 1
samgz=\${cln}_aln.sam.gz samgz=\${cln}_aln.sam.gz
racon_cons=\${cln}_racon.fa racon_cons=\${cln}_racon.fa
racon_cons1=\${cln}_racon1.fa tmpcons=\${cln}_tmcons.fa
minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} \$spoa_cons \$sample | gzip - > \$samgz minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} \$spoa_cons \$sample | gzip - > \$samgz
racon -t ${params.threads} --no-trimming -u -w 2000 \$sample \$samgz \$spoa_cons > \$racon_cons || true exitcode=0
if [ -f \$racon_cons ]; racon -t ${params.threads} --no-trimming -u -w 2000 \$sample \$samgz \$spoa_cons > \$racon_cons || exitcode=1
then if [[ \$exitcode -eq 0 ]];
cat \$racon_cons | seqkit replace -p ".*" -r cluster_\${cln} > \$racon_cons1 then
else # Rename consensus sequence name with cluster id
continue cat \$racon_cons | seqkit replace -p ".*" -r cluster_\${cln} > \$tmpcons
fi mv \$tmpcons \$racon_cons
else
echo "Polishing failed for \${cln}"
continue
fi
# polish 2 # polish 2
samgz1=\${cln}_aln_1.sam.gz minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} \$racon_cons \$sample | gzip - > \$samgz
racon_cons2=\${cln}_racon2.fa exitcode=0
minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} \$racon_cons1 \$sample | gzip - > \$samgz1 racon -t ${params.threads} -u --no-trimming \$sample \$samgz \$racon_cons > \$tmpcons || exitcode=1
racon -t ${params.threads} -u --no-trimming \$sample \$samgz1 \$racon_cons1 > \$racon_cons2 || true if [[ \$exitcode -eq 0 ]];
if [ -f \$racon_cons2 ]; then
then echo "success polish 2"
echo "success polish 2" mv \$tmpcons \$racon_cons
else else
continue echo "Polishing failed for \${cln}"
fi continue
fi
# polish 3 # polish 3
samgz2=\${cln}_aln_2.sam.gz minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} \$racon_cons \$sample | gzip - > \$samgz
minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} \$racon_cons2 \$sample | gzip - > \$samgz2 exitcode=0
EXITCODE=0 racon -t ${params.threads} -u \$sample \$samgz \$racon_cons > \$tmpcons || exitcode=1
racon -t ${params.threads} -u \$sample \$samgz2 \$racon_cons2 >> ${sample_id}_\${UNID}_final_polished_cds.fa || EXITCODE=\$? if [[ \$exitcode -eq 0 ]];
if [ \$EXITCODE -eq 0 ]; then
then cat \$tmpcons >> ${sample_id}_\${UNID}_final_polished_cds.fa
echo "finished" echo "polishing 3 success"
fi else
echo "Polishing failed for \${cln}"
fi
done done
echo "Finished backbones" echo "Finished backbones"
""" """
} }
process merge_cds { process merge_cds {
label "isoforms" label "isoforms"
input: input:
@ -121,7 +124,6 @@ process merge_cds {
output: output:
tuple val(sample_id), path("${sample_id}_cds.fa"), emit: final_polished_cds tuple val(sample_id), path("${sample_id}_cds.fa"), emit: final_polished_cds
script: script:
def merge_list
""" """
for FILE in *final_polished_cds.fa for FILE in *final_polished_cds.fa
do do
@ -138,14 +140,13 @@ process cds_align {
tuple val(sample_id), path("${sample_id}_reads_aln_sorted.bam"), emit: bam tuple val(sample_id), path("${sample_id}_reads_aln_sorted.bam"), emit: bam
tuple val(sample_id), path("${sample_id}_read_aln_stats.tsv"), emit: stats tuple val(sample_id), path("${sample_id}_read_aln_stats.tsv"), emit: stats
script: script:
def ab = "${sample_id}_reads_aln_sorted.bam"
""" """
minimap2 -t ${params.threads} \ minimap2 -t ${params.threads} \
-ax splice ${params.minimap2_opts} $polished_cds $sorted_reads_dir/sorted_reads.fastq |\ -ax splice ${params.minimap2_opts} $polished_cds $sorted_reads_dir/sorted_reads.fastq |\
samtools view -b - |\ samtools view -b - |\
samtools sort -o $ab; samtools sort -o "${sample_id}_reads_aln_sorted.bam";
samtools index $ab; samtools index "${sample_id}_reads_aln_sorted.bam";
((seqkit bam -s -j ${params.threads} ${ab} 2>&1) | tee ${sample_id}_read_aln_stats.tsv ) || true ((seqkit bam -s -j ${params.threads} "${sample_id}_reads_aln_sorted.bam" 2>&1) | tee ${sample_id}_read_aln_stats.tsv ) || true
""" """
} }
@ -168,16 +169,14 @@ process make_batches {
minimum_batch_size = 2000 minimum_batch_size = 2000
""" """
nr_bases=\$(seqkit stats -T $fastq|cut -f 5| sed '2q;d')
b=0 b=0
if [ ${params.batch_size} -lt \$b ]; if [ ${params.batch_size} -lt \$b ];
then then
nr_bases=\$(seqkit stats -T $fastq|cut -f 5| sed '2q;d') nr_bases=\$(seqkit stats -T $fastq|cut -f 5| sed '2q;d')
let batch_size=\$nr_bases/1000/$maxcpus let batch_size=\$nr_bases/1000/$maxcpus
if [ \$batch_size -lt $minimum_batch_size ]; if [ \$batch_size -lt $minimum_batch_size ];
then then
batch_size=$minimum_batch_size batch_size=$minimum_batch_size
fi fi
else else
batch_size=${params.batch_size} batch_size=${params.batch_size}
@ -186,11 +185,12 @@ process make_batches {
echo "Batch size:\$batch_size"; echo "Batch size:\$batch_size";
echo "Num bases: \$nr_bases"; echo "Num bases: \$nr_bases";
init_cls_options="--batch-size \$batch_size --kmer-size ${params.kmer_size} \ init_cls_options="--batch-size \$batch_size --kmer-size ${params.kmer_size} --window-size ${params.window_size} \
--window-size ${params.window_size} --min-shared ${params.min_shared} --min-qual ${params.min_qual} \ --min-shared ${params.min_shared} --min-qual ${params.min_qual} \
--mapped-threshold ${params.mapped_threshold} --aligned-threshold ${params.aligned_threshold} \ --mapped-threshold ${params.mapped_threshold} --aligned-threshold ${params.aligned_threshold} \
--min-fraction ${params.min_fraction} --min-prob-no-hits ${params.min_prob_no_hits} \ --min-fraction ${params.min_fraction} --min-prob-no-hits ${params.min_prob_no_hits} \
-M ${params.batch_max_seq} -P ${params.consensus_period} -g ${params.consensus_minimum} -c ${params.consensus_maximum} -F ${params.min_left_cls} " -M ${params.batch_max_seq} -P ${params.consensus_period} -g ${params.consensus_minimum} \
-c ${params.consensus_maximum} -F ${params.min_left_cls} "
mkdir -p sorted; isONclust2 sort \$init_cls_options -v -o sorted $fastq; mkdir -p sorted; isONclust2 sort \$init_cls_options -v -o sorted $fastq;
""" """
@ -206,7 +206,6 @@ process clustering() {
script: script:
""" """
run_isonclust2.py $sorted_batches run_isonclust2.py $sorted_batches
""" """
} }
@ -216,8 +215,8 @@ process cluster_quality() {
label "isoforms" label "isoforms"
input: input:
tuple val(sample_id), path(reads_fl), path(final_clusters_dir)
path reference path reference
tuple val(sample_id), path(reads_fl), path(final_clusters_dir)
output: output:
tuple val(sample_id), tuple val(sample_id),
path("${sample_id}_cluster_qc"), emit: cluster_qc_dir path("${sample_id}_cluster_qc"), emit: cluster_qc_dir
@ -229,16 +228,15 @@ process cluster_quality() {
def bam = "${qc_dir}/ref_aln.bam" def bam = "${qc_dir}/ref_aln.bam"
""" """
echo $qc_dir_raw
mkdir $qc_dir mkdir $qc_dir
mkdir $qc_dir_raw mkdir $qc_dir_raw
minimap2 -ax splice -t 2 $reference $reads_fl |\ minimap2 -ax splice -t 2 $reference $reads_fl |\
samtools view -q 2 -F 2304 -b - |\ samtools view -q 2 -F 2304 -b - |\
samtools sort - -o $bam; samtools sort - -o $bam;
samtools index $bam; samtools index $bam;
compute_cluster_quality.py --sizes $final_clusters_dir/clusters_info.tsv \ compute_cluster_quality.py --sizes $final_clusters_dir/clusters_info.tsv \
--outfile ${qc_dir}/cluster_quality.csv --ont --clusters $final_clusters_dir/clusters.tsv \ --outfile ${qc_dir}/cluster_quality.csv --ont --clusters $final_clusters_dir/clusters.tsv \
--classes $bam --report ${qc_dir}/cluster_quality.pdf --raw_data_out $qc_dir_raw --classes $bam --report ${qc_dir}/cluster_quality.pdf --raw_data_out $qc_dir_raw
""" """
} }
@ -248,37 +246,35 @@ workflow denovo_assembly {
fastq_reads_fl fastq_reads_fl
reference reference
main: main:
make_batches(fastq_reads_fl) make_batches(fastq_reads_fl)
clustering(make_batches.output.sorted_batches) clustering(make_batches.out.sorted_batches)
dump_clusters(clustering.output.root_cluster dump_clusters(
.join(make_batches.output.sorted_reads_dir)) clustering.out.root_cluster
.join(make_batches.out.sorted_reads_dir))
build_backbones(dump_clusters.output.final_clusters build_backbones(
.flatMap(map_sample_ids_cls) dump_clusters.out.final_clusters
.groupTuple(size: 10, remainder: true) .flatMap(map_sample_ids_cls)
) .groupTuple(size: 10, remainder: true))
merge_cds(build_backbones.output.polished_cds merge_cds(
.flatMap(map_sample_ids_cls) build_backbones.out.polished_cds
.groupTuple() .flatMap(map_sample_ids_cls)
) .groupTuple())
cds_align(merge_cds.out.final_polished_cds.view() cds_align(
.join(make_batches.output.sorted_reads_dir)) merge_cds.out.final_polished_cds
.join(make_batches.out.sorted_reads_dir))
if (!reference.name.startsWith('OPTIONAL_FILE')){ if (!reference.name.startsWith('OPTIONAL_FILE')){
cluster_quality(reference, fastq_reads_fl
.join(dump_clusters.out.final_clusters_dir))
cluster_quality(fastq_reads_fl cluster_quality.out.cluster_qc_dir.set { opt_qual_ch }
.join(dump_clusters.output.final_clusters_dir), reference)
cluster_quality.out.cluster_qc_dir cluster_quality.out.cluster_qc_raw.set { opt_qual_raw_ch }
.set { opt_qual_ch }
cluster_quality.output.cluster_qc_raw
.set { opt_qual_raw_ch }
} else{ } else{
Channel.empty().set { opt_qual_ch } Channel.empty().set { opt_qual_ch }
@ -286,9 +282,9 @@ workflow denovo_assembly {
} }
emit: emit:
bam = cds_align.output.bam bam = cds_align.out.bam
cds = merge_cds.out.final_polished_cds cds = merge_cds.out.final_polished_cds
stats = cds_align.output.stats stats = cds_align.out.stats
opt_qual_ch opt_qual_ch
opt_qual_raw_ch opt_qual_raw_ch
} }

View File

@ -1,4 +1,4 @@
name: epi2melabs-wf-isoforms-test name: epi2melabs-wf-isoforms
channels: channels:
- epi2melabs - epi2melabs
- bioconda - bioconda
@ -26,3 +26,4 @@ dependencies:
- isonclust2==2.3 - isonclust2==2.3
- parallel - parallel
- scikit-learn==1.0.2 - scikit-learn==1.0.2
- natsort

99
main.nf
View File

@ -42,6 +42,7 @@ process getVersions {
python -c "import pysam; print(f'pysam,{pysam.__version__}')" >> versions.txt python -c "import pysam; print(f'pysam,{pysam.__version__}')" >> versions.txt
python -c "import aplanat; print(f'aplanat,{aplanat.__version__}')" >> versions.txt python -c "import aplanat; print(f'aplanat,{aplanat.__version__}')" >> versions.txt
python -c "import pandas; print(f'pandas,{pandas.__version__}')" >> versions.txt python -c "import pandas; print(f'pandas,{pandas.__version__}')" >> versions.txt
python -c "import sklearn; print(f'scikit-learn,{sklearn.__version__}')" >> versions.txt
fastcat --version | sed 's/^/fastcat,/' >> versions.txt fastcat --version | sed 's/^/fastcat,/' >> versions.txt
minimap2 --version | sed 's/^/minimap2,/' >> versions.txt minimap2 --version | sed 's/^/minimap2,/' >> versions.txt
samtools --version | head -n 1 | sed 's/ /,/' >> versions.txt samtools --version | head -n 1 | sed 's/ /,/' >> versions.txt
@ -105,7 +106,7 @@ process build_minimap_index{
path "genome_index.mmi", emit: index path "genome_index.mmi", emit: index
script: script:
""" """
minimap2 -t ${params.threads} ${params.minimap_index_opts} -I 1000G -d "genome_index.mmi" ${reference} minimap2 -t ${params.threads} ${params.minimap_index_opts} -I 1000G -d "genome_index.mmi" ${reference}
""" """
} }
@ -128,7 +129,7 @@ process split_bam{
if (params["bundle_min_reads"] != false) if (params["bundle_min_reads"] != false)
""" """
seqkit bam -j ${params.threads} -N ${params.bundle_min_reads} ${bam} -o bam_bundles/ seqkit bam -j ${params.threads} -N ${params.bundle_min_reads} ${bam} -o bam_bundles/
mv bam_bundles/* . mv bam_bundles/* .
for f in *:*; do mv -v "\$f" \$(echo "\$f" | tr ':' '-'); done for f in *:*; do mv -v "\$f" \$(echo "\$f" | tr ':' '-'); done
""" """
else else
@ -145,7 +146,7 @@ process assemble_transcripts{
Take aligned reads in bam format that may be a chunk of a larger alignment file. Take aligned reads in bam format that may be a chunk of a larger alignment file.
Optionally use reference annotation to guide assembly. Optionally use reference annotation to guide assembly.
Output gff annotation files in a tuple with `sample_id` for combining into samples late rin the pipeline. Output gff annotation files in a tuple with `sample_id` for combining into samples later in the pipeline.
*/ */
label 'isoforms' label 'isoforms'
cpus params.threads cpus params.threads
@ -158,16 +159,17 @@ process assemble_transcripts{
script: script:
def out_filename = bam.name.replaceFirst(~/\.[^\.]+$/, '') + "_${sample_id}.gff" def out_filename = bam.name.replaceFirst(~/\.[^\.]+$/, '') + "_${sample_id}.gff"
def G_FLAG = ref_annotation.name.startsWith('OPTIONAL_FILE') ? '' : "-G ${ref_annotation}" def G_FLAG = ref_annotation.name.startsWith('OPTIONAL_FILE') ? '' : "-G ${ref_annotation}"
def prefix = bam.name.split('-')[0][5..-1]
""" """
stringtie --rf ${G_FLAG} -L -v -A gene_abund.tab -p ${params.threads} ${params.stringtie_opts} -o ${out_filename} \ stringtie --rf ${G_FLAG} -L -v -A gene_abund.tab -p ${params.threads} ${params.stringtie_opts} -o ${out_filename} \
${bam} 2>/dev/null -l $prefix ${bam} 2>/dev/null
""" """
} }
process merge_gff_bundles{ process merge_gff_bundles{
/* /*
Merge gff bundles into a single gff file. Merge gff bundles into a single gff file per sample.
*/ */
label 'isoforms' label 'isoforms'
@ -215,14 +217,14 @@ process run_gffcompare{
mkdir $out_dir mkdir $out_dir
echo "Doing comparison of reference annotation: ${ref_annotation} and the query annotation" echo "Doing comparison of reference annotation: ${ref_annotation} and the query annotation"
gffcompare -o ${out_dir}/str_merged -r ${ref_annotation} \ gffcompare -o ${out_dir}/str_merged -r ${ref_annotation} \
${params.gffcompare_opts} ${query_annotation} ${params.gffcompare_opts} ${query_annotation}
generate_tracking_summary.py --tracking $out_dir/str_merged.tracking \ generate_tracking_summary.py --tracking $out_dir/str_merged.tracking \
--output_dir ${out_dir} --annotation ${ref_annotation} --output_dir ${out_dir} --annotation ${ref_annotation}
mv *.tmap $out_dir mv *.tmap $out_dir
mv *.refmap $out_dir mv *.refmap $out_dir
""" """
} }
} }
@ -231,7 +233,6 @@ process run_gffcompare{
process get_transcriptome{ process get_transcriptome{
/* /*
Write out a transcriptome file based on the query gff annotations. Write out a transcriptome file based on the query gff annotations.
TODO: Do we need to touch merged_transcriptome or can we just pass it?
*/ */
label 'isoforms' label 'isoforms'
@ -244,13 +245,10 @@ process get_transcriptome{
def transcriptome = "${sample_id}_transcriptome.fas" def transcriptome = "${sample_id}_transcriptome.fas"
def merged_transcriptome = "${sample_id}_merged_transcriptome.fas" def merged_transcriptome = "${sample_id}_merged_transcriptome.fas"
""" """
gffread -g ${reference_seq} -w ${transcriptome} ${transcripts_gff} gffread -g ${reference_seq} -w ${transcriptome} ${transcripts_gff}
if [ "\$(ls -A $gffcmp_dir)" ]; if [ "\$(ls -A $gffcmp_dir)" ];
then then
echo "Yes" gffread -F -g ${reference_seq} -w ${merged_transcriptome} $gffcmp_dir/str_merged.annotated.gtf
gffread -F -g ${reference_seq} -w ${merged_transcriptome} \
$gffcmp_dir/str_merged.annotated.gtf
fi fi
""" """
} }
@ -274,7 +272,6 @@ process makeReport {
script: script:
// Convert the sample_id arrayList. // Convert the sample_id arrayList.
sids = new BlankSeparatedList(sample_ids) sids = new BlankSeparatedList(sample_ids)
def report_name = "wf-isoforms-report.html" def report_name = "wf-isoforms-report.html"
def OPT_ALN = denovo ? '' : "--alignment_stats ${aln_stats}" def OPT_ALN = denovo ? '' : "--alignment_stats ${aln_stats}"
def OPT_DENOVO = denovo ? "--denovo" : '' def OPT_DENOVO = denovo ? "--denovo" : ''
@ -365,61 +362,63 @@ workflow pipeline {
run_gffcompare(merge_gff_bundles.out.gff, ref_annotation) run_gffcompare(merge_gff_bundles.out.gff, ref_annotation)
if (params.denovo){ if (params.denovo){
// Use the perd-sample, de novo-assembled CDS // Use the per-sample, de novo-assembled CDS
seq_for_transcriptome_build = m.cds seq_for_transcriptome_build = m.cds
}else { }else {
// If doing reference based assembly, there is only one reference // For reference based assembly, there is only one reference
// So map this reference to all sample_ids // So map this reference to all sample_ids
seq_for_transcriptome_build = sample_ids.flatten().combine(Channel.fromPath(params.ref_genome)) seq_for_transcriptome_build = sample_ids.flatten().combine(Channel.fromPath(params.ref_genome))
} }
makeReport(software_versions, makeReport(
workflow_params, software_versions,
params.denovo, workflow_params,
summariseConcatReads.out.summary params.denovo,
.join(m.stats) summariseConcatReads.out.summary
.join(run_gffcompare.out.gffcmp_dir) .join(m.stats)
.join(preprocess_reads.out.report) .join(run_gffcompare.out.gffcmp_dir)
.join(merge_gff_bundles.out.gff) .join(preprocess_reads.out.report)
.toList().transpose().toList() .join(merge_gff_bundles.out.gff)
) .toList().transpose().toList())
report = makeReport.out.report report = makeReport.out.report
get_transcriptome(merge_gff_bundles.out.gff get_transcriptome(
merge_gff_bundles.out.gff
.join(run_gffcompare.out.gffcmp_dir) .join(run_gffcompare.out.gffcmp_dir)
.join(seq_for_transcriptome_build)) .join(seq_for_transcriptome_build))
if (use_ref_ann){ if (use_ref_ann){
results = preprocess_reads.out.report results = preprocess_reads.out.report
.concat(run_gffcompare.output.gffcmp_dir, .concat(
m.stats, run_gffcompare.output.gffcmp_dir,
get_transcriptome.out.flatMap(map_sample_ids_cls)) m.stats,
.map {it -> it[1]} get_transcriptome.out.flatMap(map_sample_ids_cls))
.concat(makeReport.out.report) .map {it -> it[1]}
.concat(makeReport.out.report)
} }
if (!use_ref_ann && !params.denovo){ if (!use_ref_ann && !params.denovo){
results = preprocess_reads.out.report results = preprocess_reads.out.report
.concat(m.stats, .concat(m.stats,
get_transcriptome.out.flatMap(map_sample_ids_cls)) get_transcriptome.out.flatMap(map_sample_ids_cls))
.map {it -> it[1]} .map {it -> it[1]}
.concat(makeReport.out.report) .concat(makeReport.out.report)
} }
if (params.denovo){ if (params.denovo){
results = m.cds results = m.cds
.concat(m.stats, .concat(m.stats,
seq_for_transcriptome_build, seq_for_transcriptome_build,
get_transcriptome.out.flatMap(map_sample_ids_cls), get_transcriptome.out.flatMap(map_sample_ids_cls),
merge_gff_bundles.out.gff, merge_gff_bundles.out.gff,
m.opt_qual_ch.flatMap {it -> m.opt_qual_ch.flatMap {it ->
l = [] l = []
for (x in it[1..-1]){ for (x in it[1..-1]){
l.add(tuple(it[0], x)) l.add(tuple(it[0], x))
} }
return l return l
}) })
.map {it -> it[1]} .map {it -> it[1]}
.concat(makeReport.out.report) .concat(makeReport.out.report)
} }

View File

@ -16,23 +16,23 @@ process map_reads{
tuple val(sample_id), path("${sample_id}_reads_aln_sorted.bam"), emit: bam tuple val(sample_id), path("${sample_id}_reads_aln_sorted.bam"), emit: bam
tuple val(sample_id), path("${sample_id}_read_aln_stats.tsv"), emit: stats tuple val(sample_id), path("${sample_id}_read_aln_stats.tsv"), emit: stats
script: script:
def ContextFilter = """AlnContext: { Ref: "${reference}", LeftShift: -${params.poly_context}, RightShift: ${params.poly_context}, def ContextFilter = """AlnContext: { Ref: "${reference}", LeftShift: -${params.poly_context},
RegexEnd: "[Aa]{${params.max_poly_run},}", RightShift: ${params.poly_context}, RegexEnd: "[Aa]{${params.max_poly_run},}",
Stranded: True,Invert: True, Tsv: "internal_priming_fail.tsv"} """ Stranded: True,Invert: True, Tsv: "internal_priming_fail.tsv"} """
""" """
seqkit fq2fa ${fastq_reads} -o "reads.fa"; seqkit fq2fa ${fastq_reads} -o "reads.fa";
minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} ${index} "reads.fa"\ minimap2 -t ${params.threads} -ax splice ${params.minimap2_opts} ${index} "reads.fa"\
| samtools view -q ${params.minimum_mapping_quality} -F 2304 -Sb -\ | samtools view -q ${params.minimum_mapping_quality} -F 2304 -Sb -\
| seqkit bam -j ${params.threads} -x -T '${ContextFilter}' -\ | seqkit bam -j ${params.threads} -x -T '${ContextFilter}' -\
| samtools sort -@ ${params.threads} -o "${sample_id}_reads_aln_sorted.bam" - ; | samtools sort -@ ${params.threads} -o "${sample_id}_reads_aln_sorted.bam" - ;
((cat "${sample_id}_reads_aln_sorted.bam" | seqkit bam -s -j ${params.threads} - 2>&1) | tee ${sample_id}_read_aln_stats.tsv ) || true ((cat "${sample_id}_reads_aln_sorted.bam" | seqkit bam -s -j ${params.threads} - 2>&1) | tee ${sample_id}_read_aln_stats.tsv ) || true
if [[ -s "internal_priming_fail.tsv" ]]; if [[ -s "internal_priming_fail.tsv" ]];
then then
tail -n +2 "internal_priming_fail.tsv" | awk '{{print ">" \$1 "\\n" \$4 }}' - > "context_internal_priming_fail_start.fasta" tail -n +2 "internal_priming_fail.tsv" | awk '{{print ">" \$1 "\\n" \$4 }}' - > "context_internal_priming_fail_start.fasta"
tail -n +2 "internal_priming_fail.tsv" | awk '{{print ">" \$1 "\\n" \$6 }}' - > "context_internal_priming_fail_end.fasta" tail -n +2 "internal_priming_fail.tsv" | awk '{{print ">" \$1 "\\n" \$6 }}' - > "context_internal_priming_fail_end.fasta"
fi fi
""" """
} }
workflow reference_assembly { workflow reference_assembly {