#!/usr/bin/make -rRf
# Run the ABySS assembler.
# Written by Shaun Jackman <sjackman@bcgsc.ca> and
# Anthony Raymond <traymond@bcgsc.ca>.

# Set pipefail to require that all commands of a pipe must succeed.
SHELL=/bin/bash -o pipefail

# Define this environment variable on Mac OS X to read
# compressed files.
export DYLD_FORCE_FLAT_NAMESPACE=1

# Integrate with Sun Grid Engine (SGE)
ifdef JOB_NAME
name?=$(JOB_NAME)
endif
ifdef SGE_TASK_ID
k?=$(SGE_TASK_ID)
endif
ifdef NSLOTS
ifneq ($(NSLOTS), 1)
np?=$(NSLOTS)
endif
endif

# Integrate with Portable Batch System (PBS)
ifdef PBS_JOBNAME
name?=$(PBS_JOBNAME)
endif
ifdef PBS_ARRAYID
k?=$(PBS_ARRAYID)
endif
ifdef PBS_NODEFILE
NSLOTS=$(shell wc -l <$(PBS_NODEFILE))
ifneq ($(NSLOTS), 1)
np?=$(NSLOTS)
endif
endif

# Integrate with Load Sharing Facility (LSF)
ifdef LSB_JOBNAME
name?=$(LSB_JOBNAME)
endif
ifdef LSB_JOBINDEX
k?=$(LSB_JOBINDEX)
endif
ifdef LSB_DJOB_NUMPROC
ifneq ($(LSB_DJOB_NUMPROC), 1)
np?=$(LSB_DJOB_NUMPROC)
endif
endif
ifdef LSF_BINDIR
mpirun?=$(LSF_BINDIR)/mpirun.lsf
endif

# Integrate with IBM LoadLeveler
ifdef LOADL_JOB_NAME
name?=$(LOADL_JOB_NAME)
endif
ifdef LOADL_STEP_ID
k?=$(LOADL_STEP_ID)
endif
ifdef LOADL_HOSTFILE
NSLOTS=$(shell wc -l <$(LOADL_HOSTFILE))
ifneq ($(NSLOTS), 1)
np?=$(NSLOTS)
endif
endif

# Integrate with SLURM
ifdef SLURM_JOB_NAME
name?=$(SLURM_JOB_NAME)
endif
ifdef SLURM_JOBID
k?=$(SLURM_JOBID)
endif
ifdef SLURM_NTASKS
np?=$(SLURM_NTASKS)
endif

# Determine the path to mpirun
mpirun?=$(shell which mpirun)
ifeq ($(mpirun),)
mpirun=mpirun
endif

# Determine the path to the ABySS executables
path?=$(shell dirname `which $(MAKEFILE_LIST)`)
ifdef path
PATH:=$(path):$(PATH)
endif

# Programs
MARKDOWN=multimarkdown

ifdef lib
map=$(foreach a,$(2),$(call $(1),$(a)))
deref=$($1)
in?=$(call map, deref, $(lib))
else
ifdef in
lib?=$(name)
$(lib)?=$(in)
endif
endif
pe?=$(lib)
mp?=$(pe)

# Strip spaces from the file paths
ifdef in
override in:=$(strip $(in))
endif
ifdef se
override se:=$(strip $(se))
endif

# ABYSS parameters
q ?= 3
abyssopt += -k$k -q$q
ifdef e
abyssopt += -e$e
endif
ifdef E
abyssopt += -E$E
endif
ifdef t
abyssopt += -t$t
endif
ifdef c
abyssopt += -c$c
endif
ifdef b
abyssopt += -b$b
pbopt += -b$b
endif
ifdef Q
abyssopt += -Q$Q
endif
ifdef ss
SS=--SS
endif
abyssopt += $v $(SS) --coverage-hist=coverage.hist -s $*-bubbles.fa

# Number of threads
ifdef PE_HOSTFILE
hostname?=$(shell hostname -f)
j?=$(shell awk '$$1 == "$(hostname)" {print $$2}' $(PE_HOSTFILE))
endif
ifeq ($j,)
j:=$(np)
endif
ifeq ($j,)
j:=2
endif

# AdjList parameters
m?=50

# filtergraph parameters
ifdef xtip
fgopt := -t$(shell echo $k*2 |bc)
endif

# PopBubbles parameters
p?=0.9
pbopt += -p$p
ifdef a
pbopt += -a$a
endif

# Keep sequence and quality strings of read alignments
ifdef ssq
ssq_t :=-ssq
endif

# Aligner parameters
aligner?=map$(ssq_t)
ifeq ($(SS),--SS)
 ifneq ($(aligner),map$(ssq_t))
  $(warning warning: setting aligner to abyss-map$(ssq_t) since other aligners do not support ss=1)
  override aligner=map$(ssq_t)
 endif
endif
align?=abyss-$(aligner)
mapopt=$v -j$j -l$($*_l) $(SS) $(ALIGNER_OPTIONS) $(MAP_OPTIONS)

# fixmate parameters
ifeq ($(align),abyss-kaligner)
fixmate?=ParseAligns
else
fixmate?=abyss-fixmate$(ssq_t)
endif
fmopt=$v -l$($*_l) $(FIXMATE_OPTIONS)

# DistanceEst parameters
DistanceEst?=DistanceEst$(ssq_t)
l?=$k
s?=200
n?=10
libs=$(pe) $(mp)
$(foreach i,$(libs),$(eval $i_l?=$l))
$(foreach i,$(libs),$(eval $i_s?=$s))
$(foreach i,$(libs),$(eval $i_n?=$n))
deopt=$v -j$j -k$k -l$($*_l) -s$($*_s) -n$($*_n) $($*_de) \
	$(DISTANCEEST_OPTIONS)

# SimpleGraph parameters
ifdef d
sgopt += -d$d
endif

# PathConsensus parameters
ifdef a
pcopt += -a$a
endif
pcopt += -p$p

# Scaffold parameters
S?=$s
N?=$n

# BWA-SW parameters
bwaswopt=-t$j
BWASW_OPTIONS='-b9 -q16 -r1 -w500'

# Remove environment variables
unexport in se $(lib) $(pe) $(mp) $(long)

# Check the mandatory parameters

ifndef name
error::
	@>&2 echo 'abyss-pe: missing parameter `name`'
endif
ifndef k
error::
	@>&2 echo 'abyss-pe: missing parameter `k`'
endif
ifeq ($(lib)$(in)$(se),)
error::
	@>&2 echo 'abyss-pe: missing parameter `lib`, `in` or `se`'
endif

default:

error::
	@>&2 echo 'Try `abyss-pe help` for more information.'
	@false

# Help and version messages

help:
	@printf '\
Usage: abyss-pe [OPTION]... [PARAMETER=VALUE]... [COMMAND]...\n\
Assemble reads into contigs and scaffolds. ABySS is a de novo\n\
sequence assembler intended for short paired-end reads and large\n\
genomes. See the abyss-pe man page for documentation of assembly\n\
parameters and commands. abyss-pe is a Makefile script, and so\n\
options of `make` may also be used with abyss-pe. See the `make`\n\
man page for documentation.\n\
\n\
Report bugs to <abyss-users@bcgsc.ca>.\n'

version:
	@printf '\
abyss-pe (ABySS) 1.5.1\n\
Written by Shaun Jackman and Anthony Raymond.\n\
\n\
Copyright 2012 Canada'\''s Michael Smith Genome Science Centre\n'

versions: version
	@echo PATH=$(PATH)
	@ABYSS --version; echo
	@-ABYSS-P --version; echo
	@AdjList --version; echo
	@DistanceEst --version; echo
	@MergeContigs --version; echo
	@MergePaths --version; echo
	@Overlap --version; echo
	@PathConsensus --version; echo
	@PathOverlap --version; echo
	@PopBubbles --version; echo
	@SimpleGraph --version; echo
	@abyss-fac --version; echo
	@abyss-filtergraph --version; echo
	@abyss-fixmate --version; echo
	@abyss-map --version; echo
	@abyss-scaffold --version; echo
	@abyss-todot --version; echo
	@$(align) --version; echo
	@awk --version; echo
	@sort --version; echo
	@-mpirun --version

# Determine the default target
default: unitigs
ifneq ($(in),)
default: contigs contigs-dot
endif
ifneq ($(mp),)
default: scaffolds scaffolds-dot
ifneq ($(long),)
default: long-scaffs long-scaffs-dot
endif
endif
default: stats

# Define the commands (phony targets)
unitigs: $(name)-unitigs.fa

unitigs-dot: $(name)-unitigs.dot

pe-index: $(name)-3.fa.fm

pe-sam: $(addsuffix -3.sam.gz, $(pe))

pe-bam: $(addsuffix -3.bam.bai, $(pe))

contigs: $(name)-contigs.fa

contigs-dot: $(name)-contigs.dot

mp-index: $(name)-6.fa.fm

mp-sam: $(addsuffix -6.sam.gz, $(mp))

mp-bam: $(addsuffix -6.bam.bai, $(mp))

scaffolds: $(name)-scaffolds.fa

scaffolds-dot: $(name)-scaffolds.dot

scaftigs: $(name)-scaftigs.fa $(name)-scaftigs.agp

long-scaffs: $(name)-long-scaffs.fa

long-scaffs-dot: $(name)-long-scaffs.dot

all: default bam stats

clean:
	rm -f *.adj *.dot *.sam.gz *.hist *.dist *.path *.path[123]

.PHONY: bam default stats \
	unitigs unitigs-dot \
	pe-index pe-sam pe-bam contigs contigs-dot \
	mp-index mp-sam mp-bam scaffolds scaffolds-dot \
	scaftigs long-scaffs long-scaffs-dot \
	all clean help version versions
.DELETE_ON_ERROR:
.SECONDARY:

# Utilities

%.fa.fm: %.fa
	abyss-index $v $<

%.bam: %.sam.gz
	samtools view -Sb $< -o $@

%.bam.bai: %.bam
	samtools index $<

# Assemble unitigs

%-1.fa:
ifdef np
	$(mpirun) -np $(np) ABYSS-P $(abyssopt) $(ABYSS_OPTIONS) -o $@ $(in) $(se)
else
	ABYSS $(abyssopt) $(ABYSS_OPTIONS) -o $@ $(in) $(se)
endif

# Find overlapping contigs

%-1.adj: %-1.fa
	AdjList $(SS) $v -k$k -m$m $< >$@

# Remove shim contigs

%-2.adj: %-1.adj
	abyss-filtergraph $v $(fgopt) $(FILTERGRAPH_OPTIONS) -k$k -g $@ $^ >$*-1.path

# Pop bubbles

%-2.path %-3.adj: %-1.fa %-2.adj
	PopBubbles $v -j$j -k$k $(SS) $(pbopt) $(POPBUBBLES_OPTIONS) -g $*-3.adj $^ >$*-2.path

%-3.fa: %-1.fa %-2.adj %-2.path
	MergeContigs $v -k$k -o $@ $^
	awk '!/^>/ {x[">" $$1]=1; next} {getline s} $$1 in x {print $$0 "\n" s}' \
		$*-2.path $*-1.fa >$*-indel.fa

%-3.dot: %-3.adj
	abyss-todot $v -k$k $< >$@

%-unitigs.fa: %-3.fa
	ln -sf $< $@

%-unitigs.dot: %-3.dot
	ln -sf $< $@

# Estimate distances between unitigs

%-3.sam.gz %-3.hist: $(name)-3.fa
	$(align) $(mapopt) $(strip $($*)) $< \
		|$(fixmate) $(fmopt) -h $*-3.hist \
		|sort -snk3 -k4 \
		|gzip >$*-3.sam.gz

%-3.bam %-3.hist: $(name)-3.fa
	$(align) $(mapopt) $(strip $($*)) $< \
		|$(fixmate) $(fmopt) -h $*-3.hist \
		|sort -snk3 -k4 \
		|samtools view -Sb - -o $*-3.bam

%-3.dist: %-3.sam.gz %-3.hist
	gunzip -c $< \
	|$(DistanceEst) $(deopt) -o $@ $*-3.hist

%-3.dist: %-3.bam %-3.hist
	samtools view -h $< \
	|$(DistanceEst) $(deopt) -o $@ $*-3.hist

%-3.dist: $(name)-3.fa
	$(align) $(mapopt) $(strip $($*)) $< \
		|$(fixmate) $(fmopt) -h $*-3.hist \
		|sort -snk3 -k4 \
		|$(DistanceEst) $(deopt) -o $@ $*-3.hist

dist=$(addsuffix -3.dist, $(pe))

ifneq ($(name)-3.dist, $(dist))
$(name)-3.dist: $(name)-3.fa $(dist)
	abyss-todot $v --dist -e $^ >$@

$(name)-3.bam: $(addsuffix -3.bam, $(pe))
	samtools merge -r $@ $^
endif

# Find overlaps between contigs

%-4.fa %-4.adj: %-3.fa %-3.adj %-3.dist
	Overlap $v $(SS) $(OVERLAP_OPTIONS) -k$k -g $*-4.adj -o $*-4.fa $^

# Assemble contigs

%-4.path1: %-4.adj %-3.dist
	SimpleGraph $v $(sgopt) $(SIMPLEGRAPH_OPTIONS) -j$j -k$k -o $@ $^

%-4.path2: %-4.adj %-4.path1
	MergePaths $v $(MERGEPATHS_OPTIONS) -j$j -k$k -o $@ $^

%-4.path3: %-4.adj %-4.path2
	PathOverlap --assemble $(SS) $v -k$k $^ >$@

ifndef cs

%-5.path %-5.fa %-5.adj: %-3.fa %-4.fa %-4.adj %-4.path3
	cat $(wordlist 1, 2, $^) \
		|PathConsensus $v -k$k $(pcopt) -o $*-5.path -s $*-5.fa -g $*-5.adj - $(wordlist 3, 4, $^)

%-6.fa: %-3.fa %-4.fa %-5.fa %-5.adj %-5.path
	cat $(wordlist 1, 3, $^) |MergeContigs $v -k$k -o $@ - $(wordlist 4, 5, $^)

else

%-5.adj %-5.path: %-4.adj %-4.path3
	ln -sf $*-4.adj $*-5.adj
	ln -sf $*-4.path3 $*-5.path

%-cs.fa: %-3.fa %-4.fa %-4.adj %-4.path3
	cat $(wordlist 1, 2, $^) |MergeContigs $v -k$k -o $@ - $(wordlist 3, 4, $^)

# Convert colour-space sequence to nucleotides

%-6.fa: %-cs.fa
	KAligner $v --seq -m -j$j -l$l $(in) $(se) $< \
		|Consensus $v -o $@ $<

endif

%-6.dot: %-5.adj %-5.path
	PathOverlap --overlap $v --dot -k$k $^ >$@

%-contigs.fa: %-6.fa
	ln -sf $< $@

%-contigs.dot: %-6.dot
	ln -sf $< $@

# Estimate distances between contigs

%-6.sam.gz %-6.hist: $(name)-6.fa
	$(align) $(mapopt) $(strip $($*)) $< \
		|$(fixmate) $(fmopt) -h $*-6.hist \
		|sort -snk3 -k4 \
		|gzip >$*-6.sam.gz

%-6.bam %-6.hist: $(name)-6.fa
	$(align) $(mapopt) $(strip $($*)) $< \
		|$(fixmate) $(fmopt) -h $*-6.hist \
		|sort -snk3 -k4 \
		|samtools view -Sb - -o $*-6.bam

%-6.dist.dot: %-6.sam.gz %-6.hist
	gunzip -c $< \
	|$(DistanceEst) --dot $(deopt) -o $@ $*-6.hist

%-6.dist.dot: %-6.bam %-6.hist
	samtools view -h $< \
	|$(DistanceEst) --dot $(deopt) -o $@ $*-6.hist

%-6.dist.dot: $(name)-6.fa
	$(align) $(mapopt) $(strip $($*)) $< \
		|$(fixmate) $(fmopt) -h $*-6.hist \
		|sort -snk3 -k4 \
		|$(DistanceEst) --dot $(deopt) -o $@ $*-6.hist

# Scaffold

%-6.path: $(name)-6.dot $(addsuffix -6.dist.dot, $(mp))
	abyss-scaffold $v $(SS) -k$k -s$S -n$N -g $@.dot $(SCAFFOLD_OPTIONS) $^ >$@

%-7.path %-7.adj %-7.fa: %-6.fa %-6.dot %-6.path
	PathConsensus $v -k$k $(pcopt) -s $*-7.fa -g $*-7.adj -o $*-7.path $^

%-8.fa: %-6.fa %-7.fa %-7.adj %-7.path
	cat $(wordlist 1, 2, $^) \
		|MergeContigs $v -k$k -o $@ - $(wordlist 3, 4, $^)

%-8.dot: %-7.adj %-7.path
	PathOverlap --overlap $v --dot -k$k $^ >$@

%-scaffolds.fa: %-8.fa
	ln -sf $< $@

%-scaffolds.dot: %-8.dot
	ln -sf $< $@

# Scaftig

%-scaftigs.fa: %-scaffolds-agp.fa
	ln -sf $< $@

%-scaftigs.agp: %-scaffolds.agp
	ln -sf $< $@

# Transcriptome assisted scaffolding

%.fa.bwt: %.fa
	bwa index $<

%-8.sam.gz: $(name)-8.fa.bwt
	bwa mem -a -t$j -S -P -k$l $(name)-8.fa $(strip $($*)) \
		|gzip >$@

%-8.dist.dot: %-8.sam.gz
	abyss-longseqdist -k$k $(LONGSEQDIST_OPTIONS) $< \
		|grep -v "l=" >$@

%-8.path: $(name)-8.dot $(addsuffix -8.dist.dot, $(long))
	abyss-scaffold $v $(SS) -k$k -s$S -n1 -g $@.dot $(SCAFFOLD_OPTIONS) $^ >$@

%-9.path %-9.adj %-9.fa: %-8.fa %-8.dot %-8.path
	PathConsensus $v -k$k $(pcopt) -s $*-9.fa -g $*-9.adj -o $*-9.path $^

%-10.fa: %-8.fa %-9.fa %-9.adj %-9.path
	cat $(wordlist 1, 2, $^) \
		|MergeContigs $v -k$k -o $@ - $(wordlist 3, 4, $^)

%-10.dot: %-9.adj %-9.path
	PathOverlap --overlap $v --dot -k$k $^ >$@

%-long-scaffs.fa: %-10.fa
	ln -sf $< $@

%-long-scaffs.dot: %-10.dot
	ln -sf $< $@

# Create the final BAM file

ifneq ($(mp),)
bam: $(name)-scaffolds.bam.bai
else
ifneq ($(in),)
bam: $(name)-contigs.bam.bai
else
bam: $(name)-unitigs.bam.bai
endif
endif

$(name)-unitigs.bam: %.bam: %.fa
	$(align) $v -j$j -l$l $(ALIGNER_OPTIONS) $(se) $< \
		|samtools view -Su - |samtools sort -o - - >$@

$(name)-contigs.bam $(name)-scaffolds.bam: %.bam: %.fa
	$(align) $v -j$j -l$l $(ALIGNER_OPTIONS) \
		$(call map, deref, $(sort $(lib) $(pe) $(mp))) $< \
		|$(fixmate) $(fmopt) \
		|sort -snk3 -k4 \
		|samtools view -Sb - >$@

# Align the variants to the assembly

%-variants.bam: %.fa.bwt
	bwa bwasw -t$j $*.fa <(cat $(name)-bubbles.fa $(name)-indel.fa) \
		|samtools view -Su - |samtools sort -o - - >$@

%-variants.vcf.gz: %.fa %-variants.bam
	samtools mpileup -Buf $^ |bcftools view -vp1 - |bgzip >$@

%.gz.tbi: %.gz
	tabix -pvcf $<

# Calculate assembly contiguity statistics

stats: $(name)-stats.tab $(name)-stats $(name)-stats.csv $(name)-stats.md
ifneq ($(shell which $(MARKDOWN) 2>/dev/null),)
stats: $(name)-stats.html
endif

%-stats: %-stats.tab
	ln -sf $< $@

$(name)-stats.tab: %-stats.tab: %-unitigs.fa
ifneq ($(in),)
$(name)-stats.tab: %-stats.tab: %-contigs.fa
endif
ifneq ($(mp),)
$(name)-stats.tab: %-stats.tab: %-scaffolds.fa
endif
ifneq ($(long),)
$(name)-stats.tab: %-stats.tab: %-long-scaffs.fa
endif
$(name)-stats.tab:
	abyss-fac $(FAC_OPTIONS) $^ |tee $@

%.csv: %.tab
	tr '\t' , <$< >$@

%.md: %.tab
	abyss-tabtomd $< >$@

%.html: %.md
	$(MARKDOWN) $< >$@

# Create an AGP file and FASTA file of scaftigs from scaffolds

%.agp %-agp.fa: %.fa
	abyss-fatoagp $(FATOAGP_OPTIONS) -f $*-agp.fa $< >$*.agp

# Align the contigs to the reference

%-$(ref).sam.gz: %.fa
	bwa bwasw $(bwaswopt) $(BWASW_OPTIONS) $($(ref)) $< |gzip >$@

# Find breakpoints in the alignments

%.break: %.sam.gz
	abyss-samtobreak $(SAMTOBREAK_OPTIONS) $< >$@

# Report ABySS configuration variable(s) and value(s) currently set.

override varList := a b c d e E j k l m n N p q s S t v cs pi \
	np pe lib mp se SS hostname xtip \
	ssq ssq_ti libs path name in mpirun \
	aligner long ref fixmate DistanceEst \
	map deref abyssopt fgopt pbopt \
	align mapopt fmopt deopt \
	pcopt sgopt bwaswopt \
	ABYSS_OPTIONS FILTERGRAPH_OPTIONS POPBUBBLES_OPTIONS \
	OVERLAP_OPTIONS SIMPLEGRAPH_OPTIONS MERGEPATHS_OPTIONS \
	SCAFFOLD_OPTIONS ALIGNER_OPTIONS MAP_OPTIONS FAC_OPTIONS \
	FIXMATE_OPTIONS BWASW_OPTIONS FATOAGP_OPTIONS SAMTOBREAK_OPTIONS \
	MARKDOWN

.PHONY: env
env:
	@echo -e "\
	List of ABySS configuration variables currently set:\n\n\
	[environment], if variable was inherited from the environment.\n\
	[command line], if variable was defined on the command line.\n\
	[file], if variable was defined in (this) makefile.\n\
	[override], if variable was defined with an override directive in (this) makefile.\n"

	@$(foreach var,$(varList),\
		echo -e $(var)" = "$($(var))"\t["$(origin $(var))"]" | grep -v "undefined";)
