@@ -206,17 +206,24 @@ void assignLineages (MAT::Tree& T, const std::string& clade_to_nid_filename, boo
206206
207207void parse_clade_mutations (const std::string& clade_mutations_filename,
208208 std::map<std::string, std::vector<MAT ::Mutation>>& clade_mutations,
209- std::unordered_set<std::string> clades_already_assigned) {
209+ std::unordered_set<std::string>& clades_already_assigned) {
210210 std::ifstream infile (clade_mutations_filename);
211211 if (!infile) {
212212 fprintf (stderr, " ERROR: Could not open the clade mutations file: %s!\n " , clade_mutations_filename.c_str ());
213213 exit (1 );
214214 }
215+ // While parsing, keep a map of lineage name to mutations for all clades, even the ones that we won't need to
216+ // search for because they're in clades_already_assigned, in case a descendant refers to the lineage as ancestor.
217+ std::map<std::string, std::vector<MAT ::Mutation>> all_clade_mutations;
215218 std::string line;
216219
217220 fprintf (stderr, " Reading clade mutations file %s.\n " , clade_mutations_filename.c_str ());
218221 bool got_error = false ;
219222 while (std::getline (infile, line)) {
223+ if (line[0 ] == ' #' ) {
224+ // Comment line -- skip it
225+ continue ;
226+ }
220227 std::vector<std::string> words;
221228 MAT::string_split (line, ' \t ' , words);
222229 // Empty second word (for root node, no mutations) is ignored by string_split; add it back.
@@ -229,15 +236,28 @@ void parse_clade_mutations(const std::string& clade_mutations_filename,
229236 continue ;
230237 }
231238 std::string clade = words[0 ];
232- // If clade has already been assigned by a method with higher precedence, move on to the next one.
233- if (clades_already_assigned.find (clade) != clades_already_assigned.end ()) {
239+ // It's an error if the same clade is defined on multiple lines (probably copy-paste symptom).
240+ if (all_clade_mutations.find (clade) != all_clade_mutations.end ()) {
241+ fprintf (stderr, " ERROR: clade %s is defined on multiple lines\n " , clade.c_str ());
242+ got_error = true ;
234243 continue ;
235244 }
236- // Parse mutations from words[1] and store in clade_mutations[words[0]]
237- std::vector<MAT ::Mutation> mutations;
238- std::vector<int > mut_positions;
245+ // Parse mutations from words[1] and store in all_clade_mutations[words[0]]
246+ // Use MAT::Node to store mutations so we can use its method add_mutation to handle multiple mutations at the
247+ // same position.
248+ MAT ::Node node;
239249 std::vector<std::string> mut_words;
240250 MAT::string_split (words[1 ], mut_words);
251+ // If first mut_word is the name of a lineage defined on a previous line, start with that lineage's mutations
252+ if (mut_words.size () > 0 ) {
253+ auto iter = all_clade_mutations.find (mut_words[0 ]);
254+ if (iter != all_clade_mutations.end ()) {
255+ // Copy the included lineage's vector of mutations
256+ node.mutations = iter->second ;
257+ // Remove the lineage name from mut_words
258+ mut_words.erase (mut_words.begin ());
259+ }
260+ }
241261 for (std::string path_el: mut_words) {
242262 // Ignore empty string or ">"
243263 if (path_el == " " || path_el == " >" ) {
@@ -256,20 +276,22 @@ void parse_clade_mutations(const std::string& clade_mutations_filename,
256276 mut_string.c_str (), clade.c_str (), path_el.c_str ());
257277 got_error = true ;
258278 } else {
259- if (std::find (mut_positions.begin (), mut_positions.end (), mut->position ) != mut_positions.end ()) {
260- fprintf (stderr, " ERROR: Clade %s: position %d used multiple times, must appear only once per clade.\n " ,
261- clade.c_str (), mut->position );
262- got_error = true ;
263- continue ;
264- }
265- mut_positions.emplace_back (mut->position );
266- mutations.emplace_back (std::move (*mut));
279+ node.add_mutation (*mut);
267280 }
281+ delete mut;
268282 }
269283 }
270- clade_mutations [clade] = mutations;
284+ all_clade_mutations [clade] = node. mutations ;
271285 }
272286 infile.close ();
287+
288+ // For all clades that are not in clades_already_assigned, add mutations to clade_mutations.
289+ for (auto it: all_clade_mutations) {
290+ const std::string clade = (const std::string)(it.first );
291+ if (clades_already_assigned.find (clade) == clades_already_assigned.end ()) {
292+ clade_mutations[clade] = it.second ;
293+ }
294+ }
273295 if (got_error) {
274296 fprintf (stderr, " Encountered errors -- exiting.\n " );
275297 exit (1 );
@@ -465,11 +487,6 @@ void assignLineages (MAT::Tree& T, const std::string& clade_filename,
465487 const std::string& mutations_filename, const std::string& details_filename) {
466488 static tbb::affinity_partitioner ap;
467489
468- fprintf (stderr, " Copying tree with uncondensed leaves.\n " );
469- timer.Start ();
470- auto uncondensed_T = MAT::get_tree_copy (T);
471- uncondensed_T.uncondense_leaves ();
472-
473490 auto dfs = T.depth_first_expansion ();
474491 size_t total_nodes = dfs.size ();
475492 FILE *mutations_file = NULL , *details_file = NULL ;
@@ -486,6 +503,8 @@ void assignLineages (MAT::Tree& T, const std::string& clade_filename,
486503 }
487504
488505
506+ fprintf (stderr, " Initializing annotations.\n " );
507+ timer.Start ();
489508 init_annotations (dfs, clear_current);
490509 std::unordered_map<std::string, size_t > dfs_idx;
491510 for (size_t idx = 0 ; idx < total_nodes; idx++) {
@@ -505,9 +524,13 @@ void assignLineages (MAT::Tree& T, const std::string& clade_filename,
505524 parse_clade_mutations (clade_mutations_filename, clade_mutations_map, clades_already_assigned);
506525 }
507526 if (clade_filename != " " ) {
527+ fprintf (stderr, " Copying tree with uncondensed leaves.\n " );
528+ timer.Start ();
529+ auto uncondensed_T = MAT::get_tree_copy (T);
530+ uncondensed_T.uncondense_leaves ();
508531 parse_clade_names (clade_filename, clade_mutations_map, clades_already_assigned, clade_map,
509532 uncondensed_T, min_freq, mask_freq);
510-
533+ fprintf (stderr, " Completed in %ld msec \n\n " , timer. Stop ());
511534 }
512535
513536 struct Node_freq {
0 commit comments