Snakefile 17 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
    stats = out_path("stats.json")
Sander Bollen's avatar
Sander Bollen committed
117
    return  fqcr + fqcm + fqcp + [stats]
Sander Bollen's avatar
Sander Bollen committed
118
119


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

rule genome:
Sander Bollen's avatar
Sander Bollen committed
126
    """Create genome file as used by bedtools"""
Sander Bollen's avatar
Sander Bollen committed
127
128
129
130
131
    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
132
    """Merge all forward fastq files into one"""
Sander Bollen's avatar
Sander Bollen committed
133
134
135
136
137
    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
138
    """Merge all reverse fastq files into one"""
Sander Bollen's avatar
Sander Bollen committed
139
140
141
142
    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
143
144

rule seqtk_r1:
Sander Bollen's avatar
Sander Bollen committed
145
    """Either subsample or link forward fastq file"""
Sander Bollen's avatar
Sander Bollen committed
146
147
    input:
        stats=out_path("{sample}/pre_process/{sample}.preqc_count.json"),
Sander Bollen's avatar
Sander Bollen committed
148
149
        fastq=out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"),
        seqtk=seq
Sander Bollen's avatar
Sander Bollen committed
150
151
152
153
    params:
        max_bases=MAX_BASES
    output:
        fastq=temp(out_path("{sample}/pre_process/{sample}.sampled_R1.fastq.gz"))
Sander Bollen's avatar
Sander Bollen committed
154
    conda: "envs/seqtk.yml"
Sander Bollen's avatar
Sander Bollen committed
155
    shell: "bash {input.seqtk} {input.stats} {input.fastq} {output.fastq} {params.max_bases}"
Sander Bollen's avatar
Sander Bollen committed
156
157
158


rule seqtk_r2:
Sander Bollen's avatar
Sander Bollen committed
159
    """Either subsample or link reverse fastq file"""
Sander Bollen's avatar
Sander Bollen committed
160
161
    input:
        stats = out_path("{sample}/pre_process/{sample}.preqc_count.json"),
Sander Bollen's avatar
Sander Bollen committed
162
163
        fastq = out_path("{sample}/pre_process/{sample}.merged_R2.fastq.gz"),
        seqtk=seq
Sander Bollen's avatar
Sander Bollen committed
164
165
166
167
    params:
        max_bases = MAX_BASES
    output:
        fastq = temp(out_path("{sample}/pre_process/{sample}.sampled_R2.fastq.gz"))
Sander Bollen's avatar
Sander Bollen committed
168
    conda: "envs/seqtk.yml"
Sander Bollen's avatar
Sander Bollen committed
169
    shell: "bash {input.seqtk} {input.stats} {input.fastq} {output.fastq} {params.max_bases}"
Sander Bollen's avatar
Sander Bollen committed
170
171


172
# contains original merged fastq files as input to prevent them from being prematurely deleted
Sander Bollen's avatar
Sander Bollen committed
173
rule sickle:
Sander Bollen's avatar
Sander Bollen committed
174
    """Trim fastq files"""
Sander Bollen's avatar
Sander Bollen committed
175
    input:
Sander Bollen's avatar
Sander Bollen committed
176
        r1 = out_path("{sample}/pre_process/{sample}.sampled_R1.fastq.gz"),
177
178
179
        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
180
181
182
183
184
    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
185
    shell: "sickle pe -f {input.r1} -r {input.r2} -t sanger -o {output.r1} "
Sander Bollen's avatar
Sander Bollen committed
186
187
188
           "-p {output.r2} -s {output.s}"

rule cutadapt:
Sander Bollen's avatar
Sander Bollen committed
189
    """Clip fastq files"""
Sander Bollen's avatar
Sander Bollen committed
190
191
192
193
194
195
196
    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
197
    shell: "cutadapt -a AGATCGGAAGAG -A AGATCGGAAGAG -m 1 -o {output.r1} "
Sander Bollen's avatar
Sander Bollen committed
198
199
200
           "{input.r1} -p {output.r2} {input.r2}"

rule align:
Sander Bollen's avatar
Sander Bollen committed
201
    """Align fastq files"""
Sander Bollen's avatar
Sander Bollen committed
202
203
204
205
206
207
208
209
    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
210
211
    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
212
213
214
           "INPUT=/dev/stdin OUTPUT={output} SORT_ORDER=coordinate"

rule markdup:
Sander Bollen's avatar
Sander Bollen committed
215
    """Mark duplicates in BAM file"""
Sander Bollen's avatar
Sander Bollen committed
216
217
    input:
        bam = out_path("{sample}/bams/{sample}.sorted.bam"),
218
        tmp = ancient(out_path("tmp"))
Sander Bollen's avatar
Sander Bollen committed
219
    output:
Sander Bollen's avatar
Sander Bollen committed
220
        bam = out_path("{sample}/bams/{sample}.markdup.bam"),
Sander Bollen's avatar
Sander Bollen committed
221
222
        metrics = out_path("{sample}/bams/{sample}.markdup.metrics")
    conda: "envs/picard.yml"
Sander Bollen's avatar
Sander Bollen committed
223
224
225
    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
226
227
228
           "MAX_FILE_HANDLES_FOR_READ_ENDS_MAP=500"

rule baserecal:
Sander Bollen's avatar
Sander Bollen committed
229
    """Base recalibrated BAM files"""
Sander Bollen's avatar
Sander Bollen committed
230
231
232
233
234
235
236
237
238
239
240
    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
241
242
243
244
245
    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
246
247
           "-knownSites {input.hapmap}"

Sander Bollen's avatar
Sander Bollen committed
248
rule gvcf_scatter:
Sander Bollen's avatar
Sander Bollen committed
249
    """Run HaplotypeCaller in GVCF mode by chunk"""
Sander Bollen's avatar
Sander Bollen committed
250
    input:
Sander Bollen's avatar
Sander Bollen committed
251
252
        bam=out_path("{sample}/bams/{sample}.markdup.bam"),
        bqsr=out_path("{sample}/bams/{sample}.baserecal.grp"),
Sander Bollen's avatar
Sander Bollen committed
253
        dbsnp=DBSNP,
Sander Bollen's avatar
Sander Bollen committed
254
255
        ref=REFERENCE,
        gatk=GATK
Sander Bollen's avatar
Sander Bollen committed
256
    params:
Sander Bollen's avatar
Sander Bollen committed
257
        chunk="{chunk}"
Sander Bollen's avatar
Sander Bollen committed
258
    output:
Sander Bollen's avatar
Sander Bollen committed
259
        gvcf=out_path("{sample}/vcf/{sample}.{chunk}.part.vcf.gz")
Sander Bollen's avatar
Sander Bollen committed
260
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
261
262
263
264
    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
265
           "-BQSR {input.bqsr}"
Sander Bollen's avatar
Sander Bollen committed
266
267


Sander Bollen's avatar
Sander Bollen committed
268
rule gvcf_gather:
Sander Bollen's avatar
Sander Bollen committed
269
    """Gather all gvcf scatters"""
Sander Bollen's avatar
Sander Bollen committed
270
    input:
Sander Bollen's avatar
Sander Bollen committed
271
272
        gvcfs=expand(out_path("{{sample}}/vcf/{{sample}}.{chunk}.part.vcf.gz"),
                     chunk=CHUNKS),
Sander Bollen's avatar
Sander Bollen committed
273
        ref=REFERENCE,
Sander Bollen's avatar
Sander Bollen committed
274
        gatk=GATK
Sander Bollen's avatar
Sander Bollen committed
275
    params:
Sander Bollen's avatar
Sander Bollen committed
276
        gvcfs="' -V '".join(expand(out_path("{{sample}}/vcf/{{sample}}.{chunk}.part.vcf.gz"),
Sander Bollen's avatar
Sander Bollen committed
277
                                 chunk=CHUNKS))
Sander Bollen's avatar
Sander Bollen committed
278
279
    output:
        gvcf=out_path("{sample}/vcf/{sample}.g.vcf.gz")
Sander Bollen's avatar
Sander Bollen committed
280
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
281
282
    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
283
284
285
286
           "-assumeSorted"


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


rule genotype_gather:
Sander Bollen's avatar
Sander Bollen committed
305
    """Gather all genotyping scatters"""
Sander Bollen's avatar
Sander Bollen committed
306
307
308
309
310
    input:
        vcfs=expand(out_path("multisample/genotype.{chunk}.part.vcf.gz"),
                    chunk=CHUNKS),
        ref=REFERENCE,
        gatk=GATK
Sander Bollen's avatar
Sander Bollen committed
311
    params:
Sander Bollen's avatar
Sander Bollen committed
312
        vcfs="' -V '".join(expand(out_path("multisample/genotype.{chunk}.part.vcf.gz"),
Sander Bollen's avatar
Sander Bollen committed
313
                                chunk=CHUNKS))
Sander Bollen's avatar
Sander Bollen committed
314
315
316
    output:
        combined=out_path("multisample/genotyped.vcf.gz")
    conda: "envs/gatk.yml"
Sander Bollen's avatar
Sander Bollen committed
317
318
    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
319
           "-assumeSorted"
Sander Bollen's avatar
Sander Bollen committed
320
321


Sander Bollen's avatar
Sander Bollen committed
322
323
324
## bam metrics

rule mapped_num:
Sander Bollen's avatar
Sander Bollen committed
325
    """Calculate number of mapped reads"""
Sander Bollen's avatar
Sander Bollen committed
326
327
328
329
330
331
332
333
334
    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
335
    """Calculate number of mapped bases"""
Sander Bollen's avatar
Sander Bollen committed
336
337
338
339
340
    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
341
    shell: "samtools view -F 4 {input.bam} | cut -f10 | wc -c > {output.num}"
Sander Bollen's avatar
Sander Bollen committed
342
343
344


rule unique_num:
Sander Bollen's avatar
Sander Bollen committed
345
    """Calculate number of unique reads"""
Sander Bollen's avatar
Sander Bollen committed
346
347
348
349
350
    input:
        bam=out_path("{sample}/bams/{sample}.markdup.bam")
    output:
        num=out_path("{sample}/bams/{sample}.unique.num")
    conda: "envs/samtools.yml"
351
    shell: "samtools view -F 4 -F 1024 {input.bam} | wc -l > {output.num}"
Sander Bollen's avatar
Sander Bollen committed
352
353
354


rule usable_basenum:
Sander Bollen's avatar
Sander Bollen committed
355
    """Calculate number of bases on unique reads"""
Sander Bollen's avatar
Sander Bollen committed
356
357
358
359
360
361
    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
362
363
364
365


## fastqc

Sander Bollen's avatar
Sander Bollen committed
366
rule fastqc_raw:
Sander Bollen's avatar
Sander Bollen committed
367
    """Run fastqc on raw fastq files"""
Sander Bollen's avatar
Sander Bollen committed
368
    input:
Sander Bollen's avatar
Sander Bollen committed
369
        r1=get_r1,
Sander Bollen's avatar
Sander Bollen committed
370
371
372
373
374
        r2=get_r2
    params:
        odir=out_path("{sample}/pre_process/raw_fastqc")
    output:
        aux=out_path("{sample}/pre_process/raw_fastqc/.done.txt")
375
    conda: "envs/fastqc.yml"
Sander Bollen's avatar
Sander Bollen committed
376
377
378
    shell: "fastqc -o {params.odir} {input.r1} {input.r2} && echo 'done' > {output.aux}"


Sander Bollen's avatar
Sander Bollen committed
379
rule fastqc_merged:
Sander Bollen's avatar
Sander Bollen committed
380
    """Run fastqc on merged fastq files"""
Sander Bollen's avatar
Sander Bollen committed
381
    input:
Sander Bollen's avatar
Sander Bollen committed
382
        r1=out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"),
Sander Bollen's avatar
Sander Bollen committed
383
384
385
386
        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
387
388
        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")
389
    conda: "envs/fastqc.yml"
Sander Bollen's avatar
Sander Bollen committed
390
    shell: "fastqc -o {params.odir} {input.r1} {input.r2}"
Sander Bollen's avatar
Sander Bollen committed
391
392


Sander Bollen's avatar
Sander Bollen committed
393
rule fastqc_postqc:
Sander Bollen's avatar
Sander Bollen committed
394
    """Run fastqc on fastq files post pre-processing"""
Sander Bollen's avatar
Sander Bollen committed
395
    input:
Sander Bollen's avatar
Sander Bollen committed
396
        r1=out_path("{sample}/pre_process/{sample}.cutadapt_R1.fastq"),
Sander Bollen's avatar
Sander Bollen committed
397
398
399
400
        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
401
402
        r1=out_path("{sample}/pre_process/postqc_fastqc/{sample}.cutadapt_R1_fastqc.zip"),
        r2=out_path("{sample}/pre_process/postqc_fastqc/{sample}.cutadapt_R1_fastqc.zip")
403
    conda: "envs/fastqc.yml"
Sander Bollen's avatar
Sander Bollen committed
404
    shell: "fastqc -o {params.odir} {input.r1} {input.r2}"
Sander Bollen's avatar
Sander Bollen committed
405
406
407
408
409


## fastq-count

rule fqcount_preqc:
Sander Bollen's avatar
Sander Bollen committed
410
    """Calculate number of reads and bases before pre-processing"""
Sander Bollen's avatar
Sander Bollen committed
411
412
413
    input:
        r1=out_path("{sample}/pre_process/{sample}.merged_R1.fastq.gz"),
        r2=out_path("{sample}/pre_process/{sample}.merged_R2.fastq.gz")
414
    params:
415
        fastqcount=fqc
Sander Bollen's avatar
Sander Bollen committed
416
417
    output:
        out_path("{sample}/pre_process/{sample}.preqc_count.json")
418
    shell: "{params.fastqcount} {input.r1} {input.r2} > {output}"
Sander Bollen's avatar
Sander Bollen committed
419
420
421


rule fqcount_postqc:
Sander Bollen's avatar
Sander Bollen committed
422
    """Calculate number of reads and bases after pre-processing"""
Sander Bollen's avatar
Sander Bollen committed
423
424
425
    input:
        r1=out_path("{sample}/pre_process/{sample}.cutadapt_R1.fastq"),
        r2=out_path("{sample}/pre_process/{sample}.cutadapt_R2.fastq")
426
    params:
427
        fastqcount=fqc
Sander Bollen's avatar
Sander Bollen committed
428
429
    output:
        out_path("{sample}/pre_process/{sample}.postqc_count.json")
Sander Bollen's avatar
Sander Bollen committed
430
431
432
433
434
435
    shell: "{params.fastqcount} {input.r1} {input.r2} > {output}"


## coverages

rule covstats:
Sander Bollen's avatar
Sander Bollen committed
436
    """Calculate coverage statistics on bam file"""
Sander Bollen's avatar
Sander Bollen committed
437
438
439
440
441
442
443
444
445
    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
446
        covp=out_path("{sample}/coverage/{bed}.covstats.png")
Sander Bollen's avatar
Sander Bollen committed
447
    conda: "envs/covstat.yml"
Sander Bollen's avatar
Sander Bollen committed
448
449
    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
450
           "--title 'Targets coverage' --subtitle '{params.subt}' > {output.covj}"
Sander Bollen's avatar
Sander Bollen committed
451
452


Sander Bollen's avatar
Sander Bollen committed
453
454
455
## vcfstats

rule vcfstats:
Sander Bollen's avatar
Sander Bollen committed
456
    """Calculate vcf statistics"""
Sander Bollen's avatar
Sander Bollen committed
457
    input:
Sander Bollen's avatar
Sander Bollen committed
458
        vcf=out_path("multisample/genotyped.vcf.gz"),
Sander Bollen's avatar
Sander Bollen committed
459
460
461
462
        vs_py=vs_py
    output:
        stats=out_path("multisample/vcfstats.json")
    conda: "envs/vcfstats.yml"
Sander Bollen's avatar
Sander Bollen committed
463
    shell: "vtools-stats -i {input.vcf} > {output.stats}"
Sander Bollen's avatar
Sander Bollen committed
464
465


Sander Bollen's avatar
Sander Bollen committed
466
467
## collection
rule collectstats:
Sander Bollen's avatar
Sander Bollen committed
468
    """Collect all stats for a particular sample"""
Sander Bollen's avatar
Sander Bollen committed
469
470
471
472
473
474
475
476
477
478
    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
479
        sample_name="{sample}",
Sander Bollen's avatar
Sander Bollen committed
480
        fthresh=FEMALE_THRESHOLD
Sander Bollen's avatar
Sander Bollen committed
481
482
    output:
        out_path("{sample}/{sample}.stats.json")
483
    conda: "envs/collectstats.yml"
Sander Bollen's avatar
Sander Bollen committed
484
485
486
487
    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} "
488
           "--female-threshold {params.fthresh} {input.cov} > {output}"
Sander Bollen's avatar
Sander Bollen committed
489
490

rule merge_stats:
Sander Bollen's avatar
Sander Bollen committed
491
    """Merge all stats of all samples"""
Sander Bollen's avatar
Sander Bollen committed
492
493
494
495
496
497
498
499
    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}"