Snakefile 17.6 KB
Newer Older
Sander Bollen's avatar
Sander Bollen committed
1
import json
Sander Bollen's avatar
Sander Bollen committed
2
from functools import partial
Sander Bollen's avatar
Sander Bollen committed
3
from os.path import join, basename
Sander Bollen's avatar
Sander Bollen committed
4
5

from pyfaidx import Fasta
Sander Bollen's avatar
Sander Bollen committed
6
7
8
9
10
11
12
13

OUT_DIR = config.get("OUTPUT_DIR")
REFERENCE = config.get("REFERENCE")
JAVA = config.get("JAVA")
GATK = config.get("GATK")
DBSNP = config.get("DBSNP")
ONETHOUSAND = config.get("ONETHOUSAND")
HAPMAP = config.get("HAPMAP")
14
15
BED = config.get("BED", "")  # comma-separated list of BED files
REFFLAT = config.get("REFFLAT", "")  # comma-separated list of refFlat files
Sander Bollen's avatar
Sander Bollen committed
16
FEMALE_THRESHOLD = config.get("FEMALE_THRESHOLD", 0.6)
17
FASTQ_COUNT = config.get("FASTQ_COUNT")
Sander Bollen's avatar
Sander Bollen committed
18
MAX_BASES = config.get("MAX_BASES", "")
Sander Bollen's avatar
Sander Bollen committed
19

20
21
22
23
24
def fsrc_dir(*args):
    """Wrapper around snakemake's srcdir to work like os.path.join"""
    if len(args) == 1:
        return srcdir(args[0])
    return srcdir(join(*args))
Sander Bollen's avatar
Sander Bollen committed
25

26
27
28
covpy = fsrc_dir("src", "covstats.py")
colpy = fsrc_dir("src", "collect_stats.py")
mpy = fsrc_dir("src", "merge_stats.py")
Sander Bollen's avatar
Sander Bollen committed
29
seq = fsrc_dir("src", "seqtk.sh")
Sander Bollen's avatar
Sander Bollen committed
30

31
if FASTQ_COUNT is None:
32
    fqc = "python {0}".format(fsrc_dir("src", "fastq-count.py"))
33
34
35
else:
    fqc = FASTQ_COUNT

Sander Bollen's avatar
Sander Bollen committed
36
37
38
39
with open(config.get("SAMPLE_CONFIG")) as handle:
    SAMPLE_CONFIG = json.load(handle)
SAMPLES = SAMPLE_CONFIG['samples'].keys()

Sander Bollen's avatar
Sander Bollen committed
40
41
42
43
44
45
BEDS = BED.split(",")
REFFLATS = REFFLAT.split(",")

BASE_BEDS = [basename(x) for x in BEDS]
BASE_REFFLATS = [basename(x) for x in BEDS]

Sander Bollen's avatar
Sander Bollen committed
46
47

def split_genome(ref, approx_n_chunks=100):
Sander Bollen's avatar
Sander Bollen committed
48
49
50
51
52
53
54
55
    """
    Split genome in chunks.

    Chunks are strings in the format: `<ctg>:<start>-<end>`
    These follow the region string format as used by htslib,
    which uses _1_-based indexing.
    See: http://www.htslib.org/doc/tabix.html
    """
Sander Bollen's avatar
Sander Bollen committed
56
57
58
59
60
    fa = Fasta(ref)
    tot_size = sum([len(x) for x in fa.records.values()])
    chunk_size = tot_size//approx_n_chunks
    chunks = []
    for chrom_name, chrom_value in fa.records.items():
Sander Bollen's avatar
Sander Bollen committed
61
        pos = 1
Sander Bollen's avatar
Sander Bollen committed
62
63
64
65
66
67
68
69
70
71
72
73
74
        while pos <= len(chrom_value):
            end = pos+chunk_size
            if end <= len(chrom_value):
                chunk = "{0}:{1}-{2}".format(chrom_name, pos, end)
            else:
                chunk = "{0}:{1}-{2}".format(chrom_name, pos, len(chrom_value))
            chunks.append(chunk)
            pos = end
    return chunks

CHUNKS = split_genome(REFERENCE)


Sander Bollen's avatar
Sander Bollen committed
75
76
77
def out_path(path):
    return join(OUT_DIR, path)

Sander Bollen's avatar
Sander Bollen committed
78
79
def get_r(strand, wildcards):
    """Get fastq files on a single strand for a sample"""
Sander Bollen's avatar
Sander Bollen committed
80
    s = SAMPLE_CONFIG['samples'].get(wildcards.sample)
Sander Bollen's avatar
Sander Bollen committed
81
    rs = []
82
    for l in sorted(s['libraries'].keys()):
Sander Bollen's avatar
Sander Bollen committed
83
84
        rs.append(s['libraries'][l][strand])
    return rs
Sander Bollen's avatar
Sander Bollen committed
85

Sander Bollen's avatar
Sander Bollen committed
86
87
get_r1 = partial(get_r, "R1")
get_r2 = partial(get_r, "R2")
Sander Bollen's avatar
Sander Bollen committed
88
89


Sander Bollen's avatar
Sander Bollen committed
90
def get_bedpath(wildcards):
Sander Bollen's avatar
Sander Bollen committed
91
92
    """Get absolute path of a bed file"""
    return next(x for x in BEDS if basename(x) == wildcards.bed)
Sander Bollen's avatar
Sander Bollen committed
93
94
95


def get_refflatpath(wildcards):
Sander Bollen's avatar
Sander Bollen committed
96
97
    """Get absolute path of a refflat file"""
    return next(x for x in REFFLATS if basename(x) == wildcards.refflat)
Sander Bollen's avatar
Sander Bollen committed
98
99


Sander Bollen's avatar
Sander Bollen committed
100
def sample_gender(wildcards):
Sander Bollen's avatar
Sander Bollen committed
101
    """Get sample gender"""
Sander Bollen's avatar
Sander Bollen committed
102
103
104
105
    sam = SAMPLE_CONFIG['samples'].get(wildcards.sample)
    return sam.get("gender", "null")


Sander Bollen's avatar
Sander Bollen committed
106
107
108
109
110
111
def metrics(do_metrics=True):
    if not do_metrics:
        return ""

    fqcr = expand(out_path("{sample}/pre_process/raw_fastqc/.done.txt"),
                  sample=SAMPLES)
Sander Bollen's avatar
Sander Bollen committed
112
    fqcm = expand(out_path("{sample}/pre_process/merged_fastqc/{sample}.merged_R1_fastqc.zip"),
Sander Bollen's avatar
Sander Bollen committed
113
                  sample=SAMPLES)
Sander Bollen's avatar
Sander Bollen committed
114
    fqcp = expand(out_path("{sample}/pre_process/postqc_fastqc/{sample}.cutadapt_R1_fastqc.zip"),
Sander Bollen's avatar
Sander Bollen committed
115
                  sample=SAMPLES)
Sander Bollen's avatar
Sander Bollen committed
116
117
    coverage_stats = expand(out_path("{sample}/coverage/{ref}.coverages.tsv"),
                            sample=SAMPLES, ref=BASE_REFFLATS)
Sander Bollen's avatar
Sander Bollen committed
118
    stats = out_path("stats.json")
Sander Bollen's avatar
Sander Bollen committed
119
    return  fqcr + fqcm + fqcp + coverage_stats + [stats]
Sander Bollen's avatar
Sander Bollen committed
120
121


Sander Bollen's avatar
Sander Bollen committed
122
123
rule all:
    input:
Sander Bollen's avatar
Sander Bollen committed
124
125
        combined=out_path("multisample/genotyped.vcf.gz"),
        stats=metrics()
Sander Bollen's avatar
Sander Bollen committed
126

Sander Bollen's avatar
Sander Bollen committed
127
128
129
130
131
132

rule create_markdup_tmp:
    """Create tmp directory for mark duplicates"""
    output: ancient(out_path("tmp"))
    shell: "mkdir -p {output}"

Sander Bollen's avatar
Sander Bollen committed
133
rule genome:
Sander Bollen's avatar
Sander Bollen committed
134
    """Create genome file as used by bedtools"""
Sander Bollen's avatar
Sander Bollen committed
135
136
137
138
139
    input: REFERENCE
    output: out_path("current.genome")
    shell: "awk -v OFS='\t' {{'print $1,$2'}} {input}.fai > {output}"

rule merge_r1:
Sander Bollen's avatar
Sander Bollen committed
140
    """Merge all forward fastq files into one"""
Sander Bollen's avatar
Sander Bollen committed
141
142
143
144
145
    input: get_r1
    output: temp(out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"))
    shell: "cat {input} > {output}"

rule merge_r2:
Sander Bollen's avatar
Sander Bollen committed
146
    """Merge all reverse fastq files into one"""
Sander Bollen's avatar
Sander Bollen committed
147
148
149
150
    input: get_r2
    output: temp(out_path("{sample}/pre_process/{sample}.merged_R2.fastq.gz"))
    shell: "cat {input} > {output}"

Sander Bollen's avatar
Sander Bollen committed
151
152

rule seqtk_r1:
Sander Bollen's avatar
Sander Bollen committed
153
    """Either subsample or link forward fastq file"""
Sander Bollen's avatar
Sander Bollen committed
154
155
    input:
        stats=out_path("{sample}/pre_process/{sample}.preqc_count.json"),
Sander Bollen's avatar
Sander Bollen committed
156
157
        fastq=out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"),
        seqtk=seq
Sander Bollen's avatar
Sander Bollen committed
158
    params:
Sander Bollen's avatar
Sander Bollen committed
159
        max_bases=str(MAX_BASES)
Sander Bollen's avatar
Sander Bollen committed
160
161
    output:
        fastq=temp(out_path("{sample}/pre_process/{sample}.sampled_R1.fastq.gz"))
Sander Bollen's avatar
Sander Bollen committed
162
    conda: "envs/seqtk.yml"
Sander Bollen's avatar
Sander Bollen committed
163
    shell: "bash {input.seqtk} {input.stats} {input.fastq} {output.fastq} {params.max_bases}"
Sander Bollen's avatar
Sander Bollen committed
164
165
166


rule seqtk_r2:
Sander Bollen's avatar
Sander Bollen committed
167
    """Either subsample or link reverse fastq file"""
Sander Bollen's avatar
Sander Bollen committed
168
169
    input:
        stats = out_path("{sample}/pre_process/{sample}.preqc_count.json"),
Sander Bollen's avatar
Sander Bollen committed
170
171
        fastq = out_path("{sample}/pre_process/{sample}.merged_R2.fastq.gz"),
        seqtk=seq
Sander Bollen's avatar
Sander Bollen committed
172
    params:
Sander Bollen's avatar
Sander Bollen committed
173
        max_bases =str(MAX_BASES)
Sander Bollen's avatar
Sander Bollen committed
174
175
    output:
        fastq = temp(out_path("{sample}/pre_process/{sample}.sampled_R2.fastq.gz"))
Sander Bollen's avatar
Sander Bollen committed
176
    conda: "envs/seqtk.yml"
Sander Bollen's avatar
Sander Bollen committed
177
    shell: "bash {input.seqtk} {input.stats} {input.fastq} {output.fastq} {params.max_bases}"
Sander Bollen's avatar
Sander Bollen committed
178
179


180
# contains original merged fastq files as input to prevent them from being prematurely deleted
Sander Bollen's avatar
Sander Bollen committed
181
rule sickle:
Sander Bollen's avatar
Sander Bollen committed
182
    """Trim fastq files"""
Sander Bollen's avatar
Sander Bollen committed
183
    input:
Sander Bollen's avatar
Sander Bollen committed
184
        r1 = out_path("{sample}/pre_process/{sample}.sampled_R1.fastq.gz"),
185
186
187
        r2 = out_path("{sample}/pre_process/{sample}.sampled_R2.fastq.gz"),
        rr1 = out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"),
        rr2 = out_path("{sample}/pre_process/{sample}.merged_R2.fastq.gz")
Sander Bollen's avatar
Sander Bollen committed
188
189
190
191
192
    output:
        r1 = temp(out_path("{sample}/pre_process/{sample}.trimmed_R1.fastq")),
        r2 = temp(out_path("{sample}/pre_process/{sample}.trimmed_R2.fastq")),
        s = out_path("{sample}/pre_process/{sample}.trimmed_singles.fastq"),
    conda: "envs/sickle.yml"
Sander Bollen's avatar
Sander Bollen committed
193
    shell: "sickle pe -f {input.r1} -r {input.r2} -t sanger -o {output.r1} "
Sander Bollen's avatar
Sander Bollen committed
194
195
196
           "-p {output.r2} -s {output.s}"

rule cutadapt:
Sander Bollen's avatar
Sander Bollen committed
197
    """Clip fastq files"""
Sander Bollen's avatar
Sander Bollen committed
198
199
200
201
202
203
204
    input:
        r1 = out_path("{sample}/pre_process/{sample}.trimmed_R1.fastq"),
        r2 = out_path("{sample}/pre_process/{sample}.trimmed_R2.fastq")
    output:
        r1 = temp(out_path("{sample}/pre_process/{sample}.cutadapt_R1.fastq")),
        r2 = temp(out_path("{sample}/pre_process/{sample}.cutadapt_R2.fastq"))
    conda: "envs/cutadapt.yml"
Sander Bollen's avatar
Sander Bollen committed
205
    shell: "cutadapt -a AGATCGGAAGAG -A AGATCGGAAGAG -m 1 -o {output.r1} "
Sander Bollen's avatar
Sander Bollen committed
206
207
208
           "{input.r1} -p {output.r2} {input.r2}"

rule align:
Sander Bollen's avatar
Sander Bollen committed
209
    """Align fastq files"""
Sander Bollen's avatar
Sander Bollen committed
210
211
212
213
214
215
216
217
    input:
        r1 = out_path("{sample}/pre_process/{sample}.cutadapt_R1.fastq"),
        r2 = out_path("{sample}/pre_process/{sample}.cutadapt_R2.fastq"),
        ref = REFERENCE
    params:
        rg = "@RG\\tID:{sample}_lib1\\tSM:{sample}\\tPL:ILLUMINA"
    output: temp(out_path("{sample}/bams/{sample}.sorted.bam"))
    conda: "envs/bwa.yml"
Sander Bollen's avatar
Sander Bollen committed
218
219
    shell: "bwa mem -t 8 -R '{params.rg}' {input.ref} {input.r1} {input.r2} "
           "| picard SortSam CREATE_INDEX=TRUE TMP_DIR=null "
Sander Bollen's avatar
Sander Bollen committed
220
221
222
           "INPUT=/dev/stdin OUTPUT={output} SORT_ORDER=coordinate"

rule markdup:
Sander Bollen's avatar
Sander Bollen committed
223
    """Mark duplicates in BAM file"""
Sander Bollen's avatar
Sander Bollen committed
224
225
    input:
        bam = out_path("{sample}/bams/{sample}.sorted.bam"),
226
        tmp = ancient(out_path("tmp"))
Sander Bollen's avatar
Sander Bollen committed
227
    output:
Sander Bollen's avatar
Sander Bollen committed
228
        bam = out_path("{sample}/bams/{sample}.markdup.bam"),
Sander Bollen's avatar
Sander Bollen committed
229
230
        metrics = out_path("{sample}/bams/{sample}.markdup.metrics")
    conda: "envs/picard.yml"
Sander Bollen's avatar
Sander Bollen committed
231
232
233
    shell: "picard MarkDuplicates CREATE_INDEX=TRUE TMP_DIR={input.tmp} "
           "INPUT={input.bam} OUTPUT={output.bam} "
           "METRICS_FILE={output.metrics} "
Sander Bollen's avatar
Sander Bollen committed
234
235
236
           "MAX_FILE_HANDLES_FOR_READ_ENDS_MAP=500"

rule baserecal:
Sander Bollen's avatar
Sander Bollen committed
237
    """Base recalibrated BAM files"""
Sander Bollen's avatar
Sander Bollen committed
238
239
240
241
242
243
244
245
246
247
248
    input:
        bam = out_path("{sample}/bams/{sample}.markdup.bam"),
        java = JAVA,
        gatk = GATK,
        ref = REFERENCE,
        dbsnp = DBSNP,
        one1kg = ONETHOUSAND,
        hapmap = HAPMAP
    output:
        grp = out_path("{sample}/bams/{sample}.baserecal.grp")
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
249
250
251
252
253
    shell: "{input.java} -jar {input.gatk} -T BaseRecalibrator "
           "-I {input.bam} -o {output.grp} -nct 8 -R {input.ref} "
           "-cov ReadGroupCovariate -cov QualityScoreCovariate "
           "-cov CycleCovariate -cov ContextCovariate -knownSites "
           "{input.dbsnp} -knownSites {input.one1kg} "
Sander Bollen's avatar
Sander Bollen committed
254
255
           "-knownSites {input.hapmap}"

Sander Bollen's avatar
Sander Bollen committed
256
rule gvcf_scatter:
Sander Bollen's avatar
Sander Bollen committed
257
    """Run HaplotypeCaller in GVCF mode by chunk"""
Sander Bollen's avatar
Sander Bollen committed
258
    input:
Sander Bollen's avatar
Sander Bollen committed
259
260
        bam=out_path("{sample}/bams/{sample}.markdup.bam"),
        bqsr=out_path("{sample}/bams/{sample}.baserecal.grp"),
Sander Bollen's avatar
Sander Bollen committed
261
        dbsnp=DBSNP,
Sander Bollen's avatar
Sander Bollen committed
262
263
        ref=REFERENCE,
        gatk=GATK
Sander Bollen's avatar
Sander Bollen committed
264
    params:
Sander Bollen's avatar
Sander Bollen committed
265
        chunk="{chunk}"
Sander Bollen's avatar
Sander Bollen committed
266
    output:
Sander Bollen's avatar
Sander Bollen committed
267
        gvcf=out_path("{sample}/vcf/{sample}.{chunk}.part.vcf.gz")
Sander Bollen's avatar
Sander Bollen committed
268
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
269
270
271
272
    shell: "java -jar -Xmx4G {input.gatk} -T HaplotypeCaller -ERC GVCF -I "
           "{input.bam} -R {input.ref} -D {input.dbsnp} "
           "-L '{params.chunk}' -o '{output.gvcf}' "
           "-variant_index_type LINEAR -variant_index_parameter 128000 "
Sander Bollen's avatar
Sander Bollen committed
273
           "-BQSR {input.bqsr}"
Sander Bollen's avatar
Sander Bollen committed
274
275


Sander Bollen's avatar
Sander Bollen committed
276
rule gvcf_gather:
Sander Bollen's avatar
Sander Bollen committed
277
    """Gather all gvcf scatters"""
Sander Bollen's avatar
Sander Bollen committed
278
    input:
Sander Bollen's avatar
Sander Bollen committed
279
280
        gvcfs=expand(out_path("{{sample}}/vcf/{{sample}}.{chunk}.part.vcf.gz"),
                     chunk=CHUNKS),
Sander Bollen's avatar
Sander Bollen committed
281
        ref=REFERENCE,
Sander Bollen's avatar
Sander Bollen committed
282
        gatk=GATK
Sander Bollen's avatar
Sander Bollen committed
283
    params:
Sander Bollen's avatar
Sander Bollen committed
284
        gvcfs="' -V '".join(expand(out_path("{{sample}}/vcf/{{sample}}.{chunk}.part.vcf.gz"),
Sander Bollen's avatar
Sander Bollen committed
285
                                 chunk=CHUNKS))
Sander Bollen's avatar
Sander Bollen committed
286
287
    output:
        gvcf=out_path("{sample}/vcf/{sample}.g.vcf.gz")
Sander Bollen's avatar
Sander Bollen committed
288
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
289
290
    shell: "java -Xmx4G -cp {input.gatk} org.broadinstitute.gatk.tools.CatVariants "
           "-R {input.ref} -V '{params.gvcfs}' -out {output.gvcf} "
Sander Bollen's avatar
Sander Bollen committed
291
292
293
294
           "-assumeSorted"


rule genotype_scatter:
Sander Bollen's avatar
Sander Bollen committed
295
    """Run GATK's GenotypeGVCFs by chunk"""
Sander Bollen's avatar
Sander Bollen committed
296
    input:
Sander Bollen's avatar
Sander Bollen committed
297
298
        gvcfs = expand(out_path("{sample}/vcf/{sample}.g.vcf.gz"),
                       sample=SAMPLES),
Sander Bollen's avatar
Sander Bollen committed
299
        ref=REFERENCE,
Sander Bollen's avatar
Sander Bollen committed
300
301
302
        gatk=GATK
    params:
        li=" -V ".join(expand(out_path("{sample}/vcf/{sample}.g.vcf.gz"),
Sander Bollen's avatar
Sander Bollen committed
303
                              sample=SAMPLES)),
Sander Bollen's avatar
Sander Bollen committed
304
305
306
        chunk="{chunk}"
    output:
        vcf=out_path("multisample/genotype.{chunk}.part.vcf.gz")
Sander Bollen's avatar
Sander Bollen committed
307
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
308
    shell: "java -jar -Xmx4G {input.gatk} -T GenotypeGVCFs -R {input.ref} "
Sander Bollen's avatar
Sander Bollen committed
309
           "-V {params.li} -L '{params.chunk}' -o '{output.vcf}'"
Sander Bollen's avatar
Sander Bollen committed
310
311
312


rule genotype_gather:
Sander Bollen's avatar
Sander Bollen committed
313
    """Gather all genotyping scatters"""
Sander Bollen's avatar
Sander Bollen committed
314
315
316
317
318
    input:
        vcfs=expand(out_path("multisample/genotype.{chunk}.part.vcf.gz"),
                    chunk=CHUNKS),
        ref=REFERENCE,
        gatk=GATK
Sander Bollen's avatar
Sander Bollen committed
319
    params:
Sander Bollen's avatar
Sander Bollen committed
320
        vcfs="' -V '".join(expand(out_path("multisample/genotype.{chunk}.part.vcf.gz"),
Sander Bollen's avatar
Sander Bollen committed
321
                                chunk=CHUNKS))
Sander Bollen's avatar
Sander Bollen committed
322
323
324
    output:
        combined=out_path("multisample/genotyped.vcf.gz")
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
325
326
    shell: "java -Xmx4G -cp {input.gatk} org.broadinstitute.gatk.tools.CatVariants "
           "-R {input.ref} -V '{params.vcfs}' -out {output.combined} "
Sander Bollen's avatar
Sander Bollen committed
327
           "-assumeSorted"
Sander Bollen's avatar
Sander Bollen committed
328
329


Sander Bollen's avatar
Sander Bollen committed
330
331
332
## bam metrics

rule mapped_num:
Sander Bollen's avatar
Sander Bollen committed
333
    """Calculate number of mapped reads"""
Sander Bollen's avatar
Sander Bollen committed
334
335
336
337
338
339
340
341
342
    input:
        bam=out_path("{sample}/bams/{sample}.sorted.bam")
    output:
        num=out_path("{sample}/bams/{sample}.mapped.num")
    conda: "envs/samtools.yml"
    shell: "samtools view -F 4 {input.bam} | wc -l > {output.num}"


rule mapped_basenum:
Sander Bollen's avatar
Sander Bollen committed
343
    """Calculate number of mapped bases"""
Sander Bollen's avatar
Sander Bollen committed
344
345
346
347
348
    input:
        bam=out_path("{sample}/bams/{sample}.sorted.bam")
    output:
        num=out_path("{sample}/bams/{sample}.mapped.basenum")
    conda: "envs/samtools.yml"
Sander Bollen's avatar
Sander Bollen committed
349
    shell: "samtools view -F 4 {input.bam} | cut -f10 | wc -c > {output.num}"
Sander Bollen's avatar
Sander Bollen committed
350
351
352


rule unique_num:
Sander Bollen's avatar
Sander Bollen committed
353
    """Calculate number of unique reads"""
Sander Bollen's avatar
Sander Bollen committed
354
355
356
357
358
    input:
        bam=out_path("{sample}/bams/{sample}.markdup.bam")
    output:
        num=out_path("{sample}/bams/{sample}.unique.num")
    conda: "envs/samtools.yml"
359
    shell: "samtools view -F 4 -F 1024 {input.bam} | wc -l > {output.num}"
Sander Bollen's avatar
Sander Bollen committed
360
361
362


rule usable_basenum:
Sander Bollen's avatar
Sander Bollen committed
363
    """Calculate number of bases on unique reads"""
Sander Bollen's avatar
Sander Bollen committed
364
365
366
367
368
369
    input:
        bam=out_path("{sample}/bams/{sample}.markdup.bam")
    output:
        num=out_path("{sample}/bams/{sample}.usable.basenum")
    conda: "envs/samtools.yml"
    shell: "samtools view -F 4 -F 1024 {input.bam} | cut -f10 | wc -c > {output.num}"
Sander Bollen's avatar
Sander Bollen committed
370
371
372
373


## fastqc

Sander Bollen's avatar
Sander Bollen committed
374
rule fastqc_raw:
Sander Bollen's avatar
Sander Bollen committed
375
    """Run fastqc on raw fastq files"""
Sander Bollen's avatar
Sander Bollen committed
376
    input:
Sander Bollen's avatar
Sander Bollen committed
377
        r1=get_r1,
Sander Bollen's avatar
Sander Bollen committed
378
379
380
381
382
        r2=get_r2
    params:
        odir=out_path("{sample}/pre_process/raw_fastqc")
    output:
        aux=out_path("{sample}/pre_process/raw_fastqc/.done.txt")
383
    conda: "envs/fastqc.yml"
Sander Bollen's avatar
Sander Bollen committed
384
385
386
    shell: "fastqc -o {params.odir} {input.r1} {input.r2} && echo 'done' > {output.aux}"


Sander Bollen's avatar
Sander Bollen committed
387
rule fastqc_merged:
Sander Bollen's avatar
Sander Bollen committed
388
    """Run fastqc on merged fastq files"""
Sander Bollen's avatar
Sander Bollen committed
389
    input:
Sander Bollen's avatar
Sander Bollen committed
390
        r1=out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"),
Sander Bollen's avatar
Sander Bollen committed
391
392
393
394
        r2=out_path("{sample}/pre_process/{sample}.merged_R2.fastq.gz")
    params:
        odir=out_path("{sample}/pre_process/merged_fastqc")
    output:
Sander Bollen's avatar
Sander Bollen committed
395
396
        r1=out_path("{sample}/pre_process/merged_fastqc/{sample}.merged_R1_fastqc.zip"),
        r2=out_path("{sample}/pre_process/merged_fastqc/{sample}.merged_R2_fastqc.zip")
397
    conda: "envs/fastqc.yml"
Sander Bollen's avatar
Sander Bollen committed
398
    shell: "fastqc -o {params.odir} {input.r1} {input.r2}"
Sander Bollen's avatar
Sander Bollen committed
399
400


Sander Bollen's avatar
Sander Bollen committed
401
rule fastqc_postqc:
Sander Bollen's avatar
Sander Bollen committed
402
    """Run fastqc on fastq files post pre-processing"""
Sander Bollen's avatar
Sander Bollen committed
403
    input:
Sander Bollen's avatar
Sander Bollen committed
404
        r1=out_path("{sample}/pre_process/{sample}.cutadapt_R1.fastq"),
Sander Bollen's avatar
Sander Bollen committed
405
406
407
408
        r2=out_path("{sample}/pre_process/{sample}.cutadapt_R2.fastq")
    params:
        odir=out_path("{sample}/pre_process/postqc_fastqc")
    output:
Sander Bollen's avatar
Sander Bollen committed
409
        r1=out_path("{sample}/pre_process/postqc_fastqc/{sample}.cutadapt_R1_fastqc.zip"),
Sander Bollen's avatar
Sander Bollen committed
410
        r2=out_path("{sample}/pre_process/postqc_fastqc/{sample}.cutadapt_R2_fastqc.zip")
411
    conda: "envs/fastqc.yml"
Sander Bollen's avatar
Sander Bollen committed
412
    shell: "fastqc -o {params.odir} {input.r1} {input.r2}"
Sander Bollen's avatar
Sander Bollen committed
413
414
415
416
417


## fastq-count

rule fqcount_preqc:
Sander Bollen's avatar
Sander Bollen committed
418
    """Calculate number of reads and bases before pre-processing"""
Sander Bollen's avatar
Sander Bollen committed
419
420
421
    input:
        r1=out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"),
        r2=out_path("{sample}/pre_process/{sample}.merged_R2.fastq.gz")
422
    params:
423
        fastqcount=fqc
Sander Bollen's avatar
Sander Bollen committed
424
425
    output:
        out_path("{sample}/pre_process/{sample}.preqc_count.json")
426
    shell: "{params.fastqcount} {input.r1} {input.r2} > {output}"
Sander Bollen's avatar
Sander Bollen committed
427
428
429


rule fqcount_postqc:
Sander Bollen's avatar
Sander Bollen committed
430
    """Calculate number of reads and bases after pre-processing"""
Sander Bollen's avatar
Sander Bollen committed
431
432
433
    input:
        r1=out_path("{sample}/pre_process/{sample}.cutadapt_R1.fastq"),
        r2=out_path("{sample}/pre_process/{sample}.cutadapt_R2.fastq")
434
    params:
435
        fastqcount=fqc
Sander Bollen's avatar
Sander Bollen committed
436
437
    output:
        out_path("{sample}/pre_process/{sample}.postqc_count.json")
Sander Bollen's avatar
Sander Bollen committed
438
439
440
441
442
443
    shell: "{params.fastqcount} {input.r1} {input.r2} > {output}"


## coverages

rule covstats:
Sander Bollen's avatar
Sander Bollen committed
444
    """Calculate coverage statistics on bam file"""
Sander Bollen's avatar
Sander Bollen committed
445
446
447
448
449
450
451
452
453
    input:
        bam=out_path("{sample}/bams/{sample}.markdup.bam"),
        genome=out_path("current.genome"),
        covpy=covpy,
        bed=get_bedpath
    params:
        subt="Sample {sample}"
    output:
        covj=out_path("{sample}/coverage/{bed}.covstats.json"),
Sander Bollen's avatar
Sander Bollen committed
454
        covp=out_path("{sample}/coverage/{bed}.covstats.png")
Sander Bollen's avatar
Sander Bollen committed
455
    conda: "envs/covstat.yml"
Sander Bollen's avatar
Sander Bollen committed
456
457
    shell: "bedtools coverage -sorted -g {input.genome} -a {input.bed} -b {input.bam} "
           "-d  | python {input.covpy} - --plot {output.covp} "
Sander Bollen's avatar
Sander Bollen committed
458
           "--title 'Targets coverage' --subtitle '{params.subt}' > {output.covj}"
Sander Bollen's avatar
Sander Bollen committed
459
460


Sander Bollen's avatar
Sander Bollen committed
461
462
463
464
465
466
467
468
469
470
471
rule vtools_coverage:
    """Calculate coverage statistics per transcript"""
    input:
        gvcf=out_path("{sample}/vcf/{sample}.g.vcf.gz")
        ref=get_refflatpath
    output:
        tsv=out_path("{sample}/coverage/{ref}.coverages.tsv")
    conda: "envs/vcfstats.yml"
    shell: "vtools-gcoverage -I {input.gvcf} -R {input.ref} > {output.tsv}"


Sander Bollen's avatar
Sander Bollen committed
472
473
474
## vcfstats

rule vcfstats:
Sander Bollen's avatar
Sander Bollen committed
475
    """Calculate vcf statistics"""
Sander Bollen's avatar
Sander Bollen committed
476
    input:
Sander Bollen's avatar
Sander Bollen committed
477
        vcf=out_path("multisample/genotyped.vcf.gz")
Sander Bollen's avatar
Sander Bollen committed
478
479
480
    output:
        stats=out_path("multisample/vcfstats.json")
    conda: "envs/vcfstats.yml"
Sander Bollen's avatar
Sander Bollen committed
481
    shell: "vtools-stats -i {input.vcf} > {output.stats}"
Sander Bollen's avatar
Sander Bollen committed
482
483


Sander Bollen's avatar
Sander Bollen committed
484
485
## collection
rule collectstats:
Sander Bollen's avatar
Sander Bollen committed
486
    """Collect all stats for a particular sample"""
Sander Bollen's avatar
Sander Bollen committed
487
488
489
490
491
492
493
494
495
496
    input:
        preqc=out_path("{sample}/pre_process/{sample}.preqc_count.json"),
        postq=out_path("{sample}/pre_process/{sample}.postqc_count.json"),
        mnum=out_path("{sample}/bams/{sample}.mapped.num"),
        mbnum=out_path("{sample}/bams/{sample}.mapped.basenum"),
        unum=out_path("{sample}/bams/{sample}.unique.num"),
        ubnum=out_path("{sample}/bams/{sample}.usable.basenum"),
        cov=expand(out_path("{{sample}}/coverage/{bed}.covstats.json"), bed=BASE_BEDS),
        colpy=colpy
    params:
Sander Bollen's avatar
Sander Bollen committed
497
        sample_name="{sample}",
Sander Bollen's avatar
Sander Bollen committed
498
        fthresh=FEMALE_THRESHOLD
Sander Bollen's avatar
Sander Bollen committed
499
500
    output:
        out_path("{sample}/{sample}.stats.json")
501
    conda: "envs/collectstats.yml"
Sander Bollen's avatar
Sander Bollen committed
502
503
504
505
    shell: "python {input.colpy} --sample-name {params.sample_name} "
           "--pre-qc-fastq {input.preqc} --post-qc-fastq {input.postq} "
           "--mapped-num {input.mnum} --mapped-basenum {input.mbnum} "
           "--unique-num {input.unum} --usable-basenum {input.ubnum} "
506
           "--female-threshold {params.fthresh} {input.cov} > {output}"
Sander Bollen's avatar
Sander Bollen committed
507
508

rule merge_stats:
Sander Bollen's avatar
Sander Bollen committed
509
    """Merge all stats of all samples"""
Sander Bollen's avatar
Sander Bollen committed
510
511
512
513
514
515
516
517
    input:
        cols=expand(out_path("{sample}/{sample}.stats.json"), sample=SAMPLES),
        vstat=out_path("multisample/vcfstats.json"),
        mpy=mpy
    output:
        stats=out_path("stats.json")
    conda: "envs/collectstats.yml"
    shell: "python {input.mpy} --vcfstats {input.vstat} {input.cols} > {output.stats}"