Skip to content

Commit 3ffd2a0

Browse files
authored
Merge branch 'master' into static_repart
2 parents e6dea46 + 9f0253b commit 3ffd2a0

13 files changed

Lines changed: 1021 additions & 8 deletions

File tree

‎include/kmtricks/cli/cli_common.hpp‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,27 @@ inline void add_common(bc::cmd_t cmd, km_options_t options)
5151
->setter(options->verbosity);
5252
}
5353

54-
}; // namespace kmdiff
54+
template <typename T>
55+
inline void add_bam_options(bc::cmd_t cmd, std::shared_ptr<T> options)
56+
{
57+
cmd->add_group("bam filtering options", "");
58+
59+
cmd->add_param("--bam-exclude-refs", "exclude reads mapping to specified references (comma-separated, e.g., chrM,chrY)")
60+
->meta("STR")
61+
->def("")
62+
->setter(options->bam_exclude_refs);
63+
64+
cmd->add_param("-f/--bam-include-flags", "include reads with ALL these flags set (samtools -f style, decimal or hex with 0x prefix)")
65+
->meta("INT")
66+
->def("0")
67+
->checker(bc::check::is_number)
68+
->setter(options->bam_include_flags);
69+
70+
cmd->add_param("-F/--bam-exclude-flags", "exclude reads with ANY of these flags set (samtools -F style, decimal or hex with 0x prefix)")
71+
->meta("INT")
72+
->def("0")
73+
->checker(bc::check::is_number)
74+
->setter(options->bam_exclude_flags);
75+
}
76+
77+
}; // namespace kmdiff

‎include/kmtricks/cmd.hpp‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,12 @@ struct main_repart
8686
opt->repart_type,
8787
1,
8888
opt->nb_parts);
89-
ConfigTask<MAX_K> config_task(opt->fof, props, opt->bloom_size, opt->nb_parts);
89+
ConfigTask<MAX_K> config_task(opt->fof, props, opt->bloom_size, opt->nb_parts,
90+
opt->bam_exclude_refs, opt->bam_include_flags, opt->bam_exclude_flags);
9091
config_task.exec();
91-
RepartTask<MAX_K> repart_task(opt->fof, "", opt->static_repart); repart_task.exec(); repart_task.postprocess();
92+
93+
RepartTask<MAX_K> repart_task(opt->fof, opt->bam_exclude_refs, opt->bam_include_flags, opt->bam_exclude_flags, "", opt->static_repart);
94+
repart_task.exec(); repart_task.postprocess();
9295

9396
Storage* config_storage = StorageFactory(STORAGE_FILE).load(KmDir::get().m_config_storage);
9497
LOCAL(config_storage);

‎include/kmtricks/cmd/all.hpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ struct all_options : km_options
6666

6767
std::string from;
6868

69+
std::string bam_exclude_refs;
70+
uint32_t bam_include_flags {0};
71+
uint32_t bam_exclude_flags {0};
72+
6973
MODE mode;
7074
FORMAT format;
7175
OUT_FORMAT out_format;
@@ -104,6 +108,9 @@ struct all_options : km_options
104108
RECORD(ss, focus);
105109
RECORD(ss, restrict_to);
106110
RECORD(ss, bwidth);
111+
RECORD(ss, bam_exclude_refs);
112+
RECORD(ss, bam_include_flags);
113+
RECORD(ss, bam_exclude_flags);
107114
#ifdef WITH_PLUGIN
108115
RECORD(ss, use_plugin);
109116
RECORD(ss, plugin);

‎include/kmtricks/cmd/repart.hpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ struct repart_options : km_options
3838
uint32_t repart_type;
3939
uint32_t nb_parts;
4040
uint64_t bloom_size;
41+
std::string bam_exclude_refs;
42+
uint32_t bam_include_flags {0};
43+
uint32_t bam_exclude_flags {0};
4144

4245
bool static_repart {false};
4346

@@ -52,6 +55,9 @@ struct repart_options : km_options
5255
RECORD(ss, repart_type);
5356
RECORD(ss, nb_parts);
5457
RECORD(ss, static_repart);
58+
RECORD(ss, bam_exclude_refs);
59+
RECORD(ss, bam_include_flags);
60+
RECORD(ss, bam_exclude_flags);
5561
std::string ret = ss.str(); ret.pop_back(); ret.pop_back();
5662
return ret;
5763
}

‎include/kmtricks/task.hpp‎

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,42 @@
4242
#include <kmtricks/plugin_manager.hpp>
4343
#endif
4444

45+
#include <gatb/bank/impl/BankBam.hpp>
46+
4547
namespace km {
4648

49+
inline void apply_bam_filtering(IBank* bank, const std::string& bam_exclude_refs,
50+
uint32_t bam_include_flags = 0, uint32_t bam_exclude_flags = 0) {
51+
BankBam* bam_bank = dynamic_cast<BankBam*>(bank);
52+
if (!bam_bank) return;
53+
54+
// Apply reference filtering
55+
if (!bam_exclude_refs.empty()) {
56+
std::set<std::string> excluded_refs;
57+
std::istringstream ss(bam_exclude_refs);
58+
std::string ref;
59+
while (std::getline(ss, ref, ',')) {
60+
// Trim whitespace
61+
ref.erase(0, ref.find_first_not_of(" \t"));
62+
ref.erase(ref.find_last_not_of(" \t") + 1);
63+
if (!ref.empty()) {
64+
excluded_refs.insert(ref);
65+
}
66+
}
67+
if (!excluded_refs.empty()) {
68+
bam_bank->setExcludedReferences(excluded_refs);
69+
}
70+
}
71+
72+
// Apply flag filtering (samtools -f/-F style)
73+
if (bam_include_flags != 0) {
74+
bam_bank->setRequireFlags(static_cast<uint16_t>(bam_include_flags));
75+
}
76+
if (bam_exclude_flags != 0) {
77+
bam_bank->setExcludeFlags(static_cast<uint16_t>(bam_exclude_flags));
78+
}
79+
}
80+
4781
namespace fs = std::filesystem;
4882
using parti_info_t = std::shared_ptr<PartiInfo<5>>;
4983

@@ -52,8 +86,11 @@ class ConfigTask : public ITask
5286
{
5387
public:
5488
ConfigTask(const std::string& path, IProperties* props, uint64_t bloom_size,
55-
uint32_t partitions)
56-
: ITask(0), m_path(path), m_props(props), m_bloom_size(bloom_size), m_nb_partitions(partitions)
89+
uint32_t partitions, const std::string& bam_exclude_refs = "",
90+
uint32_t bam_include_flags = 0, uint32_t bam_exclude_flags = 0)
91+
: ITask(0), m_path(path), m_props(props), m_bloom_size(bloom_size), m_nb_partitions(partitions),
92+
m_bam_exclude_refs(bam_exclude_refs), m_bam_include_flags(bam_include_flags),
93+
m_bam_exclude_flags(bam_exclude_flags)
5794
{}
5895

5996
void preprocess() {}
@@ -63,6 +100,7 @@ class ConfigTask : public ITask
63100
spdlog::debug("[exec] - ConfigTask");
64101
spdlog::info("{} samples found ({} read files).", KmDir::get().m_fof.size(), KmDir::get().m_fof.total());
65102
IBank* bank = Bank::open(KmDir::get().m_fof.get_all()); LOCAL(bank);
103+
apply_bam_filtering(bank, m_bam_exclude_refs, m_bam_include_flags, m_bam_exclude_flags);
66104
Storage* config_storage =
67105
StorageFactory(STORAGE_FILE).create(KmDir::get().m_config_storage, true, false);
68106
LOCAL(config_storage);
@@ -90,6 +128,9 @@ class ConfigTask : public ITask
90128
IProperties* m_props;
91129
uint64_t m_bloom_size;
92130
uint32_t m_nb_partitions;
131+
std::string m_bam_exclude_refs;
132+
uint32_t m_bam_include_flags;
133+
uint32_t m_bam_exclude_flags;
93134
};
94135

95136
void check_repart_compatibility(Configuration& c1, Configuration& c2,
@@ -109,8 +150,11 @@ template<size_t span>
109150
class RepartTask : public ITask
110151
{
111152
public:
112-
RepartTask(const std::string& path, const std::string& from = "", bool static_repart = false)
113-
: ITask(1), m_path(path), m_from(from), m_static_repart(static_repart) {}
153+
154+
RepartTask(const std::string& path, const std::string& bam_exclude_refs = "",
155+
uint32_t bam_include_flags = 0, uint32_t bam_exclude_flags = 0, const std::string& from = "", bool static_repart = false)
156+
: ITask(1), m_path(path), m_bam_exclude_refs(bam_exclude_refs),
157+
m_bam_include_flags(bam_include_flags), m_bam_exclude_flags(bam_exclude_flags), m_from(from), m_static_repart(static_repart) {}
114158

115159
void preprocess() {}
116160
void postprocess()
@@ -142,9 +186,11 @@ class RepartTask : public ITask
142186
{
143187
Fof fof(m_path);
144188
IBank* bank = Bank::open(fof.get_all()); LOCAL(bank);
189+
apply_bam_filtering(bank, m_bam_exclude_refs, m_bam_include_flags, m_bam_exclude_flags);
145190
Storage* rep_store =
146191
StorageFactory(STORAGE_FILE).create(KmDir::get().m_repart_storage, true, false);
147192

193+
148194
LOCAL(rep_store);
149195

150196
RepartitorAlgorithm<span> repartition(
@@ -180,6 +226,9 @@ class RepartTask : public ITask
180226

181227
private:
182228
std::string m_path;
229+
std::string m_bam_exclude_refs;
230+
uint32_t m_bam_include_flags;
231+
uint32_t m_bam_exclude_flags;
183232
std::string m_from;
184233
int m_cores;
185234
uint32_t m_nb_parts {0};

‎include/kmtricks/task_scheduler.hpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ class TaskScheduler
9090
1,
9191
m_opt->nb_parts,
9292
m_opt->max_memory);
93-
ConfigTask<MAX_K> config_task(m_opt->fof, props, m_opt->bloom_size, m_opt->nb_parts);
93+
ConfigTask<MAX_K> config_task(m_opt->fof, props, m_opt->bloom_size, m_opt->nb_parts,
94+
m_opt->bam_exclude_refs, m_opt->bam_include_flags, m_opt->bam_exclude_flags);
9495
config_task.exec();
9596
Storage* config_storage = StorageFactory(STORAGE_FILE).load(KmDir::get().m_config_storage);
9697
LOCAL(config_storage);

‎src/cli.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ km_options_t all_cli(std::shared_ptr<bc::Parser<1>> cli, all_options_t options)
353353
->checker(bc::check::is_number)
354354
->setter(options->bwidth);
355355

356+
add_bam_options(all_cmd, options);
357+
356358
#ifdef WITH_PLUGIN
357359
auto plugin_setter = [options](const std::string& v) {
358360
options->plugin = v;
@@ -436,6 +438,8 @@ km_options_t repart_cli(std::shared_ptr<bc::Parser<1>> cli, repart_options_t opt
436438
->checker(bc::check::is_number)
437439
->setter(options->bloom_size);
438440

441+
add_bam_options(repart_cmd, options);
442+
439443
add_common(repart_cmd, options);
440444

441445
return options;

‎test.bam‎

345 Bytes
Binary file not shown.

‎tests/bam_test.cpp‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include <gtest/gtest.h>
2+
#include <gatb/gatb_core.hpp>
3+
4+
using namespace gatb::core::bank;
5+
6+
class BamTest : public ::testing::Test {
7+
protected:
8+
void SetUp() override {
9+
test_fasta = "data/1.fasta";
10+
test_bam = "../test.bam";
11+
}
12+
13+
std::string test_fasta;
14+
std::string test_bam;
15+
};
16+
17+
TEST_F(BamTest, FormatDetection) {
18+
// Test that BAM and FASTA files are correctly detected
19+
EXPECT_EQ(Bank::getType(test_bam), "bam");
20+
EXPECT_EQ(Bank::getType(test_fasta), "fasta");
21+
}
22+
23+
TEST_F(BamTest, SequenceEquality) {
24+
// Test that BAM and FASTA return identical sequences
25+
IBank* bam_bank = Bank::open(test_bam);
26+
IBank* fasta_bank = Bank::open(test_fasta);
27+
28+
ASSERT_NE(bam_bank, nullptr);
29+
ASSERT_NE(fasta_bank, nullptr);
30+
31+
gatb::core::tools::dp::Iterator<Sequence>* bam_it = bam_bank->iterator();
32+
gatb::core::tools::dp::Iterator<Sequence>* fasta_it = fasta_bank->iterator();
33+
34+
int count = 0;
35+
for (bam_it->first(), fasta_it->first();
36+
!bam_it->isDone() && !fasta_it->isDone();
37+
bam_it->next(), fasta_it->next()) {
38+
count++;
39+
EXPECT_EQ(bam_it->item().toString(), fasta_it->item().toString())
40+
<< "Sequence " << count << " should match";
41+
}
42+
43+
EXPECT_EQ(count, 2);
44+
EXPECT_TRUE(bam_it->isDone() && fasta_it->isDone());
45+
46+
delete bam_it;
47+
delete fasta_it;
48+
delete bam_bank;
49+
delete fasta_bank;
50+
}

‎tests/test_ref_filter.cpp‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <gatb/gatb_core.hpp>
2+
#include <iostream>
3+
#include <set>
4+
5+
using namespace gatb::core::bank;
6+
7+
int main() {
8+
std::string test_bam = "../test.bam";
9+
10+
// First, read without filtering
11+
std::cout << "Reading without filtering:" << std::endl;
12+
IBank* bank1 = Bank::open(test_bam);
13+
gatb::core::tools::dp::Iterator<Sequence>* it1 = bank1->iterator();
14+
int count1 = 0;
15+
for (it1->first(); !it1->isDone(); it1->next()) {
16+
count1++;
17+
std::cout << " Read " << count1 << ": " << it1->item().getComment() << std::endl;
18+
}
19+
delete it1;
20+
delete bank1;
21+
22+
std::cout << "\nTotal reads without filtering: " << count1 << std::endl;
23+
24+
return 0;
25+
}

0 commit comments

Comments
 (0)