X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=bam_plcmd.c;h=32d050d68413aa18e6ba4ea688615579a539bf2b;hb=df7cadac556f302bdd0bc48c235c2b61b699dfe1;hp=4bdc351e18a38322bd39dac30433aea3212b3b5a;hpb=0993e913dc402cbb11783bf5d3f96eed1c437e06;p=samtools.git diff --git a/bam_plcmd.c b/bam_plcmd.c index 4bdc351..32d050d 100644 --- a/bam_plcmd.c +++ b/bam_plcmd.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include "sam.h" #include "faidx.h" #include "bam_maqcns.h" @@ -91,6 +93,21 @@ static khash_t(64) *load_pos(const char *fn, bam_header_t *h) return hash; } +static inline int printw(int c, FILE *fp) +{ + char buf[16]; + int l, x; + if (c == 0) return fputc('0', fp); + for (l = 0, x = c < 0? -c : c; x > 0; x /= 10) buf[l++] = x%10 + '0'; + if (c < 0) buf[l++] = '-'; + buf[l] = 0; + for (x = 0; x < l/2; ++x) { + int y = buf[x]; buf[x] = buf[l-1-x]; buf[l-1-x] = y; + } + fputs(buf, fp); + return 0; +} + // an analogy to pileup_func() below static int glt3_func(uint32_t tid, uint32_t pos, int n, const bam_pileup1_t *pu, void *data) { @@ -160,29 +177,38 @@ static int glt3_func(uint32_t tid, uint32_t pos, int n, const bam_pileup1_t *pu, return 0; } -static void pileup_seq(const bam_pileup1_t *p, int pos, int ref_len, const char *ref) +static inline void pileup_seq(const bam_pileup1_t *p, int pos, int ref_len, const char *ref) { - if (p->is_head) printf("^%c", p->b->core.qual > 93? 126 : p->b->core.qual + 33); + int j; + if (p->is_head) { + putchar('^'); + putchar(p->b->core.qual > 93? 126 : p->b->core.qual + 33); + } if (!p->is_del) { - int j, rb, c = bam_nt16_rev_table[bam1_seqi(bam1_seq(p->b), p->qpos)]; - rb = (ref && pos < ref_len)? ref[pos] : 'N'; - if (c == '=' || toupper(c) == toupper(rb)) c = bam1_strand(p->b)? ',' : '.'; - else c = bam1_strand(p->b)? tolower(c) : toupper(c); + int c = bam_nt16_rev_table[bam1_seqi(bam1_seq(p->b), p->qpos)]; + if (ref) { + int rb = pos < ref_len? ref[pos] : 'N'; + if (c == '=' || bam_nt16_table[c] == bam_nt16_table[rb]) c = bam1_strand(p->b)? ',' : '.'; + else c = bam1_strand(p->b)? tolower(c) : toupper(c); + } else { + if (c == '=') c = bam1_strand(p->b)? ',' : '.'; + else c = bam1_strand(p->b)? tolower(c) : toupper(c); + } putchar(c); - if (p->indel > 0) { - printf("+%d", p->indel); - for (j = 1; j <= p->indel; ++j) { - c = bam_nt16_rev_table[bam1_seqi(bam1_seq(p->b), p->qpos + j)]; - putchar(bam1_strand(p->b)? tolower(c) : toupper(c)); - } - } else if (p->indel < 0) { - printf("%d", p->indel); - for (j = 1; j <= -p->indel; ++j) { - c = (ref && (int)pos+j < ref_len)? ref[pos+j] : 'N'; - putchar(bam1_strand(p->b)? tolower(c) : toupper(c)); - } + } else putchar(p->is_refskip? (bam1_strand(p->b)? '<' : '>') : '*'); + if (p->indel > 0) { + putchar('+'); printw(p->indel, stdout); + for (j = 1; j <= p->indel; ++j) { + int c = bam_nt16_rev_table[bam1_seqi(bam1_seq(p->b), p->qpos + j)]; + putchar(bam1_strand(p->b)? tolower(c) : toupper(c)); + } + } else if (p->indel < 0) { + printw(p->indel, stdout); + for (j = 1; j <= -p->indel; ++j) { + int c = (ref && (int)pos+j < ref_len)? ref[pos+j] : 'N'; + putchar(bam1_strand(p->b)? tolower(c) : toupper(c)); } - } else putchar('*'); + } if (p->is_tail) putchar('$'); } @@ -256,22 +282,29 @@ static int pileup_func(uint32_t tid, uint32_t pos, int n, const bam_pileup1_t *p } } // print the first 3 columns - printf("%s\t%d\t%c\t", d->h->target_name[tid], pos + 1, rb); + fputs(d->h->target_name[tid], stdout); putchar('\t'); + printw(pos+1, stdout); putchar('\t'); putchar(rb); putchar('\t'); // print consensus information if required - if (d->format & BAM_PLF_CNS) - printf("%c\t%d\t%d\t%d\t", bam_nt16_rev_table[cns>>28], cns>>8&0xff, cns&0xff, cns>>16&0xff); - // print pileup sequences - printf("%d\t", n); - rms_aux = 0; // we need to recalculate rms_mapq when -c is not flagged on the command line - for (i = 0; i < n; ++i) { - const bam_pileup1_t *p = pu + i; - int tmp = p->b->core.qual < d->c->cap_mapQ? p->b->core.qual : d->c->cap_mapQ; - rms_aux += tmp * tmp; - pileup_seq(p, pos, d->len, d->ref); + if (d->format & BAM_PLF_CNS) { + putchar(bam_nt16_rev_table[cns>>28]); putchar('\t'); + printw(cns>>8&0xff, stdout); putchar('\t'); + printw(cns&0xff, stdout); putchar('\t'); + printw(cns>>16&0xff, stdout); putchar('\t'); } + // print pileup sequences + printw(n, stdout); putchar('\t'); + for (i = 0; i < n; ++i) + pileup_seq(pu + i, pos, d->len, d->ref); // finalize rms_mapq - rms_aux = (uint64_t)(sqrt((double)rms_aux / n) + .499); - if (rms_mapq < 0) rms_mapq = rms_aux; + if (d->format & BAM_PLF_CNS) { + for (i = rms_aux = 0; i < n; ++i) { + const bam_pileup1_t *p = pu + i; + int tmp = p->b->core.qual < d->c->cap_mapQ? p->b->core.qual : d->c->cap_mapQ; + rms_aux += tmp * tmp; + } + rms_aux = (uint64_t)(sqrt((double)rms_aux / n) + .499); + if (rms_mapq < 0) rms_mapq = rms_aux; + } putchar('\t'); // print quality for (i = 0; i < n; ++i) { @@ -310,7 +343,7 @@ static int pileup_func(uint32_t tid, uint32_t pos, int n, const bam_pileup1_t *p for (i = 0; i < n; ++i) { int x = pu[i].qpos; int l = pu[i].b->core.l_qseq; - printf("%d,", x < l/2? x+1 : -((l-1)-x+1)); + printw(x < l/2? x+1 : -((l-1)-x+1), stdout); putchar(','); } } putchar('\n'); @@ -442,6 +475,7 @@ int bam_pileup(int argc, char *argv[]) bam_plp_set_mask(iter, d->mask); while ((ret = samread(fp, b)) >= 0) { int skip = 0; + if ((int)b->core.tid < 0) break; // update d->ref if necessary if (d->fai && (int)b->core.tid != d->tid) { free(d->ref); @@ -496,9 +530,11 @@ int bam_pileup(int argc, char *argv[]) #define MPLP_NO_COMP 0x20 #define MPLP_NO_ORPHAN 0x40 #define MPLP_REALN 0x80 +#define MPLP_FMT_DP 0x100 +#define MPLP_FMT_SP 0x200 typedef struct { - int max_mq, min_mq, flag, min_baseQ, capQ_thres; + int max_mq, min_mq, flag, min_baseQ, capQ_thres, max_depth; char *reg, *fn_pos; faidx_t *fai; kh_64_t *hash; @@ -567,7 +603,7 @@ static void group_smpl(mplp_pileup_t *m, bam_sample_t *sm, kstring_t *buf, static int mpileup(mplp_conf_t *conf, int n, char **fn) { mplp_aux_t **data; - int i, tid, pos, *n_plp, beg0 = 0, end0 = 1u<<29, ref_len, ref_tid; + int i, tid, pos, *n_plp, beg0 = 0, end0 = 1u<<29, ref_len, ref_tid, max_depth; const bam_pileup1_t **plp; bam_mplp_t iter; bam_header_t *h = 0; @@ -660,6 +696,14 @@ static int mpileup(mplp_conf_t *conf, int n, char **fn) } ref_tid = -1; ref = 0; iter = bam_mplp_init(n, mplp_func, (void**)data); + max_depth = conf->max_depth; + if (max_depth * sm->n > 1<<20) + fprintf(stderr, "(%s) Max depth is above 1M. Potential memory hog!\n", __func__); + if (max_depth * sm->n < 8000) { + max_depth = 8000 / sm->n; + fprintf(stderr, "<%s> Set max per-sample depth to %d\n", __func__, max_depth); + } + bam_mplp_set_maxcnt(iter, max_depth); while (bam_mplp_auto(iter, &tid, &pos, n_plp, plp) > 0) { if (conf->reg && (pos < beg0 || pos >= end0)) continue; // out of the region requested if (hash) { @@ -682,9 +726,32 @@ static int mpileup(mplp_conf_t *conf, int n, char **fn) for (i = 0; i < gplp.n; ++i) bcf_call_glfgen(gplp.n_plp[i], gplp.plp[i], ref16, bca, bcr + i); bcf_call_combine(gplp.n, bcr, ref16, &bc); - bcf_call2bcf(tid, pos, &bc, b); + bcf_call2bcf(tid, pos, &bc, b, (conf->flag&(MPLP_FMT_DP|MPLP_FMT_SP))? bcr : 0, (conf->flag&MPLP_FMT_SP)); bcf_write(bp, bh, b); bcf_destroy(b); + // test if there is an indel + if (conf->flag&MPLP_REALN) { + for (i = 0; i < gplp.n; ++i) { + int j; + for (j = 0; j < gplp.n_plp[i]; ++j) { + const bam_pileup1_t *p = gplp.plp[i] + j; + if (p->indel != 0) break; + } + if (j != gplp.n_plp[i]) break; + } + if (i < gplp.n) { // at least one of the read contains a gap + for (i = 0; i < gplp.n; ++i) + bcf_call_glfgen_gap(pos, gplp.n_plp[i], gplp.plp[i], bca, bcr + i); + bcf_call_combine_gap(gplp.n, bcr, &bc); + if (bc.depth > 0) { + b = calloc(1, sizeof(bcf1_t)); + bcf_call2bcf(tid, pos, &bc, b, (conf->flag&(MPLP_FMT_DP|MPLP_FMT_SP))? bcr : 0, + (conf->flag&MPLP_FMT_SP)); + bcf_write(bp, bh, b); + bcf_destroy(b); + } + } + } } else { printf("%s\t%d\t%c", h->target_name[tid], pos + 1, (ref && pos < ref_len)? ref[pos] : 'N'); for (i = 0; i < n; ++i) { @@ -729,31 +796,90 @@ static int mpileup(mplp_conf_t *conf, int n, char **fn) return 0; } +#define MAX_PATH_LEN 1024 +int read_file_list(const char *file_list,int *n,char **argv[]) +{ + char buf[MAX_PATH_LEN]; + int len, nfiles; + char **files; + + FILE *fh = fopen(file_list,"r"); + if ( !fh ) + { + fprintf(stderr,"%s: %s\n", file_list,strerror(errno)); + return 1; + } + + // Speed is not an issue here, determine the number of files by reading the file twice + nfiles = 0; + while ( fgets(buf,MAX_PATH_LEN,fh) ) nfiles++; + + if ( fseek(fh, 0L, SEEK_SET) ) + { + fprintf(stderr,"%s: %s\n", file_list,strerror(errno)); + return 1; + } + + files = calloc(nfiles,sizeof(char*)); + nfiles = 0; + while ( fgets(buf,MAX_PATH_LEN,fh) ) + { + len = strlen(buf); + while ( len>0 && isspace(buf[len-1]) ) len--; + if ( !len ) continue; + + files[nfiles] = malloc(sizeof(char)*(len+1)); + strncpy(files[nfiles],buf,len); + files[nfiles][len] = 0; + nfiles++; + } + fclose(fh); + if ( !nfiles ) + { + fprintf(stderr,"No files read from %s\n", file_list); + return 1; + } + *argv = files; + *n = nfiles; + return 0; +} +#undef MAX_PATH_LEN + int bam_mpileup(int argc, char *argv[]) { int c; + const char *file_list = NULL; + char **fn = NULL; + int nfiles = 0; mplp_conf_t mplp; memset(&mplp, 0, sizeof(mplp_conf_t)); mplp.max_mq = 60; mplp.min_baseQ = 13; mplp.capQ_thres = 0; - while ((c = getopt(argc, argv, "gf:r:l:M:q:Q:uaORC:")) >= 0) { + mplp.max_depth = 250; + mplp.flag = MPLP_NO_ORPHAN | MPLP_REALN; + while ((c = getopt(argc, argv, "gf:r:l:M:q:Q:uaORC:BDSd:b:")) >= 0) { switch (c) { case 'f': mplp.fai = fai_load(optarg); if (mplp.fai == 0) return 1; break; + case 'd': mplp.max_depth = atoi(optarg); break; case 'r': mplp.reg = strdup(optarg); break; case 'l': mplp.fn_pos = strdup(optarg); break; case 'g': mplp.flag |= MPLP_GLF; break; case 'u': mplp.flag |= MPLP_NO_COMP | MPLP_GLF; break; case 'a': mplp.flag |= MPLP_NO_ORPHAN | MPLP_REALN; break; + case 'B': mplp.flag &= ~MPLP_REALN & ~MPLP_NO_ORPHAN; break; case 'O': mplp.flag |= MPLP_NO_ORPHAN; break; case 'R': mplp.flag |= MPLP_REALN; break; + case 'D': mplp.flag |= MPLP_FMT_DP; break; + case 'S': mplp.flag |= MPLP_FMT_SP; break; case 'C': mplp.capQ_thres = atoi(optarg); break; case 'M': mplp.max_mq = atoi(optarg); break; case 'q': mplp.min_mq = atoi(optarg); break; case 'Q': mplp.min_baseQ = atoi(optarg); break; + case 'b': file_list = optarg; break; } } if (argc == 1) { @@ -762,16 +888,29 @@ int bam_mpileup(int argc, char *argv[]) fprintf(stderr, "Options: -f FILE reference sequence file [null]\n"); fprintf(stderr, " -r STR region in which pileup is generated [null]\n"); fprintf(stderr, " -l FILE list of positions (format: chr pos) [null]\n"); + fprintf(stderr, " -b FILE list of input BAM files [null]\n"); fprintf(stderr, " -M INT cap mapping quality at INT [%d]\n", mplp.max_mq); fprintf(stderr, " -Q INT min base quality [%d]\n", mplp.min_baseQ); fprintf(stderr, " -q INT filter out alignment with MQ smaller than INT [%d]\n", mplp.min_mq); + fprintf(stderr, " -d INT max per-sample depth [%d]\n", mplp.max_depth); fprintf(stderr, " -g generate BCF output\n"); fprintf(stderr, " -u do not compress BCF output\n"); + fprintf(stderr, " -B disable BAQ computation\n"); + fprintf(stderr, " -D output per-sample DP\n"); + fprintf(stderr, " -S output per-sample SP (strand bias P-value, slow)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Notes: Assuming diploid individuals.\n\n"); return 1; } - mpileup(&mplp, argc - optind, argv + optind); + if ( file_list ) + { + if ( read_file_list(file_list,&nfiles,&fn) ) return 1; + mpileup(&mplp,nfiles,fn); + for (c=0; c