1 Star 0 Fork 0

Velcon-Zheng/samtools

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
bam_md.c 14.57 KB
一键复制 编辑 原始数据 按行查看 历史
/* bam_md.c -- calmd subcommand.
Copyright (C) 2009-2011, 2014-2015, 2019-2020 Genome Research Ltd.
Portions copyright (C) 2009-2011 Broad Institute.
Author: Heng Li <lh3@sanger.ac.uk>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include "htslib/faidx.h"
#include "htslib/sam.h"
#include "htslib/kstring.h"
#include "htslib/thread_pool.h"
#include "sam_opts.h"
#include "samtools.h"
#define USE_EQUAL 1
#define DROP_TAG 2
#define BIN_QUAL 4
#define UPDATE_NM 8
#define UPDATE_MD 16
#define HASH_QNM 32
int bam_aux_drop_other(bam1_t *b, uint8_t *s);
static int bam_fillmd1_core(const char *ref_name, bam1_t *b, char *ref,
hts_pos_t ref_len, int flag, int max_nm,
int quiet_mode, uint32_t *skipped)
{
uint8_t *seq = bam_get_seq(b);
uint32_t *cigar = bam_get_cigar(b);
bam1_core_t *c = &b->core;
int i, qpos, matched = 0;
hts_pos_t rpos;
kstring_t str = KS_INITIALIZE;
int32_t old_nm_i = -1, nm = 0;
uint32_t err = 0;
if (c->l_qseq == 0) {
if (!quiet_mode) {
if (ref_name) {
fprintf(stderr, "[bam_fillmd1] no sequence in alignment "
"record for '%s' at %s:%"PRIhts_pos", skipped\n",
bam_get_qname(b), ref_name, c->pos + 1);
} else {
fprintf(stderr, "[bam_fillmd1] no sequence in alignment "
"record for '%s', skipped", bam_get_qname(b));
}
}
if (skipped) (*skipped)++;
return 0;
}
for (i = qpos = 0, rpos = c->pos; i < c->n_cigar; ++i) {
int j, oplen = cigar[i]>>4, op = cigar[i]&0xf;
if (op == BAM_CMATCH || op == BAM_CEQUAL || op == BAM_CDIFF) {
for (j = 0; j < oplen; ++j) {
int c1, c2, z = qpos + j;
if (rpos+j >= ref_len || z >= c->l_qseq || ref[rpos+j] == '\0')
break; // out of bounds
c1 = bam_seqi(seq, z);
c2 = seq_nt16_table[(uint8_t)ref[rpos+j]];
if ((c1 == c2 && c1 != 15 && c2 != 15) || c1 == 0) { // a match
if (flag&USE_EQUAL) seq[z/2] &= (z&1)? 0xf0 : 0x0f;
++matched;
} else {
err |= kputw(matched, &str) < 0;
err |= kputc(toupper(ref[rpos+j]), &str) < 0;
matched = 0; ++nm;
}
}
if (j < oplen) break;
rpos += oplen; qpos += oplen;
} else if (op == BAM_CDEL) {
err |= kputw(matched, &str) < 0;
err |= kputc('^', &str) < 0;
for (j = 0; j < oplen; ++j) {
if (rpos+j >= ref_len || ref[rpos+j] == '\0') break;
err |= kputc(toupper(ref[rpos+j]), &str) < 0;
}
matched = 0;
rpos += j; nm += j;
if (j < oplen) break;
} else if (op == BAM_CINS || op == BAM_CSOFT_CLIP) {
qpos += oplen;
if (op == BAM_CINS) nm += oplen;
} else if (op == BAM_CREF_SKIP) {
rpos += oplen;
}
}
err |= kputw(matched, &str) < 0;
if (err) {
print_error_errno("calmd", "Couldn't build new MD string");
goto fail;
}
// apply max_nm
if (max_nm > 0 && nm >= max_nm) {
for (i = qpos = 0, rpos = c->pos; i < c->n_cigar; ++i) {
int j, oplen = cigar[i]>>4, op = cigar[i]&0xf;
if (op == BAM_CMATCH || op == BAM_CEQUAL || op == BAM_CDIFF) {
for (j = 0; j < oplen; ++j) {
int c1, c2, z = qpos + j;
if (rpos+j >= ref_len || z >= c->l_qseq || ref[rpos+j] == '\0')
break; // out of bounds
c1 = bam_seqi(seq, z);
c2 = seq_nt16_table[(uint8_t)ref[rpos+j]];
if ((c1 == c2 && c1 != 15 && c2 != 15) || c1 == 0) { // a match
seq[z/2] |= (z&1)? 0x0f : 0xf0;
bam_get_qual(b)[z] = 0;
}
}
if (j < oplen) break;
rpos += oplen; qpos += oplen;
} else if (op == BAM_CDEL || op == BAM_CREF_SKIP) rpos += oplen;
else if (op == BAM_CINS || op == BAM_CSOFT_CLIP) qpos += oplen;
}
}
// update NM
if ((flag & UPDATE_NM) && !(c->flag & BAM_FUNMAP)) {
uint8_t *old_nm = bam_aux_get(b, "NM");
if (old_nm) old_nm_i = bam_aux2i(old_nm);
if (!old_nm) {
if (bam_aux_append(b, "NM", 'i', 4, (uint8_t*)&nm) < 0)
goto aux_fail;
}
else if (nm != old_nm_i) {
if (!quiet_mode) {
fprintf(stderr, "[bam_fillmd1] different NM for read '%s': %d -> %d\n", bam_get_qname(b), old_nm_i, nm);
}
if (bam_aux_del(b, old_nm) < 0) goto aux_fail;
if (bam_aux_append(b, "NM", 'i', 4, (uint8_t*)&nm) < 0)
goto aux_fail;
}
}
// update MD
if ((flag & UPDATE_MD) && !(c->flag & BAM_FUNMAP)) {
uint8_t *old_md = bam_aux_get(b, "MD");
if (!old_md) {
if (bam_aux_append(b, "MD", 'Z', str.l + 1, (uint8_t*)str.s) < 0)
goto aux_fail;
} else {
int is_diff = 0;
if (strlen((char*)old_md+1) == str.l) {
for (i = 0; i < str.l; ++i)
if (toupper(old_md[i+1]) != toupper(str.s[i]))
break;
if (i < str.l) is_diff = 1;
} else is_diff = 1;
if (is_diff) {
if (!quiet_mode) {
fprintf(stderr, "[bam_fillmd1] different MD for read '%s': '%s' -> '%s'\n", bam_get_qname(b), old_md+1, str.s);
}
if (bam_aux_del(b, old_md) < 0) goto aux_fail;
if (bam_aux_append(b, "MD", 'Z', str.l + 1, (uint8_t*)str.s) < 0)
goto aux_fail;
}
}
}
// drop all tags but RG
if (flag&DROP_TAG) {
uint8_t *q = bam_aux_get(b, "RG");
bam_aux_drop_other(b, q);
}
// reduce the resolution of base quality
if (flag&BIN_QUAL) {
uint8_t *qual = bam_get_qual(b);
for (i = 0; i < b->core.l_qseq; ++i)
if (qual[i] >= 3) qual[i] = qual[i]/10*10 + 7;
}
free(str.s);
return 0;
aux_fail:
if (errno == ENOMEM) {
print_error("calmd", "Couldn't add aux tag (too long)");
} else if (errno == EINVAL) {
print_error("calmd", "Corrupt aux data");
} else {
print_error_errno("calmd", "Couldn't add aux tag");
}
fail:
free(str.s);
return -1;
}
int bam_fillmd1(bam1_t *b, char *ref, int flag, int quiet_mode)
{
return bam_fillmd1_core(NULL, b, ref, INT_MAX, flag, 0, quiet_mode, NULL);
}
int calmd_usage() {
fprintf(stderr,
"Usage: samtools calmd [-eubrAESQ] <aln.bam> <ref.fasta>\n"
"Options:\n"
" -e change identical bases to '='\n"
" -u uncompressed BAM output (for piping)\n"
" -b compressed BAM output\n"
" -S ignored (input format is auto-detected)\n"
" -A modify the quality string\n"
" -Q use quiet mode to output less debug info to stdout\n"
" -r compute the BQ tag (without -A) or cap baseQ by BAQ (with -A)\n"
" -E extended BAQ for better sensitivity but lower specificity\n"
" --no-PG do not add a PG line\n");
sam_global_opt_help(stderr, "-....@-.");
return 1;
}
int bam_fillmd(int argc, char *argv[])
{
int c, flt_flag, tid = -2, ret, is_bam_out, is_uncompressed, max_nm, is_realn, capQ, baq_flag, quiet_mode, no_pg = 0;
hts_pos_t len;
htsThreadPool p = {NULL, 0};
samFile *fp = NULL, *fpout = NULL;
sam_hdr_t *header = NULL;
faidx_t *fai = NULL;
char *ref = NULL, mode_w[8], *ref_file, *arg_list = NULL;
const char *ref_name = NULL;
bam1_t *b = NULL;
sam_global_args ga = SAM_GLOBAL_ARGS_INIT;
uint32_t skipped = 0;
static const struct option lopts[] = {
SAM_OPT_GLOBAL_OPTIONS('-', 0, 0, 0, 0,'@'),
{"no-PG", no_argument, NULL, 1},
{ NULL, 0, NULL, 0 }
};
flt_flag = UPDATE_NM | UPDATE_MD;
is_bam_out = is_uncompressed = is_realn = max_nm = capQ = baq_flag = quiet_mode = 0;
strcpy(mode_w, "w");
while ((c = getopt_long(argc, argv, "EqQreuNhbSC:n:Ad@:", lopts, NULL)) >= 0) {
switch (c) {
case 'r': is_realn = 1; break;
case 'e': flt_flag |= USE_EQUAL; break;
case 'd': flt_flag |= DROP_TAG; break;
case 'q': flt_flag |= BIN_QUAL; break;
case 'h': flt_flag |= HASH_QNM; break;
case 'N': flt_flag &= ~(UPDATE_MD|UPDATE_NM); break;
case 'b': is_bam_out = 1; break;
case 'u': is_uncompressed = is_bam_out = 1; break;
case 'S': break;
case 'n': max_nm = atoi(optarg); break;
case 'C': capQ = atoi(optarg); break;
case 'A': baq_flag |= 1; break;
case 'E': baq_flag |= 2; break;
case 'Q': quiet_mode = 1; break;
case 1: no_pg = 1; break;
default: if (parse_sam_global_opt(c, optarg, lopts, &ga) == 0) break;
fprintf(stderr, "[bam_fillmd] unrecognized option '-%c'\n\n", c);
/* else fall-through */
case '?': return calmd_usage();
}
}
if (is_bam_out) strcat(mode_w, "b");
else strcat(mode_w, "h");
if (is_uncompressed) strcat(mode_w, "0");
if (optind + (ga.reference == NULL) >= argc)
return calmd_usage();
fp = sam_open_format(argv[optind], "r", &ga.in);
if (fp == NULL) {
print_error_errno("calmd", "Failed to open input file '%s'", argv[optind]);
return 1;
}
if (!no_pg && !(arg_list = stringify_argv(argc+1, argv-1))) {
print_error("calmd", "failed to create arg_list");
return 1;
}
header = sam_hdr_read(fp);
if (header == NULL || sam_hdr_nref(header) == 0) {
fprintf(stderr, "[bam_fillmd] input SAM does not have header. Abort!\n");
goto fail;
}
fpout = sam_open_format("-", mode_w, &ga.out);
if (fpout == NULL) {
print_error_errno("calmd", "Failed to open output");
goto fail;
}
if (!no_pg && sam_hdr_add_pg(header, "samtools",
"VN", samtools_version(),
arg_list ? "CL": NULL,
arg_list ? arg_list : NULL,
NULL)) {
print_error("calmd", "failed to add PG line to header");
goto fail;
}
if (sam_hdr_write(fpout, header) < 0) {
print_error_errno("calmd", "Failed to write sam header");
goto fail;
}
if (ga.nthreads > 0) {
if (!(p.pool = hts_tpool_init(ga.nthreads))) {
fprintf(stderr, "Error creating thread pool\n");
goto fail;
}
hts_set_opt(fp, HTS_OPT_THREAD_POOL, &p);
hts_set_opt(fpout, HTS_OPT_THREAD_POOL, &p);
}
ref_file = argc > optind + 1 ? argv[optind+1] : ga.reference;
fai = fai_load(ref_file);
if (!fai) {
print_error_errno("calmd", "Failed to open reference file '%s'", ref_file);
goto fail;
}
b = bam_init1();
if (!b) {
fprintf(stderr, "[bam_fillmd] Failed to allocate bam struct\n");
goto fail;
}
while ((ret = sam_read1(fp, header, b)) >= 0) {
if (b->core.tid >= 0) {
if (tid != b->core.tid) {
free(ref);
ref = NULL;
len = 0;
ref_name = sam_hdr_tid2name(header, b->core.tid);
if (ref_name) {
ref = fai_fetch64(fai, ref_name, &len);
}
tid = b->core.tid;
if (ref == 0) { // FIXME: Should this always be fatal?
fprintf(stderr, "[bam_fillmd] fail to find sequence '%s' in the reference.\n",
ref_name ? ref_name : "(unknown)");
if (is_realn || capQ > 10) goto fail; // Would otherwise crash
}
}
if (is_realn) {
if (sam_prob_realn(b, ref, len, baq_flag) < -3) {
print_error_errno("calmd", "BAQ alignment failed");
goto fail;
}
}
if (capQ > 10) {
int q = sam_cap_mapq(b, ref, len, capQ);
if (b->core.qual > q) b->core.qual = q;
}
if (ref) {
if (bam_fillmd1_core(ref_name, b, ref, len, flt_flag, max_nm,
quiet_mode, &skipped) < 0)
goto fail;
}
}
if (sam_write1(fpout, header, b) < 0) {
print_error_errno("calmd", "failed to write to output file");
goto fail;
}
}
if (ret < -1) {
fprintf(stderr, "[bam_fillmd] Error reading input.\n");
goto fail;
}
if (skipped) {
fprintf(stderr, "[calmd] Warning: %"PRIu32" records skipped due "
"to no query sequence\n",
skipped);
}
bam_destroy1(b);
sam_hdr_destroy(header);
free(arg_list);
free(ref);
fai_destroy(fai);
sam_close(fp);
if (sam_close(fpout) < 0) {
fprintf(stderr, "[bam_fillmd] error when closing output file\n");
return 1;
}
if (p.pool) hts_tpool_destroy(p.pool);
return 0;
fail:
free(arg_list);
free(ref);
if (b) bam_destroy1(b);
if (header) sam_hdr_destroy(header);
if (fai) fai_destroy(fai);
if (fp) sam_close(fp);
if (fpout) sam_close(fpout);
if (p.pool) hts_tpool_destroy(p.pool);
return 1;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/Velcon-Zheng/samtools.git
git@gitee.com:Velcon-Zheng/samtools.git
Velcon-Zheng
samtools
samtools
develop

搜索帮助

0d507c66 1850385 C8b1a773 1850385